Skip to content

Commit 71b639a

Browse files
committed
Merge tag 'x86-fpu-2025-03-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86/fpu updates from Ingo Molnar: - Improve crypto performance by making kernel-mode FPU reliably usable in softirqs ((Eric Biggers) - Fully optimize out WARN_ON_FPU() (Eric Biggers) - Initial steps to support Support Intel APX (Advanced Performance Extensions) (Chang S. Bae) - Fix KASAN for arch_dup_task_struct() (Benjamin Berg) - Refine and simplify the FPU magic number check during signal return (Chang S. Bae) - Fix inconsistencies in guest FPU xfeatures (Chao Gao, Stanislav Spassov) - selftests/x86/xstate: Introduce common code for testing extended states (Chang S. Bae) - Misc fixes and cleanups (Borislav Petkov, Colin Ian King, Uros Bizjak) * tag 'x86-fpu-2025-03-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/fpu/xstate: Fix inconsistencies in guest FPU xfeatures x86/fpu: Clarify the "xa" symbolic name used in the XSTATE* macros x86/fpu: Use XSAVE{,OPT,C,S} and XRSTOR{,S} mnemonics in xstate.h x86/fpu: Improve crypto performance by making kernel-mode FPU reliably usable in softirqs x86/fpu/xstate: Simplify print_xstate_features() x86/fpu: Refine and simplify the magic number check during signal return selftests/x86/xstate: Fix spelling mistake "hader" -> "header" x86/fpu: Avoid copying dynamic FP state from init_task in arch_dup_task_struct() vmlinux.lds.h: Remove entry to place init_task onto init_stack selftests/x86/avx: Add AVX tests selftests/x86/xstate: Clarify supported xstates selftests/x86/xstate: Consolidate test invocations into a single entry selftests/x86/xstate: Introduce signal ABI test selftests/x86/xstate: Refactor ptrace ABI test selftests/x86/xstate: Refactor context switching test selftests/x86/xstate: Enumerate and name xstate components selftests/x86/xstate: Refactor XSAVE helpers for general use selftests/x86: Consolidate redundant signal helper functions x86/fpu: Fix guest FPU state buffer allocation size x86/fpu: Fully optimize out WARN_ON_FPU()
2 parents b58386a + dda3660 commit 71b639a

30 files changed

+812
-783
lines changed

arch/x86/include/asm/fpu/api.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616

1717
/*
1818
* Use kernel_fpu_begin/end() if you intend to use FPU in kernel context. It
19-
* disables preemption so be careful if you intend to use it for long periods
20-
* of time.
21-
* If you intend to use the FPU in irq/softirq you need to check first with
22-
* irq_fpu_usable() if it is possible.
19+
* disables preemption and softirq processing, so be careful if you intend to
20+
* use it for long periods of time. Kernel-mode FPU cannot be used in all
21+
* contexts -- see irq_fpu_usable() for details.
2322
*/
2423

2524
/* Kernel FPU states to initialize in kernel_fpu_begin_mask() */
@@ -50,10 +49,10 @@ static inline void kernel_fpu_begin(void)
5049
}
5150

