xref: /kvm-unit-tests/arm/gic.c (revision ba74b1063d21003ff7de7a84af121af2d3663c1f)
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>
15ac4a67b6SAndrew Jones #include <asm/setup.h>
16ac4a67b6SAndrew Jones #include <asm/processor.h>
17ac4a67b6SAndrew Jones #include <asm/delay.h>
18ac4a67b6SAndrew Jones #include <asm/gic.h>
19*ba74b106SEric Auger #include <asm/gic-v3-its.h>
20ac4a67b6SAndrew Jones #include <asm/smp.h>
21ac4a67b6SAndrew Jones #include <asm/barrier.h>
22ac4a67b6SAndrew Jones #include <asm/io.h>
23ac4a67b6SAndrew Jones 
24ca1b7a7bSAndrew Jones #define IPI_SENDER	1
25ca1b7a7bSAndrew Jones #define IPI_IRQ		1
26ca1b7a7bSAndrew Jones 
272e2d471dSAndrew Jones struct gic {
282e2d471dSAndrew Jones 	struct {
292e2d471dSAndrew Jones 		void (*send_self)(void);
302e2d471dSAndrew Jones 		void (*send_broadcast)(void);
312e2d471dSAndrew Jones 	} ipi;
322e2d471dSAndrew Jones };
332e2d471dSAndrew Jones 
342e2d471dSAndrew Jones static struct gic *gic;
35ac4a67b6SAndrew Jones static int acked[NR_CPUS], spurious[NR_CPUS];
36ca1b7a7bSAndrew Jones static int bad_sender[NR_CPUS], bad_irq[NR_CPUS];
37ac4a67b6SAndrew Jones static cpumask_t ready;
38ac4a67b6SAndrew Jones 
39ac4a67b6SAndrew Jones static void nr_cpu_check(int nr)
40ac4a67b6SAndrew Jones {
41ac4a67b6SAndrew Jones 	if (nr_cpus < nr)
42ac4a67b6SAndrew Jones 		report_abort("At least %d cpus required", nr);
43ac4a67b6SAndrew Jones }
44ac4a67b6SAndrew Jones 
45ac4a67b6SAndrew Jones static void wait_on_ready(void)
46ac4a67b6SAndrew Jones {
47ac4a67b6SAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &ready);
48ac4a67b6SAndrew Jones 	while (!cpumask_full(&ready))
49ac4a67b6SAndrew Jones 		cpu_relax();
50ac4a67b6SAndrew Jones }
51ac4a67b6SAndrew Jones 
52ca1b7a7bSAndrew Jones static void stats_reset(void)
53ca1b7a7bSAndrew Jones {
54ca1b7a7bSAndrew Jones 	int i;
55ca1b7a7bSAndrew Jones 
56ca1b7a7bSAndrew Jones 	for (i = 0; i < nr_cpus; ++i) {
57ca1b7a7bSAndrew Jones 		acked[i] = 0;
58ca1b7a7bSAndrew Jones 		bad_sender[i] = -1;
59ca1b7a7bSAndrew Jones 		bad_irq[i] = -1;
60ca1b7a7bSAndrew Jones 	}
61ca1b7a7bSAndrew Jones 	smp_wmb();
62ca1b7a7bSAndrew Jones }
63ca1b7a7bSAndrew Jones 
6496edb026SAndre Przywara static void check_acked(const char *testname, cpumask_t *mask)
65ac4a67b6SAndrew Jones {
66ac4a67b6SAndrew Jones 	int missing = 0, extra = 0, unexpected = 0;
67ac4a67b6SAndrew Jones 	int nr_pass, cpu, i;
68ca1b7a7bSAndrew Jones 	bool bad = false;
69ac4a67b6SAndrew Jones 
70ac4a67b6SAndrew Jones 	/* Wait up to 5s for all interrupts to be delivered */
71ac4a67b6SAndrew Jones 	for (i = 0; i < 50; ++i) {
72ac4a67b6SAndrew Jones 		mdelay(100);
73ac4a67b6SAndrew Jones 		nr_pass = 0;
74ac4a67b6SAndrew Jones 		for_each_present_cpu(cpu) {
75ac4a67b6SAndrew Jones 			smp_rmb();
76ac4a67b6SAndrew Jones 			nr_pass += cpumask_test_cpu(cpu, mask) ?
77ac4a67b6SAndrew Jones 				acked[cpu] == 1 : acked[cpu] == 0;
78ca1b7a7bSAndrew Jones 
79ca1b7a7bSAndrew Jones 			if (bad_sender[cpu] != -1) {
80ca1b7a7bSAndrew Jones 				printf("cpu%d received IPI from wrong sender %d\n",
81ca1b7a7bSAndrew Jones 					cpu, bad_sender[cpu]);
82ca1b7a7bSAndrew Jones 				bad = true;
83ca1b7a7bSAndrew Jones 			}
84ca1b7a7bSAndrew Jones 
85ca1b7a7bSAndrew Jones 			if (bad_irq[cpu] != -1) {
86ca1b7a7bSAndrew Jones 				printf("cpu%d received wrong irq %d\n",
87ca1b7a7bSAndrew Jones 					cpu, bad_irq[cpu]);
88ca1b7a7bSAndrew Jones 				bad = true;
89ca1b7a7bSAndrew Jones 			}
90ac4a67b6SAndrew Jones 		}
91ac4a67b6SAndrew Jones 		if (nr_pass == nr_cpus) {
92a299895bSThomas Huth 			report(!bad, "%s", testname);
9396edb026SAndre Przywara 			if (i)
9496edb026SAndre Przywara 				report_info("took more than %d ms", i * 100);
95ac4a67b6SAndrew Jones 			return;
96ac4a67b6SAndrew Jones 		}
97ac4a67b6SAndrew Jones 	}
98ac4a67b6SAndrew Jones 
99ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
100ac4a67b6SAndrew Jones 		if (cpumask_test_cpu(cpu, mask)) {
101ac4a67b6SAndrew Jones 			if (!acked[cpu])
102ac4a67b6SAndrew Jones 				++missing;
103ac4a67b6SAndrew Jones 			else if (acked[cpu] > 1)
104ac4a67b6SAndrew Jones 				++extra;
105ac4a67b6SAndrew Jones 		} else {
106ac4a67b6SAndrew Jones 			if (acked[cpu])
107ac4a67b6SAndrew Jones 				++unexpected;
108ac4a67b6SAndrew Jones 		}
109ac4a67b6SAndrew Jones 	}
110ac4a67b6SAndrew Jones 
111a299895bSThomas Huth 	report(false, "%s", testname);
11296edb026SAndre Przywara 	report_info("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d",
11396edb026SAndre Przywara 		    missing, extra, unexpected);
114ac4a67b6SAndrew Jones }
115ac4a67b6SAndrew Jones 
116ac4a67b6SAndrew Jones static void check_spurious(void)
117ac4a67b6SAndrew Jones {
118ac4a67b6SAndrew Jones 	int cpu;
119ac4a67b6SAndrew Jones 
120ac4a67b6SAndrew Jones 	smp_rmb();
121ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
122ac4a67b6SAndrew Jones 		if (spurious[cpu])
123ac4a67b6SAndrew Jones 			report_info("WARN: cpu%d got %d spurious interrupts",
124ac4a67b6SAndrew Jones 				cpu, spurious[cpu]);
125ac4a67b6SAndrew Jones 	}
126ac4a67b6SAndrew Jones }
127ac4a67b6SAndrew Jones 
128ca1b7a7bSAndrew Jones static void check_ipi_sender(u32 irqstat)
129ca1b7a7bSAndrew Jones {
130ca1b7a7bSAndrew Jones 	if (gic_version() == 2) {
131ca1b7a7bSAndrew Jones 		int src = (irqstat >> 10) & 7;
132ca1b7a7bSAndrew Jones 
133ca1b7a7bSAndrew Jones 		if (src != IPI_SENDER)
134ca1b7a7bSAndrew Jones 			bad_sender[smp_processor_id()] = src;
135ca1b7a7bSAndrew Jones 	}
136ca1b7a7bSAndrew Jones }
137ca1b7a7bSAndrew Jones 
138ca1b7a7bSAndrew Jones static void check_irqnr(u32 irqnr)
139ca1b7a7bSAndrew Jones {
140ca1b7a7bSAndrew Jones 	if (irqnr != IPI_IRQ)
141ca1b7a7bSAndrew Jones 		bad_irq[smp_processor_id()] = irqnr;
142ca1b7a7bSAndrew Jones }
143ca1b7a7bSAndrew Jones 
144ac4a67b6SAndrew Jones static void ipi_handler(struct pt_regs *regs __unused)
145ac4a67b6SAndrew Jones {
1462e2d471dSAndrew Jones 	u32 irqstat = gic_read_iar();
1472e2d471dSAndrew Jones 	u32 irqnr = gic_iar_irqnr(irqstat);
148ac4a67b6SAndrew Jones 
149ac4a67b6SAndrew Jones 	if (irqnr != GICC_INT_SPURIOUS) {
1502e2d471dSAndrew Jones 		gic_write_eoir(irqstat);
151ca1b7a7bSAndrew Jones 		smp_rmb(); /* pairs with wmb in stats_reset */
152ac4a67b6SAndrew Jones 		++acked[smp_processor_id()];
153ca1b7a7bSAndrew Jones 		check_ipi_sender(irqstat);
154ca1b7a7bSAndrew Jones 		check_irqnr(irqnr);
155ac4a67b6SAndrew Jones 		smp_wmb(); /* pairs with rmb in check_acked */
156ac4a67b6SAndrew Jones 	} else {
157ac4a67b6SAndrew Jones 		++spurious[smp_processor_id()];
158ac4a67b6SAndrew Jones 		smp_wmb();
159ac4a67b6SAndrew Jones 	}
160ac4a67b6SAndrew Jones }
161ac4a67b6SAndrew Jones 
1622e2d471dSAndrew Jones static void gicv2_ipi_send_self(void)
1632e2d471dSAndrew Jones {
164ca1b7a7bSAndrew Jones 	writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
1652e2d471dSAndrew Jones }
1662e2d471dSAndrew Jones 
1672e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void)
1682e2d471dSAndrew Jones {
169ca1b7a7bSAndrew Jones 	writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
1702e2d471dSAndrew Jones }
1712e2d471dSAndrew Jones 
1722e2d471dSAndrew Jones static void gicv3_ipi_send_self(void)
1732e2d471dSAndrew Jones {
174ca1b7a7bSAndrew Jones 	gic_ipi_send_single(IPI_IRQ, smp_processor_id());
1752e2d471dSAndrew Jones }
1762e2d471dSAndrew Jones 
1772e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void)
1782e2d471dSAndrew Jones {
179ca1b7a7bSAndrew Jones 	gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24);
1802e2d471dSAndrew Jones 	isb();
1812e2d471dSAndrew Jones }
1822e2d471dSAndrew Jones 
183ac4a67b6SAndrew Jones static void ipi_test_self(void)
184ac4a67b6SAndrew Jones {
185ac4a67b6SAndrew Jones 	cpumask_t mask;
186ac4a67b6SAndrew Jones 
187ac4a67b6SAndrew Jones 	report_prefix_push("self");
188ca1b7a7bSAndrew Jones 	stats_reset();
189ac4a67b6SAndrew Jones 	cpumask_clear(&mask);
190ca1b7a7bSAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &mask);
1912e2d471dSAndrew Jones 	gic->ipi.send_self();
19296edb026SAndre Przywara 	check_acked("IPI: self", &mask);
193ac4a67b6SAndrew Jones 	report_prefix_pop();
194ac4a67b6SAndrew Jones }
195ac4a67b6SAndrew Jones 
196ac4a67b6SAndrew Jones static void ipi_test_smp(void)
197ac4a67b6SAndrew Jones {
198ac4a67b6SAndrew Jones 	cpumask_t mask;
1992e2d471dSAndrew Jones 	int i;
200ac4a67b6SAndrew Jones 
201ac4a67b6SAndrew Jones 	report_prefix_push("target-list");
202ca1b7a7bSAndrew Jones 	stats_reset();
2032e2d471dSAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
204ca1b7a7bSAndrew Jones 	for (i = smp_processor_id() & 1; i < nr_cpus; i += 2)
2052e2d471dSAndrew Jones 		cpumask_clear_cpu(i, &mask);
206ca1b7a7bSAndrew Jones 	gic_ipi_send_mask(IPI_IRQ, &mask);
20796edb026SAndre Przywara 	check_acked("IPI: directed", &mask);
208ac4a67b6SAndrew Jones 	report_prefix_pop();
209ac4a67b6SAndrew Jones 
210ac4a67b6SAndrew Jones 	report_prefix_push("broadcast");
211ca1b7a7bSAndrew Jones 	stats_reset();
212ac4a67b6SAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
213ca1b7a7bSAndrew Jones 	cpumask_clear_cpu(smp_processor_id(), &mask);
2142e2d471dSAndrew Jones 	gic->ipi.send_broadcast();
21596edb026SAndre Przywara 	check_acked("IPI: broadcast", &mask);
216ac4a67b6SAndrew Jones 	report_prefix_pop();
217ac4a67b6SAndrew Jones }
218ac4a67b6SAndrew Jones 
21925f66327SEric Auger static void setup_irq(irq_handler_fn handler)
220ac4a67b6SAndrew Jones {
2212e2d471dSAndrew Jones 	gic_enable_defaults();
222ac4a67b6SAndrew Jones #ifdef __arm__
22325f66327SEric Auger 	install_exception_handler(EXCPTN_IRQ, handler);
224ac4a67b6SAndrew Jones #else
22525f66327SEric Auger 	install_irq_handler(EL1H_IRQ, handler);
226ac4a67b6SAndrew Jones #endif
227ac4a67b6SAndrew Jones 	local_irq_enable();
228ac4a67b6SAndrew Jones }
229ac4a67b6SAndrew Jones 
230ca1b7a7bSAndrew Jones static void ipi_send(void)
231ca1b7a7bSAndrew Jones {
23225f66327SEric Auger 	setup_irq(ipi_handler);
233ca1b7a7bSAndrew Jones 	wait_on_ready();
234ca1b7a7bSAndrew Jones 	ipi_test_self();
235ca1b7a7bSAndrew Jones 	ipi_test_smp();
236ca1b7a7bSAndrew Jones 	check_spurious();
237ca1b7a7bSAndrew Jones 	exit(report_summary());
238ca1b7a7bSAndrew Jones }
239ca1b7a7bSAndrew Jones 
240ac4a67b6SAndrew Jones static void ipi_recv(void)
241ac4a67b6SAndrew Jones {
24225f66327SEric Auger 	setup_irq(ipi_handler);
243ac4a67b6SAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &ready);
244ac4a67b6SAndrew Jones 	while (1)
245ac4a67b6SAndrew Jones 		wfi();
246ac4a67b6SAndrew Jones }
247ac4a67b6SAndrew Jones 
24800b34f56SAndrew Jones static void ipi_test(void *data __unused)
249bfd500b4SAndrew Jones {
250bfd500b4SAndrew Jones 	if (smp_processor_id() == IPI_SENDER)
251bfd500b4SAndrew Jones 		ipi_send();
252bfd500b4SAndrew Jones 	else
253bfd500b4SAndrew Jones 		ipi_recv();
254bfd500b4SAndrew Jones }
255bfd500b4SAndrew Jones 
2562e2d471dSAndrew Jones static struct gic gicv2 = {
2572e2d471dSAndrew Jones 	.ipi = {
2582e2d471dSAndrew Jones 		.send_self = gicv2_ipi_send_self,
2592e2d471dSAndrew Jones 		.send_broadcast = gicv2_ipi_send_broadcast,
2602e2d471dSAndrew Jones 	},
2612e2d471dSAndrew Jones };
2622e2d471dSAndrew Jones 
2632e2d471dSAndrew Jones static struct gic gicv3 = {
2642e2d471dSAndrew Jones 	.ipi = {
2652e2d471dSAndrew Jones 		.send_self = gicv3_ipi_send_self,
2662e2d471dSAndrew Jones 		.send_broadcast = gicv3_ipi_send_broadcast,
2672e2d471dSAndrew Jones 	},
2682e2d471dSAndrew Jones };
2692e2d471dSAndrew Jones 
270c152d8bcSChristoffer Dall static void ipi_clear_active_handler(struct pt_regs *regs __unused)
271c152d8bcSChristoffer Dall {
272c152d8bcSChristoffer Dall 	u32 irqstat = gic_read_iar();
273c152d8bcSChristoffer Dall 	u32 irqnr = gic_iar_irqnr(irqstat);
274c152d8bcSChristoffer Dall 
275c152d8bcSChristoffer Dall 	if (irqnr != GICC_INT_SPURIOUS) {
276c152d8bcSChristoffer Dall 		void *base;
277c152d8bcSChristoffer Dall 		u32 val = 1 << IPI_IRQ;
278c152d8bcSChristoffer Dall 
279c152d8bcSChristoffer Dall 		if (gic_version() == 2)
280c152d8bcSChristoffer Dall 			base = gicv2_dist_base();
281c152d8bcSChristoffer Dall 		else
2826d4d7c4bSAndrew Jones 			base = gicv3_sgi_base();
283c152d8bcSChristoffer Dall 
284c152d8bcSChristoffer Dall 		writel(val, base + GICD_ICACTIVER);
285c152d8bcSChristoffer Dall 
286c152d8bcSChristoffer Dall 		smp_rmb(); /* pairs with wmb in stats_reset */
287c152d8bcSChristoffer Dall 		++acked[smp_processor_id()];
288c152d8bcSChristoffer Dall 		check_irqnr(irqnr);
289c152d8bcSChristoffer Dall 		smp_wmb(); /* pairs with rmb in check_acked */
290c152d8bcSChristoffer Dall 	} else {
291c152d8bcSChristoffer Dall 		++spurious[smp_processor_id()];
292c152d8bcSChristoffer Dall 		smp_wmb();
293c152d8bcSChristoffer Dall 	}
294c152d8bcSChristoffer Dall }
295c152d8bcSChristoffer Dall 
296c152d8bcSChristoffer Dall static void run_active_clear_test(void)
297c152d8bcSChristoffer Dall {
298c152d8bcSChristoffer Dall 	report_prefix_push("active");
29925f66327SEric Auger 	setup_irq(ipi_clear_active_handler);
300c152d8bcSChristoffer Dall 	ipi_test_self();
301c152d8bcSChristoffer Dall 	report_prefix_pop();
302c152d8bcSChristoffer Dall }
303c152d8bcSChristoffer Dall 
30478ad7e95SAndre Przywara static bool test_ro_pattern_32(void *address, u32 pattern, u32 orig)
30578ad7e95SAndre Przywara {
30678ad7e95SAndre Przywara 	u32 reg;
30778ad7e95SAndre Przywara 
30878ad7e95SAndre Przywara 	writel(pattern, address);
30978ad7e95SAndre Przywara 	reg = readl(address);
31078ad7e95SAndre Przywara 
31178ad7e95SAndre Przywara 	if (reg != orig)
31278ad7e95SAndre Przywara 		writel(orig, address);
31378ad7e95SAndre Przywara 
31478ad7e95SAndre Przywara 	return reg == orig;
31578ad7e95SAndre Przywara }
31678ad7e95SAndre Przywara 
31778ad7e95SAndre Przywara static bool test_readonly_32(void *address, bool razwi)
31878ad7e95SAndre Przywara {
31978ad7e95SAndre Przywara 	u32 orig, pattern;
32078ad7e95SAndre Przywara 
32178ad7e95SAndre Przywara 	orig = readl(address);
32278ad7e95SAndre Przywara 	if (razwi && orig)
32378ad7e95SAndre Przywara 		return false;
32478ad7e95SAndre Przywara 
32578ad7e95SAndre Przywara 	pattern = 0xffffffff;
32678ad7e95SAndre Przywara 	if (orig != pattern) {
32778ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
32878ad7e95SAndre Przywara 			return false;
32978ad7e95SAndre Przywara 	}
33078ad7e95SAndre Przywara 
33178ad7e95SAndre Przywara 	pattern = 0xa5a55a5a;
33278ad7e95SAndre Przywara 	if (orig != pattern) {
33378ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
33478ad7e95SAndre Przywara 			return false;
33578ad7e95SAndre Przywara 	}
33678ad7e95SAndre Przywara 
33778ad7e95SAndre Przywara 	pattern = 0;
33878ad7e95SAndre Przywara 	if (orig != pattern) {
33978ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
34078ad7e95SAndre Przywara 			return false;
34178ad7e95SAndre Przywara 	}
34278ad7e95SAndre Przywara 
34378ad7e95SAndre Przywara 	return true;
34478ad7e95SAndre Przywara }
34578ad7e95SAndre Przywara 
34678ad7e95SAndre Przywara static void test_typer_v2(uint32_t reg)
34778ad7e95SAndre Przywara {
34878ad7e95SAndre Przywara 	int nr_gic_cpus = ((reg >> 5) & 0x7) + 1;
34978ad7e95SAndre Przywara 
3508e0a4f41SAndre Przywara 	report_info("nr_cpus=%d", nr_cpus);
351a299895bSThomas Huth 	report(nr_cpus == nr_gic_cpus, "all CPUs have interrupts");
35278ad7e95SAndre Przywara }
35378ad7e95SAndre Przywara 
354ff31a1c4SAndre Przywara #define BYTE(reg32, byte) (((reg32) >> ((byte) * 8)) & 0xff)
355ff31a1c4SAndre Przywara #define REPLACE_BYTE(reg32, byte, new) (((reg32) & ~(0xff << ((byte) * 8))) |\
356ff31a1c4SAndre Przywara 					((new) << ((byte) * 8)))
357ff31a1c4SAndre Przywara 
358ff31a1c4SAndre Przywara /*
359ff31a1c4SAndre Przywara  * Some registers are byte accessible, do a byte-wide read and write of known
360ff31a1c4SAndre Przywara  * content to check for this.
361ff31a1c4SAndre Przywara  * Apply a @mask to cater for special register properties.
362ff31a1c4SAndre Przywara  * @pattern contains the value already in the register.
363ff31a1c4SAndre Przywara  */
364ff31a1c4SAndre Przywara static void test_byte_access(void *base_addr, u32 pattern, u32 mask)
365ff31a1c4SAndre Przywara {
366ff31a1c4SAndre Przywara 	u32 reg = readb(base_addr + 1);
3678e0a4f41SAndre Przywara 	bool res;
368ff31a1c4SAndre Przywara 
3698e0a4f41SAndre Przywara 	res = (reg == (BYTE(pattern, 1) & (mask >> 8)));
370a299895bSThomas Huth 	report(res, "byte reads successful");
3718e0a4f41SAndre Przywara 	if (!res)
3728e0a4f41SAndre Przywara 		report_info("byte 1 of 0x%08x => 0x%02x", pattern & mask, reg);
373ff31a1c4SAndre Przywara 
374ff31a1c4SAndre Przywara 	pattern = REPLACE_BYTE(pattern, 2, 0x1f);
375ff31a1c4SAndre Przywara 	writeb(BYTE(pattern, 2), base_addr + 2);
376ff31a1c4SAndre Przywara 	reg = readl(base_addr);
3778e0a4f41SAndre Przywara 	res = (reg == (pattern & mask));
378a299895bSThomas Huth 	report(res, "byte writes successful");
3798e0a4f41SAndre Przywara 	if (!res)
3808e0a4f41SAndre Przywara 		report_info("writing 0x%02x into bytes 2 => 0x%08x",
3818e0a4f41SAndre Przywara 			    BYTE(pattern, 2), reg);
382ff31a1c4SAndre Przywara }
383ff31a1c4SAndre Przywara 
384ff31a1c4SAndre Przywara static void test_priorities(int nr_irqs, void *priptr)
385ff31a1c4SAndre Przywara {
386ff31a1c4SAndre Przywara 	u32 orig_prio, reg, pri_bits;
387ff31a1c4SAndre Przywara 	u32 pri_mask, pattern;
388ff31a1c4SAndre Przywara 	void *first_spi = priptr + GIC_FIRST_SPI;
389ff31a1c4SAndre Przywara 
390ff31a1c4SAndre Przywara 	orig_prio = readl(first_spi);
391ff31a1c4SAndre Przywara 	report_prefix_push("IPRIORITYR");
392ff31a1c4SAndre Przywara 
393ff31a1c4SAndre Przywara 	/*
394ff31a1c4SAndre Przywara 	 * Determine implemented number of priority bits by writing all 1's
395ff31a1c4SAndre Przywara 	 * and checking the number of cleared bits in the value read back.
396ff31a1c4SAndre Przywara 	 */
397ff31a1c4SAndre Przywara 	writel(0xffffffff, first_spi);
398ff31a1c4SAndre Przywara 	pri_mask = readl(first_spi);
399ff31a1c4SAndre Przywara 
400ff31a1c4SAndre Przywara 	reg = ~pri_mask;
401a299895bSThomas Huth 	report((((reg >> 16) == (reg & 0xffff)) &&
402a299895bSThomas Huth 	        ((reg & 0xff) == ((reg >> 8) & 0xff))),
403a299895bSThomas Huth 	       "consistent priority masking");
4048e0a4f41SAndre Przywara 	report_info("priority mask is 0x%08x", pri_mask);
405ff31a1c4SAndre Przywara 
406ff31a1c4SAndre Przywara 	reg = reg & 0xff;
407ff31a1c4SAndre Przywara 	for (pri_bits = 8; reg & 1; reg >>= 1, pri_bits--)
408ff31a1c4SAndre Przywara 		;
409a299895bSThomas Huth 	report(pri_bits >= 4, "implements at least 4 priority bits");
4108e0a4f41SAndre Przywara 	report_info("%d priority bits implemented", pri_bits);
411ff31a1c4SAndre Przywara 
412ff31a1c4SAndre Przywara 	pattern = 0;
413ff31a1c4SAndre Przywara 	writel(pattern, first_spi);
414a299895bSThomas Huth 	report(readl(first_spi) == pattern, "clearing priorities");
415ff31a1c4SAndre Przywara 
416ff31a1c4SAndre Przywara 	/* setting all priorities to their max valus was tested above */
417ff31a1c4SAndre Przywara 
418a299895bSThomas Huth 	report(test_readonly_32(priptr + nr_irqs, true),
419a299895bSThomas Huth 	       "accesses beyond limit RAZ/WI");
420ff31a1c4SAndre Przywara 
421ff31a1c4SAndre Przywara 	writel(pattern, priptr + nr_irqs - 4);
422a299895bSThomas Huth 	report(readl(priptr + nr_irqs - 4) == (pattern & pri_mask),
423a299895bSThomas Huth 	       "accessing last SPIs");
424ff31a1c4SAndre Przywara 
425ff31a1c4SAndre Przywara 	pattern = 0xff7fbf3f;
426ff31a1c4SAndre Przywara 	writel(pattern, first_spi);
427a299895bSThomas Huth 	report(readl(first_spi) == (pattern & pri_mask),
428a299895bSThomas Huth 	       "priorities are preserved");
429ff31a1c4SAndre Przywara 
430ff31a1c4SAndre Przywara 	/* The PRIORITY registers are byte accessible. */
431ff31a1c4SAndre Przywara 	test_byte_access(first_spi, pattern, pri_mask);
432ff31a1c4SAndre Przywara 
433ff31a1c4SAndre Przywara 	report_prefix_pop();
434ff31a1c4SAndre Przywara 	writel(orig_prio, first_spi);
435ff31a1c4SAndre Przywara }
436ff31a1c4SAndre Przywara 
437fe572a5eSAndre Przywara /* GICD_ITARGETSR is only used by GICv2. */
438fe572a5eSAndre Przywara static void test_targets(int nr_irqs)
439fe572a5eSAndre Przywara {
440fe572a5eSAndre Przywara 	void *targetsptr = gicv2_dist_base() + GICD_ITARGETSR;
441fe572a5eSAndre Przywara 	u32 orig_targets;
442fe572a5eSAndre Przywara 	u32 cpu_mask;
443fe572a5eSAndre Przywara 	u32 pattern, reg;
444fe572a5eSAndre Przywara 
445fe572a5eSAndre Przywara 	orig_targets = readl(targetsptr + GIC_FIRST_SPI);
446fe572a5eSAndre Przywara 	report_prefix_push("ITARGETSR");
447fe572a5eSAndre Przywara 
448fe572a5eSAndre Przywara 	cpu_mask = (1 << nr_cpus) - 1;
449fe572a5eSAndre Przywara 	cpu_mask |= cpu_mask << 8;
450fe572a5eSAndre Przywara 	cpu_mask |= cpu_mask << 16;
451fe572a5eSAndre Przywara 
452fe572a5eSAndre Przywara 	/* Check that bits for non implemented CPUs are RAZ/WI. */
453fe572a5eSAndre Przywara 	if (nr_cpus < 8) {
454fe572a5eSAndre Przywara 		writel(0xffffffff, targetsptr + GIC_FIRST_SPI);
455a299895bSThomas Huth 		report(!(readl(targetsptr + GIC_FIRST_SPI) & ~cpu_mask),
456a299895bSThomas Huth 		       "bits for non-existent CPUs masked");
4578e0a4f41SAndre Przywara 		report_info("%d non-existent CPUs", 8 - nr_cpus);
458fe572a5eSAndre Przywara 	} else {
459fe572a5eSAndre Przywara 		report_skip("CPU masking (all CPUs implemented)");
460fe572a5eSAndre Przywara 	}
461fe572a5eSAndre Przywara 
462a299895bSThomas Huth 	report(test_readonly_32(targetsptr + nr_irqs, true),
463a299895bSThomas Huth 	       "accesses beyond limit RAZ/WI");
464fe572a5eSAndre Przywara 
465fe572a5eSAndre Przywara 	pattern = 0x0103020f;
466fe572a5eSAndre Przywara 	writel(pattern, targetsptr + GIC_FIRST_SPI);
467fe572a5eSAndre Przywara 	reg = readl(targetsptr + GIC_FIRST_SPI);
468a299895bSThomas Huth 	report(reg == (pattern & cpu_mask), "register content preserved");
4698e0a4f41SAndre Przywara 	if (reg != (pattern & cpu_mask))
4708e0a4f41SAndre Przywara 		report_info("writing %08x reads back as %08x",
4718e0a4f41SAndre Przywara 			    pattern & cpu_mask, reg);
472fe572a5eSAndre Przywara 
473fe572a5eSAndre Przywara 	/* The TARGETS registers are byte accessible. */
474fe572a5eSAndre Przywara 	test_byte_access(targetsptr + GIC_FIRST_SPI, pattern, cpu_mask);
475fe572a5eSAndre Przywara 
476fe572a5eSAndre Przywara 	writel(orig_targets, targetsptr + GIC_FIRST_SPI);
477da5b8576SAndre Przywara 
478da5b8576SAndre Przywara 	report_prefix_pop();
479fe572a5eSAndre Przywara }
480fe572a5eSAndre Przywara 
48178ad7e95SAndre Przywara static void gic_test_mmio(void)
48278ad7e95SAndre Przywara {
48378ad7e95SAndre Przywara 	u32 reg;
48478ad7e95SAndre Przywara 	int nr_irqs;
48578ad7e95SAndre Przywara 	void *gic_dist_base, *idreg;
48678ad7e95SAndre Przywara 
48778ad7e95SAndre Przywara 	switch(gic_version()) {
48878ad7e95SAndre Przywara 	case 0x2:
48978ad7e95SAndre Przywara 		gic_dist_base = gicv2_dist_base();
49078ad7e95SAndre Przywara 		idreg = gic_dist_base + GICD_ICPIDR2;
49178ad7e95SAndre Przywara 		break;
49278ad7e95SAndre Przywara 	case 0x3:
49378ad7e95SAndre Przywara 		report_abort("GICv3 MMIO tests NYI");
49478ad7e95SAndre Przywara 	default:
49578ad7e95SAndre Przywara 		report_abort("GIC version %d not supported", gic_version());
49678ad7e95SAndre Przywara 	}
49778ad7e95SAndre Przywara 
49878ad7e95SAndre Przywara 	reg = readl(gic_dist_base + GICD_TYPER);
49978ad7e95SAndre Przywara 	nr_irqs = GICD_TYPER_IRQS(reg);
50078ad7e95SAndre Przywara 	report_info("number of implemented SPIs: %d", nr_irqs - GIC_FIRST_SPI);
50178ad7e95SAndre Przywara 
50278ad7e95SAndre Przywara 	test_typer_v2(reg);
50378ad7e95SAndre Przywara 
50478ad7e95SAndre Przywara 	report_info("IIDR: 0x%08x", readl(gic_dist_base + GICD_IIDR));
50578ad7e95SAndre Przywara 
506a299895bSThomas Huth 	report(test_readonly_32(gic_dist_base + GICD_TYPER, false),
507a299895bSThomas Huth                "GICD_TYPER is read-only");
508a299895bSThomas Huth 	report(test_readonly_32(gic_dist_base + GICD_IIDR, false),
509a299895bSThomas Huth                "GICD_IIDR is read-only");
51078ad7e95SAndre Przywara 
51178ad7e95SAndre Przywara 	reg = readl(idreg);
512a299895bSThomas Huth 	report(test_readonly_32(idreg, false), "ICPIDR2 is read-only");
5138e0a4f41SAndre Przywara 	report_info("value of ICPIDR2: 0x%08x", reg);
514ff31a1c4SAndre Przywara 
515ff31a1c4SAndre Przywara 	test_priorities(nr_irqs, gic_dist_base + GICD_IPRIORITYR);
516fe572a5eSAndre Przywara 
517fe572a5eSAndre Przywara 	if (gic_version() == 2)
518fe572a5eSAndre Przywara 		test_targets(nr_irqs);
51978ad7e95SAndre Przywara }
52078ad7e95SAndre Przywara 
521*ba74b106SEric Auger #if defined(__arm__)
522*ba74b106SEric Auger 
523*ba74b106SEric Auger static void test_its_introspection(void) {}
524*ba74b106SEric Auger 
525*ba74b106SEric Auger #else /* __aarch64__ */
526*ba74b106SEric Auger 
527*ba74b106SEric Auger static void test_its_introspection(void)
528*ba74b106SEric Auger {
529*ba74b106SEric Auger 	struct its_baser *dev_baser = &its_data.device_baser;
530*ba74b106SEric Auger 	struct its_baser *coll_baser = &its_data.coll_baser;
531*ba74b106SEric Auger 	struct its_typer *typer = &its_data.typer;
532*ba74b106SEric Auger 
533*ba74b106SEric Auger 	if (!gicv3_its_base()) {
534*ba74b106SEric Auger 		report_skip("No ITS, skip ...");
535*ba74b106SEric Auger 		return;
536*ba74b106SEric Auger 	}
537*ba74b106SEric Auger 
538*ba74b106SEric Auger 	/* IIDR */
539*ba74b106SEric Auger 	report(test_readonly_32(gicv3_its_base() + GITS_IIDR, false),
540*ba74b106SEric Auger 	       "GITS_IIDR is read-only"),
541*ba74b106SEric Auger 
542*ba74b106SEric Auger 	/* TYPER */
543*ba74b106SEric Auger 	report(test_readonly_32(gicv3_its_base() + GITS_TYPER, false),
544*ba74b106SEric Auger 	       "GITS_TYPER is read-only");
545*ba74b106SEric Auger 
546*ba74b106SEric Auger 	report(typer->phys_lpi, "ITS supports physical LPIs");
547*ba74b106SEric Auger 	report_info("vLPI support: %s", typer->virt_lpi ? "yes" : "no");
548*ba74b106SEric Auger 	report_info("ITT entry size = 0x%x", typer->ite_size);
549*ba74b106SEric Auger 	report_info("Bit Count: EventID=%d DeviceId=%d CollId=%d",
550*ba74b106SEric Auger 		    typer->eventid_bits, typer->deviceid_bits,
551*ba74b106SEric Auger 		    typer->collid_bits);
552*ba74b106SEric Auger 	report(typer->eventid_bits && typer->deviceid_bits &&
553*ba74b106SEric Auger 	       typer->collid_bits, "ID spaces");
554*ba74b106SEric Auger 	report_info("Target address format %s",
555*ba74b106SEric Auger 			typer->pta ? "Redist base address" : "PE #");
556*ba74b106SEric Auger 
557*ba74b106SEric Auger 	report(dev_baser && coll_baser, "detect device and collection BASER");
558*ba74b106SEric Auger 	report_info("device table entry_size = 0x%x", dev_baser->esz);
559*ba74b106SEric Auger 	report_info("collection table entry_size = 0x%x", coll_baser->esz);
560*ba74b106SEric Auger }
561*ba74b106SEric Auger 
562*ba74b106SEric Auger #endif
563*ba74b106SEric Auger 
564ac4a67b6SAndrew Jones int main(int argc, char **argv)
565ac4a67b6SAndrew Jones {
5662e2d471dSAndrew Jones 	if (!gic_init()) {
567ac4a67b6SAndrew Jones 		printf("No supported gic present, skipping tests...\n");
568ac4a67b6SAndrew Jones 		return report_summary();
569ac4a67b6SAndrew Jones 	}
570ac4a67b6SAndrew Jones 
5712b19b829SAndrew Jones 	report_prefix_pushf("gicv%d", gic_version());
572ac4a67b6SAndrew Jones 
5732e2d471dSAndrew Jones 	switch (gic_version()) {
5742e2d471dSAndrew Jones 	case 2:
5752e2d471dSAndrew Jones 		gic = &gicv2;
5762e2d471dSAndrew Jones 		break;
5772e2d471dSAndrew Jones 	case 3:
5782e2d471dSAndrew Jones 		gic = &gicv3;
5792e2d471dSAndrew Jones 		break;
5802e2d471dSAndrew Jones 	}
5812e2d471dSAndrew Jones 
582ac4a67b6SAndrew Jones 	if (argc < 2)
583ac4a67b6SAndrew Jones 		report_abort("no test specified");
584ac4a67b6SAndrew Jones 
585ac4a67b6SAndrew Jones 	if (strcmp(argv[1], "ipi") == 0) {
586ac4a67b6SAndrew Jones 		report_prefix_push(argv[1]);
587ac4a67b6SAndrew Jones 		nr_cpu_check(2);
58800b34f56SAndrew Jones 		on_cpus(ipi_test, NULL);
589c152d8bcSChristoffer Dall 	} else if (strcmp(argv[1], "active") == 0) {
590c152d8bcSChristoffer Dall 		run_active_clear_test();
59178ad7e95SAndre Przywara 	} else if (strcmp(argv[1], "mmio") == 0) {
59278ad7e95SAndre Przywara 		report_prefix_push(argv[1]);
59378ad7e95SAndre Przywara 		gic_test_mmio();
59478ad7e95SAndre Przywara 		report_prefix_pop();
595*ba74b106SEric Auger 	} else if (strcmp(argv[1], "its-introspection") == 0) {
596*ba74b106SEric Auger 		report_prefix_push(argv[1]);
597*ba74b106SEric Auger 		test_its_introspection();
598*ba74b106SEric Auger 		report_prefix_pop();
599ac4a67b6SAndrew Jones 	} else {
600ac4a67b6SAndrew Jones 		report_abort("Unknown subtest '%s'", argv[1]);
601ac4a67b6SAndrew Jones 	}
602ac4a67b6SAndrew Jones 
603ac4a67b6SAndrew Jones 	return report_summary();
604ac4a67b6SAndrew Jones }
605