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