5251
/*
53-
* Use fpregs_lock() while editing CPU's FPU registers or fpu->fpstate.
54-
* A context switch will (and softirq might) save CPU's FPU registers to
55-
* fpu->fpstate.regs and set TIF_NEED_FPU_LOAD leaving CPU's FPU registers in
56-
* a random state.
52+
* Use fpregs_lock() while editing CPU's FPU registers or fpu->fpstate, or while
53+
* using the FPU in kernel mode. A context switch will (and softirq might) save
54+
* CPU's FPU registers to fpu->fpstate.regs and set TIF_NEED_FPU_LOAD leaving
55+
* CPU's FPU registers in a random state.
5756
*
5857
* local_bh_disable() protects against both preemption and soft interrupts
5958
* on !RT kernels.
@@ -63,8 +62,6 @@ static inline void kernel_fpu_begin(void)
6362
* preemptible. Disabling preemption is the right choice here as bottom
6463
* half processing is always in thread context on RT kernels so it
6564
* implicitly prevents bottom half processing as well.
66-
*
67-
* Disabling preemption also serializes against kernel_fpu_begin().
6865
*/
6966
static inline void fpregs_lock(void)
7067
{

arch/x86/kernel/fpu/core.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,16 @@ bool irq_fpu_usable(void)
6060
if (WARN_ON_ONCE(in_nmi()))
6161
return false;
6262

63-
/* In kernel FPU usage already active? */
64-
if (this_cpu_read(in_kernel_fpu))
63+
/*
64+
* In kernel FPU usage already active? This detects any explicitly
65+
* nested usage in task or softirq context, which is unsupported. It
66+
* also detects attempted usage in a hardirq that has interrupted a
67+
* kernel-mode FPU section.
68+
*/
69+
if (this_cpu_read(in_kernel_fpu)) {
70+
WARN_ON_FPU(!in_hardirq());
6571
return false;
72+
}
6673

6774
/*
6875
* When not in NMI or hard interrupt context, FPU can be used in:
@@ -220,7 +227,7 @@ bool fpu_alloc_guest_fpstate(struct fpu_guest *gfpu)
220227
struct fpstate *fpstate;
221228
unsigned int size;
222229

223-
size = fpu_user_cfg.default_size + ALIGN(offsetof(struct fpstate, regs), 64);
230+
size = fpu_kernel_cfg.default_size + ALIGN(offsetof(struct fpstate, regs), 64);
224231
fpstate = vzalloc(size);
225232
if (!fpstate)
226233
return false;
@@ -232,8 +239,8 @@ bool fpu_alloc_guest_fpstate(struct fpu_guest *gfpu)
232239
fpstate->is_guest = true;
233240

234241
gfpu->fpstate = fpstate;
235-
gfpu->xfeatures = fpu_user_cfg.default_features;
236-
gfpu->perm = fpu_user_cfg.default_features;
242+
gfpu->xfeatures = fpu_kernel_cfg.default_features;
243+
gfpu->perm = fpu_kernel_cfg.default_features;
237244

238245
/*
239246
* KVM sets the FP+SSE bits in the XSAVE header when copying FPU state
@@ -420,7 +427,8 @@ EXPORT_SYMBOL_GPL(fpu_copy_uabi_to_guest_fpstate);
420427

421428
void kernel_fpu_begin_mask(unsigned int kfpu_mask)
422429
{
423-
preempt_disable();
430+
if (!irqs_disabled())
431+
fpregs_lock();
424432

425433
WARN_ON_FPU(!irq_fpu_usable());
426434
WARN_ON_FPU(this_cpu_read(in_kernel_fpu));
@@ -448,7 +456,8 @@ void kernel_fpu_end(void)
448456
WARN_ON_FPU(!this_cpu_read(in_kernel_fpu));
449457

450458
this_cpu_write(in_kernel_fpu, false);
451-
preempt_enable();
459+
if (!irqs_disabled())
460+
fpregs_unlock();
452461
}
453462
EXPORT_SYMBOL_GPL(kernel_fpu_end);
454463

arch/x86/kernel/fpu/internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ static __always_inline __pure bool use_fxsr(void)
1818
#ifdef CONFIG_X86_DEBUG_FPU
1919
# define WARN_ON_FPU(x) WARN_ON_ONCE(x)
2020
#else
21-
# define WARN_ON_FPU(x) ({ (void)(x); 0; })
21+
# define WARN_ON_FPU(x) ({ BUILD_BUG_ON_INVALID(x); 0; })
2222
#endif
2323

2424
/* Used in init.c */

arch/x86/kernel/fpu/signal.c

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,14 @@
2727
static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf,
2828
struct _fpx_sw_bytes *fx_sw)
2929
{
30-
int min_xstate_size = sizeof(struct fxregs_state) +
31-
sizeof(struct xstate_header);
3230
void __user *fpstate = fxbuf;
3331
unsigned int magic2;
3432

3533
if (__copy_from_user(fx_sw, &fxbuf->sw_reserved[0], sizeof(*fx_sw)))
3634
return false;
3735

38-
/* Check for the first magic field and other error scenarios. */
39-
if (fx_sw->magic1 != FP_XSTATE_MAGIC1 ||
40-
fx_sw->xstate_size < min_xstate_size ||
41-
fx_sw->xstate_size > current->thread.fpu.fpstate->user_size ||
42-
fx_sw->xstate_size > fx_sw->extended_size)
36+
/* Check for the first magic field */
37+
if (fx_sw->magic1 != FP_XSTATE_MAGIC1)
4338
goto setfx;
4439

4540
/*
@@ -48,7 +43,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf,
4843
* fpstate layout with out copying the extended state information
4944
* in the memory layout.
5045
*/
51-
if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size)))
46+
if (__get_user(magic2, (__u32 __user *)(fpstate + current->thread.fpu.fpstate->user_size)))
5247
return false;
5348

