linux/arch/tile/kernel/ftrace.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Tilera Corporation. All Rights Reserved.
   3 *
   4 *   This program is free software; you can redistribute it and/or
   5 *   modify it under the terms of the GNU General Public License
   6 *   as published by the Free Software Foundation, version 2.
   7 *
   8 *   This program is distributed in the hope that it will be useful, but
   9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  11 *   NON INFRINGEMENT.  See the GNU General Public License for
  12 *   more details.
  13 *
  14 * TILE-Gx specific ftrace support
  15 */
  16
  17#include <linux/ftrace.h>
  18#include <linux/uaccess.h>
  19
  20#include <asm/cacheflush.h>
  21#include <asm/ftrace.h>
  22#include <asm/sections.h>
  23
  24#include <arch/opcode.h>
  25
  26#ifdef CONFIG_DYNAMIC_FTRACE
  27
  28static inline tilegx_bundle_bits NOP(void)
  29{
  30        return create_UnaryOpcodeExtension_X0(FNOP_UNARY_OPCODE_X0) |
  31                create_RRROpcodeExtension_X0(UNARY_RRR_0_OPCODE_X0) |
  32                create_Opcode_X0(RRR_0_OPCODE_X0) |
  33                create_UnaryOpcodeExtension_X1(NOP_UNARY_OPCODE_X1) |
  34                create_RRROpcodeExtension_X1(UNARY_RRR_0_OPCODE_X1) |
  35                create_Opcode_X1(RRR_0_OPCODE_X1);
  36}
  37
  38static int machine_stopped __read_mostly;
  39
  40int ftrace_arch_code_modify_prepare(void)
  41{
  42        machine_stopped = 1;
  43        return 0;
  44}
  45
  46int ftrace_arch_code_modify_post_process(void)
  47{
  48        flush_icache_range(0, CHIP_L1I_CACHE_SIZE());
  49        machine_stopped = 0;
  50        return 0;
  51}
  52
  53/*
  54 * Put { move r10, lr; jal ftrace_caller } in a bundle, this lets dynamic
  55 * tracer just add one cycle overhead to every kernel function when disabled.
  56 */
  57static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr,
  58                                       bool link)
  59{
  60        tilegx_bundle_bits opcode_x0, opcode_x1;
  61        long pcrel_by_instr = (addr - pc) >> TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES;
  62
  63        if (link) {
  64                /* opcode: jal addr */
  65                opcode_x1 =
  66                        create_Opcode_X1(JUMP_OPCODE_X1) |
  67                        create_JumpOpcodeExtension_X1(JAL_JUMP_OPCODE_X1) |
  68                        create_JumpOff_X1(pcrel_by_instr);
  69        } else {
  70                /* opcode: j addr */
  71                opcode_x1 =
  72                        create_Opcode_X1(JUMP_OPCODE_X1) |
  73                        create_JumpOpcodeExtension_X1(J_JUMP_OPCODE_X1) |
  74                        create_JumpOff_X1(pcrel_by_instr);
  75        }
  76
  77        if (addr == FTRACE_ADDR) {
  78                /* opcode: or r10, lr, zero */
  79                opcode_x0 =
  80                        create_Dest_X0(10) |
  81                        create_SrcA_X0(TREG_LR) |
  82                        create_SrcB_X0(TREG_ZERO) |
  83                        create_RRROpcodeExtension_X0(OR_RRR_0_OPCODE_X0) |
  84                        create_Opcode_X0(RRR_0_OPCODE_X0);
  85        } else {
  86                /* opcode: fnop */
  87                opcode_x0 =
  88                        create_UnaryOpcodeExtension_X0(FNOP_UNARY_OPCODE_X0) |
  89                        create_RRROpcodeExtension_X0(UNARY_RRR_0_OPCODE_X0) |
  90                        create_Opcode_X0(RRR_0_OPCODE_X0);
  91        }
  92
  93        return opcode_x1 | opcode_x0;
  94}
  95
  96static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
  97{
  98        return NOP();
  99}
 100
 101static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
 102{
 103        return ftrace_gen_branch(pc, addr, true);
 104}
 105
 106static int ftrace_modify_code(unsigned long pc, unsigned long old,
 107                              unsigned long new)
 108{
 109        unsigned long pc_wr;
 110
 111        /* Check if the address is in kernel text space and module space. */
 112        if (!kernel_text_address(pc))
 113                return -EINVAL;
 114
 115        /* Operate on writable kernel text mapping. */
 116        pc_wr = pc - MEM_SV_START + PAGE_OFFSET;
 117
 118        if (probe_kernel_write((void *)pc_wr, &new, MCOUNT_INSN_SIZE))
 119                return -EPERM;
 120
 121        smp_wmb();
 122
 123        if (!machine_stopped && num_online_cpus() > 1)
 124                flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
 125
 126        return 0;
 127}
 128
 129int ftrace_update_ftrace_func(ftrace_func_t func)
 130{
 131        unsigned long pc, old;
 132        unsigned long new;
 133        int ret;
 134
 135        pc = (unsigned long)&ftrace_call;
 136        memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE);
 137        new = ftrace_call_replace(pc, (unsigned long)func);
 138
 139        ret = ftrace_modify_code(pc, old, new);
 140
 141        return ret;
 142}
 143
 144int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 145{
 146        unsigned long new, old;
 147        unsigned long ip = rec->ip;
 148
 149        old = ftrace_nop_replace(rec);
 150        new = ftrace_call_replace(ip, addr);
 151
 152        return ftrace_modify_code(rec->ip, old, new);
 153}
 154
 155int ftrace_make_nop(struct module *mod,
 156                    struct dyn_ftrace *rec, unsigned long addr)
 157{
 158        unsigned long ip = rec->ip;
 159        unsigned long old;
 160        unsigned long new;
 161        int ret;
 162
 163        old = ftrace_call_replace(ip, addr);
 164        new = ftrace_nop_replace(rec);
 165        ret = ftrace_modify_code(ip, old, new);
 166
 167        return ret;
 168}
 169
 170int __init ftrace_dyn_arch_init(void)
 171{
 172        return 0;
 173}
 174#endif /* CONFIG_DYNAMIC_FTRACE */
 175
 176#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 177void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
 178                           unsigned long frame_pointer)
 179{
 180        unsigned long return_hooker = (unsigned long) &return_to_handler;
 181        struct ftrace_graph_ent trace;
 182        unsigned long old;
 183        int err;
 184
 185        if (unlikely(atomic_read(&current->tracing_graph_pause)))
 186                return;
 187
 188        old = *parent;
 189        *parent = return_hooker;
 190
 191        err = ftrace_push_return_trace(old, self_addr, &trace.depth,
 192                                       frame_pointer);
 193        if (err == -EBUSY) {
 194                *parent = old;
 195                return;
 196        }
 197
 198        trace.func = self_addr;
 199
 200        /* Only trace if the calling function expects to */
 201        if (!ftrace_graph_entry(&trace)) {
 202                current->curr_ret_stack--;
 203                *parent = old;
 204        }
 205}
 206
 207#ifdef CONFIG_DYNAMIC_FTRACE
 208extern unsigned long ftrace_graph_call;
 209
 210static int __ftrace_modify_caller(unsigned long *callsite,
 211                                  void (*func) (void), bool enable)
 212{
 213        unsigned long caller_fn = (unsigned long) func;
 214        unsigned long pc = (unsigned long) callsite;
 215        unsigned long branch = ftrace_gen_branch(pc, caller_fn, false);
 216        unsigned long nop = NOP();
 217        unsigned long old = enable ? nop : branch;
 218        unsigned long new = enable ? branch : nop;
 219
 220        return ftrace_modify_code(pc, old, new);
 221}
 222
 223static int ftrace_modify_graph_caller(bool enable)
 224{
 225        int ret;
 226
 227        ret = __ftrace_modify_caller(&ftrace_graph_call,
 228                                     ftrace_graph_caller,
 229                                     enable);
 230
 231        return ret;
 232}
 233
 234int ftrace_enable_ftrace_graph_caller(void)
 235{
 236        return ftrace_modify_graph_caller(true);
 237}
 238
 239int ftrace_disable_ftrace_graph_caller(void)
 240{
 241        return ftrace_modify_graph_caller(false);
 242}
 243#endif /* CONFIG_DYNAMIC_FTRACE */
 244#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 245