1/* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License version 2 as 4 * published by the Free Software Foundation. 5 */ 6 7#include <asm/assembler.h> 8#include <asm/ftrace.h> 9#include <asm/unwind.h> 10 11#include "entry-header.S" 12 13/* 14 * When compiling with -pg, gcc inserts a call to the mcount routine at the 15 * start of every function. In mcount, apart from the function's address (in 16 * lr), we need to get hold of the function's caller's address. 17 * 18 * Older GCCs (pre-4.4) inserted a call to a routine called mcount like this: 19 * 20 * bl mcount 21 * 22 * These versions have the limitation that in order for the mcount routine to 23 * be able to determine the function's caller's address, an APCS-style frame 24 * pointer (which is set up with something like the code below) is required. 25 * 26 * mov ip, sp 27 * push {fp, ip, lr, pc} 28 * sub fp, ip, #4 29 * 30 * With EABI, these frame pointers are not available unless -mapcs-frame is 31 * specified, and if building as Thumb-2, not even then. 32 * 33 * Newer GCCs (4.4+) solve this problem by introducing a new version of mcount, 34 * with call sites like: 35 * 36 * push {lr} 37 * bl __gnu_mcount_nc 38 * 39 * With these compilers, frame pointers are not necessary. 40 * 41 * mcount can be thought of as a function called in the middle of a subroutine 42 * call. As such, it needs to be transparent for both the caller and the 43 * callee: the original lr needs to be restored when leaving mcount, and no 44 * registers should be clobbered. (In the __gnu_mcount_nc implementation, we 45 * clobber the ip register. This is OK because the ARM calling convention 46 * allows it to be clobbered in subroutines and doesn't use it to hold 47 * parameters.) 48 * 49 * When using dynamic ftrace, we patch out the mcount call by a "mov r0, r0" 50 * for the mcount case, and a "pop {lr}" for the __gnu_mcount_nc case (see 51 * arch/arm/kernel/ftrace.c). 52 */ 53 54#ifndef CONFIG_OLD_MCOUNT 55#if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4)) 56#error Ftrace requires CONFIG_FRAME_POINTER=y with GCC older than 4.4.0. 57#endif 58#endif 59 60.macro mcount_adjust_addr rd, rn 61 bic \rd, \rn, #1 @ clear the Thumb bit if present 62 sub \rd, \rd, #MCOUNT_INSN_SIZE 63.endm 64 65.macro __mcount suffix 66 mcount_enter 67 ldr r0, =ftrace_trace_function 68 ldr r2, [r0] 69 adr r0, .Lftrace_stub 70 cmp r0, r2 71 bne 1f 72 73#ifdef CONFIG_FUNCTION_GRAPH_TRACER 74 ldr r1, =ftrace_graph_return 75 ldr r2, [r1] 76 cmp r0, r2 77 bne ftrace_graph_caller\suffix 78 79 ldr r1, =ftrace_graph_entry 80 ldr r2, [r1] 81 ldr r0, =ftrace_graph_entry_stub 82 cmp r0, r2 83 bne ftrace_graph_caller\suffix 84#endif 85 86 mcount_exit 87 881: mcount_get_lr r1 @ lr of instrumented func 89 mcount_adjust_addr r0, lr @ instrumented function 90 adr lr, BSYM(2f) 91 mov pc, r2 922: mcount_exit 93.endm 94 95.macro __ftrace_caller suffix 96 mcount_enter 97 98 mcount_get_lr r1 @ lr of instrumented func 99 mcount_adjust_addr r0, lr @ instrumented function 100 101 .globl ftrace_call\suffix 102ftrace_call\suffix: 103 bl ftrace_stub 104 105#ifdef CONFIG_FUNCTION_GRAPH_TRACER 106 .globl ftrace_graph_call\suffix 107ftrace_graph_call\suffix: 108 mov r0, r0 109#endif 110 111 mcount_exit 112.endm 113 114.macro __ftrace_graph_caller 115 sub r0, fp, #4 @ &lr of instrumented routine (&parent) 116#ifdef CONFIG_DYNAMIC_FTRACE 117 @ called from __ftrace_caller, saved in mcount_enter 118 ldr r1, [sp, #16] @ instrumented routine (func) 119 mcount_adjust_addr r1, r1 120#else 121 @ called from __mcount, untouched in lr 122 mcount_adjust_addr r1, lr @ instrumented routine (func) 123#endif 124 mov r2, fp @ frame pointer 125 bl prepare_ftrace_return 126 mcount_exit 127.endm 128 129#ifdef CONFIG_OLD_MCOUNT 130/* 131 * mcount 132 */ 133 134.macro mcount_enter 135 stmdb sp!, {r0-r3, lr} 136.endm 137 138.macro mcount_get_lr reg 139 ldr \reg, [fp, #-4] 140.endm 141 142.macro mcount_exit 143 ldr lr, [fp, #-4] 144 ldmia sp!, {r0-r3, pc} 145.endm 146 147ENTRY(mcount) 148#ifdef CONFIG_DYNAMIC_FTRACE 149 stmdb sp!, {lr} 150 ldr lr, [fp, #-4] 151 ldmia sp!, {pc} 152#else 153 __mcount _old 154#endif 155ENDPROC(mcount) 156 157#ifdef CONFIG_DYNAMIC_FTRACE 158ENTRY(ftrace_caller_old) 159 __ftrace_caller _old 160ENDPROC(ftrace_caller_old) 161#endif 162 163#ifdef CONFIG_FUNCTION_GRAPH_TRACER 164ENTRY(ftrace_graph_caller_old) 165 __ftrace_graph_caller 166ENDPROC(ftrace_graph_caller_old) 167#endif 168 169.purgem mcount_enter 170.purgem mcount_get_lr 171.purgem mcount_exit 172#endif 173 174/* 175 * __gnu_mcount_nc 176 */ 177 178.macro mcount_enter 179/* 180 * This pad compensates for the push {lr} at the call site. Note that we are 181 * unable to unwind through a function which does not otherwise save its lr. 182 */ 183 UNWIND(.pad #4) 184 stmdb sp!, {r0-r3, lr} 185 UNWIND(.save {r0-r3, lr}) 186.endm 187 188.macro mcount_get_lr reg 189 ldr \reg, [sp, #20] 190.endm 191 192.macro mcount_exit 193 ldmia sp!, {r0-r3, ip, lr} 194 ret ip 195.endm 196 197ENTRY(__gnu_mcount_nc) 198UNWIND(.fnstart) 199#ifdef CONFIG_DYNAMIC_FTRACE 200 mov ip, lr 201 ldmia sp!, {lr} 202 ret ip 203#else 204 __mcount 205#endif 206UNWIND(.fnend) 207ENDPROC(__gnu_mcount_nc) 208 209#ifdef CONFIG_DYNAMIC_FTRACE 210ENTRY(ftrace_caller) 211UNWIND(.fnstart) 212 __ftrace_caller 213UNWIND(.fnend) 214ENDPROC(ftrace_caller) 215#endif 216 217#ifdef CONFIG_FUNCTION_GRAPH_TRACER 218ENTRY(ftrace_graph_caller) 219UNWIND(.fnstart) 220 __ftrace_graph_caller 221UNWIND(.fnend) 222ENDPROC(ftrace_graph_caller) 223#endif 224 225.purgem mcount_enter 226.purgem mcount_get_lr 227.purgem mcount_exit 228 229#ifdef CONFIG_FUNCTION_GRAPH_TRACER 230 .globl return_to_handler 231return_to_handler: 232 stmdb sp!, {r0-r3} 233 mov r0, fp @ frame pointer 234 bl ftrace_return_to_handler 235 mov lr, r0 @ r0 has real ret addr 236 ldmia sp!, {r0-r3} 237 ret lr 238#endif 239 240ENTRY(ftrace_stub) 241.Lftrace_stub: 242 ret lr 243ENDPROC(ftrace_stub) 244