[Devel,RHEL7,COMMIT] ms/KVM: nVMX: Fix the NMI IDT-vectoring handling

Submitted by Konstantin Khorenko on Feb. 20, 2017, 10:55 a.m.

Details

Message ID 201702201055.v1KAtM5g008690@finist_cl7.x64_64.work.ct
State New
Series "KVM: nVMX: Fix the NMI IDT-vectoring handling"
Headers show

Commit Message

Konstantin Khorenko Feb. 20, 2017, 10:55 a.m.
The commit is pushed to "branch-rh7-3.10.0-514.6.1.vz7.28.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-514.6.1.vz7.28.6
------>
commit c2ba4859eed57b4f515be92e7725b2c8f5fc6117
Author: Wanpeng Li <wanpeng.li@hotmail.com>
Date:   Mon Feb 20 14:55:22 2017 +0400

    ms/KVM: nVMX: Fix the NMI IDT-vectoring handling
    
    Run kvm-unit-tests/eventinj.flat in L1:
    
    Sending NMI to self
    After NMI to self
    FAIL: NMI
    
    This test scenario is to test whether VMM can handle NMI IDT-vectoring info correctly.
    
    At the beginning, L2 writes LAPIC to send a self NMI, the EPT page tables on both L1
    and L0 are empty so:
    
    - The L2 accesses memory can generate EPT violation which can be intercepted by L0.
    
      The EPT violation vmexit occurred during delivery of this NMI, and the NMI info is
      recorded in vmcs02's IDT-vectoring info.
    
    - L0 walks L1's EPT12 and L0 sees the mapping is invalid, it injects the EPT violation into L1.
    
      The vmcs02's IDT-vectoring info is reflected to vmcs12's IDT-vectoring info since
      it is a nested vmexit.
    
    - L1 receives the EPT violation, then fixes its EPT12.
    - L1 executes VMRESUME to resume L2 which generates vmexit and causes L1 exits to L0.
    - L0 emulates VMRESUME which is called from L1, then return to L2.
    
      L0 merges the requirement of vmcs12's IDT-vectoring info and injects it to L2 through
      vmcs02.
    
    - The L2 re-executes the fault instruction and cause EPT violation again.
    - Since the L1's EPT12 is valid, L0 can fix its EPT02
    - L0 resume L2
    
      The EPT violation vmexit occurred during delivery of this NMI again, and the NMI info
      is recorded in vmcs02's IDT-vectoring info. L0 should inject the NMI through vmentry
      event injection since it is caused by EPT02's EPT violation.
    
    However, vmx_inject_nmi() refuses to inject NMI from IDT-vectoring info if vCPU is in
    guest mode, this patch fix it by permitting to inject NMI from IDT-vectoring if it is
    the L0's responsibility to inject NMI from IDT-vectoring info to L2.
    
    Cc: Paolo Bonzini <pbonzini@redhat.com>
    Cc: Radim Krčmář <rkrcmar@redhat.com>
    Cc: Jan Kiszka <jan.kiszka@siemens.com>
    Cc: Bandan Das <bsd@redhat.com>
    Signed-off-by: Wanpeng Li <wanpeng.li@hotmail.com>
    Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
    Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
    (cherry picked from commit c5a6d5f7faad8549bb5ff7e3e5792e33933c5b9f)
    
    https://jira.sw.ru/browse/PSBM-59729
    
    Signed-off-by: Denis Plotnikov <dplotnikov@virtuozzo.com>
---
 arch/x86/kvm/vmx.c | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

Patch hide | download patch | download mbox

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index a193d23..18e761f 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -4997,29 +4997,30 @@  static void vmx_inject_nmi(struct kvm_vcpu *vcpu)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
 
-	if (is_guest_mode(vcpu))
-		return;
+	if (!is_guest_mode(vcpu)) {
+		if (!cpu_has_virtual_nmis()) {
+			/*
+			 * Tracking the NMI-blocked state in software is built upon
+			 * finding the next open IRQ window. This, in turn, depends on
+			 * well-behaving guests: They have to keep IRQs disabled at
+			 * least as long as the NMI handler runs. Otherwise we may
+			 * cause NMI nesting, maybe breaking the guest. But as this is
+			 * highly unlikely, we can live with the residual risk.
+			 */
+			vmx->soft_vnmi_blocked = 1;
+			vmx->vnmi_blocked_time = 0;
+		}
 
-	if (!cpu_has_virtual_nmis()) {
-		/*
-		 * Tracking the NMI-blocked state in software is built upon
-		 * finding the next open IRQ window. This, in turn, depends on
-		 * well-behaving guests: They have to keep IRQs disabled at
-		 * least as long as the NMI handler runs. Otherwise we may
-		 * cause NMI nesting, maybe breaking the guest. But as this is
-		 * highly unlikely, we can live with the residual risk.
-		 */
-		vmx->soft_vnmi_blocked = 1;
-		vmx->vnmi_blocked_time = 0;
+		++vcpu->stat.nmi_injections;
+		vmx->nmi_known_unmasked = false;
 	}
 
-	++vcpu->stat.nmi_injections;
-	vmx->nmi_known_unmasked = false;
 	if (vmx->rmode.vm86_active) {
 		if (kvm_inject_realmode_interrupt(vcpu, NMI_VECTOR, 0) != EMULATE_DONE)
 			kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
 		return;
 	}
+
 	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
 			INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR);
 }