Commit 28ac0a9e authored by Qing Zhang's avatar Qing Zhang Committed by Huacai Chen
Browse files

LoongArch: modules/ftrace: Initialize PLT at load time



This patch implements ftrace trampolines through plt entry.

Tested by forcing ftrace_make_call() to use the module PLT, and then
loading up a module after setting up ftrace with:

| echo ":mod:<module-name>" > set_ftrace_filter;
| echo function > current_tracer;
| modprobe <module-name>

Since FTRACE_ADDR/FTRACE_REGS_ADDR is only defined when CONFIG_DYNAMIC_
FTRACE is selected, we wrap their usage in module_init_ftrace_plt() with
ifdeffery rather than using IS_ENABLED().

Signed-off-by: default avatarQing Zhang <zhangqing@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent a51ac524
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -6,6 +6,10 @@
#ifndef _ASM_LOONGARCH_FTRACE_H
#define _ASM_LOONGARCH_FTRACE_H

#define FTRACE_PLT_IDX		0
#define FTRACE_REGS_PLT_IDX	1
#define NR_FTRACE_PLTS		2

#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))

#ifdef CONFIG_FUNCTION_TRACER
+3 −0
Original line number Diff line number Diff line
@@ -13,10 +13,12 @@

#define ADDR_IMMMASK_LU52ID	0xFFF0000000000000
#define ADDR_IMMMASK_LU32ID	0x000FFFFF00000000
#define ADDR_IMMMASK_LU12IW	0x00000000FFFFF000
#define ADDR_IMMMASK_ADDU16ID	0x00000000FFFF0000

#define ADDR_IMMSHIFT_LU52ID	52
#define ADDR_IMMSHIFT_LU32ID	32
#define ADDR_IMMSHIFT_LU12IW	12
#define ADDR_IMMSHIFT_ADDU16ID	16

#define ADDR_IMM(addr, INSN)	((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN)
@@ -360,6 +362,7 @@ u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest);
u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk);
u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj);

u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm);
u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest);
+4 −1
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@ struct mod_arch_specific {
	struct mod_section got;
	struct mod_section plt;
	struct mod_section plt_idx;

	/* For CONFIG_DYNAMIC_FTRACE */
	struct plt_entry *ftrace_trampolines;
};

struct got_entry {
@@ -49,7 +52,7 @@ static inline struct plt_entry emit_plt_entry(unsigned long val)
{
	u32 lu12iw, lu32id, lu52id, jirl;

	lu12iw = (lu12iw_op << 25 | (((val >> 12) & 0xfffff) << 5) | LOONGARCH_GPR_T1);
	lu12iw = larch_insn_gen_lu12iw(LOONGARCH_GPR_T1, ADDR_IMM(val, LU12IW));
	lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID));
	lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID));
	jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, 0, (val & 0xfff));
+1 −0
Original line number Diff line number Diff line
@@ -5,4 +5,5 @@ SECTIONS {
	.got : { BYTE(0) }
	.plt : { BYTE(0) }
	.plt.idx : { BYTE(0) }
	.ftrace_trampoline : { BYTE(0) }
}
+93 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/uaccess.h>

#include <asm/inst.h>
#include <asm/module.h>

static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, bool validate)
{
@@ -29,18 +30,78 @@ static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, bool validate)
}

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS

#ifdef CONFIG_MODULES
static inline int __get_mod(struct module **mod, unsigned long addr)
{
	preempt_disable();
	*mod = __module_text_address(addr);
	preempt_enable();

	if (WARN_ON(!(*mod)))
		return -EINVAL;

	return 0;
}

static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
{
	struct plt_entry *plt = mod->arch.ftrace_trampolines;

	if (addr == FTRACE_ADDR)
		return &plt[FTRACE_PLT_IDX];
	if (addr == FTRACE_REGS_ADDR &&
			IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
		return &plt[FTRACE_REGS_PLT_IDX];

	return NULL;
}

static unsigned long get_plt_addr(struct module *mod, unsigned long addr)
{
	struct plt_entry *plt;

	plt = get_ftrace_plt(mod, addr);
	if (!plt) {
		pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
		return -EINVAL;
	}

	return (unsigned long)plt;
}
#endif

int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned long addr)
{
	u32 old, new;
	unsigned long pc;
	long offset __maybe_unused;

	pc = rec->ip + LOONGARCH_INSN_SIZE;

#ifdef CONFIG_MODULES
	offset = (long)pc - (long)addr;

	if (offset < -SZ_128M || offset >= SZ_128M) {
		int ret;
		struct module *mod;

		ret = __get_mod(&mod, pc);
		if (ret)
			return ret;

		addr = get_plt_addr(mod, addr);

		old_addr = get_plt_addr(mod, old_addr);
	}
#endif

	new = larch_insn_gen_bl(pc, addr);
	old = larch_insn_gen_bl(pc, old_addr);

	return ftrace_modify_code(pc, old, new, true);
}

#endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */

int ftrace_update_ftrace_func(ftrace_func_t func)
@@ -91,9 +152,25 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
	u32 old, new;
	unsigned long pc;
	long offset __maybe_unused;

	pc = rec->ip + LOONGARCH_INSN_SIZE;

#ifdef CONFIG_MODULES
	offset = (long)pc - (long)addr;

	if (offset < -SZ_128M || offset >= SZ_128M) {
		int ret;
		struct module *mod;

		ret = __get_mod(&mod, pc);
		if (ret)
			return ret;

		addr = get_plt_addr(mod, addr);
	}
#endif

	old = larch_insn_gen_nop();
	new = larch_insn_gen_bl(pc, addr);

@@ -104,9 +181,25 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long ad
{
	u32 old, new;
	unsigned long pc;
	long offset __maybe_unused;

	pc = rec->ip + LOONGARCH_INSN_SIZE;

#ifdef CONFIG_MODULES
	offset = (long)pc - (long)addr;

	if (offset < -SZ_128M || offset >= SZ_128M) {
		int ret;
		struct module *mod;

		ret = __get_mod(&mod, pc);
		if (ret)
			return ret;

		addr = get_plt_addr(mod, addr);
	}
#endif

	new = larch_insn_gen_nop();
	old = larch_insn_gen_bl(pc, addr);

Loading