Commit 13cb706e authored by Atish Patra's avatar Atish Patra Committed by Anup Patel
Browse files

KVM: riscv: selftests: Add a test for PMU snapshot functionality



Verify PMU snapshot functionality by setting up the shared memory
correctly and reading the counter values from the shared memory
instead of the CSR.

Reviewed-by: default avatarAndrew Jones <ajones@ventanamicro.com>
Reviewed-by: default avatarAnup Patel <anup@brainfault.org>
Signed-off-by: default avatarAtish Patra <atishp@rivosinc.com>
Link: https://lore.kernel.org/r/20240420151741.962500-23-atishp@rivosinc.com


Signed-off-by: default avatarAnup Patel <anup@brainfault.org>
parent 158cb9e6
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -8,6 +8,12 @@
#ifndef SELFTEST_KVM_SBI_H
#define SELFTEST_KVM_SBI_H

/* SBI spec version fields */
#define SBI_SPEC_VERSION_DEFAULT	0x1
#define SBI_SPEC_VERSION_MAJOR_SHIFT	24
#define SBI_SPEC_VERSION_MAJOR_MASK	0x7f
#define SBI_SPEC_VERSION_MINOR_MASK	0xffffff

/* SBI return error codes */
#define SBI_SUCCESS				 0
#define SBI_ERR_FAILURE				-1
@@ -33,6 +39,9 @@ enum sbi_ext_id {
};

enum sbi_ext_base_fid {
	SBI_EXT_BASE_GET_SPEC_VERSION = 0,
	SBI_EXT_BASE_GET_IMP_ID,
	SBI_EXT_BASE_GET_IMP_VERSION,
	SBI_EXT_BASE_PROBE_EXT = 3,
};
enum sbi_ext_pmu_fid {
@@ -60,6 +69,12 @@ union sbi_pmu_ctr_info {
	};
};

struct riscv_pmu_snapshot_data {
	u64 ctr_overflow_mask;
	u64 ctr_values[64];
	u64 reserved[447];
};

struct sbiret {
	long error;
	long value;
@@ -113,4 +128,14 @@ struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,

bool guest_sbi_probe_extension(int extid, long *out_val);

/* Make SBI version */
static inline unsigned long sbi_mk_version(unsigned long major,
					    unsigned long minor)
{
	return ((major & SBI_SPEC_VERSION_MAJOR_MASK) << SBI_SPEC_VERSION_MAJOR_SHIFT)
		| (minor & SBI_SPEC_VERSION_MINOR_MASK);
}

unsigned long get_host_sbi_spec_version(void);

#endif /* SELFTEST_KVM_SBI_H */
+12 −0
Original line number Diff line number Diff line
@@ -502,3 +502,15 @@ bool guest_sbi_probe_extension(int extid, long *out_val)

	return true;
}

unsigned long get_host_sbi_spec_version(void)
{
	struct sbiret ret;

	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_SPEC_VERSION, 0,
		       0, 0, 0, 0, 0);

	GUEST_ASSERT(!ret.error);

	return ret.value;
}
+144 −0
Original line number Diff line number Diff line
@@ -19,6 +19,11 @@
#define RISCV_MAX_PMU_COUNTERS 64
union sbi_pmu_ctr_info ctrinfo_arr[RISCV_MAX_PMU_COUNTERS];

/* Snapshot shared memory data */
#define PMU_SNAPSHOT_GPA_BASE		BIT(30)
static void *snapshot_gva;
static vm_paddr_t snapshot_gpa;

/* Cache the available counters in a bitmask */
static unsigned long counter_mask_available;

@@ -186,6 +191,32 @@ static unsigned long read_counter(int idx, union sbi_pmu_ctr_info ctrinfo)
	return counter_val;
}

static inline void verify_sbi_requirement_assert(void)
{
	long out_val = 0;
	bool probe;

	probe = guest_sbi_probe_extension(SBI_EXT_PMU, &out_val);
	GUEST_ASSERT(probe && out_val == 1);

	if (get_host_sbi_spec_version() < sbi_mk_version(2, 0))
		__GUEST_ASSERT(0, "SBI implementation version doesn't support PMU Snapshot");
}

static void snapshot_set_shmem(vm_paddr_t gpa, unsigned long flags)
{
	unsigned long lo = (unsigned long)gpa;
#if __riscv_xlen == 32
	unsigned long hi = (unsigned long)(gpa >> 32);
#else
	unsigned long hi = gpa == -1 ? -1 : 0;
#endif
	struct sbiret ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_SNAPSHOT_SET_SHMEM,
				      lo, hi, flags, 0, 0, 0);

	GUEST_ASSERT(ret.value == 0 && ret.error == 0);
}

static void test_pmu_event(unsigned long event)
{
	unsigned long counter;
@@ -234,6 +265,59 @@ static void test_pmu_event(unsigned long event)
	stop_reset_counter(counter, 0);
}

