From: Chao Gao <chao.gao@intel.com>
To: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
kvm@vger.kernel.org
Cc: yilun.xu@intel.com, arjan.van.de.ven@intel.com,
Chao Gao <chao.gao@intel.com>, Thomas Gleixner <tglx@kernel.org>,
Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
x86@kernel.org, "H. Peter Anvin" <hpa@zytor.com>,
Kiryl Shutsemau <kas@kernel.org>,
Rick Edgecombe <rick.p.edgecombe@intel.com>
Subject: [RFC PATCH v1 2/3] x86/virt/seamldr: Support large TDX module binaries
Date: Fri, 30 Oct 2026 01:52:59 -0700 [thread overview]
Message-ID: <20261030085304.3886520-3-chao.gao@intel.com> (raw)
In-Reply-To: <20261030085304.3886520-1-chao.gao@intel.com>
The module binary is handed to P-SEAMLDR as a list of page physical
addresses in 'struct seamldr_params'. The list has 496 entries, which
caps a module at 496 pages.
TDX modules are gaining features and are about to outgrow that limit.
The P-SEAMLDR ABI was extended for this. An upcoming revision of the
SEAMLDR specification adds the following to the description of the
module page address list in 'struct seamldr_params'.
For TDX module image with larger than 495 pages, the last entry will
point to a new 4 KB page that contains up to 511 additional HPAs. Each
page will use its last HPA to reference another page if required. This
chain continues until all module pages are referenced.
Allocate the needed chain pages together with 'struct seamldr_params',
as a flexible array at its end. Link them up at allocation time. Then
fill in the module page addresses as before, but skip the link entries.
A single allocation leaves nothing to unwind on failure and keeps the
existing kfree() as the only cleanup.
Do not query P-SEAMLDR for chaining support. Userspace selects the update
image and is responsible for ensuring the module is compatible with
P-SEAMLDR.
Chaining removes the 496 page cap, but the module page count still needs
a bound. Without one, a crafted header can overflow the u32 module length
('module_nr_pages * PAGE_SIZE') and pass the image size check. It can
also make the chain page allocation exceed KMALLOC_MAX_SIZE, which warns
in the page allocator.
Raise SEAMLDR_MAX_NR_MODULE_PAGES to a 1GB software limit to rule out
both problems. 1GB is far above the few MB of current modules.
The size of module_pages_pa_list[] no longer reflects the maximum module
page count. Define a new SEAMLDR_NR_MODULE_PA_ENTRIES for it.
Thanks to Yilun for the idea of allocating the chain pages together
with 'struct seamldr_params'.
AI was used under supervision to review code and workshop logs. It
spotted both problems that the software limit addresses.
Signed-off-by: Chao Gao <chao.gao@intel.com>
---
arch/x86/virt/vmx/tdx/seamldr.c | 95 +++++++++++++++++++++++++++++++--
1 file changed, 90 insertions(+), 5 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index 3513fd28536a..09c8f6e30e9f 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -24,7 +24,14 @@
#define P_SEAMLDR_INFO 0x8000000000000000
#define P_SEAMLDR_INSTALL 0x8000000000000001
-#define SEAMLDR_MAX_NR_MODULE_PAGES 496
+/*
+ * Arbitrary software limit, far above the few MB of current modules.
+ * It keeps the module length within a u32, and 'struct seamldr_params'
+ * plus its chain pages within KMALLOC_MAX_SIZE.
+ */
+#define SEAMLDR_MAX_NR_MODULE_PAGES (SZ_1G / PAGE_SIZE)
+
+#define SEAMLDR_NR_MODULE_PA_ENTRIES 496
#define SEAMLDR_MAX_NR_SIG_PAGES 1
/*
@@ -34,6 +41,19 @@
*/
#define SEAMLDR_SCENARIO_UPDATE 1
+#define SEAMLDR_NR_CHAIN_PA_ENTRIES (PAGE_SIZE / sizeof(u64) - 1)
+/*
+ * A 4KB page holding the physical addresses of the module pages that do
+ * not fit in module_pages_pa_list[]. @next_page_pa points to the next
+ * page, forming a singly linked list.
+ */
+struct chain_page {
+ u64 pa[SEAMLDR_NR_CHAIN_PA_ENTRIES];
+ u64 next_page_pa;
+} __packed;
+
+static_assert(sizeof(struct chain_page) == 4096);
+
/*
* This is the "SEAMLDR_PARAMS" data structure defined in the
* "SEAM Loader (SEAMLDR) Interface Specification".
@@ -48,10 +68,13 @@ struct seamldr_params {
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];
+ u64 module_pages_pa_list[SEAMLDR_NR_MODULE_PA_ENTRIES];
+
+ /* Hold module pages that cannot fit in module_pages_pa_list[]. */
+ struct chain_page chain_pages[];
} __packed;
-static_assert(sizeof(struct seamldr_params) == 4096);
+static_assert(offsetof(struct seamldr_params, chain_pages) == 4096);
/*
* Serialize P-SEAMLDR calls since the hardware only allows a single CPU to
@@ -143,6 +166,68 @@ static void populate_pa_list(u64 *pa_list, const u8 *vmalloc_addr, u32 vmalloc_l
}
}
+/*
+ * Like populate_pa_list(), but skip entries pointing to the next page.
+ */
+static void populate_module_pa_list(struct seamldr_params *params,
+ const u8 *vmalloc_addr, u32 vmalloc_len_pages)
+{
+ u64 *pa_list = params->module_pages_pa_list;
+ int i, j;
+
+ for (i = 0, j = 0; i < vmalloc_len_pages; i++, j++) {
+ unsigned long offset = i * PAGE_SIZE;
+ unsigned long pfn = vmalloc_to_pfn(&vmalloc_addr[offset]);
+
+ /*
+ * Skip non-zero entries. They point to the next page and
+ * are populated during allocation.
+ */
+ if (pa_list[j])
+ j++;
+
+ pa_list[j] = pfn << PAGE_SHIFT;
+ }
+}
+
+static struct seamldr_params *alloc_seamldr_params(u32 nr_module_pages)
+{
+ struct seamldr_params *params;
+ u32 nr_chain_pages;
+ u32 nr_direct;
+ u64 *link;
+
+ /*
+ * Number of module pages that fit directly in
+ * module_pages_pa_list[]. Minus one as the last entry is
+ * reserved to point to the first chain page.
+ */
+ nr_direct = SEAMLDR_NR_MODULE_PA_ENTRIES - 1;
+
+ /* Calculate the number of extra chain pages needed. */
+ nr_chain_pages = 0;
+ if (nr_module_pages > nr_direct) {
+ u32 nr_left = nr_module_pages - nr_direct;
+
+ nr_chain_pages = DIV_ROUND_UP(nr_left, SEAMLDR_NR_CHAIN_PA_ENTRIES);
+ }
+
+ params = kzalloc_flex(*params, chain_pages, nr_chain_pages);
+ if (!params)
+ return NULL;
+
+ /* Link all the chain pages. */
+ link = ¶ms->module_pages_pa_list[nr_direct];
+ for (int i = 0; i < nr_chain_pages; i++) {
+ struct chain_page *page = ¶ms->chain_pages[i];
+
+ *link = __pa(page);
+ link = &page->next_page_pa;
+ }
+
+ return params;
+}
+
static void populate_seamldr_params(struct seamldr_params *params,
const u8 *sig, u32 sig_nr_pages,
const u8 *mod, u32 mod_nr_pages)
@@ -152,7 +237,7 @@ static void populate_seamldr_params(struct seamldr_params *params,
params->module_nr_pages = 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);
+ populate_module_pa_list(params, mod, mod_nr_pages);
}
/*
@@ -195,7 +280,7 @@ static struct seamldr_params *build_seamldr_params(const struct tdx_image *image
if (memchr_inv(header->reserved, 0, sizeof(header->reserved)))
return ERR_PTR(-EINVAL);
- params = kzalloc_obj(*params);
+ params = alloc_seamldr_params(header->module_nr_pages);
if (!params)
return ERR_PTR(-ENOMEM);
--
2.52.0
next prev parent reply other threads:[~2026-10-30 8:53 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 8:52 [RFC PATCH v1 0/3] TDX: P-SEAMLDR: Support large TDX modules and sigstructs Chao Gao
2026-10-30 8:52 ` [RFC PATCH v1 1/3] x86/virt/seamldr: Allocate 'struct seamldr_params' after image validation Chao Gao
2026-10-30 8:52 ` Chao Gao [this message]
2026-10-30 8:53 ` [RFC PATCH v1 3/3] x86/virt/seamldr: Support multiple sigstruct pages 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=20261030085304.3886520-3-chao.gao@intel.com \
--to=chao.gao@intel.com \
--cc=arjan.van.de.ven@intel.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=kas@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=rick.p.edgecombe@intel.com \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
--cc=yilun.xu@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
all inboxes | Powered by JetHome®