P-SEAMLDR is another component alongside the TDX module within the protected SEAM range. Software can invoke its functions by executing the SEAMCALL instruction with the 63 bit of RAX set to 1. P-SEAMLDR SEAMCALLs differ from those of the TDX module in terms of error codes and the handling of the current VMCS. Add a wrapper for P-SEAMLDR SEAMCALLs based on the SEAMCALL infrastructure. Intel® Trust Domain CPU Architectural Extensions (May 2021 edition) Chapter 2.3 states: 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. So, save and restore the current-VMCS pointer using VMPTRST and VMPTRLD instructions to avoid breaking KVM, which manages the current-VMCS. 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@xxxxxxxxx> Tested-by: Farrah Chen <farrah.chen@xxxxxxxxx> --- arch/x86/Kconfig | 10 ++++++++ arch/x86/virt/vmx/tdx/Makefile | 1 + arch/x86/virt/vmx/tdx/seamldr.c | 44 +++++++++++++++++++++++++++++++++ arch/x86/virt/vmx/vmx.h | 40 ++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 arch/x86/virt/vmx/tdx/seamldr.c create mode 100644 arch/x86/virt/vmx/vmx.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 4b9f378e05f6..8b1e0986b7f8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1932,6 +1932,16 @@ config INTEL_TDX_HOST If unsure, say N. +config INTEL_TDX_MODULE_UPDATE + bool "Intel TDX module runtime update" + depends on INTEL_TDX_HOST + help + This enables the kernel to support TDX module runtime update. This allows + the admin to upgrade the TDX module to a newer one 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..a252f1ae3483 --- /dev/null +++ b/arch/x86/virt/vmx/tdx/seamldr.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2025 Intel Corporation. + * + * Intel TDX module runtime update + */ +#define pr_fmt(fmt) "seamldr: " fmt + +#include <linux/cleanup.h> + +#include "tdx.h" +#include "../vmx.h" + +static __maybe_unused int seamldr_call(u64 fn, struct tdx_module_args *args) +{ + u64 vmcs; + int ret; + + if (!is_seamldr_call(fn)) + return -EINVAL; + + /* + * SEAMRET from P-SEAMLDR invalidates the current-VMCS pointer. + * Save/restore current-VMCS pointer across P-SEAMLDR SEAMCALLs so + * that VMX instructions won't fail due to an invalid current-VMCS. + * + * Disable interrupt to prevent SMP call functions from seeing the + * invalid current-VMCS. + */ + guard(irqsave)(); + + ret = cpu_vmcs_store(&vmcs); + if (ret) + return ret; + + ret = seamldr_prerr(fn, args); + + /* Restore current-VMCS pointer */ +#define INVALID_VMCS -1ULL + if (vmcs != INVALID_VMCS) + WARN_ON_ONCE(cpu_vmcs_load(vmcs)); + + return ret; +} diff --git a/arch/x86/virt/vmx/vmx.h b/arch/x86/virt/vmx/vmx.h new file mode 100644 index 000000000000..51e6460fd1fd --- /dev/null +++ b/arch/x86/virt/vmx/vmx.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ARCH_X86_VIRT_VMX_H +#define ARCH_X86_VIRT_VMX_H + +#include <linux/printk.h> + +static inline int cpu_vmcs_load(u64 vmcs_pa) +{ + asm goto("1: vmptrld %0\n\t" + ".byte 0x2e\n\t" /* branch not taken hint */ + "jna %l[error]\n\t" + _ASM_EXTABLE(1b, %l[fault]) + : : "m" (vmcs_pa) : "cc" : error, fault); + return 0; + +error: + pr_err_once("vmptrld failed: %llx\n", vmcs_pa); + return -EIO; +fault: + pr_err_once("vmptrld faulted\n"); + return -EIO; +} + +static inline int cpu_vmcs_store(u64 *vmcs_pa) +{ + int ret = -EIO; + + asm volatile("1: vmptrst %0\n\t" + "mov $0, %1\n\t" + "2:\n\t" + _ASM_EXTABLE(1b, 2b) + : "=m" (*vmcs_pa), "+r" (ret) : :); + + if (ret) + pr_err_once("vmptrst faulted\n"); + + return ret; +} + +#endif -- 2.47.1