[PATCH v2 14/21] x86/virt/seamldr: Shut down the current TDX module

Chao Gao posted 21 patches 4 months, 1 week ago
There is a newer version of this series
[PATCH v2 14/21] x86/virt/seamldr: Shut down the current TDX module
Posted by Chao Gao 4 months, 1 week ago
TDX Module updates request shutting down the existing TDX module.
During this shutdown, the module generates hand-off data, which captures
the module's states essential for preserving running TDs. The new TDX
Module can utilize this hand-off data to establish its states.

Invoke the TDH_SYS_SHUTDOWN SEAMCALL on one CPU to perform the shutdown.
This SEAMCALL requires a hand-off module version. Use the module's own
hand-off version, as it is the highest version the module can produce and
is more likely to be compatible with new modules as new modules likely have
higher hand-off version.

Generate changes to tdx_global_metadata.{hc} by following the
instructions detailed in [1], after adding the following section to the
tdx.py script:

    "handoff": [
       "MODULE_HV",
    ],

Manually add a check in get_tdx_sys_info_handoff() to guard reading the
"module_hv" field with TDX Module update support as otherwise the field
doesn't exist.

Signed-off-by: Chao Gao <chao.gao@intel.com>
Tested-by: Farrah Chen <farrah.chen@intel.com>
Link: https://lore.kernel.org/kvm/20250226181453.2311849-12-pbonzini@redhat.com/ # [1]
---
v2:
 - add a comment about how handoff version is chosen.
 - remove the first !ret in get_tdx_sys_info_handoff() as we edited the
   auto-generated code anyway
 - remove !! when determining whether a CPU is the primary one
 - remove unnecessary if-break nesting in TDP_SHUTDOWN
---
 arch/x86/include/asm/tdx_global_metadata.h  |  5 +++++
 arch/x86/virt/vmx/tdx/seamldr.c             | 10 ++++++++++
 arch/x86/virt/vmx/tdx/tdx.c                 | 16 ++++++++++++++++
 arch/x86/virt/vmx/tdx/tdx.h                 |  3 +++
 arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 13 +++++++++++++
 5 files changed, 47 insertions(+)

diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h
index 40689c8dc67e..8a9ebd895e70 100644
--- a/arch/x86/include/asm/tdx_global_metadata.h
+++ b/arch/x86/include/asm/tdx_global_metadata.h
@@ -40,12 +40,17 @@ struct tdx_sys_info_td_conf {
 	u64 cpuid_config_values[128][2];
 };
 
+struct tdx_sys_info_handoff {
+	u16 module_hv;
+};
+
 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_handoff handoff;
 };
 
 #endif
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index fca558b90f72..b9daf11e1064 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -19,6 +19,7 @@
 #include <asm/seamldr.h>
 
 #include "seamcall.h"
+#include "tdx.h"
 
 /* P-SEAMLDR SEAMCALL leaf function */
 #define P_SEAMLDR_INFO			0x8000000000000000
@@ -229,6 +230,7 @@ static struct seamldr_params *init_seamldr_params(const u8 *data, u32 size)
  */
 enum tdp_state {
 	TDP_START,
+	TDP_SHUTDOWN,
 	TDP_DONE,
 };
 
