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