KVM: x86 emulator: Correct management of REP prefix

This patch corrects some errors appearing when we have an emulation failure
on an operation using REP prefix.

When x86_emulate_insn() fails, saving EIP and ECX is not enough as emulation
should have modified other registers like RSI or RDI. Moreover, the emulation
can fail on the writeback, and in this case we are not able to restore
registers.

At beginning of x86_emulate_insn(), we restore registers from vcpu as they were
not modified by x86d_decode_insn() and we save EIP to be able to restore it
in case of failure.

Signed-off-by: Laurent Vivier <Laurent.Vivier@bull.net>
Signed-off-by: Avi Kivity <avi@qumranet.com>
diff --git a/drivers/kvm/x86_emulate.c b/drivers/kvm/x86_emulate.c
index 35069e3..887de7d 100644
--- a/drivers/kvm/x86_emulate.c
+++ b/drivers/kvm/x86_emulate.c
@@ -1146,10 +1146,18 @@
 {
 	unsigned long cr2 = ctxt->cr2;
 	u64 msr_data;
-	unsigned long saved_rcx = 0, saved_eip = 0;
+	unsigned long saved_eip;
 	struct decode_cache *c = &ctxt->decode;
 	int rc = 0;
 
+	/* Shadow copy of register state. Committed on successful emulation.
+	 * NOTE: we can copy them from vcpu as x86_decode_insn() doesn't
+	 * modify them.
+	 */
+
+	memcpy(c->regs, ctxt->vcpu->regs, sizeof c->regs);
+	saved_eip = c->eip;
+
 	if ((c->d & ModRM) && (c->modrm_mod != 3))
 		cr2 = c->modrm_ea;
 
@@ -1354,7 +1362,11 @@
 	ctxt->vcpu->rip = c->eip;
 
 done:
-	return (rc == X86EMUL_UNHANDLEABLE) ? -1 : 0;
+	if (rc == X86EMUL_UNHANDLEABLE) {
+		c->eip = saved_eip;
+		return -1;
+	}
+	return 0;
 
 special_insn:
 	if (c->twobyte)
@@ -1396,8 +1408,10 @@
 				register_address(ctxt->es_base,
 						 c->regs[VCPU_REGS_RDI]),
 				c->rep_prefix,
-				c->regs[VCPU_REGS_RDX]) == 0)
+				c->regs[VCPU_REGS_RDX]) == 0) {
+			c->eip = saved_eip;
 			return -1;
+		}
 		return 0;
 	case 0x6e:		/* outsb */
 	case 0x6f:		/* outsw/outsd */
@@ -1412,8 +1426,10 @@
 							ctxt->ds_base,
 						 c->regs[VCPU_REGS_RSI]),
 				c->rep_prefix,
-				c->regs[VCPU_REGS_RDX]) == 0)
+				c->regs[VCPU_REGS_RDX]) == 0) {
+			c->eip = saved_eip;
 			return -1;
+		}
 		return 0;
 	case 0x70 ... 0x7f: /* jcc (short) */ {
 		int rel = insn_fetch(s8, 1, c->eip);
@@ -1441,8 +1457,6 @@
 			ctxt->vcpu->rip = c->eip;
 			goto done;
 		}
-		saved_rcx = c->regs[VCPU_REGS_RCX];
-		saved_eip = c->eip;
 		c->regs[VCPU_REGS_RCX]--;
 		c->eip = ctxt->vcpu->rip;
 	}
@@ -1459,10 +1473,6 @@
 					c->regs[VCPU_REGS_RSI]),
 					&c->dst.val,
 					c->dst.bytes, ctxt->vcpu)) != 0) {
-			if (c->rep_prefix) {
-				c->regs[VCPU_REGS_RCX] = saved_rcx;
-				c->eip = saved_eip;
-			}
 			goto done;
 		}
 		register_address_increment(c->regs[VCPU_REGS_RSI],
@@ -1491,10 +1501,6 @@
 		if ((rc = ops->read_emulated(cr2, &c->dst.val,
 					     c->dst.bytes,
 					     ctxt->vcpu)) != 0) {
-			if (c->rep_prefix) {
-				c->regs[VCPU_REGS_RCX] = saved_rcx;
-				c->eip = saved_eip;
-			}
 			goto done;
 		}
 		register_address_increment(c->regs[VCPU_REGS_RSI],
@@ -1762,5 +1768,6 @@
 
 cannot_emulate:
 	DPRINTF("Cannot emulate %02x\n", c->b);
+	c->eip = saved_eip;
 	return -1;
 }