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;
  26        unsigned long return_hooker = (unsigned long)
  27                                &return_to_handler;
  28
  29        if (unlikely(ftrace_graph_is_dead()))
  30                return;
  31
  32        if (unlikely(atomic_read(&current->tracing_graph_pause)))
  33                return;
  34
  35        /*
  36         * Protect against fault, even if it shouldn't
  37         * happen. This tool is too much intrusive to
  38         * ignore such a protection.
  39         */
  40        asm volatile("  1:      lwi     %0, %2, 0;"             \
  41                        "2:     swi     %3, %2, 0;"             \
  42                        "       addik   %1, r0, 0;"             \
  43                        "3:"                                    \
  44                        "       .section .fixup, \"ax\";"       \
  45                        "4:     brid    3b;"                    \
  46                        "       addik   %1, r0, 1;"             \
  47                        "       .previous;"                     \
  48                        "       .section __ex_table,\"a\";"     \
  49                        "       .word   1b,4b;"                 \
  50                        "       .word   2b,4b;"                 \
  51                        "       .previous;"                     \
  52                        : "=&r" (old), "=r" (faulted)
  53                        : "r" (parent), "r" (return_hooker)
  54        );
  55
  56        flush_dcache_range((u32)parent, (u32)parent + 4);
  57        flush_icache_range((u32)parent, (u32)parent + 4);
  58
  59        if (unlikely(faulted)) {
  60                ftrace_graph_stop();
  61                WARN_ON(1);
  62                return;
  63        }
  64
  65        if (function_graph_enter(old, self_addr, 0, NULL))
  66                *parent = old;
  67}
  68#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
  69
  70#ifdef CONFIG_DYNAMIC_FTRACE
  71/* save value to addr - it is save to do it in asm */
  72static int ftrace_modify_code(unsigned long addr, unsigned int value)
  73{
  74        int faulted = 0;
  75
  76        __asm__ __volatile__("  1:      swi     %2, %1, 0;"             \
  77                                "       addik   %0, r0, 0;"             \
  78                                "2:"                                    \
  79                                "       .section .fixup, \"ax\";"       \
  80                                "3:     brid    2b;"                    \
  81                                "       addik   %0, r0, 1;"             \
  82                                "       .previous;"                     \
  83                                "       .section __ex_table,\"a\";"     \
  84                                "       .word   1b,3b;"                 \
  85                                "       .previous;"                     \
  86                                : "=r" (faulted)
  87                                : "r" (addr), "r" (value)
  88        );
  89
  90        if (unlikely(faulted))
  91                return -EFAULT;
  92
  93        flush_dcache_range(addr, addr + 4);
  94        flush_icache_range(addr, addr + 4);
  95
  96        return 0;
  97}
  98
  99#define MICROBLAZE_NOP 0x80000000
 100#define MICROBLAZE_BRI 0xb800000C
 101
 102static unsigned int recorded; /* if save was or not */
 103static unsigned int imm; /* saving whole imm instruction */
 104
 105/* There are two approaches howto solve ftrace_make nop function - look below */
 106#undef USE_FTRACE_NOP
 107
 108#ifdef USE_FTRACE_NOP
 109static unsigned int bralid; /* saving whole bralid instruction */
 110#endif
 111
 112int ftrace_make_nop(struct module *mod,
 113                        struct dyn_ftrace *rec, unsigned long addr)
 114{
 115        /* we have this part of code which we are working with
 116         * b000c000        imm     -16384
 117         * b9fc8e30        bralid  r15, -29136     // c0008e30 <_mcount>
 118         * 80000000        or      r0, r0, r0
 119         *
 120         * The first solution (!USE_FTRACE_NOP-could be called branch solution)
 121         * b000c000        bri  12 (0xC - jump to any other instruction)
 122         * b9fc8e30        bralid  r15, -29136     // c0008e30 <_mcount>
 123         * 80000000        or      r0, r0, r0
 124         * any other instruction
 125         *
 126         * The second solution (USE_FTRACE_NOP) - no jump just nops
 127         * 80000000        or      r0, r0, r0
 128         * 80000000        or      r0, r0, r0
 129         * 80000000        or      r0, r0, r0
 130         */
 131        int ret = 0;
 132
 133        if (recorded == 0) {
 134                recorded = 1;
 135                imm = *(unsigned int *)rec->ip;
 136                pr_debug("%s: imm:0x%x\n", __func__, imm);
 137#ifdef USE_FTRACE_NOP
 138                bralid = *(unsigned int *)(rec->ip + 4);
 139                pr_debug("%s: bralid 0x%x\n", __func__, bralid);
 140#endif /* USE_FTRACE_NOP */
 141        }
 142
 143#ifdef USE_FTRACE_NOP
 144        ret = ftrace_modify_code(rec->ip, MICROBLAZE_NOP);
 145        ret += ftrace_modify_code(rec->ip + 4, MICROBLAZE_NOP);
 146#else /* USE_FTRACE_NOP */
 147        ret = ftrace_modify_code(rec->ip, MICROBLAZE_BRI);
 148#endif /* USE_FTRACE_NOP */
 149        return ret;
 150}
 151
 152/* I believe that first is called ftrace_make_nop before this function */
 153int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 154{
 155        int ret;
 156        pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n",
 157                __func__, (unsigned int)addr, (unsigned int)rec->ip, imm);
 158        ret = ftrace_modify_code(rec->ip, imm);
 159#ifdef USE_FTRACE_NOP
 160        pr_debug("%s: bralid:0x%x\n", __func__, bralid);
 161        ret += ftrace_modify_code(rec->ip + 4, bralid);
 162#endif /* USE_FTRACE_NOP */
 163        return ret;
 164}
 165
 166int ftrace_update_ftrace_func(ftrace_func_t func)
 167{
 168        unsigned long ip = (unsigned long)(&ftrace_call);
 169        unsigned int upper = (unsigned int)func;
 170        unsigned int lower = (unsigned int)func;
 171        int ret = 0;
 172
 173        /* create proper saving to ftrace_call poll */
 174        upper = 0xb0000000 + (upper >> 16); /* imm func_upper */
 175        lower = 0x32800000 + (lower & 0xFFFF); /* addik r20, r0, func_lower */
 176
 177        pr_debug("%s: func=0x%x, ip=0x%x, upper=0x%x, lower=0x%x\n",
 178                __func__, (unsigned int)func, (unsigned int)ip, upper, lower);
 179
 180        /* save upper and lower code */
 181        ret = ftrace_modify_code(ip, upper);
 182        ret += ftrace_modify_code(ip + 4, lower);
 183
 184        /* We just need to replace the rtsd r15, 8 with NOP */
 185        ret += ftrace_modify_code((unsigned long)&ftrace_caller,
 186                                  MICROBLAZE_NOP);
 187
 188        return ret;
 189}
 190
 191#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 192unsigned int old_jump; /* saving place for jump instruction */
 193
 194int ftrace_enable_ftrace_graph_caller(void)
 195{
 196        unsigned int ret;
 197        unsigned long ip = (unsigned long)(&ftrace_call_graph);
 198
 199        old_jump = *(unsigned int *)ip; /* save jump over instruction */
 200        ret = ftrace_modify_code(ip, MICROBLAZE_NOP);
 201
 202        pr_debug("%s: Replace instruction: 0x%x\n", __func__, old_jump);
 203        return ret;
 204}
 205
 206int ftrace_disable_ftrace_graph_caller(void)
 207{
 208        unsigned int ret;
 209        unsigned long ip = (unsigned long)(&ftrace_call_graph);
 210
 211        ret = ftrace_modify_code(ip, old_jump);
 212
 213        pr_debug("%s\n", __func__);
 214        return ret;
 215}
 216#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 217#endif /* CONFIG_DYNAMIC_FTRACE */
 218