File 19614-x86-emul-lldt-ltr.patch of Package xen
# HG changeset patch
# User Keir Fraser <keir.fraser@citrix.com>
# Date 1242695376 -3600
# Node ID e421fd04e15039c4f5204a1d128af550aa9bc578
# Parent 4e7c5eb717749b043b12a3e8c43487d264276dda
x86_emulate: Emulate LLDT and LTR instructions.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
# HG changeset patch
# User Keir Fraser <keir.fraser@citrix.com>
# Date 1249635283 -3600
# Node ID 71d6d6f2ecd6e4a33b7dc1363e0dfe6b89dcc416
# Parent b9cdcf502aa35818581d41cee7e6e30ae49acb92
x86_emulate: protmode_load_seg() cannot load system segments in long mode.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -167,7 +167,7 @@ static uint8_t opcode_table[256] = {
static uint8_t twobyte_table[256] = {
/* 0x00 - 0x07 */
- 0, ImplicitOps|ModRM, 0, 0, 0, 0, ImplicitOps, 0,
+ SrcMem16|ModRM, ImplicitOps|ModRM, 0, 0, 0, 0, ImplicitOps, 0,
/* 0x08 - 0x0F */
ImplicitOps, ImplicitOps, 0, 0, 0, ImplicitOps|ModRM, 0, 0,
/* 0x10 - 0x17 */
@@ -276,6 +276,8 @@ struct operand {
/* MSRs. */
#define MSR_TSC 0x10
+#define MSR_EFER 0xc0000080
+#define EFER_LMA (1u<<10)
/* Control register flags. */
#define CR0_PE (1<<0)
@@ -937,6 +939,24 @@ in_protmode(
}
static int
+in_longmode(
+ struct x86_emulate_ctxt *ctxt,
+ struct x86_emulate_ops *ops)
+{
+#ifdef __x86_64__
+ uint64_t efer;
+
+ if (ops->read_msr == NULL)
+ return -1;
+
+ ops->read_msr(MSR_EFER, &efer, ctxt);
+ return !!(efer & EFER_LMA);
+#else
+ return 0;
+#endif
+}
+
+static int
realmode_load_seg(
enum x86_segment seg,
uint16_t sel,
@@ -966,8 +986,8 @@ protmode_load_seg(
struct { uint32_t a, b; } desc;
unsigned long val;
uint8_t dpl, rpl, cpl;
- uint32_t new_desc_b;
- int rc, fault_type = EXC_TS;
+ uint32_t new_desc_b, a_flag = 0x100;
+ int rc, fault_type = EXC_GP;
/* NULL selector? */
if ( (sel & 0xfffc) == 0 )
@@ -978,8 +998,8 @@ protmode_load_seg(
return ops->write_segment(seg, &segr, ctxt);
}
- /* LDT descriptor must be in the GDT. */
- if ( (seg == x86_seg_ldtr) && (sel & 4) )
+ /* System segment descriptors must reside in the GDT. */
+ if ( !is_x86_user_segment(seg) && (sel & 4) )
goto raise_exn;
if ( (rc = ops->read_segment(x86_seg_ss, &ss, ctxt)) ||
@@ -1008,8 +1028,17 @@ protmode_load_seg(
goto raise_exn;
}
- /* LDT descriptor is a system segment. All others are code/data. */
- if ( (desc.b & (1u<<12)) == ((seg == x86_seg_ldtr) << 12) )
+ if ( !is_x86_user_segment(seg) )
+ {
+ /* System segments must have S flag == 0. */
+ if ( desc.b & (1u << 12) )
+ goto raise_exn;
+ /* We do not support 64-bit descriptor types. */
+ if ( in_longmode(ctxt, ops) )
+ return X86EMUL_UNHANDLEABLE;
+ }
+ /* User segments must have S flag == 1. */
+ else if ( !(desc.b & (1u << 12)) )
goto raise_exn;
dpl = (desc.b >> 13) & 3;
@@ -1038,6 +1067,12 @@ protmode_load_seg(
if ( (desc.b & (15u<<8)) != (2u<<8) )
goto raise_exn;
goto skip_accessed_flag;
+ case x86_seg_tr:
+ /* Available TSS system segment? */
+ if ( (desc.b & (15u<<8)) != (9u<<8) )
+ goto raise_exn;
+ a_flag = 0x200; /* busy flag */
+ break;
default:
/* Readable code or data segment? */
if ( (desc.b & (5u<<9)) == (4u<<9) )
@@ -1050,8 +1085,8 @@ protmode_load_seg(
}
/* Ensure Accessed flag is set. */
- new_desc_b = desc.b | 0x100;
- rc = ((desc.b & 0x100) ? X86EMUL_OKAY :
+ new_desc_b = desc.b | a_flag;
+ rc = ((desc.b & a_flag) ? X86EMUL_OKAY :
ops->cmpxchg(
x86_seg_none, desctab.base + (sel & 0xfff8) + 4,
&desc.b, &new_desc_b, 4, ctxt));
@@ -1061,7 +1096,7 @@ protmode_load_seg(
return rc;
/* Force the Accessed flag in our local copy. */
- desc.b |= 0x100;
+ desc.b |= a_flag;
skip_accessed_flag:
segr.base = (((desc.b << 0) & 0xff000000u) |
@@ -3472,6 +3507,15 @@ x86_emulate(
twobyte_insn:
switch ( b )
{
+ case 0x00: /* Grp6 */
+ fail_if((modrm_reg & 6) != 2);
+ generate_exception_if(!in_protmode(ctxt, ops), EXC_UD, -1);
+ generate_exception_if(!mode_ring0(), EXC_GP, 0);
+ if ( (rc = load_seg((modrm_reg & 1) ? x86_seg_tr : x86_seg_ldtr,
+ src.val, ctxt, ops)) != 0 )
+ goto done;
+ break;
+
case 0x40 ... 0x4f: /* cmovcc */
dst.val = src.val;
if ( !test_cc(b, _regs.eflags) )