[PATCH 2/6] x86/virt/tdx: Configure add-on features on TDX module init and update
From: Xu Yilun
Date: Thu Aug 20 2026 - 23:35:54 EST
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@xxxxxxxxxxxxxxx>
Link: https://lore.kernel.org/all/20260722084634.131020-1-yilun.xu@xxxxxxxxxxxxxxx/ # [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