[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] [XEN] Emulate relative near/short jumps, including Jcc.



# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Date 1168271573 0
# Node ID f240c09f08d218a253e08f9fbdd97c7c50e8cc35
# Parent  5eeb4bac1b173d972f16f031fc8fa264c186cc5a
[XEN] Emulate relative near/short jumps, including Jcc.
Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>
---
 xen/arch/x86/x86_emulate.c |  221 ++++++++++++++++++++++++++++++++++-----------
 1 files changed, 168 insertions(+), 53 deletions(-)

diff -r 5eeb4bac1b17 -r f240c09f08d2 xen/arch/x86/x86_emulate.c
--- a/xen/arch/x86/x86_emulate.c        Mon Jan 08 14:26:57 2007 +0000
+++ b/xen/arch/x86/x86_emulate.c        Mon Jan 08 15:52:53 2007 +0000
@@ -88,8 +88,12 @@ static uint8_t opcode_table[256] = {
     /* 0x60 - 0x6F */
     0, 0, 0, DstReg|SrcMem32|ModRM|Mov /* movsxd (x86/64) */,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    /* 0x70 - 0x7F */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* 0x70 - 0x77 */
+    ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
+    ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
+    /* 0x78 - 0x7F */
+    ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
+    ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
     /* 0x80 - 0x87 */
     ByteOp|DstMem|SrcImm|ModRM, DstMem|SrcImm|ModRM,
     ByteOp|DstMem|SrcImm|ModRM, DstMem|SrcImmByte|ModRM,
@@ -130,8 +134,10 @@ static uint8_t opcode_table[256] = {
     0, 0, 0, 0,
     /* 0xD8 - 0xDF */
     0, 0, 0, 0, 0, 0, 0, 0,
-    /* 0xE0 - 0xEF */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* 0xE0 - 0xE7 */
+    0, 0, 0, ImplicitOps, 0, 0, 0, 0,
+    /* 0xE8 - 0xEF */
+    0, ImplicitOps, 0, ImplicitOps, 0, 0, 0, 0,
     /* 0xF0 - 0xF7 */
     0, 0, 0, 0,
     0, ImplicitOps, ByteOp|DstMem|SrcNone|ModRM, DstMem|SrcNone|ModRM,
@@ -165,8 +171,12 @@ static uint8_t twobyte_table[256] = {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     /* 0x70 - 0x7F */
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    /* 0x80 - 0x8F */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* 0x80 - 0x87 */
+    ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
+    ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
+    /* 0x88 - 0x8F */
+    ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
+    ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
     /* 0x90 - 0x9F */
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     /* 0xA0 - 0xA7 */
@@ -380,31 +390,82 @@ do{ __asm__ __volatile__ (              
 
 /* Fetch next part of the instruction being emulated. */
 #define insn_fetch_bytes(_size)                                         \
-({ unsigned long _x;                                                    \
-   rc = ops->insn_fetch(x86_seg_cs, _regs.eip, &_x, (_size), ctxt);     \
+({ unsigned long _x, _eip = _truncate_ea(_regs.eip, def_ad_bytes);      \
+   rc = ops->insn_fetch(x86_seg_cs, _eip, &_x, (_size), ctxt);          \
    if ( rc != 0 )                                                       \
        goto done;                                                       \
-   _regs.eip += (_size);                                                \
+   _register_address_increment(_regs.eip, (_size), def_ad_bytes);       \
    _x;                                                                  \
 })
 #define insn_fetch_type(_type) ((_type)insn_fetch_bytes(sizeof(_type)))
 
-#define truncate_ea(ea)                                 \
+#define _truncate_ea(ea, byte_width)                    \
 ({  unsigned long __ea = (ea);                          \
-    ((ad_bytes == sizeof(unsigned long)) ? __ea :       \
-     (__ea & ((1UL << (ad_bytes << 3)) - 1)));          \
+    (((byte_width) == sizeof(unsigned long)) ? __ea :   \
+     (__ea & ((1UL << ((byte_width) << 3)) - 1)));      \
 })
+#define truncate_ea(ea) _truncate_ea((ea), ad_bytes)
 
 /* Update address held in a register, based on addressing mode. */
-#define register_address_increment(reg, inc)                            \
+#define _register_address_increment(reg, inc, byte_width)               \
 do {                                                                    \
     int _inc = (inc); /* signed type ensures sign extension to long */  \
-    if ( ad_bytes == sizeof(unsigned long) )                            \
+    if ( (byte_width) == sizeof(unsigned long) )                        \
         (reg) += _inc;                                                  \
+    else if ( mode == X86EMUL_MODE_PROT64 )                             \
+        (reg) = ((reg) + _inc) & ((1UL << ((byte_width) << 3)) - 1);    \
     else                                                                \
-        (reg) = ((reg) & ~((1UL << (ad_bytes << 3)) - 1)) |             \
-                (((reg) + _inc) & ((1UL << (ad_bytes << 3)) - 1));      \
+        (reg) = ((reg) & ~((1UL << ((byte_width) << 3)) - 1)) |         \
+                (((reg) + _inc) & ((1UL << ((byte_width) << 3)) - 1));  \
 } while (0)
+#define register_address_increment(reg, inc) \
+    _register_address_increment((reg), (inc), ad_bytes)
+
+#define jmp_rel(rel)                                                    \
+do {                                                                    \
+    _regs.eip += (int)(rel);                                            \
+    if ( mode != X86EMUL_MODE_PROT64 )                                  \
+        _regs.eip = ((op_bytes == 2)                                    \
+                     ? (uint16_t)_regs.eip : (uint32_t)_regs.eip);      \
+} while (0)
+
+static int
+test_cc(
+    unsigned int condition, unsigned int flags)
+{
+    int rc = 0;
+
+    switch ( (condition & 15) >> 1 )
+    {
+    case 0: /* o */
+        rc |= (flags & EFLG_OF);
+        break;
+    case 1: /* b/c/nae */
+        rc |= (flags & EFLG_CF);
+        break;
+    case 2: /* z/e */
+        rc |= (flags & EFLG_ZF);
+        break;
+    case 3: /* be/na */
+        rc |= (flags & (EFLG_CF|EFLG_ZF));
+        break;
+    case 4: /* s */
+        rc |= (flags & EFLG_SF);
+        break;
+    case 5: /* p/pe */
+        rc |= (flags & EFLG_PF);
+        break;
+    case 7: /* le/ng */
+        rc |= (flags & EFLG_ZF);
+        /* fall through */
+    case 6: /* l/nge */
+        rc |= (!(flags & EFLG_SF) != !(flags & EFLG_OF));
+        break;
+    }
+
+    /* Odd condition identifiers (lsb == 1) have inverted sense. */
+    return (!!rc ^ (condition & 1));
+}
 
 void *
 decode_register(
@@ -456,7 +517,8 @@ x86_emulate(
 
     uint8_t b, d, sib, sib_index, sib_base, twobyte = 0, rex_prefix = 0;
     uint8_t modrm, modrm_mod = 0, modrm_reg = 0, modrm_rm = 0;
-    unsigned int op_bytes, ad_bytes, lock_prefix = 0, rep_prefix = 0, i;
+    unsigned int op_bytes, ad_bytes, def_ad_bytes;
+    unsigned int lock_prefix = 0, rep_prefix = 0, i;
     int rc = 0;
     struct operand src, dst;
     int mode = ctxt->mode;
@@ -473,20 +535,22 @@ x86_emulate(
     {
     case X86EMUL_MODE_REAL:
     case X86EMUL_MODE_PROT16:
-        op_bytes = ad_bytes = 2;
+        op_bytes = def_ad_bytes = 2;
         break;
     case X86EMUL_MODE_PROT32:
-        op_bytes = ad_bytes = 4;
+        op_bytes = def_ad_bytes = 4;
         break;
 #ifdef __x86_64__
     case X86EMUL_MODE_PROT64:
         op_bytes = 4;
-        ad_bytes = 8;
+        def_ad_bytes = 8;
         break;
 #endif
     default:
         return -1;
     }
+
+    ad_bytes = def_ad_bytes;
 
     /* Prefix bytes. */
     for ( i = 0; i < 8; i++ )
@@ -820,53 +884,62 @@ x86_emulate(
     case 0x00 ... 0x03: add: /* add */
         emulate_2op_SrcV("add", src, dst, _regs.eflags);
         break;
+
     case 0x0c ... 0x0d: /* or imm,%%eax */
         dst.reg = (unsigned long *)&_regs.eax;
         dst.val = dst.orig_val = _regs.eax;
     case 0x08 ... 0x0b: or:  /* or */
         emulate_2op_SrcV("or", src, dst, _regs.eflags);
         break;
+
     case 0x14 ... 0x15: /* adc imm,%%eax */
         dst.reg = (unsigned long *)&_regs.eax;
         dst.val = dst.orig_val = _regs.eax;
     case 0x10 ... 0x13: adc: /* adc */
         emulate_2op_SrcV("adc", src, dst, _regs.eflags);
         break;
+
     case 0x1c ... 0x1d: /* sbb imm,%%eax */
         dst.reg = (unsigned long *)&_regs.eax;
         dst.val = dst.orig_val = _regs.eax;
     case 0x18 ... 0x1b: sbb: /* sbb */
         emulate_2op_SrcV("sbb", src, dst, _regs.eflags);
         break;
+
     case 0x24 ... 0x25: /* and imm,%%eax */
         dst.reg = (unsigned long *)&_regs.eax;
         dst.val = dst.orig_val = _regs.eax;
     case 0x20 ... 0x23: and: /* and */
         emulate_2op_SrcV("and", src, dst, _regs.eflags);
         break;
+
     case 0x2c ... 0x2d: /* sub imm,%%eax */
         dst.reg = (unsigned long *)&_regs.eax;
         dst.val = dst.orig_val = _regs.eax;
     case 0x28 ... 0x2b: sub: /* sub */
         emulate_2op_SrcV("sub", src, dst, _regs.eflags);
         break;
+
     case 0x34 ... 0x35: /* xor imm,%%eax */
         dst.reg = (unsigned long *)&_regs.eax;
         dst.val = dst.orig_val = _regs.eax;
     case 0x30 ... 0x33: xor: /* xor */
         emulate_2op_SrcV("xor", src, dst, _regs.eflags);
         break;
+
     case 0x3c ... 0x3d: /* cmp imm,%%eax */
         dst.reg = (unsigned long *)&_regs.eax;
         dst.val = dst.orig_val = _regs.eax;
     case 0x38 ... 0x3b: cmp: /* cmp */
         emulate_2op_SrcV("cmp", src, dst, _regs.eflags);
         break;
+
     case 0x63: /* movsxd */
         if ( mode != X86EMUL_MODE_PROT64 )
             goto cannot_emulate;
         dst.val = (int32_t)src.val;
         break;
+
     case 0x80 ... 0x83: /* Grp1 */
         switch ( modrm_reg & 7 )
         {
@@ -880,9 +953,11 @@ x86_emulate(
         case 7: goto cmp;
         }
         break;
+
     case 0x84 ... 0x85: test: /* test */
         emulate_2op_SrcV("test", src, dst, _regs.eflags);
         break;
+
     case 0x86 ... 0x87: xchg: /* xchg */
         /* Write back the register source. */
         switch ( dst.bytes )
@@ -896,13 +971,16 @@ x86_emulate(
         dst.val = src.val;
         lock_prefix = 1;
         break;
+
     case 0x88 ... 0x8b: /* mov */
     case 0xc6 ... 0xc7: /* mov (sole member of Grp11) */
         dst.val = src.val;
         break;
+
     case 0x8d: /* lea */
         dst.val = ea.mem.off;
         break;
+
     case 0x8f: /* pop (sole member of Grp1a) */
         /* 64-bit mode: POP defaults to a 64-bit operand. */
         if ( (mode == X86EMUL_MODE_PROT64) && (dst.bytes == 4) )
@@ -912,11 +990,13 @@ x86_emulate(
             goto done;
         register_address_increment(_regs.esp, dst.bytes);
         break;
+
     case 0xb0 ... 0xb7: /* mov imm8,r8 */
         dst.reg = decode_register(
             (b & 7) | ((rex_prefix & 1) << 3), &_regs, (rex_prefix == 0));
         dst.val = src.val;
         break;
+
     case 0xb8 ... 0xbf: /* mov imm{16,32,64},r{16,32,64} */
         if ( dst.bytes == 8 ) /* Fetch more bytes to obtain imm64 */
             src.val = ((uint32_t)src.val |
@@ -925,6 +1005,7 @@ x86_emulate(
             (b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
         dst.val = src.val;
         break;
+
     case 0xc0 ... 0xc1: grp2: /* Grp2 */
         switch ( modrm_reg & 7 )
         {
@@ -952,12 +1033,15 @@ x86_emulate(
             break;
         }
         break;
+
     case 0xd0 ... 0xd1: /* Grp2 */
         src.val = 1;
         goto grp2;
+
     case 0xd2 ... 0xd3: /* Grp2 */
         src.val = _regs.ecx;
         goto grp2;
+
     case 0xf6 ... 0xf7: /* Grp3 */
         switch ( modrm_reg & 7 )
         {
@@ -983,6 +1067,7 @@ x86_emulate(
             goto cannot_emulate;
         }
         break;
+
     case 0xfe ... 0xff: /* Grp4/Grp5 */
         switch ( modrm_reg & 7 )
         {
@@ -1052,8 +1137,10 @@ x86_emulate(
  special_insn:
     /* Default action: disable writeback. There may be no dest operand. */
     dst.orig_val = dst.val;
+
     if ( twobyte )
         goto twobyte_special_insn;
+
     if ( rep_prefix )
     {
         if ( _regs.ecx == 0 )
@@ -1064,6 +1151,7 @@ x86_emulate(
         _regs.ecx--;
         _regs.eip = ctxt->regs->eip;
     }
+
     switch ( b )
     {
     case 0x40 ... 0x4f: /* inc/dec reg */
@@ -1076,6 +1164,7 @@ x86_emulate(
         else
             emulate_1op("inc", dst, _regs.eflags);
         break;
+
     case 0x50 ... 0x57: /* push reg */
         dst.type  = OP_MEM;
         dst.bytes = op_bytes;
@@ -1087,6 +1176,7 @@ x86_emulate(
         dst.mem.seg = x86_seg_ss;
         dst.mem.off = truncate_ea(_regs.esp);
         break;
+
     case 0x58 ... 0x5f: /* pop reg */
         dst.type  = OP_REG;
         dst.reg   = decode_register(
@@ -1099,9 +1189,18 @@ x86_emulate(
             goto done;
         register_address_increment(_regs.esp, dst.bytes);
         break;
+
+    case 0x70 ... 0x7f: /* jcc (short) */ {
+        int rel = insn_fetch_type(int8_t);
+        if ( test_cc(b, _regs.eflags) )
+            jmp_rel(rel);
+        break;
+    }
+
     case 0x90: /* nop / xchg %%r8,%%rax */
         if ( !(rex_prefix & 1) )
             break; /* nop */
+
     case 0x91 ... 0x97: /* xchg reg,%%rax */
         src.type = dst.type = OP_REG;
         src.bytes = dst.bytes = op_bytes;
@@ -1111,6 +1210,7 @@ x86_emulate(
             (b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
         dst.val  = dst.orig_val = *dst.reg;
         goto xchg;
+
     case 0xa0 ... 0xa1: /* mov mem.offs,{%al,%ax,%eax,%rax} */
         /* Source EA is not encoded via ModRM. */
         dst.type  = OP_REG;
@@ -1120,6 +1220,7 @@ x86_emulate(
                              &dst.val, dst.bytes, ctxt)) != 0 )
             goto done;
         break;
+
     case 0xa2 ... 0xa3: /* mov {%al,%ax,%eax,%rax},mem.offs */
         /* Destination EA is not encoded via ModRM. */
         dst.type  = OP_MEM;
@@ -1128,6 +1229,7 @@ x86_emulate(
         dst.bytes = (d & ByteOp) ? 1 : op_bytes;
         dst.val   = (unsigned long)_regs.eax;
         break;
+
     case 0xa4 ... 0xa5: /* movs */
         dst.type  = OP_MEM;
         dst.bytes = (d & ByteOp) ? 1 : op_bytes;
@@ -1141,6 +1243,7 @@ x86_emulate(
         register_address_increment(
             _regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
         break;
+
     case 0xaa ... 0xab: /* stos */
         dst.type  = OP_MEM;
         dst.bytes = (d & ByteOp) ? 1 : op_bytes;
@@ -1150,6 +1253,7 @@ x86_emulate(
         register_address_increment(
             _regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
         break;
+
     case 0xac ... 0xad: /* lods */
         dst.type  = OP_REG;
         dst.bytes = (d & ByteOp) ? 1 : op_bytes;
@@ -1160,18 +1264,40 @@ x86_emulate(
         register_address_increment(
             _regs.esi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
         break;
+
+    case 0xe3: /* jcxz/jecxz (short) */ {
+        int rel = insn_fetch_type(int8_t);
+        if ( (ad_bytes == 2) ? !(uint16_t)_regs.ecx :
+             (ad_bytes == 4) ? !(uint32_t)_regs.ecx : !_regs.ecx )
+            jmp_rel(rel);
+        break;
+    }
+
+    case 0xe9: /* jmp (short) */
+        jmp_rel(insn_fetch_type(int8_t));
+        break;
+
+    case 0xeb: /* jmp (near) */
+        jmp_rel(insn_fetch_bytes(
+            (mode == X86EMUL_MODE_PROT64) ? 4 : op_bytes));
+        break;
+
     case 0xf5: /* cmc */
         _regs.eflags ^= EFLG_CF;
         break;
+
     case 0xf8: /* clc */
         _regs.eflags &= ~EFLG_CF;
         break;
+
     case 0xf9: /* stc */
         _regs.eflags |= EFLG_CF;
         break;
+
     case 0xfc: /* cld */
         _regs.eflags &= ~EFLG_DF;
         break;
+
     case 0xfd: /* std */
         _regs.eflags |= EFLG_DF;
         break;
@@ -1183,39 +1309,9 @@ x86_emulate(
     {
     case 0x40 ... 0x4f: /* cmov */
         dst.val = dst.orig_val = src.val;
-        d &= ~Mov; /* default to no move */
-        /* First, assume we're decoding an even cmov opcode (lsb == 0). */
-        switch ( (b & 15) >> 1 )
-        {
-        case 0: /* cmovo */
-            d |= (_regs.eflags & EFLG_OF) ? Mov : 0;
-            break;
-        case 1: /* cmovb/cmovc/cmovnae */
-            d |= (_regs.eflags & EFLG_CF) ? Mov : 0;
-            break;
-        case 2: /* cmovz/cmove */
-            d |= (_regs.eflags & EFLG_ZF) ? Mov : 0;
-            break;
-        case 3: /* cmovbe/cmovna */
-            d |= (_regs.eflags & (EFLG_CF|EFLG_ZF)) ? Mov : 0;
-            break;
-        case 4: /* cmovs */
-            d |= (_regs.eflags & EFLG_SF) ? Mov : 0;
-            break;
-        case 5: /* cmovp/cmovpe */
-            d |= (_regs.eflags & EFLG_PF) ? Mov : 0;
-            break;
-        case 7: /* cmovle/cmovng */
-            d |= (_regs.eflags & EFLG_ZF) ? Mov : 0;
-            /* fall through */
-        case 6: /* cmovl/cmovnge */
-            d |= (!(_regs.eflags & EFLG_SF) != !(_regs.eflags & EFLG_OF)) ?
-                Mov : 0;
-            break;
-        }
-        /* Odd cmov opcodes (lsb == 1) have inverted sense. */
-        d ^= (b & 1) ? Mov : 0;
-        break;
+        d = (d & ~Mov) | (test_cc(b, _regs.eflags) ? Mov : 0);
+        break;
+
     case 0xb0 ... 0xb1: /* cmpxchg */
         /* Save real source value, then compare EAX against destination. */
         src.orig_val = src.val;
@@ -1235,27 +1331,34 @@ x86_emulate(
             dst.reg  = (unsigned long *)&_regs.eax;
         }
         break;
+
     case 0xa3: bt: /* bt */
         emulate_2op_SrcV_nobyte("bt", src, dst, _regs.eflags);
         break;
+
     case 0xb3: btr: /* btr */
         emulate_2op_SrcV_nobyte("btr", src, dst, _regs.eflags);
         break;
+
     case 0xab: bts: /* bts */
         emulate_2op_SrcV_nobyte("bts", src, dst, _regs.eflags);
         break;
+
     case 0xb6: /* movzx rm8,r{16,32,64} */
         /* Recompute DstReg as we may have decoded AH/BH/CH/DH. */
         dst.reg   = decode_register(modrm_reg, &_regs, 0);
         dst.bytes = op_bytes;
         dst.val   = (uint8_t)src.val;
         break;
+
     case 0xb7: /* movzx rm16,r{16,32,64} */
         dst.val = (uint16_t)src.val;
         break;
+
     case 0xbb: btc: /* btc */
         emulate_2op_SrcV_nobyte("btc", src, dst, _regs.eflags);
         break;
+
     case 0xba: /* Grp8 */
         switch ( modrm_reg & 3 )
         {
@@ -1265,15 +1368,18 @@ x86_emulate(
         case 3: goto btc;
         }
         break;
+
     case 0xbe: /* movsx rm8,r{16,32,64} */
         /* Recompute DstReg as we may have decoded AH/BH/CH/DH. */
         dst.reg   = decode_register(modrm_reg, &_regs, 0);
         dst.bytes = op_bytes;
         dst.val   = (int8_t)src.val;
         break;
+
     case 0xbf: /* movsx rm16,r{16,32,64} */
         dst.val = (int16_t)src.val;
         break;
+
     case 0xc0 ... 0xc1: /* xadd */
         /* Write back the register source. */
         switch ( dst.bytes )
@@ -1293,6 +1399,15 @@ x86_emulate(
     case 0x0d: /* GrpP (prefetch) */
     case 0x18: /* Grp16 (prefetch/nop) */
         break;
+
+    case 0x80 ... 0x8f: /* jcc (near) */ {
+        int rel = insn_fetch_bytes(
+            (mode == X86EMUL_MODE_PROT64) ? 4 : op_bytes);
+        if ( test_cc(b, _regs.eflags) )
+            jmp_rel(rel);
+        break;
+    }
+
     case 0xc7: /* Grp9 (cmpxchg8b) */
 #if defined(__i386__)
     {

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.