On Fri, Nov 10, 2023 at 12:55:46AM +1300, Kai Huang <kai.huang@xxxxxxxxx> wrote: > The TDX module global metadata provides system-wide information about > the module. The TDX module provides SEAMCALls to allow the kernel to > query one specific global metadata field (entry) or all fields. > > TL;DR: > > Use the TDH.SYS.RD SEAMCALL to read the essential global metadata for > module initialization, and at the same time, to only initialize TDX > module with version 1.5 and later. > > Long Version: > > 1) Only initialize TDX module with version 1.5 and later > > TDX module 1.0 has some compatibility issues with the later versions of > module, as documented in the "Intel TDX module ABI incompatibilities > between TDX1.0 and TDX1.5" spec. Basically there's no value to use TDX > module 1.0 when TDX module 1.5 and later versions are already available. > To keep things simple, just support initializing the TDX module 1.5 and > later. > > 2) Get the essential global metadata for module initialization > > TDX reports a list of "Convertible Memory Region" (CMR) to tell the > kernel which memory is TDX compatible. The kernel needs to build a list > of memory regions (out of CMRs) as "TDX-usable" memory and pass them to > the TDX module. The kernel does this by constructing a list of "TD > Memory Regions" (TDMRs) to cover all these memory regions and passing > them to the TDX module. > > Each TDMR is a TDX architectural data structure containing the memory > region that the TDMR covers, plus the information to track (within this > TDMR): a) the "Physical Address Metadata Table" (PAMT) to track each TDX > memory page's status (such as which TDX guest "owns" a given page, and > b) the "reserved areas" to tell memory holes that cannot be used as TDX > memory. > > The kernel needs to get below metadata from the TDX module to build the > list of TDMRs: a) the maximum number of supported TDMRs, b) the maximum > number of supported reserved areas per TDMR and, c) the PAMT entry size > for each TDX-supported page size. > > Note the TDX module internally checks whether the "TDX-usable" memory > regions passed via TDMRs are truly convertible. Just skipping reading > the CMRs and manually checking memory regions against them, but let the > TDX module do the check. > > == Implementation == > > TDX module 1.0 uses TDH.SYS.INFO SEAMCALL to report the global metadata > in a fixed-size (1024-bytes) structure 'TDSYSINFO_STRUCT'. TDX module > 1.5 adds more metadata fields, and introduces the new TDH.SYS.{RD|RDALL} > SEAMCALLs for reading the metadata. The new metadata mechanism removes > the fixed-size limitation of the structure 'TDSYSINFO_STRUCT' and allows > the TDX module to support unlimited number of metadata fields. > > TDX module 1.5 and later versions still support the TDH.SYS.INFO for > compatibility to the TDX module 1.0, but it may only report part of > metadata via the 'TDSYSINFO_STRUCT'. For any new metadata the kernel > must use TDH.SYS.{RD|RDALL} to read. > > To achieve the above two goals mentioned in 1) and 2), just use the > TDH.SYS.RD to read the essential metadata fields related to the TDMRs. > > TDH.SYS.RD returns *one* metadata field at a given "Metadata Field ID". > It is enough for getting these few fields for module initialization. > On the other hand, TDH.SYS.RDALL reports all metadata fields to a 4KB > buffer provided by the kernel which is a little bit overkill here. > > It may be beneficial to get all metadata fields at once here so they can > also be used by KVM (some are essential for creating basic TDX guests), > but technically it's unknown how many 4K pages are needed to fill all > the metadata. Thus it's better to read metadata when needed. > > Signed-off-by: Kai Huang <kai.huang@xxxxxxxxx> > --- > > v14 -> v15: > - New patch to use TDH.SYS.RD to read TDX module global metadata for > module initialization and stop initializing 1.0 module. > > --- > arch/x86/include/asm/shared/tdx.h | 1 + > arch/x86/virt/vmx/tdx/tdx.c | 75 ++++++++++++++++++++++++++++++- > arch/x86/virt/vmx/tdx/tdx.h | 39 ++++++++++++++++ > 3 files changed, 114 insertions(+), 1 deletion(-) > > diff --git a/arch/x86/include/asm/shared/tdx.h b/arch/x86/include/asm/shared/tdx.h > index a4036149c484..fdfd41511b02 100644 > --- a/arch/x86/include/asm/shared/tdx.h > +++ b/arch/x86/include/asm/shared/tdx.h > @@ -59,6 +59,7 @@ > #define TDX_PS_4K 0 > #define TDX_PS_2M 1 > #define TDX_PS_1G 2 > +#define TDX_PS_NR (TDX_PS_1G + 1) > > #ifndef __ASSEMBLY__ > > diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c > index d1affb30f74d..d24027993983 100644 > --- a/arch/x86/virt/vmx/tdx/tdx.c > +++ b/arch/x86/virt/vmx/tdx/tdx.c > @@ -235,8 +235,75 @@ static int build_tdx_memlist(struct list_head *tmb_list) > return ret; > } > > +static int read_sys_metadata_field(u64 field_id, u64 *data) > +{ > + struct tdx_module_args args = {}; > + int ret; > + > + /* > + * TDH.SYS.RD -- reads one global metadata field > + * - RDX (in): the field to read > + * - R8 (out): the field data > + */ > + args.rdx = field_id; > + ret = seamcall_prerr_ret(TDH_SYS_RD, &args); > + if (ret) > + return ret; > + > + *data = args.r8; > + > + return 0; > +} > + > +static int read_sys_metadata_field16(u64 field_id, u16 *data) > +{ > + u64 _data; > + int ret; > + > + if (WARN_ON_ONCE(MD_FIELD_ID_ELE_SIZE_CODE(field_id) != > + MD_FIELD_ID_ELE_SIZE_16BIT)) > + return -EINVAL; > + > + ret = read_sys_metadata_field(field_id, &_data); > + if (ret) > + return ret; > + > + *data = (u16)_data; > + > + return 0; > +} > + > +static int get_tdx_tdmr_sysinfo(struct tdx_tdmr_sysinfo *tdmr_sysinfo) > +{ > + int ret; > + > + ret = read_sys_metadata_field16(MD_FIELD_ID_MAX_TDMRS, > + &tdmr_sysinfo->max_tdmrs); > + if (ret) > + return ret; > + > + ret = read_sys_metadata_field16(MD_FIELD_ID_MAX_RESERVED_PER_TDMR, > + &tdmr_sysinfo->max_reserved_per_tdmr); > + if (ret) > + return ret; > + > + ret = read_sys_metadata_field16(MD_FIELD_ID_PAMT_4K_ENTRY_SIZE, > + &tdmr_sysinfo->pamt_entry_size[TDX_PS_4K]); > + if (ret) > + return ret; > + > + ret = read_sys_metadata_field16(MD_FIELD_ID_PAMT_2M_ENTRY_SIZE, > + &tdmr_sysinfo->pamt_entry_size[TDX_PS_2M]); > + if (ret) > + return ret; > + > + return read_sys_metadata_field16(MD_FIELD_ID_PAMT_1G_ENTRY_SIZE, > + &tdmr_sysinfo->pamt_entry_size[TDX_PS_1G]); > +} > + Now we don't query the versions, build info, attributes, and etc. Because it's important to know its version/attributes, can we query and print them as before? Maybe with another path. In long term, those info would be exported via sysfs, though. -- Isaku Yamahata <isaku.yamahata@xxxxxxxxxxxxxxx>