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