5449
if (likely(magic2 == FP_XSTATE_MAGIC2))

arch/x86/kernel/fpu/xstate.c

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -259,32 +259,20 @@ static void __init setup_xstate_cache(void)
259259
}
260260
}
261261

262-
static void __init print_xstate_feature(u64 xstate_mask)
263-
{
264-
const char *feature_name;
265-
266-
if (cpu_has_xfeatures(xstate_mask, &feature_name))
267-
pr_info("x86/fpu: Supporting XSAVE feature 0x%03Lx: '%s'\n", xstate_mask, feature_name);
268-
}
269-
270262
/*
271263
* Print out all the supported xstate features:
272264
*/
273265
static void __init print_xstate_features(void)
274266
{
275-
print_xstate_feature(XFEATURE_MASK_FP);
276-
print_xstate_feature(XFEATURE_MASK_SSE);
277-
print_xstate_feature(XFEATURE_MASK_YMM);
278-
print_xstate_feature(XFEATURE_MASK_BNDREGS);
279-
print_xstate_feature(XFEATURE_MASK_BNDCSR);
280-
print_xstate_feature(XFEATURE_MASK_OPMASK);
281-
print_xstate_feature(XFEATURE_MASK_ZMM_Hi256);
282-
print_xstate_feature(XFEATURE_MASK_Hi16_ZMM);
283-
print_xstate_feature(XFEATURE_MASK_PKRU);
284-
print_xstate_feature(XFEATURE_MASK_PASID);
285-
print_xstate_feature(XFEATURE_MASK_CET_USER);
286-
print_xstate_feature(XFEATURE_MASK_XTILE_CFG);
287-
print_xstate_feature(XFEATURE_MASK_XTILE_DATA);
267+
int i;
268+
269+
for (i = 0; i < XFEATURE_MAX; i++) {
270+
u64 mask = BIT_ULL(i);
271+
const char *name;
272+
273+
if (cpu_has_xfeatures(mask, &name))
274+
pr_info("x86/fpu: Supporting XSAVE feature 0x%03Lx: '%s'\n", mask, name);
275+
}
288276
}
289277

290278
/*

arch/x86/kernel/fpu/xstate.h

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -94,30 +94,33 @@ static inline int update_pkru_in_sigframe(struct xregs_state __user *buf, u64 ma
9494
/* XSAVE/XRSTOR wrapper functions */
9595

9696
#ifdef CONFIG_X86_64
97-
#define REX_PREFIX "0x48, "
97+
#define REX_SUFFIX "64"
9898
#else
99-
#define REX_PREFIX
99+
#define REX_SUFFIX
100100
#endif
101101

102-
/* These macros all use (%edi)/(%rdi) as the single memory argument. */
103-
#define XSAVE ".byte " REX_PREFIX "0x0f,0xae,0x27"
104-
#define XSAVEOPT ".byte " REX_PREFIX "0x0f,0xae,0x37"
105-
#define XSAVEC ".byte " REX_PREFIX "0x0f,0xc7,0x27"
106-
#define XSAVES ".byte " REX_PREFIX "0x0f,0xc7,0x2f"
107-
#define XRSTOR ".byte " REX_PREFIX "0x0f,0xae,0x2f"
108-
#define XRSTORS ".byte " REX_PREFIX "0x0f,0xc7,0x1f"
102+
#define XSAVE "xsave" REX_SUFFIX " %[xa]"
103+
#define XSAVEOPT "xsaveopt" REX_SUFFIX " %[xa]"
104+
#define XSAVEC "xsavec" REX_SUFFIX " %[xa]"
105+
#define XSAVES "xsaves" REX_SUFFIX " %[xa]"
106+
#define XRSTOR "xrstor" REX_SUFFIX " %[xa]"
107+
#define XRSTORS "xrstors" REX_SUFFIX " %[xa]"
109108

