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