linux/arch/microblaze/kernel/ftrace.c
<<
>>
Prefs
   1/*
   2 * Ftrace support for Microblaze.
   3 *
   4 * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
   5 * Copyright (C) 2009 PetaLogix
   6 *
   7 * Based on MIPS and PowerPC ftrace code
   8 *
   9 * This file is subject to the terms and conditions of the GNU General Public
  10 * License. See the file "COPYING" in the main directory of this archive
  11 * for more details.
  12 */
  13
  14#include <asm/cacheflush.h>
  15#include <linux/ftrace.h>
  16
  17#ifdef CONFIG_FUNCTION_GRAPH_TRACER
  18/*
  19 * Hook the return address and push it in the stack of return addrs
  20 * in current thread info.
  21 */
  22void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
  23{
  24        unsigned long old;
  25        int faulted, err;
  26        struct ftrace_graph_ent trace;
  27        unsigned long return_hooker = (unsigned long)
  28                                &return_to_handler;
  29
  30        if (unlikely(atomic_read(&current->tracing_graph_pause)))
  31                return;
  32
  33        /*
  34         * Protect against fault, even if it shouldn't
  35         * happen. This tool is too much intrusive to
  36         * ignore such a protection.
  37         */
  38        asm volatile("  1:      lwi     %0, %2, 0;              \
  39                        2:      swi     %3, %2, 0;              \
  40                                addik   %1, r0, 0;              \
  41                        3:                                      \
  42                                .section .fixup, \"ax\";        \
  43                        4:      brid    3b;                     \
  44                                addik   %1, r0, 1;              \
  45                                .previous;                      \
  46                                .section __ex_table,\"a\";      \
  47                                .word   1b,4b;                  \
  48                                .word   2b,4b;                  \
  49                                .previous;"                     \
  50                        : "=&r" (old), "=r" (faulted)
  51                        : "r" (parent), "r" (return_hooker)
  52        );
  53
  54        if (unlikely(faulted)) {
  55                ftrace_graph_stop();
  56                WARN_ON(1);
  57                return;
  58        }
  59
  60        err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0);
  61        if (err == -EBUSY) {
  62                *parent = old;
  63                return;
  64        }
  65
  66        trace.func = self_addr;
  67        /* Only trace if the calling function expects to */
  68        if (!ftrace_graph_entry(&trace)) {
  69                current->curr_ret_stack--;
  70                *parent = old;
  71        }
  72}
  73#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
  74
  75#ifdef CONFIG_DYNAMIC_FTRACE
  76/* save value to addr - it is save to do it in asm */
  77static int ftrace_modify_code(unsigned long addr, unsigned int value)
  78{
  79        int faulted = 0;
  80
  81        __asm__ __volatile__("  1:      swi     %2, %1, 0;              \
  82                                        addik   %0, r0, 0;              \
  83                                2:                                      \
  84                                        .section .fixup, \"ax\";        \
  85                                3:      brid    2b;                     \
  86                                        addik   %0, r0, 1;              \
  87                                        .previous;                      \
  88                                        .section __ex_table,\"a\";      \
  89                                        .word   1b,3b;                  \
  90                                        .previous;"                     \
  91                                : "=r" (faulted)
  92                                : "r" (addr), "r" (value)
  93        );
  94
  95        if (unlikely(faulted))
  96                return -EFAULT;
  97
  98        return 0;
  99}
 100
 101#define MICROBLAZE_NOP 0x80000000
 102#define MICROBLAZE_BRI 0xb800000C
 103
 104static unsigned int recorded; /* if save was or not */
 105static unsigned int imm; /* saving whole imm instruction */
 106
 107/* There are two approaches howto solve ftrace_make nop function - look below */
 108#undef USE_FTRACE_NOP
 109
 110#ifdef USE_FTRACE_NOP
 111static unsigned int bralid; /* saving whole bralid instruction */
 112#endif
 113
 114int ftrace_make_nop(struct module *mod,
 115                        struct dyn_ftrace *rec, unsigned long addr)
 116{
 117        /* we have this part of code which we are working with
 118         * b000c000        imm     -16384
 119         * b9fc8e30        bralid  r15, -29136     // c0008e30 <_mcount>
 120         * 80000000        or      r0, r0, r0
 121         *
 122         * The first solution (!USE_FTRACE_NOP-could be called branch solution)
 123         * b000c000        bri  12 (0xC - jump to any other instruction)
 124         * b9fc8e30        bralid  r15, -29136     // c0008e30 <_mcount>
 125         * 80000000        or      r0, r0, r0
 126         * any other instruction
 127         *
 128         * The second solution (USE_FTRACE_NOP) - no jump just nops
 129         * 80000000        or      r0, r0, r0
 130         * 80000000        or      r0, r0, r0
 131         * 80000000        or      r0, r0, r0
 132         */
 133        int ret = 0;
 134
 135        if (recorded == 0) {
 136                recorded = 1;
 137                imm = *(unsigned int *)rec->ip;
 138                pr_debug("%s: imm:0x%x\n", __func__, imm);
 139#ifdef USE_FTRACE_NOP
 140                bralid = *(unsigned int *)(rec->ip + 4);
 141                pr_debug("%s: bralid 0x%x\n", __func__, bralid);
 142#endif /* USE_FTRACE_NOP */
 143        }
 144
 145#ifdef USE_FTRACE_NOP
 146        ret = ftrace_modify_code(rec->ip, MICROBLAZE_NOP);
 147        ret += ftrace_modify_code(rec->ip + 4, MICROBLAZE_NOP);
 148#else /* USE_FTRACE_NOP */
 149        ret = ftrace_modify_code(rec->ip, MICROBLAZE_BRI);
 150#endif /* USE_FTRACE_NOP */
 151        return ret;
 152}
 153
 154/* I believe that first is called ftrace_make_nop before this function */
 155int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 156{
 157        int ret;
 158        pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n",
 159                __func__, (unsigned int)addr, (unsigned int)rec->ip, imm);
 160        ret = ftrace_modify_code(rec->ip, imm);
 161#ifdef USE_FTRACE_NOP
 162        pr_debug("%s: bralid:0x%x\n", __func__, bralid);
 163        ret += ftrace_modify_code(rec->ip + 4, bralid);
 164#endif /* USE_FTRACE_NOP */
 165        return ret;
 166}
 167
 168int __init ftrace_dyn_arch_init(void *data)
 169{
 170        /* The return code is retured via data */
 171        *(unsigned long *)data = 0;
 172
 173        return 0;
 174}
 175
 176int ftrace_update_ftrace_func(ftrace_func_t func)
 177{
 178        unsigned long ip = (unsigned long)(&ftrace_call);
 179        unsigned int upper = (unsigned int)func;
 180        unsigned int lower = (unsigned int)func;
 181        int ret = 0;
 182
 183        /* create proper saving to ftrace_call poll */
 184        upper = 0xb0000000 + (upper >> 16); /* imm func_upper */
 185        lower = 0x32800000 + (lower & 0xFFFF); /* addik r20, r0, func_lower */
 186
 187        pr_debug("%s: func=0x%x, ip=0x%x, upper=0x%x, lower=0x%x\n",
 188                __func__, (unsigned int)func, (unsigned int)ip, upper, lower);
 189
 190        /* save upper and lower code */
 191        ret = ftrace_modify_code(ip, upper);
 192        ret += ftrace_modify_code(ip + 4, lower);
 193
 194        /* We just need to replace the rtsd r15, 8 with NOP */
 195        ret += ftrace_modify_code((unsigned long)&ftrace_caller,
 196                                  MICROBLAZE_NOP);
 197
 198        /* All changes are done - lets do caches consistent */
 199        flush_icache();
 200        return ret;
 201}
 202
 203#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 204unsigned int old_jump; /* saving place for jump instruction */
 205
 206int ftrace_enable_ftrace_graph_caller(void)
 207{
 208        unsigned int ret;
 209        unsigned long ip = (unsigned long)(&ftrace_call_graph);
 210
 211        old_jump = *(unsigned int *)ip; /* save jump over instruction */
 212        ret = ftrace_modify_code(ip, MICROBLAZE_NOP);
 213        flush_icache();
 214
 215        pr_debug("%s: Replace instruction: 0x%x\n", __func__, old_jump);
 216        return ret;
 217}
 218
 219int ftrace_disable_ftrace_graph_caller(void)
 220{
 221        unsigned int ret;
 222        unsigned long ip = (unsigned long)(&ftrace_call_graph);
 223
 224        ret = ftrace_modify_code(ip, old_jump);
 225        flush_icache();
 226
 227        pr_debug("%s\n", __func__);
 228        return ret;
 229}
 230#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 231#endif /* CONFIG_DYNAMIC_FTRACE */
 232