Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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

parent 46fcc86d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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;
+16 −2
Original line number Diff line number Diff line
@@ -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;
+3 −0
Original line number Diff line number Diff line
@@ -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
+81 −84
Original line number Diff line number Diff line
@@ -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	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
1001:		ldr	sv_pc, [frame, #0]	@ get saved pc
1002:		ldr	sv_fp, [frame, #-12]	@ get saved fp

		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
		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

		/*
		 * 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}
		ldr	r3, .Ldsi		@ instruction exists,
		teq	r3, r1, lsr #10
		subeq	r0, frame, #16
		bleq	.Ldumpstm		@ dump saved registers

		/*
		 * The next framepointer must be above the
		 * current framepointer.
		 */
		cmp	next, frame
		mov	frame, next
		bhi	3b
		b	1007f
		teq	sv_fp, #0		@ zero saved fp means
		beq	no_frame		@ no further frames

/*
 * Fixup for LDMDB.  Note that this must not be in the fixup section.
 */
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
		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
+2 −2
Original line number Diff line number Diff line
@@ -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);
Loading