Add MDCR_EL2 coverage to vpmu_counter_access when EL2 is available.

For each configured PMCR_EL0.N value, verify that an arbitrary HPMN
value, including one above the configured counter count, round-trips
through KVM_SET_ONE_REG and survives SET_NR_COUNTERS. Also verify that
HPME can be toggled without disturbing HPMN and that KVM_ARM_VCPU_INIT
resets HPMN from the configured count.

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

diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c 
b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c
index 3b5bb421f401..73f04e4c6ca3 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;
@@ -466,15 +474,12 @@ static void guest_code_done(void)
        GUEST_DONE();
 }
 
-static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, 
bool expect_fail)
+static void set_nr_counters(struct kvm_vcpu *vcpu,
+                           unsigned int nr_counters, bool expect_fail)
 {
-       struct kvm_vcpu *vcpu;
        unsigned int prev;
        int ret;
 
-       create_vpmu_vm(guest_code);
-       vcpu = vpmu_vm.vcpu;
-
        prev = get_pmcr_n(vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0)));
 
        ret = __vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
@@ -486,6 +491,17 @@ static void test_create_vpmu_vm_with_nr_counters(unsigned 
int nr_counters, bool
                            nr_counters, prev);
        else
                TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret));
+}
+
+static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters,
+                                                bool expect_fail)
+{
+       struct kvm_vcpu *vcpu;
+
+       create_vpmu_vm(guest_code);
+       vcpu = vpmu_vm.vcpu;
+
+       set_nr_counters(vcpu, nr_counters, expect_fail);
 
        vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, 
KVM_ARM_VCPU_PMU_V3_INIT, NULL);
 }
@@ -588,6 +604,69 @@ 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 expected_mdcr, mdcr;
+
+       pr_debug("MDCR_EL2 test with pmcr_n %lu\n", pmcr_n);
+
+       create_vpmu_vm(guest_code);
+       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));
+       expected_mdcr = u64_replace_bits(mdcr, FIELD_MAX(MDCR_EL2_HPMN),
+                                        MDCR_EL2_HPMN);
+       vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),
+                    expected_mdcr);
+
+       mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+       TEST_ASSERT(mdcr == expected_mdcr,
+                   "MDCR_EL2 was not properly updated after HPMN write 
(expected 0x%lx, got 0x%lx)",
+                   expected_mdcr, mdcr);
+
+       set_nr_counters(vcpu, pmcr_n, false);
+
+       mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+       TEST_ASSERT(mdcr == expected_mdcr,
+                   "MDCR_EL2 changed after PMU_V3_SET_NR_COUNTERS (expected 
0x%lx, got 0x%lx)",
+                   expected_mdcr, mdcr);
+
+       vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL,
+                            KVM_ARM_VCPU_PMU_V3_INIT, NULL);
+
+       mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+       TEST_ASSERT(mdcr == expected_mdcr,
+                   "MDCR_EL2 changed after PMU_V3_INIT (expected 0x%lx, got 
0x%lx)",
+                   expected_mdcr, mdcr);
+
+       expected_mdcr = mdcr ^ MDCR_EL2_HPME;
+       vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2),
+                    expected_mdcr);
+
+       mdcr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MDCR_EL2));
+       TEST_ASSERT(mdcr == expected_mdcr,
+                   "MDCR_EL2 was not properly updated after HPME write 
(expected 0x%lx, got 0x%lx)",
+                   expected_mdcr, 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 reset 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.
@@ -672,6 +751,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