A runtime TDX module update can conflict with TD lifecycle operations that are update-sensitive. Today, update-sensitive operations include: - TD build: TD measurement is accumulated across multiple TDH.MEM.PAGE.ADD, TDH.MR.EXTEND, and TDH.MR.FINALIZE calls. - TD migration: intermediate crypto state is saved/restored across interrupted/resumed TDH.EXPORT.STATE.* and TDH.IMPORT.STATE.* flows. If an update races TD build, for example, TD measurement can become incorrect and attestation can fail. The TDX architecture exposes two approaches: 1) Avoid updates during update-sensitive operations. 2) Detect incompatibility after update and recover. Post-update detection (option #2) is not a good fit: as discussed in [1], future module behavior may expand update-sensitive operations in ways that make post-update detection fragile for existing KVM userspace APIs. "Do nothing" is also not preferred: while it keeps kernel code simple, it lets the issue leak into the broader stack, where both detection and recovery require significantly more effort. So, use option #1. Specifically, request "avoid update-sensitive" behavior during TDX module shutdown and map the resulting failure to -EBUSY so userspace can distinguish an update race from other failures. Do not disable updates when option #1 is unavailable. In that case, effectively fall back to "do nothing", and set the expectation for userspace to "update your module at your own risk". Note: this implementation is based on a reference patch by Vishal [2]. Note2: moving "NO_RBP_MOD" is just to centralize bit definitions. Signed-off-by: Chao Gao Reviewed-by: Tony Lindgren Link: https://lore.kernel.org/linux-coco/aQIbM5m09G0FYTzE@google.com/ # [1] Link: https://lore.kernel.org/linux-coco/CAGtprH_oR44Vx9Z0cfxvq5-QbyLmy_+Gn3tWm3wzHPmC1nC0eg@mail.gmail.com/ # [2] --- arch/x86/include/asm/tdx.h | 16 ++++++++++++++-- arch/x86/kvm/vmx/tdx_errno.h | 2 -- arch/x86/virt/vmx/tdx/tdx.c | 23 +++++++++++++++++++---- arch/x86/virt/vmx/tdx/tdx.h | 3 --- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index b3a7301e77c6..4c4f7acd4044 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -26,11 +26,18 @@ #define TDX_SEAMCALL_GP (TDX_SW_ERROR | X86_TRAP_GP) #define TDX_SEAMCALL_UD (TDX_SW_ERROR | X86_TRAP_UD) +#define TDX_SEAMCALL_STATUS_MASK 0xFFFFFFFF00000000ULL + /* * TDX module SEAMCALL leaf function error codes */ -#define TDX_SUCCESS 0ULL -#define TDX_RND_NO_ENTROPY 0x8000020300000000ULL +#define TDX_SUCCESS 0ULL +#define TDX_RND_NO_ENTROPY 0x8000020300000000ULL +#define TDX_UPDATE_COMPAT_SENSITIVE 0x8000051200000000ULL + +/* Bit definitions of TDX_FEATURES0 metadata field */ +#define TDX_FEATURES0_NO_RBP_MOD BIT_ULL(18) +#define TDX_FEATURES0_UPDATE_COMPAT BIT_ULL(47) #ifndef __ASSEMBLER__ @@ -109,6 +116,11 @@ static inline bool tdx_supports_runtime_update(const struct tdx_sys_info *sysinf return false; } +static inline bool tdx_supports_update_compatibility(const struct tdx_sys_info *sysinfo) +{ + return sysinfo->features.tdx_features0 & TDX_FEATURES0_UPDATE_COMPAT; +} + int tdx_guest_keyid_alloc(void); u32 tdx_get_nr_guest_keyids(void); void tdx_guest_keyid_free(unsigned int keyid); diff --git a/arch/x86/kvm/vmx/tdx_errno.h b/arch/x86/kvm/vmx/tdx_errno.h index 6ff4672c4181..215c00d76a94 100644 --- a/arch/x86/kvm/vmx/tdx_errno.h +++ b/arch/x86/kvm/vmx/tdx_errno.h @@ -4,8 +4,6 @@ #ifndef __KVM_X86_TDX_ERRNO_H #define __KVM_X86_TDX_ERRNO_H -#define TDX_SEAMCALL_STATUS_MASK 0xFFFFFFFF00000000ULL - /* * TDX SEAMCALL Status Codes (returned in RAX) */ diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 98682c4a383a..2025d00b0567 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -1176,10 +1176,13 @@ int tdx_enable(void) } EXPORT_SYMBOL_FOR_KVM(tdx_enable); +#define TDX_SYS_SHUTDOWN_AVOID_COMPAT_SENSITIVE BIT(16) + int tdx_module_shutdown(void) { struct tdx_module_args args = {}; - int ret, cpu; + u64 ret; + int cpu; /* * Shut down the TDX module and prepare handoff data for the next @@ -1189,9 +1192,21 @@ int tdx_module_shutdown(void) * modules as new modules likely have higher handoff version. */ args.rcx = tdx_sysinfo.handoff.module_hv; - ret = seamcall_prerr(TDH_SYS_SHUTDOWN, &args); - if (ret) - return ret; + + if (tdx_supports_update_compatibility(&tdx_sysinfo)) + args.rcx |= TDX_SYS_SHUTDOWN_AVOID_COMPAT_SENSITIVE; + + ret = seamcall(TDH_SYS_SHUTDOWN, &args); + + /* + * Return -EBUSY to signal that there is one or more ongoing flows + * which may not be compatible with an updated TDX module, so that + * userspace can retry on this error. + */ + if ((ret & TDX_SEAMCALL_STATUS_MASK) == TDX_UPDATE_COMPAT_SENSITIVE) + return -EBUSY; + else if (ret) + return -EIO; tdx_module_status = TDX_MODULE_UNINITIALIZED; sysinit_done = false; diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h index f8686247c660..2435f88c6994 100644 --- a/arch/x86/virt/vmx/tdx/tdx.h +++ b/arch/x86/virt/vmx/tdx/tdx.h @@ -88,9 +88,6 @@ struct tdmr_info { DECLARE_FLEX_ARRAY(struct tdmr_reserved_area, reserved_areas); } __packed __aligned(TDMR_INFO_ALIGNMENT); -/* Bit definitions of TDX_FEATURES0 metadata field */ -#define TDX_FEATURES0_NO_RBP_MOD BIT(18) - /* * Do not put any hardware-defined TDX structure representations below * this comment! -- 2.47.3