linux/arch/blackfin/kernel/ftrace-entry.S
<<
>>
Prefs
   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