GNU Linux-libre 4.19.264-gnu1
[releases.git] / arch / arm / kernel / entry-ftrace.S
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License version 2 as
4  * published by the Free Software Foundation.
5  */
6
7 #include <asm/assembler.h>
8 #include <asm/ftrace.h>
9 #include <asm/unwind.h>
10
11 #include "entry-header.S"
12
13 /*
14  * When compiling with -pg, gcc inserts a call to the mcount routine at the
15  * start of every function.  In mcount, apart from the function's address (in
16  * lr), we need to get hold of the function's caller's address.
17  *
18  * Older GCCs (pre-4.4) inserted a call to a routine called mcount like this:
19  *
20  *      bl      mcount
21  *
22  * These versions have the limitation that in order for the mcount routine to
23  * be able to determine the function's caller's address, an APCS-style frame
24  * pointer (which is set up with something like the code below) is required.
25  *
26  *      mov     ip, sp
27  *      push    {fp, ip, lr, pc}
28  *      sub     fp, ip, #4
29  *
30  * With EABI, these frame pointers are not available unless -mapcs-frame is
31  * specified, and if building as Thumb-2, not even then.
32  *
33  * Newer GCCs (4.4+) solve this problem by introducing a new version of mcount,
34  * with call sites like:
35  *
36  *      push    {lr}
37  *      bl      __gnu_mcount_nc
38  *
39  * With these compilers, frame pointers are not necessary.
40  *
41  * mcount can be thought of as a function called in the middle of a subroutine
42  * call.  As such, it needs to be transparent for both the caller and the
43  * callee: the original lr needs to be restored when leaving mcount, and no
44  * registers should be clobbered.  (In the __gnu_mcount_nc implementation, we
45  * clobber the ip register.  This is OK because the ARM calling convention
46  * allows it to be clobbered in subroutines and doesn't use it to hold
47  * parameters.)
48  *
49  * When using dynamic ftrace, we patch out the mcount call by a "mov r0, r0"
50  * for the mcount case, and a "pop {lr}" for the __gnu_mcount_nc case (see
51  * arch/arm/kernel/ftrace.c).
52  */
53
54 #ifndef CONFIG_OLD_MCOUNT
55 #if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))
56 #error Ftrace requires CONFIG_FRAME_POINTER=y with GCC older than 4.4.0.
57 #endif
58 #endif
59
60 .macro mcount_adjust_addr rd, rn
61         bic     \rd, \rn, #1            @ clear the Thumb bit if present
62         sub     \rd, \rd, #MCOUNT_INSN_SIZE
63 .endm
64
65 .macro __mcount suffix
66         mcount_enter
67         ldr     r0, =ftrace_trace_function
68         ldr     r2, [r0]
69         adr     r0, .Lftrace_stub
70         cmp     r0, r2
71         bne     1f
72
73 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
74         ldr     r1, =ftrace_graph_return
75         ldr     r2, [r1]
76         cmp     r0, r2
77         bne     ftrace_graph_caller\suffix
78
79         ldr     r1, =ftrace_graph_entry
80         ldr     r2, [r1]
81         ldr     r0, =ftrace_graph_entry_stub
82         cmp     r0, r2
83         bne     ftrace_graph_caller\suffix
84 #endif
85
86         mcount_exit
87
88 1:      mcount_get_lr   r1                      @ lr of instrumented func
89         mcount_adjust_addr      r0, lr          @ instrumented function
90         badr    lr, 2f
91         mov     pc, r2
92 2:      mcount_exit
93 .endm
94
95 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
96
97 .macro __ftrace_regs_caller
98
99         sub     sp, sp, #8      @ space for PC and CPSR OLD_R0,
100                                 @ OLD_R0 will overwrite previous LR
101
102         add     ip, sp, #12     @ move in IP the value of SP as it was
103                                 @ before the push {lr} of the mcount mechanism
104
105         str     lr, [sp, #0]    @ store LR instead of PC
106
107         ldr     lr, [sp, #8]    @ get previous LR
108
109         str     r0, [sp, #8]    @ write r0 as OLD_R0 over previous LR
110
111         stmdb   sp!, {ip, lr}
112         stmdb   sp!, {r0-r11, lr}
113
114         @ stack content at this point:
115         @ 0  4          48   52       56            60   64    68       72
116         @ R0 | R1 | ... | LR | SP + 4 | previous LR | LR | PSR | OLD_R0 |
117
118         mov r3, sp                              @ struct pt_regs*
119
120         ldr r2, =function_trace_op
121         ldr r2, [r2]                            @ pointer to the current
122                                                 @ function tracing op
123
124         ldr     r1, [sp, #S_LR]                 @ lr of instrumented func
125
126         ldr     lr, [sp, #S_PC]                 @ get LR
127
128         mcount_adjust_addr      r0, lr          @ instrumented function
129
130         .globl ftrace_regs_call
131 ftrace_regs_call:
132         bl      ftrace_stub
133
134 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
135         .globl ftrace_graph_regs_call
136 ftrace_graph_regs_call:
137         mov     r0, r0
138 #endif
139
140         @ pop saved regs
141         ldmia   sp!, {r0-r12}                   @ restore r0 through r12
142         ldr     ip, [sp, #8]                    @ restore PC
143         ldr     lr, [sp, #4]                    @ restore LR
144         ldr     sp, [sp, #0]                    @ restore SP
145         mov     pc, ip                          @ return
146 .endm
147
148 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
149 .macro __ftrace_graph_regs_caller
150
151         sub     r0, fp, #4              @ lr of instrumented routine (parent)
152
153         @ called from __ftrace_regs_caller
154         ldr     r1, [sp, #S_PC]         @ instrumented routine (func)
155         mcount_adjust_addr      r1, r1
156
157         mov     r2, fp                  @ frame pointer
158         bl      prepare_ftrace_return
159
160         @ pop registers saved in ftrace_regs_caller
161         ldmia   sp!, {r0-r12}                   @ restore r0 through r12
162         ldr     ip, [sp, #8]                    @ restore PC
163         ldr     lr, [sp, #4]                    @ restore LR
164         ldr     sp, [sp, #0]                    @ restore SP
165         mov     pc, ip                          @ return
166
167 .endm
168 #endif
169 #endif
170
171 .macro __ftrace_caller suffix
172         mcount_enter
173
174         mcount_get_lr   r1                      @ lr of instrumented func
175         mcount_adjust_addr      r0, lr          @ instrumented function
176
177 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
178         ldr r2, =function_trace_op
179         ldr r2, [r2]                            @ pointer to the current
180                                                 @ function tracing op
181         mov r3, #0                              @ regs is NULL
182 #endif
183
184         .globl ftrace_call\suffix
185 ftrace_call\suffix:
186         bl      ftrace_stub
187
188 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
189         .globl ftrace_graph_call\suffix
190 ftrace_graph_call\suffix:
191         mov     r0, r0
192 #endif
193
194         mcount_exit
195 .endm
196
197 .macro __ftrace_graph_caller
198         sub     r0, fp, #4              @ &lr of instrumented routine (&parent)
199 #ifdef CONFIG_DYNAMIC_FTRACE
200         @ called from __ftrace_caller, saved in mcount_enter
201         ldr     r1, [sp, #16]           @ instrumented routine (func)
202         mcount_adjust_addr      r1, r1
203 #else
204         @ called from __mcount, untouched in lr
205         mcount_adjust_addr      r1, lr  @ instrumented routine (func)
206 #endif
207         mov     r2, fp                  @ frame pointer
208         bl      prepare_ftrace_return
209         mcount_exit
210 .endm
211
212 #ifdef CONFIG_OLD_MCOUNT
213 /*
214  * mcount
215  */
216
217 .macro mcount_enter
218         stmdb   sp!, {r0-r3, lr}
219 .endm
220
221 .macro mcount_get_lr reg
222         ldr     \reg, [fp, #-4]
223 .endm
224
225 .macro mcount_exit
226         ldr     lr, [fp, #-4]
227         ldmia   sp!, {r0-r3, pc}
228 .endm
229
230 ENTRY(mcount)
231 #ifdef CONFIG_DYNAMIC_FTRACE
232         stmdb   sp!, {lr}
233         ldr     lr, [fp, #-4]
234         ldmia   sp!, {pc}
235 #else
236         __mcount _old
237 #endif
238 ENDPROC(mcount)
239
240 #ifdef CONFIG_DYNAMIC_FTRACE
241 ENTRY(ftrace_caller_old)
242         __ftrace_caller _old
243 ENDPROC(ftrace_caller_old)
244 #endif
245
246 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
247 ENTRY(ftrace_graph_caller_old)
248         __ftrace_graph_caller
249 ENDPROC(ftrace_graph_caller_old)
250 #endif
251
252 .purgem mcount_enter
253 .purgem mcount_get_lr
254 .purgem mcount_exit
255 #endif
256
257 /*
258  * __gnu_mcount_nc
259  */
260
261 .macro mcount_enter
262 /*
263  * This pad compensates for the push {lr} at the call site.  Note that we are
264  * unable to unwind through a function which does not otherwise save its lr.
265  */
266  UNWIND(.pad    #4)
267         stmdb   sp!, {r0-r3, lr}
268  UNWIND(.save   {r0-r3, lr})
269 .endm
270
271 .macro mcount_get_lr reg
272         ldr     \reg, [sp, #20]
273 .endm
274
275 .macro mcount_exit
276         ldmia   sp!, {r0-r3, ip, lr}
277         ret     ip
278 .endm
279
280 ENTRY(__gnu_mcount_nc)
281 UNWIND(.fnstart)
282 #ifdef CONFIG_DYNAMIC_FTRACE
283         mov     ip, lr
284         ldmia   sp!, {lr}
285         ret     ip
286 #else
287         __mcount
288 #endif
289 UNWIND(.fnend)
290 ENDPROC(__gnu_mcount_nc)
291
292 #ifdef CONFIG_DYNAMIC_FTRACE
293 ENTRY(ftrace_caller)
294 UNWIND(.fnstart)
295         __ftrace_caller
296 UNWIND(.fnend)
297 ENDPROC(ftrace_caller)
298
299 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
300 ENTRY(ftrace_regs_caller)
301 UNWIND(.fnstart)
302         __ftrace_regs_caller
303 UNWIND(.fnend)
304 ENDPROC(ftrace_regs_caller)
305 #endif
306
307 #endif
308
309 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
310 ENTRY(ftrace_graph_caller)
311 UNWIND(.fnstart)
312         __ftrace_graph_caller
313 UNWIND(.fnend)
314 ENDPROC(ftrace_graph_caller)
315
316 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
317 ENTRY(ftrace_graph_regs_caller)
318 UNWIND(.fnstart)
319         __ftrace_graph_regs_caller
320 UNWIND(.fnend)
321 ENDPROC(ftrace_graph_regs_caller)
322 #endif
323 #endif
324
325 .purgem mcount_enter
326 .purgem mcount_get_lr
327 .purgem mcount_exit
328
329 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
330         .globl return_to_handler
331 return_to_handler:
332         stmdb   sp!, {r0-r3}
333         mov     r0, fp                  @ frame pointer
334         bl      ftrace_return_to_handler
335         mov     lr, r0                  @ r0 has real ret addr
336         ldmia   sp!, {r0-r3}
337         ret     lr
338 #endif
339
340 ENTRY(ftrace_stub)
341 .Lftrace_stub:
342         ret     lr
343 ENDPROC(ftrace_stub)