162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2017  Steven Rostedt, VMware Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/linkage.h>
762306a36Sopenharmony_ci#include <asm/page_types.h>
862306a36Sopenharmony_ci#include <asm/segment.h>
962306a36Sopenharmony_ci#include <asm/export.h>
1062306a36Sopenharmony_ci#include <asm/ftrace.h>
1162306a36Sopenharmony_ci#include <asm/nospec-branch.h>
1262306a36Sopenharmony_ci#include <asm/frame.h>
1362306a36Sopenharmony_ci#include <asm/asm-offsets.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#ifdef CONFIG_FRAME_POINTER
1662306a36Sopenharmony_ci# define MCOUNT_FRAME			1	/* using frame = true  */
1762306a36Sopenharmony_ci#else
1862306a36Sopenharmony_ci# define MCOUNT_FRAME			0	/* using frame = false */
1962306a36Sopenharmony_ci#endif
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciSYM_FUNC_START(__fentry__)
2262306a36Sopenharmony_ci	RET
2362306a36Sopenharmony_ciSYM_FUNC_END(__fentry__)
2462306a36Sopenharmony_ciEXPORT_SYMBOL(__fentry__)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciSYM_CODE_START(ftrace_caller)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#ifdef CONFIG_FRAME_POINTER
2962306a36Sopenharmony_ci	/*
3062306a36Sopenharmony_ci	 * Frame pointers are of ip followed by bp.
3162306a36Sopenharmony_ci	 * Since fentry is an immediate jump, we are left with
3262306a36Sopenharmony_ci	 * parent-ip, function-ip. We need to add a frame with
3362306a36Sopenharmony_ci	 * parent-ip followed by ebp.
3462306a36Sopenharmony_ci	 */
3562306a36Sopenharmony_ci	pushl	4(%esp)				/* parent ip */
3662306a36Sopenharmony_ci	pushl	%ebp
3762306a36Sopenharmony_ci	movl	%esp, %ebp
3862306a36Sopenharmony_ci	pushl	2*4(%esp)			/* function ip */
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/* For mcount, the function ip is directly above */
4162306a36Sopenharmony_ci	pushl	%ebp
4262306a36Sopenharmony_ci	movl	%esp, %ebp
4362306a36Sopenharmony_ci#endif
4462306a36Sopenharmony_ci	pushl	%eax
4562306a36Sopenharmony_ci	pushl	%ecx
4662306a36Sopenharmony_ci	pushl	%edx
4762306a36Sopenharmony_ci	pushl	$0				/* Pass NULL as regs pointer */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#ifdef CONFIG_FRAME_POINTER
5062306a36Sopenharmony_ci	/* Load parent ebp into edx */
5162306a36Sopenharmony_ci	movl	4*4(%esp), %edx
5262306a36Sopenharmony_ci#else
5362306a36Sopenharmony_ci	/* There's no frame pointer, load the appropriate stack addr instead */
5462306a36Sopenharmony_ci	lea	4*4(%esp), %edx
5562306a36Sopenharmony_ci#endif
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	movl	(MCOUNT_FRAME+4)*4(%esp), %eax	/* load the rip */
5862306a36Sopenharmony_ci	/* Get the parent ip */
5962306a36Sopenharmony_ci	movl	4(%edx), %edx			/* edx has ebp */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	movl	function_trace_op, %ecx
6262306a36Sopenharmony_ci	subl	$MCOUNT_INSN_SIZE, %eax
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci.globl ftrace_call
6562306a36Sopenharmony_ciftrace_call:
6662306a36Sopenharmony_ci	call	ftrace_stub
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	addl	$4, %esp			/* skip NULL pointer */
6962306a36Sopenharmony_ci	popl	%edx
7062306a36Sopenharmony_ci	popl	%ecx
7162306a36Sopenharmony_ci	popl	%eax
7262306a36Sopenharmony_ci#ifdef CONFIG_FRAME_POINTER
7362306a36Sopenharmony_ci	popl	%ebp
7462306a36Sopenharmony_ci	addl	$4,%esp				/* skip function ip */
7562306a36Sopenharmony_ci	popl	%ebp				/* this is the orig bp */
7662306a36Sopenharmony_ci	addl	$4, %esp			/* skip parent ip */
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci.Lftrace_ret:
7962306a36Sopenharmony_ci#ifdef CONFIG_FUNCTION_GRAPH_TRACER
8062306a36Sopenharmony_ci.globl ftrace_graph_call
8162306a36Sopenharmony_ciftrace_graph_call:
8262306a36Sopenharmony_ci	jmp	ftrace_stub
8362306a36Sopenharmony_ci#endif
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* This is weak to keep gas from relaxing the jumps */
8662306a36Sopenharmony_ciSYM_INNER_LABEL_ALIGN(ftrace_stub, SYM_L_WEAK)
8762306a36Sopenharmony_ci	RET
8862306a36Sopenharmony_ciSYM_CODE_END(ftrace_caller)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciSYM_CODE_START(ftrace_regs_caller)
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 * We're here from an mcount/fentry CALL, and the stack frame looks like:
9362306a36Sopenharmony_ci	 *
9462306a36Sopenharmony_ci	 *  <previous context>
9562306a36Sopenharmony_ci	 *  RET-IP
9662306a36Sopenharmony_ci	 *
9762306a36Sopenharmony_ci	 * The purpose of this function is to call out in an emulated INT3
9862306a36Sopenharmony_ci	 * environment with a stack frame like:
9962306a36Sopenharmony_ci	 *
10062306a36Sopenharmony_ci	 *  <previous context>
10162306a36Sopenharmony_ci	 *  gap / RET-IP
10262306a36Sopenharmony_ci	 *  gap
10362306a36Sopenharmony_ci	 *  gap
10462306a36Sopenharmony_ci	 *  gap
10562306a36Sopenharmony_ci	 *  pt_regs
10662306a36Sopenharmony_ci	 *
10762306a36Sopenharmony_ci	 * We do _NOT_ restore: ss, flags, cs, gs, fs, es, ds
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci	subl	$3*4, %esp	# RET-IP + 3 gaps
11062306a36Sopenharmony_ci	pushl	%ss		# ss
11162306a36Sopenharmony_ci	pushl	%esp		# points at ss
11262306a36Sopenharmony_ci	addl	$5*4, (%esp)	#   make it point at <previous context>
11362306a36Sopenharmony_ci	pushfl			# flags
11462306a36Sopenharmony_ci	pushl	$__KERNEL_CS	# cs
11562306a36Sopenharmony_ci	pushl	7*4(%esp)	# ip <- RET-IP
11662306a36Sopenharmony_ci	pushl	$0		# orig_eax
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	pushl	%gs
11962306a36Sopenharmony_ci	pushl	%fs
12062306a36Sopenharmony_ci	pushl	%es
12162306a36Sopenharmony_ci	pushl	%ds
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	pushl	%eax
12462306a36Sopenharmony_ci	pushl	%ebp
12562306a36Sopenharmony_ci	pushl	%edi
12662306a36Sopenharmony_ci	pushl	%esi
12762306a36Sopenharmony_ci	pushl	%edx
12862306a36Sopenharmony_ci	pushl	%ecx
12962306a36Sopenharmony_ci	pushl	%ebx
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	ENCODE_FRAME_POINTER
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	movl	PT_EIP(%esp), %eax	# 1st argument: IP
13462306a36Sopenharmony_ci	subl	$MCOUNT_INSN_SIZE, %eax
13562306a36Sopenharmony_ci	movl	21*4(%esp), %edx	# 2nd argument: parent ip
13662306a36Sopenharmony_ci	movl	function_trace_op, %ecx	# 3rd argument: ftrace_pos
13762306a36Sopenharmony_ci	pushl	%esp			# 4th argument: pt_regs
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciSYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
14062306a36Sopenharmony_ci	call	ftrace_stub
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	addl	$4, %esp		# skip 4th argument
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* place IP below the new SP */
14562306a36Sopenharmony_ci	movl	PT_OLDESP(%esp), %eax
14662306a36Sopenharmony_ci	movl	PT_EIP(%esp), %ecx
14762306a36Sopenharmony_ci	movl	%ecx, -4(%eax)
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* place EAX below that */
15062306a36Sopenharmony_ci	movl	PT_EAX(%esp), %ecx
15162306a36Sopenharmony_ci	movl	%ecx, -8(%eax)
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	popl	%ebx
15462306a36Sopenharmony_ci	popl	%ecx
15562306a36Sopenharmony_ci	popl	%edx
15662306a36Sopenharmony_ci	popl	%esi
15762306a36Sopenharmony_ci	popl	%edi
15862306a36Sopenharmony_ci	popl	%ebp
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	lea	-8(%eax), %esp
16162306a36Sopenharmony_ci	popl	%eax
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	jmp	.Lftrace_ret
16462306a36Sopenharmony_ciSYM_CODE_END(ftrace_regs_caller)
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ciSYM_FUNC_START(ftrace_stub_direct_tramp)
16762306a36Sopenharmony_ci	CALL_DEPTH_ACCOUNT
16862306a36Sopenharmony_ci	RET
16962306a36Sopenharmony_ciSYM_FUNC_END(ftrace_stub_direct_tramp)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci#ifdef CONFIG_FUNCTION_GRAPH_TRACER
17262306a36Sopenharmony_ciSYM_CODE_START(ftrace_graph_caller)
17362306a36Sopenharmony_ci	pushl	%eax
17462306a36Sopenharmony_ci	pushl	%ecx
17562306a36Sopenharmony_ci	pushl	%edx
17662306a36Sopenharmony_ci	movl	3*4(%esp), %eax
17762306a36Sopenharmony_ci	/* Even with frame pointers, fentry doesn't have one here */
17862306a36Sopenharmony_ci	lea	4*4(%esp), %edx
17962306a36Sopenharmony_ci	movl	$0, %ecx
18062306a36Sopenharmony_ci	subl	$MCOUNT_INSN_SIZE, %eax
18162306a36Sopenharmony_ci	call	prepare_ftrace_return
18262306a36Sopenharmony_ci	popl	%edx
18362306a36Sopenharmony_ci	popl	%ecx
18462306a36Sopenharmony_ci	popl	%eax
18562306a36Sopenharmony_ci	RET
18662306a36Sopenharmony_ciSYM_CODE_END(ftrace_graph_caller)
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci.globl return_to_handler
18962306a36Sopenharmony_cireturn_to_handler:
19062306a36Sopenharmony_ci	pushl	$0
19162306a36Sopenharmony_ci	pushl	%edx
19262306a36Sopenharmony_ci	pushl	%eax
19362306a36Sopenharmony_ci	movl	%esp, %eax
19462306a36Sopenharmony_ci	call	ftrace_return_to_handler
19562306a36Sopenharmony_ci	movl	%eax, %ecx
19662306a36Sopenharmony_ci	popl	%eax
19762306a36Sopenharmony_ci	popl	%edx
19862306a36Sopenharmony_ci	addl	$4, %esp		# skip ebp
19962306a36Sopenharmony_ci	JMP_NOSPEC ecx
20062306a36Sopenharmony_ci#endif
201