summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2007-03-02 15:01:36 +0000
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-04-21 20:34:34 +0100
commit7ab3f8d595a1b1e5cf8d726b72fd476fe0d0226c (patch)
treed37cf7290d5df5927ff870bfbb40673bead8f00d
parent46fcc86dd71d70211e965102fb69414c90381880 (diff)
downloadlinux-7ab3f8d595a1b1e5cf8d726b72fd476fe0d0226c.tar.gz
linux-7ab3f8d595a1b1e5cf8d726b72fd476fe0d0226c.tar.xz
[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 = {
* come via this function. Instead, they should provide their
* own 'handler'
*/
-asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
+asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
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)
__setup("user_debug=", user_debug_setup);
#endif
-void dump_backtrace_entry(unsigned long where, unsigned long from)
+static void dump_mem(const char *str, unsigned long bottom, unsigned long top);
+
+static inline int in_exception_text(unsigned long ptr)
+{
+ extern char __exception_text_start[];
+ extern char __exception_text_end[];
+
+ return ptr >= (unsigned long)&__exception_text_start &&
+ ptr < (unsigned long)&__exception_text_end;
+}
+
+void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
{
#ifdef CONFIG_KALLSYMS
printk("[<%08lx>] ", where);
@@ -55,6 +66,9 @@ void dump_backtrace_entry(unsigned long where, unsigned long from)
#else
printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
#endif
+
+ if (in_exception_text(where))
+ dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
}
/*
@@ -266,7 +280,7 @@ void unregister_undef_hook(struct undef_hook *hook)
spin_unlock_irqrestore(&undef_lock, flags);
}
-asmlinkage void do_undefinstr(struct pt_regs *regs)
+asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
{
unsigned int correction = thumb_mode(regs) ? 2 : 4;
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
.text : { /* Real text segment */
_text = .; /* Text and read-only data */
+ __exception_text_start = .;
+ *(.exception.text)
+ __exception_text_end = .;
*(.text)
SCHED_TEXT
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 @@
@ fp is 0 or stack frame
#define frame r4
-#define next r5
-#define save r6
+#define sv_fp r5
+#define sv_pc r6
#define mask r7
#define offset r8
@@ -31,108 +31,106 @@ ENTRY(c_backtrace)
#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK)
mov pc, lr
#else
-
stmfd sp!, {r4 - r8, lr} @ Save an extra register so we have a location...
- tst r1, #0x10 @ 26 or 32-bit?
- moveq mask, #0xfc000003
- movne mask, #0
- tst mask, r0
- movne r0, #0
- movs frame, r0
-1: moveq r0, #-2
- ldmeqfd sp!, {r4 - r8, pc}
-
-2: stmfd sp!, {pc} @ calculate offset of PC in STMIA instruction
- ldr r0, [sp], #4
- adr r1, 2b - 4
+ movs frame, r0 @ if frame pointer is zero
+ beq no_frame @ we have no stack frames
+
+ tst r1, #0x10 @ 26 or 32-bit mode?
+ moveq mask, #0xfc000003 @ mask for 26-bit
+ movne mask, #0 @ mask for 32-bit
+
+1: stmfd sp!, {pc} @ calculate offset of PC stored
+ ldr r0, [sp], #4 @ by stmfd for this CPU
+ adr r1, 1b
sub offset, r0, r1
-3: tst frame, mask @ Check for address exceptions...
- bne 1b
+/*
+ * Stack frame layout:
+ * optionally saved caller registers (r4 - r10)
+ * saved fp
+ * saved sp
+ * saved lr
+ * frame => saved pc
+ * optionally saved arguments (r0 - r3)
+ * saved sp => <next word>
+ *
+ * Functions start with the following code sequence:
+ * mov ip, sp
+ * stmfd sp!, {r0 - r3} (optional)
+ * corrected pc => stmfd sp!, {..., fp, ip, lr, pc}
+ */
+for_each_frame: tst frame, mask @ Check for address exceptions
+ bne no_frame
+
+1001: ldr sv_pc, [frame, #0] @ get saved pc
+1002: ldr sv_fp, [frame, #-12] @ get saved fp
-1001: ldr next, [frame, #-12] @ get fp
-1002: ldr r2, [frame, #-4] @ get lr
-1003: ldr r3, [frame, #0] @ get pc
- sub save, r3, offset @ Correct PC for prefetching
- bic save, save, mask
-1004: ldr r1, [save, #0] @ get instruction at function
- mov r1, r1, lsr #10
- ldr r3, .Ldsi+4
- teq r1, r3
- subeq save, save, #4
- mov r0, save
- bic r1, r2, mask
+ sub sv_pc, sv_pc, offset @ Correct PC for prefetching
+ bic sv_pc, sv_pc, mask @ mask PC/LR for the mode
+
+1003: ldr r2, [sv_pc, #-4] @ if stmfd sp!, {args} exists,
+ ldr r3, .Ldsi+4 @ adjust saved 'pc' back one
+ teq r3, r2, lsr #10 @ instruction
+ subne r0, sv_pc, #4 @ allow for mov
+ subeq r0, sv_pc, #8 @ allow for mov + stmia
+
+ ldr r1, [frame, #-4] @ get saved lr
+ mov r2, frame
+ bic r1, r1, mask @ mask PC/LR for the mode
bl dump_backtrace_entry
- ldr r0, [frame, #-8] @ get sp
- sub r0, r0, #4
-1005: ldr r1, [save, #4] @ get instruction at function+4
- mov r3, r1, lsr #10
- ldr r2, .Ldsi+4
- teq r3, r2 @ Check for stmia sp!, {args}
- addeq save, save, #4 @ next instruction
- bleq .Ldumpstm
-
- sub r0, frame, #16
-1006: ldr r1, [save, #4] @ Get 'stmia sp!, {rlist, fp, ip, lr, pc}' instruction
- mov r3, r1, lsr #10
- ldr r2, .Ldsi
- teq r3, r2
- bleq .Ldumpstm
-
- /*
- * A zero next framepointer means we're done.
- */
- teq next, #0
- ldmeqfd sp!, {r4 - r8, pc}
-
- /*
- * The next framepointer must be above the
- * current framepointer.
- */
- cmp next, frame
- mov frame, next
- bhi 3b
- b 1007f
+ ldr r1, [sv_pc, #-4] @ if stmfd sp!, {args} exists,
+ ldr r3, .Ldsi+4
+ teq r3, r1, lsr #10
+ ldreq r0, [frame, #-8] @ get sp
+ subeq r0, r0, #4 @ point at the last arg
+ bleq .Ldumpstm @ dump saved registers
-/*
- * Fixup for LDMDB. Note that this must not be in the fixup section.
- */
-1007: ldr r0, =.Lbad
+1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc}
+ ldr r3, .Ldsi @ instruction exists,
+ teq r3, r1, lsr #10
+ subeq r0, frame, #16
+ bleq .Ldumpstm @ dump saved registers
+
+ teq sv_fp, #0 @ zero saved fp means
+ beq no_frame @ no further frames
+
+ cmp sv_fp, frame @ next frame must be
+ mov frame, sv_fp @ above the current frame
+ bhi for_each_frame
+
+1006: adr r0, .Lbad
mov r1, frame
bl printk
- ldmfd sp!, {r4 - r8, pc}
- .ltorg
+no_frame: ldmfd sp!, {r4 - r8, pc}
.section __ex_table,"a"
.align 3
- .long 1001b, 1007b
- .long 1002b, 1007b
- .long 1003b, 1007b
- .long 1004b, 1007b
- .long 1005b, 1007b
- .long 1006b, 1007b
+ .long 1001b, 1006b
+ .long 1002b, 1006b
+ .long 1003b, 1006b
+ .long 1004b, 1006b
.previous
#define instr r4
#define reg r5
#define stack r6
-.Ldumpstm: stmfd sp!, {instr, reg, stack, r7, r8, lr}
+.Ldumpstm: stmfd sp!, {instr, reg, stack, r7, lr}
mov stack, r0
mov instr, r1
- mov reg, #9
+ mov reg, #10
mov r7, #0
1: mov r3, #1
tst instr, r3, lsl reg
beq 2f
add r7, r7, #1
- teq r7, #4
- moveq r7, #0
- moveq r3, #'\n'
- movne r3, #' '
- ldr r2, [stack], #-4
- mov r1, reg
+ teq r7, #6
+ moveq r7, #1
+ moveq r1, #'\n'
+ movne r1, #' '
+ ldr r3, [stack], #-4
+ mov r2, reg
adr r0, .Lfp
bl printk
2: subs reg, reg, #1
@@ -140,14 +138,13 @@ ENTRY(c_backtrace)
teq r7, #0
adrne r0, .Lcr
blne printk
- mov r0, stack
- ldmfd sp!, {instr, reg, stack, r7, r8, pc}
+ ldmfd sp!, {instr, reg, stack, r7, pc}
-.Lfp: .asciz " r%d = %08X%c"
+.Lfp: .asciz "%cr%d:%08x"
.Lcr: .asciz "\n"
.Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n"
.align
-.Ldsi: .word 0x00e92dd8 >> 2
- .word 0x00e92d00 >> 2
+.Ldsi: .word 0xe92dd800 >> 10 @ stmfd sp!, {... fp, ip, lr, pc}
+ .word 0xe92d0000 >> 10 @ stmfd sp!, {}
#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 *)
/*
* Dispatch a data abort to the relevant handler.
*/
-asmlinkage void
+asmlinkage void __exception
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
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)
notify_die("", regs, &info, fsr, 0);
}
-asmlinkage void
+asmlinkage void __exception
do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
{
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 @@
#include <linux/linkage.h>
#include <linux/irqflags.h>
+#define __exception __attribute__((section(".exception.text")))
+
struct thread_info;
struct task_struct;