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 | |
36 | SYM_CODE_START_NOALIGN(asm_fred_entrypoint_user) |
37 | FRED_ENTER |
38 | call fred_entry_from_user |
39 | SYM_INNER_LABEL(asm_fred_exit_user, SYM_L_GLOBAL) |
40 | FRED_EXIT |
41 | 1: ERETU |
42 | |
43 | _ASM_EXTABLE_TYPE(1b, asm_fred_entrypoint_user, EX_TYPE_ERETU) |
44 | SYM_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 |
52 | SYM_CODE_START_NOALIGN(asm_fred_entrypoint_kernel) |
53 | FRED_ENTER |
54 | call fred_entry_from_kernel |
55 | FRED_EXIT |
56 | ERETS |
57 | SYM_CODE_END(asm_fred_entrypoint_kernel) |
58 | |
59 | #if IS_ENABLED(CONFIG_KVM_INTEL) |
60 | SYM_FUNC_START(asm_fred_entry_from_kvm) |
61 | push %rbp |
62 | mov %rsp, %rbp |
63 | |
64 | UNWIND_HINT_SAVE |
65 | |
66 | /* |
67 | * Both IRQ and NMI from VMX can be handled on current task stack |
68 | * because there is no need to protect from reentrancy and the call |
69 | * stack leading to this helper is effectively constant and shallow |
70 | * (relatively speaking). Do the same when FRED is active, i.e., no |
71 | * need to check current stack level for a stack switch. |
72 | * |
73 | * Emulate the FRED-defined redzone and stack alignment. |
74 | */ |
75 | sub $(FRED_CONFIG_REDZONE_AMOUNT << 6), %rsp |
76 | and $FRED_STACK_FRAME_RSP_MASK, %rsp |
77 | |
78 | /* |
79 | * Start to push a FRED stack frame, which is always 64 bytes: |
80 | * |
81 | * +--------+-----------------+ |
82 | * | Bytes | Usage | |
83 | * +--------+-----------------+ |
84 | * | 63:56 | Reserved | |
85 | * | 55:48 | Event Data | |
86 | * | 47:40 | SS + Event Info | |
87 | * | 39:32 | RSP | |
88 | * | 31:24 | RFLAGS | |
89 | * | 23:16 | CS + Aux Info | |
90 | * | 15:8 | RIP | |
91 | * | 7:0 | Error Code | |
92 | * +--------+-----------------+ |
93 | */ |
94 | push $0 /* Reserved, must be 0 */ |
95 | push $0 /* Event data, 0 for IRQ/NMI */ |
96 | push %rdi /* fred_ss handed in by the caller */ |
97 | push %rbp |
98 | pushf |
99 | mov $__KERNEL_CS, %rax |
100 | push %rax |
101 | |
102 | /* |
103 | * Unlike the IDT event delivery, FRED _always_ pushes an error code |
104 | * after pushing the return RIP, thus the CALL instruction CANNOT be |
105 | * used here to push the return RIP, otherwise there is no chance to |
106 | * push an error code before invoking the IRQ/NMI handler. |
107 | * |
108 | * Use LEA to get the return RIP and push it, then push an error code. |
109 | */ |
110 | lea 1f(%rip), %rax |
111 | push %rax /* Return RIP */ |
112 | push $0 /* Error code, 0 for IRQ/NMI */ |
113 | |
114 | PUSH_AND_CLEAR_REGS clear_bp=0 unwind_hint=0 |
115 | movq %rsp, %rdi /* %rdi -> pt_regs */ |
116 | call __fred_entry_from_kvm /* Call the C entry point */ |
117 | POP_REGS |
118 | ERETS |
119 | 1: |
120 | /* |
121 | * Objtool doesn't understand what ERETS does, this hint tells it that |
122 | * yes, we'll reach here and with what stack state. A save/restore pair |
123 | * isn't strictly needed, but it's the simplest form. |
124 | */ |
125 | UNWIND_HINT_RESTORE |
126 | pop %rbp |
127 | RET |
128 | |
129 | SYM_FUNC_END(asm_fred_entry_from_kvm) |
130 | EXPORT_SYMBOL_GPL(asm_fred_entry_from_kvm); |
131 | #endif |
132 | |