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
>
>

Reply via email to