1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2008 Michael Ellerman, IBM Corporation. |
4 | */ |
5 | |
6 | #include <linux/vmalloc.h> |
7 | #include <linux/init.h> |
8 | |
9 | #include <asm/code-patching.h> |
10 | |
11 | static int __init instr_is_branch_to_addr(const u32 *instr, unsigned long addr) |
12 | { |
13 | if (instr_is_branch_iform(ppc_inst_read(instr)) || |
14 | instr_is_branch_bform(ppc_inst_read(instr))) |
15 | return branch_target(instr) == addr; |
16 | |
17 | return 0; |
18 | } |
19 | |
20 | static void __init test_trampoline(void) |
21 | { |
22 | asm ("nop;nop;\n" ); |
23 | } |
24 | |
25 | #define check(x) do { \ |
26 | if (!(x)) \ |
27 | pr_err("code-patching: test failed at line %d\n", __LINE__); \ |
28 | } while (0) |
29 | |
30 | static void __init test_branch_iform(void) |
31 | { |
32 | int err; |
33 | ppc_inst_t instr; |
34 | u32 tmp[2]; |
35 | u32 *iptr = tmp; |
36 | unsigned long addr = (unsigned long)tmp; |
37 | |
38 | /* The simplest case, branch to self, no flags */ |
39 | check(instr_is_branch_iform(ppc_inst(0x48000000))); |
40 | /* All bits of target set, and flags */ |
41 | check(instr_is_branch_iform(ppc_inst(0x4bffffff))); |
42 | /* High bit of opcode set, which is wrong */ |
43 | check(!instr_is_branch_iform(ppc_inst(0xcbffffff))); |
44 | /* Middle bits of opcode set, which is wrong */ |
45 | check(!instr_is_branch_iform(ppc_inst(0x7bffffff))); |
46 | |
47 | /* Simplest case, branch to self with link */ |
48 | check(instr_is_branch_iform(ppc_inst(0x48000001))); |
49 | /* All bits of targets set */ |
50 | check(instr_is_branch_iform(ppc_inst(0x4bfffffd))); |
51 | /* Some bits of targets set */ |
52 | check(instr_is_branch_iform(ppc_inst(0x4bff00fd))); |
53 | /* Must be a valid branch to start with */ |
54 | check(!instr_is_branch_iform(ppc_inst(0x7bfffffd))); |
55 | |
56 | /* Absolute branch to 0x100 */ |
57 | ppc_inst_write(iptr, ppc_inst(0x48000103)); |
58 | check(instr_is_branch_to_addr(iptr, 0x100)); |
59 | /* Absolute branch to 0x420fc */ |
60 | ppc_inst_write(iptr, ppc_inst(0x480420ff)); |
61 | check(instr_is_branch_to_addr(iptr, 0x420fc)); |
62 | /* Maximum positive relative branch, + 20MB - 4B */ |
63 | ppc_inst_write(iptr, ppc_inst(0x49fffffc)); |
64 | check(instr_is_branch_to_addr(iptr, addr + 0x1FFFFFC)); |
65 | /* Smallest negative relative branch, - 4B */ |
66 | ppc_inst_write(iptr, ppc_inst(0x4bfffffc)); |
67 | check(instr_is_branch_to_addr(iptr, addr - 4)); |
68 | /* Largest negative relative branch, - 32 MB */ |
69 | ppc_inst_write(iptr, ppc_inst(0x4a000000)); |
70 | check(instr_is_branch_to_addr(iptr, addr - 0x2000000)); |
71 | |
72 | /* Branch to self, with link */ |
73 | err = create_branch(&instr, iptr, addr, BRANCH_SET_LINK); |
74 | ppc_inst_write(iptr, instr); |
75 | check(instr_is_branch_to_addr(iptr, addr)); |
76 | |
77 | /* Branch to self - 0x100, with link */ |
78 | err = create_branch(&instr, iptr, addr - 0x100, BRANCH_SET_LINK); |
79 | ppc_inst_write(iptr, instr); |
80 | check(instr_is_branch_to_addr(iptr, addr - 0x100)); |
81 | |
82 | /* Branch to self + 0x100, no link */ |
83 | err = create_branch(&instr, iptr, addr + 0x100, 0); |
84 | ppc_inst_write(iptr, instr); |
85 | check(instr_is_branch_to_addr(iptr, addr + 0x100)); |
86 | |
87 | /* Maximum relative negative offset, - 32 MB */ |
88 | err = create_branch(&instr, iptr, addr - 0x2000000, BRANCH_SET_LINK); |
89 | ppc_inst_write(iptr, instr); |
90 | check(instr_is_branch_to_addr(iptr, addr - 0x2000000)); |
91 | |
92 | /* Out of range relative negative offset, - 32 MB + 4*/ |
93 | err = create_branch(&instr, iptr, addr - 0x2000004, BRANCH_SET_LINK); |
94 | check(err); |
95 | |
96 | /* Out of range relative positive offset, + 32 MB */ |
97 | err = create_branch(&instr, iptr, addr + 0x2000000, BRANCH_SET_LINK); |
98 | check(err); |
99 | |
100 | /* Unaligned target */ |
101 | err = create_branch(&instr, iptr, addr + 3, BRANCH_SET_LINK); |
102 | check(err); |
103 | |
104 | /* Check flags are masked correctly */ |
105 | err = create_branch(&instr, iptr, addr, 0xFFFFFFFC); |
106 | ppc_inst_write(iptr, instr); |
107 | check(instr_is_branch_to_addr(iptr, addr)); |
108 | check(ppc_inst_equal(instr, ppc_inst(0x48000000))); |
109 | } |
110 | |
111 | static void __init test_create_function_call(void) |
112 | { |
113 | u32 *iptr; |
114 | unsigned long dest; |
115 | ppc_inst_t instr; |
116 | |
117 | /* Check we can create a function call */ |
118 | iptr = (u32 *)ppc_function_entry(test_trampoline); |
119 | dest = ppc_function_entry(test_create_function_call); |
120 | create_branch(&instr, iptr, dest, BRANCH_SET_LINK); |
121 | patch_instruction(iptr, instr); |
122 | check(instr_is_branch_to_addr(iptr, dest)); |
123 | } |
124 | |
125 | static void __init test_branch_bform(void) |
126 | { |
127 | int err; |
128 | unsigned long addr; |
129 | ppc_inst_t instr; |
130 | u32 tmp[2]; |
131 | u32 *iptr = tmp; |
132 | unsigned int flags; |
133 | |
134 | addr = (unsigned long)iptr; |
135 | |
136 | /* The simplest case, branch to self, no flags */ |
137 | check(instr_is_branch_bform(ppc_inst(0x40000000))); |
138 | /* All bits of target set, and flags */ |
139 | check(instr_is_branch_bform(ppc_inst(0x43ffffff))); |
140 | /* High bit of opcode set, which is wrong */ |
141 | check(!instr_is_branch_bform(ppc_inst(0xc3ffffff))); |
142 | /* Middle bits of opcode set, which is wrong */ |
143 | check(!instr_is_branch_bform(ppc_inst(0x7bffffff))); |
144 | |
145 | /* Absolute conditional branch to 0x100 */ |
146 | ppc_inst_write(iptr, ppc_inst(0x43ff0103)); |
147 | check(instr_is_branch_to_addr(iptr, 0x100)); |
148 | /* Absolute conditional branch to 0x20fc */ |
149 | ppc_inst_write(iptr, ppc_inst(0x43ff20ff)); |
150 | check(instr_is_branch_to_addr(iptr, 0x20fc)); |
151 | /* Maximum positive relative conditional branch, + 32 KB - 4B */ |
152 | ppc_inst_write(iptr, ppc_inst(0x43ff7ffc)); |
153 | check(instr_is_branch_to_addr(iptr, addr + 0x7FFC)); |
154 | /* Smallest negative relative conditional branch, - 4B */ |
155 | ppc_inst_write(iptr, ppc_inst(0x43fffffc)); |
156 | check(instr_is_branch_to_addr(iptr, addr - 4)); |
157 | /* Largest negative relative conditional branch, - 32 KB */ |
158 | ppc_inst_write(iptr, ppc_inst(0x43ff8000)); |
159 | check(instr_is_branch_to_addr(iptr, addr - 0x8000)); |
160 | |
161 | /* All condition code bits set & link */ |
162 | flags = 0x3ff000 | BRANCH_SET_LINK; |
163 | |
164 | /* Branch to self */ |
165 | err = create_cond_branch(&instr, iptr, addr, flags); |
166 | ppc_inst_write(iptr, instr); |
167 | check(instr_is_branch_to_addr(iptr, addr)); |
168 | |
169 | /* Branch to self - 0x100 */ |
170 | err = create_cond_branch(&instr, iptr, addr - 0x100, flags); |
171 | ppc_inst_write(iptr, instr); |
172 | check(instr_is_branch_to_addr(iptr, addr - 0x100)); |
173 | |
174 | /* Branch to self + 0x100 */ |
175 | err = create_cond_branch(&instr, iptr, addr + 0x100, flags); |
176 | ppc_inst_write(iptr, instr); |
177 | check(instr_is_branch_to_addr(iptr, addr + 0x100)); |
178 | |
179 | /* Maximum relative negative offset, - 32 KB */ |
180 | err = create_cond_branch(&instr, iptr, addr - 0x8000, flags); |
181 | ppc_inst_write(iptr, instr); |
182 | check(instr_is_branch_to_addr(iptr, addr - 0x8000)); |
183 | |
184 | /* Out of range relative negative offset, - 32 KB + 4*/ |
185 | err = create_cond_branch(&instr, iptr, addr - 0x8004, flags); |
186 | check(err); |
187 | |
188 | /* Out of range relative positive offset, + 32 KB */ |
189 | err = create_cond_branch(&instr, iptr, addr + 0x8000, flags); |
190 | check(err); |
191 | |
192 | /* Unaligned target */ |
193 | err = create_cond_branch(&instr, iptr, addr + 3, flags); |
194 | check(err); |
195 | |
196 | /* Check flags are masked correctly */ |
197 | err = create_cond_branch(&instr, iptr, addr, 0xFFFFFFFC); |
198 | ppc_inst_write(iptr, instr); |
199 | check(instr_is_branch_to_addr(iptr, addr)); |
200 | check(ppc_inst_equal(instr, ppc_inst(0x43FF0000))); |
201 | } |
202 | |
203 | static void __init test_translate_branch(void) |
204 | { |
205 | unsigned long addr; |
206 | void *p, *q; |
207 | ppc_inst_t instr; |
208 | void *buf; |
209 | |
210 | buf = vmalloc(size: PAGE_ALIGN(0x2000000 + 1)); |
211 | check(buf); |
212 | if (!buf) |
213 | return; |
214 | |
215 | /* Simple case, branch to self moved a little */ |
216 | p = buf; |
217 | addr = (unsigned long)p; |
218 | create_branch(&instr, p, addr, 0); |
219 | ppc_inst_write(p, instr); |
220 | check(instr_is_branch_to_addr(p, addr)); |
221 | q = p + 4; |
222 | translate_branch(&instr, q, p); |
223 | ppc_inst_write(q, instr); |
224 | check(instr_is_branch_to_addr(q, addr)); |
225 | |
226 | /* Maximum negative case, move b . to addr + 32 MB */ |
227 | p = buf; |
228 | addr = (unsigned long)p; |
229 | create_branch(&instr, p, addr, 0); |
230 | ppc_inst_write(p, instr); |
231 | q = buf + 0x2000000; |
232 | translate_branch(&instr, q, p); |
233 | ppc_inst_write(q, instr); |
234 | check(instr_is_branch_to_addr(p, addr)); |
235 | check(instr_is_branch_to_addr(q, addr)); |
236 | check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x4a000000))); |
237 | |
238 | /* Maximum positive case, move x to x - 32 MB + 4 */ |
239 | p = buf + 0x2000000; |
240 | addr = (unsigned long)p; |
241 | create_branch(&instr, p, addr, 0); |
242 | ppc_inst_write(p, instr); |
243 | q = buf + 4; |
244 | translate_branch(&instr, q, p); |
245 | ppc_inst_write(q, instr); |
246 | check(instr_is_branch_to_addr(p, addr)); |
247 | check(instr_is_branch_to_addr(q, addr)); |
248 | check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x49fffffc))); |
249 | |
250 | /* Jump to x + 16 MB moved to x + 20 MB */ |
251 | p = buf; |
252 | addr = 0x1000000 + (unsigned long)buf; |
253 | create_branch(&instr, p, addr, BRANCH_SET_LINK); |
254 | ppc_inst_write(p, instr); |
255 | q = buf + 0x1400000; |
256 | translate_branch(&instr, q, p); |
257 | ppc_inst_write(q, instr); |
258 | check(instr_is_branch_to_addr(p, addr)); |
259 | check(instr_is_branch_to_addr(q, addr)); |
260 | |
261 | /* Jump to x + 16 MB moved to x - 16 MB + 4 */ |
262 | p = buf + 0x1000000; |
263 | addr = 0x2000000 + (unsigned long)buf; |
264 | create_branch(&instr, p, addr, 0); |
265 | ppc_inst_write(p, instr); |
266 | q = buf + 4; |
267 | translate_branch(&instr, q, p); |
268 | ppc_inst_write(q, instr); |
269 | check(instr_is_branch_to_addr(p, addr)); |
270 | check(instr_is_branch_to_addr(q, addr)); |
271 | |
272 | |
273 | /* Conditional branch tests */ |
274 | |
275 | /* Simple case, branch to self moved a little */ |
276 | p = buf; |
277 | addr = (unsigned long)p; |
278 | create_cond_branch(&instr, p, addr, 0); |
279 | ppc_inst_write(p, instr); |
280 | check(instr_is_branch_to_addr(p, addr)); |
281 | q = buf + 4; |
282 | translate_branch(&instr, q, p); |
283 | ppc_inst_write(q, instr); |
284 | check(instr_is_branch_to_addr(q, addr)); |
285 | |
286 | /* Maximum negative case, move b . to addr + 32 KB */ |
287 | p = buf; |
288 | addr = (unsigned long)p; |
289 | create_cond_branch(&instr, p, addr, 0xFFFFFFFC); |
290 | ppc_inst_write(p, instr); |
291 | q = buf + 0x8000; |
292 | translate_branch(&instr, q, p); |
293 | ppc_inst_write(q, instr); |
294 | check(instr_is_branch_to_addr(p, addr)); |
295 | check(instr_is_branch_to_addr(q, addr)); |
296 | check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x43ff8000))); |
297 | |
298 | /* Maximum positive case, move x to x - 32 KB + 4 */ |
299 | p = buf + 0x8000; |
300 | addr = (unsigned long)p; |
301 | create_cond_branch(&instr, p, addr, 0xFFFFFFFC); |
302 | ppc_inst_write(p, instr); |
303 | q = buf + 4; |
304 | translate_branch(&instr, q, p); |
305 | ppc_inst_write(q, instr); |
306 | check(instr_is_branch_to_addr(p, addr)); |
307 | check(instr_is_branch_to_addr(q, addr)); |
308 | check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x43ff7ffc))); |
309 | |
310 | /* Jump to x + 12 KB moved to x + 20 KB */ |
311 | p = buf; |
312 | addr = 0x3000 + (unsigned long)buf; |
313 | create_cond_branch(&instr, p, addr, BRANCH_SET_LINK); |
314 | ppc_inst_write(p, instr); |
315 | q = buf + 0x5000; |
316 | translate_branch(&instr, q, p); |
317 | ppc_inst_write(q, instr); |
318 | check(instr_is_branch_to_addr(p, addr)); |
319 | check(instr_is_branch_to_addr(q, addr)); |
320 | |
321 | /* Jump to x + 8 KB moved to x - 8 KB + 4 */ |
322 | p = buf + 0x2000; |
323 | addr = 0x4000 + (unsigned long)buf; |
324 | create_cond_branch(&instr, p, addr, 0); |
325 | ppc_inst_write(p, instr); |
326 | q = buf + 4; |
327 | translate_branch(&instr, q, p); |
328 | ppc_inst_write(q, instr); |
329 | check(instr_is_branch_to_addr(p, addr)); |
330 | check(instr_is_branch_to_addr(q, addr)); |
331 | |
332 | /* Free the buffer we were using */ |
333 | vfree(addr: buf); |
334 | } |
335 | |
336 | static void __init test_prefixed_patching(void) |
337 | { |
338 | u32 *iptr = (u32 *)ppc_function_entry(test_trampoline); |
339 | u32 expected[2] = {OP_PREFIX << 26, 0}; |
340 | ppc_inst_t inst = ppc_inst_prefix(OP_PREFIX << 26, 0); |
341 | |
342 | if (!IS_ENABLED(CONFIG_PPC64)) |
343 | return; |
344 | |
345 | patch_instruction(iptr, inst); |
346 | |
347 | check(!memcmp(iptr, expected, sizeof(expected))); |
348 | } |
349 | |
350 | static int __init test_code_patching(void) |
351 | { |
352 | pr_info("Running code patching self-tests ...\n" ); |
353 | |
354 | test_branch_iform(); |
355 | test_branch_bform(); |
356 | test_create_function_call(); |
357 | test_translate_branch(); |
358 | test_prefixed_patching(); |
359 | |
360 | return 0; |
361 | } |
362 | late_initcall(test_code_patching); |
363 | |