]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/x86/kvm/emulate.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[mv-sheeva.git] / arch / x86 / kvm / emulate.c
index 0579d9dd9aac3801036e724e108eb9cdc8798b81..5ac0bb465ed67fd725881ccacfeb22e44597f4cf 100644 (file)
@@ -52,6 +52,7 @@
 #define DstMem      (3<<1)     /* Memory operand. */
 #define DstAcc      (4<<1)      /* Destination Accumulator */
 #define DstDI       (5<<1)     /* Destination is in ES:(E)DI */
+#define DstMem64    (6<<1)     /* 64bit memory operand */
 #define DstMask     (7<<1)
 /* Source operand type. */
 #define SrcNone     (0<<4)     /* No source operand. */
@@ -360,7 +361,7 @@ static u32 group_table[] = {
        DstMem | SrcImmByte | ModRM, DstMem | SrcImmByte | ModRM | Lock,
        DstMem | SrcImmByte | ModRM | Lock, DstMem | SrcImmByte | ModRM | Lock,
        [Group9*8] =
-       0, ImplicitOps | ModRM | Lock, 0, 0, 0, 0, 0, 0,
+       0, DstMem64 | ModRM | Lock, 0, 0, 0, 0, 0, 0,
 };
 
 static u32 group2_table[] = {
@@ -645,21 +646,22 @@ static unsigned long ss_base(struct x86_emulate_ctxt *ctxt)
 
 static int do_fetch_insn_byte(struct x86_emulate_ctxt *ctxt,
                              struct x86_emulate_ops *ops,
-                             unsigned long linear, u8 *dest)
+                             unsigned long eip, u8 *dest)
 {
        struct fetch_cache *fc = &ctxt->decode.fetch;
        int rc;
-       int size;
+       int size, cur_size;
 
-       if (linear < fc->start || linear >= fc->end) {
-               size = min(15UL, PAGE_SIZE - offset_in_page(linear));
-               rc = ops->fetch(linear, fc->data, size, ctxt->vcpu, NULL);
+       if (eip == fc->end) {
+               cur_size = fc->end - fc->start;
+               size = min(15UL - cur_size, PAGE_SIZE - offset_in_page(eip));
+               rc = ops->fetch(ctxt->cs_base + eip, fc->data + cur_size,
+                               size, ctxt->vcpu, NULL);
                if (rc != X86EMUL_CONTINUE)
                        return rc;
-               fc->start = linear;
-               fc->end = linear + size;
+               fc->end += size;
        }
-       *dest = fc->data[linear - fc->start];
+       *dest = fc->data[eip - fc->start];
        return X86EMUL_CONTINUE;
 }
 
@@ -672,7 +674,6 @@ static int do_insn_fetch(struct x86_emulate_ctxt *ctxt,
        /* x86 instructions are limited to 15 bytes. */
        if (eip + size - ctxt->eip > 15)
                return X86EMUL_UNHANDLEABLE;
-       eip += ctxt->cs_base;
        while (size--) {
                rc = do_fetch_insn_byte(ctxt, ops, eip++, dest++);
                if (rc != X86EMUL_CONTINUE)
@@ -927,10 +928,14 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
        int mode = ctxt->mode;
        int def_op_bytes, def_ad_bytes, group;
 
-       /* Shadow copy of register state. Committed on successful emulation. */
 
+       /* we cannot decode insn before we complete previous rep insn */
+       WARN_ON(ctxt->restart);
+
+       /* Shadow copy of register state. Committed on successful emulation. */
        memset(c, 0, sizeof(struct decode_cache));
        c->eip = ctxt->eip;
+       c->fetch.start = c->fetch.end = c->eip;
        ctxt->cs_base = seg_base(ctxt, VCPU_SREG_CS);
        memcpy(c->regs, ctxt->vcpu->arch.regs, sizeof c->regs);
 
@@ -1202,6 +1207,7 @@ done_prefixes:
                         c->twobyte && (c->b == 0xb6 || c->b == 0xb7));
                break;
        case DstMem:
+       case DstMem64:
                if ((c->d & ModRM) && c->modrm_mod == 3) {
                        c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
                        c->dst.type = OP_REG;
@@ -1211,7 +1217,10 @@ done_prefixes:
                }
                c->dst.type = OP_MEM;
                c->dst.ptr = (unsigned long *)c->modrm_ea;
-               c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
+               if ((c->d & DstMask) == DstMem64)
+                       c->dst.bytes = 8;
+               else
+                       c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
                c->dst.val = 0;
                if (c->d & BitOp) {
                        unsigned long mask = ~(c->dst.bytes * 8 - 1);
@@ -1254,6 +1263,36 @@ done:
        return (rc == X86EMUL_UNHANDLEABLE) ? -1 : 0;
 }
 
+static int pio_in_emulated(struct x86_emulate_ctxt *ctxt,
+                          struct x86_emulate_ops *ops,
+                          unsigned int size, unsigned short port,
+                          void *dest)
+{
+       struct read_cache *rc = &ctxt->decode.io_read;
+
+       if (rc->pos == rc->end) { /* refill pio read ahead */
+               struct decode_cache *c = &ctxt->decode;
+               unsigned int in_page, n;
+               unsigned int count = c->rep_prefix ?
+                       address_mask(c, c->regs[VCPU_REGS_RCX]) : 1;
+               in_page = (ctxt->eflags & EFLG_DF) ?
+                       offset_in_page(c->regs[VCPU_REGS_RDI]) :
+                       PAGE_SIZE - offset_in_page(c->regs[VCPU_REGS_RDI]);
+               n = min(min(in_page, (unsigned int)sizeof(rc->data)) / size,
+                       count);
+               if (n == 0)
+                       n = 1;
+               rc->pos = rc->end = 0;
+               if (!ops->pio_in_emulated(size, port, rc->data, n, ctxt->vcpu))
+                       return 0;
+               rc->end = n * size;
+       }
+
+       memcpy(dest, rc->data + rc->pos, size);
+       rc->pos += size;
+       return 1;
+}
+
 static u32 desc_limit_scaled(struct desc_struct *desc)
 {
        u32 limit = get_desc_limit(desc);
@@ -1673,12 +1712,7 @@ static inline int emulate_grp9(struct x86_emulate_ctxt *ctxt,
                               struct x86_emulate_ops *ops)
 {
        struct decode_cache *c = &ctxt->decode;
-       u64 old, new;
-       int rc;
-
-       rc = ops->read_emulated(c->modrm_ea, &old, 8, ctxt->vcpu);
-       if (rc != X86EMUL_CONTINUE)
-               return rc;
+       u64 old = c->dst.orig_val;
 
        if (((u32) (old >> 0) != (u32) c->regs[VCPU_REGS_RAX]) ||
            ((u32) (old >> 32) != (u32) c->regs[VCPU_REGS_RDX])) {
@@ -1686,14 +1720,10 @@ static inline int emulate_grp9(struct x86_emulate_ctxt *ctxt,
                c->regs[VCPU_REGS_RAX] = (u32) (old >> 0);
                c->regs[VCPU_REGS_RDX] = (u32) (old >> 32);
                ctxt->eflags &= ~EFLG_ZF;
-
        } else {
-               new = ((u64)c->regs[VCPU_REGS_RCX] << 32) |
+               c->dst.val = ((u64)c->regs[VCPU_REGS_RCX] << 32) |
                       (u32) c->regs[VCPU_REGS_RBX];
 
-               rc = ops->cmpxchg_emulated(c->modrm_ea, &old, &new, 8, ctxt->vcpu);
-               if (rc != X86EMUL_CONTINUE)
-                       return rc;
                ctxt->eflags |= EFLG_ZF;
        }
        return X86EMUL_CONTINUE;
@@ -2314,8 +2344,9 @@ static int task_switch_32(struct x86_emulate_ctxt *ctxt,
 }
 
 static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
-                                   struct x86_emulate_ops *ops,
-                                   u16 tss_selector, int reason)
+                                  struct x86_emulate_ops *ops,
+                                  u16 tss_selector, int reason,
+                                  bool has_error_code, u32 error_code)
 {
        struct desc_struct curr_tss_desc, next_tss_desc;
        int ret;
@@ -2372,6 +2403,8 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
        else
                ret = task_switch_16(ctxt, ops, tss_selector, old_tss_sel,
                                     old_tss_base, &next_tss_desc);
+       if (ret != X86EMUL_CONTINUE)
+               return ret;
 
        if (reason == TASK_SWITCH_CALL || reason == TASK_SWITCH_GATE)
                ctxt->eflags = ctxt->eflags | X86_EFLAGS_NT;
@@ -2386,12 +2419,22 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
        ops->set_cached_descriptor(&next_tss_desc, VCPU_SREG_TR, ctxt->vcpu);
        ops->set_segment_selector(tss_selector, VCPU_SREG_TR, ctxt->vcpu);
 
+       if (has_error_code) {
+               struct decode_cache *c = &ctxt->decode;
+
+               c->op_bytes = c->ad_bytes = (next_tss_desc.type & 8) ? 4 : 2;
+               c->lock_prefix = 0;
+               c->src.val = (unsigned long) error_code;
+               emulate_push(ctxt);
+       }
+
        return ret;
 }
 
 int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
                         struct x86_emulate_ops *ops,
-                        u16 tss_selector, int reason)
+                        u16 tss_selector, int reason,
+                        bool has_error_code, u32 error_code)
 {
        struct decode_cache *c = &ctxt->decode;
        int rc;
@@ -2399,15 +2442,18 @@ int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
        memset(c, 0, sizeof(struct decode_cache));
        c->eip = ctxt->eip;
        memcpy(c->regs, ctxt->vcpu->arch.regs, sizeof c->regs);
+       c->dst.type = OP_NONE;
 
-       rc = emulator_do_task_switch(ctxt, ops, tss_selector, reason);
+       rc = emulator_do_task_switch(ctxt, ops, tss_selector, reason,
+                                    has_error_code, error_code);
 
        if (rc == X86EMUL_CONTINUE) {
                memcpy(ctxt->vcpu->arch.regs, c->regs, sizeof c->regs);
                kvm_rip_write(ctxt->vcpu, c->eip);
+               rc = writeback(ctxt, ops);
        }
 
-       return rc;
+       return (rc == X86EMUL_UNHANDLEABLE) ? -1 : 0;
 }
 
 static void string_addr_inc(struct x86_emulate_ctxt *ctxt, unsigned long base,
@@ -2426,6 +2472,7 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
        u64 msr_data;
        struct decode_cache *c = &ctxt->decode;
        int rc = X86EMUL_CONTINUE;
+       int saved_dst_type = c->dst.type;
 
        ctxt->interruptibility = 0;
 
@@ -2454,8 +2501,11 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
        }
 
        if (c->rep_prefix && (c->d & String)) {
+               ctxt->restart = true;
                /* All REP prefixes have the same first termination condition */
                if (address_mask(c, c->regs[VCPU_REGS_RCX]) == 0) {
+               string_done:
+                       ctxt->restart = false;
                        kvm_rip_write(ctxt->vcpu, c->eip);
                        goto done;
                }
@@ -2467,17 +2517,13 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
                 *      - if REPNE/REPNZ and ZF = 1 then done
                 */
                if ((c->b == 0xa6) || (c->b == 0xa7) ||
-                               (c->b == 0xae) || (c->b == 0xaf)) {
+                   (c->b == 0xae) || (c->b == 0xaf)) {
                        if ((c->rep_prefix == REPE_PREFIX) &&
-                               ((ctxt->eflags & EFLG_ZF) == 0)) {
-                                       kvm_rip_write(ctxt->vcpu, c->eip);
-                                       goto done;
-                       }
+                           ((ctxt->eflags & EFLG_ZF) == 0))
+                               goto string_done;
                        if ((c->rep_prefix == REPNE_PREFIX) &&
-                               ((ctxt->eflags & EFLG_ZF) == EFLG_ZF)) {
-                               kvm_rip_write(ctxt->vcpu, c->eip);
-                               goto done;
-                       }
+                           ((ctxt->eflags & EFLG_ZF) == EFLG_ZF))
+                               goto string_done;
                }
                c->eip = ctxt->eip;
        }
