Add MDCR_EL2 coverage to vpmu_counter_access when EL2 is available.

For each configured PMCR_EL0.N value, verify that HPMN tracks the
configured counter count, HPME can be toggled without disturbing HPMN,
and KVM_SET_ONE_REG rejects an out-of-range HPMN without changing
MDCR_EL2. Reset the vCPU and verify that HPMN is restored from the
configured count.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Akihiko Odaki <[email protected]>
---
 .../selftests/kvm/arm64/vpmu_counter_access.c      | 76 ++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c 
b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
index 22223395969e..25203ea117b0 100644
--- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
+++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
@@ -25,6 +25,14 @@
 /* The cycle counter bit position that's common among the PMU registers */
 #define ARMV8_PMU_CYCLE_IDX            31
 
+#ifndef MDCR_EL2_HPMN
+#define MDCR_EL2_HPMN                  GENMASK_ULL(4, 0)
+#endif
+
+#ifndef MDCR_EL2_HPME
+#define MDCR_EL2_HPME                  BIT_ULL(7)
+#endif
+
 struct vpmu_vm {
        struct kvm_vm *vm;
        struct kvm_vcpu *vcpu;
@@ -583,6 +591,73 @@ static void run_pmregs_validity_test(u64 pmcr_n)
        destroy_vpmu_vm();
 }
 
+static void run_mdcr_el2_validity_test(u64 pmcr_n)
+{
+       struct kvm_vcpu_init init;
+       struct kvm_vcpu *vcpu;
+       u64 mdcr, old_mdcr;
+       int ret;
+
+       pr_debug("MDCR_EL2 test with pmcr_n %lu\n", pmcr_n);
+
+       test_create_vpmu_vm_with_nr_counters(pmcr_n, false);
+       if (!vm_supports_el2(vpmu_vm.vm)) {
+               destroy_vpmu_vm();
+               return;
+       }
+
+       vcpu = vpmu_vm.vcpu;
+
+       mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+       TEST_ASSERT(FIELD_GET(MDCR_EL2_HPMN, mdcr) == pmcr_n,
+                   "MDCR_EL2.HPMN is not updated after PMU_V3_SET_NR_COUNTERS 
(expected %lu, got %lu)",
+                   pmcr_n, FIELD_GET(MDCR_EL2_HPMN, mdcr));
+
+       old_mdcr = mdcr;
+       vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),
+                    old_mdcr ^ MDCR_EL2_HPME);
+
+       mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+       TEST_ASSERT(mdcr == (old_mdcr ^ MDCR_EL2_HPME),
+                   "MDCR_EL2 was not properly updated after HPME write 
(expected 0x%lx, got 0x%lx)",
+                   old_mdcr ^ MDCR_EL2_HPME, mdcr);
+
+       if (pmcr_n < FIELD_MAX(MDCR_EL2_HPMN)) {
+               errno = 0;
+               old_mdcr = mdcr;
+               ret = __vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),
+                                    u64_replace_bits(mdcr, pmcr_n + 1, 
MDCR_EL2_HPMN));
+               TEST_ASSERT(ret == -1 && errno == EINVAL,
+                           "Setting MDCR_EL2.HPMN to %lu unexpectedly 
succeeded",
+                           pmcr_n + 1);
+
+               mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+               TEST_ASSERT(mdcr == old_mdcr,
+                           "MDCR_EL2 changed after failed HPMN write (expected 
0x%lx, got 0x%lx)",
+                           old_mdcr, mdcr);
+       }
+
+       old_mdcr = mdcr;
+       vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),
+                    u64_replace_bits(mdcr, 0, MDCR_EL2_HPMN));
+
+       mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+       TEST_ASSERT(mdcr == u64_replace_bits(old_mdcr, 0, MDCR_EL2_HPMN),
+                   "MDCR_EL2 was not properly updated after HPMN write 
(expected 0x%lx, got 0x%lx)",
+                   u64_replace_bits(old_mdcr, 0, MDCR_EL2_HPMN), mdcr);
+
+       kvm_get_default_vcpu_target(vpmu_vm.vm, &init);
+       init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3);
+       aarch64_vcpu_setup(vcpu, &init);
+
+       mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+       TEST_ASSERT(FIELD_GET(MDCR_EL2_HPMN, mdcr) == pmcr_n,
+                   "MDCR_EL2.HPMN is not updated after INIT (expected %lu, got 
%lu)",
+                   pmcr_n, FIELD_GET(MDCR_EL2_HPMN, mdcr));
+
+       destroy_vpmu_vm();
+}
+
 /*
  * Create a guest with one vCPU, and attempt to set the PMCR_EL0.N for
  * the vCPU to @pmcr_n, which is larger than the host value.
@@ -634,6 +709,7 @@ int main(void)
        for (i = 0; i <= pmcr_n; i++) {
                run_access_test(i);
                run_pmregs_validity_test(i);
+               run_mdcr_el2_validity_test(i);
        }
 
        for (i = pmcr_n + 1; i < ARMV8_PMU_MAX_COUNTERS; i++)

-- 
2.55.0


Reply via email to