1
2
3
4
5
6
7
8
9#include <linux/oprofile.h>
10#include <linux/sched.h>
11#include <linux/mm.h>
12#include <asm/ptrace.h>
13#include <asm/uaccess.h>
14#include <asm/traps.h>
15
16
17
18
19extern int common_exception_return;
20
21
22
23
24struct frame_start {
25 unsigned long a0;
26 unsigned long a1;
27};
28
29static void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth)
30{
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);
36 int index;
37
38
39 if (pc != 0 && pc <= TASK_SIZE)
40 oprofile_add_trace(pc);
41 else
42 return;
43
44
45
46
47
48
49
50
51
52
53
54
55
56 windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
57
58
59
60
61 for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
62 if (windowstart & (1 << index)) {
63
64
65
66 a0 = regs->areg[index * 4];
67 a1 = regs->areg[index * 4 + 1];
68
69 pc = MAKE_PC_FROM_RA(a0, pc);
70
71
72 if (pc != 0 && pc <= TASK_SIZE)
73 oprofile_add_trace(pc);
74 else
75 return;
76 }
77
78
79
80
81
82 if (depth > 0) {
83
84
85 while (a0 != 0 && depth--) {
86
87 struct frame_start frame_start;
88
89
90
91 unsigned long *psp = (unsigned long *)a1;
92 psp -= 4;
93
94
95 if (!access_ok(VERIFY_READ, psp, sizeof(frame_start)))
96 return;
97
98 if (__copy_from_user_inatomic(&frame_start, psp,
99 sizeof(frame_start)))
100 return;
101
102 a0 = frame_start.a0;
103 a1 = frame_start.a1;
104 pc = MAKE_PC_FROM_RA(a0, pc);
105
106 if (pc != 0 && pc <= TASK_SIZE)
107 oprofile_add_trace(pc);
108 else
109 return;
110 }
111 }
112}
113
114static void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth)
115{
116 unsigned long pc = regs->pc;
117 unsigned long *psp;
118 unsigned long sp_start, sp_end;
119 unsigned long a0 = regs->areg[0];
120 unsigned long a1 = regs->areg[1];
121
122 sp_start = a1 & ~(THREAD_SIZE-1);
123 sp_end = sp_start + THREAD_SIZE;
124
125
126 spill_registers();
127
128
129
130
131 while (a1 > sp_start && a1 < sp_end && depth--) {
132 pc = MAKE_PC_FROM_RA(a0, pc);
133
134
135 if (kernel_text_address(pc))
136 oprofile_add_trace(pc);
137
138 if (pc == (unsigned long) &common_exception_return) {
139 regs = (struct pt_regs *)a1;
140 if (user_mode(regs)) {
141 pc = regs->pc;
142 if (pc != 0 && pc <= TASK_SIZE)
143 oprofile_add_trace(pc);
144 else
145 return;
146 return xtensa_backtrace_user(regs, depth);
147 }
148 a0 = regs->areg[0];
149 a1 = regs->areg[1];
150 continue;
151 }
152
153 psp = (unsigned long *)a1;
154
155 a0 = *(psp - 4);
156 a1 = *(psp - 3);
157
158 if (a1 <= (unsigned long)psp)
159 return;
160
161 }
162 return;
163}
164
165void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth)
166{
167 if (user_mode(regs))
168 xtensa_backtrace_user(regs, depth);
169 else
170 xtensa_backtrace_kernel(regs, depth);
171}
172