Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Atish Patra and committed by
Anup Patel
13cb706e 158cb9e6

+181
+25
tools/testing/selftests/kvm/include/riscv/sbi.h
··· 8 8 #ifndef SELFTEST_KVM_SBI_H 9 9 #define SELFTEST_KVM_SBI_H 10 10 11 + /* SBI spec version fields */ 12 + #define SBI_SPEC_VERSION_DEFAULT 0x1 13 + #define SBI_SPEC_VERSION_MAJOR_SHIFT 24 14 + #define SBI_SPEC_VERSION_MAJOR_MASK 0x7f 15 + #define SBI_SPEC_VERSION_MINOR_MASK 0xffffff 16 + 11 17 /* SBI return error codes */ 12 18 #define SBI_SUCCESS 0 13 19 #define SBI_ERR_FAILURE -1 ··· 39 33 }; 40 34 41 35 enum sbi_ext_base_fid { 36 + SBI_EXT_BASE_GET_SPEC_VERSION = 0, 37 + SBI_EXT_BASE_GET_IMP_ID, 38 + SBI_EXT_BASE_GET_IMP_VERSION, 42 39 SBI_EXT_BASE_PROBE_EXT = 3, 43 40 }; 44 41 enum sbi_ext_pmu_fid { ··· 67 58 #endif 68 59 unsigned long type:1; 69 60 }; 61 + }; 62 + 63 + struct riscv_pmu_snapshot_data { 64 + u64 ctr_overflow_mask; 65 + u64 ctr_values[64]; 66 + u64 reserved[447]; 70 67 }; 71 68 72 69 struct sbiret { ··· 127 112 unsigned long arg5); 128 113 129 114 bool guest_sbi_probe_extension(int extid, long *out_val); 115 + 116 + /* Make SBI version */ 117 + static inline unsigned long sbi_mk_version(unsigned long major, 118 + unsigned long minor) 119 + { 120 + return ((major & SBI_SPEC_VERSION_MAJOR_MASK) << SBI_SPEC_VERSION_MAJOR_SHIFT) 121 + | (minor & SBI_SPEC_VERSION_MINOR_MASK); 122 + } 123 + 124 + unsigned long get_host_sbi_spec_version(void); 130 125 131 126 #endif /* SELFTEST_KVM_SBI_H */
+12
tools/testing/selftests/kvm/lib/riscv/processor.c
··· 502 502 503 503 return true; 504 504 } 505 + 506 + unsigned long get_host_sbi_spec_version(void) 507 + { 508 + struct sbiret ret; 509 + 510 + ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_SPEC_VERSION, 0, 511 + 0, 0, 0, 0, 0); 512 + 513 + GUEST_ASSERT(!ret.error); 514 + 515 + return ret.value; 516 + }
+144
tools/testing/selftests/kvm/riscv/sbi_pmu_test.c
··· 19 19 #define RISCV_MAX_PMU_COUNTERS 64 20 20 union sbi_pmu_ctr_info ctrinfo_arr[RISCV_MAX_PMU_COUNTERS]; 21 21 22 + /* Snapshot shared memory data */ 23 + #define PMU_SNAPSHOT_GPA_BASE BIT(30) 24 + static void *snapshot_gva; 25 + static vm_paddr_t snapshot_gpa; 26 + 22 27 /* Cache the available counters in a bitmask */ 23 28 static unsigned long counter_mask_available; 24 29 ··· 191 186 return counter_val; 192 187 } 193 188 189 + static inline void verify_sbi_requirement_assert(void) 190 + { 191 + long out_val = 0; 192 + bool probe; 193 + 194 + probe = guest_sbi_probe_extension(SBI_EXT_PMU, &out_val); 195 + GUEST_ASSERT(probe && out_val == 1); 196 + 197 + if (get_host_sbi_spec_version() < sbi_mk_version(2, 0)) 198 + __GUEST_ASSERT(0, "SBI implementation version doesn't support PMU Snapshot"); 199 + } 200 + 201 + static void snapshot_set_shmem(vm_paddr_t gpa, unsigned long flags) 202 + { 203 + unsigned long lo = (unsigned long)gpa; 204 + #if __riscv_xlen == 32 205 + unsigned long hi = (unsigned long)(gpa >> 32); 206 + #else 207 + unsigned long hi = gpa == -1 ? -1 : 0; 208 + #endif 209 + struct sbiret ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_SNAPSHOT_SET_SHMEM, 210 + lo, hi, flags, 0, 0, 0); 211 + 212 + GUEST_ASSERT(ret.value == 0 && ret.error == 0); 213 + } 214 + 194 215 static void test_pmu_event(unsigned long event) 195 216 { 196 217 unsigned long counter; ··· 258 227 stop_counter(counter, 0); 259 228 260 229 counter_value_post = read_counter(counter, ctrinfo_arr[counter]); 230 + __GUEST_ASSERT(counter_value_post > counter_init_value, 231 + "Event update verification failed: post [%lx] pre [%lx]\n", 232 + counter_value_post, counter_init_value); 233 + 234 + stop_reset_counter(counter, 0); 235 + } 236 + 237 + static void test_pmu_event_snapshot(unsigned long event) 238 + { 239 + unsigned long counter; 240 + unsigned long counter_value_pre, counter_value_post; 241 + unsigned long counter_init_value = 100; 242 + struct riscv_pmu_snapshot_data *snapshot_data = snapshot_gva; 243 + 244 + counter = get_counter_index(0, counter_mask_available, 0, event); 245 + counter_value_pre = read_counter(counter, ctrinfo_arr[counter]); 246 + 247 + /* Do not set the initial value */ 248 + start_counter(counter, 0, 0); 249 + dummy_func_loop(10000); 250 + stop_counter(counter, SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT); 251 + 252 + /* The counter value is updated w.r.t relative index of cbase */ 253 + counter_value_post = READ_ONCE(snapshot_data->ctr_values[0]); 254 + __GUEST_ASSERT(counter_value_post > counter_value_pre, 255 + "Event update verification failed: post [%lx] pre [%lx]\n", 256 + counter_value_post, counter_value_pre); 257 + 258 + /* 259 + * We can't just update the counter without starting it. 260 + * Do start/stop twice to simulate that by first initializing to a very 261 + * high value and a low value after that. 262 + */ 263 + WRITE_ONCE(snapshot_data->ctr_values[0], ULONG_MAX/2); 264 + start_counter(counter, SBI_PMU_START_FLAG_INIT_SNAPSHOT, 0); 265 + stop_counter(counter, SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT); 266 + counter_value_pre = READ_ONCE(snapshot_data->ctr_values[0]); 267 + 268 + WRITE_ONCE(snapshot_data->ctr_values[0], counter_init_value); 269 + start_counter(counter, SBI_PMU_START_FLAG_INIT_SNAPSHOT, 0); 270 + stop_counter(counter, SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT); 271 + counter_value_post = READ_ONCE(snapshot_data->ctr_values[0]); 272 + __GUEST_ASSERT(counter_value_pre > counter_value_post, 273 + "Counter reinitialization verification failed : post [%lx] pre [%lx]\n", 274 + counter_value_post, counter_value_pre); 275 + 276 + /* Now set the initial value and compare */ 277 + WRITE_ONCE(snapshot_data->ctr_values[0], counter_init_value); 278 + start_counter(counter, SBI_PMU_START_FLAG_INIT_SNAPSHOT, 0); 279 + dummy_func_loop(10000); 280 + stop_counter(counter, SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT); 281 + 282 + counter_value_post = READ_ONCE(snapshot_data->ctr_values[0]); 261 283 __GUEST_ASSERT(counter_value_post > counter_init_value, 262 284 "Event update verification failed: post [%lx] pre [%lx]\n", 263 285 counter_value_post, counter_init_value); ··· 385 301 GUEST_DONE(); 386 302 } 387 303 304 + static void test_pmu_events_snaphost(void) 305 + { 306 + int num_counters = 0; 307 + struct riscv_pmu_snapshot_data *snapshot_data = snapshot_gva; 308 + int i; 309 + 310 + /* Verify presence of SBI PMU and minimum requrired SBI version */ 311 + verify_sbi_requirement_assert(); 312 + 313 + snapshot_set_shmem(snapshot_gpa, 0); 314 + 315 + /* Get the counter details */ 316 + num_counters = get_num_counters(); 317 + update_counter_info(num_counters); 318 + 319 + /* Validate shared memory access */ 320 + GUEST_ASSERT_EQ(READ_ONCE(snapshot_data->ctr_overflow_mask), 0); 321 + for (i = 0; i < num_counters; i++) { 322 + if (counter_mask_available & (BIT(i))) 323 + GUEST_ASSERT_EQ(READ_ONCE(snapshot_data->ctr_values[i]), 0); 324 + } 325 + /* Only these two events are guranteed to be present */ 326 + test_pmu_event_snapshot(SBI_PMU_HW_CPU_CYCLES); 327 + test_pmu_event_snapshot(SBI_PMU_HW_INSTRUCTIONS); 328 + 329 + GUEST_DONE(); 330 + } 331 + 388 332 static void run_vcpu(struct kvm_vcpu *vcpu) 389 333 { 390 334 struct ucall uc; ··· 469 357 test_vm_destroy(vm); 470 358 } 471 359 360 + static void test_vm_setup_snapshot_mem(struct kvm_vm *vm, struct kvm_vcpu *vcpu) 361 + { 362 + /* PMU Snapshot requires single page only */ 363 + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, PMU_SNAPSHOT_GPA_BASE, 1, 1, 0); 364 + /* PMU_SNAPSHOT_GPA_BASE is identity mapped */ 365 + virt_map(vm, PMU_SNAPSHOT_GPA_BASE, PMU_SNAPSHOT_GPA_BASE, 1); 366 + 367 + snapshot_gva = (void *)(PMU_SNAPSHOT_GPA_BASE); 368 + snapshot_gpa = addr_gva2gpa(vcpu->vm, (vm_vaddr_t)snapshot_gva); 369 + sync_global_to_guest(vcpu->vm, snapshot_gva); 370 + sync_global_to_guest(vcpu->vm, snapshot_gpa); 371 + } 372 + 373 + static void test_vm_events_snapshot_test(void *guest_code) 374 + { 375 + struct kvm_vm *vm = NULL; 376 + struct kvm_vcpu *vcpu; 377 + 378 + vm = vm_create_with_one_vcpu(&vcpu, guest_code); 379 + __TEST_REQUIRE(__vcpu_has_sbi_ext(vcpu, KVM_RISCV_SBI_EXT_PMU), 380 + "SBI PMU not available, skipping test"); 381 + 382 + test_vm_setup_snapshot_mem(vm, vcpu); 383 + 384 + run_vcpu(vcpu); 385 + 386 + test_vm_destroy(vm); 387 + } 388 + 472 389 int main(void) 473 390 { 474 391 test_vm_basic_test(test_pmu_basic_sanity); ··· 505 364 506 365 test_vm_events_test(test_pmu_events); 507 366 pr_info("SBI PMU event verification test : PASS\n"); 367 + 368 + test_vm_events_snapshot_test(test_pmu_events_snaphost); 369 + pr_info("SBI PMU event verification with snapshot test : PASS\n"); 508 370 509 371 return 0; 510 372 }