diff options
-rw-r--r-- | arch/x86/include/asm/i387.h | 54 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 1 |
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 | */ | ||
319 | static 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 | */ | ||
333 | static 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 | */ | ||
346 | static inline bool irq_fpu_usable(void) | ||
347 | { | ||
348 | return !in_interrupt() || | ||
349 | interrupted_user_mode() || | ||
350 | interrupted_kernel_fpu_idle(); | ||
351 | } | ||
352 | |||
310 | static inline void kernel_fpu_begin(void) | 353 | static 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 | ||
326 | static 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 | */ |
368 | static inline void save_init_fpu(struct task_struct *tsk) | 405 | static 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); | |||
631 | dotraplinkage void __kprobes | 631 | dotraplinkage void __kprobes |
632 | do_device_not_available(struct pt_regs *regs, long error_code) | 632 | do_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 = { }; |