Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH v3 0/4] KVM: TDX: Enable VM-DoS Prevention Features for TDX
@ 2026-08-12  8:02 Xiaoyao Li
  2026-08-12  8:02 ` [PATCH v3 1/4] KVM: TDX: Enable Notify VM exit Xiaoyao Li
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Xiaoyao Li @ 2026-08-12  8:02 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: Kiryl Shutsemau, Rick Edgecombe, kvm, linux-kernel, linux-coco,
	nik.borisov, xiaoyao.li

Hi all,

This is v3 of the series to enable the Notify VM Exit and Bus Lock VM
exit for TDX, which fixes the KVM CAP issue related with them and allow
userspace to actually enable the features.

It still tries to keep the series as small as possible because it's
targeted for being backported to stable kenrels.  This v3 splits the
patch2 of v2 into two patches (patch2 and patch3 in this v3) to make them
easier for review.  There are other issues of existing code found during
previous review but not mandatory for this series. The plan is to address
them in followup series separately.

Please refer to v1 for a full background.

v2: https://lore.kernel.org/all/20260810112200.2326727-1-xiaoyao.li@intel.com/
v1: https://lore.kernel.org/all/20260805031257.1844914-1-xiaoyao.li@intel.com/


Xiaoyao Li (4):
  KVM: TDX: Enable Notify VM exit
  KVM: TDX: Set bits 31:16 to 0 for the synthesized Exit Reason
  KVM: TDX: Don't assume exit_reason[31:16] as all-0 in
    tdx_to_vmx_exit_reason()
  KVM: TDX: Enable Bus Lock VM exit

 arch/x86/kvm/vmx/common.h | 19 ++++++++++
 arch/x86/kvm/vmx/tdx.c    | 75 +++++++++++++++++++++++++++++++--------
 arch/x86/kvm/vmx/vmx.c    | 15 ++------
 arch/x86/kvm/vmx/vmx.h    |  1 +
 4 files changed, 82 insertions(+), 28 deletions(-)


base-commit: 1b731e5ded480bd1e5546aed35584238661ce72e
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 1/4] KVM: TDX: Enable Notify VM exit
  2026-08-12  8:02 [PATCH v3 0/4] KVM: TDX: Enable VM-DoS Prevention Features for TDX Xiaoyao Li
@ 2026-08-12  8:02 ` Xiaoyao Li
  2026-08-12  8:02 ` [PATCH v3 2/4] KVM: TDX: Set bits 31:16 to 0 for the synthesized Exit Reason Xiaoyao Li
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 9+ messages in thread
From: Xiaoyao Li @ 2026-08-12  8:02 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: Kiryl Shutsemau, Rick Edgecombe, kvm, linux-kernel, linux-coco,
	nik.borisov, xiaoyao.li

Enable Notify VM exit functionality for TDX guests.

Notify VM exit is an existing feature supported by KVM.  Userspace can
enable Notify VM exit through KVM_CAP_X86_NOTIFY_VMEXIT when it's
reported as supported.  However, KVM reports the support of this CAP just
based on the hardware capability but doesn't differentiate between VMX
and TDX.  This leads to the issue that userspace can enable this cap for
TDX guests without getting an error, but the feature is not actually
enabled because KVM doesn't call the TDX module API to program the
relevant TD VMCS fields.

Enable Notify VM exit for TDX guests by:

  - Invoking TDX module API calls to set NOTIFY_VM_EXITING and Notify
    Window in TD VMCS.  It's done in tdx_vcpu_init() where other TD VMCS
    bits are set.  Since TDX vCPU cannot be reset, it only needs to be
    configured once when initializing the TDX vCPU.

  - Adding corresponding exit handler for TDX Notify VM Exit.

Notify VM exit can happen when executing the IRET instruction.  If the
IRET unblocks the NMI blocking state, bit 12 of the exit qualification
is set.  In this case, the VMM needs to restore the "blocked by NMI" state
when it decides to re-enter the guest.  For TDX, KVM cannot manage the
GUEST_INTERRUPTIBILITY_INFO and it's TDX module's responsibility to
handle it.  Extract the common part without NMI blocking handling into a
helper in common.h so that it can be shared between VMX and TDX.

Note, KVM uses "pre-production" terminology for the feature formally called
Notify VM-Exit.  All public versions of the SDM refer to the feature as
Instruction Timeout.  This will be remedied in the near future, for now,
use KVM's terminology for consistency.

Note, #2, there is no enumeration bit for Notify VM exit by TDX module
because all TDX modules support it, and allow to set the corresponding
TD VMCS fields as long as the hardware supports the feature.

Fixes: 161d34609f9b ("KVM: TDX: Make TDX VM type supported")
Cc: stable@vger.kernel.org
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Reviewed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
Changes in v3:
- Collect R-b tag from Rick

Changes in v2:
- Mention the feature name mismatch between KVM and SDM in changelog and
  leave the renaming to future, since this patch is targeted for stable
- Extract the common handling into a helper, and put the helper in
  common.h instead of refactorin the existing handle_notify() in vmx.h
- Add a note to clarify the feature is always supported by TDX module,
  to make Sashiko happy.
---
 arch/x86/kvm/vmx/common.h | 19 +++++++++++++++++++
 arch/x86/kvm/vmx/tdx.c    | 10 ++++++++++
 arch/x86/kvm/vmx/vmx.c    | 13 +------------
 3 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/arch/x86/kvm/vmx/common.h b/arch/x86/kvm/vmx/common.h
index 08005676702c..2cbaa9aba901 100644
--- a/arch/x86/kvm/vmx/common.h
+++ b/arch/x86/kvm/vmx/common.h
@@ -4,6 +4,7 @@
 
 #include <linux/kvm_host.h>
 #include <asm/posted_intr.h>
+#include <asm/vmx.h>
 
 #include "mmu.h"
 
@@ -183,6 +184,24 @@ static inline void __vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu,
 	kvm_vcpu_trigger_posted_interrupt(vcpu, POSTED_INTR_VECTOR);
 }
 
+static inline int __vmx_handle_notify(struct kvm_vcpu *vcpu,
+				      unsigned long exit_qual)
+{
+	bool context_invalid = exit_qual & NOTIFY_VM_CONTEXT_INVALID;
+
+	++vcpu->stat.notify_window_exits;
+
+	if (vcpu->kvm->arch.notify_vmexit_flags & KVM_X86_NOTIFY_VMEXIT_USER ||
+	    context_invalid) {
+		vcpu->run->exit_reason = KVM_EXIT_NOTIFY;
+		vcpu->run->notify.flags = context_invalid ?
+					  KVM_NOTIFY_CONTEXT_INVALID : 0;
+		return 0;
+	}
+
+	return 1;
+}
+
 noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu);
 
 #endif /* __KVM_X86_VMX_COMMON_H */
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index b272c20586a7..7338ac0af693 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -2126,6 +2126,9 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
 		 * - If it's not an MSMI, no need to do anything here.
 		 */
 		return 1;
+	case EXIT_REASON_NOTIFY:
+		/* NMI blocking state is handled by TDX module */
+		return __vmx_handle_notify(vcpu, vmx_get_exit_qual(vcpu));
 	default:
 		break;
 	}
@@ -3154,6 +3157,13 @@ static int tdx_vcpu_init(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *cmd)
 	td_vmcs_write64(tdx, POSTED_INTR_DESC_ADDR, __pa(&tdx->vt.pi_desc));
 	td_vmcs_setbit32(tdx, PIN_BASED_VM_EXEC_CONTROL, PIN_BASED_POSTED_INTR);
 
+	if (kvm_notify_vmexit_enabled(vcpu->kvm)) {
+		td_vmcs_setbit32(tdx, SECONDARY_VM_EXEC_CONTROL,
+				 SECONDARY_EXEC_NOTIFY_VM_EXITING);
+		td_vmcs_write32(tdx, NOTIFY_WINDOW,
+				vcpu->kvm->arch.notify_window);
+	}
+
 	tdx->state = VCPU_TD_STATE_INITIALIZED;
 
 	return 0;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e3bfe6aca1a0..e53cc96002c7 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6279,9 +6279,6 @@ static int handle_bus_lock_vmexit(struct kvm_vcpu *vcpu)
 static int handle_notify(struct kvm_vcpu *vcpu)
 {
 	unsigned long exit_qual = vmx_get_exit_qual(vcpu);
-	bool context_invalid = exit_qual & NOTIFY_VM_CONTEXT_INVALID;
-
-	++vcpu->stat.notify_window_exits;
 
 	/*
 	 * Notify VM exit happened while executing iret from NMI,
@@ -6291,15 +6288,7 @@ static int handle_notify(struct kvm_vcpu *vcpu)
 		vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO,
 			      GUEST_INTR_STATE_NMI);
 
-	if (vcpu->kvm->arch.notify_vmexit_flags & KVM_X86_NOTIFY_VMEXIT_USER ||
-	    context_invalid) {
-		vcpu->run->exit_reason = KVM_EXIT_NOTIFY;
-		vcpu->run->notify.flags = context_invalid ?
-					  KVM_NOTIFY_CONTEXT_INVALID : 0;
-		return 0;
-	}
-
-	return 1;
+	return __vmx_handle_notify(vcpu, exit_qual);
 }
 
 static int vmx_get_msr_imm_reg(struct kvm_vcpu *vcpu)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 2/4] KVM: TDX: Set bits 31:16 to 0 for the synthesized Exit Reason
  2026-08-12  8:02 [PATCH v3 0/4] KVM: TDX: Enable VM-DoS Prevention Features for TDX Xiaoyao Li
  2026-08-12  8:02 ` [PATCH v3 1/4] KVM: TDX: Enable Notify VM exit Xiaoyao Li
