[PATCH v4 24/24] [NOT-FOR-REVIEW] x86/virt/seamldr: Save and restore current VMCS
From: Chao Gao
Date: Thu Feb 12 2026 - 09:46:23 EST
P-SEAMLDR calls clobber the current VMCS as documented in Intel® Trust
Domain CPU Architectural Extensions (May 2021 edition) Chapter 2.3 [1]:
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.
Save and restore the current VMCS using VMPTRST and VMPTRLD instructions
to avoid breaking KVM.
Signed-off-by: Chao Gao <chao.gao@xxxxxxxxx>
---
This patch is needed for testing until microcode is updated to preserve
the current VMCS across P-SEAMLDR calls. Otherwise, if some normal VMs
are running before TDX Module updates, vmread/vmwrite errors may occur
immediately after updates.
---
arch/x86/include/asm/special_insns.h | 22 ++++++++++++++++++++++
arch/x86/virt/vmx/tdx/seamldr.c | 16 +++++++++++++++-
2 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h
index 46aa2c9c1bda..a3e9a139b669 100644
--- a/arch/x86/include/asm/special_insns.h
+++ b/arch/x86/include/asm/special_insns.h
@@ -303,6 +303,28 @@ static __always_inline void tile_release(void)
asm volatile(".byte 0xc4, 0xe2, 0x78, 0x49, 0xc0");
}
+static inline int vmptrst(u64 *vmcs_pa)
+{
+ asm goto("1: vmptrst %0\n\t"
+ _ASM_EXTABLE(1b, %l[error])
+ : "=m" (*vmcs_pa) : : "cc" : error);
+
+ return 0;
+error:
+ return -EIO;
+}
+
+static inline int vmptrld(u64 vmcs_pa)
+{
+ asm goto("1: vmptrld %0\n\t"
+ "jna %l[error]\n\t"
+ _ASM_EXTABLE(1b, %l[error])
+ : : "m" (vmcs_pa) : "cc" : error);
+ return 0;
+error:
+ return -EIO;
+}
+
#endif /* __KERNEL__ */
#endif /* _ASM_X86_SPECIAL_INSNS_H */
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index 3f37cc6c68ff..02695307b8a0 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -16,6 +16,7 @@
#include <linux/stop_machine.h>
#include <asm/seamldr.h>
+#include <asm/special_insns.h>
#include "seamcall_internal.h"
#include "tdx.h"
@@ -59,12 +60,25 @@ static DEFINE_RAW_SPINLOCK(seamldr_lock);
static int seamldr_call(u64 fn, struct tdx_module_args *args)
{
+ u64 current_vmcs = -1ULL;
+ int ret;
+
/*
* Serialize P-SEAMLDR calls and disable interrupts as the calls
* can be made from IRQ context.
*/
guard(raw_spinlock_irqsave)(&seamldr_lock);
- return seamcall_prerr(fn, args);
+
+ /*
+ * P-SEAMLDR calls clobber the current VMCS. Save and restore it.
+ * -1 indicates invalid VMCS and no restoration is needed.
+ */
+ WARN_ON_ONCE(vmptrst(¤t_vmcs));
+ ret = seamcall_prerr(fn, args);
+ if (current_vmcs != -1ULL)
+ WARN_ON_ONCE(vmptrld(current_vmcs));
+
+ return ret;
}
int seamldr_get_info(struct seamldr_info *seamldr_info)
--
2.47.3