110109
/*
111110
* After this @err contains 0 on success or the trap number when the
112111
* operation raises an exception.
112+
*
113+
* The [xa] input parameter below represents the struct xregs_state pointer
114+
* and the asm symbolic name for the argument used in the XSAVE/XRSTOR insns
115+
* above.
113116
*/
114117
#define XSTATE_OP(op, st, lmask, hmask, err) \
115118
asm volatile("1:" op "\n\t" \
116119
"xor %[err], %[err]\n" \
117-
"2:\n\t" \
120+
"2:\n" \
118121
_ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_FAULT_MCE_SAFE) \
119122
: [err] "=a" (err) \
120-
: "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
123+
: [xa] "m" (*(st)), "a" (lmask), "d" (hmask) \
121124
: "memory")
122125

123126
/*
@@ -137,12 +140,12 @@ static inline int update_pkru_in_sigframe(struct xregs_state __user *buf, u64 ma
137140
XSAVEOPT, X86_FEATURE_XSAVEOPT, \
138141
XSAVEC, X86_FEATURE_XSAVEC, \
139142
XSAVES, X86_FEATURE_XSAVES) \
140-
"\n" \
143+
"\n\t" \
141144
"xor %[err], %[err]\n" \
142145
"3:\n" \
143146
_ASM_EXTABLE_TYPE_REG(1b, 3b, EX_TYPE_EFAULT_REG, %[err]) \
144147
: [err] "=r" (err) \
145-
: "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
148+
: [xa] "m" (*(st)), "a" (lmask), "d" (hmask) \
146149
: "memory")
147150

148151
/*
@@ -156,7 +159,7 @@ static inline int update_pkru_in_sigframe(struct xregs_state __user *buf, u64 ma
156159
"3:\n" \
157160
_ASM_EXTABLE_TYPE(1b, 3b, EX_TYPE_FPU_RESTORE) \
158161
: \
159-
: "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
162+
: [xa] "m" (*(st)), "a" (lmask), "d" (hmask) \
160163
: "memory")
161164

162165
#if defined(CONFIG_X86_64) && defined(CONFIG_X86_DEBUG_FPU)

arch/x86/kernel/process.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,12 @@ EXPORT_PER_CPU_SYMBOL_GPL(__tss_limit_invalid);
9393
*/
9494
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
9595
{
96-
memcpy(dst, src, arch_task_struct_size);
96+
/* init_task is not dynamically sized (incomplete FPU state) */
97+
if (unlikely(src == &init_task))
98+
memcpy_and_pad(dst, arch_task_struct_size, src, sizeof(init_task), 0);
99+
else
100+
memcpy(dst, src, arch_task_struct_size);
101+
97102
#ifdef CONFIG_VM86
98103
dst->thread.vm86 = NULL;
99104
#endif

include/asm-generic/vmlinux.lds.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,6 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
409409
__start_init_stack = .; \
410410
init_thread_union = .; \
411411
init_stack = .; \
412-
KEEP(*(.data..init_task)) \
413412
KEEP(*(.data..init_thread_info)) \
414413
. = __start_init_stack + THREAD_SIZE; \
415414
__end_init_stack = .;

tools/testing/selftests/x86/Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
1919
test_FCMOV test_FCOMI test_FISTTP \
2020
vdso_restorer
2121
TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering \
22-
corrupt_xstate_header amx lam test_shadow_stack
22+
corrupt_xstate_header amx lam test_shadow_stack avx
2323
# Some selftests require 32bit support enabled also on 64bit systems
2424
TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall
2525

@@ -132,3 +132,7 @@ $(OUTPUT)/check_initial_reg_state_64: CFLAGS += -Wl,-ereal_start -static
132132

133133
$(OUTPUT)/nx_stack_32: CFLAGS += -Wl,-z,noexecstack
134134
$(OUTPUT)/nx_stack_64: CFLAGS += -Wl,-z,noexecstack
135+
136+
$(OUTPUT)/avx_64: CFLAGS += -mno-avx -mno-avx512f
137+
$(OUTPUT)/amx_64: EXTRA_FILES += xstate.c
138+
$(OUTPUT)/avx_64: EXTRA_FILES += xstate.c

0 commit comments

Comments
 (0)