* [PATCH 0/6] Enable TDX module extensions
@ 2026-08-21 3:29 Xu Yilun
2026-08-21 3:29 ` [PATCH 1/6] x86/virt/tdx: Wrap TDH.SYS.CONFIG/UPDATE operations in helpers Xu Yilun
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Xu Yilun @ 2026-08-21 3:29 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
We are trying to get detailed review and some acks for this series.
Dave please feel free to ignore. Kiryl, Rick and TDX developers, please
help take a look.
== Why it's split out ==
The extension patches are pre-requisite for several TDX features (DICE,
TDISP, TD migration). We would appreciate community review and ack so
that all of the extension-based work can build on it.
Previously this series had been posted along with DICE [1]. But now it
is split. It was confusing review to have them together and DICE needs
more time to implement feedback.
The DICE part is in the full branch [2] as an example for extensions.
But it does not include the other DICE feedbacks.
[1] https://lore.kernel.org/lkml/20260618081355.3253581-1-yilun.xu@linux.intel.com/
[2] https://github.com/intel-staging/tdx/tree/tdx-module-ext
This series retained Reviewed-by tags collected in DICE series. Please
let me know if you object.
== Branch stack ==
This is based on v7.2-rc1, the full branch contains:
Patch 1: SEAMCALL version patch [3] which is WIP on community review.
Patch 2~8: This series, including this cover-letter.
Patch 9~N: The old DICE part as an example.
[3] https://lore.kernel.org/all/20260722084634.131020-1-yilun.xu@linux.intel.com/
== Overview ==
To date, SEAMCALL execution must complete quickly to avoid stalling the
host, or yield quickly at pre-defined interrupt checkpoints. This is
acceptable for the existing SEAMCALL leaves, 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 quote". Several new features, such as DICE-based
quoting, TDISP and TD migration, use SEAMCALL leaves backed by the TDX
module extensions.
The TDX module extensions need memory for their own internal state and
data to serve these SEAMCALL leaves, so they need extra setup during TDX
module initialization. At runtime, the host invokes these SEAMCALL
leaves just as normal ones - if interrupted, simply re-invoke the leaf
to resume.
For more information on TDX module extensions, please refer to [4].
[4] https://lore.kernel.org/lkml/20260618081355.3253581-1-yilun.xu@linux.intel.com/
== Changes ==
Dave pointed out the SEAMCALL version change could be a separate
discussion. Split the patch out from this series.
Chao asked if the extensions re-initialization flow could be simplified
by unconditionally executing TDH.EXT.INIT and leaving the validity check
to the module. Yilun pointed out that if we don't honor the updated
metadata, the SEAMCALL leaf may fail then the entire update fails. Rick
pointed out if an update results in different metadata for the
extensions, it is an incompatible update so the update failure is
expected. We agreed on this, so now the simplified re-initialization
flow is: execute TDH.EXT.INIT unless the extensions were originally not
required at boot time. This also moves the extensions' metadata back to
tdx_sys_info, as they now read at boot time and never updated afterward.
Another comment is that Xiaoyao and Rick pointed out the interruptible
resumable capability provided by the extensions is not new. From the
host perspective, some existing SEAMCALL leaves can save states, return
from interrupt and resume. So I no longer emphasize that the new
SEAMCALL leaves are new type of resumable calls. Instead I explained the
yielding details in TDX module to justify why TDX module extensions are
needed.
Rick pointed out the selection of SEAMCALL leaf version for
TDH.SYS.CONFIG is the TDX ABI detail, mixing the operation with other
kernel operations makes the version selection logic tenuous. Make a new
patch which adds SEAMCALL helpers for TDH.SYS.CONFIG/UPDATE.
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/
Xu Yilun (6):
x86/virt/tdx: Wrap TDH.SYS.CONFIG/UPDATE operations in helpers
x86/virt/tdx: Configure add-on features on TDX module init and update
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
x86/virt/tdx: Re-initialize the extensions on runtime TDX module
update
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 | 245 +++++++++++++++++++-
arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 20 ++
5 files changed, 265 insertions(+), 9 deletions(-)
base-commit: d8610a4dbc62cf5ddf24ad10ba7a7324c0abb278
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/6] x86/virt/tdx: Wrap TDH.SYS.CONFIG/UPDATE operations in helpers
2026-08-21 3:29 [PATCH 0/6] Enable TDX module extensions Xu Yilun
@ 2026-08-21 3:29 ` Xu Yilun
2026-08-21 3:29 ` [PATCH 2/6] x86/virt/tdx: Configure add-on features on TDX module init and update Xu Yilun
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Xu Yilun @ 2026-08-21 3:29 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
As part of the TDX module initialization, the kernel configures the TDX
module with several information, such as TDX-usable memory regions
(TDMRs) and the global KeyID for protecting TDX metadata. During the
configuration, the kernel does 2 operations: constructing kernel data
types for the SEAMCALL leaf arguments and turning these data types into
u64's according to TDX ABI.
Both operations are implemented in one function - config_tdx_module().
This blurs the boundary between kernel managed structures and TDX ABI
definitions. Moreover, future kernel change will need to add more
nuances mandated by TDX ABI, such as setting the TDH.SYS.CONFIG leaf
version to allow configuring add-on features. Keeping these operations
tangled would further clutter the code.
Just like other SEAMCALL leaf helpers, wrap the invocation of
TDH.SYS.CONFIG in a helper. Introduce a more descriptive kernel data
type for the physical address array of TDMR information. This data type
is similar to struct seamldr_params in that it is the container of the
PA array layout which is an in-memory ABI. So the previous u64 * type
for the array is not wrong, but a named structure provides better type
safety and self-documentation. Use the data type as the argument of the
TDH.SYS.CONFIG helper.
Future kernel change will also need to set the TDH.SYS.UPDATE leaf
version for the same purpose. Add a similar helper to prepare for the
change.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
---
v1:
- This patch is split out from the last series (Rick)
---
arch/x86/virt/vmx/tdx/tdx.c | 37 ++++++++++++++++++++++++++++---------
1 file changed, 28 insertions(+), 9 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 7a89e29b118c..e6b664b76141 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -998,11 +998,26 @@ static __init int construct_tdmrs(struct list_head *tmb_list,
return ret;
}
+struct tdmr_info_pa_array {
+ DECLARE_FLEX_ARRAY(u64, phys);
+};
+
+static __init int tdx_sys_config(struct tdmr_info_pa_array *tdmr_pa_array,
+ u64 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 +1036,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);
@@ -1306,12 +1319,18 @@ int tdx_module_shutdown(void)
return 0;
}
-int tdx_module_run_update(void)
+static int tdx_sys_update(void)
{
struct tdx_module_args args = {};
+
+ return seamcall_prerr(TDH_SYS_UPDATE, &args);
+}
+
+int tdx_module_run_update(void)
+{
int ret;
- ret = seamcall_prerr(TDH_SYS_UPDATE, &args);
+ ret = tdx_sys_update();
if (ret)
return ret;
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/6] x86/virt/tdx: Configure add-on features on TDX module init and update
2026-08-21 3:29 [PATCH 0/6] Enable TDX module extensions Xu Yilun
2026-08-21 3:29 ` [PATCH 1/6] x86/virt/tdx: Wrap TDH.SYS.CONFIG/UPDATE operations in helpers Xu Yilun
@ 2026-08-21 3:29 ` Xu Yilun
2026-08-21 3:29 ` [PATCH 3/6] x86/virt/tdx: Detect if the extensions initialization is required Xu Yilun
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Xu Yilun @ 2026-08-21 3:29 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
The TDX architecture identifies some features that must be explicitly
enabled when the kernel supports them. These add-on features affect
existing TDX systems: they may change existing feature behavior, reserve
more memory, or impact TDX initialization performance. The kernel must
enable these add-on features at boot or post-update time.
TDISP, DICE-based quoting and TD migration are among those add-on
features, as their SEAMCALL leaves depend on a SEAMCALL execution
context built by the TDX module extensions. On the other hand, the TDX
architecture doesn't allow the extensions to be initialized if none of
these features are enabled. Add support for configuring add-on features,
as the prerequisite for enabling the extensions.
The TDX module extends TDH.SYS.CONFIG and TDH.SYS.UPDATE with new bitmap
parameters to specify which add-on features to enable. The bitmap
uses the same feature bits as TDX_FEATURES0. 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 helper.
The TDX module requires SEAMCALL leaf version 1 for TDH.SYS.CONFIG and
TDH.SYS.UPDATE when passing the new bitmap parameter. A previous
change [1] supports the versioned SEAMCALL leaves by adding a "version"
field in struct tdx_module_args. Set the version field to 1 if any bit
is set in this bitmap.
Compatible updates keep the reported features unchanged across updates,
so that existing TDX users can continue to operate without disruption.
To adhere to this, provide TDH.SYS.UPDATE with the same bitmap returned
by get_tdx_addon_features0(). This works because the module supported
feature bits are cached at boot and never refreshed after updates, so
the returned bitmap always matches the initial TDH.SYS.CONFIG input.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Link: https://lore.kernel.org/all/20260722084634.131020-1-yilun.xu@linux.intel.com/ # [1]
---
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 | 38 +++++++++++++++++++++++++++++++++----
1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index e6b664b76141..66b43350c6c3 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -998,12 +998,22 @@ 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 u64 get_tdx_addon_features0(void)
+{
+ return tdx_sysinfo.features.tdx_features0 &
+ TDX_KERNEL_SUPPORTED_ADDON_FEATURES0;
+}
+
struct tdmr_info_pa_array {
DECLARE_FLEX_ARRAY(u64, phys);
};
static __init int tdx_sys_config(struct tdmr_info_pa_array *tdmr_pa_array,
- u64 nr_tdmr_pa, u64 global_keyid)
+ u64 nr_tdmr_pa, u64 global_keyid,
+ u64 addon_features0)
{
struct tdx_module_args args = {
.rcx = __pa(tdmr_pa_array),
@@ -1011,12 +1021,22 @@ static __init int tdx_sys_config(struct tdmr_info_pa_array *tdmr_pa_array,
.r8 = global_keyid,
};
+ /*
+ * Use SEAMCALL version 1 that supports add-on features if any are
+ * requested. Use version 0 if none for backward compatibility.
+ */
+ if (addon_features0) {
+ args.r9 = addon_features0;
+ args.version = 1;
+ }
+
return seamcall_prerr(TDH_SYS_CONFIG, &args);
}
static __init int config_tdx_module(struct tdmr_info_list *tdmr_list,
u64 global_keyid)
{
+ u64 addon_features0 = get_tdx_addon_features0();
struct tdmr_info_pa_array *tdmr_pa_array;
size_t array_sz;
int i, ret;
@@ -1039,7 +1059,7 @@ static __init int config_tdx_module(struct tdmr_info_list *tdmr_list,
tdmr_pa_array->phys[i] = __pa(tdmr_entry(tdmr_list, i));
ret = tdx_sys_config(tdmr_pa_array, tdmr_list->nr_consumed_tdmrs,
- global_keyid);
+ global_keyid, addon_features0);
/* Free the array as it is not required anymore. */
kfree(tdmr_pa_array);
@@ -1319,18 +1339,28 @@ int tdx_module_shutdown(void)
return 0;
}
-static int tdx_sys_update(void)
+static int tdx_sys_update(u64 addon_features0)
{
struct tdx_module_args args = {};
+ /*
+ * Use SEAMCALL version 1 that supports add-on features if any are
+ * requested. Use version 0 if none for backward compatibility.
+ */
+ if (addon_features0) {
+ args.r9 = addon_features0;
+ args.version = 1;
+ }
+
return seamcall_prerr(TDH_SYS_UPDATE, &args);
}
int tdx_module_run_update(void)
{
+ u64 addon_features0 = get_tdx_addon_features0();
int ret;
- ret = tdx_sys_update();
+ ret = tdx_sys_update(addon_features0);
if (ret)
return ret;
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/6] x86/virt/tdx: Detect if the extensions initialization is required
2026-08-21 3:29 [PATCH 0/6] Enable TDX module extensions Xu Yilun
2026-08-21 3:29 ` [PATCH 1/6] x86/virt/tdx: Wrap TDH.SYS.CONFIG/UPDATE operations in helpers Xu Yilun
2026-08-21 3:29 ` [PATCH 2/6] x86/virt/tdx: Configure add-on features on TDX module init and update Xu Yilun
@ 2026-08-21 3:29 ` Xu Yilun
2026-08-21 3:29 ` [PATCH 4/6] x86/virt/tdx: Add extra memory to TDX module for the extensions Xu Yilun
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Xu Yilun @ 2026-08-21 3:29 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
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 66b43350c6c3..a0c370894c0b 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1175,6 +1175,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;
@@ -1229,6 +1253,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 related [flat|nested] 7+ messages in thread
* [PATCH 4/6] x86/virt/tdx: Add extra memory to TDX module for the extensions
2026-08-21 3:29 [PATCH 0/6] Enable TDX module extensions Xu Yilun
` (2 preceding siblings ...)
2026-08-21 3:29 ` [PATCH 3/6] x86/virt/tdx: Detect if the extensions initialization is required Xu Yilun
@ 2026-08-21 3:29 ` Xu Yilun
2026-08-21 3:29 ` [PATCH 5/6] x86/virt/tdx: Make TDX module initialize " Xu Yilun
2026-08-21 3:29 ` [PATCH 6/6] x86/virt/tdx: Re-initialize the extensions on runtime TDX module update Xu Yilun
5 siblings, 0 replies; 7+ messages in thread
From: Xu Yilun @ 2026-08-21 3:29 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
TDX module extensions need memory for their own internal state and data
to serve SEAMCALL leaves. 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 a PFN 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 host. Use contiguous page allocation to isolate these
large blocks entirely, avoiding permanent memory fragmentation and buddy
allocator efficiency loss. Print the allocation amount on TDX module
extensions initialization for visibility.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
---
v1:
- Fix return value for SEAMCALL helpers (Chao)
- Print SEAMCALL error code for SEAMCALL helpers (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 helper 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 | 118 +++++++++++++++++++-
arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 6 +
4 files changed, 123 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 a0c370894c0b..8c2fdaf0b8c0 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1175,6 +1175,120 @@ static __init int init_tdmrs(struct tdmr_info_list *tdmr_list)
return 0;
}
+#define TDX_HPA_LIST_MAX_NR_PAGES (PAGE_SIZE / sizeof(u64))
+
+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;
+
+ hpa_list = kzalloc_obj(*hpa_list);
+ if (!hpa_list)
+ return -ENOMEM;
+
+ 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;
+ }
+
+ /*
+ * Memory for TDX module extensions is never reclaimed and can be tens
+ * of megabytes. Print the amount so users know the cost.
+ */
+ pr_info("%lu KB consumed 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;
@@ -1194,9 +1308,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 related [flat|nested] 7+ messages in thread
* [PATCH 5/6] x86/virt/tdx: Make TDX module initialize the extensions
2026-08-21 3:29 [PATCH 0/6] Enable TDX module extensions Xu Yilun
` (3 preceding siblings ...)
2026-08-21 3:29 ` [PATCH 4/6] x86/virt/tdx: Add extra memory to TDX module for the extensions Xu Yilun
@ 2026-08-21 3:29 ` Xu Yilun
2026-08-21 3:29 ` [PATCH 6/6] x86/virt/tdx: Re-initialize the extensions on runtime TDX module update Xu Yilun
5 siblings, 0 replies; 7+ messages in thread
From: Xu Yilun @ 2026-08-21 3:29 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
TDX module extensions need memory for their own internal state and data
to serve SEAMCALL leaves. Several add-on features depend on the
extensions to execute their SEAMCALL leaves.
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 leaves 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 helpers (Chao)
- Print SEAMCALL error code for SEAMCALL helpers (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 8c2fdaf0b8c0..873b8393f32f 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1289,6 +1289,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;
@@ -1308,7 +1325,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 related [flat|nested] 7+ messages in thread
* [PATCH 6/6] x86/virt/tdx: Re-initialize the extensions on runtime TDX module update
2026-08-21 3:29 [PATCH 0/6] Enable TDX module extensions Xu Yilun
` (4 preceding siblings ...)
2026-08-21 3:29 ` [PATCH 5/6] x86/virt/tdx: Make TDX module initialize " Xu Yilun
@ 2026-08-21 3:29 ` Xu Yilun
5 siblings, 0 replies; 7+ messages in thread
From: Xu Yilun @ 2026-08-21 3:29 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
Runtime TDX module update introduces a mechanism to update the module
firmware while preserving and restoring TDX operations. As part of the
restoration process, the host must re-initialize the extensions to
restore their functionality.
Linux runs the updates in stop_machine() context, which prevents memory
allocation. This introduces a hard restriction that the updated TDX
environment must not consume more memory for the extensions.
The impact of the memory allocation restriction can be mitigated by
another requirement. Runtime updates should keep the add-on features
unchanged across updates, so that existing TDX users can continue to
operate without disruption. This requirement minimizes the chance of
increased memory demand. As a result, the restriction only affects the
compatibility rule for choosing the update image.
To adhere to these requirements, the post-update initialization for the
extensions can be simplified as:
- Check if the extensions were originally initialized during boot up.
If not, skip the re-initialization.
- Assume no more memory needed, skip the memory adding step.
- Re-initialize the extensions via TDH.EXT.INIT. The SEAMCALL leaf
will fail if the updated module requires more memory, or if it drops
the extensions initialization entirely, which indicates the update
image is not compatible.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
---
v1:
- Don't update the extensions metadata any more, only check the
metadata originated at boot time.
- Remove memory_pool_required_pages check, let TDH.EXT.INIT fail if
more memory required.
- Changelog & code comments
---
arch/x86/virt/vmx/tdx/tdx.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 873b8393f32f..1ca3996f32dc 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1289,7 +1289,7 @@ static __init int tdx_ext_mem_setup(void)
return ret;
}
-static __init int tdx_ext_init(void)
+static int tdx_ext_init(void)
{
struct tdx_module_args args = {};
u64 ret;
@@ -1332,6 +1332,19 @@ static __init int init_tdx_module_extensions(void)
return tdx_ext_init();
}
+/*
+ * Don't update the extensions metadata, just follow the requirement originated
+ * during TDX module initialization. Let the extensions re-initialization fail
+ * if more memory is needed, or if ext_required is dropped after updates.
+ */
+static int update_tdx_module_extensions(void)
+{
+ if (!tdx_sysinfo.ext.ext_required)
+ return 0;
+
+ return tdx_ext_init();
+}
+
static __init int init_tdx_module(void)
{
int ret;
@@ -1532,6 +1545,10 @@ int tdx_module_run_update(void)
*/
WARN_ON_ONCE(ret);
+ ret = update_tdx_module_extensions();
+ if (ret)
+ return ret;
+
tdx_module_state.initialized = true;
return 0;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-08-21 3:29 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-21 3:29 [PATCH 0/6] Enable TDX module extensions Xu Yilun
2026-08-21 3:29 ` [PATCH 1/6] x86/virt/tdx: Wrap TDH.SYS.CONFIG/UPDATE operations in helpers Xu Yilun
2026-08-21 3:29 ` [PATCH 2/6] x86/virt/tdx: Configure add-on features on TDX module init and update Xu Yilun
2026-08-21 3:29 ` [PATCH 3/6] x86/virt/tdx: Detect if the extensions initialization is required Xu Yilun
2026-08-21 3:29 ` [PATCH 4/6] x86/virt/tdx: Add extra memory to TDX module for the extensions Xu Yilun
2026-08-21 3:29 ` [PATCH 5/6] x86/virt/tdx: Make TDX module initialize " Xu Yilun
2026-08-21 3:29 ` [PATCH 6/6] x86/virt/tdx: Re-initialize the extensions on runtime TDX module update Xu Yilun
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).