aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/i387.h54
-rw-r--r--arch/x86/kernel/traps.c1
2 files changed, 47 insertions, 8 deletions
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h
index a5c7ae504176..a29571821b99 100644
--- a/arch/x86/include/asm/i387.h
+++ b/arch/x86/include/asm/i387.h
@@ -307,9 +307,54 @@ static inline void __clear_fpu(struct task_struct *tsk)
307 } 307 }
308} 308}
309 309
310/*
311 * Were we in an interrupt that interrupted kernel mode?
312 *
313 * We can do a kernel_fpu_begin/end() pair *ONLY* if that
314 * pair does nothing at all: TS_USEDFPU must be clear (so
315 * that we don't try to save the FPU state), and TS must
316 * be set (so that the clts/stts pair does nothing that is
317 * visible in the interrupted kernel thread).
318 */
319static inline bool interrupted_kernel_fpu_idle(void)
320{
321 return !(current_thread_info()->status & TS_USEDFPU) &&
322 (read_cr0() & X86_CR0_TS);
323}
324
325/*
326 * Were we in user mode (or vm86 mode) when we were
327 * interrupted?
328 *
329 * Doing kernel_fpu_begin/end() is ok if we are running
330 * in an interrupt context from user mode - we'll just
331 * save the FPU state as required.
332 */
333static inline bool interrupted_user_mode(void)
334{
335 struct pt_regs *regs = get_irq_regs();
336 return regs && user_mode_vm(regs);
337}
338
339/*
340 * Can we use the FPU in kernel mode with the
341 * whole "kernel_fpu_begin/end()" sequence?
342 *
343 * It's always ok in process context (ie "not interrupt")
344 * but it is sometimes ok even from an irq.
345 */
346static inline bool irq_fpu_usable(void)
347{
348 return !in_interrupt() ||
349 interrupted_user_mode() ||
350 interrupted_kernel_fpu_idle();
351}
352
310static inline void kernel_fpu_begin(void) 353static inline void kernel_fpu_begin(void)
311{ 354{
312 struct thread_info *me = current_thread_info(); 355 struct thread_info *me = current_thread_info();
356
357 WARN_ON_ONCE(!irq_fpu_usable());
313 preempt_disable(); 358 preempt_disable();
314 if (me->status & TS_USEDFPU) 359 if (me->status & TS_USEDFPU)
315 __save_init_fpu(me->task); 360 __save_init_fpu(me->task);
@@ -323,14 +368,6 @@ static inline void kernel_fpu_end(void)
323 preempt_enable(); 368 preempt_enable();
324} 369}
325 370
326static inline bool irq_fpu_usable(void)
327{
328 struct pt_regs *regs;
329
330 return !in_interrupt() || !(regs = get_irq_regs()) || \
331 user_mode(regs) || (read_cr0() & X86_CR0_TS);
332}
333
334/* 371/*
335 * Some instructions like VIA's padlock instructions generate a spurious 372 * Some instructions like VIA's padlock instructions generate a spurious
336 * DNA fault but don't modify SSE registers. And these instructions 373 * DNA fault but don't modify SSE registers. And these instructions
@@ -367,6 +404,7 @@ static inline void irq_ts_restore(int TS_state)
367 */ 404 */
368static inline void save_init_fpu(struct task_struct *tsk) 405static inline void save_init_fpu(struct task_struct *tsk)
369{ 406{
407 WARN_ON_ONCE(task_thread_info(tsk)->status & TS_USEDFPU);
370 preempt_disable(); 408 preempt_disable();
371 __save_init_fpu(tsk); 409 __save_init_fpu(tsk);
372 stts(); 410 stts();
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 982433b5da30..8ba27dbc107a 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -631,6 +631,7 @@ EXPORT_SYMBOL_GPL(math_state_restore);
631dotraplinkage void __kprobes 631dotraplinkage void __kprobes
632do_device_not_available(struct pt_regs *regs, long error_code) 632do_device_not_available(struct pt_regs *regs, long error_code)
633{ 633{
634 WARN_ON_ONCE(!user_mode_vm(regs));
634#ifdef CONFIG_MATH_EMULATION 635#ifdef CONFIG_MATH_EMULATION
635 if (read_cr0() & X86_CR0_EM) { 636 if (read_cr0() & X86_CR0_EM) {
636 struct math_emu_info info = { }; 637 struct math_emu_info info = { };