aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2007-03-02 10:01:36 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-04-21 15:34:34 -0400
commit7ab3f8d595a1b1e5cf8d726b72fd476fe0d0226c (patch)
treed37cf7290d5df5927ff870bfbb40673bead8f00d
parent46fcc86dd71d70211e965102fb69414c90381880 (diff)
[ARM] Add ability to dump exception stacks to kernel backtraces
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/kernel/irq.c2
-rw-r--r--arch/arm/kernel/traps.c18
-rw-r--r--arch/arm/kernel/vmlinux.lds.S3
-rw-r--r--arch/arm/lib/backtrace.S165
-rw-r--r--arch/arm/mm/fault.c4
-rw-r--r--include/asm-arm/system.h2
6 files changed, 105 insertions, 89 deletions
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index e101846ab7dd..a72b82e727f1 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -109,7 +109,7 @@ static struct irq_desc bad_irq_desc = {
109 * come via this function. Instead, they should provide their 109 * come via this function. Instead, they should provide their
110 * own 'handler' 110 * own 'handler'
111 */ 111 */
112asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) 112asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
113{ 113{
114 struct pt_regs *old_regs = set_irq_regs(regs); 114 struct pt_regs *old_regs = set_irq_regs(regs);
115 struct irq_desc *desc = irq_desc + irq; 115 struct irq_desc *desc = irq_desc + irq;
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 24095601359b..ba1c1884e68e 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -45,7 +45,18 @@ static int __init user_debug_setup(char *str)
45__setup("user_debug=", user_debug_setup); 45__setup("user_debug=", user_debug_setup);
46#endif 46#endif
47 47
48void dump_backtrace_entry(unsigned long where, unsigned long from) 48static void dump_mem(const char *str, unsigned long bottom, unsigned long top);
49
50static inline int in_exception_text(unsigned long ptr)
51{
52 extern char __exception_text_start[];
53 extern char __exception_text_end[];
54
55 return ptr >= (unsigned long)&__exception_text_start &&
56 ptr < (unsigned long)&__exception_text_end;
57}
58
59void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
49{ 60{
50#ifdef CONFIG_KALLSYMS 61#ifdef CONFIG_KALLSYMS
51 printk("[<%08lx>] ", where); 62 printk("[<%08lx>] ", where);
@@ -55,6 +66,9 @@ void dump_backtrace_entry(unsigned long where, unsigned long from)
55#else 66#else
56 printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); 67 printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
57#endif 68#endif
69
70 if (in_exception_text(where))
71 dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
58} 72}
59 73
60/* 74/*
@@ -266,7 +280,7 @@ void unregister_undef_hook(struct undef_hook *hook)
266 spin_unlock_irqrestore(&undef_lock, flags); 280 spin_unlock_irqrestore(&undef_lock, flags);
267} 281}
268 282
269asmlinkage void do_undefinstr(struct pt_regs *regs) 283asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
270{ 284{
271 unsigned int correction = thumb_mode(regs) ? 2 : 4; 285 unsigned int correction = thumb_mode(regs) ? 2 : 4;
272 unsigned int instr; 286 unsigned int instr;
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index ddbdad48f5b2..b295f6a85cf1 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -83,6 +83,9 @@ SECTIONS
83 83
84 .text : { /* Real text segment */ 84 .text : { /* Real text segment */
85 _text = .; /* Text and read-only data */ 85 _text = .; /* Text and read-only data */
86 __exception_text_start = .;
87 *(.exception.text)
88 __exception_text_end = .;
86 *(.text) 89 *(.text)
87 SCHED_TEXT 90 SCHED_TEXT
88 LOCK_TEXT 91 LOCK_TEXT
diff --git a/arch/arm/lib/backtrace.S b/arch/arm/lib/backtrace.S
index 74230083cbf4..84dc890d2bf3 100644
--- a/arch/arm/lib/backtrace.S
+++ b/arch/arm/lib/backtrace.S
@@ -17,8 +17,8 @@
17@ fp is 0 or stack frame 17@ fp is 0 or stack frame
18 18
19#define frame r4 19#define frame r4
20#define next r5 20#define sv_fp r5
21#define save r6 21#define sv_pc r6
22#define mask r7 22#define mask r7
23#define offset r8 23#define offset r8
24 24
@@ -31,108 +31,106 @@ ENTRY(c_backtrace)
31#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) 31#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK)
32 mov pc, lr 32 mov pc, lr
33#else 33#else
34
35 stmfd sp!, {r4 - r8, lr} @ Save an extra register so we have a location... 34 stmfd sp!, {r4 - r8, lr} @ Save an extra register so we have a location...
36 tst r1, #0x10 @ 26 or 32-bit? 35 movs frame, r0 @ if frame pointer is zero
37 moveq mask, #0xfc000003 36 beq no_frame @ we have no stack frames
38 movne mask, #0 37
39 tst mask, r0 38 tst r1, #0x10 @ 26 or 32-bit mode?
40 movne r0, #0 39 moveq mask, #0xfc000003 @ mask for 26-bit
41 movs frame, r0 40 movne mask, #0 @ mask for 32-bit
421: moveq r0, #-2 41
43 ldmeqfd sp!, {r4 - r8, pc} 421: stmfd sp!, {pc} @ calculate offset of PC stored
44 43 ldr r0, [sp], #4 @ by stmfd for this CPU
452: stmfd sp!, {pc} @ calculate offset of PC in STMIA instruction 44 adr r1, 1b
46 ldr r0, [sp], #4
47 adr r1, 2b - 4
48 sub offset, r0, r1 45 sub offset, r0, r1
49 46
503: tst frame, mask @ Check for address exceptions... 47/*
51 bne 1b 48 * Stack frame layout:
49 * optionally saved caller registers (r4 - r10)
50 * saved fp
51 * saved sp
52 * saved lr
53 * frame => saved pc
54 * optionally saved arguments (r0 - r3)
55 * saved sp => <next word>
56 *
57 * Functions start with the following code sequence:
58 * mov ip, sp
59 * stmfd sp!, {r0 - r3} (optional)
60 * corrected pc => stmfd sp!, {..., fp, ip, lr, pc}
61 */
62for_each_frame: tst frame, mask @ Check for address exceptions
63 bne no_frame
64
651001: ldr sv_pc, [frame, #0] @ get saved pc
661002: ldr sv_fp, [frame, #-12] @ get saved fp
52 67
531001: ldr next, [frame, #-12] @ get fp 68 sub sv_pc, sv_pc, offset @ Correct PC for prefetching
541002: ldr r2, [frame, #-4] @ get lr 69 bic sv_pc, sv_pc, mask @ mask PC/LR for the mode
551003: ldr r3, [frame, #0] @ get pc 70
56 sub save, r3, offset @ Correct PC for prefetching 711003: ldr r2, [sv_pc, #-4] @ if stmfd sp!, {args} exists,
57 bic save, save, mask 72 ldr r3, .Ldsi+4 @ adjust saved 'pc' back one
581004: ldr r1, [save, #0] @ get instruction at function 73 teq r3, r2, lsr #10 @ instruction
59 mov r1, r1, lsr #10 74 subne r0, sv_pc, #4 @ allow for mov
60 ldr r3, .Ldsi+4 75 subeq r0, sv_pc, #8 @ allow for mov + stmia
61 teq r1, r3 76
62 subeq save, save, #4 77 ldr r1, [frame, #-4] @ get saved lr
63 mov r0, save 78 mov r2, frame
64 bic r1, r2, mask 79 bic r1, r1, mask @ mask PC/LR for the mode
65 bl dump_backtrace_entry 80 bl dump_backtrace_entry
66 81
67 ldr r0, [frame, #-8] @ get sp 82 ldr r1, [sv_pc, #-4] @ if stmfd sp!, {args} exists,
68 sub r0, r0, #4 83 ldr r3, .Ldsi+4
691005: ldr r1, [save, #4] @ get instruction at function+4 84 teq r3, r1, lsr #10
70 mov r3, r1, lsr #10 85 ldreq r0, [frame, #-8] @ get sp
71 ldr r2, .Ldsi+4 86 subeq r0, r0, #4 @ point at the last arg
72 teq r3, r2 @ Check for stmia sp!, {args} 87 bleq .Ldumpstm @ dump saved registers
73 addeq save, save, #4 @ next instruction
74 bleq .Ldumpstm
75
76 sub r0, frame, #16
771006: ldr r1, [save, #4] @ Get 'stmia sp!, {rlist, fp, ip, lr, pc}' instruction
78 mov r3, r1, lsr #10
79 ldr r2, .Ldsi
80 teq r3, r2
81 bleq .Ldumpstm
82
83 /*
84 * A zero next framepointer means we're done.
85 */
86 teq next, #0
87 ldmeqfd sp!, {r4 - r8, pc}
88
89 /*
90 * The next framepointer must be above the
91 * current framepointer.
92 */
93 cmp next, frame
94 mov frame, next
95 bhi 3b
96 b 1007f
97 88
98/* 891004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc}
99 * Fixup for LDMDB. Note that this must not be in the fixup section. 90 ldr r3, .Ldsi @ instruction exists,
100 */ 91 teq r3, r1, lsr #10
1011007: ldr r0, =.Lbad 92 subeq r0, frame, #16
93 bleq .Ldumpstm @ dump saved registers
94
95 teq sv_fp, #0 @ zero saved fp means
96 beq no_frame @ no further frames
97
98 cmp sv_fp, frame @ next frame must be
99 mov frame, sv_fp @ above the current frame
100 bhi for_each_frame
101
1021006: adr r0, .Lbad
102 mov r1, frame 103 mov r1, frame
103 bl printk 104 bl printk
104 ldmfd sp!, {r4 - r8, pc} 105no_frame: ldmfd sp!, {r4 - r8, pc}
105 .ltorg
106 106
107 .section __ex_table,"a" 107 .section __ex_table,"a"
108 .align 3 108 .align 3
109 .long 1001b, 1007b 109 .long 1001b, 1006b
110 .long 1002b, 1007b 110 .long 1002b, 1006b
111 .long 1003b, 1007b 111 .long 1003b, 1006b
112 .long 1004b, 1007b 112 .long 1004b, 1006b
113 .long 1005b, 1007b
114 .long 1006b, 1007b
115 .previous 113 .previous
116 114
117#define instr r4 115#define instr r4
118#define reg r5 116#define reg r5
119#define stack r6 117#define stack r6
120 118
121.Ldumpstm: stmfd sp!, {instr, reg, stack, r7, r8, lr} 119.Ldumpstm: stmfd sp!, {instr, reg, stack, r7, lr}
122 mov stack, r0 120 mov stack, r0
123 mov instr, r1 121 mov instr, r1
124 mov reg, #9 122 mov reg, #10
125 mov r7, #0 123 mov r7, #0
1261: mov r3, #1 1241: mov r3, #1
127 tst instr, r3, lsl reg 125 tst instr, r3, lsl reg
128 beq 2f 126 beq 2f
129 add r7, r7, #1 127 add r7, r7, #1
130 teq r7, #4 128 teq r7, #6
131 moveq r7, #0 129 moveq r7, #1
132 moveq r3, #'\n' 130 moveq r1, #'\n'
133 movne r3, #' ' 131 movne r1, #' '
134 ldr r2, [stack], #-4 132 ldr r3, [stack], #-4
135 mov r1, reg 133 mov r2, reg
136 adr r0, .Lfp 134 adr r0, .Lfp
137 bl printk 135 bl printk
1382: subs reg, reg, #1 1362: subs reg, reg, #1
@@ -140,14 +138,13 @@ ENTRY(c_backtrace)
140 teq r7, #0 138 teq r7, #0
141 adrne r0, .Lcr 139 adrne r0, .Lcr
142 blne printk 140 blne printk
143 mov r0, stack 141 ldmfd sp!, {instr, reg, stack, r7, pc}
144 ldmfd sp!, {instr, reg, stack, r7, r8, pc}
145 142
146.Lfp: .asciz " r%d = %08X%c" 143.Lfp: .asciz "%cr%d:%08x"
147.Lcr: .asciz "\n" 144.Lcr: .asciz "\n"
148.Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n" 145.Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n"
149 .align 146 .align
150.Ldsi: .word 0x00e92dd8 >> 2 147.Ldsi: .word 0xe92dd800 >> 10 @ stmfd sp!, {... fp, ip, lr, pc}
151 .word 0x00e92d00 >> 2 148 .word 0xe92d0000 >> 10 @ stmfd sp!, {}
152 149
153#endif 150#endif
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 9fd6d2eafb40..fa2d107f0d44 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -438,7 +438,7 @@ hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *)
438/* 438/*
439 * Dispatch a data abort to the relevant handler. 439 * Dispatch a data abort to the relevant handler.
440 */ 440 */
441asmlinkage void 441asmlinkage void __exception
442do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 442do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
443{ 443{
444 const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6); 444 const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6);
@@ -457,7 +457,7 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
457 notify_die("", regs, &info, fsr, 0); 457 notify_die("", regs, &info, fsr, 0);
458} 458}
459 459
460asmlinkage void 460asmlinkage void __exception
461do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) 461do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
462{ 462{
463 do_translation_fault(addr, 0, regs); 463 do_translation_fault(addr, 0, regs);
diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h
index 69134c7518c1..63b3080bdac4 100644
--- a/include/asm-arm/system.h
+++ b/include/asm-arm/system.h
@@ -76,6 +76,8 @@
76#include <linux/linkage.h> 76#include <linux/linkage.h>
77#include <linux/irqflags.h> 77#include <linux/irqflags.h>
78 78
79#define __exception __attribute__((section(".exception.text")))
80
79struct thread_info; 81struct thread_info;
80struct task_struct; 82struct task_struct;
81 83