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 23 struct 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 32 static 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 42 static 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 65 static 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 94 void 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 114 void 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 */ 137 static struct fixup_entry fixup; 138 139 static long calc_offset(struct fixup_entry *entry, unsigned int *p) 140 { 141 return (unsigned long)p - (unsigned long)entry; 142 } 143 144 void 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 175 static 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 208 static 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 234 static 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 270 static 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 280 static 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 290 static 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 302 static 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 316 static 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 335 static 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 } 351 late_initcall(test_feature_fixups); 352 353 #endif /* CONFIG_FTR_FIXUP_SELFTEST */ 354