arch/powerpc/kernel/trace/ftrace.c

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

File Facts

System
Linux kernel
Corpus path
arch/powerpc/kernel/trace/ftrace.c
Extension
.c
Size
19160 bytes
Lines
697
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 (!IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) {
			addr += MCOUNT_INSN_SIZE;
			if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))
				addr += MCOUNT_INSN_SIZE;
		} else if (IS_ENABLED(CONFIG_CC_IS_CLANG) && IS_ENABLED(CONFIG_PPC64)) {
			/*
			 * addr points to global entry point though the NOP was emitted at local
			 * entry point due to https://github.com/llvm/llvm-project/issues/163706
			 * Handle that here with ppc_function_entry() for kernel symbols while
			 * adjusting module addresses in the else case, by looking for the below
			 * module global entry point sequence:
			 *	ld    r2, -8(r12)
			 *	add   r2, r2, r12
			 */
			if (is_kernel_text(addr) || is_kernel_inittext(addr))
				addr = ppc_function_entry((void *)addr);
			else if ((ppc_inst_val(ppc_inst_read((u32 *)addr)) ==
				  PPC_RAW_LD(_R2, _R12, -8)) &&
				 (ppc_inst_val(ppc_inst_read((u32 *)(addr+4))) ==
				  PPC_RAW_ADD(_R2, _R2, _R12)))
				addr += 8;
		}
	}

	return addr;
}

static ppc_inst_t ftrace_create_branch_inst(unsigned long ip, unsigned long addr, int link)
{
	ppc_inst_t op;

	WARN_ON(!is_offset_in_branch_range(addr - ip));
	create_branch(&op, (u32 *)ip, addr, link ? BRANCH_SET_LINK : 0);

	return op;
}

static inline int ftrace_read_inst(unsigned long ip, ppc_inst_t *op)
{
	if (copy_inst_from_kernel_nofault(op, (void *)ip)) {
		pr_err("0x%lx: fetching instruction failed\n", ip);
		return -EFAULT;
	}

	return 0;
}

static inline int ftrace_validate_inst(unsigned long ip, ppc_inst_t inst)
{
	ppc_inst_t op;
	int ret;

	ret = ftrace_read_inst(ip, &op);
	if (!ret && !ppc_inst_equal(op, inst)) {
		pr_err("0x%lx: expected (%08lx) != found (%08lx)\n",
		       ip, ppc_inst_as_ulong(inst), ppc_inst_as_ulong(op));
		ret = -EINVAL;
	}

	return ret;
}

static inline int ftrace_modify_code(unsigned long ip, ppc_inst_t old, ppc_inst_t new)
{
	int ret = ftrace_validate_inst(ip, old);

	if (!ret && !ppc_inst_equal(old, new))
		ret = patch_instruction((u32 *)ip, new);

	return ret;
}

static int is_bl_op(ppc_inst_t op)
{
	return (ppc_inst_val(op) & ~PPC_LI_MASK) == PPC_RAW_BL(0);
}

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

	for (i = 0; i < NUM_FTRACE_TRAMPS; i++)
		if (!ftrace_tramps[i])
			continue;
		else if (is_offset_in_branch_range(ftrace_tramps[i] - ip))
			return ftrace_tramps[i];

	return 0;
}

Annotation

Implementation Notes