Skip to content

Commit e1cc872

Browse files
reijiw-kvmoupton
authored andcommitted
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>
1 parent ada1ae6 commit e1cc872

File tree

2 files changed

+76
-7
lines changed

2 files changed

+76
-7
lines changed

tools/testing/selftests/kvm/aarch64/vpmu_counter_access.c

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
* Copyright (c) 2023 Google LLC.
66
*
77
* This test checks if the guest can see the same number of the PMU event
8-
* counters (PMCR_EL0.N) that userspace sets, and if the guest can access
9-
* those counters.
8+
* counters (PMCR_EL0.N) that userspace sets, if the guest can access
9+
* those counters, and if the guest is prevented from accessing any
10+
* other counters.
1011
* This test runs only when KVM_CAP_ARM_PMU_V3 is supported on the host.
1112
*/
1213
#include <kvm_util.h>
@@ -287,25 +288,74 @@ static void test_access_pmc_regs(struct pmc_accessor *acc, int pmc_idx)
287288
pmc_idx, PMC_ACC_TO_IDX(acc), read_data, write_data);
288289
}
289290

291+
#define INVALID_EC (-1ul)
292+
uint64_t expected_ec = INVALID_EC;
293+
290294
static void guest_sync_handler(struct ex_regs *regs)
291295
{
292296
uint64_t esr, ec;
293297

294298
esr = read_sysreg(esr_el1);
295299
ec = (esr >> ESR_EC_SHIFT) & ESR_EC_MASK;
296-
__GUEST_ASSERT(0, "PC: 0x%lx; ESR: 0x%lx; EC: 0x%lx", regs->pc, esr, ec);
300+
301+
__GUEST_ASSERT(expected_ec == ec,
302+
"PC: 0x%lx; ESR: 0x%lx; EC: 0x%lx; EC expected: 0x%lx",
303+
regs->pc, esr, ec, expected_ec);
304+
305+
/* skip the trapping instruction */
306+
regs->pc += 4;
307+
308+
/* Use INVALID_EC to indicate an exception occurred */
309+
expected_ec = INVALID_EC;
310+
}
311+
312+
/*
313+
* Run the given operation that should trigger an exception with the
314+
* given exception class. The exception handler (guest_sync_handler)
315+
* will reset op_end_addr to 0, expected_ec to INVALID_EC, and skip
316+
* the instruction that trapped.
317+
*/
318+
#define TEST_EXCEPTION(ec, ops) \
319+
({ \
320+
GUEST_ASSERT(ec != INVALID_EC); \
321+
WRITE_ONCE(expected_ec, ec); \
322+
dsb(ish); \
323+
ops; \
324+
GUEST_ASSERT(expected_ec == INVALID_EC); \
325+
})
326+
327+
/*
328+
* Tests for reading/writing registers for the unimplemented event counter
329+
* specified by @pmc_idx (>= PMCR_EL0.N).
330+
*/
331+
static void test_access_invalid_pmc_regs(struct pmc_accessor *acc, int pmc_idx)
332+
{
333+
/*
334+
* Reading/writing the event count/type registers should cause
335+
* an UNDEFINED exception.
336+
*/
337+
TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->read_cntr(pmc_idx));
338+
TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->write_cntr(pmc_idx, 0));
339+
TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->read_typer(pmc_idx));
340+
TEST_EXCEPTION(ESR_EC_UNKNOWN, acc->write_typer(pmc_idx, 0));
341+
/*
342+
* The bit corresponding to the (unimplemented) counter in
343+
* {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers should be RAZ.
344+
*/
345+
test_bitmap_pmu_regs(pmc_idx, 1);
346+
test_bitmap_pmu_regs(pmc_idx, 0);
297347
}
298348

299349
/*
300350
* The guest is configured with PMUv3 with @expected_pmcr_n number of
301351
* event counters.
302352
* Check if @expected_pmcr_n is consistent with PMCR_EL0.N, and
303-
* if reading/writing PMU registers for implemented counters works
304-
* as expected.
353+
* if reading/writing PMU registers for implemented or unimplemented
354+
* counters works as expected.
305355
*/
306356
static void guest_code(uint64_t expected_pmcr_n)
307357
{
308-
uint64_t pmcr, pmcr_n;
358+
uint64_t pmcr, pmcr_n, unimp_mask;
309359
int i, pmc;
310360

311361
__GUEST_ASSERT(expected_pmcr_n <= ARMV8_PMU_MAX_GENERAL_COUNTERS,
@@ -320,15 +370,33 @@ static void guest_code(uint64_t expected_pmcr_n)
320370
"Expected PMCR.N: 0x%lx, PMCR.N: 0x%lx",
321371
expected_pmcr_n, pmcr_n);
322372

373+
/*
374+
* Make sure that (RAZ) bits corresponding to unimplemented event
375+
* counters in {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers are reset
376+
* to zero.
377+
* (NOTE: bits for implemented event counters are reset to UNKNOWN)
378+
*/
379+
unimp_mask = GENMASK_ULL(ARMV8_PMU_MAX_GENERAL_COUNTERS - 1, pmcr_n);
380+
check_bitmap_pmu_regs(unimp_mask, false);
381+
323382
/*
324383
* Tests for reading/writing PMU registers for implemented counters.
325-
* Use each combination of PMEVT{CNTR,TYPER}<n>_EL0 accessor functions.
384+
* Use each combination of PMEV{CNTR,TYPER}<n>_EL0 accessor functions.
326385
*/
327386
for (i = 0; i < ARRAY_SIZE(pmc_accessors); i++) {
328387
for (pmc = 0; pmc < pmcr_n; pmc++)
329388
test_access_pmc_regs(&pmc_accessors[i], pmc);
330389
}
331390

391+
/*
392+
* Tests for reading/writing PMU registers for unimplemented counters.
393+
* Use each combination of PMEV{CNTR,TYPER}<n>_EL0 accessor functions.
394+
*/
395+
for (i = 0; i < ARRAY_SIZE(pmc_accessors); i++) {
396+
for (pmc = pmcr_n; pmc < ARMV8_PMU_MAX_GENERAL_COUNTERS; pmc++)
397+
test_access_invalid_pmc_regs(&pmc_accessors[i], pmc);
398+
}
399+
332400
GUEST_DONE();
333401
}
334402

tools/testing/selftests/kvm/include/aarch64/processor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ enum {
104104
#define ESR_EC_SHIFT 26
105105
#define ESR_EC_MASK (ESR_EC_NUM - 1)
106106

107+
#define ESR_EC_UNKNOWN 0x0
107108
#define ESR_EC_SVC64 0x15
108109
#define ESR_EC_IABT 0x21
109110
#define ESR_EC_DABT 0x25

0 commit comments

Comments
 (0)