@@ -269,8 +271,12 @@ static void ack_state(void)
 static int do_seamldr_install_module(void *params)
 {
 	enum tdp_state newstate, curstate = TDP_START;
+	int cpu = smp_processor_id();
+	bool primary;
 	int ret = 0;
 
+	primary = cpumask_first(cpu_online_mask) == cpu;
+
 	do {
 		/* Chill out and ensure we re-read tdp_data. */
 		cpu_relax();
@@ -279,6 +285,10 @@ static int do_seamldr_install_module(void *params)
 		if (newstate != curstate) {
 			curstate = newstate;
 			switch (curstate) {
+			case TDP_SHUTDOWN:
+				if (primary)
+					ret = tdx_module_shutdown();
+				break;
 			default:
 				break;
 			}
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index b367bb1d94ed..89b51e270274 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1191,6 +1191,22 @@ int tdx_enable(void)
 }
 EXPORT_SYMBOL_GPL(tdx_enable);
 
+int tdx_module_shutdown(void)
+{
+	struct tdx_module_args args = {};
+
+	/*
+	 * Shut down the TDX Module and prepare handoff data for the next
+	 * TDX Module. This SEAMCALL requires a hand-off module version.
+	 * Use the module's own hand-off version, as it is the highest
+	 * version the module can produce and is more likely to be
+	 * compatible with new modules as new modules likely have higher
+	 * hand-off version.
+	 */
+	args.rcx = tdx_sysinfo.handoff.module_hv;
+	return seamcall_prerr(TDH_SYS_SHUTDOWN, &args);
+}
+
 static bool is_pamt_page(unsigned long phys)
 {
 	struct tdmr_info_list *tdmr_list = &tdx_tdmr_list;
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index 82bb82be8567..1c4da9540ae0 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -46,6 +46,7 @@
 #define TDH_PHYMEM_PAGE_WBINVD		41
 #define TDH_VP_WR			43
 #define TDH_SYS_CONFIG			45
+#define TDH_SYS_SHUTDOWN		52
 
 /*
  * SEAMCALL leaf:
@@ -118,4 +119,6 @@ struct tdmr_info_list {
 	int max_tdmrs;	/* How many 'tdmr_info's are allocated */
 };
 
+int tdx_module_shutdown(void);
+
 #endif
diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
index 0454124803f3..3fdd5cbc21d8 100644
--- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
+++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c
@@ -100,6 +100,18 @@ static int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_td_conf
 	return ret;
 }
 
+static int get_tdx_sys_info_handoff(struct tdx_sys_info_handoff *sysinfo_handoff)
+{
+	int ret = 0;
+	u64 val;
+
+	if (tdx_supports_runtime_update(&tdx_sysinfo) &&
+	    !(ret = read_sys_metadata_field(0x8900000100000000, &val)))
+		sysinfo_handoff->module_hv = val;
+
+	return ret;
+}
+
 static int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
 {
 	int ret = 0;
@@ -109,6 +121,7 @@ static int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
 	ret = ret ?: get_tdx_sys_info_tdmr(&sysinfo->tdmr);
 	ret = ret ?: get_tdx_sys_info_td_ctrl(&sysinfo->td_ctrl);
 	ret = ret ?: get_tdx_sys_info_td_conf(&sysinfo->td_conf);
+	ret = ret ?: get_tdx_sys_info_handoff(&sysinfo->handoff);
 
 	return ret;
 }
-- 
2.47.3
Re: [PATCH v2 14/21] x86/virt/seamldr: Shut down the current TDX module
Posted by Binbin Wu 2 months, 1 week ago

On 10/1/2025 10:52 AM, Chao Gao wrote:
> TDX Module updates request shutting down the existing TDX module.
> During this shutdown, the module generates hand-off data, which captures
> the module's states essential for preserving running TDs. The new TDX
> Module can utilize this hand-off data to establish its states.
> 
> Invoke the TDH_SYS_SHUTDOWN SEAMCALL on one CPU to perform the shutdown.
> This SEAMCALL requires a hand-off module version. Use the module's own
> hand-off version, as it is the highest version the module can produce and
> is more likely to be compatible with new modules as new modules likely have
> higher hand-off version.

According to the TDX module base spec (348549006), each TDX module is built with
TDX Module Handoff Constants, including No-Downgrade Flag. If the current TDX
module is built with NO_DOWNGRADE=1, the hand-off module version must be the
current TDX module's HV.

This patch series doesn't seems to handle No-Downgrade Flag, IIUC it needs
to use the current TDX module's HV to avoid failures.

About "hand-off version" and "No-Downgrade Flag", I still have some questions.
Is it possible that two TDX module versions have the same hand-off version?
If the newer TDX module built with NO_DOWNGRADE=1, is it possible to downgrade
to the older TDX module when they are using the same hand-off version?

> 
> Generate changes to tdx_global_metadata.{hc} by following the
> instructions detailed in [1], after adding the following section to the
> tdx.py script:
> 
>     "handoff": [
>        "MODULE_HV",
>     ],
> 
> Manually add a check in get_tdx_sys_info_handoff() to guard reading the
> "module_hv" field with TDX Module update support as otherwise the field
> doesn't exist.
> 
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Tested-by: Farrah Chen <farrah.chen@intel.com>
> Link: https://lore.kernel.org/kvm/20250226181453.2311849-12-pbonzini@redhat.com/ # [1]

[...]
Re: [PATCH v2 14/21] x86/virt/seamldr: Shut down the current TDX module
Posted by Chao Gao 2 weeks, 6 days ago
On Wed, Dec 03, 2025 at 10:24:58AM +0800, Binbin Wu wrote:
>
>
>On 10/1/2025 10:52 AM, Chao Gao wrote:
>> TDX Module updates request shutting down the existing TDX module.
>> During this shutdown, the module generates hand-off data, which captures
>> the module's states essential for preserving running TDs. The new TDX
>> Module can utilize this hand-off data to establish its states.
>> 
>> Invoke the TDH_SYS_SHUTDOWN SEAMCALL on one CPU to perform the shutdown.
>> This SEAMCALL requires a hand-off module version. Use the module's own
>> hand-off version, as it is the highest version the module can produce and
>> is more likely to be compatible with new modules as new modules likely have
>> higher hand-off version.
>
>According to the TDX module base spec (348549006), each TDX module is built with
>TDX Module Handoff Constants, including No-Downgrade Flag. If the current TDX
>module is built with NO_DOWNGRADE=1, the hand-off module version must be the
>current TDX module's HV.
>
>This patch series doesn't seems to handle No-Downgrade Flag, IIUC it needs
>to use the current TDX module's HV to avoid failures.

Note: this patch always uses the current TDX module's HV. So, it won't fail
regardlss of No-Downgrade flag.

>
>About "hand-off version" and "No-Downgrade Flag", I still have some questions.
>Is it possible that two TDX module versions have the same hand-off version?

Yes.

>If the newer TDX module built with NO_DOWNGRADE=1, is it possible to downgrade
>to the older TDX module when they are using the same hand-off version?

AFAIK, this is possible in TDX architecture as long as the SEAMSVN (TDX
module's SVN) doesn't downgrade.

But for now, there is no plan to support downgrade (or roll-back) in any case
as it may result in lost features and cause compatibility issues. so, the
userspace tool [1] now rejects any downgrade attempts

[1]: https://github.com/intel/confidential-computing.tdx.tdx-module.binaries/blob/28a4baabc268b1998ec553ab9009f4fd3efd309d/version_select_and_load.py#L301