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: Andrew Jones <ajones@ventanamicro.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
Link: https://lore.kernel.org/r/20240420151741.962500-23-atishp@rivosinc.com
Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Atish Patra 2024-04-20 08:17:38 -07:00 committed by Anup Patel
parent 158cb9e61c
commit 13cb706e28
3 changed files with 181 additions and 0 deletions

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;
}