KVM: x86 emulator: introduce pio in string read ahead.
[deliverable/linux.git] / arch / x86 / kvm / emulate.c
index 2c66e097d916022e9781e8fda0709e8a44d1ce3a..ab3fff5bf7c467637bf53c34eac874d1ba627389 100644 (file)
@@ -927,8 +927,11 @@ 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;
        ctxt->cs_base = seg_base(ctxt, VCPU_SREG_CS);
@@ -1254,6 +1257,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);
@@ -2424,9 +2457,9 @@ int
 x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
 {
        u64 msr_data;
-       unsigned long saved_eip = 0;
        struct decode_cache *c = &ctxt->decode;
        int rc = X86EMUL_CONTINUE;
+       int saved_dst_type = c->dst.type;
 
        ctxt->interruptibility = 0;
 
@@ -2436,7 +2469,6 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
         */
 
        memcpy(c->regs, ctxt->vcpu->arch.regs, sizeof c->regs);
-       saved_eip = c->eip;
 
        if (ctxt->mode == X86EMUL_MODE_PROT64 && (c->d & No64)) {
                kvm_queue_exception(ctxt->vcpu, UD_VECTOR);
@@ -2456,8 +2488,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;
                }
@@ -2469,17 +2504,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;
        }
@@ -2621,8 +2652,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 */
@@ -2838,8 +2869,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 */
@@ -2913,6 +2944,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);
@@ -2920,19 +2957,24 @@ 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);
 
 done:
-       if (rc == X86EMUL_UNHANDLEABLE) {
-               c->eip = saved_eip;
-               return -1;
-       }
-       return 0;
+       return (rc == X86EMUL_UNHANDLEABLE) ? -1 : 0;
 
 twobyte_insn:
        switch (c->b) {
@@ -3209,6 +3251,5 @@ twobyte_insn:
 
 cannot_emulate:
        DPRINTF("Cannot emulate %02x\n", c->b);
-       c->eip = saved_eip;
        return -1;
 }
This page took 0.044186 seconds and 5 git commands to generate.