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)
}
#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();
*mod = __module_text_address(addr);
preempt_enable();
if (WARN_ON(!(*mod)))
return -EINVAL;
long offset = (long)addr - (long)pc;
return 0;
return offset >= -SZ_128M && offset < SZ_128M;
}
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;
}
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;
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) {
pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
return -EINVAL;
pr_err("ftrace: no module PLT for %ps\n", (void *)*addr);
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
......@@ -75,26 +111,14 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned
{
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);
if (!ftrace_find_callable_addr(rec, NULL, &addr))
return -EINVAL;
old_addr = get_plt_addr(mod, old_addr);
}
#endif
if (!ftrace_find_callable_addr(rec, NULL, &old_addr))
return -EINVAL;
new = larch_insn_gen_bl(pc, addr);
old = larch_insn_gen_bl(pc, old_addr);
......@@ -151,24 +175,11 @@ 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
if (!ftrace_find_callable_addr(rec, NULL, &addr))
return -EINVAL;
old = larch_insn_gen_nop();
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
{
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
if (!ftrace_find_callable_addr(rec, NULL, &addr))
return -EINVAL;
new = larch_insn_gen_nop();
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