2026-01-29 22:25:33 +08:00

327 lines
7.0 KiB
ArmAsm

/* SPDX-License-Identifier: GPL-2.0 */
/*
* arch/sw_64/kernel/entry-ftrace.S
*
* Author: linyue
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/ftrace.h>
.text
.set noat
.align 4
#define FTRACE_SP_OFF 0x50
.macro mcount_enter
subl $sp, FTRACE_SP_OFF, $sp
stl $16, 0($sp)
stl $17, 0x8($sp)
stl $18, 0x10($sp)
stl $26, 0x18($sp)
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
stl $9, 0x20($sp)
#endif
stl $28, 0x28($sp)
stl $29, 0x30($sp)
stl $19, 0x38($sp)
stl $20, 0x40($sp)
stl $21, 0x48($sp)
.endm
.macro mcount_end
ldl $16, 0($sp)
ldl $17, 0x8($sp)
ldl $18, 0x10($sp)
ldl $26, 0x18($sp)
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
ldl $9, 0x20($sp)
#endif
ldl $28, 0x28($sp)
ldl $29, 0x30($sp)
ldl $19, 0x38($sp)
ldl $20, 0x40($sp)
ldl $21, 0x48($sp)
addl $sp, FTRACE_SP_OFF, $sp
.endm
.macro RESTORE_GRAPH_ARGS
ldi $16, 0x18($sp) /* &ra */
bis $31, $9, $17 /* pc */
#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
bis $31, $15, $18 /* fp */
#endif
.endm
.macro SAVE_PT_REGS
ldi $sp, -PT_REGS_SIZE($sp)
stl $0, PT_REGS_R0($sp)
stl $1, PT_REGS_R1($sp)
stl $2, PT_REGS_R2($sp)
stl $3, PT_REGS_R3($sp)
stl $4, PT_REGS_R4($sp)
stl $5, PT_REGS_R5($sp)
stl $6, PT_REGS_R6($sp)
stl $7, PT_REGS_R7($sp)
stl $8, PT_REGS_R8($sp)
stl $9, PT_REGS_R9($sp)
stl $10, PT_REGS_R10($sp)
stl $11, PT_REGS_R11($sp)
stl $12, PT_REGS_R12($sp)
stl $13, PT_REGS_R13($sp)
stl $14, PT_REGS_R14($sp)
stl $15, PT_REGS_R15($sp)
stl $16, PT_REGS_R16($sp)
stl $17, PT_REGS_R17($sp)
stl $18, PT_REGS_R18($sp)
stl $19, PT_REGS_R19($sp)
stl $20, PT_REGS_R20($sp)
stl $21, PT_REGS_R21($sp)
stl $22, PT_REGS_R22($sp)
stl $23, PT_REGS_R23($sp)
stl $24, PT_REGS_R24($sp)
stl $25, PT_REGS_R25($sp)
stl $26, PT_REGS_R26($sp)
stl $27, PT_REGS_R27($sp)
stl $28, PT_REGS_R28($sp)
stl $29, PT_REGS_GP($sp)
ldi $0, PT_REGS_SIZE($sp)
stl $0, PT_REGS_SP($sp)
.endm
.macro RESTORE_PT_REGS
ldl $0, PT_REGS_R0($sp)
ldl $1, PT_REGS_R1($sp)
ldl $2, PT_REGS_R2($sp)
ldl $3, PT_REGS_R3($sp)
ldl $4, PT_REGS_R4($sp)
ldl $5, PT_REGS_R5($sp)
ldl $6, PT_REGS_R6($sp)
ldl $7, PT_REGS_R7($sp)
ldl $8, PT_REGS_R8($sp)
ldl $9, PT_REGS_R9($sp)
ldl $10, PT_REGS_R10($sp)
ldl $11, PT_REGS_R11($sp)
ldl $12, PT_REGS_R12($sp)
ldl $13, PT_REGS_R13($sp)
ldl $14, PT_REGS_R14($sp)
ldl $15, PT_REGS_R15($sp)
ldl $16, PT_REGS_R16($sp)
ldl $17, PT_REGS_R17($sp)
ldl $18, PT_REGS_R18($sp)
ldl $19, PT_REGS_R19($sp)
ldl $20, PT_REGS_R20($sp)
ldl $21, PT_REGS_R21($sp)
ldl $22, PT_REGS_R22($sp)
ldl $23, PT_REGS_R23($sp)
ldl $24, PT_REGS_R24($sp)
ldl $25, PT_REGS_R25($sp)
ldl $26, PT_REGS_R26($sp)
ldl $27, PT_REGS_R27($sp)
ldl $28, PT_REGS_R28($sp)
ldl $29, PT_REGS_GP($sp)
ldi $sp, PT_REGS_SIZE($sp)
.endm
.macro RESTORE_GRAPH_REG_ARGS
ldi $16, PT_REGS_R26($sp)
bis $31, $9, $17
#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
bis $31, $15, $18
#endif
.endm
/* save return value regs*/
.macro save_return_regs
subl $sp, 0x8, $sp
stl $0, 0x0($sp)
.endm
/* restore return value regs*/
.macro restore_return_regs
ldl $0, 0x0($sp)
addl $sp, 0x8, $sp
.endm
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/*
* void ftrace_graph_caller(void)
*
* Called from ftrace_caller() or ftrace_regs_caller() when function_graph
* tracer is selected.
* This function prepare_ftrace_return() fakes ra's value on the call
* stack in order to intercept instrumented function's return path and
* run return_to_handler() later on its exit.
*/
ENTRY(ftrace_graph_caller)
ldgp $29, 0($27)
ldi $sp, -16($sp)
stl $26, 0($sp)
stl $15, 8($sp)
bis $31, $sp, $15
ldi $27, prepare_ftrace_return
ftrace_graph_call:
.global ftrace_graph_call
/*
* Calling ftrace_enable/disable_ftrace_graph_caller would overwrite
* the nop below.
*/
nop /* nop, or call prepare_ftrace_return() */
ldl $26, 0($sp)
ldl $15, 8($sp)
ldi $sp, 16($sp)
ret $31, ($26), 1
ENDPROC(ftrace_graph_caller)
/*
* void return_to_handler(void)
*
* Run ftrace_return_to_handler() before going back to parent.
* @fp is checked against the value passed by ftrace_graph_caller()
* only when HAVE_FUNCTION_GRAPH_FP_TEST is enabled.
*
* It is run by "ret" instruction which does not modify $27, so it
* has to recaculate $27 before ldgp.
*/
ENTRY(return_to_handler)
br $27, 1f
1: ldgp $29, 0($27)
save_return_regs
bis $31, $15, $16 /* parent's fp */
ldi $27, ftrace_return_to_handler
call $26, ($27)
bis $31, $0, $26
restore_return_regs
ret $31, ($26), 1
END(return_to_handler)
#endif
#ifdef CONFIG_DYNAMIC_FTRACE
.global _mcount
.ent _mcount
_mcount:
ret $31, ($28), 1
.end _mcount
.global ftrace_caller
.ent ftrace_caller
ftrace_caller:
mcount_enter
br $27, 1f
1: ldgp $29, 0($27)
subl $28, MCOUNT_INSN_SIZE, $16
bis $26, $31, $17
ldl $18, function_trace_op
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/*
* the graph tracer (specifically, prepare_ftrace_return) needs these
* arguments but for now the function tracer occupies the regs, so we
* save them in callee-saved regs to recover later.
*/
bis $31, $16, $9
#endif
ldi $4, current_tracer
ldl $27, 0($4)
.global ftrace_call
ftrace_call: /* tracer(pc, ra); */
nop
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
RESTORE_GRAPH_ARGS
call ftrace_graph_caller
#endif
mcount_end
ret $31, ($28), 1
.end ftrace_caller
#else /* !CONFIG_DYNAMIC_FTRACE */
.global _mcount
.ent _mcount
_mcount:
mcount_enter
br $27, 1f
1: ldgp $29, 0($27)
ldl $27, ftrace_trace_function // if (ftrace_trace_function
ldi $5, ftrace_stub // != ftrace_stub)
cmpeq $27, $5, $6 //
bne $6, skip_ftrace
subl $28, MCOUNT_INSN_SIZE, $16 // function's pc
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
bis $31, $16, $9
#endif
bis $26, $31, $17 // function's ra (parent's pc)
call $26, ($27) // (*ftrace_trace_function)(pc, ra);
skip_ftrace:
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
ldl $4, ftrace_graph_return // if ((ftrace_graph_return
cmpeq $4, $5, $6 // != ftrace_stub)
beq $6, 2f
ldl $4, ftrace_graph_entry // || (ftrace_graph_entry
ldi $5, ftrace_graph_entry_stub // != ftrace_graph_entry_stub))
cmpeq $4, $5, $6
bne $6, 3f
2: RESTORE_GRAPH_ARGS
call ftrace_graph_caller // ftrace_graph_caller();
#endif
3: mcount_end
ret $31, ($28), 1
.end _mcount
#endif /* CONFIG_DYNAMIC_FTRACE */
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
.global ftrace_regs_caller
.ent ftrace_regs_caller
ftrace_regs_caller:
SAVE_PT_REGS
br $27, 1f
1: ldgp $29, 0($27)
subl $28, MCOUNT_INSN_SIZE, $16
bis $26, $31, $17
ldi $4, function_trace_op
ldl $18, 0($4)
mov $sp, $19
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
bis $31, $16, $9
#endif
ldi $4, current_tracer
ldl $27, 0($4)
.global ftrace_regs_call
ftrace_regs_call:
nop
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
RESTORE_GRAPH_REG_ARGS
call ftrace_graph_caller
#endif
RESTORE_PT_REGS
ret $31, ($28), 1
.end ftrace_regs_caller
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */
.global ftrace_stub
.ent ftrace_stub
ftrace_stub:
ret $31, ($26), 1
.end ftrace_stub