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