History

x86/fpu: Make init_fpstate correct with optimized XSAVE

The XSAVE init code initializes all enabled and supported components with XRSTOR(S) to init state. Then it XSAVEs the state of the components back into init_fpstate which is used in several places to fill in the init state of components. This works correctly with XSAVE, but not with XSAVEOPT and XSAVES because those use the init optimization and skip writing state of components which are in init state. So init_fpstate.xsave still contains all zeroes after this operation. There are two ways to solve that: 1) Use XSAVE unconditionally, but that requires to reshuffle the buffer when XSAVES is enabled because XSAVES uses compacted format. 2) Save the components which are known to have a non-zero init state by other means. Looking deeper, #2 is the right thing to do because all components the kernel supports have all-zeroes init state except the legacy features (FP, SSE). Those cannot be hard coded because the states are not identical on all CPUs, but they can be saved with FXSAVE which avoids all conditionals. Use FXSAVE to save the legacy FP/SSE components in init_fpstate along with a BUILD_BUG_ON() which reminds developers to validate that a newly added component has all zeroes init state. As a bonus remove the now unused copy_xregs_to_kernel_booting() crutch. The XSAVE and reshuffle method can still be implemented in the unlikely case that components are added which have a non-zero init state and no other means to save them. For now, FXSAVE is just simple and good enough. [ bp: Fix a typo or two in the text. ]

Thomas Gleixner committed 5 years ago

Commit f9dfb5e1 parent 93019822 files · +46 −25Browse files

Unified · Split

2 files
  1. arch/x86/include/asm/fpu/internal.h +8 −22
  2. arch/x86/kernel/fpu/xstate.c +38 −3
arch/x86/include/asm/fpu/internal.h+8−22
@@ -204,6 +204,14 @@ static inline void copy_fxregs_to_kernel(struct fpu *fpu) · 203 unmodified lines
204 asm volatile("fxsaveq %[fx]" : [fx] "=m" (fpu->state.fxsave));204 asm volatile("fxsaveq %[fx]" : [fx] "=m" (fpu->state.fxsave));
205}205}
206 206
207static inline void fxsave(struct fxregs_state *fx)
208{
209 if (IS_ENABLED(CONFIG_X86_32))
210 asm volatile( "fxsave %[fx]" : [fx] "=m" (*fx));
211 else
212 asm volatile("fxsaveq %[fx]" : [fx] "=m" (*fx));
213}
214
207/* These macros all use (%edi)/(%rdi) as the single memory argument. */215/* These macros all use (%edi)/(%rdi) as the single memory argument. */
208#define XSAVE ".byte " REX_PREFIX "0x0f,0xae,0x27"216#define XSAVE ".byte " REX_PREFIX "0x0f,0xae,0x27"
209#define XSAVEOPT ".byte " REX_PREFIX "0x0f,0xae,0x37"217#define XSAVEOPT ".byte " REX_PREFIX "0x0f,0xae,0x37"
@@ -268,28 +276,6 @@ static inline void copy_fxregs_to_kernel(struct fpu *fpu) · 58 unmodified lines
268 : "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \276 : "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
269 : "memory")277 : "memory")
270 278
271/*
272 * This function is called only during boot time when x86 caps are not set
273 * up and alternative can not be used yet.
274 */
275static inline void copy_xregs_to_kernel_booting(struct xregs_state *xstate)
276{
277 u64 mask = xfeatures_mask_all;
278 u32 lmask = mask;
279 u32 hmask = mask >> 32;
280 int err;
281
282 WARN_ON(system_state != SYSTEM_BOOTING);
283
284 if (boot_cpu_has(X86_FEATURE_XSAVES))
285 XSTATE_OP(XSAVES, xstate, lmask, hmask, err);
286 else
287 XSTATE_OP(XSAVE, xstate, lmask, hmask, err);
288
289 /* We should never fault when copying to a kernel buffer: */
290 WARN_ON_FPU(err);
291}
292
293/*279/*
294 * This function is called only during boot time when x86 caps are not set280 * This function is called only during boot time when x86 caps are not set
295 * up and alternative can not be used yet.281 * up and alternative can not be used yet.
arch/x86/kernel/fpu/xstate.c+38−3
@@ -440,6 +440,25 @@ static void __init print_xstate_offset_size(void) · 439 unmodified lines
440 }440 }
441}441}
442 442
443/*
444 * All supported features have either init state all zeros or are
445 * handled in setup_init_fpu() individually. This is an explicit
446 * feature list and does not use XFEATURE_MASK*SUPPORTED to catch
447 * newly added supported features at build time and make people
448 * actually look at the init state for the new feature.
449 */
450#define XFEATURES_INIT_FPSTATE_HANDLED \
451 (XFEATURE_MASK_FP | \
452 XFEATURE_MASK_SSE | \
453 XFEATURE_MASK_YMM | \
454 XFEATURE_MASK_OPMASK | \
455 XFEATURE_MASK_ZMM_Hi256 | \
456 XFEATURE_MASK_Hi16_ZMM | \
457 XFEATURE_MASK_PKRU | \
458 XFEATURE_MASK_BNDREGS | \
459 XFEATURE_MASK_BNDCSR | \
460 XFEATURE_MASK_PASID)
461
443/*462/*
444 * setup the xstate image representing the init state463 * setup the xstate image representing the init state
445 */464 */
@@ -447,6 +466,10 @@ static void __init setup_init_fpu_buf(void) · 1 unmodified line
447{466{
448 static int on_boot_cpu __initdata = 1;467 static int on_boot_cpu __initdata = 1;
449 468
469 BUILD_BUG_ON((XFEATURE_MASK_USER_SUPPORTED |
470 XFEATURE_MASK_SUPERVISOR_SUPPORTED) !=
471 XFEATURES_INIT_FPSTATE_HANDLED);
472
450 WARN_ON_FPU(!on_boot_cpu);473 WARN_ON_FPU(!on_boot_cpu);
451 on_boot_cpu = 0;474 on_boot_cpu = 0;
452 475
@@ -466,10 +489,22 @@ static void __init setup_init_fpu_buf(void) · 13 unmodified lines
466 copy_kernel_to_xregs_booting(&init_fpstate.xsave);489 copy_kernel_to_xregs_booting(&init_fpstate.xsave);
467 490
468 /*491 /*
469 * Dump the init state again. This is to identify the init state492 * All components are now in init state. Read the state back so
470 * of any feature which is not represented by all zero's.493 * that init_fpstate contains all non-zero init state. This only
494 * works with XSAVE, but not with XSAVEOPT and XSAVES because
495 * those use the init optimization which skips writing data for
496 * components in init state.
497 *
498 * XSAVE could be used, but that would require to reshuffle the
499 * data when XSAVES is available because XSAVES uses xstate
500 * compaction. But doing so is a pointless exercise because most
501 * components have an all zeros init state except for the legacy
502 * ones (FP and SSE). Those can be saved with FXSAVE into the
503 * legacy area. Adding new features requires to ensure that init
504 * state is all zeroes or if not to add the necessary handling
505 * here.
471 */506 */
472 copy_xregs_to_kernel_booting(&init_fpstate.xsave);507 fxsave(&init_fpstate.fxsave);
473}508}
474 509
475static int xfeature_uncompacted_offset(int xfeature_nr)510static int xfeature_uncompacted_offset(int xfeature_nr)