root/arch/sh/lib/mcount.S
/* SPDX-License-Identifier: GPL-2.0
 *
 * arch/sh/lib/mcount.S
 *
 *  Copyright (C) 2008, 2009  Paul Mundt
 *  Copyright (C) 2008, 2009  Matt Fleming
 */
#include <asm/ftrace.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>

#define MCOUNT_ENTER()          \
        mov.l   r4, @-r15;      \
        mov.l   r5, @-r15;      \
        mov.l   r6, @-r15;      \
        mov.l   r7, @-r15;      \
        sts.l   pr, @-r15;      \
                                \
        mov.l   @(20,r15),r4;   \
        sts     pr, r5

#define MCOUNT_LEAVE()          \
        lds.l   @r15+, pr;      \
        mov.l   @r15+, r7;      \
        mov.l   @r15+, r6;      \
        mov.l   @r15+, r5;      \
        rts;                    \
         mov.l  @r15+, r4

#ifdef CONFIG_STACK_DEBUG
/*
 * Perform diagnostic checks on the state of the kernel stack.
 *
 * Check for stack overflow. If there is less than 1KB free
 * then it has overflowed.
 *
 * Make sure the stack pointer contains a valid address. Valid
 * addresses for kernel stacks are anywhere after the bss
 * (after __bss_stop) and anywhere in init_thread_union (init_stack).
 */
#define STACK_CHECK()                                   \
        mov     #(THREAD_SIZE >> 10), r0;               \
        shll8   r0;                                     \
        shll2   r0;                                     \
                                                        \
        /* r1 = sp & (THREAD_SIZE - 1) */               \
        mov     #-1, r1;                                \
        add     r0, r1;                                 \
        and     r15, r1;                                \
                                                        \
        mov     #TI_SIZE, r3;                           \
        mov     #(STACK_WARN >> 8), r2;                 \
        shll8   r2;                                     \
        add     r3, r2;                                 \
                                                        \
        /* Is the stack overflowing? */                 \
        cmp/hi  r2, r1;                                 \
        bf      stack_panic;                            \
                                                        \
        /* If sp > __bss_stop then we're OK. */         \
        mov.l   .L_ebss, r1;                            \
        cmp/hi  r1, r15;                                \
        bt      1f;                                     \
                                                        \
        /* If sp < init_stack, we're not OK. */         \
        mov.l   .L_init_thread_union, r1;               \
        cmp/hs  r1, r15;                                \
        bf      stack_panic;                            \
                                                        \
        /* If sp > init_stack && sp < __bss_stop, not OK. */    \
        add     r0, r1;                                 \
        cmp/hs  r1, r15;                                \
        bt      stack_panic;                            \
1:
#else
#define STACK_CHECK()
#endif /* CONFIG_STACK_DEBUG */

        .align 2
        .globl  _mcount
        .type   _mcount,@function
        .globl  mcount
        .type   mcount,@function
_mcount:
mcount:
        STACK_CHECK()

#ifndef CONFIG_FUNCTION_TRACER
        rts
         nop
#else
        MCOUNT_ENTER()

#ifdef CONFIG_DYNAMIC_FTRACE
        .globl  mcount_call
mcount_call:
        mov.l   .Lftrace_stub, r6
#else
        mov.l   .Lftrace_trace_function, r6
        mov.l   ftrace_stub, r7
        cmp/eq  r6, r7
        bt      skip_trace
        mov.l   @r6, r6
#endif

        jsr     @r6
         nop

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
        mov.l   .Lftrace_graph_return, r6
        mov.l   .Lftrace_stub, r7
        cmp/eq  r6, r7
        bt      1f

        mov.l   .Lftrace_graph_caller, r0
        jmp     @r0
         nop

1:
        mov.l   .Lftrace_graph_entry, r6
        mov.l   .Lftrace_graph_entry_stub, r7
        cmp/eq  r6, r7
        bt      skip_trace

        mov.l   .Lftrace_graph_caller, r0
        jmp     @r0
         nop

        .align 2
.Lftrace_graph_return:
        .long   ftrace_graph_return
.Lftrace_graph_entry:
        .long   ftrace_graph_entry
.Lftrace_graph_entry_stub:
        .long   ftrace_graph_entry_stub
.Lftrace_graph_caller:
        .long   ftrace_graph_caller
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */

        .globl skip_trace
skip_trace:
        MCOUNT_LEAVE()

        .align 2
.Lftrace_trace_function:
        .long   ftrace_trace_function

#ifdef CONFIG_DYNAMIC_FTRACE
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/*
 * NOTE: Do not move either ftrace_graph_call or ftrace_caller
 * as this will affect the calculation of GRAPH_INSN_OFFSET.
 */
        .globl ftrace_graph_call
ftrace_graph_call:
        mov.l   .Lskip_trace, r0
        jmp     @r0
         nop

        .align 2
.Lskip_trace:
        .long   skip_trace
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */

        .globl ftrace_caller
ftrace_caller:
        MCOUNT_ENTER()

        .globl ftrace_call
ftrace_call:
        mov.l   .Lftrace_stub, r6
        jsr     @r6
         nop

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
        bra     ftrace_graph_call
         nop
#else
        MCOUNT_LEAVE()
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
#endif /* CONFIG_DYNAMIC_FTRACE */

        .align 2

/*
 * NOTE: From here on the locations of the .Lftrace_stub label and
 * ftrace_stub itself are fixed. Adding additional data here will skew
 * the displacement for the memory table and break the block replacement.
 * Place new labels either after the ftrace_stub body, or before
 * ftrace_caller. You have been warned.
 */
.Lftrace_stub:
        .long   ftrace_stub

        .globl  ftrace_stub
ftrace_stub:
        rts
         nop

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
        .globl  ftrace_graph_caller
ftrace_graph_caller:
        mov.l   2f, r1
        jmp     @r1
         nop
1:
        /*
         * MCOUNT_ENTER() pushed 5 registers onto the stack, so
         * the stack address containing our return address is
         * r15 + 20.
         */
        mov     #20, r0
        add     r15, r0
        mov     r0, r4

        mov.l   .Lprepare_ftrace_return, r0
        jsr     @r0
         nop

        MCOUNT_LEAVE()

        .align 2
2:      .long   skip_trace
.Lprepare_ftrace_return:
        .long   prepare_ftrace_return

        .globl  return_to_handler
return_to_handler:
        /*
         * Save the return values.
         */
        mov.l   r0, @-r15
        mov.l   r1, @-r15

        mov     #0, r4

        mov.l   .Lftrace_return_to_handler, r0
        jsr     @r0
         nop

        /*
         * The return value from ftrace_return_handler has the real
         * address that we should return to.
         */
        lds     r0, pr
        mov.l   @r15+, r1
        rts
         mov.l  @r15+, r0


        .align 2
.Lftrace_return_to_handler:
        .long   ftrace_return_to_handler
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
#endif /* CONFIG_FUNCTION_TRACER */

#ifdef CONFIG_STACK_DEBUG
        .globl  stack_panic
stack_panic:
        mov.l   .Ldump_stack, r0
        jsr     @r0
         nop

        mov.l   .Lpanic, r0
        jsr     @r0
         mov.l  .Lpanic_s, r4

        rts
         nop

        .align 2
.L_init_thread_union:
        .long   init_thread_union
.L_ebss:
        .long   __bss_stop
.Lpanic:
        .long   panic
.Lpanic_s:
        .long   .Lpanic_str
.Ldump_stack:
        .long   dump_stack

        .section        .rodata
        .align 2
.Lpanic_str:
        .string "Stack error"
#endif /* CONFIG_STACK_DEBUG */