~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/lib/test-code-patching.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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(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(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 void __init test_multi_instruction_patching(void)
351 {
352         u32 code[32];
353         void *buf;
354         u32 *addr32;
355         u64 *addr64;
356         ppc_inst_t inst64 = ppc_inst_prefix(OP_PREFIX << 26 | 3UL << 24, PPC_RAW_TRAP());
357         u32 inst32 = PPC_RAW_NOP();
358 
359         buf = vzalloc(PAGE_SIZE * 8);
360         check(buf);
361         if (!buf)
362                 return;
363 
364         /* Test single page 32-bit repeated instruction */
365         addr32 = buf + PAGE_SIZE;
366         check(!patch_instructions(addr32 + 1, &inst32, 12, true));
367 
368         check(addr32[0] == 0);
369         check(addr32[1] == inst32);
370         check(addr32[2] == inst32);
371         check(addr32[3] == inst32);
372         check(addr32[4] == 0);
373 
374         /* Test single page 64-bit repeated instruction */
375         if (IS_ENABLED(CONFIG_PPC64)) {
376                 check(ppc_inst_prefixed(inst64));
377 
378                 addr64 = buf + PAGE_SIZE * 2;
379                 ppc_inst_write(code, inst64);
380                 check(!patch_instructions((u32 *)(addr64 + 1), code, 24, true));
381 
382                 check(addr64[0] == 0);
383                 check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[1]), inst64));
384                 check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[2]), inst64));
385                 check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[3]), inst64));
386                 check(addr64[4] == 0);
387         }
388 
389         /* Test single page memcpy */
390         addr32 = buf + PAGE_SIZE * 3;
391 
392         for (int i = 0; i < ARRAY_SIZE(code); i++)
393                 code[i] = i + 1;
394 
395         check(!patch_instructions(addr32 + 1, code, sizeof(code), false));
396 
397         check(addr32[0] == 0);
398         check(!memcmp(&addr32[1], code, sizeof(code)));
399         check(addr32[ARRAY_SIZE(code) + 1] == 0);
400 
401         /* Test multipage 32-bit repeated instruction */
402         addr32 = buf + PAGE_SIZE * 4 - 8;
403         check(!patch_instructions(addr32 + 1, &inst32, 12, true));
404 
405         check(addr32[0] == 0);
406         check(addr32[1] == inst32);
407         check(addr32[2] == inst32);
408         check(addr32[3] == inst32);
409         check(addr32[4] == 0);
410 
411         /* Test multipage 64-bit repeated instruction */
412         if (IS_ENABLED(CONFIG_PPC64)) {
413                 check(ppc_inst_prefixed(inst64));
414 
415                 addr64 = buf + PAGE_SIZE * 5 - 8;
416                 ppc_inst_write(code, inst64);
417                 check(!patch_instructions((u32 *)(addr64 + 1), code, 24, true));
418 
419                 check(addr64[0] == 0);
420                 check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[1]), inst64));
421                 check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[2]), inst64));
422                 check(ppc_inst_equal(ppc_inst_read((u32 *)&addr64[3]), inst64));
423                 check(addr64[4] == 0);
424         }
425 
426         /* Test multipage memcpy */
427         addr32 = buf + PAGE_SIZE * 6 - 12;
428 
429         for (int i = 0; i < ARRAY_SIZE(code); i++)
430                 code[i] = i + 1;
431 
432         check(!patch_instructions(addr32 + 1, code, sizeof(code), false));
433 
434         check(addr32[0] == 0);
435         check(!memcmp(&addr32[1], code, sizeof(code)));
436         check(addr32[ARRAY_SIZE(code) + 1] == 0);
437 
438         vfree(buf);
439 }
440 
441 static int __init test_code_patching(void)
442 {
443         pr_info("Running code patching self-tests ...\n");
444 
445         test_branch_iform();
446         test_branch_bform();
447         test_create_function_call();
448         test_translate_branch();
449         test_prefixed_patching();
450         test_multi_instruction_patching();
451 
452         return 0;
453 }
454 late_initcall(test_code_patching);
455 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php