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