Merge remote-tracking branches 'regulator/fix/88pm800', 'regulator/fix/max8973',...
[linux-drm-fsl-dcu.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 .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)