root/drivers/FPU-emu/get_address.c

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

DEFINITIONS

This source file includes following definitions.
  1. sib
  2. vm86_segment
  3. pm_address
  4. get_address
  5. get_address_16

   1 /*---------------------------------------------------------------------------+
   2  |  get_address.c                                                            |
   3  |                                                                           |
   4  | Get the effective address from an FPU instruction.                        |
   5  |                                                                           |
   6  | Copyright (C) 1992,1993,1994                                              |
   7  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
   8  |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
   9  |                                                                           |
  10  |                                                                           |
  11  +---------------------------------------------------------------------------*/
  12 
  13 /*---------------------------------------------------------------------------+
  14  | Note:                                                                     |
  15  |    The file contains code which accesses user memory.                     |
  16  |    Emulator static data may change when user memory is accessed, due to   |
  17  |    other processes using the emulator while swapping is in progress.      |
  18  +---------------------------------------------------------------------------*/
  19 
  20 
  21 #include <linux/stddef.h>
  22 #include <linux/head.h>
  23 
  24 #include <asm/segment.h>
  25 
  26 #include "fpu_system.h"
  27 #include "exception.h"
  28 #include "fpu_emu.h"
  29 
  30 
  31 #define FPU_WRITE_BIT 0x10
  32 
  33 static int reg_offset[] = {
  34         offsetof(struct info,___eax),
  35         offsetof(struct info,___ecx),
  36         offsetof(struct info,___edx),
  37         offsetof(struct info,___ebx),
  38         offsetof(struct info,___esp),
  39         offsetof(struct info,___ebp),
  40         offsetof(struct info,___esi),
  41         offsetof(struct info,___edi)
  42 };
  43 
  44 #define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
  45 
  46 static int reg_offset_vm86[] = {
  47         offsetof(struct info,___cs),
  48         offsetof(struct info,___vm86_ds),
  49         offsetof(struct info,___vm86_es),
  50         offsetof(struct info,___vm86_fs),
  51         offsetof(struct info,___vm86_gs),
  52         offsetof(struct info,___ss),
  53         offsetof(struct info,___vm86_ds)
  54       };
  55 
  56 #define VM86_REG_(x) (*(unsigned short *) \
  57                       (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info))
  58 
  59 static int reg_offset_pm[] = {
  60         offsetof(struct info,___cs),
  61         offsetof(struct info,___ds),
  62         offsetof(struct info,___es),
  63         offsetof(struct info,___fs),
  64         offsetof(struct info,___gs),
  65         offsetof(struct info,___ss),
  66         offsetof(struct info,___ds)
  67       };
  68 
  69 #define PM_REG_(x) (*(unsigned short *) \
  70                       (reg_offset_pm[((unsigned)x)]+(char *) FPU_info))
  71 
  72 
  73 /* Decode the SIB byte. This function assumes mod != 0 */
  74 static int sib(int mod, unsigned long *fpu_eip)
     /* [previous][next][first][last][top][bottom][index][help] */
  75 {
  76   unsigned char ss,index,base;
  77   long offset;
  78 
  79   RE_ENTRANT_CHECK_OFF;
  80   FPU_code_verify_area(1);
  81   base = get_fs_byte((char *) (*fpu_eip));   /* The SIB byte */
  82   RE_ENTRANT_CHECK_ON;
  83   (*fpu_eip)++;
  84   ss = base >> 6;
  85   index = (base >> 3) & 7;
  86   base &= 7;
  87 
  88   if ((mod == 0) && (base == 5))
  89     offset = 0;              /* No base register */
  90   else
  91     offset = REG_(base);
  92 
  93   if (index == 4)
  94     {
  95       /* No index register */
  96       /* A non-zero ss is illegal */
  97       if ( ss )
  98         EXCEPTION(EX_Invalid);
  99     }
 100   else
 101     {
 102       offset += (REG_(index)) << ss;
 103     }
 104 
 105   if (mod == 1)
 106     {
 107       /* 8 bit signed displacement */
 108       RE_ENTRANT_CHECK_OFF;
 109       FPU_code_verify_area(1);
 110       offset += (signed char) get_fs_byte((char *) (*fpu_eip));
 111       RE_ENTRANT_CHECK_ON;
 112       (*fpu_eip)++;
 113     }
 114   else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
 115     {
 116       /* 32 bit displacement */
 117       RE_ENTRANT_CHECK_OFF;
 118       FPU_code_verify_area(4);
 119       offset += (signed) get_fs_long((unsigned long *) (*fpu_eip));
 120       RE_ENTRANT_CHECK_ON;
 121       (*fpu_eip) += 4;
 122     }
 123 
 124   return offset;
 125 }
 126 
 127 
 128 static unsigned long vm86_segment(unsigned char segment,
     /* [previous][next][first][last][top][bottom][index][help] */
 129                                   unsigned short *selector)
 130 { 
 131   segment--;
 132 #ifdef PARANOID
 133   if ( segment > PREFIX_SS_ )
 134     {
 135       EXCEPTION(EX_INTERNAL|0x130);
 136       math_abort(FPU_info,SIGSEGV);
 137     }
 138 #endif PARANOID
 139   *selector = VM86_REG_(segment);
 140   return (unsigned long)VM86_REG_(segment) << 4;
 141 }
 142 
 143 
 144 /* This should work for 16 and 32 bit protected mode. */
 145 static long pm_address(unsigned char FPU_modrm, unsigned char segment,
     /* [previous][next][first][last][top][bottom][index][help] */
 146                        unsigned short *selector, long offset)
 147 { 
 148   struct desc_struct descriptor;
 149   unsigned long base_address, limit, address, seg_top;
 150 
 151   segment--;
 152 #ifdef PARANOID
 153   if ( segment > PREFIX_SS_ )
 154     {
 155       EXCEPTION(EX_INTERNAL|0x132);
 156       math_abort(FPU_info,SIGSEGV);
 157     }
 158 #endif PARANOID
 159 
 160   *selector = PM_REG_(segment);
 161 
 162   descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
 163   base_address = SEG_BASE_ADDR(descriptor);
 164   address = base_address + offset;
 165   limit = base_address
 166         + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1;
 167   if ( limit < base_address ) limit = 0xffffffff;
 168 
 169   if ( SEG_EXPAND_DOWN(descriptor) )
 170     {
 171       if ( SEG_G_BIT(descriptor) )
 172         seg_top = 0xffffffff;
 173       else
 174         {
 175           seg_top = base_address + (1 << 20);
 176           if ( seg_top < base_address ) seg_top = 0xffffffff;
 177         }
 178       access_limit =
 179         (address <= limit) || (address >= seg_top) ? 0 :
 180           ((seg_top-address) >= 255 ? 255 : seg_top-address);
 181     }
 182   else
 183     {
 184       access_limit =
 185         (address > limit) || (address < base_address) ? 0 :
 186           ((limit-address) >= 254 ? 255 : limit-address+1);
 187     }
 188   if ( SEG_EXECUTE_ONLY(descriptor) ||
 189       (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) )
 190     {
 191       access_limit = 0;
 192     }
 193   return address;
 194 }
 195 
 196 
 197 /*
 198        MOD R/M byte:  MOD == 3 has a special use for the FPU
 199                       SIB byte used iff R/M = 100b
 200 
 201        7   6   5   4   3   2   1   0
 202        .....   .........   .........
 203         MOD    OPCODE(2)     R/M
 204 
 205 
 206        SIB byte
 207 
 208        7   6   5   4   3   2   1   0
 209        .....   .........   .........
 210         SS      INDEX        BASE
 211 
 212 */
 213 
 214 void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
     /* [previous][next][first][last][top][bottom][index][help] */
 215                   struct address *addr,
 216 /*                unsigned short *selector, unsigned long *offset, */
 217                   fpu_addr_modes addr_modes)
 218 {
 219   unsigned char mod;
 220   unsigned rm = FPU_modrm & 7;
 221   long *cpu_reg_ptr;
 222   int address = 0;     /* Initialized just to stop compiler warnings. */
 223 
 224   /* Memory accessed via the cs selector is write protected
 225      in `non-segmented' 32 bit protected mode. */
 226   if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
 227       && (addr_modes.override.segment == PREFIX_CS_) )
 228     {
 229       math_abort(FPU_info,SIGSEGV);
 230     }
 231 
 232   addr->selector = FPU_DS;   /* Default, for 32 bit non-segmented mode. */
 233 
 234   mod = (FPU_modrm >> 6) & 3;
 235 
 236   if (rm == 4 && mod != 3)
 237     {
 238       address = sib(mod, fpu_eip);
 239     }
 240   else
 241     {
 242       cpu_reg_ptr = & REG_(rm);
 243       switch (mod)
 244         {
 245         case 0:
 246           if (rm == 5)
 247             {
 248               /* Special case: disp32 */
 249               RE_ENTRANT_CHECK_OFF;
 250               FPU_code_verify_area(4);
 251               address = get_fs_long((unsigned long *) (*fpu_eip));
 252               (*fpu_eip) += 4;
 253               RE_ENTRANT_CHECK_ON;
 254               addr->offset = address;
 255               return (void *) address;
 256             }
 257           else
 258             {
 259               address = *cpu_reg_ptr;  /* Just return the contents
 260                                           of the cpu register */
 261               addr->offset = address;
 262               return (void *) address;
 263             }
 264         case 1:
 265           /* 8 bit signed displacement */
 266           RE_ENTRANT_CHECK_OFF;
 267           FPU_code_verify_area(1);
 268           address = (signed char) get_fs_byte((char *) (*fpu_eip));
 269           RE_ENTRANT_CHECK_ON;
 270           (*fpu_eip)++;
 271           break;
 272         case 2:
 273           /* 32 bit displacement */
 274           RE_ENTRANT_CHECK_OFF;
 275           FPU_code_verify_area(4);
 276           address = (signed) get_fs_long((unsigned long *) (*fpu_eip));
 277           (*fpu_eip) += 4;
 278           RE_ENTRANT_CHECK_ON;
 279           break;
 280         case 3:
 281           /* Not legal for the FPU */
 282           EXCEPTION(EX_Invalid);
 283         }
 284       address += *cpu_reg_ptr;
 285     }
 286 
 287   addr->offset = address;
 288 
 289   switch ( addr_modes.default_mode )
 290     {
 291     case 0:
 292       break;
 293     case VM86:
 294       address += vm86_segment(addr_modes.override.segment,
 295                               (unsigned short *)&(addr->selector));
 296       break;
 297     case PM16:
 298     case SEG32:
 299       address = pm_address(FPU_modrm, addr_modes.override.segment,
 300                            (unsigned short *)&(addr->selector), address);
 301       break;
 302     default:
 303       EXCEPTION(EX_INTERNAL|0x133);
 304     }
 305 
 306   return (void *)address;
 307 }
 308 
 309 
 310 void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
     /* [previous][next][first][last][top][bottom][index][help] */
 311                      struct address *addr,
 312 /*                   unsigned short *selector, unsigned long *offset, */
 313                      fpu_addr_modes addr_modes)
 314 {
 315   unsigned char mod;
 316   unsigned rm = FPU_modrm & 7;
 317   int address = 0;     /* Default used for mod == 0 */
 318 
 319   /* Memory accessed via the cs selector is write protected
 320      in `non-segmented' 32 bit protected mode. */
 321   if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
 322       && (addr_modes.override.segment == PREFIX_CS_) )
 323     {
 324       math_abort(FPU_info,SIGSEGV);
 325     }
 326 
 327   addr->selector = FPU_DS;   /* Default, for 32 bit non-segmented mode. */
 328 
 329   mod = (FPU_modrm >> 6) & 3;
 330 
 331   switch (mod)
 332     {
 333     case 0:
 334       if (rm == 6)
 335         {
 336           /* Special case: disp16 */
 337           RE_ENTRANT_CHECK_OFF;
 338           FPU_code_verify_area(2);
 339           address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip));
 340           (*fpu_eip) += 2;
 341           RE_ENTRANT_CHECK_ON;
 342           goto add_segment;
 343         }
 344       break;
 345     case 1:
 346       /* 8 bit signed displacement */
 347       RE_ENTRANT_CHECK_OFF;
 348       FPU_code_verify_area(1);
 349       address = (signed char) get_fs_byte((signed char *) (*fpu_eip));
 350       RE_ENTRANT_CHECK_ON;
 351       (*fpu_eip)++;
 352       break;
 353     case 2:
 354       /* 16 bit displacement */
 355       RE_ENTRANT_CHECK_OFF;
 356       FPU_code_verify_area(2);
 357       address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip));
 358       (*fpu_eip) += 2;
 359       RE_ENTRANT_CHECK_ON;
 360       break;
 361     case 3:
 362       /* Not legal for the FPU */
 363       EXCEPTION(EX_Invalid);
 364       break;
 365     }
 366   switch ( rm )
 367     {
 368     case 0:
 369       address += FPU_info->___ebx + FPU_info->___esi;
 370       break;
 371     case 1:
 372       address += FPU_info->___ebx + FPU_info->___edi;
 373       break;
 374     case 2:
 375       address += FPU_info->___ebp + FPU_info->___esi;
 376       if ( addr_modes.override.segment == PREFIX_DEFAULT )
 377         addr_modes.override.segment = PREFIX_SS_;
 378       break;
 379     case 3:
 380       address += FPU_info->___ebp + FPU_info->___edi;
 381       if ( addr_modes.override.segment == PREFIX_DEFAULT )
 382         addr_modes.override.segment = PREFIX_SS_;
 383       break;
 384     case 4:
 385       address += FPU_info->___esi;
 386       break;
 387     case 5:
 388       address += FPU_info->___edi;
 389       break;
 390     case 6:
 391       address += FPU_info->___ebp;
 392       if ( addr_modes.override.segment == PREFIX_DEFAULT )
 393         addr_modes.override.segment = PREFIX_SS_;
 394       break;
 395     case 7:
 396       address += FPU_info->___ebx;
 397       break;
 398     }
 399 
 400  add_segment:
 401   address &= 0xffff;
 402 
 403   addr->offset = address;
 404 
 405   switch ( addr_modes.default_mode )
 406     {
 407     case 0:
 408       break;
 409     case VM86:
 410       address += vm86_segment(addr_modes.override.segment,
 411                               (unsigned short *)&(addr->selector));
 412       break;
 413     case PM16:
 414     case SEG32:
 415       address = pm_address(FPU_modrm, addr_modes.override.segment,
 416                            (unsigned short *)&(addr->selector), address);
 417       break;
 418     default:
 419       EXCEPTION(EX_INTERNAL|0x131);
 420     }
 421 
 422   return (void *)address ;
 423 }

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