Skip to content

Commit 7031b64

Browse files
committed
Merge branch 'x86-fpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 fpu fixes and cleanups from Ingo Molnar: "This is _way_ more cleanups than fixes, but the bugs were subtle and hard to hit, and the primary reason for them existing was the unnecessary historical complexity of some of the x86/fpu interfaces. The first bunch of commits clean up and simplify the xstate user copy handling functions, in reaction to the collective head-scratching about the xstate user-copy handling code that leads up to the fix for this SkyLake xstate handling bug: 0852b37: x86/fpu: Add FPU state copying quirk to handle XRSTOR failure on Intel Skylake CPUs The cleanups don't change any functionality, they just (hopefully) make it all clearer, more consistent, more debuggable and more robust. Note that most of the linecount increase comes from these commits, where we better split the user/kernel copy logic by having more variants, instead repeated fragile patterns of: if (kbuf) { memcpy(kbuf + pos, data, copy); } else { if (__copy_to_user(ubuf + pos, data, copy)) return -EFAULT; } The next bunch of commits simplify the FPU state-machine to get rid of old lazy-FPU idiosyncrasies - a defensive simplification to make all the code easier to review and fix. No change in functionality. Then there's a couple of additional debugging tweaks: static checker warning fix and move an FPU related warning to under WARN_ON_FPU(), followed by another bunch of commits that represent a finegrained split-up of the fixes from Eric Biggers to handle weird xstate bits properly. I did this finegrained split-up because some of these fixes also impact the ABI for weird xstate handling, for which we'd like to have good bisection results, should they cause any problems. (We also had one regression with the more monolithic fixes, so splitting it all up sounded prudent for robustness reasons as well.) About the whole series: the commits up to 03eaec8 have been in -next for months - but I've recently rebased them to remove a state machine clean-up commit that was objected to, and to make it more bisectable - so technically it's a new, rebased tree. Robustness history: this series had some regressions along the way, and all reported regressions have been fixed. All but one of the regressions manifested itself as easy to report warnings. The previous version of this latest series was also in linux-next, with one (warning-only) regression reported which is fixed in the latest version. Barring last minute brown paper bag bugs (and the commits are now older by a day which I'd hope helps paperbag reduction), I'm reasonably confident about its general robustness. Famous last words ..." * 'x86-fpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (42 commits) x86/fpu: Use using_compacted_format() instead of open coded X86_FEATURE_XSAVES x86/fpu: Use validate_xstate_header() to validate the xstate_header in copy_user_to_xstate() x86/fpu: Eliminate the 'xfeatures' local variable in copy_user_to_xstate() x86/fpu: Copy the full header in copy_user_to_xstate() x86/fpu: Use validate_xstate_header() to validate the xstate_header in copy_kernel_to_xstate() x86/fpu: Eliminate the 'xfeatures' local variable in copy_kernel_to_xstate() x86/fpu: Copy the full state_header in copy_kernel_to_xstate() x86/fpu: Use validate_xstate_header() to validate the xstate_header in __fpu__restore_sig() x86/fpu: Use validate_xstate_header() to validate the xstate_header in xstateregs_set() x86/fpu: Introduce validate_xstate_header() x86/fpu: Rename fpu__activate_fpstate_read/write() to fpu__prepare_[read|write]() x86/fpu: Rename fpu__activate_curr() to fpu__initialize() x86/fpu: Simplify and speed up fpu__copy() x86/fpu: Fix stale comments about lazy FPU logic x86/fpu: Rename fpu::fpstate_active to fpu::initialized x86/fpu: Remove fpu__current_fpstate_write_begin/end() x86/fpu: Fix fpu__activate_fpstate_read() and update comments x86/fpu: Reinitialize FPU registers if restoring FPU state fails x86/fpu: Don't let userspace set bogus xcomp_bv x86/fpu: Turn WARN_ON() in context switch into WARN_ON_FPU() ...
2 parents dc972a6 + 8474c53 commit 7031b64

File tree

15 files changed

+375
-315
lines changed

15 files changed

+375
-315
lines changed

arch/x86/ia32/ia32_signal.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
231231
ksig->ka.sa.sa_restorer)
232232
sp = (unsigned long) ksig->ka.sa.sa_restorer;
233233