@ 2026-08-12  8:02 ` Xiaoyao Li
  2026-08-12  8:02 ` [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason() Xiaoyao Li
  2026-08-12  8:02 ` [PATCH v3 4/4] KVM: TDX: Enable Bus Lock VM exit Xiaoyao Li
  3 siblings, 0 replies; 9+ messages in thread
From: Xiaoyao Li @ 2026-08-12  8:02 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: Kiryl Shutsemau, Rick Edgecombe, kvm, linux-kernel, linux-coco,
	nik.borisov, xiaoyao.li

Set bits 31:16 to 0 instead of all-1s for KVM's synthesized Exit Reason.

KVM is going to support Bus Lock VM exit for TDX, after which bit 26 of
the Exit Reason becomes meaningful and indicates that a bus lock happened.
The existing synthesized Exit Reason, -1u, will cause a false positive in
that case.  Change the synthesized Exit Reason from -1u to U16_MAX, so that
bits 31:16 are set to 0.  This also avoids the potential issues when other
bits in 31:16 become valid in the future.

As a bonus, the check for synthesized Exit Reason in tdx_failed_vmentry()
becomes unnecessary. Drop it.

Cc: stable@vger.kernel.org
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
---
Note, the checking of (*reason != TDX_INVALID_EXIT_REASON) in
tdx_get_exit_info() can cause a false-positive when the real exit reason
is EPT_MISCONFIG. This issue is orthogonal to enabling Bus Lock VM exit and
it's not urgent since EPT_MISCONFIIG is not supposed to happen unless
current KVM code is buggy. We leave the fix for this issue to the future.

Note, #2, the checking of tdx_failed_vmentry() seems to miss the case
where a real EPT_MISCONFIG happens with failed_vmentry being set.
First, in practice, EPT_MISCONFIG cannot happen with failed_vmentry being
set. Second, even if it can, this is an pre-existing issue and the next
patch can address it.

Changes in v3:
- split from the patch 2 in v2.
- define a MARCO for the synthesized invalid Exit Reason.
---
 arch/x86/kvm/vmx/tdx.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 7338ac0af693..df23db9430f0 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -921,6 +921,9 @@ static __always_inline u32 tdcall_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
 	return EXIT_REASON_TDCALL;
 }
 
+/* Synthesized invalid Exit Reason */
+#define TDX_INVALID_EXIT_REASON		U16_MAX
+
 static __always_inline u32 tdx_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
 {
 	struct vcpu_tdx *tdx = to_tdx(vcpu);
@@ -934,7 +937,12 @@ static __always_inline u32 tdx_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
 	case TDX_NON_RECOVERABLE_TD_WRONG_APIC_MODE:
 		break;
 	default:
-		return -1u;
+		/*
+		 * Return the synthesized invalid Exit Reason, as the TDX
+		 * module never attempted to run the vCPU, i.e. the Exit
+		 * Reason is undefined, but this is NOT a failed VM-Enter
+		 */
+		return TDX_INVALID_EXIT_REASON;
 	}
 
 	exit_reason = tdx->vp_enter_ret;
@@ -950,7 +958,7 @@ static __always_inline u32 tdx_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
 		 * Defer KVM_BUG_ON() until tdx_handle_exit() because this is in
 		 * non-instrumentable code with interrupts disabled.
 		 */
-		return -1u;
+		return TDX_INVALID_EXIT_REASON;
 	default:
 		break;
 	}
@@ -981,8 +989,7 @@ static noinstr void tdx_vcpu_enter_exit(struct kvm_vcpu *vcpu)
 
 static bool tdx_failed_vmentry(struct kvm_vcpu *vcpu)
 {
-	return vmx_get_exit_reason(vcpu).failed_vmentry &&
-	       vmx_get_exit_reason(vcpu).full != -1u;
+	return vmx_get_exit_reason(vcpu).failed_vmentry;
 }
 
 static fastpath_t tdx_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
@@ -2144,7 +2151,7 @@ void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason,
 	struct vcpu_tdx *tdx = to_tdx(vcpu);
 
 	*reason = tdx->vt.exit_reason.full;
-	if (*reason != -1u) {
+	if (*reason != TDX_INVALID_EXIT_REASON) {
 		*info1 = vmx_get_exit_qual(vcpu);
 		*info2 = tdx->ext_exit_qualification;
 		*intr_info = vmx_get_intr_info(vcpu);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason()
  2026-08-12  8:02 [PATCH v3 0/4] KVM: TDX: Enable VM-DoS Prevention Features for TDX Xiaoyao Li
  2026-08-12  8:02 ` [PATCH v3 1/4] KVM: TDX: Enable Notify VM exit Xiaoyao Li
  2026-08-12  8:02 ` [PATCH v3 2/4] KVM: TDX: Set bits 31:16 to 0 for the synthesized Exit Reason Xiaoyao Li
@ 2026-08-12  8:02 ` Xiaoyao Li
  2026-08-12  8:23   ` sashiko-bot
  2026-08-12  8:02 ` [PATCH v3 4/4] KVM: TDX: Enable Bus Lock VM exit Xiaoyao Li
  3 siblings, 1 reply; 9+ messages in thread
From: Xiaoyao Li @ 2026-08-12  8:02 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: Kiryl Shutsemau, Rick Edgecombe, kvm, linux-kernel, linux-coco,
	nik.borisov, xiaoyao.li

When handling the real Exit Reason, don't assume the upper 16 bits as
all-0 in tdx_to_vmx_exit_reason(), in preparation for enabling Bus Lock
VM exit.

When Bus Lock VM exit is enabled, the bit 26 of Exit Reason becomes
valid and it can be 1 with various exit reasons. Change the logic in
tdx_to_vmx_exit_reason() to check the basic Exit Reason for correctness.
Also preserve the bit 31:16 when changing the (basic) Exit Reason, to
not lose the information in bit[31:16].

Change the return type of tdx_to_vmx_exit_reason() to
"union vmx_exit_reason" for the convenience of manipulating the basic
field.

Cc: stable@vger.kernel.org
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
---
Changes in v3:
- new patch split from patch 2 of v2.
---
 arch/x86/kvm/vmx/tdx.c | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index df23db9430f0..598b85d772e3 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -924,10 +924,10 @@ static __always_inline u32 tdcall_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
 /* Synthesized invalid Exit Reason */
 #define TDX_INVALID_EXIT_REASON		U16_MAX
 
-static __always_inline u32 tdx_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
+static __always_inline union vmx_exit_reason tdx_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
 {
 	struct vcpu_tdx *tdx = to_tdx(vcpu);
-	u32 exit_reason;
+	union vmx_exit_reason exit_reason;
 
 	switch (tdx->vp_enter_ret & TDX_SEAMCALL_STATUS_MASK) {
 	case TDX_SUCCESS:
@@ -942,23 +942,27 @@ static __always_inline u32 tdx_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
 		 * module never attempted to run the vCPU, i.e. the Exit
 		 * Reason is undefined, but this is NOT a failed VM-Enter
 		 */
-		return TDX_INVALID_EXIT_REASON;
+		return (union vmx_exit_reason) {
+			.basic = TDX_INVALID_EXIT_REASON,
+		};
 	}
 
-	exit_reason = tdx->vp_enter_ret;
+	exit_reason.full = (u32)tdx->vp_enter_ret;
 
-	switch (exit_reason) {
+	switch (exit_reason.basic) {
 	case EXIT_REASON_TDCALL:
 		if (tdvmcall_exit_type(vcpu))
-			return EXIT_REASON_VMCALL;
-
-		return tdcall_to_vmx_exit_reason(vcpu);
+			exit_reason.basic = EXIT_REASON_VMCALL;
+		else
+			exit_reason.basic = tdcall_to_vmx_exit_reason(vcpu);
+		break;
 	case EXIT_REASON_EPT_MISCONFIG:
 		/*
 		 * Defer KVM_BUG_ON() until tdx_handle_exit() because this is in
 		 * non-instrumentable code with interrupts disabled.
 		 */
-		return TDX_INVALID_EXIT_REASON;
+		exit_reason.basic = TDX_INVALID_EXIT_REASON;
+		break;
 	default:
 		break;
 	}
@@ -975,7 +979,7 @@ static noinstr void tdx_vcpu_enter_exit(struct kvm_vcpu *vcpu)
 
 	tdx->vp_enter_ret = tdh_vp_enter(&tdx->vp, &tdx->vp_enter_args);
 
-	vt->exit_reason.full = tdx_to_vmx_exit_reason(vcpu);
+	vt->exit_reason = tdx_to_vmx_exit_reason(vcpu);
 
 	vt->exit_qualification = tdx->vp_enter_args.rcx;
 	tdx->ext_exit_qualification = tdx->vp_enter_args.rdx;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 4/4] KVM: TDX: Enable Bus Lock VM exit
  2026-08-12  8:02 [PATCH v3 0/4] KVM: TDX: Enable VM-DoS Prevention Features for TDX Xiaoyao Li
                   ` (2 preceding siblings ...)
  2026-08-12  8:02 ` [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason() Xiaoyao Li
@ 2026-08-12  8:02 ` Xiaoyao Li
  2026-08-12  8:20   ` sashiko-bot
  3 siblings, 1 reply; 9+ messages in thread
From: Xiaoyao Li @ 2026-08-12  8:02 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: Kiryl Shutsemau, Rick Edgecombe, kvm, linux-kernel, linux-coco,
	nik.borisov, xiaoyao.li

Enable Bus Lock VM exit functionality for TDX guests.

Bus Lock VM exit is an existing feature supported by KVM.  Userspace can
enable Bus Lock VM exit through KVM_BUS_LOCK_DETECTION_EXIT when it's
reported as supported.  However, KVM reports the support of this CAP
just based on the hardware capability but doesn't differentiate between
VMX and TDX.  This leads to the issue that userspace can enable this cap
for TDX guests without getting an error, but the feature is not actually
enabled because KVM doesn't call the TDX module API to program the
relevant TD VMCS fields.

Enable Bus Lock VM exit for TDX guests by programming the
BUS_LOCK_DETECTION control in the TD VMCS and by adding the exit handler.
Clear the bus_lock_detected bit to avoid being counted multiple times if
it needs to return early for wait_for_sept_zap case in tdx_vcpu_run().
Since the wait_for_sept_zap case is expected to be rare, just do the
clearing of bus_lock_detected unconditionally.

Note, there is no enumeration bit for this feature by TDX module because
all TDX modules support it, and allow to set the TD VMCS as long as the
hardware supports the feature.

Fixes: 161d34609f9b ("KVM: TDX: Make TDX VM type supported")
Cc: stable@vger.kernel.org
Originally-by: Chenyi Qiang <chenyi.qiang@intel.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
---
Changes in v3:
- Refine the changelog. (Rick)

Changes in v2:
- Don't overwrite the negative return value to 0. (Sashiko)
- Clear the bus_lock_detected bit when it returns early for
  wait_for_sept_zap case.
- Add a note to clarify the feature is always supported by the TDX
  module, to make Sashiko happy.
---
 arch/x86/kvm/vmx/tdx.c | 28 ++++++++++++++++++++++++++--
 arch/x86/kvm/vmx/vmx.c |  2 +-
 arch/x86/kvm/vmx/vmx.h |  1 +
 3 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 598b85d772e3..e7047657ef78 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -1082,8 +1082,10 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
 	 * allowing vCPU entry to avoid contention with tdh_vp_enter() and
 	 * TDCALLs.
 	 */
-	if (unlikely(READ_ONCE(to_kvm_tdx(vcpu->kvm)->wait_for_sept_zap)))
+	if (unlikely(READ_ONCE(to_kvm_tdx(vcpu->kvm)->wait_for_sept_zap))) {
+		vt->exit_reason.bus_lock_detected = 0;
 		return EXIT_FASTPATH_EXIT_HANDLED;
+	}
 
 	trace_kvm_entry(vcpu, run_flags & KVM_RUN_FORCE_IMMEDIATE_EXIT);
 
@@ -2039,7 +2041,7 @@ int tdx_complete_emulated_msr(struct kvm_vcpu *vcpu, int err)
 }
 
 
-int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
+static int __tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
 {
 	struct vcpu_tdx *tdx = to_tdx(vcpu);
 	u64 vp_enter_ret = tdx->vp_enter_ret;
@@ -2140,6 +2142,8 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
 	case EXIT_REASON_NOTIFY:
 		/* NMI blocking state is handled by TDX module */
 		return __vmx_handle_notify(vcpu, vmx_get_exit_qual(vcpu));
+	case EXIT_REASON_BUS_LOCK:
+		return handle_bus_lock_vmexit(vcpu);
 	default:
 		break;
 	}
@@ -2149,6 +2153,22 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
 	return 0;
 }
 
+int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
+{
+	int ret = __tdx_handle_exit(vcpu, fastpath);
+
+	/* Exit to user space when bus lock was detected */
+	if (vmx_get_exit_reason(vcpu).bus_lock_detected) {
+		if (ret > 0) {
+			vcpu->run->exit_reason = KVM_EXIT_X86_BUS_LOCK;
+			ret = 0;
+		}
+
+		vcpu->run->flags |= KVM_RUN_X86_BUS_LOCK;
+	}
+	return ret;
+}
+
 void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason,
 		u64 *info1, u64 *info2, u32 *intr_info, u32 *error_code)
 {
@@ -3175,6 +3195,10 @@ static int tdx_vcpu_init(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *cmd)
 				vcpu->kvm->arch.notify_window);
 	}
 
