xref: /kvm-unit-tests/arm/gic.c (revision 2e2d471d2aeb249869b525a42dcf22659d6c4bc7)
1ac4a67b6SAndrew Jones /*
2ac4a67b6SAndrew Jones  * GIC tests
3ac4a67b6SAndrew Jones  *
4ac4a67b6SAndrew Jones  * GICv2
5ac4a67b6SAndrew Jones  *   + test sending/receiving IPIs
6*2e2d471dSAndrew Jones  * GICv3
7*2e2d471dSAndrew Jones  *   + test sending/receiving IPIs
8ac4a67b6SAndrew Jones  *
9ac4a67b6SAndrew Jones  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
10ac4a67b6SAndrew Jones  *
11ac4a67b6SAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
12ac4a67b6SAndrew Jones  */
13ac4a67b6SAndrew Jones #include <libcflat.h>
14ac4a67b6SAndrew Jones #include <asm/setup.h>
15ac4a67b6SAndrew Jones #include <asm/processor.h>
16ac4a67b6SAndrew Jones #include <asm/delay.h>
17ac4a67b6SAndrew Jones #include <asm/gic.h>
18ac4a67b6SAndrew Jones #include <asm/smp.h>
19ac4a67b6SAndrew Jones #include <asm/barrier.h>
20ac4a67b6SAndrew Jones #include <asm/io.h>
21ac4a67b6SAndrew Jones 
22*2e2d471dSAndrew Jones struct gic {
23*2e2d471dSAndrew Jones 	struct {
24*2e2d471dSAndrew Jones 		void (*send_self)(void);
25*2e2d471dSAndrew Jones 		void (*send_broadcast)(void);
26*2e2d471dSAndrew Jones 	} ipi;
27*2e2d471dSAndrew Jones };
28*2e2d471dSAndrew Jones 
29*2e2d471dSAndrew Jones static struct gic *gic;
30ac4a67b6SAndrew Jones static int acked[NR_CPUS], spurious[NR_CPUS];
31ac4a67b6SAndrew Jones static cpumask_t ready;
32ac4a67b6SAndrew Jones 
33ac4a67b6SAndrew Jones static void nr_cpu_check(int nr)
34ac4a67b6SAndrew Jones {
35ac4a67b6SAndrew Jones 	if (nr_cpus < nr)
36ac4a67b6SAndrew Jones 		report_abort("At least %d cpus required", nr);
37ac4a67b6SAndrew Jones }
38ac4a67b6SAndrew Jones 
39ac4a67b6SAndrew Jones static void wait_on_ready(void)
40ac4a67b6SAndrew Jones {
41ac4a67b6SAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &ready);
42ac4a67b6SAndrew Jones 	while (!cpumask_full(&ready))
43ac4a67b6SAndrew Jones 		cpu_relax();
44ac4a67b6SAndrew Jones }
45ac4a67b6SAndrew Jones 
46ac4a67b6SAndrew Jones static void check_acked(cpumask_t *mask)
47ac4a67b6SAndrew Jones {
48ac4a67b6SAndrew Jones 	int missing = 0, extra = 0, unexpected = 0;
49ac4a67b6SAndrew Jones 	int nr_pass, cpu, i;
50ac4a67b6SAndrew Jones 
51ac4a67b6SAndrew Jones 	/* Wait up to 5s for all interrupts to be delivered */
52ac4a67b6SAndrew Jones 	for (i = 0; i < 50; ++i) {
53ac4a67b6SAndrew Jones 		mdelay(100);
54ac4a67b6SAndrew Jones 		nr_pass = 0;
55ac4a67b6SAndrew Jones 		for_each_present_cpu(cpu) {
56ac4a67b6SAndrew Jones 			smp_rmb();
57ac4a67b6SAndrew Jones 			nr_pass += cpumask_test_cpu(cpu, mask) ?
58ac4a67b6SAndrew Jones 				acked[cpu] == 1 : acked[cpu] == 0;
59ac4a67b6SAndrew Jones 		}
60ac4a67b6SAndrew Jones 		if (nr_pass == nr_cpus) {
61ac4a67b6SAndrew Jones 			report("Completed in %d ms", true, ++i * 100);
62ac4a67b6SAndrew Jones 			return;
63ac4a67b6SAndrew Jones 		}
64ac4a67b6SAndrew Jones 	}
65ac4a67b6SAndrew Jones 
66ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
67ac4a67b6SAndrew Jones 		if (cpumask_test_cpu(cpu, mask)) {
68ac4a67b6SAndrew Jones 			if (!acked[cpu])
69ac4a67b6SAndrew Jones 				++missing;
70ac4a67b6SAndrew Jones 			else if (acked[cpu] > 1)
71ac4a67b6SAndrew Jones 				++extra;
72ac4a67b6SAndrew Jones 		} else {
73ac4a67b6SAndrew Jones 			if (acked[cpu])
74ac4a67b6SAndrew Jones 				++unexpected;
75ac4a67b6SAndrew Jones 		}
76ac4a67b6SAndrew Jones 	}
77ac4a67b6SAndrew Jones 
78ac4a67b6SAndrew Jones 	report("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d",
79ac4a67b6SAndrew Jones 	       false, missing, extra, unexpected);
80ac4a67b6SAndrew Jones }
81ac4a67b6SAndrew Jones 
82ac4a67b6SAndrew Jones static void check_spurious(void)
83ac4a67b6SAndrew Jones {
84ac4a67b6SAndrew Jones 	int cpu;
85ac4a67b6SAndrew Jones 
86ac4a67b6SAndrew Jones 	smp_rmb();
87ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
88ac4a67b6SAndrew Jones 		if (spurious[cpu])
89ac4a67b6SAndrew Jones 			report_info("WARN: cpu%d got %d spurious interrupts",
90ac4a67b6SAndrew Jones 				cpu, spurious[cpu]);
91ac4a67b6SAndrew Jones 	}
92ac4a67b6SAndrew Jones }
93ac4a67b6SAndrew Jones 
94ac4a67b6SAndrew Jones static void ipi_handler(struct pt_regs *regs __unused)
95ac4a67b6SAndrew Jones {
96*2e2d471dSAndrew Jones 	u32 irqstat = gic_read_iar();
97*2e2d471dSAndrew Jones 	u32 irqnr = gic_iar_irqnr(irqstat);
98ac4a67b6SAndrew Jones 
99ac4a67b6SAndrew Jones 	if (irqnr != GICC_INT_SPURIOUS) {
100*2e2d471dSAndrew Jones 		gic_write_eoir(irqstat);
101ac4a67b6SAndrew Jones 		smp_rmb(); /* pairs with wmb in ipi_test functions */
102ac4a67b6SAndrew Jones 		++acked[smp_processor_id()];
103ac4a67b6SAndrew Jones 		smp_wmb(); /* pairs with rmb in check_acked */
104ac4a67b6SAndrew Jones 	} else {
105ac4a67b6SAndrew Jones 		++spurious[smp_processor_id()];
106ac4a67b6SAndrew Jones 		smp_wmb();
107ac4a67b6SAndrew Jones 	}
108ac4a67b6SAndrew Jones }
109ac4a67b6SAndrew Jones 
110*2e2d471dSAndrew Jones static void gicv2_ipi_send_self(void)
111*2e2d471dSAndrew Jones {
112*2e2d471dSAndrew Jones 	writel(2 << 24, gicv2_dist_base() + GICD_SGIR);
113*2e2d471dSAndrew Jones }
114*2e2d471dSAndrew Jones 
115*2e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void)
116*2e2d471dSAndrew Jones {
117*2e2d471dSAndrew Jones 	writel(1 << 24, gicv2_dist_base() + GICD_SGIR);
118*2e2d471dSAndrew Jones }
119*2e2d471dSAndrew Jones 
120*2e2d471dSAndrew Jones static void gicv3_ipi_send_self(void)
121*2e2d471dSAndrew Jones {
122*2e2d471dSAndrew Jones 	gic_ipi_send_single(0, smp_processor_id());
123*2e2d471dSAndrew Jones }
124*2e2d471dSAndrew Jones 
125*2e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void)
126*2e2d471dSAndrew Jones {
127*2e2d471dSAndrew Jones 	gicv3_write_sgi1r(1ULL << 40);
128*2e2d471dSAndrew Jones 	isb();
129*2e2d471dSAndrew Jones }
130*2e2d471dSAndrew Jones 
131ac4a67b6SAndrew Jones static void ipi_test_self(void)
132ac4a67b6SAndrew Jones {
133ac4a67b6SAndrew Jones 	cpumask_t mask;
134ac4a67b6SAndrew Jones 
135ac4a67b6SAndrew Jones 	report_prefix_push("self");
136ac4a67b6SAndrew Jones 	memset(acked, 0, sizeof(acked));
137ac4a67b6SAndrew Jones 	smp_wmb();
138ac4a67b6SAndrew Jones 	cpumask_clear(&mask);
139ac4a67b6SAndrew Jones 	cpumask_set_cpu(0, &mask);
140*2e2d471dSAndrew Jones 	gic->ipi.send_self();
141ac4a67b6SAndrew Jones 	check_acked(&mask);
142ac4a67b6SAndrew Jones 	report_prefix_pop();
143ac4a67b6SAndrew Jones }
144ac4a67b6SAndrew Jones 
145ac4a67b6SAndrew Jones static void ipi_test_smp(void)
146ac4a67b6SAndrew Jones {
147ac4a67b6SAndrew Jones 	cpumask_t mask;
148*2e2d471dSAndrew Jones 	int i;
149ac4a67b6SAndrew Jones 
150ac4a67b6SAndrew Jones 	report_prefix_push("target-list");
151ac4a67b6SAndrew Jones 	memset(acked, 0, sizeof(acked));
152ac4a67b6SAndrew Jones 	smp_wmb();
153*2e2d471dSAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
154*2e2d471dSAndrew Jones 	for (i = 0; i < nr_cpus; i += 2)
155*2e2d471dSAndrew Jones 		cpumask_clear_cpu(i, &mask);
156*2e2d471dSAndrew Jones 	gic_ipi_send_mask(0, &mask);
157ac4a67b6SAndrew Jones 	check_acked(&mask);
158ac4a67b6SAndrew Jones 	report_prefix_pop();
159ac4a67b6SAndrew Jones 
160ac4a67b6SAndrew Jones 	report_prefix_push("broadcast");
161ac4a67b6SAndrew Jones 	memset(acked, 0, sizeof(acked));
162ac4a67b6SAndrew Jones 	smp_wmb();
163ac4a67b6SAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
164ac4a67b6SAndrew Jones 	cpumask_clear_cpu(0, &mask);
165*2e2d471dSAndrew Jones 	gic->ipi.send_broadcast();
166ac4a67b6SAndrew Jones 	check_acked(&mask);
167ac4a67b6SAndrew Jones 	report_prefix_pop();
168ac4a67b6SAndrew Jones }
169ac4a67b6SAndrew Jones 
170ac4a67b6SAndrew Jones static void ipi_enable(void)
171ac4a67b6SAndrew Jones {
172*2e2d471dSAndrew Jones 	gic_enable_defaults();
173ac4a67b6SAndrew Jones #ifdef __arm__
174ac4a67b6SAndrew Jones 	install_exception_handler(EXCPTN_IRQ, ipi_handler);
175ac4a67b6SAndrew Jones #else
176ac4a67b6SAndrew Jones 	install_irq_handler(EL1H_IRQ, ipi_handler);
177ac4a67b6SAndrew Jones #endif
178ac4a67b6SAndrew Jones 	local_irq_enable();
179ac4a67b6SAndrew Jones }
180ac4a67b6SAndrew Jones 
181ac4a67b6SAndrew Jones static void ipi_recv(void)
182ac4a67b6SAndrew Jones {
183ac4a67b6SAndrew Jones 	ipi_enable();
184ac4a67b6SAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &ready);
185ac4a67b6SAndrew Jones 	while (1)
186ac4a67b6SAndrew Jones 		wfi();
187ac4a67b6SAndrew Jones }
188ac4a67b6SAndrew Jones 
189*2e2d471dSAndrew Jones static struct gic gicv2 = {
190*2e2d471dSAndrew Jones 	.ipi = {
191*2e2d471dSAndrew Jones 		.send_self = gicv2_ipi_send_self,
192*2e2d471dSAndrew Jones 		.send_broadcast = gicv2_ipi_send_broadcast,
193*2e2d471dSAndrew Jones 	},
194*2e2d471dSAndrew Jones };
195*2e2d471dSAndrew Jones 
196*2e2d471dSAndrew Jones static struct gic gicv3 = {
197*2e2d471dSAndrew Jones 	.ipi = {
198*2e2d471dSAndrew Jones 		.send_self = gicv3_ipi_send_self,
199*2e2d471dSAndrew Jones 		.send_broadcast = gicv3_ipi_send_broadcast,
200*2e2d471dSAndrew Jones 	},
201*2e2d471dSAndrew Jones };
202*2e2d471dSAndrew Jones 
203ac4a67b6SAndrew Jones int main(int argc, char **argv)
204ac4a67b6SAndrew Jones {
205ac4a67b6SAndrew Jones 	char pfx[8];
206ac4a67b6SAndrew Jones 	int cpu;
207ac4a67b6SAndrew Jones 
208*2e2d471dSAndrew Jones 	if (!gic_init()) {
209ac4a67b6SAndrew Jones 		printf("No supported gic present, skipping tests...\n");
210ac4a67b6SAndrew Jones 		return report_summary();
211ac4a67b6SAndrew Jones 	}
212ac4a67b6SAndrew Jones 
213*2e2d471dSAndrew Jones 	snprintf(pfx, sizeof(pfx), "gicv%d", gic_version());
214ac4a67b6SAndrew Jones 	report_prefix_push(pfx);
215ac4a67b6SAndrew Jones 
216*2e2d471dSAndrew Jones 	switch (gic_version()) {
217*2e2d471dSAndrew Jones 	case 2:
218*2e2d471dSAndrew Jones 		gic = &gicv2;
219*2e2d471dSAndrew Jones 		break;
220*2e2d471dSAndrew Jones 	case 3:
221*2e2d471dSAndrew Jones 		gic = &gicv3;
222*2e2d471dSAndrew Jones 		break;
223*2e2d471dSAndrew Jones 	}
224*2e2d471dSAndrew Jones 
225ac4a67b6SAndrew Jones 	if (argc < 2)
226ac4a67b6SAndrew Jones 		report_abort("no test specified");
227ac4a67b6SAndrew Jones 
228ac4a67b6SAndrew Jones 	if (strcmp(argv[1], "ipi") == 0) {
229ac4a67b6SAndrew Jones 		report_prefix_push(argv[1]);
230ac4a67b6SAndrew Jones 		nr_cpu_check(2);
231ac4a67b6SAndrew Jones 
232ac4a67b6SAndrew Jones 		for_each_present_cpu(cpu) {
233ac4a67b6SAndrew Jones 			if (cpu == 0)
234ac4a67b6SAndrew Jones 				continue;
235ac4a67b6SAndrew Jones 			smp_boot_secondary(cpu, ipi_recv);
236ac4a67b6SAndrew Jones 		}
237ac4a67b6SAndrew Jones 		ipi_enable();
238ac4a67b6SAndrew Jones 		wait_on_ready();
239ac4a67b6SAndrew Jones 		ipi_test_self();
240ac4a67b6SAndrew Jones 		ipi_test_smp();
241ac4a67b6SAndrew Jones 		check_spurious();
242ac4a67b6SAndrew Jones 		report_prefix_pop();
243ac4a67b6SAndrew Jones 	} else {
244ac4a67b6SAndrew Jones 		report_abort("Unknown subtest '%s'", argv[1]);
245ac4a67b6SAndrew Jones 	}
246ac4a67b6SAndrew Jones 
247ac4a67b6SAndrew Jones 	return report_summary();
248ac4a67b6SAndrew Jones }
249