Commit e1cc8720 authored by Reiji Watanabe's avatar Reiji Watanabe Committed by Oliver Upton
Browse files

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: default avatarReiji Watanabe <reijiw@google.com>
Signed-off-by: default avatarRaghavendra Rao Ananta <rananta@google.com>
Signed-off-by: default avatarMarc 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: default avatarOliver Upton <oliver.upton@linux.dev>
parent ada1ae68
Loading
Loading
Loading
Loading
+75 −7
Original line number Diff line number Diff line
@@ -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();
}

+1 −0
Original line number Diff line number Diff line
@@ -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