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(ftrace_graph_is_dead())) 31 return; 32 33 if (unlikely(atomic_read(¤t->tracing_graph_pause))) 34 return; 35 36 /* 37 * Protect against fault, even if it shouldn't 38 * happen. This tool is too much intrusive to 39 * ignore such a protection. 40 */ 41 asm volatile(" 1: lwi %0, %2, 0;" \ 42 "2: swi %3, %2, 0;" \ 43 " addik %1, r0, 0;" \ 44 "3:" \ 45 " .section .fixup, \"ax\";" \ 46 "4: brid 3b;" \ 47 " addik %1, r0, 1;" \ 48 " .previous;" \ 49 " .section __ex_table,\"a\";" \ 50 " .word 1b,4b;" \ 51 " .word 2b,4b;" \ 52 " .previous;" \ 53 : "=&r" (old), "=r" (faulted) 54 : "r" (parent), "r" (return_hooker) 55 ); 56 57 flush_dcache_range((u32)parent, (u32)parent + 4); 58 flush_icache_range((u32)parent, (u32)parent + 4); 59 60 if (unlikely(faulted)) { 61 ftrace_graph_stop(); 62 WARN_ON(1); 63 return; 64 } 65 66 err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0, NULL); 67 if (err == -EBUSY) { 68 *parent = old; 69 return; 70 } 71 72 trace.func = self_addr; 73 /* Only trace if the calling function expects to */ 74 if (!ftrace_graph_entry(&trace)) { 75 current->curr_ret_stack--; 76 *parent = old; 77 } 78 } 79 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 80 81 #ifdef CONFIG_DYNAMIC_FTRACE 82 /* save value to addr - it is save to do it in asm */ 83 static int ftrace_modify_code(unsigned long addr, unsigned int value) 84 { 85 int faulted = 0; 86 87 __asm__ __volatile__(" 1: swi %2, %1, 0;" \ 88 " addik %0, r0, 0;" \ 89 "2:" \ 90 " .section .fixup, \"ax\";" \ 91 "3: brid 2b;" \ 92 " addik %0, r0, 1;" \ 93 " .previous;" \ 94 " .section __ex_table,\"a\";" \ 95 " .word 1b,3b;" \ 96 " .previous;" \ 97 : "=r" (faulted) 98 : "r" (addr), "r" (value) 99 ); 100 101 if (unlikely(faulted)) 102 return -EFAULT; 103 104 flush_dcache_range(addr, addr + 4); 105 flush_icache_range(addr, addr + 4); 106 107 return 0; 108 } 109 110 #define MICROBLAZE_NOP 0x80000000 111 #define MICROBLAZE_BRI 0xb800000C 112 113 static unsigned int recorded; /* if save was or not */ 114 static unsigned int imm; /* saving whole imm instruction */ 115 116 /* There are two approaches howto solve ftrace_make nop function - look below */ 117 #undef USE_FTRACE_NOP 118 119 #ifdef USE_FTRACE_NOP 120 static unsigned int bralid; /* saving whole bralid instruction */ 121 #endif 122 123 int ftrace_make_nop(struct module *mod, 124 struct dyn_ftrace *rec, unsigned long addr) 125 { 126 /* we have this part of code which we are working with 127 * b000c000 imm -16384 128 * b9fc8e30 bralid r15, -29136 // c0008e30 <_mcount> 129 * 80000000 or r0, r0, r0 130 * 131 * The first solution (!USE_FTRACE_NOP-could be called branch solution) 132 * b000c000 bri 12 (0xC - jump to any other instruction) 133 * b9fc8e30 bralid r15, -29136 // c0008e30 <_mcount> 134 * 80000000 or r0, r0, r0 135 * any other instruction 136 * 137 * The second solution (USE_FTRACE_NOP) - no jump just nops 138 * 80000000 or r0, r0, r0 139 * 80000000 or r0, r0, r0 140 * 80000000 or r0, r0, r0 141 */ 142 int ret = 0; 143 144 if (recorded == 0) { 145 recorded = 1; 146 imm = *(unsigned int *)rec->ip; 147 pr_debug("%s: imm:0x%x\n", __func__, imm); 148 #ifdef USE_FTRACE_NOP 149 bralid = *(unsigned int *)(rec->ip + 4); 150 pr_debug("%s: bralid 0x%x\n", __func__, bralid); 151 #endif /* USE_FTRACE_NOP */ 152 } 153 154 #ifdef USE_FTRACE_NOP 155 ret = ftrace_modify_code(rec->ip, MICROBLAZE_NOP); 156 ret += ftrace_modify_code(rec->ip + 4, MICROBLAZE_NOP); 157 #else /* USE_FTRACE_NOP */ 158 ret = ftrace_modify_code(rec->ip, MICROBLAZE_BRI); 159 #endif /* USE_FTRACE_NOP */ 160 return ret; 161 } 162 163 /* I believe that first is called ftrace_make_nop before this function */ 164 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 165 { 166 int ret; 167 pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n", 168 __func__, (unsigned int)addr, (unsigned int)rec->ip, imm); 169 ret = ftrace_modify_code(rec->ip, imm); 170 #ifdef USE_FTRACE_NOP 171 pr_debug("%s: bralid:0x%x\n", __func__, bralid); 172 ret += ftrace_modify_code(rec->ip + 4, bralid); 173 #endif /* USE_FTRACE_NOP */ 174 return ret; 175 } 176 177 int __init ftrace_dyn_arch_init(void) 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