1#![no_std]
7#![forbid(unsafe_code)]
8
9use hvdef::hypercall::HypercallOutput;
10use memory_range::AlignedSubranges;
11use memory_range::MemoryRange;
12use thiserror::Error;
13use x86defs::tdx::TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT;
14use x86defs::tdx::TdCallLeaf;
15use x86defs::tdx::TdCallResult;
16use x86defs::tdx::TdCallResultCode;
17use x86defs::tdx::TdGlaVmAndFlags;
18use x86defs::tdx::TdReport;
19use x86defs::tdx::TdVmCallR10Result;
20use x86defs::tdx::TdVmCallSubFunction;
21use x86defs::tdx::TdgMemPageAcceptRcx;
22use x86defs::tdx::TdgMemPageAttrGpaMappingReadRcxResult;
23use x86defs::tdx::TdgMemPageAttrWriteR8;
24use x86defs::tdx::TdgMemPageAttrWriteRcx;
25use x86defs::tdx::TdgMemPageGpaAttr;
26use x86defs::tdx::TdgMemPageLevel;
27use x86defs::tdx::TdgMemPageReleaseRcx;
28use x86defs::tdx::TdgMemPageReleaseRcxResult;
29use x86defs::tdx::TdgVmRdResult;
30use x86defs::tdx::TdxExtendedFieldCode;
31use x86defs::tdx::TdxGlaListInfo;
32
33#[derive(Debug)]
37pub struct TdcallInput {
38 pub leaf: TdCallLeaf,
40 pub rcx: u64,
42 pub rdx: u64,
44 pub r8: u64,
46 pub r9: u64,
48 pub r10: u64,
50 pub r11: u64,
52 pub r12: u64,
54 pub r13: u64,
56 pub r14: u64,
58 pub r15: u64,
60}
61
62#[derive(Debug)]
66pub struct TdcallOutput {
67 pub rax: TdCallResult,
69 pub rcx: u64,
71 pub rdx: u64,
73 pub r8: u64,
75 pub r10: u64,
77 pub r11: u64,
79}
80
81pub trait Tdcall {
83 fn tdcall(&mut self, input: TdcallInput) -> TdcallOutput;
85}
86
87pub fn tdcall_hypercall(
89 call: &mut impl Tdcall,
90 control: hvdef::hypercall::Control,
91 input_gpa: u64,
92 output_gpa: u64,
93) -> HypercallOutput {
94 let input = TdcallInput {
95 leaf: TdCallLeaf::VP_VMCALL,
96 rcx: 0x0d04, rdx: input_gpa,
98 r8: output_gpa,
99 r9: 0,
100 r10: u64::from(control), r11: 0,
102 r12: 0,
103 r13: 0,
104 r14: 0,
105 r15: 0,
106 };
107
108 let output = call.tdcall(input);
109
110 if output.rax.code() != TdCallResultCode::SUCCESS {
111 panic!(
115 "unexpected nonzero rax {:x} on tdcall_hypercall",
116 u64::from(output.rax)
117 );
118 }
119
120 HypercallOutput::from(output.r11)
122}
123
124pub fn tdcall_rdmsr(
126 call: &mut impl Tdcall,
127 msr_index: u32,
128 msr_value: &mut u64,
129) -> Result<(), TdVmCallR10Result> {
130 let input = TdcallInput {
131 leaf: TdCallLeaf::VP_VMCALL,
132 rcx: 0x1c00, rdx: 0,
134 r8: 0,
135 r9: 0,
136 r10: 0, r11: TdVmCallSubFunction::RdMsr as u64,
138 r12: msr_index as u64,
139 r13: 0,
140 r14: 0,
141 r15: 0,
142 };
143
144 let output = call.tdcall(input);
145
146 assert_eq!(
150 output.rax.code(),
151 TdCallResultCode::SUCCESS,
152 "unexpected nonzero rax {:x} returned by tdcall vmcall",
153 u64::from(output.rax)
154 );
155
156 let result = TdVmCallR10Result(output.r10);
157
158 *msr_value = output.r11;
159
160 #[cfg(feature = "tracing")]
161 tracing::trace!(msr_index, msr_value, output.r10, "tdcall_rdmsr");
162
163 match result {
164 TdVmCallR10Result::SUCCESS => Ok(()),
165 val => Err(val),
166 }
167}
168
169pub fn tdcall_wrmsr(
171 call: &mut impl Tdcall,
172 msr_index: u32,
173 msr_value: u64,
174) -> Result<(), TdVmCallR10Result> {
175 let input = TdcallInput {
176 leaf: TdCallLeaf::VP_VMCALL,
177 rcx: 0x3c00, rdx: 0,
179 r8: 0,
180 r9: 0,
181 r10: 0, r11: TdVmCallSubFunction::WrMsr as u64,
183 r12: msr_index as u64,
184 r13: msr_value,
185 r14: 0,
186 r15: 0,
187 };
188
189 let output = call.tdcall(input);
190
191 assert_eq!(
195 output.rax.code(),
196 TdCallResultCode::SUCCESS,
197 "unexpected nonzero rax {:x} returned by tdcall vmcall",
198 u64::from(output.rax)
199 );
200
201 let result = TdVmCallR10Result(output.r10);
202
203 match result {
204 TdVmCallR10Result::SUCCESS => Ok(()),
205 val => Err(val),
206 }
207}
208
209pub fn tdcall_io_out(
211 call: &mut impl Tdcall,
212 port: u16,
213 value: u32,
214 size: u8,
215) -> Result<(), TdVmCallR10Result> {
216 let input = TdcallInput {
217 leaf: TdCallLeaf::VP_VMCALL,
218 rcx: 0xFF00, rdx: 0,
220 r8: 0,
221 r9: 0,
222 r10: 0, r11: 30,
224 r12: size as u64,
225 r13: 1, r14: port as u64,
227 r15: value as u64,
228 };
229
230 let output = call.tdcall(input);
231
232 assert_eq!(
236 output.rax.code(),
237 TdCallResultCode::SUCCESS,
238 "unexpected nonzero rax {:x} returned by tdcall vmcall",
239 u64::from(output.rax)
240 );
241
242 if output.rax.code() != TdCallResultCode::SUCCESS {
243 panic!(
247 "unexpected nonzero rax {:x} on tdcall_io_out",
248 u64::from(output.rax)
249 );
250 }
251
252 let result = TdVmCallR10Result(output.r10);
253
254 match result {
255 TdVmCallR10Result::SUCCESS => Ok(()),
256 val => Err(val),
257 }
258}
259
260pub fn tdcall_io_in(call: &mut impl Tdcall, port: u16, size: u8) -> Result<u32, TdVmCallR10Result> {
262 let input = TdcallInput {
263 leaf: TdCallLeaf::VP_VMCALL,
264 rcx: 0xFF00, rdx: 0,
266 r8: 0,
267 r9: 0,
268 r10: 0, r11: TdVmCallSubFunction::IoInstr as u64,
270 r12: size as u64,
271 r13: 0, r14: port as u64,
273 r15: 0,
274 };
275
276 let output = call.tdcall(input);
277
278 assert_eq!(
282 output.rax.code(),
283 TdCallResultCode::SUCCESS,
284 "unexpected nonzero rax {:x} returned by tdcall vmcall",
285 u64::from(output.rax)
286 );
287
288 let result = TdVmCallR10Result(output.r10);
289
290 match result {
291 TdVmCallR10Result::SUCCESS => Ok(output.r11 as u32),
292 val => Err(val),
293 }
294}
295
296pub fn tdcall_accept_pages(
298 call: &mut impl Tdcall,
299 gpa_page_number: u64,
300 as_large_page: bool,
301) -> Result<(), TdCallResultCode> {
302 #[cfg(feature = "tracing")]
303 tracing::trace!(gpa_page_number, as_large_page, "tdcall_accept_pages");
304
305 let rcx = TdgMemPageAcceptRcx::new()
306 .with_gpa_page_number(gpa_page_number)
307 .with_level(if as_large_page {
308 TdgMemPageLevel::Size2Mb
309 } else {
310 TdgMemPageLevel::Size4k
311 });
312
313 let input = TdcallInput {
314 leaf: TdCallLeaf::MEM_PAGE_ACCEPT,
315 rcx: rcx.into(),
316 rdx: 0,
317 r8: 0,
318 r9: 0,
319 r10: 0,
320 r11: 0,
321 r12: 0,
322 r13: 0,
323 r14: 0,
324 r15: 0,
325 };
326
327 let output = call.tdcall(input);
328
329 match output.rax.code() {
330 TdCallResultCode::SUCCESS => Ok(()),
331 val => Err(val),
332 }
333}
334
335#[derive(Debug, Error)]
337pub enum TdgPageReleaseError {
338 #[error("unknown error: {0:?}")]
340 Unknown(TdCallResultCode),
341 #[error("page is not allocated to GPA space: {0:?}")]
343 NotAllocated(TdCallResultCode),
344 #[error("page has invalid state [pending: {pending:?}, mmio: {mmio:?}]: {result:?}")]
346 EntryStateInvalid {
347 result: TdCallResultCode,
349 pending: bool,
351 mmio: bool,
353 },
354 #[error("page size mismatch. Expected page level {expected_level:?}: {result:?}")]
356 PageSizeMismatch {
357 result: TdCallResultCode,
359 expected_level: TdgMemPageLevel,
361 },
362 #[error("invalid operand: {0:?}")]
364 Invalid(TdCallResultCode),
365 #[error("operand busy: {0:?}")]
367 Busy(TdCallResultCode),
368}
369
370pub fn tdcall_release_page(
372 call: &mut impl Tdcall,
373 gpa_page_number: u64,
374 as_large_page: bool,
375) -> Result<(), TdgPageReleaseError> {
376 #[cfg(feature = "tracing")]
377 tracing::trace!(gpa_page_number, as_large_page, "tdcall_release_page");
378
379 let rcx = TdgMemPageReleaseRcx::new()
380 .with_gpa_page_number(gpa_page_number)
381 .with_level(if as_large_page {
382 TdgMemPageLevel::Size2Mb
383 } else {
384 TdgMemPageLevel::Size4k
385 });
386
387 let input = TdcallInput {
388 leaf: TdCallLeaf::MEM_PAGE_RELEASE,
389 rcx: rcx.into(),
390 rdx: 0,
391 r8: 0,
392 r9: 0,
393 r10: 0,
394 r11: 0,
395 r12: 0,
396 r13: 0,
397 r14: 0,
398 r15: 0,
399 };
400
401 let output = call.tdcall(input);
402
403 let result_code = output.rax.code();
404 let result_info = TdgMemPageReleaseRcxResult::from(output.rcx);
405
406 match result_code {
407 TdCallResultCode::SUCCESS => Ok(()),
408 TdCallResultCode::EPT_ENTRY_FREE => Err(TdgPageReleaseError::NotAllocated(result_code)),
409 TdCallResultCode::EPT_ENTRY_STATE_INCORRECT => {
410 Err(TdgPageReleaseError::EntryStateInvalid {
411 result: result_code,
412 pending: result_info.pending(),
413 mmio: result_info.mmio(),
414 })
415 }
416 TdCallResultCode::PAGE_SIZE_MISMATCH => Err(TdgPageReleaseError::PageSizeMismatch {
417 result: result_code,
418 expected_level: result_info.level(),
419 }),
420 TdCallResultCode::OPERAND_INVALID => Err(TdgPageReleaseError::Invalid(result_code)),
421 TdCallResultCode::OPERAND_BUSY => Err(TdgPageReleaseError::Busy(result_code)),
422 val => Err(TdgPageReleaseError::Unknown(val)),
423 }
424}
425
426pub fn release_pages(
428 call: &mut impl Tdcall,
429 range: MemoryRange,
430) -> Result<(), TdgPageReleaseError> {
431 #[cfg(feature = "tracing")]
432 tracing::trace!(%range, "release_pages");
433
434 for_each_tdcall_page(range, |gpn, is_large_page| {
435 match tdcall_release_page(call, gpn, is_large_page) {
436 Ok(_) => Ok(TdcallPageOperationOutcome::Advance),
437 Err(TdgPageReleaseError::PageSizeMismatch {
438 expected_level: TdgMemPageLevel::Size4k,
439 ..
440 }) if is_large_page => Ok(TdcallPageOperationOutcome::Retry4k),
441 Err(e) => Err(e),
442 }
443 })
444}
445
446#[derive(Debug)]
448pub struct TdgPageAttrRdResult {
449 pub mapping: TdgMemPageAttrGpaMappingReadRcxResult,
451 pub attributes: TdgMemPageGpaAttr,
453}
454
455pub fn tdcall_page_attr_rd(
457 call: &mut impl Tdcall,
458 gpa: u64,
459) -> Result<TdgPageAttrRdResult, TdCallResultCode> {
460 #[cfg(feature = "tracing")]
461 tracing::trace!(gpa, "tdcall_page_attr_rd");
462
463 let input = TdcallInput {
464 leaf: TdCallLeaf::MEM_PAGE_ATTR_RD,
465 rcx: gpa,
466 rdx: 0,
467 r8: 0,
468 r9: 0,
469 r10: 0,
470 r11: 0,
471 r12: 0,
472 r13: 0,
473 r14: 0,
474 r15: 0,
475 };
476
477 let output = call.tdcall(input);
478
479 match output.rax.code() {
480 TdCallResultCode::SUCCESS => Ok(TdgPageAttrRdResult {
481 mapping: TdgMemPageAttrGpaMappingReadRcxResult::from(output.rcx),
482 attributes: TdgMemPageGpaAttr::from(output.rdx),
483 }),
484 val => Err(val),
485 }
486}
487
488pub fn tdcall_page_attr_wr(
490 call: &mut impl Tdcall,
491 mapping: TdgMemPageAttrWriteRcx,
492 attributes: TdgMemPageGpaAttr,
493 mask: TdgMemPageAttrWriteR8,
494) -> Result<(), TdCallResultCode> {
495 #[cfg(feature = "tracing")]
496 tracing::trace!(?mapping, ?attributes, ?mask, "tdcall_page_attr_wr");
497
498 let input = TdcallInput {
499 leaf: TdCallLeaf::MEM_PAGE_ATTR_WR,
500 rcx: mapping.into(),
501 rdx: attributes.into(),
502 r8: mask.into(),
503 r9: 0,
504 r10: 0,
505 r11: 0,
506 r12: 0,
507 r13: 0,
508 r14: 0,
509 r15: 0,
510 };
511
512 let output = call.tdcall(input);
513
514 match output.rax.code() {
517 TdCallResultCode::SUCCESS => Ok(()),
518 val => Err(val),
519 }
520}
521
522fn set_page_attr(
525 call: &mut impl Tdcall,
526 mapping: TdgMemPageAttrWriteRcx,
527 attributes: TdgMemPageGpaAttr,
528 mask: TdgMemPageAttrWriteR8,
529) -> Result<(), TdCallResultCode> {
530 match tdcall_page_attr_wr(call, mapping, attributes, mask) {
531 Ok(()) => {
532 #[cfg(debug_assertions)]
533 {
534 let result =
535 tdcall_page_attr_rd(call, mapping.gpa_page_number() * x86defs::X64_PAGE_SIZE)
536 .unwrap();
537 assert_eq!(u64::from(mapping), result.mapping.into());
538 assert_eq!(attributes.l1(), result.attributes.l1());
539 assert_eq!(
540 attributes.into_bits() & mask.with_reserved(0).into_bits(),
541 result.attributes.into_bits() & mask.with_reserved(0).into_bits()
542 );
543 }
544
545 Ok(())
546 }
547 Err(e) => Err(e),
548 }
549}
550
551#[derive(Debug, Error)]
555pub enum AcceptPagesError {
556 #[error("unknown error: {0:?}")]
558 Unknown(TdCallResultCode),
559 #[error("setting page attributes failed after accepting: {0:?}")]
561 Attributes(TdCallResultCode),
562 #[error("invalid operand: {0:?}")]
564 Invalid(TdCallResultCode),
565 #[error("operand busy: {0:?}")]
567 Busy(TdCallResultCode),
568}
569
570pub enum AcceptPagesAttributes {
572 None,
575 Set {
578 attributes: TdgMemPageGpaAttr,
580 mask: TdgMemPageAttrWriteR8,
582 },
583}
584
585pub fn accept_pages<T: Tdcall>(
587 call: &mut T,
588 range: MemoryRange,
589 attributes: AcceptPagesAttributes,
590) -> Result<(), AcceptPagesError> {
591 #[cfg(feature = "tracing")]
592 tracing::trace!(%range, "accept_pages");
593
594 let set_attributes = |call: &mut T, mapping| -> Result<(), AcceptPagesError> {
595 match attributes {
596 AcceptPagesAttributes::None => Ok(()),
597 AcceptPagesAttributes::Set { attributes, mask } => {
598 set_page_attr(call, mapping, attributes, mask).map_err(AcceptPagesError::Attributes)
599 }
600 }
601 };
602
603 for_each_tdcall_page(range, |gpn, is_large_page| {
604 match tdcall_accept_pages(call, gpn, is_large_page) {
605 Ok(_) => {
606 set_attributes(
607 call,
608 TdgMemPageAttrWriteRcx::new()
609 .with_gpa_page_number(gpn)
610 .with_level(if is_large_page {
611 TdgMemPageLevel::Size2Mb
612 } else {
613 TdgMemPageLevel::Size4k
614 }),
615 )?;
616 Ok(TdcallPageOperationOutcome::Advance)
617 }
618 Err(e) => match e {
619 TdCallResultCode::OPERAND_BUSY => Err(AcceptPagesError::Busy(e)),
620 TdCallResultCode::OPERAND_INVALID => Err(AcceptPagesError::Invalid(e)),
621 TdCallResultCode::PAGE_ALREADY_ACCEPTED => {
622 panic!("page {} already accepted", gpn);
623 }
624 TdCallResultCode::PAGE_SIZE_MISMATCH if is_large_page => {
625 #[cfg(feature = "tracing")]
626 tracing::trace!("accept pages size mismatch returned");
627 Ok(TdcallPageOperationOutcome::Retry4k)
628 }
629 _ => Err(AcceptPagesError::Unknown(e)),
630 },
631 }
632 })
633}
634
635pub fn set_page_attributes(
640 call: &mut impl Tdcall,
641 range: MemoryRange,
642 attributes: TdgMemPageGpaAttr,
643 mask: TdgMemPageAttrWriteR8,
644) -> Result<(), TdCallResultCode> {
645 #[cfg(feature = "tracing")]
646 tracing::trace!(
647 %range,
648 ?attributes,
649 ?mask,
650 "set_page_attributes"
651 );
652
653 for_each_tdcall_page(range, |gpn, is_large_page| {
654 let level = if is_large_page {
655 TdgMemPageLevel::Size2Mb
656 } else {
657 TdgMemPageLevel::Size4k
658 };
659 let mapping = TdgMemPageAttrWriteRcx::new()
660 .with_gpa_page_number(gpn)
661 .with_level(level);
662
663 match set_page_attr(call, mapping, attributes, mask) {
664 Ok(()) => Ok(TdcallPageOperationOutcome::Advance),
665 Err(TdCallResultCode::PAGE_SIZE_MISMATCH) if is_large_page => {
666 #[cfg(feature = "tracing")]
667 tracing::trace!("set pages attr size mismatch returned");
668 Ok(TdcallPageOperationOutcome::Retry4k)
669 }
670 Err(e) => Err(e),
671 }
672 })
673}
674
675pub fn tdcall_map_gpa(
687 call: &mut impl Tdcall,
688 range: MemoryRange,
689 host_visible: bool,
690) -> Result<(), TdVmCallR10Result> {
691 let mut gpa = if host_visible {
692 range.start() | TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT
693 } else {
694 range.start() & !TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT
695 };
696 let end = gpa + range.len();
697
698 while gpa < end {
699 let input = TdcallInput {
700 leaf: TdCallLeaf::VP_VMCALL,
701 rcx: 0x3c00, rdx: 0,
703 r8: 0,
704 r9: 0,
705 r10: 0, r11: TdVmCallSubFunction::MapGpa as u64,
707 r12: gpa,
708 r13: end - gpa,
709 r14: 0,
710 r15: 0,
711 };
712
713 let output = call.tdcall(input);
714
715 assert_eq!(
719 output.rax.code(),
720 TdCallResultCode::SUCCESS,
721 "unexpected nonzero rax {:x} returned by tdcall vmcall",
722 u64::from(output.rax)
723 );
724
725 let result = TdVmCallR10Result(output.r10);
726
727 match result {
728 TdVmCallR10Result::SUCCESS => gpa = end,
729 TdVmCallR10Result::RETRY => gpa = output.r11,
730 val => return Err(val),
731 }
732 }
733
734 Ok(())
735}
736
737pub fn tdcall_vp_wr(
746 call: &mut impl Tdcall,
747 field_code: TdxExtendedFieldCode,
748 value: u64,
749 mask: u64,
750) -> Result<u64, TdCallResult> {
751 let input = TdcallInput {
752 leaf: TdCallLeaf::VP_WR,
753 rcx: 0,
754 rdx: field_code.into(),
755 r8: value,
756 r9: mask,
757 r10: 0,
758 r11: 0,
759 r12: 0,
760 r13: 0,
761 r14: 0,
762 r15: 0,
763 };
764
765 let output = call.tdcall(input);
766
767 match output.rax.code() {
768 TdCallResultCode::SUCCESS => Ok(output.r8),
769 _ => Err(output.rax),
770 }
771}
772
773pub fn tdcall_vp_rd(
777 call: &mut impl Tdcall,
778 field_code: TdxExtendedFieldCode,
779) -> Result<u64, TdCallResult> {
780 let input = TdcallInput {
781 leaf: TdCallLeaf::VP_RD,
782 rcx: 0,
783 rdx: field_code.into(),
784 r8: 0,
785 r9: 0,
786 r10: 0,
787 r11: 0,
788 r12: 0,
789 r13: 0,
790 r14: 0,
791 r15: 0,
792 };
793
794 let output = call.tdcall(input);
795
796 match output.rax.code() {
797 TdCallResultCode::SUCCESS => Ok(output.r8),
798 _ => Err(output.rax),
799 }
800}
801
802pub fn tdcall_vm_wr(
811 call: &mut impl Tdcall,
812 field_code: TdxExtendedFieldCode,
813 value: u64,
814 mask: u64,
815) -> Result<u64, TdCallResult> {
816 let input = TdcallInput {
817 leaf: TdCallLeaf::VM_WR,
818 rcx: 0,
819 rdx: field_code.into(),
820 r8: value,
821 r9: mask,
822 r10: 0,
823 r11: 0,
824 r12: 0,
825 r13: 0,
826 r14: 0,
827 r15: 0,
828 };
829
830 let output = call.tdcall(input);
831
832 match output.rax.code() {
833 TdCallResultCode::SUCCESS => Ok(output.r8),
834 _ => Err(output.rax),
835 }
836}
837
838pub fn tdcall_sys_rd(call: &mut impl Tdcall, field_id: u64) -> Result<u64, TdCallResult> {
845 let input = TdcallInput {
846 leaf: TdCallLeaf::SYS_RD,
847 rcx: 0,
848 rdx: field_id,
849 r8: 0,
850 r9: 0,
851 r10: 0,
852 r11: 0,
853 r12: 0,
854 r13: 0,
855 r14: 0,
856 r15: 0,
857 };
858
859 let output = call.tdcall(input);
860
861 match output.rax.code() {
862 TdCallResultCode::SUCCESS => Ok(output.r8),
863 _ => Err(output.rax),
864 }
865}
866
867pub fn tdcall_vp_invgla(
869 call: &mut impl Tdcall,
870 gla_flags: TdGlaVmAndFlags,
871 gla_info: TdxGlaListInfo,
872) -> Result<(), TdCallResult> {
873 let input = TdcallInput {
874 leaf: TdCallLeaf::VP_INVGLA,
875 rcx: gla_flags.into(),
876 rdx: gla_info.into(),
877 r8: 0,
878 r9: 0,
879 r10: 0,
880 r11: 0,
881 r12: 0,
882 r13: 0,
883 r14: 0,
884 r15: 0,
885 };
886
887 let output = call.tdcall(input);
888
889 match output.rax.code() {
890 TdCallResultCode::SUCCESS => Ok(()),
891 _ => Err(output.rax),
892 }
893}
894
895#[repr(C, align(64))]
896struct AddlData {
897 pub report_data: [u8; 64],
899}
900
901pub fn tdcall_mr_report(call: &mut impl Tdcall, report: &mut TdReport) -> Result<(), TdCallResult> {
903 let addl_data = AddlData {
904 report_data: [0; 64],
905 };
906
907 let input = TdcallInput {
908 leaf: TdCallLeaf::MR_REPORT,
909 rcx: core::ptr::from_mut::<TdReport>(report) as u64,
910 rdx: 0,
911 r8: 0,
912 r9: 0,
913 r10: 0,
914 r11: 0,
915 r12: addl_data.report_data.as_ptr() as u64,
916 r13: 0,
917 r14: 0,
918 r15: 0,
919 };
920
921 let output = call.tdcall(input);
922
923 match output.rax.code() {
924 TdCallResultCode::SUCCESS => Ok(()),
925 _ => Err(output.rax),
926 }
927}
928
929pub fn tdcall_vm_rd(
931 call: &mut impl Tdcall,
932 field_id: TdxExtendedFieldCode,
933) -> Result<TdgVmRdResult, TdCallResult> {
934 let input = TdcallInput {
935 leaf: TdCallLeaf::VM_RD,
936 rcx: 0,
937 rdx: field_id.into_bits(),
938 r8: 0,
939 r9: 0,
940 r10: 0,
941 r11: 0,
942 r12: 0,
943 r13: 0,
944 r14: 0,
945 r15: 0,
946 };
947
948 let output = call.tdcall(input);
949 if output.rax.code() != TdCallResultCode::SUCCESS {
950 return Err(output.rax);
951 }
952
953 Ok(output.r8)
954}
955
956enum TdcallPageOperationOutcome {
958 Advance,
960
961 Retry4k,
964}
965
966fn for_each_tdcall_page<E>(
968 range: MemoryRange,
969 mut op: impl FnMut(u64, bool) -> Result<TdcallPageOperationOutcome, E>,
970) -> Result<(), E> {
971 let range = AlignedSubranges::new(range).with_max_range_len(x86defs::X64_LARGE_PAGE_SIZE);
972
973 for mut subrange in range {
974 let mut is_large_page = subrange.alignment(0) == x86defs::X64_LARGE_PAGE_SIZE;
975
976 while !subrange.is_empty() {
977 match op(subrange.start_4k_gpn(), is_large_page)? {
978 TdcallPageOperationOutcome::Advance => {
979 let page_size = if is_large_page {
980 x86defs::X64_LARGE_PAGE_SIZE
981 } else {
982 x86defs::X64_PAGE_SIZE
983 };
984
985 subrange = MemoryRange::new(subrange.start() + page_size..subrange.end())
986 }
987 TdcallPageOperationOutcome::Retry4k => {
988 assert!(
989 is_large_page,
990 "Retry4k requested while already retrying as 4K — would loop forever",
991 );
992 is_large_page = false;
993 }
994 }
995 }
996 }
997
998 Ok(())
999}