tdx_guest/
tdcall.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright(c) 2023-2024 Intel Corporation.
3
4//! The TDCALL instruction causes a VM exit to the Intel TDX module.
5//!
6//! It is used to call guest-side Intel TDX functions. For more information about
7//! TDCALL, please refer to the [Intel® TDX Module v1.5 ABI Specification](https://cdrdv2.intel.com/v1/dl/getContent/733579)
8
9use core::fmt;
10
11use bitflags::bitflags;
12
13use crate::asm::asm_td_call;
14
15/// TDCALL Instruction Leaf Numbers Definition.
16#[repr(u64)]
17pub enum TdcallNum {
18    VpInfo = 1,
19    MrRtmrExtend = 2,
20    VpVeinfoGet = 3,
21    MrReport = 4,
22    VpCpuidveSet = 5,
23    MemPageAccept = 6,
24    VmRd = 7,
25    VmWr = 8,
26    ServetdRd = 18,
27    ServetdWr = 20,
28    MrVerifyreport = 22,
29    MemPageAttrRd = 23,
30    MemPageAttrWr = 24,
31    VpEnter = 25,
32    VpInvept = 26,
33    VpInvgla = 27,
34}
35
36bitflags! {
37    /// GuestTdAttributes is defined as a 64b field that specifies various guest TD attributes.
38    /// It is reported to the guest TD by TDG.VP.INFO and as part of TDREPORT_STRUCT returned by TDG.MR.REPORT.
39    pub struct GuestTdAttributes: u64 {
40        /// Guest TD runs in off-TD debug mode.
41        /// Its VCPU state and private memory are accessible by the host VMM.
42        const DEBUG = 1 << 0;
43        /// TD is migratable (using a Migration TD).
44        const MIGRATABLE = 1 << 29;
45        /// TD is allowed to use Supervisor Protection Keys.
46        const PKS = 1 << 30;
47        /// TD is allowed to use Key Locker. Must be 0.
48        const KL = 1 << 31;
49        /// TD is allowed to use Perfmon and PERF_METRICS capabilities.
50        const PERFMON = 1 << 63;
51    }
52}
53
54bitflags! {
55    /// Controls whether CPUID executed by the guest TD will cause #VE unconditionally.
56    struct CpuidveFlag: u64 {
57        /// Flags that when CPL is 0, a CPUID executed
58        /// by the guest TD will cause a #VE unconditionally.
59        const SUPERVISOR = 1 << 0;
60        /// Flags that when CPL > 0, a CPUID executed
61        /// by the guest TD will cause a #VE unconditionally.
62        const USER = 1 << 1;
63    }
64}
65
66bitflags! {
67    /// GPA Attributes (Single VM) Definition.
68    pub struct GpaAttr: u16 {
69        /// Read.
70        const R = 1;
71        /// Write.
72        const W = 1 << 1;
73        /// Execute (Supervisor).
74        const XS = 1 << 2;
75        /// Execute (User).
76        const XU = 1 << 3;
77        /// Verify Guest Paging.
78        const VGP = 1 << 4;
79        /// Paging-Write Access.
80        const PWA = 1 << 5;
81        /// Supervisor Shadow Stack.
82        const SSS = 1 << 6;
83        /// Suppress #VE.
84        const SVE = 1 << 7;
85        /// Indicates that the other bits are valid.
86        /// If its value is 0, other fields are reserved and must be 0.
87        const VALID = 1 << 15;
88    }
89}
90pub struct PageAttr {
91    /// Actual GPA mapping of the page.
92    gpa_mapping: u64,
93    /// Guest-visible page attributes.
94    gpa_attr: GpaAttrAll,
95}
96
97/// GPA Attributes (all VMs) Definition.
98pub struct GpaAttrAll {
99    /// L1 GPA attributes.
100    l1_attr: GpaAttr,
101    /// GPA attributes for L2 VM #1.
102    vm1_attr: GpaAttr,
103    /// GPA attributes for L2 VM #2.
104    vm2_attr: GpaAttr,
105    /// GPA attributes for L2 VM #3.
106    vm3_attr: GpaAttr,
107}
108
109impl From<u64> for GpaAttrAll {
110    fn from(val: u64) -> Self {
111        GpaAttrAll {
112            l1_attr: GpaAttr::from_bits_truncate((val & 0xFFFF) as u16),
113            vm1_attr: GpaAttr::from_bits_truncate(((val >> 16) & 0xFFFF) as u16),
114            vm2_attr: GpaAttr::from_bits_truncate(((val >> 32) & 0xFFFF) as u16),
115            vm3_attr: GpaAttr::from_bits_truncate(((val >> 48) & 0xFFFF) as u16),
116        }
117    }
118}
119
120impl From<GpaAttrAll> for u64 {
121    fn from(s: GpaAttrAll) -> Self {
122        let field1 = s.l1_attr.bits() as u64;
123        let field2 = (s.vm1_attr.bits() as u64) << 16;
124        let field3 = (s.vm2_attr.bits() as u64) << 32;
125        let field4 = (s.vm3_attr.bits() as u64) << 48;
126        field4 | field3 | field2 | field1
127    }
128}
129
130#[repr(C)]
131#[derive(Debug)]
132pub struct TdReport {
133    /// REPORTMACSTRUCT for the TDG.MR.REPORT.
134    pub report_mac: ReportMac,
135    /// Additional attestable elements in the TD’s TCB are not reflected in the
136    /// REPORTMACSTRUCT.CPUSVN – includes the Intel TDX module measurements.
137    pub tee_tcb_info: [u8; 239],
138    pub reserved: [u8; 17],
139    /// TD’s attestable properties.
140    pub tdinfo: TdInfo,
141}
142
143#[repr(C)]
144#[derive(Debug)]
145pub struct ReportMac {
146    /// Type Header Structure.
147    pub report_type: ReportType,
148    pub cpu_svn: [u8; 16],
149    /// SHA384 of TEE_TCB_INFO for TEEs implemented using Intel TDX.
150    pub tee_tcb_info_hash: [u8; 48],
151    /// SHA384 of TEE_INFO: a TEE-specific info structure (TDINFO_STRUCT or SGXINFO)
152    /// or 0 if no TEE is represented.
153    pub tee_info_hash: [u8; 48],
154    /// A set of data used for communication between the caller and the target.
155    pub report_data: [u8; 64],
156    pub reserved: [u8; 32],
157    /// The MAC over the REPORTMACSTRUCT with model-specific MAC.
158    pub mac: [u8; 32],
159}
160
161#[derive(Debug)]
162pub enum TeeType {
163    SGX,
164    TDX,
165}
166
167/// REPORTTYPE indicates the reported Trusted Execution Environment (TEE) type,
168/// sub-type and version.
169#[repr(C)]
170#[derive(Debug)]
171pub struct ReportType {
172    /// Trusted Execution Environment (TEE) Type. 0x00: SGX, 0x81: TDX.
173    pub tee_type: TeeType,
174    /// TYPE-specific subtype.
175    pub sub_type: u8,
176    /// TYPE-specific version.
177    pub version: u8,
178    pub reserved: u8,
179}
180
181/// TDINFO_STRUCT is defined as the TDX-specific TEE_INFO part of TDG.MR.REPORT.
182///
183/// It contains the measurements and initial configuration of the TD that was
184/// locked at initialization and a set of measurement registers that are run-time
185/// extendable. These values are copied from the TDCS by the TDG.MR.REPORT function.
186/// Refer to the [TDX Module Base Spec] for additional details.
187#[repr(C)]
188#[derive(Debug)]
189pub struct TdInfo {
190    /// TD’s ATTRIBUTES.
191    pub attributes: u64,
192    /// TD’s XFAM.
193    pub xfam: u64,
194    /// Measurement of the initial contents of the TD.
195    pub mrtd: [u8; 48],
196    /// Software-defined ID for non-owner-defined configuration of the
197    /// guest TD – e.g., run-time or OS configuration.
198    pub mr_config_id: [u8; 48],
199    /// Software-defined ID for the guest TD’s owner.
200    pub mr_owner: [u8; 48],
201    /// Software-defined ID for owner-defined configuration of the
202    /// guest TD – e.g., specific to the workload rather than the run-time or OS.
203    pub mr_owner_config: [u8; 48],
204    /// Array of NUM_RTMRS (4) run-time extendable measurement registers.
205    pub rtmr0: [u8; 48],
206    pub rtmr1: [u8; 48],
207    pub rtmr2: [u8; 48],
208    pub rtmr3: [u8; 48],
209    /// If is one or more bound or pre-bound service TDs, SERVTD_HASH is the SHA384 hash of the
210    /// TDINFO_STRUCTs of those service TDs bound. Else, SERVTD_HASH is 0.
211    pub servtd_hash: [u8; 48],
212    pub reserved: [u8; 64],
213}
214
215#[repr(C)]
216#[derive(Debug)]
217pub struct TdgVeInfo {
218    pub exit_reason: u32,
219    /// the 64-bit value that would have been saved into the VMCS as an exit qualification
220    /// if a legacy VM exit had occurred instead of the virtualization exception.
221    pub exit_qualification: u64,
222    /// the 64-bit value that would have been saved into the VMCS as a guestlinear address
223    /// if a legacy VM exit had occurred instead of the virtualization exception.
224    pub guest_linear_address: u64,
225    /// the 64-bit value that would have been saved into the VMCS as a guestphysical address
226    /// if a legacy VM exit had occurred instead of the virtualization exception.
227    pub guest_physical_address: u64,
228    /// The 32-bit value that would have been saved into the VMCS as VM-exit instruction
229    /// length if a legacy VM exit had occurred instead of the virtualization exception.
230    pub exit_instruction_length: u32,
231    /// The 32-bit value that would have been saved into the VMCS as VM-exit instruction
232    /// information if a legacy VM exit had occurred instead of the virtualization exception.
233    pub exit_instruction_info: u32,
234}
235
236#[derive(Debug)]
237pub enum Gpaw {
238    Bit48,
239    Bit52,
240}
241
242impl From<u64> for Gpaw {
243    fn from(val: u64) -> Self {
244        match val {
245            48 => Self::Bit48,
246            52 => Self::Bit52,
247            _ => panic!("Invalid gpaw"),
248        }
249    }
250}
251
252impl From<Gpaw> for u64 {
253    fn from(s: Gpaw) -> Self {
254        match s {
255            Gpaw::Bit48 => 48,
256            Gpaw::Bit52 => 52,
257        }
258    }
259}
260
261impl fmt::Display for Gpaw {
262    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263        match self {
264            Gpaw::Bit48 => write!(f, "48-bit"),
265            Gpaw::Bit52 => write!(f, "52-bit"),
266        }
267    }
268}
269
270#[derive(Debug)]
271pub struct TdgVpInfo {
272    /// The effective GPA width (in bits) for this TD (do not confuse with MAXPA).
273    /// SHARED bit is at GPA bit GPAW-1.
274    ///
275    /// Only GPAW values 48 and 52 are possible.
276    pub gpaw: Gpaw,
277    /// The TD's ATTRIBUTES (provided as input to TDH.MNG.INIT)
278    pub attributes: GuestTdAttributes,
279    pub num_vcpus: u32,
280    pub max_vcpus: u32,
281    pub vcpu_index: u32,
282    /// Indicates that the TDG.SYS.RD/RDM/RDCALL function are avaliable.
283    pub sys_rd: u32,
284}
285
286/// L2EnterGuestState is used as input and output of enter_l2_vcpu.
287///
288/// It is an array of general-purpose (GPR) register values, organized according to their architectural number, with additional values of RFLAG, RIP and SSP.
289pub struct L2EnterGuestState {
290    pub rax: u64,
291    pub rcx: u64,
292    pub rdx: u64,
293    pub rbx: u64,
294    pub rsp: u64,
295    pub rbp: u64,
296    pub rsi: u64,
297    pub rdi: u64,
298    pub r8: u64,
299    pub r9: u64,
300    pub r10: u64,
301    pub r11: u64,
302    pub r12: u64,
303    pub r13: u64,
304    pub r14: u64,
305    pub r15: u64,
306    pub rflags: u64,
307    pub rip: u64,
308    pub ssp: u64,
309    // Bit 0:7: RVI, Bit 8-15: SVI
310    pub guest_interrupt_status: u16,
311}
312
313/// Controls how enter_l2_vcpu flushes the TLB context and extended paging structure (EPxE) caches
314/// associated with the L2 VM before entering the L2 VCPU.
315#[derive(Clone)]
316pub enum InvdTranslations {
317    NoInvalidation,
318    /// Invalidate all TLB entries and extended paging-structure translations (EPxE) associated with the L2 VM being entered.
319    InvdTlbAndEpxe,
320    /// Invalidate all TLB entries associated with the L2 VM being entered.
321    InvdTlb,
322    /// Invalidate TLB entries associated with the L2 VM being entered, excluding global translations.
323    InvdTlbExpGlobalTranslations,
324}
325
326impl From<u64> for InvdTranslations {
327    fn from(val: u64) -> Self {
328        match val {
329            0 => Self::NoInvalidation,
330            1 => Self::InvdTlbAndEpxe,
331            2 => Self::InvdTlb,
332            3 => Self::InvdTlbExpGlobalTranslations,
333            _ => panic!("Invalid value"),
334        }
335    }
336}
337
338enum Gla {
339    ListEntry(GlaListEntry),
340    ListInfo(GlaListInfo),
341}
342
343impl Gla {
344    fn value(&self) -> u64 {
345        match *self {
346            Gla::ListEntry(GlaListEntry(value)) => value,
347            Gla::ListInfo(GlaListInfo(value)) => value,
348        }
349    }
350}
351
352/// The `GlaListEntry` species a range of consecutive guest linear addresses, each aligned on 4KB.
353///
354/// The `GlaListEntry` consists of the following fields:
355/// - **Bit 0-11**  LAST_GLA_INDEX: Index of the last 4KB-aligned linear address to be processed.
356/// - **Bit 12-63** BASE_GLA: Bits 63:12 of the guest linear address of the first 4KB page to be processed.
357pub struct GlaListEntry(u64);
358
359/// The `GlaListInfo` is used as a GPR input and output operand of TDG.VP.INVGLA.
360///
361/// It provides the GPA of the GLA list page in private memory,
362/// the index of the first entry and the number of entries to be processed.
363///
364/// The `GlaListInfo` consists of the following fields:
365/// - **Bit 0-8**    FIRST_ENTRY: Index of the first entry of the list to be processed.
366/// - **Bit 9-11**   RESERVED: Reserved: must be 0.
367/// - **Bit 12-51**  LIST_GPA: Bits 51:12 of the guest physical address of the GLA list page, which must be a private GPA.
368/// - **Bit 52-61**  NUM_ENTRIES: Number of entries in the GLA list to be processed, must be between 0 through 512.
369/// - **Bit 62-63**  RESERVED: Reserved: must be 0.
370pub struct GlaListInfo(u64);
371
372#[derive(Debug, PartialEq)]
373pub enum TdCallError {
374    /// There is no valid #VE information.
375    TdxNoValidVeInfo,
376    /// Operand is invalid.
377    TdxOperandInvalid,
378    /// The operand is busy (e.g., it is locked in Exclusive mode).
379    TdxOperandBusy,
380    /// Page has already been accepted.
381    TdxPageAlreadyAccepted,
382    /// Requested page size does not match the current GPA mapping size.
383    TdxPageSizeMismatch,
384    /// The provided FIELD_ID is incorrect.
385    TdxMetadataFieldIdIncorrect,
386    /// Field code and write mask are for a read-only field.
387    TdxMetadataFieldNotWritable,
388    /// Field code is for an unreadable field.
389    TdxMetadataFieldNotReadable,
390    /// The provided field value is not valid.
391    TdxMetadataFieldValueNotValid,
392    /// The TD's OP_STATE is incorrect for the required operation.
393    TdxOpStateIncorrect,
394    /// Operand address is out of range (e.g., not in a TDMR).
395    TdxOperandAddrRangeError,
396    /// Physical page metadata (in PAMT) are incorrect for the requested operation.
397    TdxPageMetadataIncorrect,
398    /// Service TD hash of TDINFO_STRUCT does not match the currently bound hash.
399    TdxServtdInfoHashMismatch,
400    /// Service TD is not bound.
401    TdxServtdNotBound,
402    /// Service TD UUID does not match the currently bound UUID.
403    TdxServtdUuidMismatch,
404    /// Target TD UUID does not match the requested TD_UUID.
405    TdxTargetUuidMismatch,
406    /// Target TD UUID does not match the requested TD_UUID, but pre-migration target TD UUID does match it.
407    TdxTargetUuidUpdated,
408    /// TD is in a FATAL error state.
409    TdxTdFatal,
410    /// TD keys have not been configured on the hardware.
411    TdxTdKeysNotConfigured,
412    /// TDCS pages have not been allocated.
413    TdxTdcsNotAllocated,
414    Other,
415}
416
417impl From<u64> for TdCallError {
418    fn from(val: u64) -> Self {
419        match val {
420            0x0000_0B0A => Self::TdxPageAlreadyAccepted,
421            0x8000_0200 => Self::TdxOperandBusy,
422            0x8000_0810 => Self::TdxTdKeysNotConfigured,
423            0xC000_0100 => Self::TdxOperandInvalid,
424            0xC000_0101 => Self::TdxOperandAddrRangeError,
425            0xC000_0300 => Self::TdxPageMetadataIncorrect,
426            0xC000_0606 => Self::TdxTdcsNotAllocated,
427            0xC000_0608 => Self::TdxOpStateIncorrect,
428            0xC000_0704 => Self::TdxNoValidVeInfo,
429            0xC000_0B0B => Self::TdxPageSizeMismatch,
430            0xC000_0C00 => Self::TdxMetadataFieldIdIncorrect,
431            0xC000_0C01 => Self::TdxMetadataFieldNotWritable,
432            0xC000_0C02 => Self::TdxMetadataFieldNotReadable,
433            0xC000_0C03 => Self::TdxMetadataFieldValueNotValid,
434            0xC000_0D03 => Self::TdxServtdInfoHashMismatch,
435            0xC000_0D04 => Self::TdxServtdUuidMismatch,
436            0xC000_0D05 => Self::TdxServtdNotBound,
437            0xC000_0D07 => Self::TdxTargetUuidMismatch,
438            0xC000_0D08 => Self::TdxTargetUuidUpdated,
439            0xE000_0604 => Self::TdxTdFatal,
440            _ => Self::Other,
441        }
442    }
443}
444
445#[repr(C)]
446#[derive(Default)]
447pub(crate) struct TdcallArgs {
448    rax: u64,
449    rcx: u64,
450    rdx: u64,
451    r8: u64,
452    r9: u64,
453    r10: u64,
454    r11: u64,
455    r12: u64,
456    r13: u64,
457}
458
459pub enum TdxVirtualExceptionType {
460    Hlt,
461    Io,
462    MsrRead,
463    MsrWrite,
464    CpuId,
465    VmCall,
466    Mwait,
467    Monitor,
468    EptViolation,
469    Wbinvd,
470    Rdpmc,
471    Other,
472}
473
474impl From<u32> for TdxVirtualExceptionType {
475    fn from(val: u32) -> Self {
476        match val {
477            10 => Self::CpuId,
478            12 => Self::Hlt,
479            15 => Self::Rdpmc,
480            18 => Self::VmCall,
481            30 => Self::Io,
482            31 => Self::MsrRead,
483            32 => Self::MsrWrite,
484            36 => Self::Mwait,
485            39 => Self::Monitor,
486            48 => Self::EptViolation,
487            54 => Self::Wbinvd,
488            _ => Self::Other,
489        }
490    }
491}
492
493#[derive(Debug)]
494pub enum InitError {
495    TdxVendorIdError,
496    TdxCpuLeafIdError,
497    TdxGetVpInfoError(TdCallError),
498}
499
500impl From<TdCallError> for InitError {
501    fn from(error: TdCallError) -> Self {
502        InitError::TdxGetVpInfoError(error)
503    }
504}
505
506/// Get guest TD execution environment information.
507pub fn get_tdinfo() -> Result<TdgVpInfo, TdCallError> {
508    let mut args = TdcallArgs {
509        rax: TdcallNum::VpInfo as u64,
510        ..Default::default()
511    };
512    td_call(&mut args)?;
513    Ok(TdgVpInfo {
514        gpaw: Gpaw::from(args.rcx),
515        attributes: GuestTdAttributes::from_bits_truncate(args.rdx),
516        num_vcpus: args.r8 as u32,
517        max_vcpus: (args.r8 >> 32) as u32,
518        vcpu_index: args.r9 as u32,
519        sys_rd: args.r10 as u32,
520    })
521}
522
523/// Get Virtualization Exception Information for the recent #VE exception.
524pub fn get_veinfo() -> Result<TdgVeInfo, TdCallError> {
525    let mut args = TdcallArgs {
526        rax: TdcallNum::VpVeinfoGet as u64,
527        ..Default::default()
528    };
529    td_call(&mut args)?;
530    Ok(TdgVeInfo {
531        exit_reason: args.rcx as u32,
532        exit_qualification: args.rdx,
533        guest_linear_address: args.r8,
534        guest_physical_address: args.r9,
535        exit_instruction_length: args.r10 as u32,
536        exit_instruction_info: (args.r10 >> 32) as u32,
537    })
538}
539
540/// Extend a TDCS.RTMR measurement register.
541pub fn extend_rtmr(extend_data_gpa: u64, reg_idx: u64) -> Result<(), TdCallError> {
542    let mut args = TdcallArgs {
543        rax: TdcallNum::MrRtmrExtend as u64,
544        rcx: extend_data_gpa,
545        rdx: reg_idx,
546        ..Default::default()
547    };
548    td_call(&mut args)
549}
550
551/// TDG.MR.REPORT creates a TDREPORT_STRUCT structure that contains the measurements/configuration
552/// information of the guest TD that called the function, measurements/configuration information
553/// of the Intel TDX module and a REPORTMACSTRUCT.
554pub fn get_report(report_gpa: u64, data_gpa: u64) -> Result<(), TdCallError> {
555    let mut args = TdcallArgs {
556        rax: TdcallNum::MrReport as u64,
557        rcx: report_gpa,
558        rdx: data_gpa,
559        ..Default::default()
560    };
561    td_call(&mut args)
562}
563
564/// Verify a cryptographic REPORTMACSTRUCT that describes the contents of a TD,
565/// to determine that it was created on the current TEE on the current platform.
566pub fn verify_report(report_mac_gpa: &[u8]) -> Result<(), TdCallError> {
567    let mut args = TdcallArgs {
568        rax: TdcallNum::MrVerifyreport as u64,
569        rcx: report_mac_gpa.as_ptr() as u64,
570        ..Default::default()
571    };
572    td_call(&mut args)
573}
574
575/// Accept a pending private page and initialize it to all-0 using the TD ephemeral private key.
576/// # Safety
577/// The 'gpa' parameter must be a valid address.
578pub unsafe fn accept_page(sept_level: u64, gpa: u64) -> Result<(), TdCallError> {
579    let mut args = TdcallArgs {
580        rax: TdcallNum::MemPageAccept as u64,
581        rcx: sept_level | gpa,
582        ..Default::default()
583    };
584    td_call(&mut args)
585}
586
587/// Read the GPA mapping and attributes of a TD private page.
588pub fn read_page_attr(gpa: &[u8]) -> Result<PageAttr, TdCallError> {
589    let mut args = TdcallArgs {
590        rax: TdcallNum::MemPageAttrRd as u64,
591        rcx: gpa.as_ptr() as u64,
592        ..Default::default()
593    };
594    td_call(&mut args)?;
595    Ok(PageAttr {
596        gpa_mapping: args.rcx,
597        gpa_attr: GpaAttrAll::from(args.rdx),
598    })
599}
600
601/// Write the attributes of a private page. Create or remove L2 page aliases as required.
602pub fn write_page_attr(page_attr: PageAttr, attr_flags: u64) -> Result<PageAttr, TdCallError> {
603    let mut args = TdcallArgs {
604        rax: TdcallNum::MemPageAttrWr as u64,
605        rcx: page_attr.gpa_mapping,
606        rdx: u64::from(page_attr.gpa_attr),
607        r8: attr_flags,
608        ..Default::default()
609    };
610    td_call(&mut args)?;
611    Ok(PageAttr {
612        gpa_mapping: args.rcx,
613        gpa_attr: GpaAttrAll::from(args.rdx),
614    })
615}
616
617/// Read a TD-scope metadata field (control structure field) of a TD.
618pub fn read_td_metadata(field_identifier: u64) -> Result<u64, TdCallError> {
619    let mut args = TdcallArgs {
620        rax: TdcallNum::VmRd as u64,
621        rdx: field_identifier,
622        ..Default::default()
623    };
624    td_call(&mut args).map(|_| args.r8)
625}
626
627/// Write a TD-scope metadata field (control structure field) of a TD.
628///
629/// - data: data to write to the field.
630///
631/// - write_mask: a 64b write mask to indicate which bits of the value
632///   in R8 are to be written to the field.
633///
634/// It returns previous contents of the field.
635pub fn write_td_metadata(
636    field_identifier: u64,
637    data: u64,
638    write_mask: u64,
639) -> Result<u64, TdCallError> {
640    let mut args = TdcallArgs {
641        rax: TdcallNum::VmWr as u64,
642        rdx: field_identifier,
643        r8: data,
644        r9: write_mask,
645        ..Default::default()
646    };
647    td_call(&mut args).map(|_| args.r8)
648}
649
650/// TDG.VP.CPUIDVE.SET controls unconditional #VE on CPUID execution by the guest TD.
651///
652/// Note: TDG.VP.CPUIDVE.SET is provided for backward compatibility.
653///
654/// The guest TD may control the same settings by writing to the
655/// VCPU-scope metadata fields CPUID_SUPERVISOR_VE and CPUID_USER_VE using TDG.VP.WR.
656pub fn set_cpuidve(cpuidve_flag: u64) -> Result<(), TdCallError> {
657    let mut args = TdcallArgs {
658        rax: TdcallNum::VpCpuidveSet as u64,
659        rcx: cpuidve_flag,
660        ..Default::default()
661    };
662    td_call(&mut args)
663}
664
665/// Enter L2 VCPU operation.
666///
667/// Inputs:
668/// - l2_vm_idx: L2 virtual machine index (must be 1 or higher).
669/// - invd_translations: Controls how enter_l2_vcpu flushes the TLB context and extended paging structure (EPxE) caches
670///   associated with the L2 VM before entering the L2 VCPU.
671/// - guest_state_gpa: The GPA of a 256-bytes aligned L2EnterGuestState structure.
672///
673/// Outputs:
674/// - Return registers status in L2EnterGuestState.
675pub fn enter_l2_vcpu(
676    l2_vm_idx: u64,
677    invd_translations: InvdTranslations,
678    guest_state_gpa: u64,
679) -> Result<(), TdCallError> {
680    if (l2_vm_idx > 3) | (invd_translations.clone() as u64 > 3) {
681        return Err(TdCallError::TdxOperandInvalid);
682    }
683
684    let rcx = (l2_vm_idx << 52) | (invd_translations as u64);
685    let mut args = TdcallArgs {
686        rax: TdcallNum::VpEnter as u64,
687        rcx: l2_vm_idx,
688        rdx: guest_state_gpa,
689        ..Default::default()
690    };
691    td_call(&mut args)
692}
693
694/// Invalidate cached EPT translations for selected L2 VMs.
695///
696/// Inputs:
697/// - l2_vm_idx_bitmap: the index of the L2 VM to invalidate.
698///     Bit 1: Invalidate EPT for L2 VM #1.
699///     Bit 2: Invalidate EPT for L2 VM #2.
700///     Bit 3: Invalidate EPT for L2 VM #3.
701pub fn invalidate_l2_cached_ept(l2_vm_idx_bitmap: u64) -> Result<(), TdCallError> {
702    if l2_vm_idx_bitmap & !0b1110 == 0 {
703        return Err(TdCallError::TdxOperandInvalid);
704    }
705    let mut args = TdcallArgs {
706        rax: TdcallNum::VpInvept as u64,
707        rcx: l2_vm_idx_bitmap,
708        ..Default::default()
709    };
710    td_call(&mut args)
711}
712
713/// Invalidate Guest Linear Address (GLA) mappings in the translation lookaside buffers (TLBs) and paging-structure caches
714/// for a specified L2 VM and a specified list of 4KB-aligned linear addresses.
715///
716/// Inputs:
717/// - l2_vm_idx: the index of the L2 VM to invalidate.
718///     1: Invalidate EPT for L2 VM #1.
719///     2: Invalidate EPT for L2 VM #2.
720///     3: Invalidate EPT for L2 VM #3.
721/// - list: GlaListEntry or GlaListInfo Flags.
722///     0: gla contains a single GLA list entry.
723///     1: RDX contains the GPA and other information of a GLA list in memory.
724/// - gla: Depending on the list flag, it contains either of the following:
725///     - A single GlaListEntry, specifying up to 512 consecutive guest linear addresses, each aligned on 4KB.
726///     - GlaLstInfo, specifying the GPA of a guest linear address (GLA) list in private
727///       memory. Each entry in the GLA list specifies up to 512 consecutive guest linear
728///       addresses, each aligned on 4KB. GlaLstInfo also specifies the first and last GLA
729///       list entries to process.
730///
731/// Outputs:
732/// - RDX: Depending on the list flag, it contains either of the following:
733///     - If list was 0, RDX contains the single GlaListEntry provided as an input, unmodified.
734///     - If list was 1, RDX contains the GlaLstInfo provided as input,
735///       but with the FIRST_ENTRY and NUM_ENTRIES fields updated to reflect the number of entries processed so far.
736///       If all entries have been processed successfully, NUM_ENTRIES is set to 0.
737pub fn invalidate_l2_gla(l2_vm_idx: u64, list: bool, gla: u64) -> Result<u64, TdCallError> {
738    if l2_vm_idx > 3 {
739        return Err(TdCallError::TdxOperandInvalid);
740    }
741
742    let rcx = (l2_vm_idx << 52) | if list { 0b1 } else { 0 };
743    let rdx = if list {
744        Gla::ListEntry(GlaListEntry(gla))
745    } else {
746        Gla::ListInfo(GlaListInfo(gla))
747    }
748    .value();
749
750    let mut args = TdcallArgs {
751        rax: TdcallNum::VpInvgla as u64,
752        rcx,
753        rdx,
754        ..Default::default()
755    };
756    td_call(&mut args).map(|_| args.rdx)
757}
758
759/// As a service TD, read a metadata field (control structure field) of a target TD.
760///
761/// Inputs:
762/// - binding_handle: the binding handle of the target TD.
763/// - field_identifier: the identifier of the field to read.
764///   The `LAST_ELEMENT_IN_FIELD` and `LAST_FIELD_IN_SEQUENCE` components of the field identifier must be 0.
765///   `WRITE_MASK_VALID`, `INC_SIZE`, `CONTEXT_CODE` and `ELEMENT_SIZE_CODE` components of the field identifier are ignored.
766///   A value of -1 is a special case: it is not a valid field identifier; in this case the first readable field identifier is returned in `RDX`.
767/// - uuid: the TD_UUID of the target TD, using little-Endian.
768///
769/// Outputs:
770/// - Next readable field identifier. A value of -1 indicates no next field identifier is available.
771///   If the input field identifier was -1, `RDX` returns the first readable field identifier.
772///   In case of another error, `RDX` returns -1.
773/// - Contents of the field. In case of an error, as indicated by `RAX`, `R8` returns 0.
774/// - Updated target TD’s TD_UUID, using little-Endian.
775pub fn read_servetd(
776    binding_handle: u64,
777    field_identifier: u64,
778    uuid: [u64; 4],
779) -> Result<(u64, u64, [u64; 4]), TdCallError> {
780    let mut args = TdcallArgs {
781        rax: TdcallNum::ServetdRd as u64,
782        rcx: binding_handle,
783        rdx: field_identifier,
784        r10: uuid[0],
785        r11: uuid[1],
786        r12: uuid[2],
787        r13: uuid[3],
788        ..Default::default()
789    };
790    td_call(&mut args).map(|_| (args.rdx, args.r8, [args.r10, args.r11, args.r12, args.r13]))
791}
792
793/// As a service TD, write a metadata field (control structure field) of a target TD.
794///
795/// Inputs:
796/// - binding_handle: the binding handle of the target TD.
797/// - field_identifier: the identifier of the field to read.
798///   The `LAST_ELEMENT_IN_FIELD` and `LAST_FIELD_IN_SEQUENCE` components of the field identifier must be 0.
799///   `WRITE_MASK_VALID`, `INC_SIZE`, `CONTEXT_CODE` and `ELEMENT_SIZE_CODE` components of the field identifier are ignored.
800///   A value of -1 is a special case: it is not a valid field identifier; in this case the first readable field identifier is returned in `RDX`.
801/// - data: Data to write to the field.
802/// - write_mask: A 64b write mask to indicate which bits of the value in `R8` are to be written to the field.
803/// - uuid: the TD_UUID of the target TD, using little-Endian.
804///
805/// Outputs:
806/// - Previous contents of the field. In case of an error, `R8` returns 0.
807/// - Updated target TD’s TD_UUID, using little-Endian.
808pub fn write_servetd(
809    binding_handle: u64,
810    field_identifier: u64,
811    data: u64,
812    mask: u64,
813    uuid: [u64; 4],
814) -> Result<(u64, [u64; 4]), TdCallError> {
815    let mut args = TdcallArgs {
816        rax: TdcallNum::ServetdWr as u64,
817        rcx: binding_handle,
818        rdx: field_identifier,
819        r8: data,
820        r9: mask,
821        r10: uuid[0],
822        r11: uuid[1],
823        r12: uuid[2],
824        r13: uuid[3],
825    };
826    td_call(&mut args).map(|_| (args.r8, [args.r10, args.r11, args.r12, args.r13]))
827}
828
829fn td_call(args: &mut TdcallArgs) -> Result<(), TdCallError> {
830    let result = unsafe { asm_td_call(args) };
831    match result {
832        0 => Ok(()),
833        _ => Err((result >> 32).into()),
834    }
835}