diff options
author | Steven Rostedt <srostedt@redhat.com> | 2008-11-15 02:39:05 -0500 |
---|---|---|
committer | Steven Rostedt <srostedt@redhat.com> | 2008-11-20 13:52:53 -0500 |
commit | 7cc45e64323c8a1042f56e6a8d1dc982f98d52a8 (patch) | |
tree | 4402eb9bdaca4087a92f5003cfa655e6293dc890 | |
parent | f48cb8b48b0b10025ca9c451b9b32cac3fcd33ba (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>
-rw-r--r-- | arch/powerpc/include/asm/module.h | 5 | ||||
-rw-r--r-- | arch/powerpc/kernel/ftrace.c | 101 | ||||
-rw-r--r-- | arch/powerpc/kernel/module_32.c | 10 |
3 files changed, 109 insertions, 7 deletions
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h index 340bc699b620..08454880a2c0 100644 --- a/arch/powerpc/include/asm/module.h +++ b/arch/powerpc/include/asm/module.h | |||
@@ -39,11 +39,14 @@ struct mod_arch_specific { | |||
39 | unsigned long tramp; | 39 | unsigned long tramp; |
40 | #endif | 40 | #endif |
41 | 41 | ||
42 | #else | 42 | #else /* powerpc64 */ |
43 | /* Indices of PLT sections within module. */ | 43 | /* Indices of PLT sections within module. */ |
44 | unsigned int core_plt_section; | 44 | unsigned int core_plt_section; |
45 | unsigned int init_plt_section; | 45 | unsigned int init_plt_section; |
46 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
47 | unsigned long tramp; | ||
46 | #endif | 48 | #endif |
49 | #endif /* powerpc64 */ | ||
47 | 50 | ||
48 | /* List of BUG addresses, source line numbers and filenames */ | 51 | /* List of BUG addresses, source line numbers and filenames */ |
49 | struct list_head bug_list; | 52 | struct list_head bug_list; |
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) | |||
380 | static int | 434 | static 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 | } |
diff --git a/arch/powerpc/kernel/module_32.c b/arch/powerpc/kernel/module_32.c index 2df91a03462a..f832773fc28e 100644 --- a/arch/powerpc/kernel/module_32.c +++ b/arch/powerpc/kernel/module_32.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/fs.h> | 22 | #include <linux/fs.h> |
23 | #include <linux/string.h> | 23 | #include <linux/string.h> |
24 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
25 | #include <linux/ftrace.h> | ||
25 | #include <linux/cache.h> | 26 | #include <linux/cache.h> |
26 | #include <linux/bug.h> | 27 | #include <linux/bug.h> |
27 | #include <linux/sort.h> | 28 | #include <linux/sort.h> |
@@ -53,6 +54,9 @@ static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) | |||
53 | r_addend = rela[i].r_addend; | 54 | r_addend = rela[i].r_addend; |
54 | } | 55 | } |
55 | 56 | ||
57 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
58 | _count_relocs++; /* add one for ftrace_caller */ | ||
59 | #endif | ||
56 | return _count_relocs; | 60 | return _count_relocs; |
57 | } | 61 | } |
58 | 62 | ||
@@ -306,5 +310,11 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, | |||
306 | return -ENOEXEC; | 310 | return -ENOEXEC; |
307 | } | 311 | } |
308 | } | 312 | } |
313 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
314 | module->arch.tramp = | ||
315 | do_plt_call(module->module_core, | ||
316 | (unsigned long)ftrace_caller, | ||
317 | sechdrs, module); | ||
318 | #endif | ||
309 | return 0; | 319 | return 0; |
310 | } | 320 | } |