Commit | Line | Data |
---|---|---|
1c873be7 MF |
1 | /* |
2 | * mcount and friends -- ftrace stuff | |
3 | * | |
aebfef03 | 4 | * Copyright (C) 2009-2010 Analog Devices Inc. |
1c873be7 MF |
5 | * Licensed under the GPL-2 or later. |
6 | */ | |
7 | ||
8 | #include <linux/linkage.h> | |
9 | #include <asm/ftrace.h> | |
10 | ||
11 | .text | |
12 | ||
f5074429 MF |
13 | #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 | */ | |
18 | ENTRY(__mcount) | |
19 | # if ANOMALY_05000371 | |
20 | nop; nop; nop; nop; | |
21 | # endif | |
22 | rts; | |
23 | ENDPROC(__mcount) | |
24 | ||
1c873be7 MF |
25 | /* 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 | |
5bf9cbef YL |
32 | * function. And since GCC pushed the previous RETS for us, the previous |
33 | * function will be waiting there. mmmm pie. | |
1c873be7 | 34 | */ |
f5074429 | 35 | ENTRY(_ftrace_caller) |
f5074429 MF |
36 | /* save first/second/third function arg and the return register */ |
37 | [--sp] = r2; | |
38 | [--sp] = r0; | |
39 | [--sp] = r1; | |
40 | [--sp] = rets; | |
41 | ||
42 | /* function_trace_call(unsigned long ip, unsigned long parent_ip): | |
43 | * ip: this point was called by ... | |
44 | * parent_ip: ... this function | |
45 | * the ip itself will need adjusting for the mcount call | |
46 | */ | |
47 | r0 = rets; | |
48 | r1 = [sp + 16]; /* skip the 4 local regs on stack */ | |
49 | r0 += -MCOUNT_INSN_SIZE; | |
50 | ||
51 | .globl _ftrace_call | |
52 | _ftrace_call: | |
53 | call _ftrace_stub | |
54 | ||
55 | # ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
56 | .globl _ftrace_graph_call | |
57 | _ftrace_graph_call: | |
58 | nop; /* jump _ftrace_graph_caller; */ | |
59 | # endif | |
60 | ||
61 | /* restore state and get out of dodge */ | |
62 | .Lfinish_trace: | |
63 | rets = [sp++]; | |
64 | r1 = [sp++]; | |
65 | r0 = [sp++]; | |
66 | r2 = [sp++]; | |
67 | ||
68 | .globl _ftrace_stub | |
69 | _ftrace_stub: | |
70 | rts; | |
71 | ENDPROC(_ftrace_caller) | |
72 | ||
73 | #else | |
74 | ||
75 | /* See documentation for _ftrace_caller */ | |
1c873be7 MF |
76 | ENTRY(__mcount) |
77 | /* save third function arg early so we can do testing below */ | |
78 | [--sp] = r2; | |
79 | ||
80 | /* load the function pointer to the tracer */ | |
81 | p0.l = _ftrace_trace_function; | |
82 | p0.h = _ftrace_trace_function; | |
83 | r3 = [p0]; | |
84 | ||
85 | /* optional micro optimization: don't call the stub tracer */ | |
86 | r2.l = _ftrace_stub; | |
87 | r2.h = _ftrace_stub; | |
88 | cc = r2 == r3; | |
89 | if ! cc jump .Ldo_trace; | |
90 | ||
f5074429 | 91 | # ifdef CONFIG_FUNCTION_GRAPH_TRACER |
1ee76d7e MF |
92 | /* if the ftrace_graph_return function pointer is not set to |
93 | * the ftrace_stub entry, call prepare_ftrace_return(). | |
94 | */ | |
95 | p0.l = _ftrace_graph_return; | |
96 | p0.h = _ftrace_graph_return; | |
97 | r3 = [p0]; | |
98 | cc = r2 == r3; | |
99 | if ! cc jump _ftrace_graph_caller; | |
100 | ||
101 | /* similarly, if the ftrace_graph_entry function pointer is not | |
102 | * set to the ftrace_graph_entry_stub entry, ... | |
103 | */ | |
104 | p0.l = _ftrace_graph_entry; | |
105 | p0.h = _ftrace_graph_entry; | |
106 | r2.l = _ftrace_graph_entry_stub; | |
107 | r2.h = _ftrace_graph_entry_stub; | |
108 | r3 = [p0]; | |
109 | cc = r2 == r3; | |
110 | if ! cc jump _ftrace_graph_caller; | |
f5074429 | 111 | # endif |
1ee76d7e | 112 | |
1c873be7 MF |
113 | r2 = [sp++]; |
114 | rts; | |
115 | ||
116 | .Ldo_trace: | |
117 | ||
118 | /* save first/second function arg and the return register */ | |
119 | [--sp] = r0; | |
120 | [--sp] = r1; | |
121 | [--sp] = rets; | |
122 | ||
123 | /* setup the tracer function */ | |
124 | p0 = r3; | |
125 | ||
5bf9cbef YL |
126 | /* function_trace_call(unsigned long ip, unsigned long parent_ip): |
127 | * ip: this point was called by ... | |
128 | * parent_ip: ... this function | |
129 | * the ip itself will need adjusting for the mcount call | |
1c873be7 | 130 | */ |
5bf9cbef YL |
131 | r0 = rets; |
132 | r1 = [sp + 16]; /* skip the 4 local regs on stack */ | |
133 | r0 += -MCOUNT_INSN_SIZE; | |
1c873be7 MF |
134 | |
135 | /* call the tracer */ | |
136 | call (p0); | |
137 | ||
138 | /* restore state and get out of dodge */ | |
1ee76d7e | 139 | .Lfinish_trace: |
1c873be7 MF |
140 | rets = [sp++]; |
141 | r1 = [sp++]; | |
142 | r0 = [sp++]; | |
143 | r2 = [sp++]; | |
144 | ||
145 | .globl _ftrace_stub | |
146 | _ftrace_stub: | |
147 | rts; | |
148 | ENDPROC(__mcount) | |
1ee76d7e | 149 | |
f5074429 MF |
150 | #endif |
151 | ||
1ee76d7e MF |
152 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
153 | /* The prepare_ftrace_return() function is similar to the trace function | |
154 | * except it takes a pointer to the location of the frompc. This is so | |
155 | * the prepare_ftrace_return() can hijack it temporarily for probing | |
156 | * purposes. | |
157 | */ | |
158 | ENTRY(_ftrace_graph_caller) | |
f5074429 | 159 | # ifndef CONFIG_DYNAMIC_FTRACE |
1ee76d7e MF |
160 | /* save first/second function arg and the return register */ |
161 | [--sp] = r0; | |
162 | [--sp] = r1; | |
163 | [--sp] = rets; | |
164 | ||
b73faf74 MF |
165 | /* prepare_ftrace_return(parent, self_addr, frame_pointer) */ |
166 | r0 = sp; /* unsigned long *parent */ | |
167 | r1 = rets; /* unsigned long self_addr */ | |
f5074429 MF |
168 | # else |
169 | r0 = sp; /* unsigned long *parent */ | |
170 | r1 = [sp]; /* unsigned long self_addr */ | |
171 | # endif | |
172 | # ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST | |
b73faf74 | 173 | r2 = fp; /* unsigned long frame_pointer */ |
f5074429 | 174 | # endif |
5bf9cbef | 175 | r0 += 16; /* skip the 4 local regs on stack */ |
1ee76d7e MF |
176 | r1 += -MCOUNT_INSN_SIZE; |
177 | call _prepare_ftrace_return; | |
178 | ||
179 | jump .Lfinish_trace; | |
180 | ENDPROC(_ftrace_graph_caller) | |
181 | ||
182 | /* Undo the rewrite caused by ftrace_graph_caller(). The common function | |
183 | * ftrace_return_to_handler() will return the original rets so we can | |
184 | * restore it and be on our way. | |
185 | */ | |
186 | ENTRY(_return_to_handler) | |
187 | /* make sure original return values are saved */ | |
188 | [--sp] = p0; | |
189 | [--sp] = r0; | |
190 | [--sp] = r1; | |
191 | ||
192 | /* get original return address */ | |
f5074429 | 193 | # ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST |
b73faf74 | 194 | r0 = fp; /* Blackfin is sane, so omit this */ |
f5074429 | 195 | # endif |
1ee76d7e MF |
196 | call _ftrace_return_to_handler; |
197 | rets = r0; | |
198 | ||
199 | /* anomaly 05000371 - make sure we have at least three instructions | |
200 | * between rets setting and the return | |
201 | */ | |
202 | r1 = [sp++]; | |
203 | r0 = [sp++]; | |
204 | p0 = [sp++]; | |
205 | rts; | |
206 | ENDPROC(_return_to_handler) | |
207 | #endif |