1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <linux/kallsyms.h>
17#include <linux/kernel.h>
18#include <linux/sched.h>
19#include <linux/stacktrace.h>
20#include <linux/types.h>
21#include <linux/errno.h>
22#include <linux/module.h>
23#include <linux/io.h>
24#include <asm/sections.h>
25#include <asm/exceptions.h>
26#include <asm/unwind.h>
27
28struct stack_trace;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61inline long get_frame_size(unsigned long instr)
62{
63 return abs((s16)(instr & 0xFFFF));
64}
65
66
67
68
69
70
71
72
73
74
75static unsigned long *find_frame_creation(unsigned long *pc)
76{
77 int i;
78
79
80
81
82
83
84 for (i = 0; i < 1000; i++, pc--) {
85 unsigned long instr;
86 s16 frame_size;
87
88 if (!kernel_text_address((unsigned long) pc))
89 return NULL;
90
91 instr = *pc;
92
93
94 if ((instr & 0xFFFF0000) != 0x30210000)
95 continue;
96
97 frame_size = get_frame_size(instr);
98 if ((frame_size < 8) || (frame_size & 3)) {
99 pr_debug(" Invalid frame size %d at 0x%p\n",
100 frame_size, pc);
101 return NULL;
102 }
103
104 pr_debug(" Found frame creation at 0x%p, size %d\n", pc,
105 frame_size);
106 return pc;
107 }
108
109 return NULL;
110}
111
112
113
114
115
116
117
118
119
120
121
122
123static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc,
124 unsigned long leaf_return,
125 unsigned long *pprev_fp,
126 unsigned long *pprev_pc)
127{
128 unsigned long *prologue = NULL;
129
130
131 if (pc != (unsigned long) &_switch_to)
132 prologue = find_frame_creation((unsigned long *)pc);
133
134 if (prologue) {
135 long frame_size = get_frame_size(*prologue);
136
137 *pprev_fp = fp + frame_size;
138 *pprev_pc = *(unsigned long *)fp;
139 } else {
140 if (!leaf_return)
141 return -EINVAL;
142 *pprev_pc = leaf_return;
143 *pprev_fp = fp;
144 }
145
146
147
148
149 return (!*pprev_pc || (*pprev_pc & 3)) ? -EINVAL : 0;
150}
151
152static void microblaze_unwind_inner(struct task_struct *task,
153 unsigned long pc, unsigned long fp,
154 unsigned long leaf_return,
155 struct stack_trace *trace);
156
157
158
159
160
161#ifdef CONFIG_MMU
162static inline void unwind_trap(struct task_struct *task, unsigned long pc,
163 unsigned long fp, struct stack_trace *trace)
164{
165
166}
167#else
168static inline void unwind_trap(struct task_struct *task, unsigned long pc,
169 unsigned long fp, struct stack_trace *trace)
170{
171 const struct pt_regs *regs = (const struct pt_regs *) fp;
172 microblaze_unwind_inner(task, regs->pc, regs->r1, regs->r15, trace);
173}
174#endif
175
176
177
178
179
180
181
182
183
184
185
186void microblaze_unwind_inner(struct task_struct *task,
187 unsigned long pc, unsigned long fp,
188 unsigned long leaf_return,
189 struct stack_trace *trace)
190{
191 int ofs = 0;
192
193 pr_debug(" Unwinding with PC=%p, FP=%p\n", (void *)pc, (void *)fp);
194 if (!pc || !fp || (pc & 3) || (fp & 3)) {
195 pr_debug(" Invalid state for unwind, aborting\n");
196 return;
197 }
198 for (; pc != 0;) {
199 unsigned long next_fp, next_pc = 0;
200 unsigned long return_to = pc + 2 * sizeof(unsigned long);
201 const struct trap_handler_info *handler =
202 µblaze_trap_handlers;
203
204
205 if ((return_to >= (unsigned long)&_hw_exception_handler)
206 &&(return_to < (unsigned long)&ex_handler_unhandled)) {
207
208
209
210
211#ifndef CONFIG_MMU
212 const struct pt_regs *regs =
213 (const struct pt_regs *) fp;
214#endif
215 pr_info("HW EXCEPTION\n");
216#ifndef CONFIG_MMU
217 microblaze_unwind_inner(task, regs->r17 - 4,
218 fp + EX_HANDLER_STACK_SIZ,
219 regs->r15, trace);
220#endif
221 return;
222 }
223
224
225 for (; handler->start_addr; ++handler) {
226 if ((return_to >= handler->start_addr)
227 && (return_to <= handler->end_addr)) {
228 if (!trace)
229 pr_info("%s\n", handler->trap_name);
230 unwind_trap(task, pc, fp, trace);
231 return;
232 }
233 }
234 pc -= ofs;
235
236 if (trace) {
237#ifdef CONFIG_STACKTRACE
238 if (trace->skip > 0)
239 trace->skip--;
240 else
241 trace->entries[trace->nr_entries++] = pc;
242
243 if (trace->nr_entries >= trace->max_entries)
244 break;
245#endif
246 } else {
247
248 if (unlikely(pc == task_pt_regs(task)->pc)) {
249 pr_info("[<%p>] PID %lu [%s]\n",
250 (void *) pc,
251 (unsigned long) task->pid,
252 task->comm);
253 break;
254 } else
255 print_ip_sym(pc);
256 }
257
258
259 if (!kernel_text_address(pc))
260 break;
261
262 if (lookup_prev_stack_frame(fp, pc, leaf_return, &next_fp,
263 &next_pc) == 0) {
264 ofs = sizeof(unsigned long);
265 pc = next_pc & ~3;
266 fp = next_fp;
267 leaf_return = 0;
268 } else {
269 pr_debug(" Failed to find previous stack frame\n");
270 break;
271 }
272
273 pr_debug(" Next PC=%p, next FP=%p\n",
274 (void *)next_pc, (void *)next_fp);
275 }
276}
277
278
279
280
281
282
283
284void microblaze_unwind(struct task_struct *task, struct stack_trace *trace)
285{
286 if (task) {
287 if (task == current) {
288 const struct pt_regs *regs = task_pt_regs(task);
289 microblaze_unwind_inner(task, regs->pc, regs->r1,
290 regs->r15, trace);
291 } else {
292 struct thread_info *thread_info =
293 (struct thread_info *)(task->stack);
294 const struct cpu_context *cpu_context =
295 &thread_info->cpu_context;
296
297 microblaze_unwind_inner(task,
298 (unsigned long) &_switch_to,
299 cpu_context->r1,
300 cpu_context->r15, trace);
301 }
302 } else {
303 unsigned long pc, fp;
304
305 __asm__ __volatile__ ("or %0, r1, r0" : "=r" (fp));
306
307 __asm__ __volatile__ (
308 "brlid %0, 0f;"
309 "nop;"
310 "0:"
311 : "=r" (pc)
312 );
313
314
315 microblaze_unwind_inner(current, pc, fp, 0, trace);
316 }
317}
318
319