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