1/* SPDX-License-Identifier: GPL-2.0-only */ 2 3#include <asm/assembler.h> 4#include <asm/ftrace.h> 5#include <asm/unwind.h> 6 7#include "entry-header.S" 8 9/* 10 * When compiling with -pg, gcc inserts a call to the mcount routine at the 11 * start of every function. In mcount, apart from the function's address (in 12 * lr), we need to get hold of the function's caller's address. 13 * 14 * Newer GCCs (4.4+) solve this problem by using a version of mcount with call 15 * sites like: 16 * 17 * push {lr} 18 * bl __gnu_mcount_nc 19 * 20 * With these compilers, frame pointers are not necessary. 21 * 22 * mcount can be thought of as a function called in the middle of a subroutine 23 * call. As such, it needs to be transparent for both the caller and the 24 * callee: the original lr needs to be restored when leaving mcount, and no 25 * registers should be clobbered. 26 * 27 * When using dynamic ftrace, we patch out the mcount call by a "pop {lr}" 28 * instead of the __gnu_mcount_nc call (see arch/arm/kernel/ftrace.c). 29 */ 30 31.macro mcount_adjust_addr rd, rn 32 bic \rd, \rn, #1 @ clear the Thumb bit if present 33 sub \rd, \rd, #MCOUNT_INSN_SIZE 34.endm 35 36.macro __mcount suffix 37 mcount_enter 38 ldr r0, =ftrace_trace_function 39 ldr r2, [r0] 40 adr r0, .Lftrace_stub 41 cmp r0, r2 42 bne 1f 43 44#ifdef CONFIG_FUNCTION_GRAPH_TRACER 45 ldr r1, =ftrace_graph_return 46 ldr r2, [r1] 47 cmp r0, r2 48 bne ftrace_graph_caller\suffix 49 50 ldr r1, =ftrace_graph_entry 51 ldr r2, [r1] 52 ldr r0, =ftrace_graph_entry_stub 53 cmp r0, r2 54 bne ftrace_graph_caller\suffix 55#endif 56 57 mcount_exit 58 591: mcount_get_lr r1 @ lr of instrumented func 60 mcount_adjust_addr r0, lr @ instrumented function 61 badr lr, 2f 62 mov pc, r2 632: mcount_exit 64.endm 65 66#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 67 68.macro __ftrace_regs_caller 69 70 str lr, [sp, #-8]! @ store LR as PC and make space for CPSR/OLD_R0, 71 @ OLD_R0 will overwrite previous LR 72 73 ldr lr, [sp, #8] @ get previous LR 74 75 str r0, [sp, #8] @ write r0 as OLD_R0 over previous LR 76 77 str lr, [sp, #-4]! @ store previous LR as LR 78 79 add lr, sp, #16 @ move in LR the value of SP as it was 80 @ before the push {lr} of the mcount mechanism 81 82 push {r0-r11, ip, lr} 83 84 @ stack content at this point: 85 @ 0 4 48 52 56 60 64 68 72 86 @ R0 | R1 | ... | IP | SP + 4 | previous LR | LR | PSR | OLD_R0 | 87 88 mov r3, sp @ struct pt_regs* 89 90 ldr r2, =function_trace_op 91 ldr r2, [r2] @ pointer to the current 92 @ function tracing op 93 94 ldr r1, [sp, #S_LR] @ lr of instrumented func 95 96 ldr lr, [sp, #S_PC] @ get LR 97 98 mcount_adjust_addr r0, lr @ instrumented function 99 100 .globl ftrace_regs_call 101ftrace_regs_call: 102 bl ftrace_stub 103 104#ifdef CONFIG_FUNCTION_GRAPH_TRACER 105 .globl ftrace_graph_regs_call 106ftrace_graph_regs_call: 107 mov r0, r0 108#endif 109 110 @ pop saved regs 111 pop {r0-r11, ip, lr} @ restore r0 through r12 112 ldr lr, [sp], #4 @ restore LR 113 ldr pc, [sp], #12 114.endm 115 116#ifdef CONFIG_FUNCTION_GRAPH_TRACER 117.macro __ftrace_graph_regs_caller 118 119 sub r0, fp, #4 @ lr of instrumented routine (parent) 120 121 @ called from __ftrace_regs_caller 122 ldr r1, [sp, #S_PC] @ instrumented routine (func) 123 mcount_adjust_addr r1, r1 124 125 mov r2, fp @ frame pointer 126 bl prepare_ftrace_return 127 128 @ pop registers saved in ftrace_regs_caller 129 pop {r0-r11, ip, lr} @ restore r0 through r12 130 ldr lr, [sp], #4 @ restore LR 131 ldr pc, [sp], #12 132 133.endm 134#endif 135#endif 136 137.macro __ftrace_caller suffix 138 mcount_enter 139 140 mcount_get_lr r1 @ lr of instrumented func 141 mcount_adjust_addr r0, lr @ instrumented function 142 143#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 144 ldr r2, =function_trace_op 145 ldr r2, [r2] @ pointer to the current 146 @ function tracing op 147 mov r3, #0 @ regs is NULL 148#endif 149 150 .globl ftrace_call\suffix 151ftrace_call\suffix: 152 bl ftrace_stub 153 154#ifdef CONFIG_FUNCTION_GRAPH_TRACER 155 .globl ftrace_graph_call\suffix 156ftrace_graph_call\suffix: 157 mov r0, r0 158#endif 159 160 mcount_exit 161.endm 162 163.macro __ftrace_graph_caller 164 sub r0, fp, #4 @ &lr of instrumented routine (&parent) 165#ifdef CONFIG_DYNAMIC_FTRACE 166 @ called from __ftrace_caller, saved in mcount_enter 167 ldr r1, [sp, #16] @ instrumented routine (func) 168 mcount_adjust_addr r1, r1 169#else 170 @ called from __mcount, untouched in lr 171 mcount_adjust_addr r1, lr @ instrumented routine (func) 172#endif 173 mov r2, fp @ frame pointer 174 bl prepare_ftrace_return 175 mcount_exit 176.endm 177 178/* 179 * __gnu_mcount_nc 180 */ 181 182.macro mcount_enter 183/* 184 * This pad compensates for the push {lr} at the call site. Note that we are 185 * unable to unwind through a function which does not otherwise save its lr. 186 */ 187 UNWIND(.pad #4) 188 stmdb sp!, {r0-r3, lr} 189 UNWIND(.save {r0-r3, lr}) 190.endm 191 192.macro mcount_get_lr reg 193 ldr \reg, [sp, #20] 194.endm 195 196.macro mcount_exit 197 ldmia sp!, {r0-r3} 198 ldr lr, [sp, #4] 199 ldr pc, [sp], #8 200.endm 201 202ENTRY(__gnu_mcount_nc) 203UNWIND(.fnstart) 204#ifdef CONFIG_DYNAMIC_FTRACE 205 push {lr} 206 ldr lr, [sp, #4] 207 ldr pc, [sp], #8 208#else 209 __mcount 210#endif 211UNWIND(.fnend) 212ENDPROC(__gnu_mcount_nc) 213 214#ifdef CONFIG_DYNAMIC_FTRACE 215ENTRY(ftrace_caller) 216UNWIND(.fnstart) 217 __ftrace_caller 218UNWIND(.fnend) 219ENDPROC(ftrace_caller) 220 221#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 222ENTRY(ftrace_regs_caller) 223UNWIND(.fnstart) 224 __ftrace_regs_caller 225UNWIND(.fnend) 226ENDPROC(ftrace_regs_caller) 227#endif 228 229#endif 230 231#ifdef CONFIG_FUNCTION_GRAPH_TRACER 232ENTRY(ftrace_graph_caller) 233UNWIND(.fnstart) 234 __ftrace_graph_caller 235UNWIND(.fnend) 236ENDPROC(ftrace_graph_caller) 237 238#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 239ENTRY(ftrace_graph_regs_caller) 240UNWIND(.fnstart) 241 __ftrace_graph_regs_caller 242UNWIND(.fnend) 243ENDPROC(ftrace_graph_regs_caller) 244#endif 245#endif 246 247.purgem mcount_enter 248.purgem mcount_get_lr 249.purgem mcount_exit 250 251#ifdef CONFIG_FUNCTION_GRAPH_TRACER 252 .globl return_to_handler 253return_to_handler: 254 stmdb sp!, {r0-r3} 255 mov r0, fp @ frame pointer 256 bl ftrace_return_to_handler 257 mov lr, r0 @ r0 has real ret addr 258 ldmia sp!, {r0-r3} 259 ret lr 260#endif 261 262ENTRY(ftrace_stub) 263.Lftrace_stub: 264 ret lr 265ENDPROC(ftrace_stub) 266