From: Chao Gao <chao.gao@intel.com>
To: linux-coco@lists.linux.dev, linux-kernel@vger.kernel.org,
x86@kernel.org, reinette.chatre@intel.com, ira.weiny@intel.com,
kai.huang@intel.com, dan.j.williams@intel.com,
yilun.xu@linux.intel.com, sagis@google.com,
vannapurve@google.com, paulmck@kernel.org, nik.borisov@suse.com
Cc: Chao Gao <chao.gao@intel.com>,
Farrah Chen <farrah.chen@intel.com>,
Thomas Gleixner <tglx@linutronix.de>,
Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
"H. Peter Anvin" <hpa@zytor.com>,
"Kirill A. Shutemov" <kas@kernel.org>
Subject: [PATCH v2 08/21] coco/tdx-host: Implement FW_UPLOAD sysfs ABI for TDX Module updates
Date: Tue, 30 Sep 2025 19:52:52 -0700 [thread overview]
Message-ID: <20251001025442.427697-9-chao.gao@intel.com> (raw)
In-Reply-To: <20251001025442.427697-1-chao.gao@intel.com>
The firmware upload framework provides a standard mechanism for firmware
updates by allowing device drivers to expose sysfs interfaces for
user-initiated updates.
Register with this framework to expose sysfs interfaces for TDX Module
updates and implement operations to process data blobs supplied by
userspace.
Note that:
1. P-SEAMLDR processes the entire update at once rather than
chunk-by-chunk, so .write() is called only once per update; so the
offset should be always 0.
2. TDX Module Updates complete synchronously within .write(), meaning
.poll_complete() is only called after successful updates and therefore
always returns success
Why fw_upload instead of request_firmware()?
============================================
The explicit file selection capabilities of fw_upload is preferred over
the implicit file selection of request_firmware() for the following
reasons:
a. Intel distributes all versions of the TDX Module, allowing admins to
load any version rather than always defaulting to the latest. This
flexibility is necessary because future extensions may require reverting to
a previous version to clear fatal errors.
b. Some module version series are platform-specific. For example, the 1.5.x
series is for certain platform generations, while the 2.0.x series is
intended for others.
c. The update policy for TDX Module updates is non-linear at times. The
latest TDX Module may not be compatible. For example, TDX Module 1.5.x
may be updated to 1.5.y but not to 1.5.y+1. This policy is documented
separately in a file released along with each TDX Module release.
So, the default policy of "request_firmware()" of "always load latest", is
not suitable for TDX. Userspace needs to deploy a more sophisticated policy
check (e.g., latest may not be compatible), and there is potential
operator choice to consider.
Just have userspace pick rather than add kernel mechanism to change the
default policy of request_firmware().
Signed-off-by: Chao Gao <chao.gao@intel.com>
Tested-by: Farrah Chen <farrah.chen@intel.com>
---
arch/x86/Kconfig | 2 +
arch/x86/include/asm/seamldr.h | 2 +
arch/x86/include/asm/tdx.h | 5 ++
arch/x86/virt/vmx/tdx/seamldr.c | 7 ++
drivers/virt/coco/tdx-host/tdx-host.c | 122 +++++++++++++++++++++++++-
5 files changed, 137 insertions(+), 1 deletion(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 6b47383d2958..2bf4bb3dfe71 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1908,6 +1908,8 @@ config INTEL_TDX_HOST
config INTEL_TDX_MODULE_UPDATE
bool "Intel TDX module runtime update"
depends on TDX_HOST_SERVICES
+ select FW_LOADER
+ select FW_UPLOAD
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
diff --git a/arch/x86/include/asm/seamldr.h b/arch/x86/include/asm/seamldr.h
index d1e9f6e16e8d..692bde5e9bb4 100644
--- a/arch/x86/include/asm/seamldr.h
+++ b/arch/x86/include/asm/seamldr.h
@@ -20,8 +20,10 @@ struct seamldr_info {
#ifdef CONFIG_INTEL_TDX_MODULE_UPDATE
const struct seamldr_info *seamldr_get_info(void);
+int seamldr_install_module(const u8 *data, u32 size);
#else
static inline const struct seamldr_info *seamldr_get_info(void) { return NULL; }
+static inline int seamldr_install_module(const u8 *data, u32 size) { return -EOPNOTSUPP; }
#endif
#endif
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 7ad026618a23..2422904079a3 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -107,6 +107,11 @@ int tdx_enable(void);
const char *tdx_dump_mce_info(struct mce *m);
const struct tdx_sys_info *tdx_get_sysinfo(void);
+static inline bool tdx_supports_runtime_update(const struct tdx_sys_info *sysinfo)
+{
+ return false; /* To be enabled when kernel is ready */
+}
+
int tdx_guest_keyid_alloc(void);
u32 tdx_get_nr_guest_keyids(void);
void tdx_guest_keyid_free(unsigned int keyid);
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index 08c2e3fe6071..69c059194c61 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -69,3 +69,10 @@ const struct seamldr_info *seamldr_get_info(void)
return seamldr_call(P_SEAMLDR_INFO, &args) ? NULL : &seamldr_info;
}
EXPORT_SYMBOL_GPL_FOR_MODULES(seamldr_get_info, "tdx-host");
+
+int seamldr_install_module(const u8 *data, u32 size)
+{
+ /* TODO: Update TDX Module here */
+ return 0;
+}
+EXPORT_SYMBOL_GPL_FOR_MODULES(seamldr_install_module, "tdx-host");
diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index 42570c5b221b..418e90797689 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -10,6 +10,7 @@
#include <linux/mod_devicetable.h>
#include <linux/sysfs.h>
#include <linux/device/faux.h>
+#include <linux/firmware.h>
#include <asm/cpu_device_id.h>
#include <asm/seamldr.h>
#include <asm/tdx.h>
@@ -21,6 +22,13 @@ static const struct x86_cpu_id tdx_host_ids[] = {
};
MODULE_DEVICE_TABLE(x86cpu, tdx_host_ids);
+struct tdx_fw_upload_status {
+ bool cancel_request;
+};
+
+struct fw_upload *tdx_fwl;
+static struct tdx_fw_upload_status tdx_fw_upload_status;
+
static struct faux_device *fdev;
static ssize_t version_show(struct device *dev, struct device_attribute *attr,
@@ -106,6 +114,118 @@ static const struct attribute_group *tdx_host_groups[] = {
NULL,
};
+static enum fw_upload_err tdx_fw_prepare(struct fw_upload *fwl,
+ const u8 *data, u32 size)
+{
+ struct tdx_fw_upload_status *status = fwl->dd_handle;
+
+ if (status->cancel_request) {
+ status->cancel_request = false;
+ return FW_UPLOAD_ERR_CANCELED;
+ }
+
+ 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)
+{
+ struct tdx_fw_upload_status *status = fwl->dd_handle;
+
+ if (status->cancel_request) {
+ status->cancel_request = false;
+ return FW_UPLOAD_ERR_CANCELED;
+ }
+
+ /*
+ * tdx_fw_write() always processes all data on the first call with
+ * offset == 0. Since it never returns partial success (it either
+ * succeeds completely or fails), there is no subsequent call with
+ * non-zero offsets.
+ */
+ WARN_ON_ONCE(offset);
+ if (seamldr_install_module(data, size))
+ return FW_UPLOAD_ERR_FW_INVALID;
+
+ *written = size;
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err tdx_fw_poll_complete(struct fw_upload *fwl)
+{
+ /*
+ * TDX Module updates are completed in the previous phase
+ * (tdx_fw_write()). If any error occurred, the previous phase
+ * would return an error code to abort the update process. In
+ * other words, reaching this point means the update succeeded.
+ */
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void tdx_fw_cancel(struct fw_upload *fwl)
+{
+ struct tdx_fw_upload_status *status = fwl->dd_handle;
+
+ status->cancel_request = true;
+}
+
+static const struct fw_upload_ops tdx_fw_ops = {
+ .prepare = tdx_fw_prepare,
+ .write = tdx_fw_write,
+ .poll_complete = tdx_fw_poll_complete,
+ .cancel = tdx_fw_cancel,
+};
+
+static int seamldr_init(struct device *dev)
+{
+ const struct seamldr_info *seamldr_info = seamldr_get_info();
+ const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo();
+ int ret;
+
+ if (!tdx_sysinfo || !seamldr_info)
+ return -ENXIO;
+
+ if (!tdx_supports_runtime_update(tdx_sysinfo)) {
+ pr_info("Current TDX Module cannot be updated. Consider BIOS updates\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!seamldr_info->num_remaining_updates) {
+ pr_info("P-SEAMLDR doesn't support TDX Module updates\n");
+ return -EOPNOTSUPP;
+ }
+
+ tdx_fwl = firmware_upload_register(THIS_MODULE, dev, "seamldr_upload",
+ &tdx_fw_ops, &tdx_fw_upload_status);
+ ret = PTR_ERR_OR_ZERO(tdx_fwl);
+ if (ret)
+ pr_err("failed to register module uploader %d\n", ret);
+
+ return ret;
+}
+
+static void seamldr_deinit(void)
+{
+ if (tdx_fwl)
+ firmware_upload_unregister(tdx_fwl);
+}
+
+static int tdx_host_probe(struct faux_device *fdev)
+{
+ /* Only support TDX Module updates now. More TDX features could be added here. */
+ return seamldr_init(&fdev->dev);
+}
+
+static void tdx_host_remove(struct faux_device *fdev)
+{
+ seamldr_deinit();
+}
+
+static struct faux_device_ops tdx_host_ops = {
+ .probe = tdx_host_probe,
+ .remove = tdx_host_remove,
+};
+
static int __init tdx_host_init(void)
{
int r;
@@ -118,7 +238,7 @@ static int __init tdx_host_init(void)
if (r)
return r;
- fdev = faux_device_create_with_groups(KBUILD_MODNAME, NULL, NULL, tdx_host_groups);
+ fdev = faux_device_create_with_groups(KBUILD_MODNAME, NULL, &tdx_host_ops, tdx_host_groups);
if (!fdev)
return -ENODEV;
--
2.47.3
next prev parent reply other threads:[~2025-10-01 2:55 UTC|newest]
Thread overview: 75+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-01 2:52 [PATCH v2 00/21] Runtime TDX Module update support Chao Gao
2025-10-01 2:52 ` [PATCH v2 01/21] x86/virt/tdx: Print SEAMCALL leaf numbers in decimal Chao Gao
2025-10-01 2:52 ` [PATCH v2 02/21] x86/virt/tdx: Use %# prefix for hex values in SEAMCALL error messages Chao Gao
2025-10-01 2:52 ` [PATCH v2 03/21] x86/virt/tdx: Move low level SEAMCALL helpers out of <asm/tdx.h> Chao Gao
2025-10-01 2:52 ` [PATCH v2 04/21] x86/virt/tdx: Prepare to support P-SEAMLDR SEAMCALLs Chao Gao
2025-10-01 2:52 ` [PATCH v2 05/21] x86/virt/seamldr: Introduce a wrapper for " Chao Gao
2025-10-01 2:52 ` [PATCH v2 06/21] x86/virt/seamldr: Retrieve P-SEAMLDR information Chao Gao
2025-10-01 2:52 ` [PATCH v2 07/21] coco/tdx-host: Expose P-SEAMLDR information via sysfs Chao Gao
2025-10-30 21:54 ` Sagi Shahar
2025-10-30 23:05 ` dan.j.williams
2025-10-31 14:31 ` Sagi Shahar
2025-10-01 2:52 ` Chao Gao [this message]
2025-10-01 2:52 ` [PATCH v2 09/21] x86/virt/seamldr: Block TDX Module updates if any CPU is offline Chao Gao
2025-10-01 2:52 ` [PATCH v2 10/21] x86/virt/seamldr: Verify availability of slots for TDX Module updates Chao Gao
2025-10-01 2:52 ` [PATCH v2 11/21] x86/virt/seamldr: Allocate and populate a module update request Chao Gao
2025-10-01 2:52 ` [PATCH v2 12/21] x86/virt/seamldr: Introduce skeleton for TDX Module updates Chao Gao
2025-10-01 2:52 ` [PATCH v2 13/21] x86/virt/seamldr: Abort updates if errors occurred midway Chao Gao
2025-10-01 2:52 ` [PATCH v2 14/21] x86/virt/seamldr: Shut down the current TDX module Chao Gao
2025-10-01 2:52 ` [PATCH v2 15/21] x86/virt/tdx: Reset software states after TDX module shutdown Chao Gao
2025-10-01 2:53 ` [PATCH v2 16/21] x86/virt/seamldr: Handle TDX Module update failures Chao Gao
2025-10-28 2:53 ` Chao Gao
2025-10-01 2:53 ` [PATCH v2 17/21] x86/virt/seamldr: Install a new TDX Module Chao Gao
2025-10-01 2:53 ` [PATCH v2 18/21] x86/virt/seamldr: Do TDX per-CPU initialization after updates Chao Gao
2025-10-01 2:53 ` [PATCH v2 19/21] x86/virt/tdx: Establish contexts for the new TDX Module Chao Gao
2025-10-01 2:53 ` [PATCH v2 20/21] x86/virt/tdx: Update tdx_sysinfo and check features post-update Chao Gao
2025-10-01 2:53 ` [PATCH v2 21/21] x86/virt/tdx: Enable TDX Module runtime updates Chao Gao
2025-10-14 15:32 ` [PATCH v2 00/21] Runtime TDX Module update support Vishal Annapurve
2025-10-15 8:54 ` Reshetova, Elena
2025-10-15 14:19 ` Vishal Annapurve
2025-10-16 6:48 ` Reshetova, Elena
2025-10-15 15:02 ` Dave Hansen
2025-10-16 6:46 ` Reshetova, Elena
2025-10-16 17:47 ` Vishal Annapurve
2025-10-17 10:08 ` Reshetova, Elena
2025-10-18 0:01 ` Vishal Annapurve
2025-10-21 13:42 ` Reshetova, Elena
2025-10-22 7:14 ` Chao Gao
2025-10-22 15:42 ` Vishal Annapurve
2025-10-23 20:31 ` Vishal Annapurve
2025-10-23 21:10 ` Dave Hansen
2025-10-23 22:00 ` Vishal Annapurve
2025-10-24 7:43 ` Chao Gao
2025-10-24 18:02 ` Dave Hansen
2025-10-24 19:40 ` dan.j.williams
2025-10-24 20:00 ` Sean Christopherson
2025-10-24 20:14 ` Dave Hansen
2025-10-24 21:09 ` Vishal Annapurve
2025-10-24 20:13 ` Dave Hansen
2025-10-24 21:12 ` dan.j.williams
2025-10-24 21:19 ` Dave Hansen
2025-10-25 0:54 ` Vishal Annapurve
2025-10-25 1:42 ` dan.j.williams
2025-10-25 11:55 ` Vishal Annapurve
2025-10-25 12:01 ` Vishal Annapurve
2025-10-26 21:30 ` dan.j.williams
2025-10-26 22:01 ` Vishal Annapurve
2025-10-27 18:53 ` dan.j.williams
2025-10-28 0:42 ` Vishal Annapurve
2025-10-28 2:13 ` dan.j.williams
2025-10-28 17:00 ` Erdem Aktas
2025-10-29 0:56 ` Sean Christopherson
2025-10-29 2:17 ` dan.j.williams
2025-10-29 13:48 ` Sean Christopherson
2025-10-30 17:01 ` Vishal Annapurve
2025-10-31 2:53 ` Chao Gao
2025-11-19 22:44 ` Sagi Shahar
2025-11-20 2:47 ` Chao Gao
2025-10-28 23:48 ` Vishal Annapurve
2025-10-28 20:29 ` dan.j.williams
2025-10-28 20:32 ` dan.j.williams
2025-10-31 16:55 ` Sagi Shahar
2025-10-31 17:57 ` Vishal Annapurve
2025-11-01 2:18 ` Chao Gao
2025-11-01 2:05 ` Chao Gao
2025-11-12 14:09 ` Chao Gao
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251001025442.427697-9-chao.gao@intel.com \
--to=chao.gao@intel.com \
--cc=bp@alien8.de \
--cc=dan.j.williams@intel.com \
--cc=dave.hansen@linux.intel.com \
--cc=farrah.chen@intel.com \
--cc=hpa@zytor.com \
--cc=ira.weiny@intel.com \
--cc=kai.huang@intel.com \
--cc=kas@kernel.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=nik.borisov@suse.com \
--cc=paulmck@kernel.org \
--cc=reinette.chatre@intel.com \
--cc=sagis@google.com \
--cc=tglx@linutronix.de \
--cc=vannapurve@google.com \
--cc=x86@kernel.org \
--cc=yilun.xu@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).