xref: /kvm-unit-tests/arm/timer.c (revision 0074eebcb5f40f152b210a9ab6f5394a0ed6e065)
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/processor.h>
12 #include <asm/gic.h>
13 #include <asm/io.h>
14 
15 #define ARCH_TIMER_CTL_ENABLE  (1 << 0)
16 #define ARCH_TIMER_CTL_IMASK   (1 << 1)
17 #define ARCH_TIMER_CTL_ISTATUS (1 << 2)
18 
19 static void *gic_ispendr;
20 static void *gic_isenabler;
21 static void *gic_icenabler;
22 
23 static bool ptimer_unsupported;
24 
25 static void ptimer_unsupported_handler(struct pt_regs *regs, unsigned int esr)
26 {
27 	ptimer_unsupported = true;
28 	regs->pc += 4;
29 }
30 
31 static u64 read_vtimer_counter(void)
32 {
33 	return read_sysreg(cntvct_el0);
34 }
35 
36 static u64 read_vtimer_cval(void)
37 {
38 	return read_sysreg(cntv_cval_el0);
39 }
40 
41 static void write_vtimer_cval(u64 val)
42 {
43 	write_sysreg(val, cntv_cval_el0);
44 	isb();
45 }
46 
47 static s32 read_vtimer_tval(void)
48 {
49 	return read_sysreg(cntv_tval_el0);
50 }
51 
52 static void write_vtimer_tval(s32 val)
53 {
54 	write_sysreg(val, cntv_tval_el0);
55 	isb();
56 }
57 
58 static u64 read_vtimer_ctl(void)
59 {
60 	return read_sysreg(cntv_ctl_el0);
61 }
62 
63 static void write_vtimer_ctl(u64 val)
64 {
65 	write_sysreg(val, cntv_ctl_el0);
66 	isb();
67 }
68 
69 static u64 read_ptimer_counter(void)
70 {
71 	return read_sysreg(cntpct_el0);
72 }
73 
74 static u64 read_ptimer_cval(void)
75 {
76 	return read_sysreg(cntp_cval_el0);
77 }
78 
79 static void write_ptimer_cval(u64 val)
80 {
81 	write_sysreg(val, cntp_cval_el0);
82 	isb();
83 }
84 
85 static s32 read_ptimer_tval(void)
86 {
87 	return read_sysreg(cntp_tval_el0);
88 }
89 
90 static void write_ptimer_tval(s32 val)
91 {
92 	write_sysreg(val, cntp_tval_el0);
93 	isb();
94 }
95 
96 static u64 read_ptimer_ctl(void)
97 {
98 	return read_sysreg(cntp_ctl_el0);
99 }
100 
101 static void write_ptimer_ctl(u64 val)
102 {
103 	write_sysreg(val, cntp_ctl_el0);
104 	isb();
105 }
106 
107 struct timer_info {
108 	u32 irq;
109 	u32 irq_flags;
110 	bool irq_received;
111 	u64 (*read_counter)(void);
112 	u64 (*read_cval)(void);
113 	void (*write_cval)(u64);
114 	s32 (*read_tval)(void);
115 	void (*write_tval)(s32);
116 	u64 (*read_ctl)(void);
117 	void (*write_ctl)(u64);
118 };
119 
120 static struct timer_info vtimer_info = {
121 	.irq_received = false,
122 	.read_counter = read_vtimer_counter,
123 	.read_cval = read_vtimer_cval,
124 	.write_cval = write_vtimer_cval,
125 	.read_tval = read_vtimer_tval,
126 	.write_tval = write_vtimer_tval,
127 	.read_ctl = read_vtimer_ctl,
128 	.write_ctl = write_vtimer_ctl,
129 };
130 
131 static struct timer_info ptimer_info = {
132 	.irq_received = false,
133 	.read_counter = read_ptimer_counter,
134 	.read_cval = read_ptimer_cval,
135 	.write_cval = write_ptimer_cval,
136 	.read_tval = read_ptimer_tval,
137 	.write_tval = write_ptimer_tval,
138 	.read_ctl = read_ptimer_ctl,
139 	.write_ctl = write_ptimer_ctl,
140 };
141 
142 static void set_timer_irq_enabled(struct timer_info *info, bool enabled)
143 {
144 	u32 val = 1 << PPI(info->irq);
145 
146 	if (enabled)
147 		writel(val, gic_isenabler);
148 	else
149 		writel(val, gic_icenabler);
150 }
151 
152 static void irq_handler(struct pt_regs *regs)
153 {
154 	struct timer_info *info;
155 	u32 irqstat = gic_read_iar();
156 	u32 irqnr = gic_iar_irqnr(irqstat);
157 
158 	if (irqnr != GICC_INT_SPURIOUS)
159 		gic_write_eoir(irqstat);
160 
161 	if (irqnr == PPI(vtimer_info.irq)) {
162 		info = &vtimer_info;
163 	} else if (irqnr == PPI(ptimer_info.irq)) {
164 		info = &ptimer_info;
165 	} else {
166 		report_info("Unexpected interrupt: %d\n", irqnr);
167 		return;
168 	}
169 
170 	info->write_ctl(ARCH_TIMER_CTL_IMASK | ARCH_TIMER_CTL_ENABLE);
171 	info->irq_received = true;
172 }
173 
174 static bool gic_timer_pending(struct timer_info *info)
175 {
176 	return readl(gic_ispendr) & (1 << PPI(info->irq));
177 }
178 
179 static bool test_cval_10msec(struct timer_info *info)
180 {
181 	u64 time_10ms = read_sysreg(cntfrq_el0) / 100;
182 	u64 time_1us = time_10ms / 10000;
183 	u64 before_timer, after_timer;
184 	s64 difference;
185 
186 	/* Program timer to fire in 10 ms */
187 	before_timer = info->read_counter();
188 	info->write_cval(before_timer + time_10ms);
189 	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
190 
191 	/* Wait for the timer to fire */
192 	while (!(info->read_ctl() & ARCH_TIMER_CTL_ISTATUS))
193 		;
194 
195 	/* It fired, check how long it took */
196 	after_timer = info->read_counter();
197 	difference = after_timer - (before_timer + time_10ms);
198 
199 	report_info("After timer: 0x%016lx", after_timer);
200 	report_info("Expected   : 0x%016lx", before_timer + time_10ms);
201 	report_info("Difference : %ld us", difference / time_1us);
202 
203 	if (difference < 0) {
204 		printf("ISTATUS set too early\n");
205 		return false;
206 	}
207 	return difference < time_10ms;
208 }
209 
210 static void test_timer(struct timer_info *info)
211 {
212 	u64 now = info->read_counter();
213 	u64 time_10s = read_sysreg(cntfrq_el0) * 10;
214 	u64 later = now + time_10s;
215 	s32 left;
216 
217 	/* We don't want the irq handler to fire because that will change the
218 	 * timer state and we want to test the timer output signal.  We can
219 	 * still read the pending state even if it's disabled. */
220 	set_timer_irq_enabled(info, false);
221 
222 	/* Enable the timer, but schedule it for much later */
223 	info->write_cval(later);
224 	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
225 	report(!gic_timer_pending(info), "not pending before");
226 
227 	info->write_cval(now - 1);
228 	report(gic_timer_pending(info), "interrupt signal pending");
229 
230 	/* Disable the timer again and prepare to take interrupts */
231 	info->write_ctl(0);
232 	set_timer_irq_enabled(info, true);
233 	report(!gic_timer_pending(info), "interrupt signal no longer pending");
234 
235 	report(test_cval_10msec(info), "latency within 10 ms");
236 	report(info->irq_received, "interrupt received");
237 
238 	/* Disable the timer again */
239 	info->write_ctl(0);
240 
241 	/* Test TVAL and IRQ trigger */
242 	info->irq_received = false;
243 	info->write_tval(read_sysreg(cntfrq_el0) / 100);	/* 10 ms */
244 	local_irq_disable();
245 	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
246 	report_info("waiting for interrupt...");
247 	wfi();
248 	local_irq_enable();
249 	left = info->read_tval();
250 	report(info->irq_received, "interrupt received after TVAL/WFI");
251 	report(left < 0, "timer has expired");
252 	report_info("TVAL is %d ticks", left);
253 }
254 
255 static void test_vtimer(void)
256 {
257 	report_prefix_push("vtimer-busy-loop");
258 	test_timer(&vtimer_info);
259 	report_prefix_pop();
260 }
261 
262 static void test_ptimer(void)
263 {
264 	if (ptimer_unsupported)
265 		return;
266 
267 	report_prefix_push("ptimer-busy-loop");
268 	test_timer(&ptimer_info);
269 	report_prefix_pop();
270 }
271 
272 static void test_init(void)
273 {
274 	const struct fdt_property *prop;
275 	const void *fdt = dt_fdt();
276 	int node, len;
277 	u32 *data;
278 
279 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
280 	assert(node >= 0);
281 	prop = fdt_get_property(fdt, node, "interrupts", &len);
282 	assert(prop && len == (4 * 3 * sizeof(u32)));
283 
284 	data = (u32 *)prop->data;
285 	assert(fdt32_to_cpu(data[3]) == 1);
286 	ptimer_info.irq = fdt32_to_cpu(data[4]);
287 	ptimer_info.irq_flags = fdt32_to_cpu(data[5]);
288 	assert(fdt32_to_cpu(data[6]) == 1);
289 	vtimer_info.irq = fdt32_to_cpu(data[7]);
290 	vtimer_info.irq_flags = fdt32_to_cpu(data[8]);
291 
292 	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, ptimer_unsupported_handler);
293 	read_sysreg(cntp_ctl_el0);
294 	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, NULL);
295 
296 	if (ptimer_unsupported && !ERRATA(7b6b46311a85)) {
297 		report_skip("Skipping ptimer tests. Set ERRATA_7b6b46311a85=y to enable.");
298 	} else if (ptimer_unsupported) {
299 		report(false, "ptimer: read CNTP_CTL_EL0");
300 		report_info("ptimer: skipping remaining tests");
301 	}
302 
303 	gic_enable_defaults();
304 
305 	switch (gic_version()) {
306 	case 2:
307 		gic_ispendr = gicv2_dist_base() + GICD_ISPENDR;
308 		gic_isenabler = gicv2_dist_base() + GICD_ISENABLER;
309 		gic_icenabler = gicv2_dist_base() + GICD_ICENABLER;
310 		break;
311 	case 3:
312 		gic_ispendr = gicv3_sgi_base() + GICD_ISPENDR;
313 		gic_isenabler = gicv3_sgi_base() + GICR_ISENABLER0;
314 		gic_icenabler = gicv3_sgi_base() + GICR_ICENABLER0;
315 		break;
316 	}
317 
318 	install_irq_handler(EL1H_IRQ, irq_handler);
319 	local_irq_enable();
320 }
321 
322 static void print_timer_info(void)
323 {
324 	printf("CNTFRQ_EL0   : 0x%016lx\n", read_sysreg(cntfrq_el0));
325 
326 	if (!ptimer_unsupported){
327 		printf("CNTPCT_EL0   : 0x%016lx\n", read_sysreg(cntpct_el0));
328 		printf("CNTP_CTL_EL0 : 0x%016lx\n", read_sysreg(cntp_ctl_el0));
329 		printf("CNTP_CVAL_EL0: 0x%016lx\n", read_sysreg(cntp_cval_el0));
330 	}
331 
332 	printf("CNTVCT_EL0   : 0x%016lx\n", read_sysreg(cntvct_el0));
333 	printf("CNTV_CTL_EL0 : 0x%016lx\n", read_sysreg(cntv_ctl_el0));
334 	printf("CNTV_CVAL_EL0: 0x%016lx\n", read_sysreg(cntv_cval_el0));
335 }
336 
337 int main(int argc, char **argv)
338 {
339 	int i;
340 
341 	test_init();
342 
343 	print_timer_info();
344 
345 	if (argc == 1) {
346 		test_vtimer();
347 		test_ptimer();
348 	}
349 
350 	for (i = 1; i < argc; ++i) {
351 		if (strcmp(argv[i], "vtimer") == 0)
352 			test_vtimer();
353 		if (strcmp(argv[i], "ptimer") == 0)
354 			test_ptimer();
355 	}
356 
357 	return report_summary();
358 }
359