[ARM] Add ability to dump exception stacks to kernel backtraces
authorRussell King <rmk@dyn-67.arm.linux.org.uk>
Fri, 2 Mar 2007 15:01:36 +0000 (15:01 +0000)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sat, 21 Apr 2007 19:34:34 +0000 (20:34 +0100)
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/kernel/irq.c
arch/arm/kernel/traps.c
arch/arm/kernel/vmlinux.lds.S
arch/arm/lib/backtrace.S
arch/arm/mm/fault.c
include/asm-arm/system.h

index e101846..a72b82e 100644 (file)
@@ -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;
index 2409560..ba1c188 100644 (file)
@@ -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;
index ddbdad4..b295f6a 100644 (file)
@@ -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
index 7423008..84dc890 100644 (file)
@@ -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
index 9fd6d2e..fa2d107 100644 (file)
@@ -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);
index 69134c7..63b3080 100644 (file)
@@ -76,6 +76,8 @@
 #include <linux/linkage.h>
 #include <linux/irqflags.h>
 
+#define __exception    __attribute__((section(".exception.text")))
+
 struct thread_info;
 struct task_struct;