ftrace.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. * Code for replacing ftrace calls with jumps.
  3. *
  4. * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
  5. *
  6. * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
  7. *
  8. */
  9. #include <linux/spinlock.h>
  10. #include <linux/hardirq.h>
  11. #include <linux/ftrace.h>
  12. #include <linux/percpu.h>
  13. #include <linux/init.h>
  14. #include <linux/list.h>
  15. #include <asm/cacheflush.h>
  16. #define CALL_BACK 4
  17. static unsigned int ftrace_nop = 0x60000000;
  18. #ifdef CONFIG_PPC32
  19. # define GET_ADDR(addr) addr
  20. #else
  21. /* PowerPC64's functions are data that points to the functions */
  22. # define GET_ADDR(addr) *(unsigned long *)addr
  23. #endif
  24. notrace int ftrace_ip_converted(unsigned long ip)
  25. {
  26. unsigned int save;
  27. ip -= CALL_BACK;
  28. save = *(unsigned int *)ip;
  29. return save == ftrace_nop;
  30. }
  31. static unsigned int notrace ftrace_calc_offset(long ip, long addr)
  32. {
  33. return (int)((addr + CALL_BACK) - ip);
  34. }
  35. notrace unsigned char *ftrace_nop_replace(void)
  36. {
  37. return (char *)&ftrace_nop;
  38. }
  39. notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
  40. {
  41. static unsigned int op;
  42. addr = GET_ADDR(addr);
  43. /* Set to "bl addr" */
  44. op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffe);
  45. /*
  46. * No locking needed, this must be called via kstop_machine
  47. * which in essence is like running on a uniprocessor machine.
  48. */
  49. return (unsigned char *)&op;
  50. }
  51. #ifdef CONFIG_PPC64
  52. # define _ASM_ALIGN " .align 3 "
  53. # define _ASM_PTR " .llong "
  54. #else
  55. # define _ASM_ALIGN " .align 2 "
  56. # define _ASM_PTR " .long "
  57. #endif
  58. notrace int
  59. ftrace_modify_code(unsigned long ip, unsigned char *old_code,
  60. unsigned char *new_code)
  61. {
  62. unsigned replaced;
  63. unsigned old = *(unsigned *)old_code;
  64. unsigned new = *(unsigned *)new_code;
  65. int faulted = 0;
  66. /* move the IP back to the start of the call */
  67. ip -= CALL_BACK;
  68. /*
  69. * Note: Due to modules and __init, code can
  70. * disappear and change, we need to protect against faulting
  71. * as well as code changing.
  72. *
  73. * No real locking needed, this code is run through
  74. * kstop_machine.
  75. */
  76. asm volatile (
  77. "1: lwz %1, 0(%2)\n"
  78. " cmpw %1, %5\n"
  79. " bne 2f\n"
  80. " stwu %3, 0(%2)\n"
  81. "2:\n"
  82. ".section .fixup, \"ax\"\n"
  83. "3: li %0, 1\n"
  84. " b 2b\n"
  85. ".previous\n"
  86. ".section __ex_table,\"a\"\n"
  87. _ASM_ALIGN "\n"
  88. _ASM_PTR "1b, 3b\n"
  89. ".previous"
  90. : "=r"(faulted), "=r"(replaced)
  91. : "r"(ip), "r"(new),
  92. "0"(faulted), "r"(old)
  93. : "memory");
  94. if (replaced != old && replaced != new)
  95. faulted = 2;
  96. if (!faulted)
  97. flush_icache_range(ip, ip + 8);
  98. return faulted;
  99. }
  100. notrace int ftrace_update_ftrace_func(ftrace_func_t func)
  101. {
  102. unsigned long ip = (unsigned long)(&ftrace_call);
  103. unsigned char old[4], *new;
  104. int ret;
  105. ip += CALL_BACK;
  106. memcpy(old, &ftrace_call, 4);
  107. new = ftrace_call_replace(ip, (unsigned long)func);
  108. ret = ftrace_modify_code(ip, old, new);
  109. return ret;
  110. }
  111. notrace int ftrace_mcount_set(unsigned long *data)
  112. {
  113. unsigned long ip = (long)(&mcount_call);
  114. unsigned long *addr = data;
  115. unsigned char old[4], *new;
  116. /* ip is at the location, but modify code will subtact this */
  117. ip += CALL_BACK;
  118. /*
  119. * Replace the mcount stub with a pointer to the
  120. * ip recorder function.
  121. */
  122. memcpy(old, &mcount_call, 4);
  123. new = ftrace_call_replace(ip, *addr);
  124. *addr = ftrace_modify_code(ip, old, new);
  125. return 0;
  126. }
  127. int __init ftrace_dyn_arch_init(void *data)
  128. {
  129. /* This is running in kstop_machine */
  130. ftrace_mcount_set(data);
  131. return 0;
  132. }