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 badr lr, 2f 91 mov pc, r2 922: mcount_exit 93.endm 94 95#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 96 97.macro __ftrace_regs_caller 98 99 sub sp, sp, #8 @ space for PC and CPSR OLD_R0, 100 @ OLD_R0 will overwrite previous LR 101 102 add ip, sp, #12 @ move in IP the value of SP as it was 103 @ before the push {lr} of the mcount mechanism 104 105 str lr, [sp, #0] @ store LR instead of PC 106 107 ldr lr, [sp, #8] @ get previous LR 108 109 str r0, [sp, #8] @ write r0 as OLD_R0 over previous LR 110 111 stmdb sp!, {ip, lr} 112 stmdb sp!, {r0-r11, lr} 113 114 @ stack content at this point: 115 @ 0 4 48 52 56 60 64 68 72 116 @ R0 | R1 | ... | LR | SP + 4 | previous LR | LR | PSR | OLD_R0 | 117 118 mov r3, sp @ struct pt_regs* 119 120 ldr r2, =function_trace_op 121 ldr r2, [r2] @ pointer to the current 122 @ function tracing op 123 124 ldr r1, [sp, #S_LR] @ lr of instrumented func 125 126 ldr lr, [sp, #S_PC] @ get LR 127 128 mcount_adjust_addr r0, lr @ instrumented function 129 130 .globl ftrace_regs_call 131ftrace_regs_call: 132 bl ftrace_stub 133 134#ifdef CONFIG_FUNCTION_GRAPH_TRACER 135 .globl ftrace_graph_regs_call 136ftrace_graph_regs_call: 137 mov r0, r0 138#endif 139 140 @ pop saved regs 141 ldmia sp!, {r0-r12} @ restore r0 through r12 142 ldr ip, [sp, #8] @ restore PC 143 ldr lr, [sp, #4] @ restore LR 144 ldr sp, [sp, #0] @ restore SP 145 mov pc, ip @ return 146.endm 147 148#ifdef CONFIG_FUNCTION_GRAPH_TRACER 149.macro __ftrace_graph_regs_caller 150 151 sub r0, fp, #4 @ lr of instrumented routine (parent) 152 153 @ called from __ftrace_regs_caller 154 ldr r1, [sp, #S_PC] @ instrumented routine (func) 155 mcount_adjust_addr r1, r1 156 157 mov r2, fp @ frame pointer 158 bl prepare_ftrace_return 159 160 @ pop registers saved in ftrace_regs_caller 161 ldmia sp!, {r0-r12} @ restore r0 through r12 162 ldr ip, [sp, #8] @ restore PC 163 ldr lr, [sp, #4] @ restore LR 164 ldr sp, [sp, #0] @ restore SP 165 mov pc, ip @ return 166 167.endm 168#endif 169#endif 170 171.macro __ftrace_caller suffix 172 mcount_enter 173 174 mcount_get_lr r1 @ lr of instrumented func 175 mcount_adjust_addr r0, lr @ instrumented function 176 177#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 178 ldr r2, =function_trace_op 179 ldr r2, [r2] @ pointer to the current 180 @ function tracing op 181 mov r3, #0 @ regs is NULL 182#endif 183 184 .globl ftrace_call\suffix 185ftrace_call\suffix: 186 bl ftrace_stub 187 188#ifdef CONFIG_FUNCTION_GRAPH_TRACER 189 .globl ftrace_graph_call\suffix 190ftrace_graph_call\suffix: 191 mov r0, r0 192#endif 193 194 mcount_exit 195.endm 196 197.macro __ftrace_graph_caller 198 sub r0, fp, #4 @ &lr of instrumented routine (&parent) 199#ifdef CONFIG_DYNAMIC_FTRACE 200 @ called from __ftrace_caller, saved in mcount_enter 201 ldr r1, [sp, #16] @ instrumented routine (func) 202 mcount_adjust_addr r1, r1 203#else 204 @ called from __mcount, untouched in lr 205 mcount_adjust_addr r1, lr @ instrumented routine (func) 206#endif 207 mov r2, fp @ frame pointer 208 bl prepare_ftrace_return 209 mcount_exit 210.endm 211 212#ifdef CONFIG_OLD_MCOUNT 213/* 214 * mcount 215 */ 216 217.macro mcount_enter 218 stmdb sp!, {r0-r3, lr} 219.endm 220 221.macro mcount_get_lr reg 222 ldr \reg, [fp, #-4] 223.endm 224 225.macro mcount_exit 226 ldr lr, [fp, #-4] 227 ldmia sp!, {r0-r3, pc} 228.endm 229 230ENTRY(mcount) 231#ifdef CONFIG_DYNAMIC_FTRACE 232 stmdb sp!, {lr} 233 ldr lr, [fp, #-4] 234 ldmia sp!, {pc} 235#else 236 __mcount _old 237#endif 238ENDPROC(mcount) 239 240#ifdef CONFIG_DYNAMIC_FTRACE 241ENTRY(ftrace_caller_old) 242 __ftrace_caller _old 243ENDPROC(ftrace_caller_old) 244#endif 245 246#ifdef CONFIG_FUNCTION_GRAPH_TRACER 247ENTRY(ftrace_graph_caller_old) 248 __ftrace_graph_caller 249ENDPROC(ftrace_graph_caller_old) 250#endif 251 252.purgem mcount_enter 253.purgem mcount_get_lr 254.purgem mcount_exit 255#endif 256 257/* 258 * __gnu_mcount_nc 259 */ 260 261.macro mcount_enter 262/* 263 * This pad compensates for the push {lr} at the call site. Note that we are 264 * unable to unwind through a function which does not otherwise save its lr. 265 */ 266 UNWIND(.pad #4) 267 stmdb sp!, {r0-r3, lr} 268 UNWIND(.save {r0-r3, lr}) 269.endm 270 271.macro mcount_get_lr reg 272 ldr \reg, [sp, #20] 273.endm 274 275.macro mcount_exit 276 ldmia sp!, {r0-r3, ip, lr} 277 ret ip 278.endm 279 280ENTRY(__gnu_mcount_nc) 281UNWIND(.fnstart) 282#ifdef CONFIG_DYNAMIC_FTRACE 283 mov ip, lr 284 ldmia sp!, {lr} 285 ret ip 286#else 287 __mcount 288#endif 289UNWIND(.fnend) 290ENDPROC(__gnu_mcount_nc) 291 292#ifdef CONFIG_DYNAMIC_FTRACE 293ENTRY(ftrace_caller) 294UNWIND(.fnstart) 295 __ftrace_caller 296UNWIND(.fnend) 297ENDPROC(ftrace_caller) 298 299#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 300ENTRY(ftrace_regs_caller) 301UNWIND(.fnstart) 302 __ftrace_regs_caller 303UNWIND(.fnend) 304ENDPROC(ftrace_regs_caller) 305#endif 306 307#endif 308 309#ifdef CONFIG_FUNCTION_GRAPH_TRACER 310ENTRY(ftrace_graph_caller) 311UNWIND(.fnstart) 312 __ftrace_graph_caller 313UNWIND(.fnend) 314ENDPROC(ftrace_graph_caller) 315 316#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 317ENTRY(ftrace_graph_regs_caller) 318UNWIND(.fnstart) 319 __ftrace_graph_regs_caller 320UNWIND(.fnend) 321ENDPROC(ftrace_graph_regs_caller) 322#endif 323#endif 324 325.purgem mcount_enter 326.purgem mcount_get_lr 327.purgem mcount_exit 328 329#ifdef CONFIG_FUNCTION_GRAPH_TRACER 330 .globl return_to_handler 331return_to_handler: 332 stmdb sp!, {r0-r3} 333 mov r0, fp @ frame pointer 334 bl ftrace_return_to_handler 335 mov lr, r0 @ r0 has real ret addr 336 ldmia sp!, {r0-r3} 337 ret lr 338#endif 339 340ENTRY(ftrace_stub) 341.Lftrace_stub: 342 ret lr 343ENDPROC(ftrace_stub) 344