Commit 7ab3f8d5 authored by Russell King's avatar Russell King Committed by Russell King

[ARM] Add ability to dump exception stacks to kernel backtraces

Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 46fcc86d
...@@ -109,7 +109,7 @@ static struct irq_desc bad_irq_desc = { ...@@ -109,7 +109,7 @@ static struct irq_desc bad_irq_desc = {
* come via this function. Instead, they should provide their * come via this function. Instead, they should provide their
* own 'handler' * 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 pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc *desc = irq_desc + irq; struct irq_desc *desc = irq_desc + irq;
......
...@@ -45,7 +45,18 @@ static int __init user_debug_setup(char *str) ...@@ -45,7 +45,18 @@ static int __init user_debug_setup(char *str)
__setup("user_debug=", user_debug_setup); __setup("user_debug=", user_debug_setup);
#endif #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 #ifdef CONFIG_KALLSYMS
printk("[<%08lx>] ", where); printk("[<%08lx>] ", where);
...@@ -55,6 +66,9 @@ void dump_backtrace_entry(unsigned long where, unsigned long from) ...@@ -55,6 +66,9 @@ void dump_backtrace_entry(unsigned long where, unsigned long from)
#else #else
printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
#endif #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) ...@@ -266,7 +280,7 @@ void unregister_undef_hook(struct undef_hook *hook)
spin_unlock_irqrestore(&undef_lock, flags); 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 correction = thumb_mode(regs) ? 2 : 4;
unsigned int instr; unsigned int instr;
......
...@@ -83,6 +83,9 @@ SECTIONS ...@@ -83,6 +83,9 @@ SECTIONS
.text : { /* Real text segment */ .text : { /* Real text segment */
_text = .; /* Text and read-only data */ _text = .; /* Text and read-only data */
__exception_text_start = .;
*(.exception.text)
__exception_text_end = .;
*(.text) *(.text)
SCHED_TEXT SCHED_TEXT
LOCK_TEXT LOCK_TEXT
......
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
@ fp is 0 or stack frame @ fp is 0 or stack frame
#define frame r4 #define frame r4
#define next r5 #define sv_fp r5
#define save r6 #define sv_pc r6
#define mask r7 #define mask r7
#define offset r8 #define offset r8
...@@ -31,108 +31,106 @@ ENTRY(c_backtrace) ...@@ -31,108 +31,106 @@ ENTRY(c_backtrace)
#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) #if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK)
mov pc, lr mov pc, lr
#else #else
stmfd sp!, {r4 - r8, lr} @ Save an extra register so we have a location... stmfd sp!, {r4 - r8, lr} @ Save an extra register so we have a location...
tst r1, #0x10 @ 26 or 32-bit? movs frame, r0 @ if frame pointer is zero
moveq mask, #0xfc000003 beq no_frame @ we have no stack frames
movne mask, #0
tst mask, r0 tst r1, #0x10 @ 26 or 32-bit mode?
movne r0, #0 moveq mask, #0xfc000003 @ mask for 26-bit
movs frame, r0 movne mask, #0 @ mask for 32-bit
1: moveq r0, #-2
ldmeqfd sp!, {r4 - r8, pc} 1: stmfd sp!, {pc} @ calculate offset of PC stored
ldr r0, [sp], #4 @ by stmfd for this CPU
2: stmfd sp!, {pc} @ calculate offset of PC in STMIA instruction adr r1, 1b
ldr r0, [sp], #4
adr r1, 2b - 4
sub offset, r0, r1 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 next, [frame, #-12] @ get fp 1001: ldr sv_pc, [frame, #0] @ get saved pc
1002: ldr r2, [frame, #-4] @ get lr 1002: ldr sv_fp, [frame, #-12] @ get saved fp
1003: ldr r3, [frame, #0] @ get pc
sub save, r3, offset @ Correct PC for prefetching sub sv_pc, sv_pc, offset @ Correct PC for prefetching
bic save, save, mask bic sv_pc, sv_pc, mask @ mask PC/LR for the mode
1004: ldr r1, [save, #0] @ get instruction at function
mov r1, r1, lsr #10 1003: ldr r2, [sv_pc, #-4] @ if stmfd sp!, {args} exists,
ldr r3, .Ldsi+4 ldr r3, .Ldsi+4 @ adjust saved 'pc' back one
teq r1, r3 teq r3, r2, lsr #10 @ instruction
subeq save, save, #4 subne r0, sv_pc, #4 @ allow for mov
mov r0, save subeq r0, sv_pc, #8 @ allow for mov + stmia
bic r1, r2, mask
ldr r1, [frame, #-4] @ get saved lr
mov r2, frame
bic r1, r1, mask @ mask PC/LR for the mode
bl dump_backtrace_entry bl dump_backtrace_entry
ldr r0, [frame, #-8] @ get sp ldr r1, [sv_pc, #-4] @ if stmfd sp!, {args} exists,
sub r0, r0, #4 ldr r3, .Ldsi+4
1005: ldr r1, [save, #4] @ get instruction at function+4 teq r3, r1, lsr #10
mov r3, r1, lsr #10 ldreq r0, [frame, #-8] @ get sp
ldr r2, .Ldsi+4 subeq r0, r0, #4 @ point at the last arg
teq r3, r2 @ Check for stmia sp!, {args} bleq .Ldumpstm @ dump saved registers
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}
/* 1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc}
* The next framepointer must be above the ldr r3, .Ldsi @ instruction exists,
* current framepointer. teq r3, r1, lsr #10
*/ subeq r0, frame, #16
cmp next, frame bleq .Ldumpstm @ dump saved registers
mov frame, next
bhi 3b
b 1007f
/* teq sv_fp, #0 @ zero saved fp means
* Fixup for LDMDB. Note that this must not be in the fixup section. beq no_frame @ no further frames
*/
1007: ldr r0, =.Lbad 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 mov r1, frame
bl printk bl printk
ldmfd sp!, {r4 - r8, pc} no_frame: ldmfd sp!, {r4 - r8, pc}
.ltorg
.section __ex_table,"a" .section __ex_table,"a"
.align 3 .align 3
.long 1001b, 1007b .long 1001b, 1006b
.long 1002b, 1007b .long 1002b, 1006b
.long 1003b, 1007b .long 1003b, 1006b
.long 1004b, 1007b .long 1004b, 1006b
.long 1005b, 1007b
.long 1006b, 1007b
.previous .previous
#define instr r4 #define instr r4
#define reg r5 #define reg r5
#define stack r6 #define stack r6
.Ldumpstm: stmfd sp!, {instr, reg, stack, r7, r8, lr} .Ldumpstm: stmfd sp!, {instr, reg, stack, r7, lr}
mov stack, r0 mov stack, r0
mov instr, r1 mov instr, r1
mov reg, #9 mov reg, #10
mov r7, #0 mov r7, #0
1: mov r3, #1 1: mov r3, #1
tst instr, r3, lsl reg tst instr, r3, lsl reg
beq 2f beq 2f
add r7, r7, #1 add r7, r7, #1
teq r7, #4 teq r7, #6
moveq r7, #0 moveq r7, #1
moveq r3, #'\n' moveq r1, #'\n'
movne r3, #' ' movne r1, #' '
ldr r2, [stack], #-4 ldr r3, [stack], #-4
mov r1, reg mov r2, reg
adr r0, .Lfp adr r0, .Lfp
bl printk bl printk
2: subs reg, reg, #1 2: subs reg, reg, #1
...@@ -140,14 +138,13 @@ ENTRY(c_backtrace) ...@@ -140,14 +138,13 @@ ENTRY(c_backtrace)
teq r7, #0 teq r7, #0
adrne r0, .Lcr adrne r0, .Lcr
blne printk blne printk
mov r0, stack ldmfd sp!, {instr, reg, stack, r7, pc}
ldmfd sp!, {instr, reg, stack, r7, r8, pc}
.Lfp: .asciz " r%d = %08X%c" .Lfp: .asciz "%cr%d:%08x"
.Lcr: .asciz "\n" .Lcr: .asciz "\n"
.Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n" .Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n"
.align .align
.Ldsi: .word 0x00e92dd8 >> 2 .Ldsi: .word 0xe92dd800 >> 10 @ stmfd sp!, {... fp, ip, lr, pc}
.word 0x00e92d00 >> 2 .word 0xe92d0000 >> 10 @ stmfd sp!, {}
#endif #endif
...@@ -438,7 +438,7 @@ hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *) ...@@ -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. * 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) 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); 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,7 +457,7 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
notify_die("", regs, &info, fsr, 0); notify_die("", regs, &info, fsr, 0);
} }
asmlinkage void asmlinkage void __exception
do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
{ {
do_translation_fault(addr, 0, regs); do_translation_fault(addr, 0, regs);
......
...@@ -76,6 +76,8 @@ ...@@ -76,6 +76,8 @@
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/irqflags.h> #include <linux/irqflags.h>
#define __exception __attribute__((section(".exception.text")))
struct thread_info; struct thread_info;
struct task_struct; struct task_struct;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment