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: Youling Tang <tangyouling@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
This commit is contained in:
parent
819cf65575
commit
24d4f52791
@ -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();
|
||||
long offset = (long)addr - (long)pc;
|
||||
|
||||
if (WARN_ON(!(*mod)))
|
||||
return -EINVAL;
|
||||
|
||||
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);
|
||||
if (!plt) {
|
||||
pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
|
||||
return -EINVAL;
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
return (unsigned long)plt;
|
||||
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 false;
|
||||
}
|
||||
|
||||
*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 (!ftrace_find_callable_addr(rec, NULL, &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);
|
||||
}
|
||||
#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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user