1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <asm/assembler.h>
4 #include <asm/ftrace.h>
5 #include <asm/unwind.h>
7 #include "entry-header.S"
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.
14 * Newer GCCs (4.4+) solve this problem by using a version of mcount with call
20 * With these compilers, frame pointers are not necessary.
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.
27 * When using dynamic ftrace, we patch out the mcount call by a "add sp, #4"
28 * instead of the __gnu_mcount_nc call (see arch/arm/kernel/ftrace.c).
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
36 .macro __mcount suffix
38 ldr_va r2, ftrace_trace_function
39 badr r0, .Lftrace_stub
43 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
44 ldr_va r2, ftrace_graph_return
46 bne ftrace_graph_caller\suffix
48 ldr_va r2, ftrace_graph_entry
49 mov_l r0, ftrace_graph_entry_stub
51 bne ftrace_graph_caller\suffix
56 1: mcount_get_lr r1 @ lr of instrumented func
57 mcount_adjust_addr r0, lr @ instrumented function
63 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
65 .macro __ftrace_regs_caller
67 str lr, [sp, #-8]! @ store LR as PC and make space for CPSR/OLD_R0,
68 @ OLD_R0 will overwrite previous LR
70 ldr lr, [sp, #8] @ get previous LR
72 str r0, [sp, #8] @ write r0 as OLD_R0 over previous LR
74 str lr, [sp, #-4]! @ store previous LR as LR
76 add lr, sp, #16 @ move in LR the value of SP as it was
77 @ before the push {lr} of the mcount mechanism
81 @ stack content at this point:
82 @ 0 4 48 52 56 60 64 68 72
83 @ R0 | R1 | ... | IP | SP + 4 | previous LR | LR | PSR | OLD_R0 |
85 mov r3, sp @ struct pt_regs*
87 ldr_va r2, function_trace_op @ pointer to the current
90 ldr r1, [sp, #S_LR] @ lr of instrumented func
92 ldr lr, [sp, #S_PC] @ get LR
94 mcount_adjust_addr r0, lr @ instrumented function
96 .globl ftrace_regs_call
100 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
101 .globl ftrace_graph_regs_call
102 ftrace_graph_regs_call:
108 pop {r0-r11, ip, lr} @ restore r0 through r12
109 ldr lr, [sp], #4 @ restore LR
113 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
114 .macro __ftrace_graph_regs_caller
116 #ifdef CONFIG_UNWINDER_FRAME_POINTER
117 sub r0, fp, #4 @ lr of instrumented routine (parent)
122 @ called from __ftrace_regs_caller
123 ldr r1, [sp, #S_PC] @ instrumented routine (func)
124 mcount_adjust_addr r1, r1
126 mov r2, fpreg @ frame pointer
127 add r3, sp, #PT_REGS_SIZE
128 bl prepare_ftrace_return
130 @ pop registers saved in ftrace_regs_caller
131 pop {r0-r11, ip, lr} @ restore r0 through r12
132 ldr lr, [sp], #4 @ restore LR
139 .macro __ftrace_caller suffix
142 mcount_get_lr r1 @ lr of instrumented func
143 mcount_adjust_addr r0, lr @ instrumented function
145 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
146 ldr_va r2, function_trace_op @ pointer to the current
147 @ function tracing op
148 mov r3, #0 @ regs is NULL
151 .globl ftrace_call\suffix
155 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
156 .globl ftrace_graph_call\suffix
157 ftrace_graph_call\suffix:
165 .macro __ftrace_graph_caller
166 #ifdef CONFIG_UNWINDER_FRAME_POINTER
167 sub r0, fp, #4 @ &lr of instrumented routine (&parent)
171 #ifdef CONFIG_DYNAMIC_FTRACE
172 @ called from __ftrace_caller, saved in mcount_enter
173 ldr r1, [sp, #16] @ instrumented routine (func)
174 mcount_adjust_addr r1, r1
176 @ called from __mcount, untouched in lr
177 mcount_adjust_addr r1, lr @ instrumented routine (func)
179 mov r2, fpreg @ frame pointer
181 bl prepare_ftrace_return
191 * This pad compensates for the push {lr} at the call site. Note that we are
192 * unable to unwind through a function which does not otherwise save its lr.
195 stmdb sp!, {r0-r3, lr}
196 UNWIND(.save {r0-r3, lr})
199 .macro mcount_get_lr reg
209 ENTRY(__gnu_mcount_nc)
211 #ifdef CONFIG_DYNAMIC_FTRACE
219 ENDPROC(__gnu_mcount_nc)
221 #ifdef CONFIG_DYNAMIC_FTRACE
226 ENDPROC(ftrace_caller)
228 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
229 ENTRY(ftrace_regs_caller)
233 ENDPROC(ftrace_regs_caller)
238 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
239 ENTRY(ftrace_graph_caller)
241 __ftrace_graph_caller
243 ENDPROC(ftrace_graph_caller)
245 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
246 ENTRY(ftrace_graph_regs_caller)
248 __ftrace_graph_regs_caller
250 ENDPROC(ftrace_graph_regs_caller)
255 .purgem mcount_get_lr
258 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
259 ENTRY(return_to_handler)
261 add r0, sp, #16 @ sp at exit of instrumented routine
262 bl ftrace_return_to_handler
263 mov lr, r0 @ r0 has real ret addr
266 ENDPROC(return_to_handler)
274 ENTRY(ftrace_stub_graph)
276 ENDPROC(ftrace_stub_graph)
278 #ifdef CONFIG_DYNAMIC_FTRACE
282 .macro init_tramp, dst:req
283 ENTRY(\dst\()_from_init)
285 ENDPROC(\dst\()_from_init)
288 init_tramp ftrace_caller
289 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
290 init_tramp ftrace_regs_caller