root/arch/arm/lib/backtrace.S
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/backtrace.S
 *
 *  Copyright (C) 1995, 1996 Russell King
 *
 * 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
#define loglvl  r9

ENTRY(c_backtrace)

#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK)
                ret     lr
ENDPROC(c_backtrace)
#else
                stmfd   sp!, {r4 - r9, 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
                mov     loglvl, r2

                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 #11         @ 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
                mov     r3, loglvl
                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
                mov     r2, loglvl
                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
                mov     r2, loglvl
                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
#ifdef CONFIG_IRQSTACKS
                @
                @ Kernel stacks may be discontiguous in memory. If the next
                @ frame is below the previous frame, accept it as long as it
                @ lives in kernel memory.
                @
                cmpls   sv_fp, #PAGE_OFFSET
#endif
                bhi     for_each_frame

1006:           adr     r0, .Lbad
                mov     r1, loglvl
                mov     r2, frame
                bl      _printk
no_frame:       ldmfd   sp!, {r4 - r9, 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  "%sBacktrace 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