linux/arch/nds32/kernel/ftrace.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3#include <linux/ftrace.h>
   4#include <linux/uaccess.h>
   5#include <asm/cacheflush.h>
   6
   7#ifndef CONFIG_DYNAMIC_FTRACE
   8extern void (*ftrace_trace_function)(unsigned long, unsigned long,
   9                                     struct ftrace_ops*, struct pt_regs*);
  10extern void ftrace_graph_caller(void);
  11
  12noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
  13                                  struct ftrace_ops *op, struct pt_regs *regs)
  14{
  15        __asm__ ("");  /* avoid to optimize as pure function */
  16}
  17
  18noinline void _mcount(unsigned long parent_ip)
  19{
  20        /* save all state by the compiler prologue */
  21
  22        unsigned long ip = (unsigned long)__builtin_return_address(0);
  23
  24        if (ftrace_trace_function != ftrace_stub)
  25                ftrace_trace_function(ip - MCOUNT_INSN_SIZE, parent_ip,
  26                                      NULL, NULL);
  27
  28#ifdef CONFIG_FUNCTION_GRAPH_TRACER
  29        if (ftrace_graph_return != (trace_func_graph_ret_t)ftrace_stub
  30            || ftrace_graph_entry != ftrace_graph_entry_stub)
  31                ftrace_graph_caller();
  32#endif
  33
  34        /* restore all state by the compiler epilogue */
  35}
  36EXPORT_SYMBOL(_mcount);
  37
  38#else /* CONFIG_DYNAMIC_FTRACE */
  39
  40noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
  41                                  struct ftrace_ops *op, struct pt_regs *regs)
  42{
  43        __asm__ ("");  /* avoid to optimize as pure function */
  44}
  45
  46noinline void __naked _mcount(unsigned long parent_ip)
  47{
  48        __asm__ ("");  /* avoid to optimize as pure function */
  49}
  50EXPORT_SYMBOL(_mcount);
  51
  52#define XSTR(s) STR(s)
  53#define STR(s) #s
  54void _ftrace_caller(unsigned long parent_ip)
  55{
  56        /* save all state needed by the compiler prologue */
  57
  58        /*
  59         * prepare arguments for real tracing function
  60         * first  arg : __builtin_return_address(0) - MCOUNT_INSN_SIZE
  61         * second arg : parent_ip
  62         */
  63        __asm__ __volatile__ (
  64                "move $r1, %0                              \n\t"
  65                "addi $r0, %1, #-" XSTR(MCOUNT_INSN_SIZE) "\n\t"
  66                :
  67                : "r" (parent_ip), "r" (__builtin_return_address(0)));
  68
  69        /* a placeholder for the call to a real tracing function */
  70        __asm__ __volatile__ (
  71                "ftrace_call:           \n\t"
  72                "nop                    \n\t"
  73                "nop                    \n\t"
  74                "nop                    \n\t");
  75
  76#ifdef CONFIG_FUNCTION_GRAPH_TRACER
  77        /* a placeholder for the call to ftrace_graph_caller */
  78        __asm__ __volatile__ (
  79                "ftrace_graph_call:     \n\t"
  80                "nop                    \n\t"
  81                "nop                    \n\t"
  82                "nop                    \n\t");
  83#endif
  84        /* restore all state needed by the compiler epilogue */
  85}
  86
  87int __init ftrace_dyn_arch_init(void)
  88{
  89        return 0;
  90}
  91
  92int ftrace_arch_code_modify_prepare(void)
  93{
  94        set_all_modules_text_rw();
  95        return 0;
  96}
  97
  98int ftrace_arch_code_modify_post_process(void)
  99{
 100        set_all_modules_text_ro();
 101        return 0;
 102}
 103
 104static unsigned long gen_sethi_insn(unsigned long addr)
 105{
 106        unsigned long opcode = 0x46000000;
 107        unsigned long imm = addr >> 12;
 108        unsigned long rt_num = 0xf << 20;
 109
 110        return ENDIAN_CONVERT(opcode | rt_num | imm);
 111}
 112
 113static unsigned long gen_ori_insn(unsigned long addr)
 114{
 115        unsigned long opcode = 0x58000000;
 116        unsigned long imm = addr & 0x0000fff;
 117        unsigned long rt_num = 0xf << 20;
 118        unsigned long ra_num = 0xf << 15;
 119
 120        return ENDIAN_CONVERT(opcode | rt_num | ra_num | imm);
 121}
 122
 123static unsigned long gen_jral_insn(unsigned long addr)
 124{
 125        unsigned long opcode = 0x4a000001;
 126        unsigned long rt_num = 0x1e << 20;
 127        unsigned long rb_num = 0xf << 10;
 128
 129        return ENDIAN_CONVERT(opcode | rt_num | rb_num);
 130}
 131
 132static void ftrace_gen_call_insn(unsigned long *call_insns,
 133                                 unsigned long addr)
 134{
 135        call_insns[0] = gen_sethi_insn(addr); /* sethi $r15, imm20u       */
 136        call_insns[1] = gen_ori_insn(addr);   /* ori   $r15, $r15, imm15u */
 137        call_insns[2] = gen_jral_insn(addr);  /* jral  $lp,  $r15         */
 138}
 139
 140static int __ftrace_modify_code(unsigned long pc, unsigned long *old_insn,
 141                                unsigned long *new_insn, bool validate)
 142{
 143        unsigned long orig_insn[3];
 144
 145        if (validate) {
 146                if (probe_kernel_read(orig_insn, (void *)pc, MCOUNT_INSN_SIZE))
 147                        return -EFAULT;
 148                if (memcmp(orig_insn, old_insn, MCOUNT_INSN_SIZE))
 149                        return -EINVAL;
 150        }
 151
 152        if (probe_kernel_write((void *)pc, new_insn, MCOUNT_INSN_SIZE))
 153                return -EPERM;
 154
 155        return 0;
 156}
 157
 158static int ftrace_modify_code(unsigned long pc, unsigned long *old_insn,
 159                              unsigned long *new_insn, bool validate)
 160{
 161        int ret;
 162
 163        ret = __ftrace_modify_code(pc, old_insn, new_insn, validate);
 164        if (ret)
 165                return ret;
 166
 167        flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
 168
 169        return ret;
 170}
 171
 172int ftrace_update_ftrace_func(ftrace_func_t func)
 173{
 174        unsigned long pc = (unsigned long)&ftrace_call;
 175        unsigned long old_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
 176        unsigned long new_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
 177
 178        if (func != ftrace_stub)
 179                ftrace_gen_call_insn(new_insn, (unsigned long)func);
 180
 181        return ftrace_modify_code(pc, old_insn, new_insn, false);
 182}
 183
 184int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 185{
 186        unsigned long pc = rec->ip;
 187        unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
 188        unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
 189
 190        ftrace_gen_call_insn(call_insn, addr);
 191
 192        return ftrace_modify_code(pc, nop_insn, call_insn, true);
 193}
 194
 195int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
 196                    unsigned long addr)
 197{
 198        unsigned long pc = rec->ip;
 199        unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
 200        unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
 201
 202        ftrace_gen_call_insn(call_insn, addr);
 203
 204        return ftrace_modify_code(pc, call_insn, nop_insn, true);
 205}
 206#endif /* CONFIG_DYNAMIC_FTRACE */
 207
 208#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 209void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
 210                           unsigned long frame_pointer)
 211{
 212        unsigned long return_hooker = (unsigned long)&return_to_handler;
 213        unsigned long old;
 214
 215        if (unlikely(atomic_read(&current->tracing_graph_pause)))
 216                return;
 217
 218        old = *parent;
 219
 220        if (!function_graph_enter(old, self_addr, frame_pointer, NULL))
 221                *parent = return_hooker;
 222}
 223
 224noinline void ftrace_graph_caller(void)
 225{
 226        unsigned long *parent_ip =
 227                (unsigned long *)(__builtin_frame_address(2) - 4);
 228
 229        unsigned long selfpc =
 230                (unsigned long)(__builtin_return_address(1) - MCOUNT_INSN_SIZE);
 231
 232        unsigned long frame_pointer =
 233                (unsigned long)__builtin_frame_address(3);
 234
 235        prepare_ftrace_return(parent_ip, selfpc, frame_pointer);
 236}
 237
 238extern unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
 239void __naked return_to_handler(void)
 240{
 241        __asm__ __volatile__ (
 242                /* save state needed by the ABI     */
 243                "smw.adm $r0,[$sp],$r1,#0x0  \n\t"
 244
 245                /* get original return address      */
 246                "move $r0, $fp               \n\t"
 247                "bal ftrace_return_to_handler\n\t"
 248                "move $lp, $r0               \n\t"
 249
 250                /* restore state nedded by the ABI  */
 251                "lmw.bim $r0,[$sp],$r1,#0x0  \n\t");
 252}
 253
 254#ifdef CONFIG_DYNAMIC_FTRACE
 255extern unsigned long ftrace_graph_call;
 256
 257static int ftrace_modify_graph_caller(bool enable)
 258{
 259        unsigned long pc = (unsigned long)&ftrace_graph_call;
 260        unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
 261        unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
 262
 263        ftrace_gen_call_insn(call_insn, (unsigned long)ftrace_graph_caller);
 264
 265        if (enable)
 266                return ftrace_modify_code(pc, nop_insn, call_insn, true);
 267        else
 268                return ftrace_modify_code(pc, call_insn, nop_insn, true);
 269}
 270
 271int ftrace_enable_ftrace_graph_caller(void)
 272{
 273        return ftrace_modify_graph_caller(true);
 274}
 275
 276int ftrace_disable_ftrace_graph_caller(void)
 277{
 278        return ftrace_modify_graph_caller(false);
 279}
 280#endif /* CONFIG_DYNAMIC_FTRACE */
 281
 282#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 283
 284
 285#ifdef CONFIG_TRACE_IRQFLAGS
 286noinline void __trace_hardirqs_off(void)
 287{
 288        trace_hardirqs_off();
 289}
 290noinline void __trace_hardirqs_on(void)
 291{
 292        trace_hardirqs_on();
 293}
 294#endif /* CONFIG_TRACE_IRQFLAGS */
 295