mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 01:51:34 +00:00
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:
parent
158cb9e61c
commit
13cb706e28
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user