Commit | Line | Data |
---|---|---|
82112379 RK |
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 | |
14327c66 | 90 | badr lr, 2f |
82112379 RK |
91 | mov pc, r2 |
92 | 2: mcount_exit | |
93 | .endm | |
94 | ||
95 | .macro __ftrace_caller suffix | |
96 | mcount_enter | |
97 | ||
98 | mcount_get_lr r1 @ lr of instrumented func | |
99 | mcount_adjust_addr r0, lr @ instrumented function | |
100 | ||
101 | .globl ftrace_call\suffix | |
102 | ftrace_call\suffix: | |
103 | bl ftrace_stub | |
104 | ||
105 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
106 | .globl ftrace_graph_call\suffix | |
107 | ftrace_graph_call\suffix: | |
108 | mov r0, r0 | |
109 | #endif | |
110 | ||
111 | mcount_exit | |
112 | .endm | |
113 | ||
114 | .macro __ftrace_graph_caller | |
115 | sub r0, fp, #4 @ &lr of instrumented routine (&parent) | |
116 | #ifdef CONFIG_DYNAMIC_FTRACE | |
117 | @ called from __ftrace_caller, saved in mcount_enter | |
118 | ldr r1, [sp, #16] @ instrumented routine (func) | |
119 | mcount_adjust_addr r1, r1 | |
120 | #else | |
121 | @ called from __mcount, untouched in lr | |
122 | mcount_adjust_addr r1, lr @ instrumented routine (func) | |
123 | #endif | |
124 | mov r2, fp @ frame pointer | |
125 | bl prepare_ftrace_return | |
126 | mcount_exit | |
127 | .endm | |
128 | ||
129 | #ifdef CONFIG_OLD_MCOUNT | |
130 | /* | |
131 | * mcount | |
132 | */ | |
133 | ||
134 | .macro mcount_enter | |
135 | stmdb sp!, {r0-r3, lr} | |
136 | .endm | |
137 | ||
138 | .macro mcount_get_lr reg | |
139 | ldr \reg, [fp, #-4] | |
140 | .endm | |
141 | ||
142 | .macro mcount_exit | |
143 | ldr lr, [fp, #-4] | |
144 | ldmia sp!, {r0-r3, pc} | |
145 | .endm | |
146 | ||
147 | ENTRY(mcount) | |
148 | #ifdef CONFIG_DYNAMIC_FTRACE | |
149 | stmdb sp!, {lr} | |
150 | ldr lr, [fp, #-4] | |
151 | ldmia sp!, {pc} | |
152 | #else | |
153 | __mcount _old | |
154 | #endif | |
155 | ENDPROC(mcount) | |
156 | ||
157 | #ifdef CONFIG_DYNAMIC_FTRACE | |
158 | ENTRY(ftrace_caller_old) | |
159 | __ftrace_caller _old | |
160 | ENDPROC(ftrace_caller_old) | |
161 | #endif | |
162 | ||
163 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
164 | ENTRY(ftrace_graph_caller_old) | |
165 | __ftrace_graph_caller | |
166 | ENDPROC(ftrace_graph_caller_old) | |
167 | #endif | |
168 | ||
169 | .purgem mcount_enter | |
170 | .purgem mcount_get_lr | |
171 | .purgem mcount_exit | |
172 | #endif | |
173 | ||
174 | /* | |
175 | * __gnu_mcount_nc | |
176 | */ | |
177 | ||
178 | .macro mcount_enter | |
179 | /* | |
180 | * This pad compensates for the push {lr} at the call site. Note that we are | |
181 | * unable to unwind through a function which does not otherwise save its lr. | |
182 | */ | |
183 | UNWIND(.pad #4) | |
184 | stmdb sp!, {r0-r3, lr} | |
185 | UNWIND(.save {r0-r3, lr}) | |
186 | .endm | |
187 | ||
188 | .macro mcount_get_lr reg | |
189 | ldr \reg, [sp, #20] | |
190 | .endm | |
191 | ||
192 | .macro mcount_exit | |
193 | ldmia sp!, {r0-r3, ip, lr} | |
194 | ret ip | |
195 | .endm | |
196 | ||
197 | ENTRY(__gnu_mcount_nc) | |
198 | UNWIND(.fnstart) | |
199 | #ifdef CONFIG_DYNAMIC_FTRACE | |
200 | mov ip, lr | |
201 | ldmia sp!, {lr} | |
202 | ret ip | |
203 | #else | |
204 | __mcount | |
205 | #endif | |
206 | UNWIND(.fnend) | |
207 | ENDPROC(__gnu_mcount_nc) | |
208 | ||
209 | #ifdef CONFIG_DYNAMIC_FTRACE | |
210 | ENTRY(ftrace_caller) | |
211 | UNWIND(.fnstart) | |
212 | __ftrace_caller | |
213 | UNWIND(.fnend) | |
214 | ENDPROC(ftrace_caller) | |
215 | #endif | |
216 | ||
217 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
218 | ENTRY(ftrace_graph_caller) | |
219 | UNWIND(.fnstart) | |
220 | __ftrace_graph_caller | |
221 | UNWIND(.fnend) | |
222 | ENDPROC(ftrace_graph_caller) | |
223 | #endif | |
224 | ||
225 | .purgem mcount_enter | |
226 | .purgem mcount_get_lr | |
227 | .purgem mcount_exit | |
228 | ||
229 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
230 | .globl return_to_handler | |
231 | return_to_handler: | |
232 | stmdb sp!, {r0-r3} | |
233 | mov r0, fp @ frame pointer | |
234 | bl ftrace_return_to_handler | |
235 | mov lr, r0 @ r0 has real ret addr | |
236 | ldmia sp!, {r0-r3} | |
237 | ret lr | |
238 | #endif | |
239 | ||
240 | ENTRY(ftrace_stub) | |
241 | .Lftrace_stub: | |
242 | ret lr | |
243 | ENDPROC(ftrace_stub) |