Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/virt/tdx: Get module global metadata for module initialization

The TDX module global metadata provides system-wide information about
the module.

TL;DR:

Use the TDH.SYS.RD SEAMCALL to tell if the module is good or not.

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. Don't bother with module versions that
do not have a stable ABI.

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.

== Implementation ==

The TDX module has two modes of fetching the metadata: a one field at
a time, or all in one blob. Use the field at a time for now. It is
slower, but there just are not enough fields now to justify the
complexity of extra unpacking.

The err_free_tdxmem=>out_put_tdxmem goto looks wonky by itself. But
it is the first of a bunch of error handling that will get stuck at
its site.

[ dhansen: clean up changelog and add a struct to map between
the TDX module fields and 'struct tdx_tdmr_sysinfo' ]

Signed-off-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Link: https://lore.kernel.org/all/20231208170740.53979-8-dave.hansen%40intel.com

authored by

Kai Huang and committed by
Dave Hansen
cf72bc48 abe8dbab

+127 -1
+1
arch/x86/include/asm/shared/tdx.h
··· 59 59 #define TDX_PS_4K 0 60 60 #define TDX_PS_2M 1 61 61 #define TDX_PS_1G 2 62 + #define TDX_PS_NR (TDX_PS_1G + 1) 62 63 63 64 #ifndef __ASSEMBLY__ 64 65
+87 -1
arch/x86/virt/vmx/tdx/tdx.c
··· 237 237 return ret; 238 238 } 239 239 240 + static int read_sys_metadata_field(u64 field_id, u64 *data) 241 + { 242 + struct tdx_module_args args = {}; 243 + int ret; 244 + 245 + /* 246 + * TDH.SYS.RD -- reads one global metadata field 247 + * - RDX (in): the field to read 248 + * - R8 (out): the field data 249 + */ 250 + args.rdx = field_id; 251 + ret = seamcall_prerr_ret(TDH_SYS_RD, &args); 252 + if (ret) 253 + return ret; 254 + 255 + *data = args.r8; 256 + 257 + return 0; 258 + } 259 + 260 + static int read_sys_metadata_field16(u64 field_id, 261 + int offset, 262 + struct tdx_tdmr_sysinfo *ts) 263 + { 264 + u16 *ts_member = ((void *)ts) + offset; 265 + u64 tmp; 266 + int ret; 267 + 268 + if (WARN_ON_ONCE(MD_FIELD_ID_ELE_SIZE_CODE(field_id) != 269 + MD_FIELD_ID_ELE_SIZE_16BIT)) 270 + return -EINVAL; 271 + 272 + ret = read_sys_metadata_field(field_id, &tmp); 273 + if (ret) 274 + return ret; 275 + 276 + *ts_member = tmp; 277 + 278 + return 0; 279 + } 280 + 281 + struct field_mapping { 282 + u64 field_id; 283 + int offset; 284 + }; 285 + 286 + #define TD_SYSINFO_MAP(_field_id, _offset) \ 287 + { .field_id = MD_FIELD_ID_##_field_id, \ 288 + .offset = offsetof(struct tdx_tdmr_sysinfo, _offset) } 289 + 290 + /* Map TD_SYSINFO fields into 'struct tdx_tdmr_sysinfo': */ 291 + static const struct field_mapping fields[] = { 292 + TD_SYSINFO_MAP(MAX_TDMRS, max_tdmrs), 293 + TD_SYSINFO_MAP(MAX_RESERVED_PER_TDMR, max_reserved_per_tdmr), 294 + TD_SYSINFO_MAP(PAMT_4K_ENTRY_SIZE, pamt_entry_size[TDX_PS_4K]), 295 + TD_SYSINFO_MAP(PAMT_2M_ENTRY_SIZE, pamt_entry_size[TDX_PS_2M]), 296 + TD_SYSINFO_MAP(PAMT_1G_ENTRY_SIZE, pamt_entry_size[TDX_PS_1G]), 297 + }; 298 + 299 + static int get_tdx_tdmr_sysinfo(struct tdx_tdmr_sysinfo *tdmr_sysinfo) 300 + { 301 + int ret; 302 + int i; 303 + 304 + /* Populate 'tdmr_sysinfo' fields using the mapping structure above: */ 305 + for (i = 0; i < ARRAY_SIZE(fields); i++) { 306 + ret = read_sys_metadata_field16(fields[i].field_id, 307 + fields[i].offset, 308 + tdmr_sysinfo); 309 + if (ret) 310 + return ret; 311 + } 312 + 313 + return 0; 314 + } 315 + 240 316 static int init_tdx_module(void) 241 317 { 318 + struct tdx_tdmr_sysinfo tdmr_sysinfo; 242 319 int ret; 243 320 244 321 /* ··· 334 257 if (ret) 335 258 goto out_put_tdxmem; 336 259 260 + ret = get_tdx_tdmr_sysinfo(&tdmr_sysinfo); 261 + if (ret) 262 + goto err_free_tdxmem; 263 + 337 264 /* 338 265 * TODO: 339 266 * 340 - * - Get TDX module "TD Memory Region" (TDMR) global metadata. 341 267 * - Construct a list of TDMRs to cover all TDX-usable memory 342 268 * regions. 343 269 * - Configure the TDMRs and the global KeyID to the TDX module. ··· 350 270 * Return error before all steps are done. 351 271 */ 352 272 ret = -EINVAL; 273 + if (ret) 274 + goto err_free_tdxmem; 353 275 out_put_tdxmem: 354 276 /* 355 277 * @tdx_memlist is written here and read at memory hotplug time. ··· 359 277 */ 360 278 put_online_mems(); 361 279 return ret; 280 + 281 + err_free_tdxmem: 282 + free_tdx_memlist(&tdx_memlist); 283 + goto out_put_tdxmem; 362 284 } 363 285 364 286 static int __tdx_enable(void)
+39
arch/x86/virt/vmx/tdx/tdx.h
··· 2 2 #ifndef _X86_VIRT_TDX_H 3 3 #define _X86_VIRT_TDX_H 4 4 5 + #include <linux/bits.h> 6 + 5 7 /* 6 8 * This file contains both macros and data structures defined by the TDX 7 9 * architecture and Linux defined software data structures and functions. ··· 15 13 * TDX module SEAMCALL leaf functions 16 14 */ 17 15 #define TDH_SYS_INIT 33 16 + #define TDH_SYS_RD 34 18 17 #define TDH_SYS_LP_INIT 35 18 + 19 + /* 20 + * Global scope metadata field ID. 21 + * 22 + * See Table "Global Scope Metadata", TDX module 1.5 ABI spec. 23 + */ 24 + #define MD_FIELD_ID_MAX_TDMRS 0x9100000100000008ULL 25 + #define MD_FIELD_ID_MAX_RESERVED_PER_TDMR 0x9100000100000009ULL 26 + #define MD_FIELD_ID_PAMT_4K_ENTRY_SIZE 0x9100000100000010ULL 27 + #define MD_FIELD_ID_PAMT_2M_ENTRY_SIZE 0x9100000100000011ULL 28 + #define MD_FIELD_ID_PAMT_1G_ENTRY_SIZE 0x9100000100000012ULL 29 + 30 + /* 31 + * Sub-field definition of metadata field ID. 32 + * 33 + * See Table "MD_FIELD_ID (Metadata Field Identifier / Sequence Header) 34 + * Definition", TDX module 1.5 ABI spec. 35 + * 36 + * - Bit 33:32: ELEMENT_SIZE_CODE -- size of a single element of metadata 37 + * 38 + * 0: 8 bits 39 + * 1: 16 bits 40 + * 2: 32 bits 41 + * 3: 64 bits 42 + */ 43 + #define MD_FIELD_ID_ELE_SIZE_CODE(_field_id) \ 44 + (((_field_id) & GENMASK_ULL(33, 32)) >> 32) 45 + 46 + #define MD_FIELD_ID_ELE_SIZE_16BIT 1 19 47 20 48 /* 21 49 * Do not put any hardware-defined TDX structure representations below ··· 63 31 struct list_head list; 64 32 unsigned long start_pfn; 65 33 unsigned long end_pfn; 34 + }; 35 + 36 + /* "TDMR info" part of "Global Scope Metadata" for constructing TDMRs */ 37 + struct tdx_tdmr_sysinfo { 38 + u16 max_tdmrs; 39 + u16 max_reserved_per_tdmr; 40 + u16 pamt_entry_size[TDX_PS_NR]; 66 41 }; 67 42 68 43 #endif