fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / i386 / kernel / i387.c
index 0d35516..8503c46 100644 (file)
@@ -8,8 +8,8 @@
  *     Gareth Hughes <gareth@valinux.com>, May 2000
  */
 
-#include <linux/config.h>
 #include <linux/sched.h>
+#include <linux/module.h>
 #include <asm/processor.h>
 #include <asm/i387.h>
 #include <asm/math_emu.h>
@@ -24,7 +24,7 @@
 #define HAVE_HWFP 1
 #endif
 
-unsigned long mxcsr_feature_mask = 0xffffffff;
+static unsigned long mxcsr_feature_mask __read_mostly = 0xffffffff;
 
 void mxcsr_feature_mask_init(void)
 {
@@ -60,7 +60,8 @@ void init_fpu(struct task_struct *tsk)
                tsk->thread.i387.fsave.twd = 0xffffffffu;
                tsk->thread.i387.fsave.fos = 0xffff0000u;
        }
-       tsk->used_math = 1;
+       /* only the device not available exception or ptrace can call init_fpu */
+       set_stopped_child_used_math(tsk);
 }
 
 /*
@@ -78,17 +79,7 @@ void kernel_fpu_begin(void)
        }
        clts();
 }
-
-void restore_fpu( struct task_struct *tsk )
-{
-       if ( cpu_has_fxsr ) {
-               asm volatile( "fxrstor %0"
-                             : : "m" (tsk->thread.i387.fxsave) );
-       } else {
-               asm volatile( "frstor %0"
-                             : : "m" (tsk->thread.i387.fsave) );
-       }
-}
+EXPORT_SYMBOL_GPL(kernel_fpu_begin);
 
 /*
  * FPU tag word conversions.
@@ -111,16 +102,17 @@ static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
 static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
 {
        struct _fpxreg *st = NULL;
+       unsigned long tos = (fxsave->swd >> 11) & 7;
        unsigned long twd = (unsigned long) fxsave->twd;
        unsigned long tag;
        unsigned long ret = 0xffff0000u;
        int i;
 
-#define FPREG_ADDR(f, n)       ((char *)&(f)->st_space + (n) * 16);
+#define FPREG_ADDR(f, n)       ((void *)&(f)->st_space + (n) * 16);
 
        for ( i = 0 ; i < 8 ; i++ ) {
                if ( twd & 0x1 ) {
-                       st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
+                       st = FPREG_ADDR( fxsave, (i - tos) & 7 );
 
                        switch ( st->exponent & 0x7fff ) {
                        case 0x7fff:
@@ -175,6 +167,7 @@ unsigned short get_fpu_swd( struct task_struct *tsk )
        }
 }
 
+#if 0
 unsigned short get_fpu_twd( struct task_struct *tsk )
 {
        if ( cpu_has_fxsr ) {
@@ -183,6 +176,7 @@ unsigned short get_fpu_twd( struct task_struct *tsk )
                return (unsigned short)tsk->thread.i387.fsave.twd;
        }
 }
+#endif  /*  0  */
 
 unsigned short get_fpu_mxcsr( struct task_struct *tsk )
 {
@@ -193,6 +187,8 @@ unsigned short get_fpu_mxcsr( struct task_struct *tsk )
        }
 }
 
+#if 0
+
 void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd )
 {
        if ( cpu_has_fxsr ) {
@@ -220,18 +216,16 @@ void set_fpu_twd( struct task_struct *tsk, unsigned short twd )
        }
 }
 
+#endif  /*  0  */
+
 /*
  * FXSR floating point environment conversions.
  */
 
