* [RFC PATCH v2 0/2] TDX: P-SEAMLDR: Support large TDX modules and sigstructs
@ 2026-11-06 13:21 Chao Gao
2026-11-06 13:21 ` [RFC PATCH v2 1/2] x86/virt/seamldr: Support large TDX module binaries Chao Gao
2026-11-06 13:21 ` [RFC PATCH v2 2/2] x86/virt/seamldr: Support multiple sigstruct pages Chao Gao
0 siblings, 2 replies; 3+ messages in thread
From: Chao Gao @ 2026-11-06 13:21 UTC (permalink / raw)
To: x86, linux-coco, kvm, linux-kernel
Cc: yilun.xu, arjan.van.de.ven, Chao Gao, Kiryl Shutsemau,
Rick Edgecombe, Dave Hansen, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, H. Peter Anvin
This series lets TDX module updates handle large TDX modules and
sigstructs. It is an RFC for early review.
Dave, feel free to ignore this RFC for now. TDX developers, please take
a look.
Patch 1 raises the module size limit from about 2MB to about 32MB,
instead of supporting every module size the P-SEAMLDR ABI allows.
Current modules are roughly 2MB and are unlikely to approach 32MB for
years. If they do, raising SEAMLDR_PARAMS_NR_PAGES is enough. In return
the code stays much simpler, so I think this is the right direction.
Does that trade-off look right?
== Changes ==
Changes since RFC v1:
Patch 1 is reworked to be much smaller. In the last version, we tried to
size the chained pages to each module, which needed a prep patch so that
the allocation happens only after the image header is validated. now, we
make 'struct seamldr_params' a fixed 16 pages. This way, the allocation
no longer depends on the header, so the prep patch is dropped.
Other changes:
- Find each link entry by its position at the end of a page instead of
by checking for a non-zero entry. [Rick]
- Write the link entries in populate_pa_list() while filling in the page
addresses, instead of in a separate loop and helper.
- Drop the version macros in patch 2 to keep the diff minimal.
- Explain in patch 2 why the sigstruct filling code needs no change.
v1: https://lore.kernel.org/kvm/20261030085304.3886520-1-chao.gao@intel.com/
===
This series is a follow-up to the TDX module update series. As thanks
for merging that series, we agreed to take on the TDX metadata cleanup [1].
With the cleanup converging, we are starting on this work and expect it
to be merged after the cleanup.
Today a TDX module binary can be at most 496 pages and its sigstruct at
most one page. Both limits come from the P-SEAMLDR ABI.
TDX modules are outgrowing them. A post-quantum signature algorithm
pushes the sigstruct past 4KB, and new module features push the binary
past 496 pages. The P-SEAMLDR ABI was extended for both.
The kernel rejects any image over these limits. TDX modules for Diamond
Rapids exceed them, so without this series, TDX module updates fail on
Diamond Rapids. This series is all that is needed to make them work.
The image format that userspace uploads ('struct tdx_image') is generic
enough and needs no change.
== AI Usage ==
AI was used under supervision to review code and workshop logs.
== Testing ==
Tested on a Diamond Rapids system. Updates with a module larger than 2MB
and an 8KB sigstruct work, which exercises both the chained page list
and version 1 of 'struct seamldr_params'. TDs keep running across
updates.
[1]: https://lore.kernel.org/kvm/20261012094754.27702-1-chao.gao@intel.com/
Chao Gao (2):
x86/virt/seamldr: Support large TDX module binaries
x86/virt/seamldr: Support multiple sigstruct pages
arch/x86/virt/vmx/tdx/seamldr.c | 43 ++++++++++++++++++++++++++-------
1 file changed, 34 insertions(+), 9 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RFC PATCH v2 1/2] x86/virt/seamldr: Support large TDX module binaries
2026-11-06 13:21 [RFC PATCH v2 0/2] TDX: P-SEAMLDR: Support large TDX modules and sigstructs Chao Gao
@ 2026-11-06 13:21 ` Chao Gao
2026-11-06 13:21 ` [RFC PATCH v2 2/2] x86/virt/seamldr: Support multiple sigstruct pages Chao Gao
1 sibling, 0 replies; 3+ messages in thread
From: Chao Gao @ 2026-11-06 13:21 UTC (permalink / raw)
To: linux-kernel, linux-coco, kvm
Cc: yilun.xu, arjan.van.de.ven, Chao Gao, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Kiryl Shutsemau, Rick Edgecombe
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 to chain the list across 4KB pages. For
a module over 495 pages, the last of the 496 entries points to another
page of entries instead of to a module page. The last entry of that
page can point to yet another page, and so on, until every module page
is listed.
Grow module_pages_pa_list[] so that 'struct seamldr_params' spans
multiple pages (16 for now), and use the extra pages as the chained
pages. The last entry of each page then points to the entry right after
it, which starts the next page. These link entries do not hold module
pages, so leave them out of the maximum module page count.
The 16-page size is somewhat arbitrary. It fits modules of up to about
32MB, far above the roughly 2MB of current modules. It wastes 60KB for
today's modules, which is fine because the memory is freed right after
the update. Sizing module_pages_pa_list[] per module would mean either
moving the allocation after the image header is validated, or allocating
from an unvalidated page count. Either way it is more code churn.
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.
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.
Signed-off-by: Chao Gao <chao.gao@intel.com>
---
v2:
- Make 'struct seamldr_params' a fixed 16 pages instead of sizing it
to each module, capping modules at about 32MB. [Rick]
- Drop the patch that moved the allocation after image validation. [Rick]
---
arch/x86/virt/vmx/tdx/seamldr.c | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index b1137ca6150d..087bdf10a9a7 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -24,7 +24,18 @@
#define P_SEAMLDR_INFO 0x8000000000000000
#define P_SEAMLDR_INSTALL 0x8000000000000001
-#define SEAMLDR_MAX_NR_MODULE_PAGES 496
+/*
+ * module_pages_pa_list[] spans multiple pages. The last entry of each
+ * page points to the next page and does not hold a module page, so the
+ * maximum number of module pages is the array size minus one entry per
+ * page.
+ *
+ * SEAMLDR_PARAMS_NR_PAGES is somewhat arbitrary. It allows modules of
+ * up to about 32MB. Increase it if modules outgrow that.
+ */
+#define SEAMLDR_PARAMS_NR_PAGES 16
+#define SEAMLDR_NR_MODULE_PA_ENTRIES (496 + 512 * (SEAMLDR_PARAMS_NR_PAGES - 1))
+#define SEAMLDR_MAX_NR_MODULE_PAGES (SEAMLDR_NR_MODULE_PA_ENTRIES - SEAMLDR_PARAMS_NR_PAGES)
#define SEAMLDR_MAX_NR_SIG_PAGES 1
/*
@@ -48,10 +59,10 @@ 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];
} __packed;
-static_assert(sizeof(struct seamldr_params) == 4096);
+static_assert(sizeof(struct seamldr_params) == SEAMLDR_PARAMS_NR_PAGES * PAGE_SIZE);
/*
* Serialize P-SEAMLDR calls since the hardware only allows a single CPU to
@@ -129,17 +140,24 @@ struct tdx_image {
/*
* Given a vmalloc() allocation, write all of the backing physical
* addresses to pa_list[]. Caller guarantees that the array is big
- * enough.
+ * enough. If pa_list[] spans multiple pages, point the last entry
+ * of each page to the next page.
*/
static void populate_pa_list(u64 *pa_list, const u8 *vmalloc_addr, u32 vmalloc_len_pages)
{
- int i;
+ int i, j;
- for (i = 0; i < vmalloc_len_pages; i++) {
+ for (i = 0, j = 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;
+ if (PAGE_ALIGNED(&pa_list[j + 1])) {
+ pa_list[j] = __pa(&pa_list[j + 1]);
+ j++;
+ }
+
+ pa_list[j] = pfn << PAGE_SHIFT;
+ j++;
}
}
--
2.52.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RFC PATCH v2 2/2] x86/virt/seamldr: Support multiple sigstruct pages
2026-11-06 13:21 [RFC PATCH v2 0/2] TDX: P-SEAMLDR: Support large TDX modules and sigstructs Chao Gao
2026-11-06 13:21 ` [RFC PATCH v2 1/2] x86/virt/seamldr: Support large TDX module binaries Chao Gao
@ 2026-11-06 13:21 ` Chao Gao
1 sibling, 0 replies; 3+ messages in thread
From: Chao Gao @ 2026-11-06 13:21 UTC (permalink / raw)
To: linux-kernel, linux-coco, kvm
Cc: yilun.xu, arjan.van.de.ven, Chao Gao, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Kiryl Shutsemau, Rick Edgecombe
Each TDX module update image carries a sigstruct, which is passed along
with the module binary to P-SEAMLDR. 'struct seamldr_params' has a single
physical address slot for it, so a sigstruct can be at most one 4KB page.
TDX modules are moving to a post-quantum signature algorithm. Its
signatures are far larger than the ones in use today, so the sigstruct
no longer fits in 4KB.
The P-SEAMLDR ABI was extended for this. It now has four sigstruct
address slots instead of one, for sigstructs of up to 16KB. A new
'version' value of 1 tells P-SEAMLDR that this layout is in use.
Extend 'struct seamldr_params' to match the new ABI. The sigstruct
address list is already populated in a loop over all sigstruct pages, so
raising SEAMLDR_MAX_NR_SIG_PAGES is enough to fill the new slots
Set 'version' to 1 only when the sigstruct spans more than one page. Old
P-SEAMLDRs reject version 1, so this keeps updates with a single-page
sigstruct working on them.
Do not query P-SEAMLDR for version 1 support. Userspace selects the
update image and is responsible for ensuring the module is compatible
with P-SEAMLDR.
AI was used under supervision to review code and workshop logs
Signed-off-by: Chao Gao <chao.gao@intel.com>
---
v2:
- drop macros for versions to keep the diff minimal
- explain why the sigstruct filling code needs no change
---
arch/x86/virt/vmx/tdx/seamldr.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index 087bdf10a9a7..9cef6fff2a6c 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -36,7 +36,7 @@
#define SEAMLDR_PARAMS_NR_PAGES 16
#define SEAMLDR_NR_MODULE_PA_ENTRIES (496 + 512 * (SEAMLDR_PARAMS_NR_PAGES - 1))
#define SEAMLDR_MAX_NR_MODULE_PAGES (SEAMLDR_NR_MODULE_PA_ENTRIES - SEAMLDR_PARAMS_NR_PAGES)
-#define SEAMLDR_MAX_NR_SIG_PAGES 1
+#define SEAMLDR_MAX_NR_SIG_PAGES 4
/*
* The seamldr_params "scenario" field specifies the operation mode:
@@ -57,7 +57,7 @@ struct seamldr_params {
u32 version;
u32 scenario;
u64 sigstruct_pages_pa_list[SEAMLDR_MAX_NR_SIG_PAGES];
- u8 reserved[104];
+ u8 reserved[80];
u64 module_nr_pages;
u64 module_pages_pa_list[SEAMLDR_NR_MODULE_PA_ENTRIES];
} __packed;
@@ -166,6 +166,13 @@ static void populate_seamldr_params(struct seamldr_params *params,
const u8 *mod, u32 mod_nr_pages)
{
params->version = 0;
+ /*
+ * Use version 1 only for multi-page sigstructs, to stay
+ * compatible with old P-SEAMLDRs that lack version 1 support.
+ */
+ if (sig_nr_pages > 1)
+ params->version = 1;
+
params->scenario = SEAMLDR_SCENARIO_UPDATE;
params->module_nr_pages = mod_nr_pages;
--
2.52.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-06 13:21 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-06 13:21 [RFC PATCH v2 0/2] TDX: P-SEAMLDR: Support large TDX modules and sigstructs Chao Gao
2026-11-06 13:21 ` [RFC PATCH v2 1/2] x86/virt/seamldr: Support large TDX module binaries Chao Gao
2026-11-06 13:21 ` [RFC PATCH v2 2/2] x86/virt/seamldr: Support multiple sigstruct pages Chao Gao
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®