# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1195940419 0
# Node ID d5c3961288970acd963855b12c84ede7d8ebabfc
# Parent 2e7fcea74cb1117640c7ba6a2152d42223335259
x86_emulate: Support most common segment load/save instructions.
Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx>
---
xen/arch/x86/x86_emulate.c | 223 +++++++++++++++++++++++++++++++++++--
xen/include/asm-x86/hvm/hvm.h | 30 ----
xen/include/asm-x86/hvm/svm/vmcb.h | 2
xen/include/asm-x86/x86_emulate.h | 53 ++++++++
4 files changed, 264 insertions(+), 44 deletions(-)
diff -r 2e7fcea74cb1 -r d5c396128897 xen/arch/x86/x86_emulate.c
--- a/xen/arch/x86/x86_emulate.c Sat Nov 24 16:16:57 2007 +0000
+++ b/xen/arch/x86/x86_emulate.c Sat Nov 24 21:40:19 2007 +0000
@@ -60,19 +60,19 @@ static uint8_t opcode_table[256] = {
/* 0x00 - 0x07 */
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
- ByteOp|DstReg|SrcImm, DstReg|SrcImm, 0, 0,
+ ByteOp|DstReg|SrcImm, DstReg|SrcImm, ImplicitOps, ImplicitOps,
/* 0x08 - 0x0F */
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
- ByteOp|DstReg|SrcImm, DstReg|SrcImm, 0, 0,
+ ByteOp|DstReg|SrcImm, DstReg|SrcImm, ImplicitOps, 0,
/* 0x10 - 0x17 */
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
- ByteOp|DstReg|SrcImm, DstReg|SrcImm, 0, 0,
+ ByteOp|DstReg|SrcImm, DstReg|SrcImm, ImplicitOps, ImplicitOps,
/* 0x18 - 0x1F */
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
- ByteOp|DstReg|SrcImm, DstReg|SrcImm, 0, 0,
+ ByteOp|DstReg|SrcImm, DstReg|SrcImm, ImplicitOps, ImplicitOps,
/* 0x20 - 0x27 */
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
@@ -120,7 +120,8 @@ static uint8_t opcode_table[256] = {
/* 0x88 - 0x8F */
ByteOp|DstMem|SrcReg|ModRM|Mov, DstMem|SrcReg|ModRM|Mov,
ByteOp|DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
- 0, DstReg|SrcNone|ModRM, 0, DstMem|SrcNone|ModRM|Mov,
+ DstMem|SrcReg|ModRM|Mov, DstReg|SrcNone|ModRM,
+ DstReg|SrcMem|ModRM|Mov, DstMem|SrcNone|ModRM|Mov,
/* 0x90 - 0x97 */
ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
@@ -158,7 +159,7 @@ static uint8_t opcode_table[256] = {
ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
/* 0xE8 - 0xEF */
- ImplicitOps, ImplicitOps, 0, ImplicitOps,
+ ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
/* 0xF0 - 0xF7 */
0, 0, 0, 0,
@@ -170,7 +171,7 @@ static uint8_t opcode_table[256] = {
static uint8_t twobyte_table[256] = {
/* 0x00 - 0x07 */
- 0, 0, 0, 0, 0, ImplicitOps, 0, 0,
+ 0, ImplicitOps|ModRM, 0, 0, 0, ImplicitOps, 0, 0,
/* 0x08 - 0x0F */
ImplicitOps, ImplicitOps, 0, 0, 0, ImplicitOps|ModRM, 0, 0,
/* 0x10 - 0x17 */
@@ -220,9 +221,10 @@ static uint8_t twobyte_table[256] = {
ByteOp|DstMem|SrcNone|ModRM|Mov, ByteOp|DstMem|SrcNone|ModRM|Mov,
ByteOp|DstMem|SrcNone|ModRM|Mov, ByteOp|DstMem|SrcNone|ModRM|Mov,
/* 0xA0 - 0xA7 */
- 0, 0, 0, DstBitBase|SrcReg|ModRM, 0, 0, 0, 0,
+ ImplicitOps, ImplicitOps, 0, DstBitBase|SrcReg|ModRM, 0, 0, 0, 0,
/* 0xA8 - 0xAF */
- 0, 0, 0, DstBitBase|SrcReg|ModRM, 0, 0, 0, DstReg|SrcMem|ModRM,
+ ImplicitOps, ImplicitOps, 0, DstBitBase|SrcReg|ModRM,
+ 0, 0, 0, DstReg|SrcMem|ModRM,
/* 0xB0 - 0xB7 */
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
0, DstBitBase|SrcReg|ModRM,
@@ -677,6 +679,45 @@ test_cc(
return (!!rc ^ (condition & 1));
}
+static int
+in_realmode(
+ struct x86_emulate_ctxt *ctxt,
+ struct x86_emulate_ops *ops)
+{
+ unsigned long cr0;
+ int rc;
+
+ if ( ops->read_cr == NULL )
+ return 0;
+
+ rc = ops->read_cr(0, &cr0, ctxt);
+ return (!rc && !(cr0 & 1));
+}
+
+static int
+load_seg(
+ enum x86_segment seg,
+ uint16_t sel,
+ struct x86_emulate_ctxt *ctxt,
+ struct x86_emulate_ops *ops)
+{
+ struct segment_register reg;
+ int rc;
+
+ if ( !in_realmode(ctxt, ops) ||
+ (ops->read_segment == NULL) ||
+ (ops->write_segment == NULL) )
+ return X86EMUL_UNHANDLEABLE;
+
+ if ( (rc = ops->read_segment(seg, ®, ctxt)) != 0 )
+ return rc;
+
+ reg.sel = sel;
+ reg.base = (uint32_t)sel << 4;
+
+ return ops->write_segment(seg, ®, ctxt);
+}
+
void *
decode_register(
uint8_t modrm_reg, struct cpu_user_regs *regs, int highbyte_regs)
@@ -715,6 +756,24 @@ decode_register(
}
return p;
+}
+
+#define decode_segment_failed x86_seg_tr
+enum x86_segment
+decode_segment(
+ uint8_t modrm_reg)
+{
+ switch ( modrm_reg )
+ {
+ case 0: return x86_seg_es;
+ case 1: return x86_seg_cs;
+ case 2: return x86_seg_ss;
+ case 3: return x86_seg_ds;
+ case 4: return x86_seg_fs;
+ case 5: return x86_seg_gs;
+ default: break;
+ }
+ return decode_segment_failed;
}
int
@@ -1205,6 +1264,7 @@ x86_emulate(
dst.val = (dst.val & ~3) | (src_val & 3);
else
dst.type = OP_NONE;
+ generate_exception_if(in_realmode(ctxt, ops), EXC_UD);
}
break;
@@ -1283,6 +1343,28 @@ x86_emulate(
case 0x88 ... 0x8b: /* mov */
dst.val = src.val;
break;
+
+ case 0x8c: /* mov Sreg,r/m */ {
+ struct segment_register reg;
+ enum x86_segment seg = decode_segment(modrm_reg);
+ generate_exception_if(seg == decode_segment_failed, EXC_UD);
+ fail_if(ops->read_segment == NULL);
+ if ( (rc = ops->read_segment(seg, ®, ctxt)) != 0 )
+ goto done;
+ dst.val = reg.sel;
+ if ( dst.type == OP_MEM )
+ dst.bytes = 2;
+ break;
+ }
+
+ case 0x8e: /* mov r/m,Sreg */ {
+ enum x86_segment seg = decode_segment(modrm_reg);
+ generate_exception_if(seg == decode_segment_failed, EXC_UD);
+ if ( (rc = load_seg(seg, (uint16_t)src.val, ctxt, ops)) != 0 )
+ goto done;
+ dst.type = OP_NONE;
+ break;
+ }
case 0x8d: /* lea */
dst.val = ea.mem.off;
@@ -1657,6 +1739,56 @@ x86_emulate(
switch ( b )
{
+ case 0x06: /* push %%es */ {
+ struct segment_register reg;
+ src.val = x86_seg_es;
+ push_seg:
+ fail_if(ops->read_segment == NULL);
+ if ( (rc = ops->read_segment(src.val, ®, ctxt)) != 0 )
+ return rc;
+ /* 64-bit mode: PUSH defaults to a 64-bit operand. */
+ if ( mode_64bit() && (op_bytes == 4) )
+ op_bytes = 8;
+ if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
+ reg.sel, op_bytes, ctxt)) != 0 )
+ goto done;
+ break;
+ }
+
+ case 0x07: /* pop %%es */
+ src.val = x86_seg_es;
+ pop_seg:
+ fail_if(ops->write_segment == NULL);
+ /* 64-bit mode: PUSH defaults to a 64-bit operand. */
+ if ( mode_64bit() && (op_bytes == 4) )
+ op_bytes = 8;
+ if ( (rc = ops->read(x86_seg_ss, sp_post_inc(op_bytes),
+ &dst.val, op_bytes, ctxt)) != 0 )
+ goto done;
+ if ( (rc = load_seg(src.val, (uint16_t)dst.val, ctxt, ops)) != 0 )
+ return rc;
+ break;
+
+ case 0x0e: /* push %%cs */
+ src.val = x86_seg_cs;
+ goto push_seg;
+
+ case 0x16: /* push %%ss */
+ src.val = x86_seg_ss;
+ goto push_seg;
+
+ case 0x17: /* pop %%ss */
+ src.val = x86_seg_ss;
+ goto pop_seg;
+
+ case 0x1e: /* push %%ds */
+ src.val = x86_seg_ds;
+ goto push_seg;
+
+ case 0x1f: /* pop %%ds */
+ src.val = x86_seg_ds;
+ goto pop_seg;
+
case 0x27: /* daa */ {
uint8_t al = _regs.eax;
unsigned long eflags = _regs.eflags;
@@ -2066,6 +2198,18 @@ x86_emulate(
break;
}
+ case 0xea: /* jmp (far, absolute) */ {
+ uint16_t sel;
+ uint32_t eip;
+ generate_exception_if(mode_64bit(), EXC_UD);
+ eip = insn_fetch_bytes(op_bytes);
+ sel = insn_fetch_type(uint16_t);
+ if ( (rc = load_seg(x86_seg_cs, sel, ctxt, ops)) != 0 )
+ goto done;
+ _regs.eip = eip;
+ break;
+ }
+
case 0xeb: /* jmp (short) */
jmp_rel(insn_fetch_type(int8_t));
break;
@@ -2252,6 +2396,51 @@ x86_emulate(
twobyte_special_insn:
switch ( b )
{
+ case 0x01: /* Grp7 */ {
+ struct segment_register reg;
+
+ switch ( modrm_reg & 7 )
+ {
+ case 0: /* sgdt */
+ case 1: /* sidt */
+ generate_exception_if(ea.type != OP_MEM, EXC_UD);
+ fail_if(ops->read_segment == NULL);
+ if ( (rc = ops->read_segment((modrm_reg & 1) ?
+ x86_seg_idtr : x86_seg_gdtr,
+ ®, ctxt)) )
+ goto done;
+ if ( op_bytes == 2 )
+ reg.base &= 0xffffff;
+ if ( (rc = ops->write(ea.mem.seg, ea.mem.off+0,
+ reg.limit, 2, ctxt)) ||
+ (rc = ops->write(ea.mem.seg, ea.mem.off+2,
+ reg.base, mode_64bit() ? 8 : 4, ctxt)) )
+ goto done;
+ break;
+ case 2: /* lgdt */
+ case 3: /* lidt */
+ generate_exception_if(ea.type != OP_MEM, EXC_UD);
+ fail_if(ops->write_segment == NULL);
+ memset(®, 0, sizeof(reg));
+ if ( (rc = ops->read(ea.mem.seg, ea.mem.off+0,
+ (unsigned long *)®.limit, 2, ctxt)) ||
+ (rc = ops->read(ea.mem.seg, ea.mem.off+2,
+ (unsigned long *)®.base,
+ mode_64bit() ? 8 : 4, ctxt)) )
+ goto done;
+ if ( op_bytes == 2 )
+ reg.base &= 0xffffff;
+ if ( (rc = ops->write_segment((modrm_reg & 1) ?
+ x86_seg_idtr : x86_seg_gdtr,
+ ®, ctxt)) )
+ goto done;
+ break;
+ default:
+ goto cannot_emulate;
+ }
+ break;
+ }
+
case 0x06: /* clts */
generate_exception_if(!mode_ring0(), EXC_GP);
fail_if((ops->read_cr == NULL) || (ops->write_cr == NULL));
@@ -2341,6 +2530,22 @@ x86_emulate(
break;
}
+ case 0xa0: /* push %%fs */
+ src.val = x86_seg_fs;
+ goto push_seg;
+
+ case 0xa1: /* pop %%fs */
+ src.val = x86_seg_fs;
+ goto pop_seg;
+
+ case 0xa8: /* push %%gs */
+ src.val = x86_seg_gs;
+ goto push_seg;
+
+ case 0xa9: /* pop %%gs */
+ src.val = x86_seg_gs;
+ goto pop_seg;
+
case 0xc7: /* Grp9 (cmpxchg8b) */
#if defined(__i386__)
{
diff -r 2e7fcea74cb1 -r d5c396128897 xen/include/asm-x86/hvm/hvm.h
--- a/xen/include/asm-x86/hvm/hvm.h Sat Nov 24 16:16:57 2007 +0000
+++ b/xen/include/asm-x86/hvm/hvm.h Sat Nov 24 21:40:19 2007 +0000
@@ -25,36 +25,6 @@
#include <asm/x86_emulate.h>
#include <public/domctl.h>
#include <public/hvm/save.h>
-
-/*
- * Attribute for segment selector. This is a copy of bit 40:47 & 52:55 of the
- * segment descriptor. It happens to match the format of an AMD SVM VMCB.
- */
-typedef union segment_attributes {
- u16 bytes;
- struct
- {
- u16 type:4; /* 0; Bit 40-43 */
- u16 s: 1; /* 4; Bit 44 */
- u16 dpl: 2; /* 5; Bit 45-46 */
- u16 p: 1; /* 7; Bit 47 */
- u16 avl: 1; /* 8; Bit 52 */
- u16 l: 1; /* 9; Bit 53 */
- u16 db: 1; /* 10; Bit 54 */
- u16 g: 1; /* 11; Bit 55 */
- } fields;
-} __attribute__ ((packed)) segment_attributes_t;
-
-/*
- * Full state of a segment register (visible and hidden portions).
- * Again, this happens to match the format of an AMD SVM VMCB.
- */
-typedef struct segment_register {
- u16 sel;
- segment_attributes_t attr;
- u32 limit;
- u64 base;
-} __attribute__ ((packed)) segment_register_t;
/* Interrupt acknowledgement sources. */
enum hvm_intsrc {
diff -r 2e7fcea74cb1 -r d5c396128897 xen/include/asm-x86/hvm/svm/vmcb.h
--- a/xen/include/asm-x86/hvm/svm/vmcb.h Sat Nov 24 16:16:57 2007 +0000
+++ b/xen/include/asm-x86/hvm/svm/vmcb.h Sat Nov 24 21:40:19 2007 +0000
@@ -304,7 +304,7 @@ enum VMEXIT_EXITCODE
};
/* Definition of segment state is borrowed by the generic HVM code. */
-typedef segment_register_t svm_segment_register_t;
+typedef struct segment_register svm_segment_register_t;
typedef union
{
diff -r 2e7fcea74cb1 -r d5c396128897 xen/include/asm-x86/x86_emulate.h
--- a/xen/include/asm-x86/x86_emulate.h Sat Nov 24 16:16:57 2007 +0000
+++ b/xen/include/asm-x86/x86_emulate.h Sat Nov 24 21:40:19 2007 +0000
@@ -26,10 +26,7 @@
struct x86_emulate_ctxt;
-/*
- * Comprehensive enumeration of x86 segment registers. Note that the system
- * registers (TR, LDTR, GDTR, IDTR) are never referenced by the emulator.
- */
+/* Comprehensive enumeration of x86 segment registers. */
enum x86_segment {
/* General purpose. */
x86_seg_cs,
@@ -45,6 +42,36 @@ enum x86_segment {
x86_seg_idtr
};
+/*
+ * Attribute for segment selector. This is a copy of bit 40:47 & 52:55 of the
+ * segment descriptor. It happens to match the format of an AMD SVM VMCB.
+ */
+typedef union segment_attributes {
+ u16 bytes;
+ struct
+ {
+ u16 type:4; /* 0; Bit 40-43 */
+ u16 s: 1; /* 4; Bit 44 */
+ u16 dpl: 2; /* 5; Bit 45-46 */
+ u16 p: 1; /* 7; Bit 47 */
+ u16 avl: 1; /* 8; Bit 52 */
+ u16 l: 1; /* 9; Bit 53 */
+ u16 db: 1; /* 10; Bit 54 */
+ u16 g: 1; /* 11; Bit 55 */
+ } fields;
+} __attribute__ ((packed)) segment_attributes_t;
+
+/*
+ * Full state of a segment register (visible and hidden portions).
+ * Again, this happens to match the format of an AMD SVM VMCB.
+ */
+struct segment_register {
+ u16 sel;
+ segment_attributes_t attr;
+ u32 limit;
+ u64 base;
+} __attribute__ ((packed));
+
/*
* Return codes from state-accessor functions and from x86_emulate().
*/
@@ -148,6 +175,24 @@ struct x86_emulate_ops
struct x86_emulate_ctxt *ctxt);
/*
+ * read_segment: Emulate a read of full context of a segment register.
+ * @reg: [OUT] Contents of segment register (visible and hidden state).
+ */
+ int (*read_segment)(
+ enum x86_segment seg,
+ struct segment_register *reg,
+ struct x86_emulate_ctxt *ctxt);
+
+ /*
+ * write_segment: Emulate a read of full context of a segment register.
+ * @reg: [OUT] Contents of segment register (visible and hidden state).
+ */
+ int (*write_segment)(
+ enum x86_segment seg,
+ struct segment_register *reg,
+ struct x86_emulate_ctxt *ctxt);
+
+ /*
* read_io: Read from I/O port(s).
* @port: [IN ] Base port for access.
*/
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|