feature-fixups.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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_INST_NOP);
  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_ON(1);
  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. void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end)
  92. {
  93. unsigned int *start, *end, *dest;
  94. if (!(value & CPU_FTR_LWSYNC))
  95. return ;
  96. start = fixup_start;
  97. end = fixup_end;
  98. for (; start < end; start++) {
  99. dest = (void *)start + *start;
  100. patch_instruction(dest, PPC_INST_LWSYNC);
  101. }
  102. }
  103. #ifdef CONFIG_FTR_FIXUP_SELFTEST
  104. #define check(x) \
  105. if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__);
  106. /* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */
  107. static struct fixup_entry fixup;
  108. static long calc_offset(struct fixup_entry *entry, unsigned int *p)
  109. {
  110. return (unsigned long)p - (unsigned long)entry;
  111. }
  112. void test_basic_patching(void)
  113. {
  114. extern unsigned int ftr_fixup_test1;
  115. extern unsigned int end_ftr_fixup_test1;
  116. extern unsigned int ftr_fixup_test1_orig;
  117. extern unsigned int ftr_fixup_test1_expected;
  118. int size = &end_ftr_fixup_test1 - &ftr_fixup_test1;
  119. fixup.value = fixup.mask = 8;
  120. fixup.start_off = calc_offset(&fixup, &ftr_fixup_test1 + 1);
  121. fixup.end_off = calc_offset(&fixup, &ftr_fixup_test1 + 2);
  122. fixup.alt_start_off = fixup.alt_end_off = 0;
  123. /* Sanity check */
  124. check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
  125. /* Check we don't patch if the value matches */
  126. patch_feature_section(8, &fixup);
  127. check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
  128. /* Check we do patch if the value doesn't match */
  129. patch_feature_section(0, &fixup);
  130. check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
  131. /* Check we do patch if the mask doesn't match */
  132. memcpy(&ftr_fixup_test1, &ftr_fixup_test1_orig, size);
  133. check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
  134. patch_feature_section(~8, &fixup);
  135. check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
  136. }
  137. static void test_alternative_patching(void)
  138. {
  139. extern unsigned int ftr_fixup_test2;
  140. extern unsigned int end_ftr_fixup_test2;
  141. extern unsigned int ftr_fixup_test2_orig;
  142. extern unsigned int ftr_fixup_test2_alt;
  143. extern unsigned int ftr_fixup_test2_expected;
  144. int size = &end_ftr_fixup_test2 - &ftr_fixup_test2;
  145. fixup.value = fixup.mask = 0xF;
  146. fixup.start_off = calc_offset(&fixup, &ftr_fixup_test2 + 1);
  147. fixup.end_off = calc_offset(&fixup, &ftr_fixup_test2 + 2);
  148. fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test2_alt);
  149. fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test2_alt + 1);
  150. /* Sanity check */
  151. check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
  152. /* Check we don't patch if the value matches */
  153. patch_feature_section(0xF, &fixup);
  154. check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
  155. /* Check we do patch if the value doesn't match */
  156. patch_feature_section(0, &fixup);
  157. check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
  158. /* Check we do patch if the mask doesn't match */
  159. memcpy(&ftr_fixup_test2, &ftr_fixup_test2_orig, size);
  160. check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
  161. patch_feature_section(~0xF, &fixup);
  162. check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
  163. }
  164. static void test_alternative_case_too_big(void)
  165. {
  166. extern unsigned int ftr_fixup_test3;
  167. extern unsigned int end_ftr_fixup_test3;
  168. extern unsigned int ftr_fixup_test3_orig;
  169. extern unsigned int ftr_fixup_test3_alt;
  170. int size = &end_ftr_fixup_test3 - &ftr_fixup_test3;
  171. fixup.value = fixup.mask = 0xC;
  172. fixup.start_off = calc_offset(&fixup, &ftr_fixup_test3 + 1);
  173. fixup.end_off = calc_offset(&fixup, &ftr_fixup_test3 + 2);
  174. fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test3_alt);
  175. fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test3_alt + 2);
  176. /* Sanity check */
  177. check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
  178. /* Expect nothing to be patched, and the error returned to us */
  179. check(patch_feature_section(0xF, &fixup) == 1);
  180. check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
  181. check(patch_feature_section(0, &fixup) == 1);
  182. check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
  183. check(patch_feature_section(~0xF, &fixup) == 1);
  184. check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
  185. }
  186. static void test_alternative_case_too_small(void)
  187. {
  188. extern unsigned int ftr_fixup_test4;
  189. extern unsigned int end_ftr_fixup_test4;
  190. extern unsigned int ftr_fixup_test4_orig;
  191. extern unsigned int ftr_fixup_test4_alt;
  192. extern unsigned int ftr_fixup_test4_expected;
  193. int size = &end_ftr_fixup_test4 - &ftr_fixup_test4;
  194. unsigned long flag;
  195. /* Check a high-bit flag */
  196. flag = 1UL << ((sizeof(unsigned long) - 1) * 8);
  197. fixup.value = fixup.mask = flag;
  198. fixup.start_off = calc_offset(&fixup, &ftr_fixup_test4 + 1);
  199. fixup.end_off = calc_offset(&fixup, &ftr_fixup_test4 + 5);
  200. fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test4_alt);
  201. fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test4_alt + 2);
  202. /* Sanity check */
  203. check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
  204. /* Check we don't patch if the value matches */
  205. patch_feature_section(flag, &fixup);
  206. check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_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_test4, &ftr_fixup_test4_expected, size) == 0);
  210. /* Check we do patch if the mask doesn't match */
  211. memcpy(&ftr_fixup_test4, &ftr_fixup_test4_orig, size);
  212. check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
  213. patch_feature_section(~flag, &fixup);
  214. check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
  215. }
  216. static void test_alternative_case_with_branch(void)
  217. {
  218. extern unsigned int ftr_fixup_test5;
  219. extern unsigned int end_ftr_fixup_test5;
  220. extern unsigned int ftr_fixup_test5_expected;
  221. int size = &end_ftr_fixup_test5 - &ftr_fixup_test5;
  222. check(memcmp(&ftr_fixup_test5, &ftr_fixup_test5_expected, size) == 0);
  223. }
  224. static void test_alternative_case_with_external_branch(void)
  225. {
  226. extern unsigned int ftr_fixup_test6;
  227. extern unsigned int end_ftr_fixup_test6;
  228. extern unsigned int ftr_fixup_test6_expected;
  229. int size = &end_ftr_fixup_test6 - &ftr_fixup_test6;
  230. check(memcmp(&ftr_fixup_test6, &ftr_fixup_test6_expected, size) == 0);
  231. }
  232. static void test_cpu_macros(void)
  233. {
  234. extern void ftr_fixup_test_FTR_macros;
  235. extern void ftr_fixup_test_FTR_macros_expected;
  236. unsigned long size = &ftr_fixup_test_FTR_macros_expected -
  237. &ftr_fixup_test_FTR_macros;
  238. /* The fixups have already been done for us during boot */
  239. check(memcmp(&ftr_fixup_test_FTR_macros,
  240. &ftr_fixup_test_FTR_macros_expected, size) == 0);
  241. }
  242. static void test_fw_macros(void)
  243. {
  244. #ifdef CONFIG_PPC64
  245. extern void ftr_fixup_test_FW_FTR_macros;
  246. extern void ftr_fixup_test_FW_FTR_macros_expected;
  247. unsigned long size = &ftr_fixup_test_FW_FTR_macros_expected -
  248. &ftr_fixup_test_FW_FTR_macros;
  249. /* The fixups have already been done for us during boot */
  250. check(memcmp(&ftr_fixup_test_FW_FTR_macros,
  251. &ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
  252. #endif
  253. }
  254. static void test_lwsync_macros(void)
  255. {
  256. extern void lwsync_fixup_test;
  257. extern void end_lwsync_fixup_test;
  258. extern void lwsync_fixup_test_expected_LWSYNC;
  259. extern void lwsync_fixup_test_expected_SYNC;
  260. unsigned long size = &end_lwsync_fixup_test -
  261. &lwsync_fixup_test;
  262. /* The fixups have already been done for us during boot */
  263. if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) {
  264. check(memcmp(&lwsync_fixup_test,
  265. &lwsync_fixup_test_expected_LWSYNC, size) == 0);
  266. } else {
  267. check(memcmp(&lwsync_fixup_test,
  268. &lwsync_fixup_test_expected_SYNC, size) == 0);
  269. }
  270. }
  271. static int __init test_feature_fixups(void)
  272. {
  273. printk(KERN_DEBUG "Running feature fixup self-tests ...\n");
  274. test_basic_patching();
  275. test_alternative_patching();
  276. test_alternative_case_too_big();
  277. test_alternative_case_too_small();
  278. test_alternative_case_with_branch();
  279. test_alternative_case_with_external_branch();
  280. test_cpu_macros();
  281. test_fw_macros();
  282. test_lwsync_macros();
  283. return 0;
  284. }
  285. late_initcall(test_feature_fixups);
  286. #endif /* CONFIG_FTR_FIXUP_SELFTEST */