]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: VMX: Synthesize nested EPT violation GVA_IS_VALID/GVA_TRANSLATED bits
authorKevin Cheng <chengkev@google.com>
Fri, 22 May 2026 23:27:00 +0000 (16:27 -0700)
committerSean Christopherson <seanjc@google.com>
Tue, 26 May 2026 21:54:20 +0000 (14:54 -0700)
When injecting an EPT Violation into L2 in response to a fault detected
while emulating an L2 GVA access, synthesize the GVA_IS_VALID and
GVA_TRANSLATED bits using information provided by the walker, instead of
pulling the bits from vmcs02.EXIT_QUALIFICATION.  The information in
vmcs02.EXIT_QUALIFICATION is valid/correct if and only if the fault being
injected into L1 is the direct result of an EPT Violation VM-Exit from L2.

E.g. if KVM is emulating an I/O instruction and the memory operand's
translation through L1's EPT fails, using vmcs02.EXIT_QUALIFICATION is
wrong as the semantics for EXIT_QUALIFICATION would be for an I/O exit,
not an EPT Violation exit.

Opportunistically clean up the formatting for creating the mask of bits
to pull from vmcs02.EXIT_QUALIFICATION.

Signed-off-by: Kevin Cheng <chengkev@google.com>
[sean: use plumbed in @access bits, massage changelog]
Link: https://patch.msgid.link/20260522232701.3671446-5-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/mmu/paging_tmpl.h
arch/x86/kvm/vmx/nested.c

index 66eee6914234a08eae812b7bf7454cf5182ee4cc..df3ae0c7ec2c30fbf4e3784172c5598d292aa11b 100644 (file)
@@ -502,7 +502,8 @@ error:
         * [2:0] - Derive from the access bits. The exit_qualification might be
         *         out of date if it is serving an EPT misconfiguration.
         * [5:3] - Calculated by the page walk of the guest EPT page tables
-        * [7:11] - Derived from [7:11] of real exit_qualification
+        * [7:8] - Derived from "fault stage" access bits
+        * [9:11] - Derived from [9:11] of real exit_qualification
         *
         * The other bits are set to 0.
         */
@@ -516,6 +517,14 @@ error:
                else
                        walker->fault.exit_qualification |= EPT_VIOLATION_ACC_READ;
 
+               /*
+                * KVM doesn't emulate features that access GPAs directly, e.g.
+                * Intel Processor Trace.  Assume the GVA is always valid; when
+                * propagating faults from hardware, KVM will discard this info
+                * and use the EXIT_QUALIFICATION bits from the VMCS.
+                */
+               walker->fault.exit_qualification |= EPT_VIOLATION_GVA_IS_VALID;
+
                /*
                 * Accesses to guest paging structures are either "reads" or
                 * "read+write" accesses, so consider them the latter if write_fault
@@ -523,6 +532,8 @@ error:
                 */
                if (access & PFERR_GUEST_PAGE_MASK)
                        walker->fault.exit_qualification |= EPT_VIOLATION_ACC_READ;
+               else
+                       walker->fault.exit_qualification |= EPT_VIOLATION_GVA_TRANSLATED;
 
                /*
                 * Note, pte_access holds the raw RWX bits from the EPTE, not
index 3bb7eaa7b2a5a4265cec560cef1b33922a828e01..a78ce00809637cae0b9b1c4c97df42f984b075f7 100644 (file)
@@ -445,13 +445,29 @@ static void nested_ept_inject_page_fault(struct kvm_vcpu *vcpu,
                        exit_qualification = 0;
                } else {
                        u64 mask = EPT_VIOLATION_GVA_IS_VALID |
-                               EPT_VIOLATION_GVA_TRANSLATED;
+                                  EPT_VIOLATION_GVA_TRANSLATED;
+
                        if (vmx->nested.msrs.ept_caps & VMX_EPT_ADVANCED_VMEXIT_INFO_BIT)
                                mask |= EPT_VIOLATION_GVA_USER |
-                                              EPT_VIOLATION_GVA_WRITABLE |
-                                              EPT_VIOLATION_GVA_NX;
-                       exit_qualification = fault->exit_qualification;
-                       exit_qualification |= vmx_get_exit_qual(vcpu) & mask;
+                                       EPT_VIOLATION_GVA_WRITABLE |
+                                       EPT_VIOLATION_GVA_NX;
+
+                       exit_qualification = fault->exit_qualification & ~mask;
+
+                       /*
+                        * Use the EXIT_QUALIFICATION from the VMCS if and only
+                        * if the hardware VM-Exit from L2 was an EPT Violation.
+                        * If the fault is synthesized, then EXIT_QUALIFICATION
+                        * is stale and/or holds entirely different data.  And
+                        * conversely, KVM _must_ rely on EXIT_QUALIFICATION if
+                        * the fault came from hardware, because KVM only sees
+                        * and walks the faulting GPA.
+                        */
+                       if (from_hardware)
+                               exit_qualification |= vmx_get_exit_qual(vcpu) & mask;
+                       else
+                               exit_qualification |= fault->exit_qualification & mask;
+
                        vm_exit_reason = EXIT_REASON_EPT_VIOLATION;
                }