234-
if (fpu->fpstate_active) {
234+
if (fpu->initialized) {
235235
unsigned long fx_aligned, math_size;
236236

237237
sp = fpu__alloc_mathframe(sp, 1, &fx_aligned, &math_size);

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

Lines changed: 22 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,9 @@
2323
/*
2424
* High level FPU state handling functions:
2525
*/
26-
extern void fpu__activate_curr(struct fpu *fpu);
27-
extern void fpu__activate_fpstate_read(struct fpu *fpu);
28-
extern void fpu__activate_fpstate_write(struct fpu *fpu);
29-
extern void fpu__current_fpstate_write_begin(void);
30-
extern void fpu__current_fpstate_write_end(void);
26+
extern void fpu__initialize(struct fpu *fpu);
27+
extern void fpu__prepare_read(struct fpu *fpu);
28+
extern void fpu__prepare_write(struct fpu *fpu);
3129
extern void fpu__save(struct fpu *fpu);
3230
extern void fpu__restore(struct fpu *fpu);
3331
extern int fpu__restore_sig(void __user *buf, int ia32_frame);
@@ -120,20 +118,11 @@ extern void fpstate_sanitize_xstate(struct fpu *fpu);
120118
err; \
121119
})
122120

123-
#define check_insn(insn, output, input...) \
124-
({ \
125-
int err; \
121+
#define kernel_insn(insn, output, input...) \
126122
asm volatile("1:" #insn "\n\t" \
127123
"2:\n" \
128-
".section .fixup,\"ax\"\n" \
129-
"3: movl $-1,%[err]\n" \
130-
" jmp 2b\n" \
131-
".previous\n" \
132-
_ASM_EXTABLE(1b, 3b) \
133-
: [err] "=r" (err), output \
134-
: "0"(0), input); \
135-
err; \
136-
})
124+
_ASM_EXTABLE_HANDLE(1b, 2b, ex_handler_fprestore) \
125+
: output : input)
137126

138127
static inline int copy_fregs_to_user(struct fregs_state __user *fx)
139128
{
@@ -153,20 +142,16 @@ static inline int copy_fxregs_to_user(struct fxregs_state __user *fx)
153142

154143
static inline void copy_kernel_to_fxregs(struct fxregs_state *fx)
155144
{
156-
int err;
157-
158145
if (IS_ENABLED(CONFIG_X86_32)) {
159-
err = check_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
146+
kernel_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
160147
} else {
161148
if (IS_ENABLED(CONFIG_AS_FXSAVEQ)) {
162-
err = check_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
149+
kernel_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
163150
} else {
164151
/* See comment in copy_fxregs_to_kernel() below. */
165-
err = check_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx), "m" (*fx));
152+
kernel_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx), "m" (*fx));
166153
}
167154
}
168-
/* Copying from a kernel buffer to FPU registers should never fail: */
169-
WARN_ON_FPU(err);
170155
}
171156

172157
static inline int copy_user_to_fxregs(struct fxregs_state __user *fx)
@@ -183,9 +168,7 @@ static inline int copy_user_to_fxregs(struct fxregs_state __user *fx)
183168

184169
static inline void copy_kernel_to_fregs(struct fregs_state *fx)
185170
{
186-
int err = check_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
187-
188-
WARN_ON_FPU(err);
171+
kernel_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
189172
}
190173

191174
static inline int copy_user_to_fregs(struct fregs_state __user *fx)
@@ -281,18 +264,13 @@ static inline void copy_fxregs_to_kernel(struct fpu *fpu)
281264
* Use XRSTORS to restore context if it is enabled. XRSTORS supports compact
282265
* XSAVE area format.
283266
*/
284-
#define XSTATE_XRESTORE(st, lmask, hmask, err) \
267+
#define XSTATE_XRESTORE(st, lmask, hmask) \
285268
asm volatile(ALTERNATIVE(XRSTOR, \
286269
XRSTORS, X86_FEATURE_XSAVES) \
287270
"\n" \
288-
"xor %[err], %[err]\n" \
289271
"3:\n" \
290-
".pushsection .fixup,\"ax\"\n" \
291-
"4: movl $-2, %[err]\n" \
292-
"jmp 3b\n" \
293-
".popsection\n" \
294-
_ASM_EXTABLE(661b, 4b) \
295-
: [err] "=r" (err) \
272+
_ASM_EXTABLE_HANDLE(661b, 3b, ex_handler_fprestore)\
273+
: \
296274
: "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
297275
: "memory")
298276

