20. 英特尔可信域扩展 (TDX)¶
英特尔的可信域扩展 (TDX) 通过隔离访客寄存器状态和加密访客内存,保护机密访客虚拟机免受主机和物理攻击。在 TDX 中,一个在特殊模式下运行的特殊模块位于主机和访客之间,并管理访客/主机分离。
20.1. TDX 主机内核支持¶
TDX 引入了一种名为安全仲裁模式 (SEAM) 的新 CPU 模式和由 SEAM Ranger 寄存器 (SEAMRR) 指向的新隔离范围。一个名为“TDX 模块”的 CPU 证明的软件模块在新隔离范围内运行,以提供管理和运行受保护的虚拟机的各项功能。
TDX 还利用英特尔多密钥全内存加密 (MKTME) 为虚拟机提供加密保护。TDX 将部分 MKTME 密钥 ID 保留为 TDX 私有密钥 ID,这些密钥 ID 只能在 SEAM 模式下访问。BIOS 负责对传统 MKTME 密钥 ID 和 TDX 密钥 ID 进行分区。
在可以使用 TDX 模块创建和运行受保护的虚拟机之前,必须将该模块加载到隔离范围并正确初始化。TDX 架构不要求 BIOS 加载 TDX 模块,但内核假设它由 BIOS 加载。
20.1.1. TDX 启动时检测¶
内核通过在内核启动期间检测 TDX 私有密钥 ID 来检测 TDX。以下 dmesg 显示了 BIOS 启用 TDX 的情况
[..] virt/tdx: BIOS enabled: private KeyID range: [16, 64)
20.1.2. TDX 模块初始化¶
内核通过新的 SEAMCALL 指令与 TDX 模块通信。TDX 模块实现 SEAMCALL 叶函数,以允许内核对其进行初始化。
如果未加载 TDX 模块,则 SEAMCALL 指令将失败并出现特殊错误。在这种情况下,内核将无法初始化模块并报告未加载该模块
[..] virt/tdx: module not loaded
初始化 TDX 模块会消耗大约 ~1/256 的系统 RAM 大小,将其用作 TDX 内存的“元数据”。初始化这些元数据以及 TDX 模块本身也需要额外的 CPU 时间。两者都不是微不足道的。内核在运行时根据需要初始化 TDX 模块。
除了初始化 TDX 模块之外,在任何其他 CPU 上进行任何其他 SEAMCALL 之前,必须在一个 CPU 上完成每个 CPU 的初始化 SEAMCALL。
内核提供了两个函数,tdx_enable() 和 tdx_cpu_enable(),以允许 TDX 用户分别启用 TDX 模块和在本地 CPU 上启用 TDX。
进行 SEAMCALL 需要在该 CPU 上完成 VMXON。目前只有 KVM 实现了 VMXON。目前,tdx_enable() 和 tdx_cpu_enable() 都不会在内部执行 VMXON(并非微不足道),而是依赖调用者来保证这一点。
要启用 TDX,TDX 的调用者应:1) 暂时禁用 CPU 热插拔;2) 在所有联机 CPU 上执行 VMXON 和 tdx_enable_cpu();3) 调用 tdx_enable()。例如
cpus_read_lock();
on_each_cpu(vmxon_and_tdx_cpu_enable());
ret = tdx_enable();
cpus_read_unlock();
if (ret)
goto no_tdx;
// TDX is ready to use
TDX 的调用者必须保证在它想要运行任何其他 SEAMCALL 之前,已在任何 CPU 上成功完成 tdx_cpu_enable()。一种典型的用法是在 CPU 热插拔联机回调中执行 VMXON 和 tdx_cpu_enable(),如果 tdx_cpu_enable() 失败则拒绝联机。
用户可以查阅 dmesg 以查看 TDX 模块是否已初始化。
如果 TDX 模块成功初始化,dmesg 会显示如下内容
[..] virt/tdx: 262668 KBs allocated for PAMT
[..] virt/tdx: module initialized
如果 TDX 模块初始化失败,dmesg 也会显示初始化失败
[..] virt/tdx: module initialization failed ...
20.1.3. TDX 与其他内核组件的交互¶
20.1.3.1. TDX 内存策略¶
TDX 报告一个“可转换内存区域”(CMR) 列表,以告知内核哪些内存与 TDX 兼容。内核需要构建一个内存区域列表(从 CMR 中)作为“TDX 可用”内存,并将这些区域传递给 TDX 模块。一旦完成此操作,这些“TDX 可用”内存区域将在模块的生命周期内固定。
为了简单起见,目前内核只是保证页面分配器中的所有页面都是 TDX 内存。具体来说,内核在核心 mm 中“在 TDX 模块初始化时”使用所有系统内存作为 TDX 内存,同时,拒绝在内存热插拔中联机任何非 TDX 内存。
20.1.3.2. 物理内存热插拔¶
请注意,TDX 假设可转换内存在机器运行时始终以物理方式存在。没有错误的 BIOS 永远不应支持热移除任何可转换内存。此实现不处理 ACPI 内存移除,而是依赖于 BIOS 的正确行为。
20.1.3.3. CPU 热插拔¶
TDX 模块要求在一个 CPU 上完成每个 CPU 的初始化 SEAMCALL,然后才能在该 CPU 上进行任何其他 SEAMCALL。内核提供 tdx_cpu_enable() 以便 TDX 用户在用户想要使用新 CPU 执行 TDX 任务时进行此操作。
TDX 不支持物理 (ACPI) CPU 热插拔。在机器启动期间,TDX 会在启用 TDX 之前验证所有启动时存在的逻辑 CPU 是否与 TDX 兼容。没有错误的 BIOS 永远不应支持物理 CPU 的热添加/移除。目前,内核不处理物理 CPU 热插拔,而是依赖于 BIOS 的正确行为。
请注意,TDX 可与 CPU 逻辑联机/脱机配合使用,因此内核仍然允许脱机逻辑 CPU 并再次联机。
20.1.3.4. Kexec()¶
TDX 主机支持目前缺乏处理 kexec 的能力。为简单起见,只能在 Kconfig 中启用其中一个。这将在未来修复。
20.1.3.5. 勘误¶
前几代 TDX 硬件存在一个勘误。对 TDX 私有内存缓存行的部分写入将悄无声息地“破坏”该行。后续读取将消耗破坏并生成机器检查。
部分写入是指写入事务小于缓存行的内存写入落入内存控制器。CPU 通过非时间写入指令(如 MOVNTI)或通过 UC/WC 内存映射执行这些操作。设备也可以通过 DMA 执行部分写入。
从理论上讲,内核错误可能会对 TDX 私有内存进行部分写入并触发意外的机器检查。更重要的是,机器检查代码会将这些呈现为“硬件错误”,而它们实际上是软件触发的问题。但最终,这个问题很难触发。
如果平台存在此类勘误,内核会在机器检查处理程序中打印其他消息,以告知用户机器检查可能是由 TDX 私有内存上的内核错误引起的。
20.1.3.6. 与 S3 和更深层状态的交互¶
TDX 无法从 S3 和更深层状态中幸存。当平台进入 S3 和更深层状态时,硬件会完全重置并禁用 TDX。TDX 访客和 TDX 模块都会被永久销毁。
内核使用 S3 进行挂起到 RAM,使用 S4 和更深层状态进行休眠。目前,为简单起见,内核选择使 TDX 与 S3 和休眠互斥。
当休眠支持可用时,内核会在早期启动期间禁用 TDX
[..] virt/tdx: initialization failed: Hibernation support is enabled
添加 ‘nohibernate’ 内核命令行以禁用休眠,以便使用 TDX。
如果启用 TDX,则在内核早期启动期间禁用 ACPI S3。用户需要在 BIOS 中关闭 TDX 才能使用 S3。
20.2. TDX 访客支持¶
由于主机无法直接访问访客寄存器或内存,因此必须将虚拟机管理程序的大部分正常功能移入访客。这是使用由访客内核处理的虚拟化异常 (#VE) 实现的。#VE 完全在访客内核内处理,但某些 #VE 需要咨询虚拟机管理程序。
TDX 包括用于从访客与虚拟机管理程序或 TDX 模块通信的新类虚拟机调用机制。
20.2.1. 新的 TDX 异常¶
TDX 访客的行为与裸机和传统 VMX 访客不同。在 TDX 访客中,其他正常的指令或内存访问可能会导致 #VE 或 #GP 异常。
标有“*”的指令有条件地导致异常。下面讨论了这些指令的详细信息。
20.2.1.1. 基于指令的 #VE¶
端口 I/O (INS, OUTS, IN, OUT)
HLT
MONITOR, MWAIT
WBINVD, INVD
VMCALL
RDMSR*,WRMSR*
CPUID*
20.2.1.2. 基于指令的 #GP¶
所有 VMX 指令:INVEPT、INVVPID、VMCLEAR、VMFUNC、VMLAUNCH、VMPTRLD、VMPTRST、VMREAD、VMRESUME、VMWRITE、VMXOFF、VMXON
ENCLS、ENCLU
GETSEC
RSM
ENQCMD
RDMSR*,WRMSR*
20.2.1.3. RDMSR/WRMSR 行为¶
MSR 访问行为分为三种类别
生成 #GP
生成 #VE
“直接工作”
一般来说,#GP MSR 不应在客户机中使用。它们的使用可能表明客户机中存在错误。客户机可能会尝试使用系统调用来处理 #GP,但这不太可能成功。
#VE MSR 通常可以由虚拟机监控程序处理。客户机可以向虚拟机监控程序发出系统调用以处理 #VE。
“直接工作”的 MSR 不需要任何特殊的客户机处理。它们可以通过将 MSR 直接传递到硬件,或者通过捕获并在 TDX 模块中处理来实现。除了可能速度较慢之外,这些 MSR 的功能与在裸机上一样。
20.2.1.4. CPUID 行为¶
对于某些 CPUID 叶和子叶,CPUID 返回值(在客户机 EAX/EBX/ECX/EDX 中)的虚拟化位字段可由虚拟机监控程序配置。对于这种情况,Intel TDX 模块架构定义了两种虚拟化类型
虚拟机监控程序控制客户机 TD 所看到的值的位字段。
虚拟机监控程序配置值的位字段,以便客户机 TD 要么看到它们的本机值,要么看到值 0。对于这些位字段,虚拟机监控程序可以屏蔽本机值,但不能打开值。
对于 TDX 模块不知道如何处理的 CPUID 叶和子叶,会生成 #VE。客户机内核可以使用系统调用向虚拟机监控程序请求该值。
20.2.2. 内存访问上的 #VE¶
TDX 内存基本上分为两类:私有和共享。私有内存接收完整的 TDX 保护。其内容受到保护,防止虚拟机监控程序访问。共享内存预计在客户机和虚拟机监控程序之间共享,并且不接收完整的 TDX 保护。
TD 客户机控制其内存访问是被视为私有还是共享。它使用其页表项中的一个位来选择行为。这有助于确保客户机不会将敏感信息放置在共享内存中,从而将其暴露给不受信任的虚拟机监控程序。
20.2.2.2. 私有页面上的 #VE¶
访问私有映射也可能导致 #VE。由于所有内核内存也是私有内存,因此理论上内核可能需要在任意内核内存访问时处理 #VE。这是不可行的,因此 TDX 客户机确保在内核使用内存之前,所有客户机内存都已“接受”。
在内核运行之前,固件会预先接受少量内存(通常为 512M),以确保内核可以启动而不会受到 #VE 的影响。
虚拟机监控程序可以单方面地将接受的页面移动到“阻止”状态。但是,如果这样做,页面访问将不会生成 #VE。相反,它将导致“TD 退出”,虚拟机监控程序需要处理该异常。
20.2.3. Linux #VE 处理程序¶
就像页面错误或 #GP 一样,#VE 异常可以被处理或致命。通常,未处理的用户空间 #VE 会导致 SIGSEGV。未处理的内核 #VE 会导致 oops。
在 x86 上处理嵌套异常通常很麻烦。#VE 可能会被 NMI 中断,这会触发另一个 #VE,从而导致混乱。TDX #VE 架构预料到了这种情况,并包含一个功能来使其稍微不那么麻烦。
在 #VE 处理期间,TDX 模块确保所有中断(包括 NMI)都被阻止。该阻止将一直保持,直到客户机发出 TDG.VP.VEINFO.GET TDCALL。这允许客户机控制何时可以传递中断或新的 #VE。
但是,客户机内核仍然必须小心,避免在此阻止期间执行潜在的 #VE 触发操作(如上所述)。在阻止期间,任何 #VE 都会被提升为双重错误 (#DF),这是不可恢复的。
20.2.4. MMIO 处理¶
在非 TDX 虚拟机中,MMIO 通常通过允许客户机访问一个映射来实现,该映射会在访问时导致 VMEXIT,然后虚拟机监控程序模拟该访问。这在 TDX 客户机中是不可能的,因为 VMEXIT 会将寄存器状态暴露给主机。TDX 客户机不信任主机,并且不能将其状态暴露给主机。
在 TDX 中,MMIO 区域通常会在客户机中触发 #VE 异常。然后,客户机 #VE 处理程序会在客户机内部模拟 MMIO 指令,并将其转换为到主机的受控 TDCALL,而不是将客户机状态暴露给主机。
x86 上的 MMIO 地址只是特殊的物理地址。理论上,可以使用任何访问内存的指令来访问它们。但是,内核指令解码方法是有限的。它仅设计用于解码由 io.h 宏生成的指令。
通过其他方式(如结构覆盖)访问 MMIO 可能会导致 oops。
20.3. 证明¶
在向客户机配置密钥之前,证明用于向其他实体验证 TDX 客户机的可信度。例如,密钥服务器可能希望使用证明来验证客户机是否是所需的客户机,然后再释放加密密钥以挂载加密的 rootfs 或辅助驱动器。
TDX 模块使用构建时测量寄存器 (MRTD) 和运行时测量寄存器 (RTMR) 记录客户机引导过程各个阶段的 TDX 客户机状态。与客户机初始配置和固件映像相关的测量记录在 MRTD 寄存器中。与初始状态、内核映像、固件映像、命令行选项、initrd、ACPI 表等相关的测量记录在 RTMR 寄存器中。例如,有关更多详细信息,请参阅 TDX 虚拟固件设计规范中标题为“TD 测量”的部分。在 TDX 客户机运行时,证明过程用于证明这些测量值。
证明过程包括两个步骤:TDREPORT 生成和引用生成。
TDX 客户机使用 TDCALL[TDG.MR.REPORT] 从 TDX 模块获取 TDREPORT (TDREPORT_STRUCT)。TDREPORT 是由 TDX 模块生成的固定大小的数据结构,其中包含特定于客户机的信息(如构建和引导测量值)、平台安全版本和用于保护 TDREPORT 完整性的 MAC。用户提供的 64 字节 REPORTDATA 用作输入并包含在 TDREPORT 中。通常,它可以是证明服务提供的一些随机数,以便可以唯一地验证 TDREPORT。有关 TDREPORT 的更多详细信息,请参阅 Intel TDX 模块规范中标题为“TDG.MR.REPORT Leaf”的部分。
获取 TDREPORT 后,证明过程的第二步是将其发送到 Quoting Enclave (QE) 以生成引用。根据设计,TDREPORT 只能在本地平台上进行验证,因为 MAC 密钥绑定到平台。为了支持远程验证 TDREPORT,TDX 利用 Intel SGX Quoting Enclave 在本地验证 TDREPORT 并将其转换为可远程验证的引用。将 TDREPORT 发送到 QE 的方法是特定于实现的。证明软件可以选择任何可用的通信通道(即 vsock 或 TCP/IP)将 TDREPORT 发送到 QE 并接收引用。
20.4. 参考¶
TDX 参考资料在此处收集