138 lines
5.9 KiB
Diff
138 lines
5.9 KiB
Diff
|
# HG changeset patch
|
||
|
# User Keir Fraser <keir.fraser@citrix.com>
|
||
|
# Date 1250696281 -3600
|
||
|
# Node ID 68ea3be8b6c14d6de5d7c87fc00e27a596d2a00d
|
||
|
# Parent ca4db4ff9101f51d89a3de9e756b8a2f90a2baf2
|
||
|
x86-64: adjust emulation of control transfers
|
||
|
|
||
|
While Intel and AMD implementations differ in various respects when
|
||
|
it comes to non-default operand sizes of control transfer instructions
|
||
|
and segment register loads (lfs, lgs, lss), it seems to make senss to
|
||
|
(a) match their behavior if they agree and (b) prefer the more
|
||
|
permissive behavior if they don't agree:
|
||
|
|
||
|
- honor operand size overrides on near brances (AMD does, Intel
|
||
|
doesn't)
|
||
|
- honor operand size overrides on far branches (both Intel and AMD do)
|
||
|
- honor REX.W on far branches (Intel does, AMD doesn't except on far
|
||
|
returns)
|
||
|
- honor REX.W on lfs, lgs, and lss (Intel does, AMD doesn't)
|
||
|
|
||
|
Also, do not permit emulation of pushing/popping segment registers
|
||
|
other than fs and gs as well as that of les and lds (the latter are
|
||
|
particularly important due to the re-use of the respective opcodes as
|
||
|
VEX prefixes in AVX).
|
||
|
|
||
|
Signed-off-by: Jan Beulich <jbeulich@novell.com>
|
||
|
xen-unstable changeset: 20078:429ef4f4fe37
|
||
|
xen-unstable date: Wed Aug 19 13:02:04 2009 +0100
|
||
|
|
||
|
Index: xen-3.4.1-testing/xen/arch/x86/x86_emulate/x86_emulate.c
|
||
|
===================================================================
|
||
|
--- xen-3.4.1-testing.orig/xen/arch/x86/x86_emulate/x86_emulate.c
|
||
|
+++ xen-3.4.1-testing/xen/arch/x86/x86_emulate/x86_emulate.c
|
||
|
@@ -561,9 +561,10 @@ do {
|
||
|
do { \
|
||
|
int _rel = (int)(rel); \
|
||
|
_regs.eip += _rel; \
|
||
|
- if ( !mode_64bit() ) \
|
||
|
- _regs.eip = ((op_bytes == 2) \
|
||
|
- ? (uint16_t)_regs.eip : (uint32_t)_regs.eip); \
|
||
|
+ if ( op_bytes == 2 ) \
|
||
|
+ _regs.eip = (uint16_t)_regs.eip; \
|
||
|
+ else if ( !mode_64bit() ) \
|
||
|
+ _regs.eip = (uint32_t)_regs.eip; \
|
||
|
} while (0)
|
||
|
|
||
|
struct fpu_insn_ctxt {
|
||
|
@@ -1614,6 +1615,7 @@ x86_emulate(
|
||
|
struct segment_register reg;
|
||
|
src.val = x86_seg_es;
|
||
|
push_seg:
|
||
|
+ generate_exception_if(mode_64bit() && !twobyte, EXC_UD, -1);
|
||
|
fail_if(ops->read_segment == NULL);
|
||
|
if ( (rc = ops->read_segment(src.val, ®, ctxt)) != 0 )
|
||
|
return rc;
|
||
|
@@ -1629,6 +1631,7 @@ x86_emulate(
|
||
|
case 0x07: /* pop %%es */
|
||
|
src.val = x86_seg_es;
|
||
|
pop_seg:
|
||
|
+ generate_exception_if(mode_64bit() && !twobyte, EXC_UD, -1);
|
||
|
fail_if(ops->write_segment == NULL);
|
||
|
/* 64-bit mode: POP defaults to a 64-bit operand. */
|
||
|
if ( mode_64bit() && (op_bytes == 4) )
|
||
|
@@ -2074,8 +2077,8 @@ x86_emulate(
|
||
|
uint16_t sel;
|
||
|
uint32_t eip;
|
||
|
|
||
|
- fail_if(ops->read_segment == NULL);
|
||
|
generate_exception_if(mode_64bit(), EXC_UD, -1);
|
||
|
+ fail_if(ops->read_segment == NULL);
|
||
|
|
||
|
eip = insn_fetch_bytes(op_bytes);
|
||
|
sel = insn_fetch_type(uint16_t);
|
||
|
@@ -2293,7 +2296,7 @@ x86_emulate(
|
||
|
case 0xc2: /* ret imm16 (near) */
|
||
|
case 0xc3: /* ret (near) */ {
|
||
|
int offset = (b == 0xc2) ? insn_fetch_type(uint16_t) : 0;
|
||
|
- op_bytes = mode_64bit() ? 8 : op_bytes;
|
||
|
+ op_bytes = ((op_bytes == 4) && mode_64bit()) ? 8 : op_bytes;
|
||
|
if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes + offset),
|
||
|
&dst.val, op_bytes, ctxt, ops)) != 0 )
|
||
|
goto done;
|
||
|
@@ -2305,6 +2308,7 @@ x86_emulate(
|
||
|
unsigned long sel;
|
||
|
dst.val = x86_seg_es;
|
||
|
les: /* dst.val identifies the segment */
|
||
|
+ generate_exception_if(mode_64bit() && !twobyte, EXC_UD, -1);
|
||
|
generate_exception_if(src.type != OP_MEM, EXC_UD, -1);
|
||
|
if ( (rc = read_ulong(src.mem.seg, src.mem.off + src.bytes,
|
||
|
&sel, 2, ctxt, ops)) != 0 )
|
||
|
@@ -2379,7 +2383,6 @@ x86_emulate(
|
||
|
case 0xca: /* ret imm16 (far) */
|
||
|
case 0xcb: /* ret (far) */ {
|
||
|
int offset = (b == 0xca) ? insn_fetch_type(uint16_t) : 0;
|
||
|
- op_bytes = mode_64bit() ? 8 : op_bytes;
|
||
|
if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes),
|
||
|
&dst.val, op_bytes, ctxt, ops)) ||
|
||
|
(rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes + offset),
|
||
|
@@ -3032,17 +3035,17 @@ x86_emulate(
|
||
|
}
|
||
|
|
||
|
case 0xe8: /* call (near) */ {
|
||
|
- int rel = (((op_bytes == 2) && !mode_64bit())
|
||
|
+ int rel = ((op_bytes == 2)
|
||
|
? (int32_t)insn_fetch_type(int16_t)
|
||
|
: insn_fetch_type(int32_t));
|
||
|
- op_bytes = mode_64bit() ? 8 : op_bytes;
|
||
|
+ op_bytes = ((op_bytes == 4) && mode_64bit()) ? 8 : op_bytes;
|
||
|
src.val = _regs.eip;
|
||
|
jmp_rel(rel);
|
||
|
goto push;
|
||
|
}
|
||
|
|
||
|
case 0xe9: /* jmp (near) */ {
|
||
|
- int rel = (((op_bytes == 2) && !mode_64bit())
|
||
|
+ int rel = ((op_bytes == 2)
|
||
|
? (int32_t)insn_fetch_type(int16_t)
|
||
|
: insn_fetch_type(int32_t));
|
||
|
jmp_rel(rel);
|
||
|
@@ -3330,7 +3333,7 @@ x86_emulate(
|
||
|
break;
|
||
|
case 2: /* call (near) */
|
||
|
case 4: /* jmp (near) */
|
||
|
- if ( (dst.bytes != 8) && mode_64bit() )
|
||
|
+ if ( (dst.bytes == 4) && mode_64bit() )
|
||
|
{
|
||
|
dst.bytes = op_bytes = 8;
|
||
|
if ( dst.type == OP_REG )
|
||
|
@@ -3683,7 +3686,7 @@ x86_emulate(
|
||
|
}
|
||
|
|
||
|
case 0x80 ... 0x8f: /* jcc (near) */ {
|
||
|
- int rel = (((op_bytes == 2) && !mode_64bit())
|
||
|
+ int rel = ((op_bytes == 2)
|
||
|
? (int32_t)insn_fetch_type(int16_t)
|
||
|
: insn_fetch_type(int32_t));
|
||
|
if ( test_cc(b, _regs.eflags) )
|