xref: /kernel/linux/linux-6.6/arch/arm64/kvm/pmu.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2019 Arm Limited
462306a36Sopenharmony_ci * Author: Andrew Murray <Andrew.Murray@arm.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/kvm_host.h>
762306a36Sopenharmony_ci#include <linux/perf_event.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct kvm_pmu_events, kvm_pmu_events);
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Given the perf event attributes and system type, determine
1362306a36Sopenharmony_ci * if we are going to need to switch counters at guest entry/exit.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_cistatic bool kvm_pmu_switch_needed(struct perf_event_attr *attr)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	/**
1862306a36Sopenharmony_ci	 * With VHE the guest kernel runs at EL1 and the host at EL2,
1962306a36Sopenharmony_ci	 * where user (EL0) is excluded then we have no reason to switch
2062306a36Sopenharmony_ci	 * counters.
2162306a36Sopenharmony_ci	 */
2262306a36Sopenharmony_ci	if (has_vhe() && attr->exclude_user)
2362306a36Sopenharmony_ci		return false;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	/* Only switch if attributes are different */
2662306a36Sopenharmony_ci	return (attr->exclude_host != attr->exclude_guest);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct kvm_pmu_events *kvm_get_pmu_events(void)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	return this_cpu_ptr(&kvm_pmu_events);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * Add events to track that we may want to switch at guest entry/exit
3662306a36Sopenharmony_ci * time.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_civoid kvm_set_pmu_events(u32 set, struct perf_event_attr *attr)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct kvm_pmu_events *pmu = kvm_get_pmu_events();
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!kvm_arm_support_pmu_v3() || !kvm_pmu_switch_needed(attr))
4362306a36Sopenharmony_ci		return;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (!attr->exclude_host)
4662306a36Sopenharmony_ci		pmu->events_host |= set;
4762306a36Sopenharmony_ci	if (!attr->exclude_guest)
4862306a36Sopenharmony_ci		pmu->events_guest |= set;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * Stop tracking events
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_civoid kvm_clr_pmu_events(u32 clr)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct kvm_pmu_events *pmu = kvm_get_pmu_events();
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (!kvm_arm_support_pmu_v3())
5962306a36Sopenharmony_ci		return;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	pmu->events_host &= ~clr;
6262306a36Sopenharmony_ci	pmu->events_guest &= ~clr;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define PMEVTYPER_READ_CASE(idx)				\
6662306a36Sopenharmony_ci	case idx:						\
6762306a36Sopenharmony_ci		return read_sysreg(pmevtyper##idx##_el0)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define PMEVTYPER_WRITE_CASE(idx)				\
7062306a36Sopenharmony_ci	case idx:						\
7162306a36Sopenharmony_ci		write_sysreg(val, pmevtyper##idx##_el0);	\
7262306a36Sopenharmony_ci		break
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define PMEVTYPER_CASES(readwrite)				\
7562306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(0);			\
7662306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(1);			\
7762306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(2);			\
7862306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(3);			\
7962306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(4);			\
8062306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(5);			\
8162306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(6);			\
8262306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(7);			\
8362306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(8);			\
8462306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(9);			\
8562306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(10);			\
8662306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(11);			\
8762306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(12);			\
8862306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(13);			\
8962306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(14);			\
9062306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(15);			\
9162306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(16);			\
9262306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(17);			\
9362306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(18);			\
9462306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(19);			\
9562306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(20);			\
9662306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(21);			\
9762306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(22);			\
9862306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(23);			\
9962306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(24);			\
10062306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(25);			\
10162306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(26);			\
10262306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(27);			\
10362306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(28);			\
10462306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(29);			\
10562306a36Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(30)
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * Read a value direct from PMEVTYPER<idx> where idx is 0-30
10962306a36Sopenharmony_ci * or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31).
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic u64 kvm_vcpu_pmu_read_evtype_direct(int idx)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	switch (idx) {
11462306a36Sopenharmony_ci	PMEVTYPER_CASES(READ);
11562306a36Sopenharmony_ci	case ARMV8_PMU_CYCLE_IDX:
11662306a36Sopenharmony_ci		return read_sysreg(pmccfiltr_el0);
11762306a36Sopenharmony_ci	default:
11862306a36Sopenharmony_ci		WARN_ON(1);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * Write a value direct to PMEVTYPER<idx> where idx is 0-30
12662306a36Sopenharmony_ci * or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31).
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_cistatic void kvm_vcpu_pmu_write_evtype_direct(int idx, u32 val)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	switch (idx) {
13162306a36Sopenharmony_ci	PMEVTYPER_CASES(WRITE);
13262306a36Sopenharmony_ci	case ARMV8_PMU_CYCLE_IDX:
13362306a36Sopenharmony_ci		write_sysreg(val, pmccfiltr_el0);
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci	default:
13662306a36Sopenharmony_ci		WARN_ON(1);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*
14162306a36Sopenharmony_ci * Modify ARMv8 PMU events to include EL0 counting
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_cistatic void kvm_vcpu_pmu_enable_el0(unsigned long events)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	u64 typer;
14662306a36Sopenharmony_ci	u32 counter;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	for_each_set_bit(counter, &events, 32) {
14962306a36Sopenharmony_ci		typer = kvm_vcpu_pmu_read_evtype_direct(counter);
15062306a36Sopenharmony_ci		typer &= ~ARMV8_PMU_EXCLUDE_EL0;
15162306a36Sopenharmony_ci		kvm_vcpu_pmu_write_evtype_direct(counter, typer);
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * Modify ARMv8 PMU events to exclude EL0 counting
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistatic void kvm_vcpu_pmu_disable_el0(unsigned long events)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	u64 typer;
16162306a36Sopenharmony_ci	u32 counter;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	for_each_set_bit(counter, &events, 32) {
16462306a36Sopenharmony_ci		typer = kvm_vcpu_pmu_read_evtype_direct(counter);
16562306a36Sopenharmony_ci		typer |= ARMV8_PMU_EXCLUDE_EL0;
16662306a36Sopenharmony_ci		kvm_vcpu_pmu_write_evtype_direct(counter, typer);
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/*
17162306a36Sopenharmony_ci * On VHE ensure that only guest events have EL0 counting enabled.
17262306a36Sopenharmony_ci * This is called from both vcpu_{load,put} and the sysreg handling.
17362306a36Sopenharmony_ci * Since the latter is preemptible, special care must be taken to
17462306a36Sopenharmony_ci * disable preemption.
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_civoid kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct kvm_pmu_events *pmu;
17962306a36Sopenharmony_ci	u32 events_guest, events_host;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (!kvm_arm_support_pmu_v3() || !has_vhe())
18262306a36Sopenharmony_ci		return;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	preempt_disable();
18562306a36Sopenharmony_ci	pmu = kvm_get_pmu_events();
18662306a36Sopenharmony_ci	events_guest = pmu->events_guest;
18762306a36Sopenharmony_ci	events_host = pmu->events_host;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	kvm_vcpu_pmu_enable_el0(events_guest);
19062306a36Sopenharmony_ci	kvm_vcpu_pmu_disable_el0(events_host);
19162306a36Sopenharmony_ci	preempt_enable();
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci * On VHE ensure that only host events have EL0 counting enabled
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_civoid kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct kvm_pmu_events *pmu;
20062306a36Sopenharmony_ci	u32 events_guest, events_host;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!kvm_arm_support_pmu_v3() || !has_vhe())
20362306a36Sopenharmony_ci		return;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	pmu = kvm_get_pmu_events();
20662306a36Sopenharmony_ci	events_guest = pmu->events_guest;
20762306a36Sopenharmony_ci	events_host = pmu->events_host;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	kvm_vcpu_pmu_enable_el0(events_host);
21062306a36Sopenharmony_ci	kvm_vcpu_pmu_disable_el0(events_guest);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci * With VHE, keep track of the PMUSERENR_EL0 value for the host EL0 on the pCPU
21562306a36Sopenharmony_ci * where PMUSERENR_EL0 for the guest is loaded, since PMUSERENR_EL0 is switched
21662306a36Sopenharmony_ci * to the value for the guest on vcpu_load().  The value for the host EL0
21762306a36Sopenharmony_ci * will be restored on vcpu_put(), before returning to userspace.
21862306a36Sopenharmony_ci * This isn't necessary for nVHE, as the register is context switched for
21962306a36Sopenharmony_ci * every guest enter/exit.
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * Return true if KVM takes care of the register. Otherwise return false.
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_cibool kvm_set_pmuserenr(u64 val)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct kvm_cpu_context *hctxt;
22662306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (!kvm_arm_support_pmu_v3() || !has_vhe())
22962306a36Sopenharmony_ci		return false;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	vcpu = kvm_get_running_vcpu();
23262306a36Sopenharmony_ci	if (!vcpu || !vcpu_get_flag(vcpu, PMUSERENR_ON_CPU))
23362306a36Sopenharmony_ci		return false;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	hctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt;
23662306a36Sopenharmony_ci	ctxt_sys_reg(hctxt, PMUSERENR_EL0) = val;
23762306a36Sopenharmony_ci	return true;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/*
24162306a36Sopenharmony_ci * If we interrupted the guest to update the host PMU context, make
24262306a36Sopenharmony_ci * sure we re-apply the guest EL0 state.
24362306a36Sopenharmony_ci */
24462306a36Sopenharmony_civoid kvm_vcpu_pmu_resync_el0(void)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (!has_vhe() || !in_interrupt())
24962306a36Sopenharmony_ci		return;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	vcpu = kvm_get_running_vcpu();
25262306a36Sopenharmony_ci	if (!vcpu)
25362306a36Sopenharmony_ci		return;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	kvm_make_request(KVM_REQ_RESYNC_PMU_EL0, vcpu);
25662306a36Sopenharmony_ci}
257