Skip to content

Commit 14619d9

Browse files
H. Peter Anvin (Intel)bp3tk0v
authored andcommitted
x86/fred: FRED entry/exit and dispatch code
The code to actually handle kernel and event entry/exit using FRED. It is split up into two files thus: - entry_64_fred.S contains the actual entrypoints and exit code, and saves and restores registers. - entry_fred.c contains the two-level event dispatch code for FRED. The first-level dispatch is on the event type, and the second-level is on the event vector. [ bp: Fold in an allmodconfig clang build fix: https://lore.kernel.org/r/[email protected] and a CONFIG_IA32_EMULATION=n build fix: https://lore.kernel.org/r/[email protected]] Suggested-by: Thomas Gleixner <[email protected]> Originally-by: Megha Dey <[email protected]> Co-developed-by: Xin Li <[email protected]> Signed-off-by: H. Peter Anvin (Intel) <[email protected]> Signed-off-by: Xin Li <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Tested-by: Shan Kang <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent ffa4901 commit 14619d9

File tree

6 files changed

+308
-3
lines changed

6 files changed

+308
-3
lines changed

arch/x86/entry/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ obj-y += vdso/
1818
obj-y += vsyscall/
1919

2020
obj-$(CONFIG_PREEMPTION) += thunk_$(BITS).o
21+
CFLAGS_entry_fred.o += -fno-stack-protector
22+
CFLAGS_REMOVE_entry_fred.o += -pg $(CC_FLAGS_FTRACE)
23+
obj-$(CONFIG_X86_FRED) += entry_64_fred.o entry_fred.o
24+
2125
obj-$(CONFIG_IA32_EMULATION) += entry_64_compat.o syscall_32.o
2226
obj-$(CONFIG_X86_X32_ABI) += syscall_x32.o
23-

arch/x86/entry/entry_64_fred.S

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* The actual FRED entry points.
4+
*/
5+
6+
#include <asm/fred.h>
7+
8+
#include "calling.h"
9+
10+
.code64
11+
.section .noinstr.text, "ax"
12+
13+
.macro FRED_ENTER
14+
UNWIND_HINT_END_OF_STACK
15+
ENDBR
16+
PUSH_AND_CLEAR_REGS
17+
movq %rsp, %rdi /* %rdi -> pt_regs */
18+
.endm
19+
20+
.macro FRED_EXIT
21+
UNWIND_HINT_REGS
22+
POP_REGS
23+
.endm
24+
25+
/*
26+
* The new RIP value that FRED event delivery establishes is
27+
* IA32_FRED_CONFIG & ~FFFH for events that occur in ring 3.
28+
* Thus the FRED ring 3 entry point must be 4K page aligned.
29+
*/
30+
.align 4096
31+
32+
SYM_CODE_START_NOALIGN(asm_fred_entrypoint_user)
33+
FRED_ENTER
34+
call fred_entry_from_user
35+
FRED_EXIT
36+
ERETU
37+
SYM_CODE_END(asm_fred_entrypoint_user)
38+
39+
/*
40+
* The new RIP value that FRED event delivery establishes is
41+
* (IA32_FRED_CONFIG & ~FFFH) + 256 for events that occur in
42+
* ring 0, i.e., asm_fred_entrypoint_user + 256.
43+
*/
44+
.org asm_fred_entrypoint_user + 256, 0xcc
45+
SYM_CODE_START_NOALIGN(asm_fred_entrypoint_kernel)
46+
FRED_ENTER
47+
call fred_entry_from_kernel
48+
FRED_EXIT
49+
ERETS
50+
SYM_CODE_END(asm_fred_entrypoint_kernel)

arch/x86/entry/entry_fred.c

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* The FRED specific kernel/user entry functions which are invoked from
4+
* assembly code and dispatch to the associated handlers.
5+
*/
6+
#include <linux/kernel.h>
7+
#include <linux/kdebug.h>
8+
#include <linux/nospec.h>
9+
10+
#include <asm/desc.h>
11+
#include <asm/fred.h>
12+
#include <asm/idtentry.h>
13+
#include <asm/syscall.h>
14+
#include <asm/trapnr.h>
15+
#include <asm/traps.h>
16+
17+
/* FRED EVENT_TYPE_OTHER vector numbers */
18+
#define FRED_SYSCALL 1
19+
#define FRED_SYSENTER 2
20+
21+
static noinstr void fred_bad_type(struct pt_regs *regs, unsigned long error_code)
22+
{
23+
irqentry_state_t irq_state = irqentry_nmi_enter(regs);
24+
25+
instrumentation_begin();
26+
27+
/* Panic on events from a high stack level */
28+
if (regs->fred_cs.sl > 0) {
29+
pr_emerg("PANIC: invalid or fatal FRED event; event type %u "
30+
"vector %u error 0x%lx aux 0x%lx at %04x:%016lx\n",
31+
regs->fred_ss.type, regs->fred_ss.vector, regs->orig_ax,
32+
fred_event_data(regs), regs->cs, regs->ip);
33+
die("invalid or fatal FRED event", regs, regs->orig_ax);
34+
panic("invalid or fatal FRED event");
35+
} else {
36+
unsigned long flags = oops_begin();
37+
int sig = SIGKILL;
38+
39+
pr_alert("BUG: invalid or fatal FRED event; event type %u "
40+
"vector %u error 0x%lx aux 0x%lx at %04x:%016lx\n",
41+
regs->fred_ss.type, regs->fred_ss.vector, regs->orig_ax,
42+
fred_event_data(regs), regs->cs, regs->ip);
43+
44+
if (__die("Invalid or fatal FRED event", regs, regs->orig_ax))
45+
sig = 0;
46+
47+
oops_end(flags, regs, sig);
48+
}
49+
50+
instrumentation_end();
51+
irqentry_nmi_exit(regs, irq_state);
52+
}
53+
54+
static noinstr void fred_intx(struct pt_regs *regs)
55+
{
56+
switch (regs->fred_ss.vector) {
57+
/* Opcode 0xcd, 0x3, NOT INT3 (opcode 0xcc) */
58+
case X86_TRAP_BP:
59+
return exc_int3(regs);
60+
61+
/* Opcode 0xcd, 0x4, NOT INTO (opcode 0xce) */
62+
case X86_TRAP_OF:
63+
return exc_overflow(regs);
64+
65+
#ifdef CONFIG_IA32_EMULATION
66+
/* INT80 */
67+
case IA32_SYSCALL_VECTOR:
68+
if (ia32_enabled())
69+
return int80_emulation(regs);
70+
fallthrough;
71+
#endif
72+
73+
default:
74+
return exc_general_protection(regs, 0);
75+
}
76+
}
77+
78+
static __always_inline void fred_other(struct pt_regs *regs)
79+
{
80+
/* The compiler can fold these conditions into a single test */
81+
if (likely(regs->fred_ss.vector == FRED_SYSCALL && regs->fred_ss.lm)) {
82+
regs->orig_ax = regs->ax;
83+
regs->ax = -ENOSYS;
84+
do_syscall_64(regs, regs->orig_ax);
85+
return;
86+
} else if (ia32_enabled() &&
87+
likely(regs->fred_ss.vector == FRED_SYSENTER && !regs->fred_ss.lm)) {
88+
regs->orig_ax = regs->ax;
89+
regs->ax = -ENOSYS;
90+
do_fast_syscall_32(regs);
91+
return;
92+
} else {
93+
exc_invalid_op(regs);
94+
return;
95+
}
96+
}
97+
98+
#define SYSVEC(_vector, _function) [_vector - FIRST_SYSTEM_VECTOR] = fred_sysvec_##_function
99+
100+
static idtentry_t sysvec_table[NR_SYSTEM_VECTORS] __ro_after_init = {
101+
SYSVEC(ERROR_APIC_VECTOR, error_interrupt),
102+
SYSVEC(SPURIOUS_APIC_VECTOR, spurious_apic_interrupt),
103+
SYSVEC(LOCAL_TIMER_VECTOR, apic_timer_interrupt),
104+
SYSVEC(X86_PLATFORM_IPI_VECTOR, x86_platform_ipi),
105+
106+
SYSVEC(RESCHEDULE_VECTOR, reschedule_ipi),
107+
SYSVEC(CALL_FUNCTION_SINGLE_VECTOR, call_function_single),
108+
SYSVEC(CALL_FUNCTION_VECTOR, call_function),
109+
SYSVEC(REBOOT_VECTOR, reboot),
110+
111+
SYSVEC(THRESHOLD_APIC_VECTOR, threshold),
112+
SYSVEC(DEFERRED_ERROR_VECTOR, deferred_error),
113+
SYSVEC(THERMAL_APIC_VECTOR, thermal),
114+
115+
SYSVEC(IRQ_WORK_VECTOR, irq_work),
116+
117+
SYSVEC(POSTED_INTR_VECTOR, kvm_posted_intr_ipi),
118+
SYSVEC(POSTED_INTR_WAKEUP_VECTOR, kvm_posted_intr_wakeup_ipi),
119+
SYSVEC(POSTED_INTR_NESTED_VECTOR, kvm_posted_intr_nested_ipi),
120+
};
121+
122+
static noinstr void fred_extint(struct pt_regs *regs)
123+
{
124+
unsigned int vector = regs->fred_ss.vector;
125+
unsigned int index = array_index_nospec(vector - FIRST_SYSTEM_VECTOR,
126+
NR_SYSTEM_VECTORS);
127+
128+
if (WARN_ON_ONCE(vector < FIRST_EXTERNAL_VECTOR))
129+
return;
130+
131+
if (likely(vector >= FIRST_SYSTEM_VECTOR)) {
132+
irqentry_state_t state = irqentry_enter(regs);
133+
134+
instrumentation_begin();
135+
sysvec_table[index](regs);
136+
instrumentation_end();
137+
irqentry_exit(regs, state);
138+
} else {
139+
common_interrupt(regs, vector);
140+
}
141+
}
142+
143+
static noinstr void fred_hwexc(struct pt_regs *regs, unsigned long error_code)
144+
{
145+
/* Optimize for #PF. That's the only exception which matters performance wise */
146+
if (likely(regs->fred_ss.vector == X86_TRAP_PF))
147+
return exc_page_fault(regs, error_code);
148+
149+
switch (regs->fred_ss.vector) {
150+
case X86_TRAP_DE: return exc_divide_error(regs);
151+
case X86_TRAP_DB: return fred_exc_debug(regs);
152+
case X86_TRAP_BR: return exc_bounds(regs);
153+
case X86_TRAP_UD: return exc_invalid_op(regs);
154+
case X86_TRAP_NM: return exc_device_not_available(regs);
155+
case X86_TRAP_DF: return exc_double_fault(regs, error_code);
156+
case X86_TRAP_TS: return exc_invalid_tss(regs, error_code);
157+
case X86_TRAP_NP: return exc_segment_not_present(regs, error_code);
158+
case X86_TRAP_SS: return exc_stack_segment(regs, error_code);
159+
case X86_TRAP_GP: return exc_general_protection(regs, error_code);
160+
case X86_TRAP_MF: return exc_coprocessor_error(regs);
161+
case X86_TRAP_AC: return exc_alignment_check(regs, error_code);
162+
case X86_TRAP_XF: return exc_simd_coprocessor_error(regs);
163+
164+
#ifdef CONFIG_X86_MCE
165+
case X86_TRAP_MC: return fred_exc_machine_check(regs);
166+
#endif
167+
#ifdef CONFIG_INTEL_TDX_GUEST
168+
case X86_TRAP_VE: return exc_virtualization_exception(regs);
169+
#endif
170+
#ifdef CONFIG_X86_CET
171+
case X86_TRAP_CP: return exc_control_protection(regs, error_code);
172+
#endif
173+
default: return fred_bad_type(regs, error_code);
174+
}
175+
176+
}
177+
178+
static noinstr void fred_swexc(struct pt_regs *regs, unsigned long error_code)
179+
{
180+
switch (regs->fred_ss.vector) {
181+
case X86_TRAP_BP: return exc_int3(regs);
182+
case X86_TRAP_OF: return exc_overflow(regs);
183+
default: return fred_bad_type(regs, error_code);
184+
}
185+
}
186+
187+
__visible noinstr void fred_entry_from_user(struct pt_regs *regs)
188+
{
189+
unsigned long error_code = regs->orig_ax;
190+
191+
/* Invalidate orig_ax so that syscall_get_nr() works correctly */
192+
regs->orig_ax = -1;
193+
194+
switch (regs->fred_ss.type) {
195+
case EVENT_TYPE_EXTINT:
196+
return fred_extint(regs);
197+
case EVENT_TYPE_NMI:
198+
if (likely(regs->fred_ss.vector == X86_TRAP_NMI))
199+
return fred_exc_nmi(regs);
200+
break;
201+
case EVENT_TYPE_HWEXC:
202+
return fred_hwexc(regs, error_code);
203+
case EVENT_TYPE_SWINT:
204+
return fred_intx(regs);
205+
case EVENT_TYPE_PRIV_SWEXC:
206+
if (likely(regs->fred_ss.vector == X86_TRAP_DB))
207+
return fred_exc_debug(regs);
208+
break;
209+
case EVENT_TYPE_SWEXC:
210+
return fred_swexc(regs, error_code);
211+
case EVENT_TYPE_OTHER:
212+
return fred_other(regs);
213+
default: break;
214+
}
215+
216+
return fred_bad_type(regs, error_code);
217+
}
218+
219+
__visible noinstr void fred_entry_from_kernel(struct pt_regs *regs)
220+
{
221+
unsigned long error_code = regs->orig_ax;
222+
223+
/* Invalidate orig_ax so that syscall_get_nr() works correctly */
224+
regs->orig_ax = -1;
225+
226+
switch (regs->fred_ss.type) {
227+
case EVENT_TYPE_EXTINT:
228+
return fred_extint(regs);
229+
case EVENT_TYPE_NMI:
230+
if (likely(regs->fred_ss.vector == X86_TRAP_NMI))
231+
return fred_exc_nmi(regs);
232+
break;
233+
case EVENT_TYPE_HWEXC:
234+
return fred_hwexc(regs, error_code);
235+
case EVENT_TYPE_PRIV_SWEXC:
236+
if (likely(regs->fred_ss.vector == X86_TRAP_DB))
237+
return fred_exc_debug(regs);
238+
break;
239+
case EVENT_TYPE_SWEXC:
240+
return fred_swexc(regs, error_code);
241+
default: break;
242+
}
243+
244+
return fred_bad_type(regs, error_code);
245+
}

arch/x86/include/asm/asm-prototypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <asm/special_insns.h>
1313
#include <asm/preempt.h>
1414
#include <asm/asm.h>
15+
#include <asm/fred.h>
1516
#include <asm/gsseg.h>
1617

1718
#ifndef CONFIG_X86_CMPXCHG64

arch/x86/include/asm/fred.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs)
6060
return fred_info(regs)->edata;
6161
}
6262

63+
void asm_fred_entrypoint_user(void);
64+
void asm_fred_entrypoint_kernel(void);
65+
66+
__visible void fred_entry_from_user(struct pt_regs *regs);
67+
__visible void fred_entry_from_kernel(struct pt_regs *regs);
68+
6369
#else /* CONFIG_X86_FRED */
6470
static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { return 0; }
6571
#endif /* CONFIG_X86_FRED */

arch/x86/include/asm/ia32.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ extern void ia32_pick_mmap_layout(struct mm_struct *mm);
6969

7070
extern bool __ia32_enabled;
7171

72-
static inline bool ia32_enabled(void)
72+
static __always_inline bool ia32_enabled(void)
7373
{
7474
return __ia32_enabled;
7575
}
@@ -81,7 +81,7 @@ static inline void ia32_disable(void)
8181

8282
#else /* !CONFIG_IA32_EMULATION */
8383

84-
static inline bool ia32_enabled(void)
84+
static __always_inline bool ia32_enabled(void)
8585
{
8686
return IS_ENABLED(CONFIG_X86_32);
8787
}

0 commit comments

Comments
 (0)