home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / arch / i386 / math-emu / get_address.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-19  |  11.0 KB  |  424 lines

  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)
  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,
  129.                   unsigned short *selector)
  130.   segment--;
  131. #ifdef PARANOID
  132.   if ( segment > PREFIX_SS_ )
  133.     {
  134.       EXCEPTION(EX_INTERNAL|0x130);
  135.       math_abort(FPU_info,SIGSEGV);
  136.     }
  137. #endif PARANOID
  138.   *selector = VM86_REG_(segment);
  139.   return (unsigned long)VM86_REG_(segment) << 4;
  140. }
  141.  
  142.  
  143. /* This should work for 16 and 32 bit protected mode. */
  144. static long pm_address(unsigned char FPU_modrm, unsigned char segment,
  145.                unsigned short *selector, long offset)
  146.   struct desc_struct descriptor;
  147.   unsigned long base_address, limit, address, seg_top;
  148.  
  149.   segment--;
  150. #ifdef PARANOID
  151.   if ( segment > PREFIX_SS_ )
  152.     {
  153.       EXCEPTION(EX_INTERNAL|0x132);
  154.       math_abort(FPU_info,SIGSEGV);
  155.     }
  156. #endif PARANOID
  157.  
  158.   *selector = PM_REG_(segment);
  159.  
  160.   descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
  161.   base_address = SEG_BASE_ADDR(descriptor);
  162.   address = base_address + offset;
  163.   limit = base_address
  164.     + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1;
  165.   if ( limit < base_address ) limit = 0xffffffff;
  166.  
  167.   if ( SEG_EXPAND_DOWN(descriptor) )
  168.     {
  169.       if ( SEG_G_BIT(descriptor) )
  170.     seg_top = 0xffffffff;
  171.       else
  172.     {
  173.       seg_top = base_address + (1 << 20);
  174.       if ( seg_top < base_address ) seg_top = 0xffffffff;
  175.     }
  176.       access_limit =
  177.     (address <= limit) || (address >= seg_top) ? 0 :
  178.       ((seg_top-address) >= 255 ? 255 : seg_top-address);
  179.     }
  180.   else
  181.     {
  182.       access_limit =
  183.     (address > limit) || (address < base_address) ? 0 :
  184.       ((limit-address) >= 254 ? 255 : limit-address+1);
  185.     }
  186.   if ( SEG_EXECUTE_ONLY(descriptor) ||
  187.       (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) )
  188.     {
  189.       access_limit = 0;
  190.     }
  191.   return address;
  192. }
  193.  
  194.  
  195. /*
  196.        MOD R/M byte:  MOD == 3 has a special use for the FPU
  197.                       SIB byte used iff R/M = 100b
  198.  
  199.        7   6   5   4   3   2   1   0
  200.        .....   .........   .........
  201.         MOD    OPCODE(2)     R/M
  202.  
  203.  
  204.        SIB byte
  205.  
  206.        7   6   5   4   3   2   1   0
  207.        .....   .........   .........
  208.         SS      INDEX        BASE
  209.  
  210. */
  211.  
  212. void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
  213.           struct address *addr,
  214. /*          unsigned short *selector, unsigned long *offset, */
  215.           fpu_addr_modes addr_modes)
  216. {
  217.   unsigned char mod;
  218.   unsigned rm = FPU_modrm & 7;
  219.   long *cpu_reg_ptr;
  220.   int address = 0;     /* Initialized just to stop compiler warnings. */
  221.  
  222.   /* Memory accessed via the cs selector is write protected
  223.      in `non-segmented' 32 bit protected mode. */
  224.   if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
  225.       && (addr_modes.override.segment == PREFIX_CS_) )
  226.     {
  227.       math_abort(FPU_info,SIGSEGV);
  228.     }
  229.  
  230.   addr->selector = FPU_DS;   /* Default, for 32 bit non-segmented mode. */
  231.  
  232.   mod = (FPU_modrm >> 6) & 3;
  233.  
  234.   if (rm == 4 && mod != 3)
  235.     {
  236.       address = sib(mod, fpu_eip);
  237.     }
  238.   else
  239.     {
  240.       cpu_reg_ptr = & REG_(rm);
  241.       switch (mod)
  242.     {
  243.     case 0:
  244.       if (rm == 5)
  245.         {
  246.           /* Special case: disp32 */
  247.           RE_ENTRANT_CHECK_OFF;
  248.           FPU_code_verify_area(4);
  249.           address = get_fs_long((unsigned long *) (*fpu_eip));
  250.           (*fpu_eip) += 4;
  251.           RE_ENTRANT_CHECK_ON;
  252.           addr->offset = address;
  253.           return (void *) address;
  254.         }
  255.       else
  256.         {
  257.           address = *cpu_reg_ptr;  /* Just return the contents
  258.                       of the cpu register */
  259.           addr->offset = address;
  260.           return (void *) address;
  261.         }
  262.     case 1:
  263.       /* 8 bit signed displacement */
  264.       RE_ENTRANT_CHECK_OFF;
  265.       FPU_code_verify_area(1);
  266.       address = (signed char) get_fs_byte((char *) (*fpu_eip));
  267.       RE_ENTRANT_CHECK_ON;
  268.       (*fpu_eip)++;
  269.       break;
  270.     case 2:
  271.       /* 32 bit displacement */
  272.       RE_ENTRANT_CHECK_OFF;
  273.       FPU_code_verify_area(4);
  274.       address = (signed) get_fs_long((unsigned long *) (*fpu_eip));
  275.       (*fpu_eip) += 4;
  276.       RE_ENTRANT_CHECK_ON;
  277.       break;
  278.     case 3:
  279.       /* Not legal for the FPU */
  280.       EXCEPTION(EX_Invalid);
  281.     }
  282.       address += *cpu_reg_ptr;
  283.     }
  284.  
  285.   addr->offset = address;
  286.  
  287.   switch ( addr_modes.default_mode )
  288.     {
  289.     case 0:
  290.       break;
  291.     case VM86:
  292.       address += vm86_segment(addr_modes.override.segment,
  293.                   (unsigned short *)&(addr->selector));
  294.       break;
  295.     case PM16:
  296.     case SEG32:
  297.       address = pm_address(FPU_modrm, addr_modes.override.segment,
  298.                (unsigned short *)&(addr->selector), address);
  299.       break;
  300.     default:
  301.       EXCEPTION(EX_INTERNAL|0x133);
  302.     }
  303.  
  304.   return (void *)address;
  305. }
  306.  
  307.  
  308. void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
  309.              struct address *addr,
  310. /*             unsigned short *selector, unsigned long *offset, */
  311.              fpu_addr_modes addr_modes)
  312. {
  313.   unsigned char mod;
  314.   unsigned rm = FPU_modrm & 7;
  315.   int address = 0;     /* Default used for mod == 0 */
  316.  
  317.   /* Memory accessed via the cs selector is write protected
  318.      in `non-segmented' 32 bit protected mode. */
  319.   if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
  320.       && (addr_modes.override.segment == PREFIX_CS_) )
  321.     {
  322.       math_abort(FPU_info,SIGSEGV);
  323.     }
  324.  
  325.   addr->selector = FPU_DS;   /* Default, for 32 bit non-segmented mode. */
  326.  
  327.   mod = (FPU_modrm >> 6) & 3;
  328.  
  329.   switch (mod)
  330.     {
  331.     case 0:
  332.       if (rm == 6)
  333.     {
  334.       /* Special case: disp16 */
  335.       RE_ENTRANT_CHECK_OFF;
  336.       FPU_code_verify_area(2);
  337.       address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip));
  338.       (*fpu_eip) += 2;
  339.       RE_ENTRANT_CHECK_ON;
  340.       goto add_segment;
  341.     }
  342.       break;
  343.     case 1:
  344.       /* 8 bit signed displacement */
  345.       RE_ENTRANT_CHECK_OFF;
  346.       FPU_code_verify_area(1);
  347.       address = (signed char) get_fs_byte((signed char *) (*fpu_eip));
  348.       RE_ENTRANT_CHECK_ON;
  349.       (*fpu_eip)++;
  350.       break;
  351.     case 2:
  352.       /* 16 bit displacement */
  353.       RE_ENTRANT_CHECK_OFF;
  354.       FPU_code_verify_area(2);
  355.       address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip));
  356.       (*fpu_eip) += 2;
  357.       RE_ENTRANT_CHECK_ON;
  358.       break;
  359.     case 3:
  360.       /* Not legal for the FPU */
  361.       EXCEPTION(EX_Invalid);
  362.       break;
  363.     }
  364.   switch ( rm )
  365.     {
  366.     case 0:
  367.       address += FPU_info->___ebx + FPU_info->___esi;
  368.       break;
  369.     case 1:
  370.       address += FPU_info->___ebx + FPU_info->___edi;
  371.       break;
  372.     case 2:
  373.       address += FPU_info->___ebp + FPU_info->___esi;
  374.       if ( addr_modes.override.segment == PREFIX_DEFAULT )
  375.     addr_modes.override.segment = PREFIX_SS_;
  376.       break;
  377.     case 3:
  378.       address += FPU_info->___ebp + FPU_info->___edi;
  379.       if ( addr_modes.override.segment == PREFIX_DEFAULT )
  380.     addr_modes.override.segment = PREFIX_SS_;
  381.       break;
  382.     case 4:
  383.       address += FPU_info->___esi;
  384.       break;
  385.     case 5:
  386.       address += FPU_info->___edi;
  387.       break;
  388.     case 6:
  389.       address += FPU_info->___ebp;
  390.       if ( addr_modes.override.segment == PREFIX_DEFAULT )
  391.     addr_modes.override.segment = PREFIX_SS_;
  392.       break;
  393.     case 7:
  394.       address += FPU_info->___ebx;
  395.       break;
  396.     }
  397.  
  398.  add_segment:
  399.   address &= 0xffff;
  400.  
  401.   addr->offset = address;
  402.  
  403.   switch ( addr_modes.default_mode )
  404.     {
  405.     case 0:
  406.       break;
  407.     case VM86:
  408.       address += vm86_segment(addr_modes.override.segment,
  409.                   (unsigned short *)&(addr->selector));
  410.       break;
  411.     case PM16:
  412.     case SEG32:
  413.       address = pm_address(FPU_modrm, addr_modes.override.segment,
  414.                (unsigned short *)&(addr->selector), address);
  415.       break;
  416.     default:
  417.       EXCEPTION(EX_INTERNAL|0x131);
  418.     }
  419.  
  420.   return (void *)address ;
  421. }
  422.