root/arch/csky/abiv2/mcount.S
/* SPDX-License-Identifier: GPL-2.0 */
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.

#include <linux/linkage.h>
#include <asm/ftrace.h>
#include <abi/entry.h>
#include <asm/asm-offsets.h>

/*
 * csky-gcc with -pg will put the following asm after prologue:
 *      push    r15
 *      jsri    _mcount
 *
 * stack layout after mcount_enter in _mcount():
 *
 * current sp => 0:+-------+
 *                 | a0-a3 | -> must save all argument regs
 *             +16:+-------+
 *                 | lr    | -> _mcount lr (instrumente function's pc)
 *             +20:+-------+
 *                 | fp=r8 | -> instrumented function fp
 *             +24:+-------+
 *                 | plr   | -> instrumented function lr (parent's pc)
 *                 +-------+
 */

.macro mcount_enter
        subi    sp, 24
        stw     a0, (sp, 0)
        stw     a1, (sp, 4)
        stw     a2, (sp, 8)
        stw     a3, (sp, 12)
        stw     lr, (sp, 16)
        stw     r8, (sp, 20)
.endm

.macro mcount_exit
        ldw     a0, (sp, 0)
        ldw     a1, (sp, 4)
        ldw     a2, (sp, 8)
        ldw     a3, (sp, 12)
        ldw     t1, (sp, 16)
        ldw     r8, (sp, 20)
        ldw     lr, (sp, 24)
        addi    sp, 28
        jmp     t1
.endm

.macro mcount_enter_regs
        subi    sp, 8
        stw     lr, (sp, 0)
        stw     r8, (sp, 4)
        SAVE_REGS_FTRACE
.endm

.macro mcount_exit_regs
        RESTORE_REGS_FTRACE
        subi    sp, 152
        ldw     t1, (sp, 4)
        addi    sp, 152
        ldw     r8, (sp, 4)
        ldw     lr, (sp, 8)
        addi    sp, 12
        jmp     t1
.endm

.macro save_return_regs
        subi    sp, 16
        stw     a0, (sp, 0)
        stw     a1, (sp, 4)
        stw     a2, (sp, 8)
        stw     a3, (sp, 12)
.endm

.macro restore_return_regs
        mov     lr, a0
        ldw     a0, (sp, 0)
        ldw     a1, (sp, 4)
        ldw     a2, (sp, 8)
        ldw     a3, (sp, 12)
        addi    sp, 16
.endm

.macro nop32_stub
        nop32
        nop32
        nop32
.endm

ENTRY(ftrace_stub)
        jmp     lr
END(ftrace_stub)

#ifndef CONFIG_DYNAMIC_FTRACE
ENTRY(_mcount)
        mcount_enter

        /* r26 is link register, only used with jsri translation */
        lrw     r26, ftrace_trace_function
        ldw     r26, (r26, 0)
        lrw     a1, ftrace_stub
        cmpne   r26, a1
        bf      skip_ftrace

        mov     a0, lr
        subi    a0, 4
        ldw     a1, (sp, 24)
        lrw     a2, function_trace_op
        ldw     a2, (a2, 0)

        jsr     r26

#ifndef CONFIG_FUNCTION_GRAPH_TRACER
skip_ftrace:
        mcount_exit
#else
skip_ftrace:
        lrw     a0, ftrace_graph_return
        ldw     a0, (a0, 0)
        lrw     a1, ftrace_stub
        cmpne   a0, a1
        bt      ftrace_graph_caller

        lrw     a0, ftrace_graph_entry
        ldw     a0, (a0, 0)
        lrw     a1, ftrace_graph_entry_stub
        cmpne   a0, a1
        bt      ftrace_graph_caller

        mcount_exit
#endif
END(_mcount)
#else /* CONFIG_DYNAMIC_FTRACE */
ENTRY(_mcount)
        mov     t1, lr
        ldw     lr, (sp, 0)
        addi    sp, 4
        jmp     t1
ENDPROC(_mcount)

ENTRY(ftrace_caller)
        mcount_enter

        ldw     a0, (sp, 16)
        subi    a0, 4
        ldw     a1, (sp, 24)
        lrw     a2, function_trace_op
        ldw     a2, (a2, 0)

        nop
GLOBAL(ftrace_call)
        nop32_stub

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
        nop
GLOBAL(ftrace_graph_call)
        nop32_stub
#endif

        mcount_exit
ENDPROC(ftrace_caller)
#endif /* CONFIG_DYNAMIC_FTRACE */

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
ENTRY(ftrace_graph_caller)
        mov     a0, sp
        addi    a0, 24
        ldw     a1, (sp, 16)
        subi    a1, 4
        mov     a2, r8
        lrw     r26, prepare_ftrace_return
        jsr     r26
        mcount_exit
END(ftrace_graph_caller)

ENTRY(return_to_handler)
        save_return_regs
        mov     a0, r8
        jsri    ftrace_return_to_handler
        restore_return_regs
        jmp     lr
END(return_to_handler)
#endif

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
ENTRY(ftrace_regs_caller)
        mcount_enter_regs

        lrw     t1, PT_FRAME_SIZE
        add     t1, sp

        ldw     a0, (t1, 0)
        subi    a0, 4
        ldw     a1, (t1, 8)
        lrw     a2, function_trace_op
        ldw     a2, (a2, 0)
        mov     a3, sp

        nop
GLOBAL(ftrace_regs_call)
        nop32_stub

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
        nop
GLOBAL(ftrace_graph_regs_call)
        nop32_stub
#endif

        mcount_exit_regs
ENDPROC(ftrace_regs_caller)
#endif /* CONFIG_DYNAMIC_FTRACE */