1
2
3
4
5
6
7
8
9
10
11#include <linux/export.h>
12#include <linux/sched.h>
13#include <linux/stacktrace.h>
14
15#include <asm/stacktrace.h>
16#include <asm/traps.h>
17#include <linux/uaccess.h>
18
19#if IS_ENABLED(CONFIG_PERF_EVENTS)
20
21
22
23
24extern int common_exception_return;
25
26void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
27 int (*ufn)(struct stackframe *frame, void *data),
28 void *data)
29{
30 unsigned long windowstart = regs->windowstart;
31 unsigned long windowbase = regs->windowbase;
32 unsigned long a0 = regs->areg[0];
33 unsigned long a1 = regs->areg[1];
34 unsigned long pc = regs->pc;
35 struct stackframe frame;
36 int index;
37
38 if (!depth--)
39 return;
40
41 frame.pc = pc;
42 frame.sp = a1;
43
44 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
45 return;
46
47 if (IS_ENABLED(CONFIG_USER_ABI_CALL0_ONLY) ||
48 (IS_ENABLED(CONFIG_USER_ABI_CALL0_PROBE) &&
49 !(regs->ps & PS_WOE_MASK)))
50 return;
51
52
53
54
55
56
57
58
59
60
61
62
63
64 windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
65
66
67
68
69 for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
70 if (windowstart & (1 << index)) {
71
72 pc = MAKE_PC_FROM_RA(a0, pc);
73
74
75
76 a0 = regs->areg[index * 4];
77 a1 = regs->areg[index * 4 + 1];
78
79 frame.pc = pc;
80 frame.sp = a1;
81
82 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
83 return;
84 }
85
86
87
88
89
90 if (!depth)
91 return;
92
93
94
95 while (a0 != 0 && depth--) {
96 pc = MAKE_PC_FROM_RA(a0, pc);
97
98
99 if (!access_ok(&SPILL_SLOT(a1, 0), 8))
100 return;
101
102 if (__get_user(a0, &SPILL_SLOT(a1, 0)) ||
103 __get_user(a1, &SPILL_SLOT(a1, 1)))
104 return;
105
106 frame.pc = pc;
107 frame.sp = a1;
108
109 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
110 return;
111 }
112}
113EXPORT_SYMBOL(xtensa_backtrace_user);
114
115void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth,
116 int (*kfn)(struct stackframe *frame, void *data),
117 int (*ufn)(struct stackframe *frame, void *data),
118 void *data)
119{
120 unsigned long pc = regs->depc > VALID_DOUBLE_EXCEPTION_ADDRESS ?
121 regs->depc : regs->pc;
122 unsigned long sp_start, sp_end;
123 unsigned long a0 = regs->areg[0];
124 unsigned long a1 = regs->areg[1];
125
126 sp_start = a1 & ~(THREAD_SIZE - 1);
127 sp_end = sp_start + THREAD_SIZE;
128
129
130 spill_registers();
131
132
133
134
135 while (a1 > sp_start && a1 < sp_end && depth--) {
136 struct stackframe frame;
137
138 frame.pc = pc;
139 frame.sp = a1;
140
141 if (kernel_text_address(pc) && kfn(&frame, data))
142 return;
143
144 if (pc == (unsigned long)&common_exception_return) {
145 regs = (struct pt_regs *)a1;
146 if (user_mode(regs)) {
147 if (ufn == NULL)
148 return;
149 xtensa_backtrace_user(regs, depth, ufn, data);
150 return;
151 }
152 a0 = regs->areg[0];
153 a1 = regs->areg[1];
154 continue;
155 }
156
157 sp_start = a1;
158
159 pc = MAKE_PC_FROM_RA(a0, pc);
160 a0 = SPILL_SLOT(a1, 0);
161 a1 = SPILL_SLOT(a1, 1);
162 }
163}
164EXPORT_SYMBOL(xtensa_backtrace_kernel);
165
166#endif
167
168void walk_stackframe(unsigned long *sp,
169 int (*fn)(struct stackframe *frame, void *data),
170 void *data)
171{
172 unsigned long a0, a1;
173 unsigned long sp_end;
174
175 a1 = (unsigned long)sp;
176 sp_end = ALIGN(a1, THREAD_SIZE);
177
178 spill_registers();
179
180 while (a1 < sp_end) {
181 struct stackframe frame;
182
183 sp = (unsigned long *)a1;
184
185 a0 = SPILL_SLOT(a1, 0);
186 a1 = SPILL_SLOT(a1, 1);
187
188 if (a1 <= (unsigned long)sp)
189 break;
190
191 frame.pc = MAKE_PC_FROM_RA(a0, a1);
192 frame.sp = a1;
193
194 if (fn(&frame, data))
195 return;
196 }
197}
198
199#ifdef CONFIG_STACKTRACE
200
201struct stack_trace_data {
202 struct stack_trace *trace;
203 unsigned skip;
204};
205
206static int stack_trace_cb(struct stackframe *frame, void *data)
207{
208 struct stack_trace_data *trace_data = data;
209 struct stack_trace *trace = trace_data->trace;
210
211 if (trace_data->skip) {
212 --trace_data->skip;
213 return 0;
214 }
215 if (!kernel_text_address(frame->pc))
216 return 0;
217
218 trace->entries[trace->nr_entries++] = frame->pc;
219 return trace->nr_entries >= trace->max_entries;
220}
221
222void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
223{
224 struct stack_trace_data trace_data = {
225 .trace = trace,
226 .skip = trace->skip,
227 };
228 walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data);
229}
230EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
231
232void save_stack_trace(struct stack_trace *trace)
233{
234 save_stack_trace_tsk(current, trace);
235}
236EXPORT_SYMBOL_GPL(save_stack_trace);
237
238#endif
239
240#ifdef CONFIG_FRAME_POINTER
241
242struct return_addr_data {
243 unsigned long addr;
244 unsigned skip;
245};
246
247static int return_address_cb(struct stackframe *frame, void *data)
248{
249 struct return_addr_data *r = data;
250
251 if (r->skip) {
252 --r->skip;
253 return 0;
254 }
255 if (!kernel_text_address(frame->pc))
256 return 0;
257 r->addr = frame->pc;
258 return 1;
259}
260
261
262
263
264
265unsigned long return_address(unsigned level)
266{
267 struct return_addr_data r = {
268 .skip = level,
269 };
270 walk_stackframe(stack_pointer(NULL), return_address_cb, &r);
271 return r.addr;
272}
273EXPORT_SYMBOL(return_address);
274
275#endif
276