Skip to content

Commit 8ad7e8f

Browse files
committed
x86/fpu/xsave: Support XSAVEC in the kernel
XSAVEC is the user space counterpart of XSAVES which cannot save supervisor state. In virtualization scenarios the hypervisor does not expose XSAVES but XSAVEC to the guest, though the kernel does not make use of it. That's unfortunate because XSAVEC uses the compacted format of saving the XSTATE. This is more efficient in terms of storage space vs. XSAVE[OPT] as it does not create holes for XSTATE components which are not supported or enabled by the kernel but are available in hardware. There is room for further optimizations when XSAVEC/S and XGETBV1 are supported. In order to support XSAVEC: - Define the XSAVEC ASM macro as it's not yet supported by the required minimal toolchain. - Create a software defined X86_FEATURE_XCOMPACTED to select the compacted XSTATE buffer format for both XSAVEC and XSAVES. - Make XSAVEC an option in the 'XSAVE' ASM alternatives Requested-by: Andrew Cooper <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent af2d861 commit 8ad7e8f

File tree

3 files changed

+48
-26
lines changed

3 files changed

+48
-26
lines changed

arch/x86/include/asm/cpufeatures.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@
201201
#define X86_FEATURE_INVPCID_SINGLE ( 7*32+ 7) /* Effectively INVPCID && CR4.PCIDE=1 */
202202
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
203203
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
204-
/* FREE! ( 7*32+10) */
204+
#define X86_FEATURE_XCOMPACTED ( 7*32+10) /* "" Use compacted XSTATE (XSAVES or XSAVEC) */
205205
#define X86_FEATURE_PTI ( 7*32+11) /* Kernel Page Table Isolation enabled */
206206
#define X86_FEATURE_RETPOLINE ( 7*32+12) /* "" Generic Retpoline mitigation for Spectre variant 2 */
207207
#define X86_FEATURE_RETPOLINE_LFENCE ( 7*32+13) /* "" Use LFENCE for Spectre variant 2 */

arch/x86/kernel/fpu/xstate.c

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ static unsigned int xfeature_get_offset(u64 xcomp_bv, int xfeature)
142142
* Non-compacted format and legacy features use the cached fixed
143143
* offsets.
144144
*/
145-
if (!cpu_feature_enabled(X86_FEATURE_XSAVES) || xfeature <= XFEATURE_SSE)
145+
if (!cpu_feature_enabled(X86_FEATURE_XCOMPACTED) ||
146+
xfeature <= XFEATURE_SSE)
146147
return xstate_offsets[xfeature];
147148

