]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/x86/kvm/emulate.c
KVM: x86 emulator: Add Virtual-8086 mode of emulation
[karo-tx-linux.git] / arch / x86 / kvm / emulate.c
index 7e8faea4651e1a89dc2755f044681b11f94a78e2..e4e2df3b60381a623a6ecc5827d6de1410d36a62 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/module.h>
 #include <asm/kvm_emulate.h>
 
-#include "mmu.h"               /* for is_long_mode() */
+#include "x86.h"
 
 /*
  * Opcode effective-address decode tables.
@@ -88,6 +88,7 @@
 enum {
        Group1_80, Group1_81, Group1_82, Group1_83,
        Group1A, Group3_Byte, Group3, Group4, Group5, Group7,
+       Group8, Group9,
 };
 
 static u32 opcode_table[256] = {
@@ -267,11 +268,12 @@ static u32 twobyte_table[256] = {
        0, 0, ByteOp | DstReg | SrcMem | ModRM | Mov,
            DstReg | SrcMem16 | ModRM | Mov,
        /* 0xB8 - 0xBF */
-       0, 0, DstMem | SrcImmByte | ModRM, DstMem | SrcReg | ModRM | BitOp,
+       0, 0, Group | Group8, DstMem | SrcReg | ModRM | BitOp,
        0, 0, ByteOp | DstReg | SrcMem | ModRM | Mov,
            DstReg | SrcMem16 | ModRM | Mov,
        /* 0xC0 - 0xCF */
