* [PATCH v10 00/25] Runtime TDX module update support
@ 2026-05-20 13:38 Chao Gao
2026-05-20 13:38 ` [PATCH v10 25/25] x86/virt/tdx: Document TDX module update Chao Gao
2026-05-20 13:46 ` [PATCH v10 00/25] Runtime TDX module update support Chao Gao
0 siblings, 2 replies; 3+ messages in thread
From: Chao Gao @ 2026-05-20 13:38 UTC (permalink / raw)
To: kvm, linux-coco, x86, linux-kernel, linux-rt-devel, linux-doc
Cc: binbin.wu, dave.hansen, djbw, ira.weiny, kai.huang, kas,
nik.borisov, paulmck, pbonzini, reinette.chatre, rick.p.edgecombe,
sagis, seanjc, tony.lindgren, vannapurve, vishal.l.verma,
yilun.xu, xiaoyao.li, yan.y.zhao, Chao Gao, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, H. Peter Anvin,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
Jonathan Corbet, Shuah Khan
Hi Dave & Rick,
Thanks for your thorough review of v9. This v10 addresses the issues you
pointed out. The main changes in this version are polishing changelogs
and variable renames to improve readability. Specifically:
- Patches 1-2 (new): Split the original "Consolidate TDX global
initialization states" into two steps — first move the statics to
file scope, then clarify the result-caching logic in
try_init_module_global().
- Patch 6: Removed user-facing Kconfig help text for TDX_HOST_SERVICES
(now a silent tristate auto-selected by INTEL_TDX_HOST).
- Patch 13: Renamed "size" to "data_len" in seamldr_install_module()
and init_seamldr_params(); renamed "HEADER_SIZE" to
"TDX_IMAGE_HEADER_SIZE"; renamed "primary" to "is_lead_cpu" in the
update state machine.
- Patch 13: Added early data_len validation and explicit bounds checks
on sigstruct_nr_pages/module_nr_pages against SEAMLDR_MAX_NR_*
limits, removing the implicit clamping in populate_pa_list().
- Patch 22: Fixed BIT(16) -> BIT_ULL(16) for
TDX_SYS_SHUTDOWN_AVOID_COMPAT_SENSITIVE.
- Patch 22: Removed unused TDX_FEATURES0_UPDATE_COMPAT definition.
- Various patches: Shortened sysfs ABI descriptions, tightened
comments across seamldr.h and seamldr.c, and minor style fixes
(return 0 -> return false, unfolded conditionals)
Please take a look at this new version. I hope it can still be merged
for 7.2.
---
(For transparency, note that I used AI tools to help proofread this
cover-letter and commit messages)
This series adds support for runtime TDX module updates that preserve
running TDX guests. It is also available at:
https://github.com/gaochaointel/linux-dev/commits/tdx-module-updates-v10/
== Background ==
Intel TDX isolates Trusted Domains (TDs), or confidential guests, from the
host. A key component of Intel TDX is the TDX module, which enforces
security policies to protect the memory and CPU states of TDs from the
host. However, the TDX module is software that requires updates.
== Problems ==
Currently, the TDX module is loaded by the BIOS at boot time, and the only
way to update it is through a reboot, which results in significant system
downtime. Users expect the TDX module to be updatable at runtime without
disrupting TDX guests.
== Solution ==
On TDX platforms, P-SEAMLDR[1] is a component within the protected SEAM
range. It is loaded by the BIOS and provides the host with functions to
install a TDX module at runtime.
This series implements runtime TDX module updates through the fw_upload
mechanism. That interface is a good fit because TDX module selection is not
a simple "load a known file from disk" problem. The update image to load
depends on module versioning, compatibility rules. fw_upload lets userspace
choose the module explicitly while the kernel provides the update
mechanism.
This design intentionally keeps most update validation/policy in userspace.
The kernel exposes the information userspace needs, such as TDX module
version and P-SEAMLDR information, but userspace is responsible for
understanding TDX module's versioning and compatibility rules and for
choosing an appropriate update image (see "TDX module versioning" below).
The kernel still enforces the pieces that must be handled in-kernel:
1. Validate the tdx_blob header fields that are not passed through tothe
TDX module. Just the standard overflow and reserved bits defensive ABI stuff.
2. Make sure no non-update SEAMCALLs are called during the update.
3. Make sure SEAMCALLs are on the right CPU, for any the user has made
available to the kernel.
4. Handle the race between updates and concurrent TD builds by
returning -EBUSY to userspace.
Everything else remains a userspace responsibility.
In the unlikely event the update fails, for example userspace picks an
incompatible update image, or the image is otherwise corrupted, all TDs
will experience SEAMCALL failures and be killed. The recovery of TD
operation from that event requires a reboot.
Given there is no mechanism to quiesce SEAMCALLs, the TDs themselves must
pause execution over an update. The most straightforward way to meet the
'pause TDs while update executes' constraint is to run the update in
stop_machine() context. All other evaluated solutions export more
complexity to KVM, or exports more fragility to userspace.
== How to test this series ==
NOTE: This v10 uses a new tdx_blob format. The scripts and module blobs in
https://github.com/intel/tdx-module-binaries have not yet been updated
to match this version. Those updates will be done separately later.
== Other information relevant to Runtime TDX module updates ==
=== TDX module versioning ===
Each TDX module is assigned a version number x.y.z, where x represents the
"major" version, y the "minor" version, and z the "update" version.
Runtime TDX module updates are restricted to Z-stream releases.
Note that Z-stream releases do not necessarily guarantee compatibility. A
new release may not be compatible with all previous versions. To address this,
Intel provides a separate file containing compatibility information, which
specifies the minimum module version required for a particular update. This
information is referenced by the tool to determine if two modules are
compatible.
=== TCB Stability ===
Updates change the TCB as viewed by attestation reports. In TDX there is
a distinction between "launch-time" version and "current" version where
runtime TDX module updates cause that "current" version number to change,
subject to Z-stream constraints.
The concern that a malicious host may attack confidential VMs by loading
insecure updates was addressed by Alex in [3]. Similarly, the scenario
where some "theoretical paranoid tenant" in the cloud wants to audit
updates and stop trusting the host after updates until audit completion
was also addressed in [4]. Users not in the cloud control the host machine
and can manage updates themselves, so they don't have these concerns.
See more about the implications of current TCB version changes in
attestation as summarized by Dave in [5].
=== TDX module Distribution Model ===
At a high level, Intel publishes all TDX modules on the github [2], along
with a mapping_file.json which documents the compatibility information
about each TDX module and a userspace tool to install the TDX module. OS
vendors can package these modules and distribute them. Administrators
install the package and use the tool to select the appropriate TDX module
and install it via the interfaces exposed by this series.
[1]: https://cdrdv2.intel.com/v1/dl/getContent/733584
[2]: https://github.com/intel/tdx-module-binaries
[3]: https://lore.kernel.org/all/665c5ae0-4b7c-4852-8995-255adf7b3a2f@amazon.com/
[4]: https://lore.kernel.org/all/5d1da767-491b-4077-b472-2cc3d73246d6@amazon.com/
[5]: https://lore.kernel.org/all/94d6047e-3b7c-4bc1-819c-85c16ff85abf@intel.com/
Chao Gao (24):
x86/virt/tdx: Clarify try_init_module_global() result caching
x86/virt/tdx: Move TDX global initialization states to file scope
x86/virt/tdx: Consolidate TDX global initialization states
x86/virt/tdx: Move TDX_FEATURES0 bits to asm/tdx.h
coco/tdx-host: Introduce a "tdx_host" device
coco/tdx-host: Expose TDX module version
x86/virt/seamldr: Introduce a wrapper for P-SEAMLDR SEAMCALLs
x86/virt/seamldr: Add a helper to retrieve P-SEAMLDR information
coco/tdx-host: Expose P-SEAMLDR information via sysfs
coco/tdx-host: Don't expose P-SEAMLDR information on CPUs with erratum
coco/tdx-host: Implement firmware upload sysfs ABI for TDX module
updates
x86/virt/seamldr: Allocate and populate a module update request
x86/virt/seamldr: Introduce skeleton for TDX module updates
x86/virt/seamldr: Abort updates after a failed step
x86/virt/seamldr: Shut down the current TDX module
x86/virt/tdx: Reset software states during TDX module shutdown
x86/virt/seamldr: Install a new TDX module
x86/virt/seamldr: Do TDX global and per-CPU init after module
installation
x86/virt/tdx: Restore TDX module state
x86/virt/tdx: Refresh TDX module version after update
x86/virt/tdx: Reject updates during compatibility-sensitive operations
x86/virt/tdx: Enable TDX module runtime updates
coco/tdx-host: Document TDX module update compatibility criteria
x86/virt/tdx: Document TDX module update
Kai Huang (1):
x86/virt/tdx: Move low level SEAMCALL helpers out of <asm/tdx.h>
.../ABI/testing/sysfs-devices-faux-tdx-host | 66 ++++
Documentation/arch/x86/tdx.rst | 34 ++
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/seamldr.h | 36 ++
arch/x86/include/asm/tdx.h | 66 +---
arch/x86/include/asm/tdx_global_metadata.h | 4 +
arch/x86/include/asm/vmx.h | 1 +
arch/x86/virt/vmx/tdx/Makefile | 2 +-
arch/x86/virt/vmx/tdx/seamcall_internal.h | 109 ++++++
arch/x86/virt/vmx/tdx/seamldr.c | 324 ++++++++++++++++++
arch/x86/virt/vmx/tdx/tdx.c | 169 +++++----
arch/x86/virt/vmx/tdx/tdx.h | 8 +-
arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 17 +-
drivers/virt/coco/Kconfig | 2 +
drivers/virt/coco/Makefile | 1 +
drivers/virt/coco/tdx-host/Kconfig | 6 +
drivers/virt/coco/tdx-host/Makefile | 1 +
drivers/virt/coco/tdx-host/tdx-host.c | 231 +++++++++++++
18 files changed, 965 insertions(+), 113 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-devices-faux-tdx-host
create mode 100644 arch/x86/include/asm/seamldr.h
create mode 100644 arch/x86/virt/vmx/tdx/seamcall_internal.h
create mode 100644 arch/x86/virt/vmx/tdx/seamldr.c
create mode 100644 drivers/virt/coco/tdx-host/Kconfig
create mode 100644 drivers/virt/coco/tdx-host/Makefile
create mode 100644 drivers/virt/coco/tdx-host/tdx-host.c
base-commit: 5209e5bfe5cab593476c3e7754e42c5e47ce36de
--
2.52.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v10 25/25] x86/virt/tdx: Document TDX module update
2026-05-20 13:38 [PATCH v10 00/25] Runtime TDX module update support Chao Gao
@ 2026-05-20 13:38 ` Chao Gao
2026-05-20 13:46 ` [PATCH v10 00/25] Runtime TDX module update support Chao Gao
1 sibling, 0 replies; 3+ messages in thread
From: Chao Gao @ 2026-05-20 13:38 UTC (permalink / raw)
To: kvm, linux-coco, linux-kernel, linux-doc
Cc: binbin.wu, dave.hansen, djbw, ira.weiny, kai.huang, kas,
nik.borisov, paulmck, pbonzini, reinette.chatre, rick.p.edgecombe,
sagis, seanjc, tony.lindgren, vannapurve, vishal.l.verma,
yilun.xu, xiaoyao.li, yan.y.zhao, Chao Gao, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin,
Jonathan Corbet, Shuah Khan
Document TDX module update as a subsection of "TDX Host Kernel Support" to
provide background information and cover key points that developers and
users may need to know, for example:
- update is done in stop_machine() context
- update instructions and results
- update policy and tooling
Signed-off-by: Chao Gao <chao.gao@intel.com>
Reviewed-by: Kai Huang <kai.huang@intel.com>
Reviewed-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
Reviewed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
Documentation/arch/x86/tdx.rst | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/Documentation/arch/x86/tdx.rst b/Documentation/arch/x86/tdx.rst
index 1a3b5bac1021..9d2b7db166b5 100644
--- a/Documentation/arch/x86/tdx.rst
+++ b/Documentation/arch/x86/tdx.rst
@@ -73,6 +73,40 @@ initialize::
[..] virt/tdx: TDX-Module initialization failed ...
+TDX module Runtime Update
+-------------------------
+
+The TDX architecture includes a persistent SEAM loader (P-SEAMLDR) that
+runs in SEAM mode separately from the TDX module. The kernel can
+communicate with P-SEAMLDR to perform runtime updates of the TDX module.
+
+During updates, the TDX module becomes unresponsive to other TDX
+operations. To prevent components using TDX (such as KVM) from
+experiencing unexpected errors during updates, updates are performed in
+stop_machine() context.
+
+TDX module updates have complex compatibility requirements; the new module
+must be compatible with the current CPU, P-SEAMLDR, and running TDX module.
+Rather than implementing complex module selection and policy enforcement
+logic in the kernel, userspace is responsible for auditing and selecting
+appropriate updates.
+
+Updates use the standard firmware upload interface. See
+Documentation/driver-api/firmware/fw_upload.rst for detailed instructions.
+
+If updates failed, running TDs may be killed and further TDX operations may
+not be possible until reboot. For detailed error information, see
+Documentation/ABI/testing/sysfs-devices-faux-tdx-host.
+
+Given the risk of losing existing TDs, userspace should verify that the
+update is compatible with the current system and properly validated before
+applying it.
+
+A reference userspace tool that implements necessary checks is available
+at:
+
+ https://github.com/intel/tdx-module-binaries
+
TDX Interaction to Other Kernel Components
------------------------------------------
--
2.52.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v10 00/25] Runtime TDX module update support
2026-05-20 13:38 [PATCH v10 00/25] Runtime TDX module update support Chao Gao
2026-05-20 13:38 ` [PATCH v10 25/25] x86/virt/tdx: Document TDX module update Chao Gao
@ 2026-05-20 13:46 ` Chao Gao
1 sibling, 0 replies; 3+ messages in thread
From: Chao Gao @ 2026-05-20 13:46 UTC (permalink / raw)
To: kvm, linux-coco, x86, linux-kernel, linux-rt-devel, linux-doc
Cc: binbin.wu, dave.hansen, djbw, ira.weiny, kai.huang, kas,
nik.borisov, paulmck, pbonzini, reinette.chatre, rick.p.edgecombe,
sagis, seanjc, tony.lindgren, vannapurve, vishal.l.verma,
yilun.xu, xiaoyao.li, yan.y.zhao, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, H. Peter Anvin, Sebastian Andrzej Siewior,
Clark Williams, Steven Rostedt, Jonathan Corbet, Shuah Khan
On Wed, May 20, 2026 at 06:38:03AM -0700, Chao Gao wrote:
>Hi Dave & Rick,
>
>Thanks for your thorough review of v9. This v10 addresses the issues you
>pointed out. The main changes in this version are polishing changelogs
>and variable renames to improve readability. Specifically:
>
> - Patches 1-2 (new): Split the original "Consolidate TDX global
> initialization states" into two steps — first move the statics to
> file scope, then clarify the result-caching logic in
> try_init_module_global().
> - Patch 6: Removed user-facing Kconfig help text for TDX_HOST_SERVICES
> (now a silent tristate auto-selected by INTEL_TDX_HOST).
> - Patch 13: Renamed "size" to "data_len" in seamldr_install_module()
> and init_seamldr_params(); renamed "HEADER_SIZE" to
> "TDX_IMAGE_HEADER_SIZE"; renamed "primary" to "is_lead_cpu" in the
> update state machine.
> - Patch 13: Added early data_len validation and explicit bounds checks
> on sigstruct_nr_pages/module_nr_pages against SEAMLDR_MAX_NR_*
> limits, removing the implicit clamping in populate_pa_list().
> - Patch 22: Fixed BIT(16) -> BIT_ULL(16) for
> TDX_SYS_SHUTDOWN_AVOID_COMPAT_SENSITIVE.
> - Patch 22: Removed unused TDX_FEATURES0_UPDATE_COMPAT definition.
> - Various patches: Shortened sysfs ABI descriptions, tightened
> comments across seamldr.h and seamldr.c, and minor style fixes
> (return 0 -> return false, unfolded conditionals)
FYI, below is the diff between v9 and v10:
diff --git a/Documentation/ABI/testing/sysfs-devices-faux-tdx-host b/Documentation/ABI/testing/sysfs-devices-faux-tdx-host
index 9e08db231da1..5f18ac972468 100644
--- a/Documentation/ABI/testing/sysfs-devices-faux-tdx-host
+++ b/Documentation/ABI/testing/sysfs-devices-faux-tdx-host
@@ -1,16 +1,14 @@
What: /sys/devices/faux/tdx_host/version
Contact: linux-coco@lists.linux.dev
-Description: (RO) Report the version of the loaded TDX module. The TDX module
- version is formatted as x.y.z, where "x" is the major version,
- "y" is the minor version and "z" is the update version. Versions
- are used for bug reporting, TDX module updates etc.
+Description: (RO) Report the version of the loaded TDX module.
+ Formatted as "major.minor.update". Used by TDX module
+ update tooling. Example: "1.2.03".
What: /sys/devices/faux/tdx_host/seamldr_version
Contact: linux-coco@lists.linux.dev
-Description: (RO) Report the version of the loaded P-SEAMLDR. The P-SEAMLDR
- version is formatted as x.y.z, where "x" is the major version,
- "y" is the minor version and "z" is the update version. Versions
- are used for bug reporting and compatibility checks.
+Description: (RO) Report the version of the loaded P-SEAMLDR.
+ Formatted as a TDX module version. Used by TDX module
+ update tooling.
What: /sys/devices/faux/tdx_host/num_remaining_updates
Contact: linux-coco@lists.linux.dev
diff --git a/arch/x86/include/asm/seamldr.h b/arch/x86/include/asm/seamldr.h
index ac6f80f7208b..43084e2daa2d 100644
--- a/arch/x86/include/asm/seamldr.h
+++ b/arch/x86/include/asm/seamldr.h
@@ -5,11 +5,10 @@
#include <linux/types.h>
/*
- * This is called the "SEAMLDR_INFO" data structure and is defined
- * in "SEAM Loader (SEAMLDR) Interface Specification".
+ * This is the "SEAMLDR_INFO" data structure defined in the
+ * "SEAM Loader (SEAMLDR) Interface Specification".
*
- * The SEAMLDR.INFO documentation requires this to be aligned to a
- * 256-byte boundary.
+ * Must be aligned to a 256-byte boundary.
*/
struct seamldr_info {
u32 version;
@@ -32,6 +31,6 @@ struct seamldr_info {
static_assert(sizeof(struct seamldr_info) == 256);
int seamldr_get_info(struct seamldr_info *seamldr_info);
-int seamldr_install_module(const u8 *data, u32 size);
+int seamldr_install_module(const u8 *data, u32 data_len);
#endif /* _ASM_X86_SEAMLDR_H */
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index ac042b369843..c848483d815f 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -36,7 +36,6 @@
/* Bit definitions of TDX_FEATURES0 metadata field */
#define TDX_FEATURES0_TD_PRESERVING BIT_ULL(1)
#define TDX_FEATURES0_NO_RBP_MOD BIT_ULL(18)
-#define TDX_FEATURES0_UPDATE_COMPAT BIT_ULL(47)
#ifndef __ASSEMBLER__
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index 6a39c9e3ef7d..ff95d8dd1162 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -32,10 +32,12 @@
#define SEAMLDR_SCENARIO_UPDATE 1
/*
- * This is called the "SEAMLDR_PARAMS" data structure and is defined
- * in "SEAM Loader (SEAMLDR) Interface Specification".
+ * This is the "SEAMLDR_PARAMS" data structure defined in the
+ * "SEAM Loader (SEAMLDR) Interface Specification".
*
- * It describes the TDX module that will be installed.
+ * It is the in-memory ABI that the kernel passes to the P-SEAMLDR
+ * to update the TDX module. It breaks the TDX module image up in
+ * page-size pieces.
*/
struct seamldr_params {
u32 version;
@@ -87,7 +89,7 @@ static int seamldr_install(const struct seamldr_params *params)
#define TDX_IMAGE_VERSION_2 0x200
struct tdx_image_header {
- u16 version; // This ABI is always 0x200
+ u16 version;
u16 checksum;
u8 signature[8];
u32 sigstruct_nr_pages;
@@ -95,23 +97,28 @@ struct tdx_image_header {
u8 reserved[4076];
} __packed;
-#define HEADER_SIZE sizeof(struct tdx_image_header)
-static_assert(HEADER_SIZE == 4096);
+#define TDX_IMAGE_HEADER_SIZE sizeof(struct tdx_image_header)
+static_assert(TDX_IMAGE_HEADER_SIZE == 4096);
-/* Intel TDX module update ABI structure. aka. "TDX module blob". */
+/*
+ * Intel TDX module update ABI structure. aka. "TDX module blob".
+ *
+ * @payload contains sigstruct pages followed by module pages.
+ */
struct tdx_image {
struct tdx_image_header header;
- u8 payload[]; // Contains sigstruct pages followed by module pages
+ u8 payload[];
};
-static void populate_pa_list(u64 *pa_list, u32 max_entries, const u8 *start, u32 nr_pages)
+static void populate_pa_list(u64 *pa_list, const u8 *vmalloc_addr, u32 vmalloc_len_pages)
{
int i;
- nr_pages = MIN(nr_pages, max_entries);
- for (i = 0; i < nr_pages; i++) {
- pa_list[i] = vmalloc_to_pfn(start) << PAGE_SHIFT;
- start += PAGE_SIZE;
+ for (i = 0; i < vmalloc_len_pages; i++) {
+ unsigned long offset = i * PAGE_SIZE;
+ unsigned long pfn = vmalloc_to_pfn(&vmalloc_addr[offset]);
+
+ pa_list[i] = pfn << PAGE_SHIFT;
}
}
@@ -123,39 +130,43 @@ static void populate_seamldr_params(struct seamldr_params *params,
params->scenario = SEAMLDR_SCENARIO_UPDATE;
params->module_nr_pages = mod_nr_pages;
- populate_pa_list(params->sigstruct_pages_pa_list, SEAMLDR_MAX_NR_SIG_PAGES,
- sig, sig_nr_pages);
- populate_pa_list(params->module_pages_pa_list, SEAMLDR_MAX_NR_MODULE_PAGES,
- mod, mod_nr_pages);
+ populate_pa_list(params->sigstruct_pages_pa_list, sig, sig_nr_pages);
+ populate_pa_list(params->module_pages_pa_list, mod, mod_nr_pages);
}
-static int init_seamldr_params(struct seamldr_params *params, const u8 *data, u32 size)
+/*
+ * @image points to a vmalloc()'d 'struct tdx_image'. Transform
+ * it into @params which is the P-SEAMLDR ABI format.
+ */
+static int init_seamldr_params(struct seamldr_params *params,
+ const struct tdx_image *image,
+ u32 image_len)
{
- const struct tdx_image *image = (const void *)data;
const struct tdx_image_header *header = &image->header;
u32 sigstruct_len = header->sigstruct_nr_pages * PAGE_SIZE;
u32 module_len = header->module_nr_pages * PAGE_SIZE;
u8 *header_start = (u8 *)header;
- u8 *header_end = header_start + HEADER_SIZE;
+ u8 *header_end = header_start + TDX_IMAGE_HEADER_SIZE;
u8 *sigstruct_start = header_end;
u8 *sigstruct_end = sigstruct_start + sigstruct_len;
u8 *module_start = sigstruct_end;
- /* Check the calculated payload size against the data size. */
- if (HEADER_SIZE + sigstruct_len + module_len != size)
+ /* Check the calculated payload size against the image size. */
+ if (TDX_IMAGE_HEADER_SIZE + sigstruct_len + module_len != image_len)
return -EINVAL;
- /*
- * Don't care about user passing the wrong file, but protect
- * kernel ABI by preventing accepting garbage.
- */
+ /* Reject unsupported tdx_image ABI versions. */
if (header->version != TDX_IMAGE_VERSION_2)
return -EINVAL;
+ if (header->sigstruct_nr_pages > SEAMLDR_MAX_NR_SIG_PAGES ||
+ header->module_nr_pages > SEAMLDR_MAX_NR_MODULE_PAGES)
+ return -EINVAL;
+
if (memcmp(header->signature, "TDX-BLOB", sizeof(header->signature)))
return -EINVAL;
@@ -163,7 +174,7 @@ static int init_seamldr_params(struct seamldr_params *params, const u8 *data, u3
return -EINVAL;
populate_seamldr_params(params, sigstruct_start, header->sigstruct_nr_pages,
- module_start, header->module_nr_pages);
+ module_start, header->module_nr_pages);
return 0;
}
@@ -230,14 +241,14 @@ static int do_seamldr_install_module(void *seamldr_params)
{
enum module_update_state newstate, curstate = MODULE_UPDATE_START;
int cpu = smp_processor_id();
- bool primary;
+ bool is_lead_cpu;
int ret = 0;
/*
- * Use CPU 0 to execute update steps that must run exactly once.
- * Note CPU 0 is always online.
+ * Some steps must be run on exactly one CPU. Pick a "lead" CPU to
+ * execute those steps. Use CPU 0 because it is always online.
*/
- primary = cpu == 0;
+ is_lead_cpu = cpu == 0;
do {
newstate = READ_ONCE(update_ctrl.state);
@@ -250,7 +261,7 @@ static int do_seamldr_install_module(void *seamldr_params)
curstate = newstate;
switch (curstate) {
case MODULE_UPDATE_SHUTDOWN:
- if (primary)
+ if (is_lead_cpu)
ret = tdx_module_shutdown();
break;
case MODULE_UPDATE_CPU_INSTALL:
@@ -260,7 +271,7 @@ static int do_seamldr_install_module(void *seamldr_params)
ret = tdx_cpu_enable();
break;
case MODULE_UPDATE_RUN_UPDATE:
- if (primary)
+ if (is_lead_cpu)
ret = tdx_module_run_update();
break;
default:
@@ -276,20 +287,27 @@ static int do_seamldr_install_module(void *seamldr_params)
/**
* seamldr_install_module - Install a new TDX module.
* @data: Pointer to the TDX module image.
- * @size: Size of the TDX module image.
+ * @data_len: Size of the TDX module image.
*
* Returns 0 on success, negative error code on failure.
*/
-int seamldr_install_module(const u8 *data, u32 size)
+int seamldr_install_module(const u8 *data, u32 data_len)
{
struct seamldr_params *params;
+ const struct tdx_image *image;
int ret;
+ if (data_len < TDX_IMAGE_HEADER_SIZE)
+ return -EINVAL;
+
+ image = (const struct tdx_image *)data;
+
params = kzalloc_obj(*params);
if (!params)
return -ENOMEM;
- ret = init_seamldr_params(params, data, size);
+ /* Populate 'params' from 'image'. */
+ ret = init_seamldr_params(params, image, data_len);
if (ret)
goto out;
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 2ab6f6efe6d1..0c5660c9ab45 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -69,6 +69,8 @@ static LIST_HEAD(tdx_memlist);
static struct tdx_sys_info tdx_sysinfo;
+static DEFINE_RAW_SPINLOCK(sysinit_lock);
+
/*
* Do the module global initialization once and return its result.
* It can be done on any cpu, and from task or IRQ context.
@@ -76,29 +78,34 @@ static struct tdx_sys_info tdx_sysinfo;
static int try_init_module_global(void)
{
struct tdx_module_args args = {};
- static DEFINE_RAW_SPINLOCK(sysinit_lock);
+ int ret;
raw_spin_lock(&sysinit_lock);
- if (tdx_module_state.sysinit_done)
+ /* Return the "cached" return code. */
+ if (tdx_module_state.sysinit_done) {
+ ret = tdx_module_state.sysinit_ret;
goto out;
+ }
/* RCX is module attributes and all bits are reserved */
args.rcx = 0;
- tdx_module_state.sysinit_ret = seamcall_prerr(TDH_SYS_INIT, &args);
+ ret = seamcall_prerr(TDH_SYS_INIT, &args);
/*
* The first SEAMCALL also detects the TDX module, thus
* it can fail due to the TDX module is not loaded.
* Dump message to let the user know.
*/
- if (tdx_module_state.sysinit_ret == -ENODEV)
+ if (ret == -ENODEV)
pr_err("module not loaded\n");
+ /* Save the return code for later callers. */
tdx_module_state.sysinit_done = true;
+ tdx_module_state.sysinit_ret = ret;
out:
raw_spin_unlock(&sysinit_lock);
- return tdx_module_state.sysinit_ret;
+ return ret;
}
/**
@@ -1267,7 +1274,7 @@ static __init int tdx_enable(void)
}
subsys_initcall(tdx_enable);
-#define TDX_SYS_SHUTDOWN_AVOID_COMPAT_SENSITIVE BIT(16)
+#define TDX_SYS_SHUTDOWN_AVOID_COMPAT_SENSITIVE BIT_ULL(16)
int tdx_module_shutdown(void)
{
diff --git a/drivers/virt/coco/tdx-host/Kconfig b/drivers/virt/coco/tdx-host/Kconfig
index ca600a39d97b..57d0c01a4357 100644
--- a/drivers/virt/coco/tdx-host/Kconfig
+++ b/drivers/virt/coco/tdx-host/Kconfig
@@ -1,12 +1,6 @@
config TDX_HOST_SERVICES
- tristate "TDX Host Services Driver"
+ tristate
depends on INTEL_TDX_HOST
select FW_LOADER
select FW_UPLOAD
default m
- help
- Enable access to TDX host services like module update and
- extensions (e.g. TDX Connect).
-
- Say y or m if enabling support for confidential virtual machine
- support (CONFIG_INTEL_TDX_HOST). The module is called tdx_host.ko.
diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index ad116e56aa1a..291464490fe0 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -76,6 +76,10 @@ static ssize_t num_remaining_updates_show(struct device *dev,
return sysfs_emit(buf, "%u\n", info.num_remaining_updates);
}
+/*
+ * These attributes are intended for managing TDX module updates. Reading
+ * them issues a slow, serialized P-SEAMLDR query, so keep them admin-only.
+ */
static DEVICE_ATTR_ADMIN_RO(seamldr_version);
static DEVICE_ATTR_ADMIN_RO(num_remaining_updates);
@@ -90,7 +94,10 @@ static bool supports_runtime_update(void)
const struct tdx_sys_info *sysinfo = tdx_get_sysinfo();
if (!sysinfo)
- return 0;
+ return false;
+
+ if (!tdx_supports_runtime_update(sysinfo))
+ return false;
/*
* Calling P-SEAMLDR on CPUs with the seamret_invd_vmcs bug clears
@@ -98,14 +105,17 @@ static bool supports_runtime_update(void)
* present before exposing P-SEAMLDR features.
*/
if (boot_cpu_has_bug(X86_BUG_SEAMRET_INVD_VMCS))
- return 0;
+ return false;
- return tdx_supports_runtime_update(sysinfo);
+ return true;
}
static umode_t seamldr_group_visible(struct kobject *kobj, struct attribute *attr, int idx)
{
- return supports_runtime_update() ? attr->mode : 0;
+ if (!supports_runtime_update())
+ return 0;
+
+ return attr->mode;
}
static const struct attribute_group seamldr_group = {
@@ -120,20 +130,20 @@ static const struct attribute_group *tdx_host_groups[] = {
};
static enum fw_upload_err tdx_fw_prepare(struct fw_upload *fwl,
- const u8 *data, u32 size)
+ const u8 *data, u32 data_len)
{
return FW_UPLOAD_ERR_NONE;
}
static enum fw_upload_err tdx_fw_write(struct fw_upload *fwl, const u8 *data,
- u32 offset, u32 size, u32 *written)
+ u32 offset, u32 data_len, u32 *written)
{
int ret;
- ret = seamldr_install_module(data, size);
+ ret = seamldr_install_module(data, data_len);
switch (ret) {
case 0:
- *written = size;
+ *written = data_len;
return FW_UPLOAD_ERR_NONE;
case -EBUSY:
return FW_UPLOAD_ERR_BUSY;
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-20 13:46 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-20 13:38 [PATCH v10 00/25] Runtime TDX module update support Chao Gao
2026-05-20 13:38 ` [PATCH v10 25/25] x86/virt/tdx: Document TDX module update Chao Gao
2026-05-20 13:46 ` [PATCH v10 00/25] Runtime TDX module update support Chao Gao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox