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(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); 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 */ 83static 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 113static unsigned int recorded; /* if save was or not */ 114static 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 120static unsigned int bralid; /* saving whole bralid instruction */ 121#endif 122 123int 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 */ 164int 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 177int __init ftrace_dyn_arch_init(void) 178{ 179 return 0; 180} 181 182int 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 208unsigned int old_jump; /* saving place for jump instruction */ 209 210int 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 222int 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