Currently, with VHE, KVM sets ER, CR, SW and EN bits of PMUSERENR_EL0 to 1 on vcpu_load(). So, if the value of those bits are cleared after vcpu_load() (the perf subsystem would do when PMU counters are programmed for the guest), PMU access from the guest EL0 might be trapped to the guest EL1 directly regardless of the current PMUSERENR_EL0 value of the vCPU. With VHE, fix this by setting those bits of the register on every guest entry (as with nVHE). Also, opportunistically make the similar change for PMSELR_EL0, which is cleared by vcpu_load(), to ensure it is always set to zero on guest entry (PMXEVCNTR_EL0 access might cause UNDEF at EL1 instead of being trapped to EL2, depending on the value of PMSELR_EL0). I think that would be more robust, although I don't find any kernel code that writes PMSELR_EL0. Fixes: 83a7a4d643d3 ("arm64: perf: Enable PMU counter userspace access for perf event") Signed-off-by: Reiji Watanabe <reijiw@xxxxxxxxxx> --- arch/arm64/kvm/hyp/include/hyp/switch.h | 29 +++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 44b84fbdde0d..7d39882d8a73 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -74,18 +74,6 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu) /* Trap on AArch32 cp15 c15 (impdef sysregs) accesses (EL1 or EL0) */ write_sysreg(1 << 15, hstr_el2); - /* - * Make sure we trap PMU access from EL0 to EL2. Also sanitize - * PMSELR_EL0 to make sure it never contains the cycle - * counter, which could make a PMXEVCNTR_EL0 access UNDEF at - * EL1 instead of being trapped to EL2. - */ - if (kvm_arm_support_pmu_v3()) { - write_sysreg(0, pmselr_el0); - vcpu->arch.host_pmuserenr_el0 = read_sysreg(pmuserenr_el0); - write_sysreg(ARMV8_PMU_USERENR_MASK, pmuserenr_el0); - } - vcpu->arch.mdcr_el2_host = read_sysreg(mdcr_el2); write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2); @@ -106,8 +94,6 @@ static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu) write_sysreg(vcpu->arch.mdcr_el2_host, mdcr_el2); write_sysreg(0, hstr_el2); - if (kvm_arm_support_pmu_v3()) - write_sysreg(vcpu->arch.host_pmuserenr_el0, pmuserenr_el0); if (cpus_have_final_cap(ARM64_SME)) { sysreg_clear_set_s(SYS_HFGRTR_EL2, 0, @@ -130,6 +116,18 @@ static inline void ___activate_traps(struct kvm_vcpu *vcpu) if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && (hcr & HCR_VSE)) write_sysreg_s(vcpu->arch.vsesr_el2, SYS_VSESR_EL2); + + /* + * Make sure we trap PMU access from EL0 to EL2. Also sanitize + * PMSELR_EL0 to make sure it never contains the cycle + * counter, which could make a PMXEVCNTR_EL0 access UNDEF at + * EL1 instead of being trapped to EL2. + */ + if (kvm_arm_support_pmu_v3()) { + write_sysreg(0, pmselr_el0); + vcpu->arch.host_pmuserenr_el0 = read_sysreg(pmuserenr_el0); + write_sysreg(ARMV8_PMU_USERENR_MASK, pmuserenr_el0); + } } static inline void ___deactivate_traps(struct kvm_vcpu *vcpu) @@ -144,6 +142,9 @@ static inline void ___deactivate_traps(struct kvm_vcpu *vcpu) vcpu->arch.hcr_el2 &= ~HCR_VSE; vcpu->arch.hcr_el2 |= read_sysreg(hcr_el2) & HCR_VSE; } + + if (kvm_arm_support_pmu_v3()) + write_sysreg(vcpu->arch.host_pmuserenr_el0, pmuserenr_el0); } static inline bool __populate_fault_info(struct kvm_vcpu *vcpu) -- 2.40.0.348.gf938b09366-goog