ftrace.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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. #include <asm/ftrace.h>
  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. static unsigned int notrace ftrace_calc_offset(long ip, long addr)
  25. {
  26. return (int)(addr - ip);
  27. }
  28. notrace unsigned char *ftrace_nop_replace(void)
  29. {
  30. return (char *)&ftrace_nop;
  31. }
  32. notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
  33. {
  34. static unsigned int op;
  35. /*
  36. * It would be nice to just use create_function_call, but that will
  37. * update the code itself. Here we need to just return the
  38. * instruction that is going to be modified, without modifying the
  39. * code.
  40. */
  41. addr = GET_ADDR(addr);
  42. /* Set to "bl addr" */
  43. op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffc);
  44. /*
  45. * No locking needed, this must be called via kstop_machine
  46. * which in essence is like running on a uniprocessor machine.
  47. */
  48. return (unsigned char *)&op;
  49. }
  50. #ifdef CONFIG_PPC64
  51. # define _ASM_ALIGN " .align 3 "
  52. # define _ASM_PTR " .llong "
  53. #else
  54. # define _ASM_ALIGN " .align 2 "
  55. # define _ASM_PTR " .long "
  56. #endif
  57. notrace int
  58. ftrace_modify_code(unsigned long ip, unsigned char *old_code,
  59. unsigned char *new_code)
  60. {
  61. unsigned replaced;
  62. unsigned old = *(unsigned *)old_code;
  63. unsigned new = *(unsigned *)new_code;
  64. int faulted = 0;
  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: lwz %1, 0(%2)\n"
  75. " cmpw %1, %5\n"
  76. " bne 2f\n"
  77. " stwu %3, 0(%2)\n"
  78. "2:\n"
  79. ".section .fixup, \"ax\"\n"
  80. "3: li %0, 1\n"
  81. " b 2b\n"
  82. ".previous\n"
  83. ".section __ex_table,\"a\"\n"
  84. _ASM_ALIGN "\n"
  85. _ASM_PTR "1b, 3b\n"
  86. ".previous"
  87. : "=r"(faulted), "=r"(replaced)
  88. : "r"(ip), "r"(new),
  89. "0"(faulted), "r"(old)
  90. : "memory");
  91. if (replaced != old && replaced != new)
  92. faulted = 2;
  93. if (!faulted)
  94. flush_icache_range(ip, ip + 8);
  95. return faulted;
  96. }
  97. notrace int ftrace_update_ftrace_func(ftrace_func_t func)
  98. {
  99. unsigned long ip = (unsigned long)(&ftrace_call);
  100. unsigned char old[MCOUNT_INSN_SIZE], *new;
  101. int ret;
  102. memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
  103. new = ftrace_call_replace(ip, (unsigned long)func);
  104. ret = ftrace_modify_code(ip, old, new);
  105. return ret;
  106. }
  107. notrace int ftrace_mcount_set(unsigned long *data)
  108. {
  109. unsigned long ip = (long)(&mcount_call);
  110. unsigned long *addr = data;
  111. unsigned char old[MCOUNT_INSN_SIZE], *new;
  112. /*
  113. * Replace the mcount stub with a pointer to the
  114. * ip recorder function.
  115. */
  116. memcpy(old, &mcount_call, MCOUNT_INSN_SIZE);
  117. new = ftrace_call_replace(ip, *addr);
  118. *addr = ftrace_modify_code(ip, old, new);
  119. return 0;
  120. }
  121. int __init ftrace_dyn_arch_init(void *data)
  122. {
  123. /* This is running in kstop_machine */
  124. ftrace_mcount_set(data);
  125. return 0;
  126. }