Commit 24d4f527 authored by Youling Tang's avatar Youling Tang Committed by Huacai Chen

LoongArch: ftrace: Implement ftrace_find_callable_addr() to simplify code

In the module processing functions, the same logic can be reused by
implementing ftrace_find_callable_addr().
Signed-off-by: default avatarYouling Tang <tangyouling@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 819cf655
...@@ -31,16 +31,11 @@ static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, bool validate) ...@@ -31,16 +31,11 @@ static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, bool validate)
} }
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
static inline int __get_mod(struct module **mod, unsigned long addr) static bool reachable_by_bl(unsigned long addr, unsigned long pc)
{ {
preempt_disable(); long offset = (long)addr - (long)pc;
*mod = __module_text_address(addr);
preempt_enable();
if (WARN_ON(!(*mod))) return offset >= -SZ_128M && offset < SZ_128M;
return -EINVAL;
return 0;
} }
static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr) static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
...@@ -56,17 +51,58 @@ static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr) ...@@ -56,17 +51,58 @@ static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
return NULL; return NULL;
} }
static unsigned long get_plt_addr(struct module *mod, unsigned long addr) /*
* Find the address the callsite must branch to in order to reach '*addr'.
*
* Due to the limited range of 'bl' instruction, modules may be placed too far
* away to branch directly and we must use a PLT.
*
* Returns true when '*addr' contains a reachable target address, or has been
* modified to contain a PLT address. Returns false otherwise.
*/
static bool ftrace_find_callable_addr(struct dyn_ftrace *rec, struct module *mod, unsigned long *addr)
{ {
unsigned long pc = rec->ip + LOONGARCH_INSN_SIZE;
struct plt_entry *plt; struct plt_entry *plt;
plt = get_ftrace_plt(mod, addr); /*
* When the target is within range of the 'bl' instruction, use 'addr'
* as-is and branch to that directly.
*/
if (reachable_by_bl(*addr, pc))
return true;
/*
* 'mod' is only set at module load time, but if we end up
* dealing with an out-of-range condition, we can assume it
* is due to a module being loaded far away from the kernel.
*
* NOTE: __module_text_address() must be called with preemption
* disabled, but we can rely on ftrace_lock to ensure that 'mod'
* retains its validity throughout the remainder of this code.
*/
if (!mod) {
preempt_disable();
mod = __module_text_address(pc);
preempt_enable();
}
if (WARN_ON(!mod))
return false;
plt = get_ftrace_plt(mod, *addr);
if (!plt) { if (!plt) {
pr_err("ftrace: no module PLT for %ps\n", (void *)addr); pr_err("ftrace: no module PLT for %ps\n", (void *)*addr);
return -EINVAL; return false;
} }
return (unsigned long)plt; *addr = (unsigned long)plt;
return true;
}
#else /* !CONFIG_MODULES */
static bool ftrace_find_callable_addr(struct dyn_ftrace *rec, struct module *mod, unsigned long *addr)
{
return true;
} }
#endif #endif
...@@ -75,26 +111,14 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned ...@@ -75,26 +111,14 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned
{ {
u32 old, new; u32 old, new;
unsigned long pc; unsigned long pc;
long offset __maybe_unused;
pc = rec->ip + LOONGARCH_INSN_SIZE; pc = rec->ip + LOONGARCH_INSN_SIZE;
#ifdef CONFIG_MODULES if (!ftrace_find_callable_addr(rec, NULL, &addr))
offset = (long)pc - (long)addr; return -EINVAL;
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); if (!ftrace_find_callable_addr(rec, NULL, &old_addr))
} return -EINVAL;
#endif
new = larch_insn_gen_bl(pc, addr); new = larch_insn_gen_bl(pc, addr);
old = larch_insn_gen_bl(pc, old_addr); old = larch_insn_gen_bl(pc, old_addr);
...@@ -151,24 +175,11 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -151,24 +175,11 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{ {
u32 old, new; u32 old, new;
unsigned long pc; unsigned long pc;
long offset __maybe_unused;
pc = rec->ip + LOONGARCH_INSN_SIZE; pc = rec->ip + LOONGARCH_INSN_SIZE;
#ifdef CONFIG_MODULES if (!ftrace_find_callable_addr(rec, NULL, &addr))
offset = (long)pc - (long)addr; return -EINVAL;
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(); old = larch_insn_gen_nop();
new = larch_insn_gen_bl(pc, addr); new = larch_insn_gen_bl(pc, addr);
...@@ -180,24 +191,11 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long ad ...@@ -180,24 +191,11 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long ad
{ {
u32 old, new; u32 old, new;
unsigned long pc; unsigned long pc;
long offset __maybe_unused;
pc = rec->ip + LOONGARCH_INSN_SIZE; pc = rec->ip + LOONGARCH_INSN_SIZE;
#ifdef CONFIG_MODULES if (!ftrace_find_callable_addr(rec, NULL, &addr))
offset = (long)pc - (long)addr; return -EINVAL;
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(); new = larch_insn_gen_nop();
old = larch_insn_gen_bl(pc, addr); old = larch_insn_gen_bl(pc, addr);
......
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