@@ -336,7 +314,10 @@ static inline void copy_kernel_to_xregs_booting(struct xregs_state *xstate)
336314
else
337315
XSTATE_OP(XRSTOR, xstate, lmask, hmask, err);
338316

339-
/* We should never fault when copying from a kernel buffer: */
317+
/*
318+
* We should never fault when copying from a kernel buffer, and the FPU
319+
* state we set at boot time should be valid.
320+
*/
340321
WARN_ON_FPU(err);
341322
}
342323

@@ -350,7 +331,7 @@ static inline void copy_xregs_to_kernel(struct xregs_state *xstate)
350331
u32 hmask = mask >> 32;
351332
int err;
352333

353-
WARN_ON(!alternatives_patched);
334+
WARN_ON_FPU(!alternatives_patched);
354335

355336
XSTATE_XSAVE(xstate, lmask, hmask, err);
356337

@@ -365,12 +346,8 @@ static inline void copy_kernel_to_xregs(struct xregs_state *xstate, u64 mask)
365346
{
366347
u32 lmask = mask;
367348
u32 hmask = mask >> 32;
368-
int err;
369-
370-
XSTATE_XRESTORE(xstate, lmask, hmask, err);
371349

372-
/* We should never fault when copying from a kernel buffer: */
373-
WARN_ON_FPU(err);
350+
XSTATE_XRESTORE(xstate, lmask, hmask);
374351
}
375352

376353
/*
@@ -526,37 +503,16 @@ static inline int fpregs_state_valid(struct fpu *fpu, unsigned int cpu)
526503
*/
527504
static inline void fpregs_deactivate(struct fpu *fpu)
528505
{
529-
WARN_ON_FPU(!fpu->fpregs_active);
530-
531-
fpu->fpregs_active = 0;
532506
this_cpu_write(fpu_fpregs_owner_ctx, NULL);
533507
trace_x86_fpu_regs_deactivated(fpu);
534508
}
535509

536510
static inline void fpregs_activate(struct fpu *fpu)
537511
{
538-
WARN_ON_FPU(fpu->fpregs_active);
539-
540-
fpu->fpregs_active = 1;
541512
this_cpu_write(fpu_fpregs_owner_ctx, fpu);
542513
trace_x86_fpu_regs_activated(fpu);
543514
}
544515

