xref: /linux/arch/arm/kernel/entry-ftrace.S (revision f3a8b6645dc2e60d11f20c1c23afd964ff4e55ae)
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#include <asm/export.h>
11
12#include "entry-header.S"
13
14/*
15 * When compiling with -pg, gcc inserts a call to the mcount routine at the
16 * start of every function.  In mcount, apart from the function's address (in
17 * lr), we need to get hold of the function's caller's address.
18 *
19 * Older GCCs (pre-4.4) inserted a call to a routine called mcount like this:
20 *
21 *	bl	mcount
22 *
23 * These versions have the limitation that in order for the mcount routine to
24 * be able to determine the function's caller's address, an APCS-style frame
25 * pointer (which is set up with something like the code below) is required.
26 *
27 *	mov     ip, sp
28 *	push    {fp, ip, lr, pc}
29 *	sub     fp, ip, #4
30 *
31 * With EABI, these frame pointers are not available unless -mapcs-frame is
32 * specified, and if building as Thumb-2, not even then.
33 *
34 * Newer GCCs (4.4+) solve this problem by introducing a new version of mcount,
35 * with call sites like:
36 *
37 *	push	{lr}
38 *	bl	__gnu_mcount_nc
39 *
40 * With these compilers, frame pointers are not necessary.
41 *
42 * mcount can be thought of as a function called in the middle of a subroutine
43 * call.  As such, it needs to be transparent for both the caller and the
44 * callee: the original lr needs to be restored when leaving mcount, and no
45 * registers should be clobbered.  (In the __gnu_mcount_nc implementation, we
46 * clobber the ip register.  This is OK because the ARM calling convention
47 * allows it to be clobbered in subroutines and doesn't use it to hold
48 * parameters.)
49 *
50 * When using dynamic ftrace, we patch out the mcount call by a "mov r0, r0"
51 * for the mcount case, and a "pop {lr}" for the __gnu_mcount_nc case (see
52 * arch/arm/kernel/ftrace.c).
53 */
54
55#ifndef CONFIG_OLD_MCOUNT
56#if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))
57#error Ftrace requires CONFIG_FRAME_POINTER=y with GCC older than 4.4.0.
58#endif
59#endif
60
61.macro mcount_adjust_addr rd, rn
62	bic	\rd, \rn, #1		@ clear the Thumb bit if present
63	sub	\rd, \rd, #MCOUNT_INSN_SIZE
64.endm
65
66.macro __mcount suffix
67	mcount_enter
68	ldr	r0, =ftrace_trace_function
69	ldr	r2, [r0]
70	adr	r0, .Lftrace_stub
71	cmp	r0, r2
72	bne	1f
73
74#ifdef CONFIG_FUNCTION_GRAPH_TRACER
75	ldr     r1, =ftrace_graph_return
76	ldr     r2, [r1]
77	cmp     r0, r2
78	bne     ftrace_graph_caller\suffix
79
80	ldr     r1, =ftrace_graph_entry
81	ldr     r2, [r1]
82	ldr     r0, =ftrace_graph_entry_stub
83	cmp     r0, r2
84	bne     ftrace_graph_caller\suffix
85#endif
86
87	mcount_exit
88
891: 	mcount_get_lr	r1			@ lr of instrumented func
90	mcount_adjust_addr	r0, lr		@ instrumented function
91	badr	lr, 2f
92	mov	pc, r2
932:	mcount_exit
94.endm
95
96.macro __ftrace_caller suffix
97	mcount_enter
98
99	mcount_get_lr	r1			@ lr of instrumented func
100	mcount_adjust_addr	r0, lr		@ instrumented function
101
102	.globl ftrace_call\suffix
103ftrace_call\suffix:
104	bl	ftrace_stub
105
106#ifdef CONFIG_FUNCTION_GRAPH_TRACER
107	.globl ftrace_graph_call\suffix
108ftrace_graph_call\suffix:
109	mov	r0, r0
110#endif
111
112	mcount_exit
113.endm
114
115.macro __ftrace_graph_caller
116	sub	r0, fp, #4		@ &lr of instrumented routine (&parent)
117#ifdef CONFIG_DYNAMIC_FTRACE
118	@ called from __ftrace_caller, saved in mcount_enter
119	ldr	r1, [sp, #16]		@ instrumented routine (func)
120	mcount_adjust_addr	r1, r1
121#else
122	@ called from __mcount, untouched in lr
123	mcount_adjust_addr	r1, lr	@ instrumented routine (func)
124#endif
125	mov	r2, fp			@ frame pointer
126	bl	prepare_ftrace_return
127	mcount_exit
128.endm
129
130#ifdef CONFIG_OLD_MCOUNT
131/*
132 * mcount
133 */
134
135.macro mcount_enter
136	stmdb	sp!, {r0-r3, lr}
137.endm
138
139.macro mcount_get_lr reg
140	ldr	\reg, [fp, #-4]
141.endm
142
143.macro mcount_exit
144	ldr	lr, [fp, #-4]
145	ldmia	sp!, {r0-r3, pc}
146.endm
147
148ENTRY(mcount)
149#ifdef CONFIG_DYNAMIC_FTRACE
150	stmdb	sp!, {lr}
151	ldr	lr, [fp, #-4]
152	ldmia	sp!, {pc}
153#else
154	__mcount _old
155#endif
156ENDPROC(mcount)
157EXPORT_SYMBOL(mcount)
158
159#ifdef CONFIG_DYNAMIC_FTRACE
160ENTRY(ftrace_caller_old)
161	__ftrace_caller _old
162ENDPROC(ftrace_caller_old)
163#endif
164
165#ifdef CONFIG_FUNCTION_GRAPH_TRACER
166ENTRY(ftrace_graph_caller_old)
167	__ftrace_graph_caller
168ENDPROC(ftrace_graph_caller_old)
169#endif
170
171.purgem mcount_enter
172.purgem mcount_get_lr
173.purgem mcount_exit
174#endif
175
176/*
177 * __gnu_mcount_nc
178 */
179
180.macro mcount_enter
181/*
182 * This pad compensates for the push {lr} at the call site.  Note that we are
183 * unable to unwind through a function which does not otherwise save its lr.
184 */
185 UNWIND(.pad	#4)
186	stmdb	sp!, {r0-r3, lr}
187 UNWIND(.save	{r0-r3, lr})
188.endm
189
190.macro mcount_get_lr reg
191	ldr	\reg, [sp, #20]
192.endm
193
194.macro mcount_exit
195	ldmia	sp!, {r0-r3, ip, lr}
196	ret	ip
197.endm
198
199ENTRY(__gnu_mcount_nc)
200UNWIND(.fnstart)
201#ifdef CONFIG_DYNAMIC_FTRACE
202	mov	ip, lr
203	ldmia	sp!, {lr}
204	ret	ip
205#else
206	__mcount
207#endif
208UNWIND(.fnend)
209ENDPROC(__gnu_mcount_nc)
210EXPORT_SYMBOL(__gnu_mcount_nc)
211
212#ifdef CONFIG_DYNAMIC_FTRACE
213ENTRY(ftrace_caller)
214UNWIND(.fnstart)
215	__ftrace_caller
216UNWIND(.fnend)
217ENDPROC(ftrace_caller)
218#endif
219
220#ifdef CONFIG_FUNCTION_GRAPH_TRACER
221ENTRY(ftrace_graph_caller)
222UNWIND(.fnstart)
223	__ftrace_graph_caller
224UNWIND(.fnend)
225ENDPROC(ftrace_graph_caller)
226#endif
227
228.purgem mcount_enter
229.purgem mcount_get_lr
230.purgem mcount_exit
231
232#ifdef CONFIG_FUNCTION_GRAPH_TRACER
233	.globl return_to_handler
234return_to_handler:
235	stmdb	sp!, {r0-r3}
236	mov	r0, fp			@ frame pointer
237	bl	ftrace_return_to_handler
238	mov	lr, r0			@ r0 has real ret addr
239	ldmia	sp!, {r0-r3}
240	ret	lr
241#endif
242
243ENTRY(ftrace_stub)
244.Lftrace_stub:
245	ret	lr
246ENDPROC(ftrace_stub)
247