KVM: x86 emulator: handle "far address" source operand
[deliverable/linux.git] / arch / x86 / kvm / emulate.c
index 5ac0bb465ed67fd725881ccacfeb22e44597f4cf..687ea0906b79291ca4a3af387f610eedb98db28c 100644 (file)
@@ -67,6 +67,8 @@
 #define SrcImmUByte (8<<4)      /* 8-bit unsigned immediate operand. */
 #define SrcImmU     (9<<4)      /* Immediate operand, unsigned */
 #define SrcSI       (0xa<<4)   /* Source is in the DS:RSI */
+#define SrcImmFAddr (0xb<<4)   /* Source is immediate far address */
+#define SrcMemFAddr (0xc<<4)   /* Source is far address in memory */
 #define SrcMask     (0xf<<4)
 /* Generic ModRM decode. */
 #define ModRM       (1<<8)
 #define Src2CL      (1<<29)
 #define Src2ImmByte (2<<29)
 #define Src2One     (3<<29)
-#define Src2Imm16   (4<<29)
-#define Src2Mem16   (5<<29) /* Used for Ep encoding. First argument has to be
-                              in memory and second argument is located
-                              immediately after the first one in memory. */
 #define Src2Mask    (7<<29)
 
 enum {
@@ -171,11 +169,11 @@ static u32 opcode_table[256] = {
        ByteOp | DstMem | SrcReg | ModRM | Mov, DstMem | SrcReg | ModRM | Mov,
        ByteOp | DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
        DstMem | SrcReg | ModRM | Mov, ModRM | DstReg,
-       DstReg | SrcMem | ModRM | Mov, Group | Group1A,
+       ImplicitOps | SrcMem | ModRM, Group | Group1A,
        /* 0x90 - 0x97 */
        DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg,
        /* 0x98 - 0x9F */
-       0, 0, SrcImm | Src2Imm16 | No64, 0,
+       0, 0, SrcImmFAddr | No64, 0,
        ImplicitOps | Stack, ImplicitOps | Stack, 0, 0,
        /* 0xA0 - 0xA7 */
        ByteOp | DstReg | SrcMem | Mov | MemAbs, DstReg | SrcMem | Mov | MemAbs,
@@ -215,7 +213,7 @@ static u32 opcode_table[256] = {
        ByteOp | SrcImmUByte | DstAcc, SrcImmUByte | DstAcc,
        /* 0xE8 - 0xEF */
        SrcImm | Stack, SrcImm | ImplicitOps,
-       SrcImmU | Src2Imm16 | No64, SrcImmByte | ImplicitOps,
+       SrcImmFAddr | No64, SrcImmByte | ImplicitOps,
        SrcNone | ByteOp | DstAcc, SrcNone | DstAcc,
        SrcNone | ByteOp | DstAcc, SrcNone | DstAcc,
        /* 0xF0 - 0xF7 */
@@ -350,7 +348,7 @@ static u32 group_table[] = {
        [Group5*8] =
        DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM,
        SrcMem | ModRM | Stack, 0,
-       SrcMem | ModRM | Stack, SrcMem | ModRM | Src2Mem16 | ImplicitOps,
+       SrcMem | ModRM | Stack, SrcMemFAddr | ModRM | ImplicitOps,
        SrcMem | ModRM | Stack, 0,
        [Group7*8] =
        0, 0, ModRM | SrcMem | Priv, ModRM | SrcMem | Priv,
@@ -576,6 +574,13 @@ static u32 group2_table[] = {
        (_type)_x;                                                      \
 })
 
+#define insn_fetch_arr(_arr, _size, _eip)                                \
+({     rc = do_insn_fetch(ctxt, ops, (_eip), _arr, (_size));           \
+       if (rc != X86EMUL_CONTINUE)                                     \
+               goto done;                                              \
+       (_eip) += (_size);                                              \
+})
+
 static inline unsigned long ad_mask(struct decode_cache *c)
 {
        return (1UL << (c->ad_bytes << 3)) - 1;
@@ -1160,6 +1165,17 @@ done_prefixes:
                                         c->regs[VCPU_REGS_RSI]);
                c->src.val = 0;
                break;
+       case SrcImmFAddr:
+               c->src.type = OP_IMM;
+               c->src.ptr = (unsigned long *)c->eip;
+               c->src.bytes = c->op_bytes + 2;
+               insn_fetch_arr(c->src.valptr, c->src.bytes, c->eip);
+               break;
+       case SrcMemFAddr:
+               c->src.type = OP_MEM;
+               c->src.ptr = (unsigned long *)c->modrm_ea;
+               c->src.bytes = c->op_bytes + 2;
+               break;
        }
 
        /*
@@ -1179,22 +1195,10 @@ done_prefixes:
                c->src2.bytes = 1;
                c->src2.val = insn_fetch(u8, 1, c->eip);
                break;
-       case Src2Imm16:
-               c->src2.type = OP_IMM;
-               c->src2.ptr = (unsigned long *)c->eip;
-               c->src2.bytes = 2;
-               c->src2.val = insn_fetch(u16, 2, c->eip);
-               break;
        case Src2One:
                c->src2.bytes = 1;
                c->src2.val = 1;
                break;
-       case Src2Mem16:
-               c->src2.type = OP_MEM;
-               c->src2.bytes = 2;
-               c->src2.ptr = (unsigned long *)(c->modrm_ea + c->src.bytes);
-               c->src2.val = 0;
-               break;
        }
 
        /* Decode and fetch the destination operand: register or memory. */
@@ -1263,6 +1267,33 @@ done:
        return (rc == X86EMUL_UNHANDLEABLE) ? -1 : 0;
 }
 
+static int read_emulated(struct x86_emulate_ctxt *ctxt,
+                        struct x86_emulate_ops *ops,
+                        unsigned long addr, void *dest, unsigned size)
+{
+       int rc;
+       struct read_cache *mc = &ctxt->decode.mem_read;
+
+       while (size) {
+               int n = min(size, 8u);
+               size -= n;
+               if (mc->pos < mc->end)
+                       goto read_cached;
+
+               rc = ops->read_emulated(addr, mc->data + mc->end, n, ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
+                       return rc;
+               mc->end += n;
+
+       read_cached:
+               memcpy(dest, mc->data + mc->pos, n);
+               mc->pos += n;
+               dest += n;
+               addr += n;
+       }
+       return X86EMUL_CONTINUE;
+}
+
 static int pio_in_emulated(struct x86_emulate_ctxt *ctxt,
                           struct x86_emulate_ops *ops,
                           unsigned int size, unsigned short port,
@@ -1504,9 +1535,9 @@ static int emulate_pop(struct x86_emulate_ctxt *ctxt,
        struct decode_cache *c = &ctxt->decode;
        int rc;
 
-       rc = ops->read_emulated(register_address(c, ss_base(ctxt),
-                                                c->regs[VCPU_REGS_RSP]),
-                               dest, len, ctxt->vcpu);
+       rc = read_emulated(ctxt, ops, register_address(c, ss_base(ctxt),
+                                                      c->regs[VCPU_REGS_RSP]),
+                          dest, len);
        if (rc != X86EMUL_CONTINUE)
                return rc;
 
@@ -2475,6 +2506,7 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
        int saved_dst_type = c->dst.type;
 
        ctxt->interruptibility = 0;
+       ctxt->decode.mem_read.pos = 0;
 
        /* Shadow copy of register state. Committed on successful emulation.
         * NOTE: we can copy them from vcpu as x86_decode_insn() doesn't
@@ -2529,20 +2561,16 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
        }
 
        if (c->src.type == OP_MEM) {
-               rc = ops->read_emulated((unsigned long)c->src.ptr,
-                                       &c->src.val,
-                                       c->src.bytes,
-                                       ctxt->vcpu);
+               rc = read_emulated(ctxt, ops, (unsigned long)c->src.ptr,
+                                       c->src.valptr, c->src.bytes);
                if (rc != X86EMUL_CONTINUE)
                        goto done;
                c->src.orig_val = c->src.val;
        }
 
        if (c->src2.type == OP_MEM) {
-               rc = ops->read_emulated((unsigned long)c->src2.ptr,
-                                       &c->src2.val,
-                                       c->src2.bytes,
-                                       ctxt->vcpu);
+               rc = read_emulated(ctxt, ops, (unsigned long)c->src2.ptr,
+                                       &c->src2.val, c->src2.bytes);
                if (rc != X86EMUL_CONTINUE)
                        goto done;
        }
@@ -2553,8 +2581,8 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
 
        if ((c->dst.type == OP_MEM) && !(c->d & Mov)) {
                /* optimisation - avoid slow emulated read if Mov */
-               rc = ops->read_emulated((unsigned long)c->dst.ptr, &c->dst.val,
-                                       c->dst.bytes, ctxt->vcpu);
+               rc = read_emulated(ctxt, ops, (unsigned long)c->dst.ptr,
+                                  &c->dst.val, c->dst.bytes);
                if (rc != X86EMUL_CONTINUE)
                        goto done;
        }
@@ -2775,13 +2803,13 @@ special_insn:
                        goto done;
                break;
        case 0x90: /* nop / xchg r8,rax */
-               if (!(c->rex_prefix & 1)) { /* nop */
-                       c->dst.type = OP_NONE;
+               if (c->dst.ptr == (unsigned long *)&c->regs[VCPU_REGS_RAX]) {
+                       c->dst.type = OP_NONE;  /* nop */
                        break;
                }
        case 0x91 ... 0x97: /* xchg reg,rax */
-               c->src.type = c->dst.type = OP_REG;
-               c->src.bytes = c->dst.bytes = c->op_bytes;
+               c->src.type = OP_REG;
+               c->src.bytes = c->op_bytes;
                c->src.ptr = (unsigned long *) &c->regs[VCPU_REGS_RAX];
                c->src.val = *(c->src.ptr);
                goto xchg;
@@ -2860,14 +2888,18 @@ special_insn:
        }
        case 0xe9: /* jmp rel */
                goto jmp;
-       case 0xea: /* jmp far */
+       case 0xea: { /* jmp far */
+               unsigned short sel;
        jump_far:
-               if (load_segment_descriptor(ctxt, ops, c->src2.val,
-                                           VCPU_SREG_CS))
+               memcpy(&sel, c->src.valptr + c->op_bytes, 2);
+
+               if (load_segment_descriptor(ctxt, ops, sel, VCPU_SREG_CS))
                        goto done;
 
-               c->eip = c->src.val;
+               c->eip = 0;
+               memcpy(&c->eip, c->src.valptr, c->op_bytes);
                break;
+       }
        case 0xeb:
              jmp:              /* jmp rel short */
                jmp_rel(c, c->src.val);
@@ -2981,7 +3013,11 @@ writeback:
                    (rc->end != 0 && rc->end == rc->pos))
                        ctxt->restart = false;
        }
-
+       /*
+        * reset read cache here in case string instruction is restared
+        * without decoding
+        */
+       ctxt->decode.mem_read.end = 0;
        /* Commit shadow register state. */
        memcpy(ctxt->vcpu->arch.regs, c->regs, sizeof c->regs);
        kvm_rip_write(ctxt->vcpu, c->eip);
This page took 0.043767 seconds and 5 git commands to generate.