From 7ab3f8d595a1b1e5cf8d726b72fd476fe0d0226c Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 2 Mar 2007 15:01:36 +0000 Subject: [PATCH] [ARM] Add ability to dump exception stacks to kernel backtraces Signed-off-by: Russell King --- arch/arm/kernel/irq.c | 2 +- arch/arm/kernel/traps.c | 18 ++++- arch/arm/kernel/vmlinux.lds.S | 3 + arch/arm/lib/backtrace.S | 165 +++++++++++++++++++++--------------------- arch/arm/mm/fault.c | 4 +- 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 e101846..a72b82e 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 2409560..ba1c188 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 ddbdad4..b295f6a 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 7423008..84dc890 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 => + * + * 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 9fd6d2e..fa2d107 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 69134c7..63b3080 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -76,6 +76,8 @@ #include #include +#define __exception __attribute__((section(".exception.text"))) + struct thread_info; struct task_struct; -- 2.7.4