static void test_pmu_event_snapshot(unsigned long event)
{
	unsigned long counter;
	unsigned long counter_value_pre, counter_value_post;
	unsigned long counter_init_value = 100;
	struct riscv_pmu_snapshot_data *snapshot_data = snapshot_gva;

	counter = get_counter_index(0, counter_mask_available, 0, event);
	counter_value_pre = read_counter(counter, ctrinfo_arr[counter]);

	/* Do not set the initial value */
	start_counter(counter, 0, 0);
	dummy_func_loop(10000);
	stop_counter(counter, SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT);

	/* The counter value is updated w.r.t relative index of cbase */
	counter_value_post = READ_ONCE(snapshot_data->ctr_values[0]);
	__GUEST_ASSERT(counter_value_post > counter_value_pre,
		       "Event update verification failed: post [%lx] pre [%lx]\n",
		       counter_value_post, counter_value_pre);

	/*
	 * We can't just update the counter without starting it.
	 * Do start/stop twice to simulate that by first initializing to a very
	 * high value and a low value after that.
	 */
	WRITE_ONCE(snapshot_data->ctr_values[0], ULONG_MAX/2);
	start_counter(counter, SBI_PMU_START_FLAG_INIT_SNAPSHOT, 0);
	stop_counter(counter, SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT);
	counter_value_pre = READ_ONCE(snapshot_data->ctr_values[0]);

	WRITE_ONCE(snapshot_data->ctr_values[0], counter_init_value);
	start_counter(counter, SBI_PMU_START_FLAG_INIT_SNAPSHOT, 0);
	stop_counter(counter, SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT);
	counter_value_post = READ_ONCE(snapshot_data->ctr_values[0]);
	__GUEST_ASSERT(counter_value_pre > counter_value_post,
		       "Counter reinitialization verification failed : post [%lx] pre [%lx]\n",
		       counter_value_post, counter_value_pre);

	/* Now set the initial value and compare */
	WRITE_ONCE(snapshot_data->ctr_values[0], counter_init_value);
	start_counter(counter, SBI_PMU_START_FLAG_INIT_SNAPSHOT, 0);
	dummy_func_loop(10000);
	stop_counter(counter, SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT);

	counter_value_post = READ_ONCE(snapshot_data->ctr_values[0]);
	__GUEST_ASSERT(counter_value_post > counter_init_value,
		       "Event update verification failed: post [%lx] pre [%lx]\n",
		       counter_value_post, counter_init_value);

	stop_reset_counter(counter, 0);
}

static void test_invalid_event(void)
{
	struct sbiret ret;
@@ -301,6 +385,34 @@ static void test_pmu_basic_sanity(void)
	GUEST_DONE();
}

static void test_pmu_events_snaphost(void)
{
	int num_counters = 0;
	struct riscv_pmu_snapshot_data *snapshot_data = snapshot_gva;
	int i;

	/* Verify presence of SBI PMU and minimum requrired SBI version */
	verify_sbi_requirement_assert();

	snapshot_set_shmem(snapshot_gpa, 0);

	/* Get the counter details */
	num_counters = get_num_counters();
	update_counter_info(num_counters);

	/* Validate shared memory access */
	GUEST_ASSERT_EQ(READ_ONCE(snapshot_data->ctr_overflow_mask), 0);
	for (i = 0; i < num_counters; i++) {
		if (counter_mask_available & (BIT(i)))
			GUEST_ASSERT_EQ(READ_ONCE(snapshot_data->ctr_values[i]), 0);
	}
	/* Only these two events are guranteed to be present */
	test_pmu_event_snapshot(SBI_PMU_HW_CPU_CYCLES);
	test_pmu_event_snapshot(SBI_PMU_HW_INSTRUCTIONS);

	GUEST_DONE();
}

static void run_vcpu(struct kvm_vcpu *vcpu)
{
	struct ucall uc;
@@ -357,6 +469,35 @@ static void test_vm_events_test(void *guest_code)
	test_vm_destroy(vm);
}

static void test_vm_setup_snapshot_mem(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
{
	/* PMU Snapshot requires single page only */
	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, PMU_SNAPSHOT_GPA_BASE, 1, 1, 0);
	/* PMU_SNAPSHOT_GPA_BASE is identity mapped */
	virt_map(vm, PMU_SNAPSHOT_GPA_BASE, PMU_SNAPSHOT_GPA_BASE, 1);

	snapshot_gva = (void *)(PMU_SNAPSHOT_GPA_BASE);
	snapshot_gpa = addr_gva2gpa(vcpu->vm, (vm_vaddr_t)snapshot_gva);
	sync_global_to_guest(vcpu->vm, snapshot_gva);
	sync_global_to_guest(vcpu->vm, snapshot_gpa);
}

static void test_vm_events_snapshot_test(void *guest_code)
{
	struct kvm_vm *vm = NULL;
	struct kvm_vcpu *vcpu;

	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
	__TEST_REQUIRE(__vcpu_has_sbi_ext(vcpu, KVM_RISCV_SBI_EXT_PMU),
				   "SBI PMU not available, skipping test");

	test_vm_setup_snapshot_mem(vm, vcpu);

	run_vcpu(vcpu);

	test_vm_destroy(vm);
}

int main(void)
{
	test_vm_basic_test(test_pmu_basic_sanity);
@@ -365,5 +506,8 @@ int main(void)
	test_vm_events_test(test_pmu_events);
	pr_info("SBI PMU event verification test : PASS\n");

	test_vm_events_snapshot_test(test_pmu_events_snaphost);
	pr_info("SBI PMU event verification with snapshot test : PASS\n");

	return 0;
}