]> xenbits.xensource.com Git - people/pauldu/linux.git/commitdiff
KVM: selftests: aarch64: vPMU register test for unimplemented counters
authorReiji Watanabe <reijiw@google.com>
Fri, 20 Oct 2023 21:40:51 +0000 (21:40 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Tue, 24 Oct 2023 22:59:31 +0000 (22:59 +0000)
Add a new test case to the vpmu_counter_access test to check
if PMU registers or their bits for unimplemented counters are not
accessible or are RAZ, as expected.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231020214053.2144305-12-rananta@google.com
[Oliver: fix issues relating to exception return address]
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
tools/testing/selftests/kvm/aarch64/vpmu_counter_access.c
tools/testing/selftests/kvm/include/aarch64/processor.h

index a579286b6f116d0ecf221899f3e1643f832df7f2..8694e89684bfe434388c8b575cfd295a6e3bd00f 100644 (file)
@@ -5,8 +5,9 @@
  * Copyright (c) 2023 Google LLC.
  *
  * This test checks if the guest can see the same number of the PMU event
- * counters (PMCR_EL0.N) that userspace sets, and if the guest can access
- * those counters.
+ * counters (PMCR_EL0.N) that userspace sets, if the guest can access
+ * those counters, and if the guest is prevented from accessing any
+ * other counters.
  * This test runs only when KVM_CAP_ARM_PMU_V3 is supported on the host.
  */
 #include <kvm_util.h>
@@ -287,25 +288,74 @@ static void test_access_pmc_regs(struct pmc_accessor *acc, int pmc_idx)
                       pmc_idx, PMC_ACC_TO_IDX(acc), read_data, write_data);
 }
 
+#define INVALID_EC     (-1ul)
+uint64_t expected_ec = INVALID_EC;
+
 static void guest_sync_handler(struct ex_regs *regs)
 {
        uint64_t esr, ec;
 
        esr = read_sysreg(esr_el1);
        ec = (esr >> ESR_EC_SHIFT) & ESR_EC_MASK;
-       __GUEST_ASSERT(0, "PC: 0x%lx; ESR: 0x%lx; EC: 0x%lx", regs->pc, esr, ec);
+
+       __GUEST_ASSERT(expected_ec == ec,
+                       "PC: 0x%lx; ESR: 0x%lx; EC: 0x%lx; EC expected: 0x%lx",
+                       regs->pc, esr, ec, expected_ec);
+
+       /* skip the trapping instruction */
+       regs->pc += 4;
+
+       /* Use INVALID_EC to indicate an exception occurred */
+       expected_ec = INVALID_EC;
+}
+
+/*
+ * Run the given operation that should trigger an exception with the
+ * given exception class. The exception handler (guest_sync_handler)
+ * will reset op_end_addr to 0, expected_ec to INVALID_EC, and skip
+ * the instruction that trapped.
+ */
+#define TEST_EXCEPTION(ec, ops)                                \
+({                                                     \
+       GUEST_ASSERT(ec != INVALID_EC);                 \
+       WRITE_ONCE(expected_ec, ec);                    \
+       dsb(ish);                                       \
+       ops;                                            \
+       GUEST_ASSERT(expected_ec == INVALID_EC);        \
+})
+
+/*
+ * Tests for reading/writing registers for the unimplemented event counter
+ * specified by @pmc_idx (>= PMCR_EL0.N).
+ */
+static void test_access_invalid_pmc_regs(struct pmc_accessor *acc, int pmc_idx)
+{
+       /*
+        * Reading/writing the event count/type registers should cause
+        * an UNDEFINED exception.
+        */
+       TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->read_cntr(pmc_idx));
+       TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->write_cntr(pmc_idx, 0));
+       TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->read_typer(pmc_idx));
+       TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->write_typer(pmc_idx, 0));
+       /*
+        * The bit corresponding to the (unimplemented) counter in
+        * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers should be RAZ.
+        */
+       test_bitmap_pmu_regs(pmc_idx, 1);
+       test_bitmap_pmu_regs(pmc_idx, 0);
 }
 
 /*
  * The guest is configured with PMUv3 with @expected_pmcr_n number of
  * event counters.
  * Check if @expected_pmcr_n is consistent with PMCR_EL0.N, and
- * if reading/writing PMU registers for implemented counters works
- * as expected.
+ * if reading/writing PMU registers for implemented or unimplemented
+ * counters works as expected.
  */
 static void guest_code(uint64_t expected_pmcr_n)
 {
-       uint64_t pmcr, pmcr_n;
+       uint64_t pmcr, pmcr_n, unimp_mask;
        int i, pmc;
 
        __GUEST_ASSERT(expected_pmcr_n <= ARMV8_PMU_MAX_GENERAL_COUNTERS,
@@ -320,15 +370,33 @@ static void guest_code(uint64_t expected_pmcr_n)
                        "Expected PMCR.N: 0x%lx, PMCR.N: 0x%lx",
                        expected_pmcr_n, pmcr_n);
 
+       /*
+        * Make sure that (RAZ) bits corresponding to unimplemented event
+        * counters in {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers are reset
+        * to zero.
+        * (NOTE: bits for implemented event counters are reset to UNKNOWN)
+        */
+       unimp_mask = GENMASK_ULL(ARMV8_PMU_MAX_GENERAL_COUNTERS - 1, pmcr_n);
+       check_bitmap_pmu_regs(unimp_mask, false);
+
        /*
         * Tests for reading/writing PMU registers for implemented counters.
-        * Use each combination of PMEVT{CNTR,TYPER}<n>_EL0 accessor functions.
+        * Use each combination of PMEV{CNTR,TYPER}<n>_EL0 accessor functions.
         */
        for (i = 0; i < ARRAY_SIZE(pmc_accessors); i++) {
                for (pmc = 0; pmc < pmcr_n; pmc++)
                        test_access_pmc_regs(&pmc_accessors[i], pmc);
        }
 
+       /*
+        * Tests for reading/writing PMU registers for unimplemented counters.
+        * Use each combination of PMEV{CNTR,TYPER}<n>_EL0 accessor functions.
+        */
+       for (i = 0; i < ARRAY_SIZE(pmc_accessors); i++) {
+               for (pmc = pmcr_n; pmc < ARMV8_PMU_MAX_GENERAL_COUNTERS; pmc++)
+                       test_access_invalid_pmc_regs(&pmc_accessors[i], pmc);
+       }
+
        GUEST_DONE();
 }
 
index cb537253a6b9c8e7d115ec60a67cb6bdd4f854b4..c42d683102c7a477ddca956285d8854bd44fa7c6 100644 (file)
@@ -104,6 +104,7 @@ enum {
 #define ESR_EC_SHIFT           26
 #define ESR_EC_MASK            (ESR_EC_NUM - 1)
 
+#define ESR_EC_UNKNOWN         0x0
 #define ESR_EC_SVC64           0x15
 #define ESR_EC_IABT            0x21
 #define ESR_EC_DABT            0x25