1f30a578dSChristophe Leroy // SPDX-License-Identifier: GPL-2.0-or-later
2f30a578dSChristophe Leroy /*
3f30a578dSChristophe Leroy  *  Copyright 2008 Michael Ellerman, IBM Corporation.
4f30a578dSChristophe Leroy  */
5f30a578dSChristophe Leroy 
6f30a578dSChristophe Leroy #include <linux/vmalloc.h>
7f30a578dSChristophe Leroy #include <linux/init.h>
8f30a578dSChristophe Leroy 
9*0c3beacfSMike Rapoport (Microsoft) #include <asm/text-patching.h>
10f30a578dSChristophe Leroy 
instr_is_branch_to_addr(const u32 * instr,unsigned long addr)11f30a578dSChristophe Leroy static int __init instr_is_branch_to_addr(const u32 *instr, unsigned long addr)
12f30a578dSChristophe Leroy {
13f30a578dSChristophe Leroy 	if (instr_is_branch_iform(ppc_inst_read(instr)) ||
14f30a578dSChristophe Leroy 	    instr_is_branch_bform(ppc_inst_read(instr)))
15f30a578dSChristophe Leroy 		return branch_target(instr) == addr;
16f30a578dSChristophe Leroy 
17f30a578dSChristophe Leroy 	return 0;
18f30a578dSChristophe Leroy }
19f30a578dSChristophe Leroy 
test_trampoline(void)20f30a578dSChristophe Leroy static void __init test_trampoline(void)
21f30a578dSChristophe Leroy {
22f30a578dSChristophe Leroy 	asm ("nop;nop;\n");
23f30a578dSChristophe Leroy }
24f30a578dSChristophe Leroy 
25f30a578dSChristophe Leroy #define check(x)	do {	\
26f30a578dSChristophe Leroy 	if (!(x))		\
27f30a578dSChristophe Leroy 		pr_err("code-patching: test failed at line %d\n", __LINE__); \
28f30a578dSChristophe Leroy } while (0)
29f30a578dSChristophe Leroy 
test_branch_iform(void)30f30a578dSChristophe Leroy static void __init test_branch_iform(void)
31f30a578dSChristophe Leroy {
32f30a578dSChristophe Leroy 	int err;
33f30a578dSChristophe Leroy 	ppc_inst_t instr;
34f30a578dSChristophe Leroy 	u32 tmp[2];
35f30a578dSChristophe Leroy 	u32 *iptr = tmp;
36f30a578dSChristophe Leroy 	unsigned long addr = (unsigned long)tmp;
37f30a578dSChristophe Leroy 
38f30a578dSChristophe Leroy 	/* The simplest case, branch to self, no flags */
39f30a578dSChristophe Leroy 	check(instr_is_branch_iform(ppc_inst(0x48000000)));
40f30a578dSChristophe Leroy 	/* All bits of target set, and flags */
41f30a578dSChristophe Leroy 	check(instr_is_branch_iform(ppc_inst(0x4bffffff)));
42f30a578dSChristophe Leroy 	/* High bit of opcode set, which is wrong */
43f30a578dSChristophe Leroy 	check(!instr_is_branch_iform(ppc_inst(0xcbffffff)));
44f30a578dSChristophe Leroy 	/* Middle bits of opcode set, which is wrong */
45f30a578dSChristophe Leroy 	check(!instr_is_branch_iform(ppc_inst(0x7bffffff)));
46f30a578dSChristophe Leroy 
47f30a578dSChristophe Leroy 	/* Simplest case, branch to self with link */
48f30a578dSChristophe Leroy 	check(instr_is_branch_iform(ppc_inst(0x48000001)));
49f30a578dSChristophe Leroy 	/* All bits of targets set */
50f30a578dSChristophe Leroy 	check(instr_is_branch_iform(ppc_inst(0x4bfffffd)));
51f30a578dSChristophe Leroy 	/* Some bits of targets set */
52f30a578dSChristophe Leroy 	check(instr_is_branch_iform(ppc_inst(0x4bff00fd)));
53f30a578dSChristophe Leroy 	/* Must be a valid branch to start with */
54f30a578dSChristophe Leroy 	check(!instr_is_branch_iform(ppc_inst(0x7bfffffd)));
55f30a578dSChristophe Leroy 
56f30a578dSChristophe Leroy 	/* Absolute branch to 0x100 */
57309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x48000103));
58f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, 0x100));
59f30a578dSChristophe Leroy 	/* Absolute branch to 0x420fc */
60309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x480420ff));
61f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, 0x420fc));
62f30a578dSChristophe Leroy 	/* Maximum positive relative branch, + 20MB - 4B */
63309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x49fffffc));
64f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr + 0x1FFFFFC));
65f30a578dSChristophe Leroy 	/* Smallest negative relative branch, - 4B */
66309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x4bfffffc));
67f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr - 4));
68f30a578dSChristophe Leroy 	/* Largest negative relative branch, - 32 MB */
69309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x4a000000));
70f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr - 0x2000000));
71f30a578dSChristophe Leroy 
72f30a578dSChristophe Leroy 	/* Branch to self, with link */
73f30a578dSChristophe Leroy 	err = create_branch(&instr, iptr, addr, BRANCH_SET_LINK);
74309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
75f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr));
76f30a578dSChristophe Leroy 
77f30a578dSChristophe Leroy 	/* Branch to self - 0x100, with link */
78f30a578dSChristophe Leroy 	err = create_branch(&instr, iptr, addr - 0x100, BRANCH_SET_LINK);
79309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
80f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr - 0x100));
81f30a578dSChristophe Leroy 
82f30a578dSChristophe Leroy 	/* Branch to self + 0x100, no link */
83f30a578dSChristophe Leroy 	err = create_branch(&instr, iptr, addr + 0x100, 0);
84309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
85f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr + 0x100));
86f30a578dSChristophe Leroy 
87f30a578dSChristophe Leroy 	/* Maximum relative negative offset, - 32 MB */
88f30a578dSChristophe Leroy 	err = create_branch(&instr, iptr, addr - 0x2000000, BRANCH_SET_LINK);
89309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
90f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr - 0x2000000));
91f30a578dSChristophe Leroy 
92f30a578dSChristophe Leroy 	/* Out of range relative negative offset, - 32 MB + 4*/
93f30a578dSChristophe Leroy 	err = create_branch(&instr, iptr, addr - 0x2000004, BRANCH_SET_LINK);
94f30a578dSChristophe Leroy 	check(err);
95f30a578dSChristophe Leroy 
96f30a578dSChristophe Leroy 	/* Out of range relative positive offset, + 32 MB */
97f30a578dSChristophe Leroy 	err = create_branch(&instr, iptr, addr + 0x2000000, BRANCH_SET_LINK);
98f30a578dSChristophe Leroy 	check(err);
99f30a578dSChristophe Leroy 
100f30a578dSChristophe Leroy 	/* Unaligned target */
101f30a578dSChristophe Leroy 	err = create_branch(&instr, iptr, addr + 3, BRANCH_SET_LINK);
102f30a578dSChristophe Leroy 	check(err);
103f30a578dSChristophe Leroy 
104f30a578dSChristophe Leroy 	/* Check flags are masked correctly */
105f30a578dSChristophe Leroy 	err = create_branch(&instr, iptr, addr, 0xFFFFFFFC);
106309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
107f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr));
108f30a578dSChristophe Leroy 	check(ppc_inst_equal(instr, ppc_inst(0x48000000)));
109f30a578dSChristophe Leroy }
110f30a578dSChristophe Leroy 
test_create_function_call(void)111f30a578dSChristophe Leroy static void __init test_create_function_call(void)
112f30a578dSChristophe Leroy {
113f30a578dSChristophe Leroy 	u32 *iptr;
114f30a578dSChristophe Leroy 	unsigned long dest;
115f30a578dSChristophe Leroy 	ppc_inst_t instr;
116f30a578dSChristophe Leroy 
117f30a578dSChristophe Leroy 	/* Check we can create a function call */
118f30a578dSChristophe Leroy 	iptr = (u32 *)ppc_function_entry(test_trampoline);
119f30a578dSChristophe Leroy 	dest = ppc_function_entry(test_create_function_call);
120f30a578dSChristophe Leroy 	create_branch(&instr, iptr, dest, BRANCH_SET_LINK);
121f30a578dSChristophe Leroy 	patch_instruction(iptr, instr);
122f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, dest));
123f30a578dSChristophe Leroy }
124f30a578dSChristophe Leroy 
test_branch_bform(void)125f30a578dSChristophe Leroy static void __init test_branch_bform(void)
126f30a578dSChristophe Leroy {
127f30a578dSChristophe Leroy 	int err;
128f30a578dSChristophe Leroy 	unsigned long addr;
129f30a578dSChristophe Leroy 	ppc_inst_t instr;
130f30a578dSChristophe Leroy 	u32 tmp[2];
131f30a578dSChristophe Leroy 	u32 *iptr = tmp;
132f30a578dSChristophe Leroy 	unsigned int flags;
133f30a578dSChristophe Leroy 
134f30a578dSChristophe Leroy 	addr = (unsigned long)iptr;
135f30a578dSChristophe Leroy 
136f30a578dSChristophe Leroy 	/* The simplest case, branch to self, no flags */
137f30a578dSChristophe Leroy 	check(instr_is_branch_bform(ppc_inst(0x40000000)));
138f30a578dSChristophe Leroy 	/* All bits of target set, and flags */
139f30a578dSChristophe Leroy 	check(instr_is_branch_bform(ppc_inst(0x43ffffff)));
140f30a578dSChristophe Leroy 	/* High bit of opcode set, which is wrong */
141f30a578dSChristophe Leroy 	check(!instr_is_branch_bform(ppc_inst(0xc3ffffff)));
142f30a578dSChristophe Leroy 	/* Middle bits of opcode set, which is wrong */
143f30a578dSChristophe Leroy 	check(!instr_is_branch_bform(ppc_inst(0x7bffffff)));
144f30a578dSChristophe Leroy 
145f30a578dSChristophe Leroy 	/* Absolute conditional branch to 0x100 */
146309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x43ff0103));
147f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, 0x100));
148f30a578dSChristophe Leroy 	/* Absolute conditional branch to 0x20fc */
149309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x43ff20ff));
150f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, 0x20fc));
151f30a578dSChristophe Leroy 	/* Maximum positive relative conditional branch, + 32 KB - 4B */
152309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x43ff7ffc));
153f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr + 0x7FFC));
154f30a578dSChristophe Leroy 	/* Smallest negative relative conditional branch, - 4B */
155309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x43fffffc));
156f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr - 4));
157f30a578dSChristophe Leroy 	/* Largest negative relative conditional branch, - 32 KB */
158309a0a60SChristophe Leroy 	ppc_inst_write(iptr, ppc_inst(0x43ff8000));
159f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr - 0x8000));
160f30a578dSChristophe Leroy 
161f30a578dSChristophe Leroy 	/* All condition code bits set & link */
162f30a578dSChristophe Leroy 	flags = 0x3ff000 | BRANCH_SET_LINK;
163f30a578dSChristophe Leroy 
164f30a578dSChristophe Leroy 	/* Branch to self */
165f30a578dSChristophe Leroy 	err = create_cond_branch(&instr, iptr, addr, flags);
166309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
167f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr));
168f30a578dSChristophe Leroy 
169f30a578dSChristophe Leroy 	/* Branch to self - 0x100 */
170f30a578dSChristophe Leroy 	err = create_cond_branch(&instr, iptr, addr - 0x100, flags);
171309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
172f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr - 0x100));
173f30a578dSChristophe Leroy 
174f30a578dSChristophe Leroy 	/* Branch to self + 0x100 */
175f30a578dSChristophe Leroy 	err = create_cond_branch(&instr, iptr, addr + 0x100, flags);
176309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
177f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr + 0x100));
178f30a578dSChristophe Leroy 
179f30a578dSChristophe Leroy 	/* Maximum relative negative offset, - 32 KB */
180f30a578dSChristophe Leroy 	err = create_cond_branch(&instr, iptr, addr - 0x8000, flags);
181309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
182f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr - 0x8000));
183f30a578dSChristophe Leroy 
184f30a578dSChristophe Leroy 	/* Out of range relative negative offset, - 32 KB + 4*/
185f30a578dSChristophe Leroy 	err = create_cond_branch(&instr, iptr, addr - 0x8004, flags);
186f30a578dSChristophe Leroy 	check(err);
187f30a578dSChristophe Leroy 
188f30a578dSChristophe Leroy 	/* Out of range relative positive offset, + 32 KB */
189f30a578dSChristophe Leroy 	err = create_cond_branch(&instr, iptr, addr + 0x8000, flags);
190f30a578dSChristophe Leroy 	check(err);
191f30a578dSChristophe Leroy 
192f30a578dSChristophe Leroy 	/* Unaligned target */
193f30a578dSChristophe Leroy 	err = create_cond_branch(&instr, iptr, addr + 3, flags);
194f30a578dSChristophe Leroy 	check(err);
195f30a578dSChristophe Leroy 
196f30a578dSChristophe Leroy 	/* Check flags are masked correctly */
197f30a578dSChristophe Leroy 	err = create_cond_branch(&instr, iptr, addr, 0xFFFFFFFC);
198309a0a60SChristophe Leroy 	ppc_inst_write(iptr, instr);
199f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(iptr, addr));
200f30a578dSChristophe Leroy 	check(ppc_inst_equal(instr, ppc_inst(0x43FF0000)));
201f30a578dSChristophe Leroy }
202f30a578dSChristophe Leroy 
test_translate_branch(void)203f30a578dSChristophe Leroy static void __init test_translate_branch(void)
204f30a578dSChristophe Leroy {
205f30a578dSChristophe Leroy 	unsigned long addr;
206f30a578dSChristophe Leroy 	void *p, *q;
207f30a578dSChristophe Leroy 	ppc_inst_t instr;
208f30a578dSChristophe Leroy 	void *buf;
209f30a578dSChristophe Leroy 
210f30a578dSChristophe Leroy 	buf = vmalloc(PAGE_ALIGN(0x2000000 + 1));
211f30a578dSChristophe Leroy 	check(buf);
212f30a578dSChristophe Leroy 	if (!buf)
213f30a578dSChristophe Leroy 		return;
214f30a578dSChristophe Leroy 
215f30a578dSChristophe Leroy 	/* Simple case, branch to self moved a little */
216f30a578dSChristophe Leroy 	p = buf;
217f30a578dSChristophe Leroy 	addr = (unsigned long)p;
218309a0a60SChristophe Leroy 	create_branch(&instr, p, addr, 0);
219309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
220f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
221f30a578dSChristophe Leroy 	q = p + 4;
222f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
223309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
224f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
225f30a578dSChristophe Leroy 
226f30a578dSChristophe Leroy 	/* Maximum negative case, move b . to addr + 32 MB */
227f30a578dSChristophe Leroy 	p = buf;
228f30a578dSChristophe Leroy 	addr = (unsigned long)p;
229309a0a60SChristophe Leroy 	create_branch(&instr, p, addr, 0);
230309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
231f30a578dSChristophe Leroy 	q = buf + 0x2000000;
232f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
233309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
234f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
235f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
236f30a578dSChristophe Leroy 	check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x4a000000)));
237f30a578dSChristophe Leroy 
238f30a578dSChristophe Leroy 	/* Maximum positive case, move x to x - 32 MB + 4 */
239f30a578dSChristophe Leroy 	p = buf + 0x2000000;
240f30a578dSChristophe Leroy 	addr = (unsigned long)p;
241309a0a60SChristophe Leroy 	create_branch(&instr, p, addr, 0);
242309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
243f30a578dSChristophe Leroy 	q = buf + 4;
244f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
245309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
246f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
247f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
248f30a578dSChristophe Leroy 	check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x49fffffc)));
249f30a578dSChristophe Leroy 
250f30a578dSChristophe Leroy 	/* Jump to x + 16 MB moved to x + 20 MB */
251f30a578dSChristophe Leroy 	p = buf;
252f30a578dSChristophe Leroy 	addr = 0x1000000 + (unsigned long)buf;
253309a0a60SChristophe Leroy 	create_branch(&instr, p, addr, BRANCH_SET_LINK);
254309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
255f30a578dSChristophe Leroy 	q = buf + 0x1400000;
256f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
257309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
258f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
259f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
260f30a578dSChristophe Leroy 
261f30a578dSChristophe Leroy 	/* Jump to x + 16 MB moved to x - 16 MB + 4 */
262f30a578dSChristophe Leroy 	p = buf + 0x1000000;
263f30a578dSChristophe Leroy 	addr = 0x2000000 + (unsigned long)buf;
264309a0a60SChristophe Leroy 	create_branch(&instr, p, addr, 0);
265309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
266f30a578dSChristophe Leroy 	q = buf + 4;
267f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
268309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
269f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
270f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
271f30a578dSChristophe Leroy 
272f30a578dSChristophe Leroy 
273f30a578dSChristophe Leroy 	/* Conditional branch tests */
274f30a578dSChristophe Leroy 
275f30a578dSChristophe Leroy 	/* Simple case, branch to self moved a little */
276f30a578dSChristophe Leroy 	p = buf;
277f30a578dSChristophe Leroy 	addr = (unsigned long)p;
278f30a578dSChristophe Leroy 	create_cond_branch(&instr, p, addr, 0);
279309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
280f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
281f30a578dSChristophe Leroy 	q = buf + 4;
282f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
283309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
284f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
285f30a578dSChristophe Leroy 
286f30a578dSChristophe Leroy 	/* Maximum negative case, move b . to addr + 32 KB */
287f30a578dSChristophe Leroy 	p = buf;
288f30a578dSChristophe Leroy 	addr = (unsigned long)p;
289f30a578dSChristophe Leroy 	create_cond_branch(&instr, p, addr, 0xFFFFFFFC);
290309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
291f30a578dSChristophe Leroy 	q = buf + 0x8000;
292f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
293309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
294f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
295f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
296f30a578dSChristophe Leroy 	check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x43ff8000)));
297f30a578dSChristophe Leroy 
298f30a578dSChristophe Leroy 	/* Maximum positive case, move x to x - 32 KB + 4 */
299f30a578dSChristophe Leroy 	p = buf + 0x8000;
300f30a578dSChristophe Leroy 	addr = (unsigned long)p;
301f30a578dSChristophe Leroy 	create_cond_branch(&instr, p, addr, 0xFFFFFFFC);
302309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
303f30a578dSChristophe Leroy 	q = buf + 4;
304f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
305309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
306f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
307f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
308f30a578dSChristophe Leroy 	check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x43ff7ffc)));
309f30a578dSChristophe Leroy 
310f30a578dSChristophe Leroy 	/* Jump to x + 12 KB moved to x + 20 KB */
311f30a578dSChristophe Leroy 	p = buf;
312f30a578dSChristophe Leroy 	addr = 0x3000 + (unsigned long)buf;
313f30a578dSChristophe Leroy 	create_cond_branch(&instr, p, addr, BRANCH_SET_LINK);
314309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
315f30a578dSChristophe Leroy 	q = buf + 0x5000;
316f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
317309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
318f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
319f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
320f30a578dSChristophe Leroy 
321f30a578dSChristophe Leroy 	/* Jump to x + 8 KB moved to x - 8 KB + 4 */
322f30a578dSChristophe Leroy 	p = buf + 0x2000;
323f30a578dSChristophe Leroy 	addr = 0x4000 + (unsigned long)buf;
324f30a578dSChristophe Leroy 	create_cond_branch(&instr, p, addr, 0);
325309a0a60SChristophe Leroy 	ppc_inst_write(p, instr);
326f30a578dSChristophe Leroy 	q = buf + 4;
327f30a578dSChristophe Leroy 	translate_branch(&instr, q, p);
328309a0a60SChristophe Leroy 	ppc_inst_write(q, instr);
329f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(p, addr));
330f30a578dSChristophe Leroy 	check(instr_is_branch_to_addr(q, addr));
331f30a578dSChristophe Leroy 
332f30a578dSChristophe Leroy 	/* Free the buffer we were using */
333f30a578dSChristophe Leroy 	vfree(buf);
334f30a578dSChristophe Leroy }
335f30a578dSChristophe Leroy 
test_prefixed_patching(void)336f30a578dSChristophe Leroy static void __init test_prefixed_patching(void)
337f30a578dSChristophe Leroy {
338f30a578dSChristophe Leroy 	u32 *iptr = (u32 *)ppc_function_entry(test_trampoline);
339f30a578dSChristophe Leroy 	u32 expected[2] = {OP_PREFIX << 26, 0};
340f30a578dSChristophe Leroy 	ppc_inst_t inst = ppc_inst_prefix(OP_PREFIX << 26, 0);
341f30a578dSChristophe Leroy 
342f30a578dSChristophe Leroy 	if (!IS_ENABLED(CONFIG_PPC64))
343f30a578dSChristophe Leroy 		return;
344f30a578dSChristophe Leroy 
345f30a578dSChristophe Leroy 	patch_instruction(iptr, inst);
346f30a578dSChristophe Leroy 
347f30a578dSChristophe Leroy 	check(!memcmp(iptr, expected, sizeof(expected)));
348f30a578dSChristophe Leroy }
349f30a578dSChristophe Leroy 
test_multi_instruction_patching(void)350c5ef5e35SBenjamin Gray static void __init test_multi_instruction_patching(void)
351c5ef5e35SBenjamin Gray {
352c5ef5e35SBenjamin Gray 	u32 code[32];
353c5ef5e35SBenjamin Gray 	void *buf;
354c5ef5e35SBenjamin Gray 	u32 *addr32;
355c5ef5e35SBenjamin Gray 	u64 *addr64;
356c5ef5e35SBenjamin Gray 	ppc_inst_t inst64 = ppc_inst_prefix(OP_PREFIX << 26 | 3UL << 24, PPC_RAW_TRAP());
357c5ef5e35SBenjamin Gray 	u32 inst32 = PPC_RAW_NOP();
358c5ef5e35SBenjamin Gray 
359c5ef5e35SBenjamin Gray 	buf = vzalloc(PAGE_SIZE * 8);
360c5ef5e35SBenjamin Gray 	check(buf);
361c5ef5e35SBenjamin Gray 	if (!buf)
362c5ef5e35SBenjamin Gray 		return;
363c5ef5e35SBenjamin Gray 
364c5ef5e35SBenjamin Gray 	/* Test single page 32-bit repeated instruction */
365c5ef5e35SBenjamin Gray 	addr32 = buf + PAGE_SIZE;
366c5ef5e35SBenjamin Gray 	check(!patch_instructions(addr32 + 1, &inst32, 12, true));
367c5ef5e35SBenjamin Gray 
368c5ef5e35SBenjamin Gray 	check(addr32[0] == 0);
369c5ef5e35SBenjamin Gray 	check(addr32[1] == inst32);
370c5ef5e35SBenjamin Gray 	check(addr32[2] == inst32);
371c5ef5e35SBenjamin Gray 	check(addr32[3] == inst32);
372c5ef5e35SBenjamin Gray 	check(addr32[4] == 0);
373c5ef5e35SBenjamin Gray 
374c5ef5e35SBenjamin Gray 	/* Test single page 64-bit repeated instruction */
375c5ef5e35SBenjamin Gray 	if (IS_ENABLED(CONFIG_PPC64)) {
376c5ef5e35SBenjamin Gray 		check(ppc_inst_prefixed(inst64));
377c5ef5e35SBenjamin Gray 
378c5ef5e35SBenjamin Gray 		addr64 = buf + PAGE_SIZE * 2;
379c5ef5e35SBenjamin Gray 		ppc_inst_write(code, inst64);
380c5ef5e35SBenjamin Gray 		check(!patch_instructions((u32 *)(addr64 + 1), code, 24, true));
381c5ef5e35SBenjamin Gray 
382c5ef5e35SBenjamin Gray 		check(addr64[0] == 0);
383c5ef5e35SBenjamin Gray 		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[1]), inst64));
384c5ef5e35SBenjamin Gray 		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[2]), inst64));
385c5ef5e35SBenjamin Gray 		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[3]), inst64));
386c5ef5e35SBenjamin Gray 		check(addr64[4] == 0);
387c5ef5e35SBenjamin Gray 	}
388c5ef5e35SBenjamin Gray 
389c5ef5e35SBenjamin Gray 	/* Test single page memcpy */
390c5ef5e35SBenjamin Gray 	addr32 = buf + PAGE_SIZE * 3;
391c5ef5e35SBenjamin Gray 
392c5ef5e35SBenjamin Gray 	for (int i = 0; i < ARRAY_SIZE(code); i++)
393c5ef5e35SBenjamin Gray 		code[i] = i + 1;
394c5ef5e35SBenjamin Gray 
395c5ef5e35SBenjamin Gray 	check(!patch_instructions(addr32 + 1, code, sizeof(code), false));
396c5ef5e35SBenjamin Gray 
397c5ef5e35SBenjamin Gray 	check(addr32[0] == 0);
398c5ef5e35SBenjamin Gray 	check(!memcmp(&addr32[1], code, sizeof(code)));
399c5ef5e35SBenjamin Gray 	check(addr32[ARRAY_SIZE(code) + 1] == 0);
400c5ef5e35SBenjamin Gray 
401c5ef5e35SBenjamin Gray 	/* Test multipage 32-bit repeated instruction */
402c5ef5e35SBenjamin Gray 	addr32 = buf + PAGE_SIZE * 4 - 8;
403c5ef5e35SBenjamin Gray 	check(!patch_instructions(addr32 + 1, &inst32, 12, true));
404c5ef5e35SBenjamin Gray 
405c5ef5e35SBenjamin Gray 	check(addr32[0] == 0);
406c5ef5e35SBenjamin Gray 	check(addr32[1] == inst32);
407c5ef5e35SBenjamin Gray 	check(addr32[2] == inst32);
408c5ef5e35SBenjamin Gray 	check(addr32[3] == inst32);
409c5ef5e35SBenjamin Gray 	check(addr32[4] == 0);
410c5ef5e35SBenjamin Gray 
411c5ef5e35SBenjamin Gray 	/* Test multipage 64-bit repeated instruction */
412c5ef5e35SBenjamin Gray 	if (IS_ENABLED(CONFIG_PPC64)) {
413c5ef5e35SBenjamin Gray 		check(ppc_inst_prefixed(inst64));
414c5ef5e35SBenjamin Gray 
415c5ef5e35SBenjamin Gray 		addr64 = buf + PAGE_SIZE * 5 - 8;
416c5ef5e35SBenjamin Gray 		ppc_inst_write(code, inst64);
417c5ef5e35SBenjamin Gray 		check(!patch_instructions((u32 *)(addr64 + 1), code, 24, true));
418c5ef5e35SBenjamin Gray 
419c5ef5e35SBenjamin Gray 		check(addr64[0] == 0);
420c5ef5e35SBenjamin Gray 		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[1]), inst64));
421c5ef5e35SBenjamin Gray 		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[2]), inst64));
422c5ef5e35SBenjamin Gray 		check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[3]), inst64));
423c5ef5e35SBenjamin Gray 		check(addr64[4] == 0);
424c5ef5e35SBenjamin Gray 	}
425c5ef5e35SBenjamin Gray 
426c5ef5e35SBenjamin Gray 	/* Test multipage memcpy */
427c5ef5e35SBenjamin Gray 	addr32 = buf + PAGE_SIZE * 6 - 12;
428c5ef5e35SBenjamin Gray 
429c5ef5e35SBenjamin Gray 	for (int i = 0; i < ARRAY_SIZE(code); i++)
430c5ef5e35SBenjamin Gray 		code[i] = i + 1;
431c5ef5e35SBenjamin Gray 
432c5ef5e35SBenjamin Gray 	check(!patch_instructions(addr32 + 1, code, sizeof(code), false));
433c5ef5e35SBenjamin Gray 
434c5ef5e35SBenjamin Gray 	check(addr32[0] == 0);
435c5ef5e35SBenjamin Gray 	check(!memcmp(&addr32[1], code, sizeof(code)));
436c5ef5e35SBenjamin Gray 	check(addr32[ARRAY_SIZE(code) + 1] == 0);
437c5ef5e35SBenjamin Gray 
438c5ef5e35SBenjamin Gray 	vfree(buf);
439c5ef5e35SBenjamin Gray }
440c5ef5e35SBenjamin Gray 
test_data_patching(void)441b7d47339SBenjamin Gray static void __init test_data_patching(void)
442b7d47339SBenjamin Gray {
443b7d47339SBenjamin Gray 	void *buf;
444b7d47339SBenjamin Gray 	u32 *addr32;
445b7d47339SBenjamin Gray 
446b7d47339SBenjamin Gray 	buf = vzalloc(PAGE_SIZE);
447b7d47339SBenjamin Gray 	check(buf);
448b7d47339SBenjamin Gray 	if (!buf)
449b7d47339SBenjamin Gray 		return;
450b7d47339SBenjamin Gray 
451b7d47339SBenjamin Gray 	addr32 = buf + 128;
452b7d47339SBenjamin Gray 
453b7d47339SBenjamin Gray 	addr32[1] = 0xA0A1A2A3;
454b7d47339SBenjamin Gray 	addr32[2] = 0xB0B1B2B3;
455b7d47339SBenjamin Gray 
456b7d47339SBenjamin Gray 	check(!patch_uint(&addr32[1], 0xC0C1C2C3));
457b7d47339SBenjamin Gray 
458b7d47339SBenjamin Gray 	check(addr32[0] == 0);
459b7d47339SBenjamin Gray 	check(addr32[1] == 0xC0C1C2C3);
460b7d47339SBenjamin Gray 	check(addr32[2] == 0xB0B1B2B3);
461b7d47339SBenjamin Gray 	check(addr32[3] == 0);
462b7d47339SBenjamin Gray 
463b7d47339SBenjamin Gray 	/* Unaligned patch_ulong() should fail */
464b7d47339SBenjamin Gray 	if (IS_ENABLED(CONFIG_PPC64))
465b7d47339SBenjamin Gray 		check(patch_ulong(&addr32[1], 0xD0D1D2D3) == -EINVAL);
466b7d47339SBenjamin Gray 
467b7d47339SBenjamin Gray 	check(!patch_ulong(&addr32[2], 0xD0D1D2D3));
468b7d47339SBenjamin Gray 
469b7d47339SBenjamin Gray 	check(addr32[0] == 0);
470b7d47339SBenjamin Gray 	check(addr32[1] == 0xC0C1C2C3);
471b7d47339SBenjamin Gray 	check(*(unsigned long *)(&addr32[2]) == 0xD0D1D2D3);
472b7d47339SBenjamin Gray 
473b7d47339SBenjamin Gray 	if (!IS_ENABLED(CONFIG_PPC64))
474b7d47339SBenjamin Gray 		check(addr32[3] == 0);
475b7d47339SBenjamin Gray 
476b7d47339SBenjamin Gray 	check(addr32[4] == 0);
477b7d47339SBenjamin Gray 
478b7d47339SBenjamin Gray 	vfree(buf);
479b7d47339SBenjamin Gray }
480b7d47339SBenjamin Gray 
test_code_patching(void)481f30a578dSChristophe Leroy static int __init test_code_patching(void)
482f30a578dSChristophe Leroy {
483f30a578dSChristophe Leroy 	pr_info("Running code patching self-tests ...\n");
484f30a578dSChristophe Leroy 
485f30a578dSChristophe Leroy 	test_branch_iform();
486f30a578dSChristophe Leroy 	test_branch_bform();
487f30a578dSChristophe Leroy 	test_create_function_call();
488f30a578dSChristophe Leroy 	test_translate_branch();
489f30a578dSChristophe Leroy 	test_prefixed_patching();
490c5ef5e35SBenjamin Gray 	test_multi_instruction_patching();
491b7d47339SBenjamin Gray 	test_data_patching();
492f30a578dSChristophe Leroy 
493f30a578dSChristophe Leroy 	return 0;
494f30a578dSChristophe Leroy }
495f30a578dSChristophe Leroy late_initcall(test_code_patching);
496