+	if (vcpu->kvm->arch.bus_lock_detection_enabled)
+		td_vmcs_setbit32(tdx, SECONDARY_VM_EXEC_CONTROL,
+				 SECONDARY_EXEC_BUS_LOCK_DETECTION);
+
 	tdx->state = VCPU_TD_STATE_INITIALIZED;
 
 	return 0;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e53cc96002c7..c429db9b9205 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6265,7 +6265,7 @@ static int handle_encls(struct kvm_vcpu *vcpu)
 }
 #endif /* CONFIG_X86_SGX_KVM */
 
-static int handle_bus_lock_vmexit(struct kvm_vcpu *vcpu)
+int handle_bus_lock_vmexit(struct kvm_vcpu *vcpu)
 {
 	/*
 	 * Hardware may or may not set the BUS_LOCK_DETECTED flag on BUS_LOCK
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index dc8517f15bc4..8faf04c09721 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -379,6 +379,7 @@ bool __vmx_vcpu_run(struct vcpu_vmx *vmx, unsigned int flags);
 void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu);
 
 void vmx_set_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type, bool set);
+int handle_bus_lock_vmexit(struct kvm_vcpu *vcpu);
 
 static inline void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu,
 						 u32 msr, int type)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 4/4] KVM: TDX: Enable Bus Lock VM exit
  2026-08-12  8:02 ` [PATCH v3 4/4] KVM: TDX: Enable Bus Lock VM exit Xiaoyao Li
@ 2026-08-12  8:20   ` sashiko-bot
  2026-08-12  9:11     ` Xiaoyao Li
  0 siblings, 1 reply; 9+ messages in thread
From: sashiko-bot @ 2026-08-12  8:20 UTC (permalink / raw)
  To: Xiaoyao Li; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The preexisting VMX handler vmx_handle_exit() silently drops negative error codes when a bus lock is detected.
--

--- Patch [4]: [PATCH v3 4/4] KVM: TDX: Enable Bus Lock VM exit ---
commit 9ca659baf3acd47c81da72d83946e28ee4de710a
Author: Xiaoyao Li <xiaoyao.li@intel.com>

KVM: TDX: Enable Bus Lock VM exit

This patch enables Bus Lock VM exit functionality for TDX guests by programming
the BUS_LOCK_DETECTION control in the TD VMCS. It also introduces the exit
handler for TDX guests and ensures the bus_lock_detected bit is appropriately
managed without double-counting.

> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> index 598b85d772e37..e7047657ef780 100644
> --- a/arch/x86/kvm/vmx/tdx.c
> +++ b/arch/x86/kvm/vmx/tdx.c
[ ... ]
> @@ -2149,6 +2153,22 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
>  	return 0;
>  }
>  
> +int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)
> +{
> +	int ret = __tdx_handle_exit(vcpu, fastpath);
> +
> +	/* Exit to user space when bus lock was detected */
> +	if (vmx_get_exit_reason(vcpu).bus_lock_detected) {
> +		if (ret > 0) {
> +			vcpu->run->exit_reason = KVM_EXIT_X86_BUS_LOCK;
> +			ret = 0;
> +		}
> +
> +		vcpu->run->flags |= KVM_RUN_X86_BUS_LOCK;
> +	}
> +	return ret;
> +}

[Severity: High]
This isn't a bug introduced by this patch, but while you correctly avoid
overwriting a negative return value here, the pre-existing vmx_handle_exit()
in arch/x86/kvm/vmx/vmx.c appears to still have this flaw.

If __vmx_handle_exit() returns a negative error code (like -EFAULT), and
a bus lock is detected, does vmx_handle_exit() silently drop the negative
error code by unconditionally returning 0?

If this causes ioctl(KVM_RUN) to return 0 instead of -1, could userspace
process stale or uninitialized exit information because
vcpu->run->exit_reason was never updated? 

Should vmx_handle_exit() receive a similar fix?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260812080229.2481439-1-xiaoyao.li@intel.com?part=4

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason()
  2026-08-12  8:02 ` [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason() Xiaoyao Li
@ 2026-08-12  8:23   ` sashiko-bot
  2026-08-12  9:07     ` Xiaoyao Li
  0 siblings, 1 reply; 9+ messages in thread
From: sashiko-bot @ 2026-08-12  8:23 UTC (permalink / raw)
  To: Xiaoyao Li; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Medium] `tdx_get_exit_info()` incorrectly compares the full 32-bit exit reason against the 16-bit `TDX_INVALID_EXIT_REASON`, exposing meaningless/inconsistent exit qualification and interrupt info.

Pre-existing issues:
- [High] Strict equality checks against `vp_enter_ret` in `tdx_handle_exit()` and `tdx_vcpu_run()` fail to mask the basic exit reason, bypassing critical bug checks when upper exit reason bits are set.
--

--- Patch [3]: [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason() ---
commit 129e5cab6b9416a1d4e5f1f8c42f24dc1a44940c
Author: Xiaoyao Li <xiaoyao.li@intel.com>

KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason()
    
When handling the real Exit Reason, don't assume the upper 16 bits as
all-0 in tdx_to_vmx_exit_reason(), in preparation for enabling Bus Lock
VM exit. Also preserve the bit 31:16 when changing the (basic) Exit
Reason, to not lose the information in bit[31:16].

> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> index df23db9430f06..598b85d772e37 100644
> --- a/arch/x86/kvm/vmx/tdx.c
> +++ b/arch/x86/kvm/vmx/tdx.c
[ ... ]
> @@ -942,23 +942,27 @@ static __always_inline u32 tdx_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
>  		 * module never attempted to run the vCPU, i.e. the Exit
>  		 * Reason is undefined, but this is NOT a failed VM-Enter
>  		 */
> -		return TDX_INVALID_EXIT_REASON;
> +		return (union vmx_exit_reason) {
> +			.basic = TDX_INVALID_EXIT_REASON,
> +		};
>  	}
>  
> -	exit_reason = tdx->vp_enter_ret;
> +	exit_reason.full = (u32)tdx->vp_enter_ret;
>  
> -	switch (exit_reason) {
> +	switch (exit_reason.basic) {
>  	case EXIT_REASON_TDCALL:
>  		if (tdvmcall_exit_type(vcpu))
> -			return EXIT_REASON_VMCALL;
> -
> -		return tdcall_to_vmx_exit_reason(vcpu);
> +			exit_reason.basic = EXIT_REASON_VMCALL;
> +		else
> +			exit_reason.basic = tdcall_to_vmx_exit_reason(vcpu);
> +		break;
>  	case EXIT_REASON_EPT_MISCONFIG:
>  		/*
>  		 * Defer KVM_BUG_ON() until tdx_handle_exit() because this is in
>  		 * non-instrumentable code with interrupts disabled.
>  		 */
> -		return TDX_INVALID_EXIT_REASON;
> +		exit_reason.basic = TDX_INVALID_EXIT_REASON;
> +		break;

[Severity: Medium]
When the basic exit reason is set to TDX_INVALID_EXIT_REASON here, but upper 
bits like Bus Lock are preserved in exit_reason.full, does this break 
tdx_get_exit_info()?

In tdx_get_exit_info(), it evaluates against the full 32-bit exit reason:

    *reason = tdx->vt.exit_reason.full;
    if (*reason != TDX_INVALID_EXIT_REASON) {
        *info1 = vmx_get_exit_qual(vcpu);
        ...

Since TDX_INVALID_EXIT_REASON is the 16-bit 0xFFFF, this check will pass if 
any upper bits are set, even if the basic reason was marked invalid, 
incorrectly exposing meaningless hardware state.

[Severity: High]
This isn't a bug introduced by this patch, but by highlighting that 
vp_enter_ret can contain upper exit reason bits (like bit 26), does this 
expose a flaw in the strict equality checks for EPT misconfigurations?

In tdx_vcpu_run(), the check is strictly against the 64-bit value:

    if (unlikely(tdx->vp_enter_ret == EXIT_REASON_EPT_MISCONFIG))
        return EXIT_FASTPATH_NONE;

Similarly, in tdx_handle_exit(), the bug check is also a strict comparison:

    if (unlikely(vp_enter_ret == EXIT_REASON_EPT_MISCONFIG)) {
        KVM_BUG_ON(1, vcpu->kvm);
        return -EIO;
    }

If a hardware EPT misconfig occurs while upper bits are set in the raw 
vp_enter_ret, won't these checks fail to identify the misconfig, bypassing 
the KVM_BUG_ON() entirely?

>  	default:
>  		break;
>  	}
>
>  	return exit_reason;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260812080229.2481439-1-xiaoyao.li@intel.com?part=3

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason()
  2026-08-12  8:23   ` sashiko-bot
@ 2026-08-12  9:07     ` Xiaoyao Li
  0 siblings, 0 replies; 9+ messages in thread
From: Xiaoyao Li @ 2026-08-12  9:07 UTC (permalink / raw)
  To: sashiko-reviews, Sean Christopherson, Paolo Bonzini; +Cc: kvm

On 8/12/2026 4:23 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> 
> New issues:
> - [Medium] `tdx_get_exit_info()` incorrectly compares the full 32-bit exit reason against the 16-bit `TDX_INVALID_EXIT_REASON`, exposing meaningless/inconsistent exit qualification and interrupt info.
> 
> Pre-existing issues:
> - [High] Strict equality checks against `vp_enter_ret` in `tdx_handle_exit()` and `tdx_vcpu_run()` fail to mask the basic exit reason, bypassing critical bug checks when upper exit reason bits are set.
> --
> 
> --- Patch [3]: [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason() ---
> commit 129e5cab6b9416a1d4e5f1f8c42f24dc1a44940c
> Author: Xiaoyao Li <xiaoyao.li@intel.com>
> 
> KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason()
>      
> When handling the real Exit Reason, don't assume the upper 16 bits as
> all-0 in tdx_to_vmx_exit_reason(), in preparation for enabling Bus Lock
> VM exit. Also preserve the bit 31:16 when changing the (basic) Exit
> Reason, to not lose the information in bit[31:16].
> 
>> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
>> index df23db9430f06..598b85d772e37 100644
>> --- a/arch/x86/kvm/vmx/tdx.c
>> +++ b/arch/x86/kvm/vmx/tdx.c
> [ ... ]
>> @@ -942,23 +942,27 @@ static __always_inline u32 tdx_to_vmx_exit_reason(struct kvm_vcpu *vcpu)
>>   		 * module never attempted to run the vCPU, i.e. the Exit
>>   		 * Reason is undefined, but this is NOT a failed VM-Enter
>>   		 */
>> -		return TDX_INVALID_EXIT_REASON;
>> +		return (union vmx_exit_reason) {
>> +			.basic = TDX_INVALID_EXIT_REASON,
>> +		};
>>   	}
>>   
>> -	exit_reason = tdx->vp_enter_ret;
>> +	exit_reason.full = (u32)tdx->vp_enter_ret;
>>   
>> -	switch (exit_reason) {
>> +	switch (exit_reason.basic) {
>>   	case EXIT_REASON_TDCALL:
>>   		if (tdvmcall_exit_type(vcpu))
>> -			return EXIT_REASON_VMCALL;
>> -
>> -		return tdcall_to_vmx_exit_reason(vcpu);
>> +			exit_reason.basic = EXIT_REASON_VMCALL;
>> +		else
>> +			exit_reason.basic = tdcall_to_vmx_exit_reason(vcpu);
>> +		break;
>>   	case EXIT_REASON_EPT_MISCONFIG:
>>   		/*
>>   		 * Defer KVM_BUG_ON() until tdx_handle_exit() because this is in
>>   		 * non-instrumentable code with interrupts disabled.
>>   		 */
>> -		return TDX_INVALID_EXIT_REASON;
>> +		exit_reason.basic = TDX_INVALID_EXIT_REASON;
>> +		break;
> 
> [Severity: Medium]
> When the basic exit reason is set to TDX_INVALID_EXIT_REASON here, but upper
> bits like Bus Lock are preserved in exit_reason.full, does this break
> tdx_get_exit_info()?
> 
> In tdx_get_exit_info(), it evaluates against the full 32-bit exit reason:
> 
>      *reason = tdx->vt.exit_reason.full;
>      if (*reason != TDX_INVALID_EXIT_REASON) {
>          *info1 = vmx_get_exit_qual(vcpu);
>          ...
> 
> Since TDX_INVALID_EXIT_REASON is the 16-bit 0xFFFF, this check will pass if
> any upper bits are set, even if the basic reason was marked invalid,
> incorrectly exposing meaningless hardware state.

Oh, I should have called it out it's intentional to check the full 32 
bits Exit Reason instead of checking the lower 16 bits .basic field as 
what it did in the v2.

The purpose here is to return zero value for info1/info2/intr_info when 
there is no real Exit Reason, i.e., no real VM-Enter has happened. "Full 
32 bits Exit Reason equals TDX_INVALID_EXIT_REASON" indicates the case 
of no real Exit Reason.

There is one exception though: when real EPT_MISCONFIG happens without 
bus_lock_detected being 1. In this case, it's a false positive and KVM 
should go to the else path to read the info1/info2/intr_info. As noted 
in patch 2 under "---", this is an pre-existing issue and it will be 
address in a future series.

For the case that Sashiko cares, the only possible one is that real 
EPT_MISCONFIG happens when bus_lock_detected. In this case, passing the 
check of (*reason != TDX_INVALID_EXIT_REASON) to read the value of 
info1/info2/intr_info happens to be what we expect. (However, the exit 
reason is 0xFFFF instead of EPT_MISCONFIG). Anyway, it won't expose 
meaningless hardware state.

So, considering that the false-positive on EPT_MISCONFIG cannot be fully 
addressed with either checking the full 32 bits or checking the 16 bits 
basic field, and checking the full 32 bits can ensure identifying the no 
real Exit Reason cases. I just leave it as-is instead of changing it to 
check the basic field.

> [Severity: High]
> This isn't a bug introduced by this patch, but by highlighting that
> vp_enter_ret can contain upper exit reason bits (like bit 26), does this
> expose a flaw in the strict equality checks for EPT misconfigurations?
> 
> In tdx_vcpu_run(), the check is strictly against the 64-bit value:
> 
>      if (unlikely(tdx->vp_enter_ret == EXIT_REASON_EPT_MISCONFIG))
>          return EXIT_FASTPATH_NONE;
> 
> Similarly, in tdx_handle_exit(), the bug check is also a strict comparison:
> 
>      if (unlikely(vp_enter_ret == EXIT_REASON_EPT_MISCONFIG)) {
>          KVM_BUG_ON(1, vcpu->kvm);
>          return -EIO;
>      }
> 
> If a hardware EPT misconfig occurs while upper bits are set in the raw
> vp_enter_ret, won't these checks fail to identify the misconfig, bypassing
> the KVM_BUG_ON() entirely?

If it happens, it bypasses the KVM_BUG_ON(). But it will finally exit to 
userspace due to invalid Exit Reason, 0xFFFF.

Indeed, it may cause behavior change. But considering the EPT_MISCONFIG 
is not supposed to happen with current KVM's code, and we want to 
backport the series to stable kernels. We don't want to handle the 
theoretical issue that cannot happen in practice with this series, to 
make the series simple. We can harden the EPT_MISCONFIG code with 
another series separately.

>>   	default:
>>   		break;
>>   	}
>>
>>   	return exit_reason;
>>   }
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 4/4] KVM: TDX: Enable Bus Lock VM exit
  2026-08-12  8:20   ` sashiko-bot
@ 2026-08-12  9:11     ` Xiaoyao Li
  0 siblings, 0 replies; 9+ messages in thread
From: Xiaoyao Li @ 2026-08-12  9:11 UTC (permalink / raw)
  To: sashiko-reviews, Sean Christopherson; +Cc: kvm

On 8/12/2026 4:20 PM, sashiko-bot@kernel.org wrote:
> Pre-existing issues:
> - [High] The preexisting VMX handler vmx_handle_exit() silently drops negative error codes when a bus lock is detected.

As replied in the v2: VMX fix was sent already

https://lore.kernel.org/all/20260806111923.1990562-2-xiaoyao.li@intel.com/

BTW, Sean,

Will you pick the VMX fix separately from the series, if it looks OK to 
you? So the next version of that series can focus on the general x86 CAP 
changes.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-08-12  9:13 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-12  8:02 [PATCH v3 0/4] KVM: TDX: Enable VM-DoS Prevention Features for TDX Xiaoyao Li
2026-08-12  8:02 ` [PATCH v3 1/4] KVM: TDX: Enable Notify VM exit Xiaoyao Li
2026-08-12  8:02 ` [PATCH v3 2/4] KVM: TDX: Set bits 31:16 to 0 for the synthesized Exit Reason Xiaoyao Li
2026-08-12  8:02 ` [PATCH v3 3/4] KVM: TDX: Don't assume exit_reason[31:16] as all-0 in tdx_to_vmx_exit_reason() Xiaoyao Li
2026-08-12  8:23   ` sashiko-bot
2026-08-12  9:07     ` Xiaoyao Li
2026-08-12  8:02 ` [PATCH v3 4/4] KVM: TDX: Enable Bus Lock VM exit Xiaoyao Li
2026-08-12  8:20   ` sashiko-bot
2026-08-12  9:11     ` Xiaoyao Li

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox