| From 6ea6e84309ca7e0e850b3083e6b09344ee15c290 Mon Sep 17 00:00:00 2001 |
| From: Paolo Bonzini <pbonzini@redhat.com> |
| Date: Fri, 10 Nov 2017 10:49:38 +0100 |
| Subject: KVM: x86: inject exceptions produced by x86_decode_insn |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Paolo Bonzini <pbonzini@redhat.com> |
| |
| commit 6ea6e84309ca7e0e850b3083e6b09344ee15c290 upstream. |
| |
| Sometimes, a processor might execute an instruction while another |
| processor is updating the page tables for that instruction's code page, |
| but before the TLB shootdown completes. The interesting case happens |
| if the page is in the TLB. |
| |
| In general, the processor will succeed in executing the instruction and |
| nothing bad happens. However, what if the instruction is an MMIO access? |
| If *that* happens, KVM invokes the emulator, and the emulator gets the |
| updated page tables. If the update side had marked the code page as non |
| present, the page table walk then will fail and so will x86_decode_insn. |
| |
| Unfortunately, even though kvm_fetch_guest_virt is correctly returning |
| X86EMUL_PROPAGATE_FAULT, x86_decode_insn's caller treats the failure as |
| a fatal error if the instruction cannot simply be reexecuted (as is the |
| case for MMIO). And this in fact happened sometimes when rebooting |
| Windows 2012r2 guests. Just checking ctxt->have_exception and injecting |
| the exception if true is enough to fix the case. |
| |
| Thanks to Eduardo Habkost for helping in the debugging of this issue. |
| |
| Reported-by: Yanan Fu <yfu@redhat.com> |
| Cc: Eduardo Habkost <ehabkost@redhat.com> |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| Signed-off-by: Radim Krčmář <rkrcmar@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/kvm/x86.c | 2 ++ |
| 1 file changed, 2 insertions(+) |
| |
| --- a/arch/x86/kvm/x86.c |
| +++ b/arch/x86/kvm/x86.c |
| @@ -5429,6 +5429,8 @@ int x86_emulate_instruction(struct kvm_v |
| if (reexecute_instruction(vcpu, cr2, write_fault_to_spt, |
| emulation_type)) |
| return EMULATE_DONE; |
| + if (ctxt->have_exception && inject_emulated_exception(vcpu)) |
| + return EMULATE_DONE; |
| if (emulation_type & EMULTYPE_SKIP) |
| return EMULATE_FAIL; |
| return handle_emulation_failure(vcpu); |