mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	Recent kernels have changed their behaviour to be more inconsistent when handling printk continuations. With todays kernels, the output looks sane on the console, but dmesg splits individual printk()s which do not have the KERN_CONT prefix into separate lines. Since the assembly code is not trivial to add the KERN_CONT, and we ideally want to avoid using KERN_CONT (as multiple printk()s can race between different threads), convert the assembly dumping the register values to C code, and have the C code build the output a line at a time before dumping to the console. This avoids the KERN_CONT issue, and also avoids situations where the output is intermixed with other console activity. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
		
			
				
	
	
		
			121 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
/*
 | 
						|
 *  linux/arch/arm/lib/backtrace.S
 | 
						|
 *
 | 
						|
 *  Copyright (C) 1995, 1996 Russell King
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License version 2 as
 | 
						|
 * published by the Free Software Foundation.
 | 
						|
 *
 | 
						|
 * 27/03/03 Ian Molton Clean up CONFIG_CPU
 | 
						|
 *
 | 
						|
 */
 | 
						|
#include <linux/kern_levels.h>
 | 
						|
#include <linux/linkage.h>
 | 
						|
#include <asm/assembler.h>
 | 
						|
		.text
 | 
						|
 | 
						|
@ fp is 0 or stack frame
 | 
						|
 | 
						|
#define frame	r4
 | 
						|
#define sv_fp	r5
 | 
						|
#define sv_pc	r6
 | 
						|
#define mask	r7
 | 
						|
#define offset	r8
 | 
						|
 | 
						|
ENTRY(c_backtrace)
 | 
						|
 | 
						|
#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK)
 | 
						|
		ret	lr
 | 
						|
ENDPROC(c_backtrace)
 | 
						|
#else
 | 
						|
		stmfd	sp!, {r4 - r8, lr}	@ Save an extra register so we have a location...
 | 
						|
		movs	frame, r0		@ if frame pointer is zero
 | 
						|
		beq	no_frame		@ we have no stack frames
 | 
						|
 | 
						|
		tst	r1, #0x10		@ 26 or 32-bit mode?
 | 
						|
 ARM(		moveq	mask, #0xfc000003	)
 | 
						|
 THUMB(		moveq	mask, #0xfc000000	)
 | 
						|
 THUMB(		orreq	mask, #0x03		)
 | 
						|
		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
 | 
						|
 | 
						|
/*
 | 
						|
 * 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
 | 
						|
 | 
						|
		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	r1, [sv_pc, #-4]	@ if stmfd sp!, {args} exists,
 | 
						|
		ldr	r3, .Ldsi+4
 | 
						|
		teq	r3, r1, lsr #11
 | 
						|
		ldreq	r0, [frame, #-8]	@ get sp
 | 
						|
		subeq	r0, r0, #4		@ point at the last arg
 | 
						|
		bleq	dump_backtrace_stm	@ dump saved registers
 | 
						|
 | 
						|
1004:		ldr	r1, [sv_pc, #0]		@ if stmfd sp!, {..., fp, ip, lr, pc}
 | 
						|
		ldr	r3, .Ldsi		@ instruction exists,
 | 
						|
		teq	r3, r1, lsr #11
 | 
						|
		subeq	r0, frame, #16
 | 
						|
		bleq	dump_backtrace_stm	@ 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
 | 
						|
no_frame:	ldmfd	sp!, {r4 - r8, pc}
 | 
						|
ENDPROC(c_backtrace)
 | 
						|
		
 | 
						|
		.pushsection __ex_table,"a"
 | 
						|
		.align	3
 | 
						|
		.long	1001b, 1006b
 | 
						|
		.long	1002b, 1006b
 | 
						|
		.long	1003b, 1006b
 | 
						|
		.long	1004b, 1006b
 | 
						|
		.popsection
 | 
						|
 | 
						|
.Lbad:		.asciz	"Backtrace aborted due to bad frame pointer <%p>\n"
 | 
						|
		.align
 | 
						|
.Ldsi:		.word	0xe92dd800 >> 11	@ stmfd sp!, {... fp, ip, lr, pc}
 | 
						|
		.word	0xe92d0000 >> 11	@ stmfd sp!, {}
 | 
						|
 | 
						|
#endif
 |