@@ -2619,8 +2665,8 @@ special_insn:
                        kvm_inject_gp(ctxt->vcpu, 0);
                        goto done;
                }
-               if (!ops->pio_in_emulated(c->dst.bytes, c->regs[VCPU_REGS_RDX],
-                                         &c->dst.val, 1, ctxt->vcpu))
+               if (!pio_in_emulated(ctxt, ops, c->dst.bytes,
+                                    c->regs[VCPU_REGS_RDX], &c->dst.val))
                        goto done; /* IO is needed, skip writeback */
                break;
        case 0x6e:              /* outsb */
@@ -2836,8 +2882,8 @@ special_insn:
                        kvm_inject_gp(ctxt->vcpu, 0);
                        goto done;
                }
-               if (!ops->pio_in_emulated(c->dst.bytes, c->src.val,
-                                         &c->dst.val, 1, ctxt->vcpu))
+               if (!pio_in_emulated(ctxt, ops, c->dst.bytes, c->src.val,
+                                    &c->dst.val))
                        goto done; /* IO is needed */
                break;
        case 0xee: /* out al,dx */
@@ -2911,6 +2957,12 @@ writeback:
        if (rc != X86EMUL_CONTINUE)
                goto done;
 
+       /*
+        * restore dst type in case the decoding will be reused
+        * (happens for string instruction )
+        */
+       c->dst.type = saved_dst_type;
+
        if ((c->d & SrcMask) == SrcSI)
                string_addr_inc(ctxt, seg_override_base(ctxt, c), VCPU_REGS_RSI,
                                &c->src);
