Set up MDCR_EL2 to handle a Partitioned PMU. If partitioned, set the HPME, HPMD, and HCCD bits. If we have the ability to use Fine Grain Traps (FEAT_FGT) also, unset the TPM and TPMCR bits that trap all PMU accesses and set HPMN to the correct number of guest counters so hardware enforces the right values.
Protect vcpu->arch.mdcr_el2 updates with local_irq_save() to prevent races against PMU interrupt handlers modifying MDCR_EL2.HPME. Signed-off-by: Colton Lewis <[email protected]> --- arch/arm64/kvm/debug.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c index f4d7b12045e8f..a722fd4594e09 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -37,14 +37,16 @@ static int cpu_has_spe(u64 dfr0) */ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) { - preempt_disable(); + unsigned long flags; + + local_irq_save(flags); /* * This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK * to disable guest access to the profiling and trace buffers */ - vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN, - *host_data_ptr(nr_event_counters)); + + vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN, *host_data_ptr(nr_event_counters)); vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM | MDCR_EL2_TPMS | MDCR_EL2_TTRF | @@ -52,6 +54,35 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) MDCR_EL2_TDRA | MDCR_EL2_TDOSA); + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + u8 nr_guest_cntr = vcpu->kvm->arch.nr_pmu_counters; + u64 hpmn = FIELD_GET(MDCR_EL2_HPMN, read_sysreg(mdcr_el2)); + bool hpme; + + if (hpmn < *host_data_ptr(nr_event_counters)) + hpme = !!(read_sysreg(mdcr_el2) & MDCR_EL2_HPME); + else + hpme = !!(read_sysreg(pmcr_el0) & ARMV8_PMU_PMCR_E); + + vcpu->arch.mdcr_el2 |= (MDCR_EL2_HPMD | MDCR_EL2_HCCD); + if (hpme) + vcpu->arch.mdcr_el2 |= MDCR_EL2_HPME; + + /* + * Take out the coarse grain traps if we are using + * fine grain traps and enforce counter access with + * HPMN. + */ + if (!vcpu_on_unsupported_cpu(vcpu) && + (cpus_have_final_cap(ARM64_HAS_HPMN0) || nr_guest_cntr > 0)) { + vcpu->arch.mdcr_el2 &= ~MDCR_EL2_HPMN; + vcpu->arch.mdcr_el2 |= FIELD_PREP(MDCR_EL2_HPMN, nr_guest_cntr); + + if (cpus_have_final_cap(ARM64_HAS_FGT)) + vcpu->arch.mdcr_el2 &= ~(MDCR_EL2_TPM | MDCR_EL2_TPMCR); + } + } + /* Is the VM being debugged by userspace? */ if (vcpu->guest_debug) /* Route all software debug exceptions to EL2 */ @@ -70,7 +101,7 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) if (has_vhe()) write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2); - preempt_enable(); + local_irq_restore(flags); } void kvm_init_host_debug_data(void) -- 2.56.0.rc1.310.g51773c2048-goog

