xref: /kvm-unit-tests/arm/gic.c (revision 4363f1d9a646a5c7ea673bee8fc33ca6f2cddbd8)
1 /*
2  * GIC tests
3  *
4  * GICv2
5  *   + test sending/receiving IPIs
6  * GICv3
7  *   + test sending/receiving IPIs
8  *
9  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
10  *
11  * This work is licensed under the terms of the GNU LGPL, version 2.
12  */
13 #include <libcflat.h>
14 #include <asm/setup.h>
15 #include <asm/processor.h>
16 #include <asm/delay.h>
17 #include <asm/gic.h>
18 #include <asm/smp.h>
19 #include <asm/barrier.h>
20 #include <asm/io.h>
21 
22 #define IPI_SENDER	1
23 #define IPI_IRQ		1
24 
25 struct gic {
26 	struct {
27 		void (*send_self)(void);
28 		void (*send_broadcast)(void);
29 	} ipi;
30 };
31 
32 static struct gic *gic;
33 static int acked[NR_CPUS], spurious[NR_CPUS];
34 static int bad_sender[NR_CPUS], bad_irq[NR_CPUS];
35 static cpumask_t ready;
36 
37 static void nr_cpu_check(int nr)
38 {
39 	if (nr_cpus < nr)
40 		report_abort("At least %d cpus required", nr);
41 }
42 
43 static void wait_on_ready(void)
44 {
45 	cpumask_set_cpu(smp_processor_id(), &ready);
46 	while (!cpumask_full(&ready))
47 		cpu_relax();
48 }
49 
50 static void stats_reset(void)
51 {
52 	int i;
53 
54 	for (i = 0; i < nr_cpus; ++i) {
55 		acked[i] = 0;
56 		bad_sender[i] = -1;
57 		bad_irq[i] = -1;
58 	}
59 	smp_wmb();
60 }
61 
62 static void check_acked(cpumask_t *mask)
63 {
64 	int missing = 0, extra = 0, unexpected = 0;
65 	int nr_pass, cpu, i;
66 	bool bad = false;
67 
68 	/* Wait up to 5s for all interrupts to be delivered */
69 	for (i = 0; i < 50; ++i) {
70 		mdelay(100);
71 		nr_pass = 0;
72 		for_each_present_cpu(cpu) {
73 			smp_rmb();
74 			nr_pass += cpumask_test_cpu(cpu, mask) ?
75 				acked[cpu] == 1 : acked[cpu] == 0;
76 
77 			if (bad_sender[cpu] != -1) {
78 				printf("cpu%d received IPI from wrong sender %d\n",
79 					cpu, bad_sender[cpu]);
80 				bad = true;
81 			}
82 
83 			if (bad_irq[cpu] != -1) {
84 				printf("cpu%d received wrong irq %d\n",
85 					cpu, bad_irq[cpu]);
86 				bad = true;
87 			}
88 		}
89 		if (nr_pass == nr_cpus) {
90 			report("Completed in %d ms", !bad, ++i * 100);
91 			return;
92 		}
93 	}
94 
95 	for_each_present_cpu(cpu) {
96 		if (cpumask_test_cpu(cpu, mask)) {
97 			if (!acked[cpu])
98 				++missing;
99 			else if (acked[cpu] > 1)
100 				++extra;
101 		} else {
102 			if (acked[cpu])
103 				++unexpected;
104 		}
105 	}
106 
107 	report("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d",
108 	       false, missing, extra, unexpected);
109 }
110 
111 static void check_spurious(void)
112 {
113 	int cpu;
114 
115 	smp_rmb();
116 	for_each_present_cpu(cpu) {
117 		if (spurious[cpu])
118 			report_info("WARN: cpu%d got %d spurious interrupts",
119 				cpu, spurious[cpu]);
120 	}
121 }
122 
123 static void check_ipi_sender(u32 irqstat)
124 {
125 	if (gic_version() == 2) {
126 		int src = (irqstat >> 10) & 7;
127 
128 		if (src != IPI_SENDER)
129 			bad_sender[smp_processor_id()] = src;
130 	}
131 }
132 
133 static void check_irqnr(u32 irqnr)
134 {
135 	if (irqnr != IPI_IRQ)
136 		bad_irq[smp_processor_id()] = irqnr;
137 }
138 
139 static void ipi_handler(struct pt_regs *regs __unused)
140 {
141 	u32 irqstat = gic_read_iar();
142 	u32 irqnr = gic_iar_irqnr(irqstat);
143 
144 	if (irqnr != GICC_INT_SPURIOUS) {
145 		gic_write_eoir(irqstat);
146 		smp_rmb(); /* pairs with wmb in stats_reset */
147 		++acked[smp_processor_id()];
148 		check_ipi_sender(irqstat);
149 		check_irqnr(irqnr);
150 		smp_wmb(); /* pairs with rmb in check_acked */
151 	} else {
152 		++spurious[smp_processor_id()];
153 		smp_wmb();
154 	}
155 }
156 
157 static void gicv2_ipi_send_self(void)
158 {
159 	writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
160 }
161 
162 static void gicv2_ipi_send_broadcast(void)
163 {
164 	writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
165 }
166 
167 static void gicv3_ipi_send_self(void)
168 {
169 	gic_ipi_send_single(IPI_IRQ, smp_processor_id());
170 }
171 
172 static void gicv3_ipi_send_broadcast(void)
173 {
174 	gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24);
175 	isb();
176 }
177 
178 static void ipi_test_self(void)
179 {
180 	cpumask_t mask;
181 
182 	report_prefix_push("self");
183 	stats_reset();
184 	cpumask_clear(&mask);
185 	cpumask_set_cpu(smp_processor_id(), &mask);
186 	gic->ipi.send_self();
187 	check_acked(&mask);
188 	report_prefix_pop();
189 }
190 
191 static void ipi_test_smp(void)
192 {
193 	cpumask_t mask;
194 	int i;
195 
196 	report_prefix_push("target-list");
197 	stats_reset();
198 	cpumask_copy(&mask, &cpu_present_mask);
199 	for (i = smp_processor_id() & 1; i < nr_cpus; i += 2)
200 		cpumask_clear_cpu(i, &mask);
201 	gic_ipi_send_mask(IPI_IRQ, &mask);
202 	check_acked(&mask);
203 	report_prefix_pop();
204 
205 	report_prefix_push("broadcast");
206 	stats_reset();
207 	cpumask_copy(&mask, &cpu_present_mask);
208 	cpumask_clear_cpu(smp_processor_id(), &mask);
209 	gic->ipi.send_broadcast();
210 	check_acked(&mask);
211 	report_prefix_pop();
212 }
213 
214 static void ipi_enable(void)
215 {
216 	gic_enable_defaults();
217 #ifdef __arm__
218 	install_exception_handler(EXCPTN_IRQ, ipi_handler);
219 #else
220 	install_irq_handler(EL1H_IRQ, ipi_handler);
221 #endif
222 	local_irq_enable();
223 }
224 
225 static void ipi_send(void)
226 {
227 	ipi_enable();
228 	wait_on_ready();
229 	ipi_test_self();
230 	ipi_test_smp();
231 	check_spurious();
232 	exit(report_summary());
233 }
234 
235 static void ipi_recv(void)
236 {
237 	ipi_enable();
238 	cpumask_set_cpu(smp_processor_id(), &ready);
239 	while (1)
240 		wfi();
241 }
242 
243 static void ipi_test(void *data __unused)
244 {
245 	if (smp_processor_id() == IPI_SENDER)
246 		ipi_send();
247 	else
248 		ipi_recv();
249 }
250 
251 static struct gic gicv2 = {
252 	.ipi = {
253 		.send_self = gicv2_ipi_send_self,
254 		.send_broadcast = gicv2_ipi_send_broadcast,
255 	},
256 };
257 
258 static struct gic gicv3 = {
259 	.ipi = {
260 		.send_self = gicv3_ipi_send_self,
261 		.send_broadcast = gicv3_ipi_send_broadcast,
262 	},
263 };
264 
265 static void ipi_clear_active_handler(struct pt_regs *regs __unused)
266 {
267 	u32 irqstat = gic_read_iar();
268 	u32 irqnr = gic_iar_irqnr(irqstat);
269 
270 	if (irqnr != GICC_INT_SPURIOUS) {
271 		void *base;
272 		u32 val = 1 << IPI_IRQ;
273 
274 		if (gic_version() == 2)
275 			base = gicv2_dist_base();
276 		else
277 			base = gicv3_redist_base();
278 
279 		writel(val, base + GICD_ICACTIVER);
280 
281 		smp_rmb(); /* pairs with wmb in stats_reset */
282 		++acked[smp_processor_id()];
283 		check_irqnr(irqnr);
284 		smp_wmb(); /* pairs with rmb in check_acked */
285 	} else {
286 		++spurious[smp_processor_id()];
287 		smp_wmb();
288 	}
289 }
290 
291 static void run_active_clear_test(void)
292 {
293 	report_prefix_push("active");
294 	gic_enable_defaults();
295 #ifdef __arm__
296 	install_exception_handler(EXCPTN_IRQ, ipi_clear_active_handler);
297 #else
298 	install_irq_handler(EL1H_IRQ, ipi_clear_active_handler);
299 #endif
300 	local_irq_enable();
301 
302 	ipi_test_self();
303 	report_prefix_pop();
304 }
305 
306 int main(int argc, char **argv)
307 {
308 	if (!gic_init()) {
309 		printf("No supported gic present, skipping tests...\n");
310 		return report_summary();
311 	}
312 
313 	report_prefix_pushf("gicv%d", gic_version());
314 
315 	switch (gic_version()) {
316 	case 2:
317 		gic = &gicv2;
318 		break;
319 	case 3:
320 		gic = &gicv3;
321 		break;
322 	}
323 
324 	if (argc < 2)
325 		report_abort("no test specified");
326 
327 	if (strcmp(argv[1], "ipi") == 0) {
328 		report_prefix_push(argv[1]);
329 		nr_cpu_check(2);
330 		on_cpus(ipi_test, NULL);
331 	} else if (strcmp(argv[1], "active") == 0) {
332 		run_active_clear_test();
333 	} else {
334 		report_abort("Unknown subtest '%s'", argv[1]);
335 	}
336 
337 	return report_summary();
338 }
339