/* * mcount and friends -- ftrace stuff * * Copyright (C) 2009-2010 Analog Devices Inc. * Licensed under the GPL-2 or later. */ #include <linux/linkage.h> #include <asm/ftrace.h> .text #ifdef CONFIG_DYNAMIC_FTRACE /* Simple stub so we can boot the kernel until runtime patching has * disabled all calls to this. Then it'll be unused. */ ENTRY(__mcount) # if ANOMALY_05000371 nop; nop; nop; nop; # endif rts; ENDPROC(__mcount) /* GCC will have called us before setting up the function prologue, so we * can clobber the normal scratch registers, but we need to make sure to * save/restore the registers used for argument passing (R0-R2) in case * the profiled function is using them. With data registers, R3 is the * only one we can blow away. With pointer registers, we have P0-P2. * * Upon entry, the RETS will point to the top of the current profiled * function. And since GCC pushed the previous RETS for us, the previous * function will be waiting there. mmmm pie. */ ENTRY(_ftrace_caller) # ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST /* optional micro optimization: return if stopped */ p1.l = _function_trace_stop; p1.h = _function_trace_stop; r3 = [p1]; cc = r3 == 0; if ! cc jump _ftrace_stub (bp); # endif /* save first/second/third function arg and the return register */ [--sp] = r2; [--sp] = r0; [--sp] = r1; [--sp] = rets; /* function_trace_call(unsigned long ip, unsigned long parent_ip): * ip: this point was called by ... * parent_ip: ... this function * the ip itself will need adjusting for the mcount call */ r0 = rets; r1 = [sp + 16]; /* skip the 4 local regs on stack */ r0 += -MCOUNT_INSN_SIZE; .globl _ftrace_call _ftrace_call: call _ftrace_stub # ifdef CONFIG_FUNCTION_GRAPH_TRACER .globl _ftrace_graph_call _ftrace_graph_call: nop; /* jump _ftrace_graph_caller; */ # endif /* restore state and get out of dodge */ .Lfinish_trace: rets = [sp++]; r1 = [sp++]; r0 = [sp++]; r2 = [sp++]; .globl _ftrace_stub _ftrace_stub: rts; ENDPROC(_ftrace_caller) #else /* See documentation for _ftrace_caller */ ENTRY(__mcount) # ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST /* optional micro optimization: return if stopped */ p1.l = _function_trace_stop; p1.h = _function_trace_stop; r3 = [p1]; cc = r3 == 0; if ! cc jump _ftrace_stub (bp); # endif /* save third function arg early so we can do testing below */ [--sp] = r2; /* load the function pointer to the tracer */ p0.l = _ftrace_trace_function; p0.h = _ftrace_trace_function; r3 = [p0]; /* optional micro optimization: don't call the stub tracer */ r2.l = _ftrace_stub; r2.h = _ftrace_stub; cc = r2 == r3; if ! cc jump .Ldo_trace; # ifdef CONFIG_FUNCTION_GRAPH_TRACER /* if the ftrace_graph_return function pointer is not set to * the ftrace_stub entry, call prepare_ftrace_return(). */ p0.l = _ftrace_graph_return; p0.h = _ftrace_graph_return; r3 = [p0]; cc = r2 == r3; if ! cc jump _ftrace_graph_caller; /* similarly, if the ftrace_graph_entry function pointer is not * set to the ftrace_graph_entry_stub entry, ... */ p0.l = _ftrace_graph_entry; p0.h = _ftrace_graph_entry; r2.l = _ftrace_graph_entry_stub; r2.h = _ftrace_graph_entry_stub; r3 = [p0]; cc = r2 == r3; if ! cc jump _ftrace_graph_caller; # endif r2 = [sp++]; rts; .Ldo_trace: /* save first/second function arg and the return register */ [--sp] = r0; [--sp] = r1; [--sp] = rets; /* setup the tracer function */ p0 = r3; /* function_trace_call(unsigned long ip, unsigned long parent_ip): * ip: this point was called by ... * parent_ip: ... this function * the ip itself will need adjusting for the mcount call */ r0 = rets; r1 = [sp + 16]; /* skip the 4 local regs on stack */ r0 += -MCOUNT_INSN_SIZE; /* call the tracer */ call (p0); /* restore state and get out of dodge */ .Lfinish_trace: rets = [sp++]; r1 = [sp++]; r0 = [sp++]; r2 = [sp++]; .globl _ftrace_stub _ftrace_stub: rts; ENDPROC(__mcount) #endif #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* The prepare_ftrace_return() function is similar to the trace function * except it takes a pointer to the location of the frompc. This is so * the prepare_ftrace_return() can hijack it temporarily for probing * purposes. */ ENTRY(_ftrace_graph_caller) # ifndef CONFIG_DYNAMIC_FTRACE /* save first/second function arg and the return register */ [--sp] = r0; [--sp] = r1; [--sp] = rets; /* prepare_ftrace_return(parent, self_addr, frame_pointer) */ r0 = sp; /* unsigned long *parent */ r1 = rets; /* unsigned long self_addr */ # else r0 = sp; /* unsigned long *parent */ r1 = [sp]; /* unsigned long self_addr */ # endif # ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST r2 = fp; /* unsigned long frame_pointer */ # endif r0 += 16; /* skip the 4 local regs on stack */ r1 += -MCOUNT_INSN_SIZE; call _prepare_ftrace_return; jump .Lfinish_trace; ENDPROC(_ftrace_graph_caller) /* Undo the rewrite caused by ftrace_graph_caller(). The common function * ftrace_return_to_handler() will return the original rets so we can * restore it and be on our way. */ ENTRY(_return_to_handler) /* make sure original return values are saved */ [--sp] = p0; [--sp] = r0; [--sp] = r1; /* get original return address */ # ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST r0 = fp; /* Blackfin is sane, so omit this */ # endif call _ftrace_return_to_handler; rets = r0; /* anomaly 05000371 - make sure we have at least three instructions * between rets setting and the return */ r1 = [sp++]; r0 = [sp++]; p0 = [sp++]; rts; ENDPROC(_return_to_handler) #endif