Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Russell King and committed by
Russell King
7ab3f8d5 46fcc86d

+101 -85
+1 -1
arch/arm/kernel/irq.c
··· 109 109 * come via this function. Instead, they should provide their 110 110 * own 'handler' 111 111 */ 112 - asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) 112 + asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) 113 113 { 114 114 struct pt_regs *old_regs = set_irq_regs(regs); 115 115 struct irq_desc *desc = irq_desc + irq;
+16 -2
arch/arm/kernel/traps.c
··· 45 45 __setup("user_debug=", user_debug_setup); 46 46 #endif 47 47 48 - void dump_backtrace_entry(unsigned long where, unsigned long from) 48 + static void dump_mem(const char *str, unsigned long bottom, unsigned long top); 49 + 50 + static inline int in_exception_text(unsigned long ptr) 51 + { 52 + extern char __exception_text_start[]; 53 + extern char __exception_text_end[]; 54 + 55 + return ptr >= (unsigned long)&__exception_text_start && 56 + ptr < (unsigned long)&__exception_text_end; 57 + } 58 + 59 + void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame) 49 60 { 50 61 #ifdef CONFIG_KALLSYMS 51 62 printk("[<%08lx>] ", where); ··· 66 55 #else 67 56 printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); 68 57 #endif 58 + 59 + if (in_exception_text(where)) 60 + dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); 69 61 } 70 62 71 63 /* ··· 280 266 spin_unlock_irqrestore(&undef_lock, flags); 281 267 } 282 268 283 - asmlinkage void do_undefinstr(struct pt_regs *regs) 269 + asmlinkage void __exception do_undefinstr(struct pt_regs *regs) 284 270 { 285 271 unsigned int correction = thumb_mode(regs) ? 2 : 4; 286 272 unsigned int instr;
+3
arch/arm/kernel/vmlinux.lds.S
··· 83 83 84 84 .text : { /* Real text segment */ 85 85 _text = .; /* Text and read-only data */ 86 + __exception_text_start = .; 87 + *(.exception.text) 88 + __exception_text_end = .; 86 89 *(.text) 87 90 SCHED_TEXT 88 91 LOCK_TEXT
+77 -80
arch/arm/lib/backtrace.S
··· 17 17 @ fp is 0 or stack frame 18 18 19 19 #define frame r4 20 - #define next r5 21 - #define save r6 20 + #define sv_fp r5 21 + #define sv_pc r6 22 22 #define mask r7 23 23 #define offset r8 24 24 ··· 31 31 #if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) 32 32 mov pc, lr 33 33 #else 34 - 35 34 stmfd sp!, {r4 - r8, lr} @ Save an extra register so we have a location... 36 - tst r1, #0x10 @ 26 or 32-bit? 37 - moveq mask, #0xfc000003 38 - movne mask, #0 39 - tst mask, r0 40 - movne r0, #0 41 - movs frame, r0 42 - 1: moveq r0, #-2 43 - ldmeqfd sp!, {r4 - r8, pc} 35 + movs frame, r0 @ if frame pointer is zero 36 + beq no_frame @ we have no stack frames 44 37 45 - 2: stmfd sp!, {pc} @ calculate offset of PC in STMIA instruction 46 - ldr r0, [sp], #4 47 - adr r1, 2b - 4 38 + tst r1, #0x10 @ 26 or 32-bit mode? 39 + moveq mask, #0xfc000003 @ mask for 26-bit 40 + movne mask, #0 @ mask for 32-bit 41 + 42 + 1: stmfd sp!, {pc} @ calculate offset of PC stored 43 + ldr r0, [sp], #4 @ by stmfd for this CPU 44 + adr r1, 1b 48 45 sub offset, r0, r1 49 46 50 - 3: tst frame, mask @ Check for address exceptions... 51 - bne 1b 47 + /* 48 + * Stack frame layout: 49 + * optionally saved caller registers (r4 - r10) 50 + * saved fp 51 + * saved sp 52 + * saved lr 53 + * frame => saved pc 54 + * optionally saved arguments (r0 - r3) 55 + * saved sp => <next word> 56 + * 57 + * Functions start with the following code sequence: 58 + * mov ip, sp 59 + * stmfd sp!, {r0 - r3} (optional) 60 + * corrected pc => stmfd sp!, {..., fp, ip, lr, pc} 61 + */ 62 + for_each_frame: tst frame, mask @ Check for address exceptions 63 + bne no_frame 52 64 53 - 1001: ldr next, [frame, #-12] @ get fp 54 - 1002: ldr r2, [frame, #-4] @ get lr 55 - 1003: ldr r3, [frame, #0] @ get pc 56 - sub save, r3, offset @ Correct PC for prefetching 57 - bic save, save, mask 58 - 1004: ldr r1, [save, #0] @ get instruction at function 59 - mov r1, r1, lsr #10 60 - ldr r3, .Ldsi+4 61 - teq r1, r3 62 - subeq save, save, #4 63 - mov r0, save 64 - bic r1, r2, mask 65 + 1001: ldr sv_pc, [frame, #0] @ get saved pc 66 + 1002: ldr sv_fp, [frame, #-12] @ get saved fp 67 + 68 + sub sv_pc, sv_pc, offset @ Correct PC for prefetching 69 + bic sv_pc, sv_pc, mask @ mask PC/LR for the mode 70 + 71 + 1003: ldr r2, [sv_pc, #-4] @ if stmfd sp!, {args} exists, 72 + ldr r3, .Ldsi+4 @ adjust saved 'pc' back one 73 + teq r3, r2, lsr #10 @ instruction 74 + subne r0, sv_pc, #4 @ allow for mov 75 + subeq r0, sv_pc, #8 @ allow for mov + stmia 76 + 77 + ldr r1, [frame, #-4] @ get saved lr 78 + mov r2, frame 79 + bic r1, r1, mask @ mask PC/LR for the mode 65 80 bl dump_backtrace_entry 66 81 67 - ldr r0, [frame, #-8] @ get sp 68 - sub r0, r0, #4 69 - 1005: ldr r1, [save, #4] @ get instruction at function+4 70 - mov r3, r1, lsr #10 71 - ldr r2, .Ldsi+4 72 - teq r3, r2 @ Check for stmia sp!, {args} 73 - addeq save, save, #4 @ next instruction 74 - bleq .Ldumpstm 82 + ldr r1, [sv_pc, #-4] @ if stmfd sp!, {args} exists, 83 + ldr r3, .Ldsi+4 84 + teq r3, r1, lsr #10 85 + ldreq r0, [frame, #-8] @ get sp 86 + subeq r0, r0, #4 @ point at the last arg 87 + bleq .Ldumpstm @ dump saved registers 75 88 76 - sub r0, frame, #16 77 - 1006: ldr r1, [save, #4] @ Get 'stmia sp!, {rlist, fp, ip, lr, pc}' instruction 78 - mov r3, r1, lsr #10 79 - ldr r2, .Ldsi 80 - teq r3, r2 81 - bleq .Ldumpstm 89 + 1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc} 90 + ldr r3, .Ldsi @ instruction exists, 91 + teq r3, r1, lsr #10 92 + subeq r0, frame, #16 93 + bleq .Ldumpstm @ dump saved registers 82 94 83 - /* 84 - * A zero next framepointer means we're done. 85 - */ 86 - teq next, #0 87 - ldmeqfd sp!, {r4 - r8, pc} 95 + teq sv_fp, #0 @ zero saved fp means 96 + beq no_frame @ no further frames 88 97 89 - /* 90 - * The next framepointer must be above the 91 - * current framepointer. 92 - */ 93 - cmp next, frame 94 - mov frame, next 95 - bhi 3b 96 - b 1007f 98 + cmp sv_fp, frame @ next frame must be 99 + mov frame, sv_fp @ above the current frame 100 + bhi for_each_frame 97 101 98 - /* 99 - * Fixup for LDMDB. Note that this must not be in the fixup section. 100 - */ 101 - 1007: ldr r0, =.Lbad 102 + 1006: adr r0, .Lbad 102 103 mov r1, frame 103 104 bl printk 104 - ldmfd sp!, {r4 - r8, pc} 105 - .ltorg 105 + no_frame: ldmfd sp!, {r4 - r8, pc} 106 106 107 107 .section __ex_table,"a" 108 108 .align 3 109 - .long 1001b, 1007b 110 - .long 1002b, 1007b 111 - .long 1003b, 1007b 112 - .long 1004b, 1007b 113 - .long 1005b, 1007b 114 - .long 1006b, 1007b 109 + .long 1001b, 1006b 110 + .long 1002b, 1006b 111 + .long 1003b, 1006b 112 + .long 1004b, 1006b 115 113 .previous 116 114 117 115 #define instr r4 118 116 #define reg r5 119 117 #define stack r6 120 118 121 - .Ldumpstm: stmfd sp!, {instr, reg, stack, r7, r8, lr} 119 + .Ldumpstm: stmfd sp!, {instr, reg, stack, r7, lr} 122 120 mov stack, r0 123 121 mov instr, r1 124 - mov reg, #9 122 + mov reg, #10 125 123 mov r7, #0 126 124 1: mov r3, #1 127 125 tst instr, r3, lsl reg 128 126 beq 2f 129 127 add r7, r7, #1 130 - teq r7, #4 131 - moveq r7, #0 132 - moveq r3, #'\n' 133 - movne r3, #' ' 134 - ldr r2, [stack], #-4 135 - mov r1, reg 128 + teq r7, #6 129 + moveq r7, #1 130 + moveq r1, #'\n' 131 + movne r1, #' ' 132 + ldr r3, [stack], #-4 133 + mov r2, reg 136 134 adr r0, .Lfp 137 135 bl printk 138 136 2: subs reg, reg, #1 ··· 138 140 teq r7, #0 139 141 adrne r0, .Lcr 140 142 blne printk 141 - mov r0, stack 142 - ldmfd sp!, {instr, reg, stack, r7, r8, pc} 143 + ldmfd sp!, {instr, reg, stack, r7, pc} 143 144 144 - .Lfp: .asciz " r%d = %08X%c" 145 + .Lfp: .asciz "%cr%d:%08x" 145 146 .Lcr: .asciz "\n" 146 147 .Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n" 147 148 .align 148 - .Ldsi: .word 0x00e92dd8 >> 2 149 - .word 0x00e92d00 >> 2 149 + .Ldsi: .word 0xe92dd800 >> 10 @ stmfd sp!, {... fp, ip, lr, pc} 150 + .word 0xe92d0000 >> 10 @ stmfd sp!, {} 150 151 151 152 #endif
+2 -2
arch/arm/mm/fault.c
··· 438 438 /* 439 439 * Dispatch a data abort to the relevant handler. 440 440 */ 441 - asmlinkage void 441 + asmlinkage void __exception 442 442 do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 443 443 { 444 444 const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6); ··· 457 457 notify_die("", regs, &info, fsr, 0); 458 458 } 459 459 460 - asmlinkage void 460 + asmlinkage void __exception 461 461 do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) 462 462 { 463 463 do_translation_fault(addr, 0, regs);
+2
include/asm-arm/system.h
··· 76 76 #include <linux/linkage.h> 77 77 #include <linux/irqflags.h> 78 78 79 + #define __exception __attribute__((section(".exception.text"))) 80 + 79 81 struct thread_info; 80 82 struct task_struct; 81 83