148149
/*
@@ -369,12 +370,12 @@ static void __init setup_init_fpu_buf(void)
369370
/*
370371
* All components are now in init state. Read the state back so
371372
* that init_fpstate contains all non-zero init state. This only
372-
* works with XSAVE, but not with XSAVEOPT and XSAVES because
373+
* works with XSAVE, but not with XSAVEOPT and XSAVEC/S because
373374
* those use the init optimization which skips writing data for
374375
* components in init state.
375376
*
376377
* XSAVE could be used, but that would require to reshuffle the
377-
* data when XSAVES is available because XSAVES uses xstate
378+
* data when XSAVEC/S is available because XSAVEC/S uses xstate
378379
* compaction. But doing so is a pointless exercise because most
379380
* components have an all zeros init state except for the legacy
380381
* ones (FP and SSE). Those can be saved with FXSAVE into the
@@ -584,7 +585,8 @@ static unsigned int xstate_calculate_size(u64 xfeatures, bool compacted)
584585
*/
585586
static bool __init paranoid_xstate_size_valid(unsigned int kernel_size)
586587
{
587-
bool compacted = cpu_feature_enabled(X86_FEATURE_XSAVES);
588+
bool compacted = cpu_feature_enabled(X86_FEATURE_XCOMPACTED);
589+
bool xsaves = cpu_feature_enabled(X86_FEATURE_XSAVES);
588590
unsigned int size = FXSAVE_SIZE + XSAVE_HDR_SIZE;
589591
int i;
590592

@@ -595,7 +597,7 @@ static bool __init paranoid_xstate_size_valid(unsigned int kernel_size)
595597
* Supervisor state components can be managed only by
596598
* XSAVES.
597599
*/
598-
if (!compacted && xfeature_is_supervisor(i)) {
600+
if (!xsaves && xfeature_is_supervisor(i)) {
599601
XSTATE_WARN_ON(1);
600602
return false;
601603
}
@@ -612,8 +614,11 @@ static bool __init paranoid_xstate_size_valid(unsigned int kernel_size)
612614
* the size of the *user* states. If we use it to size a buffer
613615
* that we use 'XSAVES' on, we could potentially overflow the
614616
* buffer because 'XSAVES' saves system states too.
617+
*
618+
* This also takes compaction into account. So this works for
619+
* XSAVEC as well.
615620
*/
616-
static unsigned int __init get_xsaves_size(void)
621+
static unsigned int __init get_compacted_size(void)
617622
{
618623
unsigned int eax, ebx, ecx, edx;
619624
/*
@@ -623,6 +628,10 @@ static unsigned int __init get_xsaves_size(void)
623628
* containing all the state components
624629
* corresponding to bits currently set in
625630
* XCR0 | IA32_XSS.
631+
*
632+
* When XSAVES is not available but XSAVEC is (virt), then there
633+
* are no supervisor states, but XSAVEC still uses compacted
634+
* format.
626635
*/
627636
cpuid_count(XSTATE_CPUID, 1, &eax, &ebx, &ecx, &edx);
628637
return ebx;
@@ -632,13 +641,13 @@ static unsigned int __init get_xsaves_size(void)
632641
* Get the total size of the enabled xstates without the independent supervisor
633642
* features.
634643
*/
635-
static unsigned int __init get_xsaves_size_no_independent(void)
644+
static unsigned int __init get_xsave_compacted_size(void)
636645
{
637646
u64 mask = xfeatures_mask_independent();
638647
unsigned int size;
639648

640649
if (!mask)
641-
return get_xsaves_size();
650+
return get_compacted_size();
642651

643652
/* Disable independent features. */
644653
wrmsrl(MSR_IA32_XSS, xfeatures_mask_supervisor());
@@ -647,7 +656,7 @@ static unsigned int __init get_xsaves_size_no_independent(void)
647656
* Ask the hardware what size is required of the buffer.
648657
* This is the size required for the task->fpu buffer.
649658
*/
650-
size = get_xsaves_size();
659+
size = get_compacted_size();
651660

652661
/* Re-enable independent features so XSAVES will work on them again. */
653662
wrmsrl(MSR_IA32_XSS, xfeatures_mask_supervisor() | mask);
@@ -687,20 +696,21 @@ static int __init init_xstate_size(void)
687696
{
688697
/* Recompute the context size for enabled features: */
689698
unsigned int user_size, kernel_size, kernel_default_size;
690-
bool compacted = cpu_feature_enabled(X86_FEATURE_XSAVES);
699+
bool compacted = cpu_feature_enabled(X86_FEATURE_XCOMPACTED);
691700

692701
/* Uncompacted user space size */
693702
user_size = get_xsave_size_user();
694703

695704
/*
696-
* XSAVES kernel size includes supervisor states and
697-
* uses compacted format when available.
705+
* XSAVES kernel size includes supervisor states and uses compacted
706+
* format. XSAVEC uses compacted format, but does not save
707+
* supervisor states.
698708
*
699-
* XSAVE does not support supervisor states so
700-
* kernel and user size is identical.
709+
* XSAVE[OPT] do not support supervisor states so kernel and user
710+
* size is identical.
701711
*/
702712
if (compacted)
703-
kernel_size = get_xsaves_size_no_independent();
713+
kernel_size = get_xsave_compacted_size();
704714
else
705715
kernel_size = user_size;
706716

@@ -813,8 +823,11 @@ void __init fpu__init_system_xstate(unsigned int legacy_size)
813823
if (!cpu_feature_enabled(X86_FEATURE_XFD))
814824
fpu_kernel_cfg.max_features &= ~XFEATURE_MASK_USER_DYNAMIC;
815825

816-
fpu_kernel_cfg.max_features &= XFEATURE_MASK_USER_SUPPORTED |
817-
XFEATURE_MASK_SUPERVISOR_SUPPORTED;
826+
if (!cpu_feature_enabled(X86_FEATURE_XSAVES))
827+
fpu_kernel_cfg.max_features &= XFEATURE_MASK_USER_SUPPORTED;
828+
else
829+
fpu_kernel_cfg.max_features &= XFEATURE_MASK_USER_SUPPORTED |
830+
XFEATURE_MASK_SUPERVISOR_SUPPORTED;
818831

819832
fpu_user_cfg.max_features = fpu_kernel_cfg.max_features;
820833
fpu_user_cfg.max_features &= XFEATURE_MASK_USER_SUPPORTED;
@@ -837,6 +850,11 @@ void __init fpu__init_system_xstate(unsigned int legacy_size)
837850
*/
838851
init_fpstate.xfd = fpu_user_cfg.max_features & XFEATURE_MASK_USER_DYNAMIC;
839852

853+
/* Set up compaction feature bit */
854+
if (cpu_feature_enabled(X86_FEATURE_XSAVEC) ||
855+
cpu_feature_enabled(X86_FEATURE_XSAVES))
856+
setup_force_cpu_cap(X86_FEATURE_XCOMPACTED);
857+
840858
/* Enable xstate instructions to be able to continue with initialization: */
841859
fpu__init_cpu_xstate();
842860

@@ -873,7 +891,7 @@ void __init fpu__init_system_xstate(unsigned int legacy_size)
873891
pr_info("x86/fpu: Enabled xstate features 0x%llx, context size is %d bytes, using '%s' format.\n",
874892
fpu_kernel_cfg.max_features,
875893
fpu_kernel_cfg.max_size,
876-
boot_cpu_has(X86_FEATURE_XSAVES) ? "compacted" : "standard");
894+
boot_cpu_has(X86_FEATURE_XCOMPACTED) ? "compacted" : "standard");
877895
return;
878896

879897
out_disable:
@@ -917,7 +935,7 @@ static void *__raw_xsave_addr(struct xregs_state *xsave, int xfeature_nr)
917935
if (WARN_ON_ONCE(!xfeature_enabled(xfeature_nr)))
918936
return NULL;
919937

920-
if (cpu_feature_enabled(X86_FEATURE_XSAVES)) {
938+
if (cpu_feature_enabled(X86_FEATURE_XCOMPACTED)) {
921939
if (WARN_ON_ONCE(!(xcomp_bv & BIT_ULL(xfeature_nr))))
922940
return NULL;
923941
}
@@ -1525,7 +1543,7 @@ static int __xstate_request_perm(u64 permitted, u64 requested, bool guest)
15251543
* vendors into extending XFD for the pre AMX states, especially
15261544
* AVX512.
15271545
*/
1528-
bool compacted = cpu_feature_enabled(X86_FEATURE_XSAVES);
1546+
bool compacted = cpu_feature_enabled(X86_FEATURE_XCOMPACTED);
15291547
struct fpu *fpu = &current->group_leader->thread.fpu;
15301548
struct fpu_state_perm *perm;
15311549
unsigned int ksize, usize;

arch/x86/kernel/fpu/xstate.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ static inline void xstate_init_xcomp_bv(struct xregs_state *xsave, u64 mask)
1616
* XRSTORS requires these bits set in xcomp_bv, or it will
1717
* trigger #GP:
1818
*/
19-
if (cpu_feature_enabled(X86_FEATURE_XSAVES))
19+
if (cpu_feature_enabled(X86_FEATURE_XCOMPACTED))
2020
xsave->header.xcomp_bv = mask | XCOMP_BV_COMPACTED_FORMAT;
2121
}
2222

@@ -79,6 +79,7 @@ static inline u64 xfeatures_mask_independent(void)
7979
/* These macros all use (%edi)/(%rdi) as the single memory argument. */
8080
#define XSAVE ".byte " REX_PREFIX "0x0f,0xae,0x27"
8181
#define XSAVEOPT ".byte " REX_PREFIX "0x0f,0xae,0x37"
82+
#define XSAVEC ".byte " REX_PREFIX "0x0f,0xc7,0x27"
8283
#define XSAVES ".byte " REX_PREFIX "0x0f,0xc7,0x2f"
8384
#define XRSTOR ".byte " REX_PREFIX "0x0f,0xae,0x2f"
8485
#define XRSTORS ".byte " REX_PREFIX "0x0f,0xc7,0x1f"
@@ -97,9 +98,11 @@ static inline u64 xfeatures_mask_independent(void)
9798
: "memory")
9899

99100
/*
100-
* If XSAVES is enabled, it replaces XSAVEOPT because it supports a compact
101-
* format and supervisor states in addition to modified optimization in
102-
* XSAVEOPT.
101+
* If XSAVES is enabled, it replaces XSAVEC because it supports supervisor
102+
* states in addition to XSAVEC.
103+
*
104+
* Otherwise if XSAVEC is enabled, it replaces XSAVEOPT because it supports
105+
* compacted storage format in addition to XSAVEOPT.
103106
*
104107
* Otherwise, if XSAVEOPT is enabled, XSAVEOPT replaces XSAVE because XSAVEOPT
105108
* supports modified optimization which is not supported by XSAVE.
@@ -111,8 +114,9 @@ static inline u64 xfeatures_mask_independent(void)
111114
* address of the instruction where we might get an exception at.
112115
*/
113116
#define XSTATE_XSAVE(st, lmask, hmask, err) \
114-
asm volatile(ALTERNATIVE_2(XSAVE, \
117+
asm volatile(ALTERNATIVE_3(XSAVE, \
115118
XSAVEOPT, X86_FEATURE_XSAVEOPT, \
119+
XSAVEC, X86_FEATURE_XSAVEC, \
116120
XSAVES, X86_FEATURE_XSAVES) \
117121
"\n" \
118122
"xor %[err], %[err]\n" \

0 commit comments

Comments
 (0)