ftrace.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. * Dynamic function tracing support.
  3. *
  4. * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com>
  5. * Copyright (C) 2010 Rabin Vincent <rabin@rab.in>
  6. *
  7. * For licencing details, see COPYING.
  8. *
  9. * Defines low-level handling of mcount calls when the kernel
  10. * is compiled with the -pg flag. When using dynamic ftrace, the
  11. * mcount call-sites get patched with NOP till they are enabled.
  12. * All code mutation routines here are called under stop_machine().
  13. */
  14. #include <linux/ftrace.h>
  15. #include <linux/uaccess.h>
  16. #include <asm/cacheflush.h>
  17. #include <asm/ftrace.h>
  18. #define NOP 0xe8bd4000 /* pop {lr} */
  19. #ifdef CONFIG_OLD_MCOUNT
  20. #define OLD_MCOUNT_ADDR ((unsigned long) mcount)
  21. #define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old)
  22. #define OLD_NOP 0xe1a00000 /* mov r0, r0 */
  23. static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
  24. {
  25. return rec->arch.old_mcount ? OLD_NOP : NOP;
  26. }
  27. static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
  28. {
  29. if (!rec->arch.old_mcount)
  30. return addr;
  31. if (addr == MCOUNT_ADDR)
  32. addr = OLD_MCOUNT_ADDR;
  33. else if (addr == FTRACE_ADDR)
  34. addr = OLD_FTRACE_ADDR;
  35. return addr;
  36. }
  37. #else
  38. static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
  39. {
  40. return NOP;
  41. }
  42. static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
  43. {
  44. return addr;
  45. }
  46. #endif
  47. /* construct a branch (BL) instruction to addr */
  48. static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
  49. {
  50. long offset;
  51. offset = (long)addr - (long)(pc + 8);
  52. if (unlikely(offset < -33554432 || offset > 33554428)) {
  53. /* Can't generate branches that far (from ARM ARM). Ftrace
  54. * doesn't generate branches outside of kernel text.
  55. */
  56. WARN_ON_ONCE(1);
  57. return 0;
  58. }
  59. offset = (offset >> 2) & 0x00ffffff;
  60. return 0xeb000000 | offset;
  61. }
  62. static int ftrace_modify_code(unsigned long pc, unsigned long old,
  63. unsigned long new)
  64. {
  65. unsigned long replaced;
  66. if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE))
  67. return -EFAULT;
  68. if (replaced != old)
  69. return -EINVAL;
  70. if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE))
  71. return -EPERM;
  72. flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
  73. return 0;
  74. }
  75. int ftrace_update_ftrace_func(ftrace_func_t func)
  76. {
  77. unsigned long pc, old;
  78. unsigned long new;
  79. int ret;
  80. pc = (unsigned long)&ftrace_call;
  81. memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE);
  82. new = ftrace_call_replace(pc, (unsigned long)func);
  83. ret = ftrace_modify_code(pc, old, new);
  84. #ifdef CONFIG_OLD_MCOUNT
  85. if (!ret) {
  86. pc = (unsigned long)&ftrace_call_old;
  87. memcpy(&old, &ftrace_call_old, MCOUNT_INSN_SIZE);
  88. new = ftrace_call_replace(pc, (unsigned long)func);
  89. ret = ftrace_modify_code(pc, old, new);
  90. }
  91. #endif
  92. return ret;
  93. }
  94. int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
  95. {
  96. unsigned long new, old;
  97. unsigned long ip = rec->ip;
  98. old = ftrace_nop_replace(rec);
  99. new = ftrace_call_replace(ip, adjust_address(rec, addr));
  100. return ftrace_modify_code(rec->ip, old, new);
  101. }
  102. int ftrace_make_nop(struct module *mod,
  103. struct dyn_ftrace *rec, unsigned long addr)
  104. {
  105. unsigned long ip = rec->ip;
  106. unsigned long old;
  107. unsigned long new;
  108. int ret;
  109. old = ftrace_call_replace(ip, adjust_address(rec, addr));
  110. new = ftrace_nop_replace(rec);
  111. ret = ftrace_modify_code(ip, old, new);
  112. #ifdef CONFIG_OLD_MCOUNT
  113. if (ret == -EINVAL && addr == MCOUNT_ADDR) {
  114. rec->arch.old_mcount = true;
  115. old = ftrace_call_replace(ip, adjust_address(rec, addr));
  116. new = ftrace_nop_replace(rec);
  117. ret = ftrace_modify_code(ip, old, new);
  118. }
  119. #endif
  120. return ret;
  121. }
  122. int __init ftrace_dyn_arch_init(void *data)
  123. {
  124. *(unsigned long *)data = 0;
  125. return 0;
  126. }