英特尔可信域扩展 (TDX)

概述

英特尔的可信域扩展 (TDX) 保护机密客户虚拟机免受主机和物理攻击。一个名为“TDX 模块”的 CPU 认证软件模块在一个新的 CPU 隔离范围内运行,以提供管理和运行受保护虚拟机(又称 TDX 客户机或 TD)的功能。

请参阅 [1] 获取白皮书、规范和其他资源。

本文档描述了 TDX 特有的 KVM ABI。TDX 模块在使用 KVM 运行任何 TDX 客户机之前需要进行初始化。主机核心内核提供初始化 TDX 模块的支持,具体描述请参见英特尔可信域扩展 (TDX)

API 描述

KVM_MEMORY_ENCRYPT_OP

类型:

VM ioctl, VCPU ioctl

对于 TDX 操作,KVM_MEMORY_ENCRYPT_OP 被重新用作带有 TDX 特定子 ioctl() 命令的通用 ioctl。

/* Trust Domain Extensions sub-ioctl() commands. */
enum kvm_tdx_cmd_id {
        KVM_TDX_CAPABILITIES = 0,
        KVM_TDX_INIT_VM,
        KVM_TDX_INIT_VCPU,
        KVM_TDX_INIT_MEM_REGION,
        KVM_TDX_FINALIZE_VM,
        KVM_TDX_GET_CPUID,

        KVM_TDX_CMD_NR_MAX,
};

struct kvm_tdx_cmd {
      /* enum kvm_tdx_cmd_id */
      __u32 id;
      /* flags for sub-command. If sub-command doesn't use this, set zero. */
      __u32 flags;
      /*
       * data for each sub-command. An immediate or a pointer to the actual
       * data in process virtual address.  If sub-command doesn't use it,
       * set zero.
       */
      __u64 data;
      /*
       * Auxiliary error code.  The sub-command may return TDX SEAMCALL
       * status code in addition to -Exxx.
       */
      __u64 hw_error;
};

KVM_TDX_CAPABILITIES

类型:

VM ioctl

返回值:

成功时为 0,错误时为 <0

返回当前 KVM 在系统中加载特定 TDX 模块后支持的 TDX 功能。它报告允许为 TDX 客户机配置哪些功能/特性。

  • id: KVM_TDX_CAPABILITIES

  • flags: 必须为 0

  • data: 指向 struct kvm_tdx_capabilities 的指针

  • hw_error: 必须为 0

struct kvm_tdx_capabilities {
      __u64 supported_attrs;
      __u64 supported_xfam;
      __u64 reserved[254];

      /* Configurable CPUID bits for userspace */
      struct kvm_cpuid2 cpuid;
};

KVM_TDX_INIT_VM

类型:

VM ioctl

返回值:

成功时为 0,错误时为 <0

执行 TDX 特定的 VM 初始化。这需要在调用 KVM_CREATE_VM 之后、创建任何 VCPU 之前调用。

  • id: KVM_TDX_INIT_VM

  • flags: 必须为 0

  • data: 指向 struct kvm_tdx_init_vm 的指针

  • hw_error: 必须为 0

struct kvm_tdx_init_vm {
        __u64 attributes;
        __u64 xfam;
        __u64 mrconfigid[6];          /* sha384 digest */
        __u64 mrowner[6];             /* sha384 digest */
        __u64 mrownerconfig[6];       /* sha384 digest */

        /* The total space for TD_PARAMS before the CPUIDs is 256 bytes */
        __u64 reserved[12];

      /*
       * Call KVM_TDX_INIT_VM before vcpu creation, thus before
       * KVM_SET_CPUID2.
       * This configuration supersedes KVM_SET_CPUID2s for VCPUs because the
       * TDX module directly virtualizes those CPUIDs without VMM.  The user
       * space VMM, e.g. qemu, should make KVM_SET_CPUID2 consistent with
       * those values.  If it doesn't, KVM may have wrong idea of vCPUIDs of
       * the guest, and KVM may wrongly emulate CPUIDs or MSRs that the TDX
       * module doesn't virtualize.
       */
        struct kvm_cpuid2 cpuid;
};

KVM_TDX_INIT_VCPU

类型:

VCPU ioctl

