Skip to content

Commit eeedf15

Browse files
yyu-intel-comsuryasaimadhu
authored andcommitted
x86/fpu: Introduce copy_supervisor_to_kernel()
The XSAVES instruction takes a mask and saves only the features specified in that mask. The kernel normally specifies that all features be saved. XSAVES also unconditionally uses the "compacted format" which means that all specified features are saved next to each other in memory. If a feature is removed from the mask, all the features after it will "move up" into earlier locations in the buffer. Introduce copy_supervisor_to_kernel(), which saves only supervisor states and then moves those states into the standard location where they are normally found. Signed-off-by: Yu-cheng Yu <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent c95473e commit eeedf15

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int of
7575
int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset, unsigned int size);
7676
int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf);
7777
int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf);
78+
void copy_supervisor_to_kernel(struct xregs_state *xsave);
7879

7980
/* Validate an xstate header supplied by userspace (ptrace or sigreturn) */
8081
int validate_user_xstate_header(const struct xstate_header *hdr);

arch/x86/kernel/fpu/xstate.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ u64 xfeatures_mask_all __read_mostly;
6262
static unsigned int xstate_offsets[XFEATURE_MAX] = { [ 0 ... XFEATURE_MAX - 1] = -1};
6363
static unsigned int xstate_sizes[XFEATURE_MAX] = { [ 0 ... XFEATURE_MAX - 1] = -1};
6464
static unsigned int xstate_comp_offsets[XFEATURE_MAX] = { [ 0 ... XFEATURE_MAX - 1] = -1};
65+
static unsigned int xstate_supervisor_only_offsets[XFEATURE_MAX] = { [ 0 ... XFEATURE_MAX - 1] = -1};
6566

6667
/*
6768
* The XSAVE area of kernel can be in standard or compacted format;
@@ -392,6 +393,33 @@ static void __init setup_xstate_comp_offsets(void)
392393
}
393394
}
394395

396+
/*
397+
* Setup offsets of a supervisor-state-only XSAVES buffer:
398+
*
399+
* The offsets stored in xstate_comp_offsets[] only work for one specific
400+
* value of the Requested Feature BitMap (RFBM). In cases where a different
401+
* RFBM value is used, a different set of offsets is required. This set of
402+
* offsets is for when RFBM=xfeatures_mask_supervisor().
403+
*/
404+
static void __init setup_supervisor_only_offsets(void)
405+
{
406+
unsigned int next_offset;
407+
int i;
408+
409+
next_offset = FXSAVE_SIZE + XSAVE_HDR_SIZE;
410+
411+
for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
412+
if (!xfeature_enabled(i) || !xfeature_is_supervisor(i))
413+
continue;
414+
415+
if (xfeature_is_aligned(i))
416+
next_offset = ALIGN(next_offset, 64);
417+
418+
xstate_supervisor_only_offsets[i] = next_offset;
419+
next_offset += xstate_sizes[i];
420+
}
421+
}
422+
395423
/*
396424
* Print out xstate component offsets and sizes
397425
*/
@@ -790,6 +818,7 @@ void __init fpu__init_system_xstate(void)
790818
fpu__init_prepare_fx_sw_frame();
791819
setup_init_fpu_buf();
792820
setup_xstate_comp_offsets();
821+
setup_supervisor_only_offsets();
793822
print_xstate_offset_size();
794823

795824
pr_info("x86/fpu: Enabled xstate features 0x%llx, context size is %d bytes, using '%s' format.\n",
@@ -1262,6 +1291,61 @@ int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf)
12621291
return 0;
12631292
}
12641293

1294+
/*
1295+
* Save only supervisor states to the kernel buffer. This blows away all
1296+
* old states, and is intended to be used only in __fpu__restore_sig(), where
1297+
* user states are restored from the user buffer.
1298+
*/
1299+
void copy_supervisor_to_kernel(struct xregs_state *xstate)
1300+
{
1301+
struct xstate_header *header;
1302+
u64 max_bit, min_bit;
1303+
u32 lmask, hmask;
1304+
int err, i;
1305+
1306+
if (WARN_ON(!boot_cpu_has(X86_FEATURE_XSAVES)))
1307+
return;
1308+
1309+
if (!xfeatures_mask_supervisor())
1310+
return;
1311+
1312+
max_bit = __fls(xfeatures_mask_supervisor());
1313+
min_bit = __ffs(xfeatures_mask_supervisor());
1314+
1315+
lmask = xfeatures_mask_supervisor();
1316+
hmask = xfeatures_mask_supervisor() >> 32;
1317+
XSTATE_OP(XSAVES, xstate, lmask, hmask, err);
1318+
1319+
/* We should never fault when copying to a kernel buffer: */
1320+
if (WARN_ON_FPU(err))
1321+
return;
1322+
1323+
/*
1324+
* At this point, the buffer has only supervisor states and must be
1325+
* converted back to normal kernel format.
1326+
*/
1327+
header = &xstate->header;
1328+
header->xcomp_bv |= xfeatures_mask_all;
1329+
1330+
/*
1331+
* This only moves states up in the buffer. Start with
1332+
* the last state and move backwards so that states are
1333+
* not overwritten until after they are moved. Note:
1334+
* memmove() allows overlapping src/dst buffers.
1335+
*/
1336+
for (i = max_bit; i >= min_bit; i--) {
1337+
u8 *xbuf = (u8 *)xstate;
1338+
1339+
if (!((header->xfeatures >> i) & 1))
1340+
continue;
1341+
1342+
/* Move xfeature 'i' into its normal location */
1343+
memmove(xbuf + xstate_comp_offsets[i],
1344+
xbuf + xstate_supervisor_only_offsets[i],
1345+
xstate_sizes[i]);
1346+
}
1347+
}
1348+
12651349
#ifdef CONFIG_PROC_PID_ARCH_STATUS
12661350
/*
12671351
* Report the amount of time elapsed in millisecond since last AVX512

0 commit comments

Comments
 (0)