| .. SPDX-License-Identifier: GPL-2.0 |
| |
| =================================== |
| Intel Trust Domain Extensions (TDX) |
| =================================== |
| |
| Overview |
| ======== |
| Intel's Trust Domain Extensions (TDX) protect confidential guest VMs from the |
| host and physical attacks. A CPU-attested software module called 'the TDX |
| module' runs inside a new CPU isolated range to provide the functionalities to |
| manage and run protected VMs, a.k.a, TDX guests or TDs. |
| |
| Please refer to [1] for the whitepaper, specifications and other resources. |
| |
| This documentation describes TDX-specific KVM ABIs. The TDX module needs to be |
| initialized before it can be used by KVM to run any TDX guests. The host |
| core-kernel provides the support of initializing the TDX module, which is |
| described in the Documentation/arch/x86/tdx.rst. |
| |
| API description |
| =============== |
| |
| KVM_MEMORY_ENCRYPT_OP |
| --------------------- |
| :Type: vm ioctl, vcpu ioctl |
| |
| For TDX operations, KVM_MEMORY_ENCRYPT_OP is re-purposed to be generic |
| ioctl with TDX specific sub-ioctl() commands. |
| |
| :: |
| |
| /* 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 |
| -------------------- |
| :Type: vm ioctl |
| :Returns: 0 on success, <0 on error |
| |
| Return the TDX capabilities that current KVM supports with the specific TDX |
| module loaded in the system. It reports what features/capabilities are allowed |
| to be configured to the TDX guest. |
| |
| - id: KVM_TDX_CAPABILITIES |
| - flags: must be 0 |
| - data: pointer to struct kvm_tdx_capabilities |
| - hw_error: must be 0 |
| |
| :: |
| |
| struct kvm_tdx_capabilities { |
| __u64 supported_attrs; |
| __u64 supported_xfam; |
| |
| /* TDG.VP.VMCALL hypercalls executed in kernel and forwarded to |
| * userspace, respectively |
| */ |
| __u64 kernel_tdvmcallinfo_1_r11; |
| __u64 user_tdvmcallinfo_1_r11; |
| |
| /* TDG.VP.VMCALL instruction executions subfunctions executed in kernel |
| * and forwarded to userspace, respectively |
| */ |
| __u64 kernel_tdvmcallinfo_1_r12; |
| __u64 user_tdvmcallinfo_1_r12; |
| |
| __u64 reserved[250]; |
| |
| /* Configurable CPUID bits for userspace */ |
| struct kvm_cpuid2 cpuid; |
| }; |
| |
| |
| KVM_TDX_INIT_VM |
| --------------- |
| :Type: vm ioctl |
| :Returns: 0 on success, <0 on error |
| |
| Perform TDX specific VM initialization. This needs to be called after |
| KVM_CREATE_VM and before creating any VCPUs. |
| |
| - id: KVM_TDX_INIT_VM |
| - flags: must be 0 |
| - data: pointer to struct kvm_tdx_init_vm |
| - hw_error: must be 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 |
| ----------------- |
| :Type: vcpu ioctl |
| :Returns: 0 on success, <0 on error |
| |
| Perform TDX specific VCPU initialization. |
| |
| - id: KVM_TDX_INIT_VCPU |
| - flags: must be 0 |
| - data: initial value of the guest TD VCPU RCX |
| - hw_error: must be 0 |
| |
| KVM_TDX_INIT_MEM_REGION |
| ----------------------- |
| :Type: vcpu ioctl |
| :Returns: 0 on success, <0 on error |
| |
| Initialize @nr_pages TDX guest private memory starting from @gpa with userspace |
| provided data from @source_addr. |
| |
| Note, before calling this sub command, memory attribute of the range |
| [gpa, gpa + nr_pages] needs to be private. Userspace can use |
| KVM_SET_MEMORY_ATTRIBUTES to set the attribute. |
| |
| If KVM_TDX_MEASURE_MEMORY_REGION flag is specified, it also extends measurement. |
| |
| - id: KVM_TDX_INIT_MEM_REGION |
| - flags: currently only KVM_TDX_MEASURE_MEMORY_REGION is defined |
| - data: pointer to struct kvm_tdx_init_mem_region |
| - hw_error: must be 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 |
| ------------------- |
| :Type: vm ioctl |
| :Returns: 0 on success, <0 on error |
| |
| Complete measurement of the initial TD contents and mark it ready to run. |
| |
| - id: KVM_TDX_FINALIZE_VM |
| - flags: must be 0 |
| - data: must be 0 |
| - hw_error: must be 0 |
| |
| |
| KVM_TDX_GET_CPUID |
| ----------------- |
| :Type: vcpu ioctl |
| :Returns: 0 on success, <0 on error |
| |
| Get the CPUID values that the TDX module virtualizes for the TD guest. |
| When it returns -E2BIG, the user space should allocate a larger buffer and |
| retry. The minimum buffer size is updated in the nent field of the |
| struct kvm_cpuid2. |
| |
| - id: KVM_TDX_GET_CPUID |
| - flags: must be 0 |
| - data: pointer to struct kvm_cpuid2 (in/out) |
| - hw_error: must be 0 (out) |
| |
| :: |
| |
| 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 creation flow |
| ===================== |
| In addition to the standard KVM flow, new TDX ioctls need to be called. The |
| control flow is as follows: |
| |
| #. Check system wide capability |
| |
| * KVM_CAP_VM_TYPES: Check if VM type is supported and if KVM_X86_TDX_VM |
| is supported. |
| |
| #. Create VM |
| |
| * KVM_CREATE_VM |
| * KVM_TDX_CAPABILITIES: Query TDX capabilities for creating TDX guests. |
| * KVM_CHECK_EXTENSION(KVM_CAP_MAX_VCPUS): Query maximum VCPUs the TD can |
| support at VM level (TDX has its own limitation on this). |
| * KVM_SET_TSC_KHZ: Configure TD's TSC frequency if a different TSC frequency |
| than host is desired. This is Optional. |
| * KVM_TDX_INIT_VM: Pass TDX specific VM parameters. |
| |
| #. Create VCPU |
| |
| * KVM_CREATE_VCPU |
| * KVM_TDX_INIT_VCPU: Pass TDX specific VCPU parameters. |
| * KVM_SET_CPUID2: Configure TD's CPUIDs. |
| * KVM_SET_MSRS: Configure TD's MSRs. |
| |
| #. Initialize initial guest memory |
| |
| * Prepare content of initial guest memory. |
| * KVM_TDX_INIT_MEM_REGION: Add initial guest memory. |
| * KVM_TDX_FINALIZE_VM: Finalize the measurement of the TDX guest. |
| |
| #. Run VCPU |
| |
| References |
| ========== |
| |
| https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/documentation.html |