* [PATCH v2 0/5] Enable TDX module extensions
@ 2026-09-15 10:26 Xu Yilun
2026-09-15 10:26 ` [PATCH v2 1/5] x86/virt/tdx: Move TDH.SYS.CONFIG operations into a wrapper Xu Yilun
` (5 more replies)
0 siblings, 6 replies; 13+ messages in thread
From: Xu Yilun @ 2026-09-15 10:26 UTC (permalink / raw)
To: x86, linux-coco, linux-kernel
Cc: kas, rick.p.edgecombe, yilun.xu, yilun.xu, xiaoyao.li,
sohil.mehta, adrian.hunter, kishen.maloor, tony.lindgren,
peter.fang, baolu.lu, zhenzhong.duan, chao.gao, artem.bityutskiy,
kvm, nik.borisov
Hi,
This is the first respin of the TDX module extensions series. Thank you
all for the design discussion and architectural review. Hope the design
could be settled and some more RBs got in this version.
Dave please feel free to ignore. Kiryl, Rick and TDX developers, please
help take a look.
== Changes ==
A major change is that the add-on feature re-configuration and
extensions re-initialization operations during runtime update have been
dropped. IOW, the update flow is left untouched. This stems from the
architectural discussion that the basic update flow should start with a
simple functional requirement - just restore everything (for example,
the add-on features re-enabling, TDH.EXT.INIT) that was configured
during boot, no extra input from the host is needed. Do improvement
later if any new requirement arise.
The extensions consume tens of megabytes memory that will never be
returned to the host. How to consume this amount of memory forever is an
open question though v1 uses alloc_contig_pages() for memory
de-fragmentation. Kiryl implied fragmentation is an important concern.
He answered that de-fragmentation is to prevent 2M pageblocks from being
partially occupied. alloc_contig_pages() can be more than needed but
should be good enough when we do it once during the boot time. In
patch 4, keep the alloc_contig_pages() but add a code comment for
clarification.
Dave asked why pass the addon_features0 around as a function parameter,
it can always be return from the global, static
get_tdx_addon_features0() helper. Yilun admitted this is redundant. In
patch 2, change to call the helper a second time where the value is
needed.
Rick pointed out that in patch 1, the changelog said we introduced a
kernel data type for TDMR info PA array argument but the code is still
setting TDX ABI data type (u64) for PAs. He suggested justifying that
this is to avoid duplicating allocations and copies in SEAMCALL
wrappers. Yilun noted this is a general in-memory ABI pattern which
exists in several places in TDX. Add the statement of in-memory ABI
handling in the changelog.
Other changes:
- Patch 2: Drop the changelog section explaining why add-on feature
enabling is needed in this series, as it is a generally understood
pattern (Rick)
- Patch 2: Add a note in the changelog that Dynamic PAMT is not
enabled via the new bitmap argument of TDH.SYS.CONFIG (Rick)
- Patch 4: Add code comments for hpa_list container page allocation
(Kiryl)
- Patch 4: s/PFN array/HPA array in changelog (Kiryl)
- Patch 4: State the alternative memory adding solution and why we
don't use it in changelog (Rick)
v1: https://lore.kernel.org/all/20260821032920.256225-1-yilun.xu@linux.intel.com/
Quoting v2: https://lore.kernel.org/lkml/20260618081355.3253581-1-yilun.xu@linux.intel.com/
Quoting v1: https://lore.kernel.org/all/20260522034128.3144354-1-yilun.xu@linux.intel.com/
== Overview ==
To date, SEAMCALL execution must either complete quickly to avoid
stalling the host, or yield quickly at pre-defined interrupt
checkpoints. This is acceptable for the existing SEAMCALL leafs,
which perform simple, bounded operations.
However, some new features such as attestation and TD migration require
higher level security protocols inside the TDX module, which cannot fit
within that constraint. TDX solves this by making those operations
inherently preemptible and resumable like OS tasks. TDX provides a
separate SEAMCALL execution environment - the TDX module extensions -
for those operations.
This capability allows for higher-level SEAMCALL ABI design - like
"create a DICE-based attestation quote". Several new features, such as
DICE-based attestation, TDISP and TD migration, use SEAMCALL leafs
backed by the TDX module extensions.
The TDX module extensions need memory for their execution environment
to serve these SEAMCALL leafs, so they need extra setup steps during TDX
module initialization. The bulk of this series implements these setup
steps.
At runtime, the host invokes these SEAMCALL leafs just as normal ones -
if interrupted, simply re-invoke the leaf to resume.
For more information on TDX module extensions, please refer to [1].
[1] https://lore.kernel.org/lkml/20260618081355.3253581-1-yilun.xu@linux.intel.com/
== Branch stack ==
This is based on v7.3-rc1. You can find the full branch stack at [2].
The DICE part is in the full branch as an example for extensions. But it
does not include the other DICE feedbacks.
The full branch contains:
Dependency: Patch 1: SEAMCALL version patch [3] which is WIP on
community review.
This series: Patch 2~7: This series, including this cover-letter.
Use case: Patch 8~N: The old DICE part as an example.
[2] https://github.com/intel-staging/tdx/tree/tdx-module-ext
[3] https://lore.kernel.org/all/20260910104347.625378-1-yilun.xu@linux.intel.com/
Xu Yilun (5):
x86/virt/tdx: Move TDH.SYS.CONFIG operations into a wrapper
x86/virt/tdx: Configure add-on features on TDX module init
x86/virt/tdx: Detect if the extensions initialization is required
x86/virt/tdx: Add extra memory to TDX module for the extensions
x86/virt/tdx: Make TDX module initialize the extensions
arch/x86/include/asm/tdx.h | 1 +
arch/x86/include/asm/tdx_global_metadata.h | 6 +
arch/x86/virt/vmx/tdx/tdx.h | 2 +
arch/x86/virt/vmx/tdx/tdx.c | 230 +++++++++++++++++++-
arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 20 ++
5 files changed, 252 insertions(+), 7 deletions(-)
base-commit: 3bfade11ab7be0a90eecc8a549b181863960b14e
--
2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 1/5] x86/virt/tdx: Move TDH.SYS.CONFIG operations into a wrapper
2026-09-15 10:26 [PATCH v2 0/5] Enable TDX module extensions Xu Yilun
@ 2026-09-15 10:26 ` Xu Yilun
2026-09-15 20:45 ` Edgecombe, Rick P
2026-09-15 10:26 ` [PATCH v2 2/5] x86/virt/tdx: Configure add-on features on TDX module init Xu Yilun
` (4 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Xu Yilun @ 2026-09-15 10:26 UTC (permalink / raw)
To: x86, linux-coco, linux-kernel
Cc: kas, rick.p.edgecombe, yilun.xu, yilun.xu, xiaoyao.li,
sohil.mehta, adrian.hunter, kishen.maloor, tony.lindgren,
peter.fang, baolu.lu, zhenzhong.duan, chao.gao, artem.bityutskiy,
kvm, nik.borisov
In Linux, SEAMCALL wrappers are introduced to avoid broad SEAMCALL
access by exposing only a selection of SEAMCALL leafs, but also to
abstract the SEAMCALL register ABIs. The latter improves readability and
reuse for SEAMCALL leafs that are called multiple times.
Some SEAMCALL leafs are not explicitly wrapped because the level of TDX
ABI details needed to perform the call is low enough to flow well with
the calling code.
For some of the currently unwrapped SEAMCALL leafs, TDX architecture
adjusts the ABI and adds SEAMCALL version selection for backward
compatibility. Future kernel will need to support the changes. This will
leak more ABI details into the surrounding caller code and decrease
readability of the other logic. To keep the ABI details contained, move
the SEAMCALL leafs that will need version selection into wrappers.
The cleanest separation would be to have kernel data types for the
SEAMCALL wrapper arguments, and have them marshaled into SEAMCALL leaf
ABI types (often u64s) inside the wrapper. This works for many SEAMCALL
leafs but becomes cumbersome when the register ABI type is a physical
address which points to a buffer for an in-memory ABI. If the SEAMCALL
wrapper only accepts kernel data types, it may need duplicate buffer
allocation and copies to match the in-memory ABI. Another solution is
to define a named helper structure that mirrors the in-memory ABI,
populate it in a separate flow, then pass it to the SEAMCALL wrapper.
struct seamldr_params is an existing example of this pattern.
TDH.SYS.CONFIG requires a list of TDMR information in the form of a PA
array. The PA array is the in-memory ABI. Create a structure for the PA
array, use it as the argument when creating the wrapper for
TDH.SYS.CONFIG.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Nikolay Borisov <nik.borisov@suse.com>
---
v2:
- Remove TDH.SYS.UPDATE wrapper (Dave & Rick)
- Talk about the handling of in-memory ABIs for SEAMCALL wrappers
(Rick)
- Refactor the entire changelog according to Rick's suggestion (Rick)
- Add code comment for struct tdmr_info_pa_array (AI nitpicker)
- Use kernel data type for nr_tdmr_pa parameter (AI nitpicker)
v1:
- This patch is split out from the last series (Rick)
---
arch/x86/virt/vmx/tdx/tdx.c | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 1668f8615607..e06932f80395 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -998,11 +998,33 @@ static __init int construct_tdmrs(struct list_head *tmb_list,
return ret;
}
+/*
+ * This is an array of HPAs, each points to a TDMR_INFO data structure (see
+ * struct tdmr_info).
+ *
+ * It is the in-memory ABI that the kernel passes to the TDX module to specify
+ * the ranges of TD Memory Regions (TDMRs) and their associated PAMT memory.
+ */
+struct tdmr_info_pa_array {
+ DECLARE_FLEX_ARRAY(u64, phys);
+};
+
+static __init int tdx_sys_config(struct tdmr_info_pa_array *tdmr_pa_array,
+ unsigned int nr_tdmr_pa, u64 global_keyid)
+{
+ struct tdx_module_args args = {
+ .rcx = __pa(tdmr_pa_array),
+ .rdx = nr_tdmr_pa,
+ .r8 = global_keyid,
+ };
+
+ return seamcall_prerr(TDH_SYS_CONFIG, &args);
+}
+
static __init int config_tdx_module(struct tdmr_info_list *tdmr_list,
u64 global_keyid)
{
- struct tdx_module_args args = {};
- u64 *tdmr_pa_array;
+ struct tdmr_info_pa_array *tdmr_pa_array;
size_t array_sz;
int i, ret;
@@ -1021,12 +1043,10 @@ static __init int config_tdx_module(struct tdmr_info_list *tdmr_list,
return -ENOMEM;
for (i = 0; i < tdmr_list->nr_consumed_tdmrs; i++)
- tdmr_pa_array[i] = __pa(tdmr_entry(tdmr_list, i));
+ tdmr_pa_array->phys[i] = __pa(tdmr_entry(tdmr_list, i));
- args.rcx = __pa(tdmr_pa_array);
- args.rdx = tdmr_list->nr_consumed_tdmrs;
- args.r8 = global_keyid;
- ret = seamcall_prerr(TDH_SYS_CONFIG, &args);
+ ret = tdx_sys_config(tdmr_pa_array, tdmr_list->nr_consumed_tdmrs,
+ global_keyid);
/* Free the array as it is not required anymore. */
kfree(tdmr_pa_array);
--
2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 2/5] x86/virt/tdx: Configure add-on features on TDX module init
2026-09-15 10:26 [PATCH v2 0/5] Enable TDX module extensions Xu Yilun
2026-09-15 10:26 ` [PATCH v2 1/5] x86/virt/tdx: Move TDH.SYS.CONFIG operations into a wrapper Xu Yilun
@ 2026-09-15 10:26 ` Xu Yilun
2026-09-15 20:54 ` Edgecombe, Rick P
2026-09-16 3:23 ` Chao Gao
2026-09-15 10:26 ` [PATCH v2 3/5] x86/virt/tdx: Detect if the extensions initialization is required Xu Yilun
` (3 subsequent siblings)
5 siblings, 2 replies; 13+ messages in thread
From: Xu Yilun @ 2026-09-15 10:26 UTC (permalink / raw)
To: x86, linux-coco, linux-kernel
Cc: kas, rick.p.edgecombe, yilun.xu, yilun.xu, xiaoyao.li,
sohil.mehta, adrian.hunter, kishen.maloor, tony.lindgren,
peter.fang, baolu.lu, zhenzhong.duan, chao.gao, artem.bityutskiy,
kvm, nik.borisov
The TDX architecture identifies some features that are off by default
but can be enabled by the host during TDX module initialization. They
are classified as add-on features because enabling them affects existing
TDX systems: they may change existing feature behavior, or reserve more
memory.
The TDX module extends TDH.SYS.CONFIG with a new register argument to
specify which add-on features to enable. This new argument is a bitmap
that uses the same feature bits as TDX_FEATURES0. Note that Dynamic PAMT
is an exception: although it is an add-on feature, it is controlled via
a legacy, dedicated register argument [1].
The kernel needs to enable these add-on features when it supports them.
Add a get_tdx_addon_features0() helper to return the bitmap of the add-on
features that the module & kernel both support. Initially, this helper
returns 0. It will be updated to return specific feature bits as full
kernel support lands. Pass this extra bitmap to TDH.SYS.CONFIG wrapper.
The TDX module requires SEAMCALL leaf version 1 for TDH.SYS.CONFIG when
passing the new bitmap argument. A previous change [2] supports the
versioned SEAMCALL leafs by adding a "version" field in
struct tdx_module_args. Set the version field to 1 if the new bitmap
argument is used to enable any add-on feature, otherwise keep the
version as 0. This retains backward compatibility with older modules
that don't recognize version 1.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Link: https://lore.kernel.org/all/20260904215841.303070-10-rick.p.edgecombe@intel.com/ # [1]
Link: https://lore.kernel.org/all/20260722084634.131020-1-yilun.xu@linux.intel.com/ # [2]
---
v2:
- Don't pass addon_features0 parameter around, get it in the wrappers
(Dave)
- Remove TDH.SYS.UPDATE wrapper (Dave & Rick)
- Drop the changelog section explaining why add-on feature enabling is
needed in this series, as it is a generally understood pattern (Rick)
- Add a note in the changelog that Dynamic PAMT is not enabled via the
new bitmap argument (Rick)
- Add __init tag for get_tdx_addon_features0() (AI nitpicker)
v1:
- Use tdx_module_args.version to assign SEAMCALL leaf versions (Dave)
- Remove DICE specific descriptions (Rick)
- Remove the global var tdx_addon_features0 (Chao)
- Add a Macro to collect kernel supported add-on feature bits (Rick)
- Changelog & code comments change
---
arch/x86/virt/vmx/tdx/tdx.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index e06932f80395..763c2d1b25d0 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -998,6 +998,15 @@ static __init int construct_tdmrs(struct list_head *tmb_list,
return ret;
}
+/* List all kernel supported add-on features0 bits here */
+#define TDX_KERNEL_SUPPORTED_ADDON_FEATURES0 (0)
+
+static __init u64 get_tdx_addon_features0(void)
+{
+ return tdx_sysinfo.features.tdx_features0 &
+ TDX_KERNEL_SUPPORTED_ADDON_FEATURES0;
+}
+
/*
* This is an array of HPAs, each points to a TDMR_INFO data structure (see
* struct tdmr_info).
@@ -1012,12 +1021,22 @@ struct tdmr_info_pa_array {
static __init int tdx_sys_config(struct tdmr_info_pa_array *tdmr_pa_array,
unsigned int nr_tdmr_pa, u64 global_keyid)
{
+ u64 addon_features0 = get_tdx_addon_features0();
struct tdx_module_args args = {
.rcx = __pa(tdmr_pa_array),
.rdx = nr_tdmr_pa,
.r8 = global_keyid,
};
+ /*
+ * Use SEAMCALL version 1 that supports add-on features if any are
+ * requested. Otherwise use version 0 for backward compatibility.
+ */
+ if (addon_features0) {
+ args.r9 = addon_features0;
+ args.version = 1;
+ }
+
return seamcall_prerr(TDH_SYS_CONFIG, &args);
}
--
2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 3/5] x86/virt/tdx: Detect if the extensions initialization is required
2026-09-15 10:26 [PATCH v2 0/5] Enable TDX module extensions Xu Yilun
2026-09-15 10:26 ` [PATCH v2 1/5] x86/virt/tdx: Move TDH.SYS.CONFIG operations into a wrapper Xu Yilun
2026-09-15 10:26 ` [PATCH v2 2/5] x86/virt/tdx: Configure add-on features on TDX module init Xu Yilun
@ 2026-09-15 10:26 ` Xu Yilun
2026-09-15 21:14 ` Edgecombe, Rick P
2026-09-15 10:26 ` [PATCH v2 4/5] x86/virt/tdx: Add extra memory to TDX module for the extensions Xu Yilun
` (2 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Xu Yilun @ 2026-09-15 10:26 UTC (permalink / raw)
To: x86, linux-coco, linux-kernel
Cc: kas, rick.p.edgecombe, yilun.xu, yilun.xu, xiaoyao.li,
sohil.mehta, adrian.hunter, kishen.maloor, tony.lindgren,
peter.fang, baolu.lu, zhenzhong.duan, chao.gao, artem.bityutskiy,
kvm, nik.borisov
Some add-on features require TDX module extensions. The TDX module
provides a metadata field "ext_required" to indicate this requirement.
Add the first step of TDX module extensions initialization by detecting
if the extensions are required:
1. Check if the extensions are supported via TDX_FEATURES0_EXT. If
not, ext_required is not readable.
2. Check if any TDX feature needs the extensions via ext_required.
Skip the extensions initialization when it is not required.
Currently all metadata fields are read at the very beginning of TDX
module initialization. However, ext_required is only valid after the
add-on feature configuration, so it cannot use the existing metadata
reading method.
Add a dedicated metadata reading interface for the extensions, call it
after add-on feature configuration.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Tony Lindgren <tony.lindgren@linux.intel.com>
---
v1:
- Include struct tdx_sys_info_ext in struct tdx_sys_info.
---
arch/x86/include/asm/tdx.h | 1 +
arch/x86/include/asm/tdx_global_metadata.h | 5 ++++
arch/x86/virt/vmx/tdx/tdx.c | 28 +++++++++++++++++++++
arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 14 +++++++++++
4 files changed, 48 insertions(+)
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 89e97d5761d8..6657f2db0330 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -36,6 +36,7 @@
/* Bit definitions of TDX_FEATURES0 metadata field */
#define TDX_FEATURES0_TD_PRESERVING BIT_ULL(1)
#define TDX_FEATURES0_NO_RBP_MOD BIT_ULL(18)
+#define TDX_FEATURES0_EXT BIT_ULL(39)
#ifndef __ASSEMBLER__
diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h
index 41150d546589..fe3fe91de71f 100644
--- a/arch/x86/include/asm/tdx_global_metadata.h
+++ b/arch/x86/include/asm/tdx_global_metadata.h
@@ -44,12 +44,17 @@ struct tdx_sys_info_handoff {
u16 module_hv;
};
+struct tdx_sys_info_ext {
+ bool ext_required;
+};
+
struct tdx_sys_info {
struct tdx_sys_info_version version;
struct tdx_sys_info_features features;
struct tdx_sys_info_tdmr tdmr;
struct tdx_sys_info_td_ctrl td_ctrl;
struct tdx_sys_info_td_conf td_conf;
+ struct tdx_sys_info_ext ext;
};
#endif
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 763c2d1b25d0..916a8906da10 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1181,6 +1181,30 @@ static __init int init_tdmrs(struct tdmr_info_list *tdmr_list)
return 0;
}
+static __init int init_tdx_module_extensions(void)
+{
+ int ret;
+
+ if (!(tdx_sysinfo.features.tdx_features0 & TDX_FEATURES0_EXT))
+ return 0;
+
+ ret = get_tdx_sys_info_ext(&tdx_sysinfo.ext);
+ if (ret)
+ return ret;
+
+ /*
+ * ext_required indicates if any add-on features requiring TDX module
+ * extensions are configured via TDH.SYS.CONFIG. If none, skip the
+ * initialization.
+ */
+ if (!tdx_sysinfo.ext.ext_required)
+ return 0;
+
+ /* TODO: add the extensions enabling steps here */
+
+ return 0;
+}
+
static __init int init_tdx_module(void)
{
int ret;
@@ -1235,6 +1259,10 @@ static __init int init_tdx_module(void)
if (ret)
goto err_reset_pamts;
+ ret = init_tdx_module_extensions();
+ if (ret)
+ goto err_reset_pamts;
+
pr_info("%lu KB allocated for PAMT\n", tdmrs_count_pamt_kb(&tdx_tdmr_list));
out_put_tdxmem:
diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
index e49c300f23d4..b9e1c011a990 100644
--- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
+++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
@@ -131,3 +131,17 @@ static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
return ret;
}
+
+static __init int get_tdx_sys_info_ext(struct tdx_sys_info_ext *sysinfo_ext)
+{
+ int ret;
+ u64 val;
+
+ ret = read_sys_metadata_field(0x3100000000000001, &val);
+ if (ret)
+ return ret;
+
+ sysinfo_ext->ext_required = val;
+
+ return 0;
+}
--
2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 4/5] x86/virt/tdx: Add extra memory to TDX module for the extensions
2026-09-15 10:26 [PATCH v2 0/5] Enable TDX module extensions Xu Yilun
` (2 preceding siblings ...)
2026-09-15 10:26 ` [PATCH v2 3/5] x86/virt/tdx: Detect if the extensions initialization is required Xu Yilun
@ 2026-09-15 10:26 ` Xu Yilun
2026-09-15 21:19 ` Edgecombe, Rick P
2026-09-16 7:40 ` Chao Gao
2026-09-15 10:26 ` [PATCH v2 5/5] x86/virt/tdx: Make TDX module initialize " Xu Yilun
2026-09-15 22:09 ` [PATCH v2 0/5] Enable TDX module extensions Edgecombe, Rick P
5 siblings, 2 replies; 13+ messages in thread
From: Xu Yilun @ 2026-09-15 10:26 UTC (permalink / raw)
To: x86, linux-coco, linux-kernel
Cc: kas, rick.p.edgecombe, yilun.xu, yilun.xu, xiaoyao.li,
sohil.mehta, adrian.hunter, kishen.maloor, tony.lindgren,
peter.fang, baolu.lu, zhenzhong.duan, chao.gao, artem.bityutskiy,
kvm, nik.borisov
TDX module extensions need memory for their execution environment to
serve SEAMCALL leafs. The TDX architecture implements the extensions
in such a way that they use the memory outside of SEAM range, so the
kernel should add the memory upfront at initialization time.
Introduce a new memory adding process backed by a new SEAMCALL leaf
TDH.EXT.MEM.ADD. The kernel queries TDX module how much memory needed,
allocates it, add it to the module, and never gets it back.
The TDX module accepts the memory in the form of an HPA array. This
array is passed via a single 64-bit SEAMCALL leaf parameter, which
encodes two values: the PFN of the container page holding the array, and
the number of entries in the array. Create a helper to encode this
format and name it after the TDX module term: HPA_LIST_INFO.
TDX module extensions consume tens of megabytes memory that will never
be returned to the host. Use contiguous page allocation to isolate these
large blocks entirely. This is a simple way to avoid permanent memory
fragmentation: requiring the full size to be contiguous is more
expensive than needed but should be good during the boot time. Print the
allocation amount on TDX module extensions initialization for
visibility.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
---
An alternative solution is to use a loop that gives memory on a memory
error code - TDX_EXT_MEMORY_POOL_REQUIRED, add one page per iteration
until TDH.EXT.INIT succeeds. Something like:
do {
ret = tdh_sys_init();
if (ret == TDX_EXT_MEMORY_POOL_REQUIRED)
tdh_ext_mem_add(); //single page
} while (ret == TDX_EXT_MEMORY_POOL_REQUIRED);
This approach is slightly simpler as we don't have to query the module
for the total memory, no memory pre-allocation or segmentation math.
But allocating a single 4K page per iteration may cause permanent memory
fragmentation.
v2:
- Add code comments for container page allocation (Kiryl)
- Adjust comments/changelog for contiguous allocation of extensions
memory (Kiryl)
- s/PFN array/HPA array in commit log (Kiryl)
- State the alternative solution (Rick)
- Add code comment for struct tdx_hpa_list
v1:
- Fix return value for SEAMCALL wrappers (Chao)
- Print SEAMCALL error code for SEAMCALL wrappers (Xiaoyao)
- Rename local vars to make the ext memory adding loop clear (Rick)
- Remove input parameters for tdx_ext_mem_setup() (Kevin)
- Add a Macro for tdh_hpa_list size.
- Change the SEAMALL wrapper parameter type,
struct page *hpa_list => struct tdx_hpa_list *hpa_list
- changelog & code comments
---
arch/x86/include/asm/tdx_global_metadata.h | 1 +
arch/x86/virt/vmx/tdx/tdx.h | 1 +
arch/x86/virt/vmx/tdx/tdx.c | 134 +++++++++++++++++++-
arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 6 +
4 files changed, 139 insertions(+), 3 deletions(-)
diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h
index fe3fe91de71f..43b8761c0854 100644
--- a/arch/x86/include/asm/tdx_global_metadata.h
+++ b/arch/x86/include/asm/tdx_global_metadata.h
@@ -45,6 +45,7 @@ struct tdx_sys_info_handoff {
};
struct tdx_sys_info_ext {
+ u32 memory_pool_required_pages;
bool ext_required;
};
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index 63e3acfb5d0c..52888424fe7d 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -48,6 +48,7 @@
#define TDH_SYS_CONFIG 45
#define TDH_SYS_SHUTDOWN 52
#define TDH_SYS_UPDATE 53
+#define TDH_EXT_MEM_ADD 61
#define TDH_SYS_DISABLE 69
/* TDX page types */
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 916a8906da10..3cdc5ba8e2ad 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1181,6 +1181,136 @@ static __init int init_tdmrs(struct tdmr_info_list *tdmr_list)
return 0;
}
+#define TDX_HPA_LIST_MAX_NR_PAGES (PAGE_SIZE / sizeof(u64))
+
+/*
+ * This is the "HPA_LIST" data structure defined in the "Intel TDX Module ABI
+ * Specification".
+ *
+ * It is the in-memory ABI that the kernel uses to add memory to the TDX
+ * module.
+ */
+struct tdx_hpa_list {
+ u64 phys[TDX_HPA_LIST_MAX_NR_PAGES];
+};
+
+static_assert(sizeof(struct tdx_hpa_list) == PAGE_SIZE);
+
+#define HPA_LIST_INFO_FIRST_ENTRY GENMASK_U64(11, 3)
+#define HPA_LIST_INFO_PFN GENMASK_U64(51, 12)
+#define HPA_LIST_INFO_LAST_ENTRY GENMASK_U64(63, 55)
+
+static __init u64 to_hpa_list_info(struct tdx_hpa_list *hpa_list,
+ unsigned int nr_pages)
+{
+ return FIELD_PREP(HPA_LIST_INFO_FIRST_ENTRY, 0) |
+ FIELD_PREP(HPA_LIST_INFO_PFN, PFN_DOWN(__pa(hpa_list))) |
+ FIELD_PREP(HPA_LIST_INFO_LAST_ENTRY, nr_pages - 1);
+}
+
+static __init int tdx_ext_mem_add(struct tdx_hpa_list *hpa_list,
+ unsigned int nr_pages)
+{
+ struct tdx_module_args args = {
+ .rcx = to_hpa_list_info(hpa_list, nr_pages),
+ };
+ u64 ret;
+
+ do {
+ /*
+ * The TDX module overwrites RCX to track progress when this
+ * SEAMCALL leaf is interrupted. Use seamcall_ret() to save and
+ * pass the updated value back on retry.
+ */
+ ret = seamcall_ret(TDH_EXT_MEM_ADD, &args);
+ } while (ret == TDX_INTERRUPTED_RESUMABLE);
+
+ if (ret != TDX_SUCCESS) {
+ pr_err("TDH.EXT.MEM.ADD failed: 0x%016llx\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static __init int tdx_ext_mem_setup(void)
+{
+ unsigned int required_pages = tdx_sysinfo.ext.memory_pool_required_pages;
+ struct tdx_hpa_list *hpa_list;
+ unsigned int added_pages;
+ struct page *page;
+ int ret;
+
+ /*
+ * TDX module uses the metadata memory_pool_required_pages to indicate
+ * how much memory is still needed. This value decreases each time
+ * memory is added via TDH.EXT.MEM.ADD.
+ *
+ * On first time initialization, a value of 0 before any memory is
+ * added is unusual. But host makes no assumptions. Skip the memory
+ * setup and let subsequent steps catch any actual errors.
+ */
+ if (!required_pages)
+ return 0;
+
+ /*
+ * Allocate the container page for the HPA_LIST. tdx_hpa_list is
+ * guaranteed to be page-sized by static_assert(), so kzalloc()
+ * guarantees the page alignment.
+ */
+ hpa_list = kzalloc_obj(*hpa_list);
+ if (!hpa_list)
+ return -ENOMEM;
+
+ /*
+ * Memory for TDX module extensions is never reclaimed and can be tens
+ * of megabytes. Allocating a physically contiguous chunk is a simple
+ * way to avoid permanent memory fragmentation: requiring the full size
+ * to be contiguous is more expensive than needed but should be good
+ * during the boot time.
+ */
+ page = alloc_contig_pages(required_pages, GFP_KERNEL, numa_mem_id(),
+ &node_online_map);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out_free_hpa_list;
+ }
+
+ added_pages = 0;
+ while (added_pages < required_pages) {
+ unsigned int chunk_pages = min(required_pages - added_pages,
+ TDX_HPA_LIST_MAX_NR_PAGES);
+ struct page *chunk = page + added_pages;
+ unsigned int i;
+
+ for (i = 0; i < chunk_pages; i++)
+ hpa_list->phys[i] = page_to_phys(chunk + i);
+
+ ret = tdx_ext_mem_add(hpa_list, chunk_pages);
+ if (ret) {
+ /*
+ * This SEAMCALL leaf shouldn't fail, and if it does,
+ * things are broken enough that complex error handling
+ * isn't worth it. Intentionally leak all pages,
+ * including un-added pages.
+ */
+ WARN(1, "Fatal: TDX module rejected memory for extensions, stranded all pages\n");
+ break;
+ }
+
+ added_pages += chunk_pages;
+ }
+
+ /* Print the amount so users know the cost. */
+ pr_info("%lu KB allocated for TDX module extensions\n",
+ required_pages * PAGE_SIZE / 1024);
+
+out_free_hpa_list:
+ kfree(hpa_list);
+
+ return ret;
+}
+
static __init int init_tdx_module_extensions(void)
{
int ret;
@@ -1200,9 +1330,7 @@ static __init int init_tdx_module_extensions(void)
if (!tdx_sysinfo.ext.ext_required)
return 0;
- /* TODO: add the extensions enabling steps here */
-
- return 0;
+ return tdx_ext_mem_setup();
}
static __init int init_tdx_module(void)
diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
index b9e1c011a990..720cdaf76492 100644
--- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
+++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
@@ -137,6 +137,12 @@ static __init int get_tdx_sys_info_ext(struct tdx_sys_info_ext *sysinfo_ext)
int ret;
u64 val;
+ ret = read_sys_metadata_field(0x3100000200000000, &val);
+ if (ret)
+ return ret;
+
+ sysinfo_ext->memory_pool_required_pages = val;
+
ret = read_sys_metadata_field(0x3100000000000001, &val);
if (ret)
return ret;
--
2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 5/5] x86/virt/tdx: Make TDX module initialize the extensions
2026-09-15 10:26 [PATCH v2 0/5] Enable TDX module extensions Xu Yilun
` (3 preceding siblings ...)
2026-09-15 10:26 ` [PATCH v2 4/5] x86/virt/tdx: Add extra memory to TDX module for the extensions Xu Yilun
@ 2026-09-15 10:26 ` Xu Yilun
2026-09-15 22:09 ` [PATCH v2 0/5] Enable TDX module extensions Edgecombe, Rick P
5 siblings, 0 replies; 13+ messages in thread
From: Xu Yilun @ 2026-09-15 10:26 UTC (permalink / raw)
To: x86, linux-coco, linux-kernel
Cc: kas, rick.p.edgecombe, yilun.xu, yilun.xu, xiaoyao.li,
sohil.mehta, adrian.hunter, kishen.maloor, tony.lindgren,
peter.fang, baolu.lu, zhenzhong.duan, chao.gao, artem.bityutskiy,
kvm, nik.borisov
TDX module extensions need memory for their execution environment
to serve SEAMCALL leafs. Several add-on features depend on the
extensions to execute their SEAMCALL leafs.
After providing all required memory to the TDX module, initialize TDX
module extensions via TDH.EXT.INIT, then those add-on features can use
their SEAMCALL leafs normally.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Reviewed-by: Tony Lindgren <tony.lindgren@linux.intel.com>
Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
---
v1:
- Fix return value for SEAMCALL wrappers (Chao)
- Print SEAMCALL error code for SEAMCALL wrappers (Xiaoyao)
- Changelog & code comments
---
arch/x86/virt/vmx/tdx/tdx.h | 1 +
arch/x86/virt/vmx/tdx/tdx.c | 23 ++++++++++++++++++++++-
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index 52888424fe7d..1f43d2eb2345 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -48,6 +48,7 @@
#define TDH_SYS_CONFIG 45
#define TDH_SYS_SHUTDOWN 52
#define TDH_SYS_UPDATE 53
+#define TDH_EXT_INIT 60
#define TDH_EXT_MEM_ADD 61
#define TDH_SYS_DISABLE 69
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 3cdc5ba8e2ad..2d42191a56e6 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1311,6 +1311,23 @@ static __init int tdx_ext_mem_setup(void)
return ret;
}
+static __init int tdx_ext_init(void)
+{
+ struct tdx_module_args args = {};
+ u64 ret;
+
+ do {
+ ret = seamcall(TDH_EXT_INIT, &args);
+ } while (ret == TDX_INTERRUPTED_RESUMABLE);
+
+ if (ret != TDX_SUCCESS) {
+ pr_err("TDH.EXT.INIT failed: 0x%016llx\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static __init int init_tdx_module_extensions(void)
{
int ret;
@@ -1330,7 +1347,11 @@ static __init int init_tdx_module_extensions(void)
if (!tdx_sysinfo.ext.ext_required)
return 0;
- return tdx_ext_mem_setup();
+ ret = tdx_ext_mem_setup();
+ if (ret)
+ return ret;
+
+ return tdx_ext_init();
}
static __init int init_tdx_module(void)
--
2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/5] x86/virt/tdx: Move TDH.SYS.CONFIG operations into a wrapper
2026-09-15 10:26 ` [PATCH v2 1/5] x86/virt/tdx: Move TDH.SYS.CONFIG operations into a wrapper Xu Yilun
@ 2026-09-15 20:45 ` Edgecombe, Rick P
0 siblings, 0 replies; 13+ messages in thread
From: Edgecombe, Rick P @ 2026-09-15 20:45 UTC (permalink / raw)
To: linux-coco, linux-kernel, yilun.xu, x86
Cc: Gao, Chao, Xu, Yilun, Duan, Zhenzhong, kas, baolu.lu, Li,
Xiaoyao, Maloor, Kishen, Hunter, Adrian, tony.lindgren, Mehta,
Sohil, Fang, Peter, nik.borisov, kvm, artem.bityutskiy
On Tue, 2026-09-15 at 18:26 +0800, Xu Yilun wrote:
> In Linux, SEAMCALL wrappers are introduced to avoid broad SEAMCALL
> access by exposing only a selection of SEAMCALL leafs, but also to
> abstract the SEAMCALL register ABIs.
>
> The latter improves readability and
> reuse for SEAMCALL leafs that are called multiple times.
I'm trying to adjust to not using former/latter. The feedback I've seen is that
it is too much to remember as you read along. How about:
The abstraction improves...
>
> Some SEAMCALL leafs are not explicitly wrapped because the level of TDX
> ABI details needed to perform the call is low enough to flow well with
^are
> the calling code.
>
> For some of the currently unwrapped SEAMCALL leafs, TDX architecture
> adjusts the ABI and adds SEAMCALL version selection for backward
> compatibility.
>
Reads a little weird to me. Like I'm not sure when this adjusting is happening.
How about:
..., the TDX architecture has evolved the ABI to introduce new versions of
existing SEAMCALLs. VMM code can select the version to call based on what is
supported by the loaded TDX module.
> Future kernel will need to support the changes.
>
?? I guess you mean selecting between SEAMCALL versions?
> This will
> leak more ABI details into the surrounding caller code and decrease
> readability of the other logic. To keep the ABI details contained, move
> the SEAMCALL leafs that will need version selection into wrappers.
>
> The cleanest separation would be to have kernel data types for the
> SEAMCALL wrapper arguments,
>
This is now talking about general seamcall wrapper design. It could read like
it's instead talking about clean separation of SEAMCALL versions?
> and have them marshaled into SEAMCALL leaf
> ABI types (often u64s) inside the wrapper. This works for many SEAMCALL
> leafs but becomes cumbersome when the register ABI type is a physical
> address which points to a buffer for an in-memory ABI. If the SEAMCALL
> wrapper only accepts kernel data types, it may need duplicate buffer
> allocation and copies to match the in-memory ABI. Another solution is
> to define a named helper structure that mirrors the in-memory ABI,
> populate it in a separate flow, then pass it to the SEAMCALL wrapper.
> struct seamldr_params is an existing example of this pattern.
>
> TDH.SYS.CONFIG requires a list of TDMR information in the form of a PA
> array. The PA array is the in-memory ABI. Create a structure for the PA
> array, use it as the argument when creating the wrapper for
> TDH.SYS.CONFIG.
>
> Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
> Reviewed-by: Nikolay Borisov <nik.borisov@suse.com>
> ---
> v2:
> - Remove TDH.SYS.UPDATE wrapper (Dave & Rick)
> - Talk about the handling of in-memory ABIs for SEAMCALL wrappers
> (Rick)
> - Refactor the entire changelog according to Rick's suggestion (Rick)
> - Add code comment for struct tdmr_info_pa_array (AI nitpicker)
> - Use kernel data type for nr_tdmr_pa parameter (AI nitpicker)
>
> v1:
> - This patch is split out from the last series (Rick)
> ---
> arch/x86/virt/vmx/tdx/tdx.c | 34 +++++++++++++++++++++++++++-------
> 1 file changed, 27 insertions(+), 7 deletions(-)
>
> diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> index 1668f8615607..e06932f80395 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.c
> +++ b/arch/x86/virt/vmx/tdx/tdx.c
> @@ -998,11 +998,33 @@ static __init int construct_tdmrs(struct list_head *tmb_list,
> return ret;
> }
>
> +/*
> + * This is an array of HPAs, each points to a TDMR_INFO data structure (see
> + * struct tdmr_info).
> + *
> + * It is the in-memory ABI that the kernel passes to the TDX module to specify
> + * the ranges of TD Memory Regions (TDMRs) and their associated PAMT memory.
> + */
> +struct tdmr_info_pa_array {
> + DECLARE_FLEX_ARRAY(u64, phys);
> +};
> +
> +static __init int tdx_sys_config(struct tdmr_info_pa_array *tdmr_pa_array,
> + unsigned int nr_tdmr_pa, u64 global_keyid)
> +{
> + struct tdx_module_args args = {
> + .rcx = __pa(tdmr_pa_array),
> + .rdx = nr_tdmr_pa,
> + .r8 = global_keyid,
> + };
> +
> + return seamcall_prerr(TDH_SYS_CONFIG, &args);
> +}
> +
> static __init int config_tdx_module(struct tdmr_info_list *tdmr_list,
> u64 global_keyid)
> {
> - struct tdx_module_args args = {};
> - u64 *tdmr_pa_array;
> + struct tdmr_info_pa_array *tdmr_pa_array;
> size_t array_sz;
> int i, ret;
>
> @@ -1021,12 +1043,10 @@ static __init int config_tdx_module(struct tdmr_info_list *tdmr_list,
> return -ENOMEM;
Outside the diff it has:
array_sz = tdmr_list->nr_consumed_tdmrs * sizeof(u64);
Could be now changed to:
array_sz = tdmr_list->nr_consumed_tdmrs * sizeof(*tdmr_pa_array->phys);
A bit of existing cleanup, but the u64 is especially tucked away compared to
before, so I'd argue its maintaining readability of the existing code.
>
> for (i = 0; i < tdmr_list->nr_consumed_tdmrs; i++)
> - tdmr_pa_array[i] = __pa(tdmr_entry(tdmr_list, i));
> + tdmr_pa_array->phys[i] = __pa(tdmr_entry(tdmr_list, i));
>
> - args.rcx = __pa(tdmr_pa_array);
> - args.rdx = tdmr_list->nr_consumed_tdmrs;
> - args.r8 = global_keyid;
> - ret = seamcall_prerr(TDH_SYS_CONFIG, &args);
> + ret = tdx_sys_config(tdmr_pa_array, tdmr_list->nr_consumed_tdmrs,
> + global_keyid);
>
> /* Free the array as it is not required anymore. */
> kfree(tdmr_pa_array);
> --
> 2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/5] x86/virt/tdx: Configure add-on features on TDX module init
2026-09-15 10:26 ` [PATCH v2 2/5] x86/virt/tdx: Configure add-on features on TDX module init Xu Yilun
@ 2026-09-15 20:54 ` Edgecombe, Rick P
2026-09-16 3:23 ` Chao Gao
1 sibling, 0 replies; 13+ messages in thread
From: Edgecombe, Rick P @ 2026-09-15 20:54 UTC (permalink / raw)
To: linux-coco, linux-kernel, yilun.xu, x86
Cc: Gao, Chao, Xu, Yilun, Duan, Zhenzhong, kas, baolu.lu, Li,
Xiaoyao, Maloor, Kishen, Hunter, Adrian, tony.lindgren, Mehta,
Sohil, Fang, Peter, nik.borisov, kvm, artem.bityutskiy
On Tue, 2026-09-15 at 18:26 +0800, Xu Yilun wrote:
> @@ -1012,12 +1021,22 @@ struct tdmr_info_pa_array {
> static __init int tdx_sys_config(struct tdmr_info_pa_array *tdmr_pa_array,
> unsigned int nr_tdmr_pa, u64 global_keyid)
> {
> + u64 addon_features0 = get_tdx_addon_features0();
> struct tdx_module_args args = {
> .rcx = __pa(tdmr_pa_array),
> .rdx = nr_tdmr_pa,
> .r8 = global_keyid,
> };
It seems inconsistent that global_keyid is passed in even though it is basically
global state, while addon_features0 is retrieved from the global state. Passing
the result of get_tdx_addon_features0() in from the caller doesn't make a ton of
sense. And we already reference tdx_global_keyid globally from another seamcall
wrapper. So I'd think dropping the arg when you create the wrapper would be best
in the end.
The smallest change to stay consistent would be go add an addon_features0 arg to
tdx_sys_config(), but it's hard to justify as good code. I'd maybe go with the
global_keyid change and it probably is a separate patch, but we are brushing up
against mixing cleanup and feature enabling... thoughts?
>
> + /*
> + * Use SEAMCALL version 1 that supports add-on features if any are
> + * requested. Otherwise use version 0 for backward compatibility.
> + */
> + if (addon_features0) {
> + args.r9 = addon_features0;
> + args.version = 1;
> + }
> +
> return seamcall_prerr(TDH_SYS_CONFIG, &args);
> }
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 3/5] x86/virt/tdx: Detect if the extensions initialization is required
2026-09-15 10:26 ` [PATCH v2 3/5] x86/virt/tdx: Detect if the extensions initialization is required Xu Yilun
@ 2026-09-15 21:14 ` Edgecombe, Rick P
0 siblings, 0 replies; 13+ messages in thread
From: Edgecombe, Rick P @ 2026-09-15 21:14 UTC (permalink / raw)
To: linux-coco, linux-kernel, yilun.xu, x86
Cc: Gao, Chao, Xu, Yilun, Duan, Zhenzhong, kas, baolu.lu, Li,
Xiaoyao, Maloor, Kishen, Hunter, Adrian, tony.lindgren, Mehta,
Sohil, Fang, Peter, nik.borisov, kvm, artem.bityutskiy
On Tue, 2026-09-15 at 18:26 +0800, Xu Yilun wrote:
> Some add-on features require TDX module extensions. The TDX module
> provides a metadata field "ext_required" to indicate this requirement.
>
> Add the first step of TDX module extensions initialization by detecting
> if the extensions are required:
>
> 1. Check if the extensions are supported via TDX_FEATURES0_EXT. If
> not, ext_required is not readable.
> 2. Check if any TDX feature needs the extensions via ext_required.
>
> Skip the extensions initialization when it is not required.
>
> Currently all metadata fields are read at the very beginning of TDX
> module initialization. However, ext_required is only valid after the
> add-on feature configuration, so it cannot use the existing metadata
> reading method.
>
> Add a dedicated metadata reading interface for the extensions, call it
> after add-on feature configuration.
>
> Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
> Reviewed-by: Tony Lindgren <tony.lindgren@linux.intel.com>
> ---
> v1:
> - Include struct tdx_sys_info_ext in struct tdx_sys_info.
> ---
> arch/x86/include/asm/tdx.h | 1 +
> arch/x86/include/asm/tdx_global_metadata.h | 5 ++++
> arch/x86/virt/vmx/tdx/tdx.c | 28 +++++++++++++++++++++
> arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 14 +++++++++++
> 4 files changed, 48 insertions(+)
>
> diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
> index 89e97d5761d8..6657f2db0330 100644
> --- a/arch/x86/include/asm/tdx.h
> +++ b/arch/x86/include/asm/tdx.h
> @@ -36,6 +36,7 @@
> /* Bit definitions of TDX_FEATURES0 metadata field */
> #define TDX_FEATURES0_TD_PRESERVING BIT_ULL(1)
> #define TDX_FEATURES0_NO_RBP_MOD BIT_ULL(18)
> +#define TDX_FEATURES0_EXT BIT_ULL(39)
>
> #ifndef __ASSEMBLER__
>
> diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h
> index 41150d546589..fe3fe91de71f 100644
> --- a/arch/x86/include/asm/tdx_global_metadata.h
> +++ b/arch/x86/include/asm/tdx_global_metadata.h
> @@ -44,12 +44,17 @@ struct tdx_sys_info_handoff {
> u16 module_hv;
> };
>
> +struct tdx_sys_info_ext {
> + bool ext_required;
> +};
> +
> struct tdx_sys_info {
> struct tdx_sys_info_version version;
> struct tdx_sys_info_features features;
> struct tdx_sys_info_tdmr tdmr;
> struct tdx_sys_info_td_ctrl td_ctrl;
> struct tdx_sys_info_td_conf td_conf;
> + struct tdx_sys_info_ext ext;
> };
>
> #endif
> diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> index 763c2d1b25d0..916a8906da10 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.c
> +++ b/arch/x86/virt/vmx/tdx/tdx.c
> @@ -1181,6 +1181,30 @@ static __init int init_tdmrs(struct tdmr_info_list *tdmr_list)
> return 0;
> }
>
> +static __init int init_tdx_module_extensions(void)
> +{
> + int ret;
> +
> + if (!(tdx_sysinfo.features.tdx_features0 & TDX_FEATURES0_EXT))
> + return 0;
> +
> + ret = get_tdx_sys_info_ext(&tdx_sysinfo.ext);
> + if (ret)
> + return ret;
> +
> + /*
> + * ext_required indicates if any add-on features requiring TDX module
> + * extensions are configured via TDH.SYS.CONFIG. If none, skip the
> + * initialization.
> + */
> + if (!tdx_sysinfo.ext.ext_required)
> + return 0;
Hmm, now that we don't need to do this on update, we don't need to save
ext.ext_required metadata.
Also, per:
https://lore.kernel.org/all/aox8DgpnSnhZRfCv@yilunxu-OptiPlex-7050/
the reason we even need to check anything is because TDH_EXT_INIT returns a
strange error code TDX_EXT_MEMORY_POOL_REQUIRED. In the case of a TDX module
that supports extensions, but none that the kernel knows about... what if
TDH_EXT_INIT returned SUCCESS meaning that all requested extensions (0 of them)
are initialized.
AFAICT the specs don't describe what to do in this situation. But does describe
SUCCESS as a possible error code. And TDX_EXT_MEMORY_POOL_REQUIRED is wrong. So
its probably the most sensible bug fix anyway.
Then basically this patch goes away? We still check TDX_FEATURES0_EXT, but it
can be squashed into the other one?
> +
> + /* TODO: add the extensions enabling steps here */
> +
> + return 0;
> +}
> +
> static __init int init_tdx_module(void)
> {
> int ret;
> @@ -1235,6 +1259,10 @@ static __init int init_tdx_module(void)
> if (ret)
> goto err_reset_pamts;
>
> + ret = init_tdx_module_extensions();
> + if (ret)
> + goto err_reset_pamts;
> +
> pr_info("%lu KB allocated for PAMT\n", tdmrs_count_pamt_kb(&tdx_tdmr_list));
>
> out_put_tdxmem:
> diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
> index e49c300f23d4..b9e1c011a990 100644
> --- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
> +++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
> @@ -131,3 +131,17 @@ static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
>
> return ret;
> }
> +
> +static __init int get_tdx_sys_info_ext(struct tdx_sys_info_ext *sysinfo_ext)
> +{
> + int ret;
> + u64 val;
> +
> + ret = read_sys_metadata_field(0x3100000000000001, &val);
> + if (ret)
> + return ret;
> +
> + sysinfo_ext->ext_required = val;
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 4/5] x86/virt/tdx: Add extra memory to TDX module for the extensions
2026-09-15 10:26 ` [PATCH v2 4/5] x86/virt/tdx: Add extra memory to TDX module for the extensions Xu Yilun
@ 2026-09-15 21:19 ` Edgecombe, Rick P
2026-09-16 7:40 ` Chao Gao
1 sibling, 0 replies; 13+ messages in thread
From: Edgecombe, Rick P @ 2026-09-15 21:19 UTC (permalink / raw)
To: linux-coco, linux-kernel, yilun.xu, x86
Cc: Gao, Chao, Xu, Yilun, Duan, Zhenzhong, kas, baolu.lu, Li,
Xiaoyao, Maloor, Kishen, Hunter, Adrian, tony.lindgren, Mehta,
Sohil, Fang, Peter, nik.borisov, kvm, artem.bityutskiy
Hey Kiryl,
On Tue, 2026-09-15 at 18:26 +0800, Xu Yilun wrote:
> An alternative solution is to use a loop that gives memory on a memory
> error code - TDX_EXT_MEMORY_POOL_REQUIRED, add one page per iteration
> until TDH.EXT.INIT succeeds. Something like:
>
> do {
> ret = tdh_sys_init();
> if (ret == TDX_EXT_MEMORY_POOL_REQUIRED)
> tdh_ext_mem_add(); //single page
> } while (ret == TDX_EXT_MEMORY_POOL_REQUIRED);
>
> This approach is slightly simpler as we don't have to query the module
> for the total memory, no memory pre-allocation or segmentation math.
> But allocating a single 4K page per iteration may cause permanent memory
> fragmentation.
So this is definitely a bad thing to do? Even if this is running at boot before
much fragmentation could have happened? Don't early boot allocations tend to be
more physically contiguous?
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 0/5] Enable TDX module extensions
2026-09-15 10:26 [PATCH v2 0/5] Enable TDX module extensions Xu Yilun
` (4 preceding siblings ...)
2026-09-15 10:26 ` [PATCH v2 5/5] x86/virt/tdx: Make TDX module initialize " Xu Yilun
@ 2026-09-15 22:09 ` Edgecombe, Rick P
5 siblings, 0 replies; 13+ messages in thread
From: Edgecombe, Rick P @ 2026-09-15 22:09 UTC (permalink / raw)
To: linux-coco, linux-kernel, yilun.xu, x86
Cc: Gao, Chao, Xu, Yilun, Duan, Zhenzhong, kas, baolu.lu, Li,
Xiaoyao, Maloor, Kishen, Hunter, Adrian, tony.lindgren, Mehta,
Sohil, Fang, Peter, nik.borisov, kvm, artem.bityutskiy
On Tue, 2026-09-15 at 18:26 +0800, Xu Yilun wrote:
> This is based on v7.3-rc1. You can find the full branch stack at [2].
> The DICE part is in the full branch as an example for extensions. But it
> does not include the other DICE feedbacks.
>
> The full branch contains:
>
> Dependency: Patch 1: SEAMCALL version patch [3] which is WIP on
> community review.
> This series: Patch 2~7: This series, including this cover-letter.
> Use case: Patch 8~N: The old DICE part as an example.
A downside to these stacks is that we don't get Sashiko review. I wonder if it
would pick this up:
https://b4.docs.kernel.org/en/latest/contributor/prep.html#using-the-message-id
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/5] x86/virt/tdx: Configure add-on features on TDX module init
2026-09-15 10:26 ` [PATCH v2 2/5] x86/virt/tdx: Configure add-on features on TDX module init Xu Yilun
2026-09-15 20:54 ` Edgecombe, Rick P
@ 2026-09-16 3:23 ` Chao Gao
1 sibling, 0 replies; 13+ messages in thread
From: Chao Gao @ 2026-09-16 3:23 UTC (permalink / raw)
To: Xu Yilun
Cc: x86, linux-coco, linux-kernel, kas, rick.p.edgecombe, yilun.xu,
xiaoyao.li, sohil.mehta, adrian.hunter, kishen.maloor,
tony.lindgren, peter.fang, baolu.lu, zhenzhong.duan,
artem.bityutskiy, kvm, nik.borisov
On Tue, Sep 15, 2026 at 06:26:55PM +0800, Xu Yilun wrote:
>The TDX architecture identifies some features that are off by default
>but can be enabled by the host during TDX module initialization. They
>are classified as add-on features because enabling them affects existing
>TDX systems: they may change existing feature behavior, or reserve more
>memory.
>
>The TDX module extends TDH.SYS.CONFIG with a new register argument to
>specify which add-on features to enable. This new argument is a bitmap
>that uses the same feature bits as TDX_FEATURES0. Note that Dynamic PAMT
>is an exception: although it is an add-on feature, it is controlled via
>a legacy, dedicated register argument [1].
>
>The kernel needs to enable these add-on features when it supports them.
>Add a get_tdx_addon_features0() helper to return the bitmap of the add-on
>features that the module & kernel both support. Initially, this helper
>returns 0. It will be updated to return specific feature bits as full
>kernel support lands. Pass this extra bitmap to TDH.SYS.CONFIG wrapper.
The last sentence is stale: the bitmap is no longer passed to the wrapper.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 4/5] x86/virt/tdx: Add extra memory to TDX module for the extensions
2026-09-15 10:26 ` [PATCH v2 4/5] x86/virt/tdx: Add extra memory to TDX module for the extensions Xu Yilun
2026-09-15 21:19 ` Edgecombe, Rick P
@ 2026-09-16 7:40 ` Chao Gao
1 sibling, 0 replies; 13+ messages in thread
From: Chao Gao @ 2026-09-16 7:40 UTC (permalink / raw)
To: Xu Yilun
Cc: x86, linux-coco, linux-kernel, kas, rick.p.edgecombe, yilun.xu,
xiaoyao.li, sohil.mehta, adrian.hunter, kishen.maloor,
tony.lindgren, peter.fang, baolu.lu, zhenzhong.duan,
artem.bityutskiy, kvm, nik.borisov
>+static __init int tdx_ext_mem_setup(void)
>+{
>+ unsigned int required_pages = tdx_sysinfo.ext.memory_pool_required_pages;
>+ struct tdx_hpa_list *hpa_list;
>+ unsigned int added_pages;
>+ struct page *page;
>+ int ret;
>+
>+ /*
>+ * TDX module uses the metadata memory_pool_required_pages to indicate
>+ * how much memory is still needed. This value decreases each time
>+ * memory is added via TDH.EXT.MEM.ADD.
>+ *
>+ * On first time initialization, a value of 0 before any memory is
>+ * added is unusual. But host makes no assumptions. Skip the memory
>+ * setup and let subsequent steps catch any actual errors.
>+ */
The fact that the value decreases on each TDH.EXT.MEM.ADD is not relevant
here.
Also, calling 0 "unusual" does not help the reader. It is either a valid
value the kernel needs to handle, or an invalid one the kernel can assert
on.
How about:
/*
* The TDX module may require no memory at all. Skip the memory
* setup in that case.
*/
>+ if (!required_pages)
>+ return 0;
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-09-16 7:41 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 10:26 [PATCH v2 0/5] Enable TDX module extensions Xu Yilun
2026-09-15 10:26 ` [PATCH v2 1/5] x86/virt/tdx: Move TDH.SYS.CONFIG operations into a wrapper Xu Yilun
2026-09-15 20:45 ` Edgecombe, Rick P
2026-09-15 10:26 ` [PATCH v2 2/5] x86/virt/tdx: Configure add-on features on TDX module init Xu Yilun
2026-09-15 20:54 ` Edgecombe, Rick P
2026-09-16 3:23 ` Chao Gao
2026-09-15 10:26 ` [PATCH v2 3/5] x86/virt/tdx: Detect if the extensions initialization is required Xu Yilun
2026-09-15 21:14 ` Edgecombe, Rick P
2026-09-15 10:26 ` [PATCH v2 4/5] x86/virt/tdx: Add extra memory to TDX module for the extensions Xu Yilun
2026-09-15 21:19 ` Edgecombe, Rick P
2026-09-16 7:40 ` Chao Gao
2026-09-15 10:26 ` [PATCH v2 5/5] x86/virt/tdx: Make TDX module initialize " Xu Yilun
2026-09-15 22:09 ` [PATCH v2 0/5] Enable TDX module extensions Edgecombe, Rick P
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®