Old TDX modules can clobber RBP in the TDH.VP.ENTER SEAMCALL. However RBP is used as frame pointer in the x86_64 calling convention, and clobbering RBP could result in bad things like being unable to unwind the stack if any non-maskable exceptions (NMI, #MC etc) happens in that gap. A new "NO_RBP_MOD" feature was introduced to more recent TDX modules to not clobber RBP. This feature is reported in the TDX_FEATURES0 global metadata field via bit 18. Don't initialize the TDX module if this feature is not supported [1]. Link: https://lore.kernel.org/all/fc0e8ab7-86d4-4428-be31-82e1ece6dd21@xxxxxxxxx/ [1] Signed-off-by: Kai Huang <kai.huang@xxxxxxxxx> Reviewed-by: Nikolay Borisov <nik.borisov@xxxxxxxx> Reviewed-by: Adrian Hunter <adrian.hunter@xxxxxxxxx> Reviewed-by: Dan Williams <dan.j.williams@xxxxxxxxx> --- v4 -> v5: - Rebase due to patch 3 change. v3 -> v4: - Move reading TDX_FEATURES0 code to this patch. - Change patch title and use permalink - Dan. Hi Dan, Ardian, Nikolay, The code to read TDX_FEATURES0 was not included in this patch when you gave your tag. I didn't remove them. Please let me know if you want me to remove your tag. Thanks! v2 -> v3: - check_module_compatibility() -> check_features(). - Improve error message. https://lore.kernel.org/kvm/cover.1721186590.git.kai.huang@xxxxxxxxx/T/#md9e2eeef927838cbf20d7b361cdbea518b8aec50 --- arch/x86/virt/vmx/tdx/tdx.c | 36 ++++++++++++++++++++++++++++++++++++ arch/x86/virt/vmx/tdx/tdx.h | 16 ++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 130ddac47f64..c877d02ca057 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -292,6 +292,21 @@ static int __read_sys_metadata_field(u64 field_id, void *val, int size) __read_sys_metadata_field(_field_id, _val, sizeof(*(_val))); \ }) +static int get_tdx_sys_info_features(struct tdx_sys_info_features *sysinfo_features) +{ + int ret = 0; + +#define READ_SYS_INFO(_field_id, _member) \ + ret = ret ?: read_sys_metadata_field(MD_FIELD_ID_##_field_id, \ + &sysinfo_features->_member) + + READ_SYS_INFO(TDX_FEATURES0, tdx_features0); + +#undef READ_SYS_INFO + + return ret; +} + static int get_tdx_sys_info_version(struct tdx_sys_info_version *sysinfo_version) { int ret = 0; @@ -335,6 +350,10 @@ static int get_tdx_sys_info(struct tdx_sys_info *sysinfo) { int ret; + ret = get_tdx_sys_info_features(&sysinfo->features); + if (ret) + return ret; + ret = get_tdx_sys_info_version(&sysinfo->version); if (ret) return ret; @@ -364,6 +383,18 @@ static void print_basic_sys_info(struct tdx_sys_info *sysinfo) print_sys_info_version(&sysinfo->version); } +static int check_features(struct tdx_sys_info *sysinfo) +{ + u64 tdx_features0 = sysinfo->features.tdx_features0; + + if (!(tdx_features0 & TDX_FEATURES0_NO_RBP_MOD)) { + pr_err("frame pointer (RBP) clobber bug present, upgrade TDX module\n"); + return -EINVAL; + } + + return 0; +} + /* Calculate the actual TDMR size */ static int tdmr_size_single(u16 max_reserved_per_tdmr) { @@ -1145,6 +1176,11 @@ static int init_tdx_module(void) print_basic_sys_info(&sysinfo); + /* Check whether the kernel can support this module */ + ret = check_features(&sysinfo); + if (ret) + return ret; + /* * To keep things simple, assume that all TDX-protected memory * will come from the page allocator. Make sure all pages in the diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h index 0203528da024..18c54e1e3a4a 100644 --- a/arch/x86/virt/vmx/tdx/tdx.h +++ b/arch/x86/virt/vmx/tdx/tdx.h @@ -31,6 +31,7 @@ * * See the "global_metadata.json" in the "TDX 1.5 ABI definitions". */ +#define MD_FIELD_ID_TDX_FEATURES0 0x0A00000300000008ULL #define MD_FIELD_ID_BUILD_DATE 0x8800000200000001ULL #define MD_FIELD_ID_BUILD_NUM 0x8800000100000002ULL #define MD_FIELD_ID_MINOR_VERSION 0x0800000100000003ULL @@ -105,6 +106,20 @@ struct tdmr_info { * those used by the kernel are. */ +/* + * Class "TDX Module Info". + * + * This class also contains other fields like SYS_ATTRIBUTES and the + * NUM_TDX_FEATURES. For now only TDX_FEATURES0 is needed, but still + * keep the structure to follow the spec (and for future extension). + */ +struct tdx_sys_info_features { + u64 tdx_features0; +}; + +/* Bit definitions of TDX_FEATURES0 metadata field */ +#define TDX_FEATURES0_NO_RBP_MOD _BITULL(18) + /* Class "TDX Module Version" */ struct tdx_sys_info_version { u16 major; @@ -123,6 +138,7 @@ struct tdx_sys_info_tdmr { }; struct tdx_sys_info { + struct tdx_sys_info_features features; struct tdx_sys_info_version version; struct tdx_sys_info_tdmr tdmr; }; -- 2.46.2