xref: /kvm-unit-tests/arm/gic.c (revision 7af008b1d44e8161ae596ddfcb2eaeb8e63d6904)
1ac4a67b6SAndrew Jones /*
2ac4a67b6SAndrew Jones  * GIC tests
3ac4a67b6SAndrew Jones  *
4ac4a67b6SAndrew Jones  * GICv2
5ac4a67b6SAndrew Jones  *   + test sending/receiving IPIs
678ad7e95SAndre Przywara  *   + MMIO access tests
72e2d471dSAndrew Jones  * GICv3
82e2d471dSAndrew Jones  *   + test sending/receiving IPIs
9ac4a67b6SAndrew Jones  *
10ac4a67b6SAndrew Jones  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
11ac4a67b6SAndrew Jones  *
12ac4a67b6SAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
13ac4a67b6SAndrew Jones  */
14ac4a67b6SAndrew Jones #include <libcflat.h>
15de582149SEric Auger #include <errata.h>
16ac4a67b6SAndrew Jones #include <asm/setup.h>
17ac4a67b6SAndrew Jones #include <asm/processor.h>
18ac4a67b6SAndrew Jones #include <asm/delay.h>
19ac4a67b6SAndrew Jones #include <asm/gic.h>
20ba74b106SEric Auger #include <asm/gic-v3-its.h>
21ac4a67b6SAndrew Jones #include <asm/smp.h>
22ac4a67b6SAndrew Jones #include <asm/barrier.h>
23ac4a67b6SAndrew Jones #include <asm/io.h>
24ac4a67b6SAndrew Jones 
25ca1b7a7bSAndrew Jones #define IPI_SENDER	1
26ca1b7a7bSAndrew Jones #define IPI_IRQ		1
27ca1b7a7bSAndrew Jones 
282e2d471dSAndrew Jones struct gic {
292e2d471dSAndrew Jones 	struct {
302e2d471dSAndrew Jones 		void (*send_self)(void);
312e2d471dSAndrew Jones 		void (*send_broadcast)(void);
322e2d471dSAndrew Jones 	} ipi;
332e2d471dSAndrew Jones };
342e2d471dSAndrew Jones 
352e2d471dSAndrew Jones static struct gic *gic;
36ac4a67b6SAndrew Jones static int acked[NR_CPUS], spurious[NR_CPUS];
37ca1b7a7bSAndrew Jones static int bad_sender[NR_CPUS], bad_irq[NR_CPUS];
38ac4a67b6SAndrew Jones static cpumask_t ready;
39ac4a67b6SAndrew Jones 
40ac4a67b6SAndrew Jones static void nr_cpu_check(int nr)
41ac4a67b6SAndrew Jones {
42ac4a67b6SAndrew Jones 	if (nr_cpus < nr)
43ac4a67b6SAndrew Jones 		report_abort("At least %d cpus required", nr);
44ac4a67b6SAndrew Jones }
45ac4a67b6SAndrew Jones 
46ac4a67b6SAndrew Jones static void wait_on_ready(void)
47ac4a67b6SAndrew Jones {
48ac4a67b6SAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &ready);
49ac4a67b6SAndrew Jones 	while (!cpumask_full(&ready))
50ac4a67b6SAndrew Jones 		cpu_relax();
51ac4a67b6SAndrew Jones }
52ac4a67b6SAndrew Jones 
53ca1b7a7bSAndrew Jones static void stats_reset(void)
54ca1b7a7bSAndrew Jones {
55ca1b7a7bSAndrew Jones 	int i;
56ca1b7a7bSAndrew Jones 
57ca1b7a7bSAndrew Jones 	for (i = 0; i < nr_cpus; ++i) {
58ca1b7a7bSAndrew Jones 		acked[i] = 0;
59ca1b7a7bSAndrew Jones 		bad_sender[i] = -1;
60ca1b7a7bSAndrew Jones 		bad_irq[i] = -1;
61ca1b7a7bSAndrew Jones 	}
62ca1b7a7bSAndrew Jones }
63ca1b7a7bSAndrew Jones 
64*7af008b1SAlexandru Elisei static void wait_for_interrupts(cpumask_t *mask)
65ac4a67b6SAndrew Jones {
66ac4a67b6SAndrew Jones 	int nr_pass, cpu, i;
67ac4a67b6SAndrew Jones 
68ac4a67b6SAndrew Jones 	/* Wait up to 5s for all interrupts to be delivered */
69*7af008b1SAlexandru Elisei 	for (i = 0; i < 50; i++) {
70ac4a67b6SAndrew Jones 		mdelay(100);
71ac4a67b6SAndrew Jones 		nr_pass = 0;
72ac4a67b6SAndrew Jones 		for_each_present_cpu(cpu) {
73*7af008b1SAlexandru Elisei 			/*
74*7af008b1SAlexandru Elisei 			 * A CPU having received more than one interrupts will
75*7af008b1SAlexandru Elisei 			 * show up in check_acked(), and no matter how long we
76*7af008b1SAlexandru Elisei 			 * wait it cannot un-receive it. Consider at least one
77*7af008b1SAlexandru Elisei 			 * interrupt as a pass.
78*7af008b1SAlexandru Elisei 			 */
79ac4a67b6SAndrew Jones 			nr_pass += cpumask_test_cpu(cpu, mask) ?
80*7af008b1SAlexandru Elisei 				acked[cpu] >= 1 : acked[cpu] == 0;
81ca1b7a7bSAndrew Jones 		}
82ca1b7a7bSAndrew Jones 
83ac4a67b6SAndrew Jones 		if (nr_pass == nr_cpus) {
8496edb026SAndre Przywara 			if (i)
85*7af008b1SAlexandru Elisei 				report_info("interrupts took more than %d ms", i * 100);
86*7af008b1SAlexandru Elisei 			/* Wait for unexpected interrupts to fire */
87*7af008b1SAlexandru Elisei 			mdelay(100);
88ac4a67b6SAndrew Jones 			return;
89ac4a67b6SAndrew Jones 		}
90ac4a67b6SAndrew Jones 	}
91ac4a67b6SAndrew Jones 
92*7af008b1SAlexandru Elisei 	report_info("interrupts timed-out (5s)");
93*7af008b1SAlexandru Elisei }
94*7af008b1SAlexandru Elisei 
95*7af008b1SAlexandru Elisei static bool check_acked(cpumask_t *mask)
96*7af008b1SAlexandru Elisei {
97*7af008b1SAlexandru Elisei 	int missing = 0, extra = 0, unexpected = 0;
98*7af008b1SAlexandru Elisei 	bool pass = true;
99*7af008b1SAlexandru Elisei 	int cpu;
100*7af008b1SAlexandru Elisei 
101ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
102ac4a67b6SAndrew Jones 		if (cpumask_test_cpu(cpu, mask)) {
103ac4a67b6SAndrew Jones 			if (!acked[cpu])
104ac4a67b6SAndrew Jones 				++missing;
105ac4a67b6SAndrew Jones 			else if (acked[cpu] > 1)
106ac4a67b6SAndrew Jones 				++extra;
107ac4a67b6SAndrew Jones 		} else {
108ac4a67b6SAndrew Jones 			if (acked[cpu])
109ac4a67b6SAndrew Jones 				++unexpected;
110ac4a67b6SAndrew Jones 		}
111*7af008b1SAlexandru Elisei 		smp_rmb(); /* pairs with smp_wmb in ipi_handler */
112*7af008b1SAlexandru Elisei 
113*7af008b1SAlexandru Elisei 		if (bad_sender[cpu] != -1) {
114*7af008b1SAlexandru Elisei 			report_info("cpu%d received IPI from wrong sender %d",
115*7af008b1SAlexandru Elisei 					cpu, bad_sender[cpu]);
116*7af008b1SAlexandru Elisei 			pass = false;
117ac4a67b6SAndrew Jones 		}
118ac4a67b6SAndrew Jones 
119*7af008b1SAlexandru Elisei 		if (bad_irq[cpu] != -1) {
120*7af008b1SAlexandru Elisei 			report_info("cpu%d received wrong irq %d",
121*7af008b1SAlexandru Elisei 					cpu, bad_irq[cpu]);
122*7af008b1SAlexandru Elisei 			pass = false;
123*7af008b1SAlexandru Elisei 		}
124*7af008b1SAlexandru Elisei 	}
125*7af008b1SAlexandru Elisei 
126*7af008b1SAlexandru Elisei 	if (missing || extra || unexpected) {
127*7af008b1SAlexandru Elisei 		report_info("ACKS: missing=%d extra=%d unexpected=%d",
12896edb026SAndre Przywara 				missing, extra, unexpected);
129*7af008b1SAlexandru Elisei 		pass = false;
130*7af008b1SAlexandru Elisei 	}
131*7af008b1SAlexandru Elisei 
132*7af008b1SAlexandru Elisei 	return pass;
133ac4a67b6SAndrew Jones }
134ac4a67b6SAndrew Jones 
135ac4a67b6SAndrew Jones static void check_spurious(void)
136ac4a67b6SAndrew Jones {
137ac4a67b6SAndrew Jones 	int cpu;
138ac4a67b6SAndrew Jones 
139ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
140ac4a67b6SAndrew Jones 		if (spurious[cpu])
141ac4a67b6SAndrew Jones 			report_info("WARN: cpu%d got %d spurious interrupts",
142ac4a67b6SAndrew Jones 				cpu, spurious[cpu]);
143ac4a67b6SAndrew Jones 	}
144ac4a67b6SAndrew Jones }
145ac4a67b6SAndrew Jones 
14664366016SAlexandru Elisei static void check_ipi_sender(u32 irqstat, int sender)
147ca1b7a7bSAndrew Jones {
148ca1b7a7bSAndrew Jones 	if (gic_version() == 2) {
149ca1b7a7bSAndrew Jones 		int src = (irqstat >> 10) & 7;
150ca1b7a7bSAndrew Jones 
15164366016SAlexandru Elisei 		if (src != sender)
152ca1b7a7bSAndrew Jones 			bad_sender[smp_processor_id()] = src;
153ca1b7a7bSAndrew Jones 	}
154ca1b7a7bSAndrew Jones }
155ca1b7a7bSAndrew Jones 
156ca1b7a7bSAndrew Jones static void check_irqnr(u32 irqnr)
157ca1b7a7bSAndrew Jones {
158ca1b7a7bSAndrew Jones 	if (irqnr != IPI_IRQ)
159ca1b7a7bSAndrew Jones 		bad_irq[smp_processor_id()] = irqnr;
160ca1b7a7bSAndrew Jones }
161ca1b7a7bSAndrew Jones 
162ac4a67b6SAndrew Jones static void ipi_handler(struct pt_regs *regs __unused)
163ac4a67b6SAndrew Jones {
1642e2d471dSAndrew Jones 	u32 irqstat = gic_read_iar();
1652e2d471dSAndrew Jones 	u32 irqnr = gic_iar_irqnr(irqstat);
166ac4a67b6SAndrew Jones 
167ac4a67b6SAndrew Jones 	if (irqnr != GICC_INT_SPURIOUS) {
1682e2d471dSAndrew Jones 		gic_write_eoir(irqstat);
16964366016SAlexandru Elisei 		check_ipi_sender(irqstat, IPI_SENDER);
170ca1b7a7bSAndrew Jones 		check_irqnr(irqnr);
171718d77f1SAlexandru Elisei 		smp_wmb(); /* pairs with smp_rmb in check_acked */
172718d77f1SAlexandru Elisei 		++acked[smp_processor_id()];
173ac4a67b6SAndrew Jones 	} else {
174ac4a67b6SAndrew Jones 		++spurious[smp_processor_id()];
175ac4a67b6SAndrew Jones 	}
176b3029e53SAlexandru Elisei 
177b3029e53SAlexandru Elisei 	/* Wait for writes to acked/spurious to complete */
178b3029e53SAlexandru Elisei 	dsb(ishst);
179ac4a67b6SAndrew Jones }
180ac4a67b6SAndrew Jones 
1810ef02cd6SEric Auger static void setup_irq(irq_handler_fn handler)
1820ef02cd6SEric Auger {
1830ef02cd6SEric Auger 	gic_enable_defaults();
1840ef02cd6SEric Auger #ifdef __arm__
1850ef02cd6SEric Auger 	install_exception_handler(EXCPTN_IRQ, handler);
1860ef02cd6SEric Auger #else
1870ef02cd6SEric Auger 	install_irq_handler(EL1H_IRQ, handler);
1880ef02cd6SEric Auger #endif
1890ef02cd6SEric Auger 	local_irq_enable();
1900ef02cd6SEric Auger }
1910ef02cd6SEric Auger 
1920ef02cd6SEric Auger #if defined(__aarch64__)
1930ef02cd6SEric Auger struct its_event {
1940ef02cd6SEric Auger 	int cpu_id;
1950ef02cd6SEric Auger 	int lpi_id;
1960ef02cd6SEric Auger };
1970ef02cd6SEric Auger 
1980ef02cd6SEric Auger struct its_stats {
1990ef02cd6SEric Auger 	struct its_event expected;
2000ef02cd6SEric Auger 	struct its_event observed;
2010ef02cd6SEric Auger };
2020ef02cd6SEric Auger 
2030ef02cd6SEric Auger static struct its_stats lpi_stats;
2040ef02cd6SEric Auger 
2050ef02cd6SEric Auger static void lpi_handler(struct pt_regs *regs __unused)
2060ef02cd6SEric Auger {
2070ef02cd6SEric Auger 	u32 irqstat = gic_read_iar();
2080ef02cd6SEric Auger 	int irqnr = gic_iar_irqnr(irqstat);
2090ef02cd6SEric Auger 
2100ef02cd6SEric Auger 	gic_write_eoir(irqstat);
2110ef02cd6SEric Auger 	assert(irqnr >= 8192);
2120ef02cd6SEric Auger 	smp_rmb(); /* pairs with wmb in lpi_stats_expect */
2130ef02cd6SEric Auger 	lpi_stats.observed.cpu_id = smp_processor_id();
2140ef02cd6SEric Auger 	lpi_stats.observed.lpi_id = irqnr;
215de582149SEric Auger 	acked[lpi_stats.observed.cpu_id]++;
2160ef02cd6SEric Auger 	smp_wmb(); /* pairs with rmb in check_lpi_stats */
2170ef02cd6SEric Auger }
2180ef02cd6SEric Auger 
2190ef02cd6SEric Auger static void lpi_stats_expect(int exp_cpu_id, int exp_lpi_id)
2200ef02cd6SEric Auger {
2210ef02cd6SEric Auger 	lpi_stats.expected.cpu_id = exp_cpu_id;
2220ef02cd6SEric Auger 	lpi_stats.expected.lpi_id = exp_lpi_id;
2230ef02cd6SEric Auger 	lpi_stats.observed.cpu_id = -1;
2240ef02cd6SEric Auger 	lpi_stats.observed.lpi_id = -1;
2250ef02cd6SEric Auger 	smp_wmb(); /* pairs with rmb in handler */
2260ef02cd6SEric Auger }
2270ef02cd6SEric Auger 
2280ef02cd6SEric Auger static void check_lpi_stats(const char *msg)
2290ef02cd6SEric Auger {
2300ef02cd6SEric Auger 	int i;
2310ef02cd6SEric Auger 
2320ef02cd6SEric Auger 	for (i = 0; i < 50; i++) {
2330ef02cd6SEric Auger 		mdelay(100);
2340ef02cd6SEric Auger 		smp_rmb(); /* pairs with wmb in lpi_handler */
2350ef02cd6SEric Auger 		if (lpi_stats.observed.cpu_id == lpi_stats.expected.cpu_id &&
2360ef02cd6SEric Auger 		    lpi_stats.observed.lpi_id == lpi_stats.expected.lpi_id) {
2370ef02cd6SEric Auger 			report(true, "%s", msg);
2380ef02cd6SEric Auger 			return;
2390ef02cd6SEric Auger 		}
2400ef02cd6SEric Auger 	}
2410ef02cd6SEric Auger 
2420ef02cd6SEric Auger 	if (lpi_stats.observed.cpu_id == -1 && lpi_stats.observed.lpi_id == -1) {
2430ef02cd6SEric Auger 		report_info("No LPI received whereas (cpuid=%d, intid=%d) "
2440ef02cd6SEric Auger 			    "was expected", lpi_stats.expected.cpu_id,
2450ef02cd6SEric Auger 			    lpi_stats.expected.lpi_id);
2460ef02cd6SEric Auger 	} else {
2470ef02cd6SEric Auger 		report_info("Unexpected LPI (cpuid=%d, intid=%d)",
2480ef02cd6SEric Auger 			    lpi_stats.observed.cpu_id,
2490ef02cd6SEric Auger 			    lpi_stats.observed.lpi_id);
2500ef02cd6SEric Auger 	}
2510ef02cd6SEric Auger 	report(false, "%s", msg);
2520ef02cd6SEric Auger }
2530ef02cd6SEric Auger 
2540ef02cd6SEric Auger static void secondary_lpi_test(void)
2550ef02cd6SEric Auger {
2560ef02cd6SEric Auger 	setup_irq(lpi_handler);
2570ef02cd6SEric Auger 	cpumask_set_cpu(smp_processor_id(), &ready);
2580ef02cd6SEric Auger 	while (1)
2590ef02cd6SEric Auger 		wfi();
2600ef02cd6SEric Auger }
261de582149SEric Auger 
262de582149SEric Auger static void check_lpi_hits(int *expected, const char *msg)
263de582149SEric Auger {
264de582149SEric Auger 	bool pass = true;
265de582149SEric Auger 	int i;
266de582149SEric Auger 
267de582149SEric Auger 	for_each_present_cpu(i) {
268de582149SEric Auger 		if (acked[i] != expected[i]) {
269de582149SEric Auger 			report_info("expected %d LPIs on PE #%d, %d observed",
270de582149SEric Auger 				    expected[i], i, acked[i]);
271de582149SEric Auger 			pass = false;
272de582149SEric Auger 			break;
273de582149SEric Auger 		}
274de582149SEric Auger 	}
275de582149SEric Auger 	report(pass, "%s", msg);
276de582149SEric Auger }
2770ef02cd6SEric Auger #endif
2780ef02cd6SEric Auger 
2792e2d471dSAndrew Jones static void gicv2_ipi_send_self(void)
2802e2d471dSAndrew Jones {
28110e3685fSAlexandru Elisei 	/*
28210e3685fSAlexandru Elisei 	 * The wmb() in writel and rmb() when acknowledging the interrupt are
28310e3685fSAlexandru Elisei 	 * sufficient for ensuring that writes that happen in program order
28410e3685fSAlexandru Elisei 	 * before the interrupt are observed in the interrupt handler after
28510e3685fSAlexandru Elisei 	 * acknowledging the interrupt.
28610e3685fSAlexandru Elisei 	 */
287ca1b7a7bSAndrew Jones 	writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
2882e2d471dSAndrew Jones }
2892e2d471dSAndrew Jones 
2902e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void)
2912e2d471dSAndrew Jones {
29210e3685fSAlexandru Elisei 	/* No barriers are needed, same situation as gicv2_ipi_send_self() */
293ca1b7a7bSAndrew Jones 	writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
2942e2d471dSAndrew Jones }
2952e2d471dSAndrew Jones 
2962e2d471dSAndrew Jones static void gicv3_ipi_send_self(void)
2972e2d471dSAndrew Jones {
298ca1b7a7bSAndrew Jones 	gic_ipi_send_single(IPI_IRQ, smp_processor_id());
2992e2d471dSAndrew Jones }
3002e2d471dSAndrew Jones 
3012e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void)
3022e2d471dSAndrew Jones {
3030c03f4b1SAlexandru Elisei 	/*
3040c03f4b1SAlexandru Elisei 	 * Ensure stores to Normal memory are visible to other CPUs before
3050c03f4b1SAlexandru Elisei 	 * sending the IPI
3060c03f4b1SAlexandru Elisei 	 */
3070c03f4b1SAlexandru Elisei 	wmb();
308ca1b7a7bSAndrew Jones 	gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24);
3092e2d471dSAndrew Jones 	isb();
3102e2d471dSAndrew Jones }
3112e2d471dSAndrew Jones 
312ac4a67b6SAndrew Jones static void ipi_test_self(void)
313ac4a67b6SAndrew Jones {
314ac4a67b6SAndrew Jones 	cpumask_t mask;
315ac4a67b6SAndrew Jones 
316ac4a67b6SAndrew Jones 	report_prefix_push("self");
317ca1b7a7bSAndrew Jones 	stats_reset();
318ac4a67b6SAndrew Jones 	cpumask_clear(&mask);
319ca1b7a7bSAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &mask);
3202e2d471dSAndrew Jones 	gic->ipi.send_self();
321*7af008b1SAlexandru Elisei 	wait_for_interrupts(&mask);
322*7af008b1SAlexandru Elisei 	report(check_acked(&mask), "Interrupts received");
323ac4a67b6SAndrew Jones 	report_prefix_pop();
324ac4a67b6SAndrew Jones }
325ac4a67b6SAndrew Jones 
326ac4a67b6SAndrew Jones static void ipi_test_smp(void)
327ac4a67b6SAndrew Jones {
328ac4a67b6SAndrew Jones 	cpumask_t mask;
3292e2d471dSAndrew Jones 	int i;
330ac4a67b6SAndrew Jones 
331ac4a67b6SAndrew Jones 	report_prefix_push("target-list");
332ca1b7a7bSAndrew Jones 	stats_reset();
3332e2d471dSAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
334ca1b7a7bSAndrew Jones 	for (i = smp_processor_id() & 1; i < nr_cpus; i += 2)
3352e2d471dSAndrew Jones 		cpumask_clear_cpu(i, &mask);
336ca1b7a7bSAndrew Jones 	gic_ipi_send_mask(IPI_IRQ, &mask);
337*7af008b1SAlexandru Elisei 	wait_for_interrupts(&mask);
338*7af008b1SAlexandru Elisei 	report(check_acked(&mask), "Interrupts received");
339ac4a67b6SAndrew Jones 	report_prefix_pop();
340ac4a67b6SAndrew Jones 
341ac4a67b6SAndrew Jones 	report_prefix_push("broadcast");
342ca1b7a7bSAndrew Jones 	stats_reset();
343ac4a67b6SAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
344ca1b7a7bSAndrew Jones 	cpumask_clear_cpu(smp_processor_id(), &mask);
3452e2d471dSAndrew Jones 	gic->ipi.send_broadcast();
346*7af008b1SAlexandru Elisei 	wait_for_interrupts(&mask);
347*7af008b1SAlexandru Elisei 	report(check_acked(&mask), "Interrupts received");
348ac4a67b6SAndrew Jones 	report_prefix_pop();
349ac4a67b6SAndrew Jones }
350ac4a67b6SAndrew Jones 
351ca1b7a7bSAndrew Jones static void ipi_send(void)
352ca1b7a7bSAndrew Jones {
35325f66327SEric Auger 	setup_irq(ipi_handler);
354ca1b7a7bSAndrew Jones 	wait_on_ready();
355ca1b7a7bSAndrew Jones 	ipi_test_self();
356ca1b7a7bSAndrew Jones 	ipi_test_smp();
357ca1b7a7bSAndrew Jones 	check_spurious();
358ca1b7a7bSAndrew Jones 	exit(report_summary());
359ca1b7a7bSAndrew Jones }
360ca1b7a7bSAndrew Jones 
361ac4a67b6SAndrew Jones static void ipi_recv(void)
362ac4a67b6SAndrew Jones {
36325f66327SEric Auger 	setup_irq(ipi_handler);
364ac4a67b6SAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &ready);
365ac4a67b6SAndrew Jones 	while (1)
366ac4a67b6SAndrew Jones 		wfi();
367ac4a67b6SAndrew Jones }
368ac4a67b6SAndrew Jones 
36900b34f56SAndrew Jones static void ipi_test(void *data __unused)
370bfd500b4SAndrew Jones {
371bfd500b4SAndrew Jones 	if (smp_processor_id() == IPI_SENDER)
372bfd500b4SAndrew Jones 		ipi_send();
373bfd500b4SAndrew Jones 	else
374bfd500b4SAndrew Jones 		ipi_recv();
375bfd500b4SAndrew Jones }
376bfd500b4SAndrew Jones 
3772e2d471dSAndrew Jones static struct gic gicv2 = {
3782e2d471dSAndrew Jones 	.ipi = {
3792e2d471dSAndrew Jones 		.send_self = gicv2_ipi_send_self,
3802e2d471dSAndrew Jones 		.send_broadcast = gicv2_ipi_send_broadcast,
3812e2d471dSAndrew Jones 	},
3822e2d471dSAndrew Jones };
3832e2d471dSAndrew Jones 
3842e2d471dSAndrew Jones static struct gic gicv3 = {
3852e2d471dSAndrew Jones 	.ipi = {
3862e2d471dSAndrew Jones 		.send_self = gicv3_ipi_send_self,
3872e2d471dSAndrew Jones 		.send_broadcast = gicv3_ipi_send_broadcast,
3882e2d471dSAndrew Jones 	},
3892e2d471dSAndrew Jones };
3902e2d471dSAndrew Jones 
391680beae9SAlexandru Elisei /* Runs on the same CPU as the sender, no need for memory synchronization */
392c152d8bcSChristoffer Dall static void ipi_clear_active_handler(struct pt_regs *regs __unused)
393c152d8bcSChristoffer Dall {
394c152d8bcSChristoffer Dall 	u32 irqstat = gic_read_iar();
395c152d8bcSChristoffer Dall 	u32 irqnr = gic_iar_irqnr(irqstat);
396c152d8bcSChristoffer Dall 
397c152d8bcSChristoffer Dall 	if (irqnr != GICC_INT_SPURIOUS) {
398c152d8bcSChristoffer Dall 		void *base;
399c152d8bcSChristoffer Dall 		u32 val = 1 << IPI_IRQ;
400c152d8bcSChristoffer Dall 
401c152d8bcSChristoffer Dall 		if (gic_version() == 2)
402c152d8bcSChristoffer Dall 			base = gicv2_dist_base();
403c152d8bcSChristoffer Dall 		else
4046d4d7c4bSAndrew Jones 			base = gicv3_sgi_base();
405c152d8bcSChristoffer Dall 
406c152d8bcSChristoffer Dall 		writel(val, base + GICD_ICACTIVER);
407c152d8bcSChristoffer Dall 
40864366016SAlexandru Elisei 		check_ipi_sender(irqstat, smp_processor_id());
409c152d8bcSChristoffer Dall 		check_irqnr(irqnr);
410718d77f1SAlexandru Elisei 		++acked[smp_processor_id()];
411c152d8bcSChristoffer Dall 	} else {
412c152d8bcSChristoffer Dall 		++spurious[smp_processor_id()];
413c152d8bcSChristoffer Dall 	}
414c152d8bcSChristoffer Dall }
415c152d8bcSChristoffer Dall 
416c152d8bcSChristoffer Dall static void run_active_clear_test(void)
417c152d8bcSChristoffer Dall {
418c152d8bcSChristoffer Dall 	report_prefix_push("active");
41925f66327SEric Auger 	setup_irq(ipi_clear_active_handler);
420c152d8bcSChristoffer Dall 	ipi_test_self();
42164366016SAlexandru Elisei 	check_spurious();
422c152d8bcSChristoffer Dall 	report_prefix_pop();
423c152d8bcSChristoffer Dall }
424c152d8bcSChristoffer Dall 
42578ad7e95SAndre Przywara static bool test_ro_pattern_32(void *address, u32 pattern, u32 orig)
42678ad7e95SAndre Przywara {
42778ad7e95SAndre Przywara 	u32 reg;
42878ad7e95SAndre Przywara 
42978ad7e95SAndre Przywara 	writel(pattern, address);
43078ad7e95SAndre Przywara 	reg = readl(address);
43178ad7e95SAndre Przywara 
43278ad7e95SAndre Przywara 	if (reg != orig)
43378ad7e95SAndre Przywara 		writel(orig, address);
43478ad7e95SAndre Przywara 
43578ad7e95SAndre Przywara 	return reg == orig;
43678ad7e95SAndre Przywara }
43778ad7e95SAndre Przywara 
43878ad7e95SAndre Przywara static bool test_readonly_32(void *address, bool razwi)
43978ad7e95SAndre Przywara {
44078ad7e95SAndre Przywara 	u32 orig, pattern;
44178ad7e95SAndre Przywara 
44278ad7e95SAndre Przywara 	orig = readl(address);
44378ad7e95SAndre Przywara 	if (razwi && orig)
44478ad7e95SAndre Przywara 		return false;
44578ad7e95SAndre Przywara 
44678ad7e95SAndre Przywara 	pattern = 0xffffffff;
44778ad7e95SAndre Przywara 	if (orig != pattern) {
44878ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
44978ad7e95SAndre Przywara 			return false;
45078ad7e95SAndre Przywara 	}
45178ad7e95SAndre Przywara 
45278ad7e95SAndre Przywara 	pattern = 0xa5a55a5a;
45378ad7e95SAndre Przywara 	if (orig != pattern) {
45478ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
45578ad7e95SAndre Przywara 			return false;
45678ad7e95SAndre Przywara 	}
45778ad7e95SAndre Przywara 
45878ad7e95SAndre Przywara 	pattern = 0;
45978ad7e95SAndre Przywara 	if (orig != pattern) {
46078ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
46178ad7e95SAndre Przywara 			return false;
46278ad7e95SAndre Przywara 	}
46378ad7e95SAndre Przywara 
46478ad7e95SAndre Przywara 	return true;
46578ad7e95SAndre Przywara }
46678ad7e95SAndre Przywara 
46778ad7e95SAndre Przywara static void test_typer_v2(uint32_t reg)
46878ad7e95SAndre Przywara {
46978ad7e95SAndre Przywara 	int nr_gic_cpus = ((reg >> 5) & 0x7) + 1;
47078ad7e95SAndre Przywara 
4718e0a4f41SAndre Przywara 	report_info("nr_cpus=%d", nr_cpus);
472a299895bSThomas Huth 	report(nr_cpus == nr_gic_cpus, "all CPUs have interrupts");
47378ad7e95SAndre Przywara }
47478ad7e95SAndre Przywara 
475ff31a1c4SAndre Przywara #define BYTE(reg32, byte) (((reg32) >> ((byte) * 8)) & 0xff)
476ff31a1c4SAndre Przywara #define REPLACE_BYTE(reg32, byte, new) (((reg32) & ~(0xff << ((byte) * 8))) |\
477ff31a1c4SAndre Przywara 					((new) << ((byte) * 8)))
478ff31a1c4SAndre Przywara 
479ff31a1c4SAndre Przywara /*
480ff31a1c4SAndre Przywara  * Some registers are byte accessible, do a byte-wide read and write of known
481ff31a1c4SAndre Przywara  * content to check for this.
482ff31a1c4SAndre Przywara  * Apply a @mask to cater for special register properties.
483ff31a1c4SAndre Przywara  * @pattern contains the value already in the register.
484ff31a1c4SAndre Przywara  */
485ff31a1c4SAndre Przywara static void test_byte_access(void *base_addr, u32 pattern, u32 mask)
486ff31a1c4SAndre Przywara {
487ff31a1c4SAndre Przywara 	u32 reg = readb(base_addr + 1);
4888e0a4f41SAndre Przywara 	bool res;
489ff31a1c4SAndre Przywara 
4908e0a4f41SAndre Przywara 	res = (reg == (BYTE(pattern, 1) & (mask >> 8)));
491a299895bSThomas Huth 	report(res, "byte reads successful");
4928e0a4f41SAndre Przywara 	if (!res)
49346ca10f4SAlexandru Elisei 		report_info("byte 1 of 0x%08"PRIx32" => 0x%02"PRIx32, pattern & mask, reg);
494ff31a1c4SAndre Przywara 
495ff31a1c4SAndre Przywara 	pattern = REPLACE_BYTE(pattern, 2, 0x1f);
496ff31a1c4SAndre Przywara 	writeb(BYTE(pattern, 2), base_addr + 2);
497ff31a1c4SAndre Przywara 	reg = readl(base_addr);
4988e0a4f41SAndre Przywara 	res = (reg == (pattern & mask));
499a299895bSThomas Huth 	report(res, "byte writes successful");
5008e0a4f41SAndre Przywara 	if (!res)
50146ca10f4SAlexandru Elisei 		report_info("writing 0x%02"PRIx32" into bytes 2 => 0x%08"PRIx32,
5028e0a4f41SAndre Przywara 			    BYTE(pattern, 2), reg);
503ff31a1c4SAndre Przywara }
504ff31a1c4SAndre Przywara 
505ff31a1c4SAndre Przywara static void test_priorities(int nr_irqs, void *priptr)
506ff31a1c4SAndre Przywara {
507ff31a1c4SAndre Przywara 	u32 orig_prio, reg, pri_bits;
508ff31a1c4SAndre Przywara 	u32 pri_mask, pattern;
509ff31a1c4SAndre Przywara 	void *first_spi = priptr + GIC_FIRST_SPI;
510ff31a1c4SAndre Przywara 
511ff31a1c4SAndre Przywara 	orig_prio = readl(first_spi);
512ff31a1c4SAndre Przywara 	report_prefix_push("IPRIORITYR");
513ff31a1c4SAndre Przywara 
514ff31a1c4SAndre Przywara 	/*
515ff31a1c4SAndre Przywara 	 * Determine implemented number of priority bits by writing all 1's
516ff31a1c4SAndre Przywara 	 * and checking the number of cleared bits in the value read back.
517ff31a1c4SAndre Przywara 	 */
518ff31a1c4SAndre Przywara 	writel(0xffffffff, first_spi);
519ff31a1c4SAndre Przywara 	pri_mask = readl(first_spi);
520ff31a1c4SAndre Przywara 
521ff31a1c4SAndre Przywara 	reg = ~pri_mask;
522a299895bSThomas Huth 	report((((reg >> 16) == (reg & 0xffff)) &&
523a299895bSThomas Huth 	        ((reg & 0xff) == ((reg >> 8) & 0xff))),
524a299895bSThomas Huth 	       "consistent priority masking");
52546ca10f4SAlexandru Elisei 	report_info("priority mask is 0x%08"PRIx32, pri_mask);
526ff31a1c4SAndre Przywara 
527ff31a1c4SAndre Przywara 	reg = reg & 0xff;
528ff31a1c4SAndre Przywara 	for (pri_bits = 8; reg & 1; reg >>= 1, pri_bits--)
529ff31a1c4SAndre Przywara 		;
530a299895bSThomas Huth 	report(pri_bits >= 4, "implements at least 4 priority bits");
53146ca10f4SAlexandru Elisei 	report_info("%"PRIu32" priority bits implemented", pri_bits);
532ff31a1c4SAndre Przywara 
533ff31a1c4SAndre Przywara 	pattern = 0;
534ff31a1c4SAndre Przywara 	writel(pattern, first_spi);
535a299895bSThomas Huth 	report(readl(first_spi) == pattern, "clearing priorities");
536ff31a1c4SAndre Przywara 
537ff31a1c4SAndre Przywara 	/* setting all priorities to their max valus was tested above */
538ff31a1c4SAndre Przywara 
539a299895bSThomas Huth 	report(test_readonly_32(priptr + nr_irqs, true),
540a299895bSThomas Huth 	       "accesses beyond limit RAZ/WI");
541ff31a1c4SAndre Przywara 
542ff31a1c4SAndre Przywara 	writel(pattern, priptr + nr_irqs - 4);
543a299895bSThomas Huth 	report(readl(priptr + nr_irqs - 4) == (pattern & pri_mask),
544a299895bSThomas Huth 	       "accessing last SPIs");
545ff31a1c4SAndre Przywara 
546ff31a1c4SAndre Przywara 	pattern = 0xff7fbf3f;
547ff31a1c4SAndre Przywara 	writel(pattern, first_spi);
548a299895bSThomas Huth 	report(readl(first_spi) == (pattern & pri_mask),
549a299895bSThomas Huth 	       "priorities are preserved");
550ff31a1c4SAndre Przywara 
551ff31a1c4SAndre Przywara 	/* The PRIORITY registers are byte accessible. */
552ff31a1c4SAndre Przywara 	test_byte_access(first_spi, pattern, pri_mask);
553ff31a1c4SAndre Przywara 
554ff31a1c4SAndre Przywara 	report_prefix_pop();
555ff31a1c4SAndre Przywara 	writel(orig_prio, first_spi);
556ff31a1c4SAndre Przywara }
557ff31a1c4SAndre Przywara 
558fe572a5eSAndre Przywara /* GICD_ITARGETSR is only used by GICv2. */
559fe572a5eSAndre Przywara static void test_targets(int nr_irqs)
560fe572a5eSAndre Przywara {
561fe572a5eSAndre Przywara 	void *targetsptr = gicv2_dist_base() + GICD_ITARGETSR;
562fe572a5eSAndre Przywara 	u32 orig_targets;
563fe572a5eSAndre Przywara 	u32 cpu_mask;
564fe572a5eSAndre Przywara 	u32 pattern, reg;
565fe572a5eSAndre Przywara 
566fe572a5eSAndre Przywara 	orig_targets = readl(targetsptr + GIC_FIRST_SPI);
567fe572a5eSAndre Przywara 	report_prefix_push("ITARGETSR");
568fe572a5eSAndre Przywara 
569fe572a5eSAndre Przywara 	cpu_mask = (1 << nr_cpus) - 1;
570fe572a5eSAndre Przywara 	cpu_mask |= cpu_mask << 8;
571fe572a5eSAndre Przywara 	cpu_mask |= cpu_mask << 16;
572fe572a5eSAndre Przywara 
573fe572a5eSAndre Przywara 	/* Check that bits for non implemented CPUs are RAZ/WI. */
574fe572a5eSAndre Przywara 	if (nr_cpus < 8) {
575fe572a5eSAndre Przywara 		writel(0xffffffff, targetsptr + GIC_FIRST_SPI);
576a299895bSThomas Huth 		report(!(readl(targetsptr + GIC_FIRST_SPI) & ~cpu_mask),
577a299895bSThomas Huth 		       "bits for non-existent CPUs masked");
5788e0a4f41SAndre Przywara 		report_info("%d non-existent CPUs", 8 - nr_cpus);
579fe572a5eSAndre Przywara 	} else {
580fe572a5eSAndre Przywara 		report_skip("CPU masking (all CPUs implemented)");
581fe572a5eSAndre Przywara 	}
582fe572a5eSAndre Przywara 
583a299895bSThomas Huth 	report(test_readonly_32(targetsptr + nr_irqs, true),
584a299895bSThomas Huth 	       "accesses beyond limit RAZ/WI");
585fe572a5eSAndre Przywara 
586fe572a5eSAndre Przywara 	pattern = 0x0103020f;
587fe572a5eSAndre Przywara 	writel(pattern, targetsptr + GIC_FIRST_SPI);
588fe572a5eSAndre Przywara 	reg = readl(targetsptr + GIC_FIRST_SPI);
589a299895bSThomas Huth 	report(reg == (pattern & cpu_mask), "register content preserved");
5908e0a4f41SAndre Przywara 	if (reg != (pattern & cpu_mask))
59146ca10f4SAlexandru Elisei 		report_info("writing %08"PRIx32" reads back as %08"PRIx32,
5928e0a4f41SAndre Przywara 			    pattern & cpu_mask, reg);
593fe572a5eSAndre Przywara 
594fe572a5eSAndre Przywara 	/* The TARGETS registers are byte accessible. */
595fe572a5eSAndre Przywara 	test_byte_access(targetsptr + GIC_FIRST_SPI, pattern, cpu_mask);
596fe572a5eSAndre Przywara 
597fe572a5eSAndre Przywara 	writel(orig_targets, targetsptr + GIC_FIRST_SPI);
598da5b8576SAndre Przywara 
599da5b8576SAndre Przywara 	report_prefix_pop();
600fe572a5eSAndre Przywara }
601fe572a5eSAndre Przywara 
60278ad7e95SAndre Przywara static void gic_test_mmio(void)
60378ad7e95SAndre Przywara {
60478ad7e95SAndre Przywara 	u32 reg;
60578ad7e95SAndre Przywara 	int nr_irqs;
60678ad7e95SAndre Przywara 	void *gic_dist_base, *idreg;
60778ad7e95SAndre Przywara 
60878ad7e95SAndre Przywara 	switch(gic_version()) {
60978ad7e95SAndre Przywara 	case 0x2:
61078ad7e95SAndre Przywara 		gic_dist_base = gicv2_dist_base();
61178ad7e95SAndre Przywara 		idreg = gic_dist_base + GICD_ICPIDR2;
61278ad7e95SAndre Przywara 		break;
61378ad7e95SAndre Przywara 	case 0x3:
61478ad7e95SAndre Przywara 		report_abort("GICv3 MMIO tests NYI");
61578ad7e95SAndre Przywara 	default:
61678ad7e95SAndre Przywara 		report_abort("GIC version %d not supported", gic_version());
61778ad7e95SAndre Przywara 	}
61878ad7e95SAndre Przywara 
61978ad7e95SAndre Przywara 	reg = readl(gic_dist_base + GICD_TYPER);
62078ad7e95SAndre Przywara 	nr_irqs = GICD_TYPER_IRQS(reg);
62178ad7e95SAndre Przywara 	report_info("number of implemented SPIs: %d", nr_irqs - GIC_FIRST_SPI);
62278ad7e95SAndre Przywara 
62378ad7e95SAndre Przywara 	test_typer_v2(reg);
62478ad7e95SAndre Przywara 
62546ca10f4SAlexandru Elisei 	report_info("IIDR: 0x%08"PRIx32, readl(gic_dist_base + GICD_IIDR));
62678ad7e95SAndre Przywara 
627a299895bSThomas Huth 	report(test_readonly_32(gic_dist_base + GICD_TYPER, false),
628a299895bSThomas Huth                "GICD_TYPER is read-only");
629a299895bSThomas Huth 	report(test_readonly_32(gic_dist_base + GICD_IIDR, false),
630a299895bSThomas Huth                "GICD_IIDR is read-only");
63178ad7e95SAndre Przywara 
63278ad7e95SAndre Przywara 	reg = readl(idreg);
633a299895bSThomas Huth 	report(test_readonly_32(idreg, false), "ICPIDR2 is read-only");
63446ca10f4SAlexandru Elisei 	report_info("value of ICPIDR2: 0x%08"PRIx32, reg);
635ff31a1c4SAndre Przywara 
636ff31a1c4SAndre Przywara 	test_priorities(nr_irqs, gic_dist_base + GICD_IPRIORITYR);
637fe572a5eSAndre Przywara 
638fe572a5eSAndre Przywara 	if (gic_version() == 2)
639fe572a5eSAndre Przywara 		test_targets(nr_irqs);
64078ad7e95SAndre Przywara }
64178ad7e95SAndre Przywara 
642ba74b106SEric Auger #if defined(__arm__)
643ba74b106SEric Auger 
644ba74b106SEric Auger static void test_its_introspection(void) {}
6450ef02cd6SEric Auger static void test_its_trigger(void) {}
64664260a5fSEric Auger static void test_its_migration(void) {}
647de582149SEric Auger static void test_its_pending_migration(void) {}
648de582149SEric Auger static void test_migrate_unmapped_collection(void) {}
649ba74b106SEric Auger 
650ba74b106SEric Auger #else /* __aarch64__ */
651ba74b106SEric Auger 
652ba74b106SEric Auger static void test_its_introspection(void)
653ba74b106SEric Auger {
654ba74b106SEric Auger 	struct its_baser *dev_baser = &its_data.device_baser;
655ba74b106SEric Auger 	struct its_baser *coll_baser = &its_data.coll_baser;
656ba74b106SEric Auger 	struct its_typer *typer = &its_data.typer;
657ba74b106SEric Auger 
658ba74b106SEric Auger 	if (!gicv3_its_base()) {
659ba74b106SEric Auger 		report_skip("No ITS, skip ...");
660ba74b106SEric Auger 		return;
661ba74b106SEric Auger 	}
662ba74b106SEric Auger 
663ba74b106SEric Auger 	/* IIDR */
664ba74b106SEric Auger 	report(test_readonly_32(gicv3_its_base() + GITS_IIDR, false),
665ba74b106SEric Auger 	       "GITS_IIDR is read-only"),
666ba74b106SEric Auger 
667ba74b106SEric Auger 	/* TYPER */
668ba74b106SEric Auger 	report(test_readonly_32(gicv3_its_base() + GITS_TYPER, false),
669ba74b106SEric Auger 	       "GITS_TYPER is read-only");
670ba74b106SEric Auger 
671ba74b106SEric Auger 	report(typer->phys_lpi, "ITS supports physical LPIs");
672ba74b106SEric Auger 	report_info("vLPI support: %s", typer->virt_lpi ? "yes" : "no");
673ba74b106SEric Auger 	report_info("ITT entry size = 0x%x", typer->ite_size);
674ba74b106SEric Auger 	report_info("Bit Count: EventID=%d DeviceId=%d CollId=%d",
675ba74b106SEric Auger 		    typer->eventid_bits, typer->deviceid_bits,
676ba74b106SEric Auger 		    typer->collid_bits);
677ba74b106SEric Auger 	report(typer->eventid_bits && typer->deviceid_bits &&
678ba74b106SEric Auger 	       typer->collid_bits, "ID spaces");
679ba74b106SEric Auger 	report_info("Target address format %s",
680ba74b106SEric Auger 			typer->pta ? "Redist base address" : "PE #");
681ba74b106SEric Auger 
682ba74b106SEric Auger 	report(dev_baser && coll_baser, "detect device and collection BASER");
683ba74b106SEric Auger 	report_info("device table entry_size = 0x%x", dev_baser->esz);
684ba74b106SEric Auger 	report_info("collection table entry_size = 0x%x", coll_baser->esz);
685ba74b106SEric Auger }
686ba74b106SEric Auger 
6870ef02cd6SEric Auger static int its_prerequisites(int nb_cpus)
6880ef02cd6SEric Auger {
6890ef02cd6SEric Auger 	int cpu;
6900ef02cd6SEric Auger 
6910ef02cd6SEric Auger 	if (!gicv3_its_base()) {
6920ef02cd6SEric Auger 		report_skip("No ITS, skip ...");
6930ef02cd6SEric Auger 		return -1;
6940ef02cd6SEric Auger 	}
6950ef02cd6SEric Auger 
6960ef02cd6SEric Auger 	if (nr_cpus < nb_cpus) {
6970ef02cd6SEric Auger 		report_skip("Test requires at least %d vcpus", nb_cpus);
6980ef02cd6SEric Auger 		return -1;
6990ef02cd6SEric Auger 	}
7000ef02cd6SEric Auger 
7010ef02cd6SEric Auger 	stats_reset();
7020ef02cd6SEric Auger 
7030ef02cd6SEric Auger 	setup_irq(lpi_handler);
7040ef02cd6SEric Auger 
7050ef02cd6SEric Auger 	for_each_present_cpu(cpu) {
7060ef02cd6SEric Auger 		if (cpu == 0)
7070ef02cd6SEric Auger 			continue;
7080ef02cd6SEric Auger 		smp_boot_secondary(cpu, secondary_lpi_test);
7090ef02cd6SEric Auger 	}
7100ef02cd6SEric Auger 	wait_on_ready();
7110ef02cd6SEric Auger 
7120ef02cd6SEric Auger 	its_enable_defaults();
7130ef02cd6SEric Auger 
7140ef02cd6SEric Auger 	return 0;
7150ef02cd6SEric Auger }
7160ef02cd6SEric Auger 
71764260a5fSEric Auger /*
71864260a5fSEric Auger  * Setup the configuration for those mappings:
71964260a5fSEric Auger  * dev_id=2 event=20 -> vcpu 3, intid=8195
72064260a5fSEric Auger  * dev_id=7 event=255 -> vcpu 2, intid=8196
72164260a5fSEric Auger  * LPIs ready to hit
72264260a5fSEric Auger  */
72364260a5fSEric Auger static int its_setup1(void)
7240ef02cd6SEric Auger {
7250ef02cd6SEric Auger 	struct its_collection *col3, *col2;
7260ef02cd6SEric Auger 	struct its_device *dev2, *dev7;
7270ef02cd6SEric Auger 
7280ef02cd6SEric Auger 	if (its_prerequisites(4))
72964260a5fSEric Auger 		return -1;
7300ef02cd6SEric Auger 
7310ef02cd6SEric Auger 	dev2 = its_create_device(2 /* dev id */, 8 /* nb_ites */);
7320ef02cd6SEric Auger 	dev7 = its_create_device(7 /* dev id */, 8 /* nb_ites */);
7330ef02cd6SEric Auger 
7340ef02cd6SEric Auger 	col3 = its_create_collection(3 /* col id */, 3/* target PE */);
7350ef02cd6SEric Auger 	col2 = its_create_collection(2 /* col id */, 2/* target PE */);
7360ef02cd6SEric Auger 
7370ef02cd6SEric Auger 	gicv3_lpi_set_config(8195, LPI_PROP_DEFAULT);
7380ef02cd6SEric Auger 	gicv3_lpi_set_config(8196, LPI_PROP_DEFAULT);
7390ef02cd6SEric Auger 
7400ef02cd6SEric Auger 	/*
7410ef02cd6SEric Auger 	 * dev=2, eventid=20  -> lpi= 8195, col=3
7420ef02cd6SEric Auger 	 * dev=7, eventid=255 -> lpi= 8196, col=2
7430ef02cd6SEric Auger 	 */
7440ef02cd6SEric Auger 	its_send_mapd(dev2, true);
7450ef02cd6SEric Auger 	its_send_mapd(dev7, true);
7460ef02cd6SEric Auger 
7470ef02cd6SEric Auger 	its_send_mapc(col3, true);
7480ef02cd6SEric Auger 	its_send_mapc(col2, true);
7490ef02cd6SEric Auger 
7500ef02cd6SEric Auger 	its_send_invall(col2);
7510ef02cd6SEric Auger 	its_send_invall(col3);
7520ef02cd6SEric Auger 
7530ef02cd6SEric Auger 	its_send_mapti(dev2, 8195 /* lpi id */, 20 /* event id */, col3);
7540ef02cd6SEric Auger 	its_send_mapti(dev7, 8196 /* lpi id */, 255 /* event id */, col2);
75564260a5fSEric Auger 	return 0;
75664260a5fSEric Auger }
75764260a5fSEric Auger 
75864260a5fSEric Auger static void test_its_trigger(void)
75964260a5fSEric Auger {
76064260a5fSEric Auger 	struct its_collection *col3;
76164260a5fSEric Auger 	struct its_device *dev2, *dev7;
76264260a5fSEric Auger 
76364260a5fSEric Auger 	if (its_setup1())
76464260a5fSEric Auger 		return;
76564260a5fSEric Auger 
76664260a5fSEric Auger 	col3 = its_get_collection(3);
76764260a5fSEric Auger 	dev2 = its_get_device(2);
76864260a5fSEric Auger 	dev7 = its_get_device(7);
76964260a5fSEric Auger 
77064260a5fSEric Auger 	report_prefix_push("int");
7710ef02cd6SEric Auger 
7720ef02cd6SEric Auger 	lpi_stats_expect(3, 8195);
7730ef02cd6SEric Auger 	its_send_int(dev2, 20);
7740ef02cd6SEric Auger 	check_lpi_stats("dev=2, eventid=20  -> lpi= 8195, col=3");
7750ef02cd6SEric Auger 
7760ef02cd6SEric Auger 	lpi_stats_expect(2, 8196);
7770ef02cd6SEric Auger 	its_send_int(dev7, 255);
7780ef02cd6SEric Auger 	check_lpi_stats("dev=7, eventid=255 -> lpi= 8196, col=2");
7790ef02cd6SEric Auger 
7800ef02cd6SEric Auger 	report_prefix_pop();
7810ef02cd6SEric Auger 
7820ef02cd6SEric Auger 	report_prefix_push("inv/invall");
7830ef02cd6SEric Auger 
7840ef02cd6SEric Auger 	/*
7850ef02cd6SEric Auger 	 * disable 8195, check dev2/eventid=20 does not trigger the
7860ef02cd6SEric Auger 	 * corresponding LPI
7870ef02cd6SEric Auger 	 */
7880ef02cd6SEric Auger 	gicv3_lpi_set_config(8195, LPI_PROP_DEFAULT & ~LPI_PROP_ENABLED);
7890ef02cd6SEric Auger 	its_send_inv(dev2, 20);
7900ef02cd6SEric Auger 
7910ef02cd6SEric Auger 	lpi_stats_expect(-1, -1);
7920ef02cd6SEric Auger 	its_send_int(dev2, 20);
7930ef02cd6SEric Auger 	check_lpi_stats("dev2/eventid=20 does not trigger any LPI");
7940ef02cd6SEric Auger 
7950ef02cd6SEric Auger 	/*
7960ef02cd6SEric Auger 	 * re-enable the LPI but willingly do not call invall
7970ef02cd6SEric Auger 	 * so the change in config is not taken into account.
7980ef02cd6SEric Auger 	 * The LPI should not hit
7990ef02cd6SEric Auger 	 */
8000ef02cd6SEric Auger 	gicv3_lpi_set_config(8195, LPI_PROP_DEFAULT);
8010ef02cd6SEric Auger 	lpi_stats_expect(-1, -1);
8020ef02cd6SEric Auger 	its_send_int(dev2, 20);
8030ef02cd6SEric Auger 	check_lpi_stats("dev2/eventid=20 still does not trigger any LPI");
8040ef02cd6SEric Auger 
8050ef02cd6SEric Auger 	/* Now call the invall and check the LPI hits */
8060ef02cd6SEric Auger 	its_send_invall(col3);
8070ef02cd6SEric Auger 	lpi_stats_expect(3, 8195);
8080ef02cd6SEric Auger 	its_send_int(dev2, 20);
8090ef02cd6SEric Auger 	check_lpi_stats("dev2/eventid=20 now triggers an LPI");
8100ef02cd6SEric Auger 
8110ef02cd6SEric Auger 	report_prefix_pop();
8120ef02cd6SEric Auger 
8130ef02cd6SEric Auger 	report_prefix_push("mapd valid=false");
8140ef02cd6SEric Auger 	/*
8150ef02cd6SEric Auger 	 * Unmap device 2 and check the eventid 20 formerly
8160ef02cd6SEric Auger 	 * attached to it does not hit anymore
8170ef02cd6SEric Auger 	 */
8180ef02cd6SEric Auger 
8190ef02cd6SEric Auger 	its_send_mapd(dev2, false);
8200ef02cd6SEric Auger 	lpi_stats_expect(-1, -1);
8210ef02cd6SEric Auger 	its_send_int(dev2, 20);
8220ef02cd6SEric Auger 	check_lpi_stats("no LPI after device unmap");
8230ef02cd6SEric Auger 	report_prefix_pop();
8240ef02cd6SEric Auger }
82564260a5fSEric Auger 
82664260a5fSEric Auger static void test_its_migration(void)
82764260a5fSEric Auger {
82864260a5fSEric Auger 	struct its_device *dev2, *dev7;
82964260a5fSEric Auger 	bool test_skipped = false;
83064260a5fSEric Auger 
83164260a5fSEric Auger 	if (its_setup1()) {
83264260a5fSEric Auger 		test_skipped = true;
83364260a5fSEric Auger 		goto do_migrate;
83464260a5fSEric Auger 	}
83564260a5fSEric Auger 
83664260a5fSEric Auger 	dev2 = its_get_device(2);
83764260a5fSEric Auger 	dev7 = its_get_device(7);
83864260a5fSEric Auger 
83964260a5fSEric Auger do_migrate:
84064260a5fSEric Auger 	puts("Now migrate the VM, then press a key to continue...\n");
84164260a5fSEric Auger 	(void)getchar();
84264260a5fSEric Auger 	report_info("Migration complete");
84364260a5fSEric Auger 	if (test_skipped)
84464260a5fSEric Auger 		return;
84564260a5fSEric Auger 
84664260a5fSEric Auger 	lpi_stats_expect(3, 8195);
84764260a5fSEric Auger 	its_send_int(dev2, 20);
84864260a5fSEric Auger 	check_lpi_stats("dev2/eventid=20 triggers LPI 8195 on PE #3 after migration");
84964260a5fSEric Auger 
85064260a5fSEric Auger 	lpi_stats_expect(2, 8196);
85164260a5fSEric Auger 	its_send_int(dev7, 255);
85264260a5fSEric Auger 	check_lpi_stats("dev7/eventid=255 triggers LPI 8196 on PE #2 after migration");
85364260a5fSEric Auger }
854de582149SEric Auger 
855de582149SEric Auger #define ERRATA_UNMAPPED_COLLECTIONS "ERRATA_8c58be34494b"
856de582149SEric Auger 
857de582149SEric Auger static void test_migrate_unmapped_collection(void)
858de582149SEric Auger {
859de582149SEric Auger 	struct its_collection *col = NULL;
860de582149SEric Auger 	struct its_device *dev2 = NULL, *dev7 = NULL;
861de582149SEric Auger 	bool test_skipped = false;
862de582149SEric Auger 	int pe0 = 0;
863de582149SEric Auger 	u8 config;
864de582149SEric Auger 
865de582149SEric Auger 	if (its_setup1()) {
866de582149SEric Auger 		test_skipped = true;
867de582149SEric Auger 		goto do_migrate;
868de582149SEric Auger 	}
869de582149SEric Auger 
870de582149SEric Auger 	if (!errata(ERRATA_UNMAPPED_COLLECTIONS)) {
871de582149SEric Auger 		report_skip("Skipping test, as this test hangs without the fix. "
872de582149SEric Auger 			    "Set %s=y to enable.", ERRATA_UNMAPPED_COLLECTIONS);
873de582149SEric Auger 		test_skipped = true;
874de582149SEric Auger 		goto do_migrate;
875de582149SEric Auger 	}
876de582149SEric Auger 
877de582149SEric Auger 	col = its_create_collection(pe0, pe0);
878de582149SEric Auger 	dev2 = its_get_device(2);
879de582149SEric Auger 	dev7 = its_get_device(7);
880de582149SEric Auger 
881de582149SEric Auger 	/* MAPTI with the collection unmapped */
882de582149SEric Auger 	its_send_mapti(dev2, 8192, 0, col);
883de582149SEric Auger 	gicv3_lpi_set_config(8192, LPI_PROP_DEFAULT);
884de582149SEric Auger 
885de582149SEric Auger do_migrate:
886de582149SEric Auger 	puts("Now migrate the VM, then press a key to continue...\n");
887de582149SEric Auger 	(void)getchar();
888de582149SEric Auger 	report_info("Migration complete");
889de582149SEric Auger 	if (test_skipped)
890de582149SEric Auger 		return;
891de582149SEric Auger 
892de582149SEric Auger 	/* on the destination, map the collection */
893de582149SEric Auger 	its_send_mapc(col, true);
894de582149SEric Auger 	its_send_invall(col);
895de582149SEric Auger 
896de582149SEric Auger 	lpi_stats_expect(2, 8196);
897de582149SEric Auger 	its_send_int(dev7, 255);
898de582149SEric Auger 	check_lpi_stats("dev7/eventid= 255 triggered LPI 8196 on PE #2");
899de582149SEric Auger 
900de582149SEric Auger 	config = gicv3_lpi_get_config(8192);
901de582149SEric Auger 	report(config == LPI_PROP_DEFAULT,
902de582149SEric Auger 	       "Config of LPI 8192 was properly migrated");
903de582149SEric Auger 
904de582149SEric Auger 	lpi_stats_expect(pe0, 8192);
905de582149SEric Auger 	its_send_int(dev2, 0);
906de582149SEric Auger 	check_lpi_stats("dev2/eventid = 0 triggered LPI 8192 on PE0");
907de582149SEric Auger }
908de582149SEric Auger 
909de582149SEric Auger static void test_its_pending_migration(void)
910de582149SEric Auger {
911de582149SEric Auger 	struct its_device *dev;
912de582149SEric Auger 	struct its_collection *collection[2];
913de582149SEric Auger 	int *expected = calloc(nr_cpus, sizeof(int));
914de582149SEric Auger 	int pe0 = nr_cpus - 1, pe1 = nr_cpus - 2;
915de582149SEric Auger 	bool test_skipped = false;
916de582149SEric Auger 	u64 pendbaser;
917de582149SEric Auger 	void *ptr;
918de582149SEric Auger 	int i;
919de582149SEric Auger 
920de582149SEric Auger 	if (its_prerequisites(4)) {
921de582149SEric Auger 		test_skipped = true;
922de582149SEric Auger 		goto do_migrate;
923de582149SEric Auger 	}
924de582149SEric Auger 
925de582149SEric Auger 	dev = its_create_device(2 /* dev id */, 8 /* nb_ites */);
926de582149SEric Auger 	its_send_mapd(dev, true);
927de582149SEric Auger 
928de582149SEric Auger 	collection[0] = its_create_collection(pe0, pe0);
929de582149SEric Auger 	collection[1] = its_create_collection(pe1, pe1);
930de582149SEric Auger 	its_send_mapc(collection[0], true);
931de582149SEric Auger 	its_send_mapc(collection[1], true);
932de582149SEric Auger 
933de582149SEric Auger 	/* disable lpi at redist level */
934de582149SEric Auger 	gicv3_lpi_rdist_disable(pe0);
935de582149SEric Auger 	gicv3_lpi_rdist_disable(pe1);
936de582149SEric Auger 
937de582149SEric Auger 	/* lpis are interleaved inbetween the 2 PEs */
938de582149SEric Auger 	for (i = 0; i < 256; i++) {
939de582149SEric Auger 		struct its_collection *col = i % 2 ? collection[0] :
940de582149SEric Auger 						     collection[1];
941de582149SEric Auger 		int vcpu = col->target_address >> 16;
942de582149SEric Auger 
943de582149SEric Auger 		its_send_mapti(dev, LPI(i), i, col);
944de582149SEric Auger 		gicv3_lpi_set_config(LPI(i), LPI_PROP_DEFAULT);
945de582149SEric Auger 		gicv3_lpi_set_clr_pending(vcpu, LPI(i), true);
946de582149SEric Auger 	}
947de582149SEric Auger 	its_send_invall(collection[0]);
948de582149SEric Auger 	its_send_invall(collection[1]);
949de582149SEric Auger 
950de582149SEric Auger 	/* Clear the PTZ bit on each pendbaser */
951de582149SEric Auger 
952de582149SEric Auger 	expected[pe0] = 128;
953de582149SEric Auger 	expected[pe1] = 128;
954de582149SEric Auger 
955de582149SEric Auger 	ptr = gicv3_data.redist_base[pe0] + GICR_PENDBASER;
956de582149SEric Auger 	pendbaser = readq(ptr);
957de582149SEric Auger 	writeq(pendbaser & ~GICR_PENDBASER_PTZ, ptr);
958de582149SEric Auger 
959de582149SEric Auger 	ptr = gicv3_data.redist_base[pe1] + GICR_PENDBASER;
960de582149SEric Auger 	pendbaser = readq(ptr);
961de582149SEric Auger 	writeq(pendbaser & ~GICR_PENDBASER_PTZ, ptr);
962de582149SEric Auger 
963de582149SEric Auger 	gicv3_lpi_rdist_enable(pe0);
964de582149SEric Auger 	gicv3_lpi_rdist_enable(pe1);
965de582149SEric Auger 
966de582149SEric Auger do_migrate:
967de582149SEric Auger 	puts("Now migrate the VM, then press a key to continue...\n");
968de582149SEric Auger 	(void)getchar();
969de582149SEric Auger 	report_info("Migration complete");
970de582149SEric Auger 	if (test_skipped)
971de582149SEric Auger 		return;
972de582149SEric Auger 
973de582149SEric Auger 	/* let's wait for the 256 LPIs to be handled */
974de582149SEric Auger 	mdelay(1000);
975de582149SEric Auger 
976de582149SEric Auger 	check_lpi_hits(expected, "128 LPIs on both PE0 and PE1 after migration");
977de582149SEric Auger }
978ba74b106SEric Auger #endif
979ba74b106SEric Auger 
980ac4a67b6SAndrew Jones int main(int argc, char **argv)
981ac4a67b6SAndrew Jones {
9822e2d471dSAndrew Jones 	if (!gic_init()) {
983ac4a67b6SAndrew Jones 		printf("No supported gic present, skipping tests...\n");
984ac4a67b6SAndrew Jones 		return report_summary();
985ac4a67b6SAndrew Jones 	}
986ac4a67b6SAndrew Jones 
9872b19b829SAndrew Jones 	report_prefix_pushf("gicv%d", gic_version());
988ac4a67b6SAndrew Jones 
9892e2d471dSAndrew Jones 	switch (gic_version()) {
9902e2d471dSAndrew Jones 	case 2:
9912e2d471dSAndrew Jones 		gic = &gicv2;
9922e2d471dSAndrew Jones 		break;
9932e2d471dSAndrew Jones 	case 3:
9942e2d471dSAndrew Jones 		gic = &gicv3;
9952e2d471dSAndrew Jones 		break;
9962e2d471dSAndrew Jones 	}
9972e2d471dSAndrew Jones 
998ac4a67b6SAndrew Jones 	if (argc < 2)
999ac4a67b6SAndrew Jones 		report_abort("no test specified");
1000ac4a67b6SAndrew Jones 
1001ac4a67b6SAndrew Jones 	if (strcmp(argv[1], "ipi") == 0) {
1002ac4a67b6SAndrew Jones 		report_prefix_push(argv[1]);
1003ac4a67b6SAndrew Jones 		nr_cpu_check(2);
100400b34f56SAndrew Jones 		on_cpus(ipi_test, NULL);
1005c152d8bcSChristoffer Dall 	} else if (strcmp(argv[1], "active") == 0) {
1006c152d8bcSChristoffer Dall 		run_active_clear_test();
100778ad7e95SAndre Przywara 	} else if (strcmp(argv[1], "mmio") == 0) {
100878ad7e95SAndre Przywara 		report_prefix_push(argv[1]);
100978ad7e95SAndre Przywara 		gic_test_mmio();
101078ad7e95SAndre Przywara 		report_prefix_pop();
10110ef02cd6SEric Auger 	} else if (!strcmp(argv[1], "its-trigger")) {
10120ef02cd6SEric Auger 		report_prefix_push(argv[1]);
10130ef02cd6SEric Auger 		test_its_trigger();
10140ef02cd6SEric Auger 		report_prefix_pop();
101564260a5fSEric Auger 	} else if (!strcmp(argv[1], "its-migration")) {
101664260a5fSEric Auger 		report_prefix_push(argv[1]);
101764260a5fSEric Auger 		test_its_migration();
101864260a5fSEric Auger 		report_prefix_pop();
1019de582149SEric Auger 	} else if (!strcmp(argv[1], "its-pending-migration")) {
1020de582149SEric Auger 		report_prefix_push(argv[1]);
1021de582149SEric Auger 		test_its_pending_migration();
1022de582149SEric Auger 		report_prefix_pop();
1023de582149SEric Auger 	} else if (!strcmp(argv[1], "its-migrate-unmapped-collection")) {
1024de582149SEric Auger 		report_prefix_push(argv[1]);
1025de582149SEric Auger 		test_migrate_unmapped_collection();
1026de582149SEric Auger 		report_prefix_pop();
1027ba74b106SEric Auger 	} else if (strcmp(argv[1], "its-introspection") == 0) {
1028ba74b106SEric Auger 		report_prefix_push(argv[1]);
1029ba74b106SEric Auger 		test_its_introspection();
1030ba74b106SEric Auger 		report_prefix_pop();
1031ac4a67b6SAndrew Jones 	} else {
1032ac4a67b6SAndrew Jones 		report_abort("Unknown subtest '%s'", argv[1]);
1033ac4a67b6SAndrew Jones 	}
1034ac4a67b6SAndrew Jones 
1035ac4a67b6SAndrew Jones 	return report_summary();
1036ac4a67b6SAndrew Jones }
1037