1mod deferred;
7pub mod register;
8
9pub mod aarch64;
10pub mod cca;
11pub mod snp;
12pub mod tdx;
13pub mod x64;
14
15use self::deferred::DeferredActionSlots;
16use self::ioctls::*;
17use crate::GuestVtl;
18use crate::ioctl::deferred::DeferredAction;
19use crate::ioctl::register::GetRegError;
20use crate::ioctl::register::SetRegError;
21use crate::mapped_page::MappedPage;
22use crate::protocol;
23use crate::protocol::EnterModes;
24use crate::protocol::HCL_REG_PAGE_OFFSET;
25use crate::protocol::HCL_VMSA_GUEST_VSM_PAGE_OFFSET;
26use crate::protocol::HCL_VMSA_PAGE_OFFSET;
27use crate::protocol::MSHV_APIC_PAGE_OFFSET;
28use crate::protocol::hcl_intr_offload_flags;
29use crate::protocol::hcl_run;
30use bitvec::vec::BitVec;
31use cvm_tracing::CVM_ALLOWED;
32use deferred::RegisteredDeferredActions;
33use deferred::push_deferred_action;
34use deferred::register_deferred_actions;
35use hv1_structs::ProcessorSet;
36use hv1_structs::VtlArray;
37use hvdef::HV_PAGE_SIZE;
38use hvdef::HV_PARTITION_ID_SELF;
39use hvdef::HvAarch64RegisterPage;
40use hvdef::HvError;
41use hvdef::HvMapGpaFlags;
42use hvdef::HvMessage;
43use hvdef::HvRegisterVsmPartitionConfig;
44use hvdef::HvStatus;
45use hvdef::HvX64RegisterPage;
46use hvdef::HypercallCode;
47use hvdef::Vtl;
48use hvdef::hypercall::AssertVirtualInterrupt;
49use hvdef::hypercall::HostVisibilityType;
50use hvdef::hypercall::HvGpaRange;
51use hvdef::hypercall::HvGpaRangeExtended;
52use hvdef::hypercall::HvInputVtl;
53use hvdef::hypercall::HvInterceptParameters;
54use hvdef::hypercall::HvInterceptType;
55use hvdef::hypercall::HypercallOutput;
56use hvdef::hypercall::InitialVpContextX64;
57use hvdef::hypercall::ModifyHostVisibility;
58use memory_range::MemoryRange;
59use pal::unix::pthread::*;
60use parking_lot::Mutex;
61use private::BackingPrivate;
62use sidecar_client::SidecarClient;
63use sidecar_client::SidecarRun;
64use sidecar_client::SidecarVp;
65use std::cell::UnsafeCell;
66use std::fmt::Debug;
67use std::fs::File;
68use std::io;
69use std::os::unix::prelude::*;
70use std::sync::Arc;
71use std::sync::Once;
72use std::sync::atomic::AtomicU8;
73use std::sync::atomic::AtomicU32;
74use std::sync::atomic::Ordering;
75use thiserror::Error;
76use user_driver::DmaClient;
77use user_driver::memory::MemoryBlock;
78use x86defs::snp::SevAvicPage;
79use x86defs::snp::SevVmsa;
80use x86defs::tdx::TdCallResultCode;
81use x86defs::vmx::VmxApicPage;
82use zerocopy::FromBytes;
83use zerocopy::FromZeros;
84use zerocopy::Immutable;
85use zerocopy::IntoBytes;
86use zerocopy::KnownLayout;
87
88#[derive(Error, Debug)]
91#[expect(missing_docs)]
92pub enum Error {
93 #[error("failed to open mshv device")]
94 OpenMshv(#[source] io::Error),
95 #[error("failed to open hvcall device")]
96 OpenHvcall(#[source] io::Error),
97 #[error("failed to open lower VTL memory device")]
98 OpenGpa(#[source] io::Error),
99 #[error("ReturnToLowerVtl")]
100 ReturnToLowerVtl(#[source] nix::Error),
101 #[error("AddVtl0Memory")]
102 AddVtl0Memory(#[source] nix::Error),
103 #[error("hcl_request_interrupt")]
104 RequestInterrupt(#[source] HvError),
105 #[error("failed to signal event")]
106 SignalEvent(#[source] HvError),
107 #[error("failed to mmap the vp context {:?}", .1.map(|vtl| format!("for VTL {:?}", vtl)).unwrap_or("".to_string()))]
108 MmapVp(#[source] io::Error, Option<Vtl>),
109 #[error("failed to set the poll file")]
110 SetPollFile(#[source] nix::Error),
111 #[error("failed to check hcl capabilities {0}")]
112 CheckExtensions(u32, #[source] nix::Error),
113 #[error("failed to mmap the register page")]
114 MmapRegPage(#[source] io::Error),
115 #[error("failed to create vtl")]
116 CreateVTL(#[source] nix::Error),
117 #[error("gpa failed vtl access check")]
118 CheckVtlAccess(#[source] HvError),
119 #[error("sidecar error")]
120 Sidecar(#[source] sidecar_client::SidecarError),
121 #[error(
122 "mismatch between requested isolation type {requested:?} and supported isolation type {supported:?}"
123 )]
124 MismatchedIsolation {
125 supported: IsolationType,
126 requested: IsolationType,
127 },
128 #[error("private page pool allocator missing, required for requested isolation type")]
129 MissingPrivateMemory,
130 #[error("failed to allocate pages for vp")]
131 AllocVp(#[source] anyhow::Error),
132 #[error("failed to map or unmap redirected device interrupt")]
133 MapRedirectedDeviceInterrupt(#[source] nix::Error),
134 #[error("failed to restore partition time")]
135 RestorePartitionTime(#[source] nix::Error),
136 #[error("failed to set registers using set_vp_registers hypercall")]
138 SetRegisters(#[source] SetRegError),
139 #[error("Invalid register value")]
140 InvalidRegisterValue,
141}
142
143#[derive(Debug, Error)]
145#[error("hcl request failed")]
146pub struct IoctlError(#[source] pub(crate) nix::Error);
147
148#[derive(Debug, Error)]
150#[expect(missing_docs)]
151pub enum HypercallError {
152 #[error("hypercall failed with {0:?}")]
153 Hypervisor(HvError),
154 #[error("ioctl failed")]
155 Ioctl(#[source] IoctlError),
156}
157
158impl HypercallError {
159 pub(crate) fn check(r: Result<i32, nix::Error>) -> Result<(), Self> {
160 match r {
161 Ok(n) => HvStatus(n.try_into().expect("hypervisor result out of range"))
162 .result()
163 .map_err(Self::Hypervisor),
164 Err(err) => Err(Self::Ioctl(IoctlError(err))),
165 }
166 }
167}
168
169#[derive(Error, Debug)]
171#[expect(missing_docs)]
172pub enum HvcallError {
173 #[error(
174 "kernel rejected the hypercall, most likely due to the hypercall code not being allowed via set_allowed_hypercalls"
175 )]
176 HypercallIoctlFailed(#[source] nix::Error),
177 #[error("input parameters are larger than a page")]
178 InputParametersTooLarge,
179 #[error("output parameters are larger than a page")]
180 OutputParametersTooLarge,
181 #[error("output and input list lengths do not match")]
182 InputOutputRepListMismatch,
183}
184
185#[derive(Error, Debug)]
188#[expect(missing_docs)]
189pub enum ApplyVtlProtectionsError {
190 #[error("hypervisor failed with {output:?} when protecting pages {range} for vtl {vtl:?}")]
191 Hypervisor {
192 range: MemoryRange,
193 output: HypercallOutput,
194 #[source]
195 hv_error: HvError,
196 vtl: HvInputVtl,
197 },
198 #[error("snp failure to protect pages {range} with {permissions:x?} for vtl {vtl:?}")]
199 Snp {
200 #[source]
201 failed_operation: snp::SnpPageError,
202 range: MemoryRange,
203 permissions: x86defs::snp::SevRmpAdjust,
204 vtl: HvInputVtl,
205 },
206 #[error(
207 "tdcall failed with {error:?} when protecting pages {range} with permissions {permissions:x?} for vtl {vtl:?}"
208 )]
209 Tdx {
210 error: TdCallResultCode,
211 range: MemoryRange,
212 permissions: x86defs::tdx::TdgMemPageGpaAttr,
213 vtl: HvInputVtl,
214 },
215 #[error("cca failed when protecting pages {range} with permissions for vtl {vtl:?}")]
216 Cca { range: MemoryRange, vtl: HvInputVtl },
217 #[error("no valid protections for vtl {0:?}")]
218 InvalidVtl(Vtl),
219}
220
221#[derive(Error, Debug)]
223#[expect(missing_docs)]
224pub enum SetVsmPartitionConfigError {
225 #[error("hypervisor failed when configuring vsm partition config {config:?}")]
226 Hypervisor {
227 config: HvRegisterVsmPartitionConfig,
228 #[source]
229 hv_error: HvError,
230 },
231}
232
233#[derive(Error, Debug)]
235#[expect(missing_docs)]
236pub enum TranslateGvaToGpaError {
237 #[error("hypervisor failed when translating gva {gva:#x}")]
238 Hypervisor {
239 gva: u64,
240 #[source]
241 hv_error: HvError,
242 },
243 #[error("sidecar kernel failed when translating gva {gva:#x}")]
244 Sidecar {
245 gva: u64,
246 #[source]
247 error: sidecar_client::SidecarError,
248 },
249}
250
251#[derive(Debug)]
253pub struct CheckVtlAccessResult {
254 pub vtl: Vtl,
256 pub denied_flags: HvMapGpaFlags,
258}
259
260#[derive(Error, Debug)]
263#[expect(missing_docs)]
264pub enum AcceptPagesError {
265 #[error("hypervisor failed to accept pages {range} with {output:?}")]
266 Hypervisor {
267 range: MemoryRange,
268 output: HypercallOutput,
269 #[source]
270 hv_error: HvError,
271 },
272 #[error("snp failure to protect pages {range}")]
273 Snp {
274 #[source]
275 failed_operation: snp::SnpPageError,
276 range: MemoryRange,
277 },
278 #[error("tdcall failure when accepting pages {range}")]
279 Tdx {
280 #[source]
281 error: tdcall::AcceptPagesError,
282 range: MemoryRange,
283 },
284}
285
286#[derive(Debug, Copy, Clone)]
288enum GpaPinUnpinAction {
289 PinGpaRange,
290 UnpinGpaRange,
291}
292
293#[derive(Error, Debug)]
295#[error("partial success: {ranges_processed} operations succeeded, but encountered an error")]
296struct PinUnpinError {
297 ranges_processed: usize,
298 #[source]
299 error: HvError,
300}
301
302pub struct TranslateResult {
304 pub gpa_page: u64,
306 pub overlay_page: bool, }
309
310enum HvcallRepInput<'a, T> {
312 Elements(&'a [T]),
314 Count(u16),
316}
317
318pub(crate) mod ioctls {
319 use super::cca;
320 use crate::protocol;
321 use hvdef::hypercall::HvRegisterAssoc;
322 use nix::ioctl_none;
323 use nix::ioctl_read;
324 use nix::ioctl_readwrite;
325 use nix::ioctl_write_ptr;
326
327 const MSHV_IOCTL: u8 = 0xb8;
330 const MSHV_VTL_RETURN_TO_LOWER_VTL: u16 = 0x27;
331 const MSHV_SET_VP_REGISTERS: u16 = 0x6;
332 const MSHV_GET_VP_REGISTERS: u16 = 0x5;
333 const MSHV_RESTORE_PARTITION_TIME: u16 = 0x13;
334 const MSHV_HVCALL_SETUP: u16 = 0x1E;
335 const MSHV_HVCALL: u16 = 0x1F;
336 const MSHV_VTL_ADD_VTL0_MEMORY: u16 = 0x21;
337 const MSHV_VTL_SET_POLL_FILE: u16 = 0x25;
338 const MSHV_CREATE_VTL: u16 = 0x1D;
339 const MSHV_CHECK_EXTENSION: u16 = 0x00;
340 const MSHV_VTL_PVALIDATE: u16 = 0x28;
341 const MSHV_VTL_RMPADJUST: u16 = 0x29;
342 const MSHV_VTL_TDCALL: u16 = 0x32;
343 const MSHV_VTL_READ_VMX_CR4_FIXED1: u16 = 0x33;
344 const MSHV_VTL_GUEST_VSM_VMSA_PFN: u16 = 0x34;
345 const MSHV_VTL_RMPQUERY: u16 = 0x35;
346 const MSHV_INVLPGB: u16 = 0x36;
347 const MSHV_TLBSYNC: u16 = 0x37;
348 const MSHV_KICKCPUS: u16 = 0x38;
349 const MSHV_MAP_REDIRECTED_DEVICE_INTERRUPT: u16 = 0x39;
350 const MSHV_VTL_SECURE_AVIC_VTL0_PFN: u16 = 0x40;
351 const MSHV_VTL_REALM_CONFIG: u16 = 0x41;
352 const MSHV_VTL_RSI_SYSREG_READ: u16 = 0x42;
353 const MSHV_VTL_RSI_SYSREG_WRITE: u16 = 0x43;
354 const MSHV_VTL_RSI_SET_MEM_PERM: u16 = 0x44;
355 const MSHV_VTL_RSI_GET_IPA_STATE: u16 = 0x45;
356
357 #[repr(C)]
358 #[derive(Copy, Clone)]
359 pub struct mshv_vp_registers {
360 pub count: ::std::os::raw::c_int,
361 pub regs: *mut HvRegisterAssoc,
362 }
363
364 #[repr(C, packed)]
365 #[derive(Copy, Clone)]
366 pub struct mshv_pvalidate {
367 pub start_pfn: ::std::os::raw::c_ulonglong,
369 pub page_count: ::std::os::raw::c_ulonglong,
370 pub validate: ::std::os::raw::c_uchar,
371 pub terminate_on_failure: ::std::os::raw::c_uchar,
372 pub ram: u8,
375 pub padding: [::std::os::raw::c_uchar; 1],
376 }
377
378 #[repr(C, packed)]
379 #[derive(Copy, Clone)]
380 pub struct mshv_rmpadjust {
381 pub start_pfn: ::std::os::raw::c_ulonglong,
383 pub page_count: ::std::os::raw::c_ulonglong,
384 pub value: ::std::os::raw::c_ulonglong,
385 pub terminate_on_failure: ::std::os::raw::c_uchar,
386 pub ram: u8,
389 pub padding: [::std::os::raw::c_uchar; 6],
390 }
391
392 #[repr(C, packed)]
393 #[derive(Copy, Clone)]
394 pub struct mshv_rmpquery {
395 pub start_pfn: ::std::os::raw::c_ulonglong,
397 pub page_count: ::std::os::raw::c_ulonglong,
398 pub terminate_on_failure: ::std::os::raw::c_uchar,
399 pub ram: u8,
402 pub padding: [::std::os::raw::c_uchar; 6],
403 pub flags: *mut ::std::os::raw::c_ulonglong,
405 pub page_size: *mut ::std::os::raw::c_ulonglong,
407 pub pages_processed: *mut ::std::os::raw::c_ulonglong,
409 }
410
411 #[repr(C, packed)]
412 #[derive(Copy, Clone)]
413 pub struct mshv_tdcall {
414 pub rax: u64, pub rcx: u64,
416 pub rdx: u64,
417 pub r8: u64,
418 pub r9: u64,
419 pub r10_out: u64, pub r11_out: u64, }
422
423 #[repr(C)]
424 #[derive(Copy, Clone)]
425 pub struct mshv_map_device_int {
426 pub vector: u32,
427 pub apic_id: u32,
428 pub create_mapping: u8,
429 pub padding: [u8; 7],
430 }
431
432 #[repr(C)]
433 #[derive(Copy, Clone)]
434 pub struct mshv_restore_partition_time {
435 pub tsc_sequence: u32,
436 pub reserved: u32,
437 pub reference_time_in_100_ns: u64,
438 pub tsc: u64,
439 }
440
441 ioctl_none!(
442 hcl_return_to_lower_vtl,
444 MSHV_IOCTL,
445 MSHV_VTL_RETURN_TO_LOWER_VTL
446 );
447
448 ioctl_write_ptr!(
449 hcl_set_vp_register,
454 MSHV_IOCTL,
455 MSHV_SET_VP_REGISTERS,
456 mshv_vp_registers
457 );
458
459 ioctl_readwrite!(
460 hcl_get_vp_register,
465 MSHV_IOCTL,
466 MSHV_GET_VP_REGISTERS,
467 mshv_vp_registers
468 );
469
470 ioctl_write_ptr!(
471 hcl_add_vtl0_memory,
474 MSHV_IOCTL,
475 MSHV_VTL_ADD_VTL0_MEMORY,
476 protocol::hcl_pfn_range_t
477 );
478
479 ioctl_write_ptr!(
480 hcl_set_poll_file,
483 MSHV_IOCTL,
484 MSHV_VTL_SET_POLL_FILE,
485 protocol::hcl_set_poll_file
486 );
487
488 ioctl_write_ptr!(
489 hcl_hvcall_setup,
492 MSHV_IOCTL,
493 MSHV_HVCALL_SETUP,
494 protocol::hcl_hvcall_setup
495 );
496
497 ioctl_readwrite!(
498 hcl_hvcall,
500 MSHV_IOCTL,
501 MSHV_HVCALL,
502 protocol::hcl_hvcall
503 );
504
505 ioctl_write_ptr!(
506 hcl_pvalidate_pages,
508 MSHV_IOCTL,
509 MSHV_VTL_PVALIDATE,
510 mshv_pvalidate
511 );
512
513 ioctl_write_ptr!(
514 hcl_rmpadjust_pages,
516 MSHV_IOCTL,
517 MSHV_VTL_RMPADJUST,
518 mshv_rmpadjust
519 );
520
521 ioctl_write_ptr!(
522 hcl_rmpquery_pages,
524 MSHV_IOCTL,
525 MSHV_VTL_RMPQUERY,
526 mshv_rmpquery
527 );
528
529 ioctl_readwrite!(
530 hcl_tdcall,
532 MSHV_IOCTL,
533 MSHV_VTL_TDCALL,
534 mshv_tdcall
535 );
536
537 ioctl_read!(
538 hcl_read_vmx_cr4_fixed1,
539 MSHV_IOCTL,
540 MSHV_VTL_READ_VMX_CR4_FIXED1,
541 u64
542 );
543
544 ioctl_readwrite!(
545 hcl_read_guest_vsm_page_pfn,
546 MSHV_IOCTL,
547 MSHV_VTL_GUEST_VSM_VMSA_PFN,
548 u64
549 );
550
551 ioctl_readwrite!(
552 hcl_read_secure_avic_vtl0_pfn,
553 MSHV_IOCTL,
554 MSHV_VTL_SECURE_AVIC_VTL0_PFN,
555 u64
556 );
557
558 pub const HCL_CAP_REGISTER_PAGE: u32 = 1;
559 pub const HCL_CAP_VTL_RETURN_ACTION: u32 = 2;
560 pub const HCL_CAP_DR6_SHARED: u32 = 3;
561 pub const HCL_CAP_LOWER_VTL_TIMER_VIRT: u32 = 4;
562
563 ioctl_write_ptr!(
564 hcl_check_extension,
566 MSHV_IOCTL,
567 MSHV_CHECK_EXTENSION,
568 u32
569 );
570
571 ioctl_read!(mshv_create_vtl, MSHV_IOCTL, MSHV_CREATE_VTL, u8);
572
573 ioctl_read!(
575 hcl_realm_config,
576 MSHV_IOCTL,
577 MSHV_VTL_REALM_CONFIG,
578 cca::mshv_realm_config
579 );
580
581 ioctl_write_ptr!(
583 hcl_rsi_sysreg_write,
584 MSHV_IOCTL,
585 MSHV_VTL_RSI_SYSREG_WRITE,
586 cca::mshv_rsi_sysreg_rw
587 );
588
589 ioctl_readwrite!(
591 hcl_rsi_sysreg_read,
592 MSHV_IOCTL,
593 MSHV_VTL_RSI_SYSREG_READ,
594 cca::mshv_rsi_sysreg_rw
595 );
596
597 ioctl_readwrite!(
599 hcl_rsi_ipa_state_read,
600 MSHV_IOCTL,
601 MSHV_VTL_RSI_GET_IPA_STATE,
602 cca::mshv_rsi_get_ipa_state
603 );
604
605 ioctl_write_ptr!(
613 hcl_rsi_set_mem_perm,
614 MSHV_IOCTL,
615 MSHV_VTL_RSI_SET_MEM_PERM,
616 cca::mshv_rsi_set_mem_perm
617 );
618
619 #[repr(C)]
620 pub struct mshv_invlpgb {
621 pub rax: u64,
622 pub _pad0: u32,
623 pub edx: u32,
624 pub _pad1: u32,
625 pub ecx: u32,
626 }
627
628 ioctl_write_ptr!(
629 hcl_invlpgb,
631 MSHV_IOCTL,
632 MSHV_INVLPGB,
633 mshv_invlpgb
634 );
635
636 ioctl_none!(
637 hcl_tlbsync,
639 MSHV_IOCTL,
640 MSHV_TLBSYNC
641 );
642
643 ioctl_write_ptr!(
644 hcl_kickcpus,
646 MSHV_IOCTL,
647 MSHV_KICKCPUS,
648 protocol::hcl_kick_cpus
649 );
650
651 ioctl_readwrite!(
652 hcl_map_redirected_device_interrupt,
654 MSHV_IOCTL,
655 MSHV_MAP_REDIRECTED_DEVICE_INTERRUPT,
656 mshv_map_device_int
657 );
658
659 ioctl_write_ptr!(
660 hcl_restore_partition_time,
662 MSHV_IOCTL,
663 MSHV_RESTORE_PARTITION_TIME,
664 mshv_restore_partition_time
665 );
666}
667
668pub struct MshvVtlLow {
670 file: File,
671}
672
673impl MshvVtlLow {
674 pub fn new() -> Result<Self, Error> {
676 let file = fs_err::OpenOptions::new()
677 .read(true)
678 .write(true)
679 .open("/dev/mshv_vtl_low")
680 .map_err(Error::OpenGpa)?;
681
682 Ok(Self { file: file.into() })
683 }
684
685 pub fn get(&self) -> &File {
687 &self.file
688 }
689
690 pub const SHARED_MEMORY_FLAG: u64 = 1 << 63;
693}
694
695pub struct Mshv {
697 file: File,
698}
699
700impl Mshv {
701 pub fn new() -> Result<Self, Error> {
703 let file = fs_err::OpenOptions::new()
704 .read(true)
705 .write(true)
706 .open("/dev/mshv")
707 .map_err(Error::OpenMshv)?;
708
709 Ok(Self { file: file.into() })
710 }
711
712 fn check_extension(&self, cap: u32) -> Result<bool, Error> {
713 let supported = unsafe {
715 hcl_check_extension(self.file.as_raw_fd(), &cap)
716 .map_err(|e| Error::CheckExtensions(cap, e))?
717 };
718 Ok(supported != 0)
719 }
720
721 pub fn create_vtl(&self) -> Result<MshvVtl, Error> {
723 let cap = &mut 0_u8;
724 let supported =
726 unsafe { mshv_create_vtl(self.file.as_raw_fd(), cap).map_err(Error::CreateVTL)? };
727 let vtl_file = unsafe { File::from_raw_fd(supported) };
729 Ok(MshvVtl { file: vtl_file })
730 }
731}
732
733#[derive(Debug)]
735pub struct MshvVtl {
736 file: File,
737}
738
739impl MshvVtl {
740 pub fn add_vtl0_memory(&self, mem_range: MemoryRange, shared: bool) -> Result<(), Error> {
742 let flags = if shared {
743 MshvVtlLow::SHARED_MEMORY_FLAG / HV_PAGE_SIZE
744 } else {
745 0
746 };
747 let ram_disposition = protocol::hcl_pfn_range_t {
748 start_pfn: mem_range.start_4k_gpn() | flags,
749 last_pfn: mem_range.end_4k_gpn(),
750 };
751
752 unsafe {
754 hcl_add_vtl0_memory(self.file.as_raw_fd(), &ram_disposition)
755 .map_err(Error::AddVtl0Memory)?;
756 }
757
758 Ok(())
759 }
760}
761
762#[derive(Debug)]
765pub struct MshvHvcall(File);
766
767impl MshvHvcall {
768 pub fn new() -> Result<Self, Error> {
770 let file = fs_err::OpenOptions::new()
771 .read(true)
772 .write(true)
773 .open("/dev/mshv_hvcall")
774 .map_err(Error::OpenHvcall)?;
775
776 Ok(Self(file.into()))
777 }
778
779 pub fn set_allowed_hypercalls(&self, codes: &[HypercallCode]) {
781 type ItemType = u64;
782 let item_size_bytes = size_of::<ItemType>();
783 let item_size_bits = item_size_bytes * 8;
784
785 let mut allow_bitmap = Vec::<ItemType>::new();
786 for &code in codes {
787 let map_index = (code.0 as usize) / item_size_bits;
788 if map_index >= allow_bitmap.len() {
789 allow_bitmap.resize(map_index + 1, 0);
790 }
791 allow_bitmap[map_index] |= (1 as ItemType) << (code.0 % item_size_bits as u16);
792 }
793
794 let hvcall_setup = protocol::hcl_hvcall_setup {
795 allow_bitmap_size: (allow_bitmap.len() * item_size_bytes) as u64,
796 allow_bitmap_ptr: allow_bitmap.as_ptr(),
797 };
798
799 unsafe {
801 hcl_hvcall_setup(self.0.as_raw_fd(), &hvcall_setup)
802 .expect("Hypercall setup IOCTL must be supported");
803 }
804 }
805
806 pub fn accept_gpa_pages(
810 &self,
811 range: MemoryRange,
812 memory_type: hvdef::hypercall::AcceptMemoryType,
813 ) -> Result<(), AcceptPagesError> {
814 const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize
815 - size_of::<hvdef::hypercall::AcceptGpaPages>())
816 / size_of::<u64>();
817
818 let span = tracing::info_span!("accept_pages", CVM_ALLOWED, ?range);
819 let _enter = span.enter();
820
821 let mut current_page = range.start() / HV_PAGE_SIZE;
822 let end = range.end() / HV_PAGE_SIZE;
823
824 while current_page < end {
825 let header = hvdef::hypercall::AcceptGpaPages {
826 partition_id: HV_PARTITION_ID_SELF,
827 page_attributes: hvdef::hypercall::AcceptPagesAttributes::new()
828 .with_memory_type(memory_type.0)
829 .with_host_visibility(HostVisibilityType::PRIVATE)
830 .with_vtl_set(0), vtl_permission_set: hvdef::hypercall::VtlPermissionSet {
832 vtl_permission_from_1: [0; hvdef::hypercall::HV_VTL_PERMISSION_SET_SIZE],
833 },
834 gpa_page_base: current_page,
835 };
836
837 let remaining_pages = end - current_page;
838 let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64);
839
840 let output = unsafe {
848 self.hvcall_rep::<hvdef::hypercall::AcceptGpaPages, u8, u8>(
849 HypercallCode::HvCallAcceptGpaPages,
850 &header,
851 HvcallRepInput::Count(count as u16),
852 None,
853 )
854 .expect("kernel hypercall submission should always succeed")
855 };
856
857 output
858 .result()
859 .map_err(|err| AcceptPagesError::Hypervisor {
860 range: MemoryRange::from_4k_gpn_range(current_page..current_page + count),
861 output,
862 hv_error: err,
863 })?;
864
865 current_page += count;
866
867 assert_eq!(output.elements_processed() as u64, count);
868 }
869 Ok(())
870 }
871
872 pub fn modify_gpa_visibility(
883 &self,
884 host_visibility: HostVisibilityType,
885 mut gpns: &[u64],
886 ) -> Result<(), (HvError, usize)> {
887 const GPNS_PER_CALL: usize = (HV_PAGE_SIZE as usize
888 - size_of::<hvdef::hypercall::ModifySparsePageVisibility>())
889 / size_of::<u64>();
890
891 while !gpns.is_empty() {
892 let n = gpns.len().min(GPNS_PER_CALL);
893 let result = unsafe {
896 self.hvcall_rep(
897 HypercallCode::HvCallModifySparseGpaPageHostVisibility,
898 &hvdef::hypercall::ModifySparsePageVisibility {
899 partition_id: HV_PARTITION_ID_SELF,
900 host_visibility: ModifyHostVisibility::new()
901 .with_host_visibility(host_visibility),
902 reserved: 0,
903 },
904 HvcallRepInput::Elements(&gpns[..n]),
905 None::<&mut [u8]>,
906 )
907 .unwrap()
908 };
909
910 match result.result() {
911 Ok(()) => {
912 assert_eq!({ result.elements_processed() }, n);
913 }
914 Err(HvError::Timeout) => {}
915 Err(e) => return Err((e, result.elements_processed())),
916 }
917 gpns = &gpns[result.elements_processed()..];
918 }
919 Ok(())
920 }
921
922 unsafe fn invoke_hvcall_ioctl(
953 &self,
954 mut call_object: protocol::hcl_hvcall,
955 ) -> Result<HypercallOutput, HvcallError> {
956 loop {
957 unsafe {
965 hcl_hvcall(self.0.as_raw_fd(), &mut call_object)
966 .map_err(HvcallError::HypercallIoctlFailed)?;
967 }
968
969 if call_object.status.call_status() == Err(HvError::Timeout).into() {
970 call_object
975 .control
976 .set_rep_start(call_object.status.elements_processed());
977 } else {
978 if call_object.control.rep_count() == 0 {
979 assert_eq!(call_object.status.elements_processed(), 0);
981 } else {
982 assert!(
986 (call_object.status.result().is_ok()
987 && call_object.control.rep_count()
988 == call_object.status.elements_processed())
989 || (call_object.status.result().is_err()
990 && call_object.control.rep_count()
991 > call_object.status.elements_processed())
992 );
993 }
994
995 return Ok(call_object.status);
996 }
997 }
998 }
999
1000 unsafe fn hvcall<I, O>(
1028 &self,
1029 code: HypercallCode,
1030 input: &I,
1031 output: &mut O,
1032 ) -> Result<HypercallOutput, HvcallError>
1033 where
1034 I: IntoBytes + Sized + Immutable + KnownLayout,
1035 O: IntoBytes + FromBytes + Sized + Immutable + KnownLayout,
1036 {
1037 const fn assert_size<I, O>()
1038 where
1039 I: Sized,
1040 O: Sized,
1041 {
1042 assert!(size_of::<I>() <= HV_PAGE_SIZE as usize);
1043 assert!(size_of::<O>() <= HV_PAGE_SIZE as usize);
1044 }
1045 assert_size::<I, O>();
1046
1047 let control = hvdef::hypercall::Control::new().with_code(code.0);
1048
1049 let call_object = protocol::hcl_hvcall {
1050 control,
1051 input_data: input.as_bytes().as_ptr().cast(),
1052 input_size: size_of::<I>(),
1053 status: FromZeros::new_zeroed(),
1054 output_data: output.as_bytes().as_ptr().cast(),
1055 output_size: size_of::<O>(),
1056 };
1057
1058 unsafe { self.invoke_hvcall_ioctl(call_object) }
1060 }
1061
1062 unsafe fn hvcall_rep<InputHeader, InputRep, O>(
1095 &self,
1096 code: HypercallCode,
1097 input_header: &InputHeader,
1098 input_rep: HvcallRepInput<'_, InputRep>,
1099 output_rep: Option<&mut [O]>,
1100 ) -> Result<HypercallOutput, HvcallError>
1101 where
1102 InputHeader: IntoBytes + Sized + Immutable + KnownLayout,
1103 InputRep: IntoBytes + Sized + Immutable + KnownLayout,
1104 O: IntoBytes + FromBytes + Sized + Immutable + KnownLayout,
1105 {
1106 let (input, count) = match input_rep {
1108 HvcallRepInput::Elements(e) => {
1109 ([input_header.as_bytes(), e.as_bytes()].concat(), e.len())
1110 }
1111 HvcallRepInput::Count(c) => (input_header.as_bytes().to_vec(), c.into()),
1112 };
1113
1114 if input.len() > HV_PAGE_SIZE as usize {
1115 return Err(HvcallError::InputParametersTooLarge);
1116 }
1117
1118 if let Some(output_rep) = &output_rep {
1119 if output_rep.as_bytes().len() > HV_PAGE_SIZE as usize {
1120 return Err(HvcallError::OutputParametersTooLarge);
1121 }
1122
1123 if count != output_rep.len() {
1124 return Err(HvcallError::InputOutputRepListMismatch);
1125 }
1126 }
1127
1128 let (output_data, output_size) = match output_rep {
1129 Some(output_rep) => (
1130 output_rep.as_bytes().as_ptr().cast(),
1131 output_rep.as_bytes().len(),
1132 ),
1133 None => (std::ptr::null(), 0),
1134 };
1135
1136 let control = hvdef::hypercall::Control::new()
1137 .with_code(code.0)
1138 .with_rep_count(count);
1139
1140 let call_object = protocol::hcl_hvcall {
1141 control,
1142 input_data: input.as_ptr().cast(),
1143 input_size: input.len(),
1144 status: HypercallOutput::new(),
1145 output_data,
1146 output_size,
1147 };
1148
1149 unsafe { self.invoke_hvcall_ioctl(call_object) }
1151 }
1152
1153 unsafe fn hvcall_var<I, O>(
1174 &self,
1175 code: HypercallCode,
1176 input: &I,
1177 variable_input: &[u8],
1178 output: &mut O,
1179 ) -> Result<HypercallOutput, HvcallError>
1180 where
1181 I: IntoBytes + Sized + Immutable + KnownLayout,
1182 O: IntoBytes + FromBytes + Sized + Immutable + KnownLayout,
1183 {
1184 const fn assert_size<I, O>()
1185 where
1186 I: Sized,
1187 O: Sized,
1188 {
1189 assert!(size_of::<I>() <= HV_PAGE_SIZE as usize);
1190 assert!(size_of::<O>() <= HV_PAGE_SIZE as usize);
1191 }
1192 assert_size::<I, O>();
1193 assert!(variable_input.len().is_multiple_of(8));
1194
1195 let input = [input.as_bytes(), variable_input].concat();
1196 if input.len() > HV_PAGE_SIZE as usize {
1197 return Err(HvcallError::InputParametersTooLarge);
1198 }
1199
1200 let control = hvdef::hypercall::Control::new()
1201 .with_code(code.0)
1202 .with_variable_header_size(variable_input.len() / 8);
1203
1204 let call_object = protocol::hcl_hvcall {
1205 control,
1206 input_data: input.as_bytes().as_ptr().cast(),
1207 input_size: input.len(),
1208 status: FromZeros::new_zeroed(),
1209 output_data: output.as_bytes().as_ptr().cast(),
1210 output_size: size_of::<O>(),
1211 };
1212
1213 unsafe { self.invoke_hvcall_ioctl(call_object) }
1215 }
1216
1217 pub fn modify_vtl_protection_mask(
1221 &self,
1222 range: MemoryRange,
1223 map_flags: HvMapGpaFlags,
1224 target_vtl: HvInputVtl,
1225 ) -> Result<(), ApplyVtlProtectionsError> {
1226 let header = hvdef::hypercall::ModifyVtlProtectionMask {
1227 partition_id: HV_PARTITION_ID_SELF,
1228 map_flags,
1229 target_vtl,
1230 reserved: [0; 3],
1231 };
1232
1233 const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize
1234 - size_of::<hvdef::hypercall::ModifyVtlProtectionMask>())
1235 / size_of::<u64>();
1236
1237 let span = tracing::info_span!("modify_vtl_protection_mask", CVM_ALLOWED, ?range);
1238 let _enter = span.enter();
1239
1240 let start = range.start() / HV_PAGE_SIZE;
1241 let end = range.end() / HV_PAGE_SIZE;
1242
1243 let mut pages = Vec::new();
1245 for current_page in (start..end).step_by(MAX_INPUT_ELEMENTS) {
1246 let remaining_pages = end - current_page;
1247 let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64);
1248 pages.clear();
1249 pages.extend(current_page..current_page + count);
1250
1251 let output = unsafe {
1257 self.hvcall_rep::<hvdef::hypercall::ModifyVtlProtectionMask, u64, u8>(
1258 HypercallCode::HvCallModifyVtlProtectionMask,
1259 &header,
1260 HvcallRepInput::Elements(pages.as_slice()),
1261 None,
1262 )
1263 .expect("kernel hypercall submission should always succeed")
1264 };
1265
1266 output.result().map_err(|err| {
1267 let page_range =
1268 *pages.first().expect("not empty")..*pages.last().expect("not empty") + 1;
1269 ApplyVtlProtectionsError::Hypervisor {
1270 range: MemoryRange::from_4k_gpn_range(page_range),
1271 output,
1272 hv_error: err,
1273 vtl: target_vtl,
1274 }
1275 })?;
1276
1277 assert_eq!(output.elements_processed() as u64, count);
1278 }
1279
1280 Ok(())
1281 }
1282
1283 pub fn mmio_read(&self, gpa: u64, data: &mut [u8]) -> Result<(), HvError> {
1285 assert!(data.len() <= hvdef::hypercall::HV_HYPERCALL_MMIO_MAX_DATA_LENGTH);
1286
1287 let header = hvdef::hypercall::MemoryMappedIoRead {
1288 gpa,
1289 access_width: data.len() as u32,
1290 reserved_z0: 0,
1291 };
1292
1293 let mut output: hvdef::hypercall::MemoryMappedIoReadOutput = FromZeros::new_zeroed();
1294
1295 let status = unsafe {
1298 self.hvcall(
1299 HypercallCode::HvCallMemoryMappedIoRead,
1300 &header,
1301 &mut output,
1302 )
1303 .expect("submitting hypercall should not fail")
1304 };
1305
1306 if status.result().is_ok() {
1308 data.copy_from_slice(&output.data[..data.len()]);
1309 };
1310
1311 status.result()
1312 }
1313
1314 pub fn mmio_write(&self, gpa: u64, data: &[u8]) -> Result<(), HvError> {
1316 assert!(data.len() <= hvdef::hypercall::HV_HYPERCALL_MMIO_MAX_DATA_LENGTH);
1317
1318 let mut header = hvdef::hypercall::MemoryMappedIoWrite {
1319 gpa,
1320 access_width: data.len() as u32,
1321 reserved_z0: 0,
1322 data: [0; hvdef::hypercall::HV_HYPERCALL_MMIO_MAX_DATA_LENGTH],
1323 };
1324
1325 header.data[..data.len()].copy_from_slice(data);
1326
1327 let status = unsafe {
1330 self.hvcall(HypercallCode::HvCallMemoryMappedIoWrite, &header, &mut ())
1331 .expect("submitting hypercall should not fail")
1332 };
1333
1334 status.result()
1335 }
1336
1337 pub fn vbs_vm_call_report(
1344 &self,
1345 report_data: &[u8],
1346 ) -> Result<[u8; hvdef::hypercall::VBS_VM_MAX_REPORT_SIZE], HvError> {
1347 if report_data.len() > hvdef::hypercall::VBS_VM_REPORT_DATA_SIZE {
1348 return Err(HvError::InvalidParameter);
1349 }
1350
1351 let mut header = hvdef::hypercall::VbsVmCallReport {
1352 report_data: [0; hvdef::hypercall::VBS_VM_REPORT_DATA_SIZE],
1353 };
1354
1355 header.report_data[..report_data.len()].copy_from_slice(report_data);
1356
1357 let mut output: hvdef::hypercall::VbsVmCallReportOutput = FromZeros::new_zeroed();
1358
1359 let status = unsafe {
1362 self.hvcall(HypercallCode::HvCallVbsVmCallReport, &header, &mut output)
1363 .expect("submitting hypercall should not fail")
1364 };
1365
1366 if status.result().is_ok() {
1367 Ok(output.report)
1368 } else {
1369 Err(status.result().unwrap_err())
1370 }
1371 }
1372}
1373
1374#[derive(Debug)]
1376pub struct Hcl {
1377 mshv_hvcall: MshvHvcall,
1378 mshv_vtl: MshvVtl,
1379 vps: Vec<HclVp>,
1380 supports_vtl_ret_action: bool,
1381 supports_register_page: bool,
1382 dr6_shared: bool,
1383 supports_lower_vtl_timer_virt: bool,
1384 isolation: IsolationType,
1385 snp_register_bitmap: [u8; 64],
1386 sidecar: Option<SidecarClient>,
1387}
1388
1389#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1391pub enum IsolationType {
1392 None,
1394 Vbs,
1396 Snp,
1398 Tdx,
1400 Cca,
1402}
1403
1404impl IsolationType {
1405 pub fn is_isolated(&self) -> bool {
1407 !matches!(self, Self::None)
1408 }
1409
1410 pub fn is_hardware_isolated(&self) -> bool {
1412 matches!(self, Self::Snp | Self::Tdx | Self::Cca)
1413 }
1414}
1415
1416impl Hcl {
1417 pub fn dr6_shared(&self) -> bool {
1419 self.dr6_shared
1420 }
1421
1422 pub fn supports_lower_vtl_timer_virt(&self) -> bool {
1424 self.supports_lower_vtl_timer_virt
1425 }
1426}
1427
1428#[derive(Debug)]
1429struct HclVp {
1430 state: Mutex<VpState>,
1431 run: MappedPage<hcl_run>,
1432 backing: BackingState,
1433}
1434
1435#[derive(Debug)]
1436enum BackingState {
1437 MshvAarch64 {
1438 reg_page: Option<MappedPage<HvAarch64RegisterPage>>,
1439 },
1440 MshvX64 {
1441 reg_page: Option<MappedPage<HvX64RegisterPage>>,
1442 },
1443 Snp {
1444 vmsa: VtlArray<MappedPage<SevVmsa>, 2>,
1445 vtl0_apic_page: MappedPage<SevAvicPage>,
1446 vtl1_apic_page: MemoryBlock,
1448 },
1449 Tdx {
1450 vtl0_apic_page: MappedPage<VmxApicPage>,
1451 vtl1_apic_page: MemoryBlock,
1452 },
1453 Cca {
1454 plane_run: MemoryBlock,
1455 },
1456}
1457
1458#[derive(Debug)]
1459enum VpState {
1460 Running(Pthread),
1461 NotRunning,
1462}
1463
1464impl HclVp {
1465 fn new(
1466 hcl: &Hcl,
1467 vp: u32,
1468 map_reg_page: bool,
1469 isolation_type: IsolationType,
1470 private_dma_client: Option<&Arc<dyn DmaClient>>,
1471 ) -> Result<Self, Error> {
1472 let fd = &hcl.mshv_vtl.file;
1473 let run: MappedPage<hcl_run> =
1474 MappedPage::new(fd, vp as i64).map_err(|e| Error::MmapVp(e, None))?;
1475 if isolation_type.is_hardware_isolated() {
1481 unsafe {
1484 (*run.as_ptr()).proxy_irr_blocked.fill(!0);
1485 }
1486 }
1487
1488 let backing = match isolation_type {
1489 IsolationType::None | IsolationType::Vbs if cfg!(guest_arch = "aarch64") => {
1490 BackingState::MshvAarch64 {
1491 reg_page: if map_reg_page {
1492 Some(
1493 MappedPage::new(fd, HCL_REG_PAGE_OFFSET | vp as i64)
1494 .map_err(Error::MmapRegPage)?,
1495 )
1496 } else {
1497 None
1498 },
1499 }
1500 }
1501 IsolationType::None | IsolationType::Vbs => BackingState::MshvX64 {
1502 reg_page: if map_reg_page {
1503 Some(
1504 MappedPage::new(fd, HCL_REG_PAGE_OFFSET | vp as i64)
1505 .map_err(Error::MmapRegPage)?,
1506 )
1507 } else {
1508 None
1509 },
1510 },
1511 IsolationType::Snp => {
1512 unsafe {
1515 let context: &mut protocol::snp_vp_context =
1516 &mut *(&raw mut (*run.as_ptr()).context).cast();
1517 context
1518 .vmsa_tweak_bitmap
1519 .copy_from_slice(&hcl.snp_register_bitmap);
1520 }
1521 let vmsa_vtl0 = MappedPage::new(fd, HCL_VMSA_PAGE_OFFSET | vp as i64)
1522 .map_err(|e| Error::MmapVp(e, Some(Vtl::Vtl0)))?;
1523 let vmsa_vtl1 = MappedPage::new(fd, HCL_VMSA_GUEST_VSM_PAGE_OFFSET | vp as i64)
1524 .map_err(|e| Error::MmapVp(e, Some(Vtl::Vtl1)))?;
1525 BackingState::Snp {
1526 vmsa: [vmsa_vtl0, vmsa_vtl1].into(),
1527 vtl0_apic_page: MappedPage::new(fd, MSHV_APIC_PAGE_OFFSET | vp as i64)
1528 .map_err(|e| Error::MmapVp(e, Some(Vtl::Vtl0)))?,
1529 vtl1_apic_page: private_dma_client
1530 .ok_or(Error::MissingPrivateMemory)?
1531 .allocate_dma_buffer(HV_PAGE_SIZE as usize)
1532 .map_err(Error::AllocVp)?,
1533 }
1534 }
1535 IsolationType::Tdx => BackingState::Tdx {
1536 vtl0_apic_page: MappedPage::new(fd, MSHV_APIC_PAGE_OFFSET | vp as i64)
1537 .map_err(|e| Error::MmapVp(e, Some(Vtl::Vtl0)))?,
1538 vtl1_apic_page: private_dma_client
1539 .ok_or(Error::MissingPrivateMemory)?
1540 .allocate_dma_buffer(HV_PAGE_SIZE as usize)
1541 .map_err(Error::AllocVp)?,
1542 },
1543 IsolationType::Cca => BackingState::Cca {
1544 plane_run: private_dma_client
1545 .ok_or(Error::MissingPrivateMemory)?
1546 .allocate_dma_buffer(HV_PAGE_SIZE as usize)
1547 .map_err(Error::AllocVp)?,
1548 },
1549 };
1550
1551 Ok(Self {
1552 state: Mutex::new(VpState::NotRunning),
1553 run,
1554 backing,
1555 })
1556 }
1557}
1558
1559pub struct ProcessorRunner<'a, T: Backing<'a>> {
1561 hcl: &'a Hcl,
1562 vp: &'a HclVp,
1563 sidecar: Option<SidecarVp<'a>>,
1564 deferred_actions: Option<RegisteredDeferredActions<'a>>,
1565 run: &'a UnsafeCell<hcl_run>,
1566 intercept_message: &'a UnsafeCell<HvMessage>,
1567 state: T,
1568}
1569
1570#[derive(Debug, Error)]
1572pub enum NoRunner {
1573 #[error("mismatched isolation type")]
1575 MismatchedIsolation,
1576 #[error("missing sidecar")]
1578 MissingSidecar,
1579 #[error("sidecar communication error")]
1581 Sidecar(#[source] sidecar_client::SidecarError),
1582}
1583
1584#[expect(private_bounds)]
1586pub trait Backing<'a>: BackingPrivate<'a> {}
1587
1588impl<'a, T: BackingPrivate<'a>> Backing<'a> for T {}
1589
1590mod private {
1591 use super::Hcl;
1592 use super::HclVp;
1593 use super::NoRunner;
1594 use super::ProcessorRunner;
1595 use crate::GuestVtl;
1596 use hvdef::HvRegisterName;
1597 use hvdef::HvRegisterValue;
1598 use sidecar_client::SidecarVp;
1599
1600 pub(super) trait BackingPrivate<'a>: Sized {
1601 fn new(vp: &'a HclVp, sidecar: Option<&SidecarVp<'a>>, hcl: &Hcl)
1602 -> Result<Self, NoRunner>;
1603
1604 fn try_set_reg(
1605 runner: &mut ProcessorRunner<'a, Self>,
1606 vtl: GuestVtl,
1607 name: HvRegisterName,
1608 value: HvRegisterValue,
1609 ) -> bool;
1610
1611 fn must_flush_regs_on(runner: &ProcessorRunner<'a, Self>, name: HvRegisterName) -> bool;
1612
1613 fn try_get_reg(
1614 runner: &ProcessorRunner<'a, Self>,
1615 vtl: GuestVtl,
1616 name: HvRegisterName,
1617 ) -> Option<HvRegisterValue>;
1618
1619 fn flush_register_page(runner: &mut ProcessorRunner<'a, Self>);
1620 }
1621}
1622
1623impl<'a, T: Backing<'a>> Drop for ProcessorRunner<'a, T> {
1624 fn drop(&mut self) {
1625 self.flush_deferred_state();
1626 drop(self.deferred_actions.take());
1627 let old_state = std::mem::replace(&mut *self.vp.state.lock(), VpState::NotRunning);
1628 assert!(matches!(old_state, VpState::Running(thread) if thread == Pthread::current()));
1629 }
1630}
1631
1632impl<'a, T: Backing<'a>> ProcessorRunner<'a, T> {
1633 pub fn flush_deferred_state(&mut self) {
1636 T::flush_register_page(self);
1637 if let Some(actions) = &mut self.deferred_actions {
1638 actions.flush();
1639 }
1640 }
1641
1642 pub fn clear_cancel(&mut self) {
1644 if !self.is_sidecar() {
1645 let cancel = unsafe { &*(&raw mut (*self.run.get()).cancel).cast::<AtomicU32>() };
1648 cancel.store(0, Ordering::SeqCst);
1649 }
1650 }
1651
1652 pub fn set_halted(&mut self, halted: bool) {
1655 let flags = unsafe { &mut (*self.run.get()).flags };
1658 if halted {
1659 *flags |= protocol::MSHV_VTL_RUN_FLAG_HALTED
1660 } else {
1661 *flags &= !protocol::MSHV_VTL_RUN_FLAG_HALTED
1662 }
1663 }
1664
1665 pub fn proxy_irr_vtl0(&mut self) -> Option<[u32; 8]> {
1667 unsafe {
1671 let scan_proxy_irr = &*((&raw mut (*self.run.get()).scan_proxy_irr).cast::<AtomicU8>());
1672 let proxy_irr = &*((&raw mut (*self.run.get()).proxy_irr).cast::<[AtomicU32; 8]>());
1673 if scan_proxy_irr.load(Ordering::Acquire) == 0 {
1674 return None;
1675 }
1676
1677 scan_proxy_irr.store(0, Ordering::SeqCst);
1678 let mut r = [0; 8];
1679 for (irr, r) in proxy_irr.iter().zip(r.iter_mut()) {
1680 if irr.load(Ordering::Relaxed) != 0 {
1681 *r = irr.swap(0, Ordering::Relaxed);
1682 }
1683 }
1684 Some(r)
1685 }
1686 }
1687
1688 pub fn update_proxy_irr_filter_vtl0(&mut self, irr_filter: &[u32; 8]) {
1690 let proxy_irr_blocked = unsafe {
1693 &mut *((&raw mut (*self.run.get()).proxy_irr_blocked).cast::<[AtomicU32; 8]>())
1694 };
1695
1696 for (filter, irr) in proxy_irr_blocked.iter_mut().zip(irr_filter.iter()) {
1700 filter.store(!irr, Ordering::Relaxed);
1701 tracing::debug!(irr, "update_proxy_irr_filter");
1702 }
1703 }
1704
1705 pub fn proxy_irr_exit_mut_vtl0(&mut self) -> &mut [u32; 8] {
1710 unsafe { &mut (*self.run.get()).proxy_irr_exit }
1712 }
1713
1714 pub fn offload_flags_mut(&mut self) -> &mut hcl_intr_offload_flags {
1716 unsafe { &mut (*self.run.get()).offload_flags }
1718 }
1719
1720 pub fn run_sidecar(&mut self) -> Result<SidecarRun<'_, 'a>, Error> {
1722 self.sidecar.as_mut().unwrap().run().map_err(Error::Sidecar)
1723 }
1724
1725 pub fn run(&mut self) -> Result<bool, Error> {
1731 assert!(self.sidecar.is_none());
1732 if let Some(actions) = &mut self.deferred_actions {
1734 debug_assert!(self.hcl.supports_vtl_ret_action);
1735 let mut slots = unsafe { DeferredActionSlots::new(self.run) };
1738 actions.move_to_slots(&mut slots);
1739 };
1740
1741 let r = unsafe { hcl_return_to_lower_vtl(self.hcl.mshv_vtl.file.as_raw_fd()) };
1745
1746 let has_intercept = match r {
1747 Ok(_) => true,
1748 Err(nix::errno::Errno::EINTR) => false,
1749 Err(err) => return Err(Error::ReturnToLowerVtl(err)),
1750 };
1751 Ok(has_intercept)
1752 }
1753
1754 pub fn enter_mode(&mut self) -> Option<&mut EnterModes> {
1757 if self.sidecar.is_some() {
1758 None
1759 } else {
1760 Some(unsafe { &mut (*self.run.get()).mode })
1763 }
1764 }
1765
1766 pub fn exit_message(&self) -> &HvMessage {
1768 unsafe { &*self.intercept_message.get() }
1771 }
1772
1773 pub fn is_sidecar(&self) -> bool {
1775 self.sidecar.is_some()
1776 }
1777
1778 pub fn set_exit_vtl(&mut self, vtl: GuestVtl) {
1780 unsafe { (*self.run.get()).target_vtl = vtl.into() }
1784 }
1785}
1786
1787impl Hcl {
1788 pub fn new(isolation: IsolationType, sidecar: Option<SidecarClient>) -> Result<Hcl, Error> {
1790 static SIGNAL_HANDLER_INIT: Once = Once::new();
1791 SIGNAL_HANDLER_INIT.call_once(|| unsafe {
1794 signal_hook::low_level::register(libc::SIGRTMIN(), || {
1795 })
1797 .unwrap();
1798 });
1799
1800 let mshv_fd = Mshv::new()?;
1802
1803 let validate_isolation = |supported_isolation| {
1810 if isolation != supported_isolation {
1811 Err(Error::MismatchedIsolation {
1812 supported: supported_isolation,
1813 requested: isolation,
1814 })
1815 } else {
1816 Ok(())
1817 }
1818 };
1819
1820 #[cfg(guest_arch = "x86_64")]
1821 {
1822 #[cfg(target_arch = "x86_64")]
1824 let supported_isolation = {
1825 let result = safe_intrinsics::cpuid(
1826 hvdef::HV_CPUID_FUNCTION_MS_HV_ISOLATION_CONFIGURATION,
1827 0,
1828 );
1829 match result.ebx & 0xF {
1830 0 => IsolationType::None,
1831 1 => IsolationType::Vbs,
1832 2 => IsolationType::Snp,
1833 3 => IsolationType::Tdx,
1834 ty => panic!("unknown isolation type {ty:#x}"),
1835 }
1836 };
1837 #[cfg(not(target_arch = "x86_64"))]
1839 let supported_isolation = unreachable!();
1840
1841 validate_isolation(supported_isolation)?;
1842 }
1843
1844 let supports_vtl_ret_action = mshv_fd.check_extension(HCL_CAP_VTL_RETURN_ACTION)?;
1845 let supports_register_page = mshv_fd.check_extension(HCL_CAP_REGISTER_PAGE)?;
1846 let dr6_shared = mshv_fd.check_extension(HCL_CAP_DR6_SHARED)?;
1847 let supports_lower_vtl_timer_virt =
1850 match mshv_fd.check_extension(HCL_CAP_LOWER_VTL_TIMER_VIRT) {
1851 Ok(supported) => supported,
1852 Err(Error::CheckExtensions(_, nix::errno::Errno::EOPNOTSUPP))
1853 if isolation != IsolationType::Tdx =>
1854 {
1855 false
1856 }
1857 Err(err) => return Err(err),
1858 };
1859
1860 tracing::debug!(
1861 supports_vtl_ret_action,
1862 supports_register_page,
1863 supports_lower_vtl_timer_virt,
1864 "HCL capabilities",
1865 );
1866
1867 let vtl_fd = mshv_fd.create_vtl()?;
1868
1869 #[cfg(guest_arch = "aarch64")]
1870 {
1871 let supported_isolation = match isolation {
1872 IsolationType::Cca => {
1873 if vtl_fd.get_realm_config().is_ok() {
1876 IsolationType::Cca
1877 } else {
1878 IsolationType::None
1879 }
1880 }
1881 _ => IsolationType::None,
1882 };
1883
1884 validate_isolation(supported_isolation)?;
1885 }
1886
1887 #[cfg(not(any(guest_arch = "x86_64", guest_arch = "aarch64")))]
1888 {
1889 let supported_isolation = IsolationType::None;
1890
1891 validate_isolation(supported_isolation)?;
1892 }
1893
1894 let mshv_hvcall = MshvHvcall::new()?;
1896
1897 let supports_vtl_ret_action = supports_vtl_ret_action && !isolation.is_hardware_isolated();
1902 let supports_register_page = supports_register_page && !isolation.is_hardware_isolated();
1903 let snp_register_bitmap = [0u8; 64];
1904
1905 Ok(Hcl {
1906 mshv_hvcall,
1907 mshv_vtl: vtl_fd,
1908 vps: Vec::new(),
1909 supports_vtl_ret_action,
1910 supports_register_page,
1911 dr6_shared,
1912 supports_lower_vtl_timer_virt,
1913 isolation,
1914 snp_register_bitmap,
1915 sidecar,
1916 })
1917 }
1918
1919 pub fn set_allowed_hypercalls(&self, codes: &[HypercallCode]) {
1921 self.mshv_hvcall.set_allowed_hypercalls(codes)
1922 }
1923
1924 pub fn set_snp_register_bitmap(&mut self, register_bitmap: [u8; 64]) {
1926 self.snp_register_bitmap = register_bitmap;
1927 }
1928
1929 pub fn add_vps(
1931 &mut self,
1932 vp_count: u32,
1933 private_pool: Option<&Arc<dyn DmaClient>>,
1934 ) -> Result<(), Error> {
1935 self.vps = (0..vp_count)
1936 .map(|vp| {
1937 HclVp::new(
1938 self,
1939 vp,
1940 self.supports_register_page,
1941 self.isolation,
1942 private_pool,
1943 )
1944 })
1945 .collect::<Result<_, _>>()?;
1946
1947 Ok(())
1948 }
1949
1950 pub fn register_intercept(
1952 &self,
1953 intercept_type: HvInterceptType,
1954 access_type_mask: u32,
1955 intercept_parameters: HvInterceptParameters,
1956 ) -> Result<(), HvError> {
1957 let intercept_info = hvdef::hypercall::InstallIntercept {
1958 partition_id: HV_PARTITION_ID_SELF,
1959 access_type_mask,
1960 intercept_type,
1961 intercept_parameters,
1962 };
1963
1964 unsafe {
1966 self.mshv_hvcall
1967 .hvcall(
1968 HypercallCode::HvCallInstallIntercept,
1969 &intercept_info,
1970 &mut (),
1971 )
1972 .unwrap()
1973 .result()
1974 }
1975 }
1976
1977 pub fn sidecar_base_cpu(&self, vp_index: u32) -> Option<u32> {
1979 Some(self.sidecar.as_ref()?.base_cpu(vp_index))
1980 }
1981
1982 pub fn sidecar_enabled(&self) -> bool {
1984 self.sidecar.is_some()
1985 }
1986
1987 pub fn runner<'a, T: Backing<'a>>(
1989 &'a self,
1990 vp_index: u32,
1991 use_sidecar: bool,
1992 ) -> Result<ProcessorRunner<'a, T>, NoRunner> {
1993 let vp = &self.vps[vp_index as usize];
1994
1995 let sidecar = if use_sidecar {
1996 Some(
1997 self.sidecar
1998 .as_ref()
1999 .ok_or(NoRunner::MissingSidecar)?
2000 .vp(vp_index),
2001 )
2002 } else {
2003 None
2004 };
2005
2006 let state = T::new(vp, sidecar.as_ref(), self)?;
2007
2008 let VpState::NotRunning =
2010 std::mem::replace(&mut *vp.state.lock(), VpState::Running(Pthread::current()))
2011 else {
2012 panic!("another runner already exists")
2013 };
2014
2015 let actions = if sidecar.is_none() && self.supports_vtl_ret_action {
2016 Some(register_deferred_actions(self))
2017 } else {
2018 None
2019 };
2020
2021 let intercept_message = unsafe {
2024 &*sidecar.as_ref().map_or(
2025 std::ptr::addr_of!((*vp.run.as_ptr()).exit_message).cast(),
2026 |s| s.intercept_message().cast(),
2027 )
2028 };
2029
2030 Ok(ProcessorRunner {
2031 hcl: self,
2032 vp,
2033 deferred_actions: actions,
2034 run: vp.run.as_ref(),
2035 intercept_message,
2036 state,
2037 sidecar,
2038 })
2039 }
2040
2041 pub fn request_interrupt(
2043 &self,
2044 interrupt_control: hvdef::HvInterruptControl,
2045 destination_address: u64,
2046 requested_vector: u32,
2047 target_vtl: GuestVtl,
2048 ) -> Result<(), Error> {
2049 tracing::trace!(
2050 ?interrupt_control,
2051 destination_address,
2052 requested_vector,
2053 "requesting interrupt"
2054 );
2055
2056 assert!(!self.isolation.is_hardware_isolated());
2057
2058 let request = AssertVirtualInterrupt {
2059 partition_id: HV_PARTITION_ID_SELF,
2060 interrupt_control,
2061 destination_address,
2062 requested_vector,
2063 target_vtl: target_vtl as u8,
2064 rsvd0: 0,
2065 rsvd1: 0,
2066 };
2067
2068 let output = unsafe {
2070 self.mshv_hvcall.hvcall(
2071 HypercallCode::HvCallAssertVirtualInterrupt,
2072 &request,
2073 &mut (),
2074 )
2075 }
2076 .unwrap();
2077
2078 output.result().map_err(Error::RequestInterrupt)
2079 }
2080
2081 pub fn signal_event_direct(&self, vp: u32, sint: u8, flag: u16) {
2086 tracing::trace!(vp, sint, flag, "signaling event");
2087 push_deferred_action(self, DeferredAction::SignalEvent { vp, sint, flag });
2088 }
2089
2090 fn hvcall_signal_event_direct(&self, vp: u32, sint: u8, flag: u16) -> Result<bool, Error> {
2091 let signal_event_input = hvdef::hypercall::SignalEventDirect {
2092 target_partition: HV_PARTITION_ID_SELF,
2093 target_vp: vp,
2094 target_vtl: Vtl::Vtl0 as u8,
2095 target_sint: sint,
2096 flag_number: flag,
2097 };
2098 let mut signal_event_output = hvdef::hypercall::SignalEventDirectOutput {
2099 newly_signaled: 0,
2100 rsvd: [0; 7],
2101 };
2102
2103 let output = unsafe {
2105 self.mshv_hvcall.hvcall(
2106 HypercallCode::HvCallSignalEventDirect,
2107 &signal_event_input,
2108 &mut signal_event_output,
2109 )
2110 }
2111 .unwrap();
2112
2113 output
2114 .result()
2115 .map(|_| signal_event_output.newly_signaled != 0)
2116 .map_err(Error::SignalEvent)
2117 }
2118
2119 pub fn post_message_direct(
2121 &self,
2122 vp: u32,
2123 sint: u8,
2124 message: &HvMessage,
2125 ) -> Result<(), HvError> {
2126 tracing::trace!(vp, sint, "posting message");
2127
2128 let post_message = hvdef::hypercall::PostMessageDirect {
2129 partition_id: HV_PARTITION_ID_SELF,
2130 vp_index: vp,
2131 vtl: Vtl::Vtl0 as u8,
2132 padding0: [0; 3],
2133 sint,
2134 padding1: [0; 3],
2135 message: zerocopy::Unalign::new(*message),
2136 padding2: 0,
2137 };
2138
2139 let output = unsafe {
2141 self.mshv_hvcall.hvcall(
2142 HypercallCode::HvCallPostMessageDirect,
2143 &post_message,
2144 &mut (),
2145 )
2146 }
2147 .unwrap();
2148
2149 output.result()
2150 }
2151
2152 pub fn set_poll_file(&self, vp: u32, file: RawFd) -> Result<(), Error> {
2155 unsafe {
2159 hcl_set_poll_file(
2160 self.mshv_vtl.file.as_raw_fd(),
2161 &protocol::hcl_set_poll_file {
2162 cpu: vp as i32,
2163 fd: file,
2164 },
2165 )
2166 .map_err(Error::SetPollFile)?;
2167 }
2168 Ok(())
2169 }
2170
2171 fn to_hv_gpa_range_array(gpa_memory_ranges: &[MemoryRange]) -> Vec<HvGpaRange> {
2172 const PAGES_PER_ENTRY: u64 = 2048;
2173 const PAGE_SIZE: u64 = HV_PAGE_SIZE;
2174
2175 let estimated_size: usize = gpa_memory_ranges
2177 .iter()
2178 .map(|memory_range| {
2179 let total_pages = (memory_range.end() - memory_range.start()).div_ceil(PAGE_SIZE);
2180 total_pages.div_ceil(PAGES_PER_ENTRY)
2181 })
2182 .sum::<u64>() as usize;
2183
2184 let mut hv_gpa_ranges = Vec::with_capacity(estimated_size);
2186
2187 for memory_range in gpa_memory_ranges {
2188 let total_pages = (memory_range.end() - memory_range.start()).div_ceil(PAGE_SIZE);
2190
2191 let start_page = memory_range.start_4k_gpn();
2193
2194 hv_gpa_ranges.extend(
2196 (0..total_pages)
2197 .step_by(PAGES_PER_ENTRY as usize)
2198 .map(|start| {
2199 let end = std::cmp::min(total_pages, start + PAGES_PER_ENTRY);
2200 let pages_in_this_range = end - start;
2201 let gpa_page_number = start_page + start;
2202
2203 let extended = HvGpaRangeExtended::new()
2204 .with_additional_pages(pages_in_this_range - 1)
2205 .with_large_page(false) .with_gpa_page_number(gpa_page_number);
2207
2208 HvGpaRange(extended.into_bits())
2209 }),
2210 );
2211 }
2212
2213 hv_gpa_ranges }
2215
2216 fn pin_unpin_gpa_ranges_internal(
2217 &self,
2218 gpa_ranges: &[HvGpaRange],
2219 action: GpaPinUnpinAction,
2220 ) -> Result<(), PinUnpinError> {
2221 const PIN_REQUEST_HEADER_SIZE: usize =
2222 size_of::<hvdef::hypercall::PinUnpinGpaPageRangesHeader>();
2223 const MAX_INPUT_ELEMENTS: usize =
2224 (HV_PAGE_SIZE as usize - PIN_REQUEST_HEADER_SIZE) / size_of::<u64>();
2225
2226 let header = hvdef::hypercall::PinUnpinGpaPageRangesHeader { reserved: 0 };
2227 let mut ranges_processed = 0;
2228
2229 for chunk in gpa_ranges.chunks(MAX_INPUT_ELEMENTS) {
2230 let output = unsafe {
2236 self.mshv_hvcall
2237 .hvcall_rep(
2238 match action {
2239 GpaPinUnpinAction::PinGpaRange => HypercallCode::HvCallPinGpaPageRanges,
2240 GpaPinUnpinAction::UnpinGpaRange => {
2241 HypercallCode::HvCallUnpinGpaPageRanges
2242 }
2243 },
2244 &header,
2245 HvcallRepInput::Elements(chunk),
2246 None::<&mut [u8]>,
2247 )
2248 .expect("submitting pin/unpin hypercall should not fail")
2249 };
2250
2251 ranges_processed += output.elements_processed();
2252
2253 output.result().map_err(|e| PinUnpinError {
2254 ranges_processed,
2255 error: e,
2256 })?;
2257 }
2258
2259 if ranges_processed == gpa_ranges.len() {
2261 Ok(())
2262 } else {
2263 Err(PinUnpinError {
2264 ranges_processed,
2265 error: HvError::OperationFailed,
2266 })
2267 }
2268 }
2269
2270 fn perform_pin_unpin_gpa_ranges(
2271 &self,
2272 gpa_ranges: &[MemoryRange],
2273 action: GpaPinUnpinAction,
2274 rollback_action: GpaPinUnpinAction,
2275 ) -> Result<(), HvError> {
2276 let hv_gpa_ranges: Vec<HvGpaRange> = Self::to_hv_gpa_range_array(gpa_ranges);
2277
2278 match self.pin_unpin_gpa_ranges_internal(&hv_gpa_ranges, action) {
2280 Ok(_) => Ok(()),
2281 Err(PinUnpinError {
2282 error,
2283 ranges_processed,
2284 }) => {
2285 let pinned_ranges = &hv_gpa_ranges[..ranges_processed];
2287 if let Err(rollback_error) =
2288 self.pin_unpin_gpa_ranges_internal(pinned_ranges, rollback_action)
2289 {
2290 panic!(
2292 "Failed to perform action {:?} on ranges. Error : {:?}. \
2293 Attempted to rollback {:?} ranges out of {:?}.\n rollback error: {:?}",
2294 action,
2295 error,
2296 ranges_processed,
2297 gpa_ranges.len(),
2298 rollback_error
2299 );
2300 }
2301 Err(error)
2303 }
2304 }
2305 }
2306
2307 pub fn pin_gpa_ranges(&self, ranges: &[MemoryRange]) -> Result<(), HvError> {
2315 self.perform_pin_unpin_gpa_ranges(
2316 ranges,
2317 GpaPinUnpinAction::PinGpaRange,
2318 GpaPinUnpinAction::UnpinGpaRange,
2319 )
2320 }
2321
2322 pub fn unpin_gpa_ranges(&self, ranges: &[MemoryRange]) -> Result<(), HvError> {
2329 self.perform_pin_unpin_gpa_ranges(
2330 ranges,
2331 GpaPinUnpinAction::UnpinGpaRange,
2332 GpaPinUnpinAction::PinGpaRange,
2333 )
2334 }
2335
2336 pub fn modify_vtl_protection_mask(
2338 &self,
2339 range: MemoryRange,
2340 map_flags: HvMapGpaFlags,
2341 target_vtl: HvInputVtl,
2342 ) -> Result<(), ApplyVtlProtectionsError> {
2343 if self.isolation.is_hardware_isolated() {
2344 todo!();
2346 }
2347
2348 self.mshv_hvcall
2349 .modify_vtl_protection_mask(range, map_flags, target_vtl)
2350 }
2351
2352 pub fn check_vtl_access(
2354 &self,
2355 gpa: u64,
2356 target_vtl: GuestVtl,
2357 flags: HvMapGpaFlags,
2358 ) -> Result<Option<CheckVtlAccessResult>, Error> {
2359 assert!(!self.isolation.is_hardware_isolated());
2360
2361 let header = hvdef::hypercall::CheckSparseGpaPageVtlAccess {
2362 partition_id: HV_PARTITION_ID_SELF,
2363 target_vtl: HvInputVtl::from(target_vtl),
2364 desired_access: u32::from(flags) as u8,
2365 reserved0: 0,
2366 reserved1: 0,
2367 };
2368
2369 let mut output = [hvdef::hypercall::CheckSparseGpaPageVtlAccessOutput::new()];
2370
2371 let status = unsafe {
2374 self.mshv_hvcall.hvcall_rep::<hvdef::hypercall::CheckSparseGpaPageVtlAccess, u64, hvdef::hypercall::CheckSparseGpaPageVtlAccessOutput>(
2375 HypercallCode::HvCallCheckSparseGpaPageVtlAccess,
2376 &header,
2377 HvcallRepInput::Elements(&[gpa >> hvdef::HV_PAGE_SHIFT]),
2378 Some(&mut output),
2379 )
2380 .expect("check_vtl_access hypercall should not fail")
2381 };
2382
2383 status.result().map_err(Error::CheckVtlAccess)?;
2384
2385 let access_result = output[0];
2386
2387 if access_result.result_code() as u32
2388 != hvdef::hypercall::CheckGpaPageVtlAccessResultCode::SUCCESS.0
2389 {
2390 return Ok(Some(CheckVtlAccessResult {
2391 vtl: (access_result.intercepting_vtl() as u8)
2392 .try_into()
2393 .expect("checking vtl permissions failure should return valid vtl"),
2394 denied_flags: (access_result.denied_access() as u32).into(),
2395 }));
2396 }
2397
2398 assert_eq!(status.elements_processed(), 1);
2399 Ok(None)
2400 }
2401
2402 pub fn enable_partition_vtl(
2404 &self,
2405 vtl: GuestVtl,
2406 flags: hvdef::hypercall::EnablePartitionVtlFlags,
2407 ) -> Result<(), HvError> {
2408 use hvdef::hypercall;
2409
2410 let header = hypercall::EnablePartitionVtl {
2411 partition_id: HV_PARTITION_ID_SELF,
2412 target_vtl: vtl.into(),
2413 flags,
2414 reserved_z0: 0,
2415 reserved_z1: 0,
2416 };
2417
2418 let status = unsafe {
2421 self.mshv_hvcall
2422 .hvcall(HypercallCode::HvCallEnablePartitionVtl, &header, &mut ())
2423 .expect("submitting hypercall should not fail")
2424 };
2425
2426 status.result()
2427 }
2428
2429 pub fn enable_vp_vtl(
2431 &self,
2432 vp_index: u32,
2433 vtl: GuestVtl,
2434 hv_vp_context: InitialVpContextX64,
2435 ) -> Result<(), HvError> {
2436 use hvdef::hypercall;
2437
2438 let header = hypercall::EnableVpVtlX64 {
2439 partition_id: HV_PARTITION_ID_SELF,
2440 vp_index,
2441 target_vtl: vtl.into(),
2442 reserved: [0; 3],
2443 vp_vtl_context: hv_vp_context,
2444 };
2445
2446 let status = unsafe {
2449 self.mshv_hvcall
2450 .hvcall(HypercallCode::HvCallEnableVpVtl, &header, &mut ())
2451 .expect("submitting hypercall should not fail")
2452 };
2453
2454 status.result()
2455 }
2456
2457 pub fn vtl1_vmsa_pfn(&self, cpu_index: u32) -> u64 {
2459 let mut vp_pfn = cpu_index as u64; unsafe {
2465 hcl_read_guest_vsm_page_pfn(self.mshv_vtl.file.as_raw_fd(), &mut vp_pfn)
2466 .expect("should always succeed");
2467 }
2468
2469 vp_pfn
2470 }
2471
2472 pub fn secure_avic_vtl0_pfn(&self, cpu_index: u32) -> u64 {
2480 let mut savic_pfn = cpu_index as u64; unsafe {
2486 hcl_read_secure_avic_vtl0_pfn(self.mshv_vtl.file.as_raw_fd(), &mut savic_pfn)
2487 .expect("should always succeed");
2488 }
2489
2490 savic_pfn
2491 }
2492
2493 pub fn isolation(&self) -> IsolationType {
2495 self.isolation
2496 }
2497
2498 pub fn read_vmx_cr4_fixed1(&self) -> u64 {
2500 let mut value = 0;
2501
2502 unsafe {
2505 hcl_read_vmx_cr4_fixed1(self.mshv_vtl.file.as_raw_fd(), &mut value)
2506 .expect("should always succeed");
2507 }
2508
2509 value
2510 }
2511
2512 pub fn tdx_enable_hw_seal_keys(&self) -> Result<bool, x86defs::tdx::TdCallResult> {
2521 self.mshv_vtl.tdx_enable_hw_seal_keys()
2522 }
2523
2524 pub fn tdx_read_features0(
2531 &self,
2532 ) -> Result<x86defs::tdx::TdxFeatures0, x86defs::tdx::TdCallResult> {
2533 self.mshv_vtl.tdx_read_features0()
2534 }
2535
2536 pub fn retarget_device_interrupt(
2539 &self,
2540 device_id: u64,
2541 entry: hvdef::hypercall::InterruptEntry,
2542 vector: u32,
2543 multicast: bool,
2544 target_processors: ProcessorSet<'_>,
2545 proxy_redirect: bool,
2546 ) -> Result<(), HvError> {
2547 let header = hvdef::hypercall::RetargetDeviceInterrupt {
2548 partition_id: HV_PARTITION_ID_SELF,
2549 device_id,
2550 entry,
2551 rsvd: 0,
2552 target_header: hvdef::hypercall::InterruptTarget {
2553 vector,
2554 flags: hvdef::hypercall::HvInterruptTargetFlags::default()
2555 .with_multicast(multicast)
2556 .with_processor_set(true)
2557 .with_proxy_redirect(proxy_redirect),
2558 mask_or_format: hvdef::hypercall::HV_GENERIC_SET_SPARSE_4K,
2561 },
2562 };
2563 let processor_set = Vec::from_iter(target_processors.as_generic_set());
2564
2565 let status = unsafe {
2568 self.mshv_hvcall
2569 .hvcall_var(
2570 HypercallCode::HvCallRetargetDeviceInterrupt,
2571 &header,
2572 processor_set.as_bytes(),
2573 &mut (),
2574 )
2575 .expect("submitting hypercall should not fail")
2576 };
2577
2578 status.result()
2579 }
2580
2581 #[cfg(debug_assertions)]
2584 pub fn rmp_query(&self, gpa: u64, vtl: GuestVtl) -> x86defs::snp::SevRmpAdjust {
2585 use x86defs::snp::SevRmpAdjust;
2586
2587 let page_count = 1u64;
2588 let flags = [u64::from(SevRmpAdjust::new().with_target_vmpl(match vtl {
2589 GuestVtl::Vtl0 => 2,
2590 GuestVtl::Vtl1 => 1,
2591 }))];
2592 let page_size = [0u64];
2593 let pages_processed = 0;
2594
2595 debug_assert!(flags.len() == page_count as usize);
2596 debug_assert!(page_size.len() == page_count as usize);
2597
2598 let query = mshv_rmpquery {
2599 start_pfn: gpa / HV_PAGE_SIZE,
2600 page_count,
2601 terminate_on_failure: 0,
2602 ram: 0,
2603 padding: Default::default(),
2604 flags: flags.as_ptr().cast_mut(),
2605 page_size: page_size.as_ptr().cast_mut(),
2606 pages_processed: core::ptr::from_ref(&pages_processed).cast_mut(),
2607 };
2608
2609 unsafe {
2611 hcl_rmpquery_pages(self.mshv_vtl.file.as_raw_fd(), &query)
2612 .expect("should always succeed");
2613 }
2614 debug_assert!(pages_processed <= page_count);
2615
2616 SevRmpAdjust::from(flags[0])
2617 }
2618
2619 pub fn invlpgb(&self, rax: u64, edx: u32, ecx: u32) {
2621 let data = mshv_invlpgb {
2622 rax,
2623 edx,
2624 ecx,
2625 _pad0: 0,
2626 _pad1: 0,
2627 };
2628 unsafe {
2630 hcl_invlpgb(self.mshv_vtl.file.as_raw_fd(), &data).expect("should always succeed");
2631 }
2632 }
2633
2634 pub fn tlbsync(&self) {
2636 unsafe {
2638 hcl_tlbsync(self.mshv_vtl.file.as_raw_fd()).expect("should always succeed");
2639 }
2640 }
2641
2642 pub fn kick_cpus(
2644 &self,
2645 cpus: impl IntoIterator<Item = u32>,
2646 cancel_run: bool,
2647 wait_for_other_cpus: bool,
2648 ) {
2649 let mut cpu_bitmap: BitVec<u8> = BitVec::from_vec(vec![0; self.vps.len().div_ceil(8)]);
2650 for cpu in cpus {
2651 cpu_bitmap.set(cpu as usize, true);
2652 }
2653
2654 let data = protocol::hcl_kick_cpus {
2655 len: cpu_bitmap.len() as u64,
2656 cpu_mask: cpu_bitmap.as_bitptr().pointer(),
2657 flags: protocol::hcl_kick_cpus_flags::new()
2658 .with_cancel_run(cancel_run)
2659 .with_wait_for_other_cpus(wait_for_other_cpus),
2660 };
2661
2662 unsafe {
2664 hcl_kickcpus(self.mshv_vtl.file.as_raw_fd(), &data).expect("should always succeed");
2665 }
2666 }
2667
2668 pub fn map_redirected_device_interrupt(
2670 &self,
2671 vector: u32,
2672 apic_id: u32,
2673 create_mapping: bool,
2674 ) -> Result<u32, Error> {
2675 let mut param = mshv_map_device_int {
2676 vector,
2677 apic_id,
2678 create_mapping: create_mapping.into(),
2679 padding: [0; 7],
2680 };
2681
2682 unsafe {
2684 hcl_map_redirected_device_interrupt(self.mshv_vtl.file.as_raw_fd(), &mut param)
2685 .map_err(Error::MapRedirectedDeviceInterrupt)?;
2686 }
2687
2688 Ok(param.vector)
2689 }
2690
2691 pub fn restore_partition_time(
2694 &self,
2695 tsc_sequence: u32,
2696 reference_time_in_100_ns: u64,
2697 tsc: u64,
2698 ) -> Result<(), Error> {
2699 let partition_time = mshv_restore_partition_time {
2700 tsc_sequence,
2701 reserved: 0,
2702 reference_time_in_100_ns,
2703 tsc,
2704 };
2705
2706 unsafe {
2708 hcl_restore_partition_time(self.mshv_vtl.file.as_raw_fd(), &partition_time)
2709 .map_err(Error::RestorePartitionTime)?;
2710 }
2711
2712 Ok(())
2713 }
2714}