Assert the following: - KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY is unset at initialization. - KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY can be set. - Setting KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY for the first time after setting an event filter results in EBUSY. - KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY can be set again even if an event filter has already been set. - Setting KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY after running a VCPU results in EBUSY. - The existing test cases pass with KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY set. Signed-off-by: Akihiko Odaki --- .../selftests/kvm/arm64/vpmu_counter_access.c | 148 +++++++++++++++++---- 1 file changed, 122 insertions(+), 26 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c index ae36325c022f..156bfa636923 100644 --- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c +++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c @@ -403,12 +403,7 @@ static void create_vpmu_vm(void *guest_code) { struct kvm_vcpu_init init; uint8_t pmuver, ec; - uint64_t dfr0, irq = 23; - struct kvm_device_attr irq_attr = { - .group = KVM_ARM_VCPU_PMU_V3_CTRL, - .attr = KVM_ARM_VCPU_PMU_V3_IRQ, - .addr = (uint64_t)&irq, - }; + uint64_t dfr0; /* The test creates the vpmu_vm multiple times. Ensure a clean state */ memset(&vpmu_vm, 0, sizeof(vpmu_vm)); @@ -434,8 +429,6 @@ static void create_vpmu_vm(void *guest_code) TEST_ASSERT(pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF && pmuver >= ID_AA64DFR0_EL1_PMUVer_IMP, "Unexpected PMUVER (0x%x) on the vCPU with PMUv3", pmuver); - - vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &irq_attr); } static void destroy_vpmu_vm(void) @@ -461,15 +454,25 @@ static void run_vcpu(struct kvm_vcpu *vcpu, uint64_t pmcr_n) } } -static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool expect_fail) +static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, + bool fixed_counters_only, + bool expect_fail) { struct kvm_vcpu *vcpu; unsigned int prev; int ret; + uint64_t irq = 23; create_vpmu_vm(guest_code); vcpu = vpmu_vm.vcpu; + if (fixed_counters_only) + vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + + vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_IRQ, &irq); + 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, @@ -489,15 +492,15 @@ static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool * Create a guest with one vCPU, set the PMCR_EL0.N for the vCPU to @pmcr_n, * and run the test. */ -static void run_access_test(uint64_t pmcr_n) +static void run_access_test(uint64_t pmcr_n, bool fixed_counters_only) { uint64_t sp; struct kvm_vcpu *vcpu; struct kvm_vcpu_init init; - pr_debug("Test with pmcr_n %lu\n", pmcr_n); + pr_debug("Test with pmcr_n %lu, fixed_counters_only %d\n", pmcr_n, fixed_counters_only); - test_create_vpmu_vm_with_nr_counters(pmcr_n, false); + test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, false); vcpu = vpmu_vm.vcpu; /* Save the initial sp to restore them later to run the guest again */ @@ -531,14 +534,14 @@ static struct pmreg_sets validity_check_reg_sets[] = { * Create a VM, and check if KVM handles the userspace accesses of * the PMU register sets in @validity_check_reg_sets[] correctly. */ -static void run_pmregs_validity_test(uint64_t pmcr_n) +static void run_pmregs_validity_test(uint64_t pmcr_n, bool fixed_counters_only) { int i; struct kvm_vcpu *vcpu; uint64_t set_reg_id, clr_reg_id, reg_val; uint64_t valid_counters_mask, max_counters_mask; - test_create_vpmu_vm_with_nr_counters(pmcr_n, false); + test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, false); vcpu = vpmu_vm.vcpu; valid_counters_mask = get_counters_mask(pmcr_n); @@ -588,11 +591,11 @@ static void run_pmregs_validity_test(uint64_t pmcr_n) * the vCPU to @pmcr_n, which is larger than the host value. * The attempt should fail as @pmcr_n is too big to set for the vCPU. */ -static void run_error_test(uint64_t pmcr_n) +static void run_error_test(uint64_t pmcr_n, bool fixed_counters_only) { pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n); - test_create_vpmu_vm_with_nr_counters(pmcr_n, true); + test_create_vpmu_vm_with_nr_counters(pmcr_n, fixed_counters_only, true); destroy_vpmu_vm(); } @@ -622,22 +625,115 @@ static bool kvm_supports_nr_counters_attr(void) return supported; } -int main(void) +static void test_config(uint64_t pmcr_n, bool fixed_counters_only) { - uint64_t i, pmcr_n; - - TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); - TEST_REQUIRE(kvm_supports_vgic_v3()); - TEST_REQUIRE(kvm_supports_nr_counters_attr()); + uint64_t i; - pmcr_n = get_pmcr_n_limit(); for (i = 0; i <= pmcr_n; i++) { - run_access_test(i); - run_pmregs_validity_test(i); + run_access_test(i, fixed_counters_only); + run_pmregs_validity_test(i, fixed_counters_only); } for (i = pmcr_n + 1; i < ARMV8_PMU_MAX_COUNTERS; i++) - run_error_test(i); + run_error_test(i, fixed_counters_only); +} + +static void test_fixed_counters_only(void) +{ + struct kvm_pmu_event_filter filter = { .nevents = 0 }; + struct kvm_vm *vm; + struct kvm_vcpu *running_vcpu; + struct kvm_vcpu *stopped_vcpu; + struct kvm_vcpu_init init; + int ret; + uint64_t irq = 23; + + create_vpmu_vm(guest_code); + ret = __vcpu_has_device_attr(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY); + if (ret) { + TEST_ASSERT(ret == -1 && errno == ENXIO, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + destroy_vpmu_vm(); + return; + } + + /* Assert that FIXED_COUNTERS_ONLY is unset at initialization. */ + ret = __vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + TEST_ASSERT(ret == -1 && errno == ENXIO, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + + /* Assert that setting FIXED_COUNTERS_ONLY succeeds. */ + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + + /* Assert that getting FIXED_COUNTERS_ONLY succeeds. */ + vcpu_device_attr_get(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + + /* + * Assert that setting FIXED_COUNTERS_ONLY again succeeds even if an + * event filter has already been set. + */ + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FILTER, &filter); + + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + + destroy_vpmu_vm(); + + create_vpmu_vm(guest_code); + + /* + * Assert that setting FIXED_COUNTERS_ONLY results in EBUSY if an event + * filter has already been set while FIXED_COUNTERS_ONLY has not. + */ + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FILTER, &filter); + + ret = __vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + TEST_ASSERT(ret == -1 && errno == EBUSY, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + + destroy_vpmu_vm(); + + /* + * Assert that setting FIXED_COUNTERS_ONLY after running a VCPU results + * in EBUSY. + */ + vm = vm_create(2); + vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init); + init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3); + running_vcpu = aarch64_vcpu_add(vm, 0, &init, guest_code); + stopped_vcpu = aarch64_vcpu_add(vm, 1, &init, guest_code); + kvm_arch_vm_finalize_vcpus(vm); + vcpu_device_attr_set(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_IRQ, &irq); + vcpu_device_attr_set(running_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_INIT, NULL); + vcpu_run(running_vcpu); + + ret = __vcpu_device_attr_set(stopped_vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, + KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY, NULL); + TEST_ASSERT(ret == -1 && errno == EBUSY, + KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); + + kvm_vm_free(vm); + + test_config(0, true); +} + +int main(void) +{ + TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); + TEST_REQUIRE(kvm_supports_vgic_v3()); + TEST_REQUIRE(kvm_supports_nr_counters_attr()); + + test_config(get_pmcr_n_limit(), false); + test_fixed_counters_only(); return 0; } -- 2.53.0