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,1993                                                   |
   7  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
   8  |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
   9  |                                                                           |
  10  | See the files "README" and "COPYING" for further copyright and warranty   |
  11  | information.                                                              |
  12  |                                                                           |
  13  +---------------------------------------------------------------------------*/
  14 
  15 /*---------------------------------------------------------------------------+
  16  | Note:                                                                     |
  17  |    The file contains code which accesses user memory.                     |
  18  |    Emulator static data may change when user memory is accessed, due to   |
  19  |    other processes using the emulator while swapping is in progress.      |
  20  +---------------------------------------------------------------------------*/
  21 
  22 /*---------------------------------------------------------------------------+
  23  | math_emulate() is the sole entry point for wm-FPU-emu                     |
  24  +---------------------------------------------------------------------------*/
  25 
  26 #include <linux/config.h>
  27 
  28 #ifdef CONFIG_MATH_EMULATION
  29 
  30 #include <linux/signal.h>
  31 #include <linux/segment.h>
  32 
  33 #include "fpu_system.h"
  34 #include "fpu_emu.h"
  35 #include "exception.h"
  36 #include "control_w.h"
  37 #include "status_w.h"
  38 
  39 #include <asm/segment.h>
  40 
  41 
  42 #define __BAD__ Un_impl   /* Not implemented */
  43 
  44 #ifndef NO_UNDOC_CODE    /* Un-documented FPU op-codes supported by default. */
  45 
  46 /* WARNING: These codes are not documented by Intel in their 80486 manual
  47    and may not work on FPU clones or later Intel FPUs. */
  48 
  49 /* Changes to support the un-doc codes provided by Linus Torvalds. */
  50 
  51 #define _d9_d8_ fstp_i    /* unofficial code (19) */
  52 #define _dc_d0_ fcom_st   /* unofficial code (14) */
  53 #define _dc_d8_ fcompst   /* unofficial code (1c) */
  54 #define _dd_c8_ fxch_i    /* unofficial code (0d) */
  55 #define _de_d0_ fcompst   /* unofficial code (16) */
  56 #define _df_c0_ ffreep    /* unofficial code (07) ffree + pop */
  57 #define _df_c8_ fxch_i    /* unofficial code (0f) */
  58 #define _df_d0_ fstp_i    /* unofficial code (17) */
  59 #define _df_d8_ fstp_i    /* unofficial code (1f) */
  60 
  61 static FUNC st_instr_table[64] = {
  62   fadd__,   fld_i_,  __BAD__, __BAD__, fadd_i,  ffree_,  faddp_,  _df_c0_,
  63   fmul__,   fxch_i,  __BAD__, __BAD__, fmul_i,  _dd_c8_, fmulp_,  _df_c8_,
  64   fcom_st,  fp_nop,  __BAD__, __BAD__, _dc_d0_, fst_i_,  _de_d0_, _df_d0_,
  65   fcompst,  _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i,  fcompp,  _df_d8_,
  66   fsub__,   fp_etc,  __BAD__, finit_,  fsubri,  fucom_,  fsubrp,  fstsw_,
  67   fsubr_,   fconst,  fucompp, __BAD__, fsub_i,  fucomp,  fsubp_,  __BAD__,
  68   fdiv__,   trig_a,  __BAD__, __BAD__, fdivri,  __BAD__, fdivrp,  __BAD__,
  69   fdivr_,   trig_b,  __BAD__, __BAD__, fdiv_i,  __BAD__, fdivp_,  __BAD__,
  70 };
  71 
  72 #else     /* Support only documented FPU op-codes */
  73 
  74 static FUNC st_instr_table[64] = {
  75   fadd__,   fld_i_,  __BAD__, __BAD__, fadd_i,  ffree_,  faddp_,  __BAD__,
  76   fmul__,   fxch_i,  __BAD__, __BAD__, fmul_i,  __BAD__, fmulp_,  __BAD__,
  77   fcom_st,  fp_nop,  __BAD__, __BAD__, __BAD__, fst_i_,  __BAD__, __BAD__,
  78   fcompst,  __BAD__, __BAD__, __BAD__, __BAD__, fstp_i,  fcompp,  __BAD__,
  79   fsub__,   fp_etc,  __BAD__, finit_,  fsubri,  fucom_,  fsubrp,  fstsw_,
  80   fsubr_,   fconst,  fucompp, __BAD__, fsub_i,  fucomp,  fsubp_,  __BAD__,
  81   fdiv__,   trig_a,  __BAD__, __BAD__, fdivri,  __BAD__, fdivrp,  __BAD__,
  82   fdivr_,   trig_b,  __BAD__, __BAD__, fdiv_i,  __BAD__, fdivp_,  __BAD__,
  83 };
  84 
  85 #endif NO_UNDOC_CODE
  86 
  87 
  88 #define _NONE_ 0   /* Take no special action */
  89 #define _REG0_ 1   /* Need to check for not empty st(0) */
  90 #define _REGI_ 2   /* Need to check for not empty st(0) and st(rm) */
  91 #define _REGi_ 0   /* Uses st(rm) */
  92 #define _PUSH_ 3   /* Need to check for space to push onto stack */
  93 #define _null_ 4   /* Function illegal or not implemented */
  94 #define _REGIi 5   /* Uses st(0) and st(rm), result to st(rm) */
  95 #define _REGIp 6   /* Uses st(0) and st(rm), result to st(rm) then pop */
  96 #define _REGIc 0   /* Compare st(0) and st(rm) */
  97 #define _REGIn 0   /* Uses st(0) and st(rm), but handle checks later */
  98 
  99 #ifndef NO_UNDOC_CODE
 100 
 101 /* Un-documented FPU op-codes supported by default. (see above) */
 102 
 103 static unsigned char type_table[64] = {
 104   _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
 105   _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
 106   _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
 107   _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
 108   _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
 109   _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
 110   _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
 111   _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
 112 };
 113 
 114 #else     /* Support only documented FPU op-codes */
 115 
 116 static unsigned char type_table[64] = {
 117   _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
 118   _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
 119   _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
 120   _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
 121   _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
 122   _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
 123   _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
 124   _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
 125 };
 126 
 127 #endif NO_UNDOC_CODE
 128 
 129 
 130 /* Be careful when using any of these global variables...
 131    they might change if swapping is triggered */
 132 unsigned char  FPU_rm;
 133 char           FPU_st0_tag;
 134 FPU_REG       *FPU_st0_ptr;
 135 
 136 #ifdef PARANOID
 137 char emulating=0;
 138 #endif PARANOID
 139 
 140 #define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
 141 
 142 
 143 extern "C" void math_emulate(long arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 144 {
 145   unsigned char  FPU_modrm;
 146   unsigned short code;
 147 
 148 #ifdef PARANOID
 149   if ( emulating )
 150     {
 151       printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
 152     }
 153   RE_ENTRANT_CHECK_ON
 154 #endif PARANOID
 155 
 156   if (!current->used_math)
 157     {
 158       finit();
 159       current->used_math = 1;
 160     }
 161 
 162   FPU_info = (struct info *) &arg;
 163 
 164   /* We cannot handle emulation in v86-mode */
 165   if (FPU_EFLAGS & 0x00020000)
 166     {
 167       FPU_ORIG_EIP = FPU_EIP;
 168       math_abort(FPU_info,SIGILL);
 169     }
 170 
 171   /* user code space? */
 172   if (FPU_CS != USER_CS)
 173     {
 174       printk("math_emulate: %04x:%08x\n",FPU_CS,FPU_EIP);
 175       panic("Math emulation needed in kernel");
 176     }
 177 
 178   FPU_lookahead = 1;
 179   if (current->flags & PF_PTRACED)
 180         FPU_lookahead = 0;
 181 
 182 do_another_FPU_instruction:
 183 
 184   RE_ENTRANT_CHECK_OFF
 185   code = get_fs_word((unsigned short *) FPU_EIP);
 186   RE_ENTRANT_CHECK_ON
 187 
 188   if ( (code & 0xff) == 0x9b )  /* fwait */
 189     {
 190       if (status_word & SW_Summary)
 191         goto do_the_FPU_interrupt;
 192       else
 193         {
 194           FPU_EIP++;
 195           goto FPU_instruction_done;
 196         }
 197     }
 198 
 199   if (status_word & SW_Summary)
 200     {
 201       /* Ignore the error for now if the current instruction is a no-wait
 202          control instruction */
 203       /* The 80486 manual contradicts itself on this topic,
 204          so I use the following list of such instructions until
 205          I can check on a real 80486:
 206          fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
 207        */
 208       if ( ! ( (((code & 0xf803) == 0xe003) ||    /* fnclex, fninit, fnstsw */
 209                 (((code & 0x3003) == 0x3001) &&   /* fnsave, fnstcw, fnstenv,
 210                                                      fnstsw */
 211                  ((code & 0xc000) != 0xc000))) ) )
 212         {
 213           /* This is a guess about what a real FPU might do to this bit: */
 214 /*        status_word &= ~SW_Summary; ****/
 215 
 216           /*
 217            *  We need to simulate the action of the kernel to FPU
 218            *  interrupts here.
 219            *  Currently, the "real FPU" part of the kernel (0.99.10)
 220            *  clears the exception flags, sets the registers to empty,
 221            *  and passes information back to the interrupted process
 222            *  via the cs selector and operand selector, so we do the same.
 223            */
 224         do_the_FPU_interrupt:
 225           cs_selector &= 0xffff0000;
 226           cs_selector |= (status_word & ~SW_Top) | ((top&7) << SW_Top_Shift);
 227           operand_selector = tag_word();
 228           status_word = 0;
 229           top = 0;
 230           {
 231             int r;
 232             for (r = 0; r < 8; r++)
 233               {
 234                 regs[r].tag = TW_Empty;
 235               }
 236           }
 237 
 238           RE_ENTRANT_CHECK_OFF
 239           send_sig(SIGFPE, current, 1);
 240           return;
 241         }
 242     }
 243 
 244   FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
 245 
 246   if ( (code & 0xff) == 0x66 )  /* size prefix */
 247     {
 248       FPU_EIP++;
 249       RE_ENTRANT_CHECK_OFF
 250       code = get_fs_word((unsigned short *) FPU_EIP);
 251       RE_ENTRANT_CHECK_ON
 252     }
 253   FPU_EIP += 2;
 254 
 255   FPU_modrm = code >> 8;
 256   FPU_rm = FPU_modrm & 7;
 257 
 258   if ( FPU_modrm < 0300 )
 259     {
 260       /* All of these instructions use the mod/rm byte to get a data address */
 261       get_address(FPU_modrm);
 262       if ( !(code & 1) )
 263         {
 264           unsigned short status1 = status_word;
 265           FPU_st0_ptr = &st(0);
 266           FPU_st0_tag = FPU_st0_ptr->tag;
 267 
 268           /* Stack underflow has priority */
 269           if ( NOT_EMPTY_0 )
 270             {
 271               switch ( (code >> 1) & 3 )
 272                 {
 273                 case 0:
 274                   reg_load_single();
 275                   break;
 276                 case 1:
 277                   reg_load_int32();
 278                   break;
 279                 case 2:
 280                   reg_load_double();
 281                   break;
 282                 case 3:
 283                   reg_load_int16();
 284                   break;
 285                 }
 286               
 287               /* No more access to user memory, it is safe
 288                  to use static data now */
 289               FPU_st0_ptr = &st(0);
 290               FPU_st0_tag = FPU_st0_ptr->tag;
 291 
 292               /* NaN operands have the next priority. */
 293               /* We have to delay looking at st(0) until after
 294                  loading the data, because that data might contain an SNaN */
 295               if ( (FPU_st0_tag == TW_NaN) ||
 296                   (FPU_loaded_data.tag == TW_NaN) )
 297                 {
 298                   /* Restore the status word; we might have loaded a
 299                      denormal. */
 300                   status_word = status1;
 301                   if ( (FPU_modrm & 0x30) == 0x10 )
 302                     {
 303                       /* fcom or fcomp */
 304                       EXCEPTION(EX_Invalid);
 305                       setcc(SW_C3 | SW_C2 | SW_C0);
 306                       if ( FPU_modrm & 0x08 )
 307                         pop();             /* fcomp, so we pop. */
 308                     }
 309                   else
 310                     real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
 311                   goto reg_mem_instr_done;
 312                 }
 313 
 314               switch ( (FPU_modrm >> 3) & 7 )
 315                 {
 316                 case 0:         /* fadd */
 317                   reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
 318                           control_word);
 319                   break;
 320                 case 1:         /* fmul */
 321                   reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
 322                           control_word);
 323                   break;
 324                 case 2:         /* fcom */
 325                   compare_st_data();
 326                   break;
 327                 case 3:         /* fcomp */
 328                   compare_st_data();
 329                   pop();
 330                   break;
 331                 case 4:         /* fsub */
 332                   reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
 333                           control_word);
 334                   break;
 335                 case 5:         /* fsubr */
 336                   reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
 337                           control_word);
 338                   break;
 339                 case 6:         /* fdiv */
 340                   reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
 341                           control_word);
 342                   break;
 343                 case 7:         /* fdivr */
 344                   if ( FPU_st0_tag == TW_Zero )
 345                     status_word = status1;  /* Undo any denorm tag,
 346                                                zero-divide has priority. */
 347                   reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
 348                           control_word);
 349                   break;
 350                 }
 351             }
 352           else
 353             {
 354               if ( (FPU_modrm & 0x30) == 0x10 )
 355                 {
 356                   /* The instruction is fcom or fcomp */
 357                   EXCEPTION(EX_StackUnder);
 358                   setcc(SW_C3 | SW_C2 | SW_C0);
 359                   if ( FPU_modrm & 0x08 )
 360                     pop();             /* fcomp, Empty or not, we pop. */
 361                 }
 362               else
 363                 stack_underflow();
 364             }
 365         }
 366       else
 367         {
 368           load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
 369         }
 370 
 371     reg_mem_instr_done:
 372 
 373       data_operand_offset = (unsigned long)FPU_data_address;
 374     }
 375   else
 376     {
 377       /* None of these instructions access user memory */
 378       unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
 379 
 380       FPU_st0_ptr = &st(0);
 381       FPU_st0_tag = FPU_st0_ptr->tag;
 382       switch ( type_table[(int) instr_index] )
 383         {
 384         case _NONE_:   /* also _REGIc: _REGIn */
 385           break;
 386         case _REG0_:
 387           if ( !NOT_EMPTY_0 )
 388             {
 389               stack_underflow();
 390               goto FPU_instruction_done;
 391             }
 392           break;
 393         case _REGIi:
 394           if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
 395             {
 396               stack_underflow_i(FPU_rm);
 397               goto FPU_instruction_done;
 398             }
 399           break;
 400         case _REGIp:
 401           if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
 402             {
 403               stack_underflow_i(FPU_rm);
 404               pop();
 405               goto FPU_instruction_done;
 406             }
 407           break;
 408         case _REGI_:
 409           if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
 410             {
 411               stack_underflow();
 412               goto FPU_instruction_done;
 413             }
 414           break;
 415         case _PUSH_:     /* Only used by the fld st(i) instruction */
 416           break;
 417         case _null_:
 418           Un_impl();
 419           goto FPU_instruction_done;
 420         default:
 421           EXCEPTION(EX_INTERNAL|0x111);
 422           goto FPU_instruction_done;
 423         }
 424       (*st_instr_table[(int) instr_index])();
 425     }
 426 
 427 FPU_instruction_done:
 428 
 429   ip_offset = FPU_entry_eip;
 430   bswapw(code);
 431   *(1 + (unsigned short *)&cs_selector) = code & 0x7ff;
 432 
 433 #ifdef DEBUG
 434   RE_ENTRANT_CHECK_OFF
 435   emu_printall();
 436   RE_ENTRANT_CHECK_ON
 437 #endif DEBUG
 438 
 439   if (FPU_lookahead && !need_resched)
 440     {
 441       unsigned char next;
 442 
 443       /* (This test should generate no machine code) */
 444       while ( 1 )
 445         {
 446           RE_ENTRANT_CHECK_OFF
 447           next = get_fs_byte((unsigned char *) FPU_EIP);
 448           RE_ENTRANT_CHECK_ON
 449           if ( ((next & 0xf8) == 0xd8) || (next == 0x9b) )  /* fwait */
 450             {
 451               goto do_another_FPU_instruction;
 452             }
 453           else if ( next == 0x66 )  /* size prefix */
 454             {
 455               RE_ENTRANT_CHECK_OFF
 456               next = get_fs_byte((unsigned char *) (FPU_EIP+1));
 457               RE_ENTRANT_CHECK_ON
 458               if ( (next & 0xf8) == 0xd8 )
 459                 {
 460                   FPU_EIP++;
 461                   goto do_another_FPU_instruction;
 462                 }
 463             }
 464           break;
 465         }
 466     }
 467 
 468   RE_ENTRANT_CHECK_OFF
 469 }
 470 
 471 
 472 void __math_abort(struct info * info, unsigned int signal)
     /* [previous][next][first][last][top][bottom][index][help] */
 473 {
 474         FPU_EIP = FPU_ORIG_EIP;
 475         send_sig(signal,current,1);
 476         RE_ENTRANT_CHECK_OFF
 477         __asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4));
 478 #ifdef PARANOID
 479       printk("ERROR: wm-FPU-emu math_abort failed!\n");
 480 #endif PARANOID
 481 }
 482 
 483 #else /* no math emulation */
 484 
 485 #include <linux/signal.h>
 486 #include <linux/sched.h>
 487 
 488 extern "C" void math_emulate(long arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 489 {
 490   printk("math-meulation not enabled and no coprocessor found.\n");
 491   printk("killing %s.\n",current->comm);
 492   send_sig(SIGFPE,current,1);
 493   schedule();
 494 }
 495 
 496 #endif /* CONFIG_MATH_EMULATION */

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