4 * @remark Copyright 2008 Tensilica Inc.
5 * @remark Read the file COPYING
9 #include <linux/oprofile.h>
10 #include <linux/sched.h>
12 #include <asm/ptrace.h>
13 #include <asm/uaccess.h>
14 #include <asm/traps.h>
16 /* Address of common_exception_return, used to check the
17 * transition from kernel to user space.
19 extern int common_exception_return
;
21 /* A struct that maps to the part of the frame containing the a0 and
29 static void xtensa_backtrace_user(struct pt_regs
*regs
, unsigned int depth
)
31 unsigned long windowstart
= regs
->windowstart
;
32 unsigned long windowbase
= regs
->windowbase
;
33 unsigned long a0
= regs
->areg
[0];
34 unsigned long a1
= regs
->areg
[1];
35 unsigned long pc
= MAKE_PC_FROM_RA(a0
, regs
->pc
);
38 /* First add the current PC to the trace. */
39 if (pc
!= 0 && pc
<= TASK_SIZE
)
40 oprofile_add_trace(pc
);
46 * 1. Look through the register window for the
47 * previous PCs in the call trace.
49 * 2. Look on the stack.
53 /* Rotate WINDOWSTART to move the bit corresponding to
54 * the current window to the bit #0.
56 windowstart
= (windowstart
<< WSBITS
| windowstart
) >> windowbase
;
58 /* Look for bits that are set, they correspond to
61 for (index
= WSBITS
- 1; (index
> 0) && depth
; depth
--, index
--)
62 if (windowstart
& (1 << index
)) {
63 /* Read a0 and a1 from the
64 * corresponding position in AREGs.
66 a0
= regs
->areg
[index
* 4];
67 a1
= regs
->areg
[index
* 4 + 1];
68 /* Get the PC from a0 and a1. */
69 pc
= MAKE_PC_FROM_RA(a0
, pc
);
71 /* Add the PC to the trace. */
72 if (pc
!= 0 && pc
<= TASK_SIZE
)
73 oprofile_add_trace(pc
);
79 /* We are done with the register window, we need to
80 * look through the stack.
83 /* Start from the a1 register. */
84 /* a1 = regs->areg[1]; */
85 while (a0
!= 0 && depth
--) {
87 struct frame_start frame_start
;
88 /* Get the location for a1, a0 for the
89 * previous frame from the current a1.
91 unsigned long *psp
= (unsigned long *)a1
;
94 /* Check if the region is OK to access. */
95 if (!access_ok(VERIFY_READ
, psp
, sizeof(frame_start
)))
97 /* Copy a1, a0 from user space stack frame. */
98 if (__copy_from_user_inatomic(&frame_start
, psp
,
104 pc
= MAKE_PC_FROM_RA(a0
, pc
);
106 if (pc
!= 0 && pc
<= TASK_SIZE
)
107 oprofile_add_trace(pc
);
114 static void xtensa_backtrace_kernel(struct pt_regs
*regs
, unsigned int depth
)
116 unsigned long pc
= regs
->pc
;
118 unsigned long sp_start
, sp_end
;
119 unsigned long a0
= regs
->areg
[0];
120 unsigned long a1
= regs
->areg
[1];
122 sp_start
= a1
& ~(THREAD_SIZE
-1);
123 sp_end
= sp_start
+ THREAD_SIZE
;
125 /* Spill the register window to the stack first. */
128 /* Read the stack frames one by one and create the PC
129 * from the a0 and a1 registers saved there.
131 while (a1
> sp_start
&& a1
< sp_end
&& depth
--) {
132 pc
= MAKE_PC_FROM_RA(a0
, pc
);
134 /* Add the PC to the trace. */
135 oprofile_add_trace(pc
);
136 if (pc
== (unsigned long) &common_exception_return
) {
137 regs
= (struct pt_regs
*)a1
;
138 if (user_mode(regs
)) {
140 if (pc
!= 0 && pc
<= TASK_SIZE
)
141 oprofile_add_trace(pc
);
144 return xtensa_backtrace_user(regs
, depth
);
151 psp
= (unsigned long *)a1
;
156 if (a1
<= (unsigned long)psp
)
163 void xtensa_backtrace(struct pt_regs
* const regs
, unsigned int depth
)
166 xtensa_backtrace_user(regs
, depth
);
168 xtensa_backtrace_kernel(regs
, depth
);