@@ -2918,12 +2970,22 @@ writeback:
        if ((c->d & DstMask) == DstDI)
                string_addr_inc(ctxt, es_base(ctxt), VCPU_REGS_RDI, &c->dst);
 
-       if (c->rep_prefix && (c->d & String))
+       if (c->rep_prefix && (c->d & String)) {
+               struct read_cache *rc = &ctxt->decode.io_read;
                register_address_increment(c, &c->regs[VCPU_REGS_RCX], -1);
+               /*
+                * Re-enter guest when pio read ahead buffer is empty or,
+                * if it is not used, after each 1024 iteration.
+                */
+               if ((rc->end == 0 && !(c->regs[VCPU_REGS_RCX] & 0x3ff)) ||
+                   (rc->end != 0 && rc->end == rc->pos))
+                       ctxt->restart = false;
+       }
 
        /* Commit shadow register state. */
        memcpy(ctxt->vcpu->arch.regs, c->regs, sizeof c->regs);
        kvm_rip_write(ctxt->vcpu, c->eip);
+       ops->set_rflags(ctxt->vcpu, ctxt->eflags);
 
 done:
        return (rc == X86EMUL_UNHANDLEABLE) ? -1 : 0;
@@ -3196,7 +3258,6 @@ twobyte_insn:
                rc = emulate_grp9(ctxt, ops);
                if (rc != X86EMUL_CONTINUE)
                        goto done;
-               c->dst.type = OP_NONE;
                break;
        }
        goto writeback;