xref: /kvm-unit-tests/powerpc/mmu.c (revision d4c8e725478d05179b23be44fc61357a92da4912)
1*d4c8e725SNicholas Piggin // SPDX-License-Identifier: GPL-2.0-only
2*d4c8e725SNicholas Piggin /*
3*d4c8e725SNicholas Piggin  * MMU Tests
4*d4c8e725SNicholas Piggin  *
5*d4c8e725SNicholas Piggin  * Copyright 2024 Nicholas Piggin, IBM Corp.
6*d4c8e725SNicholas Piggin  */
7*d4c8e725SNicholas Piggin #include <libcflat.h>
8*d4c8e725SNicholas Piggin #include <asm/atomic.h>
9*d4c8e725SNicholas Piggin #include <asm/barrier.h>
10*d4c8e725SNicholas Piggin #include <asm/processor.h>
11*d4c8e725SNicholas Piggin #include <asm/mmu.h>
12*d4c8e725SNicholas Piggin #include <asm/smp.h>
13*d4c8e725SNicholas Piggin #include <asm/setup.h>
14*d4c8e725SNicholas Piggin #include <asm/ppc_asm.h>
15*d4c8e725SNicholas Piggin #include <vmalloc.h>
16*d4c8e725SNicholas Piggin #include <devicetree.h>
17*d4c8e725SNicholas Piggin 
18*d4c8e725SNicholas Piggin static volatile bool tlbie_test_running = true;
19*d4c8e725SNicholas Piggin static volatile bool tlbie_test_failed = false;
20*d4c8e725SNicholas Piggin static int tlbie_fn_started;
21*d4c8e725SNicholas Piggin 
22*d4c8e725SNicholas Piggin static void *memory;
23*d4c8e725SNicholas Piggin 
trap_handler(struct pt_regs * regs,void * opaque)24*d4c8e725SNicholas Piggin static void trap_handler(struct pt_regs *regs, void *opaque)
25*d4c8e725SNicholas Piggin {
26*d4c8e725SNicholas Piggin 	tlbie_test_failed = true;
27*d4c8e725SNicholas Piggin 	regs_advance_insn(regs);
28*d4c8e725SNicholas Piggin }
29*d4c8e725SNicholas Piggin 
tlbie_fn(int cpu_id)30*d4c8e725SNicholas Piggin static void tlbie_fn(int cpu_id)
31*d4c8e725SNicholas Piggin {
32*d4c8e725SNicholas Piggin 	volatile char *m = memory;
33*d4c8e725SNicholas Piggin 
34*d4c8e725SNicholas Piggin 	setup_mmu(0, NULL);
35*d4c8e725SNicholas Piggin 
36*d4c8e725SNicholas Piggin 	atomic_fetch_inc(&tlbie_fn_started);
37*d4c8e725SNicholas Piggin 	while (tlbie_test_running) {
38*d4c8e725SNicholas Piggin 		unsigned long tmp;
39*d4c8e725SNicholas Piggin 
40*d4c8e725SNicholas Piggin 		/*
41*d4c8e725SNicholas Piggin 		 * This is intended to execuse a QEMU TCG bug by forming a
42*d4c8e725SNicholas Piggin 		 * large TB which can prevent async work from running while the
43*d4c8e725SNicholas Piggin 		 * TB executes, so it could miss a broadcast TLB invalidation
44*d4c8e725SNicholas Piggin 		 * and pick up a stale translation.
45*d4c8e725SNicholas Piggin 		 */
46*d4c8e725SNicholas Piggin 		asm volatile (".rept 256 ; lbz %0,0(%1) ; tdnei %0,0 ; .endr" : "=&r"(tmp) : "r"(m));
47*d4c8e725SNicholas Piggin 	}
48*d4c8e725SNicholas Piggin }
49*d4c8e725SNicholas Piggin 
50*d4c8e725SNicholas Piggin #define ITERS 100000
51*d4c8e725SNicholas Piggin 
test_tlbie(int argc,char ** argv)52*d4c8e725SNicholas Piggin static void test_tlbie(int argc, char **argv)
53*d4c8e725SNicholas Piggin {
54*d4c8e725SNicholas Piggin 	void *m[2];
55*d4c8e725SNicholas Piggin 	phys_addr_t p[2];
56*d4c8e725SNicholas Piggin 	pteval_t pteval[2];
57*d4c8e725SNicholas Piggin 	pteval_t *ptep;
58*d4c8e725SNicholas Piggin 	int i;
59*d4c8e725SNicholas Piggin 
60*d4c8e725SNicholas Piggin 	if (argc > 2)
61*d4c8e725SNicholas Piggin 		report_abort("Unsupported argument: '%s'", argv[2]);
62*d4c8e725SNicholas Piggin 
63*d4c8e725SNicholas Piggin 	if (nr_cpus_present < 2) {
64*d4c8e725SNicholas Piggin 		report_skip("Requires SMP (2 or more CPUs)");
65*d4c8e725SNicholas Piggin 		return;
66*d4c8e725SNicholas Piggin 	}
67*d4c8e725SNicholas Piggin 
68*d4c8e725SNicholas Piggin 	handle_exception(0x700, &trap_handler, NULL);
69*d4c8e725SNicholas Piggin 
70*d4c8e725SNicholas Piggin 	m[0] = alloc_page();
71*d4c8e725SNicholas Piggin 	p[0] = virt_to_phys(m[0]);
72*d4c8e725SNicholas Piggin 	memset(m[0], 0, PAGE_SIZE);
73*d4c8e725SNicholas Piggin 	m[1] = alloc_page();
74*d4c8e725SNicholas Piggin 	p[1] = virt_to_phys(m[1]);
75*d4c8e725SNicholas Piggin 	memset(m[1], 0, PAGE_SIZE);
76*d4c8e725SNicholas Piggin 
77*d4c8e725SNicholas Piggin 	memory = alloc_vpages(1);
78*d4c8e725SNicholas Piggin 	ptep = install_page(NULL, p[0], memory);
79*d4c8e725SNicholas Piggin 	pteval[0] = *ptep;
80*d4c8e725SNicholas Piggin 	assert(ptep == install_page(NULL, p[1], memory));
81*d4c8e725SNicholas Piggin 	pteval[1] = *ptep;
82*d4c8e725SNicholas Piggin 	assert(ptep == install_page(NULL, p[0], memory));
83*d4c8e725SNicholas Piggin 	assert(pteval[0] == *ptep);
84*d4c8e725SNicholas Piggin 	flush_tlb_page((unsigned long)memory);
85*d4c8e725SNicholas Piggin 
86*d4c8e725SNicholas Piggin 	if (!start_all_cpus(tlbie_fn))
87*d4c8e725SNicholas Piggin 		report_abort("Failed to start secondary cpus");
88*d4c8e725SNicholas Piggin 
89*d4c8e725SNicholas Piggin 	while (tlbie_fn_started < nr_cpus_present - 1) {
90*d4c8e725SNicholas Piggin 		cpu_relax();
91*d4c8e725SNicholas Piggin 	}
92*d4c8e725SNicholas Piggin 
93*d4c8e725SNicholas Piggin 	for (i = 0; i < ITERS; i++) {
94*d4c8e725SNicholas Piggin 		*ptep = pteval[1];
95*d4c8e725SNicholas Piggin 		flush_tlb_page((unsigned long)memory);
96*d4c8e725SNicholas Piggin 		*(long *)m[0] = -1;
97*d4c8e725SNicholas Piggin 		barrier();
98*d4c8e725SNicholas Piggin 		*(long *)m[0] = 0;
99*d4c8e725SNicholas Piggin 		barrier();
100*d4c8e725SNicholas Piggin 		*ptep = pteval[0];
101*d4c8e725SNicholas Piggin 		flush_tlb_page((unsigned long)memory);
102*d4c8e725SNicholas Piggin 		*(long *)m[1] = -1;
103*d4c8e725SNicholas Piggin 		barrier();
104*d4c8e725SNicholas Piggin 		*(long *)m[1] = 0;
105*d4c8e725SNicholas Piggin 		barrier();
106*d4c8e725SNicholas Piggin 		if (tlbie_test_failed)
107*d4c8e725SNicholas Piggin 			break;
108*d4c8e725SNicholas Piggin 	}
109*d4c8e725SNicholas Piggin 
110*d4c8e725SNicholas Piggin 	tlbie_test_running = false;
111*d4c8e725SNicholas Piggin 
112*d4c8e725SNicholas Piggin 	stop_all_cpus();
113*d4c8e725SNicholas Piggin 
114*d4c8e725SNicholas Piggin 	handle_exception(0x700, NULL, NULL);
115*d4c8e725SNicholas Piggin 
116*d4c8e725SNicholas Piggin 	/* TCG has a known race invalidating other CPUs */
117*d4c8e725SNicholas Piggin 	report_kfail(host_is_tcg, !tlbie_test_failed, "tlbie");
118*d4c8e725SNicholas Piggin }
119*d4c8e725SNicholas Piggin 
120*d4c8e725SNicholas Piggin #define THIS_ITERS 100000
121*d4c8e725SNicholas Piggin 
test_tlbie_this_cpu(int argc,char ** argv)122*d4c8e725SNicholas Piggin static void test_tlbie_this_cpu(int argc, char **argv)
123*d4c8e725SNicholas Piggin {
124*d4c8e725SNicholas Piggin 	void *m[2];
125*d4c8e725SNicholas Piggin 	phys_addr_t p[2];
126*d4c8e725SNicholas Piggin 	pteval_t pteval[2];
127*d4c8e725SNicholas Piggin 	pteval_t *ptep;
128*d4c8e725SNicholas Piggin 	int i;
129*d4c8e725SNicholas Piggin 	bool success;
130*d4c8e725SNicholas Piggin 
131*d4c8e725SNicholas Piggin 	if (argc > 2)
132*d4c8e725SNicholas Piggin 		report_abort("Unsupported argument: '%s'", argv[2]);
133*d4c8e725SNicholas Piggin 
134*d4c8e725SNicholas Piggin 	m[0] = alloc_page();
135*d4c8e725SNicholas Piggin 	p[0] = virt_to_phys(m[0]);
136*d4c8e725SNicholas Piggin 	memset(m[0], 0, PAGE_SIZE);
137*d4c8e725SNicholas Piggin 	m[1] = alloc_page();
138*d4c8e725SNicholas Piggin 	p[1] = virt_to_phys(m[1]);
139*d4c8e725SNicholas Piggin 	memset(m[1], 0, PAGE_SIZE);
140*d4c8e725SNicholas Piggin 
141*d4c8e725SNicholas Piggin 	memory = alloc_vpages(1);
142*d4c8e725SNicholas Piggin 	ptep = install_page(NULL, p[0], memory);
143*d4c8e725SNicholas Piggin 	pteval[0] = *ptep;
144*d4c8e725SNicholas Piggin 	assert(ptep == install_page(NULL, p[1], memory));
145*d4c8e725SNicholas Piggin 	pteval[1] = *ptep;
146*d4c8e725SNicholas Piggin 	assert(ptep == install_page(NULL, p[0], memory));
147*d4c8e725SNicholas Piggin 	assert(pteval[0] == *ptep);
148*d4c8e725SNicholas Piggin 	flush_tlb_page((unsigned long)memory);
149*d4c8e725SNicholas Piggin 
150*d4c8e725SNicholas Piggin 	*(long *)m[0] = 0;
151*d4c8e725SNicholas Piggin 	*(long *)m[1] = -1;
152*d4c8e725SNicholas Piggin 
153*d4c8e725SNicholas Piggin 	success = true;
154*d4c8e725SNicholas Piggin 	for (i = 0; i < THIS_ITERS; i++) {
155*d4c8e725SNicholas Piggin 		if (*(long *)memory != 0) {
156*d4c8e725SNicholas Piggin 			success = false;
157*d4c8e725SNicholas Piggin 			break;
158*d4c8e725SNicholas Piggin 		}
159*d4c8e725SNicholas Piggin 		*ptep = pteval[1];
160*d4c8e725SNicholas Piggin 		flush_tlb_page_local((unsigned long)memory);
161*d4c8e725SNicholas Piggin 		if (*(long *)memory != -1) {
162*d4c8e725SNicholas Piggin 			success = false;
163*d4c8e725SNicholas Piggin 			break;
164*d4c8e725SNicholas Piggin 		}
165*d4c8e725SNicholas Piggin 		*ptep = pteval[0];
166*d4c8e725SNicholas Piggin 		flush_tlb_page_local((unsigned long)memory);
167*d4c8e725SNicholas Piggin 	}
168*d4c8e725SNicholas Piggin 	report(success, "tlbiel");
169*d4c8e725SNicholas Piggin 
170*d4c8e725SNicholas Piggin 	success = true;
171*d4c8e725SNicholas Piggin 	flush_tlb_page((unsigned long)memory);
172*d4c8e725SNicholas Piggin 	for (i = 0; i < THIS_ITERS; i++) {
173*d4c8e725SNicholas Piggin 		if (*(long *)memory != 0) {
174*d4c8e725SNicholas Piggin 			success = false;
175*d4c8e725SNicholas Piggin 			break;
176*d4c8e725SNicholas Piggin 		}
177*d4c8e725SNicholas Piggin 		*ptep = pteval[1];
178*d4c8e725SNicholas Piggin 		flush_tlb_page((unsigned long)memory);
179*d4c8e725SNicholas Piggin 		if (*(long *)memory != -1) {
180*d4c8e725SNicholas Piggin 			success = false;
181*d4c8e725SNicholas Piggin 			break;
182*d4c8e725SNicholas Piggin 		}
183*d4c8e725SNicholas Piggin 		*ptep = pteval[0];
184*d4c8e725SNicholas Piggin 		flush_tlb_page((unsigned long)memory);
185*d4c8e725SNicholas Piggin 	}
186*d4c8e725SNicholas Piggin 	report(success, "tlbie");
187*d4c8e725SNicholas Piggin }
188*d4c8e725SNicholas Piggin 
189*d4c8e725SNicholas Piggin 
190*d4c8e725SNicholas Piggin struct {
191*d4c8e725SNicholas Piggin 	const char *name;
192*d4c8e725SNicholas Piggin 	void (*func)(int argc, char **argv);
193*d4c8e725SNicholas Piggin } hctests[] = {
194*d4c8e725SNicholas Piggin 	{ "tlbi-this-cpu", test_tlbie_this_cpu },
195*d4c8e725SNicholas Piggin 	{ "tlbi-other-cpu", test_tlbie },
196*d4c8e725SNicholas Piggin 	{ NULL, NULL }
197*d4c8e725SNicholas Piggin };
198*d4c8e725SNicholas Piggin 
main(int argc,char ** argv)199*d4c8e725SNicholas Piggin int main(int argc, char **argv)
200*d4c8e725SNicholas Piggin {
201*d4c8e725SNicholas Piggin 	bool all;
202*d4c8e725SNicholas Piggin 	int i;
203*d4c8e725SNicholas Piggin 
204*d4c8e725SNicholas Piggin 	if (!vm_available()) {
205*d4c8e725SNicholas Piggin 		report_skip("MMU is only supported for radix");
206*d4c8e725SNicholas Piggin 		return 0;
207*d4c8e725SNicholas Piggin 	}
208*d4c8e725SNicholas Piggin 
209*d4c8e725SNicholas Piggin 	setup_vm();
210*d4c8e725SNicholas Piggin 
211*d4c8e725SNicholas Piggin 	all = argc == 1 || !strcmp(argv[1], "all");
212*d4c8e725SNicholas Piggin 
213*d4c8e725SNicholas Piggin 	report_prefix_push("mmu");
214*d4c8e725SNicholas Piggin 
215*d4c8e725SNicholas Piggin 	for (i = 0; hctests[i].name != NULL; i++) {
216*d4c8e725SNicholas Piggin 		if (all || strcmp(argv[1], hctests[i].name) == 0) {
217*d4c8e725SNicholas Piggin 			report_prefix_push(hctests[i].name);
218*d4c8e725SNicholas Piggin 			hctests[i].func(argc, argv);
219*d4c8e725SNicholas Piggin 			report_prefix_pop();
220*d4c8e725SNicholas Piggin 		}
221*d4c8e725SNicholas Piggin 	}
222*d4c8e725SNicholas Piggin 
223*d4c8e725SNicholas Piggin 	report_prefix_pop();
224*d4c8e725SNicholas Piggin 	return report_summary();
225*d4c8e725SNicholas Piggin }
226