root/kernel/FPU-emu/fpu_entry.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. math_emulate
  2. __math_abort
  3. math_emulate

   1 /*---------------------------------------------------------------------------+
   2  |  fpu_entry.c                                                              |
   3  |                                                                           |
   4  | The entry function for wm-FPU-emu                                         |
   5  |                                                                           |
   6  | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
   7  |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
   8  |                                                                           |
   9  | See the files "README" and "COPYING" for further copyright and warranty   |
  10  | information.                                                              |
  11  |                                                                           |
  12  +---------------------------------------------------------------------------*/
  13 
  14 /*---------------------------------------------------------------------------+
  15  | Note:                                                                     |
  16  |    The file contains code which accesses user memory.                     |
  17  |    Emulator static data may change when user memory is accessed, due to   |
  18  |    other processes using the emulator while swapping is in progress.      |
  19  +---------------------------------------------------------------------------*/
  20 
  21 /*---------------------------------------------------------------------------+
  22  | math_emulate() is the sole entry point for wm-FPU-emu                     |
  23  +---------------------------------------------------------------------------*/
  24 
  25 #include <linux/config.h>
  26 
  27 #ifdef CONFIG_MATH_EMULATION
  28 
  29 #include <linux/signal.h>
  30 
  31 #include "fpu_system.h"
  32 #include "fpu_emu.h"
  33 #include "exception.h"
  34 
  35 #include <asm/segment.h>
  36 
  37 
  38 #define __BAD__ Un_impl   /* Not implemented */
  39 
  40 #ifndef NO_UNDOC_CODE    /* Un-documented FPU op-codes supported by default. */
  41 
  42 /* WARNING: These codes are not documented by Intel in their 80486 manual
  43    and may not work on FPU clones or later Intel FPUs. */
  44 
  45 /* Changes to support the un-doc codes provided by Linus Torvalds. */
  46 
  47 #define _d9_d8_ fstp_i    /* unofficial code (19) */
  48 #define _dc_d0_ fcom_st   /* unofficial code (14) */
  49 #define _dc_d8_ fcompst   /* unofficial code (1c) */
  50 #define _dd_c8_ fxch_i    /* unofficial code (0d) */
  51 #define _de_d0_ fcompst   /* unofficial code (16) */
  52 #define _df_c0_ ffreep    /* unofficial code (07) ffree + pop */
  53 #define _df_c8_ fxch_i    /* unofficial code (0f) */
  54 #define _df_d0_ fstp_i    /* unofficial code (17) */
  55 #define _df_d8_ fstp_i    /* unofficial code (1f) */
  56 
  57 static FUNC st_instr_table[64] = {
  58   fadd__,   fld_i_,  __BAD__, __BAD__, fadd_i,  ffree_,  faddp_,  _df_c0_,
  59   fmul__,   fxch_i,  __BAD__, __BAD__, fmul_i,  _dd_c8_, fmulp_,  _df_c8_,
  60   fcom_st,  fp_nop,  __BAD__, __BAD__, _dc_d0_, fst_i_,  _de_d0_, _df_d0_,
  61   fcompst,  _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i,  fcompp,  _df_d8_,
  62   fsub__,   fp_etc,  __BAD__, finit_,  fsubri,  fucom_,  fsubrp,  fstsw_,
  63   fsubr_,   fconst,  fucompp, __BAD__, fsub_i,  fucomp,  fsubp_,  __BAD__,
  64   fdiv__,   trig_a,  __BAD__, __BAD__, fdivri,  __BAD__, fdivrp,  __BAD__,
  65   fdivr_,   trig_b,  __BAD__, __BAD__, fdiv_i,  __BAD__, fdivp_,  __BAD__,
  66 };
  67 
  68 #else     /* Support only documented FPU op-codes */
  69 
  70 static FUNC st_instr_table[64] = {
  71   fadd__,   fld_i_,  __BAD__, __BAD__, fadd_i,  ffree_,  faddp_,  __BAD__,
  72   fmul__,   fxch_i,  __BAD__, __BAD__, fmul_i,  __BAD__, fmulp_,  __BAD__,
  73   fcom_st,  fp_nop,  __BAD__, __BAD__, __BAD__, fst_i_,  __BAD__, __BAD__,
  74   fcompst,  __BAD__, __BAD__, __BAD__, __BAD__, fstp_i,  fcompp,  __BAD__,
  75   fsub__,   fp_etc,  __BAD__, finit_,  fsubri,  fucom_,  fsubrp,  fstsw_,
  76   fsubr_,   fconst,  fucompp, __BAD__, fsub_i,  fucomp,  fsubp_,  __BAD__,
  77   fdiv__,   trig_a,  __BAD__, __BAD__, fdivri,  __BAD__, fdivrp,  __BAD__,
  78   fdivr_,   trig_b,  __BAD__, __BAD__, fdiv_i,  __BAD__, fdivp_,  __BAD__,
  79 };
  80 
  81 #endif NO_UNDOC_CODE
  82 
  83 
  84 #define _NONE_ 0   /* Take no special action */
  85 #define _REG0_ 1   /* Need to check for not empty st(0) */
  86 #define _REGI_ 2   /* Need to check for not empty st(0) and st(rm) */
  87 #define _REGi_ 0   /* Uses st(rm) */
  88 #define _PUSH_ 3   /* Need to check for space to push onto stack */
  89 #define _null_ 4   /* Function illegal or not implemented */
  90 
  91 #ifndef NO_UNDOC_CODE
  92 
  93 /* Un-documented FPU op-codes supported by default. (see above) */
  94 
  95 static unsigned char type_table[64] = {
  96   _REGI_, _NONE_, _null_, _null_, _REGI_, _REGi_, _REGI_, _REGi_,
  97   _REGI_, _REGI_, _null_, _null_, _REGI_, _REGI_, _REGI_, _REGI_,
  98   _REGI_, _NONE_, _null_, _null_, _REGI_, _REG0_, _REGI_, _REG0_,
  99   _REGI_, _REG0_, _null_, _null_, _REGI_, _REG0_, _REGI_, _REG0_,
 100   _REGI_, _NONE_, _null_, _NONE_, _REGI_, _REGI_, _REGI_, _NONE_,
 101   _REGI_, _NONE_, _REGI_, _null_, _REGI_, _REGI_, _REGI_, _null_,
 102   _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
 103   _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_
 104 };
 105 
 106 #else     /* Support only documented FPU op-codes */
 107 
 108 static unsigned char type_table[64] = {
 109   _REGI_, _NONE_, _null_, _null_, _REGI_, _REGi_, _REGI_, _null_,
 110   _REGI_, _REGI_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
 111   _REGI_, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
 112   _REGI_, _null_, _null_, _null_, _null_, _REG0_, _REGI_, _null_,
 113   _REGI_, _NONE_, _null_, _NONE_, _REGI_, _REGI_, _REGI_, _NONE_,
 114   _REGI_, _NONE_, _REGI_, _null_, _REGI_, _REGI_, _REGI_, _null_,
 115   _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
 116   _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_
 117 };
 118 
 119 #endif NO_UNDOC_CODE
 120 
 121 
 122 /* Be careful when using any of these global variables...
 123    they might change if swapping is triggered */
 124 unsigned char  FPU_rm;
 125 char           FPU_st0_tag;
 126 FPU_REG       *FPU_st0_ptr;
 127 
 128 #ifdef PARANOID
 129 char emulating=0;
 130 #endif PARANOID
 131 
 132 #define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
 133 
 134 
 135 void math_emulate(long arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137   unsigned char  FPU_modrm;
 138   unsigned short code;
 139 
 140 #ifdef PARANOID
 141   if ( emulating )
 142     {
 143       printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\r\n");
 144     }
 145   RE_ENTRANT_CHECK_ON
 146 #endif PARANOID
 147 
 148   if (!current->used_math)
 149     {
 150       finit();
 151       current->used_math = 1;
 152       control_word = 0x037f;
 153       status_word = 0x0000;
 154     }
 155 
 156   FPU_info = (struct info *) &arg;
 157 
 158   /* We cannot handle emulation in v86-mode */
 159   if (FPU_EFLAGS & 0x00020000)
 160     math_abort(FPU_info,SIGILL);
 161 
 162   /* 0x000f means user code space */
 163   if (FPU_CS != 0x000f)
 164     {
 165       printk("math_emulate: %04x:%08x\n\r",FPU_CS,FPU_EIP);
 166       panic("Math emulation needed in kernel");
 167     }
 168 
 169   FPU_lookahead = 1;
 170   if (current->flags & PF_PTRACED)
 171         FPU_lookahead = 0;
 172 do_another:
 173 
 174   FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
 175 
 176   RE_ENTRANT_CHECK_OFF
 177   code = get_fs_word((unsigned short *) FPU_EIP);
 178   RE_ENTRANT_CHECK_ON
 179 
 180   if ( (code & 0xff) == 0x66 )
 181     {
 182       FPU_EIP++;
 183       RE_ENTRANT_CHECK_OFF
 184       code = get_fs_word((unsigned short *) FPU_EIP);
 185       RE_ENTRANT_CHECK_ON
 186     }
 187   FPU_EIP += 2;
 188 
 189   FPU_modrm = code >> 8;
 190   FPU_rm = FPU_modrm & 7;
 191 
 192   if ( FPU_modrm < 0300 )
 193     {
 194       /* All of these instructions use the mod/rm byte to get a data address */
 195       get_address(FPU_modrm);
 196       if ( !(code & 1) )
 197         {
 198           switch ( (code >> 1) & 3 )
 199             {
 200             case 0:
 201               reg_load_single();
 202               break;
 203             case 1:
 204               reg_load_int32();
 205               break;
 206             case 2:
 207               reg_load_double();
 208               break;
 209             case 3:
 210               reg_load_int16();
 211               break;
 212             }
 213 
 214           /* No more access to user memory, it is safe
 215              to use static data now */
 216           FPU_st0_ptr = &st(0);
 217           FPU_st0_tag = FPU_st0_ptr->tag;
 218           if ( NOT_EMPTY_0 )
 219             {
 220               switch ( (FPU_modrm >> 3) & 7 )
 221                 {
 222                 case 0:         /* fadd */
 223                   reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
 224                   break;
 225                 case 1:         /* fmul */
 226                   reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
 227                   break;
 228                 case 2:         /* fcom */
 229                   compare_st_data();
 230                   break;
 231                 case 3:         /* fcomp */
 232                   compare_st_data();
 233                   pop();
 234                   break;
 235                 case 4:         /* fsub */
 236                   reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
 237                   break;
 238                 case 5:         /* fsubr */
 239                   reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr);
 240                   break;
 241                 case 6:         /* fdiv */
 242                   reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
 243                   break;
 244                 case 7:         /* fdivr */
 245                   reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr);
 246                   break;
 247                 }
 248             }
 249           else
 250             stack_underflow();
 251         }
 252       else
 253         {
 254           load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
 255         }
 256 
 257       data_operand_offset = (unsigned long)FPU_data_address;
 258     }
 259   else
 260     {
 261       /* None of these instructions access user memory */
 262       unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
 263       FPU_st0_ptr = &st(0);
 264       FPU_st0_tag = FPU_st0_ptr->tag;
 265       switch ( type_table[(int) instr_index] )
 266         {
 267         case _NONE_:
 268           break;
 269         case _REG0_:
 270           if ( !NOT_EMPTY_0 )
 271             {
 272               stack_underflow();
 273               goto instruction_done;
 274             }
 275           break;
 276         case _REGI_:
 277           if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
 278             {
 279               stack_underflow();
 280               goto instruction_done;
 281             }
 282           break;
 283         case _PUSH_:     /* Only used by the fld st(i) instruction */
 284           break;
 285         case _null_:
 286           Un_impl();
 287           goto instruction_done;
 288         default:
 289           EXCEPTION(EX_INTERNAL|0x111);
 290           goto instruction_done;
 291         }
 292       (*st_instr_table[(int) instr_index])();
 293     }
 294 
 295 instruction_done:
 296 
 297   ip_offset = FPU_entry_eip;
 298   bswapw(code);
 299   *(1 + (unsigned short *)&cs_selector) = code & 0x7ff;
 300 
 301   if (FPU_lookahead && !need_resched)
 302     {
 303       unsigned char next;
 304 skip_fwait:
 305       RE_ENTRANT_CHECK_OFF
 306       next = get_fs_byte((unsigned char *) FPU_EIP);
 307       RE_ENTRANT_CHECK_ON
 308 test_for_fp:
 309       if ( (next & 0xf8) == 0xd8 )
 310         {
 311           goto do_another;
 312         }
 313       if ( next == 0x9b )  /* fwait */
 314         { FPU_EIP++; goto skip_fwait; }
 315       if ( next == 0x66 )  /* size prefix */
 316         {
 317           RE_ENTRANT_CHECK_OFF
 318           next = get_fs_byte((unsigned char *) (FPU_EIP+1));
 319           RE_ENTRANT_CHECK_ON
 320           if ( (next & 0xf8) == 0xd8 )
 321             goto test_for_fp;
 322         }
 323     }
 324 
 325   RE_ENTRANT_CHECK_OFF
 326 }
 327 
 328 
 329 void __math_abort(struct info * info, unsigned int signal)
     /* [previous][next][first][last][top][bottom][index][help] */
 330 {
 331         FPU_EIP = FPU_ORIG_EIP;
 332         send_sig(signal,current,1);
 333         __asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4));
 334 }
 335 
 336 #else /* no math emulation */
 337 
 338 #include <linux/signal.h>
 339 #include <linux/sched.h>
 340 
 341 void math_emulate(long arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 342 {
 343   printk("math-meulation not enabled and no coprocessor found.\n");
 344   printk("killing %s.\n",current->comm);
 345   send_sig(SIGFPE,current,1);
 346   schedule();
 347 }
 348 
 349 #endif /* CONFIG_MATH_EMULATION */

/* [previous][next][first][last][top][bottom][index][help] */