xref: /linux/tools/testing/selftests/kvm/loongarch/arch_timer.c (revision 51d90a15fedf8366cb96ef68d0ea2d0bf15417d2)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * The test validates periodic/one-shot constant timer IRQ using
4  * CSR.TCFG and CSR.TVAL registers.
5  */
6 #include "arch_timer.h"
7 #include "kvm_util.h"
8 #include "processor.h"
9 #include "timer_test.h"
10 #include "ucall_common.h"
11 
do_idle(void)12 static void do_idle(void)
13 {
14 	unsigned int intid;
15 	unsigned long estat;
16 
17 	__asm__ __volatile__("idle 0" : : : "memory");
18 
19 	estat = csr_read(LOONGARCH_CSR_ESTAT);
20 	intid = !!(estat & BIT(INT_TI));
21 
22 	/* Make sure pending timer IRQ arrived */
23 	GUEST_ASSERT_EQ(intid, 1);
24 	csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
25 }
26 
guest_irq_handler(struct ex_regs * regs)27 static void guest_irq_handler(struct ex_regs *regs)
28 {
29 	unsigned int intid;
30 	uint32_t cpu = guest_get_vcpuid();
31 	uint64_t xcnt, val, cfg, xcnt_diff_us;
32 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
33 
34 	intid = !!(regs->estat & BIT(INT_TI));
35 
36 	/* Make sure we are dealing with the correct timer IRQ */
37 	GUEST_ASSERT_EQ(intid, 1);
38 
39 	cfg = timer_get_cfg();
40 	if (cfg & CSR_TCFG_PERIOD) {
41 		WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter - 1);
42 		if (shared_data->nr_iter == 0)
43 			disable_timer();
44 		csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
45 		return;
46 	}
47 
48 	/*
49 	 * On real machine, value of LOONGARCH_CSR_TVAL is BIT_ULL(48) - 1
50 	 * On virtual machine, its value counts down from BIT_ULL(48) - 1
51 	 */
52 	val = timer_get_val();
53 	xcnt = timer_get_cycles();
54 	xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
55 
56 	/* Basic 'timer condition met' check */
57 	__GUEST_ASSERT(val > cfg,
58 			"val = 0x%lx, cfg = 0x%lx, xcnt_diff_us = 0x%lx",
59 			val, cfg, xcnt_diff_us);
60 
61 	csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
62 	WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
63 }
64 
guest_test_period_timer(uint32_t cpu)65 static void guest_test_period_timer(uint32_t cpu)
66 {
67 	uint32_t irq_iter, config_iter;
68 	uint64_t us;
69 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
70 
71 	shared_data->nr_iter = test_args.nr_iter;
72 	shared_data->xcnt = timer_get_cycles();
73 	us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us;
74 	timer_set_next_cmp_ms(test_args.timer_period_ms, true);
75 
76 	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
77 		/* Setup a timeout for the interrupt to arrive */
78 		udelay(us);
79 	}
80 
81 	irq_iter = READ_ONCE(shared_data->nr_iter);
82 	__GUEST_ASSERT(irq_iter == 0,
83 			"irq_iter = 0x%x.\n"
84 			"  Guest period timer interrupt was not triggered within the specified\n"
85 			"  interval, try to increase the error margin by [-e] option.\n",
86 			irq_iter);
87 }
88 
guest_test_oneshot_timer(uint32_t cpu)89 static void guest_test_oneshot_timer(uint32_t cpu)
90 {
91 	uint32_t irq_iter, config_iter;
92 	uint64_t us;
93 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
94 
95 	shared_data->nr_iter = 0;
96 	shared_data->guest_stage = 0;
97 	us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us;
98 	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
99 		shared_data->xcnt = timer_get_cycles();
100 
101 		/* Setup the next interrupt */
102 		timer_set_next_cmp_ms(test_args.timer_period_ms, false);
103 		/* Setup a timeout for the interrupt to arrive */
104 		udelay(us);
105 
106 		irq_iter = READ_ONCE(shared_data->nr_iter);
107 		__GUEST_ASSERT(config_iter + 1 == irq_iter,
108 				"config_iter + 1 = 0x%x, irq_iter = 0x%x.\n"
109 				"  Guest timer interrupt was not triggered within the specified\n"
110 				"  interval, try to increase the error margin by [-e] option.\n",
111 				config_iter + 1, irq_iter);
112 	}
113 }
114 
guest_test_emulate_timer(uint32_t cpu)115 static void guest_test_emulate_timer(uint32_t cpu)
116 {
117 	uint32_t config_iter;
118 	uint64_t xcnt_diff_us, us;
119 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
120 
121 	local_irq_disable();
122 	shared_data->nr_iter = 0;
123 	us = msecs_to_usecs(test_args.timer_period_ms);
124 	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
125 		shared_data->xcnt = timer_get_cycles();
126 
127 		/* Setup the next interrupt */
128 		timer_set_next_cmp_ms(test_args.timer_period_ms, false);
129 		do_idle();
130 
131 		xcnt_diff_us = cycles_to_usec(timer_get_cycles() - shared_data->xcnt);
132 		__GUEST_ASSERT(xcnt_diff_us >= us,
133 				"xcnt_diff_us = 0x%lx, us = 0x%lx.\n",
134 				xcnt_diff_us, us);
135 	}
136 	local_irq_enable();
137 }
138 
guest_time_count_test(uint32_t cpu)139 static void guest_time_count_test(uint32_t cpu)
140 {
141 	uint32_t config_iter;
142 	unsigned long start, end, prev, us;
143 
144 	/* Assuming that test case starts to run in 1 second */
145 	start = timer_get_cycles();
146 	us = msec_to_cycles(1000);
147 	__GUEST_ASSERT(start <= us,
148 			"start = 0x%lx, us = 0x%lx.\n",
149 			start, us);
150 
151 	us = msec_to_cycles(test_args.timer_period_ms);
152 	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
153 		start = timer_get_cycles();
154 		end = start + us;
155 		/* test time count growing up always */
156 		while (start < end) {
157 			prev = start;
158 			start = timer_get_cycles();
159 			__GUEST_ASSERT(prev <= start,
160 					"prev = 0x%lx, start = 0x%lx.\n",
161 					prev, start);
162 		}
163 	}
164 }
165 
guest_code(void)166 static void guest_code(void)
167 {
168 	uint32_t cpu = guest_get_vcpuid();
169 
170 	/* must run at first */
171 	guest_time_count_test(cpu);
172 
173 	timer_irq_enable();
174 	local_irq_enable();
175 	guest_test_period_timer(cpu);
176 	guest_test_oneshot_timer(cpu);
177 	guest_test_emulate_timer(cpu);
178 
179 	GUEST_DONE();
180 }
181 
test_vm_create(void)182 struct kvm_vm *test_vm_create(void)
183 {
184 	struct kvm_vm *vm;
185 	int nr_vcpus = test_args.nr_vcpus;
186 
187 	vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
188 	vm_init_descriptor_tables(vm);
189 	vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler);
190 
191 	/* Make all the test's cmdline args visible to the guest */
192 	sync_global_to_guest(vm, test_args);
193 
194 	return vm;
195 }
196 
test_vm_cleanup(struct kvm_vm * vm)197 void test_vm_cleanup(struct kvm_vm *vm)
198 {
199 	kvm_vm_free(vm);
200 }
201