blob: 7eed00bbd26df9fe2cae268e1ccca2cbe38122b4 [file] [log] [blame]
Mike Frysinger1c873be2009-06-09 07:25:09 -04001/*
2 * mcount and friends -- ftrace stuff
3 *
Mike Frysingeraebfef02010-01-22 07:35:20 -05004 * Copyright (C) 2009-2010 Analog Devices Inc.
Mike Frysinger1c873be2009-06-09 07:25:09 -04005 * Licensed under the GPL-2 or later.
6 */
7
8#include <linux/linkage.h>
9#include <asm/ftrace.h>
10
11.text
12
Mike Frysingerf5074422010-07-21 09:13:02 -040013#ifdef CONFIG_DYNAMIC_FTRACE
14
15/* Simple stub so we can boot the kernel until runtime patching has
16 * disabled all calls to this. Then it'll be unused.
17 */
18ENTRY(__mcount)
19# if ANOMALY_05000371
20 nop; nop; nop; nop;
21# endif
22 rts;
23ENDPROC(__mcount)
24
Mike Frysinger1c873be2009-06-09 07:25:09 -040025/* GCC will have called us before setting up the function prologue, so we
26 * can clobber the normal scratch registers, but we need to make sure to
27 * save/restore the registers used for argument passing (R0-R2) in case
28 * the profiled function is using them. With data registers, R3 is the
29 * only one we can blow away. With pointer registers, we have P0-P2.
30 *
31 * Upon entry, the RETS will point to the top of the current profiled
Yi Li5bf9cbe2009-09-15 09:24:31 +000032 * function. And since GCC pushed the previous RETS for us, the previous
33 * function will be waiting there. mmmm pie.
Mike Frysinger1c873be2009-06-09 07:25:09 -040034 */
Mike Frysingerf5074422010-07-21 09:13:02 -040035ENTRY(_ftrace_caller)
36# ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
Mike Frysingeraebfef02010-01-22 07:35:20 -050037 /* optional micro optimization: return if stopped */
38 p1.l = _function_trace_stop;
39 p1.h = _function_trace_stop;
40 r3 = [p1];
41 cc = r3 == 0;
42 if ! cc jump _ftrace_stub (bp);
Mike Frysingerf5074422010-07-21 09:13:02 -040043# endif
44
45 /* save first/second/third function arg and the return register */
46 [--sp] = r2;
47 [--sp] = r0;
48 [--sp] = r1;
49 [--sp] = rets;
50
51 /* function_trace_call(unsigned long ip, unsigned long parent_ip):
52 * ip: this point was called by ...
53 * parent_ip: ... this function
54 * the ip itself will need adjusting for the mcount call
55 */
56 r0 = rets;
57 r1 = [sp + 16]; /* skip the 4 local regs on stack */
58 r0 += -MCOUNT_INSN_SIZE;
59
60.globl _ftrace_call
61_ftrace_call:
62 call _ftrace_stub
63
64# ifdef CONFIG_FUNCTION_GRAPH_TRACER
65.globl _ftrace_graph_call
66_ftrace_graph_call:
67 nop; /* jump _ftrace_graph_caller; */
68# endif
69
70 /* restore state and get out of dodge */
71.Lfinish_trace:
72 rets = [sp++];
73 r1 = [sp++];
74 r0 = [sp++];
75 r2 = [sp++];
76
77.globl _ftrace_stub
78_ftrace_stub:
79 rts;
80ENDPROC(_ftrace_caller)
81
82#else
83
84/* See documentation for _ftrace_caller */
85ENTRY(__mcount)
86# ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
87 /* optional micro optimization: return if stopped */
88 p1.l = _function_trace_stop;
89 p1.h = _function_trace_stop;
90 r3 = [p1];
91 cc = r3 == 0;
92 if ! cc jump _ftrace_stub (bp);
93# endif
Mike Frysingeraebfef02010-01-22 07:35:20 -050094
Mike Frysinger1c873be2009-06-09 07:25:09 -040095 /* save third function arg early so we can do testing below */
96 [--sp] = r2;
97
98 /* load the function pointer to the tracer */
99 p0.l = _ftrace_trace_function;
100 p0.h = _ftrace_trace_function;
101 r3 = [p0];
102
103 /* optional micro optimization: don't call the stub tracer */
104 r2.l = _ftrace_stub;
105 r2.h = _ftrace_stub;
106 cc = r2 == r3;
107 if ! cc jump .Ldo_trace;
108
Mike Frysingerf5074422010-07-21 09:13:02 -0400109# ifdef CONFIG_FUNCTION_GRAPH_TRACER
Mike Frysinger1ee76d72009-06-10 04:45:29 -0400110 /* if the ftrace_graph_return function pointer is not set to
111 * the ftrace_stub entry, call prepare_ftrace_return().
112 */
113 p0.l = _ftrace_graph_return;
114 p0.h = _ftrace_graph_return;
115 r3 = [p0];
116 cc = r2 == r3;
117 if ! cc jump _ftrace_graph_caller;
118
119 /* similarly, if the ftrace_graph_entry function pointer is not
120 * set to the ftrace_graph_entry_stub entry, ...
121 */
122 p0.l = _ftrace_graph_entry;
123 p0.h = _ftrace_graph_entry;
124 r2.l = _ftrace_graph_entry_stub;
125 r2.h = _ftrace_graph_entry_stub;
126 r3 = [p0];
127 cc = r2 == r3;
128 if ! cc jump _ftrace_graph_caller;
Mike Frysingerf5074422010-07-21 09:13:02 -0400129# endif
Mike Frysinger1ee76d72009-06-10 04:45:29 -0400130
Mike Frysinger1c873be2009-06-09 07:25:09 -0400131 r2 = [sp++];
132 rts;
133
134.Ldo_trace:
135
136 /* save first/second function arg and the return register */
137 [--sp] = r0;
138 [--sp] = r1;
139 [--sp] = rets;
140
141 /* setup the tracer function */
142 p0 = r3;
143
Yi Li5bf9cbe2009-09-15 09:24:31 +0000144 /* function_trace_call(unsigned long ip, unsigned long parent_ip):
145 * ip: this point was called by ...
146 * parent_ip: ... this function
147 * the ip itself will need adjusting for the mcount call
Mike Frysinger1c873be2009-06-09 07:25:09 -0400148 */
Yi Li5bf9cbe2009-09-15 09:24:31 +0000149 r0 = rets;
150 r1 = [sp + 16]; /* skip the 4 local regs on stack */
151 r0 += -MCOUNT_INSN_SIZE;
Mike Frysinger1c873be2009-06-09 07:25:09 -0400152
153 /* call the tracer */
154 call (p0);
155
156 /* restore state and get out of dodge */
Mike Frysinger1ee76d72009-06-10 04:45:29 -0400157.Lfinish_trace:
Mike Frysinger1c873be2009-06-09 07:25:09 -0400158 rets = [sp++];
159 r1 = [sp++];
160 r0 = [sp++];
161 r2 = [sp++];
162
163.globl _ftrace_stub
164_ftrace_stub:
165 rts;
166ENDPROC(__mcount)
Mike Frysinger1ee76d72009-06-10 04:45:29 -0400167
Mike Frysingerf5074422010-07-21 09:13:02 -0400168#endif
169
Mike Frysinger1ee76d72009-06-10 04:45:29 -0400170#ifdef CONFIG_FUNCTION_GRAPH_TRACER
171/* The prepare_ftrace_return() function is similar to the trace function
172 * except it takes a pointer to the location of the frompc. This is so
173 * the prepare_ftrace_return() can hijack it temporarily for probing
174 * purposes.
175 */
176ENTRY(_ftrace_graph_caller)
Mike Frysingerf5074422010-07-21 09:13:02 -0400177# ifndef CONFIG_DYNAMIC_FTRACE
Mike Frysinger1ee76d72009-06-10 04:45:29 -0400178 /* save first/second function arg and the return register */
179 [--sp] = r0;
180 [--sp] = r1;
181 [--sp] = rets;
182
Mike Frysingerb73faf72010-01-22 07:59:32 -0500183 /* prepare_ftrace_return(parent, self_addr, frame_pointer) */
184 r0 = sp; /* unsigned long *parent */
185 r1 = rets; /* unsigned long self_addr */
Mike Frysingerf5074422010-07-21 09:13:02 -0400186# else
187 r0 = sp; /* unsigned long *parent */
188 r1 = [sp]; /* unsigned long self_addr */
189# endif
190# ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST
Mike Frysingerb73faf72010-01-22 07:59:32 -0500191 r2 = fp; /* unsigned long frame_pointer */
Mike Frysingerf5074422010-07-21 09:13:02 -0400192# endif
Yi Li5bf9cbe2009-09-15 09:24:31 +0000193 r0 += 16; /* skip the 4 local regs on stack */
Mike Frysinger1ee76d72009-06-10 04:45:29 -0400194 r1 += -MCOUNT_INSN_SIZE;
195 call _prepare_ftrace_return;
196
197 jump .Lfinish_trace;
198ENDPROC(_ftrace_graph_caller)
199
200/* Undo the rewrite caused by ftrace_graph_caller(). The common function
201 * ftrace_return_to_handler() will return the original rets so we can
202 * restore it and be on our way.
203 */
204ENTRY(_return_to_handler)
205 /* make sure original return values are saved */
206 [--sp] = p0;
207 [--sp] = r0;
208 [--sp] = r1;
209
210 /* get original return address */
Mike Frysingerf5074422010-07-21 09:13:02 -0400211# ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST
Mike Frysingerb73faf72010-01-22 07:59:32 -0500212 r0 = fp; /* Blackfin is sane, so omit this */
Mike Frysingerf5074422010-07-21 09:13:02 -0400213# endif
Mike Frysinger1ee76d72009-06-10 04:45:29 -0400214 call _ftrace_return_to_handler;
215 rets = r0;
216
217 /* anomaly 05000371 - make sure we have at least three instructions
218 * between rets setting and the return
219 */
220 r1 = [sp++];
221 r0 = [sp++];
222 p0 = [sp++];
223 rts;
224ENDPROC(_return_to_handler)
225#endif