xref: /kvm-unit-tests/arm/timer.c (revision e526bc786e9878c3880ae4b09b01a4572756e492)
1 /*
2  * Timer tests for the ARM virt machine.
3  *
4  * Copyright (C) 2017, Alexander Graf <agraf@suse.de>
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2.
7  */
8 #include <libcflat.h>
9 #include <devicetree.h>
10 #include <errata.h>
11 #include <asm/timer.h>
12 #include <asm/delay.h>
13 #include <asm/processor.h>
14 #include <asm/gic.h>
15 #include <asm/io.h>
16 
17 static bool ptimer_unsupported;
18 
ptimer_unsupported_handler(struct pt_regs * regs,unsigned int esr)19 static void ptimer_unsupported_handler(struct pt_regs *regs, unsigned int esr)
20 {
21 	ptimer_unsupported = true;
22 	regs->pc += 4;
23 }
24 
read_vtimer_counter(void)25 static u64 read_vtimer_counter(void)
26 {
27 	isb();
28 	return read_sysreg(cntvct_el0);
29 }
30 
read_vtimer_cval(void)31 static u64 read_vtimer_cval(void)
32 {
33 	return read_sysreg(cntv_cval_el0);
34 }
35 
write_vtimer_cval(u64 val)36 static void write_vtimer_cval(u64 val)
37 {
38 	write_sysreg(val, cntv_cval_el0);
39 	isb();
40 }
41 
read_vtimer_tval(void)42 static s32 read_vtimer_tval(void)
43 {
44 	return read_sysreg(cntv_tval_el0);
45 }
46 
write_vtimer_tval(s32 val)47 static void write_vtimer_tval(s32 val)
48 {
49 	write_sysreg(val, cntv_tval_el0);
50 	isb();
51 }
52 
read_vtimer_ctl(void)53 static u64 read_vtimer_ctl(void)
54 {
55 	return read_sysreg(cntv_ctl_el0);
56 }
57 
write_vtimer_ctl(u64 val)58 static void write_vtimer_ctl(u64 val)
59 {
60 	write_sysreg(val, cntv_ctl_el0);
61 	isb();
62 }
63 
read_ptimer_counter(void)64 static u64 read_ptimer_counter(void)
65 {
66 	isb();
67 	return read_sysreg(cntpct_el0);
68 }
69 
read_ptimer_cval(void)70 static u64 read_ptimer_cval(void)
71 {
72 	return read_sysreg(cntp_cval_el0);
73 }
74 
write_ptimer_cval(u64 val)75 static void write_ptimer_cval(u64 val)
76 {
77 	write_sysreg(val, cntp_cval_el0);
78 	isb();
79 }
80 
read_ptimer_tval(void)81 static s32 read_ptimer_tval(void)
82 {
83 	return read_sysreg(cntp_tval_el0);
84 }
85 
write_ptimer_tval(s32 val)86 static void write_ptimer_tval(s32 val)
87 {
88 	write_sysreg(val, cntp_tval_el0);
89 	isb();
90 }
91 
read_ptimer_ctl(void)92 static u64 read_ptimer_ctl(void)
93 {
94 	return read_sysreg(cntp_ctl_el0);
95 }
96 
write_ptimer_ctl(u64 val)97 static void write_ptimer_ctl(u64 val)
98 {
99 	write_sysreg(val, cntp_ctl_el0);
100 	isb();
101 }
102 
103 struct timer_info {
104 	u32 irq;
105 	volatile bool irq_received;
106 	u64 (*read_counter)(void);
107 	u64 (*read_cval)(void);
108 	void (*write_cval)(u64);
109 	s32 (*read_tval)(void);
110 	void (*write_tval)(s32);
111 	u64 (*read_ctl)(void);
112 	void (*write_ctl)(u64);
113 };
114 
115 static struct timer_info vtimer_info = {
116 	.irq_received = false,
117 	.read_counter = read_vtimer_counter,
118 	.read_cval = read_vtimer_cval,
119 	.write_cval = write_vtimer_cval,
120 	.read_tval = read_vtimer_tval,
121 	.write_tval = write_vtimer_tval,
122 	.read_ctl = read_vtimer_ctl,
123 	.write_ctl = write_vtimer_ctl,
124 };
125 
126 static struct timer_info ptimer_info = {
127 	.irq_received = false,
128 	.read_counter = read_ptimer_counter,
129 	.read_cval = read_ptimer_cval,
130 	.write_cval = write_ptimer_cval,
131 	.read_tval = read_ptimer_tval,
132 	.write_tval = write_ptimer_tval,
133 	.read_ctl = read_ptimer_ctl,
134 	.write_ctl = write_ptimer_ctl,
135 };
136 
set_timer_irq_enabled(struct timer_info * info,bool enabled)137 static void set_timer_irq_enabled(struct timer_info *info, bool enabled)
138 {
139 	u32 irq = info->irq;
140 
141 	if (enabled)
142 		gic_enable_irq(irq);
143 	else
144 		gic_disable_irq(irq);
145 }
146 
irq_handler(struct pt_regs * regs)147 static void irq_handler(struct pt_regs *regs)
148 {
149 	struct timer_info *info;
150 	u32 irqstat = gic_read_iar();
151 	u32 irqnr = gic_iar_irqnr(irqstat);
152 
153 	if (irqnr == vtimer_info.irq) {
154 		info = &vtimer_info;
155 	} else if (irqnr == ptimer_info.irq) {
156 		info = &ptimer_info;
157 	} else {
158 		if (irqnr != GICC_INT_SPURIOUS)
159 			gic_write_eoir(irqstat);
160 		report_info("Unexpected interrupt: %d\n", irqnr);
161 		return;
162 	}
163 
164 	info->write_ctl(ARCH_TIMER_CTL_IMASK | ARCH_TIMER_CTL_ENABLE);
165 	gic_write_eoir(irqstat);
166 
167 	info->irq_received = true;
168 }
169 
170 /* Check that the timer condition is met. */
timer_pending(struct timer_info * info)171 static bool timer_pending(struct timer_info *info)
172 {
173 	return (info->read_ctl() & ARCH_TIMER_CTL_ENABLE) &&
174 		(info->read_ctl() & ARCH_TIMER_CTL_ISTATUS);
175 }
176 
gic_timer_check_state(struct timer_info * info,enum gic_irq_state expected_state)177 static bool gic_timer_check_state(struct timer_info *info,
178 				  enum gic_irq_state expected_state)
179 {
180 	int i;
181 
182 	/* Wait for up to 1s for the GIC to sample the interrupt. */
183 	for (i = 0; i < 10; i++) {
184 		mdelay(100);
185 		if (gic_irq_state(info->irq) == expected_state) {
186 			mdelay(100);
187 			if (gic_irq_state(info->irq) == expected_state)
188 				return true;
189 		}
190 	}
191 
192 	return false;
193 }
194 
test_cval_10msec(struct timer_info * info)195 static bool test_cval_10msec(struct timer_info *info)
196 {
197 	u64 time_10ms = read_sysreg(cntfrq_el0) / 100;
198 	u64 time_1us = time_10ms / 10000;
199 	u64 before_timer, after_timer;
200 	s64 difference;
201 
202 	/* Program timer to fire in 10 ms */
203 	before_timer = info->read_counter();
204 	info->write_cval(before_timer + time_10ms);
205 	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
206 
207 	/* Wait for the timer to fire */
208 	while (!timer_pending(info))
209 		;
210 
211 	/* It fired, check how long it took */
212 	after_timer = info->read_counter();
213 	difference = after_timer - (before_timer + time_10ms);
214 
215 	report_info("After timer: 0x%016lx", after_timer);
216 	report_info("Expected   : 0x%016lx", before_timer + time_10ms);
217 	report_info("Difference : %ld us", difference / time_1us);
218 
219 	if (difference < 0) {
220 		printf("ISTATUS set too early\n");
221 		return false;
222 	}
223 	return difference < time_10ms;
224 }
225 
disable_timer(struct timer_info * info)226 static void disable_timer(struct timer_info *info)
227 {
228 	info->write_ctl(0);
229 	info->irq_received = false;
230 }
231 
test_timer_pending(struct timer_info * info)232 static void test_timer_pending(struct timer_info *info)
233 {
234 	u64 now = info->read_counter();
235 	u64 time_10s = read_sysreg(cntfrq_el0) * 10;
236 	u64 later = now + time_10s;
237 
238 	/*
239 	 * We don't want the irq handler to fire because that will change the
240 	 * timer state and we want to test the timer output signal.  We can
241 	 * still read the pending state even if it's disabled.
242 	 */
243 	set_timer_irq_enabled(info, false);
244 
245 	/* Enable the timer, but schedule it for much later */
246 	info->write_cval(later);
247 	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
248 	report(!timer_pending(info) && gic_timer_check_state(info, GIC_IRQ_STATE_INACTIVE),
249 			"not pending before");
250 
251 	info->write_cval(now - 1);
252 	report(timer_pending(info) && gic_timer_check_state(info, GIC_IRQ_STATE_PENDING),
253 			"interrupt signal pending");
254 
255 	disable_timer(info);
256 	set_timer_irq_enabled(info, true);
257 
258 	report(!info->irq_received, "no interrupt when timer is disabled");
259 	report(!timer_pending(info) && gic_timer_check_state(info, GIC_IRQ_STATE_INACTIVE),
260 			"interrupt signal no longer pending");
261 
262 	info->write_cval(now - 1);
263 	info->write_ctl(ARCH_TIMER_CTL_ENABLE | ARCH_TIMER_CTL_IMASK);
264 	report(timer_pending(info) && gic_timer_check_state(info, GIC_IRQ_STATE_INACTIVE),
265 			"interrupt signal not pending");
266 
267 	disable_timer(info);
268 }
269 
test_timer_cval(struct timer_info * info)270 static void test_timer_cval(struct timer_info *info)
271 {
272 	report(test_cval_10msec(info), "latency within 10 ms");
273 	report(info->irq_received, "interrupt received");
274 
275 	disable_timer(info);
276 }
277 
timer_do_wfi(struct timer_info * info)278 static void timer_do_wfi(struct timer_info *info)
279 {
280 	local_irq_disable();
281 	if (info->irq_received)
282 		goto out;
283 	report_info("waiting for interrupt...");
284 	wfi();
285 out:
286 	local_irq_enable();
287 }
288 
test_timer_tval(struct timer_info * info)289 static void test_timer_tval(struct timer_info *info)
290 {
291 	u64 time_10ms = read_sysreg(cntfrq_el0) / 100;
292 	s32 left;
293 	int i;
294 
295 	info->write_tval(time_10ms);
296 	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
297 
298 	for (;;) {
299 		timer_do_wfi(info);
300 		left = info->read_tval();
301 		if (info->irq_received || left <= 0)
302 			break;
303 	}
304 
305 	/* Wait one second for the GIC to update the interrupt state. */
306 	if (left <= 0 && !info->irq_received) {
307 		for (i = 0; i < 10; i++) {
308 			timer_do_wfi(info);
309 			if (info->irq_received)
310 				break;
311 			mdelay(100);
312 		}
313 		left = info->read_tval();
314 	}
315 
316 	report(info->irq_received, "interrupt received after TVAL/WFI");
317 	report(left <= 0, "timer has expired");
318 	report_info("TVAL is %d ticks", left);
319 
320 	disable_timer(info);
321 }
322 
test_timer(struct timer_info * info)323 static void test_timer(struct timer_info *info)
324 {
325 	test_timer_cval(info);
326 	test_timer_pending(info);
327 	test_timer_tval(info);
328 }
329 
test_vtimer(void)330 static void test_vtimer(void)
331 {
332 	report_prefix_push("vtimer-busy-loop");
333 	test_timer(&vtimer_info);
334 	report_prefix_pop();
335 }
336 
test_ptimer(void)337 static void test_ptimer(void)
338 {
339 	if (ptimer_unsupported)
340 		return;
341 
342 	report_prefix_push("ptimer-busy-loop");
343 	test_timer(&ptimer_info);
344 	report_prefix_pop();
345 }
346 
test_init(void)347 static void test_init(void)
348 {
349 	assert(TIMER_PTIMER_IRQ != -1 && TIMER_VTIMER_IRQ != -1);
350 	ptimer_info.irq = TIMER_PTIMER_IRQ;
351 	vtimer_info.irq = TIMER_VTIMER_IRQ;
352 
353 	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, ptimer_unsupported_handler);
354 	ptimer_info.read_ctl();
355 	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, NULL);
356 
357 	if (ptimer_unsupported && !ERRATA(7b6b46311a85)) {
358 		report_skip("Skipping ptimer tests. Set ERRATA_7b6b46311a85=y to enable.");
359 	} else if (ptimer_unsupported) {
360 		report_fail("ptimer: read CNTP_CTL_EL0");
361 		report_info("ptimer: skipping remaining tests");
362 	}
363 
364 	gic_enable_defaults();
365 
366 	install_irq_handler(EL1H_IRQ, irq_handler);
367 	set_timer_irq_enabled(&ptimer_info, true);
368 	set_timer_irq_enabled(&vtimer_info, true);
369 	local_irq_enable();
370 }
371 
print_timer_info(void)372 static void print_timer_info(void)
373 {
374 	printf("CNTFRQ_EL0   : 0x%016lx\n", read_sysreg(cntfrq_el0));
375 
376 	if (!ptimer_unsupported) {
377 		printf("CNTPCT_EL0   : 0x%016lx\n", ptimer_info.read_counter());
378 		printf("CNTP_CTL_EL0 : 0x%016lx\n", ptimer_info.read_ctl());
379 		printf("CNTP_CVAL_EL0: 0x%016lx\n", ptimer_info.read_cval());
380 	}
381 
382 	printf("CNTVCT_EL0   : 0x%016lx\n", vtimer_info.read_counter());
383 	printf("CNTV_CTL_EL0 : 0x%016lx\n", vtimer_info.read_ctl());
384 	printf("CNTV_CVAL_EL0: 0x%016lx\n", vtimer_info.read_cval());
385 }
386 
main(int argc,char ** argv)387 int main(int argc, char **argv)
388 {
389 	int i;
390 
391 	test_init();
392 
393 	print_timer_info();
394 
395 	if (argc == 1) {
396 		test_vtimer();
397 		test_ptimer();
398 	}
399 
400 	for (i = 1; i < argc; ++i) {
401 		if (strcmp(argv[i], "vtimer") == 0)
402 			test_vtimer();
403 		if (strcmp(argv[i], "ptimer") == 0)
404 			test_ptimer();
405 	}
406 
407 	return report_summary();
408 }
409