Intel TDX (Trust Domain Extensions)
Intel Trust Domain Extensions (TDX) is Intel’s hardware feature for confidential virtual machines: it lets a guest VM — called a Trust Domain (TD) — run with its memory encrypted and its register state and memory mappings integrity-protected against the host, so that “Intel’s Trust Domain Extensions (TDX) protect confidential guest VMs from the host and physical attacks” (kernel docs). The defining architectural choice is that TDX interposes “a CPU-attested software module called ‘the TDX module’” between the host hypervisor and the guest. This module runs in a brand-new, isolated CPU mode — SEAM (Secure Arbitration Mode) — that neither the host kernel nor the guest can enter or inspect, and it owns all the security-critical operations the host used to perform: creating the guest’s private page tables, entering and exiting the guest, and mediating every host-guest interaction. Because the host can no longer touch guest registers or guest memory directly, much of what a hypervisor normally does has to move into the guest (cooperative virtualization) or into the TDX module. This note traces that architecture and pins its Linux/KVM support dates; the cross-vendor rationale lives in Confidential Computing and the Trust Boundary, the proof-of-isolation in Attestation for Confidential VMs, and the AMD counterpart in AMD SEV and SEV-SNP.
This note pins to the 6.16 kernel for host support and the 6.12 LTS baseline for cross-references, dating every claim. The headline, verified below: TDX guest support (running Linux as a TD) landed in Linux 5.19 (2022), but TDX host support — KVM gaining the ability to run TDs — landed only in Linux 6.16 (2025). This is the single most version-sensitive fact in the whole virtualization area and is the resolution of the standing uncertainty flag in the Linux Virtualization MOC.
Mental Model — A Measured Module in the Middle
The mental shift TDX demands is that the hypervisor is no longer trusted, so a third party — the TDX module — sits between host and guest and does the security-critical work on behalf of both, with the CPU enforcing that neither side can reach around it. The host asks the module to do things via a new instruction (SEAMCALL); the guest asks via a different one (TDCALL); the module is itself measured at boot so a remote verifier can confirm which module is running.
flowchart TB subgraph HOST["Host: KVM (untrusted, VMX root)"] KVM["KVM TDX host code<br/>arch/x86/kvm/vmx/tdx.c<br/>cannot read TD memory or registers"] end subgraph SEAM["SEAM range (isolated by SEAMRR)"] MOD["TDX module (CPU-attested)<br/>owns Secure-EPT, TD entry/exit,<br/>key management, attestation"] end subgraph TD["Trust Domain (the guest)"] GK["TD guest kernel + apps<br/>private memory encrypted<br/>#VE handler + TDVMCALL for host services"] end KVM -->|"SEAMCALL (TDH.* host leaves)"| MOD GK -->|"TDCALL (TDG.* guest leaves)"| MOD GK -.->|"TDVMCALL via TDG.VP.VMCALL<br/>(host services: MMIO, hypercalls)"| KVM MOD -->|"mediates TD entry/exit,<br/>edits Secure-EPT"| TD MOD -.->|"signs TDREPORT"| GK
The TDX trust topology. What it shows: the host (KVM) and the guest (the TD) never touch each other directly — every privileged interaction goes through the TDX module, which the host invokes with SEAMCALL and the guest with TDCALL. The module runs in the SEAM range that the SEAMRR register fences off from everyone. The one direct host↔guest channel is the TDVMCALL (a guest hypercall the module forwards out to KVM) for services like MMIO. The insight to take: TDX replaces “trust the hypervisor” with “trust a small, measured, CPU-isolated module” — the host becomes just another untrusted party that must ask the module’s permission. This is structurally different from AMD SEV-SNP, where there is no in-CPU software module: AMD trusts a separate co-processor (the AMD-SP) and overlays integrity with a flat Reverse Map Table rather than taking the page tables away from the host.
SEAM, SEAMCALL, and the TDX Module
The hardware foundation is a new CPU operating mode, SEAM (Secure Arbitration Mode). The TDX module — a signed binary Intel ships and the platform loads at boot — “runs within an isolated memory range designated by the SEAM Ranger Register (SEAMRR)” (kernel TDX arch docs). Nothing outside SEAM, not even the host kernel running in VMX root mode (ring 0), can read or write that range; the CPU enforces the fence. The module is “CPU-attested,” meaning its measurement is available to the attestation chain so a remote party can verify which module version mediates a given TD.
Three instructions wire the module into the system. The host kernel calls into the module with SEAMCALL — “the kernel communicates with the TDX module through the SEAMCALL instruction, which implements leaf functions for module initialization and management” (kernel arch docs). A guest calls in with TDCALL (guest-to-module). The module returns to its caller with SEAMRET. By convention the host-side leaf functions are named TDH.* (“TD Host”, e.g. TDH.VP.ENTER, TDH.MEM.SEPT.ADD) and the guest-side leaves TDG.* (“TD Guest”, e.g. TDG.VP.VMCALL, TDG.MR.REPORT). In the KVM source these surface as wrapper functions such as tdh_vp_enter(), which tdx_vcpu_run() uses to actually enter the guest (v6.16 arch/x86/kvm/vmx/tdx.c).
The practical consequence is that KVM’s role shrinks dramatically. KVM cannot do a VM-entry itself for a TD; it asks the module to (TDH.VP.ENTER). It cannot edit the guest’s private page tables; it asks the module to. It cannot read the exit reason from a VMCS field; the module hands it a sanitized exit. The hypervisor becomes a requestor, and the module is the executor — the inversion that makes “the host is outside the TCB” real.
Private vs Shared Memory and the Shared Bit
A TD’s guest-physical address space is split into two halves distinguished by a single high bit of the guest-physical address — the “shared” bit (sometimes called the S-bit; its position depends on the guest’s physical-address width). “Guests control this distinction through a page table entry bit” (kernel arch docs). When the shared bit is clear, the address is private: “Private memory receives full TDX protections with encrypted content inaccessible to the hypervisor.” When the shared bit is set, the address is shared: memory “expected to be shared between guest and hypervisor [that] does not receive full TDX protections” — used for virtio rings, DMA bounce buffers, and any data the guest deliberately exposes. The docs warn pointedly: “Shared mappings must never be used for sensitive memory content like kernel stacks” (kernel arch docs).
Because private and shared memory are governed by different page tables (see Secure-EPT below), converting a page between the two states is an explicit, coordinated operation. A TD guest flips a page from private to shared with set_memory_decrypted() and back with set_memory_encrypted() — the same kernel helpers used for SEV — which under the hood issue a TDG.VP.VMCALL<MapGPA> to ask the host to update its shared mapping (kernel arch docs). Before a private page may be used, the guest must accept it (the TDX analogue of AMD’s PVALIDATE) so that “memory acceptance occurs before kernel use to prevent unnecessary VE exceptions on private pages” (kernel arch docs). On the host/KVM side, private guest memory is backed by guest_memfd — a special file descriptor (introduced for exactly this purpose, shared by SNP and TDX) that holds memory the host cannot map into its own address space, which is what makes “the host cannot read private memory” enforceable rather than merely policy. Userspace marks the relevant GPA range private with KVM_SET_MEMORY_ATTRIBUTES before initialising it (kernel docs).
Secure-EPT — Taking Private Page Tables Away From the Host
The integrity mechanism is Secure-EPT (S-EPT). Recall that ordinary KVM uses Extended Page Tables (EPT) to map guest-physical to host-physical addresses, and the host controls that EPT — which is precisely what lets a malicious host remap a guest’s pages. TDX splits the EPT in two. Shared memory continues to use a normal, host-controlled EPT. Private memory uses Secure-EPT, which only the TDX module may edit — the host cannot author or alter private mappings at all. KVM keeps a mirror of the Secure-EPT structure for its own bookkeeping (so it knows the shape of the page tables), but every actual modification is performed by asking the module via SEAMCALL. The Linux 6.16 merge describes exactly this: “support for private page tables (managed by the TDX module and mirrored in KVM for efficiency)” (Phoronix, via search snippet of the 6.16 KVM TDX merge).
The KVM host code makes the division concrete. In v6.16’s arch/x86/kvm/vmx/tdx.c, the Secure-EPT operations are a family of tdx_sept_* callbacks that translate KVM’s page-table intentions into module calls: tdx_sept_set_private_spte() maps a private guest page; tdx_sept_link_private_spt() builds interior Secure-EPT structures via TDH.MEM.SEPT.ADD; tdx_sept_drop_private_spte() unmaps via TDH.MEM.PAGE.REMOVE; tdx_sept_zap_private_spte() blocks a range via TDH.MEM.RANGE.BLOCK; and tdx_sept_free_private_spt() reclaims a page-table page. EPT faults route through tdx_handle_ept_violation(), which distinguishes private from shared faults (v6.16 tdx.c). The contrast with AMD SEV-SNP is the most important architectural distinction in confidential computing: SNP leaves the page tables under host control and adds a separate Reverse Map Table that the CPU checks on every access to catch a lying host; TDX removes the private page tables from the host entirely so a lying host cannot author a bad mapping in the first place.
TDVMCALL and GHCI — How a TD Talks to the Host
Just as a TD’s confidentiality breaks normal trap-and-emulate, TDX needs a cooperative channel for the guest to request host services (MMIO to virtio devices, hypercalls, port I/O). Two mechanisms provide it.
First, the VE exception (virtualization exception, vector 20): when a TD executes something that would need host involvement, the CPU/module delivers a VE inside the guest instead of silently exiting to the host. The guest’s VE handler reads the exit details with TDG.VP.VEINFO.GET, and the docs note a subtle hardening detail: “During VE handling, the TDX module ensures that all interrupts (including NMIs) are blocked” until the guest executes that TDCALL, closing a re-entrancy window (kernel arch docs).
Second, the TDVMCALL (the GHCI — Guest-Host Communication Interface): the guest packages a request and issues TDG.VP.VMCALL, which the TDX module forwards to the host. The kernel docs describe these as “TDG.VP.VMCALL hypercalls executed in kernel and forwarded to userspace” (kernel docs) — some TDVMCALLs KVM handles in-kernel; others (device MMIO that needs the userspace VMM’s device model) it forwards out via the kvm_run structure. In v6.16’s tdx.c, handle_tdvmcall() dispatches these, with helpers like tdx_map_gpa() (TDVMCALL_MAP_GPA, the private↔shared conversion above) and tdx_emulate_mmio() (TDVMCALL_MMIO, device access) (v6.16 tdx.c). The TDVMCALL pair is the structural mirror of AMD’s VC exception + GHCB (see AMD SEV and SEV-SNP): same idea — the guest cooperatively decides what to expose — implemented by each vendor’s instruction set.
The TD Lifecycle — KVM_TDX_* Sub-ioctls (host support: Linux 6.16)
On the host, KVM drives the TDX module through one umbrella ioctl, KVM_MEMORY_ENCRYPT_OP, carrying TDX-specific sub-commands (kernel docs). The lifecycle is a strict sequence:
KVM_TDX_CAPABILITIES— query what the TDX module on this platform supports (CPUID bits, attributes, which TDVMCALLs are handled where).KVM_TDX_INIT_VM— “Perform TDX specific VM initialization.” Must be called afterKVM_CREATE_VM(with VM typeKVM_X86_TDX_VM, gated byKVM_CAP_VM_TYPES) and before any vCPU is created.KVM_TDX_INIT_VCPU— “Perform TDX specific VCPU initialization,” once per vCPU.KVM_TDX_INIT_MEM_REGION— load the initial guest image into private memory and optionally fold it into the launch measurement (theKVM_TDX_MEASURE_MEMORY_REGIONflag). The target GPA range “needs to be private” first, set viaKVM_SET_MEMORY_ATTRIBUTES.KVM_TDX_FINALIZE_VM— “Complete measurement of the initial TD contents and mark it ready to run.” After this the measurement is frozen; nothing more can be injected without detection.KVM_TDX_GET_CPUID— retrieve the TDX-virtualized CPUID values the module will present to the guest.
The mandated order is: create VM → KVM_TDX_INIT_VM → create vCPUs → KVM_TDX_INIT_VCPU → KVM_TDX_INIT_MEM_REGION → KVM_TDX_FINALIZE_VM → run (kernel docs; v6.16 docs). On the source side, the host machinery lives in arch/x86/kvm/vmx/tdx.c — tdx_vm_init(), tdx_vcpu_create(), tdx_vcpu_run() (which calls tdh_vp_enter()), tdx_handle_exit(), tdx_vm_destroy(), and tdx_mmu_release_hkid() (releasing the Host Key ID at teardown) (v6.16 tdx.c).
Version pinning — the resolved timeline
| Feature | First mainline kernel | Date | Primary evidence |
|---|---|---|---|
| TDX guest (Linux as a TD) | 5.19 | 2022 | kernelnewbies 5.19; Phoronix Linux 5.19 |
| TDX host (KVM runs TDs) | 6.16 | 2025 | kernelnewbies 6.16: “initial support for Intel’s Trust Domain Extensions”; “KVM: Tdx initialization + vcpu/vm creation”; code at v6.16 tdx.c |
This resolves the Linux Virtualization MOC header uncertainty: TDX host support = Linux 6.16 (verified verbatim against the kernelnewbies 6.16 changelog, and confirmed by arch/x86/kvm/vmx/tdx.c existing in v6.16 but returning 404 in v6.12 — i.e. it is not in the 6.12 LTS). TDX guest support = Linux 5.19. The task brief’s hint (host = 6.16) is correct.
Uncertain
Verify: that the 6.16 TDX-host merge is the complete host implementation versus an “initial/partial” one (the changelog wording is “initial support”). Reason: the 6.16 changelog and merge describe “initialization + vcpu/vm creation” and “initial support,” which suggests further TDX host features may land in later releases (6.17+); a 6.12-LTS reader will not have TDX host at all. To resolve: track subsequent kernel changelogs (6.17, 6.18) for follow-on TDX-host series, and note that distro backports (RHEL/CentOS-Stream, Ubuntu) may carry TDX host on kernels older than 6.16. uncertain
Configuration — Launching a TD with QEMU
qemu-system-x86_64 \
-accel kvm \
-object tdx-guest,id=tdx0 \ # the TDX confidential-guest object
-machine q35,confidential-guest-support=tdx0,\ # bind machine to the TDX object
hpet=off \ # legacy timers a TD does not use
-bios OVMF.tdx.fd \ # TDX-enabled firmware (TDVF), measured into the TD
-cpu host \ # TD inherits host CPU features the module allows
-m 4G -smp 4 \
...Line-by-line: -object tdx-guest,id=tdx0 creates the TDX confidential-guest object; confidential-guest-support=tdx0 flips the machine into a TD (KVM creates it as KVM_X86_TDX_VM and runs the KVM_TDX_INIT_VM → … → KVM_TDX_FINALIZE_VM sequence under the hood); -bios OVMF.tdx.fd is the TDVF (TDX Virtual Firmware), a TDX-aware UEFI build whose bytes are measured into the TD’s launch measurement so the guest owner can attest exactly what firmware booted; hpet=off and a trimmed device set reflect that a TD only consumes virtio + a minimal platform. QEMU has supported TDX since the 10.x series and libvirt correspondingly (QEMU TDX docs; Ubuntu TDX guide).
Uncertain
Verify: the exact QEMU
tdx-guestobject/property names and the minimum QEMU/libvirt versions paired with a 6.16 host. Reason: the QEMU TDX object interface is illustrative here and was not line-verified against the specific QEMU release used with 6.16; QEMU’s confidential-guest property names have changed across versions. To resolve: check QEMU’sdocs/system/i386/tdx.rstfor the matching release. uncertain
Failure Modes and Common Misunderstandings
“TDX has been in Linux since 5.19.” The version trap, identical to SEV-SNP’s. 5.19 added running Linux as a TD (guest); a cloud operator could not host TDs with mainline KVM until 6.16. A 6.12-LTS host has no TDX host support at all (arch/x86/kvm/vmx/tdx.c does not exist there) — this is the most consequential dating error to avoid.
Touching unaccepted private memory. A TD must accept a private page before using it, or it takes a VE. Guest kernels and firmware that predate “unaccepted memory” support crash on boot under TDX — the same class of bug as AMD SNP’s PVALIDATE, and the reason the UEFI unaccepted-memory type exists.
Putting secrets in shared memory. The shared bit makes a page host-readable. Code that accidentally maps a kernel stack or secret into the shared half hands it straight to the untrusted host — hence the docs’ explicit warning. Diagnosing this means auditing which allocations set the shared bit.
Expecting normal device emulation. A TD’s private memory is invisible to the host, so the VMM cannot DMA directly into it; I/O must go through shared bounce buffers and virtio over shared memory. Performance and driver assumptions that worked for a normal guest can break.
Live migration. As with SEV, TD memory is encrypted under a key the host never sees; migration requires module-mediated re-encryption between attested hosts and is far more constrained than ordinary migration (cross-link Dirty Page Tracking and Live Migration).
Alternatives — TDX vs SEV-SNP
The honest, mechanism-level contrast with AMD SEV-SNP:
- Integrity mechanism. TDX uses Secure-EPT — private page tables only the TDX module may edit, so the host cannot author a bad private mapping. SNP uses the Reverse Map Table (RMP) — the host keeps the page tables, but the CPU checks a per-physical-page ownership entry on every access and faults if the host lied. Take the tables away (Intel) vs police the tables (AMD).
- Trusted intermediary. TDX trusts the TDX module, attested software in the SEAM CPU mode. SNP trusts the AMD Secure Processor, a separate on-die co-processor running signed firmware. Measured module vs hardware co-processor.
- Guest cooperation. TDX uses VE + TDVMCALL/GHCI; SNP uses VC + GHCB. Near-mirror designs.
- Mainline maturity. TDX host is the newer of the two in Linux — 6.16 vs SNP-host’s 6.11 — so on equal-vintage kernels AMD’s confidential-VM path has been runnable about five releases longer.
The shared concepts — private/shared memory, attestation, cooperative guests, guest_memfd — are covered once in Confidential Computing and the Trust Boundary rather than duplicated here.
Production Notes
Confidential-VM offerings on Intel silicon — Google Cloud, Azure, and others — use TDX, and the TD’s trustworthiness is established by attestation: the guest generates a TDREPORT via TDCALL[TDG.MR.REPORT], which “contains guest-specific information (such as build and boot measurements), platform security version, and the MAC to protect the integrity,” then sends it to “Intel’s Quoting Enclave for remote-verifiable Quote generation” (kernel arch docs). A relying party verifies the Quote’s signature chain (rooted in Intel keys) and checks the measurements against expected values before releasing secrets. Operationally, TDX has the same version-skew trap as SNP and then some: it needs a ≥ 6.16 mainline host kernel (or a distro backport — Red Hat’s CentOS-Stream and Ubuntu both carry TDX host on kernels predating mainline 6.16), a loaded-and-initialized TDX module on the platform, TDX-enabled BIOS, a TDVF firmware, and a TDX-aware guest kernel. The narrow mainline window (host support is only months old as of mid-2026) means most real deployments still run on vendor-backported kernels rather than upstream 6.16+.
Uncertain
Verify: which clouds offer TDX confidential VMs in 2026 and the exact
TDG.MR.REPORT/attestation-driver interface in the 6.16 guest. Reason: cloud-provider availability is point-in-time and was not re-confirmed against 2026 provider docs in this task; the TDREPORT mechanism is quoted from the kernel arch docs but the specific guest-side ioctl path was not line-verified against v6.16 source. To resolve: check each provider’s confidential-computing docs anddrivers/virt/coco/tdx-guest/in v6.16. uncertain
See Also
- AMD SEV and SEV-SNP — the AMD counterpart; Secure-EPT vs RMP, TDX-module vs AMD-SP, TDVMCALL vs GHCB
- Confidential Computing and the Trust Boundary — the shared rationale: removing the host from the guest TCB
- Attestation for Confidential VMs — how a remote party verifies a TD’s TDREPORT/Quote before trusting it
- Guest Physical Memory and Memory Slots — the GPA space that the shared bit partitions and Secure-EPT governs
- Two-Dimensional Paging (EPT and NPT) — the ordinary EPT that TDX splits into shared-EPT + Secure-EPT
- Dirty Page Tracking and Live Migration — why encrypted TD memory reshapes migration
- Nested Virtualization — sibling §9 frontier topic
- Linux Virtualization MOC — parent map (§9 Nested Virtualization and Confidential Computing)