1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * The actual FRED entry points.
4 */
5
6#include <linux/export.h>
7
8#include <asm/asm.h>
9#include <asm/fred.h>
10#include <asm/segment.h>
11
12#include "calling.h"
13
14	.code64
15	.section .noinstr.text, "ax"
16
17.macro FRED_ENTER
18	UNWIND_HINT_END_OF_STACK
19	ENDBR
20	PUSH_AND_CLEAR_REGS
21	movq	%rsp, %rdi	/* %rdi -> pt_regs */
22.endm
23
24.macro FRED_EXIT
25	UNWIND_HINT_REGS
26	POP_REGS
27.endm
28
29/*
30 * The new RIP value that FRED event delivery establishes is
31 * IA32_FRED_CONFIG & ~FFFH for events that occur in ring 3.
32 * Thus the FRED ring 3 entry point must be 4K page aligned.
33 */
34	.align 4096
35
36SYM_CODE_START_NOALIGN(asm_fred_entrypoint_user)
37	FRED_ENTER
38	call	fred_entry_from_user
39SYM_INNER_LABEL(asm_fred_exit_user, SYM_L_GLOBAL)
40	FRED_EXIT
411:	ERETU
42
43	_ASM_EXTABLE_TYPE(1b, asm_fred_entrypoint_user, EX_TYPE_ERETU)
44SYM_CODE_END(asm_fred_entrypoint_user)
45
46/*
47 * The new RIP value that FRED event delivery establishes is
48 * (IA32_FRED_CONFIG & ~FFFH) + 256 for events that occur in
49 * ring 0, i.e., asm_fred_entrypoint_user + 256.
50 */
51	.org asm_fred_entrypoint_user + 256, 0xcc
52SYM_CODE_START_NOALIGN(asm_fred_entrypoint_kernel)
53	FRED_ENTER
54	call	fred_entry_from_kernel
55	FRED_EXIT
56	ERETS
57SYM_CODE_END(asm_fred_entrypoint_kernel)
58
59#if IS_ENABLED(CONFIG_KVM_INTEL)
60SYM_FUNC_START(asm_fred_entry_from_kvm)
61	ANNOTATE_NOENDBR
62	push %rbp
63	mov %rsp, %rbp
64
65	UNWIND_HINT_SAVE
66
67	/*
68	 * Both IRQ and NMI from VMX can be handled on current task stack
69	 * because there is no need to protect from reentrancy and the call
70	 * stack leading to this helper is effectively constant and shallow
71	 * (relatively speaking). Do the same when FRED is active, i.e., no
72	 * need to check current stack level for a stack switch.
73	 *
74	 * Emulate the FRED-defined redzone and stack alignment.
75	 */
76	sub $(FRED_CONFIG_REDZONE_AMOUNT << 6), %rsp
77	and $FRED_STACK_FRAME_RSP_MASK, %rsp
78
79	/*
80	 * Start to push a FRED stack frame, which is always 64 bytes:
81	 *
82	 * +--------+-----------------+
83	 * | Bytes  | Usage           |
84	 * +--------+-----------------+
85	 * | 63:56  | Reserved        |
86	 * | 55:48  | Event Data      |
87	 * | 47:40  | SS + Event Info |
88	 * | 39:32  | RSP             |
89	 * | 31:24  | RFLAGS          |
90	 * | 23:16  | CS + Aux Info   |
91	 * |  15:8  | RIP             |
92	 * |   7:0  | Error Code      |
93	 * +--------+-----------------+
94	 */
95	push $0				/* Reserved, must be 0 */
96	push $0				/* Event data, 0 for IRQ/NMI */
97	push %rdi			/* fred_ss handed in by the caller */
98	push %rbp
99	pushf
100	mov $__KERNEL_CS, %rax
101	push %rax
102
103	/*
104	 * Unlike the IDT event delivery, FRED _always_ pushes an error code
105	 * after pushing the return RIP, thus the CALL instruction CANNOT be
106	 * used here to push the return RIP, otherwise there is no chance to
107	 * push an error code before invoking the IRQ/NMI handler.
108	 *
109	 * Use LEA to get the return RIP and push it, then push an error code.
110	 */
111	lea 1f(%rip), %rax
112	push %rax				/* Return RIP */
113	push $0					/* Error code, 0 for IRQ/NMI */
114
115	PUSH_AND_CLEAR_REGS clear_bp=0 unwind_hint=0
116	movq %rsp, %rdi				/* %rdi -> pt_regs */
117	call __fred_entry_from_kvm		/* Call the C entry point */
118	POP_REGS
119	ERETS
1201:
121	/*
122	 * Objtool doesn't understand what ERETS does, this hint tells it that
123	 * yes, we'll reach here and with what stack state. A save/restore pair
124	 * isn't strictly needed, but it's the simplest form.
125	 */
126	UNWIND_HINT_RESTORE
127	pop %rbp
128	RET
129
130SYM_FUNC_END(asm_fred_entry_from_kvm)
131EXPORT_SYMBOL_GPL(asm_fred_entry_from_kvm);
132#endif
133