linux/arch/powerpc/lib/feature-fixups.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org)
   4 *
   5 *  Modifications for ppc64:
   6 *      Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com>
   7 *
   8 *  Copyright 2008 Michael Ellerman, IBM Corporation.
   9 */
  10
  11#include <linux/types.h>
  12#include <linux/jump_label.h>
  13#include <linux/kernel.h>
  14#include <linux/string.h>
  15#include <linux/init.h>
  16#include <linux/sched/mm.h>
  17#include <asm/cputable.h>
  18#include <asm/code-patching.h>
  19#include <asm/page.h>
  20#include <asm/sections.h>
  21#include <asm/setup.h>
  22#include <asm/security_features.h>
  23#include <asm/firmware.h>
  24
  25struct fixup_entry {
  26        unsigned long   mask;
  27        unsigned long   value;
  28        long            start_off;
  29        long            end_off;
  30        long            alt_start_off;
  31        long            alt_end_off;
  32};
  33
  34static unsigned int *calc_addr(struct fixup_entry *fcur, long offset)
  35{
  36        /*
  37         * We store the offset to the code as a negative offset from
  38         * the start of the alt_entry, to support the VDSO. This
  39         * routine converts that back into an actual address.
  40         */
  41        return (unsigned int *)((unsigned long)fcur + offset);
  42}
  43
  44static int patch_alt_instruction(unsigned int *src, unsigned int *dest,
  45                                 unsigned int *alt_start, unsigned int *alt_end)
  46{
  47        unsigned int instr;
  48
  49        instr = *src;
  50
  51        if (instr_is_relative_branch(*src)) {
  52                unsigned int *target = (unsigned int *)branch_target(src);
  53
  54                /* Branch within the section doesn't need translating */
  55                if (target < alt_start || target > alt_end) {
  56                        instr = translate_branch(dest, src);
  57                        if (!instr)
  58                                return 1;
  59                }
  60        }
  61
  62        raw_patch_instruction(dest, instr);
  63
  64        return 0;
  65}
  66
  67static int patch_feature_section(unsigned long value, struct fixup_entry *fcur)
  68{
  69        unsigned int *start, *end, *alt_start, *alt_end, *src, *dest;
  70
  71        start = calc_addr(fcur, fcur->start_off);
  72        end = calc_addr(fcur, fcur->end_off);
  73        alt_start = calc_addr(fcur, fcur->alt_start_off);
  74        alt_end = calc_addr(fcur, fcur->alt_end_off);
  75
  76        if ((alt_end - alt_start) > (end - start))
  77                return 1;
  78
  79        if ((value & fcur->mask) == fcur->value)
  80                return 0;
  81
  82        src = alt_start;
  83        dest = start;
  84
  85        for (; src < alt_end; src++, dest++) {
  86                if (patch_alt_instruction(src, dest, alt_start, alt_end))
  87                        return 1;
  88        }
  89
  90        for (; dest < end; dest++)
  91                raw_patch_instruction(dest, PPC_INST_NOP);
  92
  93        return 0;
  94}
  95
  96void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
  97{
  98        struct fixup_entry *fcur, *fend;
  99
 100        fcur = fixup_start;
 101        fend = fixup_end;
 102
 103        for (; fcur < fend; fcur++) {
 104                if (patch_feature_section(value, fcur)) {
 105                        WARN_ON(1);
 106                        printk("Unable to patch feature section at %p - %p" \
 107                                " with %p - %p\n",
 108                                calc_addr(fcur, fcur->start_off),
 109                                calc_addr(fcur, fcur->end_off),
 110                                calc_addr(fcur, fcur->alt_start_off),
 111                                calc_addr(fcur, fcur->alt_end_off));
 112                }
 113        }
 114}
 115
 116#ifdef CONFIG_PPC_BOOK3S_64
 117static void do_stf_entry_barrier_fixups(enum stf_barrier_type types)
 118{
 119        unsigned int instrs[3], *dest;
 120        long *start, *end;
 121        int i;
 122
 123        start = PTRRELOC(&__start___stf_entry_barrier_fixup),
 124        end = PTRRELOC(&__stop___stf_entry_barrier_fixup);
 125
 126        instrs[0] = 0x60000000; /* nop */
 127        instrs[1] = 0x60000000; /* nop */
 128        instrs[2] = 0x60000000; /* nop */
 129
 130        i = 0;
 131        if (types & STF_BARRIER_FALLBACK) {
 132                instrs[i++] = 0x7d4802a6; /* mflr r10           */
 133                instrs[i++] = 0x60000000; /* branch patched below */
 134                instrs[i++] = 0x7d4803a6; /* mtlr r10           */
 135        } else if (types & STF_BARRIER_EIEIO) {
 136                instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */
 137        } else if (types & STF_BARRIER_SYNC_ORI) {
 138                instrs[i++] = 0x7c0004ac; /* hwsync             */
 139                instrs[i++] = 0xe94d0000; /* ld r10,0(r13)      */
 140                instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
 141        }
 142
 143        for (i = 0; start < end; start++, i++) {
 144                dest = (void *)start + *start;
 145
 146                pr_devel("patching dest %lx\n", (unsigned long)dest);
 147
 148                patch_instruction(dest, instrs[0]);
 149
 150                if (types & STF_BARRIER_FALLBACK)
 151                        patch_branch(dest + 1, (unsigned long)&stf_barrier_fallback,
 152                                     BRANCH_SET_LINK);
 153                else
 154                        patch_instruction(dest + 1, instrs[1]);
 155
 156                patch_instruction(dest + 2, instrs[2]);
 157        }
 158
 159        printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i,
 160                (types == STF_BARRIER_NONE)                  ? "no" :
 161                (types == STF_BARRIER_FALLBACK)              ? "fallback" :
 162                (types == STF_BARRIER_EIEIO)                 ? "eieio" :
 163                (types == (STF_BARRIER_SYNC_ORI))            ? "hwsync"
 164                                                           : "unknown");
 165}
 166
 167static void do_stf_exit_barrier_fixups(enum stf_barrier_type types)
 168{
 169        unsigned int instrs[6], *dest;
 170        long *start, *end;
 171        int i;
 172
 173        start = PTRRELOC(&__start___stf_exit_barrier_fixup),
 174        end = PTRRELOC(&__stop___stf_exit_barrier_fixup);
 175
 176        instrs[0] = 0x60000000; /* nop */
 177        instrs[1] = 0x60000000; /* nop */
 178        instrs[2] = 0x60000000; /* nop */
 179        instrs[3] = 0x60000000; /* nop */
 180        instrs[4] = 0x60000000; /* nop */
 181        instrs[5] = 0x60000000; /* nop */
 182
 183        i = 0;
 184        if (types & STF_BARRIER_FALLBACK || types & STF_BARRIER_SYNC_ORI) {
 185                if (cpu_has_feature(CPU_FTR_HVMODE)) {
 186                        instrs[i++] = 0x7db14ba6; /* mtspr 0x131, r13 (HSPRG1) */
 187                        instrs[i++] = 0x7db04aa6; /* mfspr r13, 0x130 (HSPRG0) */
 188                } else {
 189                        instrs[i++] = 0x7db243a6; /* mtsprg 2,r13       */
 190                        instrs[i++] = 0x7db142a6; /* mfsprg r13,1    */
 191                }
 192                instrs[i++] = 0x7c0004ac; /* hwsync             */
 193                instrs[i++] = 0xe9ad0000; /* ld r13,0(r13)      */
 194                instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
 195                if (cpu_has_feature(CPU_FTR_HVMODE)) {
 196                        instrs[i++] = 0x7db14aa6; /* mfspr r13, 0x131 (HSPRG1) */
 197                } else {
 198                        instrs[i++] = 0x7db242a6; /* mfsprg r13,2 */
 199                }
 200        } else if (types & STF_BARRIER_EIEIO) {
 201                instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */
 202        }
 203
 204        for (i = 0; start < end; start++, i++) {
 205                dest = (void *)start + *start;
 206
 207                pr_devel("patching dest %lx\n", (unsigned long)dest);
 208
 209                patch_instruction(dest, instrs[0]);
 210                patch_instruction(dest + 1, instrs[1]);
 211                patch_instruction(dest + 2, instrs[2]);
 212                patch_instruction(dest + 3, instrs[3]);
 213                patch_instruction(dest + 4, instrs[4]);
 214                patch_instruction(dest + 5, instrs[5]);
 215        }
 216        printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i,
 217                (types == STF_BARRIER_NONE)                  ? "no" :
 218                (types == STF_BARRIER_FALLBACK)              ? "fallback" :
 219                (types == STF_BARRIER_EIEIO)                 ? "eieio" :
 220                (types == (STF_BARRIER_SYNC_ORI))            ? "hwsync"
 221                                                           : "unknown");
 222}
 223
 224
 225void do_stf_barrier_fixups(enum stf_barrier_type types)
 226{
 227        do_stf_entry_barrier_fixups(types);
 228        do_stf_exit_barrier_fixups(types);
 229}
 230
 231void do_rfi_flush_fixups(enum l1d_flush_type types)
 232{
 233        unsigned int instrs[3], *dest;
 234        long *start, *end;
 235        int i;
 236
 237        start = PTRRELOC(&__start___rfi_flush_fixup),
 238        end = PTRRELOC(&__stop___rfi_flush_fixup);
 239
 240        instrs[0] = 0x60000000; /* nop */
 241        instrs[1] = 0x60000000; /* nop */
 242        instrs[2] = 0x60000000; /* nop */
 243
 244        if (types & L1D_FLUSH_FALLBACK)
 245                /* b .+16 to fallback flush */
 246                instrs[0] = 0x48000010;
 247
 248        i = 0;
 249        if (types & L1D_FLUSH_ORI) {
 250                instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
 251                instrs[i++] = 0x63de0000; /* ori 30,30,0 L1d flush*/
 252        }
 253
 254        if (types & L1D_FLUSH_MTTRIG)
 255                instrs[i++] = 0x7c12dba6; /* mtspr TRIG2,r0 (SPR #882) */
 256
 257        for (i = 0; start < end; start++, i++) {
 258                dest = (void *)start + *start;
 259
 260                pr_devel("patching dest %lx\n", (unsigned long)dest);
 261
 262                patch_instruction(dest, instrs[0]);
 263                patch_instruction(dest + 1, instrs[1]);
 264                patch_instruction(dest + 2, instrs[2]);
 265        }
 266
 267        printk(KERN_DEBUG "rfi-flush: patched %d locations (%s flush)\n", i,
 268                (types == L1D_FLUSH_NONE)       ? "no" :
 269                (types == L1D_FLUSH_FALLBACK)   ? "fallback displacement" :
 270                (types &  L1D_FLUSH_ORI)        ? (types & L1D_FLUSH_MTTRIG)
 271                                                        ? "ori+mttrig type"
 272                                                        : "ori type" :
 273                (types &  L1D_FLUSH_MTTRIG)     ? "mttrig type"
 274                                                : "unknown");
 275}
 276
 277void do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end)
 278{
 279        unsigned int instr, *dest;
 280        long *start, *end;
 281        int i;
 282
 283        start = fixup_start;
 284        end = fixup_end;
 285
 286        instr = 0x60000000; /* nop */
 287
 288        if (enable) {
 289                pr_info("barrier-nospec: using ORI speculation barrier\n");
 290                instr = 0x63ff0000; /* ori 31,31,0 speculation barrier */
 291        }
 292
 293        for (i = 0; start < end; start++, i++) {
 294                dest = (void *)start + *start;
 295
 296                pr_devel("patching dest %lx\n", (unsigned long)dest);
 297                patch_instruction(dest, instr);
 298        }
 299
 300        printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i);
 301}
 302
 303#endif /* CONFIG_PPC_BOOK3S_64 */
 304
 305#ifdef CONFIG_PPC_BARRIER_NOSPEC
 306void do_barrier_nospec_fixups(bool enable)
 307{
 308        void *start, *end;
 309
 310        start = PTRRELOC(&__start___barrier_nospec_fixup),
 311        end = PTRRELOC(&__stop___barrier_nospec_fixup);
 312
 313        do_barrier_nospec_fixups_range(enable, start, end);
 314}
 315#endif /* CONFIG_PPC_BARRIER_NOSPEC */
 316
 317#ifdef CONFIG_PPC_FSL_BOOK3E
 318void do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end)
 319{
 320        unsigned int instr[2], *dest;
 321        long *start, *end;
 322        int i;
 323
 324        start = fixup_start;
 325        end = fixup_end;
 326
 327        instr[0] = PPC_INST_NOP;
 328        instr[1] = PPC_INST_NOP;
 329
 330        if (enable) {
 331                pr_info("barrier-nospec: using isync; sync as speculation barrier\n");
 332                instr[0] = PPC_INST_ISYNC;
 333                instr[1] = PPC_INST_SYNC;
 334        }
 335
 336        for (i = 0; start < end; start++, i++) {
 337                dest = (void *)start + *start;
 338
 339                pr_devel("patching dest %lx\n", (unsigned long)dest);
 340                patch_instruction(dest, instr[0]);
 341                patch_instruction(dest + 1, instr[1]);
 342        }
 343
 344        printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i);
 345}
 346
 347static void patch_btb_flush_section(long *curr)
 348{
 349        unsigned int *start, *end;
 350
 351        start = (void *)curr + *curr;
 352        end = (void *)curr + *(curr + 1);
 353        for (; start < end; start++) {
 354                pr_devel("patching dest %lx\n", (unsigned long)start);
 355                patch_instruction(start, PPC_INST_NOP);
 356        }
 357}
 358
 359void do_btb_flush_fixups(void)
 360{
 361        long *start, *end;
 362
 363        start = PTRRELOC(&__start__btb_flush_fixup);
 364        end = PTRRELOC(&__stop__btb_flush_fixup);
 365
 366        for (; start < end; start += 2)
 367                patch_btb_flush_section(start);
 368}
 369#endif /* CONFIG_PPC_FSL_BOOK3E */
 370
 371void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end)
 372{
 373        long *start, *end;
 374        unsigned int *dest;
 375
 376        if (!(value & CPU_FTR_LWSYNC))
 377                return ;
 378
 379        start = fixup_start;
 380        end = fixup_end;
 381
 382        for (; start < end; start++) {
 383                dest = (void *)start + *start;
 384                raw_patch_instruction(dest, PPC_INST_LWSYNC);
 385        }
 386}
 387
 388static void do_final_fixups(void)
 389{
 390#if defined(CONFIG_PPC64) && defined(CONFIG_RELOCATABLE)
 391        int *src, *dest;
 392        unsigned long length;
 393
 394        if (PHYSICAL_START == 0)
 395                return;
 396
 397        src = (int *)(KERNELBASE + PHYSICAL_START);
 398        dest = (int *)KERNELBASE;
 399        length = (__end_interrupts - _stext) / sizeof(int);
 400
 401        while (length--) {
 402                raw_patch_instruction(dest, *src);
 403                src++;
 404                dest++;
 405        }
 406#endif
 407}
 408
 409static unsigned long __initdata saved_cpu_features;
 410static unsigned int __initdata saved_mmu_features;
 411#ifdef CONFIG_PPC64
 412static unsigned long __initdata saved_firmware_features;
 413#endif
 414
 415void __init apply_feature_fixups(void)
 416{
 417        struct cpu_spec *spec = PTRRELOC(*PTRRELOC(&cur_cpu_spec));
 418
 419        *PTRRELOC(&saved_cpu_features) = spec->cpu_features;
 420        *PTRRELOC(&saved_mmu_features) = spec->mmu_features;
 421
 422        /*
 423         * Apply the CPU-specific and firmware specific fixups to kernel text
 424         * (nop out sections not relevant to this CPU or this firmware).
 425         */
 426        do_feature_fixups(spec->cpu_features,
 427                          PTRRELOC(&__start___ftr_fixup),
 428                          PTRRELOC(&__stop___ftr_fixup));
 429
 430        do_feature_fixups(spec->mmu_features,
 431                          PTRRELOC(&__start___mmu_ftr_fixup),
 432                          PTRRELOC(&__stop___mmu_ftr_fixup));
 433
 434        do_lwsync_fixups(spec->cpu_features,
 435                         PTRRELOC(&__start___lwsync_fixup),
 436                         PTRRELOC(&__stop___lwsync_fixup));
 437
 438#ifdef CONFIG_PPC64
 439        saved_firmware_features = powerpc_firmware_features;
 440        do_feature_fixups(powerpc_firmware_features,
 441                          &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup);
 442#endif
 443        do_final_fixups();
 444}
 445
 446void __init setup_feature_keys(void)
 447{
 448        /*
 449         * Initialise jump label. This causes all the cpu/mmu_has_feature()
 450         * checks to take on their correct polarity based on the current set of
 451         * CPU/MMU features.
 452         */
 453        jump_label_init();
 454        cpu_feature_keys_init();
 455        mmu_feature_keys_init();
 456}
 457
 458static int __init check_features(void)
 459{
 460        WARN(saved_cpu_features != cur_cpu_spec->cpu_features,
 461             "CPU features changed after feature patching!\n");
 462        WARN(saved_mmu_features != cur_cpu_spec->mmu_features,
 463             "MMU features changed after feature patching!\n");
 464#ifdef CONFIG_PPC64
 465        WARN(saved_firmware_features != powerpc_firmware_features,
 466             "Firmware features changed after feature patching!\n");
 467#endif
 468
 469        return 0;
 470}
 471late_initcall(check_features);
 472
 473#ifdef CONFIG_FTR_FIXUP_SELFTEST
 474
 475#define check(x)        \
 476        if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__);
 477
 478/* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */
 479static struct fixup_entry fixup;
 480
 481static long calc_offset(struct fixup_entry *entry, unsigned int *p)
 482{
 483        return (unsigned long)p - (unsigned long)entry;
 484}
 485
 486static void test_basic_patching(void)
 487{
 488        extern unsigned int ftr_fixup_test1[];
 489        extern unsigned int end_ftr_fixup_test1[];
 490        extern unsigned int ftr_fixup_test1_orig[];
 491        extern unsigned int ftr_fixup_test1_expected[];
 492        int size = 4 * (end_ftr_fixup_test1 - ftr_fixup_test1);
 493
 494        fixup.value = fixup.mask = 8;
 495        fixup.start_off = calc_offset(&fixup, ftr_fixup_test1 + 1);
 496        fixup.end_off = calc_offset(&fixup, ftr_fixup_test1 + 2);
 497        fixup.alt_start_off = fixup.alt_end_off = 0;
 498
 499        /* Sanity check */
 500        check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
 501
 502        /* Check we don't patch if the value matches */
 503        patch_feature_section(8, &fixup);
 504        check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
 505
 506        /* Check we do patch if the value doesn't match */
 507        patch_feature_section(0, &fixup);
 508        check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0);
 509
 510        /* Check we do patch if the mask doesn't match */
 511        memcpy(ftr_fixup_test1, ftr_fixup_test1_orig, size);
 512        check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
 513        patch_feature_section(~8, &fixup);
 514        check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0);
 515}
 516
 517static void test_alternative_patching(void)
 518{
 519        extern unsigned int ftr_fixup_test2[];
 520        extern unsigned int end_ftr_fixup_test2[];
 521        extern unsigned int ftr_fixup_test2_orig[];
 522        extern unsigned int ftr_fixup_test2_alt[];
 523        extern unsigned int ftr_fixup_test2_expected[];
 524        int size = 4 * (end_ftr_fixup_test2 - ftr_fixup_test2);
 525
 526        fixup.value = fixup.mask = 0xF;
 527        fixup.start_off = calc_offset(&fixup, ftr_fixup_test2 + 1);
 528        fixup.end_off = calc_offset(&fixup, ftr_fixup_test2 + 2);
 529        fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test2_alt);
 530        fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test2_alt + 1);
 531
 532        /* Sanity check */
 533        check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
 534
 535        /* Check we don't patch if the value matches */
 536        patch_feature_section(0xF, &fixup);
 537        check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
 538
 539        /* Check we do patch if the value doesn't match */
 540        patch_feature_section(0, &fixup);
 541        check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0);
 542
 543        /* Check we do patch if the mask doesn't match */
 544        memcpy(ftr_fixup_test2, ftr_fixup_test2_orig, size);
 545        check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
 546        patch_feature_section(~0xF, &fixup);
 547        check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0);
 548}
 549
 550static void test_alternative_case_too_big(void)
 551{
 552        extern unsigned int ftr_fixup_test3[];
 553        extern unsigned int end_ftr_fixup_test3[];
 554        extern unsigned int ftr_fixup_test3_orig[];
 555        extern unsigned int ftr_fixup_test3_alt[];
 556        int size = 4 * (end_ftr_fixup_test3 - ftr_fixup_test3);
 557
 558        fixup.value = fixup.mask = 0xC;
 559        fixup.start_off = calc_offset(&fixup, ftr_fixup_test3 + 1);
 560        fixup.end_off = calc_offset(&fixup, ftr_fixup_test3 + 2);
 561        fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test3_alt);
 562        fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test3_alt + 2);
 563
 564        /* Sanity check */
 565        check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
 566
 567        /* Expect nothing to be patched, and the error returned to us */
 568        check(patch_feature_section(0xF, &fixup) == 1);
 569        check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
 570        check(patch_feature_section(0, &fixup) == 1);
 571        check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
 572        check(patch_feature_section(~0xF, &fixup) == 1);
 573        check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
 574}
 575
 576static void test_alternative_case_too_small(void)
 577{
 578        extern unsigned int ftr_fixup_test4[];
 579        extern unsigned int end_ftr_fixup_test4[];
 580        extern unsigned int ftr_fixup_test4_orig[];
 581        extern unsigned int ftr_fixup_test4_alt[];
 582        extern unsigned int ftr_fixup_test4_expected[];
 583        int size = 4 * (end_ftr_fixup_test4 - ftr_fixup_test4);
 584        unsigned long flag;
 585
 586        /* Check a high-bit flag */
 587        flag = 1UL << ((sizeof(unsigned long) - 1) * 8);
 588        fixup.value = fixup.mask = flag;
 589        fixup.start_off = calc_offset(&fixup, ftr_fixup_test4 + 1);
 590        fixup.end_off = calc_offset(&fixup, ftr_fixup_test4 + 5);
 591        fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test4_alt);
 592        fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test4_alt + 2);
 593
 594        /* Sanity check */
 595        check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
 596
 597        /* Check we don't patch if the value matches */
 598        patch_feature_section(flag, &fixup);
 599        check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
 600
 601        /* Check we do patch if the value doesn't match */
 602        patch_feature_section(0, &fixup);
 603        check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0);
 604
 605        /* Check we do patch if the mask doesn't match */
 606        memcpy(ftr_fixup_test4, ftr_fixup_test4_orig, size);
 607        check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
 608        patch_feature_section(~flag, &fixup);
 609        check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0);
 610}
 611
 612static void test_alternative_case_with_branch(void)
 613{
 614        extern unsigned int ftr_fixup_test5[];
 615        extern unsigned int end_ftr_fixup_test5[];
 616        extern unsigned int ftr_fixup_test5_expected[];
 617        int size = 4 * (end_ftr_fixup_test5 - ftr_fixup_test5);
 618
 619        check(memcmp(ftr_fixup_test5, ftr_fixup_test5_expected, size) == 0);
 620}
 621
 622static void test_alternative_case_with_external_branch(void)
 623{
 624        extern unsigned int ftr_fixup_test6[];
 625        extern unsigned int end_ftr_fixup_test6[];
 626        extern unsigned int ftr_fixup_test6_expected[];
 627        int size = 4 * (end_ftr_fixup_test6 - ftr_fixup_test6);
 628
 629        check(memcmp(ftr_fixup_test6, ftr_fixup_test6_expected, size) == 0);
 630}
 631
 632static void test_alternative_case_with_branch_to_end(void)
 633{
 634        extern unsigned int ftr_fixup_test7[];
 635        extern unsigned int end_ftr_fixup_test7[];
 636        extern unsigned int ftr_fixup_test7_expected[];
 637        int size = 4 * (end_ftr_fixup_test7 - ftr_fixup_test7);
 638
 639        check(memcmp(ftr_fixup_test7, ftr_fixup_test7_expected, size) == 0);
 640}
 641
 642static void test_cpu_macros(void)
 643{
 644        extern u8 ftr_fixup_test_FTR_macros[];
 645        extern u8 ftr_fixup_test_FTR_macros_expected[];
 646        unsigned long size = ftr_fixup_test_FTR_macros_expected -
 647                             ftr_fixup_test_FTR_macros;
 648
 649        /* The fixups have already been done for us during boot */
 650        check(memcmp(ftr_fixup_test_FTR_macros,
 651                     ftr_fixup_test_FTR_macros_expected, size) == 0);
 652}
 653
 654static void test_fw_macros(void)
 655{
 656#ifdef CONFIG_PPC64
 657        extern u8 ftr_fixup_test_FW_FTR_macros[];
 658        extern u8 ftr_fixup_test_FW_FTR_macros_expected[];
 659        unsigned long size = ftr_fixup_test_FW_FTR_macros_expected -
 660                             ftr_fixup_test_FW_FTR_macros;
 661
 662        /* The fixups have already been done for us during boot */
 663        check(memcmp(ftr_fixup_test_FW_FTR_macros,
 664                     ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
 665#endif
 666}
 667
 668static void test_lwsync_macros(void)
 669{
 670        extern u8 lwsync_fixup_test[];
 671        extern u8 end_lwsync_fixup_test[];
 672        extern u8 lwsync_fixup_test_expected_LWSYNC[];
 673        extern u8 lwsync_fixup_test_expected_SYNC[];
 674        unsigned long size = end_lwsync_fixup_test -
 675                             lwsync_fixup_test;
 676
 677        /* The fixups have already been done for us during boot */
 678        if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) {
 679                check(memcmp(lwsync_fixup_test,
 680                             lwsync_fixup_test_expected_LWSYNC, size) == 0);
 681        } else {
 682                check(memcmp(lwsync_fixup_test,
 683                             lwsync_fixup_test_expected_SYNC, size) == 0);
 684        }
 685}
 686
 687static int __init test_feature_fixups(void)
 688{
 689        printk(KERN_DEBUG "Running feature fixup self-tests ...\n");
 690
 691        test_basic_patching();
 692        test_alternative_patching();
 693        test_alternative_case_too_big();
 694        test_alternative_case_too_small();
 695        test_alternative_case_with_branch();
 696        test_alternative_case_with_external_branch();
 697        test_alternative_case_with_branch_to_end();
 698        test_cpu_macros();
 699        test_fw_macros();
 700        test_lwsync_macros();
 701
 702        return 0;
 703}
 704late_initcall(test_feature_fixups);
 705
 706#endif /* CONFIG_FTR_FIXUP_SELFTEST */
 707