linux-old/arch/i386/kernel/i387.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/i386/kernel/i387.c
   3 *
   4 *  Copyright (C) 1994 Linus Torvalds
   5 *
   6 *  Pentium III FXSR, SSE support
   7 *  General FPU state handling cleanups
   8 *      Gareth Hughes <gareth@valinux.com>, May 2000
   9 */
  10
  11#include <linux/config.h>
  12#include <linux/sched.h>
  13#include <asm/processor.h>
  14#include <asm/i387.h>
  15#include <asm/math_emu.h>
  16#include <asm/sigcontext.h>
  17#include <asm/user.h>
  18#include <asm/ptrace.h>
  19#include <asm/uaccess.h>
  20
  21#ifdef CONFIG_MATH_EMULATION
  22#define HAVE_HWFP (boot_cpu_data.hard_math)
  23#else
  24#define HAVE_HWFP 1
  25#endif
  26
  27/*
  28 * The _current_ task is using the FPU for the first time
  29 * so initialize it and set the mxcsr to its default
  30 * value at reset if we support XMM instructions and then
  31 * remeber the current task has used the FPU.
  32 */
  33void init_fpu(void)
  34{
  35        __asm__("fninit");
  36        if ( cpu_has_xmm )
  37                load_mxcsr(0x1f80);
  38                
  39        current->used_math = 1;
  40}
  41
  42/*
  43 * FPU lazy state save handling.
  44 */
  45
  46static inline void __save_init_fpu( struct task_struct *tsk )
  47{
  48        if ( cpu_has_fxsr ) {
  49                asm volatile( "fxsave %0 ; fnclex"
  50                              : "=m" (tsk->thread.i387.fxsave) );
  51        } else {
  52                asm volatile( "fnsave %0 ; fwait"
  53                              : "=m" (tsk->thread.i387.fsave) );
  54        }
  55        tsk->flags &= ~PF_USEDFPU;
  56}
  57
  58void save_init_fpu( struct task_struct *tsk )
  59{
  60        __save_init_fpu(tsk);
  61        stts();
  62}
  63
  64void kernel_fpu_begin(void)
  65{
  66        struct task_struct *tsk = current;
  67
  68        if (tsk->flags & PF_USEDFPU) {
  69                __save_init_fpu(tsk);
  70                return;
  71        }
  72        clts();
  73}
  74
  75void restore_fpu( struct task_struct *tsk )
  76{
  77        if ( cpu_has_fxsr ) {
  78                asm volatile( "fxrstor %0"
  79                              : : "m" (tsk->thread.i387.fxsave) );
  80        } else {
  81                asm volatile( "frstor %0"
  82                              : : "m" (tsk->thread.i387.fsave) );
  83        }
  84}
  85
  86/*
  87 * FPU tag word conversions.
  88 */
  89
  90static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
  91{
  92        unsigned int tmp; /* to avoid 16 bit prefixes in the code */
  93 
  94        /* Transform each pair of bits into 01 (valid) or 00 (empty) */
  95        tmp = ~twd;
  96        tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
  97        /* and move the valid bits to the lower byte. */
  98        tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
  99        tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
 100        tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
 101        return tmp;
 102}
 103
 104static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
 105{
 106        struct _fpxreg *st = NULL;
 107        unsigned long twd = (unsigned long) fxsave->twd;
 108        unsigned long tag;
 109        unsigned long ret = 0xffff0000;
 110        int i;
 111
 112#define FPREG_ADDR(f, n)        ((char *)&(f)->st_space + (n) * 16);
 113
 114        for ( i = 0 ; i < 8 ; i++ ) {
 115                if ( twd & 0x1 ) {
 116                        st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
 117
 118                        switch ( st->exponent & 0x7fff ) {
 119                        case 0x7fff:
 120                                tag = 2;                /* Special */
 121                                break;
 122                        case 0x0000:
 123                                if ( !st->significand[0] &&
 124                                     !st->significand[1] &&
 125                                     !st->significand[2] &&
 126                                     !st->significand[3] ) {
 127                                        tag = 1;        /* Zero */
 128                                } else {
 129                                        tag = 2;        /* Special */
 130                                }
 131                                break;
 132                        default:
 133                                if ( st->significand[3] & 0x8000 ) {
 134                                        tag = 0;        /* Valid */
 135                                } else {
 136                                        tag = 2;        /* Special */
 137                                }
 138                                break;
 139                        }
 140                } else {
 141                        tag = 3;                        /* Empty */
 142                }
 143                ret |= (tag << (2 * i));
 144                twd = twd >> 1;
 145        }
 146        return ret;
 147}
 148
 149/*
 150 * FPU state interaction.
 151 */
 152
 153unsigned short get_fpu_cwd( struct task_struct *tsk )
 154{
 155        if ( cpu_has_fxsr ) {
 156                return tsk->thread.i387.fxsave.cwd;
 157        } else {
 158                return (unsigned short)tsk->thread.i387.fsave.cwd;
 159        }
 160}
 161
 162unsigned short get_fpu_swd( struct task_struct *tsk )
 163{
 164        if ( cpu_has_fxsr ) {
 165                return tsk->thread.i387.fxsave.swd;
 166        } else {
 167                return (unsigned short)tsk->thread.i387.fsave.swd;
 168        }
 169}
 170
 171unsigned short get_fpu_twd( struct task_struct *tsk )
 172{
 173        if ( cpu_has_fxsr ) {
 174                return tsk->thread.i387.fxsave.twd;
 175        } else {
 176                return (unsigned short)tsk->thread.i387.fsave.twd;
 177        }
 178}
 179
 180unsigned short get_fpu_mxcsr( struct task_struct *tsk )
 181{
 182        if ( cpu_has_xmm ) {
 183                return tsk->thread.i387.fxsave.mxcsr;
 184        } else {
 185                return 0x1f80;
 186        }
 187}
 188
 189void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd )
 190{
 191        if ( cpu_has_fxsr ) {
 192                tsk->thread.i387.fxsave.cwd = cwd;
 193        } else {
 194                tsk->thread.i387.fsave.cwd = ((long)cwd | 0xffff0000);
 195        }
 196}
 197
 198void set_fpu_swd( struct task_struct *tsk, unsigned short swd )
 199{
 200        if ( cpu_has_fxsr ) {
 201                tsk->thread.i387.fxsave.swd = swd;
 202        } else {
 203                tsk->thread.i387.fsave.swd = ((long)swd | 0xffff0000);
 204        }
 205}
 206
 207void set_fpu_twd( struct task_struct *tsk, unsigned short twd )
 208{
 209        if ( cpu_has_fxsr ) {
 210                tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd);
 211        } else {
 212                tsk->thread.i387.fsave.twd = ((long)twd | 0xffff0000);
 213        }
 214}
 215
 216void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr )
 217{
 218        if ( cpu_has_xmm ) {
 219                tsk->thread.i387.fxsave.mxcsr = (mxcsr & 0xffbf);
 220        }
 221}
 222
 223/*
 224 * FXSR floating point environment conversions.
 225 */
 226
 227static inline int convert_fxsr_to_user( struct _fpstate *buf,
 228                                        struct i387_fxsave_struct *fxsave )
 229{
 230        unsigned long env[7];
 231        struct _fpreg *to;
 232        struct _fpxreg *from;
 233        int i;
 234
 235        env[0] = (unsigned long)fxsave->cwd | 0xffff0000;
 236        env[1] = (unsigned long)fxsave->swd | 0xffff0000;
 237        env[2] = twd_fxsr_to_i387(fxsave);
 238        env[3] = fxsave->fip;
 239        env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16);
 240        env[5] = fxsave->foo;
 241        env[6] = fxsave->fos;
 242
 243        if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) )
 244                return 1;
 245
 246        to = &buf->_st[0];
 247        from = (struct _fpxreg *) &fxsave->st_space[0];
 248        for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
 249                if ( __copy_to_user( to, from, sizeof(*to) ) )
 250                        return 1;
 251        }
 252        return 0;
 253}
 254
 255static inline int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
 256                                          struct _fpstate *buf )
 257{
 258        unsigned long env[7];
 259        struct _fpxreg *to;
 260        struct _fpreg *from;
 261        int i;
 262
 263        if ( __copy_from_user( env, buf, 7 * sizeof(long) ) )
 264                return 1;
 265
 266        fxsave->cwd = (unsigned short)(env[0] & 0xffff);
 267        fxsave->swd = (unsigned short)(env[1] & 0xffff);
 268        fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
 269        fxsave->fip = env[3];
 270        fxsave->fop = (unsigned short)((env[4] & 0xffff0000) >> 16);
 271        fxsave->fcs = (env[4] & 0xffff);
 272        fxsave->foo = env[5];
 273        fxsave->fos = env[6];
 274
 275        to = (struct _fpxreg *) &fxsave->st_space[0];
 276        from = &buf->_st[0];
 277        for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
 278                if ( __copy_from_user( to, from, sizeof(*from) ) )
 279                        return 1;
 280        }
 281        return 0;
 282}
 283
 284/*
 285 * Signal frame handlers.
 286 */
 287
 288static inline int save_i387_fsave( struct _fpstate *buf )
 289{
 290        struct task_struct *tsk = current;
 291
 292        unlazy_fpu( tsk );
 293        tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd;
 294        if ( __copy_to_user( buf, &tsk->thread.i387.fsave,
 295                             sizeof(struct i387_fsave_struct) ) )
 296                return -1;
 297        return 1;
 298}
 299
 300static inline int save_i387_fxsave( struct _fpstate *buf )
 301{
 302        struct task_struct *tsk = current;
 303        int err = 0;
 304
 305        unlazy_fpu( tsk );
 306
 307        if ( convert_fxsr_to_user( buf, &tsk->thread.i387.fxsave ) )
 308                return -1;
 309
 310        err |= __put_user( tsk->thread.i387.fxsave.swd, &buf->status );
 311        err |= __put_user( X86_FXSR_MAGIC, &buf->magic );
 312        if ( err )
 313                return -1;
 314
 315        if ( __copy_to_user( &buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
 316                             sizeof(struct i387_fxsave_struct) ) )
 317                return -1;
 318        return 1;
 319}
 320
 321int save_i387( struct _fpstate *buf )
 322{
 323        if ( !current->used_math )
 324                return 0;
 325
 326        /* This will cause a "finit" to be triggered by the next
 327         * attempted FPU operation by the 'current' process.
 328         */
 329        current->used_math = 0;
 330
 331        if ( HAVE_HWFP ) {
 332                if ( cpu_has_fxsr ) {
 333                        return save_i387_fxsave( buf );
 334                } else {
 335                        return save_i387_fsave( buf );
 336                }
 337        } else {
 338                return save_i387_soft( &current->thread.i387.soft, buf );
 339        }
 340}
 341
 342static inline int restore_i387_fsave( struct _fpstate *buf )
 343{
 344        struct task_struct *tsk = current;
 345        clear_fpu( tsk );
 346        return __copy_from_user( &tsk->thread.i387.fsave, buf,
 347                                 sizeof(struct i387_fsave_struct) );
 348}
 349
 350static inline int restore_i387_fxsave( struct _fpstate *buf )
 351{
 352        struct task_struct *tsk = current;
 353        clear_fpu( tsk );
 354        if ( __copy_from_user( &tsk->thread.i387.fxsave, &buf->_fxsr_env[0],
 355                               sizeof(struct i387_fxsave_struct) ) )
 356                return 1;
 357        /* mxcsr bit 6 and 31-16 must be zero for security reasons */
 358        tsk->thread.i387.fxsave.mxcsr &= 0xffbf;
 359        return convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf );
 360}
 361
 362int restore_i387( struct _fpstate *buf )
 363{
 364        int err;
 365
 366        if ( HAVE_HWFP ) {
 367                if ( cpu_has_fxsr ) {
 368                        err =  restore_i387_fxsave( buf );
 369                } else {
 370                        err = restore_i387_fsave( buf );
 371                }
 372        } else {
 373                err = restore_i387_soft( &current->thread.i387.soft, buf );
 374        }
 375        current->used_math = 1;
 376        return err;
 377}
 378
 379/*
 380 * ptrace request handlers.
 381 */
 382
 383static inline int get_fpregs_fsave( struct user_i387_struct *buf,
 384                                    struct task_struct *tsk )
 385{
 386        return __copy_to_user( buf, &tsk->thread.i387.fsave,
 387                               sizeof(struct user_i387_struct) );
 388}
 389
 390static inline int get_fpregs_fxsave( struct user_i387_struct *buf,
 391                                     struct task_struct *tsk )
 392{
 393        return convert_fxsr_to_user( (struct _fpstate *)buf,
 394                                     &tsk->thread.i387.fxsave );
 395}
 396
 397int get_fpregs( struct user_i387_struct *buf, struct task_struct *tsk )
 398{
 399        if ( HAVE_HWFP ) {
 400                if ( cpu_has_fxsr ) {
 401                        return get_fpregs_fxsave( buf, tsk );
 402                } else {
 403                        return get_fpregs_fsave( buf, tsk );
 404                }
 405        } else {
 406                return save_i387_soft( &tsk->thread.i387.soft,
 407                                       (struct _fpstate *)buf );
 408        }
 409}
 410
 411static inline int set_fpregs_fsave( struct task_struct *tsk,
 412                                    struct user_i387_struct *buf )
 413{
 414        return __copy_from_user( &tsk->thread.i387.fsave, buf,
 415                                 sizeof(struct user_i387_struct) );
 416}
 417
 418static inline int set_fpregs_fxsave( struct task_struct *tsk,
 419                                     struct user_i387_struct *buf )
 420{
 421        return convert_fxsr_from_user( &tsk->thread.i387.fxsave,
 422                                       (struct _fpstate *)buf );
 423}
 424
 425int set_fpregs( struct task_struct *tsk, struct user_i387_struct *buf )
 426{
 427        if ( HAVE_HWFP ) {
 428                if ( cpu_has_fxsr ) {
 429                        return set_fpregs_fxsave( tsk, buf );
 430                } else {
 431                        return set_fpregs_fsave( tsk, buf );
 432                }
 433        } else {
 434                return restore_i387_soft( &tsk->thread.i387.soft,
 435                                          (struct _fpstate *)buf );
 436        }
 437}
 438
 439int get_fpxregs( struct user_fxsr_struct *buf, struct task_struct *tsk )
 440{
 441        if ( cpu_has_fxsr ) {
 442                if (__copy_to_user( (void *)buf, &tsk->thread.i387.fxsave,
 443                                    sizeof(struct user_fxsr_struct) ))
 444                        return -EFAULT;
 445                return 0;
 446        } else {
 447                return -EIO;
 448        }
 449}
 450
 451int set_fpxregs( struct task_struct *tsk, struct user_fxsr_struct *buf )
 452{
 453        if ( cpu_has_fxsr ) {
 454                __copy_from_user( &tsk->thread.i387.fxsave, (void *)buf,
 455                                  sizeof(struct user_fxsr_struct) );
 456                /* mxcsr bit 6 and 31-16 must be zero for security reasons */
 457                tsk->thread.i387.fxsave.mxcsr &= 0xffbf;
 458                return 0;
 459        } else {
 460                return -EIO;
 461        }
 462}
 463
 464/*
 465 * FPU state for core dumps.
 466 */
 467
 468static inline void copy_fpu_fsave( struct task_struct *tsk,
 469                                   struct user_i387_struct *fpu )
 470{
 471        memcpy( fpu, &tsk->thread.i387.fsave,
 472                sizeof(struct user_i387_struct) );
 473}
 474
 475static inline void copy_fpu_fxsave( struct task_struct *tsk,
 476                                   struct user_i387_struct *fpu )
 477{
 478        unsigned short *to;
 479        unsigned short *from;
 480        int i;
 481
 482        memcpy( fpu, &tsk->thread.i387.fxsave, 7 * sizeof(long) );
 483
 484        to = (unsigned short *)&fpu->st_space[0];
 485        from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0];
 486        for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) {
 487                memcpy( to, from, 5 * sizeof(unsigned short) );
 488        }
 489}
 490
 491int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
 492{
 493        int fpvalid;
 494        struct task_struct *tsk = current;
 495
 496        fpvalid = tsk->used_math;
 497        if ( fpvalid ) {
 498                unlazy_fpu( tsk );
 499                if ( cpu_has_fxsr ) {
 500                        copy_fpu_fxsave( tsk, fpu );
 501                } else {
 502                        copy_fpu_fsave( tsk, fpu );
 503                }
 504        }
 505
 506        return fpvalid;
 507}
 508
 509int dump_extended_fpu( struct pt_regs *regs, struct user_fxsr_struct *fpu )
 510{
 511        int fpvalid;
 512        struct task_struct *tsk = current;
 513
 514        fpvalid = tsk->used_math && cpu_has_fxsr;
 515        if ( fpvalid ) {
 516                unlazy_fpu( tsk );
 517                memcpy( fpu, &tsk->thread.i387.fxsave,
 518                        sizeof(struct user_fxsr_struct) );
 519        }
 520
 521        return fpvalid;
 522}
 523
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.