ftrace.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Code for replacing ftrace calls with jumps.
  4. *
  5. * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
  6. *
  7. * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
  8. *
  9. * Added function graph tracer code, taken from x86 that was written
  10. * by Frederic Weisbecker, and ported to PPC by Steven Rostedt.
  11. *
  12. */
  13. #define pr_fmt(fmt) "ftrace-powerpc: " fmt
  14. #include <linux/spinlock.h>
  15. #include <linux/hardirq.h>
  16. #include <linux/uaccess.h>
  17. #include <linux/module.h>
  18. #include <linux/ftrace.h>
  19. #include <linux/percpu.h>
  20. #include <linux/init.h>
  21. #include <linux/list.h>
  22. #include <asm/cacheflush.h>
  23. #include <asm/code-patching.h>
  24. #include <asm/ftrace.h>
  25. #include <asm/syscall.h>
  26. #include <asm/inst.h>
  27. #include <asm/sections.h>
  28. #define NUM_FTRACE_TRAMPS 2
  29. static unsigned long ftrace_tramps[NUM_FTRACE_TRAMPS];
  30. unsigned long ftrace_call_adjust(unsigned long addr)
  31. {
  32. if (addr >= (unsigned long)__exittext_begin && addr < (unsigned long)__exittext_end)
  33. return 0;
  34. if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY))
  35. addr += MCOUNT_INSN_SIZE;
  36. return addr;
  37. }
  38. static ppc_inst_t ftrace_create_branch_inst(unsigned long ip, unsigned long addr, int link)
  39. {
  40. ppc_inst_t op;
  41. WARN_ON(!is_offset_in_branch_range(addr - ip));
  42. create_branch(&op, (u32 *)ip, addr, link ? BRANCH_SET_LINK : 0);
  43. return op;
  44. }
  45. static inline int ftrace_read_inst(unsigned long ip, ppc_inst_t *op)
  46. {
  47. if (copy_inst_from_kernel_nofault(op, (void *)ip)) {
  48. pr_err("0x%lx: fetching instruction failed\n", ip);
  49. return -EFAULT;
  50. }
  51. return 0;
  52. }
  53. static inline int ftrace_validate_inst(unsigned long ip, ppc_inst_t inst)
  54. {
  55. ppc_inst_t op;
  56. int ret;
  57. ret = ftrace_read_inst(ip, &op);
  58. if (!ret && !ppc_inst_equal(op, inst)) {
  59. pr_err("0x%lx: expected (%08lx) != found (%08lx)\n",
  60. ip, ppc_inst_as_ulong(inst), ppc_inst_as_ulong(op));
  61. ret = -EINVAL;
  62. }
  63. return ret;
  64. }
  65. static inline int ftrace_modify_code(unsigned long ip, ppc_inst_t old, ppc_inst_t new)
  66. {
  67. int ret = ftrace_validate_inst(ip, old);
  68. if (!ret)
  69. ret = patch_instruction((u32 *)ip, new);
  70. return ret;
  71. }
  72. static int is_bl_op(ppc_inst_t op)
  73. {
  74. return (ppc_inst_val(op) & ~PPC_LI_MASK) == PPC_RAW_BL(0);
  75. }
  76. static unsigned long find_ftrace_tramp(unsigned long ip)
  77. {
  78. int i;
  79. for (i = 0; i < NUM_FTRACE_TRAMPS; i++)
  80. if (!ftrace_tramps[i])
  81. continue;
  82. else if (is_offset_in_branch_range(ftrace_tramps[i] - ip))
  83. return ftrace_tramps[i];
  84. return 0;
  85. }
  86. static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_inst_t *call_inst)
  87. {
  88. unsigned long ip = rec->ip;
  89. unsigned long stub;
  90. if (is_offset_in_branch_range(addr - ip)) {
  91. /* Within range */
  92. stub = addr;
  93. #ifdef CONFIG_MODULES
  94. } else if (rec->arch.mod) {
  95. /* Module code would be going to one of the module stubs */
  96. stub = (addr == (unsigned long)ftrace_caller ? rec->arch.mod->arch.tramp :
  97. rec->arch.mod->arch.tramp_regs);
  98. #endif
  99. } else if (core_kernel_text(ip)) {
  100. /* We would be branching to one of our ftrace stubs */
  101. stub = find_ftrace_tramp(ip);
  102. if (!stub) {
  103. pr_err("0x%lx: No ftrace stubs reachable\n", ip);
  104. return -EINVAL;
  105. }
  106. } else {
  107. return -EINVAL;
  108. }
  109. *call_inst = ftrace_create_branch_inst(ip, stub, 1);
  110. return 0;
  111. }
  112. #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
  113. int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned long addr)
  114. {
  115. /* This should never be called since we override ftrace_replace_code() */
  116. WARN_ON(1);
  117. return -EINVAL;
  118. }
  119. #endif
  120. int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
  121. {
  122. ppc_inst_t old, new;
  123. int ret;
  124. /* This can only ever be called during module load */
  125. if (WARN_ON(!IS_ENABLED(CONFIG_MODULES) || core_kernel_text(rec->ip)))
  126. return -EINVAL;
  127. old = ppc_inst(PPC_RAW_NOP());
  128. ret = ftrace_get_call_inst(rec, addr, &new);
  129. if (ret)
  130. return ret;
  131. return ftrace_modify_code(rec->ip, old, new);
  132. }
  133. int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr)
  134. {
  135. /*
  136. * This should never be called since we override ftrace_replace_code(),
  137. * as well as ftrace_init_nop()
  138. */
  139. WARN_ON(1);
  140. return -EINVAL;
  141. }
  142. void ftrace_replace_code(int enable)
  143. {
  144. ppc_inst_t old, new, call_inst, new_call_inst;
  145. ppc_inst_t nop_inst = ppc_inst(PPC_RAW_NOP());
  146. unsigned long ip, new_addr, addr;
  147. struct ftrace_rec_iter *iter;
  148. struct dyn_ftrace *rec;
  149. int ret = 0, update;
  150. for_ftrace_rec_iter(iter) {
  151. rec = ftrace_rec_iter_record(iter);
  152. ip = rec->ip;
  153. if (rec->flags & FTRACE_FL_DISABLED && !(rec->flags & FTRACE_FL_ENABLED))
  154. continue;
  155. addr = ftrace_get_addr_curr(rec);
  156. new_addr = ftrace_get_addr_new(rec);
  157. update = ftrace_update_record(rec, enable);
  158. switch (update) {
  159. case FTRACE_UPDATE_IGNORE:
  160. default:
  161. continue;
  162. case FTRACE_UPDATE_MODIFY_CALL:
  163. ret = ftrace_get_call_inst(rec, new_addr, &new_call_inst);
  164. ret |= ftrace_get_call_inst(rec, addr, &call_inst);
  165. old = call_inst;
  166. new = new_call_inst;
  167. break;
  168. case FTRACE_UPDATE_MAKE_NOP:
  169. ret = ftrace_get_call_inst(rec, addr, &call_inst);
  170. old = call_inst;
  171. new = nop_inst;
  172. break;
  173. case FTRACE_UPDATE_MAKE_CALL:
  174. ret = ftrace_get_call_inst(rec, new_addr, &call_inst);
  175. old = nop_inst;
  176. new = call_inst;
  177. break;
  178. }
  179. if (!ret)
  180. ret = ftrace_modify_code(ip, old, new);
  181. if (ret)
  182. goto out;
  183. }
  184. out:
  185. if (ret)
  186. ftrace_bug(ret, rec);
  187. return;
  188. }
  189. int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
  190. {
  191. unsigned long addr, ip = rec->ip;
  192. ppc_inst_t old, new;
  193. int ret = 0;
  194. /* Verify instructions surrounding the ftrace location */
  195. if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) {
  196. /* Expect nops */
  197. ret = ftrace_validate_inst(ip - 4, ppc_inst(PPC_RAW_NOP()));
  198. if (!ret)
  199. ret = ftrace_validate_inst(ip, ppc_inst(PPC_RAW_NOP()));
  200. } else if (IS_ENABLED(CONFIG_PPC32)) {
  201. /* Expected sequence: 'mflr r0', 'stw r0,4(r1)', 'bl _mcount' */
  202. ret = ftrace_validate_inst(ip - 8, ppc_inst(PPC_RAW_MFLR(_R0)));
  203. if (!ret)
  204. ret = ftrace_validate_inst(ip - 4, ppc_inst(PPC_RAW_STW(_R0, _R1, 4)));
  205. } else if (IS_ENABLED(CONFIG_MPROFILE_KERNEL)) {
  206. /* Expected sequence: 'mflr r0', ['std r0,16(r1)'], 'bl _mcount' */
  207. ret = ftrace_read_inst(ip - 4, &old);
  208. if (!ret && !ppc_inst_equal(old, ppc_inst(PPC_RAW_MFLR(_R0)))) {
  209. ret = ftrace_validate_inst(ip - 8, ppc_inst(PPC_RAW_MFLR(_R0)));
  210. ret |= ftrace_validate_inst(ip - 4, ppc_inst(PPC_RAW_STD(_R0, _R1, 16)));
  211. }
  212. } else {
  213. return -EINVAL;
  214. }
  215. if (ret)
  216. return ret;
  217. if (!core_kernel_text(ip)) {
  218. if (!mod) {
  219. pr_err("0x%lx: No module provided for non-kernel address\n", ip);
  220. return -EFAULT;
  221. }
  222. rec->arch.mod = mod;
  223. }
  224. /* Nop-out the ftrace location */
  225. new = ppc_inst(PPC_RAW_NOP());
  226. addr = MCOUNT_ADDR;
  227. if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) {
  228. /* we instead patch-in the 'mflr r0' */
  229. old = ppc_inst(PPC_RAW_NOP());
  230. new = ppc_inst(PPC_RAW_MFLR(_R0));
  231. ret = ftrace_modify_code(ip - 4, old, new);
  232. } else if (is_offset_in_branch_range(addr - ip)) {
  233. /* Within range */
  234. old = ftrace_create_branch_inst(ip, addr, 1);
  235. ret = ftrace_modify_code(ip, old, new);
  236. } else if (core_kernel_text(ip) || (IS_ENABLED(CONFIG_MODULES) && mod)) {
  237. /*
  238. * We would be branching to a linker-generated stub, or to the module _mcount
  239. * stub. Let's just confirm we have a 'bl' here.
  240. */
  241. ret = ftrace_read_inst(ip, &old);
  242. if (ret)
  243. return ret;
  244. if (!is_bl_op(old)) {
  245. pr_err("0x%lx: expected (bl) != found (%08lx)\n", ip, ppc_inst_as_ulong(old));
  246. return -EINVAL;
  247. }
  248. ret = patch_instruction((u32 *)ip, new);
  249. } else {
  250. return -EINVAL;
  251. }
  252. return ret;
  253. }
  254. int ftrace_update_ftrace_func(ftrace_func_t func)
  255. {
  256. unsigned long ip = (unsigned long)(&ftrace_call);
  257. ppc_inst_t old, new;
  258. int ret;
  259. old = ppc_inst_read((u32 *)&ftrace_call);
  260. new = ftrace_create_branch_inst(ip, ppc_function_entry(func), 1);
  261. ret = ftrace_modify_code(ip, old, new);
  262. /* Also update the regs callback function */
  263. if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS) && !ret) {
  264. ip = (unsigned long)(&ftrace_regs_call);
  265. old = ppc_inst_read((u32 *)&ftrace_regs_call);
  266. new = ftrace_create_branch_inst(ip, ppc_function_entry(func), 1);
  267. ret = ftrace_modify_code(ip, old, new);
  268. }
  269. return ret;
  270. }
  271. /*
  272. * Use the default ftrace_modify_all_code, but without
  273. * stop_machine().
  274. */
  275. void arch_ftrace_update_code(int command)
  276. {
  277. ftrace_modify_all_code(command);
  278. }
  279. void ftrace_free_init_tramp(void)
  280. {
  281. int i;
  282. for (i = 0; i < NUM_FTRACE_TRAMPS && ftrace_tramps[i]; i++)
  283. if (ftrace_tramps[i] == (unsigned long)ftrace_tramp_init) {
  284. ftrace_tramps[i] = 0;
  285. return;
  286. }
  287. }
  288. static void __init add_ftrace_tramp(unsigned long tramp)
  289. {
  290. int i;
  291. for (i = 0; i < NUM_FTRACE_TRAMPS; i++)
  292. if (!ftrace_tramps[i]) {
  293. ftrace_tramps[i] = tramp;
  294. return;
  295. }
  296. }
  297. int __init ftrace_dyn_arch_init(void)
  298. {
  299. unsigned int *tramp[] = { ftrace_tramp_text, ftrace_tramp_init };
  300. unsigned long addr = FTRACE_REGS_ADDR;
  301. long reladdr;
  302. int i;
  303. u32 stub_insns[] = {
  304. #ifdef CONFIG_PPC_KERNEL_PCREL
  305. /* pla r12,addr */
  306. PPC_PREFIX_MLS | __PPC_PRFX_R(1),
  307. PPC_INST_PADDI | ___PPC_RT(_R12),
  308. PPC_RAW_MTCTR(_R12),
  309. PPC_RAW_BCTR()
  310. #elif defined(CONFIG_PPC64)
  311. PPC_RAW_LD(_R12, _R13, offsetof(struct paca_struct, kernel_toc)),
  312. PPC_RAW_ADDIS(_R12, _R12, 0),
  313. PPC_RAW_ADDI(_R12, _R12, 0),
  314. PPC_RAW_MTCTR(_R12),
  315. PPC_RAW_BCTR()
  316. #else
  317. PPC_RAW_LIS(_R12, 0),
  318. PPC_RAW_ADDI(_R12, _R12, 0),
  319. PPC_RAW_MTCTR(_R12),
  320. PPC_RAW_BCTR()
  321. #endif
  322. };
  323. if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) {
  324. for (i = 0; i < 2; i++) {
  325. reladdr = addr - (unsigned long)tramp[i];
  326. if (reladdr >= (long)SZ_8G || reladdr < -(long)SZ_8G) {
  327. pr_err("Address of %ps out of range of pcrel address.\n",
  328. (void *)addr);
  329. return -1;
  330. }
  331. memcpy(tramp[i], stub_insns, sizeof(stub_insns));
  332. tramp[i][0] |= IMM_H18(reladdr);
  333. tramp[i][1] |= IMM_L(reladdr);
  334. add_ftrace_tramp((unsigned long)tramp[i]);
  335. }
  336. } else if (IS_ENABLED(CONFIG_PPC64)) {
  337. reladdr = addr - kernel_toc_addr();
  338. if (reladdr >= (long)SZ_2G || reladdr < -(long long)SZ_2G) {
  339. pr_err("Address of %ps out of range of kernel_toc.\n",
  340. (void *)addr);
  341. return -1;
  342. }
  343. for (i = 0; i < 2; i++) {
  344. memcpy(tramp[i], stub_insns, sizeof(stub_insns));
  345. tramp[i][1] |= PPC_HA(reladdr);
  346. tramp[i][2] |= PPC_LO(reladdr);
  347. add_ftrace_tramp((unsigned long)tramp[i]);
  348. }
  349. } else {
  350. for (i = 0; i < 2; i++) {
  351. memcpy(tramp[i], stub_insns, sizeof(stub_insns));
  352. tramp[i][0] |= PPC_HA(addr);
  353. tramp[i][1] |= PPC_LO(addr);
  354. add_ftrace_tramp((unsigned long)tramp[i]);
  355. }
  356. }
  357. return 0;
  358. }
  359. #ifdef CONFIG_FUNCTION_GRAPH_TRACER
  360. void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
  361. struct ftrace_ops *op, struct ftrace_regs *fregs)
  362. {
  363. unsigned long sp = fregs->regs.gpr[1];
  364. int bit;
  365. if (unlikely(ftrace_graph_is_dead()))
  366. goto out;
  367. if (unlikely(atomic_read(&current->tracing_graph_pause)))
  368. goto out;
  369. bit = ftrace_test_recursion_trylock(ip, parent_ip);
  370. if (bit < 0)
  371. goto out;
  372. if (!function_graph_enter(parent_ip, ip, 0, (unsigned long *)sp))
  373. parent_ip = ppc_function_entry(return_to_handler);
  374. ftrace_test_recursion_unlock(bit);
  375. out:
  376. fregs->regs.link = parent_ip;
  377. }
  378. #endif /* CONFIG_FUNCTION_GRAPH_TRACER */