# 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
|