lkml.org 
[lkml]   [2022]   [Nov]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v6 8/9] LoongArch: modules/ftrace: Initialize PLT at load time
    Date
    To Implement ftrace trampiones 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 its use along with most of module_init_ftrace_plt() with
    ifdeffery rather than using IS_ENABLED().

    Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
    ---
    arch/loongarch/include/asm/ftrace.h | 4 ++
    arch/loongarch/include/asm/inst.h | 3 +
    arch/loongarch/include/asm/module.h | 5 +-
    arch/loongarch/include/asm/module.lds.h | 1 +
    arch/loongarch/kernel/ftrace_dyn.c | 79 +++++++++++++++++++++++++
    arch/loongarch/kernel/inst.c | 11 ++++
    arch/loongarch/kernel/module-sections.c | 11 ++++
    arch/loongarch/kernel/module.c | 22 +++++++
    8 files changed, 135 insertions(+), 1 deletion(-)

    diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
    index 1583ec2171fc..8d194f6da8af 100644
    --- a/arch/loongarch/include/asm/ftrace.h
    +++ b/arch/loongarch/include/asm/ftrace.h
    @@ -8,6 +8,10 @@

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

    +#define FTRACE_PLT_IDX 0
    +#define FTRACE_REGS_PLT_IDX 1
    +#define NR_FTRACE_PLTS 2
    +
    #ifdef CONFIG_FUNCTION_TRACER
    #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */

    diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
    index 0ec775d39ca5..3c13fb673f1e 100644
    --- a/arch/loongarch/include/asm/inst.h
    +++ b/arch/loongarch/include/asm/inst.h
    @@ -16,10 +16,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)
    @@ -364,6 +366,7 @@ 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);
    diff --git a/arch/loongarch/include/asm/module.h b/arch/loongarch/include/asm/module.h
    index b29b19a46f42..a311cfec2b23 100644
    --- a/arch/loongarch/include/asm/module.h
    +++ b/arch/loongarch/include/asm/module.h
    @@ -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));
    diff --git a/arch/loongarch/include/asm/module.lds.h b/arch/loongarch/include/asm/module.lds.h
    index a3d1bc0fcc72..438f09d4ccf4 100644
    --- a/arch/loongarch/include/asm/module.lds.h
    +++ b/arch/loongarch/include/asm/module.lds.h
    @@ -5,4 +5,5 @@ SECTIONS {
    .got : { BYTE(0) }
    .plt : { BYTE(0) }
    .plt.idx : { BYTE(0) }
    + .ftrace_trampoline : { BYTE(0) }
    }
    diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
    index cd64887e26b5..b070ce225989 100644
    --- a/arch/loongarch/kernel/ftrace_dyn.c
    +++ b/arch/loongarch/kernel/ftrace_dyn.c
    @@ -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)
    @@ -72,12 +73,63 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
    return ftrace_modify_code(pc, old, new, true);
    }

    +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;
    +}
    +
    int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
    {
    unsigned long pc;
    + long offset;
    u32 old, new;

    pc = rec->ip + LOONGARCH_INSN_SIZE;
    + 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 = larch_insn_gen_nop();
    new = larch_insn_gen_bl(pc, addr);
    @@ -89,9 +141,22 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
    unsigned long addr)
    {
    unsigned long pc;
    + long offset;
    u32 old, new;

    pc = rec->ip + LOONGARCH_INSN_SIZE;
    + 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);
    + }

    new = larch_insn_gen_nop();
    old = larch_insn_gen_bl(pc, addr);
    @@ -108,6 +173,20 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
    u32 old, new;

    pc = rec->ip + LOONGARCH_INSN_SIZE;
    + 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);
    + }

    old = larch_insn_gen_bl(pc, old_addr);
    new = larch_insn_gen_bl(pc, addr);
    diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
    index 2d2e942eb06a..0d6bd7000ba6 100644
    --- a/arch/loongarch/kernel/inst.c
    +++ b/arch/loongarch/kernel/inst.c
    @@ -103,6 +103,17 @@ u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
    return insn.word;
    }

    +u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
    +{
    + union loongarch_instruction insn;
    +
    + insn.reg1i20_format.opcode = lu12iw_op;
    + insn.reg1i20_format.rd = rd;
    + insn.reg1i20_format.immediate = imm;
    +
    + return insn.word;
    +}
    +
    u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
    {
    union loongarch_instruction insn;
    diff --git a/arch/loongarch/kernel/module-sections.c b/arch/loongarch/kernel/module-sections.c
    index d296a70b758f..bd1a96691c98 100644
    --- a/arch/loongarch/kernel/module-sections.c
    +++ b/arch/loongarch/kernel/module-sections.c
    @@ -4,6 +4,7 @@
    */

    #include <linux/elf.h>
    +#include <linux/ftrace.h>
    #include <linux/kernel.h>
    #include <linux/module.h>

    @@ -103,6 +104,7 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
    char *secstrings, struct module *mod)
    {
    unsigned int i, num_plts = 0, num_gots = 0;
    + Elf_Shdr *tramp = NULL;

    /*
    * Find the empty .plt sections.
    @@ -114,6 +116,8 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
    mod->arch.plt.shdr = sechdrs + i;
    else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx"))
    mod->arch.plt_idx.shdr = sechdrs + i;
    + else if (!strcmp(secstrings + sechdrs[i].sh_name, ".ftrace_trampoline"))
    + tramp = sechdrs + i;
    }

    if (!mod->arch.got.shdr) {
    @@ -166,5 +170,12 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
    mod->arch.plt_idx.num_entries = 0;
    mod->arch.plt_idx.max_entries = num_plts;

    + if (tramp) {
    + tramp->sh_type = SHT_NOBITS;
    + tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
    + tramp->sh_addralign = __alignof__(struct plt_entry);
    + tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry);
    + }
    +
    return 0;
    }
    diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c
    index 825fcf77f9e7..a606a0cab968 100644
    --- a/arch/loongarch/kernel/module.c
    +++ b/arch/loongarch/kernel/module.c
    @@ -10,6 +10,7 @@

    #include <linux/moduleloader.h>
    #include <linux/elf.h>
    +#include <linux/ftrace.h>
    #include <linux/mm.h>
    #include <linux/numa.h>
    #include <linux/vmalloc.h>
    @@ -18,6 +19,7 @@
    #include <linux/string.h>
    #include <linux/kernel.h>
    #include <asm/alternative.h>
    +#include <asm/inst.h>

    static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
    {
    @@ -458,6 +460,24 @@ void *module_alloc(unsigned long size)
    GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0));
    }

    +static int module_init_ftrace_plt(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
    + struct module *mod)
    +{
    +#ifdef CONFIG_DYNAMIC_FTRACE
    + struct plt_entry *ftrace_plts;
    +
    + ftrace_plts = (void *)sechdrs->sh_addr;
    +
    + ftrace_plts[FTRACE_PLT_IDX] = emit_plt_entry(FTRACE_ADDR);
    +
    + if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
    + ftrace_plts[FTRACE_REGS_PLT_IDX] = emit_plt_entry(FTRACE_REGS_ADDR);
    +
    + mod->arch.ftrace_trampolines = ftrace_plts;
    +#endif
    + return 0;
    +}
    +
    int module_finalize(const Elf_Ehdr *hdr,
    const Elf_Shdr *sechdrs, struct module *mod)
    {
    @@ -467,6 +487,8 @@ int module_finalize(const Elf_Ehdr *hdr,
    for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
    if (!strcmp(".altinstructions", secstrs + s->sh_name))
    apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size);
    + if (!strcmp(".ftrace_trampoline", secstrs + s->sh_name))
    + return module_init_ftrace_plt(hdr, s, mod);
    }

    return 0;
    --
    2.36.0
    \
     
     \ /
      Last update: 2022-11-15 03:58    [W:2.967 / U:0.032 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site