blob: 9ac2502cdd3d30d0ee0f8fe8861f8aba307d7862 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Copyright (C) 1994 Linus Torvalds
3 *
4 * Pentium III FXSR, SSE support
5 * General FPU state handling cleanups
6 * Gareth Hughes <gareth@valinux.com>, May 2000
7 */
8
9#ifndef __ASM_I386_I387_H
10#define __ASM_I386_I387_H
11
12#include <linux/sched.h>
13#include <linux/init.h>
Andi Kleen18bd0572006-04-20 02:36:45 +020014#include <linux/kernel_stat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <asm/processor.h>
16#include <asm/sigcontext.h>
17#include <asm/user.h>
18
19extern void mxcsr_feature_mask_init(void);
20extern void init_fpu(struct task_struct *);
Linus Torvalds8ed13832005-07-22 16:06:16 -040021
Linus Torvalds1da177e2005-04-16 15:20:36 -070022/*
23 * FPU lazy state save handling...
24 */
Linus Torvalds8ed13832005-07-22 16:06:16 -040025
26/*
27 * The "nop" is needed to make the instructions the same
28 * length.
29 */
30#define restore_fpu(tsk) \
31 alternative_input( \
32 "nop ; frstor %1", \
33 "fxrstor %1", \
34 X86_FEATURE_FXSR, \
Linus Torvalds2847e342005-07-22 18:19:20 -040035 "m" ((tsk)->thread.i387.fxsave))
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37extern void kernel_fpu_begin(void);
38#define kernel_fpu_end() do { stts(); preempt_enable(); } while(0)
39
Andi Kleen18bd0572006-04-20 02:36:45 +020040/* We need a safe address that is cheap to find and that is already
41 in L1 during context switch. The best choices are unfortunately
42 different for UP and SMP */
43#ifdef CONFIG_SMP
44#define safe_address (__per_cpu_offset[0])
45#else
46#define safe_address (kstat_cpu(0).cpustat.user)
47#endif
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049/*
50 * These must be called with preempt disabled
51 */
52static inline void __save_init_fpu( struct task_struct *tsk )
53{
Andi Kleen18bd0572006-04-20 02:36:45 +020054 /* Use more nops than strictly needed in case the compiler
55 varies code */
Linus Torvalds2847e342005-07-22 18:19:20 -040056 alternative_input(
Andi Kleen18bd0572006-04-20 02:36:45 +020057 "fnsave %[fx] ;fwait;" GENERIC_NOP8 GENERIC_NOP4,
58 "fxsave %[fx]\n"
Chuck Ebbert543f2a32006-04-29 14:07:49 -040059 "bt $7,%[fsw] ; jnc 1f ; fnclex\n1:",
Linus Torvalds2847e342005-07-22 18:19:20 -040060 X86_FEATURE_FXSR,
Andi Kleen18bd0572006-04-20 02:36:45 +020061 [fx] "m" (tsk->thread.i387.fxsave),
62 [fsw] "m" (tsk->thread.i387.fxsave.swd) : "memory");
63 /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception
64 is pending. Clear the x87 state here by setting it to fixed
Chuck Ebbert543f2a32006-04-29 14:07:49 -040065 values. safe_address is a random variable that should be in L1 */
Andi Kleen18bd0572006-04-20 02:36:45 +020066 alternative_input(
67 GENERIC_NOP8 GENERIC_NOP2,
68 "emms\n\t" /* clear stack tags */
69 "fildl %[addr]", /* set F?P to defined value */
70 X86_FEATURE_FXSAVE_LEAK,
71 [addr] "m" (safe_address));
Al Viro06b425d2006-01-12 01:05:40 -080072 task_thread_info(tsk)->status &= ~TS_USEDFPU;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073}
74
Jan Kiszkac41bf8f2007-05-02 19:27:21 +020075#define __unlazy_fpu( tsk ) do { \
76 if (task_thread_info(tsk)->status & TS_USEDFPU) { \
77 __save_init_fpu(tsk); \
78 stts(); \
79 } else \
80 tsk->fpu_counter = 0; \
Linus Torvalds1da177e2005-04-16 15:20:36 -070081} while (0)
82
83#define __clear_fpu( tsk ) \
84do { \
Jan Kiszka02b64da2007-05-02 19:27:21 +020085 if (task_thread_info(tsk)->status & TS_USEDFPU) { \
86 asm volatile("fnclex ; fwait"); \
Al Viro06b425d2006-01-12 01:05:40 -080087 task_thread_info(tsk)->status &= ~TS_USEDFPU; \
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 stts(); \
89 } \
90} while (0)
91
92
93/*
94 * These disable preemption on their own and are safe
95 */
96static inline void save_init_fpu( struct task_struct *tsk )
97{
98 preempt_disable();
99 __save_init_fpu(tsk);
100 stts();
101 preempt_enable();
102}
103
104#define unlazy_fpu( tsk ) do { \
105 preempt_disable(); \
106 __unlazy_fpu(tsk); \
107 preempt_enable(); \
108} while (0)
109
110#define clear_fpu( tsk ) do { \
111 preempt_disable(); \
112 __clear_fpu( tsk ); \
113 preempt_enable(); \
114} while (0)
Jan Kiszka02b64da2007-05-02 19:27:21 +0200115
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116/*
117 * FPU state interaction...
118 */
119extern unsigned short get_fpu_cwd( struct task_struct *tsk );
120extern unsigned short get_fpu_swd( struct task_struct *tsk );
121extern unsigned short get_fpu_mxcsr( struct task_struct *tsk );
Chuck Ebbertacc20762006-12-07 02:14:01 +0100122extern asmlinkage void math_state_restore(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
124/*
125 * Signal frame handlers...
126 */
127extern int save_i387( struct _fpstate __user *buf );
128extern int restore_i387( struct _fpstate __user *buf );
129
130/*
131 * ptrace request handers...
132 */
133extern int get_fpregs( struct user_i387_struct __user *buf,
134 struct task_struct *tsk );
135extern int set_fpregs( struct task_struct *tsk,
136 struct user_i387_struct __user *buf );
137
138extern int get_fpxregs( struct user_fxsr_struct __user *buf,
139 struct task_struct *tsk );
140extern int set_fpxregs( struct task_struct *tsk,
141 struct user_fxsr_struct __user *buf );
142
143/*
144 * FPU state for core dumps...
145 */
146extern int dump_fpu( struct pt_regs *regs,
147 struct user_i387_struct *fpu );
148
149#endif /* __ASM_I386_I387_H */