feature-fixups.c 11 KB

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