Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+20 −77
Original line number Diff line number Diff line
@@ -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)
	 */

	pr_devel("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc);

	/* 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;
	}
	tramp = (void *)find_bl_target(ip, op);

	pr_devel(" %08x %08x", jmp[0], jmp[1]);
	pr_devel("ip:%lx jumps to %p", ip, tramp);

	/* 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 */