feature-fixups.c 13 KB

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