arch/powerpc/kernel/trace/ftrace_64_pg.c

Source file repositories/reference/linux-study-clean/arch/powerpc/kernel/trace/ftrace_64_pg.c

File Facts

System
Linux kernel
Corpus path
arch/powerpc/kernel/trace/ftrace_64_pg.c
Extension
.c
Size
20999 bytes
Lines
833
Domain
Architecture Layer
Bucket
arch/powerpc
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

if (copy_inst_from_kernel_nofault(&op, (void *)(ip - 4))) {
			pr_err("Fetching instruction at %lx failed.\n", ip - 4);
			return -EFAULT;
		}

		/* We expect either a mflr r0, or a std r0, LRSAVE(r1) */
		if (!ppc_inst_equal(op, ppc_inst(PPC_RAW_MFLR(_R0))) &&
		    !ppc_inst_equal(op, ppc_inst(PPC_INST_STD_LR))) {
			pr_err("Unexpected instruction %08lx around bl _mcount\n",
			       ppc_inst_as_ulong(op));
			return -EINVAL;
		}
	} else if (IS_ENABLED(CONFIG_PPC64)) {
		/*
		 * Check what is in the next instruction. We can see ld r2,40(r1), but
		 * on first pass after boot we will see mflr r0.
		 */
		if (copy_inst_from_kernel_nofault(&op, (void *)(ip + 4))) {
			pr_err("Fetching op failed.\n");
			return -EFAULT;
		}

		if (!ppc_inst_equal(op,  ppc_inst(PPC_INST_LD_TOC))) {
			pr_err("Expected %08lx found %08lx\n", PPC_INST_LD_TOC,
			       ppc_inst_as_ulong(op));
			return -EINVAL;
		}
	}

	/*
	 * When using -mprofile-kernel or PPC32 there is no load to jump over.
	 *
	 * Otherwise 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.
	 */
	if (IS_ENABLED(CONFIG_MPROFILE_KERNEL) || IS_ENABLED(CONFIG_PPC32))
		pop = ppc_inst(PPC_RAW_NOP());
	else
		pop = ppc_inst(PPC_RAW_BRANCH(8));	/* b +8 */

	if (patch_instruction((u32 *)ip, pop)) {
		pr_err("Patching NOP failed.\n");
		return -EPERM;
	}

	return 0;
}
#else
static int __ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr)
{
	return 0;
}
#endif /* CONFIG_MODULES */

static unsigned long find_ftrace_tramp(unsigned long ip)
{
	int i;

	/*
	 * We have the compiler generated long_branch tramps at the end
	 * and we prefer those
	 */
	for (i = NUM_FTRACE_TRAMPS - 1; i >= 0; i--)
		if (!ftrace_tramps[i])
			continue;
		else if (is_offset_in_branch_range(ftrace_tramps[i] - ip))
			return ftrace_tramps[i];

	return 0;
}

static int add_ftrace_tramp(unsigned long tramp)
{
	int i;

	for (i = 0; i < NUM_FTRACE_TRAMPS; i++)
		if (!ftrace_tramps[i]) {
			ftrace_tramps[i] = tramp;
			return 0;
		}

Annotation

Implementation Notes