123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /*
- * 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)
- /* 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)
- /* 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
|