ftrace.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /*
  2. * Code for replacing ftrace calls with jumps.
  3. *
  4. * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
  5. * Copyright (C) 2009 DSLab, Lanzhou University, China
  6. * Author: Wu Zhangjin <wuzj@lemote.com>
  7. *
  8. * Thanks goes to Steven Rostedt for writing the original x86 version.
  9. */
  10. #include <linux/uaccess.h>
  11. #include <linux/init.h>
  12. #include <linux/ftrace.h>
  13. #include <asm/cacheflush.h>
  14. #ifdef CONFIG_DYNAMIC_FTRACE
  15. #define JAL 0x0c000000 /* jump & link: ip --> ra, jump to target */
  16. #define ADDR_MASK 0x03ffffff /* op_code|addr : 31...26|25 ....0 */
  17. #define jump_insn_encode(op_code, addr) \
  18. ((unsigned int)((op_code) | (((addr) >> 2) & ADDR_MASK)))
  19. static unsigned int ftrace_nop = 0x00000000;
  20. static int ftrace_modify_code(unsigned long ip, unsigned int new_code)
  21. {
  22. *(unsigned int *)ip = new_code;
  23. flush_icache_range(ip, ip + 8);
  24. return 0;
  25. }
  26. static int lui_v1;
  27. static int jal_mcount;
  28. int ftrace_make_nop(struct module *mod,
  29. struct dyn_ftrace *rec, unsigned long addr)
  30. {
  31. unsigned int new;
  32. unsigned long ip = rec->ip;
  33. /* We have compiled module with -mlong-calls, but compiled the kernel
  34. * without it, we need to cope with them respectively. */
  35. if (ip & 0x40000000) {
  36. /* record it for ftrace_make_call */
  37. if (lui_v1 == 0)
  38. lui_v1 = *(unsigned int *)ip;
  39. /* lui v1, hi_16bit_of_mcount --> b 1f (0x10000004)
  40. * addiu v1, v1, low_16bit_of_mcount
  41. * move at, ra
  42. * jalr v1
  43. * nop
  44. * 1f: (ip + 12)
  45. */
  46. new = 0x10000004;
  47. } else {
  48. /* record/calculate it for ftrace_make_call */
  49. if (jal_mcount == 0) {
  50. /* We can record it directly like this:
  51. * jal_mcount = *(unsigned int *)ip;
  52. * Herein, jump over the first two nop instructions */
  53. jal_mcount = jump_insn_encode(JAL, (MCOUNT_ADDR + 8));
  54. }
  55. /* move at, ra
  56. * jalr v1 --> nop
  57. */
  58. new = ftrace_nop;
  59. }
  60. return ftrace_modify_code(ip, new);
  61. }
  62. static int modified; /* initialized as 0 by default */
  63. int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
  64. {
  65. unsigned int new;
  66. unsigned long ip = rec->ip;
  67. /* We just need to remove the "b ftrace_stub" at the fist time! */
  68. if (modified == 0) {
  69. modified = 1;
  70. ftrace_modify_code(addr, ftrace_nop);
  71. }
  72. /* ip, module: 0xc0000000, kernel: 0x80000000 */
  73. new = (ip & 0x40000000) ? lui_v1 : jal_mcount;
  74. return ftrace_modify_code(ip, new);
  75. }
  76. #define FTRACE_CALL_IP ((unsigned long)(&ftrace_call))
  77. int ftrace_update_ftrace_func(ftrace_func_t func)
  78. {
  79. unsigned int new;
  80. new = jump_insn_encode(JAL, (unsigned long)func);
  81. return ftrace_modify_code(FTRACE_CALL_IP, new);
  82. }
  83. int __init ftrace_dyn_arch_init(void *data)
  84. {
  85. /* The return code is retured via data */
  86. *(unsigned long *)data = 0;
  87. return 0;
  88. }
  89. #endif /* CONFIG_DYNAMIC_FTRACE */