linux/arch/arm/kernel/ftrace.c
<<
>>
Prefs
   1/*
   2 * Dynamic function tracing support.
   3 *
   4 * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com>
   5 * Copyright (C) 2010 Rabin Vincent <rabin@rab.in>
   6 *
   7 * For licencing details, see COPYING.
   8 *
   9 * Defines low-level handling of mcount calls when the kernel
  10 * is compiled with the -pg flag. When using dynamic ftrace, the
  11 * mcount call-sites get patched with NOP till they are enabled.
  12 * All code mutation routines here are called under stop_machine().
  13 */
  14
  15#include <linux/ftrace.h>
  16#include <linux/uaccess.h>
  17#include <linux/module.h>
  18#include <linux/stop_machine.h>
  19
  20#include <asm/cacheflush.h>
  21#include <asm/opcodes.h>
  22#include <asm/ftrace.h>
  23#include <asm/insn.h>
  24#include <asm/set_memory.h>
  25#include <asm/patch.h>
  26
  27#ifdef CONFIG_THUMB2_KERNEL
  28#define NOP             0xf85deb04      /* pop.w {lr} */
  29#else
  30#define NOP             0xe8bd4000      /* pop {lr} */
  31#endif
  32
  33#ifdef CONFIG_DYNAMIC_FTRACE
  34
  35static int __ftrace_modify_code(void *data)
  36{
  37        int *command = data;
  38
  39        ftrace_modify_all_code(*command);
  40
  41        return 0;
  42}
  43
  44void arch_ftrace_update_code(int command)
  45{
  46        stop_machine(__ftrace_modify_code, &command, NULL);
  47}
  48
  49static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
  50{
  51        return NOP;
  52}
  53
  54static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
  55{
  56        return addr;
  57}
  58
  59int ftrace_arch_code_modify_prepare(void)
  60{
  61        return 0;
  62}
  63
  64int ftrace_arch_code_modify_post_process(void)
  65{
  66        /* Make sure any TLB misses during machine stop are cleared. */
  67        flush_tlb_all();
  68        return 0;
  69}
  70
  71static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr,
  72                                         bool warn)
  73{
  74        return arm_gen_branch_link(pc, addr, warn);
  75}
  76
  77static int ftrace_modify_code(unsigned long pc, unsigned long old,
  78                              unsigned long new, bool validate)
  79{
  80        unsigned long replaced;
  81
  82        if (IS_ENABLED(CONFIG_THUMB2_KERNEL))
  83                old = __opcode_to_mem_thumb32(old);
  84        else
  85                old = __opcode_to_mem_arm(old);
  86
  87        if (validate) {
  88                if (copy_from_kernel_nofault(&replaced, (void *)pc,
  89                                MCOUNT_INSN_SIZE))
  90                        return -EFAULT;
  91
  92                if (replaced != old)
  93                        return -EINVAL;
  94        }
  95
  96        __patch_text((void *)pc, new);
  97
  98        return 0;
  99}
 100
 101int ftrace_update_ftrace_func(ftrace_func_t func)
 102{
 103        unsigned long pc;
 104        unsigned long new;
 105        int ret;
 106
 107        pc = (unsigned long)&ftrace_call;
 108        new = ftrace_call_replace(pc, (unsigned long)func, true);
 109
 110        ret = ftrace_modify_code(pc, 0, new, false);
 111
 112#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
 113        if (!ret) {
 114                pc = (unsigned long)&ftrace_regs_call;
 115                new = ftrace_call_replace(pc, (unsigned long)func, true);
 116
 117                ret = ftrace_modify_code(pc, 0, new, false);
 118        }
 119#endif
 120
 121        return ret;
 122}
 123
 124int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 125{
 126        unsigned long new, old;
 127        unsigned long ip = rec->ip;
 128        unsigned long aaddr = adjust_address(rec, addr);
 129        struct module *mod = NULL;
 130
 131#ifdef CONFIG_ARM_MODULE_PLTS
 132        mod = rec->arch.mod;
 133#endif
 134
 135        old = ftrace_nop_replace(rec);
 136
 137        new = ftrace_call_replace(ip, aaddr, !mod);
 138#ifdef CONFIG_ARM_MODULE_PLTS
 139        if (!new && mod) {
 140                aaddr = get_module_plt(mod, ip, aaddr);
 141                new = ftrace_call_replace(ip, aaddr, true);
 142        }
 143#endif
 144
 145        return ftrace_modify_code(rec->ip, old, new, true);
 146}
 147
 148#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
 149
 150int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
 151                                unsigned long addr)
 152{
 153        unsigned long new, old;
 154        unsigned long ip = rec->ip;
 155
 156        old = ftrace_call_replace(ip, adjust_address(rec, old_addr), true);
 157
 158        new = ftrace_call_replace(ip, adjust_address(rec, addr), true);
 159
 160        return ftrace_modify_code(rec->ip, old, new, true);
 161}
 162
 163#endif
 164
 165int ftrace_make_nop(struct module *mod,
 166                    struct dyn_ftrace *rec, unsigned long addr)
 167{
 168        unsigned long aaddr = adjust_address(rec, addr);
 169        unsigned long ip = rec->ip;
 170        unsigned long old;
 171        unsigned long new;
 172        int ret;
 173
 174#ifdef CONFIG_ARM_MODULE_PLTS
 175        /* mod is only supplied during module loading */
 176        if (!mod)
 177                mod = rec->arch.mod;
 178        else
 179                rec->arch.mod = mod;
 180#endif
 181
 182        old = ftrace_call_replace(ip, aaddr,
 183                                  !IS_ENABLED(CONFIG_ARM_MODULE_PLTS) || !mod);
 184#ifdef CONFIG_ARM_MODULE_PLTS
 185        if (!old && mod) {
 186                aaddr = get_module_plt(mod, ip, aaddr);
 187                old = ftrace_call_replace(ip, aaddr, true);
 188        }
 189#endif
 190
 191        new = ftrace_nop_replace(rec);
 192        ret = ftrace_modify_code(ip, old, new, true);
 193
 194        return ret;
 195}
 196
 197int __init ftrace_dyn_arch_init(void)
 198{
 199        return 0;
 200}
 201#endif /* CONFIG_DYNAMIC_FTRACE */
 202
 203#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 204void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
 205                           unsigned long frame_pointer)
 206{
 207        unsigned long return_hooker = (unsigned long) &return_to_handler;
 208        unsigned long old;
 209
 210        if (unlikely(atomic_read(&current->tracing_graph_pause)))
 211                return;
 212
 213        old = *parent;
 214        *parent = return_hooker;
 215
 216        if (function_graph_enter(old, self_addr, frame_pointer, NULL))
 217                *parent = old;
 218}
 219
 220#ifdef CONFIG_DYNAMIC_FTRACE
 221extern unsigned long ftrace_graph_call;
 222extern unsigned long ftrace_graph_call_old;
 223extern void ftrace_graph_caller_old(void);
 224extern unsigned long ftrace_graph_regs_call;
 225extern void ftrace_graph_regs_caller(void);
 226
 227static int __ftrace_modify_caller(unsigned long *callsite,
 228                                  void (*func) (void), bool enable)
 229{
 230        unsigned long caller_fn = (unsigned long) func;
 231        unsigned long pc = (unsigned long) callsite;
 232        unsigned long branch = arm_gen_branch(pc, caller_fn);
 233        unsigned long nop = 0xe1a00000; /* mov r0, r0 */
 234        unsigned long old = enable ? nop : branch;
 235        unsigned long new = enable ? branch : nop;
 236
 237        return ftrace_modify_code(pc, old, new, true);
 238}
 239
 240static int ftrace_modify_graph_caller(bool enable)
 241{
 242        int ret;
 243
 244        ret = __ftrace_modify_caller(&ftrace_graph_call,
 245                                     ftrace_graph_caller,
 246                                     enable);
 247
 248#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
 249        if (!ret)
 250                ret = __ftrace_modify_caller(&ftrace_graph_regs_call,
 251                                     ftrace_graph_regs_caller,
 252                                     enable);
 253#endif
 254
 255
 256        return ret;
 257}
 258
 259int ftrace_enable_ftrace_graph_caller(void)
 260{
 261        return ftrace_modify_graph_caller(true);
 262}
 263
 264int ftrace_disable_ftrace_graph_caller(void)
 265{
 266        return ftrace_modify_graph_caller(false);
 267}
 268#endif /* CONFIG_DYNAMIC_FTRACE */
 269#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 270