xref: /kvm-unit-tests/arm/gic.c (revision 10396befca9f50e7442cfda33bd0b8c3c0a0dcea)
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];
370e0a39dfSAlexandru Elisei static int irq_sender[NR_CPUS], irq_number[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;
590e0a39dfSAlexandru Elisei 		irq_sender[i] = -1;
600e0a39dfSAlexandru Elisei 		irq_number[i] = -1;
61ca1b7a7bSAndrew Jones 	}
62ca1b7a7bSAndrew Jones }
63ca1b7a7bSAndrew Jones 
647af008b1SAlexandru 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 */
697af008b1SAlexandru Elisei 	for (i = 0; i < 50; i++) {
70ac4a67b6SAndrew Jones 		mdelay(100);
71ac4a67b6SAndrew Jones 		nr_pass = 0;
72ac4a67b6SAndrew Jones 		for_each_present_cpu(cpu) {
737af008b1SAlexandru Elisei 			/*
747af008b1SAlexandru Elisei 			 * A CPU having received more than one interrupts will
757af008b1SAlexandru Elisei 			 * show up in check_acked(), and no matter how long we
767af008b1SAlexandru Elisei 			 * wait it cannot un-receive it. Consider at least one
777af008b1SAlexandru Elisei 			 * interrupt as a pass.
787af008b1SAlexandru Elisei 			 */
79ac4a67b6SAndrew Jones 			nr_pass += cpumask_test_cpu(cpu, mask) ?
807af008b1SAlexandru Elisei 				acked[cpu] >= 1 : acked[cpu] == 0;
81ca1b7a7bSAndrew Jones 		}
82ca1b7a7bSAndrew Jones 
83ac4a67b6SAndrew Jones 		if (nr_pass == nr_cpus) {
8496edb026SAndre Przywara 			if (i)
857af008b1SAlexandru Elisei 				report_info("interrupts took more than %d ms", i * 100);
867af008b1SAlexandru Elisei 			/* Wait for unexpected interrupts to fire */
877af008b1SAlexandru Elisei 			mdelay(100);
88ac4a67b6SAndrew Jones 			return;
89ac4a67b6SAndrew Jones 		}
90ac4a67b6SAndrew Jones 	}
91ac4a67b6SAndrew Jones 
927af008b1SAlexandru Elisei 	report_info("interrupts timed-out (5s)");
937af008b1SAlexandru Elisei }
947af008b1SAlexandru Elisei 
950e0a39dfSAlexandru Elisei static bool check_acked(cpumask_t *mask, int sender, int irqnum)
967af008b1SAlexandru Elisei {
977af008b1SAlexandru Elisei 	int missing = 0, extra = 0, unexpected = 0;
980e0a39dfSAlexandru Elisei 	bool has_gicv2 = (gic_version() == 2);
997af008b1SAlexandru Elisei 	bool pass = true;
1007af008b1SAlexandru Elisei 	int cpu;
1017af008b1SAlexandru Elisei 
102ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
103ac4a67b6SAndrew Jones 		if (cpumask_test_cpu(cpu, mask)) {
104ac4a67b6SAndrew Jones 			if (!acked[cpu])
105ac4a67b6SAndrew Jones 				++missing;
106ac4a67b6SAndrew Jones 			else if (acked[cpu] > 1)
107ac4a67b6SAndrew Jones 				++extra;
1081b3e4553SAlexandru Elisei 		} else if (acked[cpu]) {
109ac4a67b6SAndrew Jones 				++unexpected;
110ac4a67b6SAndrew Jones 		}
1110e0a39dfSAlexandru Elisei 		if (!acked[cpu])
1120e0a39dfSAlexandru Elisei 			continue;
1131b3e4553SAlexandru Elisei 		smp_rmb(); /* pairs with smp_wmb in irq_handler */
1147af008b1SAlexandru Elisei 
1150e0a39dfSAlexandru Elisei 		if (has_gicv2 && irq_sender[cpu] != sender) {
1167af008b1SAlexandru Elisei 			report_info("cpu%d received IPI from wrong sender %d",
1170e0a39dfSAlexandru Elisei 					cpu, irq_sender[cpu]);
1187af008b1SAlexandru Elisei 			pass = false;
119ac4a67b6SAndrew Jones 		}
120ac4a67b6SAndrew Jones 
1210e0a39dfSAlexandru Elisei 		if (irq_number[cpu] != irqnum) {
1227af008b1SAlexandru Elisei 			report_info("cpu%d received wrong irq %d",
1230e0a39dfSAlexandru Elisei 					cpu, irq_number[cpu]);
1247af008b1SAlexandru Elisei 			pass = false;
1257af008b1SAlexandru Elisei 		}
1267af008b1SAlexandru Elisei 	}
1277af008b1SAlexandru Elisei 
1287af008b1SAlexandru Elisei 	if (missing || extra || unexpected) {
1297af008b1SAlexandru Elisei 		report_info("ACKS: missing=%d extra=%d unexpected=%d",
13096edb026SAndre Przywara 				missing, extra, unexpected);
1317af008b1SAlexandru Elisei 		pass = false;
1327af008b1SAlexandru Elisei 	}
1337af008b1SAlexandru Elisei 
1347af008b1SAlexandru Elisei 	return pass;
135ac4a67b6SAndrew Jones }
136ac4a67b6SAndrew Jones 
137ac4a67b6SAndrew Jones static void check_spurious(void)
138ac4a67b6SAndrew Jones {
139ac4a67b6SAndrew Jones 	int cpu;
140ac4a67b6SAndrew Jones 
141ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
142ac4a67b6SAndrew Jones 		if (spurious[cpu])
143ac4a67b6SAndrew Jones 			report_info("WARN: cpu%d got %d spurious interrupts",
144ac4a67b6SAndrew Jones 				cpu, spurious[cpu]);
145ac4a67b6SAndrew Jones 	}
146ac4a67b6SAndrew Jones }
147ac4a67b6SAndrew Jones 
1480e0a39dfSAlexandru Elisei static int gic_get_sender(int irqstat)
149ca1b7a7bSAndrew Jones {
1500e0a39dfSAlexandru Elisei 	if (gic_version() == 2)
1511b3e4553SAlexandru Elisei 		/* GICC_IAR.CPUID is RAZ for non-SGIs */
1520e0a39dfSAlexandru Elisei 		return (irqstat >> 10) & 7;
1530e0a39dfSAlexandru Elisei 	return -1;
154ca1b7a7bSAndrew Jones }
155ca1b7a7bSAndrew Jones 
1561b3e4553SAlexandru Elisei static void irq_handler(struct pt_regs *regs __unused)
157ac4a67b6SAndrew Jones {
1582e2d471dSAndrew Jones 	u32 irqstat = gic_read_iar();
1592e2d471dSAndrew Jones 	u32 irqnr = gic_iar_irqnr(irqstat);
1600e0a39dfSAlexandru Elisei 	int this_cpu = smp_processor_id();
161ac4a67b6SAndrew Jones 
162ac4a67b6SAndrew Jones 	if (irqnr != GICC_INT_SPURIOUS) {
1632e2d471dSAndrew Jones 		gic_write_eoir(irqstat);
1640e0a39dfSAlexandru Elisei 		irq_sender[this_cpu] = gic_get_sender(irqstat);
1650e0a39dfSAlexandru Elisei 		irq_number[this_cpu] = irqnr;
166718d77f1SAlexandru Elisei 		smp_wmb(); /* pairs with smp_rmb in check_acked */
1670e0a39dfSAlexandru Elisei 		++acked[this_cpu];
168ac4a67b6SAndrew Jones 	} else {
1690e0a39dfSAlexandru Elisei 		++spurious[this_cpu];
170ac4a67b6SAndrew Jones 	}
171b3029e53SAlexandru Elisei 
172b3029e53SAlexandru Elisei 	/* Wait for writes to acked/spurious to complete */
173b3029e53SAlexandru Elisei 	dsb(ishst);
174ac4a67b6SAndrew Jones }
175ac4a67b6SAndrew Jones 
1760ef02cd6SEric Auger static void setup_irq(irq_handler_fn handler)
1770ef02cd6SEric Auger {
1780ef02cd6SEric Auger 	gic_enable_defaults();
1790ef02cd6SEric Auger #ifdef __arm__
1800ef02cd6SEric Auger 	install_exception_handler(EXCPTN_IRQ, handler);
1810ef02cd6SEric Auger #else
1820ef02cd6SEric Auger 	install_irq_handler(EL1H_IRQ, handler);
1830ef02cd6SEric Auger #endif
1840ef02cd6SEric Auger 	local_irq_enable();
1850ef02cd6SEric Auger }
1860ef02cd6SEric Auger 
1870ef02cd6SEric Auger #if defined(__aarch64__)
188de582149SEric Auger static void check_lpi_hits(int *expected, const char *msg)
189de582149SEric Auger {
190de582149SEric Auger 	bool pass = true;
191de582149SEric Auger 	int i;
192de582149SEric Auger 
193de582149SEric Auger 	for_each_present_cpu(i) {
194de582149SEric Auger 		if (acked[i] != expected[i]) {
195de582149SEric Auger 			report_info("expected %d LPIs on PE #%d, %d observed",
196de582149SEric Auger 				    expected[i], i, acked[i]);
197de582149SEric Auger 			pass = false;
198de582149SEric Auger 			break;
199de582149SEric Auger 		}
200de582149SEric Auger 	}
201de582149SEric Auger 	report(pass, "%s", msg);
202de582149SEric Auger }
2030ef02cd6SEric Auger #endif
2040ef02cd6SEric Auger 
2052e2d471dSAndrew Jones static void gicv2_ipi_send_self(void)
2062e2d471dSAndrew Jones {
20710e3685fSAlexandru Elisei 	/*
20810e3685fSAlexandru Elisei 	 * The wmb() in writel and rmb() when acknowledging the interrupt are
20910e3685fSAlexandru Elisei 	 * sufficient for ensuring that writes that happen in program order
21010e3685fSAlexandru Elisei 	 * before the interrupt are observed in the interrupt handler after
21110e3685fSAlexandru Elisei 	 * acknowledging the interrupt.
21210e3685fSAlexandru Elisei 	 */
213ca1b7a7bSAndrew Jones 	writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
2142e2d471dSAndrew Jones }
2152e2d471dSAndrew Jones 
2162e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void)
2172e2d471dSAndrew Jones {
21810e3685fSAlexandru Elisei 	/* No barriers are needed, same situation as gicv2_ipi_send_self() */
219ca1b7a7bSAndrew Jones 	writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
2202e2d471dSAndrew Jones }
2212e2d471dSAndrew Jones 
2222e2d471dSAndrew Jones static void gicv3_ipi_send_self(void)
2232e2d471dSAndrew Jones {
224ca1b7a7bSAndrew Jones 	gic_ipi_send_single(IPI_IRQ, smp_processor_id());
2252e2d471dSAndrew Jones }
2262e2d471dSAndrew Jones 
2272e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void)
2282e2d471dSAndrew Jones {
2290c03f4b1SAlexandru Elisei 	/*
2300c03f4b1SAlexandru Elisei 	 * Ensure stores to Normal memory are visible to other CPUs before
2310c03f4b1SAlexandru Elisei 	 * sending the IPI
2320c03f4b1SAlexandru Elisei 	 */
2330c03f4b1SAlexandru Elisei 	wmb();
234ca1b7a7bSAndrew Jones 	gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24);
2352e2d471dSAndrew Jones 	isb();
2362e2d471dSAndrew Jones }
2372e2d471dSAndrew Jones 
238ac4a67b6SAndrew Jones static void ipi_test_self(void)
239ac4a67b6SAndrew Jones {
2400e0a39dfSAlexandru Elisei 	int this_cpu = smp_processor_id();
241ac4a67b6SAndrew Jones 	cpumask_t mask;
242ac4a67b6SAndrew Jones 
243ac4a67b6SAndrew Jones 	report_prefix_push("self");
244ca1b7a7bSAndrew Jones 	stats_reset();
245ac4a67b6SAndrew Jones 	cpumask_clear(&mask);
2460e0a39dfSAlexandru Elisei 	cpumask_set_cpu(this_cpu, &mask);
2472e2d471dSAndrew Jones 	gic->ipi.send_self();
2487af008b1SAlexandru Elisei 	wait_for_interrupts(&mask);
2490e0a39dfSAlexandru Elisei 	report(check_acked(&mask, this_cpu, IPI_IRQ), "Interrupts received");
250ac4a67b6SAndrew Jones 	report_prefix_pop();
251ac4a67b6SAndrew Jones }
252ac4a67b6SAndrew Jones 
253ac4a67b6SAndrew Jones static void ipi_test_smp(void)
254ac4a67b6SAndrew Jones {
2550e0a39dfSAlexandru Elisei 	int this_cpu = smp_processor_id();
256ac4a67b6SAndrew Jones 	cpumask_t mask;
2572e2d471dSAndrew Jones 	int i;
258ac4a67b6SAndrew Jones 
259ac4a67b6SAndrew Jones 	report_prefix_push("target-list");
260ca1b7a7bSAndrew Jones 	stats_reset();
2612e2d471dSAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
2620e0a39dfSAlexandru Elisei 	for (i = this_cpu & 1; i < nr_cpus; i += 2)
2632e2d471dSAndrew Jones 		cpumask_clear_cpu(i, &mask);
264ca1b7a7bSAndrew Jones 	gic_ipi_send_mask(IPI_IRQ, &mask);
2657af008b1SAlexandru Elisei 	wait_for_interrupts(&mask);
2660e0a39dfSAlexandru Elisei 	report(check_acked(&mask, this_cpu, IPI_IRQ), "Interrupts received");
267ac4a67b6SAndrew Jones 	report_prefix_pop();
268ac4a67b6SAndrew Jones 
269ac4a67b6SAndrew Jones 	report_prefix_push("broadcast");
270ca1b7a7bSAndrew Jones 	stats_reset();
271ac4a67b6SAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
2720e0a39dfSAlexandru Elisei 	cpumask_clear_cpu(this_cpu, &mask);
2732e2d471dSAndrew Jones 	gic->ipi.send_broadcast();
2747af008b1SAlexandru Elisei 	wait_for_interrupts(&mask);
2750e0a39dfSAlexandru Elisei 	report(check_acked(&mask, this_cpu, IPI_IRQ), "Interrupts received");
276ac4a67b6SAndrew Jones 	report_prefix_pop();
277ac4a67b6SAndrew Jones }
278ac4a67b6SAndrew Jones 
279ca1b7a7bSAndrew Jones static void ipi_send(void)
280ca1b7a7bSAndrew Jones {
2811b3e4553SAlexandru Elisei 	setup_irq(irq_handler);
282ca1b7a7bSAndrew Jones 	wait_on_ready();
283ca1b7a7bSAndrew Jones 	ipi_test_self();
284ca1b7a7bSAndrew Jones 	ipi_test_smp();
285ca1b7a7bSAndrew Jones 	check_spurious();
286ca1b7a7bSAndrew Jones 	exit(report_summary());
287ca1b7a7bSAndrew Jones }
288ca1b7a7bSAndrew Jones 
2891b3e4553SAlexandru Elisei static void irq_recv(void)
290ac4a67b6SAndrew Jones {
2911b3e4553SAlexandru Elisei 	setup_irq(irq_handler);
292ac4a67b6SAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &ready);
293ac4a67b6SAndrew Jones 	while (1)
294ac4a67b6SAndrew Jones 		wfi();
295ac4a67b6SAndrew Jones }
296ac4a67b6SAndrew Jones 
29700b34f56SAndrew Jones static void ipi_test(void *data __unused)
298bfd500b4SAndrew Jones {
299bfd500b4SAndrew Jones 	if (smp_processor_id() == IPI_SENDER)
300bfd500b4SAndrew Jones 		ipi_send();
301bfd500b4SAndrew Jones 	else
3021b3e4553SAlexandru Elisei 		irq_recv();
303bfd500b4SAndrew Jones }
304bfd500b4SAndrew Jones 
3052e2d471dSAndrew Jones static struct gic gicv2 = {
3062e2d471dSAndrew Jones 	.ipi = {
3072e2d471dSAndrew Jones 		.send_self = gicv2_ipi_send_self,
3082e2d471dSAndrew Jones 		.send_broadcast = gicv2_ipi_send_broadcast,
3092e2d471dSAndrew Jones 	},
3102e2d471dSAndrew Jones };
3112e2d471dSAndrew Jones 
3122e2d471dSAndrew Jones static struct gic gicv3 = {
3132e2d471dSAndrew Jones 	.ipi = {
3142e2d471dSAndrew Jones 		.send_self = gicv3_ipi_send_self,
3152e2d471dSAndrew Jones 		.send_broadcast = gicv3_ipi_send_broadcast,
3162e2d471dSAndrew Jones 	},
3172e2d471dSAndrew Jones };
3182e2d471dSAndrew Jones 
319680beae9SAlexandru Elisei /* Runs on the same CPU as the sender, no need for memory synchronization */
320c152d8bcSChristoffer Dall static void ipi_clear_active_handler(struct pt_regs *regs __unused)
321c152d8bcSChristoffer Dall {
322c152d8bcSChristoffer Dall 	u32 irqstat = gic_read_iar();
323c152d8bcSChristoffer Dall 	u32 irqnr = gic_iar_irqnr(irqstat);
3240e0a39dfSAlexandru Elisei 	int this_cpu = smp_processor_id();
325c152d8bcSChristoffer Dall 
326c152d8bcSChristoffer Dall 	if (irqnr != GICC_INT_SPURIOUS) {
327c152d8bcSChristoffer Dall 		void *base;
328c152d8bcSChristoffer Dall 		u32 val = 1 << IPI_IRQ;
329c152d8bcSChristoffer Dall 
330c152d8bcSChristoffer Dall 		if (gic_version() == 2)
331c152d8bcSChristoffer Dall 			base = gicv2_dist_base();
332c152d8bcSChristoffer Dall 		else
3336d4d7c4bSAndrew Jones 			base = gicv3_sgi_base();
334c152d8bcSChristoffer Dall 
335c152d8bcSChristoffer Dall 		writel(val, base + GICD_ICACTIVER);
336c152d8bcSChristoffer Dall 
3370e0a39dfSAlexandru Elisei 		irq_sender[this_cpu] = gic_get_sender(irqstat);
3380e0a39dfSAlexandru Elisei 		irq_number[this_cpu] = irqnr;
3390e0a39dfSAlexandru Elisei 		++acked[this_cpu];
340c152d8bcSChristoffer Dall 	} else {
3410e0a39dfSAlexandru Elisei 		++spurious[this_cpu];
342c152d8bcSChristoffer Dall 	}
343c152d8bcSChristoffer Dall }
344c152d8bcSChristoffer Dall 
345c152d8bcSChristoffer Dall static void run_active_clear_test(void)
346c152d8bcSChristoffer Dall {
347c152d8bcSChristoffer Dall 	report_prefix_push("active");
34825f66327SEric Auger 	setup_irq(ipi_clear_active_handler);
349c152d8bcSChristoffer Dall 	ipi_test_self();
35064366016SAlexandru Elisei 	check_spurious();
351c152d8bcSChristoffer Dall 	report_prefix_pop();
352c152d8bcSChristoffer Dall }
353c152d8bcSChristoffer Dall 
35478ad7e95SAndre Przywara static bool test_ro_pattern_32(void *address, u32 pattern, u32 orig)
35578ad7e95SAndre Przywara {
35678ad7e95SAndre Przywara 	u32 reg;
35778ad7e95SAndre Przywara 
35878ad7e95SAndre Przywara 	writel(pattern, address);
35978ad7e95SAndre Przywara 	reg = readl(address);
36078ad7e95SAndre Przywara 
36178ad7e95SAndre Przywara 	if (reg != orig)
36278ad7e95SAndre Przywara 		writel(orig, address);
36378ad7e95SAndre Przywara 
36478ad7e95SAndre Przywara 	return reg == orig;
36578ad7e95SAndre Przywara }
36678ad7e95SAndre Przywara 
36778ad7e95SAndre Przywara static bool test_readonly_32(void *address, bool razwi)
36878ad7e95SAndre Przywara {
36978ad7e95SAndre Przywara 	u32 orig, pattern;
37078ad7e95SAndre Przywara 
37178ad7e95SAndre Przywara 	orig = readl(address);
37278ad7e95SAndre Przywara 	if (razwi && orig)
37378ad7e95SAndre Przywara 		return false;
37478ad7e95SAndre Przywara 
37578ad7e95SAndre Przywara 	pattern = 0xffffffff;
37678ad7e95SAndre Przywara 	if (orig != pattern) {
37778ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
37878ad7e95SAndre Przywara 			return false;
37978ad7e95SAndre Przywara 	}
38078ad7e95SAndre Przywara 
38178ad7e95SAndre Przywara 	pattern = 0xa5a55a5a;
38278ad7e95SAndre Przywara 	if (orig != pattern) {
38378ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
38478ad7e95SAndre Przywara 			return false;
38578ad7e95SAndre Przywara 	}
38678ad7e95SAndre Przywara 
38778ad7e95SAndre Przywara 	pattern = 0;
38878ad7e95SAndre Przywara 	if (orig != pattern) {
38978ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
39078ad7e95SAndre Przywara 			return false;
39178ad7e95SAndre Przywara 	}
39278ad7e95SAndre Przywara 
39378ad7e95SAndre Przywara 	return true;
39478ad7e95SAndre Przywara }
39578ad7e95SAndre Przywara 
39678ad7e95SAndre Przywara static void test_typer_v2(uint32_t reg)
39778ad7e95SAndre Przywara {
39878ad7e95SAndre Przywara 	int nr_gic_cpus = ((reg >> 5) & 0x7) + 1;
39978ad7e95SAndre Przywara 
4008e0a4f41SAndre Przywara 	report_info("nr_cpus=%d", nr_cpus);
401a299895bSThomas Huth 	report(nr_cpus == nr_gic_cpus, "all CPUs have interrupts");
40278ad7e95SAndre Przywara }
40378ad7e95SAndre Przywara 
404ff31a1c4SAndre Przywara #define BYTE(reg32, byte) (((reg32) >> ((byte) * 8)) & 0xff)
405ff31a1c4SAndre Przywara #define REPLACE_BYTE(reg32, byte, new) (((reg32) & ~(0xff << ((byte) * 8))) |\
406ff31a1c4SAndre Przywara 					((new) << ((byte) * 8)))
407ff31a1c4SAndre Przywara 
408ff31a1c4SAndre Przywara /*
409ff31a1c4SAndre Przywara  * Some registers are byte accessible, do a byte-wide read and write of known
410ff31a1c4SAndre Przywara  * content to check for this.
411ff31a1c4SAndre Przywara  * Apply a @mask to cater for special register properties.
412ff31a1c4SAndre Przywara  * @pattern contains the value already in the register.
413ff31a1c4SAndre Przywara  */
414ff31a1c4SAndre Przywara static void test_byte_access(void *base_addr, u32 pattern, u32 mask)
415ff31a1c4SAndre Przywara {
416ff31a1c4SAndre Przywara 	u32 reg = readb(base_addr + 1);
4178e0a4f41SAndre Przywara 	bool res;
418ff31a1c4SAndre Przywara 
4198e0a4f41SAndre Przywara 	res = (reg == (BYTE(pattern, 1) & (mask >> 8)));
420a299895bSThomas Huth 	report(res, "byte reads successful");
4218e0a4f41SAndre Przywara 	if (!res)
42246ca10f4SAlexandru Elisei 		report_info("byte 1 of 0x%08"PRIx32" => 0x%02"PRIx32, pattern & mask, reg);
423ff31a1c4SAndre Przywara 
424ff31a1c4SAndre Przywara 	pattern = REPLACE_BYTE(pattern, 2, 0x1f);
425ff31a1c4SAndre Przywara 	writeb(BYTE(pattern, 2), base_addr + 2);
426ff31a1c4SAndre Przywara 	reg = readl(base_addr);
4278e0a4f41SAndre Przywara 	res = (reg == (pattern & mask));
428a299895bSThomas Huth 	report(res, "byte writes successful");
4298e0a4f41SAndre Przywara 	if (!res)
43046ca10f4SAlexandru Elisei 		report_info("writing 0x%02"PRIx32" into bytes 2 => 0x%08"PRIx32,
4318e0a4f41SAndre Przywara 			    BYTE(pattern, 2), reg);
432ff31a1c4SAndre Przywara }
433ff31a1c4SAndre Przywara 
434ff31a1c4SAndre Przywara static void test_priorities(int nr_irqs, void *priptr)
435ff31a1c4SAndre Przywara {
436ff31a1c4SAndre Przywara 	u32 orig_prio, reg, pri_bits;
437ff31a1c4SAndre Przywara 	u32 pri_mask, pattern;
438ff31a1c4SAndre Przywara 	void *first_spi = priptr + GIC_FIRST_SPI;
439ff31a1c4SAndre Przywara 
440ff31a1c4SAndre Przywara 	orig_prio = readl(first_spi);
441ff31a1c4SAndre Przywara 	report_prefix_push("IPRIORITYR");
442ff31a1c4SAndre Przywara 
443ff31a1c4SAndre Przywara 	/*
444ff31a1c4SAndre Przywara 	 * Determine implemented number of priority bits by writing all 1's
445ff31a1c4SAndre Przywara 	 * and checking the number of cleared bits in the value read back.
446ff31a1c4SAndre Przywara 	 */
447ff31a1c4SAndre Przywara 	writel(0xffffffff, first_spi);
448ff31a1c4SAndre Przywara 	pri_mask = readl(first_spi);
449ff31a1c4SAndre Przywara 
450ff31a1c4SAndre Przywara 	reg = ~pri_mask;
451a299895bSThomas Huth 	report((((reg >> 16) == (reg & 0xffff)) &&
452a299895bSThomas Huth 	        ((reg & 0xff) == ((reg >> 8) & 0xff))),
453a299895bSThomas Huth 	       "consistent priority masking");
45446ca10f4SAlexandru Elisei 	report_info("priority mask is 0x%08"PRIx32, pri_mask);
455ff31a1c4SAndre Przywara 
456ff31a1c4SAndre Przywara 	reg = reg & 0xff;
457ff31a1c4SAndre Przywara 	for (pri_bits = 8; reg & 1; reg >>= 1, pri_bits--)
458ff31a1c4SAndre Przywara 		;
459a299895bSThomas Huth 	report(pri_bits >= 4, "implements at least 4 priority bits");
46046ca10f4SAlexandru Elisei 	report_info("%"PRIu32" priority bits implemented", pri_bits);
461ff31a1c4SAndre Przywara 
462ff31a1c4SAndre Przywara 	pattern = 0;
463ff31a1c4SAndre Przywara 	writel(pattern, first_spi);
464a299895bSThomas Huth 	report(readl(first_spi) == pattern, "clearing priorities");
465ff31a1c4SAndre Przywara 
466ff31a1c4SAndre Przywara 	/* setting all priorities to their max valus was tested above */
467ff31a1c4SAndre Przywara 
468a299895bSThomas Huth 	report(test_readonly_32(priptr + nr_irqs, true),
469a299895bSThomas Huth 	       "accesses beyond limit RAZ/WI");
470ff31a1c4SAndre Przywara 
471ff31a1c4SAndre Przywara 	writel(pattern, priptr + nr_irqs - 4);
472a299895bSThomas Huth 	report(readl(priptr + nr_irqs - 4) == (pattern & pri_mask),
473a299895bSThomas Huth 	       "accessing last SPIs");
474ff31a1c4SAndre Przywara 
475ff31a1c4SAndre Przywara 	pattern = 0xff7fbf3f;
476ff31a1c4SAndre Przywara 	writel(pattern, first_spi);
477a299895bSThomas Huth 	report(readl(first_spi) == (pattern & pri_mask),
478a299895bSThomas Huth 	       "priorities are preserved");
479ff31a1c4SAndre Przywara 
480ff31a1c4SAndre Przywara 	/* The PRIORITY registers are byte accessible. */
481ff31a1c4SAndre Przywara 	test_byte_access(first_spi, pattern, pri_mask);
482ff31a1c4SAndre Przywara 
483ff31a1c4SAndre Przywara 	report_prefix_pop();
484ff31a1c4SAndre Przywara 	writel(orig_prio, first_spi);
485ff31a1c4SAndre Przywara }
486ff31a1c4SAndre Przywara 
487fe572a5eSAndre Przywara /* GICD_ITARGETSR is only used by GICv2. */
488fe572a5eSAndre Przywara static void test_targets(int nr_irqs)
489fe572a5eSAndre Przywara {
490fe572a5eSAndre Przywara 	void *targetsptr = gicv2_dist_base() + GICD_ITARGETSR;
491fe572a5eSAndre Przywara 	u32 orig_targets;
492fe572a5eSAndre Przywara 	u32 cpu_mask;
493fe572a5eSAndre Przywara 	u32 pattern, reg;
494fe572a5eSAndre Przywara 
495fe572a5eSAndre Przywara 	orig_targets = readl(targetsptr + GIC_FIRST_SPI);
496fe572a5eSAndre Przywara 	report_prefix_push("ITARGETSR");
497fe572a5eSAndre Przywara 
498fe572a5eSAndre Przywara 	cpu_mask = (1 << nr_cpus) - 1;
499fe572a5eSAndre Przywara 	cpu_mask |= cpu_mask << 8;
500fe572a5eSAndre Przywara 	cpu_mask |= cpu_mask << 16;
501fe572a5eSAndre Przywara 
502fe572a5eSAndre Przywara 	/* Check that bits for non implemented CPUs are RAZ/WI. */
503fe572a5eSAndre Przywara 	if (nr_cpus < 8) {
504fe572a5eSAndre Przywara 		writel(0xffffffff, targetsptr + GIC_FIRST_SPI);
505a299895bSThomas Huth 		report(!(readl(targetsptr + GIC_FIRST_SPI) & ~cpu_mask),
506a299895bSThomas Huth 		       "bits for non-existent CPUs masked");
5078e0a4f41SAndre Przywara 		report_info("%d non-existent CPUs", 8 - nr_cpus);
508fe572a5eSAndre Przywara 	} else {
509fe572a5eSAndre Przywara 		report_skip("CPU masking (all CPUs implemented)");
510fe572a5eSAndre Przywara 	}
511fe572a5eSAndre Przywara 
512a299895bSThomas Huth 	report(test_readonly_32(targetsptr + nr_irqs, true),
513a299895bSThomas Huth 	       "accesses beyond limit RAZ/WI");
514fe572a5eSAndre Przywara 
515fe572a5eSAndre Przywara 	pattern = 0x0103020f;
516fe572a5eSAndre Przywara 	writel(pattern, targetsptr + GIC_FIRST_SPI);
517fe572a5eSAndre Przywara 	reg = readl(targetsptr + GIC_FIRST_SPI);
518a299895bSThomas Huth 	report(reg == (pattern & cpu_mask), "register content preserved");
5198e0a4f41SAndre Przywara 	if (reg != (pattern & cpu_mask))
52046ca10f4SAlexandru Elisei 		report_info("writing %08"PRIx32" reads back as %08"PRIx32,
5218e0a4f41SAndre Przywara 			    pattern & cpu_mask, reg);
522fe572a5eSAndre Przywara 
523fe572a5eSAndre Przywara 	/* The TARGETS registers are byte accessible. */
524fe572a5eSAndre Przywara 	test_byte_access(targetsptr + GIC_FIRST_SPI, pattern, cpu_mask);
525fe572a5eSAndre Przywara 
526fe572a5eSAndre Przywara 	writel(orig_targets, targetsptr + GIC_FIRST_SPI);
527da5b8576SAndre Przywara 
528da5b8576SAndre Przywara 	report_prefix_pop();
529fe572a5eSAndre Przywara }
530fe572a5eSAndre Przywara 
53178ad7e95SAndre Przywara static void gic_test_mmio(void)
53278ad7e95SAndre Przywara {
53378ad7e95SAndre Przywara 	u32 reg;
53478ad7e95SAndre Przywara 	int nr_irqs;
53578ad7e95SAndre Przywara 	void *gic_dist_base, *idreg;
53678ad7e95SAndre Przywara 
53778ad7e95SAndre Przywara 	switch(gic_version()) {
53878ad7e95SAndre Przywara 	case 0x2:
53978ad7e95SAndre Przywara 		gic_dist_base = gicv2_dist_base();
54078ad7e95SAndre Przywara 		idreg = gic_dist_base + GICD_ICPIDR2;
54178ad7e95SAndre Przywara 		break;
54278ad7e95SAndre Przywara 	case 0x3:
54378ad7e95SAndre Przywara 		report_abort("GICv3 MMIO tests NYI");
54478ad7e95SAndre Przywara 	default:
54578ad7e95SAndre Przywara 		report_abort("GIC version %d not supported", gic_version());
54678ad7e95SAndre Przywara 	}
54778ad7e95SAndre Przywara 
54878ad7e95SAndre Przywara 	reg = readl(gic_dist_base + GICD_TYPER);
54978ad7e95SAndre Przywara 	nr_irqs = GICD_TYPER_IRQS(reg);
55078ad7e95SAndre Przywara 	report_info("number of implemented SPIs: %d", nr_irqs - GIC_FIRST_SPI);
55178ad7e95SAndre Przywara 
55278ad7e95SAndre Przywara 	test_typer_v2(reg);
55378ad7e95SAndre Przywara 
55446ca10f4SAlexandru Elisei 	report_info("IIDR: 0x%08"PRIx32, readl(gic_dist_base + GICD_IIDR));
55578ad7e95SAndre Przywara 
556a299895bSThomas Huth 	report(test_readonly_32(gic_dist_base + GICD_TYPER, false),
557a299895bSThomas Huth                "GICD_TYPER is read-only");
558a299895bSThomas Huth 	report(test_readonly_32(gic_dist_base + GICD_IIDR, false),
559a299895bSThomas Huth                "GICD_IIDR is read-only");
56078ad7e95SAndre Przywara 
56178ad7e95SAndre Przywara 	reg = readl(idreg);
562a299895bSThomas Huth 	report(test_readonly_32(idreg, false), "ICPIDR2 is read-only");
56346ca10f4SAlexandru Elisei 	report_info("value of ICPIDR2: 0x%08"PRIx32, reg);
564ff31a1c4SAndre Przywara 
565ff31a1c4SAndre Przywara 	test_priorities(nr_irqs, gic_dist_base + GICD_IPRIORITYR);
566fe572a5eSAndre Przywara 
567fe572a5eSAndre Przywara 	if (gic_version() == 2)
568fe572a5eSAndre Przywara 		test_targets(nr_irqs);
56978ad7e95SAndre Przywara }
57078ad7e95SAndre Przywara 
571ba74b106SEric Auger #if defined(__arm__)
572ba74b106SEric Auger 
573ba74b106SEric Auger static void test_its_introspection(void) {}
5740ef02cd6SEric Auger static void test_its_trigger(void) {}
57564260a5fSEric Auger static void test_its_migration(void) {}
576de582149SEric Auger static void test_its_pending_migration(void) {}
577de582149SEric Auger static void test_migrate_unmapped_collection(void) {}
578ba74b106SEric Auger 
579ba74b106SEric Auger #else /* __aarch64__ */
580ba74b106SEric Auger 
581ba74b106SEric Auger static void test_its_introspection(void)
582ba74b106SEric Auger {
583ba74b106SEric Auger 	struct its_baser *dev_baser = &its_data.device_baser;
584ba74b106SEric Auger 	struct its_baser *coll_baser = &its_data.coll_baser;
585ba74b106SEric Auger 	struct its_typer *typer = &its_data.typer;
586ba74b106SEric Auger 
587ba74b106SEric Auger 	if (!gicv3_its_base()) {
588ba74b106SEric Auger 		report_skip("No ITS, skip ...");
589ba74b106SEric Auger 		return;
590ba74b106SEric Auger 	}
591ba74b106SEric Auger 
592ba74b106SEric Auger 	/* IIDR */
593ba74b106SEric Auger 	report(test_readonly_32(gicv3_its_base() + GITS_IIDR, false),
594ba74b106SEric Auger 	       "GITS_IIDR is read-only"),
595ba74b106SEric Auger 
596ba74b106SEric Auger 	/* TYPER */
597ba74b106SEric Auger 	report(test_readonly_32(gicv3_its_base() + GITS_TYPER, false),
598ba74b106SEric Auger 	       "GITS_TYPER is read-only");
599ba74b106SEric Auger 
600ba74b106SEric Auger 	report(typer->phys_lpi, "ITS supports physical LPIs");
601ba74b106SEric Auger 	report_info("vLPI support: %s", typer->virt_lpi ? "yes" : "no");
602ba74b106SEric Auger 	report_info("ITT entry size = 0x%x", typer->ite_size);
603ba74b106SEric Auger 	report_info("Bit Count: EventID=%d DeviceId=%d CollId=%d",
604ba74b106SEric Auger 		    typer->eventid_bits, typer->deviceid_bits,
605ba74b106SEric Auger 		    typer->collid_bits);
606ba74b106SEric Auger 	report(typer->eventid_bits && typer->deviceid_bits &&
607ba74b106SEric Auger 	       typer->collid_bits, "ID spaces");
608ba74b106SEric Auger 	report_info("Target address format %s",
609ba74b106SEric Auger 			typer->pta ? "Redist base address" : "PE #");
610ba74b106SEric Auger 
611ba74b106SEric Auger 	report(dev_baser && coll_baser, "detect device and collection BASER");
612ba74b106SEric Auger 	report_info("device table entry_size = 0x%x", dev_baser->esz);
613ba74b106SEric Auger 	report_info("collection table entry_size = 0x%x", coll_baser->esz);
614ba74b106SEric Auger }
615ba74b106SEric Auger 
6160ef02cd6SEric Auger static int its_prerequisites(int nb_cpus)
6170ef02cd6SEric Auger {
6180ef02cd6SEric Auger 	int cpu;
6190ef02cd6SEric Auger 
6200ef02cd6SEric Auger 	if (!gicv3_its_base()) {
6210ef02cd6SEric Auger 		report_skip("No ITS, skip ...");
6220ef02cd6SEric Auger 		return -1;
6230ef02cd6SEric Auger 	}
6240ef02cd6SEric Auger 
6250ef02cd6SEric Auger 	if (nr_cpus < nb_cpus) {
6260ef02cd6SEric Auger 		report_skip("Test requires at least %d vcpus", nb_cpus);
6270ef02cd6SEric Auger 		return -1;
6280ef02cd6SEric Auger 	}
6290ef02cd6SEric Auger 
6301b3e4553SAlexandru Elisei 	setup_irq(irq_handler);
6310ef02cd6SEric Auger 
6320ef02cd6SEric Auger 	for_each_present_cpu(cpu) {
6330ef02cd6SEric Auger 		if (cpu == 0)
6340ef02cd6SEric Auger 			continue;
6351b3e4553SAlexandru Elisei 		smp_boot_secondary(cpu, irq_recv);
6360ef02cd6SEric Auger 	}
6370ef02cd6SEric Auger 	wait_on_ready();
6380ef02cd6SEric Auger 
6390ef02cd6SEric Auger 	its_enable_defaults();
6400ef02cd6SEric Auger 
6410ef02cd6SEric Auger 	return 0;
6420ef02cd6SEric Auger }
6430ef02cd6SEric Auger 
64464260a5fSEric Auger /*
64564260a5fSEric Auger  * Setup the configuration for those mappings:
64664260a5fSEric Auger  * dev_id=2 event=20 -> vcpu 3, intid=8195
64764260a5fSEric Auger  * dev_id=7 event=255 -> vcpu 2, intid=8196
64864260a5fSEric Auger  * LPIs ready to hit
64964260a5fSEric Auger  */
65064260a5fSEric Auger static int its_setup1(void)
6510ef02cd6SEric Auger {
6520ef02cd6SEric Auger 	struct its_collection *col3, *col2;
6530ef02cd6SEric Auger 	struct its_device *dev2, *dev7;
6540ef02cd6SEric Auger 
6550ef02cd6SEric Auger 	if (its_prerequisites(4))
65664260a5fSEric Auger 		return -1;
6570ef02cd6SEric Auger 
6580ef02cd6SEric Auger 	dev2 = its_create_device(2 /* dev id */, 8 /* nb_ites */);
6590ef02cd6SEric Auger 	dev7 = its_create_device(7 /* dev id */, 8 /* nb_ites */);
6600ef02cd6SEric Auger 
6610ef02cd6SEric Auger 	col3 = its_create_collection(3 /* col id */, 3/* target PE */);
6620ef02cd6SEric Auger 	col2 = its_create_collection(2 /* col id */, 2/* target PE */);
6630ef02cd6SEric Auger 
6640ef02cd6SEric Auger 	gicv3_lpi_set_config(8195, LPI_PROP_DEFAULT);
6650ef02cd6SEric Auger 	gicv3_lpi_set_config(8196, LPI_PROP_DEFAULT);
6660ef02cd6SEric Auger 
6670ef02cd6SEric Auger 	/*
6680ef02cd6SEric Auger 	 * dev=2, eventid=20  -> lpi= 8195, col=3
6690ef02cd6SEric Auger 	 * dev=7, eventid=255 -> lpi= 8196, col=2
6700ef02cd6SEric Auger 	 */
6710ef02cd6SEric Auger 	its_send_mapd(dev2, true);
6720ef02cd6SEric Auger 	its_send_mapd(dev7, true);
6730ef02cd6SEric Auger 
6740ef02cd6SEric Auger 	its_send_mapc(col3, true);
6750ef02cd6SEric Auger 	its_send_mapc(col2, true);
6760ef02cd6SEric Auger 
6770ef02cd6SEric Auger 	its_send_invall(col2);
6780ef02cd6SEric Auger 	its_send_invall(col3);
6790ef02cd6SEric Auger 
6800ef02cd6SEric Auger 	its_send_mapti(dev2, 8195 /* lpi id */, 20 /* event id */, col3);
6810ef02cd6SEric Auger 	its_send_mapti(dev7, 8196 /* lpi id */, 255 /* event id */, col2);
68264260a5fSEric Auger 	return 0;
68364260a5fSEric Auger }
68464260a5fSEric Auger 
68564260a5fSEric Auger static void test_its_trigger(void)
68664260a5fSEric Auger {
68764260a5fSEric Auger 	struct its_collection *col3;
68864260a5fSEric Auger 	struct its_device *dev2, *dev7;
6891b3e4553SAlexandru Elisei 	cpumask_t mask;
69064260a5fSEric Auger 
69164260a5fSEric Auger 	if (its_setup1())
69264260a5fSEric Auger 		return;
69364260a5fSEric Auger 
69464260a5fSEric Auger 	col3 = its_get_collection(3);
69564260a5fSEric Auger 	dev2 = its_get_device(2);
69664260a5fSEric Auger 	dev7 = its_get_device(7);
69764260a5fSEric Auger 
69864260a5fSEric Auger 	report_prefix_push("int");
6990ef02cd6SEric Auger 
7001b3e4553SAlexandru Elisei 	stats_reset();
7011b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
7021b3e4553SAlexandru Elisei 	cpumask_set_cpu(3, &mask);
7030ef02cd6SEric Auger 	its_send_int(dev2, 20);
7041b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
7051b3e4553SAlexandru Elisei 	report(check_acked(&mask, 0, 8195),
7061b3e4553SAlexandru Elisei 			"dev=2, eventid=20  -> lpi= 8195, col=3");
7070ef02cd6SEric Auger 
7081b3e4553SAlexandru Elisei 	stats_reset();
7091b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
7101b3e4553SAlexandru Elisei 	cpumask_set_cpu(2, &mask);
7110ef02cd6SEric Auger 	its_send_int(dev7, 255);
7121b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
7131b3e4553SAlexandru Elisei 	report(check_acked(&mask, 0, 8196),
7141b3e4553SAlexandru Elisei 			"dev=7, eventid=255 -> lpi= 8196, col=2");
7150ef02cd6SEric Auger 
7160ef02cd6SEric Auger 	report_prefix_pop();
7170ef02cd6SEric Auger 
7180ef02cd6SEric Auger 	report_prefix_push("inv/invall");
7190ef02cd6SEric Auger 
7200ef02cd6SEric Auger 	/*
7210ef02cd6SEric Auger 	 * disable 8195, check dev2/eventid=20 does not trigger the
7220ef02cd6SEric Auger 	 * corresponding LPI
7230ef02cd6SEric Auger 	 */
7240ef02cd6SEric Auger 	gicv3_lpi_set_config(8195, LPI_PROP_DEFAULT & ~LPI_PROP_ENABLED);
7250ef02cd6SEric Auger 	its_send_inv(dev2, 20);
7260ef02cd6SEric Auger 
7271b3e4553SAlexandru Elisei 	stats_reset();
7281b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
7290ef02cd6SEric Auger 	its_send_int(dev2, 20);
7301b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
7311b3e4553SAlexandru Elisei 	report(check_acked(&mask, -1, -1),
7321b3e4553SAlexandru Elisei 			"dev2/eventid=20 does not trigger any LPI");
7330ef02cd6SEric Auger 
7340ef02cd6SEric Auger 	/*
735*10396befSAlex Bennée 	 * re-enable the LPI. While "A change to the LPI configuration
736*10396befSAlex Bennée 	 * is not guaranteed to be visible until an appropriate
737*10396befSAlex Bennée 	 * invalidation operation has completed" hardware that doesn't
738*10396befSAlex Bennée 	 * implement caches may have delivered the event at any point
739*10396befSAlex Bennée 	 * after the enabling. Check the LPI has hit by the time the
740*10396befSAlex Bennée 	 * invall is done.
7410ef02cd6SEric Auger 	 */
7420ef02cd6SEric Auger 	gicv3_lpi_set_config(8195, LPI_PROP_DEFAULT);
7431b3e4553SAlexandru Elisei 	stats_reset();
7441b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
7450ef02cd6SEric Auger 	its_send_int(dev2, 20);
7461b3e4553SAlexandru Elisei 	cpumask_set_cpu(3, &mask);
7470ef02cd6SEric Auger 	its_send_invall(col3);
7481b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
7491b3e4553SAlexandru Elisei 	report(check_acked(&mask, 0, 8195),
7501b3e4553SAlexandru Elisei 			"dev2/eventid=20 pending LPI is received");
751ae7dac4eSAlexandru Elisei 
7521b3e4553SAlexandru Elisei 	stats_reset();
7531b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
7541b3e4553SAlexandru Elisei 	cpumask_set_cpu(3, &mask);
7550ef02cd6SEric Auger 	its_send_int(dev2, 20);
7561b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
7571b3e4553SAlexandru Elisei 	report(check_acked(&mask, 0, 8195),
7581b3e4553SAlexandru Elisei 			"dev2/eventid=20 now triggers an LPI");
7590ef02cd6SEric Auger 
7600ef02cd6SEric Auger 	report_prefix_pop();
7610ef02cd6SEric Auger 
7620ef02cd6SEric Auger 	report_prefix_push("mapd valid=false");
7630ef02cd6SEric Auger 	/*
7640ef02cd6SEric Auger 	 * Unmap device 2 and check the eventid 20 formerly
7650ef02cd6SEric Auger 	 * attached to it does not hit anymore
7660ef02cd6SEric Auger 	 */
7670ef02cd6SEric Auger 
7680ef02cd6SEric Auger 	its_send_mapd(dev2, false);
7691b3e4553SAlexandru Elisei 	stats_reset();
7701b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
7710ef02cd6SEric Auger 	its_send_int(dev2, 20);
7721b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
7731b3e4553SAlexandru Elisei 	report(check_acked(&mask, -1, -1), "no LPI after device unmap");
7741b3e4553SAlexandru Elisei 
7751b3e4553SAlexandru Elisei 	check_spurious();
7760ef02cd6SEric Auger 	report_prefix_pop();
7770ef02cd6SEric Auger }
77864260a5fSEric Auger 
77964260a5fSEric Auger static void test_its_migration(void)
78064260a5fSEric Auger {
78164260a5fSEric Auger 	struct its_device *dev2, *dev7;
78264260a5fSEric Auger 	bool test_skipped = false;
7831b3e4553SAlexandru Elisei 	cpumask_t mask;
78464260a5fSEric Auger 
78564260a5fSEric Auger 	if (its_setup1()) {
78664260a5fSEric Auger 		test_skipped = true;
78764260a5fSEric Auger 		goto do_migrate;
78864260a5fSEric Auger 	}
78964260a5fSEric Auger 
79064260a5fSEric Auger 	dev2 = its_get_device(2);
79164260a5fSEric Auger 	dev7 = its_get_device(7);
79264260a5fSEric Auger 
79364260a5fSEric Auger do_migrate:
79464260a5fSEric Auger 	puts("Now migrate the VM, then press a key to continue...\n");
79564260a5fSEric Auger 	(void)getchar();
79664260a5fSEric Auger 	report_info("Migration complete");
79764260a5fSEric Auger 	if (test_skipped)
79864260a5fSEric Auger 		return;
79964260a5fSEric Auger 
8001b3e4553SAlexandru Elisei 	stats_reset();
8011b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
8021b3e4553SAlexandru Elisei 	cpumask_set_cpu(3, &mask);
80364260a5fSEric Auger 	its_send_int(dev2, 20);
8041b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
8051b3e4553SAlexandru Elisei 	report(check_acked(&mask, 0, 8195),
8061b3e4553SAlexandru Elisei 			"dev2/eventid=20 triggers LPI 8195 on PE #3 after migration");
80764260a5fSEric Auger 
8081b3e4553SAlexandru Elisei 	stats_reset();
8091b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
8101b3e4553SAlexandru Elisei 	cpumask_set_cpu(2, &mask);
81164260a5fSEric Auger 	its_send_int(dev7, 255);
8121b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
8131b3e4553SAlexandru Elisei 	report(check_acked(&mask, 0, 8196),
8141b3e4553SAlexandru Elisei 			"dev7/eventid=255 triggers LPI 8196 on PE #2 after migration");
8151b3e4553SAlexandru Elisei 
8161b3e4553SAlexandru Elisei 	check_spurious();
81764260a5fSEric Auger }
818de582149SEric Auger 
819de582149SEric Auger #define ERRATA_UNMAPPED_COLLECTIONS "ERRATA_8c58be34494b"
820de582149SEric Auger 
821de582149SEric Auger static void test_migrate_unmapped_collection(void)
822de582149SEric Auger {
823de582149SEric Auger 	struct its_collection *col = NULL;
824de582149SEric Auger 	struct its_device *dev2 = NULL, *dev7 = NULL;
825de582149SEric Auger 	bool test_skipped = false;
8261b3e4553SAlexandru Elisei 	cpumask_t mask;
827de582149SEric Auger 	int pe0 = 0;
828de582149SEric Auger 	u8 config;
829de582149SEric Auger 
830de582149SEric Auger 	if (its_setup1()) {
831de582149SEric Auger 		test_skipped = true;
832de582149SEric Auger 		goto do_migrate;
833de582149SEric Auger 	}
834de582149SEric Auger 
835de582149SEric Auger 	if (!errata(ERRATA_UNMAPPED_COLLECTIONS)) {
836de582149SEric Auger 		report_skip("Skipping test, as this test hangs without the fix. "
837de582149SEric Auger 			    "Set %s=y to enable.", ERRATA_UNMAPPED_COLLECTIONS);
838de582149SEric Auger 		test_skipped = true;
839de582149SEric Auger 		goto do_migrate;
840de582149SEric Auger 	}
841de582149SEric Auger 
842de582149SEric Auger 	col = its_create_collection(pe0, pe0);
843de582149SEric Auger 	dev2 = its_get_device(2);
844de582149SEric Auger 	dev7 = its_get_device(7);
845de582149SEric Auger 
846de582149SEric Auger 	/* MAPTI with the collection unmapped */
847de582149SEric Auger 	its_send_mapti(dev2, 8192, 0, col);
848de582149SEric Auger 	gicv3_lpi_set_config(8192, LPI_PROP_DEFAULT);
849de582149SEric Auger 
850de582149SEric Auger do_migrate:
851de582149SEric Auger 	puts("Now migrate the VM, then press a key to continue...\n");
852de582149SEric Auger 	(void)getchar();
853de582149SEric Auger 	report_info("Migration complete");
854de582149SEric Auger 	if (test_skipped)
855de582149SEric Auger 		return;
856de582149SEric Auger 
857de582149SEric Auger 	/* on the destination, map the collection */
858de582149SEric Auger 	its_send_mapc(col, true);
859de582149SEric Auger 	its_send_invall(col);
860de582149SEric Auger 
8611b3e4553SAlexandru Elisei 	stats_reset();
8621b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
8631b3e4553SAlexandru Elisei 	cpumask_set_cpu(2, &mask);
864de582149SEric Auger 	its_send_int(dev7, 255);
8651b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
8661b3e4553SAlexandru Elisei 	report(check_acked(&mask, 0, 8196),
8671b3e4553SAlexandru Elisei 			"dev7/eventid= 255 triggered LPI 8196 on PE #2");
868de582149SEric Auger 
869de582149SEric Auger 	config = gicv3_lpi_get_config(8192);
870de582149SEric Auger 	report(config == LPI_PROP_DEFAULT,
871de582149SEric Auger 	       "Config of LPI 8192 was properly migrated");
872de582149SEric Auger 
8731b3e4553SAlexandru Elisei 	stats_reset();
8741b3e4553SAlexandru Elisei 	cpumask_clear(&mask);
8751b3e4553SAlexandru Elisei 	cpumask_set_cpu(pe0, &mask);
876de582149SEric Auger 	its_send_int(dev2, 0);
8771b3e4553SAlexandru Elisei 	wait_for_interrupts(&mask);
8781b3e4553SAlexandru Elisei 	report(check_acked(&mask, 0, 8192),
8791b3e4553SAlexandru Elisei 			"dev2/eventid = 0 triggered LPI 8192 on PE0");
8801b3e4553SAlexandru Elisei 
8811b3e4553SAlexandru Elisei 	check_spurious();
882de582149SEric Auger }
883de582149SEric Auger 
884de582149SEric Auger static void test_its_pending_migration(void)
885de582149SEric Auger {
886de582149SEric Auger 	struct its_device *dev;
887de582149SEric Auger 	struct its_collection *collection[2];
888de582149SEric Auger 	int *expected = calloc(nr_cpus, sizeof(int));
889de582149SEric Auger 	int pe0 = nr_cpus - 1, pe1 = nr_cpus - 2;
890de582149SEric Auger 	bool test_skipped = false;
891de582149SEric Auger 	u64 pendbaser;
892de582149SEric Auger 	void *ptr;
893de582149SEric Auger 	int i;
894de582149SEric Auger 
895de582149SEric Auger 	if (its_prerequisites(4)) {
896de582149SEric Auger 		test_skipped = true;
897de582149SEric Auger 		goto do_migrate;
898de582149SEric Auger 	}
899de582149SEric Auger 
900de582149SEric Auger 	dev = its_create_device(2 /* dev id */, 8 /* nb_ites */);
901de582149SEric Auger 	its_send_mapd(dev, true);
902de582149SEric Auger 
903de582149SEric Auger 	collection[0] = its_create_collection(pe0, pe0);
904de582149SEric Auger 	collection[1] = its_create_collection(pe1, pe1);
905de582149SEric Auger 	its_send_mapc(collection[0], true);
906de582149SEric Auger 	its_send_mapc(collection[1], true);
907de582149SEric Auger 
908de582149SEric Auger 	/* disable lpi at redist level */
909de582149SEric Auger 	gicv3_lpi_rdist_disable(pe0);
910de582149SEric Auger 	gicv3_lpi_rdist_disable(pe1);
911de582149SEric Auger 
912de582149SEric Auger 	/* lpis are interleaved inbetween the 2 PEs */
913de582149SEric Auger 	for (i = 0; i < 256; i++) {
914de582149SEric Auger 		struct its_collection *col = i % 2 ? collection[0] :
915de582149SEric Auger 						     collection[1];
916de582149SEric Auger 		int vcpu = col->target_address >> 16;
917de582149SEric Auger 
918de582149SEric Auger 		its_send_mapti(dev, LPI(i), i, col);
919de582149SEric Auger 		gicv3_lpi_set_config(LPI(i), LPI_PROP_DEFAULT);
920de582149SEric Auger 		gicv3_lpi_set_clr_pending(vcpu, LPI(i), true);
921de582149SEric Auger 	}
922de582149SEric Auger 	its_send_invall(collection[0]);
923de582149SEric Auger 	its_send_invall(collection[1]);
924de582149SEric Auger 
925de582149SEric Auger 	/* Clear the PTZ bit on each pendbaser */
926de582149SEric Auger 
927de582149SEric Auger 	expected[pe0] = 128;
928de582149SEric Auger 	expected[pe1] = 128;
929de582149SEric Auger 
930de582149SEric Auger 	ptr = gicv3_data.redist_base[pe0] + GICR_PENDBASER;
931de582149SEric Auger 	pendbaser = readq(ptr);
932de582149SEric Auger 	writeq(pendbaser & ~GICR_PENDBASER_PTZ, ptr);
933de582149SEric Auger 
934de582149SEric Auger 	ptr = gicv3_data.redist_base[pe1] + GICR_PENDBASER;
935de582149SEric Auger 	pendbaser = readq(ptr);
936de582149SEric Auger 	writeq(pendbaser & ~GICR_PENDBASER_PTZ, ptr);
937de582149SEric Auger 
9381b3e4553SAlexandru Elisei 	/*
9391b3e4553SAlexandru Elisei 	 * Reset and initialization values for acked are the same, so we don't
9401b3e4553SAlexandru Elisei 	 * need to explicitely call stats_reset().
9411b3e4553SAlexandru Elisei 	 */
942de582149SEric Auger 	gicv3_lpi_rdist_enable(pe0);
943de582149SEric Auger 	gicv3_lpi_rdist_enable(pe1);
944de582149SEric Auger 
945de582149SEric Auger do_migrate:
946de582149SEric Auger 	puts("Now migrate the VM, then press a key to continue...\n");
947de582149SEric Auger 	(void)getchar();
948de582149SEric Auger 	report_info("Migration complete");
949de582149SEric Auger 	if (test_skipped)
950de582149SEric Auger 		return;
951de582149SEric Auger 
952de582149SEric Auger 	/* let's wait for the 256 LPIs to be handled */
953de582149SEric Auger 	mdelay(1000);
954de582149SEric Auger 
955de582149SEric Auger 	check_lpi_hits(expected, "128 LPIs on both PE0 and PE1 after migration");
956de582149SEric Auger }
957ba74b106SEric Auger #endif
958ba74b106SEric Auger 
959ac4a67b6SAndrew Jones int main(int argc, char **argv)
960ac4a67b6SAndrew Jones {
9612e2d471dSAndrew Jones 	if (!gic_init()) {
962ac4a67b6SAndrew Jones 		printf("No supported gic present, skipping tests...\n");
963ac4a67b6SAndrew Jones 		return report_summary();
964ac4a67b6SAndrew Jones 	}
965ac4a67b6SAndrew Jones 
9662b19b829SAndrew Jones 	report_prefix_pushf("gicv%d", gic_version());
967ac4a67b6SAndrew Jones 
9682e2d471dSAndrew Jones 	switch (gic_version()) {
9692e2d471dSAndrew Jones 	case 2:
9702e2d471dSAndrew Jones 		gic = &gicv2;
9712e2d471dSAndrew Jones 		break;
9722e2d471dSAndrew Jones 	case 3:
9732e2d471dSAndrew Jones 		gic = &gicv3;
9742e2d471dSAndrew Jones 		break;
9752e2d471dSAndrew Jones 	}
9762e2d471dSAndrew Jones 
977ac4a67b6SAndrew Jones 	if (argc < 2)
978ac4a67b6SAndrew Jones 		report_abort("no test specified");
979ac4a67b6SAndrew Jones 
980ac4a67b6SAndrew Jones 	if (strcmp(argv[1], "ipi") == 0) {
981ac4a67b6SAndrew Jones 		report_prefix_push(argv[1]);
982ac4a67b6SAndrew Jones 		nr_cpu_check(2);
98300b34f56SAndrew Jones 		on_cpus(ipi_test, NULL);
984c152d8bcSChristoffer Dall 	} else if (strcmp(argv[1], "active") == 0) {
985c152d8bcSChristoffer Dall 		run_active_clear_test();
98678ad7e95SAndre Przywara 	} else if (strcmp(argv[1], "mmio") == 0) {
98778ad7e95SAndre Przywara 		report_prefix_push(argv[1]);
98878ad7e95SAndre Przywara 		gic_test_mmio();
98978ad7e95SAndre Przywara 		report_prefix_pop();
9900ef02cd6SEric Auger 	} else if (!strcmp(argv[1], "its-trigger")) {
9910ef02cd6SEric Auger 		report_prefix_push(argv[1]);
9920ef02cd6SEric Auger 		test_its_trigger();
9930ef02cd6SEric Auger 		report_prefix_pop();
99464260a5fSEric Auger 	} else if (!strcmp(argv[1], "its-migration")) {
99564260a5fSEric Auger 		report_prefix_push(argv[1]);
99664260a5fSEric Auger 		test_its_migration();
99764260a5fSEric Auger 		report_prefix_pop();
998de582149SEric Auger 	} else if (!strcmp(argv[1], "its-pending-migration")) {
999de582149SEric Auger 		report_prefix_push(argv[1]);
1000de582149SEric Auger 		test_its_pending_migration();
1001de582149SEric Auger 		report_prefix_pop();
1002de582149SEric Auger 	} else if (!strcmp(argv[1], "its-migrate-unmapped-collection")) {
1003de582149SEric Auger 		report_prefix_push(argv[1]);
1004de582149SEric Auger 		test_migrate_unmapped_collection();
1005de582149SEric Auger 		report_prefix_pop();
1006ba74b106SEric Auger 	} else if (strcmp(argv[1], "its-introspection") == 0) {
1007ba74b106SEric Auger 		report_prefix_push(argv[1]);
1008ba74b106SEric Auger 		test_its_introspection();
1009ba74b106SEric Auger 		report_prefix_pop();
1010ac4a67b6SAndrew Jones 	} else {
1011ac4a67b6SAndrew Jones 		report_abort("Unknown subtest '%s'", argv[1]);
1012ac4a67b6SAndrew Jones 	}
1013ac4a67b6SAndrew Jones 
1014ac4a67b6SAndrew Jones 	return report_summary();
1015ac4a67b6SAndrew Jones }
1016