[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v3] x86emul: support LAR/LSL/VERR/VERW



This involves protmode_load_seg() accepting x86_seg_none as input, with
the meaning to
- suppress any exceptions other than #PF,
- not commit any state.

Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
---
v3: Re-base.
v2: Extend commit message and add a respective code comment. Add
    ASSERT()s to ensure/document that only #PF can make it out of
    protmode_load_seg().

--- a/tools/tests/x86_emulator/test_x86_emulator.c
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
@@ -44,7 +44,47 @@ static int read(
     if ( verbose )
         printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes);
 
-    bytes_read += bytes;
+    switch ( seg )
+    {
+        uint64_t value;
+
+    case x86_seg_gdtr:
+        /* Fake system segment type matching table index. */
+        if ( (offset & 7) || (bytes > 8) )
+            return X86EMUL_UNHANDLEABLE;
+#ifdef __x86_64__
+        if ( !(offset & 8) )
+        {
+            memset(p_data, 0, bytes);
+            return X86EMUL_OKAY;
+        }
+        value = (offset - 8) >> 4;
+#else
+        value = (offset - 8) >> 3;
+#endif
+        if ( value >= 0x10 )
+            return X86EMUL_UNHANDLEABLE;
+        value |= value << 40;
+        memcpy(p_data, &value, bytes);
+        return X86EMUL_OKAY;
+
+    case x86_seg_ldtr:
+        /* Fake user segment type matching table index. */
+        if ( (offset & 7) || (bytes > 8) )
+            return X86EMUL_UNHANDLEABLE;
+        value = offset >> 3;
+        if ( value >= 0x10 )
+            return X86EMUL_UNHANDLEABLE;
+        value |= (value | 0x10) << 40;
+        memcpy(p_data, &value, bytes);
+        return X86EMUL_OKAY;
+
+    default:
+        if ( !is_x86_user_segment(seg) )
+            return X86EMUL_UNHANDLEABLE;
+        bytes_read += bytes;
+        break;
+    }
     memcpy(p_data, (void *)offset, bytes);
     return X86EMUL_OKAY;
 }
@@ -73,6 +113,8 @@ static int write(
     if ( verbose )
         printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes);
 
+    if ( !is_x86_user_segment(seg) )
+        return X86EMUL_UNHANDLEABLE;
     memcpy((void *)offset, p_data, bytes);
     return X86EMUL_OKAY;
 }
@@ -88,17 +130,48 @@ static int cmpxchg(
     if ( verbose )
         printf("** %s(%u, %p,, %u,)\n", __func__, seg, (void *)offset, bytes);
 
+    if ( !is_x86_user_segment(seg) )
+        return X86EMUL_UNHANDLEABLE;
     memcpy((void *)offset, new, bytes);
     return X86EMUL_OKAY;
 }
 
+static int read_segment(
+    enum x86_segment seg,
+    struct segment_register *reg,
+    struct x86_emulate_ctxt *ctxt)
+{
+    if ( !is_x86_user_segment(seg) )
+        return X86EMUL_UNHANDLEABLE;
+    memset(reg, 0, sizeof(*reg));
+    reg->attr.fields.p = 1;
+    return X86EMUL_OKAY;
+}
+
+static int read_msr(
+    unsigned int reg,
+    uint64_t *val,
+    struct x86_emulate_ctxt *ctxt)
+{
+    switch ( reg )
+    {
+    case 0xc0000080: /* EFER */
+        *val = ctxt->addr_size > 32 ? 0x500 /* LME|LMA */ : 0;
+        return X86EMUL_OKAY;
+    }
+
+    return X86EMUL_UNHANDLEABLE;
+}
+
 static struct x86_emulate_ops emulops = {
     .read       = read,
     .insn_fetch = fetch,
     .write      = write,
     .cmpxchg    = cmpxchg,
+    .read_segment = read_segment,
     .cpuid      = emul_test_cpuid,
     .read_cr    = emul_test_read_cr,
+    .read_msr   = read_msr,
     .get_fpu    = emul_test_get_fpu,
 };
 
