]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
4e491d14 SR |
2 | /* |
3 | * Code for replacing ftrace calls with jumps. | |
4 | * | |
5 | * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> | |
6 | * | |
7 | * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box. | |
8 | * | |
6794c782 SR |
9 | * Added function graph tracer code, taken from x86 that was written |
10 | * by Frederic Weisbecker, and ported to PPC by Steven Rostedt. | |
11 | * | |
4e491d14 SR |
12 | */ |
13 | ||
072c4c01 ME |
14 | #define pr_fmt(fmt) "ftrace-powerpc: " fmt |
15 | ||
4e491d14 SR |
16 | #include <linux/spinlock.h> |
17 | #include <linux/hardirq.h> | |
e4486fe3 | 18 | #include <linux/uaccess.h> |
f48cb8b4 | 19 | #include <linux/module.h> |
4e491d14 SR |
20 | #include <linux/ftrace.h> |
21 | #include <linux/percpu.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/list.h> | |
24 | ||
b3a7864c | 25 | #include <asm/asm-prototypes.h> |
4e491d14 | 26 | #include <asm/cacheflush.h> |
f48cb8b4 | 27 | #include <asm/code-patching.h> |
395a59d0 | 28 | #include <asm/ftrace.h> |
02424d89 | 29 | #include <asm/syscall.h> |
4e491d14 | 30 | |
4e491d14 | 31 | |
6794c782 | 32 | #ifdef CONFIG_DYNAMIC_FTRACE |
b54dcfe1 | 33 | static unsigned int |
46542888 | 34 | ftrace_call_replace(unsigned long ip, unsigned long addr, int link) |
4e491d14 | 35 | { |
b54dcfe1 | 36 | unsigned int op; |
4e491d14 | 37 | |
4a9e3f8e | 38 | addr = ppc_function_entry((void *)addr); |
4e491d14 | 39 | |
46542888 | 40 | /* if (link) set op to 'bl' else 'b' */ |
bb9b9035 | 41 | op = create_branch((unsigned int *)ip, addr, link ? 1 : 0); |
4e491d14 | 42 | |
b54dcfe1 | 43 | return op; |
4e491d14 SR |
44 | } |
45 | ||
8fd6e5a8 | 46 | static int |
b54dcfe1 | 47 | ftrace_modify_code(unsigned long ip, unsigned int old, unsigned int new) |
4e491d14 | 48 | { |
b54dcfe1 | 49 | unsigned int replaced; |
4e491d14 | 50 | |
4e491d14 | 51 | /* |
c02e0349 L |
52 | * Note: |
53 | * We are paranoid about modifying text, as if a bug was to happen, it | |
54 | * could cause us to read or write to someplace that could cause harm. | |
55 | * Carefully read and modify the code with probe_kernel_*(), and make | |
56 | * sure what we read is what we expected it to be before modifying it. | |
4e491d14 | 57 | */ |
e4486fe3 SR |
58 | |
59 | /* read the text we want to modify */ | |
b54dcfe1 | 60 | if (probe_kernel_read(&replaced, (void *)ip, MCOUNT_INSN_SIZE)) |
e4486fe3 SR |
61 | return -EFAULT; |
62 | ||
63 | /* Make sure it is what we expect it to be */ | |
15308664 TD |
64 | if (replaced != old) { |
65 | pr_err("%p: replaced (%#x) != old (%#x)", | |
66 | (void *)ip, replaced, old); | |
e4486fe3 | 67 | return -EINVAL; |
15308664 | 68 | } |
e4486fe3 SR |
69 | |
70 | /* replace the text with the new text */ | |
65b8c722 | 71 | if (patch_instruction((unsigned int *)ip, new)) |
e4486fe3 SR |
72 | return -EPERM; |
73 | ||
e4486fe3 | 74 | return 0; |
4e491d14 SR |
75 | } |
76 | ||
f48cb8b4 SR |
77 | /* |
78 | * Helper functions that are the same for both PPC64 and PPC32. | |
79 | */ | |
8fd6e5a8 SR |
80 | static int test_24bit_addr(unsigned long ip, unsigned long addr) |
81 | { | |
a95fc585 | 82 | addr = ppc_function_entry((void *)addr); |
8fd6e5a8 | 83 | |
0029ff87 SR |
84 | /* use the create_branch to verify that this offset can be branched */ |
85 | return create_branch((unsigned int *)ip, addr, 0); | |
8fd6e5a8 SR |
86 | } |
87 | ||
17be5b3d SR |
88 | #ifdef CONFIG_MODULES |
89 | ||
f48cb8b4 SR |
90 | static int is_bl_op(unsigned int op) |
91 | { | |
92 | return (op & 0xfc000003) == 0x48000001; | |
93 | } | |
94 | ||
f48cb8b4 SR |
95 | static unsigned long find_bl_target(unsigned long ip, unsigned int op) |
96 | { | |
97 | static int offset; | |
98 | ||
99 | offset = (op & 0x03fffffc); | |
100 | /* make it signed */ | |
101 | if (offset & 0x02000000) | |
102 | offset |= 0xfe000000; | |
103 | ||
104 | return ip + (long)offset; | |
105 | } | |
106 | ||
f48cb8b4 SR |
107 | #ifdef CONFIG_PPC64 |
108 | static int | |
109 | __ftrace_make_nop(struct module *mod, | |
110 | struct dyn_ftrace *rec, unsigned long addr) | |
111 | { | |
f17c4e01 | 112 | unsigned long entry, ptr, tramp; |
f48cb8b4 | 113 | unsigned long ip = rec->ip; |
15308664 | 114 | unsigned int op, pop; |
f48cb8b4 SR |
115 | |
116 | /* read where this goes */ | |
15308664 TD |
117 | if (probe_kernel_read(&op, (void *)ip, sizeof(int))) { |
118 | pr_err("Fetching opcode failed.\n"); | |
f48cb8b4 | 119 | return -EFAULT; |
15308664 | 120 | } |
f48cb8b4 SR |
121 | |
122 | /* Make sure that that this is still a 24bit jump */ | |
d9af12b7 | 123 | if (!is_bl_op(op)) { |
072c4c01 | 124 | pr_err("Not expected bl: opcode is %x\n", op); |
f48cb8b4 SR |
125 | return -EINVAL; |
126 | } | |
127 | ||
128 | /* lets find where the pointer goes */ | |
f17c4e01 | 129 | tramp = find_bl_target(ip, op); |
f48cb8b4 | 130 | |
f17c4e01 | 131 | pr_devel("ip:%lx jumps to %lx", ip, tramp); |
f48cb8b4 | 132 | |
62c9da6a | 133 | if (module_trampoline_target(mod, tramp, &ptr)) { |
072c4c01 | 134 | pr_err("Failed to get trampoline target\n"); |
f48cb8b4 SR |
135 | return -EFAULT; |
136 | } | |
137 | ||
62c9da6a | 138 | pr_devel("trampoline target %lx", ptr); |
f48cb8b4 | 139 | |
d84e0d69 | 140 | entry = ppc_global_function_entry((void *)addr); |
f48cb8b4 | 141 | /* This should match what was called */ |
d84e0d69 | 142 | if (ptr != entry) { |
072c4c01 | 143 | pr_err("addr %lx does not match expected %lx\n", ptr, entry); |
f48cb8b4 SR |
144 | return -EINVAL; |
145 | } | |
146 | ||
9d636109 ME |
147 | #ifdef CC_USING_MPROFILE_KERNEL |
148 | /* When using -mkernel_profile there is no load to jump over */ | |
149 | pop = PPC_INST_NOP; | |
150 | ||
151 | if (probe_kernel_read(&op, (void *)(ip - 4), 4)) { | |
152 | pr_err("Fetching instruction at %lx failed.\n", ip - 4); | |
153 | return -EFAULT; | |
154 | } | |
155 | ||
156 | /* We expect either a mflr r0, or a std r0, LRSAVE(r1) */ | |
157 | if (op != PPC_INST_MFLR && op != PPC_INST_STD_LR) { | |
158 | pr_err("Unexpected instruction %08x around bl _mcount\n", op); | |
159 | return -EINVAL; | |
160 | } | |
161 | #else | |
f48cb8b4 | 162 | /* |
62c9da6a AB |
163 | * Our original call site looks like: |
164 | * | |
165 | * bl <tramp> | |
166 | * ld r2,XX(r1) | |
167 | * | |
168 | * Milton Miller pointed out that we can not simply nop the branch. | |
169 | * If a task was preempted when calling a trace function, the nops | |
170 | * will remove the way to restore the TOC in r2 and the r2 TOC will | |
171 | * get corrupted. | |
172 | * | |
173 | * Use a b +8 to jump over the load. | |
f48cb8b4 | 174 | */ |
f48cb8b4 | 175 | |
15308664 TD |
176 | pop = PPC_INST_BRANCH | 8; /* b +8 */ |
177 | ||
178 | /* | |
179 | * Check what is in the next instruction. We can see ld r2,40(r1), but | |
180 | * on first pass after boot we will see mflr r0. | |
181 | */ | |
182 | if (probe_kernel_read(&op, (void *)(ip+4), MCOUNT_INSN_SIZE)) { | |
183 | pr_err("Fetching op failed.\n"); | |
184 | return -EFAULT; | |
185 | } | |
186 | ||
187 | if (op != PPC_INST_LD_TOC) { | |
9d636109 ME |
188 | pr_err("Expected %08x found %08x\n", PPC_INST_LD_TOC, op); |
189 | return -EINVAL; | |
15308664 | 190 | } |
9d636109 | 191 | #endif /* CC_USING_MPROFILE_KERNEL */ |
15308664 TD |
192 | |
193 | if (patch_instruction((unsigned int *)ip, pop)) { | |
194 | pr_err("Patching NOP failed.\n"); | |
f48cb8b4 | 195 | return -EPERM; |
15308664 | 196 | } |
f48cb8b4 SR |
197 | |
198 | return 0; | |
199 | } | |
200 | ||
201 | #else /* !PPC64 */ | |
202 | static int | |
203 | __ftrace_make_nop(struct module *mod, | |
204 | struct dyn_ftrace *rec, unsigned long addr) | |
205 | { | |
d9af12b7 SR |
206 | unsigned int op; |
207 | unsigned int jmp[4]; | |
7cc45e64 SR |
208 | unsigned long ip = rec->ip; |
209 | unsigned long tramp; | |
7cc45e64 | 210 | |
d9af12b7 | 211 | if (probe_kernel_read(&op, (void *)ip, MCOUNT_INSN_SIZE)) |
7cc45e64 SR |
212 | return -EFAULT; |
213 | ||
214 | /* Make sure that that this is still a 24bit jump */ | |
d9af12b7 | 215 | if (!is_bl_op(op)) { |
072c4c01 | 216 | pr_err("Not expected bl: opcode is %x\n", op); |
7cc45e64 SR |
217 | return -EINVAL; |
218 | } | |
219 | ||
220 | /* lets find where the pointer goes */ | |
d9af12b7 | 221 | tramp = find_bl_target(ip, op); |
7cc45e64 SR |
222 | |
223 | /* | |
224 | * On PPC32 the trampoline looks like: | |
fd5a4298 | 225 | * 0x3d, 0x80, 0x00, 0x00 lis r12,sym@ha |
226 | * 0x39, 0x8c, 0x00, 0x00 addi r12,r12,sym@l | |
227 | * 0x7d, 0x89, 0x03, 0xa6 mtctr r12 | |
d9af12b7 | 228 | * 0x4e, 0x80, 0x04, 0x20 bctr |
7cc45e64 SR |
229 | */ |
230 | ||
021376a3 | 231 | pr_devel("ip:%lx jumps to %lx", ip, tramp); |
7cc45e64 SR |
232 | |
233 | /* Find where the trampoline jumps to */ | |
d9af12b7 | 234 | if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { |
072c4c01 | 235 | pr_err("Failed to read %lx\n", tramp); |
7cc45e64 SR |
236 | return -EFAULT; |
237 | } | |
238 | ||
021376a3 | 239 | pr_devel(" %08x %08x ", jmp[0], jmp[1]); |
d9af12b7 SR |
240 | |
241 | /* verify that this is what we expect it to be */ | |
fd5a4298 | 242 | if (((jmp[0] & 0xffff0000) != 0x3d800000) || |
243 | ((jmp[1] & 0xffff0000) != 0x398c0000) || | |
244 | (jmp[2] != 0x7d8903a6) || | |
d9af12b7 | 245 | (jmp[3] != 0x4e800420)) { |
072c4c01 | 246 | pr_err("Not a trampoline\n"); |
d9af12b7 SR |
247 | return -EINVAL; |
248 | } | |
7cc45e64 | 249 | |
d9af12b7 SR |
250 | tramp = (jmp[1] & 0xffff) | |
251 | ((jmp[0] & 0xffff) << 16); | |
7cc45e64 SR |
252 | if (tramp & 0x8000) |
253 | tramp -= 0x10000; | |
254 | ||
021376a3 | 255 | pr_devel(" %lx ", tramp); |
7cc45e64 SR |
256 | |
257 | if (tramp != addr) { | |
072c4c01 | 258 | pr_err("Trampoline location %08lx does not match addr\n", |
7cc45e64 SR |
259 | tramp); |
260 | return -EINVAL; | |
261 | } | |
262 | ||
16c57b36 | 263 | op = PPC_INST_NOP; |
7cc45e64 | 264 | |
65b8c722 | 265 | if (patch_instruction((unsigned int *)ip, op)) |
7cc45e64 SR |
266 | return -EPERM; |
267 | ||
f48cb8b4 SR |
268 | return 0; |
269 | } | |
270 | #endif /* PPC64 */ | |
17be5b3d | 271 | #endif /* CONFIG_MODULES */ |
f48cb8b4 | 272 | |
8fd6e5a8 SR |
273 | int ftrace_make_nop(struct module *mod, |
274 | struct dyn_ftrace *rec, unsigned long addr) | |
275 | { | |
f48cb8b4 | 276 | unsigned long ip = rec->ip; |
b54dcfe1 | 277 | unsigned int old, new; |
8fd6e5a8 SR |
278 | |
279 | /* | |
280 | * If the calling address is more that 24 bits away, | |
281 | * then we had to use a trampoline to make the call. | |
282 | * Otherwise just update the call site. | |
283 | */ | |
f48cb8b4 | 284 | if (test_24bit_addr(ip, addr)) { |
8fd6e5a8 | 285 | /* within range */ |
46542888 | 286 | old = ftrace_call_replace(ip, addr, 1); |
92e02a51 | 287 | new = PPC_INST_NOP; |
f48cb8b4 SR |
288 | return ftrace_modify_code(ip, old, new); |
289 | } | |
290 | ||
17be5b3d | 291 | #ifdef CONFIG_MODULES |
f48cb8b4 SR |
292 | /* |
293 | * Out of range jumps are called from modules. | |
294 | * We should either already have a pointer to the module | |
295 | * or it has been passed in. | |
296 | */ | |
297 | if (!rec->arch.mod) { | |
298 | if (!mod) { | |
072c4c01 | 299 | pr_err("No module loaded addr=%lx\n", addr); |
f48cb8b4 SR |
300 | return -EFAULT; |
301 | } | |
302 | rec->arch.mod = mod; | |
303 | } else if (mod) { | |
304 | if (mod != rec->arch.mod) { | |
072c4c01 | 305 | pr_err("Record mod %p not equal to passed in mod %p\n", |
f48cb8b4 SR |
306 | rec->arch.mod, mod); |
307 | return -EINVAL; | |
308 | } | |
309 | /* nothing to do if mod == rec->arch.mod */ | |
310 | } else | |
311 | mod = rec->arch.mod; | |
f48cb8b4 SR |
312 | |
313 | return __ftrace_make_nop(mod, rec, addr); | |
17be5b3d SR |
314 | #else |
315 | /* We should not get here without modules */ | |
316 | return -EINVAL; | |
317 | #endif /* CONFIG_MODULES */ | |
f48cb8b4 SR |
318 | } |
319 | ||
17be5b3d | 320 | #ifdef CONFIG_MODULES |
f48cb8b4 | 321 | #ifdef CONFIG_PPC64 |
15308664 TD |
322 | /* |
323 | * Examine the existing instructions for __ftrace_make_call. | |
324 | * They should effectively be a NOP, and follow formal constraints, | |
325 | * depending on the ABI. Return false if they don't. | |
326 | */ | |
327 | #ifndef CC_USING_MPROFILE_KERNEL | |
f48cb8b4 | 328 | static int |
15308664 | 329 | expected_nop_sequence(void *ip, unsigned int op0, unsigned int op1) |
f48cb8b4 | 330 | { |
f48cb8b4 | 331 | /* |
24a1bdc3 AB |
332 | * We expect to see: |
333 | * | |
334 | * b +8 | |
335 | * ld r2,XX(r1) | |
336 | * | |
337 | * The load offset is different depending on the ABI. For simplicity | |
338 | * just mask it out when doing the compare. | |
f48cb8b4 | 339 | */ |
15308664 TD |
340 | if ((op0 != 0x48000008) || ((op1 & 0xffff0000) != 0xe8410000)) |
341 | return 0; | |
342 | return 1; | |
343 | } | |
344 | #else | |
345 | static int | |
346 | expected_nop_sequence(void *ip, unsigned int op0, unsigned int op1) | |
347 | { | |
348 | /* look for patched "NOP" on ppc64 with -mprofile-kernel */ | |
349 | if (op0 != PPC_INST_NOP) | |
350 | return 0; | |
351 | return 1; | |
352 | } | |
353 | #endif | |
354 | ||
355 | static int | |
356 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |
357 | { | |
358 | unsigned int op[2]; | |
359 | void *ip = (void *)rec->ip; | |
360 | ||
361 | /* read where this goes */ | |
362 | if (probe_kernel_read(op, ip, sizeof(op))) | |
363 | return -EFAULT; | |
364 | ||
365 | if (!expected_nop_sequence(ip, op[0], op[1])) { | |
366 | pr_err("Unexpected call sequence at %p: %x %x\n", | |
367 | ip, op[0], op[1]); | |
f48cb8b4 SR |
368 | return -EINVAL; |
369 | } | |
370 | ||
371 | /* If we never set up a trampoline to ftrace_caller, then bail */ | |
372 | if (!rec->arch.mod->arch.tramp) { | |
072c4c01 | 373 | pr_err("No ftrace trampoline\n"); |
f48cb8b4 SR |
374 | return -EINVAL; |
375 | } | |
376 | ||
24a1bdc3 | 377 | /* Ensure branch is within 24 bits */ |
b7b348c6 | 378 | if (!create_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) { |
072c4c01 | 379 | pr_err("Branch out of range\n"); |
f48cb8b4 | 380 | return -EINVAL; |
8fd6e5a8 SR |
381 | } |
382 | ||
24a1bdc3 | 383 | if (patch_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) { |
072c4c01 | 384 | pr_err("REL24 out of range!\n"); |
24a1bdc3 AB |
385 | return -EINVAL; |
386 | } | |
ec682cef | 387 | |
8fd6e5a8 SR |
388 | return 0; |
389 | } | |
15308664 TD |
390 | |
391 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | |
392 | int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, | |
393 | unsigned long addr) | |
394 | { | |
395 | return ftrace_make_call(rec, addr); | |
396 | } | |
397 | #endif | |
398 | ||
399 | #else /* !CONFIG_PPC64: */ | |
f48cb8b4 SR |
400 | static int |
401 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |
402 | { | |
d9af12b7 | 403 | unsigned int op; |
7cc45e64 | 404 | unsigned long ip = rec->ip; |
7cc45e64 SR |
405 | |
406 | /* read where this goes */ | |
d9af12b7 | 407 | if (probe_kernel_read(&op, (void *)ip, MCOUNT_INSN_SIZE)) |
7cc45e64 SR |
408 | return -EFAULT; |
409 | ||
410 | /* It should be pointing to a nop */ | |
16c57b36 | 411 | if (op != PPC_INST_NOP) { |
072c4c01 | 412 | pr_err("Expected NOP but have %x\n", op); |
7cc45e64 SR |
413 | return -EINVAL; |
414 | } | |
415 | ||
416 | /* If we never set up a trampoline to ftrace_caller, then bail */ | |
417 | if (!rec->arch.mod->arch.tramp) { | |
072c4c01 | 418 | pr_err("No ftrace trampoline\n"); |
7cc45e64 SR |
419 | return -EINVAL; |
420 | } | |
421 | ||
0029ff87 SR |
422 | /* create the branch to the trampoline */ |
423 | op = create_branch((unsigned int *)ip, | |
424 | rec->arch.mod->arch.tramp, BRANCH_SET_LINK); | |
425 | if (!op) { | |
072c4c01 | 426 | pr_err("REL24 out of range!\n"); |
7cc45e64 SR |
427 | return -EINVAL; |
428 | } | |
429 | ||
021376a3 | 430 | pr_devel("write to %lx\n", rec->ip); |
7cc45e64 | 431 | |
65b8c722 | 432 | if (patch_instruction((unsigned int *)ip, op)) |
7cc45e64 SR |
433 | return -EPERM; |
434 | ||
f48cb8b4 SR |
435 | return 0; |
436 | } | |
437 | #endif /* CONFIG_PPC64 */ | |
17be5b3d | 438 | #endif /* CONFIG_MODULES */ |
8fd6e5a8 SR |
439 | |
440 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |
441 | { | |
f48cb8b4 | 442 | unsigned long ip = rec->ip; |
b54dcfe1 | 443 | unsigned int old, new; |
8fd6e5a8 SR |
444 | |
445 | /* | |
446 | * If the calling address is more that 24 bits away, | |
447 | * then we had to use a trampoline to make the call. | |
448 | * Otherwise just update the call site. | |
449 | */ | |
f48cb8b4 | 450 | if (test_24bit_addr(ip, addr)) { |
8fd6e5a8 | 451 | /* within range */ |
92e02a51 | 452 | old = PPC_INST_NOP; |
46542888 | 453 | new = ftrace_call_replace(ip, addr, 1); |
f48cb8b4 | 454 | return ftrace_modify_code(ip, old, new); |
8fd6e5a8 SR |
455 | } |
456 | ||
17be5b3d | 457 | #ifdef CONFIG_MODULES |
f48cb8b4 SR |
458 | /* |
459 | * Out of range jumps are called from modules. | |
460 | * Being that we are converting from nop, it had better | |
461 | * already have a module defined. | |
462 | */ | |
463 | if (!rec->arch.mod) { | |
072c4c01 | 464 | pr_err("No module loaded\n"); |
f48cb8b4 SR |
465 | return -EINVAL; |
466 | } | |
f48cb8b4 SR |
467 | |
468 | return __ftrace_make_call(rec, addr); | |
17be5b3d SR |
469 | #else |
470 | /* We should not get here without modules */ | |
471 | return -EINVAL; | |
472 | #endif /* CONFIG_MODULES */ | |
8fd6e5a8 SR |
473 | } |
474 | ||
15adc048 | 475 | int ftrace_update_ftrace_func(ftrace_func_t func) |
4e491d14 SR |
476 | { |
477 | unsigned long ip = (unsigned long)(&ftrace_call); | |
b54dcfe1 | 478 | unsigned int old, new; |
4e491d14 SR |
479 | int ret; |
480 | ||
b54dcfe1 | 481 | old = *(unsigned int *)&ftrace_call; |
46542888 | 482 | new = ftrace_call_replace(ip, (unsigned long)func, 1); |
4e491d14 SR |
483 | ret = ftrace_modify_code(ip, old, new); |
484 | ||
485 | return ret; | |
486 | } | |
487 | ||
ee456bb3 SR |
488 | static int __ftrace_replace_code(struct dyn_ftrace *rec, int enable) |
489 | { | |
490 | unsigned long ftrace_addr = (unsigned long)FTRACE_ADDR; | |
491 | int ret; | |
492 | ||
493 | ret = ftrace_update_record(rec, enable); | |
494 | ||
495 | switch (ret) { | |
496 | case FTRACE_UPDATE_IGNORE: | |
497 | return 0; | |
498 | case FTRACE_UPDATE_MAKE_CALL: | |
499 | return ftrace_make_call(rec, ftrace_addr); | |
500 | case FTRACE_UPDATE_MAKE_NOP: | |
501 | return ftrace_make_nop(NULL, rec, ftrace_addr); | |
502 | } | |
503 | ||
504 | return 0; | |
505 | } | |
506 | ||
507 | void ftrace_replace_code(int enable) | |
508 | { | |
509 | struct ftrace_rec_iter *iter; | |
510 | struct dyn_ftrace *rec; | |
511 | int ret; | |
512 | ||
513 | for (iter = ftrace_rec_iter_start(); iter; | |
514 | iter = ftrace_rec_iter_next(iter)) { | |
515 | rec = ftrace_rec_iter_record(iter); | |
516 | ret = __ftrace_replace_code(rec, enable); | |
517 | if (ret) { | |
4fd3279b | 518 | ftrace_bug(ret, rec); |
ee456bb3 SR |
519 | return; |
520 | } | |
521 | } | |
522 | } | |
523 | ||
c96f8385 TD |
524 | /* |
525 | * Use the default ftrace_modify_all_code, but without | |
526 | * stop_machine(). | |
527 | */ | |
ee456bb3 SR |
528 | void arch_ftrace_update_code(int command) |
529 | { | |
c96f8385 | 530 | ftrace_modify_all_code(command); |
ee456bb3 SR |
531 | } |
532 | ||
3a36cb11 | 533 | int __init ftrace_dyn_arch_init(void) |
4e491d14 | 534 | { |
4e491d14 SR |
535 | return 0; |
536 | } | |
6794c782 SR |
537 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
538 | ||
539 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
540 | ||
46542888 SR |
541 | #ifdef CONFIG_DYNAMIC_FTRACE |
542 | extern void ftrace_graph_call(void); | |
543 | extern void ftrace_graph_stub(void); | |
544 | ||
545 | int ftrace_enable_ftrace_graph_caller(void) | |
546 | { | |
547 | unsigned long ip = (unsigned long)(&ftrace_graph_call); | |
548 | unsigned long addr = (unsigned long)(&ftrace_graph_caller); | |
549 | unsigned long stub = (unsigned long)(&ftrace_graph_stub); | |
b54dcfe1 | 550 | unsigned int old, new; |
46542888 | 551 | |
b54dcfe1 | 552 | old = ftrace_call_replace(ip, stub, 0); |
46542888 SR |
553 | new = ftrace_call_replace(ip, addr, 0); |
554 | ||
555 | return ftrace_modify_code(ip, old, new); | |
556 | } | |
557 | ||
558 | int ftrace_disable_ftrace_graph_caller(void) | |
559 | { | |
560 | unsigned long ip = (unsigned long)(&ftrace_graph_call); | |
561 | unsigned long addr = (unsigned long)(&ftrace_graph_caller); | |
562 | unsigned long stub = (unsigned long)(&ftrace_graph_stub); | |
b54dcfe1 | 563 | unsigned int old, new; |
46542888 | 564 | |
b54dcfe1 | 565 | old = ftrace_call_replace(ip, addr, 0); |
46542888 SR |
566 | new = ftrace_call_replace(ip, stub, 0); |
567 | ||
568 | return ftrace_modify_code(ip, old, new); | |
569 | } | |
570 | #endif /* CONFIG_DYNAMIC_FTRACE */ | |
571 | ||
6794c782 SR |
572 | /* |
573 | * Hook the return address and push it in the stack of return addrs | |
b3c18725 | 574 | * in current thread info. Return the address we want to divert to. |
6794c782 | 575 | */ |
b3c18725 | 576 | unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip) |
6794c782 | 577 | { |
6794c782 | 578 | struct ftrace_graph_ent trace; |
7d56c65a | 579 | unsigned long return_hooker; |
6794c782 | 580 | |
96d4f43e | 581 | if (unlikely(ftrace_graph_is_dead())) |
b3c18725 | 582 | goto out; |
96d4f43e | 583 | |
6794c782 | 584 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) |
b3c18725 | 585 | goto out; |
6794c782 | 586 | |
7d56c65a | 587 | return_hooker = ppc_function_entry(return_to_handler); |
6794c782 | 588 | |
b3c18725 | 589 | trace.func = ip; |
bac821a6 | 590 | trace.depth = current->curr_ret_stack + 1; |
6794c782 SR |
591 | |
592 | /* Only trace if the calling function expects to */ | |
b3c18725 AB |
593 | if (!ftrace_graph_entry(&trace)) |
594 | goto out; | |
595 | ||
9a7c348b JP |
596 | if (ftrace_push_return_trace(parent, ip, &trace.depth, 0, |
597 | NULL) == -EBUSY) | |
b3c18725 | 598 | goto out; |
bac821a6 | 599 | |
b3c18725 AB |
600 | parent = return_hooker; |
601 | out: | |
602 | return parent; | |
6794c782 SR |
603 | } |
604 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | |
02424d89 IM |
605 | |
606 | #if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_PPC64) | |
607 | unsigned long __init arch_syscall_addr(int nr) | |
608 | { | |
609 | return sys_call_table[nr*2]; | |
610 | } | |
611 | #endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_PPC64 */ | |
7132e2d6 | 612 | |
f55d9665 | 613 | #ifdef PPC64_ELF_ABI_v1 |
7132e2d6 TJB |
614 | char *arch_ftrace_match_adjust(char *str, const char *search) |
615 | { | |
616 | if (str[0] == '.' && search[0] != '.') | |
617 | return str + 1; | |
618 | else | |
619 | return str; | |
620 | } | |
f55d9665 | 621 | #endif /* PPC64_ELF_ABI_v1 */ |