-       0, 0, 0, DstMem | SrcReg | ModRM | Mov, 0, 0, 0, ImplicitOps | ModRM,
+       0, 0, 0, DstMem | SrcReg | ModRM | Mov,
+       0, 0, 0, Group | GroupDual | Group9,
        0, 0, 0, 0, 0, 0, 0, 0,
        /* 0xD0 - 0xDF */
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -323,6 +325,12 @@ static u32 group_table[] = {
        0, 0, ModRM | SrcMem, ModRM | SrcMem,
        SrcNone | ModRM | DstMem | Mov, 0,
        SrcMem16 | ModRM | Mov, SrcMem | ModRM | ByteOp,
+       [Group8*8] =
+       0, 0, 0, 0,
+       DstMem | SrcImmByte | ModRM, DstMem | SrcImmByte | ModRM,
+       DstMem | SrcImmByte | ModRM, DstMem | SrcImmByte | ModRM,
+       [Group9*8] =
+       0, ImplicitOps | ModRM, 0, 0, 0, 0, 0, 0,
 };
 
 static u32 group2_table[] = {
@@ -330,6 +338,8 @@ static u32 group2_table[] = {
        SrcNone | ModRM, 0, 0, SrcNone | ModRM,
        SrcNone | ModRM | DstMem | Mov, 0,
        SrcMem16 | ModRM | Mov, 0,
+       [Group9*8] =
+       0, 0, 0, 0, 0, 0, 0, 0,
 };
 
 /* EFLAGS bit definitions. */
@@ -889,6 +899,7 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
 
        switch (mode) {
        case X86EMUL_MODE_REAL:
+       case X86EMUL_MODE_VM86:
        case X86EMUL_MODE_PROT16:
                def_op_bytes = def_ad_bytes = 2;
                break;
@@ -1196,7 +1207,7 @@ static int emulate_pop(struct x86_emulate_ctxt *ctxt,
        rc = ops->read_emulated(register_address(c, ss_base(ctxt),
                                                 c->regs[VCPU_REGS_RSP]),
                                dest, len, ctxt->vcpu);
-       if (rc != 0)
+       if (rc != X86EMUL_CONTINUE)
                return rc;
 
        register_address_increment(c, &c->regs[VCPU_REGS_RSP], len);
@@ -1370,7 +1381,7 @@ static inline int emulate_grp9(struct x86_emulate_ctxt *ctxt,
        int rc;
 
        rc = ops->read_emulated(memop, &old, 8, ctxt->vcpu);
-       if (rc != 0)
+       if (rc != X86EMUL_CONTINUE)
                return rc;
 
        if (((u32) (old >> 0) != (u32) c->regs[VCPU_REGS_RAX]) ||
@@ -1385,7 +1396,7 @@ static inline int emulate_grp9(struct x86_emulate_ctxt *ctxt,
                       (u32) c->regs[VCPU_REGS_RBX];
 
                rc = ops->cmpxchg_emulated(memop, &old, &new, 8, ctxt->vcpu);
-               if (rc != 0)
+               if (rc != X86EMUL_CONTINUE)
                        return rc;
                ctxt->eflags |= EFLG_ZF;
        }
@@ -1451,7 +1462,7 @@ static inline int writeback(struct x86_emulate_ctxt *ctxt,
                                        &c->dst.val,
                                        c->dst.bytes,
                                        ctxt->vcpu);
-               if (rc != 0)
+               if (rc != X86EMUL_CONTINUE)
                        return rc;
                break;
        case OP_NONE:
@@ -1515,7 +1526,7 @@ emulate_syscall(struct x86_emulate_ctxt *ctxt)
 
        /* syscall is not available in real mode */
        if (c->lock_prefix || ctxt->mode == X86EMUL_MODE_REAL
-               || !(ctxt->vcpu->arch.cr0 & X86_CR0_PE))
+           || ctxt->mode == X86EMUL_MODE_VM86)
                return -1;
 
        setup_syscalls_segments(ctxt, &cs, &ss);
@@ -1567,9 +1578,8 @@ emulate_sysenter(struct x86_emulate_ctxt *ctxt)
        if (c->lock_prefix)
                return -1;
 
-       /* inject #GP if in real mode or paging is disabled */
-       if (ctxt->mode == X86EMUL_MODE_REAL ||
-               !(ctxt->vcpu->arch.cr0 & X86_CR0_PE)) {
+       /* inject #GP if in real mode */
+       if (ctxt->mode == X86EMUL_MODE_REAL) {
                kvm_inject_gp(ctxt->vcpu, 0);
                return -1;
        }
@@ -1633,9 +1643,9 @@ emulate_sysexit(struct x86_emulate_ctxt *ctxt)
        if (c->lock_prefix)
                return -1;
 
-       /* inject #GP if in real mode or paging is disabled */
-       if (ctxt->mode == X86EMUL_MODE_REAL
-               || !(ctxt->vcpu->arch.cr0 & X86_CR0_PE)) {
+       /* inject #GP if in real mode or Virtual 8086 mode */
+       if (ctxt->mode == X86EMUL_MODE_REAL ||
+           ctxt->mode == X86EMUL_MODE_VM86) {
                kvm_inject_gp(ctxt->vcpu, 0);
                return -1;
        }
@@ -1749,7 +1759,7 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
                                        &c->src.val,
                                        c->src.bytes,
                                        ctxt->vcpu);
-               if (rc != 0)
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
                c->src.orig_val = c->src.val;
        }
@@ -1768,12 +1778,15 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
                        c->dst.ptr = (void *)c->dst.ptr +
                                                   (c->src.val & mask) / 8;
                }
-               if (!(c->d & Mov) &&
-                                  /* optimisation - avoid slow emulated read */
-                   ((rc = ops->read_emulated((unsigned long)c->dst.ptr,
-                                          &c->dst.val,
-                                         c->dst.bytes, ctxt->vcpu)) != 0))
-                       goto done;
+               if (!(c->d & Mov)) {
+                       /* optimisation - avoid slow emulated read */
+                       rc = ops->read_emulated((unsigned long)c->dst.ptr,
+                                               &c->dst.val,
+                                               c->dst.bytes,
+                                               ctxt->vcpu);
+                       if (rc != X86EMUL_CONTINUE)
+                               goto done;
+               }
        }
        c->dst.orig_val = c->dst.val;
 
@@ -2039,11 +2052,12 @@ special_insn:
                c->dst.ptr = (unsigned long *)register_address(c,
                                                   es_base(ctxt),
                                                   c->regs[VCPU_REGS_RDI]);
-               if ((rc = ops->read_emulated(register_address(c,
-                                          seg_override_base(ctxt, c),
-                                       c->regs[VCPU_REGS_RSI]),
+               rc = ops->read_emulated(register_address(c,
+                                               seg_override_base(ctxt, c),
+                                               c->regs[VCPU_REGS_RSI]),
                                        &c->dst.val,
-                                       c->dst.bytes, ctxt->vcpu)) != 0)
+                                       c->dst.bytes, ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
                register_address_increment(c, &c->regs[VCPU_REGS_RSI],
                                       (ctxt->eflags & EFLG_DF) ? -c->dst.bytes
@@ -2058,10 +2072,11 @@ special_insn:
                c->src.ptr = (unsigned long *)register_address(c,
                                       seg_override_base(ctxt, c),
                                                   c->regs[VCPU_REGS_RSI]);
-               if ((rc = ops->read_emulated((unsigned long)c->src.ptr,
-                                               &c->src.val,
-                                               c->src.bytes,
-                                               ctxt->vcpu)) != 0)
+               rc = ops->read_emulated((unsigned long)c->src.ptr,
+                                       &c->src.val,
+                                       c->src.bytes,
+                                       ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
 
                c->dst.type = OP_NONE; /* Disable writeback. */
@@ -2069,10 +2084,11 @@ special_insn:
                c->dst.ptr = (unsigned long *)register_address(c,
                                                   es_base(ctxt),
                                                   c->regs[VCPU_REGS_RDI]);
-               if ((rc = ops->read_emulated((unsigned long)c->dst.ptr,
-                                               &c->dst.val,
-                                               c->dst.bytes,
-                                               ctxt->vcpu)) != 0)
+               rc = ops->read_emulated((unsigned long)c->dst.ptr,
+                                       &c->dst.val,
+                                       c->dst.bytes,
+                                       ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
 
                DPRINTF("cmps: mem1=0x%p mem2=0x%p\n", c->src.ptr, c->dst.ptr);
@@ -2102,12 +2118,13 @@ special_insn:
                c->dst.type = OP_REG;
                c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
                c->dst.ptr = (unsigned long *)&c->regs[VCPU_REGS_RAX];
-               if ((rc = ops->read_emulated(register_address(c,
-                                                seg_override_base(ctxt, c),
-                                                c->regs[VCPU_REGS_RSI]),
-                                                &c->dst.val,
-                                                c->dst.bytes,
-                                                ctxt->vcpu)) != 0)
+               rc = ops->read_emulated(register_address(c,
+                                               seg_override_base(ctxt, c),
+                                               c->regs[VCPU_REGS_RSI]),
+                                       &c->dst.val,
+                                       c->dst.bytes,
+                                       ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
                register_address_increment(c, &c->regs[VCPU_REGS_RSI],
                                       (ctxt->eflags & EFLG_DF) ? -c->dst.bytes