-static int convert_fxsr_to_user( struct _fpstate __user *buf,
-                                       struct i387_fxsave_struct *fxsave )
+static inline void
+convert_fxsr_env_to_i387(unsigned long env[7],
+                        struct i387_fxsave_struct *fxsave)
 {
-       unsigned long env[7];
-       struct _fpreg __user *to;
-       struct _fpxreg *from;
-       int i;
-
        env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul;
        env[1] = (unsigned long)fxsave->swd | 0xffff0000ul;
        env[2] = twd_fxsr_to_i387(fxsave);
@@ -239,14 +233,24 @@ static int convert_fxsr_to_user( struct _fpstate __user *buf,
        env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
        env[5] = fxsave->foo;
        env[6] = fxsave->fos;
+}
 
+static int convert_fxsr_to_user(struct _fpstate __user *buf,
+                               struct i387_fxsave_struct *fxsave)
+{
+       unsigned long env[7];
+       struct _fpreg __user *to;
+       struct _fpxreg *from;
+       int i;
+
+       convert_fxsr_env_to_i387(env, fxsave);
        if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
                return 1;
 
        to = &buf->_st[0];
        from = (struct _fpxreg *) &fxsave->st_space[0];
        for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
-               unsigned long *t = (unsigned long *)to;
+               unsigned long __user *t = (unsigned long __user *)to;
                unsigned long *f = (unsigned long *)from;
 
                if (__put_user(*f, t) ||
@@ -257,6 +261,20 @@ static int convert_fxsr_to_user( struct _fpstate __user *buf,
        return 0;
 }
 
+static inline void
+convert_fxsr_env_from_i387(struct i387_fxsave_struct *fxsave,
+                          const unsigned long env[7])
+{
+       fxsave->cwd = (unsigned short)(env[0] & 0xffff);
+       fxsave->swd = (unsigned short)(env[1] & 0xffff);
+       fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
+       fxsave->fip = env[3];
+       fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16);
+       fxsave->fcs = (env[4] & 0xffff);
+       fxsave->foo = env[5];
+       fxsave->fos = env[6];
+}
+
 static int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
                                          struct _fpstate __user *buf )
 {
@@ -268,20 +286,13 @@ static int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
        if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
                return 1;
 
-       fxsave->cwd = (unsigned short)(env[0] & 0xffff);
-       fxsave->swd = (unsigned short)(env[1] & 0xffff);
-       fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
-       fxsave->fip = env[3];
-       fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16);
-       fxsave->fcs = (env[4] & 0xffff);
-       fxsave->foo = env[5];
-       fxsave->fos = env[6];
+       convert_fxsr_env_from_i387(fxsave, env);
 
        to = (struct _fpxreg *) &fxsave->st_space[0];
        from = &buf->_st[0];
        for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
                unsigned long *t = (unsigned long *)to;
-               unsigned long *f = (unsigned long *)from;
+               unsigned long __user *f = (unsigned long __user *)from;
 
                if (__get_user(*t, f) ||
                                __get_user(*(t + 1), f + 1) ||
@@ -330,13 +341,13 @@ static int save_i387_fxsave( struct _fpstate __user *buf )
 
 int save_i387( struct _fpstate __user *buf )
 {
-       if ( !current->used_math )
+       if ( !used_math() )
                return 0;
 
        /* This will cause a "finit" to be triggered by the next
         * attempted FPU operation by the 'current' process.
         */
-       current->used_math = 0;
+       clear_used_math();
 
        if ( HAVE_HWFP ) {
                if ( cpu_has_fxsr ) {
@@ -382,7 +393,7 @@ int restore_i387( struct _fpstate __user *buf )
        } else {
                err = restore_i387_soft( &current->thread.i387.soft, buf );
        }
-       current->used_math = 1;
+       set_used_math();
        return err;
 }
 
@@ -390,88 +401,82 @@ int restore_i387( struct _fpstate __user *buf )
  * ptrace request handlers.
  */
 
-static inline int get_fpregs_fsave( struct user_i387_struct __user *buf,
-                                   struct task_struct *tsk )
+static inline void get_fpregs_fsave(struct user_i387_struct *buf,
+                                   struct task_struct *tsk)
 {
-       return __copy_to_user( buf, &tsk->thread.i387.fsave,
-                              sizeof(struct user_i387_struct) );
+       memcpy(buf, &tsk->thread.i387.fsave, sizeof(struct user_i387_struct));
 }
 
-static inline int get_fpregs_fxsave( struct user_i387_struct __user *buf,
-                                    struct task_struct *tsk )
+static inline void get_fpregs_fxsave(struct user_i387_struct *buf,
+                                    struct task_struct *tsk)
 {
-       return convert_fxsr_to_user( (struct _fpstate __user *)buf,
-                                    &tsk->thread.i387.fxsave );
+       struct _fpreg *to;
+       const struct _fpxreg *from;
+       unsigned int i;
+
+       convert_fxsr_env_to_i387((unsigned long *) buf,
+                                &tsk->thread.i387.fxsave);
+
+       to = (struct _fpreg *) buf->st_space;
+       from = (const struct _fpxreg *) &tsk->thread.i387.fxsave.st_space[0];
+       for (i = 0; i < 8; i++, to++, from++)
+               *to = *(const struct _fpreg *) from;
 }
 
-int get_fpregs( struct user_i387_struct __user *buf, struct task_struct *tsk )
+int get_fpregs(struct user_i387_struct *buf, struct task_struct *tsk)
 {
        if ( HAVE_HWFP ) {
-               if ( cpu_has_fxsr ) {
-                       return get_fpregs_fxsave( buf, tsk );
-               } else {
-                       return get_fpregs_fsave( buf, tsk );
-               }
+               if (cpu_has_fxsr)
+                       get_fpregs_fxsave(buf, tsk);
+               else
+                       get_fpregs_fsave(buf, tsk);
+               return 0;
        } else {
                return save_i387_soft( &tsk->thread.i387.soft,
                                       (struct _fpstate __user *)buf );
        }
 }
 
-static inline int set_fpregs_fsave( struct task_struct *tsk,
-                                   struct user_i387_struct __user *buf )
+static inline void set_fpregs_fsave(struct task_struct *tsk,
+                                   const struct user_i387_struct *buf)
 {
-       return __copy_from_user( &tsk->thread.i387.fsave, buf,
-                                sizeof(struct user_i387_struct) );
+       memcpy(&tsk->thread.i387.fsave, buf, sizeof(struct user_i387_struct));
 }
 
-static inline int set_fpregs_fxsave( struct task_struct *tsk,
-                                    struct user_i387_struct __user *buf )
+static inline void set_fpregs_fxsave(struct task_struct *tsk,
+                                    const struct user_i387_struct *buf)
 {
-       return convert_fxsr_from_user( &tsk->thread.i387.fxsave,
-                                      (struct _fpstate __user *)buf );
+       struct _fpxreg *to;
+       const struct _fpreg *from;
+       unsigned int i;
+
+       convert_fxsr_env_from_i387(&tsk->thread.i387.fxsave,
+                                  (unsigned long *) buf);
+
+       to = (struct _fpxreg *) &tsk->thread.i387.fxsave.st_space[0];
+       from = (const struct _fpreg *) buf->st_space;
+       for (i = 0; i < 8; i++, to++, from++)
+               *(struct _fpreg *) to = *from;
 }
 
-int set_fpregs( struct task_struct *tsk, struct user_i387_struct __user *buf )
+int set_fpregs(struct task_struct *tsk, const struct user_i387_struct *buf)
 {
        if ( HAVE_HWFP ) {
-               if ( cpu_has_fxsr ) {
-                       return set_fpregs_fxsave( tsk, buf );
-               } else {
-                       return set_fpregs_fsave( tsk, buf );
-               }
+               if (cpu_has_fxsr)
+                       set_fpregs_fxsave(tsk, buf);
+               else
+                       set_fpregs_fsave(tsk, buf);
+               return 0;
        } else {
                return restore_i387_soft( &tsk->thread.i387.soft,
                                          (struct _fpstate __user *)buf );
        }
 }
 
-int get_fpxregs( struct user_fxsr_struct __user *buf, struct task_struct *tsk )
-{
-       if ( cpu_has_fxsr ) {
-               if (__copy_to_user( buf, &tsk->thread.i387.fxsave,
-                                   sizeof(struct user_fxsr_struct) ))
-                       return -EFAULT;
-               return 0;
-       } else {
-               return -EIO;
-       }
-}
-
-int set_fpxregs( struct task_struct *tsk, struct user_fxsr_struct __user *buf )
+void updated_fpxregs(struct task_struct *tsk)
 {
-       int ret = 0;
-
-       if ( cpu_has_fxsr ) {
-               if (__copy_from_user( &tsk->thread.i387.fxsave, buf,
-                                 sizeof(struct user_fxsr_struct) ))
-                       ret = -EFAULT;
-               /* mxcsr reserved bits must be masked to zero for security reasons */
-               tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
-       } else {
-               ret = -EIO;
-       }
-       return ret;
+       /* mxcsr reserved bits must be masked to zero for security reasons */
+       tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask;
 }
 
 /*
@@ -506,7 +511,7 @@ int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
        int fpvalid;
        struct task_struct *tsk = current;
 
-       fpvalid = tsk->used_math;
+       fpvalid = !!used_math();
        if ( fpvalid ) {
                unlazy_fpu( tsk );
                if ( cpu_has_fxsr ) {
@@ -518,25 +523,11 @@ int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
 
        return fpvalid;
 }
-
-int dump_extended_fpu( struct pt_regs *regs, struct user_fxsr_struct *fpu )
-{
-       int fpvalid;
-       struct task_struct *tsk = current;
-
-       fpvalid = tsk->used_math && cpu_has_fxsr;
-       if ( fpvalid ) {
-               unlazy_fpu( tsk );
-               memcpy( fpu, &tsk->thread.i387.fxsave,
-                       sizeof(struct user_fxsr_struct) );
-       }
-
-       return fpvalid;
-}
+EXPORT_SYMBOL(dump_fpu);
 
 int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
 {
-       int fpvalid = tsk->used_math;
+       int fpvalid = !!tsk_used_math(tsk);
 
        if (fpvalid) {
                if (tsk == current)
@@ -551,7 +542,7 @@ int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
 
 int dump_task_extended_fpu(struct task_struct *tsk, struct user_fxsr_struct *fpu)
 {
-       int fpvalid = tsk->used_math && cpu_has_fxsr;
+       int fpvalid = tsk_used_math(tsk) && cpu_has_fxsr;
 
        if (fpvalid) {
                if (tsk == current)