backtrace.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /**
  2. * @file backtrace.c
  3. *
  4. * @remark Copyright 2008 Tensilica Inc.
  5. * @remark Read the file COPYING
  6. *
  7. */
  8. #include <linux/oprofile.h>
  9. #include <linux/sched.h>
  10. #include <linux/mm.h>
  11. #include <asm/ptrace.h>
  12. #include <asm/uaccess.h>
  13. #include <asm/traps.h>
  14. /* Address of common_exception_return, used to check the
  15. * transition from kernel to user space.
  16. */
  17. extern int common_exception_return;
  18. /* A struct that maps to the part of the frame containing the a0 and
  19. * a1 registers.
  20. */
  21. struct frame_start {
  22. unsigned long a0;
  23. unsigned long a1;
  24. };
  25. static void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth)
  26. {
  27. unsigned long windowstart = regs->windowstart;
  28. unsigned long windowbase = regs->windowbase;
  29. unsigned long a0 = regs->areg[0];
  30. unsigned long a1 = regs->areg[1];
  31. unsigned long pc = MAKE_PC_FROM_RA(a0, regs->pc);
  32. int index;
  33. /* First add the current PC to the trace. */
  34. if (pc != 0 && pc <= TASK_SIZE)
  35. oprofile_add_trace(pc);
  36. else
  37. return;
  38. /* Two steps:
  39. *
  40. * 1. Look through the register window for the
  41. * previous PCs in the call trace.
  42. *
  43. * 2. Look on the stack.
  44. */
  45. /* Step 1. */
  46. /* Rotate WINDOWSTART to move the bit corresponding to
  47. * the current window to the bit #0.
  48. */
  49. windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
  50. /* Look for bits that are set, they correspond to
  51. * valid windows.
  52. */
  53. for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
  54. if (windowstart & (1 << index)) {
  55. /* Read a0 and a1 from the
  56. * corresponding position in AREGs.
  57. */
  58. a0 = regs->areg[index * 4];
  59. a1 = regs->areg[index * 4 + 1];
  60. /* Get the PC from a0 and a1. */
  61. pc = MAKE_PC_FROM_RA(a0, pc);
  62. /* Add the PC to the trace. */
  63. if (pc != 0 && pc <= TASK_SIZE)
  64. oprofile_add_trace(pc);
  65. else
  66. return;
  67. }
  68. /* Step 2. */
  69. /* We are done with the register window, we need to
  70. * look through the stack.
  71. */
  72. if (depth > 0) {
  73. /* Start from the a1 register. */
  74. /* a1 = regs->areg[1]; */
  75. while (a0 != 0 && depth--) {
  76. struct frame_start frame_start;
  77. /* Get the location for a1, a0 for the
  78. * previous frame from the current a1.
  79. */
  80. unsigned long *psp = (unsigned long *)a1;
  81. psp -= 4;
  82. /* Check if the region is OK to access. */
  83. if (!access_ok(VERIFY_READ, psp, sizeof(frame_start)))
  84. return;
  85. /* Copy a1, a0 from user space stack frame. */
  86. if (__copy_from_user_inatomic(&frame_start, psp,
  87. sizeof(frame_start)))
  88. return;
  89. a0 = frame_start.a0;
  90. a1 = frame_start.a1;
  91. pc = MAKE_PC_FROM_RA(a0, pc);
  92. if (pc != 0 && pc <= TASK_SIZE)
  93. oprofile_add_trace(pc);
  94. else
  95. return;
  96. }
  97. }
  98. }
  99. static void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth)
  100. {
  101. unsigned long pc = regs->pc;
  102. unsigned long *psp;
  103. unsigned long sp_start, sp_end;
  104. unsigned long a0 = regs->areg[0];
  105. unsigned long a1 = regs->areg[1];
  106. sp_start = a1 & ~(THREAD_SIZE-1);
  107. sp_end = sp_start + THREAD_SIZE;
  108. /* Spill the register window to the stack first. */
  109. spill_registers();
  110. /* Read the stack frames one by one and create the PC
  111. * from the a0 and a1 registers saved there.
  112. */
  113. while (a1 > sp_start && a1 < sp_end && depth--) {
  114. pc = MAKE_PC_FROM_RA(a0, pc);
  115. /* Add the PC to the trace. */
  116. if (kernel_text_address(pc))
  117. oprofile_add_trace(pc);
  118. if (pc == (unsigned long) &common_exception_return) {
  119. regs = (struct pt_regs *)a1;
  120. if (user_mode(regs)) {
  121. pc = regs->pc;
  122. if (pc != 0 && pc <= TASK_SIZE)
  123. oprofile_add_trace(pc);
  124. else
  125. return;
  126. return xtensa_backtrace_user(regs, depth);
  127. }
  128. a0 = regs->areg[0];
  129. a1 = regs->areg[1];
  130. continue;
  131. }
  132. psp = (unsigned long *)a1;
  133. a0 = *(psp - 4);
  134. a1 = *(psp - 3);
  135. if (a1 <= (unsigned long)psp)
  136. return;
  137. }
  138. return;
  139. }
  140. void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth)
  141. {
  142. if (user_mode(regs))
  143. xtensa_backtrace_user(regs, depth);
  144. else
  145. xtensa_backtrace_kernel(regs, depth);
  146. }