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

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