KVM: arm64: Fix MDCR_EL2.HPMN reset value
authorMarc Zyngier <maz@kernel.org>
Mon, 17 Feb 2025 10:17:20 +0000 (10:17 +0000)
committerMarc Zyngier <maz@kernel.org>
Fri, 11 Apr 2025 11:58:51 +0000 (12:58 +0100)
The MDCR_EL2 documentation indicates that the HPMN field has
the following behaviour:

"On a Warm reset, this field resets to the expression NUM_PMU_COUNTERS."

However, it appears we reset it to zero, which is not very useful.

Add a reset helper for MDCR_EL2, and handle the case where userspace
changes the target PMU, which may force us to change HPMN again.

Reported-by: Joey Gouly <joey.gouly@arm.com>
Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/pmu-emul.c
arch/arm64/kvm/sys_regs.c

index 60b5a5e4a6c59576eb731a4217624c4f5d903a19..2df54508f5aee751affd7df9f88491bcaf0f9b6e 100644 (file)
@@ -1027,12 +1027,30 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
        return bitmap_weight(arm_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS);
 }
 
+static void kvm_arm_set_nr_counters(struct kvm *kvm, unsigned int nr)
+{
+       kvm->arch.nr_pmu_counters = nr;
+
+       /* Reset MDCR_EL2.HPMN behind the vcpus' back... */
+       if (test_bit(KVM_ARM_VCPU_HAS_EL2, kvm->arch.vcpu_features)) {
+               struct kvm_vcpu *vcpu;
+               unsigned long i;
+
+               kvm_for_each_vcpu(i, vcpu, kvm) {
+                       u64 val = __vcpu_sys_reg(vcpu, MDCR_EL2);
+                       val &= ~MDCR_EL2_HPMN;
+                       val |= FIELD_PREP(MDCR_EL2_HPMN, kvm->arch.nr_pmu_counters);
+                       __vcpu_sys_reg(vcpu, MDCR_EL2) = val;
+               }
+       }
+}
+
 static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)
 {
        lockdep_assert_held(&kvm->arch.config_lock);
 
        kvm->arch.arm_pmu = arm_pmu;
-       kvm->arch.nr_pmu_counters = kvm_arm_pmu_get_max_counters(kvm);
+       kvm_arm_set_nr_counters(kvm, kvm_arm_pmu_get_max_counters(kvm));
 }
 
 /**
index aec7d9104cfeeee756b1e0c830a582b6fba7e563..1e4265172d9d7410177f154d8b971e53afdd4ab5 100644 (file)
@@ -2698,6 +2698,12 @@ static int set_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
        .set_user = set_imp_id_reg,                     \
        .reset = reset_imp_id_reg,                      \
        .val = mask,                                    \
+       }
+
+static u64 reset_mdcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
+{
+       __vcpu_sys_reg(vcpu, r->reg) = vcpu->kvm->arch.nr_pmu_counters;
+       return vcpu->kvm->arch.nr_pmu_counters;
 }
 
 /*
@@ -3243,7 +3249,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        EL2_REG(SCTLR_EL2, access_rw, reset_val, SCTLR_EL2_RES1),
        EL2_REG(ACTLR_EL2, access_rw, reset_val, 0),
        EL2_REG_VNCR(HCR_EL2, reset_hcr, 0),
-       EL2_REG(MDCR_EL2, access_mdcr, reset_val, 0),
+       EL2_REG(MDCR_EL2, access_mdcr, reset_mdcr, 0),
        EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1),
        EL2_REG_VNCR(HSTR_EL2, reset_val, 0),
        EL2_REG_VNCR(HFGRTR_EL2, reset_val, 0),