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) )
openSUSE Build Service is sponsored by