KVM: x86: Move nEPT exit_qualification field from kvm_vcpu_arch to x86_exception
authorSean Christopherson <seanjc@google.com>
Fri, 9 Feb 2024 22:16:59 +0000 (14:16 -0800)
committerSean Christopherson <seanjc@google.com>
Tue, 9 Apr 2024 17:24:36 +0000 (10:24 -0700)
Move the exit_qualification field that is used to track information about
in-flight nEPT violations from "struct kvm_vcpu_arch" to "x86_exception",
i.e. associate the information with the actual nEPT violation instead of
the vCPU.  To handle bits that are pulled from vmcs.EXIT_QUALIFICATION,
i.e. that are propagated from the "original" EPT violation VM-Exit, simply
grab them from the VMCS on-demand when injecting a nEPT Violation or a PML
Full VM-exit.

Aside from being ugly, having an exit_qualification field in kvm_vcpu_arch
is outright dangerous, e.g. see commit d7f0a00e438d ("KVM: VMX: Report
up-to-date exit qualification to userspace").

Opportunstically add a comment to call out that PML Full and EPT Violation
VM-Exits use the same bit to report NMI blocking information.

Link: https://lore.kernel.org/r/20240209221700.393189-3-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/kvm_emulate.h
arch/x86/kvm/mmu/paging_tmpl.h
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/vmx.c

index 16e07a2eee195d48e29536dc69c5d6e975c99d7f..de03d9d90f2cb899cd281adf0bee6235634a62b3 100644 (file)
@@ -993,9 +993,6 @@ struct kvm_vcpu_arch {
 
        u64 msr_kvm_poll_control;
 
-       /* set at EPT violation at this point */
-       unsigned long exit_qualification;
-
        /* pv related host specific info */
        struct {
                bool pv_unhalted;
index 5382646162a38710f4c85bc7b0f28f1dc944ee06..29ea4313e1bb1eea7df2639e8379140c77bb1292 100644 (file)
@@ -26,6 +26,7 @@ struct x86_exception {
        bool nested_page_fault;
        u64 address; /* cr2 or nested page fault gpa */
        u8 async_page_fault;
+       unsigned long exit_qualification;
 };
 
 /*
index 4d4e98fe4f3548baf9156f3a9e0fd67003df7fdf..7a87097cb45b52d920570eeb09f5b5945a604eb2 100644 (file)
@@ -497,21 +497,21 @@ error:
         * The other bits are set to 0.
         */
        if (!(errcode & PFERR_RSVD_MASK)) {
-               vcpu->arch.exit_qualification &= (EPT_VIOLATION_GVA_IS_VALID |
-                                                 EPT_VIOLATION_GVA_TRANSLATED);
+               walker->fault.exit_qualification = 0;
+
                if (write_fault)
-                       vcpu->arch.exit_qualification |= EPT_VIOLATION_ACC_WRITE;
+                       walker->fault.exit_qualification |= EPT_VIOLATION_ACC_WRITE;
                if (user_fault)
-                       vcpu->arch.exit_qualification |= EPT_VIOLATION_ACC_READ;
+                       walker->fault.exit_qualification |= EPT_VIOLATION_ACC_READ;
                if (fetch_fault)
-                       vcpu->arch.exit_qualification |= EPT_VIOLATION_ACC_INSTR;
+                       walker->fault.exit_qualification |= EPT_VIOLATION_ACC_INSTR;
 
                /*
                 * Note, pte_access holds the raw RWX bits from the EPTE, not
                 * ACC_*_MASK flags!
                 */
-               vcpu->arch.exit_qualification |= (pte_access & VMX_EPT_RWX_MASK) <<
-                                                EPT_VIOLATION_RWX_SHIFT;
+               walker->fault.exit_qualification |= (pte_access & VMX_EPT_RWX_MASK) <<
+                                                    EPT_VIOLATION_RWX_SHIFT;
        }
 #endif
        walker->fault.address = addr;
index 695558ff40e1b7e96f01babe565f968164bfc135..cebbc4d47000fca88d1d32f952acd4111dd58327 100644 (file)
@@ -409,18 +409,28 @@ static void nested_ept_inject_page_fault(struct kvm_vcpu *vcpu,
 {
        struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
        struct vcpu_vmx *vmx = to_vmx(vcpu);
+       unsigned long exit_qualification;
        u32 vm_exit_reason;
-       unsigned long exit_qualification = vcpu->arch.exit_qualification;
 
        if (vmx->nested.pml_full) {
                vm_exit_reason = EXIT_REASON_PML_FULL;
                vmx->nested.pml_full = false;
-               exit_qualification &= INTR_INFO_UNBLOCK_NMI;
+
+               /*
+                * PML Full and EPT Violation VM-Exits both use bit 12 to report
+                * "NMI unblocking due to IRET", i.e. the bit can be propagated
+                * as-is from the original EXIT_QUALIFICATION.
+                */
+               exit_qualification = vmx_get_exit_qual(vcpu) & INTR_INFO_UNBLOCK_NMI;
        } else {
                if (fault->error_code & PFERR_RSVD_MASK) {
                        vm_exit_reason = EXIT_REASON_EPT_MISCONFIG;
                        exit_qualification = 0;
                } else {
+                       exit_qualification = fault->exit_qualification;
+                       exit_qualification |= vmx_get_exit_qual(vcpu) &
+                                             (EPT_VIOLATION_GVA_IS_VALID |
+                                              EPT_VIOLATION_GVA_TRANSLATED);
                        vm_exit_reason = EXIT_REASON_EPT_VIOLATION;
                }
 
index c37a89eda90f8219de02c96452a5cdbe5d13da83..e27740c1b0f7ce96b8a452775c3db4b1d8ab3abf 100644 (file)
@@ -5768,8 +5768,6 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
        error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) != 0 ?
               PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK;
 
-       vcpu->arch.exit_qualification = exit_qualification;
-
        /*
         * Check that the GPA doesn't exceed physical memory limits, as that is
         * a guest page fault.  We have to emulate the instruction here, because