root/arch/microblaze/kernel/ftrace.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. prepare_ftrace_return
  2. ftrace_modify_code
  3. ftrace_make_nop
  4. ftrace_make_call
  5. ftrace_dyn_arch_init
  6. ftrace_update_ftrace_func
  7. ftrace_enable_ftrace_graph_caller
  8. ftrace_disable_ftrace_graph_caller

   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  */
  22 void 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 */
  72 static 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 
 102 static unsigned int recorded; /* if save was or not */
 103 static 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
 109 static unsigned int bralid; /* saving whole bralid instruction */
 110 #endif
 111 
 112 int 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 */
 153 int 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 
 166 int __init ftrace_dyn_arch_init(void)
 167 {
 168         return 0;
 169 }
 170 
 171 int ftrace_update_ftrace_func(ftrace_func_t func)
 172 {
 173         unsigned long ip = (unsigned long)(&ftrace_call);
 174         unsigned int upper = (unsigned int)func;
 175         unsigned int lower = (unsigned int)func;
 176         int ret = 0;
 177 
 178         /* create proper saving to ftrace_call poll */
 179         upper = 0xb0000000 + (upper >> 16); /* imm func_upper */
 180         lower = 0x32800000 + (lower & 0xFFFF); /* addik r20, r0, func_lower */
 181 
 182         pr_debug("%s: func=0x%x, ip=0x%x, upper=0x%x, lower=0x%x\n",
 183                 __func__, (unsigned int)func, (unsigned int)ip, upper, lower);
 184 
 185         /* save upper and lower code */
 186         ret = ftrace_modify_code(ip, upper);
 187         ret += ftrace_modify_code(ip + 4, lower);
 188 
 189         /* We just need to replace the rtsd r15, 8 with NOP */
 190         ret += ftrace_modify_code((unsigned long)&ftrace_caller,
 191                                   MICROBLAZE_NOP);
 192 
 193         return ret;
 194 }
 195 
 196 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 197 unsigned int old_jump; /* saving place for jump instruction */
 198 
 199 int ftrace_enable_ftrace_graph_caller(void)
 200 {
 201         unsigned int ret;
 202         unsigned long ip = (unsigned long)(&ftrace_call_graph);
 203 
 204         old_jump = *(unsigned int *)ip; /* save jump over instruction */
 205         ret = ftrace_modify_code(ip, MICROBLAZE_NOP);
 206 
 207         pr_debug("%s: Replace instruction: 0x%x\n", __func__, old_jump);
 208         return ret;
 209 }
 210 
 211 int ftrace_disable_ftrace_graph_caller(void)
 212 {
 213         unsigned int ret;
 214         unsigned long ip = (unsigned long)(&ftrace_call_graph);
 215 
 216         ret = ftrace_modify_code(ip, old_jump);
 217 
 218         pr_debug("%s\n", __func__);
 219         return ret;
 220 }
 221 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 222 #endif /* CONFIG_DYNAMIC_FTRACE */

/* [<][>][^][v][top][bottom][index][help] */