[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251001025442.427697-6-chao.gao@intel.com>
Date: Tue, 30 Sep 2025 19:52:49 -0700
From: Chao Gao <chao.gao@...el.com>
To: linux-coco@...ts.linux.dev,
linux-kernel@...r.kernel.org,
x86@...nel.org,
reinette.chatre@...el.com,
ira.weiny@...el.com,
kai.huang@...el.com,
dan.j.williams@...el.com,
yilun.xu@...ux.intel.com,
sagis@...gle.com,
vannapurve@...gle.com,
paulmck@...nel.org,
nik.borisov@...e.com
Cc: Chao Gao <chao.gao@...el.com>,
Farrah Chen <farrah.chen@...el.com>,
Thomas Gleixner <tglx@...utronix.de>,
Ingo Molnar <mingo@...hat.com>,
Borislav Petkov <bp@...en8.de>,
Dave Hansen <dave.hansen@...ux.intel.com>,
"H. Peter Anvin" <hpa@...or.com>,
"Kirill A. Shutemov" <kas@...nel.org>
Subject: [PATCH v2 05/21] x86/virt/seamldr: Introduce a wrapper for P-SEAMLDR SEAMCALLs
Software needs to talk with P-SEAMLDR via P-SEAMLDR SEAMCALLs. So, add a
wrapper for P-SEAMLDR SEAMCALLs.
Save and restore the current VMCS using VMPTRST and VMPTRLD instructions
to avoid breaking KVM. Doing so is because P-SEAMLDR SEAMCALLs would
invalidate 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.
Disable interrupts to prevent KVM code from interfering with P-SEAMLDR
SEAMCALLs. For example, if a vCPU is scheduled before the current VMCS is
restored, it may encounter an invalid current VMCS, causing its VMX
instruction to fail. Additionally, if KVM sends IPIs to invalidate a
current VMCS and the invalidation occurs right after the current VMCS is
saved, that VMCS will be reloaded after P-SEAMLDR SEAMCALLs, leading to
unexpected behavior.
NMIs are not a problem, as the only scenario where instructions relying on
the current-VMCS are used is during guest PMI handling in KVM. This occurs
immediately after VM exits with IRQ and NMI disabled, ensuring no
interference with P-SEAMLDR SEAMCALLs.
Signed-off-by: Chao Gao <chao.gao@...el.com>
Tested-by: Farrah Chen <farrah.chen@...el.com>
Link: https://cdrdv2.intel.com/v1/dl/getContent/733582 # [1]
---
v2:
- don't create a new, inferior framework to save/restore VMCS
- use human-friendly language, just "current VMCS" rather than
SDM term "current-VMCS pointer"
- don't mix guard() with goto
---
arch/x86/Kconfig | 10 ++++++
arch/x86/virt/vmx/tdx/Makefile | 1 +
arch/x86/virt/vmx/tdx/seamldr.c | 56 +++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
create mode 100644 arch/x86/virt/vmx/tdx/seamldr.c
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 58d890fe2100..6b47383d2958 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1905,6 +1905,16 @@ config INTEL_TDX_HOST
If unsure, say N.
+config INTEL_TDX_MODULE_UPDATE
+ bool "Intel TDX module runtime update"
+ depends on TDX_HOST_SERVICES
+ help
+ This enables the kernel to support TDX module runtime update. This
+ allows the admin to update the TDX module to the same or any newer
+ version without the need to terminate running TDX guests.
+
+ If unsure, say N.
+
config EFI
bool "EFI runtime service support"
depends on ACPI
diff --git a/arch/x86/virt/vmx/tdx/Makefile b/arch/x86/virt/vmx/tdx/Makefile
index 90da47eb85ee..26aea3531c36 100644
--- a/arch/x86/virt/vmx/tdx/Makefile
+++ b/arch/x86/virt/vmx/tdx/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += seamcall.o tdx.o
+obj-$(CONFIG_INTEL_TDX_MODULE_UPDATE) += seamldr.o
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
new file mode 100644
index 000000000000..b99d73f7bb08
--- /dev/null
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(c) 2025 Intel Corporation.
+ *
+ * Intel TDX module runtime update
+ */
+#define pr_fmt(fmt) "seamldr: " fmt
+
+#include <linux/irqflags.h>
+#include <linux/types.h>
+
+#include "seamcall.h"
+
+static __maybe_unused int seamldr_call(u64 fn, struct tdx_module_args *args)
+{
+ unsigned long flags;
+ u64 vmcs;
+ int ret;
+
+ if (!is_seamldr_call(fn))
+ return -EINVAL;
+
+ /*
+ * SEAMRET from P-SEAMLDR invalidates the current VMCS. Save/restore
+ * the VMCS across P-SEAMLDR SEAMCALLs to avoid clobbering KVM state.
+ * Disable interrupts as KVM is allowed to do VMREAD/VMWRITE in IRQ
+ * context (but not NMI context).
+ */
+ local_irq_save(flags);
+
+ asm goto("1: vmptrst %0\n\t"
+ _ASM_EXTABLE(1b, %l[error])
+ : "=m" (vmcs) : : "cc" : error);
+
+ ret = seamldr_prerr(fn, args);
+
+ /*
+ * Restore the current VMCS pointer. VMPTSTR "returns" all ones if the
+ * current VMCS is invalid.
+ */
+ if (vmcs != -1ULL) {
+ asm goto("1: vmptrld %0\n\t"
+ "jna %l[error]\n\t"
+ _ASM_EXTABLE(1b, %l[error])
+ : : "m" (vmcs) : "cc" : error);
+ }
+
+ local_irq_restore(flags);
+ return ret;
+
+error:
+ local_irq_restore(flags);
+
+ WARN_ONCE(1, "Failed to save/restore the current VMCS");
+ return -EIO;
+}
--
2.47.3
Powered by blists - more mailing lists