linux/arch/powerpc/lib/feature-fixups.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org)
   3 *
   4 *  Modifications for ppc64:
   5 *      Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com>
   6 *
   7 *  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 */
  14
  15#include <linux/types.h>
  16#include <linux/kernel.h>
  17#include <linux/string.h>
  18#include <linux/init.h>
  19#include <asm/cputable.h>
  20#include <asm/code-patching.h>
  21
  22
  23struct fixup_entry {
  24        unsigned long   mask;
  25        unsigned long   value;
  26        long            start_off;
  27        long            end_off;
  28        long            alt_start_off;
  29        long            alt_end_off;
  30};
  31
  32static unsigned int *calc_addr(struct fixup_entry *fcur, long offset)
  33{
  34        /*
  35         * We store the offset to the code as a negative offset from
  36         * the start of the alt_entry, to support the VDSO. This
  37         * routine converts that back into an actual address.
  38         */
  39        return (unsigned int *)((unsigned long)fcur + offset);
  40}
  41
  42static int patch_alt_instruction(unsigned int *src, unsigned int *dest,
  43                                 unsigned int *alt_start, unsigned int *alt_end)
  44{
  45        unsigned int instr;
  46
  47        instr = *src;
  48
  49        if (instr_is_relative_branch(*src)) {
  50                unsigned int *target = (unsigned int *)branch_target(src);
  51
  52                /* Branch within the section doesn't need translating */
  53                if (target < alt_start || target >= alt_end) {
  54                        instr = translate_branch(dest, src);
  55                        if (!instr)
  56                                return 1;
  57                }
  58        }
  59
  60        patch_instruction(dest, instr);
  61
  62        return 0;
  63}
  64
  65static int patch_feature_section(unsigned long value, struct fixup_entry *fcur)
  66{
  67        unsigned int *start, *end, *alt_start, *alt_end, *src, *dest;
  68
  69        start = calc_addr(fcur, fcur->start_off);
  70        end = calc_addr(fcur, fcur->end_off);
  71        alt_start = calc_addr(fcur, fcur->alt_start_off);
  72        alt_end = calc_addr(fcur, fcur->alt_end_off);
  73
  74        if ((alt_end - alt_start) > (end - start))
  75                return 1;
  76
  77        if ((value & fcur->mask) == fcur->value)
  78                return 0;
  79
  80        src = alt_start;
  81        dest = start;
  82
  83        for (; src < alt_end; src++, dest++) {
  84                if (patch_alt_instruction(src, dest, alt_start, alt_end))
  85                        return 1;
  86        }
  87
  88        for (; dest < end; dest++)
  89                patch_instruction(dest, PPC_INST_NOP);
  90
  91        return 0;
  92}
  93
  94void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
  95{
  96        struct fixup_entry *fcur, *fend;
  97
  98        fcur = fixup_start;
  99        fend = fixup_end;
 100
 101        for (; fcur < fend; fcur++) {
 102                if (patch_feature_section(value, fcur)) {
 103                        WARN_ON(1);
 104                        printk("Unable to patch feature section at %p - %p" \
 105                                " with %p - %p\n",
 106                                calc_addr(fcur, fcur->start_off),
 107                                calc_addr(fcur, fcur->end_off),
 108                                calc_addr(fcur, fcur->alt_start_off),
 109                                calc_addr(fcur, fcur->alt_end_off));
 110                }
 111        }
 112}
 113
 114void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end)
 115{
 116        long *start, *end;
 117        unsigned int *dest;
 118
 119        if (!(value & CPU_FTR_LWSYNC))
 120                return ;
 121
 122        start = fixup_start;
 123        end = fixup_end;
 124
 125        for (; start < end; start++) {
 126                dest = (void *)start + *start;
 127                patch_instruction(dest, PPC_INST_LWSYNC);
 128        }
 129}
 130
 131#ifdef CONFIG_FTR_FIXUP_SELFTEST
 132
 133#define check(x)        \
 134        if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__);
 135
 136/* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */
 137static struct fixup_entry fixup;
 138
 139static long calc_offset(struct fixup_entry *entry, unsigned int *p)
 140{
 141        return (unsigned long)p - (unsigned long)entry;
 142}
 143
 144void test_basic_patching(void)
 145{
 146        extern unsigned int ftr_fixup_test1;
 147        extern unsigned int end_ftr_fixup_test1;
 148        extern unsigned int ftr_fixup_test1_orig;
 149        extern unsigned int ftr_fixup_test1_expected;
 150        int size = &end_ftr_fixup_test1 - &ftr_fixup_test1;
 151
 152        fixup.value = fixup.mask = 8;
 153        fixup.start_off = calc_offset(&fixup, &ftr_fixup_test1 + 1);
 154        fixup.end_off = calc_offset(&fixup, &ftr_fixup_test1 + 2);
 155        fixup.alt_start_off = fixup.alt_end_off = 0;
 156
 157        /* Sanity check */
 158        check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
 159
 160        /* Check we don't patch if the value matches */
 161        patch_feature_section(8, &fixup);
 162        check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
 163
 164        /* Check we do patch if the value doesn't match */
 165        patch_feature_section(0, &fixup);
 166        check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
 167
 168        /* Check we do patch if the mask doesn't match */
 169        memcpy(&ftr_fixup_test1, &ftr_fixup_test1_orig, size);
 170        check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
 171        patch_feature_section(~8, &fixup);
 172        check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
 173}
 174
 175static void test_alternative_patching(void)
 176{
 177        extern unsigned int ftr_fixup_test2;
 178        extern unsigned int end_ftr_fixup_test2;
 179        extern unsigned int ftr_fixup_test2_orig;
 180        extern unsigned int ftr_fixup_test2_alt;
 181        extern unsigned int ftr_fixup_test2_expected;
 182        int size = &end_ftr_fixup_test2 - &ftr_fixup_test2;
 183
 184        fixup.value = fixup.mask = 0xF;
 185        fixup.start_off = calc_offset(&fixup, &ftr_fixup_test2 + 1);
 186        fixup.end_off = calc_offset(&fixup, &ftr_fixup_test2 + 2);
 187        fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test2_alt);
 188        fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test2_alt + 1);
 189
 190        /* Sanity check */
 191        check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
 192
 193        /* Check we don't patch if the value matches */
 194        patch_feature_section(0xF, &fixup);
 195        check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
 196
 197        /* Check we do patch if the value doesn't match */
 198        patch_feature_section(0, &fixup);
 199        check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
 200
 201        /* Check we do patch if the mask doesn't match */
 202        memcpy(&ftr_fixup_test2, &ftr_fixup_test2_orig, size);
 203        check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
 204        patch_feature_section(~0xF, &fixup);
 205        check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
 206}
 207
 208static void test_alternative_case_too_big(void)
 209{
 210        extern unsigned int ftr_fixup_test3;
 211        extern unsigned int end_ftr_fixup_test3;
 212        extern unsigned int ftr_fixup_test3_orig;
 213        extern unsigned int ftr_fixup_test3_alt;
 214        int size = &end_ftr_fixup_test3 - &ftr_fixup_test3;
 215
 216        fixup.value = fixup.mask = 0xC;
 217        fixup.start_off = calc_offset(&fixup, &ftr_fixup_test3 + 1);
 218        fixup.end_off = calc_offset(&fixup, &ftr_fixup_test3 + 2);
 219        fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test3_alt);
 220        fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test3_alt + 2);
 221
 222        /* Sanity check */
 223        check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
 224
 225        /* Expect nothing to be patched, and the error returned to us */
 226        check(patch_feature_section(0xF, &fixup) == 1);
 227        check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
 228        check(patch_feature_section(0, &fixup) == 1);
 229        check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
 230        check(patch_feature_section(~0xF, &fixup) == 1);
 231        check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
 232}
 233
 234static void test_alternative_case_too_small(void)
 235{
 236        extern unsigned int ftr_fixup_test4;
 237        extern unsigned int end_ftr_fixup_test4;
 238        extern unsigned int ftr_fixup_test4_orig;
 239        extern unsigned int ftr_fixup_test4_alt;
 240        extern unsigned int ftr_fixup_test4_expected;
 241        int size = &end_ftr_fixup_test4 - &ftr_fixup_test4;
 242        unsigned long flag;
 243
 244        /* Check a high-bit flag */
 245        flag = 1UL << ((sizeof(unsigned long) - 1) * 8);
 246        fixup.value = fixup.mask = flag;
 247        fixup.start_off = calc_offset(&fixup, &ftr_fixup_test4 + 1);
 248        fixup.end_off = calc_offset(&fixup, &ftr_fixup_test4 + 5);
 249        fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test4_alt);
 250        fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test4_alt + 2);
 251
 252        /* Sanity check */
 253        check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
 254
 255        /* Check we don't patch if the value matches */
 256        patch_feature_section(flag, &fixup);
 257        check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
 258
 259        /* Check we do patch if the value doesn't match */
 260        patch_feature_section(0, &fixup);
 261        check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
 262
 263        /* Check we do patch if the mask doesn't match */
 264        memcpy(&ftr_fixup_test4, &ftr_fixup_test4_orig, size);
 265        check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
 266        patch_feature_section(~flag, &fixup);
 267        check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
 268}
 269
 270static void test_alternative_case_with_branch(void)
 271{
 272        extern unsigned int ftr_fixup_test5;
 273        extern unsigned int end_ftr_fixup_test5;
 274        extern unsigned int ftr_fixup_test5_expected;
 275        int size = &end_ftr_fixup_test5 - &ftr_fixup_test5;
 276
 277        check(memcmp(&ftr_fixup_test5, &ftr_fixup_test5_expected, size) == 0);
 278}
 279
 280static void test_alternative_case_with_external_branch(void)
 281{
 282        extern unsigned int ftr_fixup_test6;
 283        extern unsigned int end_ftr_fixup_test6;
 284        extern unsigned int ftr_fixup_test6_expected;
 285        int size = &end_ftr_fixup_test6 - &ftr_fixup_test6;
 286
 287        check(memcmp(&ftr_fixup_test6, &ftr_fixup_test6_expected, size) == 0);
 288}
 289
 290static void test_cpu_macros(void)
 291{
 292        extern u8 ftr_fixup_test_FTR_macros;
 293        extern u8 ftr_fixup_test_FTR_macros_expected;
 294        unsigned long size = &ftr_fixup_test_FTR_macros_expected -
 295                             &ftr_fixup_test_FTR_macros;
 296
 297        /* The fixups have already been done for us during boot */
 298        check(memcmp(&ftr_fixup_test_FTR_macros,
 299                     &ftr_fixup_test_FTR_macros_expected, size) == 0);
 300}
 301
 302static void test_fw_macros(void)
 303{
 304#ifdef CONFIG_PPC64
 305        extern u8 ftr_fixup_test_FW_FTR_macros;
 306        extern u8 ftr_fixup_test_FW_FTR_macros_expected;
 307        unsigned long size = &ftr_fixup_test_FW_FTR_macros_expected -
 308                             &ftr_fixup_test_FW_FTR_macros;
 309
 310        /* The fixups have already been done for us during boot */
 311        check(memcmp(&ftr_fixup_test_FW_FTR_macros,
 312                     &ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
 313#endif
 314}
 315
 316static void test_lwsync_macros(void)
 317{
 318        extern u8 lwsync_fixup_test;
 319        extern u8 end_lwsync_fixup_test;
 320        extern u8 lwsync_fixup_test_expected_LWSYNC;
 321        extern u8 lwsync_fixup_test_expected_SYNC;
 322        unsigned long size = &end_lwsync_fixup_test -
 323                             &lwsync_fixup_test;
 324
 325        /* The fixups have already been done for us during boot */
 326        if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) {
 327                check(memcmp(&lwsync_fixup_test,
 328                             &lwsync_fixup_test_expected_LWSYNC, size) == 0);
 329        } else {
 330                check(memcmp(&lwsync_fixup_test,
 331                             &lwsync_fixup_test_expected_SYNC, size) == 0);
 332        }
 333}
 334
 335static int __init test_feature_fixups(void)
 336{
 337        printk(KERN_DEBUG "Running feature fixup self-tests ...\n");
 338
 339        test_basic_patching();
 340        test_alternative_patching();
 341        test_alternative_case_too_big();
 342        test_alternative_case_too_small();
 343        test_alternative_case_with_branch();
 344        test_alternative_case_with_external_branch();
 345        test_cpu_macros();
 346        test_fw_macros();
 347        test_lwsync_macros();
 348
 349        return 0;
 350}
 351late_initcall(test_feature_fixups);
 352
 353#endif /* CONFIG_FTR_FIXUP_SELFTEST */
 354