This patch implements DYNAMIC_FTRACE_WITH_REGS on LoongArch, which allows a traced function's arguments (and some other registers) to be captured into a struct pt_regs, allowing these to be inspected and modified. Co-developed-by: Jinyang He <hejinyang@xxxxxxxxxxx> Signed-off-by: Jinyang He <hejinyang@xxxxxxxxxxx> Signed-off-by: Qing Zhang <zhangqing@xxxxxxxxxxx> --- arch/loongarch/Kconfig | 1 + arch/loongarch/include/asm/ftrace.h | 3 +++ arch/loongarch/kernel/entry_dyn.S | 36 +++++++++++++++++++++++++++-- arch/loongarch/kernel/ftrace_dyn.c | 17 ++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index f2d4899b1a0e..22eb3d6f8537 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -84,6 +84,7 @@ config LOONGARCH select HAVE_DEBUG_STACKOVERFLOW select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE + select HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_EXIT_THREAD select HAVE_FAST_GUP select HAVE_FTRACE_MCOUNT_RECORD diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h index 76ca58767f4d..a3f974a7a5ce 100644 --- a/arch/loongarch/include/asm/ftrace.h +++ b/arch/loongarch/include/asm/ftrace.h @@ -28,6 +28,9 @@ struct dyn_ftrace; int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec); #define ftrace_init_nop ftrace_init_nop +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +#define ARCH_SUPPORTS_FTRACE_OPS 1 +#endif #endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* __ASSEMBLY__ */ #endif /* CONFIG_FUNCTION_TRACER */ diff --git a/arch/loongarch/kernel/entry_dyn.S b/arch/loongarch/kernel/entry_dyn.S index 4e3fb0c9a48f..38f616c1b4db 100644 --- a/arch/loongarch/kernel/entry_dyn.S +++ b/arch/loongarch/kernel/entry_dyn.S @@ -27,7 +27,7 @@ * follows the LoongArch psABI well. */ - .macro ftrace_regs_entry + .macro ftrace_regs_entry allregs=0 PTR_ADDI sp, sp, -PT_SIZE /* Save trace function ra at PT_ERA */ PTR_S ra, sp, PT_ERA @@ -43,16 +43,48 @@ PTR_S a7, sp, PT_R11 PTR_S fp, sp, PT_R22 + .if \allregs + PTR_S t0, sp, PT_R12 + PTR_S t1, sp, PT_R13 + PTR_S t2, sp, PT_R14 + PTR_S t3, sp, PT_R15 + PTR_S t4, sp, PT_R16 + PTR_S t5, sp, PT_R17 + PTR_S t6, sp, PT_R18 + PTR_S t7, sp, PT_R19 + PTR_S t8, sp, PT_R20 + PTR_S s0, sp, PT_R23 + PTR_S s1, sp, PT_R24 + PTR_S s2, sp, PT_R25 + PTR_S s3, sp, PT_R26 + PTR_S s4, sp, PT_R27 + PTR_S s5, sp, PT_R28 + PTR_S s6, sp, PT_R29 + PTR_S s7, sp, PT_R30 + PTR_S s8, sp, PT_R31 + PTR_S tp, sp, PT_R2 + /* Clear it for later use as a flag sometimes. */ + PTR_S zero, sp, PT_R0 + PTR_S $r21, sp, PT_R21 + .endif + PTR_ADDI t8, sp, PT_SIZE PTR_S t8, sp, PT_R3 .endm SYM_CODE_START(ftrace_caller) - ftrace_regs_entry + ftrace_regs_entry allregs=0 b ftrace_common SYM_CODE_END(ftrace_caller) +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +SYM_CODE_START(ftrace_regs_caller) + ftrace_regs_entry allregs=1 + b ftrace_common +SYM_CODE_END(ftrace_regs_caller) +#endif + SYM_CODE_START(ftrace_common) PTR_ADDI a0, ra, -8 /* arg0: ip */ move a1, t0 /* arg1: parent_ip */ diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c index 3fe791b6783e..ec3d951be50c 100644 --- a/arch/loongarch/kernel/ftrace_dyn.c +++ b/arch/loongarch/kernel/ftrace_dyn.c @@ -99,6 +99,23 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, return ftrace_modify_code(pc, old, new, true); } +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, + unsigned long addr) +{ + unsigned long pc; + long offset; + u32 old, new; + + pc = rec->ip + LOONGARCH_INSN_SIZE; + + old = larch_insn_gen_bl(pc, old_addr); + new = larch_insn_gen_bl(pc, addr); + + return ftrace_modify_code(pc, old, new, true); +} +#endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */ + void arch_ftrace_update_code(int command) { command |= FTRACE_MAY_SLEEP; -- 2.36.1