1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/uaccess.h>
16#include <linux/ftrace.h>
17#include <linux/string.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/kernel.h>
21#include <asm/ftrace.h>
22#include <asm/cacheflush.h>
23#include <asm/unistd.h>
24#include <trace/syscall.h>
25
26#ifdef CONFIG_DYNAMIC_FTRACE
27static unsigned char ftrace_replaced_code[MCOUNT_INSN_SIZE];
28
29static unsigned char ftrace_nop[4];
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48static unsigned char *ftrace_nop_replace(unsigned long ip)
49{
50 __raw_writel(ip + MCOUNT_INSN_SIZE, ftrace_nop);
51 return ftrace_nop;
52}
53
54static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
55{
56
57 __raw_writel(addr, ftrace_replaced_code);
58
59
60
61
62
63 return ftrace_replaced_code;
64}
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94#define MOD_CODE_WRITE_FLAG (1 << 31)
95static atomic_t nmi_running = ATOMIC_INIT(0);
96static int mod_code_status;
97static void *mod_code_ip;
98static void *mod_code_newcode;
99
100static void clear_mod_flag(void)
101{
102 int old = atomic_read(&nmi_running);
103
104 for (;;) {
105 int new = old & ~MOD_CODE_WRITE_FLAG;
106
107 if (old == new)
108 break;
109
110 old = atomic_cmpxchg(&nmi_running, old, new);
111 }
112}
113
114static void ftrace_mod_code(void)
115{
116
117
118
119
120
121
122 mod_code_status = copy_to_kernel_nofault(mod_code_ip, mod_code_newcode,
123 MCOUNT_INSN_SIZE);
124
125
126 if (mod_code_status)
127 clear_mod_flag();
128}
129
130void arch_ftrace_nmi_enter(void)
131{
132 if (atomic_inc_return(&nmi_running) & MOD_CODE_WRITE_FLAG) {
133 smp_rmb();
134 ftrace_mod_code();
135 }
136
137 smp_mb();
138}
139
140void arch_ftrace_nmi_exit(void)
141{
142
143 smp_mb();
144 atomic_dec(&nmi_running);
145}
146
147static void wait_for_nmi_and_set_mod_flag(void)
148{
149 if (!atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG))
150 return;
151
152 do {
153 cpu_relax();
154 } while (atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG));
155}
156
157static void wait_for_nmi(void)
158{
159 if (!atomic_read(&nmi_running))
160 return;
161
162 do {
163 cpu_relax();
164 } while (atomic_read(&nmi_running));
165}
166
167static int
168do_ftrace_mod_code(unsigned long ip, void *new_code)
169{
170 mod_code_ip = (void *)ip;
171 mod_code_newcode = new_code;
172
173
174 smp_mb();
175
176 wait_for_nmi_and_set_mod_flag();
177
178
179 smp_mb();
180
181 ftrace_mod_code();
182
183
184 smp_mb();
185
186 clear_mod_flag();
187 wait_for_nmi();
188
189 return mod_code_status;
190}
191
192static int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
193 unsigned char *new_code)
194{
195 unsigned char replaced[MCOUNT_INSN_SIZE];
196
197
198
199
200
201
202
203
204
205
206 if (copy_from_kernel_nofault(replaced, (void *)ip, MCOUNT_INSN_SIZE))
207 return -EFAULT;
208
209
210 if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
211 return -EINVAL;
212
213
214 if (do_ftrace_mod_code(ip, new_code))
215 return -EPERM;
216
217 flush_icache_range(ip, ip + MCOUNT_INSN_SIZE);
218
219 return 0;
220}
221
222int ftrace_update_ftrace_func(ftrace_func_t func)
223{
224 unsigned long ip = (unsigned long)(&ftrace_call) + MCOUNT_INSN_OFFSET;
225 unsigned char old[MCOUNT_INSN_SIZE], *new;
226
227 memcpy(old, (unsigned char *)ip, MCOUNT_INSN_SIZE);
228 new = ftrace_call_replace(ip, (unsigned long)func);
229
230 return ftrace_modify_code(ip, old, new);
231}
232
233int ftrace_make_nop(struct module *mod,
234 struct dyn_ftrace *rec, unsigned long addr)
235{
236 unsigned char *new, *old;
237 unsigned long ip = rec->ip;
238
239 old = ftrace_call_replace(ip, addr);
240 new = ftrace_nop_replace(ip);
241
242 return ftrace_modify_code(rec->ip, old, new);
243}
244
245int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
246{
247 unsigned char *new, *old;
248 unsigned long ip = rec->ip;
249
250 old = ftrace_nop_replace(ip);
251 new = ftrace_call_replace(ip, addr);
252
253 return ftrace_modify_code(rec->ip, old, new);
254}
255#endif
256
257#ifdef CONFIG_FUNCTION_GRAPH_TRACER
258#ifdef CONFIG_DYNAMIC_FTRACE
259extern void ftrace_graph_call(void);
260
261static int ftrace_mod(unsigned long ip, unsigned long old_addr,
262 unsigned long new_addr)
263{
264 unsigned char code[MCOUNT_INSN_SIZE];
265
266 if (copy_from_kernel_nofault(code, (void *)ip, MCOUNT_INSN_SIZE))
267 return -EFAULT;
268
269 if (old_addr != __raw_readl((unsigned long *)code))
270 return -EINVAL;
271
272 __raw_writel(new_addr, ip);
273 return 0;
274}
275
276int ftrace_enable_ftrace_graph_caller(void)
277{
278 unsigned long ip, old_addr, new_addr;
279
280 ip = (unsigned long)(&ftrace_graph_call) + GRAPH_INSN_OFFSET;
281 old_addr = (unsigned long)(&skip_trace);
282 new_addr = (unsigned long)(&ftrace_graph_caller);
283
284 return ftrace_mod(ip, old_addr, new_addr);
285}
286
287int ftrace_disable_ftrace_graph_caller(void)
288{
289 unsigned long ip, old_addr, new_addr;
290
291 ip = (unsigned long)(&ftrace_graph_call) + GRAPH_INSN_OFFSET;
292 old_addr = (unsigned long)(&ftrace_graph_caller);
293 new_addr = (unsigned long)(&skip_trace);
294
295 return ftrace_mod(ip, old_addr, new_addr);
296}
297#endif
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
317{
318 unsigned long old;
319 int faulted;
320 unsigned long return_hooker = (unsigned long)&return_to_handler;
321
322 if (unlikely(ftrace_graph_is_dead()))
323 return;
324
325 if (unlikely(atomic_read(¤t->tracing_graph_pause)))
326 return;
327
328
329
330
331
332
333 __asm__ __volatile__(
334 "1: \n\t"
335 "mov.l @%2, %0 \n\t"
336 "2: \n\t"
337 "mov.l %3, @%2 \n\t"
338 "mov #0, %1 \n\t"
339 "3: \n\t"
340 ".section .fixup, \"ax\" \n\t"
341 "4: \n\t"
342 "mov.l 5f, %0 \n\t"
343 "jmp @%0 \n\t"
344 " mov #1, %1 \n\t"
345 ".balign 4 \n\t"
346 "5: .long 3b \n\t"
347 ".previous \n\t"
348 ".section __ex_table,\"a\" \n\t"
349 ".long 1b, 4b \n\t"
350 ".long 2b, 4b \n\t"
351 ".previous \n\t"
352 : "=&r" (old), "=r" (faulted)
353 : "r" (parent), "r" (return_hooker)
354 );
355
356 if (unlikely(faulted)) {
357 ftrace_graph_stop();
358 WARN_ON(1);
359 return;
360 }
361
362 if (function_graph_enter(old, self_addr, 0, NULL))
363 __raw_writel(old, parent);
364}
365#endif
366