feature-fixups.c 9.7 KB

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