ia64/xen-unstable

changeset 8447:5fa0c70663f1

Generic x86 emulator now properly supports 16-bit addressing
by narrowing accesses to SI/DI/SP registers. Also supports
real-mode addressing by shifting and adding the appropriate
segment register for certain stack and string operations
where the effective address is not already known.

todo: Stack operations should have address size specified
by B bit in stack segment descriptor, not by default address
size or address-size override. Probably nothing depends on
the proper behaviour though.

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri Dec 23 18:28:33 2005 +0100 (2005-12-23)
parents 829517be689f
children 7c1f2e20123a
files xen/arch/x86/x86_emulate.c xen/include/asm-x86/x86_emulate.h
line diff
     1.1 --- a/xen/arch/x86/x86_emulate.c	Fri Dec 23 16:42:46 2005 +0100
     1.2 +++ b/xen/arch/x86/x86_emulate.c	Fri Dec 23 18:28:33 2005 +0100
     1.3 @@ -371,6 +371,21 @@ do{ __asm__ __volatile__ (              
     1.4     (_type)_x; \
     1.5  })
     1.6  
     1.7 +/* Access/update address held in a register, based on addressing mode. */
     1.8 +#define register_address(sel, reg)                                      \
     1.9 +    ((ad_bytes == sizeof(unsigned long)) ? (reg) :                      \
    1.10 +     ((mode == X86EMUL_MODE_REAL) ? /* implies ad_bytes == 2 */         \
    1.11 +      (((unsigned long)(sel) << 4) + ((reg) & 0xffff)) :                \
    1.12 +      ((reg) & ((1UL << (ad_bytes << 3)) - 1))))
    1.13 +#define register_address_increment(reg, inc)                            \
    1.14 +do {                                                                    \
    1.15 +    if ( ad_bytes == sizeof(unsigned long) )                            \
    1.16 +        (reg) += (inc);                                                 \
    1.17 +    else                                                                \
    1.18 +        (reg) = ((reg) & ~((1UL << (ad_bytes << 3)) - 1)) |             \
    1.19 +                (((reg) + (inc)) & ((1UL << (ad_bytes << 3)) - 1));     \
    1.20 +} while (0)
    1.21 +
    1.22  void *
    1.23  decode_register(
    1.24      uint8_t modrm_reg, struct cpu_user_regs *regs, int highbyte_regs)
    1.25 @@ -420,32 +435,64 @@ x86_emulate_memop(
    1.26  {
    1.27      uint8_t b, d, sib, twobyte = 0, rex_prefix = 0;
    1.28      uint8_t modrm, modrm_mod = 0, modrm_reg = 0, modrm_rm = 0;
    1.29 -    unsigned int op_bytes = (mode == 8) ? 4 : mode, ad_bytes = mode;
    1.30 -    unsigned int lock_prefix = 0, rep_prefix = 0, i;
    1.31 +    uint16_t *seg = NULL; /* override segment */
    1.32 +    unsigned int op_bytes, ad_bytes, lock_prefix = 0, rep_prefix = 0, i;
    1.33      int rc = 0;
    1.34      struct operand src, dst;
    1.35  
    1.36      /* Shadow copy of register state. Committed on successful emulation. */
    1.37      struct cpu_user_regs _regs = *regs;
    1.38  
    1.39 +    switch ( mode )
    1.40 +    {
    1.41 +    case X86EMUL_MODE_REAL:
    1.42 +    case X86EMUL_MODE_PROT16:
    1.43 +        op_bytes = ad_bytes = 2;
    1.44 +        break;
    1.45 +    case X86EMUL_MODE_PROT32:
    1.46 +        op_bytes = ad_bytes = 4;
    1.47 +        break;
    1.48 +#ifdef __x86_64__
    1.49 +    case X86EMUL_MODE_PROT64:
    1.50 +        op_bytes = 4;
    1.51 +        ad_bytes = 8;
    1.52 +        break;
    1.53 +#endif
    1.54 +    default:
    1.55 +        return -1;
    1.56 +    }
    1.57 +
    1.58      /* Legacy prefixes. */
    1.59      for ( i = 0; i < 8; i++ )
    1.60      {
    1.61          switch ( b = insn_fetch(uint8_t, 1, _regs.eip) )
    1.62          {
    1.63          case 0x66: /* operand-size override */
    1.64 -            op_bytes ^= 6;                    /* switch between 2/4 bytes */
    1.65 +            op_bytes ^= 6;      /* switch between 2/4 bytes */
    1.66              break;
    1.67          case 0x67: /* address-size override */
    1.68 -            ad_bytes ^= (mode == 8) ? 12 : 6; /* switch between 2/4/8 bytes */
    1.69 +            if ( mode == X86EMUL_MODE_PROT64 )
    1.70 +                ad_bytes ^= 12; /* switch between 4/8 bytes */
    1.71 +            else
    1.72 +                ad_bytes ^= 6;  /* switch between 2/4 bytes */
    1.73              break;
    1.74          case 0x2e: /* CS override */
    1.75 +            seg = &_regs.cs;
    1.76 +            break;
    1.77          case 0x3e: /* DS override */
    1.78 +            seg = &_regs.ds;
    1.79 +            break;
    1.80          case 0x26: /* ES override */
    1.81 +            seg = &_regs.es;
    1.82 +            break;
    1.83          case 0x64: /* FS override */
    1.84 +            seg = &_regs.fs;
    1.85 +            break;
    1.86          case 0x65: /* GS override */
    1.87 +            seg = &_regs.gs;
    1.88 +            break;
    1.89          case 0x36: /* SS override */
    1.90 -            DPRINTF("Warning: ignoring a segment override.\n");
    1.91 +            seg = &_regs.ss;
    1.92              break;
    1.93          case 0xf0: /* LOCK */
    1.94              lock_prefix = 1;
    1.95 @@ -461,8 +508,12 @@ x86_emulate_memop(
    1.96      }
    1.97   done_prefixes:
    1.98  
    1.99 +    /* Note quite the same as 80386 real mode, but hopefully good enough. */
   1.100 +    if ( (mode == X86EMUL_MODE_REAL) && (ad_bytes != 2) )
   1.101 +        goto cannot_emulate;
   1.102 +
   1.103      /* REX prefix. */
   1.104 -    if ( (mode == 8) && ((b & 0xf0) == 0x40) )
   1.105 +    if ( (mode == X86EMUL_MODE_PROT64) && ((b & 0xf0) == 0x40) )
   1.106      {
   1.107          rex_prefix = b;
   1.108          if ( b & 8 )
   1.109 @@ -674,7 +725,7 @@ x86_emulate_memop(
   1.110          emulate_2op_SrcV("cmp", src, dst, _regs.eflags);
   1.111          break;
   1.112      case 0x63: /* movsxd */
   1.113 -        if ( mode != 8 ) /* x86/64 long mode only */
   1.114 +        if ( mode != X86EMUL_MODE_PROT64 )
   1.115              goto cannot_emulate;
   1.116          dst.val = (int32_t)src.val;
   1.117          break;
   1.118 @@ -721,12 +772,13 @@ x86_emulate_memop(
   1.119          dst.val = src.val;
   1.120          break;
   1.121      case 0x8f: /* pop (sole member of Grp1a) */
   1.122 -        /* 64-bit mode: POP defaults to 64-bit operands. */
   1.123 -        if ( (mode == 8) && (dst.bytes == 4) )
   1.124 +        /* 64-bit mode: POP always pops a 64-bit operand. */
   1.125 +        if ( mode == X86EMUL_MODE_PROT64 )
   1.126              dst.bytes = 8;
   1.127 -        if ( (rc = ops->read_std(_regs.esp, &dst.val, dst.bytes)) != 0 )
   1.128 +        if ( (rc = ops->read_std(register_address(_regs.ss, _regs.esp),
   1.129 +                                 &dst.val, dst.bytes)) != 0 )
   1.130              goto done;
   1.131 -        _regs.esp += dst.bytes;
   1.132 +        register_address_increment(_regs.esp, dst.bytes);
   1.133          break;
   1.134      case 0xc0 ... 0xc1: grp2: /* Grp2 */
   1.135          switch ( modrm_reg )
   1.136 @@ -797,16 +849,17 @@ x86_emulate_memop(
   1.137              emulate_1op("dec", dst, _regs.eflags);
   1.138              break;
   1.139          case 6: /* push */
   1.140 -            /* 64-bit mode: PUSH defaults to 64-bit operands. */
   1.141 -            if ( (mode == 8) && (dst.bytes == 4) )
   1.142 +            /* 64-bit mode: PUSH always pushes a 64-bit operand. */
   1.143 +            if ( mode == X86EMUL_MODE_PROT64 )
   1.144              {
   1.145                  dst.bytes = 8;
   1.146                  if ( (rc = ops->read_std((unsigned long)dst.ptr,
   1.147                                           &dst.val, 8)) != 0 )
   1.148                      goto done;
   1.149              }
   1.150 -            _regs.esp -= dst.bytes;
   1.151 -            if ( (rc = ops->write_std(_regs.esp, dst.val, dst.bytes)) != 0 )
   1.152 +            register_address_increment(_regs.esp, -dst.bytes);
   1.153 +            if ( (rc = ops->write_std(register_address(_regs.ss, _regs.esp),
   1.154 +                                      dst.val, dst.bytes)) != 0 )
   1.155                  goto done;
   1.156              dst.val = dst.orig_val; /* skanky: disable writeback */
   1.157              break;
   1.158 @@ -873,19 +926,22 @@ x86_emulate_memop(
   1.159          {
   1.160              /* Write fault: destination is special memory. */
   1.161              dst.ptr = (unsigned long *)cr2;
   1.162 -            if ( (rc = ops->read_std(_regs.esi - _regs.edi + cr2, 
   1.163 +            if ( (rc = ops->read_std(register_address(seg ? *seg : _regs.ds,
   1.164 +                                                      _regs.esi),
   1.165                                       &dst.val, dst.bytes)) != 0 )
   1.166                  goto done;
   1.167          }
   1.168          else
   1.169          {
   1.170              /* Read fault: source is special memory. */
   1.171 -            dst.ptr = (unsigned long *)(_regs.edi - _regs.esi + cr2);
   1.172 +            dst.ptr = (unsigned long *)register_address(_regs.es, _regs.edi);
   1.173              if ( (rc = ops->read_emulated(cr2, &dst.val, dst.bytes)) != 0 )
   1.174                  goto done;
   1.175          }
   1.176 -        _regs.esi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes;
   1.177 -        _regs.edi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes;
   1.178 +        register_address_increment(
   1.179 +            _regs.esi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
   1.180 +        register_address_increment(
   1.181 +            _regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
   1.182          break;
   1.183      case 0xa6 ... 0xa7: /* cmps */
   1.184          DPRINTF("Urk! I don't handle CMPS.\n");
   1.185 @@ -895,7 +951,8 @@ x86_emulate_memop(
   1.186          dst.bytes = (d & ByteOp) ? 1 : op_bytes;
   1.187          dst.ptr   = (unsigned long *)cr2;
   1.188          dst.val   = _regs.eax;
   1.189 -        _regs.edi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes;
   1.190 +        register_address_increment(
   1.191 +            _regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
   1.192          break;
   1.193      case 0xac ... 0xad: /* lods */
   1.194          dst.type  = OP_REG;
   1.195 @@ -903,7 +960,8 @@ x86_emulate_memop(
   1.196          dst.ptr   = (unsigned long *)&_regs.eax;
   1.197          if ( (rc = ops->read_emulated(cr2, &dst.val, dst.bytes)) != 0 )
   1.198              goto done;
   1.199 -        _regs.esi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes;
   1.200 +        register_address_increment(
   1.201 +            _regs.esi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
   1.202          break;
   1.203      case 0xae ... 0xaf: /* scas */
   1.204          DPRINTF("Urk! I don't handle SCAS.\n");
     2.1 --- a/xen/include/asm-x86/x86_emulate.h	Fri Dec 23 16:42:46 2005 +0100
     2.2 +++ b/xen/include/asm-x86/x86_emulate.h	Fri Dec 23 18:28:33 2005 +0100
     2.3 @@ -141,6 +141,12 @@ x86_emulate_write_std(
     2.4  
     2.5  struct cpu_user_regs;
     2.6  
     2.7 +/* Current execution mode, passed to the emulator. */
     2.8 +#define X86EMUL_MODE_REAL     0
     2.9 +#define X86EMUL_MODE_PROT16   2
    2.10 +#define X86EMUL_MODE_PROT32   4
    2.11 +#define X86EMUL_MODE_PROT64   8
    2.12 +
    2.13  /*
    2.14   * x86_emulate_memop: Emulate an instruction that faulted attempting to
    2.15   *                    read/write a 'special' memory area.
    2.16 @@ -149,6 +155,8 @@ struct cpu_user_regs;
    2.17   *  @ops:  Interface to access special memory.
    2.18   *  @mode: Current execution mode, represented by the default size of memory
    2.19   *         addresses, in bytes. Valid values are 2, 4 and 8 (x86/64 only).
    2.20 + *         Alternatively use the appropriate X86EMUL_MODE value (which also
    2.21 + *         includes a value for emulating real mode).
    2.22   */
    2.23  extern int
    2.24  x86_emulate_memop(