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

