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}