KVM: selftests: aarch64: vPMU register test for unimplemented counters
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>
This commit is contained in:
committed by
Oliver Upton
parent
ada1ae6826
commit
e1cc872063
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user