From nobody Mon Jun 8 06:36:51 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 82F0640BCBE; Fri, 5 Jun 2026 21:25: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=1780694725; cv=none; b=JC90Pg4JD9jJO+TlpOMTTZ5ckwn/7fSj/rO5l7pz1v2uvyBsHtzB2Vk4SuyEMLVFKXPkXTvaVwgOBMWg6w4ycSjI3rSJn7Sg7VFGPn4AqdpTjxnzQpHASpWFAB8Uqle9Q7Z/dKdGDbwLDctHq0TrV71SktYGCvI/GF7YvYP1Y1U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780694725; c=relaxed/simple; bh=F+hUZOXg68LRnur9asAbcBGTomms9YdP9airt/C3D8o=; h=Date:From:To:Subject:Cc:In-Reply-To:References:MIME-Version: Message-ID:Content-Type; b=atzKhU7JWJjafOQjinM5JC+mQnTU5QKjJuVuzR/JszSUhZIIv15FfODeJYa5O6828/GuM3lU9GTM7e/FllGuRk23z5ndnOIkBBlnk+2vG4JSx5nthQrDwp2BraAnmd5Vg+tkYASZaZ6/24G5Xgety/3EMrlCvKv1p70PsWSS+j8= 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=LXTqfyl7; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=I7skN4p3; 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="LXTqfyl7"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="I7skN4p3" Date: Fri, 05 Jun 2026 21:25:20 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1780694722; 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=g2uVtL1T3VmN2j8PmR1LB75+Rgh2Iuh66/hj5quQ6/0=; b=LXTqfyl7KLx2zidzUuJ2l8eJq9Zjk7VSpEUs6k8xdCG8++B5ozQhNIwgOWqsApREdf0GsL 8ue5WEgoHImQl/bexikEnSVNggWAE4ywcDvMF8wYi+a/MbjJlXujrogl6f97GWtzr4l4uc KY8jDybuH8Ny/Zj6xZ1sOpZu1cNzNtBjdVYDbwoqpKgkXpz+oyvAVrcTdR0G7nU3r8uJQz ee8a6RevYWKMd7xOuElPkIaiwVI78NfITMpnOeIm/QTw9bAUg7ZMTaiGWopFY2MeSu2j8B 5sGWp0kIeFQ8qKoMpEAdOVzUwhfJhjjQX8RKigHHuT8p1DVs3qCfYscu6wV+gg== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1780694722; 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=g2uVtL1T3VmN2j8PmR1LB75+Rgh2Iuh66/hj5quQ6/0=; b=I7skN4p3EBxtKk/ACFnBPz2vuYYXD7CV4TUwqiydsPi4iWdEg+Qj/gI53irg0bJrlC8DMk l6eYsrlaxe+ZC2DA== 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] x86/virt/seamldr: Allocate and populate a module update request Cc: Chao Gao , Dave Hansen , x86@kernel.org, linux-kernel@vger.kernel.org In-Reply-To: <20260520133909.409394-14-chao.gao@intel.com> References: <20260520133909.409394-14-chao.gao@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <178069472040.710.3868357801817007679.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: 23a81e6cce154950ad203fd8bc5a016038c173b3 Gitweb: https://git.kernel.org/tip/23a81e6cce154950ad203fd8bc5a01603= 8c173b3 Author: Chao Gao AuthorDate: Wed, 20 May 2026 15:29:01 -07:00 Committer: Dave Hansen CommitterDate: Wed, 03 Jun 2026 08:14:51 -07:00 x86/virt/seamldr: Allocate and populate a module update request There are two important ABIs here: 'struct tdx_image' - The on-disk and in-memory format for a TDX module update image. 'struct seamldr_params' - The in-memory ABI passed to the TDX module loader. Points to a single 'struct tdx_image' broken up into 4k pages. Userspace supplies the update image in 'struct tdx_image' format. The image consists of a header followed by a sigstruct and the module binary. P-SEAMLDR, however, consumes 'struct seamldr_params' rather than the image directly. Parse the 'struct tdx_image' provided by userspace and populate a matching 'struct seamldr_params'. The 'tdx_image' ABI is versioned. Two public versions exist today: 0x100 and 0x200. This kernel only accepts 0x200. The older 0x100 format is being deprecated and is intentionally not supported here. Future versions of the module might be able to use the same ABIs (user/kernel and kernel/SEAMLDR) but they will not be able to use this kernel code. Reject module images without that specific version. This ensures that the kernel is able to understand the passed-in format. Validate the 'struct tdx_image' header before using it, because the header is consumed solely by the kernel to locate the sigstruct and module within the image. Do not validate the payload itself. The sigstruct and module pages are passed through to P-SEAMLDR, which validates them as part of the update. sigstruct_pages_pa_list currently has only one entry, but it will grow to four pages in the future. Keep it as an array for symmetry with module_pages_pa_list and for extensibility. [ dhansen: normal changelog clarification/munging ] Signed-off-by: Chao Gao Signed-off-by: Dave Hansen Link: https://patch.msgid.link/20260520133909.409394-14-chao.gao@intel.com --- arch/x86/virt/vmx/tdx/seamldr.c | 157 ++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 1 deletion(-) diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamld= r.c index b2b6a43..bcb1386 100644 --- a/arch/x86/virt/vmx/tdx/seamldr.c +++ b/arch/x86/virt/vmx/tdx/seamldr.c @@ -7,6 +7,8 @@ #define pr_fmt(fmt) "seamldr: " fmt =20 #include +#include +#include #include =20 #include @@ -18,6 +20,35 @@ /* P-SEAMLDR SEAMCALL leaf function */ #define P_SEAMLDR_INFO 0x8000000000000000 =20 +#define SEAMLDR_MAX_NR_MODULE_PAGES 496 +#define SEAMLDR_MAX_NR_SIG_PAGES 1 + +/* + * The seamldr_params "scenario" field specifies the operation mode: + * 0: Install TDX module from scratch (not used by kernel) + * 1: Update existing TDX module to a compatible version + */ +#define SEAMLDR_SCENARIO_UPDATE 1 + +/* + * This is the "SEAMLDR_PARAMS" data structure defined in the + * "SEAM Loader (SEAMLDR) Interface Specification". + * + * 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; + u32 scenario; + u64 sigstruct_pages_pa_list[SEAMLDR_MAX_NR_SIG_PAGES]; + u8 reserved[104]; + u64 module_nr_pages; + u64 module_pages_pa_list[SEAMLDR_MAX_NR_MODULE_PAGES]; +} __packed; + +static_assert(sizeof(struct seamldr_params) =3D=3D 4096); + /* * Serialize P-SEAMLDR calls since the hardware only allows a single CPU to * interact with P-SEAMLDR simultaneously. Use raw version as the calls can @@ -55,6 +86,106 @@ int seamldr_get_info(struct seamldr_info *seamldr_info) } EXPORT_SYMBOL_FOR_MODULES(seamldr_get_info, "tdx-host"); =20 +#define TDX_IMAGE_VERSION_2 0x200 + +/* First page of the on-disk module update image: */ +struct tdx_image_header { + u16 version; + u16 checksum; + u8 signature[8]; + u32 sigstruct_nr_pages; + u32 module_nr_pages; + u8 reserved[4076]; +} __packed; + +#define TDX_IMAGE_HEADER_SIZE sizeof(struct tdx_image_header) +static_assert(TDX_IMAGE_HEADER_SIZE =3D=3D 4096); + +/* + * Intel TDX module update ABI structure. aka. "TDX module blob". + * This is the on-disk format that fw_upload lands in a kernel + * buffer. + * + * @payload contains sigstruct pages followed by module pages. + */ +struct tdx_image { + struct tdx_image_header header; + u8 payload[]; +}; + +/* + * Given a vmalloc() allocation, write all of the backing physical + * addresses to pa_list[]. Caller guarantees that the array is big + * enough. + */ +static void populate_pa_list(u64 *pa_list, const u8 *vmalloc_addr, u32 vma= lloc_len_pages) +{ + int i; + + for (i =3D 0; i < vmalloc_len_pages; i++) { + unsigned long offset =3D i * PAGE_SIZE; + unsigned long pfn =3D vmalloc_to_pfn(&vmalloc_addr[offset]); + + pa_list[i] =3D pfn << PAGE_SHIFT; + } +} + +static void populate_seamldr_params(struct seamldr_params *params, + const u8 *sig, u32 sig_nr_pages, + const u8 *mod, u32 mod_nr_pages) +{ + params->version =3D 0; + params->scenario =3D SEAMLDR_SCENARIO_UPDATE; + params->module_nr_pages =3D 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); +} + +/* + * @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_header *header =3D &image->header; + + u32 sigstruct_len =3D header->sigstruct_nr_pages * PAGE_SIZE; + u32 module_len =3D header->module_nr_pages * PAGE_SIZE; + + u8 *header_start =3D (u8 *)header; + u8 *header_end =3D header_start + TDX_IMAGE_HEADER_SIZE; + + u8 *sigstruct_start =3D header_end; + u8 *sigstruct_end =3D sigstruct_start + sigstruct_len; + + u8 *module_start =3D sigstruct_end; + + /* Check the calculated payload size against the image size. */ + if (TDX_IMAGE_HEADER_SIZE + sigstruct_len + module_len !=3D image_len) + return -EINVAL; + + /* Reject unsupported tdx_image ABI versions. */ + if (header->version !=3D 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; + + if (memchr_inv(header->reserved, 0, sizeof(header->reserved))) + return -EINVAL; + + populate_seamldr_params(params, sigstruct_start, header->sigstruct_nr_pag= es, + module_start, header->module_nr_pages); + return 0; +} + /** * seamldr_install_module - Install a new TDX module. * @data: Pointer to the TDX module image. @@ -64,7 +195,31 @@ EXPORT_SYMBOL_FOR_MODULES(seamldr_get_info, "tdx-host"); */ int seamldr_install_module(const u8 *data, u32 data_len) { + struct seamldr_params *params; + const struct tdx_image *image; + int ret; + + /* + * init_seamldr_params() reads the header early. + * Ensure there is enough data to do at least that. + */ + if (data_len < TDX_IMAGE_HEADER_SIZE) + return -EINVAL; + + image =3D (const struct tdx_image *)data; + + params =3D kzalloc_obj(*params); + if (!params) + return -ENOMEM; + + /* Populate 'params' from 'image'. */ + ret =3D init_seamldr_params(params, image, data_len); + if (ret) + goto out; + /* TODO: Update TDX module here */ - return 0; +out: + kfree(params); + return ret; } EXPORT_SYMBOL_FOR_MODULES(seamldr_install_module, "tdx-host");