1*4882a593Smuzhiyun/* SPDX-License-Identifier: GPL-2.0-only */ 2*4882a593Smuzhiyun/* 3*4882a593Smuzhiyun * linux/arch/arm/lib/backtrace.S 4*4882a593Smuzhiyun * 5*4882a593Smuzhiyun * Copyright (C) 1995, 1996 Russell King 6*4882a593Smuzhiyun * 7*4882a593Smuzhiyun * 27/03/03 Ian Molton Clean up CONFIG_CPU 8*4882a593Smuzhiyun */ 9*4882a593Smuzhiyun#include <linux/kern_levels.h> 10*4882a593Smuzhiyun#include <linux/linkage.h> 11*4882a593Smuzhiyun#include <asm/assembler.h> 12*4882a593Smuzhiyun .text 13*4882a593Smuzhiyun 14*4882a593Smuzhiyun@ fp is 0 or stack frame 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun#define frame r4 17*4882a593Smuzhiyun#define sv_fp r5 18*4882a593Smuzhiyun#define sv_pc r6 19*4882a593Smuzhiyun#define mask r7 20*4882a593Smuzhiyun#define offset r8 21*4882a593Smuzhiyun#define loglvl r9 22*4882a593Smuzhiyun 23*4882a593SmuzhiyunENTRY(c_backtrace) 24*4882a593Smuzhiyun 25*4882a593Smuzhiyun#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) 26*4882a593Smuzhiyun ret lr 27*4882a593SmuzhiyunENDPROC(c_backtrace) 28*4882a593Smuzhiyun#else 29*4882a593Smuzhiyun stmfd sp!, {r4 - r9, lr} @ Save an extra register so we have a location... 30*4882a593Smuzhiyun movs frame, r0 @ if frame pointer is zero 31*4882a593Smuzhiyun beq no_frame @ we have no stack frames 32*4882a593Smuzhiyun mov loglvl, r2 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun tst r1, #0x10 @ 26 or 32-bit mode? 35*4882a593Smuzhiyun ARM( moveq mask, #0xfc000003 ) 36*4882a593Smuzhiyun THUMB( moveq mask, #0xfc000000 ) 37*4882a593Smuzhiyun THUMB( orreq mask, #0x03 ) 38*4882a593Smuzhiyun movne mask, #0 @ mask for 32-bit 39*4882a593Smuzhiyun 40*4882a593Smuzhiyun1: stmfd sp!, {pc} @ calculate offset of PC stored 41*4882a593Smuzhiyun ldr r0, [sp], #4 @ by stmfd for this CPU 42*4882a593Smuzhiyun adr r1, 1b 43*4882a593Smuzhiyun sub offset, r0, r1 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun/* 46*4882a593Smuzhiyun * Stack frame layout: 47*4882a593Smuzhiyun * optionally saved caller registers (r4 - r10) 48*4882a593Smuzhiyun * saved fp 49*4882a593Smuzhiyun * saved sp 50*4882a593Smuzhiyun * saved lr 51*4882a593Smuzhiyun * frame => saved pc 52*4882a593Smuzhiyun * optionally saved arguments (r0 - r3) 53*4882a593Smuzhiyun * saved sp => <next word> 54*4882a593Smuzhiyun * 55*4882a593Smuzhiyun * Functions start with the following code sequence: 56*4882a593Smuzhiyun * mov ip, sp 57*4882a593Smuzhiyun * stmfd sp!, {r0 - r3} (optional) 58*4882a593Smuzhiyun * corrected pc => stmfd sp!, {..., fp, ip, lr, pc} 59*4882a593Smuzhiyun */ 60*4882a593Smuzhiyunfor_each_frame: tst frame, mask @ Check for address exceptions 61*4882a593Smuzhiyun bne no_frame 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun1001: ldr sv_pc, [frame, #0] @ get saved pc 64*4882a593Smuzhiyun1002: ldr sv_fp, [frame, #-12] @ get saved fp 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun sub sv_pc, sv_pc, offset @ Correct PC for prefetching 67*4882a593Smuzhiyun bic sv_pc, sv_pc, mask @ mask PC/LR for the mode 68*4882a593Smuzhiyun 69*4882a593Smuzhiyun1003: ldr r2, [sv_pc, #-4] @ if stmfd sp!, {args} exists, 70*4882a593Smuzhiyun ldr r3, .Ldsi+4 @ adjust saved 'pc' back one 71*4882a593Smuzhiyun teq r3, r2, lsr #11 @ instruction 72*4882a593Smuzhiyun subne r0, sv_pc, #4 @ allow for mov 73*4882a593Smuzhiyun subeq r0, sv_pc, #8 @ allow for mov + stmia 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun ldr r1, [frame, #-4] @ get saved lr 76*4882a593Smuzhiyun mov r2, frame 77*4882a593Smuzhiyun bic r1, r1, mask @ mask PC/LR for the mode 78*4882a593Smuzhiyun mov r3, loglvl 79*4882a593Smuzhiyun bl dump_backtrace_entry 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun ldr r1, [sv_pc, #-4] @ if stmfd sp!, {args} exists, 82*4882a593Smuzhiyun ldr r3, .Ldsi+4 83*4882a593Smuzhiyun teq r3, r1, lsr #11 84*4882a593Smuzhiyun ldreq r0, [frame, #-8] @ get sp 85*4882a593Smuzhiyun subeq r0, r0, #4 @ point at the last arg 86*4882a593Smuzhiyun mov r2, loglvl 87*4882a593Smuzhiyun bleq dump_backtrace_stm @ dump saved registers 88*4882a593Smuzhiyun 89*4882a593Smuzhiyun1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc} 90*4882a593Smuzhiyun ldr r3, .Ldsi @ instruction exists, 91*4882a593Smuzhiyun teq r3, r1, lsr #11 92*4882a593Smuzhiyun subeq r0, frame, #16 93*4882a593Smuzhiyun mov r2, loglvl 94*4882a593Smuzhiyun bleq dump_backtrace_stm @ dump saved registers 95*4882a593Smuzhiyun 96*4882a593Smuzhiyun teq sv_fp, #0 @ zero saved fp means 97*4882a593Smuzhiyun beq no_frame @ no further frames 98*4882a593Smuzhiyun 99*4882a593Smuzhiyun cmp sv_fp, frame @ next frame must be 100*4882a593Smuzhiyun mov frame, sv_fp @ above the current frame 101*4882a593Smuzhiyun bhi for_each_frame 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun1006: adr r0, .Lbad 104*4882a593Smuzhiyun mov r1, loglvl 105*4882a593Smuzhiyun mov r2, frame 106*4882a593Smuzhiyun bl printk 107*4882a593Smuzhiyunno_frame: ldmfd sp!, {r4 - r9, pc} 108*4882a593SmuzhiyunENDPROC(c_backtrace) 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun .pushsection __ex_table,"a" 111*4882a593Smuzhiyun .align 3 112*4882a593Smuzhiyun .long 1001b, 1006b 113*4882a593Smuzhiyun .long 1002b, 1006b 114*4882a593Smuzhiyun .long 1003b, 1006b 115*4882a593Smuzhiyun .long 1004b, 1006b 116*4882a593Smuzhiyun .popsection 117*4882a593Smuzhiyun 118*4882a593Smuzhiyun.Lbad: .asciz "%sBacktrace aborted due to bad frame pointer <%p>\n" 119*4882a593Smuzhiyun .align 120*4882a593Smuzhiyun.Ldsi: .word 0xe92dd800 >> 11 @ stmfd sp!, {... fp, ip, lr, pc} 121*4882a593Smuzhiyun .word 0xe92d0000 >> 11 @ stmfd sp!, {} 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun#endif 124