diff --git a/arch/x86/include/asm/tdx_global_metadata.h b/arch/x86/include/asm/tdx_global_metadata.h index 40689c8dc67e..41150d546589 100644 --- a/arch/x86/include/asm/tdx_global_metadata.h +++ b/arch/x86/include/asm/tdx_global_metadata.h @@ -40,6 +40,10 @@ 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; diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c index b03dce213102..3fe1d397ecd1 100644 --- a/arch/x86/virt/vmx/tdx/seamldr.c +++ b/arch/x86/virt/vmx/tdx/seamldr.c @@ -18,6 +18,7 @@ #include #include "seamcall_internal.h" +#include "tdx.h" /* P-SEAMLDR SEAMCALL leaf function */ #define P_SEAMLDR_INFO 0x8000000000000000 @@ -196,6 +197,7 @@ static int init_seamldr_params(struct seamldr_params *params, */ enum module_update_state { MODULE_UPDATE_START, + MODULE_UPDATE_SHUTDOWN, MODULE_UPDATE_DONE, }; @@ -247,8 +249,16 @@ static int do_seamldr_install_module(void *seamldr_params) { enum module_update_state curstate = MODULE_UPDATE_START; enum module_update_state newstate; + bool is_lead_cpu = false; int ret = 0; + /* + * Some steps must be run on exactly one CPU. Pick a "lead" CPU to + * execute those steps. Use CPU 0 because it is always online. + */ + if (smp_processor_id() == 0) + is_lead_cpu = true; + do { newstate = READ_ONCE(update_ctrl.state); @@ -259,7 +269,10 @@ static int do_seamldr_install_module(void *seamldr_params) curstate = newstate; switch (curstate) { - /* TODO: add the update steps. */ + case MODULE_UPDATE_SHUTDOWN: + if (is_lead_cpu) + 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 53cf99c41dbb..3fe01b546fb8 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -328,7 +328,7 @@ static __init int build_tdx_memlist(struct list_head *tmb_list) return ret; } -static __init int read_sys_metadata_field(u64 field_id, u64 *data) +static int read_sys_metadata_field(u64 field_id, u64 *data) { struct tdx_module_args args = {}; int ret; @@ -1274,6 +1274,29 @@ static __init int tdx_enable(void) } subsys_initcall(tdx_enable); +int tdx_module_shutdown(void) +{ + struct tdx_sys_info_handoff handoff = {}; + struct tdx_module_args args = {}; + int ret; + + ret = get_tdx_sys_info_handoff(&handoff); + /* + * Handoff information is required for proper + * shutdown. Refuse to shut down without it. + */ + if (ret) + return ret; + + /* + * Use the module's handoff version as it is the highest the + * module can produce and most likely supported by newer modules. + */ + args.rcx = 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 76c5fb1e1ffe..f0c20dea0388 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 #define TDH_SYS_DISABLE 69 /* @@ -108,4 +109,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 d54d4227990c..e793dec688ab 100644 --- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c +++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c @@ -100,6 +100,19 @@ static __init int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_ return ret; } +static int get_tdx_sys_info_handoff(struct tdx_sys_info_handoff *sysinfo_handoff) +{ + int ret; + u64 val; + + ret = read_sys_metadata_field(0x8900000100000000, &val); + if (ret) + return ret; + + sysinfo_handoff->module_hv = val; + return 0; +} + static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo) { int ret = 0;