@@ -611,6 +684,156 @@ int main(int argc, char **argv)
         goto fail;
     printf("okay\n");
 
+    printf("%-40s", "Testing lar (null selector)...");
+    instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc1;
+    regs.eflags = 0x240;
+    regs.eip    = (unsigned long)&instr[0];
+    regs.ecx    = 0;
+    regs.eax    = 0x11111111;
+    rc = x86_emulate(&ctxt, &emulops);
+    if ( (rc != X86EMUL_OKAY) ||
+         (regs.eax != 0x11111111) ||
+         (regs.eflags != 0x200) ||
+         (regs.eip != (unsigned long)&instr[3]) )
+        goto fail;
+    printf("okay\n");
+
+    printf("%-40s", "Testing lsl (null selector)...");
+    instr[0] = 0x0f; instr[1] = 0x03; instr[2] = 0xca;
+    regs.eflags = 0x240;
+    regs.eip    = (unsigned long)&instr[0];
+    regs.edx    = 0;
+    regs.ecx    = 0x11111111;
+    rc = x86_emulate(&ctxt, &emulops);
+    if ( (rc != X86EMUL_OKAY) ||
+         (regs.ecx != 0x11111111) ||
+         (regs.eflags != 0x200) ||
+         (regs.eip != (unsigned long)&instr[3]) )
+        goto fail;
+    printf("okay\n");
+
+    printf("%-40s", "Testing verr (null selector)...");
+    instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0x21;
+    regs.eflags = 0x240;
+    regs.eip    = (unsigned long)&instr[0];
+    regs.ecx    = (unsigned long)res;
+    *res        = 0;
+    rc = x86_emulate(&ctxt, &emulops);
+    if ( (rc != X86EMUL_OKAY) ||
+         (regs.eflags != 0x200) ||
+         (regs.eip != (unsigned long)&instr[3]) )
+        goto fail;
+    printf("okay\n");
+
+    printf("%-40s", "Testing verw (null selector)...");
+    instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0x2a;
+    regs.eflags = 0x240;
+    regs.eip    = (unsigned long)&instr[0];
+    regs.ecx    = 0;
+    regs.edx    = (unsigned long)res;
+    rc = x86_emulate(&ctxt, &emulops);
+    if ( (rc != X86EMUL_OKAY) ||
+         (regs.eflags != 0x200) ||
+         (regs.eip != (unsigned long)&instr[3]) )
+        goto fail;
+    printf("okay\n");
+
+    printf("%-40s", "Testing lar/lsl/verr/verw (all types)...");
+    for ( i = 0; i < 0x20; ++i )
+    {
+        unsigned int sel = i < 0x10 ?
+#ifndef __x86_64__
+                                      (i << 3) + 8
+#else
+                                      (i << 4) + 8
+#endif
+                                    : ((i - 0x10) << 3) | 4;
+        bool failed;
+
+#ifndef __x86_64__
+# define LAR_VALID 0xffff1a3eU
+# define LSL_VALID 0xffff0a0eU
+#else
+# define LAR_VALID 0xffff1a04U
+# define LSL_VALID 0xffff0a04U
+#endif
+#define VERR_VALID 0xccff0000U
+#define VERW_VALID 0x00cc0000U
+
+        instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc2;
+        regs.eflags = (LAR_VALID >> i) & 1 ? 0x200 : 0x240;
+        regs.eip    = (unsigned long)&instr[0];
+        regs.edx    = sel;
+        regs.eax    = 0x11111111;
+        rc = x86_emulate(&ctxt, &emulops);
+        if ( (rc != X86EMUL_OKAY) ||
+             (regs.eip != (unsigned long)&instr[3]) )
+            goto fail;
+        if ( (LAR_VALID >> i) & 1 )
+            failed = (regs.eflags != 0x240) ||
+                     ((regs.eax & 0xf0ff00) != (i << 8));
+        else
+            failed = (regs.eflags != 0x200) ||
+                     (regs.eax != 0x11111111);
+        if ( failed )
+        {
+            printf("LAR %04x (type %02x) ", sel, i);
+            goto fail;
+        }
+
+        instr[0] = 0x0f; instr[1] = 0x03; instr[2] = 0xd1;
+        regs.eflags = (LSL_VALID >> i) & 1 ? 0x200 : 0x240;
+        regs.eip    = (unsigned long)&instr[0];
+        regs.ecx    = sel;
+        regs.edx    = 0x11111111;
+        rc = x86_emulate(&ctxt, &emulops);
+        if ( (rc != X86EMUL_OKAY) ||
+             (regs.eip != (unsigned long)&instr[3]) )
+            goto fail;
+        if ( (LSL_VALID >> i) & 1 )
+            failed = (regs.eflags != 0x240) ||
+                     (regs.edx != (i & 0xf));
+        else
+            failed = (regs.eflags != 0x200) ||
+                     (regs.edx != 0x11111111);
+        if ( failed )
+        {
+            printf("LSL %04x (type %02x) ", sel, i);
+            goto fail;
+        }
+
+        instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0xe2;
+        regs.eflags = (VERR_VALID >> i) & 1 ? 0x200 : 0x240;
+        regs.eip    = (unsigned long)&instr[0];
+        regs.ecx    = 0;
+        regs.edx    = sel;
+        rc = x86_emulate(&ctxt, &emulops);
+        if ( (rc != X86EMUL_OKAY) ||
+             (regs.eip != (unsigned long)&instr[3]) )
+            goto fail;
+        if ( regs.eflags != ((VERR_VALID >> i) & 1 ? 0x240 : 0x200) )
+        {
+            printf("VERR %04x (type %02x) ", sel, i);
+            goto fail;
+        }
+
+        instr[0] = 0x0f; instr[1] = 0x00; instr[2] = 0xe9;
+        regs.eflags = (VERW_VALID >> i) & 1 ? 0x200 : 0x240;
+        regs.eip    = (unsigned long)&instr[0];
+        regs.ecx    = sel;
+        regs.edx    = 0;
+        rc = x86_emulate(&ctxt, &emulops);
+        if ( (rc != X86EMUL_OKAY) ||
+             (regs.eip != (unsigned long)&instr[3]) )
+            goto fail;
+        if ( regs.eflags != ((VERW_VALID >> i) & 1 ? 0x240 : 0x200) )
+        {
+            printf("VERW %04x (type %02x) ", sel, i);
+            goto fail;
+        }
+    }
+    printf("okay\n");
+
 #define decl_insn(which) extern const unsigned char which[], which##_len[]
 #define put_insn(which, insn) ".pushsection .test, \"ax\", @progbits\n" \
                               #which ": " insn "\n"                     \
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -182,7 +182,7 @@ static const opcode_desc_t opcode_table[
 
 static const opcode_desc_t twobyte_table[256] = {
     /* 0x00 - 0x07 */
-    ModRM, ImplicitOps|ModRM, ModRM, ModRM,
+    ModRM, ImplicitOps|ModRM, DstReg|SrcMem16|ModRM, DstReg|SrcMem16|ModRM,
     0, ImplicitOps, ImplicitOps, ImplicitOps,
     /* 0x08 - 0x0F */
     ImplicitOps, ImplicitOps, 0, ImplicitOps,
@@ -1365,6 +1365,11 @@ realmode_load_seg(
     return rc;
 }
 
+/*
+ * Passing in x86_seg_none means
+ * - suppress any exceptions other than #PF,
+ * - don't commit any state.
+ */
 static int
 protmode_load_seg(
     enum x86_segment seg,
@@ -1407,7 +1412,7 @@ protmode_load_seg(
     }
 
     /* System segment descriptors must reside in the GDT. */
-    if ( !is_x86_user_segment(seg) && (sel & 4) )
+    if ( is_x86_system_segment(seg) && (sel & 4) )
         goto raise_exn;
 
     switch ( rc = ops->read(sel_seg, sel & 0xfff8, &desc, sizeof(desc), ctxt) )
@@ -1424,14 +1429,11 @@ protmode_load_seg(
         return rc;
     }
 
-    if ( !is_x86_user_segment(seg) )
-    {
-        /* System segments must have S flag == 0. */
-        if ( desc.b & (1u << 12) )
-            goto raise_exn;
-    }
+    /* System segments must have S flag == 0. */
+    if ( is_x86_system_segment(seg) && (desc.b & (1u << 12)) )
+        goto raise_exn;
     /* User segments must have S flag == 1. */
-    else if ( !(desc.b & (1u << 12)) )
+    if ( is_x86_user_segment(seg) && !(desc.b & (1u << 12)) )
         goto raise_exn;
 
     dpl = (desc.b >> 13) & 3;
@@ -1493,10 +1495,17 @@ protmode_load_seg(
              ((dpl < cpl) || (dpl < rpl)) )
             goto raise_exn;
         break;
+    case x86_seg_none:
+        /* Non-conforming segment: check DPL against RPL and CPL. */
+        if ( ((desc.b & (0x1c << 8)) != (0x1c << 8)) &&
+             ((dpl < cpl) || (dpl < rpl)) )
+            return X86EMUL_EXCEPTION;
+        a_flag = 0;
+        break;
     }
 
     /* Segment present in memory? */
-    if ( !(desc.b & (1 << 15)) )
+    if ( !(desc.b & (1 << 15)) && seg != x86_seg_none )
     {
         fault_type = seg != x86_seg_ss ? EXC_NP : EXC_SS;
         goto raise_exn;
@@ -1504,7 +1513,7 @@ protmode_load_seg(
 
     if ( !is_x86_user_segment(seg) )
     {
-        int lm = in_longmode(ctxt, ops);
+        int lm = (desc.b & (1u << 12)) ? 0 : in_longmode(ctxt, ops);
 
         if ( lm < 0 )
             return X86EMUL_UNHANDLEABLE;
@@ -1524,7 +1533,8 @@ protmode_load_seg(
                 return rc;
             }
             if ( (desc_hi.b & 0x00001f00) ||
-                 !is_canonical_address((uint64_t)desc_hi.a << 32) )
+                 (seg != x86_seg_none &&
+                  !is_canonical_address((uint64_t)desc_hi.a << 32)) )
                 goto raise_exn;
         }
     }
@@ -1567,7 +1577,8 @@ protmode_load_seg(
     return X86EMUL_OKAY;
 
  raise_exn:
-    generate_exception(fault_type, sel & 0xfffc);
+    generate_exception_if(seg != x86_seg_none, fault_type, sel & 0xfffc);
+    rc = X86EMUL_EXCEPTION;
  done:
     return rc;
 }
@@ -4421,6 +4432,29 @@ x86_emulate(
             if ( (rc = load_seg(seg, src.val, 0, NULL, ctxt, ops)) != 0 )
                 goto done;
             break;
+        case 4: /* verr / verw */
+            _regs.eflags &= ~EFLG_ZF;
+            switch ( rc = protmode_load_seg(x86_seg_none, src.val, false,
+                                            &sreg, ctxt, ops) )
+            {
+            case X86EMUL_OKAY:
+                if ( sreg.attr.fields.s &&
+                     ((modrm_reg & 1) ? ((sreg.attr.fields.type & 0xa) == 0x2)
+                                      : ((sreg.attr.fields.type & 0xa) != 
0x8)) )
+                    _regs.eflags |= EFLG_ZF;
+                break;
+            case X86EMUL_EXCEPTION:
+                if ( ctxt->event_pending )
+                {
+                    ASSERT(ctxt->event.vector == EXC_PF);
+            default:
+                    goto done;
+                }
+                /* Instead of the exception, ZF remains cleared. */
+                rc = X86EMUL_OKAY;
+                break;
+            }
+            break;
         default:
             generate_exception_if(true, EXC_UD);
             break;
@@ -4629,6 +4663,98 @@ x86_emulate(
         break;
     }
 
+    case X86EMUL_OPC(0x0f, 0x02): /* lar */
+        generate_exception_if(!in_protmode(ctxt, ops), EXC_UD);
+        _regs.eflags &= ~EFLG_ZF;
+        switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg,
+                                        ctxt, ops) )
+        {
+        case X86EMUL_OKAY:
+            if ( !sreg.attr.fields.s )
+            {
+                switch ( sreg.attr.fields.type )
+                {
+                case 0x01: /* available 16-bit TSS */
+                case 0x03: /* busy 16-bit TSS */
+                case 0x04: /* 16-bit call gate */
+                case 0x05: /* 16/32-bit task gate */
+                    if ( in_longmode(ctxt, ops) )
+                        break;
+                    /* fall through */
+                case 0x02: /* LDT */
+                case 0x09: /* available 32/64-bit TSS */
+                case 0x0b: /* busy 32/64-bit TSS */
+                case 0x0c: /* 32/64-bit call gate */
+                    _regs.eflags |= EFLG_ZF;
+                    break;
+                }
+            }
+            else
+                _regs.eflags |= EFLG_ZF;
+            break;
+        case X86EMUL_EXCEPTION:
+            if ( ctxt->event_pending )
+            {
+                ASSERT(ctxt->event.vector == EXC_PF);
+        default:
+                goto done;
+            }
+            /* Instead of the exception, ZF remains cleared. */
+            rc = X86EMUL_OKAY;
+            break;
+        }
+        if ( _regs.eflags & EFLG_ZF )
+            dst.val = ((sreg.attr.bytes & 0xff) << 8) |
+                      ((sreg.limit >> (sreg.attr.fields.g ? 12 : 0)) &
+                       0xf0000) |
+                      ((sreg.attr.bytes & 0xf00) << 12);
+        else
+            dst.type = OP_NONE;
+        break;
+
+    case X86EMUL_OPC(0x0f, 0x03): /* lsl */
+        generate_exception_if(!in_protmode(ctxt, ops), EXC_UD);
+        _regs.eflags &= ~EFLG_ZF;
+        switch ( rc = protmode_load_seg(x86_seg_none, src.val, false, &sreg,
+                                        ctxt, ops) )
+        {
+        case X86EMUL_OKAY:
+            if ( !sreg.attr.fields.s )
+            {
+                switch ( sreg.attr.fields.type )
+                {
+                case 0x01: /* available 16-bit TSS */
+                case 0x03: /* busy 16-bit TSS */
+                    if ( in_longmode(ctxt, ops) )
+                        break;
+                    /* fall through */
+                case 0x02: /* LDT */
+                case 0x09: /* available 32/64-bit TSS */
+                case 0x0b: /* busy 32/64-bit TSS */
+                    _regs.eflags |= EFLG_ZF;
+                    break;
+                }
+            }
+            else
+                _regs.eflags |= EFLG_ZF;
+            break;
+        case X86EMUL_EXCEPTION:
+            if ( ctxt->event_pending )
+            {
+                ASSERT(ctxt->event.vector == EXC_PF);
+        default:
+                goto done;
+            }
+            /* Instead of the exception, ZF remains cleared. */
+            rc = X86EMUL_OKAY;
+            break;
+        }
+        if ( _regs.eflags & EFLG_ZF )
+            dst.val = sreg.limit;
+        else
+            dst.type = OP_NONE;
+        break;
+
     case X86EMUL_OPC(0x0f, 0x05): /* syscall */ {
         uint64_t msr_content;
 


Attachment: x86emul-lar-lsl-verX.patch
Description: Text document

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.