diff options
author | Paul Mundt <lethal@linux-sh.org> | 2012-06-12 23:12:14 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2012-06-12 23:12:14 -0400 |
commit | f21efd45362d555b3b93960a5736ad7c6fc1f367 (patch) | |
tree | e86510670b3543be81814d0099954b8a83cd8dfb /arch/sh/kernel | |
parent | 380622e9ff56201f9c3c1f153d1ca0a24324a1d4 (diff) | |
parent | 3a898c0f36f9fe312cb6e98865a6833110e67cb2 (diff) |
Merge branch 'sh/multi-unwinders' into sh-latest
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r-- | arch/sh/kernel/cpu/sh5/unwind.c | 63 | ||||
-rw-r--r-- | arch/sh/kernel/dumpstack.c | 58 | ||||
-rw-r--r-- | arch/sh/kernel/traps_32.c | 50 | ||||
-rw-r--r-- | arch/sh/kernel/traps_64.c | 26 |
4 files changed, 99 insertions, 98 deletions
diff --git a/arch/sh/kernel/cpu/sh5/unwind.c b/arch/sh/kernel/cpu/sh5/unwind.c index b205b25eaf45..10aed41757fc 100644 --- a/arch/sh/kernel/cpu/sh5/unwind.c +++ b/arch/sh/kernel/cpu/sh5/unwind.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include <asm/ptrace.h> | 16 | #include <asm/ptrace.h> |
17 | #include <asm/processor.h> | 17 | #include <asm/processor.h> |
18 | #include <asm/io.h> | 18 | #include <asm/io.h> |
19 | #include <asm/unwinder.h> | ||
20 | #include <asm/stacktrace.h> | ||
19 | 21 | ||
20 | static u8 regcache[63]; | 22 | static u8 regcache[63]; |
21 | 23 | ||
@@ -199,8 +201,11 @@ static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, | |||
199 | return 0; | 201 | return 0; |
200 | } | 202 | } |
201 | 203 | ||
202 | /* Don't put this on the stack since we'll want to call sh64_unwind | 204 | /* |
203 | * when we're close to underflowing the stack anyway. */ | 205 | * Don't put this on the stack since we'll want to call in to |
206 | * sh64_unwinder_dump() when we're close to underflowing the stack | ||
207 | * anyway. | ||
208 | */ | ||
204 | static struct pt_regs here_regs; | 209 | static struct pt_regs here_regs; |
205 | 210 | ||
206 | extern const char syscall_ret; | 211 | extern const char syscall_ret; |
@@ -208,17 +213,19 @@ extern const char ret_from_syscall; | |||
208 | extern const char ret_from_exception; | 213 | extern const char ret_from_exception; |
209 | extern const char ret_from_irq; | 214 | extern const char ret_from_irq; |
210 | 215 | ||
211 | static void sh64_unwind_inner(struct pt_regs *regs); | 216 | static void sh64_unwind_inner(const struct stacktrace_ops *ops, |
217 | void *data, struct pt_regs *regs); | ||
212 | 218 | ||
213 | static void unwind_nested (unsigned long pc, unsigned long fp) | 219 | static inline void unwind_nested(const struct stacktrace_ops *ops, void *data, |
220 | unsigned long pc, unsigned long fp) | ||
214 | { | 221 | { |
215 | if ((fp >= __MEMORY_START) && | 222 | if ((fp >= __MEMORY_START) && |
216 | ((fp & 7) == 0)) { | 223 | ((fp & 7) == 0)) |
217 | sh64_unwind_inner((struct pt_regs *) fp); | 224 | sh64_unwind_inner(ops, data, (struct pt_regs *)fp); |
218 | } | ||
219 | } | 225 | } |
220 | 226 | ||
221 | static void sh64_unwind_inner(struct pt_regs *regs) | 227 | static void sh64_unwind_inner(const struct stacktrace_ops *ops, |
228 | void *data, struct pt_regs *regs) | ||
222 | { | 229 | { |
223 | unsigned long pc, fp; | 230 | unsigned long pc, fp; |
224 | int ofs = 0; | 231 | int ofs = 0; |
@@ -232,29 +239,29 @@ static void sh64_unwind_inner(struct pt_regs *regs) | |||
232 | int cond; | 239 | int cond; |
233 | unsigned long next_fp, next_pc; | 240 | unsigned long next_fp, next_pc; |
234 | 241 | ||
235 | if (pc == ((unsigned long) &syscall_ret & ~1)) { | 242 | if (pc == ((unsigned long)&syscall_ret & ~1)) { |
236 | printk("SYSCALL\n"); | 243 | printk("SYSCALL\n"); |
237 | unwind_nested(pc,fp); | 244 | unwind_nested(ops, data, pc, fp); |
238 | return; | 245 | return; |
239 | } | 246 | } |
240 | 247 | ||
241 | if (pc == ((unsigned long) &ret_from_syscall & ~1)) { | 248 | if (pc == ((unsigned long)&ret_from_syscall & ~1)) { |
242 | printk("SYSCALL (PREEMPTED)\n"); | 249 | printk("SYSCALL (PREEMPTED)\n"); |
243 | unwind_nested(pc,fp); | 250 | unwind_nested(ops, data, pc, fp); |
244 | return; | 251 | return; |
245 | } | 252 | } |
246 | 253 | ||
247 | /* In this case, the PC is discovered by lookup_prev_stack_frame but | 254 | /* In this case, the PC is discovered by lookup_prev_stack_frame but |
248 | it has 4 taken off it to look like the 'caller' */ | 255 | it has 4 taken off it to look like the 'caller' */ |
249 | if (pc == ((unsigned long) &ret_from_exception & ~1)) { | 256 | if (pc == ((unsigned long)&ret_from_exception & ~1)) { |
250 | printk("EXCEPTION\n"); | 257 | printk("EXCEPTION\n"); |
251 | unwind_nested(pc,fp); | 258 | unwind_nested(ops, data, pc, fp); |
252 | return; | 259 | return; |
253 | } | 260 | } |
254 | 261 | ||
255 | if (pc == ((unsigned long) &ret_from_irq & ~1)) { | 262 | if (pc == ((unsigned long)&ret_from_irq & ~1)) { |
256 | printk("IRQ\n"); | 263 | printk("IRQ\n"); |
257 | unwind_nested(pc,fp); | 264 | unwind_nested(ops, data, pc, fp); |
258 | return; | 265 | return; |
259 | } | 266 | } |
260 | 267 | ||
@@ -263,8 +270,7 @@ static void sh64_unwind_inner(struct pt_regs *regs) | |||
263 | 270 | ||
264 | pc -= ofs; | 271 | pc -= ofs; |
265 | 272 | ||
266 | printk("[<%08lx>] ", pc); | 273 | ops->address(data, pc, 1); |
267 | print_symbol("%s\n", pc); | ||
268 | 274 | ||
269 | if (first_pass) { | 275 | if (first_pass) { |
270 | /* If the innermost frame is a leaf function, it's | 276 | /* If the innermost frame is a leaf function, it's |
@@ -287,10 +293,13 @@ static void sh64_unwind_inner(struct pt_regs *regs) | |||
287 | } | 293 | } |
288 | 294 | ||
289 | printk("\n"); | 295 | printk("\n"); |
290 | |||
291 | } | 296 | } |
292 | 297 | ||
293 | void sh64_unwind(struct pt_regs *regs) | 298 | static void sh64_unwinder_dump(struct task_struct *task, |
299 | struct pt_regs *regs, | ||
300 | unsigned long *sp, | ||
301 | const struct stacktrace_ops *ops, | ||
302 | void *data) | ||
294 | { | 303 | { |
295 | if (!regs) { | 304 | if (!regs) { |
296 | /* | 305 | /* |
@@ -320,7 +329,17 @@ void sh64_unwind(struct pt_regs *regs) | |||
320 | ); | 329 | ); |
321 | } | 330 | } |
322 | 331 | ||
323 | printk("\nCall Trace:\n"); | 332 | sh64_unwind_inner(ops, data, regs); |
324 | sh64_unwind_inner(regs); | ||
325 | } | 333 | } |
326 | 334 | ||
335 | static struct unwinder sh64_unwinder = { | ||
336 | .name = "sh64-unwinder", | ||
337 | .dump = sh64_unwinder_dump, | ||
338 | .rating = 150, | ||
339 | }; | ||
340 | |||
341 | static int __init sh64_unwinder_init(void) | ||
342 | { | ||
343 | return unwinder_register(&sh64_unwinder); | ||
344 | } | ||
345 | early_initcall(sh64_unwinder_init); | ||
diff --git a/arch/sh/kernel/dumpstack.c b/arch/sh/kernel/dumpstack.c index 694158b9a50f..7617dc4129ac 100644 --- a/arch/sh/kernel/dumpstack.c +++ b/arch/sh/kernel/dumpstack.c | |||
@@ -2,13 +2,48 @@ | |||
2 | * Copyright (C) 1991, 1992 Linus Torvalds | 2 | * Copyright (C) 1991, 1992 Linus Torvalds |
3 | * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs | 3 | * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs |
4 | * Copyright (C) 2009 Matt Fleming | 4 | * Copyright (C) 2009 Matt Fleming |
5 | * Copyright (C) 2002 - 2012 Paul Mundt | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file "COPYING" in the main directory of this archive | ||
9 | * for more details. | ||
5 | */ | 10 | */ |
6 | #include <linux/kallsyms.h> | 11 | #include <linux/kallsyms.h> |
7 | #include <linux/ftrace.h> | 12 | #include <linux/ftrace.h> |
8 | #include <linux/debug_locks.h> | 13 | #include <linux/debug_locks.h> |
14 | #include <linux/kdebug.h> | ||
15 | #include <linux/export.h> | ||
16 | #include <linux/uaccess.h> | ||
9 | #include <asm/unwinder.h> | 17 | #include <asm/unwinder.h> |
10 | #include <asm/stacktrace.h> | 18 | #include <asm/stacktrace.h> |
11 | 19 | ||
20 | void dump_mem(const char *str, unsigned long bottom, unsigned long top) | ||
21 | { | ||
22 | unsigned long p; | ||
23 | int i; | ||
24 | |||
25 | printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); | ||
26 | |||
27 | for (p = bottom & ~31; p < top; ) { | ||
28 | printk("%04lx: ", p & 0xffff); | ||
29 | |||
30 | for (i = 0; i < 8; i++, p += 4) { | ||
31 | unsigned int val; | ||
32 | |||
33 | if (p < bottom || p >= top) | ||
34 | printk(" "); | ||
35 | else { | ||
36 | if (__get_user(val, (unsigned int __user *)p)) { | ||
37 | printk("\n"); | ||
38 | return; | ||
39 | } | ||
40 | printk("%08x ", val); | ||
41 | } | ||
42 | } | ||
43 | printk("\n"); | ||
44 | } | ||
45 | } | ||
46 | |||
12 | void printk_address(unsigned long address, int reliable) | 47 | void printk_address(unsigned long address, int reliable) |
13 | { | 48 | { |
14 | printk(" [<%p>] %s%pS\n", (void *) address, | 49 | printk(" [<%p>] %s%pS\n", (void *) address, |
@@ -106,3 +141,26 @@ void show_trace(struct task_struct *tsk, unsigned long *sp, | |||
106 | 141 | ||
107 | debug_show_held_locks(tsk); | 142 | debug_show_held_locks(tsk); |
108 | } | 143 | } |
144 | |||
145 | void show_stack(struct task_struct *tsk, unsigned long *sp) | ||
146 | { | ||
147 | unsigned long stack; | ||
148 | |||
149 | if (!tsk) | ||
150 | tsk = current; | ||
151 | if (tsk == current) | ||
152 | sp = (unsigned long *)current_stack_pointer; | ||
153 | else | ||
154 | sp = (unsigned long *)tsk->thread.sp; | ||
155 | |||
156 | stack = (unsigned long)sp; | ||
157 | dump_mem("Stack: ", stack, THREAD_SIZE + | ||
158 | (unsigned long)task_stack_page(tsk)); | ||
159 | show_trace(tsk, sp, NULL); | ||
160 | } | ||
161 | |||
162 | void dump_stack(void) | ||
163 | { | ||
164 | show_stack(NULL, NULL); | ||
165 | } | ||
166 | EXPORT_SYMBOL(dump_stack); | ||
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index a37175deb73f..b8f5a51841ec 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c | |||
@@ -48,33 +48,6 @@ | |||
48 | #define TRAP_ILLEGAL_SLOT_INST 13 | 48 | #define TRAP_ILLEGAL_SLOT_INST 13 |
49 | #endif | 49 | #endif |
50 | 50 | ||
51 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) | ||
52 | { | ||
53 | unsigned long p; | ||
54 | int i; | ||
55 | |||
56 | printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); | ||
57 | |||
58 | for (p = bottom & ~31; p < top; ) { | ||
59 | printk("%04lx: ", p & 0xffff); | ||
60 | |||
61 | for (i = 0; i < 8; i++, p += 4) { | ||
62 | unsigned int val; | ||
63 | |||
64 | if (p < bottom || p >= top) | ||
65 | printk(" "); | ||
66 | else { | ||
67 | if (__get_user(val, (unsigned int __user *)p)) { | ||
68 | printk("\n"); | ||
69 | return; | ||
70 | } | ||
71 | printk("%08x ", val); | ||
72 | } | ||
73 | } | ||
74 | printk("\n"); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | static DEFINE_SPINLOCK(die_lock); | 51 | static DEFINE_SPINLOCK(die_lock); |
79 | 52 | ||
80 | void die(const char * str, struct pt_regs * regs, long err) | 53 | void die(const char * str, struct pt_regs * regs, long err) |
@@ -900,26 +873,3 @@ void __init trap_init(void) | |||
900 | set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler); | 873 | set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler); |
901 | #endif | 874 | #endif |
902 | } | 875 | } |
903 | |||
904 | void show_stack(struct task_struct *tsk, unsigned long *sp) | ||
905 | { | ||
906 | unsigned long stack; | ||
907 | |||
908 | if (!tsk) | ||
909 | tsk = current; | ||
910 | if (tsk == current) | ||
911 | sp = (unsigned long *)current_stack_pointer; | ||
912 | else | ||
913 | sp = (unsigned long *)tsk->thread.sp; | ||
914 | |||
915 | stack = (unsigned long)sp; | ||
916 | dump_mem("Stack: ", stack, THREAD_SIZE + | ||
917 | (unsigned long)task_stack_page(tsk)); | ||
918 | show_trace(tsk, sp, NULL); | ||
919 | } | ||
920 | |||
921 | void dump_stack(void) | ||
922 | { | ||
923 | show_stack(NULL, NULL); | ||
924 | } | ||
925 | EXPORT_SYMBOL(dump_stack); | ||
diff --git a/arch/sh/kernel/traps_64.c b/arch/sh/kernel/traps_64.c index 8dae93ed8aff..ba95d63e623d 100644 --- a/arch/sh/kernel/traps_64.c +++ b/arch/sh/kernel/traps_64.c | |||
@@ -253,32 +253,6 @@ int do_unknown_trapa(unsigned long scId, struct pt_regs *regs) | |||
253 | return -ENOSYS; | 253 | return -ENOSYS; |
254 | } | 254 | } |
255 | 255 | ||
256 | void show_stack(struct task_struct *tsk, unsigned long *sp) | ||
257 | { | ||
258 | #ifdef CONFIG_KALLSYMS | ||
259 | extern void sh64_unwind(struct pt_regs *regs); | ||
260 | struct pt_regs *regs; | ||
261 | |||
262 | regs = tsk ? tsk->thread.kregs : NULL; | ||
263 | |||
264 | sh64_unwind(regs); | ||
265 | #else | ||
266 | printk(KERN_ERR "Can't backtrace on sh64 without CONFIG_KALLSYMS\n"); | ||
267 | #endif | ||
268 | } | ||
269 | |||
270 | void show_task(unsigned long *sp) | ||
271 | { | ||
272 | show_stack(NULL, sp); | ||
273 | } | ||
274 | |||
275 | void dump_stack(void) | ||
276 | { | ||
277 | show_task(NULL); | ||
278 | } | ||
279 | /* Needed by any user of WARN_ON in view of the defn in include/asm-sh/bug.h */ | ||
280 | EXPORT_SYMBOL(dump_stack); | ||
281 | |||
282 | static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, | 256 | static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, |
283 | unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk) | 257 | unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk) |
284 | { | 258 | { |