Commit | Line | Data |
---|---|---|
3e4196a5 | 1 | /* |
5fdf377d | 2 | * Kernel and userspace stack tracing. |
3e4196a5 MF |
3 | * |
4 | * This file is subject to the terms and conditions of the GNU General Public | |
5 | * License. See the file "COPYING" in the main directory of this archive | |
6 | * for more details. | |
7 | * | |
8 | * Copyright (C) 2001 - 2013 Tensilica Inc. | |
5fdf377d | 9 | * Copyright (C) 2015 Cadence Design Systems Inc. |
3e4196a5 MF |
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> | |
5fdf377d MF |
17 | #include <asm/uaccess.h> |
18 | ||
19 | #if IS_ENABLED(CONFIG_OPROFILE) || IS_ENABLED(CONFIG_PERF_EVENTS) | |
20 | ||
21 | /* Address of common_exception_return, used to check the | |
22 | * transition from kernel to user space. | |
23 | */ | |
24 | extern int common_exception_return; | |
25 | ||
26 | /* A struct that maps to the part of the frame containing the a0 and | |
27 | * a1 registers. | |
28 | */ | |
29 | struct frame_start { | |
30 | unsigned long a0; | |
31 | unsigned long a1; | |
32 | }; | |
33 | ||
34 | void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth, | |
35 | int (*ufn)(struct stackframe *frame, void *data), | |
36 | void *data) | |
37 | { | |
38 | unsigned long windowstart = regs->windowstart; | |
39 | unsigned long windowbase = regs->windowbase; | |
40 | unsigned long a0 = regs->areg[0]; | |
41 | unsigned long a1 = regs->areg[1]; | |
42 | unsigned long pc = regs->pc; | |
43 | struct stackframe frame; | |
44 | int index; | |
45 | ||
46 | if (!depth--) | |
47 | return; | |
48 | ||
49 | frame.pc = pc; | |
50 | frame.sp = a1; | |
51 | ||
52 | if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data)) | |
53 | return; | |
54 | ||
55 | /* Two steps: | |
56 | * | |
57 | * 1. Look through the register window for the | |
58 | * previous PCs in the call trace. | |
59 | * | |
60 | * 2. Look on the stack. | |
61 | */ | |
62 | ||
63 | /* Step 1. */ | |
64 | /* Rotate WINDOWSTART to move the bit corresponding to | |
65 | * the current window to the bit #0. | |
66 | */ | |
67 | windowstart = (windowstart << WSBITS | windowstart) >> windowbase; | |
68 | ||
69 | /* Look for bits that are set, they correspond to | |
70 | * valid windows. | |
71 | */ | |
72 | for (index = WSBITS - 1; (index > 0) && depth; depth--, index--) | |
73 | if (windowstart & (1 << index)) { | |
74 | /* Get the PC from a0 and a1. */ | |
75 | pc = MAKE_PC_FROM_RA(a0, pc); | |
76 | /* Read a0 and a1 from the | |
77 | * corresponding position in AREGs. | |
78 | */ | |
79 | a0 = regs->areg[index * 4]; | |
80 | a1 = regs->areg[index * 4 + 1]; | |
81 | ||
82 | frame.pc = pc; | |
83 | frame.sp = a1; | |
84 | ||
85 | if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data)) | |
86 | return; | |
87 | } | |
88 | ||
89 | /* Step 2. */ | |
90 | /* We are done with the register window, we need to | |
91 | * look through the stack. | |
92 | */ | |
93 | if (!depth) | |
94 | return; | |
95 | ||
96 | /* Start from the a1 register. */ | |
97 | /* a1 = regs->areg[1]; */ | |
98 | while (a0 != 0 && depth--) { | |
99 | struct frame_start frame_start; | |
100 | /* Get the location for a1, a0 for the | |
101 | * previous frame from the current a1. | |
102 | */ | |
103 | unsigned long *psp = (unsigned long *)a1; | |
104 | ||
105 | psp -= 4; | |
106 | ||
107 | /* Check if the region is OK to access. */ | |
108 | if (!access_ok(VERIFY_READ, psp, sizeof(frame_start))) | |
109 | return; | |
110 | /* Copy a1, a0 from user space stack frame. */ | |
111 | if (__copy_from_user_inatomic(&frame_start, psp, | |
112 | sizeof(frame_start))) | |
113 | return; | |
114 | ||
115 | pc = MAKE_PC_FROM_RA(a0, pc); | |
116 | a0 = frame_start.a0; | |
117 | a1 = frame_start.a1; | |
118 | ||
119 | frame.pc = pc; | |
120 | frame.sp = a1; | |
121 | ||
122 | if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data)) | |
123 | return; | |
124 | } | |
125 | } | |
126 | EXPORT_SYMBOL(xtensa_backtrace_user); | |
127 | ||
128 | void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth, | |
129 | int (*kfn)(struct stackframe *frame, void *data), | |
130 | int (*ufn)(struct stackframe *frame, void *data), | |
131 | void *data) | |
132 | { | |
133 | unsigned long pc = regs->depc > VALID_DOUBLE_EXCEPTION_ADDRESS ? | |
134 | regs->depc : regs->pc; | |
135 | unsigned long sp_start, sp_end; | |
136 | unsigned long a0 = regs->areg[0]; | |
137 | unsigned long a1 = regs->areg[1]; | |
138 | ||
139 | sp_start = a1 & ~(THREAD_SIZE - 1); | |
140 | sp_end = sp_start + THREAD_SIZE; | |
141 | ||
142 | /* Spill the register window to the stack first. */ | |
143 | spill_registers(); | |
144 | ||
145 | /* Read the stack frames one by one and create the PC | |
146 | * from the a0 and a1 registers saved there. | |
147 | */ | |
148 | while (a1 > sp_start && a1 < sp_end && depth--) { | |
149 | struct stackframe frame; | |
150 | unsigned long *psp = (unsigned long *)a1; | |
151 | ||
152 | frame.pc = pc; | |
153 | frame.sp = a1; | |
154 | ||
155 | if (kernel_text_address(pc) && kfn(&frame, data)) | |
156 | return; | |
157 | ||
158 | if (pc == (unsigned long)&common_exception_return) { | |
159 | regs = (struct pt_regs *)a1; | |
160 | if (user_mode(regs)) { | |
161 | if (ufn == NULL) | |
162 | return; | |
163 | xtensa_backtrace_user(regs, depth, ufn, data); | |
164 | return; | |
165 | } | |
166 | a0 = regs->areg[0]; | |
167 | a1 = regs->areg[1]; | |
168 | continue; | |
169 | } | |
170 | ||
171 | sp_start = a1; | |
172 | ||
173 | pc = MAKE_PC_FROM_RA(a0, pc); | |
174 | a0 = *(psp - 4); | |
175 | a1 = *(psp - 3); | |
176 | } | |
177 | } | |
178 | EXPORT_SYMBOL(xtensa_backtrace_kernel); | |
179 | ||
180 | #endif | |
3e4196a5 MF |
181 | |
182 | void walk_stackframe(unsigned long *sp, | |
183 | int (*fn)(struct stackframe *frame, void *data), | |
184 | void *data) | |
185 | { | |
186 | unsigned long a0, a1; | |
187 | unsigned long sp_end; | |
188 | ||
189 | a1 = (unsigned long)sp; | |
190 | sp_end = ALIGN(a1, THREAD_SIZE); | |
191 | ||
192 | spill_registers(); | |
193 | ||
194 | while (a1 < sp_end) { | |
195 | struct stackframe frame; | |
196 | ||
197 | sp = (unsigned long *)a1; | |
198 | ||
199 | a0 = *(sp - 4); | |
200 | a1 = *(sp - 3); | |
201 | ||
202 | if (a1 <= (unsigned long)sp) | |
203 | break; | |
204 | ||
205 | frame.pc = MAKE_PC_FROM_RA(a0, a1); | |
206 | frame.sp = a1; | |
207 | ||
208 | if (fn(&frame, data)) | |
209 | return; | |
210 | } | |
211 | } | |
212 | ||
213 | #ifdef CONFIG_STACKTRACE | |
214 | ||
215 | struct stack_trace_data { | |
216 | struct stack_trace *trace; | |
217 | unsigned skip; | |
218 | }; | |
219 | ||
220 | static int stack_trace_cb(struct stackframe *frame, void *data) | |
221 | { | |
222 | struct stack_trace_data *trace_data = data; | |
223 | struct stack_trace *trace = trace_data->trace; | |
224 | ||
225 | if (trace_data->skip) { | |
226 | --trace_data->skip; | |
227 | return 0; | |
228 | } | |
229 | if (!kernel_text_address(frame->pc)) | |
230 | return 0; | |
231 | ||
232 | trace->entries[trace->nr_entries++] = frame->pc; | |
233 | return trace->nr_entries >= trace->max_entries; | |
234 | } | |
235 | ||
236 | void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace) | |
237 | { | |
238 | struct stack_trace_data trace_data = { | |
239 | .trace = trace, | |
240 | .skip = trace->skip, | |
241 | }; | |
242 | walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data); | |
243 | } | |
244 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | |
245 | ||
246 | void save_stack_trace(struct stack_trace *trace) | |
247 | { | |
248 | save_stack_trace_tsk(current, trace); | |
249 | } | |
250 | EXPORT_SYMBOL_GPL(save_stack_trace); | |
251 | ||
252 | #endif | |
3ae908c9 MF |
253 | |
254 | #ifdef CONFIG_FRAME_POINTER | |
255 | ||
256 | struct return_addr_data { | |
257 | unsigned long addr; | |
258 | unsigned skip; | |
259 | }; | |
260 | ||
261 | static int return_address_cb(struct stackframe *frame, void *data) | |
262 | { | |
263 | struct return_addr_data *r = data; | |
264 | ||
265 | if (r->skip) { | |
266 | --r->skip; | |
267 | return 0; | |
268 | } | |
269 | if (!kernel_text_address(frame->pc)) | |
270 | return 0; | |
271 | r->addr = frame->pc; | |
272 | return 1; | |
273 | } | |
274 | ||
275 | unsigned long return_address(unsigned level) | |
276 | { | |
277 | struct return_addr_data r = { | |
278 | .skip = level + 1, | |
279 | }; | |
280 | walk_stackframe(stack_pointer(NULL), return_address_cb, &r); | |
281 | return r.addr; | |
282 | } | |
283 | EXPORT_SYMBOL(return_address); | |
284 | ||
285 | #endif |