545-
/*
546-
* The question "does this thread have fpu access?"
547-
* is slightly racy, since preemption could come in
548-
* and revoke it immediately after the test.
549-
*
550-
* However, even in that very unlikely scenario,
551-
* we can just assume we have FPU access - typically
552-
* to save the FP state - we'll just take a #NM
553-
* fault and get the FPU access back.
554-
*/
555-
static inline int fpregs_active(void)
556-
{
557-
return current->thread.fpu.fpregs_active;
558-
}
559-
560516
/*
561517
* FPU state switching for scheduling.
562518
*
@@ -571,14 +527,13 @@ static inline int fpregs_active(void)
571527
static inline void
572528
switch_fpu_prepare(struct fpu *old_fpu, int cpu)
573529
{
574-
if (old_fpu->fpregs_active) {
530+
if (old_fpu->initialized) {
575531
if (!copy_fpregs_to_fpstate(old_fpu))
576532
old_fpu->last_cpu = -1;
577533
else
578534
old_fpu->last_cpu = cpu;
579535

580536
/* But leave fpu_fpregs_owner_ctx! */
581-
old_fpu->fpregs_active = 0;
582537
trace_x86_fpu_regs_deactivated(old_fpu);
583538
} else
584539
old_fpu->last_cpu = -1;
@@ -595,7 +550,7 @@ switch_fpu_prepare(struct fpu *old_fpu, int cpu)
595550
static inline void switch_fpu_finish(struct fpu *new_fpu, int cpu)
596551
{
597552
bool preload = static_cpu_has(X86_FEATURE_FPU) &&
598-
new_fpu->fpstate_active;
553+
new_fpu->initialized;
599554

600555
if (preload) {
601556
if (!fpregs_state_valid(new_fpu, cpu))
@@ -617,8 +572,7 @@ static inline void user_fpu_begin(void)
617572
struct fpu *fpu = &current->thread.fpu;
618573

619574
preempt_disable();
620-
if (!fpregs_active())
621-
fpregs_activate(fpu);
575+
fpregs_activate(fpu);
622576
preempt_enable();
623577
}
624578

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

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ struct fxregs_state {
6868
/* Default value for fxregs_state.mxcsr: */
6969
#define MXCSR_DEFAULT 0x1f80
7070

71+
/* Copy both mxcsr & mxcsr_flags with a single u64 memcpy: */
72+
#define MXCSR_AND_FLAGS_SIZE sizeof(u64)
73+
7174
/*
7275
* Software based FPU emulation state. This is arbitrary really,
7376
* it matches the x87 format to make it easier to understand:
@@ -290,36 +293,13 @@ struct fpu {
290293
unsigned int last_cpu;
291294

292295
/*
293-
* @fpstate_active:
296+
* @initialized:
294297
*
295-
* This flag indicates whether this context is active: if the task
298+
* This flag indicates whether this context is initialized: if the task
296299
* is not running then we can restore from this context, if the task
297300
* is running then we should save into this context.
298301
*/
299-
unsigned char fpstate_active;
300-
301-
/*
302-
* @fpregs_active:
303-
*
304-
* This flag determines whether a given context is actively
305-
* loaded into the FPU's registers and that those registers
306-
* represent the task's current FPU state.
307-
*
308-
* Note the interaction with fpstate_active:
309-
*
310-
* # task does not use the FPU:
311-
* fpstate_active == 0
312-
*
313-
* # task uses the FPU and regs are active:
314-
* fpstate_active == 1 && fpregs_active == 1
315-
*
316-
* # the regs are inactive but still match fpstate:
317-
* fpstate_active == 1 && fpregs_active == 0 && fpregs_owner == fpu
318-
*
319-
* The third state is what we use for the lazy restore optimization
320-
* on lazy-switching CPUs.
321-
*/
322-
unsigned char fpregs_active;
302+
unsigned char initialized;
323303

324304
/*
325305
* @state:

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,12 @@ void fpu__xstate_clear_all_cpu_caps(void);
4848
void *get_xsave_addr(struct xregs_state *xsave, int xstate);
4949
const void *get_xsave_field_ptr(int xstate_field);
5050
int using_compacted_format(void);
51-
int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf,
52-
void __user *ubuf, struct xregs_state *xsave);
53-
int copyin_to_xsaves(const void *kbuf, const void __user *ubuf,
54-
struct xregs_state *xsave);
51+
int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int offset, unsigned int size);
52+
int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset, unsigned int size);
53+
int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf);
54+
int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf);
55+
56+
/* Validate an xstate header supplied by userspace (ptrace or sigreturn) */
57+
extern int validate_xstate_header(const struct xstate_header *hdr);
58+
5559
#endif

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,22 @@ DECLARE_EVENT_CLASS(x86_fpu,
1212

1313
TP_STRUCT__entry(
1414
__field(struct fpu *, fpu)
15-
__field(bool, fpregs_active)
16-
__field(bool, fpstate_active)
15+
__field(bool, initialized)
1716
__field(u64, xfeatures)
1817
__field(u64, xcomp_bv)
1918
),
2019

2120
TP_fast_assign(
2221
__entry->fpu = fpu;
23-
__entry->fpregs_active = fpu->fpregs_active;
24-
__entry->fpstate_active = fpu->fpstate_active;
22+
__entry->initialized = fpu->initialized;
2523
if (boot_cpu_has(X86_FEATURE_OSXSAVE)) {
2624
__entry->xfeatures = fpu->state.xsave.header.xfeatures;
2725
__entry->xcomp_bv = fpu->state.xsave.header.xcomp_bv;
2826
}
2927
),
30-
TP_printk("x86/fpu: %p fpregs_active: %d fpstate_active: %d xfeatures: %llx xcomp_bv: %llx",
28+
TP_printk("x86/fpu: %p initialized: %d xfeatures: %llx xcomp_bv: %llx",
3129
__entry->fpu,
32-
__entry->fpregs_active,
33-
__entry->fpstate_active,
30+
__entry->initialized,
3431
__entry->xfeatures,
3532
__entry->xcomp_bv
3633
)

0 commit comments

Comments
 (0)