KVM: TDX: Detect unexpected SEPT violations due to pending SPTEs
authorYan Zhao <yan.y.zhao@intel.com>
Thu, 27 Feb 2025 01:20:03 +0000 (09:20 +0800)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 14 Mar 2025 18:20:56 +0000 (14:20 -0400)
Detect SEPT violations that occur when an SEPT entry is in PENDING state
while the TD is configured not to receive #VE on SEPT violations.

A TD guest can be configured not to receive #VE by setting SEPT_VE_DISABLE
to 1 in tdh_mng_init() or modifying pending_ve_disable to 1 in TDCS when
flexible_pending_ve is permitted. In such cases, the TDX module will not
inject #VE into the TD upon encountering an EPT violation caused by an SEPT
entry in the PENDING state. Instead, TDX module will exit to VMM and set
extended exit qualification type to PENDING_EPT_VIOLATION and exit
qualification bit 6:3 to 0.

Since #VE will not be injected to such TDs, they are not able to be
notified to accept a GPA. TD accessing before accepting a private GPA
is regarded as an error within the guest.

Detect such guest error by inspecting the (extended) exit qualification
bits and make such VM dead.

Cc: Xiaoyao Li <xiaoyao.li@intel.com>
Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Yan Zhao <yan.y.zhao@intel.com>
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Message-ID: <20250227012021.1778144-3-binbin.wu@linux.intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/include/asm/vmx.h
arch/x86/kvm/vmx/tdx.c
arch/x86/kvm/vmx/tdx_arch.h

index 9298fb9d4bb3160adf539eb79eb3e1deded18536..028f3b8db2afe94a819157ce239ef463aeea1866 100644 (file)
@@ -585,12 +585,14 @@ enum vm_entry_failure_code {
 #define EPT_VIOLATION_ACC_WRITE_BIT    1
 #define EPT_VIOLATION_ACC_INSTR_BIT    2
 #define EPT_VIOLATION_RWX_SHIFT                3
+#define EPT_VIOLATION_EXEC_R3_LIN_BIT  6
 #define EPT_VIOLATION_GVA_IS_VALID_BIT 7
 #define EPT_VIOLATION_GVA_TRANSLATED_BIT 8
 #define EPT_VIOLATION_ACC_READ         (1 << EPT_VIOLATION_ACC_READ_BIT)
 #define EPT_VIOLATION_ACC_WRITE                (1 << EPT_VIOLATION_ACC_WRITE_BIT)
 #define EPT_VIOLATION_ACC_INSTR                (1 << EPT_VIOLATION_ACC_INSTR_BIT)
 #define EPT_VIOLATION_RWX_MASK         (VMX_EPT_RWX_MASK << EPT_VIOLATION_RWX_SHIFT)
+#define EPT_VIOLATION_EXEC_FOR_RING3_LIN (1 << EPT_VIOLATION_EXEC_R3_LIN_BIT)
 #define EPT_VIOLATION_GVA_IS_VALID     (1 << EPT_VIOLATION_GVA_IS_VALID_BIT)
 #define EPT_VIOLATION_GVA_TRANSLATED   (1 << EPT_VIOLATION_GVA_TRANSLATED_BIT)
 
index fb2ca11c6fda97bbd0e27d43552138fdd4ad4953..fa8d4f9b54bf41ed9a44f8db8eae200b5e638ee4 100644 (file)
@@ -1709,12 +1709,29 @@ void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
        trace_kvm_apicv_accept_irq(vcpu->vcpu_id, delivery_mode, trig_mode, vector);
 }
 
+static inline bool tdx_is_sept_violation_unexpected_pending(struct kvm_vcpu *vcpu)
+{
+       u64 eeq_type = to_tdx(vcpu)->ext_exit_qualification & TDX_EXT_EXIT_QUAL_TYPE_MASK;
+       u64 eq = vmx_get_exit_qual(vcpu);
+
+       if (eeq_type != TDX_EXT_EXIT_QUAL_TYPE_PENDING_EPT_VIOLATION)
+               return false;
+
+       return !(eq & EPT_VIOLATION_RWX_MASK) && !(eq & EPT_VIOLATION_EXEC_FOR_RING3_LIN);
+}
+
 static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu)
 {
        unsigned long exit_qual;
        gpa_t gpa = to_tdx(vcpu)->exit_gpa;
 
        if (vt_is_tdx_private_gpa(vcpu->kvm, gpa)) {
+               if (tdx_is_sept_violation_unexpected_pending(vcpu)) {
+                       pr_warn("Guest access before accepting 0x%llx on vCPU %d\n",
+                               gpa, vcpu->vcpu_id);
+                       kvm_vm_dead(vcpu->kvm);
+                       return -EIO;
+               }
                /*
                 * Always treat SEPT violations as write faults.  Ignore the
                 * EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations.
index 58bda8a5ce9ad633d75be95d20d48dfc6b3d5b9d..6cf68ea6965b25a1fb833506a5d49448f51fe0d0 100644 (file)
@@ -70,6 +70,8 @@ struct tdx_cpuid_value {
 #define TDX_TD_ATTR_KL                 BIT_ULL(31)
 #define TDX_TD_ATTR_PERFMON            BIT_ULL(63)
 
+#define TDX_EXT_EXIT_QUAL_TYPE_MASK    GENMASK(3, 0)
+#define TDX_EXT_EXIT_QUAL_TYPE_PENDING_EPT_VIOLATION  6
 /*
  * TD_PARAMS is provided as an input to TDH_MNG_INIT, the size of which is 1024B.
  */