/* SPDX-License-Identifier: GPL-2.0-only */12#include <asm/assembler.h>3#include <asm/ftrace.h>4#include <asm/unwind.h>56#include "entry-header.S"78/*9* When compiling with -pg, gcc inserts a call to the mcount routine at the10* start of every function. In mcount, apart from the function's address (in11* lr), we need to get hold of the function's caller's address.12*13* Newer GCCs (4.4+) solve this problem by using a version of mcount with call14* sites like:15*16* push {lr}17* bl __gnu_mcount_nc18*19* With these compilers, frame pointers are not necessary.20*21* mcount can be thought of as a function called in the middle of a subroutine22* call. As such, it needs to be transparent for both the caller and the23* callee: the original lr needs to be restored when leaving mcount, and no24* registers should be clobbered.25*26* When using dynamic ftrace, we patch out the mcount call by a "add sp, #4"27* instead of the __gnu_mcount_nc call (see arch/arm/kernel/ftrace.c).28*/2930.macro mcount_adjust_addr rd, rn31bic \rd, \rn, #1 @ clear the Thumb bit if present32sub \rd, \rd, #MCOUNT_INSN_SIZE33.endm3435.macro __mcount suffix36mcount_enter37ldr_va r2, ftrace_trace_function38badr r0, .Lftrace_stub39cmp r0, r240bne 1f4142#ifdef CONFIG_FUNCTION_GRAPH_TRACER43ldr_va r2, ftrace_graph_return44cmp r0, r245bne ftrace_graph_caller\suffix4647ldr_va r2, ftrace_graph_entry48mov_l r0, ftrace_graph_entry_stub49cmp r0, r250bne ftrace_graph_caller\suffix51#endif5253mcount_exit54551: mcount_get_lr r1 @ lr of instrumented func56mcount_adjust_addr r0, lr @ instrumented function57badr lr, 2f58mov pc, r2592: mcount_exit60.endm6162#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS6364.macro __ftrace_regs_caller6566str lr, [sp, #-8]! @ store LR as PC and make space for CPSR/OLD_R0,67@ OLD_R0 will overwrite previous LR6869ldr lr, [sp, #8] @ get previous LR7071str r0, [sp, #8] @ write r0 as OLD_R0 over previous LR7273str lr, [sp, #-4]! @ store previous LR as LR7475add lr, sp, #16 @ move in LR the value of SP as it was76@ before the push {lr} of the mcount mechanism7778push {r0-r11, ip, lr}7980@ stack content at this point:81@ 0 4 48 52 56 60 64 68 7282@ R0 | R1 | ... | IP | SP + 4 | previous LR | LR | PSR | OLD_R0 |8384mov r3, sp @ struct pt_regs*8586ldr_va r2, function_trace_op @ pointer to the current87@ function tracing op8889ldr r1, [sp, #S_LR] @ lr of instrumented func9091ldr lr, [sp, #S_PC] @ get LR9293mcount_adjust_addr r0, lr @ instrumented function9495.globl ftrace_regs_call96ftrace_regs_call:97bl ftrace_stub9899#ifdef CONFIG_FUNCTION_GRAPH_TRACER100.globl ftrace_graph_regs_call101ftrace_graph_regs_call:102ARM( mov r0, r0 )103THUMB( nop.w )104#endif105106@ pop saved regs107pop {r0-r11, ip, lr} @ restore r0 through r12108ldr lr, [sp], #4 @ restore LR109ldr pc, [sp], #12110.endm111112#ifdef CONFIG_FUNCTION_GRAPH_TRACER113.macro __ftrace_graph_regs_caller114115#ifdef CONFIG_UNWINDER_FRAME_POINTER116sub r0, fp, #4 @ lr of instrumented routine (parent)117#else118add r0, sp, #S_LR119#endif120121@ called from __ftrace_regs_caller122ldr r1, [sp, #S_PC] @ instrumented routine (func)123mcount_adjust_addr r1, r1124125mov r2, fpreg @ frame pointer126add r3, sp, #PT_REGS_SIZE127bl prepare_ftrace_return128129@ pop registers saved in ftrace_regs_caller130pop {r0-r11, ip, lr} @ restore r0 through r12131ldr lr, [sp], #4 @ restore LR132ldr pc, [sp], #12133134.endm135#endif136#endif137138.macro __ftrace_caller suffix139mcount_enter140141mcount_get_lr r1 @ lr of instrumented func142mcount_adjust_addr r0, lr @ instrumented function143144#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS145ldr_va r2, function_trace_op @ pointer to the current146@ function tracing op147mov r3, #0 @ regs is NULL148#endif149150.globl ftrace_call\suffix151ftrace_call\suffix:152bl ftrace_stub153154#ifdef CONFIG_FUNCTION_GRAPH_TRACER155.globl ftrace_graph_call\suffix156ftrace_graph_call\suffix:157ARM( mov r0, r0 )158THUMB( nop.w )159#endif160161mcount_exit162.endm163164.macro __ftrace_graph_caller165#ifdef CONFIG_UNWINDER_FRAME_POINTER166sub r0, fp, #4 @ &lr of instrumented routine (&parent)167#else168add r0, sp, #20169#endif170#ifdef CONFIG_DYNAMIC_FTRACE171@ called from __ftrace_caller, saved in mcount_enter172ldr r1, [sp, #16] @ instrumented routine (func)173mcount_adjust_addr r1, r1174#else175@ called from __mcount, untouched in lr176mcount_adjust_addr r1, lr @ instrumented routine (func)177#endif178mov r2, fpreg @ frame pointer179add r3, sp, #24180bl prepare_ftrace_return181mcount_exit182.endm183184/*185* __gnu_mcount_nc186*/187188.macro mcount_enter189/*190* This pad compensates for the push {lr} at the call site. Note that we are191* unable to unwind through a function which does not otherwise save its lr.192*/193UNWIND(.pad #4)194stmdb sp!, {r0-r3, lr}195UNWIND(.save {r0-r3, lr})196.endm197198.macro mcount_get_lr reg199ldr \reg, [sp, #20]200.endm201202.macro mcount_exit203ldmia sp!, {r0-r3}204ldr lr, [sp, #4]205ldr pc, [sp], #8206.endm207208ENTRY(__gnu_mcount_nc)209UNWIND(.fnstart)210#ifdef CONFIG_DYNAMIC_FTRACE211push {lr}212ldr lr, [sp, #4]213ldr pc, [sp], #8214#else215__mcount216#endif217UNWIND(.fnend)218ENDPROC(__gnu_mcount_nc)219220#ifdef CONFIG_DYNAMIC_FTRACE221ENTRY(ftrace_caller)222UNWIND(.fnstart)223__ftrace_caller224UNWIND(.fnend)225ENDPROC(ftrace_caller)226227#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS228ENTRY(ftrace_regs_caller)229UNWIND(.fnstart)230__ftrace_regs_caller231UNWIND(.fnend)232ENDPROC(ftrace_regs_caller)233#endif234235#endif236237#ifdef CONFIG_FUNCTION_GRAPH_TRACER238ENTRY(ftrace_graph_caller)239UNWIND(.fnstart)240__ftrace_graph_caller241UNWIND(.fnend)242ENDPROC(ftrace_graph_caller)243244#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS245ENTRY(ftrace_graph_regs_caller)246UNWIND(.fnstart)247__ftrace_graph_regs_caller248UNWIND(.fnend)249ENDPROC(ftrace_graph_regs_caller)250#endif251#endif252253.purgem mcount_enter254.purgem mcount_get_lr255.purgem mcount_exit256257#ifdef CONFIG_FUNCTION_GRAPH_TRACER258ENTRY(return_to_handler)259stmdb sp!, {r0-r3}260add r0, sp, #16 @ sp at exit of instrumented routine261bl ftrace_return_to_handler262mov lr, r0 @ r0 has real ret addr263ldmia sp!, {r0-r3}264ret lr265ENDPROC(return_to_handler)266#endif267268ENTRY(ftrace_stub)269.Lftrace_stub:270ret lr271ENDPROC(ftrace_stub)272273ENTRY(ftrace_stub_graph)274ret lr275ENDPROC(ftrace_stub_graph)276277#ifdef CONFIG_DYNAMIC_FTRACE278279__INIT280281.macro init_tramp, dst:req282ENTRY(\dst\()_from_init)283ldr pc, =\dst284ENDPROC(\dst\()_from_init)285.endm286287init_tramp ftrace_caller288#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS289init_tramp ftrace_regs_caller290#endif291#endif292293294