1919#define RISCV_MAX_PMU_COUNTERS 64
2020union sbi_pmu_ctr_info ctrinfo_arr [RISCV_MAX_PMU_COUNTERS ];
2121
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+
2227/* Cache the available counters in a bitmask */
2328static unsigned long counter_mask_available ;
2429
@@ -186,6 +191,32 @@ static unsigned long read_counter(int idx, union sbi_pmu_ctr_info ctrinfo)
186191 return counter_val ;
187192}
188193
194+ static inline void verify_sbi_requirement_assert (void )
195+ {
196+ long out_val = 0 ;
197+ bool probe ;
198+
199+ probe = guest_sbi_probe_extension (SBI_EXT_PMU , & out_val );
200+ GUEST_ASSERT (probe && out_val == 1 );
201+
202+ if (get_host_sbi_spec_version () < sbi_mk_version (2 , 0 ))
203+ __GUEST_ASSERT (0 , "SBI implementation version doesn't support PMU Snapshot" );
204+ }
205+
206+ static void snapshot_set_shmem (vm_paddr_t gpa , unsigned long flags )
207+ {
208+ unsigned long lo = (unsigned long )gpa ;
209+ #if __riscv_xlen == 32
210+ unsigned long hi = (unsigned long )(gpa >> 32 );
211+ #else
212+ unsigned long hi = gpa == -1 ? -1 : 0 ;
213+ #endif
214+ struct sbiret ret = sbi_ecall (SBI_EXT_PMU , SBI_EXT_PMU_SNAPSHOT_SET_SHMEM ,
215+ lo , hi , flags , 0 , 0 , 0 );
216+
217+ GUEST_ASSERT (ret .value == 0 && ret .error == 0 );
218+ }
219+
189220static void test_pmu_event (unsigned long event )
190221{
191222 unsigned long counter ;
@@ -234,6 +265,59 @@ static void test_pmu_event(unsigned long event)
234265 stop_reset_counter (counter , 0 );
235266}
236267
268+ static void test_pmu_event_snapshot (unsigned long event )
269+ {
270+ unsigned long counter ;
271+ unsigned long counter_value_pre , counter_value_post ;
272+ unsigned long counter_init_value = 100 ;
273+ struct riscv_pmu_snapshot_data * snapshot_data = snapshot_gva ;
274+
275+ counter = get_counter_index (0 , counter_mask_available , 0 , event );
276+ counter_value_pre = read_counter (counter , ctrinfo_arr [counter ]);
277+
278+ /* Do not set the initial value */
279+ start_counter (counter , 0 , 0 );
280+ dummy_func_loop (10000 );
281+ stop_counter (counter , SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT );
282+
283+ /* The counter value is updated w.r.t relative index of cbase */
284+ counter_value_post = READ_ONCE (snapshot_data -> ctr_values [0 ]);
285+ __GUEST_ASSERT (counter_value_post > counter_value_pre ,
286+ "Event update verification failed: post [%lx] pre [%lx]\n" ,
287+ counter_value_post , counter_value_pre );
288+
289+ /*
290+ * We can't just update the counter without starting it.
291+ * Do start/stop twice to simulate that by first initializing to a very
292+ * high value and a low value after that.
293+ */
294+ WRITE_ONCE (snapshot_data -> ctr_values [0 ], ULONG_MAX /2 );
295+ start_counter (counter , SBI_PMU_START_FLAG_INIT_SNAPSHOT , 0 );
296+ stop_counter (counter , SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT );
297+ counter_value_pre = READ_ONCE (snapshot_data -> ctr_values [0 ]);
298+
299+ WRITE_ONCE (snapshot_data -> ctr_values [0 ], counter_init_value );
300+ start_counter (counter , SBI_PMU_START_FLAG_INIT_SNAPSHOT , 0 );
301+ stop_counter (counter , SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT );
302+ counter_value_post = READ_ONCE (snapshot_data -> ctr_values [0 ]);
303+ __GUEST_ASSERT (counter_value_pre > counter_value_post ,
304+ "Counter reinitialization verification failed : post [%lx] pre [%lx]\n" ,
305+ counter_value_post , counter_value_pre );
306+
307+ /* Now set the initial value and compare */
308+ WRITE_ONCE (snapshot_data -> ctr_values [0 ], counter_init_value );
309+ start_counter (counter , SBI_PMU_START_FLAG_INIT_SNAPSHOT , 0 );
310+ dummy_func_loop (10000 );
311+ stop_counter (counter , SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT );
312+
313+ counter_value_post = READ_ONCE (snapshot_data -> ctr_values [0 ]);
314+ __GUEST_ASSERT (counter_value_post > counter_init_value ,
315+ "Event update verification failed: post [%lx] pre [%lx]\n" ,
316+ counter_value_post , counter_init_value );
317+
318+ stop_reset_counter (counter , 0 );
319+ }
320+
237321static void test_invalid_event (void )
238322{
239323 struct sbiret ret ;
@@ -301,6 +385,34 @@ static void test_pmu_basic_sanity(void)
301385 GUEST_DONE ();
302386}
303387
388+ static void test_pmu_events_snaphost (void )
389+ {
390+ int num_counters = 0 ;
391+ struct riscv_pmu_snapshot_data * snapshot_data = snapshot_gva ;
392+ int i ;
393+
394+ /* Verify presence of SBI PMU and minimum requrired SBI version */
395+ verify_sbi_requirement_assert ();
396+
397+ snapshot_set_shmem (snapshot_gpa , 0 );
398+
399+ /* Get the counter details */
400+ num_counters = get_num_counters ();
401+ update_counter_info (num_counters );
402+
403+ /* Validate shared memory access */
404+ GUEST_ASSERT_EQ (READ_ONCE (snapshot_data -> ctr_overflow_mask ), 0 );
405+ for (i = 0 ; i < num_counters ; i ++ ) {
406+ if (counter_mask_available & (BIT (i )))
407+ GUEST_ASSERT_EQ (READ_ONCE (snapshot_data -> ctr_values [i ]), 0 );
408+ }
409+ /* Only these two events are guranteed to be present */
410+ test_pmu_event_snapshot (SBI_PMU_HW_CPU_CYCLES );
411+ test_pmu_event_snapshot (SBI_PMU_HW_INSTRUCTIONS );
412+
413+ GUEST_DONE ();
414+ }
415+
304416static void run_vcpu (struct kvm_vcpu * vcpu )
305417{
306418 struct ucall uc ;
@@ -357,6 +469,35 @@ static void test_vm_events_test(void *guest_code)
357469 test_vm_destroy (vm );
358470}
359471
472+ static void test_vm_setup_snapshot_mem (struct kvm_vm * vm , struct kvm_vcpu * vcpu )
473+ {
474+ /* PMU Snapshot requires single page only */
475+ vm_userspace_mem_region_add (vm , VM_MEM_SRC_ANONYMOUS , PMU_SNAPSHOT_GPA_BASE , 1 , 1 , 0 );
476+ /* PMU_SNAPSHOT_GPA_BASE is identity mapped */
477+ virt_map (vm , PMU_SNAPSHOT_GPA_BASE , PMU_SNAPSHOT_GPA_BASE , 1 );
478+
479+ snapshot_gva = (void * )(PMU_SNAPSHOT_GPA_BASE );
480+ snapshot_gpa = addr_gva2gpa (vcpu -> vm , (vm_vaddr_t )snapshot_gva );
481+ sync_global_to_guest (vcpu -> vm , snapshot_gva );
482+ sync_global_to_guest (vcpu -> vm , snapshot_gpa );
483+ }
484+
485+ static void test_vm_events_snapshot_test (void * guest_code )
486+ {
487+ struct kvm_vm * vm = NULL ;
488+ struct kvm_vcpu * vcpu ;
489+
490+ vm = vm_create_with_one_vcpu (& vcpu , guest_code );
491+ __TEST_REQUIRE (__vcpu_has_sbi_ext (vcpu , KVM_RISCV_SBI_EXT_PMU ),
492+ "SBI PMU not available, skipping test" );
493+
494+ test_vm_setup_snapshot_mem (vm , vcpu );
495+
496+ run_vcpu (vcpu );
497+
498+ test_vm_destroy (vm );
499+ }
500+
360501int main (void )
361502{
362503 test_vm_basic_test (test_pmu_basic_sanity );
@@ -365,5 +506,8 @@ int main(void)
365506 test_vm_events_test (test_pmu_events );
366507 pr_info ("SBI PMU event verification test : PASS\n" );
367508
509+ test_vm_events_snapshot_test (test_pmu_events_snaphost );
510+ pr_info ("SBI PMU event verification with snapshot test : PASS\n" );
511+
368512 return 0 ;
369513}
0 commit comments