返回值:

成功时为 0,错误时为 <0

执行 TDX 特定的 VCPU 初始化。

  • id: KVM_TDX_INIT_VCPU

  • flags: 必须为 0

  • data: 客户机 TD VCPU RCX 的初始值

  • hw_error: 必须为 0

KVM_TDX_INIT_MEM_REGION

类型:

VCPU ioctl

返回值:

成功时为 0,错误时为 <0

使用用户空间提供的 @source_addr 数据,从 @gpa 开始初始化 @nr_pages 的 TDX 客户机私有内存。

注意,在调用此子命令之前,范围 [gpa, gpa + nr_pages] 的内存属性需要是私有的。用户空间可以使用 KVM_SET_MEMORY_ATTRIBUTES 来设置属性。

如果指定了 KVM_TDX_MEASURE_MEMORY_REGION 标志,它还会扩展测量。

  • id: KVM_TDX_INIT_MEM_REGION

  • flags: 目前仅定义了 KVM_TDX_MEASURE_MEMORY_REGION

  • data: 指向 struct kvm_tdx_init_mem_region 的指针

  • hw_error: 必须为 0

#define KVM_TDX_MEASURE_MEMORY_REGION   (1UL << 0)

struct kvm_tdx_init_mem_region {
        __u64 source_addr;
        __u64 gpa;
        __u64 nr_pages;
};

KVM_TDX_FINALIZE_VM

类型:

VM ioctl

返回值:

成功时为 0,错误时为 <0

完成初始 TD 内容的测量并标记为可运行。

  • id: KVM_TDX_FINALIZE_VM

  • flags: 必须为 0

  • data: 必须为 0

  • hw_error: 必须为 0

KVM_TDX_GET_CPUID

类型:

VCPU ioctl

返回值:

成功时为 0,错误时为 <0

获取 TDX 模块为 TD 客户机虚拟化的 CPUID 值。当返回 -E2BIG 时,用户空间应分配更大的缓冲区并重试。最小缓冲区大小会在 struct kvm_cpuid2 的 nent 字段中更新。

  • id: KVM_TDX_GET_CPUID

  • flags: 必须为 0

  • data: 指向 struct kvm_cpuid2 的指针 (输入/输出)

  • hw_error: 必须为 0 (输出)

struct kvm_cpuid2 {
        __u32 nent;
        __u32 padding;
        struct kvm_cpuid_entry2 entries[0];
};

struct kvm_cpuid_entry2 {
        __u32 function;
        __u32 index;
        __u32 flags;
        __u32 eax;
        __u32 ebx;
        __u32 ecx;
        __u32 edx;
        __u32 padding[3];
};

KVM TDX 创建流程

除了标准的 KVM 流程外,还需要调用新的 TDX ioctl。控制流程如下

  1. 检查系统范围功能

    • KVM_CAP_VM_TYPES: 检查是否支持 VM 类型以及是否支持 KVM_X86_TDX_VM。

  2. 创建 VM

    • KVM_CREATE_VM

    • KVM_TDX_CAPABILITIES: 查询用于创建 TDX 客户机的 TDX 功能。

    • KVM_CHECK_EXTENSION(KVM_CAP_MAX_VCPUS): 查询 TD 在 VM 级别可以支持的最大 VCPU 数量 (TDX 在此方面有自己的限制)。

    • KVM_SET_TSC_KHZ: 如果需要与主机不同的 TSC 频率,请配置 TD 的 TSC 频率。这是可选的。

    • KVM_TDX_INIT_VM: 传递 TDX 特定的 VM 参数。

  3. 创建 VCPU

    • KVM_CREATE_VCPU

    • KVM_TDX_INIT_VCPU: 传递 TDX 特定的 VCPU 参数。

    • KVM_SET_CPUID2: 配置 TD 的 CPUID。

    • KVM_SET_MSRS: 配置 TD 的 MSR。

  4. 初始化初始客户机内存

    • 准备初始客户机内存的内容。

    • KVM_TDX_INIT_MEM_REGION: 添加初始客户机内存。

    • KVM_TDX_FINALIZE_VM: 完成 TDX 客户机的测量。

  5. 运行 VCPU

参考

https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/documentation.html