1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * arch_timer.c - Tests the riscv64 sstc timer IRQ functionality
4  *
5  * The test validates the sstc timer IRQs using vstimecmp registers.
6  * It's ported from the aarch64 arch_timer test.
7  *
8  * Copyright (c) 2024, Intel Corporation.
9  */
10 
11 #define _GNU_SOURCE
12 
13 #include "arch_timer.h"
14 #include "kvm_util.h"
15 #include "processor.h"
16 #include "timer_test.h"
17 
18 static int timer_irq = IRQ_S_TIMER;
19 
20 static void guest_irq_handler(struct ex_regs *regs)
21 {
22 	uint64_t xcnt, xcnt_diff_us, cmp;
23 	unsigned int intid = regs->cause & ~CAUSE_IRQ_FLAG;
24 	uint32_t cpu = guest_get_vcpuid();
25 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
26 
27 	timer_irq_disable();
28 
29 	xcnt = timer_get_cycles();
30 	cmp = timer_get_cmp();
31 	xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
32 
33 	/* Make sure we are dealing with the correct timer IRQ */
34 	GUEST_ASSERT_EQ(intid, timer_irq);
35 
36 	__GUEST_ASSERT(xcnt >= cmp,
37 			"xcnt = 0x%"PRIx64", cmp = 0x%"PRIx64", xcnt_diff_us = 0x%" PRIx64,
38 			xcnt, cmp, xcnt_diff_us);
39 
40 	WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
41 }
42 
43 static void guest_run(struct test_vcpu_shared_data *shared_data)
44 {
45 	uint32_t irq_iter, config_iter;
46 
47 	shared_data->nr_iter = 0;
48 	shared_data->guest_stage = 0;
49 
50 	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
51 		/* Setup the next interrupt */
52 		timer_set_next_cmp_ms(test_args.timer_period_ms);
53 		shared_data->xcnt = timer_get_cycles();
54 		timer_irq_enable();
55 
56 		/* Setup a timeout for the interrupt to arrive */
57 		udelay(msecs_to_usecs(test_args.timer_period_ms) +
58 			test_args.timer_err_margin_us);
59 
60 		irq_iter = READ_ONCE(shared_data->nr_iter);
61 		__GUEST_ASSERT(config_iter + 1 == irq_iter,
62 				"config_iter + 1 = 0x%x, irq_iter = 0x%x.\n"
63 				"  Guest timer interrupt was not triggered within the specified\n"
64 				"  interval, try to increase the error margin by [-e] option.\n",
65 				config_iter + 1, irq_iter);
66 	}
67 }
68 
69 static void guest_code(void)
70 {
71 	uint32_t cpu = guest_get_vcpuid();
72 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
73 
74 	timer_irq_disable();
75 	local_irq_enable();
76 
77 	guest_run(shared_data);
78 
79 	GUEST_DONE();
80 }
81 
82 struct kvm_vm *test_vm_create(void)
83 {
84 	struct kvm_vm *vm;
85 	int nr_vcpus = test_args.nr_vcpus;
86 
87 	vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
88 	__TEST_REQUIRE(__vcpu_has_ext(vcpus[0], RISCV_ISA_EXT_REG(KVM_RISCV_ISA_EXT_SSTC)),
89 				   "SSTC not available, skipping test\n");
90 
91 	vm_init_vector_tables(vm);
92 	vm_install_interrupt_handler(vm, guest_irq_handler);
93 
94 	for (int i = 0; i < nr_vcpus; i++)
95 		vcpu_init_vector_tables(vcpus[i]);
96 
97 	/* Initialize guest timer frequency. */
98 	vcpu_get_reg(vcpus[0], RISCV_TIMER_REG(frequency), &timer_freq);
99 	sync_global_to_guest(vm, timer_freq);
100 	pr_debug("timer_freq: %lu\n", timer_freq);
101 
102 	/* Make all the test's cmdline args visible to the guest */
103 	sync_global_to_guest(vm, test_args);
104 
105 	return vm;
106 }
107 
108 void test_vm_cleanup(struct kvm_vm *vm)
109 {
110 	kvm_vm_free(vm);
111 }
112