Run separate test cases for a partitioned PMU in vpmu_counter_access. Notably, partitioning the PMU untraps PMCR_EL0.N, so that is no longer settable by KVM.
Add a boolean argument to run_access_test() that will partition the PMU by reserving one host counter if true then run the test for the PMCR_EL0.N value that implies, one less than the number of counters on the host system.
Signed-off-by: Colton Lewis coltonlewis@google.com --- tools/include/uapi/linux/kvm.h | 2 + .../selftests/kvm/arm64/vpmu_counter_access.c | 40 ++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index b6ae8ad8934b..cb72b57b9b6c 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -930,6 +930,7 @@ struct kvm_enable_cap { #define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237 #define KVM_CAP_X86_GUEST_MODE 238 #define KVM_CAP_ARM_WRITABLE_IMP_ID_REGS 239 +#define KVM_CAP_ARM_PARTITION_PMU 242
struct kvm_irq_routing_irqchip { __u32 irqchip; @@ -1356,6 +1357,7 @@ struct kvm_vfio_spapr_tce { #define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log) /* Memory Encryption Commands */ #define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xba, unsigned long) +#define KVM_ARM_PARTITION_PMU _IOWR(KVMIO, 0xce, u8)
struct kvm_enc_region { __u64 addr; diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c index f16b3b27e32e..e06448c1fbb5 100644 --- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c +++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c @@ -369,6 +369,7 @@ static void guest_code(uint64_t expected_pmcr_n) pmcr = read_sysreg(pmcr_el0); pmcr_n = get_pmcr_n(pmcr);
+ /* __GUEST_ASSERT(0, "Expect PMCR: %lx", pmcr); */ /* Make sure that PMCR_EL0.N indicates the value userspace set */ __GUEST_ASSERT(pmcr_n == expected_pmcr_n, "Expected PMCR.N: 0x%lx, PMCR.N: 0x%lx", @@ -508,16 +509,18 @@ static void test_create_vpmu_vm_with_pmcr_n(uint64_t pmcr_n, bool expect_fail) * 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 partition) { uint64_t sp; struct kvm_vcpu *vcpu; struct kvm_vcpu_init init; + uint8_t host_counters = (uint8_t)partition;
pr_debug("Test with pmcr_n %lu\n", pmcr_n);
test_create_vpmu_vm_with_pmcr_n(pmcr_n, false); vcpu = vpmu_vm.vcpu; + vcpu_ioctl(vcpu, KVM_ARM_PARTITION_PMU, &host_counters);
/* Save the initial sp to restore them later to run the guest again */ sp = vcpu_get_reg(vcpu, ARM64_CORE_REG(sp_el1)); @@ -529,6 +532,8 @@ static void run_access_test(uint64_t pmcr_n) * check if PMCR_EL0.N is preserved. */ vm_ioctl(vpmu_vm.vm, KVM_ARM_PREFERRED_TARGET, &init); + vcpu_ioctl(vcpu, KVM_ARM_PARTITION_PMU, &host_counters); + init.features[0] |= (1 << KVM_ARM_VCPU_PMU_V3); aarch64_vcpu_setup(vcpu, &init); vcpu_init_descriptor_tables(vcpu); @@ -609,7 +614,7 @@ static void run_pmregs_validity_test(uint64_t pmcr_n) */ static void run_error_test(uint64_t pmcr_n) { - pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n); + pr_debug("Error test with pmcr_n %lu (larger than the host allows)\n", pmcr_n);
test_create_vpmu_vm_with_pmcr_n(pmcr_n, true); destroy_vpmu_vm(); @@ -629,20 +634,45 @@ static uint64_t get_pmcr_n_limit(void) return get_pmcr_n(pmcr); }
-int main(void) +void test_emulated_pmu(void) { uint64_t i, pmcr_n;
- TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); + pr_info("Testing Emulated PMU\n");
pmcr_n = get_pmcr_n_limit(); for (i = 0; i <= pmcr_n; i++) { - run_access_test(i); + run_access_test(i, false); run_pmregs_validity_test(i); }
for (i = pmcr_n + 1; i < ARMV8_PMU_MAX_COUNTERS; i++) run_error_test(i); +} + +void test_partitioned_pmu(void) +{ + uint64_t i, pmcr_n; + + pr_info("Testing Partitioned PMU\n"); + + pmcr_n = get_pmcr_n_limit(); + run_access_test(pmcr_n - 1, true); + + /* Partitioning implies only one PMCR.N allowed */ + for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) + if (i != pmcr_n) + run_error_test(i); +} + +int main(void) +{ + TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); + + test_emulated_pmu(); + + if (kvm_has_cap(KVM_CAP_ARM_PARTITION_PMU)) + test_partitioned_pmu();
return 0; }