aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/ftrace.c
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2008-11-15 02:39:05 -0500
committerSteven Rostedt <srostedt@redhat.com>2008-11-20 13:52:53 -0500
commit7cc45e64323c8a1042f56e6a8d1dc982f98d52a8 (patch)
tree4402eb9bdaca4087a92f5003cfa655e6293dc890 /arch/powerpc/kernel/ftrace.c
parentf48cb8b48b0b10025ca9c451b9b32cac3fcd33ba (diff)
powerpc/ppc32: ftrace, dynamic ftrace to handle modules
Impact: add ability to trace modules on 32 bit PowerPC This patch performs the necessary trampoline calls to handle modules with dynamic ftrace on 32 bit PowerPC. Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Diffstat (limited to 'arch/powerpc/kernel/ftrace.c')
-rw-r--r--arch/powerpc/kernel/ftrace.c101
1 files changed, 95 insertions, 6 deletions
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
index 1aec559bdfcb..3271cd698e4c 100644
--- a/arch/powerpc/kernel/ftrace.c
+++ b/arch/powerpc/kernel/ftrace.c
@@ -274,7 +274,63 @@ static int
274__ftrace_make_nop(struct module *mod, 274__ftrace_make_nop(struct module *mod,
275 struct dyn_ftrace *rec, unsigned long addr) 275 struct dyn_ftrace *rec, unsigned long addr)
276{ 276{
277 /* Ignore modules for PPC32 (for now) */ 277 unsigned char replaced[MCOUNT_INSN_SIZE];
278 unsigned int *op = (unsigned *)&replaced;
279 unsigned char jmp[8];
280 unsigned int *ptr = (unsigned int *)&jmp;
281 unsigned long ip = rec->ip;
282 unsigned long tramp;
283 int offset;
284
285 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
286 return -EFAULT;
287
288 /* Make sure that that this is still a 24bit jump */
289 if (!is_bl_op(*op)) {
290 printk(KERN_ERR "Not expected bl: opcode is %x\n", *op);
291 return -EINVAL;
292 }
293
294 /* lets find where the pointer goes */
295 tramp = find_bl_target(ip, *op);
296
297 /*
298 * On PPC32 the trampoline looks like:
299 * lis r11,sym@ha
300 * addi r11,r11,sym@l
301 * mtctr r11
302 * bctr
303 */
304
305 DEBUGP("ip:%lx jumps to %lx", ip, tramp);
306
307 /* Find where the trampoline jumps to */
308 if (probe_kernel_read(jmp, (void *)tramp, 8)) {
309 printk(KERN_ERR "Failed to read %lx\n", tramp);
310 return -EFAULT;
311 }
312
313 DEBUGP(" %08x %08x ", ptr[0], ptr[1]);
314
315 tramp = (ptr[1] & 0xffff) |
316 ((ptr[0] & 0xffff) << 16);
317 if (tramp & 0x8000)
318 tramp -= 0x10000;
319
320 DEBUGP(" %x ", tramp);
321
322 if (tramp != addr) {
323 printk(KERN_ERR
324 "Trampoline location %08lx does not match addr\n",
325 tramp);
326 return -EINVAL;
327 }
328
329 op[0] = PPC_NOP_INSTR;
330
331 if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE))
332 return -EPERM;
333
278 return 0; 334 return 0;
279} 335}
280#endif /* PPC64 */ 336#endif /* PPC64 */
@@ -297,7 +353,6 @@ int ftrace_make_nop(struct module *mod,
297 return ftrace_modify_code(ip, old, new); 353 return ftrace_modify_code(ip, old, new);
298 } 354 }
299 355
300#ifdef CONFIG_PPC64
301 /* 356 /*
302 * Out of range jumps are called from modules. 357 * Out of range jumps are called from modules.
303 * We should either already have a pointer to the module 358 * We should either already have a pointer to the module
@@ -320,7 +375,6 @@ int ftrace_make_nop(struct module *mod,
320 /* nothing to do if mod == rec->arch.mod */ 375 /* nothing to do if mod == rec->arch.mod */
321 } else 376 } else
322 mod = rec->arch.mod; 377 mod = rec->arch.mod;
323#endif /* CONFIG_PPC64 */
324 378
325 return __ftrace_make_nop(mod, rec, addr); 379 return __ftrace_make_nop(mod, rec, addr);
326 380
@@ -380,7 +434,44 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
380static int 434static int
381__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 435__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
382{ 436{
383 /* PPC32 ignores modules for now */ 437 unsigned char replaced[MCOUNT_INSN_SIZE];
438 unsigned int *op = (unsigned *)&replaced;
439 unsigned long ip = rec->ip;
440 unsigned long offset;
441
442 /* read where this goes */
443 if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
444 return -EFAULT;
445
446 /* It should be pointing to a nop */
447 if (op[0] != PPC_NOP_INSTR) {
448 printk(KERN_ERR "Expected NOP but have %x\n", op[0]);
449 return -EINVAL;
450 }
451
452 /* If we never set up a trampoline to ftrace_caller, then bail */
453 if (!rec->arch.mod->arch.tramp) {
454 printk(KERN_ERR "No ftrace trampoline\n");
455 return -EINVAL;
456 }
457
458 /* now calculate a jump to the ftrace caller trampoline */
459 offset = rec->arch.mod->arch.tramp - ip;
460
461 if (test_offset(offset)) {
462 printk(KERN_ERR "REL24 %li out of range!\n",
463 (long int)offset);
464 return -EINVAL;
465 }
466
467 /* Set to "bl addr" */
468 op[0] = branch_offset(offset);
469
470 DEBUGP("write to %lx\n", rec->ip);
471
472 if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE))
473 return -EPERM;
474
384 return 0; 475 return 0;
385} 476}
386#endif /* CONFIG_PPC64 */ 477#endif /* CONFIG_PPC64 */
@@ -402,7 +493,6 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
402 return ftrace_modify_code(ip, old, new); 493 return ftrace_modify_code(ip, old, new);
403 } 494 }
404 495
405#ifdef CONFIG_PPC64
406 /* 496 /*
407 * Out of range jumps are called from modules. 497 * Out of range jumps are called from modules.
408 * Being that we are converting from nop, it had better 498 * Being that we are converting from nop, it had better
@@ -412,7 +502,6 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
412 printk(KERN_ERR "No module loaded\n"); 502 printk(KERN_ERR "No module loaded\n");
413 return -EINVAL; 503 return -EINVAL;
414 } 504 }
415#endif
416 505
417 return __ftrace_make_call(rec, addr); 506 return __ftrace_make_call(rec, addr);
418} 507}