[PATCH v9 11/22] KVM: arm64: Set up MDCR_EL2 to handle a Partitioned PMU
From: Colton Lewis
Date: Thu Sep 24 2026 - 14:30:05 EST
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 <coltonlewis@xxxxxxxxxx>
---
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