On Mon, 20 Jul 2026 at 06:44, Akihiko Odaki <[email protected]> wrote: > > 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]>
Reviewed-by: Fuad Tabba <[email protected]> Cheers, /fuad > --- > .../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 > >

