| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org) |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com> |
|---|
| 6 | 7 | * |
|---|
| 7 | 8 | * Copyright 2008 Michael Ellerman, IBM Corporation. |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or |
|---|
| 10 | | - * modify it under the terms of the GNU General Public License |
|---|
| 11 | | - * as published by the Free Software Foundation; either version |
|---|
| 12 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 13 | 9 | */ |
|---|
| 14 | 10 | |
|---|
| 15 | 11 | #include <linux/types.h> |
|---|
| .. | .. |
|---|
| 26 | 22 | #include <asm/setup.h> |
|---|
| 27 | 23 | #include <asm/security_features.h> |
|---|
| 28 | 24 | #include <asm/firmware.h> |
|---|
| 25 | +#include <asm/inst.h> |
|---|
| 29 | 26 | |
|---|
| 30 | 27 | struct fixup_entry { |
|---|
| 31 | 28 | unsigned long mask; |
|---|
| .. | .. |
|---|
| 36 | 33 | long alt_end_off; |
|---|
| 37 | 34 | }; |
|---|
| 38 | 35 | |
|---|
| 39 | | -static unsigned int *calc_addr(struct fixup_entry *fcur, long offset) |
|---|
| 36 | +static struct ppc_inst *calc_addr(struct fixup_entry *fcur, long offset) |
|---|
| 40 | 37 | { |
|---|
| 41 | 38 | /* |
|---|
| 42 | 39 | * We store the offset to the code as a negative offset from |
|---|
| 43 | 40 | * the start of the alt_entry, to support the VDSO. This |
|---|
| 44 | 41 | * routine converts that back into an actual address. |
|---|
| 45 | 42 | */ |
|---|
| 46 | | - return (unsigned int *)((unsigned long)fcur + offset); |
|---|
| 43 | + return (struct ppc_inst *)((unsigned long)fcur + offset); |
|---|
| 47 | 44 | } |
|---|
| 48 | 45 | |
|---|
| 49 | | -static int patch_alt_instruction(unsigned int *src, unsigned int *dest, |
|---|
| 50 | | - unsigned int *alt_start, unsigned int *alt_end) |
|---|
| 46 | +static int patch_alt_instruction(struct ppc_inst *src, struct ppc_inst *dest, |
|---|
| 47 | + struct ppc_inst *alt_start, struct ppc_inst *alt_end) |
|---|
| 51 | 48 | { |
|---|
| 52 | | - unsigned int instr; |
|---|
| 49 | + int err; |
|---|
| 50 | + struct ppc_inst instr; |
|---|
| 53 | 51 | |
|---|
| 54 | | - instr = *src; |
|---|
| 52 | + instr = ppc_inst_read(src); |
|---|
| 55 | 53 | |
|---|
| 56 | 54 | if (instr_is_relative_branch(*src)) { |
|---|
| 57 | | - unsigned int *target = (unsigned int *)branch_target(src); |
|---|
| 55 | + struct ppc_inst *target = (struct ppc_inst *)branch_target(src); |
|---|
| 58 | 56 | |
|---|
| 59 | 57 | /* Branch within the section doesn't need translating */ |
|---|
| 60 | 58 | if (target < alt_start || target > alt_end) { |
|---|
| 61 | | - instr = translate_branch(dest, src); |
|---|
| 62 | | - if (!instr) |
|---|
| 59 | + err = translate_branch(&instr, dest, src); |
|---|
| 60 | + if (err) |
|---|
| 63 | 61 | return 1; |
|---|
| 64 | 62 | } |
|---|
| 65 | 63 | } |
|---|
| .. | .. |
|---|
| 71 | 69 | |
|---|
| 72 | 70 | static int patch_feature_section(unsigned long value, struct fixup_entry *fcur) |
|---|
| 73 | 71 | { |
|---|
| 74 | | - unsigned int *start, *end, *alt_start, *alt_end, *src, *dest; |
|---|
| 72 | + struct ppc_inst *start, *end, *alt_start, *alt_end, *src, *dest, nop; |
|---|
| 75 | 73 | |
|---|
| 76 | 74 | start = calc_addr(fcur, fcur->start_off); |
|---|
| 77 | 75 | end = calc_addr(fcur, fcur->end_off); |
|---|
| .. | .. |
|---|
| 87 | 85 | src = alt_start; |
|---|
| 88 | 86 | dest = start; |
|---|
| 89 | 87 | |
|---|
| 90 | | - for (; src < alt_end; src++, dest++) { |
|---|
| 88 | + for (; src < alt_end; src = ppc_inst_next(src, src), |
|---|
| 89 | + dest = ppc_inst_next(dest, dest)) { |
|---|
| 91 | 90 | if (patch_alt_instruction(src, dest, alt_start, alt_end)) |
|---|
| 92 | 91 | return 1; |
|---|
| 93 | 92 | } |
|---|
| 94 | 93 | |
|---|
| 95 | | - for (; dest < end; dest++) |
|---|
| 96 | | - raw_patch_instruction(dest, PPC_INST_NOP); |
|---|
| 94 | + nop = ppc_inst(PPC_INST_NOP); |
|---|
| 95 | + for (; dest < end; dest = ppc_inst_next(dest, &nop)) |
|---|
| 96 | + raw_patch_instruction(dest, nop); |
|---|
| 97 | 97 | |
|---|
| 98 | 98 | return 0; |
|---|
| 99 | 99 | } |
|---|
| .. | .. |
|---|
| 119 | 119 | } |
|---|
| 120 | 120 | |
|---|
| 121 | 121 | #ifdef CONFIG_PPC_BOOK3S_64 |
|---|
| 122 | | -void do_stf_entry_barrier_fixups(enum stf_barrier_type types) |
|---|
| 122 | +static void do_stf_entry_barrier_fixups(enum stf_barrier_type types) |
|---|
| 123 | 123 | { |
|---|
| 124 | 124 | unsigned int instrs[3], *dest; |
|---|
| 125 | 125 | long *start, *end; |
|---|
| .. | .. |
|---|
| 150 | 150 | |
|---|
| 151 | 151 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
|---|
| 152 | 152 | |
|---|
| 153 | | - patch_instruction(dest, instrs[0]); |
|---|
| 153 | + patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0])); |
|---|
| 154 | 154 | |
|---|
| 155 | 155 | if (types & STF_BARRIER_FALLBACK) |
|---|
| 156 | | - patch_branch(dest + 1, (unsigned long)&stf_barrier_fallback, |
|---|
| 156 | + patch_branch((struct ppc_inst *)(dest + 1), |
|---|
| 157 | + (unsigned long)&stf_barrier_fallback, |
|---|
| 157 | 158 | BRANCH_SET_LINK); |
|---|
| 158 | 159 | else |
|---|
| 159 | | - patch_instruction(dest + 1, instrs[1]); |
|---|
| 160 | + patch_instruction((struct ppc_inst *)(dest + 1), |
|---|
| 161 | + ppc_inst(instrs[1])); |
|---|
| 160 | 162 | |
|---|
| 161 | | - patch_instruction(dest + 2, instrs[2]); |
|---|
| 163 | + patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2])); |
|---|
| 162 | 164 | } |
|---|
| 163 | 165 | |
|---|
| 164 | 166 | printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i, |
|---|
| .. | .. |
|---|
| 169 | 171 | : "unknown"); |
|---|
| 170 | 172 | } |
|---|
| 171 | 173 | |
|---|
| 172 | | -void do_stf_exit_barrier_fixups(enum stf_barrier_type types) |
|---|
| 174 | +static void do_stf_exit_barrier_fixups(enum stf_barrier_type types) |
|---|
| 173 | 175 | { |
|---|
| 174 | 176 | unsigned int instrs[6], *dest; |
|---|
| 175 | 177 | long *start, *end; |
|---|
| .. | .. |
|---|
| 211 | 213 | |
|---|
| 212 | 214 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
|---|
| 213 | 215 | |
|---|
| 214 | | - patch_instruction(dest, instrs[0]); |
|---|
| 215 | | - patch_instruction(dest + 1, instrs[1]); |
|---|
| 216 | | - patch_instruction(dest + 2, instrs[2]); |
|---|
| 217 | | - patch_instruction(dest + 3, instrs[3]); |
|---|
| 218 | | - patch_instruction(dest + 4, instrs[4]); |
|---|
| 219 | | - patch_instruction(dest + 5, instrs[5]); |
|---|
| 216 | + patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0])); |
|---|
| 217 | + patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1])); |
|---|
| 218 | + patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2])); |
|---|
| 219 | + patch_instruction((struct ppc_inst *)(dest + 3), ppc_inst(instrs[3])); |
|---|
| 220 | + patch_instruction((struct ppc_inst *)(dest + 4), ppc_inst(instrs[4])); |
|---|
| 221 | + patch_instruction((struct ppc_inst *)(dest + 5), ppc_inst(instrs[5])); |
|---|
| 220 | 222 | } |
|---|
| 221 | 223 | printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i, |
|---|
| 222 | 224 | (types == STF_BARRIER_NONE) ? "no" : |
|---|
| .. | .. |
|---|
| 280 | 282 | |
|---|
| 281 | 283 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
|---|
| 282 | 284 | |
|---|
| 283 | | - patch_instruction(dest, instrs[0]); |
|---|
| 285 | + patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0])); |
|---|
| 284 | 286 | |
|---|
| 285 | | - patch_instruction((dest + 1), instrs[1]); |
|---|
| 286 | | - patch_instruction((dest + 2), instrs[2]); |
|---|
| 287 | | - patch_instruction((dest + 3), instrs[3]); |
|---|
| 287 | + patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1])); |
|---|
| 288 | + patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2])); |
|---|
| 289 | + patch_instruction((struct ppc_inst *)(dest + 3), ppc_inst(instrs[3])); |
|---|
| 288 | 290 | } |
|---|
| 289 | 291 | |
|---|
| 290 | 292 | printk(KERN_DEBUG "uaccess-flush: patched %d locations (%s flush)\n", i, |
|---|
| .. | .. |
|---|
| 303 | 305 | unsigned int instrs[3], *dest; |
|---|
| 304 | 306 | long *start, *end; |
|---|
| 305 | 307 | int i; |
|---|
| 306 | | - |
|---|
| 307 | | - start = PTRRELOC(&__start___entry_flush_fixup); |
|---|
| 308 | | - end = PTRRELOC(&__stop___entry_flush_fixup); |
|---|
| 309 | 308 | |
|---|
| 310 | 309 | instrs[0] = 0x60000000; /* nop */ |
|---|
| 311 | 310 | instrs[1] = 0x60000000; /* nop */ |
|---|
| .. | .. |
|---|
| 326 | 325 | if (types & L1D_FLUSH_MTTRIG) |
|---|
| 327 | 326 | instrs[i++] = 0x7c12dba6; /* mtspr TRIG2,r0 (SPR #882) */ |
|---|
| 328 | 327 | |
|---|
| 328 | + start = PTRRELOC(&__start___entry_flush_fixup); |
|---|
| 329 | + end = PTRRELOC(&__stop___entry_flush_fixup); |
|---|
| 329 | 330 | for (i = 0; start < end; start++, i++) { |
|---|
| 330 | 331 | dest = (void *)start + *start; |
|---|
| 331 | 332 | |
|---|
| 332 | 333 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
|---|
| 333 | 334 | |
|---|
| 334 | | - patch_instruction(dest, instrs[0]); |
|---|
| 335 | + patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0])); |
|---|
| 335 | 336 | |
|---|
| 336 | 337 | if (types == L1D_FLUSH_FALLBACK) |
|---|
| 337 | | - patch_branch((dest + 1), (unsigned long)&entry_flush_fallback, |
|---|
| 338 | + patch_branch((struct ppc_inst *)(dest + 1), (unsigned long)&entry_flush_fallback, |
|---|
| 338 | 339 | BRANCH_SET_LINK); |
|---|
| 339 | 340 | else |
|---|
| 340 | | - patch_instruction((dest + 1), instrs[1]); |
|---|
| 341 | + patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1])); |
|---|
| 341 | 342 | |
|---|
| 342 | | - patch_instruction((dest + 2), instrs[2]); |
|---|
| 343 | + patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2])); |
|---|
| 343 | 344 | } |
|---|
| 345 | + |
|---|
| 346 | + start = PTRRELOC(&__start___scv_entry_flush_fixup); |
|---|
| 347 | + end = PTRRELOC(&__stop___scv_entry_flush_fixup); |
|---|
| 348 | + for (; start < end; start++, i++) { |
|---|
| 349 | + dest = (void *)start + *start; |
|---|
| 350 | + |
|---|
| 351 | + pr_devel("patching dest %lx\n", (unsigned long)dest); |
|---|
| 352 | + |
|---|
| 353 | + patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0])); |
|---|
| 354 | + |
|---|
| 355 | + if (types == L1D_FLUSH_FALLBACK) |
|---|
| 356 | + patch_branch((struct ppc_inst *)(dest + 1), (unsigned long)&scv_entry_flush_fallback, |
|---|
| 357 | + BRANCH_SET_LINK); |
|---|
| 358 | + else |
|---|
| 359 | + patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1])); |
|---|
| 360 | + |
|---|
| 361 | + patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2])); |
|---|
| 362 | + } |
|---|
| 363 | + |
|---|
| 344 | 364 | |
|---|
| 345 | 365 | printk(KERN_DEBUG "entry-flush: patched %d locations (%s flush)\n", i, |
|---|
| 346 | 366 | (types == L1D_FLUSH_NONE) ? "no" : |
|---|
| .. | .. |
|---|
| 396 | 416 | |
|---|
| 397 | 417 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
|---|
| 398 | 418 | |
|---|
| 399 | | - patch_instruction(dest, instrs[0]); |
|---|
| 400 | | - patch_instruction(dest + 1, instrs[1]); |
|---|
| 401 | | - patch_instruction(dest + 2, instrs[2]); |
|---|
| 419 | + patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0])); |
|---|
| 420 | + patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1])); |
|---|
| 421 | + patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2])); |
|---|
| 402 | 422 | } |
|---|
| 403 | 423 | |
|---|
| 404 | 424 | printk(KERN_DEBUG "rfi-flush: patched %d locations (%s flush)\n", i, |
|---|
| .. | .. |
|---|
| 431 | 451 | dest = (void *)start + *start; |
|---|
| 432 | 452 | |
|---|
| 433 | 453 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
|---|
| 434 | | - patch_instruction(dest, instr); |
|---|
| 454 | + patch_instruction((struct ppc_inst *)dest, ppc_inst(instr)); |
|---|
| 435 | 455 | } |
|---|
| 436 | 456 | |
|---|
| 437 | 457 | printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); |
|---|
| .. | .. |
|---|
| 474 | 494 | dest = (void *)start + *start; |
|---|
| 475 | 495 | |
|---|
| 476 | 496 | pr_devel("patching dest %lx\n", (unsigned long)dest); |
|---|
| 477 | | - patch_instruction(dest, instr[0]); |
|---|
| 478 | | - patch_instruction(dest + 1, instr[1]); |
|---|
| 497 | + patch_instruction((struct ppc_inst *)dest, ppc_inst(instr[0])); |
|---|
| 498 | + patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instr[1])); |
|---|
| 479 | 499 | } |
|---|
| 480 | 500 | |
|---|
| 481 | 501 | printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); |
|---|
| .. | .. |
|---|
| 489 | 509 | end = (void *)curr + *(curr + 1); |
|---|
| 490 | 510 | for (; start < end; start++) { |
|---|
| 491 | 511 | pr_devel("patching dest %lx\n", (unsigned long)start); |
|---|
| 492 | | - patch_instruction(start, PPC_INST_NOP); |
|---|
| 512 | + patch_instruction((struct ppc_inst *)start, ppc_inst(PPC_INST_NOP)); |
|---|
| 493 | 513 | } |
|---|
| 494 | 514 | } |
|---|
| 495 | 515 | |
|---|
| .. | .. |
|---|
| 508 | 528 | void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end) |
|---|
| 509 | 529 | { |
|---|
| 510 | 530 | long *start, *end; |
|---|
| 511 | | - unsigned int *dest; |
|---|
| 531 | + struct ppc_inst *dest; |
|---|
| 512 | 532 | |
|---|
| 513 | 533 | if (!(value & CPU_FTR_LWSYNC)) |
|---|
| 514 | 534 | return ; |
|---|
| .. | .. |
|---|
| 518 | 538 | |
|---|
| 519 | 539 | for (; start < end; start++) { |
|---|
| 520 | 540 | dest = (void *)start + *start; |
|---|
| 521 | | - raw_patch_instruction(dest, PPC_INST_LWSYNC); |
|---|
| 541 | + raw_patch_instruction(dest, ppc_inst(PPC_INST_LWSYNC)); |
|---|
| 522 | 542 | } |
|---|
| 523 | 543 | } |
|---|
| 524 | 544 | |
|---|
| 525 | 545 | static void do_final_fixups(void) |
|---|
| 526 | 546 | { |
|---|
| 527 | 547 | #if defined(CONFIG_PPC64) && defined(CONFIG_RELOCATABLE) |
|---|
| 528 | | - int *src, *dest; |
|---|
| 529 | | - unsigned long length; |
|---|
| 548 | + struct ppc_inst inst, *src, *dest, *end; |
|---|
| 530 | 549 | |
|---|
| 531 | 550 | if (PHYSICAL_START == 0) |
|---|
| 532 | 551 | return; |
|---|
| 533 | 552 | |
|---|
| 534 | | - src = (int *)(KERNELBASE + PHYSICAL_START); |
|---|
| 535 | | - dest = (int *)KERNELBASE; |
|---|
| 536 | | - length = (__end_interrupts - _stext) / sizeof(int); |
|---|
| 553 | + src = (struct ppc_inst *)(KERNELBASE + PHYSICAL_START); |
|---|
| 554 | + dest = (struct ppc_inst *)KERNELBASE; |
|---|
| 555 | + end = (void *)src + (__end_interrupts - _stext); |
|---|
| 537 | 556 | |
|---|
| 538 | | - while (length--) { |
|---|
| 539 | | - raw_patch_instruction(dest, *src); |
|---|
| 540 | | - src++; |
|---|
| 541 | | - dest++; |
|---|
| 557 | + while (src < end) { |
|---|
| 558 | + inst = ppc_inst_read(src); |
|---|
| 559 | + raw_patch_instruction(dest, inst); |
|---|
| 560 | + src = ppc_inst_next(src, src); |
|---|
| 561 | + dest = ppc_inst_next(dest, dest); |
|---|
| 542 | 562 | } |
|---|
| 543 | 563 | #endif |
|---|
| 544 | 564 | } |
|---|
| .. | .. |
|---|
| 821 | 841 | } |
|---|
| 822 | 842 | } |
|---|
| 823 | 843 | |
|---|
| 844 | +#ifdef CONFIG_PPC64 |
|---|
| 845 | +static void __init test_prefix_patching(void) |
|---|
| 846 | +{ |
|---|
| 847 | + extern unsigned int ftr_fixup_prefix1[]; |
|---|
| 848 | + extern unsigned int end_ftr_fixup_prefix1[]; |
|---|
| 849 | + extern unsigned int ftr_fixup_prefix1_orig[]; |
|---|
| 850 | + extern unsigned int ftr_fixup_prefix1_expected[]; |
|---|
| 851 | + int size = sizeof(unsigned int) * (end_ftr_fixup_prefix1 - ftr_fixup_prefix1); |
|---|
| 852 | + |
|---|
| 853 | + fixup.value = fixup.mask = 8; |
|---|
| 854 | + fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix1 + 1); |
|---|
| 855 | + fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix1 + 3); |
|---|
| 856 | + fixup.alt_start_off = fixup.alt_end_off = 0; |
|---|
| 857 | + |
|---|
| 858 | + /* Sanity check */ |
|---|
| 859 | + check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_orig, size) == 0); |
|---|
| 860 | + |
|---|
| 861 | + patch_feature_section(0, &fixup); |
|---|
| 862 | + check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_expected, size) == 0); |
|---|
| 863 | + check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_orig, size) != 0); |
|---|
| 864 | +} |
|---|
| 865 | + |
|---|
| 866 | +static void __init test_prefix_alt_patching(void) |
|---|
| 867 | +{ |
|---|
| 868 | + extern unsigned int ftr_fixup_prefix2[]; |
|---|
| 869 | + extern unsigned int end_ftr_fixup_prefix2[]; |
|---|
| 870 | + extern unsigned int ftr_fixup_prefix2_orig[]; |
|---|
| 871 | + extern unsigned int ftr_fixup_prefix2_expected[]; |
|---|
| 872 | + extern unsigned int ftr_fixup_prefix2_alt[]; |
|---|
| 873 | + int size = sizeof(unsigned int) * (end_ftr_fixup_prefix2 - ftr_fixup_prefix2); |
|---|
| 874 | + |
|---|
| 875 | + fixup.value = fixup.mask = 8; |
|---|
| 876 | + fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix2 + 1); |
|---|
| 877 | + fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix2 + 3); |
|---|
| 878 | + fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_prefix2_alt); |
|---|
| 879 | + fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_prefix2_alt + 2); |
|---|
| 880 | + /* Sanity check */ |
|---|
| 881 | + check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_orig, size) == 0); |
|---|
| 882 | + |
|---|
| 883 | + patch_feature_section(0, &fixup); |
|---|
| 884 | + check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_expected, size) == 0); |
|---|
| 885 | + check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_orig, size) != 0); |
|---|
| 886 | +} |
|---|
| 887 | + |
|---|
| 888 | +static void __init test_prefix_word_alt_patching(void) |
|---|
| 889 | +{ |
|---|
| 890 | + extern unsigned int ftr_fixup_prefix3[]; |
|---|
| 891 | + extern unsigned int end_ftr_fixup_prefix3[]; |
|---|
| 892 | + extern unsigned int ftr_fixup_prefix3_orig[]; |
|---|
| 893 | + extern unsigned int ftr_fixup_prefix3_expected[]; |
|---|
| 894 | + extern unsigned int ftr_fixup_prefix3_alt[]; |
|---|
| 895 | + int size = sizeof(unsigned int) * (end_ftr_fixup_prefix3 - ftr_fixup_prefix3); |
|---|
| 896 | + |
|---|
| 897 | + fixup.value = fixup.mask = 8; |
|---|
| 898 | + fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix3 + 1); |
|---|
| 899 | + fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix3 + 4); |
|---|
| 900 | + fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_prefix3_alt); |
|---|
| 901 | + fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_prefix3_alt + 3); |
|---|
| 902 | + /* Sanity check */ |
|---|
| 903 | + check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_orig, size) == 0); |
|---|
| 904 | + |
|---|
| 905 | + patch_feature_section(0, &fixup); |
|---|
| 906 | + check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_expected, size) == 0); |
|---|
| 907 | + patch_feature_section(0, &fixup); |
|---|
| 908 | + check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_orig, size) != 0); |
|---|
| 909 | +} |
|---|
| 910 | +#else |
|---|
| 911 | +static inline void test_prefix_patching(void) {} |
|---|
| 912 | +static inline void test_prefix_alt_patching(void) {} |
|---|
| 913 | +static inline void test_prefix_word_alt_patching(void) {} |
|---|
| 914 | +#endif /* CONFIG_PPC64 */ |
|---|
| 915 | + |
|---|
| 824 | 916 | static int __init test_feature_fixups(void) |
|---|
| 825 | 917 | { |
|---|
| 826 | 918 | printk(KERN_DEBUG "Running feature fixup self-tests ...\n"); |
|---|
| .. | .. |
|---|
| 835 | 927 | test_cpu_macros(); |
|---|
| 836 | 928 | test_fw_macros(); |
|---|
| 837 | 929 | test_lwsync_macros(); |
|---|
| 930 | + test_prefix_patching(); |
|---|
| 931 | + test_prefix_alt_patching(); |
|---|
| 932 | + test_prefix_word_alt_patching(); |
|---|
| 838 | 933 | |
|---|
| 839 | 934 | return 0; |
|---|
| 840 | 935 | } |
|---|