diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2007-03-02 10:01:36 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-04-21 15:34:34 -0400 |
commit | 7ab3f8d595a1b1e5cf8d726b72fd476fe0d0226c (patch) | |
tree | d37cf7290d5df5927ff870bfbb40673bead8f00d | |
parent | 46fcc86dd71d70211e965102fb69414c90381880 (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.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 18 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 3 | ||||
-rw-r--r-- | arch/arm/lib/backtrace.S | 165 | ||||
-rw-r--r-- | arch/arm/mm/fault.c | 4 | ||||
-rw-r--r-- | include/asm-arm/system.h | 2 |
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 | */ |
112 | asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) | 112 | asmlinkage 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 | ||
48 | void dump_backtrace_entry(unsigned long where, unsigned long from) | 48 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top); |
49 | |||
50 | static 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 | |||
59 | void 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 | ||
269 | asmlinkage void do_undefinstr(struct pt_regs *regs) | 283 | asmlinkage 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 |
42 | 1: moveq r0, #-2 | 41 | |
43 | ldmeqfd sp!, {r4 - r8, pc} | 42 | 1: stmfd sp!, {pc} @ calculate offset of PC stored |
44 | 43 | ldr r0, [sp], #4 @ by stmfd for this CPU | |
45 | 2: 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 | ||
50 | 3: 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 | */ | ||
62 | for_each_frame: tst frame, mask @ Check for address exceptions | ||
63 | bne no_frame | ||
64 | |||
65 | 1001: ldr sv_pc, [frame, #0] @ get saved pc | ||
66 | 1002: ldr sv_fp, [frame, #-12] @ get saved fp | ||
52 | 67 | ||
53 | 1001: ldr next, [frame, #-12] @ get fp | 68 | sub sv_pc, sv_pc, offset @ Correct PC for prefetching |
54 | 1002: ldr r2, [frame, #-4] @ get lr | 69 | bic sv_pc, sv_pc, mask @ mask PC/LR for the mode |
55 | 1003: ldr r3, [frame, #0] @ get pc | 70 | |
56 | sub save, r3, offset @ Correct PC for prefetching | 71 | 1003: 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 |
58 | 1004: 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 |
69 | 1005: 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 | ||
77 | 1006: 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 | /* | 89 | 1004: 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 |
101 | 1007: 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 | |||
102 | 1006: adr r0, .Lbad | ||
102 | mov r1, frame | 103 | mov r1, frame |
103 | bl printk | 104 | bl printk |
104 | ldmfd sp!, {r4 - r8, pc} | 105 | no_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 |
126 | 1: mov r3, #1 | 124 | 1: 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 |
138 | 2: subs reg, reg, #1 | 136 | 2: 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 | */ |
441 | asmlinkage void | 441 | asmlinkage void __exception |
442 | do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | 442 | do_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 | ||
460 | asmlinkage void | 460 | asmlinkage void __exception |
461 | do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) | 461 | do_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 | |||
79 | struct thread_info; | 81 | struct thread_info; |
80 | struct task_struct; | 82 | struct task_struct; |
81 | 83 | ||