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 = {
* 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;
......
......@@ -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;
......
......@@ -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
......
......@@ -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
......@@ -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);
......
......@@ -76,6 +76,8 @@
#include <linux/linkage.h>
#include <linux/irqflags.h>
#define __exception __attribute__((section(".exception.text")))
struct thread_info;
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