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 = { }; |
