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