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