diff options
author | Laurent Vivier <Laurent.Vivier@bull.net> | 2007-10-01 11:01:06 +0200 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-10-02 08:05:27 +0200 |
commit | 06ba8d55ba4052ded08eea5bed9519c6f6780d8c (patch) | |
tree | a26f40da61c8beac0dd5a9fae36e2ca8b66a280c | |
parent | c78fc6fb953bbbd9e0b931759b03162768991132 (diff) |
KVM: x86 emulator: Correct management of REP prefixkvm-45
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>
-rw-r--r-- | drivers/kvm/x86_emulate.c | 35 |
1 files changed, 21 insertions, 14 deletions
diff --git a/drivers/kvm/x86_emulate.c b/drivers/kvm/x86_emulate.c index 35069e36268f..887de7de4fdc 100644 --- a/drivers/kvm/x86_emulate.c +++ b/drivers/kvm/x86_emulate.c @@ -1146,10 +1146,18 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops) { 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 @@ writeback: 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 @@ special_insn: 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 @@ special_insn: 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 @@ special_insn: 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 @@ special_insn: 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 @@ special_insn: 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 @@ twobyte_special_insn: cannot_emulate: DPRINTF("Cannot emulate %02x\n", c->b); + c->eip = saved_eip; return -1; } |