1/* 2 * mcount and friends -- ftrace stuff 3 * 4 * Copyright (C) 2009-2010 Analog Devices Inc. 5 * Licensed under the GPL-2 or later. 6 */ 7 8#include <linux/linkage.h> 9#include <asm/ftrace.h> 10 11.text 12 13#ifdef CONFIG_DYNAMIC_FTRACE 14 15/* Simple stub so we can boot the kernel until runtime patching has 16 * disabled all calls to this. Then it'll be unused. 17 */ 18ENTRY(__mcount) 19# if ANOMALY_05000371 20 nop; nop; nop; nop; 21# endif 22 rts; 23ENDPROC(__mcount) 24 25/* GCC will have called us before setting up the function prologue, so we 26 * can clobber the normal scratch registers, but we need to make sure to 27 * save/restore the registers used for argument passing (R0-R2) in case 28 * the profiled function is using them. With data registers, R3 is the 29 * only one we can blow away. With pointer registers, we have P0-P2. 30 * 31 * Upon entry, the RETS will point to the top of the current profiled 32 * function. And since GCC pushed the previous RETS for us, the previous 33 * function will be waiting there. mmmm pie. 34 */ 35ENTRY(_ftrace_caller) 36# ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST 37 /* optional micro optimization: return if stopped */ 38 p1.l = _function_trace_stop; 39 p1.h = _function_trace_stop; 40 r3 = [p1]; 41 cc = r3 == 0; 42 if ! cc jump _ftrace_stub (bp); 43# endif 44 45 /* save first/second/third function arg and the return register */ 46 [--sp] = r2; 47 [--sp] = r0; 48 [--sp] = r1; 49 [--sp] = rets; 50 51 /* function_trace_call(unsigned long ip, unsigned long parent_ip): 52 * ip: this point was called by ... 53 * parent_ip: ... this function 54 * the ip itself will need adjusting for the mcount call 55 */ 56 r0 = rets; 57 r1 = [sp + 16]; /* skip the 4 local regs on stack */ 58 r0 += -MCOUNT_INSN_SIZE; 59 60.globl _ftrace_call 61_ftrace_call: 62 call _ftrace_stub 63 64# ifdef CONFIG_FUNCTION_GRAPH_TRACER 65.globl _ftrace_graph_call 66_ftrace_graph_call: 67 nop; /* jump _ftrace_graph_caller; */ 68# endif 69 70 /* restore state and get out of dodge */ 71.Lfinish_trace: 72 rets = [sp++]; 73 r1 = [sp++]; 74 r0 = [sp++]; 75 r2 = [sp++]; 76 77.globl _ftrace_stub 78_ftrace_stub: 79 rts; 80ENDPROC(_ftrace_caller) 81 82#else 83 84/* See documentation for _ftrace_caller */ 85ENTRY(__mcount) 86# ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST 87 /* optional micro optimization: return if stopped */ 88 p1.l = _function_trace_stop; 89 p1.h = _function_trace_stop; 90 r3 = [p1]; 91 cc = r3 == 0; 92 if ! cc jump _ftrace_stub (bp); 93# endif 94 95 /* save third function arg early so we can do testing below */ 96 [--sp] = r2; 97 98 /* load the function pointer to the tracer */ 99 p0.l = _ftrace_trace_function; 100 p0.h = _ftrace_trace_function; 101 r3 = [p0]; 102 103 /* optional micro optimization: don't call the stub tracer */ 104 r2.l = _ftrace_stub; 105 r2.h = _ftrace_stub; 106 cc = r2 == r3; 107 if ! cc jump .Ldo_trace; 108 109# ifdef CONFIG_FUNCTION_GRAPH_TRACER 110 /* if the ftrace_graph_return function pointer is not set to 111 * the ftrace_stub entry, call prepare_ftrace_return(). 112 */ 113 p0.l = _ftrace_graph_return; 114 p0.h = _ftrace_graph_return; 115 r3 = [p0]; 116 cc = r2 == r3; 117 if ! cc jump _ftrace_graph_caller; 118 119 /* similarly, if the ftrace_graph_entry function pointer is not 120 * set to the ftrace_graph_entry_stub entry, ... 121 */ 122 p0.l = _ftrace_graph_entry; 123 p0.h = _ftrace_graph_entry; 124 r2.l = _ftrace_graph_entry_stub; 125 r2.h = _ftrace_graph_entry_stub; 126 r3 = [p0]; 127 cc = r2 == r3; 128 if ! cc jump _ftrace_graph_caller; 129# endif 130 131 r2 = [sp++]; 132 rts; 133 134.Ldo_trace: 135 136 /* save first/second function arg and the return register */ 137 [--sp] = r0; 138 [--sp] = r1; 139 [--sp] = rets; 140 141 /* setup the tracer function */ 142 p0 = r3; 143 144 /* function_trace_call(unsigned long ip, unsigned long parent_ip): 145 * ip: this point was called by ... 146 * parent_ip: ... this function 147 * the ip itself will need adjusting for the mcount call 148 */ 149 r0 = rets; 150 r1 = [sp + 16]; /* skip the 4 local regs on stack */ 151 r0 += -MCOUNT_INSN_SIZE; 152 153 /* call the tracer */ 154 call (p0); 155 156 /* restore state and get out of dodge */ 157.Lfinish_trace: 158 rets = [sp++]; 159 r1 = [sp++]; 160 r0 = [sp++]; 161 r2 = [sp++]; 162 163.globl _ftrace_stub 164_ftrace_stub: 165 rts; 166ENDPROC(__mcount) 167 168#endif 169 170#ifdef CONFIG_FUNCTION_GRAPH_TRACER 171/* The prepare_ftrace_return() function is similar to the trace function 172 * except it takes a pointer to the location of the frompc. This is so 173 * the prepare_ftrace_return() can hijack it temporarily for probing 174 * purposes. 175 */ 176ENTRY(_ftrace_graph_caller) 177# ifndef CONFIG_DYNAMIC_FTRACE 178 /* save first/second function arg and the return register */ 179 [--sp] = r0; 180 [--sp] = r1; 181 [--sp] = rets; 182 183 /* prepare_ftrace_return(parent, self_addr, frame_pointer) */ 184 r0 = sp; /* unsigned long *parent */ 185 r1 = rets; /* unsigned long self_addr */ 186# else 187 r0 = sp; /* unsigned long *parent */ 188 r1 = [sp]; /* unsigned long self_addr */ 189# endif 190# ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST 191 r2 = fp; /* unsigned long frame_pointer */ 192# endif 193 r0 += 16; /* skip the 4 local regs on stack */ 194 r1 += -MCOUNT_INSN_SIZE; 195 call _prepare_ftrace_return; 196 197 jump .Lfinish_trace; 198ENDPROC(_ftrace_graph_caller) 199 200/* Undo the rewrite caused by ftrace_graph_caller(). The common function 201 * ftrace_return_to_handler() will return the original rets so we can 202 * restore it and be on our way. 203 */ 204ENTRY(_return_to_handler) 205 /* make sure original return values are saved */ 206 [--sp] = p0; 207 [--sp] = r0; 208 [--sp] = r1; 209 210 /* get original return address */ 211# ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST 212 r0 = fp; /* Blackfin is sane, so omit this */ 213# endif 214 call _ftrace_return_to_handler; 215 rets = r0; 216 217 /* anomaly 05000371 - make sure we have at least three instructions 218 * between rets setting and the return 219 */ 220 r1 = [sp++]; 221 r0 = [sp++]; 222 p0 = [sp++]; 223 rts; 224ENDPROC(_return_to_handler) 225#endif 226