diff options
Diffstat (limited to 'arch/arm/kernel/entry-ftrace.S')
-rw-r--r-- | arch/arm/kernel/entry-ftrace.S | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/arch/arm/kernel/entry-ftrace.S b/arch/arm/kernel/entry-ftrace.S new file mode 100644 index 000000000000..fe57c73e70a4 --- /dev/null +++ b/arch/arm/kernel/entry-ftrace.S | |||
@@ -0,0 +1,243 @@ | |||
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 | adr lr, BSYM(2f) | ||
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) | ||