Commit 62c9da6a authored by Anton Blanchard's avatar Anton Blanchard

powerpc/ftrace: Use module loader helpers to parse trampolines

Now we have is_module_trampoline() and module_trampoline_target()
we can remove a bunch of intimate kernel module trampoline
knowledge from ftrace.
Signed-off-by: default avatarAnton Blanchard <anton@samba.org>
parent dd9fa162
......@@ -105,11 +105,9 @@ __ftrace_make_nop(struct module *mod,
struct dyn_ftrace *rec, unsigned long addr)
{
unsigned int op;
unsigned int jmp[5];
unsigned long ptr;
unsigned long ip = rec->ip;
unsigned long tramp;
int offset;
void *tramp;
/* read where this goes */
if (probe_kernel_read(&op, (void *)ip, sizeof(int)))
......@@ -122,96 +120,41 @@ __ftrace_make_nop(struct module *mod,
}
/* lets find where the pointer goes */
tramp = find_bl_target(ip, op);
/*
* On PPC64 the trampoline looks like:
* 0x3d, 0x82, 0x00, 0x00, addis r12,r2, <high>
* 0x39, 0x8c, 0x00, 0x00, addi r12,r12, <low>
* Where the bytes 2,3,6 and 7 make up the 32bit offset
* to the TOC that holds the pointer.
* to jump to.
* 0xf8, 0x41, 0x00, 0x28, std r2,40(r1)
* 0xe9, 0x6c, 0x00, 0x20, ld r11,32(r12)
* The actually address is 32 bytes from the offset
* into the TOC.
* 0xe8, 0x4c, 0x00, 0x28, ld r2,40(r12)
*/
tramp = (void *)find_bl_target(ip, op);
pr_devel("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc);
pr_devel("ip:%lx jumps to %p", ip, tramp);
/* Find where the trampoline jumps to */
if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) {
printk(KERN_ERR "Failed to read %lx\n", tramp);
return -EFAULT;
}
pr_devel(" %08x %08x", jmp[0], jmp[1]);
/* verify that this is what we expect it to be */
if (((jmp[0] & 0xffff0000) != 0x3d820000) ||
((jmp[1] & 0xffff0000) != 0x398c0000) ||
(jmp[2] != 0xf8410028) ||
(jmp[3] != 0xe96c0020) ||
(jmp[4] != 0xe84c0028)) {
if (!is_module_trampoline(tramp)) {
printk(KERN_ERR "Not a trampoline\n");
return -EINVAL;
}
/* The bottom half is signed extended */
offset = ((unsigned)((unsigned short)jmp[0]) << 16) +
(int)((short)jmp[1]);
pr_devel(" %x ", offset);
/* get the address this jumps too */
tramp = mod->arch.toc + offset + 32;
pr_devel("toc: %lx", tramp);
if (probe_kernel_read(jmp, (void *)tramp, 8)) {
printk(KERN_ERR "Failed to read %lx\n", tramp);
if (module_trampoline_target(mod, tramp, &ptr)) {
printk(KERN_ERR "Failed to get trampoline target\n");
return -EFAULT;
}
pr_devel(" %08x %08x\n", jmp[0], jmp[1]);
#ifdef __LITTLE_ENDIAN__
ptr = ((unsigned long)jmp[1] << 32) + jmp[0];
#else
ptr = ((unsigned long)jmp[0] << 32) + jmp[1];
#endif
pr_devel("trampoline target %lx", ptr);
/* This should match what was called */
if (ptr != ppc_function_entry((void *)addr)) {
printk(KERN_ERR "addr does not match %lx\n", ptr);
printk(KERN_ERR "addr %lx does not match expected %lx\n",
ptr, ppc_function_entry((void *)addr));
return -EINVAL;
}
/*
* We want to nop the line, but the next line is
* 0xe8, 0x41, 0x00, 0x28 ld r2,40(r1)
* This needs to be turned to a nop too.
*/
if (probe_kernel_read(&op, (void *)(ip+4), MCOUNT_INSN_SIZE))
return -EFAULT;
if (op != 0xe8410028) {
printk(KERN_ERR "Next line is not ld! (%08x)\n", op);
return -EINVAL;
}
/*
* Milton Miller pointed out that we can not blindly do nops.
* If a task was preempted when calling a trace function,
* the nops will remove the way to restore the TOC in r2
* and the r2 TOC will get corrupted.
*/
/*
* Replace:
* bl <tramp> <==== will be replaced with "b 1f"
* ld r2,40(r1)
* 1:
* Our original call site looks like:
*
* bl <tramp>
* ld r2,XX(r1)
*
* Milton Miller pointed out that we can not simply nop the branch.
* If a task was preempted when calling a trace function, the nops
* will remove the way to restore the TOC in r2 and the r2 TOC will
* get corrupted.
*
* Use a b +8 to jump over the load.
*/
op = 0x48000008; /* b +8 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment