[PATCH] coco/tdx-host: Don't expose P-SEAMLDR features on CPUs with erratum

From: Chao Gao

Date: Tue Mar 10 2026 - 21:49:41 EST


Some TDX-capable CPUs have an erratum, as documented in Intel� Trust
Domain CPU Architectural Extensions (May 2021 edition) Chapter 2.3:

SEAMRET from the P-SEAMLDR clears the current VMCS structure pointed
to by the current-VMCS pointer. A VMM that invokes the P-SEAMLDR using
SEAMCALL must reload the current-VMCS, if required, using the VMPTRLD
instruction.

Clearing the current VMCS behind KVM's back will break KVM.

This erratum is not present when IA32_VMX_BASIC[60] is set. Check for
the erratum and refuse to expose P-SEAMLDR features (e.g., TDX module
updates) on affected CPUs.

== Alternatives ==
Two workarounds were considered but both were rejected:

1. Save/restore the current VMCS around P-SEAMLDR calls. This produces ugly
assembly code [1] and doesn't play well with #MCE or #NMI if they
need to use the current VMCS.

2. Move KVM's VMCS tracking logic to the TDX core code, which would break
the boundary between KVM and the TDX core code [2].

Signed-off-by: Chao Gao <chao.gao@xxxxxxxxx>
Link: https://lore.kernel.org/kvm/fedb3192-e68c-423c-93b2-a4dc2f964148@xxxxxxxxx/ # [1]
Link: https://lore.kernel.org/kvm/aYIXFmT-676oN6j0@xxxxxxxxxx/ # [2]
---
arch/x86/include/asm/vmx.h | 1 +
drivers/virt/coco/tdx-host/tdx-host.c | 12 ++++++++++++
2 files changed, 13 insertions(+)

diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index c85c50019523..d066c50b9051 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -135,6 +135,7 @@
#define VMX_BASIC_INOUT BIT_ULL(54)
#define VMX_BASIC_TRUE_CTLS BIT_ULL(55)
#define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56)
+#define VMX_BASIC_PRESERVE_CURRENT_VMCS BIT_ULL(60)

static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic)
{
diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index 891cc6a083e0..13c23769d09d 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -12,8 +12,10 @@
#include <linux/sysfs.h>

#include <asm/cpu_device_id.h>
+#include <asm/msr.h>
#include <asm/seamldr.h>
#include <asm/tdx.h>
+#include <asm/vmx.h>

static const struct x86_cpu_id tdx_host_ids[] = {
X86_MATCH_FEATURE(X86_FEATURE_TDX_HOST_PLATFORM, NULL),
@@ -175,6 +177,7 @@ static int seamldr_init(struct device *dev)
{
const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo();
struct fw_upload *tdx_fwl;
+ u64 basic_msr;

if (WARN_ON_ONCE(!tdx_sysinfo))
return -EIO;
@@ -182,6 +185,15 @@ static int seamldr_init(struct device *dev)
if (!tdx_supports_runtime_update(tdx_sysinfo))
return 0;

+ /*
+ * Some TDX-capable CPUs have an erratum where the current VMCS may
+ * be cleared after calling into P-SEAMLDR. Ensure no such erratum
+ * exists before exposing any P-SEAMLDR functions.
+ */
+ rdmsrq(MSR_IA32_VMX_BASIC, basic_msr);
+ if (!(basic_msr & VMX_BASIC_PRESERVE_CURRENT_VMCS))
+ return 0;
+
tdx_fwl = firmware_upload_register(THIS_MODULE, dev, "tdx_module",
&tdx_fw_ops, NULL);
if (IS_ERR(tdx_fwl))
--
2.47.3