From nobody Sun May 24 19:34:21 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0CCA53655E4; Fri, 22 May 2026 17:26:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779470785; cv=none; b=cdBMpxLhurbHSAZoxZGNERgJf4RhCKo+PPZlaeMGvuFX4PP3+cex0TrQNrbItOYAuDJYRzr/pGdd5ZT4oYcwXBt0N31WoxAywndkkP6yyFzZmPvZZrDzm1d7rHFiVMfPGfYLSVdQs9zhZyDxyzGytWNKcHFbzudWz9dyJTtEgEI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779470785; c=relaxed/simple; bh=CDIX2bmfan9lRId5tiCQcyWGIJN4s0/PBksVyYaZtgc=; h=Date:From:To:Subject:Cc:In-Reply-To:References:MIME-Version: Message-ID:Content-Type; b=MLWlX2iFB6k/kc4awxfRLEPIJgyzvZOK+gJis5QtNVD7rU2azyOyNfXR9FQ6RyZhOxmKWr4IEAowpUJNqY0yQfjYmXdbn9dfCVOjxlPL2F+SZulM1CIq5mjhsspmW4M1DZEb9qoejMyltG7rhT9NuSAhYX+7g0Ibx5AHzLiQTws= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=tq1HWNcj; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=8j+FrV2G; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="tq1HWNcj"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="8j+FrV2G" Date: Fri, 22 May 2026 17:26:21 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1779470782; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mguKPU1c8MLkfQd7QehSiZL/cnrtjgq6CCGbXKqJ17s=; b=tq1HWNcjgI+qhAzPJNasxJ0S74YHsmlVKOrb/qUcGCz7JOJMePm9Z95nBNhjD/3MLuQE3L S5P8r5Ny37qmB1zFrxfgjWM92IE4CY9ULXYGuImLwNGHARXlemEkp4I/QiySMSdmOSITyF d27UaIzJMaTgKnIzRf0NXqsensICDf1kojAVyEjTj1viy1l3blVxwe49aAdwf9jWRmxYX4 4DF2thhBDc/cXLpxUw0x+UKxSh0rYfHwhb3S9I/hRSdL+9WpJKhdHyClsNvRV461fwF47v tpO3K4pVAK/6G19p9MNOIZEaipujPfJr2EmiWR1z0dnPHcEuESIEn0zNUTrU1Q== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1779470782; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mguKPU1c8MLkfQd7QehSiZL/cnrtjgq6CCGbXKqJ17s=; b=8j+FrV2G/+IC0ybspYh+tyK5i6ldPerF018UnotpEOCOl2L7laofL62T5Q+YTlL/4tzC6p gM8oLfSpUeta5xAQ== From: "tip-bot2 for Chao Gao" Sender: tip-bot2@linutronix.de Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: x86/tdx] coco/tdx-host: Implement firmware upload sysfs ABI for TDX module updates Cc: Chao Gao , Dave Hansen , Tony Lindgren , Kai Huang , "Kiryl Shutsemau (Meta)" , x86@kernel.org, linux-kernel@vger.kernel.org In-Reply-To: <20260520222900.BD91A8FF@davehans-spike.ostc.intel.com> References: <20260520222900.BD91A8FF@davehans-spike.ostc.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <177947078106.711.3018251301004125536.tip-bot2@tip-bot2> Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Precedence: bulk Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The following commit has been merged into the x86/tdx branch of tip: Commit-ID: 000c293c24bc40ecfe9083000a0d8369920e65a9 Gitweb: https://git.kernel.org/tip/000c293c24bc40ecfe9083000a0d83699= 20e65a9 Author: Chao Gao AuthorDate: Wed, 20 May 2026 15:29:00 -07:00 Committer: Dave Hansen CommitterDate: Wed, 20 May 2026 15:37:10 -07:00 coco/tdx-host: Implement firmware upload sysfs ABI for TDX module updates tl;dr: Select fw_upload for doing TDX module updates. The process of selecting among available update images is complicated and nuanced. Punt the selection process out to userspace. One existing userspace implementation today is the script in the Intel TDX Module Binaries repository[1]. Long Version: The kernel supports two primary firmware update mechanisms: 1. request_firmware() - used by microcode, SEV firmware, hundreds of other drivers 2. 'struct fw_upload' - used by CXL, FPGA updates, dozens of others The key difference between is that request_firmware() loads a named file from the filesystem where the filename is kernel-controlled, while fw_upload accepts firmware data directly from userspace. TDX module firmware update selection policy is too complex for the kernel. Leave it to userspace and use fw_upload. Add a skeleton fw_upload implementation to be fleshed out in subsequent patches. Refactor the sysfs visiblity attribute function so it can be used as a more generic flag for the presence of viable runtime update support. Why fw_upload instead of request_firmware()? =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Selecting a TDX module update image is not a simple "load the latest" decision. Userspace needs to choose an image that is compatible with both the platform and the currently running module. Some constraints are hard requirements: a. Module version series are platform-specific. For example, the 1.5.x series runs on Sapphire Rapids but not Granite Rapids, which needs 2.0.x. b. Updates are also constrained by version distance. A 1.5.6 module might permit updates to 1.5.7 but not to 1.5.50. There may also be userspace policy choices: c. Decide the update direction: upgrade or downgrade d. Choose whether to optimize for fewer updates or smaller version steps, for example, 1.2.3=3D>1.2.5 versus 1.2.3=3D>1.2.4=3D>1.2.5. Given that complexity, leave module selection to userspace and use fw_upload. 1. https://github.com/intel/confidential-computing.tdx.tdx-module.binaries/= blob/main/version_select_and_load.py [ dhansen: add version script link, add more explanation of code moves, fix some minor whitespace issues ] Signed-off-by: Chao Gao Signed-off-by: Dave Hansen Reviewed-by: Tony Lindgren Reviewed-by: Kai Huang Reviewed-by: Kiryl Shutsemau (Meta) Link: https://lore.kernel.org/kvm/01fc8946-eb84-46fa-9458-f345dd3f6033@inte= l.com/ Link: https://patch.msgid.link/20260520133909.409394-13-chao.gao@intel.com Link: https://patch.msgid.link/20260520222900.BD91A8FF@davehans-spike.ostc.= intel.com --- arch/x86/include/asm/seamldr.h | 1 +- arch/x86/virt/vmx/tdx/seamldr.c | 14 ++++- drivers/virt/coco/tdx-host/Kconfig | 2 +- drivers/virt/coco/tdx-host/tdx-host.c | 93 ++++++++++++++++++++++++-- 4 files changed, 106 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/seamldr.h b/arch/x86/include/asm/seamldr.h index a74151b..43084e2 100644 --- a/arch/x86/include/asm/seamldr.h +++ b/arch/x86/include/asm/seamldr.h @@ -31,5 +31,6 @@ struct seamldr_info { static_assert(sizeof(struct seamldr_info) =3D=3D 256); =20 int seamldr_get_info(struct seamldr_info *seamldr_info); +int seamldr_install_module(const u8 *data, u32 data_len); =20 #endif /* _ASM_X86_SEAMLDR_H */ diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamld= r.c index baa86f2..b2b6a43 100644 --- a/arch/x86/virt/vmx/tdx/seamldr.c +++ b/arch/x86/virt/vmx/tdx/seamldr.c @@ -54,3 +54,17 @@ int seamldr_get_info(struct seamldr_info *seamldr_info) return seamldr_call(P_SEAMLDR_INFO, &args); } EXPORT_SYMBOL_FOR_MODULES(seamldr_get_info, "tdx-host"); + +/** + * seamldr_install_module - Install a new TDX module. + * @data: Pointer to 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 data_len) +{ + /* TODO: Update TDX module here */ + return 0; +} +EXPORT_SYMBOL_FOR_MODULES(seamldr_install_module, "tdx-host"); diff --git a/drivers/virt/coco/tdx-host/Kconfig b/drivers/virt/coco/tdx-hos= t/Kconfig index cfe81b9..57d0c01 100644 --- a/drivers/virt/coco/tdx-host/Kconfig +++ b/drivers/virt/coco/tdx-host/Kconfig @@ -1,4 +1,6 @@ config TDX_HOST_SERVICES tristate depends on INTEL_TDX_HOST + select FW_LOADER + select FW_UPLOAD default m diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-= host/tdx-host.c index 2886554..e5a672b 100644 --- a/drivers/virt/coco/tdx-host/tdx-host.c +++ b/drivers/virt/coco/tdx-host/tdx-host.c @@ -6,6 +6,7 @@ */ =20 #include +#include #include #include #include @@ -88,15 +89,15 @@ static struct attribute *seamldr_attrs[] =3D { NULL, }; =20 -static umode_t seamldr_group_visible(struct kobject *kobj, struct attribut= e *attr, int idx) +static bool supports_runtime_update(void) { const struct tdx_sys_info *sysinfo =3D tdx_get_sysinfo(); =20 if (!sysinfo) - return 0; + return false; =20 if (!tdx_supports_runtime_update(sysinfo)) - return 0; + return false; =20 /* * This bug makes P-SEAMLDR calls clobber the current VMCS @@ -104,6 +105,14 @@ static umode_t seamldr_group_visible(struct kobject *k= obj, struct attribute *att * attributes if the CPU has this bug. */ if (boot_cpu_has_bug(X86_BUG_SEAMRET_INVD_VMCS)) + return false; + + return true; +} + +static umode_t seamldr_group_visible(struct kobject *kobj, struct attribut= e *attr, int idx) +{ + if (!supports_runtime_update()) return 0; =20 return attr->mode; @@ -120,6 +129,80 @@ static const struct attribute_group *tdx_host_groups[]= =3D { NULL, }; =20 +static enum fw_upload_err tdx_fw_prepare(struct fw_upload *fwl, + 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 *da= ta, + u32 offset, u32 data_len, u32 *written) +{ + int ret; + + ret =3D seamldr_install_module(data, data_len); + switch (ret) { + case 0: + *written =3D data_len; + return FW_UPLOAD_ERR_NONE; + default: + return FW_UPLOAD_ERR_FW_INVALID; + } +} + +static enum fw_upload_err tdx_fw_poll_complete(struct fw_upload *fwl) +{ + /* + * The upload completed during tdx_fw_write(). + * Never poll for completion. + */ + return FW_UPLOAD_ERR_NONE; +} + +static void tdx_fw_cancel(struct fw_upload *fwl) +{ + /* + * TDX module updates are not cancellable. + * Provide a no-op callback to satisfy fw_upload_ops. + */ +} + +static const struct fw_upload_ops tdx_fw_ops =3D { + .prepare =3D tdx_fw_prepare, + .write =3D tdx_fw_write, + .poll_complete =3D tdx_fw_poll_complete, + .cancel =3D tdx_fw_cancel, +}; + +static void seamldr_deinit(void *tdx_fwl) +{ + firmware_upload_unregister(tdx_fwl); +} + +static int seamldr_init(struct device *dev) +{ + struct fw_upload *tdx_fwl; + + if (!supports_runtime_update()) + return 0; + + tdx_fwl =3D firmware_upload_register(THIS_MODULE, dev, "tdx_module", + &tdx_fw_ops, NULL); + if (IS_ERR(tdx_fwl)) + return PTR_ERR(tdx_fwl); + + return devm_add_action_or_reset(dev, seamldr_deinit, tdx_fwl); +} + +static int tdx_host_probe(struct faux_device *fdev) +{ + return seamldr_init(&fdev->dev); +} + +static const struct faux_device_ops tdx_host_ops =3D { + .probe =3D tdx_host_probe, +}; + static struct faux_device *fdev; =20 static int __init tdx_host_init(void) @@ -127,7 +210,9 @@ static int __init tdx_host_init(void) if (!x86_match_cpu(tdx_host_ids) || !tdx_get_sysinfo()) return -ENODEV; =20 - fdev =3D faux_device_create_with_groups(KBUILD_MODNAME, NULL, NULL, tdx_h= ost_groups); + fdev =3D faux_device_create_with_groups(KBUILD_MODNAME, NULL, + &tdx_host_ops, + tdx_host_groups); if (!fdev) return -ENODEV; =20