ftrace.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. * Code for replacing ftrace calls with jumps.
  3. *
  4. * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
  5. *
  6. * Thanks goes to Ingo Molnar, for suggesting the idea.
  7. * Mathieu Desnoyers, for suggesting postponing the modifications.
  8. * Arjan van de Ven, for keeping me straight, and explaining to me
  9. * the dangers of modifying code on the run.
  10. */
  11. #include <linux/spinlock.h>
  12. #include <linux/hardirq.h>
  13. #include <linux/ftrace.h>
  14. #include <linux/percpu.h>
  15. #include <linux/init.h>
  16. #include <linux/list.h>
  17. #include <asm/alternative.h>
  18. #define CALL_BACK 5
  19. /* Long is fine, even if it is only 4 bytes ;-) */
  20. static long *ftrace_nop;
  21. union ftrace_code_union {
  22. char code[5];
  23. struct {
  24. char e8;
  25. int offset;
  26. } __attribute__((packed));
  27. };
  28. notrace int ftrace_ip_converted(unsigned long ip)
  29. {
  30. unsigned long save;
  31. ip -= CALL_BACK;
  32. save = *(long *)ip;
  33. return save == *ftrace_nop;
  34. }
  35. static int notrace ftrace_calc_offset(long ip, long addr)
  36. {
  37. return (int)(addr - ip);
  38. }
  39. notrace unsigned char *ftrace_nop_replace(void)
  40. {
  41. return (char *)ftrace_nop;
  42. }
  43. notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
  44. {
  45. static union ftrace_code_union calc;
  46. calc.e8 = 0xe8;
  47. calc.offset = ftrace_calc_offset(ip, addr);
  48. /*
  49. * No locking needed, this must be called via kstop_machine
  50. * which in essence is like running on a uniprocessor machine.
  51. */
  52. return calc.code;
  53. }
  54. notrace int
  55. ftrace_modify_code(unsigned long ip, unsigned char *old_code,
  56. unsigned char *new_code)
  57. {
  58. unsigned replaced;
  59. unsigned old = *(unsigned *)old_code; /* 4 bytes */
  60. unsigned new = *(unsigned *)new_code; /* 4 bytes */
  61. unsigned char newch = new_code[4];
  62. int faulted = 0;
  63. /* move the IP back to the start of the call */
  64. ip -= CALL_BACK;
  65. /*
  66. * Note: Due to modules and __init, code can
  67. * disappear and change, we need to protect against faulting
  68. * as well as code changing.
  69. *
  70. * No real locking needed, this code is run through
  71. * kstop_machine.
  72. */
  73. asm volatile (
  74. "1: lock\n"
  75. " cmpxchg %3, (%2)\n"
  76. " jnz 2f\n"
  77. " movb %b4, 4(%2)\n"
  78. "2:\n"
  79. ".section .fixup, \"ax\"\n"
  80. "3: movl $1, %0\n"
  81. " jmp 2b\n"
  82. ".previous\n"
  83. _ASM_EXTABLE(1b, 3b)
  84. : "=r"(faulted), "=a"(replaced)
  85. : "r"(ip), "r"(new), "r"(newch),
  86. "0"(faulted), "a"(old)
  87. : "memory");
  88. sync_core();
  89. if (replaced != old && replaced != new)
  90. faulted = 2;
  91. return faulted;
  92. }
  93. notrace int ftrace_update_ftrace_func(ftrace_func_t func)
  94. {
  95. unsigned long ip = (unsigned long)(&ftrace_call);
  96. unsigned char old[5], *new;
  97. int ret;
  98. ip += CALL_BACK;
  99. memcpy(old, &ftrace_call, 5);
  100. new = ftrace_call_replace(ip, (unsigned long)func);
  101. ret = ftrace_modify_code(ip, old, new);
  102. return ret;
  103. }
  104. notrace int ftrace_mcount_set(unsigned long *data)
  105. {
  106. unsigned long ip = (long)(&mcount_call);
  107. unsigned long *addr = data;
  108. unsigned char old[5], *new;
  109. /* ip is at the location, but modify code will subtact this */
  110. ip += CALL_BACK;
  111. /*
  112. * Replace the mcount stub with a pointer to the
  113. * ip recorder function.
  114. */
  115. memcpy(old, &mcount_call, 5);
  116. new = ftrace_call_replace(ip, *addr);
  117. *addr = ftrace_modify_code(ip, old, new);
  118. return 0;
  119. }
  120. int __init ftrace_dyn_arch_init(void *data)
  121. {
  122. const unsigned char *const *noptable = find_nop_table();
  123. /* This is running in kstop_machine */
  124. ftrace_mcount_set(data);
  125. ftrace_nop = (unsigned long *)noptable[CALL_BACK];
  126. return 0;
  127. }