Skip to main content

x86defs/
tdx.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Intel TDX specific definitions.
5
6use crate::vmx;
7use bitfield_struct::bitfield;
8use open_enum::open_enum;
9use zerocopy::FromBytes;
10use zerocopy::Immutable;
11use zerocopy::IntoBytes;
12use zerocopy::KnownLayout;
13
14pub const TDX_SHARED_GPA_BOUNDARY_BITS: u8 = 47;
15pub const TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT: u64 = 1 << TDX_SHARED_GPA_BOUNDARY_BITS;
16pub const RESET_VECTOR_PAGE: u64 = 0xfffff000;
17
18/// Size of the [`TdReport`].
19pub const TDX_REPORT_SIZE: usize = 0x400;
20
21/// Size of `report_data` member in [`ReportMac`].
22pub const TDX_REPORT_DATA_SIZE: usize = 64;
23
24/// Size of the `TDKEYREQUEST` structure ([`TdKeyRequest`]) that is passed as the
25/// input to the `TDG.MR.KEY.GET` TDCALL.
26pub const TDX_TDKEYREQUEST_SIZE: usize = 128;
27
28/// Maximum size of the key derived by the `TDG.MR.KEY.GET` TDCALL (256 bits).
29pub const TDX_DERIVED_KEY_SIZE: usize = 32;
30
31open_enum! {
32    /// TDCALL instruction leafs that are passed into the tdcall instruction
33    /// in eax.
34    pub enum TdCallLeaf: u64 {
35        VP_VMCALL = 0,
36        VP_INFO = 1,
37        MR_RTMR_EXTEND = 2,
38        VP_VEINFO_GET = 3,
39        MR_REPORT = 4,
40        VP_CPUIDVE_SET = 5,
41        MEM_PAGE_ACCEPT = 6,
42        VM_RD = 7,
43        VM_WR = 8,
44        VP_RD = 9,
45        VP_WR = 10,
46        SYS_RD = 11,
47        MEM_PAGE_ATTR_RD = 23,
48        MEM_PAGE_ATTR_WR = 24,
49        VP_ENTER = 25,
50        VP_INVGLA = 27,
51        MR_KEY_GET = 29,
52        MEM_PAGE_RELEASE = 30,
53        // Intel TDX Connect (ABI EAS 2.1 v0.61); numeric leaves from the
54        // TDX Module Base Spec. TDG.TDI.VALIDATE (would be 69) is deprecated.
55        TDI_RD = 67,
56        TDI_START = 68,
57        DMAR_ACCEPT = 70,
58        TDI_MMIO_ACCEPT = 71,
59    }
60}
61
62#[bitfield(u64)]
63pub struct TdConfigFlags {
64    pub gpaw: bool,
65    pub flexible_pending_ve: bool,
66    pub no_rbp_mod: bool,
67    pub maxpa_virt: bool,
68    pub maxgpa_virt: bool,
69    pub tdx_connect: bool,
70    pub page_release: bool,
71    pub sealing: bool,
72    #[bits(56)]
73    pub reserved: u64,
74}
75
76pub type TdgVmRdResult = u64;
77
78/// Level used in various TDG.MEM.PAGE calls for GPA_MAPPING types.
79#[repr(u8)]
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub enum TdgMemPageLevel {
82    Size4k = 0,
83    Size2Mb = 1,
84    Size1Gb = 2,
85}
86
87impl TdgMemPageLevel {
88    const fn from_bits(value: u64) -> Self {
89        match value {
90            0 => Self::Size4k,
91            1 => Self::Size2Mb,
92            2 => Self::Size1Gb,
93            _ => panic!("invalid TdgMemPageLevel value"),
94        }
95    }
96
97    const fn into_bits(self) -> u64 {
98        self as u64
99    }
100}
101
102/// Attributes for a single VM.
103#[bitfield(u16)]
104#[derive(PartialEq, Eq)]
105pub struct GpaVmAttributes {
106    pub read: bool,
107    pub write: bool,
108    pub kernel_execute: bool,
109    pub user_execute: bool,
110    #[bits(3)]
111    reserved: u8,
112    suppress_ve: bool,
113    #[bits(7)]
114    reserved2: u8,
115    pub valid: bool,
116}
117
118// Required impls for using within bitfield macros in other structs.
119impl GpaVmAttributes {
120    pub const FULL_ACCESS: Self = Self::new()
121        .with_read(true)
122        .with_write(true)
123        .with_kernel_execute(true)
124        .with_user_execute(true)
125        .with_valid(true);
126}
127
128/// Attributes mask used to set which bits are updated in TDG.MEM.PAGE.ATTR.WR.
129#[bitfield(u16)]
130pub struct GpaVmAttributesMask {
131    pub read: bool,
132    pub write: bool,
133    pub kernel_execute: bool,
134    pub user_execute: bool,
135    #[bits(3)]
136    reserved: u8,
137    pub suppress_ve: bool,
138    #[bits(7)]
139    reserved2: u8,
140    /// invalidate ept for this vm
141    pub inv_ept: bool,
142}
143
144impl GpaVmAttributesMask {
145    pub const ALL_CHANGED: Self = Self::new()
146        .with_read(true)
147        .with_write(true)
148        .with_kernel_execute(true)
149        .with_user_execute(true);
150}
151
152/// Corresponds to GPA_ATTR, which is used as input to TDG.MEM.PAGE.ATTR.WR and
153/// returned from TDG.MEM.PAGE.ATTR.RD.
154#[bitfield(u64)]
155#[derive(PartialEq, Eq)]
156pub struct TdgMemPageGpaAttr {
157    /// represents L1 vm aka VTL2
158    #[bits(16)]
159    pub l1: GpaVmAttributes,
160    /// Represetns L2 vm #1 which we use as VTL0
161    #[bits(16)]
162    pub l2_vm1: GpaVmAttributes,
163    /// Represents L2 vm #2 which we use as VTL1
164    #[bits(16)]
165    pub l2_vm2: GpaVmAttributes,
166    #[bits(16)]
167    pub l2_vm3: GpaVmAttributes,
168}
169
170#[bitfield(u64)]
171pub struct TdgMemPageAcceptRcx {
172    #[bits(3)]
173    pub level: TdgMemPageLevel,
174    #[bits(9)]
175    pub reserved: u64,
176    /// The page number for this accept call.
177    #[bits(40)]
178    pub gpa_page_number: u64,
179    #[bits(12)]
180    pub reserved2: u64,
181}
182
183#[bitfield(u64)]
184pub struct TdgMemPageReleaseRcx {
185    #[bits(3)]
186    pub level: TdgMemPageLevel,
187    #[bits(9)]
188    pub reserved: u64,
189    /// The page number for this release call.
190    #[bits(40)]
191    pub gpa_page_number: u64,
192    #[bits(12)]
193    pub reserved2: u64,
194}
195
196#[bitfield(u64)]
197pub struct TdgMemPageReleaseRcxResult {
198    #[bits(3)]
199    pub level: TdgMemPageLevel,
200    #[bits(9)]
201    pub reserved: u64,
202    /// The page number for this release call.
203    #[bits(40)]
204    pub gpa_page_number: u64,
205    #[bits(9)]
206    pub reserved2: u64,
207    #[bits(1)]
208    pub mmio: bool,
209    #[bits(1)]
210    pub pending: bool,
211    #[bits(1)]
212    pub reserved3: u8,
213}
214
215#[bitfield(u64)]
216pub struct TdgMemPageAttrGpaMappingReadRcxResult {
217    #[bits(3)]
218    pub level: TdgMemPageLevel,
219    #[bits(9)]
220    pub reserved: u64,
221    /// The page number for this accept call.
222    #[bits(40)]
223    pub gpa_page_number: u64,
224    #[bits(9)]
225    pub reserved2: u64,
226    /// If this page is a private MMIO page rather than ordinary private
227    /// memory. Only meaningful on a TDX Connect capable module.
228    #[bits(1)]
229    pub mmio: bool,
230    /// If this page's attributes are pending, meaning it will be applied when
231    /// the page is accepted. Also covers MMIO_PENDING on a TDX Connect capable
232    /// module.
233    #[bits(1)]
234    pub pending: bool,
235    #[bits(1)]
236    pub reserved3: u8,
237}
238
239/// RCX input to TDG.MEM.PAGE.ATTR.WR.
240#[bitfield(u64)]
241#[derive(PartialEq, Eq)]
242pub struct TdgMemPageAttrWriteRcx {
243    #[bits(3)]
244    pub level: TdgMemPageLevel,
245    #[bits(9)]
246    pub reserved: u64,
247    /// The page number for this write call.
248    #[bits(40)]
249    pub gpa_page_number: u64,
250    #[bits(12)]
251    pub reserved2: u64,
252}
253
254/// R8 input to TDG.MEM.PAGE.ATTR.WR.
255#[bitfield(u64)]
256pub struct TdgMemPageAttrWriteR8 {
257    #[bits(16)]
258    pub reserved: u64,
259    /// Corresponds to ATTR_MASK1
260    #[bits(16)]
261    pub l2_vm1: GpaVmAttributesMask,
262    /// Corresponds to ATTR_MASK2
263    #[bits(16)]
264    pub l2_vm2: GpaVmAttributesMask,
265    /// Corresponds to ATTR_MASK3
266    #[bits(16)]
267    pub l2_vm3: GpaVmAttributesMask,
268}
269
270/// RDX (`DMAR_TARGET`) input to TDG.DMAR.ACCEPT. `vm_idx` = 0 for a
271/// non-partitioned TD or L1; 1-3 select L2 VM1-VM3 (reassignment path).
272#[bitfield(u64)]
273pub struct DmarTarget {
274    pub vm_idx: u8,
275    #[bits(56)]
276    pub reserved: u64,
277}
278
279/// RCX (`GPA_BASE_AND_LVL`) input to TDG.TDI.MMIO.ACCEPT. Also decodes the
280/// resumed GPA returned in RCX. Mirrors [`TdgMemPageAcceptRcx`].
281#[bitfield(u64)]
282pub struct TdgTdiMmioAcceptRcx {
283    #[bits(3)]
284    pub level: TdgMemPageLevel,
285    #[bits(9)]
286    pub reserved: u64,
287    /// The MMIO page number for this accept call.
288    #[bits(40)]
289    pub gpa_page_number: u64,
290    #[bits(12)]
291    pub reserved2: u64,
292}
293
294/// R9 (`RANGE_SIZE_OFFSET`) input to TDG.TDI.MMIO.ACCEPT. Also the remaining
295/// size/offset returned in R9 on resume (once the R9 output path is plumbed).
296#[bitfield(u64)]
297pub struct TdgTdiMmioAcceptR9 {
298    /// Number of pages to accept.
299    pub range_size: u32,
300    /// Starting sub-range offset, in pages.
301    pub range_offset: u32,
302}
303
304open_enum! {
305    /// RDX `FIELD` code selector for TDG.TDI.RD (field code, bits 7:0).
306    pub enum TdiRdField: u64 {
307        GET_TDISP_VERSION = 0x01,
308        GET_TDISP_STATE = 0x02,
309        GET_TDISP_REPORT_HASH = 0x03,
310        GET_DEVICE_ATTESTATION_INFO_HASH = 0x04,
311        GET_BIND_SESSION_ID = 0x05,
312    }
313}
314
315/// See the FUNCTION_ID table in the TDX Connect ABI EAS and [PCI-SIG, TDISP]
316/// v1.0.
317#[bitfield(u32)]
318pub struct TdxFunctionId {
319    /// The PCIe requester ID (bus/device/function) of the interface.
320    #[bits(16)]
321    pub requester_id: u16,
322    /// The requester segment. Reserved if `segment_valid` is clear.
323    #[bits(8)]
324    pub requester_segment: u8,
325    /// Whether `requester_segment` holds a valid value.
326    pub segment_valid: bool,
327    #[bits(7)]
328    pub reserved: u8,
329}
330
331open_enum! {
332    /// The TDISP interface state returned in RCX by
333    /// TDG.TDI.RD(`GET_TDISP_STATE`).
334    ///
335    /// The TDX Connect ABI EAS defers the encoding to [PCI-SIG, TDISP] v1.0,
336    /// which is where these values come from.
337    pub enum TdispInterfaceState: u64 {
338        CONFIG_UNLOCKED = 0,
339        CONFIG_LOCKED = 1,
340        RUN = 2,
341        ERROR = 3,
342    }
343}
344
345/// The value specified in `r11` when making a TD vmcall, specified by `r10 =
346/// 0`.
347#[repr(u64)]
348pub enum TdVmCallSubFunction {
349    IoInstr = 0x1e,
350    RdMsr = 0x1f,
351    WrMsr = 0x20,
352    MapGpa = 0x10001,
353}
354
355open_enum! {
356    /// Result code for `tdcall` to the TDX module, returned in RAX.
357    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
358    pub enum TdCallResultCode: u32 {
359        SUCCESS = 0x00000000,
360        NON_RECOVERABLE_VCPU = 0x40000001,
361        NON_RECOVERABLE_TD = 0x60000002,
362        INTERRUPTED_RESUMABLE = 0x80000003,
363        INTERRUPTED_RESTARTABLE = 0x80000004,
364        NON_RECOVERABLE_TD_NON_ACCESSIBLE = 0x60000005,
365        INVALID_RESUMPTION = 0xC0000006,
366        NON_RECOVERABLE_TD_WRONG_APIC_MODE = 0xE0000007,
367        CROSS_TD_FAULT = 0x80000008,
368        CROSS_TD_TRAP = 0x90000009,
369        NON_RECOVERABLE_TD_CORRUPTED_MD = 0x6000000A,
370        OPERAND_INVALID = 0xC0000100,
371        OPERAND_ADDR_RANGE_ERROR = 0xC0000101,
372        OPERAND_BUSY = 0x80000200,
373        PREVIOUS_TLB_EPOCH_BUSY = 0x80000201,
374        SYS_BUSY = 0x80000202,
375        RND_NO_ENTROPY = 0x80000203,
376        OPERAND_BUSY_HOST_PRIORITY = 0x80000204,
377        HOST_PRIORITY_BUSY_TIMEOUT = 0x90000205,
378        PAGE_METADATA_INCORRECT = 0xC0000300,
379        PAGE_ALREADY_FREE = 0x00000301,
380        PAGE_NOT_OWNED_BY_TD = 0xC0000302,
381        PAGE_NOT_FREE = 0xC0000303,
382        TD_ASSOCIATED_PAGES_EXIST = 0xC0000400,
383        SYS_INIT_NOT_PENDING = 0xC0000500,
384        SYS_LP_INIT_NOT_DONE = 0xC0000502,
385        SYS_LP_INIT_DONE = 0xC0000503,
386        SYS_NOT_READY = 0xC0000505,
387        SYS_SHUTDOWN = 0xC0000506,
388        SYS_KEY_CONFIG_NOT_PENDING = 0xC0000507,
389        SYS_STATE_INCORRECT = 0xC0000508,
390        SYS_INVALID_HANDOFF = 0xC0000509,
391        SYS_INCOMPATIBLE_SIGSTRUCT = 0xC000050A,
392        SYS_LP_INIT_NOT_PENDING = 0xC000050B,
393        SYS_CONFIG_NOT_PENDING = 0xC000050C,
394        INCOMPATIBLE_SEAM_CAPABILITIES = 0xC000050D,
395        TD_FATAL = 0xE0000604,
396        TD_NON_DEBUG = 0xC0000605,
397        TDCS_NOT_ALLOCATED = 0xC0000606,
398        LIFECYCLE_STATE_INCORRECT = 0xC0000607,
399        OP_STATE_INCORRECT = 0xC0000608,
400        NO_VCPUS = 0xC0000609,
401        TDCX_NUM_INCORRECT = 0xC0000610,
402        VCPU_STATE_INCORRECT = 0xC0000700,
403        VCPU_ASSOCIATED = 0x80000701,
404        VCPU_NOT_ASSOCIATED = 0x80000702,
405        NO_VALID_VE_INFO = 0xC0000704,
406        MAX_VCPUS_EXCEEDED = 0xC0000705,
407        TSC_ROLLBACK = 0xC0000706,
408        TD_VMCS_FIELD_NOT_INITIALIZED = 0xC0000730,
409        MCS_FIELD_ERROR = 0xC0000731,
410        KEY_GENERATION_FAILED = 0x80000800,
411        TD_KEYS_NOT_CONFIGURED = 0x80000810,
412        KEY_STATE_INCORRECT = 0xC0000811,
413        KEY_CONFIGURED = 0x00000815,
414        WBCACHE_NOT_COMPLETE = 0x80000817,
415        HKID_NOT_FREE = 0xC0000820,
416        NO_HKID_READY_TO_WBCACHE = 0x00000821,
417        WBCACHE_RESUME_ERROR = 0xC0000823,
418        FLUSHVP_NOT_DONE = 0x80000824,
419        NUM_ACTIVATED_HKIDS_NOT_SUPPORTED = 0xC0000825,
420        INCORRECT_CPUID_VALUE = 0xC0000900,
421        LIMIT_CPUID_MAXVAL_SET = 0xC0000901,
422        INCONSISTENT_CPUID_FIELD = 0xC0000902,
423        CPUID_MAX_SUBLEAVES_UNRECOGNIZED = 0xC0000903,
424        CPUID_LEAF_1F_FORMAT_UNRECOGNIZED = 0xC0000904,
425        INVALID_WBINVD_SCOPE = 0xC0000905,
426        INVALID_PKG_ID = 0xC0000906,
427        ENABLE_MONITOR_FSM_NOT_SET = 0xC0000907,
428        CPUID_LEAF_NOT_SUPPORTED = 0xC0000908,
429        SMRR_NOT_LOCKED = 0xC0000910,
430        INVALID_SMRR_CONFIGURATION = 0xC0000911,
431        SMRR_OVERLAPS_CMR = 0xC0000912,
432        SMRR_LOCK_NOT_SUPPORTED = 0xC0000913,
433        SMRR_NOT_SUPPORTED = 0xC0000914,
434        INCONSISTENT_MSR = 0xC0000920,
435        INCORRECT_MSR_VALUE = 0xC0000921,
436        SEAMREPORT_NOT_AVAILABLE = 0xC0000930,
437        SEAMDB_GETREF_NOT_AVAILABLE = 0xC0000931,
438        SEAMDB_REPORT_NOT_AVAILABLE = 0xC0000932,
439        SEAMVERIFYREPORT_NOT_AVAILABLE = 0xC0000933,
440        INVALID_TDMR = 0xC0000A00,
441        NON_ORDERED_TDMR = 0xC0000A01,
442        TDMR_OUTSIDE_CMRS = 0xC0000A02,
443        TDMR_ALREADY_INITIALIZED = 0x00000A03,
444        INVALID_PAMT = 0xC0000A10,
445        PAMT_OUTSIDE_CMRS = 0xC0000A11,
446        PAMT_OVERLAP = 0xC0000A12,
447        INVALID_RESERVED_IN_TDMR = 0xC0000A20,
448        NON_ORDERED_RESERVED_IN_TDMR = 0xC0000A21,
449        CMR_LIST_INVALID = 0xC0000A22,
450        EPT_WALK_FAILED = 0xC0000B00,
451        EPT_ENTRY_FREE = 0xC0000B01,
452        EPT_ENTRY_NOT_FREE = 0xC0000B02,
453        EPT_ENTRY_NOT_PRESENT = 0xC0000B03,
454        EPT_ENTRY_NOT_LEAF = 0xC0000B04,
455        EPT_ENTRY_LEAF = 0xC0000B05,
456        GPA_RANGE_NOT_BLOCKED = 0xC0000B06,
457        GPA_RANGE_ALREADY_BLOCKED = 0x00000B07,
458        TLB_TRACKING_NOT_DONE = 0xC0000B08,
459        EPT_INVALID_PROMOTE_CONDITIONS = 0xC0000B09,
460        PAGE_ALREADY_ACCEPTED = 0x00000B0A,
461        PAGE_SIZE_MISMATCH = 0xC0000B0B,
462        GPA_RANGE_BLOCKED = 0xC0000B0C,
463        EPT_ENTRY_STATE_INCORRECT = 0xC0000B0D,
464        EPT_PAGE_NOT_FREE = 0xC0000B0E,
465        L2_SEPT_WALK_FAILED = 0xC0000B0F,
466        L2_SEPT_ENTRY_NOT_FREE = 0xC0000B10,
467        PAGE_ATTR_INVALID = 0xC0000B11,
468        L2_SEPT_PAGE_NOT_PROVIDED = 0xC0000B12,
469        METADATA_FIELD_ID_INCORRECT = 0xC0000C00,
470        METADATA_FIELD_NOT_WRITABLE = 0xC0000C01,
471        METADATA_FIELD_NOT_READABLE = 0xC0000C02,
472        METADATA_FIELD_VALUE_NOT_VALID = 0xC0000C03,
473        METADATA_LIST_OVERFLOW = 0xC0000C04,
474        INVALID_METADATA_LIST_HEADER = 0xC0000C05,
475        REQUIRED_METADATA_FIELD_MISSING = 0xC0000C06,
476        METADATA_ELEMENT_SIZE_INCORRECT = 0xC0000C07,
477        METADATA_LAST_ELEMENT_INCORRECT = 0xC0000C08,
478        METADATA_FIELD_CURRENTLY_NOT_WRITABLE = 0xC0000C09,
479        METADATA_WR_MASK_NOT_VALID = 0xC0000C0A,
480        METADATA_FIRST_FIELD_ID_IN_CONTEXT = 0x00000C0B,
481        METADATA_FIELD_SKIP = 0x00000C0C,
482        SERVTD_ALREADY_BOUND_FOR_TYPE = 0xC0000D00,
483        SERVTD_TYPE_MISMATCH = 0xC0000D01,
484        SERVTD_ATTR_MISMATCH = 0xC0000D02,
485        SERVTD_INFO_HASH_MISMATCH = 0xC0000D03,
486        SERVTD_UUID_MISMATCH = 0xC0000D04,
487        SERVTD_NOT_BOUND = 0xC0000D05,
488        SERVTD_BOUND = 0xC0000D06,
489        TARGET_UUID_MISMATCH = 0xC0000D07,
490        TARGET_UUID_UPDATED = 0xC0000D08,
491        INVALID_MBMD = 0xC0000E00,
492        INCORRECT_MBMD_MAC = 0xC0000E01,
493        NOT_WRITE_BLOCKED = 0xC0000E02,
494        ALREADY_WRITE_BLOCKED = 0x00000E03,
495        NOT_EXPORTED = 0xC0000E04,
496        MIGRATION_STREAM_STATE_INCORRECT = 0xC0000E05,
497        MAX_MIGS_NUM_EXCEEDED = 0xC0000E06,
498        EXPORTED_DIRTY_PAGES_REMAIN = 0xC0000E07,
499        MIGRATION_DECRYPTION_KEY_NOT_SET = 0xC0000E08,
500        TD_NOT_MIGRATABLE = 0xC0000E09,
501        PREVIOUS_EXPORT_CLEANUP_INCOMPLETE = 0xC0000E0A,
502        NUM_MIGS_HIGHER_THAN_CREATED = 0xC0000E0B,
503        IMPORT_MISMATCH = 0xC0000E0C,
504        MIGRATION_EPOCH_OVERFLOW = 0xC0000E0D,
505        MAX_EXPORTS_EXCEEDED = 0xC0000E0E,
506        INVALID_PAGE_MAC = 0xC0000E0F,
507        MIGRATED_IN_CURRENT_EPOCH = 0xC0000E10,
508        DISALLOWED_IMPORT_OVER_REMOVED = 0xC0000E11,
509        SOME_VCPUS_NOT_MIGRATED = 0xC0000E12,
510        ALL_VCPUS_IMPORTED = 0xC0000E13,
511        MIN_MIGS_NOT_CREATED = 0xC0000E14,
512        VCPU_ALREADY_EXPORTED = 0xC0000E15,
513        INVALID_MIGRATION_DECRYPTION_KEY = 0xC0000E16,
514        INVALID_CPUSVN = 0xC0001000,
515        INVALID_REPORTMACSTRUCT = 0xC0001001,
516        L2_EXIT_HOST_ROUTED_ASYNC = 0x00001100,
517        L2_EXIT_HOST_ROUTED_TDVMCALL = 0x00001101,
518        L2_EXIT_PENDING_INTERRUPT = 0x00001102,
519        PENDING_INTERRUPT = 0x00001120,
520        TD_EXIT_BEFORE_L2_ENTRY = 0x00001140,
521        TD_EXIT_ON_L2_VM_EXIT = 0x00001141,
522        TD_EXIT_ON_L2_TO_L1 = 0x00001142,
523        GLA_NOT_CANONICAL = 0xC0001160,
524        // TDX Connect status codes (TDX Module Base Spec). Stored as the high
525        // 32 bits of the 64-bit RAX status, matching the convention used by the
526        // codes above (e.g. OPERAND_INVALID is Intel's 0xC0000100_00000000).
527        DMAR_INVALID_MAPPING_STATE = 0xC0000F07,
528        TDI_INVALID_STATE = 0xC0000F11,
529        IDE_STREAM_INVALID_STATE = 0xC0000F24,
530        MMIO_TDI_OWNER_MISMATCH = 0xC0000F27,
531        TDI_NOT_PRESENT = 0xC0000F40,
532        TDI_INVALID_METADATA = 0xC0000F41,
533        MMIO_PAGE_NOT_IN_ASSOC_RANGE = 0xC0000F4C,
534        MMIO_INVALID_HPA_OFFSET = 0xC0000F4D,
535    }
536}
537
538impl TdCallResultCode {
539    const fn from_bits(value: u64) -> Self {
540        Self(value as u32)
541    }
542
543    const fn into_bits(self) -> u64 {
544        self.0 as u64
545    }
546}
547
548/// The result returned by a tdcall instruction in rax.
549#[bitfield(u64)]
550pub struct TdCallResult {
551    pub details: u32,
552    #[bits(32)]
553    pub code: TdCallResultCode,
554}
555
556open_enum! {
557    /// The result returned by a tdg.vm.call in r10.
558    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
559    pub enum TdVmCallR10Result: u64 {
560        SUCCESS = 0,
561        RETRY = 1,
562        OPERAND_INVALID = 0x80000000_00000000,
563        GPA_INUSE = 0x80000000_00000001,
564        ALIGN_ERROR = 0x80000000_00000002,
565    }
566}
567
568/// Field size for [`TdxExtendedFieldCode`].
569#[repr(u64)]
570#[derive(Debug)]
571pub enum FieldSize {
572    Invalid = 0,
573    Size16Bit = 1,
574    Size32Bit = 2,
575    Size64Bit = 3,
576}
577
578impl FieldSize {
579    const fn from_bits(value: u64) -> Self {
580        match value {
581            0 => FieldSize::Invalid,
582            1 => FieldSize::Size16Bit,
583            2 => FieldSize::Size32Bit,
584            3 => FieldSize::Size64Bit,
585            _ => panic!("Invalid field size"),
586        }
587    }
588
589    const fn into_bits(self) -> u64 {
590        self as u64
591    }
592}
593
594open_enum! {
595    pub enum TdVpsClassCode: u8 {
596        TD_VMCS = 0,
597        VAPIC = 1,
598        VE_INFO = 2,
599        GUEST_GPR_STATE = 16,
600        GUEST_STATE = 17,
601        GUEST_EXT_STATE = 18,
602        GUEST_MSR_STATE = 19,
603        MANAGEMENT = 32,
604        CPUID_CONTROL = 33,
605        EPT_VIOLATION_LOG = 34,
606        VMCS_1 = 36,
607        MSR_BITMAPS_1 = 37,
608        MSR_BITMAPS_SHADOW_1 = 38,
609        VMCS_2 = 44,
610        MSR_BITMAPS_2 = 45,
611        MSR_BITMAPS_SHADOW_2 = 46,
612        VMCS_3 = 52,
613    }
614}
615
616open_enum! {
617    pub enum TdxContextCode: u8 {
618        PLATFORM = 0,
619        TD = 1,
620        TD_VCPU = 2,
621    }
622}
623
624impl TdxContextCode {
625    const fn from_bits(value: u64) -> Self {
626        Self(value as u8)
627    }
628    const fn into_bits(self) -> u64 {
629        self.0 as u64
630    }
631}
632
633// VCPU-Scope Metadata
634pub const TDX_FIELD_CODE_L2_CTLS_VM1: TdxExtendedFieldCode =
635    TdxExtendedFieldCode(0xA020000300000051);
636pub const TDX_FIELD_CODE_L2_CTLS_VM2: TdxExtendedFieldCode =
637    TdxExtendedFieldCode(0xA020000300000052);
638
639// TD-Scope Metadata
640pub const TDX_FIELD_CODE_CONFIG_FLAGS: TdxExtendedFieldCode =
641    TdxExtendedFieldCode(0x9110000300000016);
642
643/// Field code for the TDCS `TD_CTLS` TD-scope metadata field, accessed via
644/// TDG.VM.RD and TDG.VM.WR.
645pub const TDX_FIELD_CODE_TD_CTLS: TdxExtendedFieldCode = TdxExtendedFieldCode(0x1110000300000017);
646
647/// Metadata field ID for the global-scope `TDX_FEATURES0` field, read by the
648/// guest via the `TDG.SYS.RD` TDCALL.
649pub const TDX_FIELD_ID_TDX_FEATURES0: u64 = 0x0A00000300000008;
650
651/// The global-scope `TDX_FEATURES0` metadata field, enumerating optional TDX
652/// module features. Read by the guest via `TDG.SYS.RD`
653/// ([`TdCallLeaf::SYS_RD`]).
654///
655/// Only the bits relevant to hardware-bound sealing are modeled here; the
656/// remaining bits are documented by the Intel TDX Module ABI specification.
657#[bitfield(u64)]
658#[derive(PartialEq, Eq)]
659pub struct TdxFeatures0 {
660    #[bits(12)]
661    _reserved0: u64,
662    /// `SEALING` (bit 12) - the TDX module supports signed TDs and seal keys
663    /// bound to TD properties, exposing the `TDG.MR.KEY.GET` interface.
664    pub sealing: bool,
665    #[bits(9)]
666    _reserved1: u64,
667    /// `TD_SIGNING_AND_SVN` (bit 22) - prerequisite support that `SEALING`
668    /// depends on.
669    pub td_signing_and_svn: bool,
670    #[bits(27)]
671    _reserved2: u64,
672    /// `SEALKEY_128` (bit 50) - enumerates the seal key size.
673    pub sealkey_128: bool,
674    #[bits(13)]
675    _reserved3: u64,
676}
677
678/// The TDCS `TD_CTLS` TD-scope control field, accessed via TDG.VM.RD and
679/// TDG.VM.WR.
680#[bitfield(u64)]
681#[derive(PartialEq, Eq)]
682pub struct TdCtls {
683    /// `PENDING_VE_DISABLE`
684    pub pending_ve_disable: bool,
685    /// `ENUM_TOPOLOGY`
686    pub enum_topology: bool,
687    /// `VIRT_CPUID2`
688    pub virt_cpuid2: bool,
689    /// `REDUCE_VE`
690    pub reduce_ve: bool,
691    /// `ENABLE_HW_SEAL_KEYS` - opts the TD into hardware-bound seal keys
692    /// returned by `TDG.MR.KEY.GET`, even when
693    /// `CONFIG_FLAGS.SEAL_KEY_SUPPORT` is not set.
694    pub enable_hw_seal_keys: bool,
695    #[bits(58)]
696    _reserved: u64,
697    /// `LOCK` - once set, `TD_CTLS` becomes read-only.
698    pub lock: bool,
699}
700
701/// Extended field code for the Metadata Access Interface TDCalls:
702/// TDG.VP.WR, TDG.VP.RD, TDG.VM.WR, TDG.VM.RD
703#[bitfield(u64)]
704#[derive(PartialEq, Eq)]
705pub struct TdxExtendedFieldCode {
706    #[bits(24)]
707    pub field_code: u32,
708    #[bits(8)]
709    _reserved0: u64,
710    #[bits(2)]
711    pub field_size: FieldSize,
712    #[bits(4)]
713    pub last_element: u8,
714    #[bits(9)]
715    pub last_field: u16,
716    #[bits(3)]
717    _reserved1: u64,
718    pub increment_size: bool,
719    pub write_mask_valid: bool,
720    #[bits(3)]
721    pub context_code: TdxContextCode,
722    #[bits(1)]
723    _reserved2: u64,
724    #[bits(6)]
725    pub class_code: u8,
726    #[bits(1)]
727    _reserved3: u64,
728    pub non_arch: bool,
729}
730
731/// Instruction info returned in r11 for a TDG.VP.ENTER call.
732#[bitfield(u64)]
733pub struct TdxInstructionInfo {
734    pub info: u32,
735    pub length: u32,
736}
737
738#[bitfield(u64)]
739pub struct TdxL2Ctls {
740    pub enable_shared_ept: bool,
741    pub enable_tdvmcall: bool,
742    #[bits(62)]
743    pub reserved: u64,
744}
745
746#[bitfield(u64)]
747pub struct TdxVpEnterRaxResult {
748    /// The VMX exit code for VP.ENTER, if valid.
749    #[bits(32)]
750    pub vmx_exit: vmx::VmxExit,
751    /// The TDX specific exit code.
752    #[bits(32)]
753    pub tdx_exit: TdCallResultCode,
754}
755
756#[bitfield(u64)]
757pub struct TdxExtendedExitQualification {
758    #[bits(4)]
759    pub ty: TdxExtendedExitQualificationType,
760    #[bits(60)]
761    _reserved: u64,
762}
763
764open_enum! {
765    pub enum TdxExtendedExitQualificationType: u8 {
766        NONE = 0,
767        PENDING_EPT_VIOLATION = 6,
768    }
769}
770
771impl TdxExtendedExitQualificationType {
772    const fn from_bits(value: u64) -> Self {
773        Self(value as u8)
774    }
775
776    const fn into_bits(self) -> u64 {
777        self.0 as u64
778    }
779}
780
781/// The GPR list used for TDG.VP.ENTER. Specified in the TDX specification as
782/// L2_ENTER_GUEST_STATE.
783#[repr(C)]
784#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
785pub struct TdxL2EnterGuestState {
786    /// GPs in the usual order.
787    pub gps: [u64; 16],
788    pub rflags: u64,
789    pub rip: u64,
790    pub ssp: u64,
791    pub rvi: u8, // GUEST_INTERRUPT_STATUS lower bits
792    pub svi: u8, // GUEST_INTERRUPT_STATUS upper bits
793    pub reserved: [u8; 6],
794}
795
796pub enum TdxGp {}
797impl TdxGp {
798    pub const RAX: usize = 0;
799    pub const RCX: usize = 1;
800    pub const RDX: usize = 2;
801    pub const RBX: usize = 3;
802    pub const RSP: usize = 4;
803    pub const RBP: usize = 5;
804    pub const RSI: usize = 6;
805    pub const RDI: usize = 7;
806    pub const R8: usize = 8;
807    pub const R9: usize = 9;
808    pub const R10: usize = 10;
809    pub const R11: usize = 11;
810    pub const R12: usize = 12;
811    pub const R13: usize = 13;
812    pub const R14: usize = 14;
813    pub const R15: usize = 15;
814}
815
816#[bitfield(u64)]
817pub struct TdxGlaListInfo {
818    #[bits(9)]
819    pub first_entry: u64,
820    #[bits(3)]
821    _reserved_z0: u64,
822    #[bits(40)]
823    pub list_gpa: u64,
824    #[bits(10)]
825    pub num_entries: u64,
826    #[bits(2)]
827    _reserved_z1: u64,
828}
829
830#[bitfield(u64)]
831pub struct TdGlaVmAndFlags {
832    pub list: bool,
833    #[bits(51)]
834    _reserved_z0: u64,
835    #[bits(2)]
836    pub vm_index: u64,
837    #[bits(10)]
838    _reserved_z1: u64,
839}
840
841#[bitfield(u64)]
842#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
843pub struct TdxVmFlags {
844    #[bits(2)]
845    pub invd_translations: u8,
846
847    #[bits(50)]
848    _reserved: u64,
849
850    /// Starts at 1, not 0.
851    #[bits(2)]
852    pub vm_index: u8,
853
854    #[bits(10)]
855    _reserved_2: u64,
856}
857
858pub const TDX_VP_ENTER_INVD_INVEPT: u8 = 1;
859pub const TDX_VP_ENTER_INVD_INVVPID: u8 = 2;
860pub const TDX_VP_ENTER_INVD_INVVPID_NON_GLOBAL: u8 = 3;
861
862/// Report structure.
863/// See `TDREPORT_STRUCT` in Table 3.29, "Intel TDX Module v1.5 ABI specification", March 2024.
864#[repr(C)]
865#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
866pub struct TdReport {
867    /// An instance of [`ReportMac`]
868    pub report_mac_struct: ReportMac,
869    /// An instance of [`TeeTcbInfo`].
870    pub tee_tcb_info: TeeTcbInfo,
871    /// Reserved
872    pub _reserved: [u8; 17],
873    /// An instance of [`TdInfo`].
874    pub td_info: TdInfo,
875}
876
877static_assertions::const_assert_eq!(TDX_REPORT_SIZE, size_of::<TdReport>());
878
879/// See `REPORTMACSTRUCT` in Table 3.31, "Intel TDX Module v1.5 ABI specification", March 2024.
880#[repr(C)]
881#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
882pub struct ReportMac {
883    /// Type header structure
884    pub report_type: ReportType,
885    /// Must be zero
886    pub _reserved0: [u8; 12],
887    /// CPU SVN
888    pub cpu_svn: [u8; 16],
889    /// SHA384 of [`TeeTcbInfo`]
890    pub tee_tcb_info_hash: [u8; 48],
891    /// SHA384 of [`TdInfo`] for TDX
892    pub tee_info_hash: [u8; 48],
893    /// A set of data used for communication between the caller and the target
894    pub report_data: [u8; TDX_REPORT_DATA_SIZE],
895    /// Must be zero
896    pub _reserved1: [u8; 32],
897    /// The MAC over above data.
898    pub mac: [u8; 32],
899}
900
901/// See `REPORTTYPE` in Table 3.32, "Intel TDX Module v1.5 ABI specification", March 2024.
902#[repr(C)]
903#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
904pub struct ReportType {
905    /// TEE type
906    /// 0x00: SGX
907    /// 0x81: TDX
908    pub tee_type: u8,
909    /// TEE type-specific subtype
910    /// 0: Standard TDX report
911    pub sub_type: u8,
912    /// TEE type-specific version
913    /// For TDX
914    ///    0: `TDINFO_STRUCT.SERVTD_HASH` is not used (all 0's)
915    ///    1: `TDINFO_STRUCT.SERVTD_HASH` is used
916    pub version: u8,
917    /// Must be zero
918    pub _reserved: u8,
919}
920
921/// See `TEE_TCB_INFO` in Table 3.29, "Intel TDX Module v1.5 ABI specification", March 2024.
922#[repr(C)]
923#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
924pub struct TeeTcbInfo {
925    /// Indicates which fields are valid.
926    /// Set to 0x301ff.
927    pub valid: [u8; 8],
928    /// [`TeeTcbSvn`] of the TDX module that created the TD on the current
929    /// platform.
930    pub tee_tcb_svn: TeeTcbSvn,
931    /// The measurement of the TDX module that created the TD on the
932    /// current platform.
933    pub mr_seam: [u8; 48],
934    /// Set to all 0's.
935    pub mr_signer_seam: [u8; 48],
936    /// Set to all 0's.
937    pub attributes: [u8; 8],
938    /// [`TeeTcbSvn`] of the current TDX module on the current platform.
939    pub tee_tcb_svn2: TeeTcbSvn,
940    /// Reserved
941    pub reserved: [u8; 95],
942}
943
944/// See `TEE_TCB_SVN` in Section 3.9.4, "Intel TDX Module v1.5 ABI specification", March 2024.
945#[repr(C)]
946#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
947pub struct TeeTcbSvn {
948    /// TDX module minor SVN
949    pub tdx_module_svn_minor: u8,
950    /// TDX module major SVN
951    pub tdx_module_svn_major: u8,
952    /// Microcode SE_SVN at the time the TDX module was loaded
953    pub seam_last_patch_svn: u8,
954    /// Reserved
955    pub _reserved: [u8; 13],
956}
957
958/// See `TDINFO_STRUCT` in Table 3.33, "Intel TDX Module v1.5 ABI specification", March 2024.
959#[repr(C)]
960#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
961pub struct TdInfo {
962    /// An instance of [`TdInfoBase`]
963    pub td_info_base: TdInfoBase,
964    /// Must be zero when `version` in [`ReportType`] is 0 or 1.
965    pub td_info_extension: [u8; 64],
966}
967
968/// Run-time extendable measurement register.
969pub type Rtmr = [u8; 48];
970
971/// See `ATTRIBUTES` in Table 3.9, "Intel TDX Module v1.5 ABI specification", March 2024.
972#[bitfield(u64)]
973#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
974pub struct TdAttributes {
975    #[bits(1)]
976    pub debug: bool,
977    #[bits(3)]
978    _reserved1: u8,
979    #[bits(1)]
980    pub hgs_plus_prof: bool,
981    #[bits(1)]
982    pub perf_prof: bool,
983    #[bits(1)]
984    pub pmt_prof: bool,
985    #[bits(9)]
986    _reserved2: u16,
987    #[bits(7)]
988    _reserved_p: u8,
989    #[bits(4)]
990    _reserved_n: u8,
991    #[bits(1)]
992    pub lass: bool,
993    #[bits(1)]
994    pub sept_ve_disable: bool,
995    #[bits(1)]
996    pub migratable: bool,
997    #[bits(1)]
998    pub pks: bool,
999    #[bits(1)]
1000    pub kl: bool,
1001    #[bits(24)]
1002    _reserved3: u32,
1003    #[bits(6)]
1004    _reserved4: u32,
1005    #[bits(1)]
1006    pub tpa: bool,
1007    #[bits(1)]
1008    pub perfmon: bool,
1009}
1010
1011/// See `TDINFO_BASE` in Table 3.34, "Intel TDX Module v1.5 ABI specification", March 2024.
1012#[repr(C)]
1013#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
1014pub struct TdInfoBase {
1015    /// TD's attributes
1016    pub attributes: TdAttributes,
1017    /// TD's XFAM
1018    pub xfam: [u8; 8],
1019    /// Measurement of the initial contents of the TDX in SHA384
1020    pub mr_td: [u8; 48],
1021    /// Software-defined ID for non-owner-defined configuration of the guest TD
1022    /// in SHA384
1023    pub mr_config_id: [u8; 48],
1024    /// Software-defined ID for the guest TD's owner in SHA384
1025    pub mr_owner: [u8; 48],
1026    /// Software-defined ID for owner-defined configuration of the guest TD
1027    /// in SHA384
1028    pub mr_owner_config: [u8; 48],
1029    /// Array of 4 [`Rtmr`]
1030    pub rtmr: [Rtmr; 4],
1031    /// SHA384 of the `TDINFO_STRUCTs` of bound service TDs if there is any.
1032    pub servd_hash: [u8; 48],
1033}
1034
1035/// Selects which TD-specific measurement and configuration registers are mixed
1036/// into the key derived by the `TDG.MR.KEY.GET` TDCALL. This is the analog of
1037/// SNP's [`GuestFieldSelect`](crate::snp::GuestFieldSelect).
1038///
1039/// See `TDKEYPOLICY`, Table 2-2, "System Architecture Specification: Sealing
1040/// Support for Intel TDX", Revision 0.7, September 2025.
1041#[bitfield(u64)]
1042#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
1043pub struct TdxKeyPolicy {
1044    /// Include `MRTD` (bit 0).
1045    pub mr_td: bool,
1046    /// Reserved (bit 1).
1047    _reserved0: bool,
1048    /// Include `MROWNER` (bit 2).
1049    pub mr_owner: bool,
1050    /// Include `MRCONFIGID` (bit 3).
1051    pub mr_config_id: bool,
1052    /// Include `MROWNERCONFIG` (bit 4).
1053    pub mr_owner_config: bool,
1054    /// Reserved (bits 31:5).
1055    #[bits(27)]
1056    _reserved1: u32,
1057    /// `RTMR` selection (bits 47:32). Bits 4:0 select `RTMR n`, bits 7:5 are
1058    /// reserved and must be zero.
1059    #[bits(16)]
1060    pub rtmr: u16,
1061    /// Reserved (bits 63:48), must be zero.
1062    #[bits(16)]
1063    _reserved2: u16,
1064}
1065
1066/// `KEYNAME` value for [`TdKeyRequest::key_name`] that requests a seal key.
1067pub const TDX_KEY_NAME_SEAL: u16 = 0;
1068
1069/// `KEYSIZE` value for [`TdKeyRequest::key_size`] selecting a 128-bit key.
1070pub const TDX_KEY_SIZE_128: u8 = 0;
1071
1072/// `KEYSIZE` value for [`TdKeyRequest::key_size`] selecting a 256-bit key.
1073pub const TDX_KEY_SIZE_256: u8 = 1;
1074
1075/// Request structure (`TDKEYREQUEST`) that is passed as the input to the
1076/// `TDG.MR.KEY.GET` TDCALL to derive a persistent key bound to the TD's
1077/// measurements and policy.
1078///
1079/// See `TDKEYREQUEST`, Table 2-1, "System Architecture Specification: Sealing
1080/// Support for Intel TDX", Revision 0.7, September 2025.
1081#[repr(C)]
1082#[derive(Debug, Clone, Copy, IntoBytes, Immutable, KnownLayout, FromBytes)]
1083pub struct TdKeyRequest {
1084    /// `KEYNAME` - Identifies the key being requested. See
1085    /// [`TDX_KEY_NAME_SEAL`].
1086    pub key_name: u16,
1087    /// `SWKEYNAME` - TD software-assigned key name/identifier.
1088    pub sw_key_name: u8,
1089    /// `KEYSIZE` - Requested key size. See [`TDX_KEY_SIZE_128`] and
1090    /// [`TDX_KEY_SIZE_256`].
1091    pub key_size: u8,
1092    /// Reserved.
1093    pub _reserved0: [u8; 4],
1094    /// `KEYPOLICY` - Selects which measurement and configuration registers are
1095    /// mixed into the derived key.
1096    pub key_policy: TdxKeyPolicy,
1097    /// `ATTRIBUTESMASK` - Mask applied to `TDCS.ATTRIBUTES` before mixing into
1098    /// the key.
1099    pub attributes_mask: u64,
1100    /// `XFAMMASK` - Mask applied to `TDCS.XFAM` before mixing into the key.
1101    pub xfam_mask: u64,
1102    /// `CPUSVN`
1103    pub cpu_svn: [u8; 16],
1104    /// `TEE_TCB_SVN`
1105    pub tee_tcb_svn: [u8; 16],
1106    /// `ISVSVN`
1107    pub isv_svn: u16,
1108    /// `MRCONFIGSVN`
1109    pub mr_config_svn: u16,
1110    /// `MROWNERCONFIGSVN`
1111    pub mr_owner_config_svn: u16,
1112    /// `SALT` - Caller-supplied salt mixed into the derived key, allowing the
1113    /// same TD to derive multiple distinct keys.
1114    pub salt: [u8; 32],
1115    /// Reserved.
1116    pub _reserved1: [u8; 26],
1117}
1118
1119static_assertions::const_assert_eq!(TDX_TDKEYREQUEST_SIZE, size_of::<TdKeyRequest>());