1#![cfg(all(target_os = "linux", guest_is_native))]
7#![expect(unsafe_code)]
9
10#[cfg(guest_arch = "aarch64")]
11mod aarch64;
12#[cfg(guest_arch = "x86_64")]
13mod x86_64;
14
15#[cfg(guest_arch = "aarch64")]
16use aarch64 as arch;
17#[cfg(guest_arch = "x86_64")]
18use x86_64 as arch;
19
20pub mod irqfd;
23
24use guestmem::DoorbellRegistration;
25use guestmem::GuestMemory;
26use hv1_emulator::message_queues::MessageQueues;
27use hvdef::HV_PAGE_SHIFT;
28use hvdef::HvDeliverabilityNotificationsRegister;
29use hvdef::HvError;
30use hvdef::HvMessage;
31use hvdef::HvMessageType;
32use hvdef::HvPartitionPropertyCode;
33use hvdef::Vtl;
34use hvdef::hypercall::HV_INTERCEPT_ACCESS_MASK_EXECUTE;
35use hvdef::hypercall::HvRegisterAssoc;
36use inspect::Inspect;
37use inspect::InspectMut;
38use mshv_bindings::MSHV_SET_MEM_BIT_EXECUTABLE;
39use mshv_bindings::MSHV_SET_MEM_BIT_WRITABLE;
40use mshv_bindings::mshv_install_intercept;
41use mshv_bindings::mshv_user_mem_region;
42use mshv_ioctls::Mshv;
43use mshv_ioctls::MshvError;
44use mshv_ioctls::VcpuFd;
45use mshv_ioctls::VmFd;
46use mshv_ioctls::set_bits;
47use pal::unix::pthread::*;
48use pal_event::Event;
49use parking_lot::Mutex;
50use parking_lot::RwLock;
51use std::convert::Infallible;
52use std::future::poll_fn;
53use std::io;
54use std::os::fd::AsFd;
55use std::os::fd::AsRawFd;
56use std::os::fd::IntoRawFd as _;
57use std::sync::Arc;
58use std::sync::Once;
59use std::sync::Weak;
60use std::sync::atomic::AtomicBool;
61use std::sync::atomic::Ordering;
62use std::task::Waker;
63use thiserror::Error;
64use virt::NeedsYield;
65use virt::PartitionAccessState;
66use virt::ProtoPartitionConfig;
67use virt::StopVp;
68use virt::VpHaltReason;
69use virt::VpIndex;
70use virt::io::CpuIo;
71use vmcore::interrupt::Interrupt;
72use vmcore::reference_time::GetReferenceTime;
73use vmcore::reference_time::ReferenceTimeResult;
74use vmcore::synic::GuestEventPort;
75
76trait VcpuFdExt {
78 fn get_hvdef_regs(&self, regs: &mut [HvRegisterAssoc]) -> Result<(), KernelError>;
79 fn set_hvdef_regs(&self, regs: &[HvRegisterAssoc]) -> Result<(), KernelError>;
80}
81
82impl VcpuFdExt for VcpuFd {
83 fn get_hvdef_regs(&self, regs: &mut [HvRegisterAssoc]) -> Result<(), KernelError> {
84 use mshv_bindings::hv_register_assoc;
85 const {
86 assert!(size_of::<HvRegisterAssoc>() == size_of::<hv_register_assoc>());
87 assert!(align_of::<HvRegisterAssoc>() >= align_of::<hv_register_assoc>());
88 }
89 self.get_reg(unsafe {
91 std::mem::transmute::<&mut [HvRegisterAssoc], &mut [hv_register_assoc]>(regs)
92 })?;
93 Ok(())
94 }
95
96 fn set_hvdef_regs(&self, regs: &[HvRegisterAssoc]) -> Result<(), KernelError> {
97 use mshv_bindings::hv_register_assoc;
98 const {
99 assert!(size_of::<HvRegisterAssoc>() == size_of::<hv_register_assoc>());
100 assert!(align_of::<HvRegisterAssoc>() >= align_of::<hv_register_assoc>());
101 }
102 self.set_reg(unsafe {
104 std::mem::transmute::<&[HvRegisterAssoc], &[hv_register_assoc]>(regs)
105 })?;
106 Ok(())
107 }
108}
109
110#[derive(Debug)]
112pub struct LinuxMshv {
113 mshv: Mshv,
114 snp_disable_cpuid_offload: bool,
115}
116
117impl LinuxMshv {
118 pub fn new() -> io::Result<Self> {
120 let file = fs_err::File::open("/dev/mshv")?;
121 Ok(Self::from(std::fs::File::from(file)))
122 }
123
124 pub fn with_snp_cpuid_offload_disabled(mut self, disabled: bool) -> Self {
126 self.snp_disable_cpuid_offload = disabled;
127 self
128 }
129}
130
131impl From<std::fs::File> for LinuxMshv {
132 fn from(file: std::fs::File) -> Self {
133 LinuxMshv {
134 mshv: unsafe { Mshv::new_with_fd_number(file.into_raw_fd()) },
137 snp_disable_cpuid_offload: false,
138 }
139 }
140}
141
142impl<'a> MshvProtoPartition<'a> {
143 fn new(config: ProtoPartitionConfig<'a>, vmfd: VmFd) -> Result<Self, Error> {
147 if config.processor_topology.vp_count() > u8::MAX as u32 {
148 return Err(ErrorInner::TooManyVps(config.processor_topology.vp_count()).into());
149 }
150
151 let vps = config
152 .processor_topology
153 .vps_arch()
154 .map(|vp| MshvVpInner {
155 vp_info: vp,
156 thread: RwLock::new(None),
157 needs_yield: NeedsYield::new(),
158 message_queues: MessageQueues::new(),
159 message_queues_pending: AtomicBool::new(false),
160 waker: RwLock::new(None),
161 })
162 .collect();
163
164 let bsp = vmfd
165 .create_vcpu(0)
166 .map_err(|e| ErrorInner::CreateVcpu(e.into()))?;
167
168 vmfd.install_intercept(mshv_install_intercept {
170 access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
171 intercept_type: hvdef::hypercall::HvInterceptType::HvInterceptTypeHypercall.0,
172 intercept_parameter: Default::default(),
173 })
174 .map_err(|e| ErrorInner::InstallIntercept(e.into()))?;
175
176 vmfd.install_intercept(mshv_install_intercept {
177 access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
178 intercept_type:
179 hvdef::hypercall::HvInterceptType::HvInterceptTypeUnknownSynicConnection.0,
180 intercept_parameter: Default::default(),
181 })
182 .map_err(|e| ErrorInner::InstallIntercept(e.into()))?;
183
184 vmfd.install_intercept(mshv_install_intercept {
185 access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
186 intercept_type:
187 hvdef::hypercall::HvInterceptType::HvInterceptTypeRetargetInterruptWithUnknownDeviceId.0,
188 intercept_parameter: Default::default(),
189 })
190 .map_err(|e| ErrorInner::InstallIntercept(e.into()))?;
191
192 static SIGNAL_HANDLER_INIT: Once = Once::new();
194 SIGNAL_HANDLER_INIT.call_once(|| unsafe {
197 signal_hook::low_level::register(libc::SIGRTMIN(), || {
198 })
202 .unwrap();
203 });
204
205 if let Some(hv_config) = &config.hv_config {
206 if hv_config.vtl2.is_some() {
207 return Err(ErrorInner::Vtl2NotSupported.into());
208 }
209 }
210
211 Ok(MshvProtoPartition {
212 config,
213 #[cfg(guest_arch = "x86_64")]
214 isolation: arch::MshvProtoPartitionIsolation::None,
215 vmfd,
216 vps,
217 bsp,
218 })
219 }
220}
221
222pub fn is_available() -> Result<bool, Error> {
224 match std::fs::metadata("/dev/mshv") {
225 Ok(_) => Ok(true),
226 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(false),
227 Err(err) => Err(ErrorInner::AvailableCheck(err).into()),
228 }
229}
230
231pub struct MshvProtoPartition<'a> {
233 config: ProtoPartitionConfig<'a>,
234 #[cfg(guest_arch = "x86_64")]
235 isolation: arch::MshvProtoPartitionIsolation,
236 vmfd: VmFd,
237 vps: Vec<MshvVpInner>,
238 bsp: VcpuFd,
239}
240
241#[derive(Inspect)]
243pub struct MshvPartition {
244 #[inspect(flatten)]
245 inner: Arc<MshvPartitionInner>,
246 #[inspect(skip)]
247 synic_ports: Arc<virt::synic::SynicPorts<MshvPartitionInner>>,
248}
249
250enum MshvIsolationState {
251 None,
252 #[cfg(guest_arch = "x86_64")]
253 Snp(arch::SnpPartitionState),
254}
255
256impl MshvIsolationState {
257 #[cfg(guest_arch = "x86_64")]
258 fn is_isolated(&self) -> bool {
259 !matches!(self, Self::None)
260 }
261
262 #[cfg(guest_arch = "x86_64")]
263 fn initial_vp_state_source(&self) -> virt::InitialVpStateSource {
264 match self {
265 Self::None => virt::InitialVpStateSource::Registers,
266 Self::Snp(_) => virt::InitialVpStateSource::ImportedContext,
267 }
268 }
269
270 #[cfg(guest_arch = "x86_64")]
271 fn snp(&self) -> Option<&arch::SnpPartitionState> {
272 match self {
273 Self::None => None,
274 Self::Snp(snp) => Some(snp),
275 }
276 }
277
278 fn map_user_memory(&self, vmfd: &VmFd, region: mshv_user_mem_region) -> anyhow::Result<bool> {
279 match self {
280 Self::None => {
281 vmfd.map_user_memory(region)?;
282 Ok(true)
283 }
284 #[cfg(guest_arch = "x86_64")]
285 Self::Snp(snp) => match *snp.launch_state.lock() {
286 arch::SnpLaunchState::NotStarted => Ok(false),
289 arch::SnpLaunchState::Started => {
290 anyhow::bail!("cannot add a memory mapping while SNP launch is in progress")
291 }
292 arch::SnpLaunchState::Finished => {
293 vmfd.map_user_memory(region)?;
294 Ok(true)
295 }
296 arch::SnpLaunchState::Failed => {
297 anyhow::bail!("cannot add a memory mapping after SNP launch failed")
298 }
299 },
300 }
301 }
302}
303
304#[derive(Inspect)]
305struct MshvPartitionInner {
306 #[inspect(skip)]
307 vmfd: VmFd,
308 #[inspect(skip)]
311 bsp_vcpufd: VcpuFd,
312 #[inspect(skip)]
313 memory: Mutex<MshvMemoryRangeState>,
314 gm: GuestMemory,
315 mem_layout: vm_topology::memory::MemoryLayout,
316 #[inspect(skip)]
317 vps: Vec<MshvVpInner>,
318 #[cfg(guest_arch = "x86_64")]
319 irq_routes: virt::irqcon::IrqRoutes,
320 #[inspect(skip)]
321 gsi_states: Mutex<Box<[irqfd::GsiState; irqfd::NUM_GSIS]>>,
322 caps: virt::PartitionCapabilities,
323 synic_ports: virt::synic::SynicPortMap,
324 #[cfg(guest_arch = "x86_64")]
325 software_devices: virt::x86::apic_software_device::ApicSoftwareDevices,
326 #[inspect(skip)]
327 isolation: MshvIsolationState,
328 time_frozen: Mutex<bool>,
331 #[cfg(guest_arch = "aarch64")]
334 #[inspect(skip)]
335 gic_msi: vm_topology::processor::aarch64::GicMsiController,
336}
337
338struct MshvVpInner {
339 vp_info: vm_topology::processor::TargetVpInfo,
340 thread: RwLock<Option<Pthread>>,
341 needs_yield: NeedsYield,
342 message_queues: MessageQueues,
343 message_queues_pending: AtomicBool,
346 waker: RwLock<Option<Waker>>,
349}
350
351struct MshvVpInnerCleaner<'a> {
352 vpinner: &'a MshvVpInner,
353}
354
355impl Drop for MshvVpInnerCleaner<'_> {
356 fn drop(&mut self) {
357 self.vpinner.thread.write().take();
358 }
359}
360
361impl GetReferenceTime for MshvPartitionInner {
362 fn now(&self) -> ReferenceTimeResult {
363 let ref_time = self
366 .vmfd
367 .get_partition_property(HvPartitionPropertyCode::ReferenceTime.0)
368 .unwrap();
369 ReferenceTimeResult {
370 ref_time,
371 system_time: None,
372 }
373 }
374}
375
376impl MshvPartitionInner {
377 fn vp(&self, vp_index: VpIndex) -> &MshvVpInner {
378 &self.vps[vp_index.index() as usize]
379 }
380
381 fn freeze_time(&self) -> Result<(), Error> {
384 let mut frozen = self.time_frozen.lock();
385 if !*frozen {
386 self.vmfd
387 .set_partition_property(HvPartitionPropertyCode::TimeFreeze.0, 1)
388 .map_err(|e| ErrorInner::SetPartitionProperty(e.into()))?;
389 *frozen = true;
390 }
391 Ok(())
392 }
393
394 fn thaw_time(&self) -> Result<(), Error> {
397 let mut frozen = self.time_frozen.lock();
398 if *frozen {
399 self.vmfd
400 .set_partition_property(HvPartitionPropertyCode::TimeFreeze.0, 0)
401 .map_err(|e| ErrorInner::SetPartitionProperty(e.into()))?;
402 *frozen = false;
403 }
404 Ok(())
405 }
406
407 fn post_message(&self, vp_index: VpIndex, sint: u8, message: &HvMessage) {
408 let vp = self.vp(vp_index);
409 let wake = vp.message_queues.enqueue_message(sint, message);
410 if wake && !vp.message_queues_pending.swap(true, Ordering::Release) {
412 if let Some(waker) = &*vp.waker.read() {
413 waker.wake_by_ref();
414 }
415 }
416 }
417
418 fn post_message_direct(&self, vp: u32, sint: u8, message: &HvMessage) -> Result<(), MshvError> {
425 use mshv_bindings::mshv_root_hvcall;
426
427 let post_message = hvdef::hypercall::PostMessageDirect {
428 partition_id: 0,
429 vp_index: vp,
430 vtl: Vtl::Vtl0 as u8,
431 padding0: [0; 3],
432 sint,
433 padding1: [0; 3],
434 message: zerocopy::Unalign::new(*message),
435 padding2: 0,
436 };
437
438 let mut args = mshv_root_hvcall {
439 code: hvdef::HypercallCode::HvCallPostMessageDirect.0,
440 in_sz: size_of::<hvdef::hypercall::PostMessageDirect>() as u16,
441 in_ptr: std::ptr::addr_of!(post_message) as u64,
442 ..Default::default()
443 };
444 self.vmfd.hvcall(&mut args)
445 }
446
447 fn signal_event_direct(&self, vp: u32, sint: u8, flag: u16) -> Result<(), MshvError> {
454 use mshv_bindings::mshv_root_hvcall;
455 use zerocopy::FromZeros;
456
457 let input = hvdef::hypercall::SignalEventDirect {
458 target_partition: 0,
459 target_vp: vp,
460 target_vtl: 0,
461 target_sint: sint,
462 flag_number: flag,
463 };
464 let mut output = hvdef::hypercall::SignalEventDirectOutput::new_zeroed();
465
466 let mut args = mshv_root_hvcall {
467 code: hvdef::HypercallCode::HvCallSignalEventDirect.0,
468 in_sz: size_of::<hvdef::hypercall::SignalEventDirect>() as u16,
469 out_sz: size_of::<hvdef::hypercall::SignalEventDirectOutput>() as u16,
470 in_ptr: std::ptr::addr_of!(input) as u64,
471 out_ptr: std::ptr::addr_of_mut!(output) as u64,
472 ..Default::default()
473 };
474 self.vmfd.hvcall(&mut args)
475 }
476}
477
478pub struct MshvProcessorBinder {
480 partition: Arc<MshvPartitionInner>,
481 vcpufd: Option<VcpuFd>,
482 vpindex: VpIndex,
483 #[cfg(guest_arch = "x86_64")]
484 snp: Option<arch::SnpVpState>,
485}
486
487struct MshvVpRunner<'a> {
490 vcpufd: &'a VcpuFd,
491 #[cfg(guest_arch = "x86_64")]
492 reg_page: Option<*mut hvdef::HvX64RegisterPage>,
493 #[cfg(guest_arch = "x86_64")]
494 ghcb_page: Option<*mut x86defs::snp::GhcbPage>,
495}
496
497impl MshvVpRunner<'_> {
498 fn run(&mut self) -> Result<HvMessage, MshvError> {
499 self.vcpufd.run().map(|msg| {
500 unsafe { std::mem::transmute::<mshv_bindings::hv_message, HvMessage>(msg) }
504 })
505 }
506
507 #[cfg(guest_arch = "x86_64")]
508 fn reg_page(&mut self) -> &mut hvdef::HvX64RegisterPage {
509 let page = self
510 .reg_page
511 .expect("register page is unavailable for isolated VPs");
512 unsafe { &mut *page }
516 }
517
518 #[cfg(guest_arch = "x86_64")]
519 fn ghcb_page(&mut self) -> Option<&mut x86defs::snp::GhcbPage> {
520 self.ghcb_page.map(|page| unsafe { &mut *page })
523 }
524}
525
526#[derive(InspectMut)]
528pub struct MshvProcessor<'a> {
529 #[inspect(skip)]
530 partition: &'a MshvPartitionInner,
531 #[inspect(skip)]
532 inner: &'a MshvVpInner,
533 #[inspect(skip)]
534 vpindex: VpIndex,
535 #[inspect(skip)]
536 runner: MshvVpRunner<'a>,
537 #[inspect(skip)]
540 deliverability_notifications: HvDeliverabilityNotificationsRegister,
541}
542
543impl MshvProcessor<'_> {
544 fn flush_messages(&mut self, deliverable_sints: u16) {
548 let nonempty_sints =
549 self.inner
550 .message_queues
551 .post_pending_messages(deliverable_sints, |sint, message| {
552 match self
553 .partition
554 .post_message_direct(self.vpindex.index(), sint, message)
555 {
556 Ok(()) => {
557 tracing::trace!(sint, "sint message posted successfully");
558 Ok(())
559 }
560 Err(e) => {
561 tracelimit::warn_ratelimited!(
562 error = &e as &dyn std::error::Error,
563 "dropping sint message"
564 );
565 Err(HvError::ObjectInUse)
566 }
567 }
568 });
569
570 if self.deliverability_notifications.sints() != nonempty_sints {
571 let notifications = self.deliverability_notifications.with_sints(nonempty_sints);
572 tracing::trace!(?notifications, "setting deliverability notifications");
573 self.partition
574 .vmfd
575 .register_deliverabilty_notifications(
576 self.vpindex.index(),
577 u64::from(notifications),
578 )
579 .expect("requesting deliverability is not a fallible operation");
580 self.deliverability_notifications = notifications;
581 }
582 }
583
584 fn handle_sint_deliverable(&mut self, deliverable_sints: u16) {
588 self.deliverability_notifications
590 .set_sints(self.deliverability_notifications.sints() & !deliverable_sints);
591
592 self.flush_messages(deliverable_sints);
593 }
594
595 fn reset_synic_state(&mut self) {
597 self.inner.message_queues.clear();
598 self.inner
599 .message_queues_pending
600 .store(false, Ordering::Relaxed);
601 self.deliverability_notifications = HvDeliverabilityNotificationsRegister::new();
602 }
603}
604
605impl virt::Processor for MshvProcessor<'_> {
606 type StateAccess<'a>
607 = &'a mut Self
608 where
609 Self: 'a;
610
611 fn set_debug_state(
612 &mut self,
613 _vtl: Vtl,
614 _state: Option<&virt::x86::DebugState>,
615 ) -> Result<(), <&mut Self as virt::vp::AccessVpState>::Error> {
616 Err(ErrorInner::NotSupported.into())
617 }
618
619 async fn run_vp(
620 &mut self,
621 stop: StopVp<'_>,
622 dev: &impl CpuIo,
623 ) -> Result<Infallible, VpHaltReason> {
624 let vpinner = self.inner;
625 let _cleaner = MshvVpInnerCleaner { vpinner };
626
627 assert!(vpinner.thread.write().replace(Pthread::current()).is_none());
628
629 self.partition
630 .thaw_time()
631 .expect("failed to thaw partition time");
632
633 if vpinner.message_queues.pending_sints() != 0 {
636 vpinner
637 .message_queues_pending
638 .store(true, Ordering::Relaxed);
639 }
640
641 let mut last_waker: Option<Waker> = None;
642
643 loop {
644 vpinner.needs_yield.maybe_yield().await;
645 stop.check()?;
646
647 poll_fn(|cx| {
649 if !last_waker.as_ref().is_some_and(|w| cx.waker().will_wake(w)) {
650 last_waker = Some(cx.waker().clone());
651 *vpinner.waker.write() = last_waker.clone();
652 }
653 std::task::Poll::Ready(())
654 })
655 .await;
656
657 if vpinner.message_queues_pending.load(Ordering::Relaxed) {
659 vpinner
660 .message_queues_pending
661 .store(false, Ordering::SeqCst);
662 let pending_sints = vpinner.message_queues.pending_sints();
663 if pending_sints != 0 {
664 self.flush_messages(pending_sints);
665 }
666 }
667
668 match self.runner.run() {
669 Ok(exit) => {
670 self.handle_exit(&exit, dev).await?;
671 }
672 Err(e) => match e.errno() {
673 libc::EAGAIN | libc::EINTR => {}
674 _ => tracing::error!(
675 error = &e as &dyn std::error::Error,
676 "vcpufd.run returned error"
677 ),
678 },
679 }
680 }
681 }
682
683 fn flush_async_requests(&mut self) {}
684
685 fn access_state(&mut self, vtl: Vtl) -> Self::StateAccess<'_> {
686 assert_eq!(vtl, Vtl::Vtl0);
687 self
688 }
689
690 fn reset(&mut self) -> Result<(), impl std::error::Error + Send + Sync + 'static> {
691 use virt::vp::AccessVpState;
692
693 let vp_info = self.inner.vp_info;
694 self.access_state(Vtl::Vtl0)
695 .reset_all(&vp_info)
696 .map_err(|e| ErrorInner::ResetState(Box::new(e)))?;
697
698 self.reset_synic_state();
699
700 Ok::<(), Error>(())
701 }
702}
703
704impl hv1_hypercall::PostMessage for arch::MshvHypercallHandler<'_> {
705 fn post_message(&mut self, connection_id: u32, message: &[u8]) -> hvdef::HvResult<()> {
706 self.partition
707 .synic_ports
708 .handle_post_message(Vtl::Vtl0, connection_id, false, message)
709 }
710}
711
712impl hv1_hypercall::SignalEvent for arch::MshvHypercallHandler<'_> {
713 fn signal_event(&mut self, connection_id: u32, flag: u16) -> hvdef::HvResult<()> {
714 self.partition
715 .synic_ports
716 .handle_signal_event(Vtl::Vtl0, connection_id, flag)
717 }
718}
719
720#[derive(Error, Debug)]
722#[error(transparent)]
723pub struct Error(ErrorInner);
724
725impl<T: Into<ErrorInner>> From<T> for Error {
726 fn from(err: T) -> Self {
727 Error(err.into())
728 }
729}
730
731#[derive(Error, Debug)]
733enum ErrorInner {
734 #[error("operation not supported")]
735 NotSupported,
736 #[error("create_vm failed")]
737 CreateVMFailed,
738 #[error("failed to initialize VM")]
739 CreateVMInitFailed(#[source] anyhow::Error),
740 #[error("failed to create VCPU")]
741 CreateVcpu(#[source] KernelError),
742 #[cfg(guest_arch = "x86_64")]
743 #[error("VP register page is unavailable")]
744 MissingRegisterPage,
745 #[cfg(guest_arch = "x86_64")]
746 #[error(transparent)]
747 Snp(#[from] arch::SnpError),
748 #[error("vtl2 not supported")]
749 Vtl2NotSupported,
750 #[error("isolation not supported")]
751 IsolationNotSupported,
752 #[cfg(guest_arch = "x86_64")]
753 #[error("invalid MSHV configuration: {0}")]
754 InvalidConfiguration(&'static str),
755 #[cfg(guest_arch = "x86_64")]
756 #[error("SNP IGVM requests unsupported highest VTL {0}")]
757 UnsupportedSnpVtl(u8),
758 #[cfg(guest_arch = "x86_64")]
759 #[error("SNP IGVM requests unsupported shared GPA boundary {0:#x}")]
760 UnsupportedSnpSharedGpaBoundary(u64),
761 #[cfg(guest_arch = "x86_64")]
762 #[error("MSHV does not support SNP IGVM relocation")]
763 SnpIgvmRelocationUnsupported,
764 #[cfg(guest_arch = "x86_64")]
765 #[error("SNP IGVM must contain exactly one BSP VP context")]
766 InvalidSnpIgvmTopology,
767 #[cfg(guest_arch = "x86_64")]
768 #[error("failed to parse the SNP IGVM VMSA")]
769 InvalidSnpIgvmVmsa,
770 #[cfg(guest_arch = "x86_64")]
771 #[error(
772 "unsupported SNP IGVM VMSA state: SEV features {sev_features:#x}, virtual TOM {virtual_tom:#x}"
773 )]
774 UnsupportedSnpIgvmVmsa { sev_features: u64, virtual_tom: u64 },
775 #[cfg(guest_arch = "x86_64")]
776 #[error("SNP IGVM VMSA backing memory is not contiguous")]
777 InvalidSnpVmsaBacking,
778 #[cfg(guest_arch = "x86_64")]
779 #[error("SNP IGVM VMSA GPA {0:#x} is invalid")]
780 InvalidSnpVmsaGpa(u64),
781 #[cfg(guest_arch = "x86_64")]
782 #[error("SNP IGVM VMSA overlaps configured guest RAM")]
783 SnpVmsaOverlapsRam,
784 #[cfg(guest_arch = "x86_64")]
785 #[error("SNP VMSA import does not match the configured IGVM VP context")]
786 InvalidSnpVmsaImport,
787 #[error("failed to stat /dev/mshv")]
788 AvailableCheck(#[source] io::Error),
789 #[cfg(guest_arch = "x86_64")]
790 #[error("failed to get partition property")]
791 GetPartitionProperty(#[source] KernelError),
792 #[error("failed to set partition property")]
793 SetPartitionProperty(#[source] KernelError),
794 #[error("register access error")]
795 Register(#[source] KernelError),
796 #[cfg(guest_arch = "x86_64")]
797 #[error("failed to get VP state {ty}")]
798 GetVpState {
799 #[source]
800 error: KernelError,
801 ty: u8,
802 },
803 #[cfg(guest_arch = "x86_64")]
804 #[error("failed to set VP state {ty}")]
805 SetVpState {
806 #[source]
807 error: KernelError,
808 ty: u8,
809 },
810 #[error("failed to reset state")]
811 ResetState(#[source] Box<virt::state::StateError<Error>>),
812 #[error("install intercept failed")]
813 InstallIntercept(#[source] KernelError),
814 #[cfg(guest_arch = "x86_64")]
815 #[error("failed to register cpuid override")]
816 RegisterCpuid(#[source] KernelError),
817 #[error("too many virtual processors: {0}")]
818 TooManyVps(u32),
819 #[cfg(guest_arch = "x86_64")]
820 #[error("unsupported processor vendor: {0:?}")]
821 UnsupportedProcessorVendor(hvdef::HvProcessorVendor),
822 #[cfg(guest_arch = "x86_64")]
823 #[error("failed to create virtual device")]
824 NewDevice(#[source] virt::x86::apic_software_device::DeviceIdInUse),
825}
826
827#[derive(Error, Debug)]
829enum KernelError {
830 #[error("kernel error")]
831 Kernel(#[source] io::Error),
832 #[error("hypercall {code:#x?} error")]
833 Hypercall {
834 code: hvdef::HypercallCode,
835 #[source]
836 error: HvError,
837 },
838}
839
840impl From<MshvError> for KernelError {
841 fn from(err: MshvError) -> Self {
842 match err {
843 MshvError::Errno(e) => KernelError::Kernel(e.into()),
844 MshvError::Hypercall {
845 code,
846 status_raw,
847 status: _,
848 } => KernelError::Hypercall {
849 code: hvdef::HypercallCode(code),
850 error: HvError::from(
851 std::num::NonZeroU16::new(status_raw)
852 .expect("not an error, hypercall returned success"),
853 ),
854 },
855 }
856 }
857}
858
859fn create_vm_with_retry(
861 mshv: &Mshv,
862 args: &mshv_bindings::mshv_create_partition_v2,
863) -> Result<VmFd, Error> {
864 loop {
865 match mshv.create_vm_with_args(args) {
866 Ok(fd) => return Ok(fd),
867 Err(e) => {
868 if e.errno() == libc::EINTR {
869 continue;
870 } else {
871 return Err(ErrorInner::CreateVMFailed.into());
872 }
873 }
874 }
875 }
876}
877
878fn common_synthetic_features() -> hvdef::HvPartitionSyntheticProcessorFeatures {
882 hvdef::HvPartitionSyntheticProcessorFeatures::new()
883 .with_hypervisor_present(true)
884 .with_hv1(true)
885 .with_access_vp_run_time_reg(true)
886 .with_access_partition_reference_counter(true)
887 .with_access_synic_regs(true)
888 .with_access_synthetic_timer_regs(true)
889 .with_access_intr_ctrl_regs(true)
890 .with_access_hypercall_regs(true)
891 .with_access_vp_index(true)
892 .with_fast_hypercall_output(true)
893 .with_direct_synthetic_timers(true)
894 .with_extended_processor_masks(true)
895 .with_tb_flush_hypercalls(true)
896 .with_synthetic_cluster_ipi(true)
897 .with_notify_long_spin_wait(true)
898 .with_query_numa_distance(true)
899 .with_signal_events(true)
900 .with_retarget_device_interrupt(true)
901}
902
903impl PartitionAccessState for MshvPartition {
904 type StateAccess<'a> = &'a MshvPartition;
905
906 fn access_state(&self, vtl: Vtl) -> Self::StateAccess<'_> {
907 assert_eq!(vtl, Vtl::Vtl0);
908 self
909 }
910}
911
912#[derive(Debug, Default)]
913struct MshvMemoryRangeState {
914 ranges: Vec<Option<MshvMemoryRange>>,
915}
916
917#[derive(Debug, Copy, Clone)]
918struct MshvMemoryRange {
919 region: mshv_user_mem_region,
920 mapped: bool,
921}
922
923impl virt::PartitionMemoryMapper for MshvPartition {
924 fn memory_mapper(&self, vtl: Vtl) -> Arc<dyn virt::PartitionMemoryMap> {
925 assert_eq!(vtl, Vtl::Vtl0);
926 self.inner.clone()
927 }
928
929 fn host_access(&self) -> Option<Arc<dyn virt::PartitionHostAccess>> {
930 #[cfg(guest_arch = "x86_64")]
931 if self.inner.isolation.snp().is_some() {
932 return Some(self.inner.clone());
933 }
934 None
935 }
936}
937
938impl virt::PartitionHostAccess for MshvPartitionInner {
940 fn acquire_host_access(&self, _addr: u64, _size: u64, _write: bool) -> anyhow::Result<()> {
941 #[cfg(guest_arch = "x86_64")]
949 if self.isolation.snp().is_some() {
950 return arch::acquire_snp_host_access(self, _addr, _size);
951 }
952 anyhow::bail!("acquiring host access is not supported")
953 }
954}
955
956impl virt::PartitionMemoryMap for MshvPartitionInner {
957 unsafe fn map_range(
958 &self,
959 data: *mut u8,
960 size: usize,
961 addr: u64,
962 writable: bool,
963 exec: bool,
964 ) -> anyhow::Result<()> {
965 let mut state = self.memory.lock();
966
967 let mut slot_to_use = None;
970 for (slot, range) in state.ranges.iter_mut().enumerate() {
971 match range {
972 Some(range) if range.region.userspace_addr == data as u64 => {
973 slot_to_use = Some(slot);
974 break;
975 }
976 Some(_) => (),
977 None => slot_to_use = Some(slot),
978 }
979 }
980 if slot_to_use.is_none() {
981 slot_to_use = Some(state.ranges.len());
982 state.ranges.push(None);
983 }
984 let slot_to_use = slot_to_use.unwrap();
985
986 let mut flags = 0;
987 if writable {
988 flags |= set_bits!(u8, MSHV_SET_MEM_BIT_WRITABLE);
989 }
990 if exec {
991 flags |= set_bits!(u8, MSHV_SET_MEM_BIT_EXECUTABLE);
992 }
993 let mem_region = mshv_user_mem_region {
994 size: size as u64,
995 guest_pfn: addr >> HV_PAGE_SHIFT,
996 userspace_addr: data as u64,
997 flags,
998 rsvd: [0; 7],
999 };
1000
1001 let _span = tracing::info_span!(
1002 "mshv map user memory",
1003 guest_pfn = mem_region.guest_pfn,
1004 size = mem_region.size,
1005 writable,
1006 exec,
1007 )
1008 .entered();
1009 let mapped = self.isolation.map_user_memory(&self.vmfd, mem_region)?;
1010 state.ranges[slot_to_use] = Some(MshvMemoryRange {
1011 region: mem_region,
1012 mapped,
1013 });
1014 Ok(())
1015 }
1016
1017 fn unmap_range(&self, addr: u64, size: u64) -> anyhow::Result<()> {
1018 let unmap_start = addr >> HV_PAGE_SHIFT;
1019 let unmap_end = addr
1020 .checked_add(size)
1021 .ok_or_else(|| anyhow::anyhow!("unmap range overflows the guest address space"))?
1022 >> HV_PAGE_SHIFT;
1023 let mut state = self.memory.lock();
1024 for range in state.ranges.iter().flatten() {
1025 let region_start = range.region.guest_pfn;
1026 let region_end = region_start
1027 .checked_add(range.region.size >> HV_PAGE_SHIFT)
1028 .ok_or_else(|| anyhow::anyhow!("tracked memory region overflows GPA space"))?;
1029 anyhow::ensure!(
1030 (unmap_start <= region_start && region_end <= unmap_end)
1031 || region_end <= unmap_start
1032 || unmap_end <= region_start,
1033 "unmap range partially overlaps a tracked memory region"
1034 );
1035 }
1036
1037 for entry in &mut state.ranges {
1038 let Some(range) = entry.as_ref() else {
1039 continue;
1040 };
1041 let region = &range.region;
1042 let region_start = region.guest_pfn;
1043 let region_end = region_start
1044 .checked_add(region.size >> HV_PAGE_SHIFT)
1045 .ok_or_else(|| anyhow::anyhow!("tracked memory region overflows GPA space"))?;
1046 if unmap_start <= region_start && region_end <= unmap_end {
1047 let _span = tracing::info_span!(
1049 "mshv unmap user memory",
1050 guest_pfn = region.guest_pfn,
1051 size = region.size,
1052 )
1053 .entered();
1054 if range.mapped {
1055 self.vmfd.unmap_user_memory(*region)?;
1056 }
1057 *entry = None;
1058 }
1059 }
1060 Ok(())
1061 }
1062}
1063
1064struct MshvDoorbellEntry {
1070 partition: Weak<MshvPartitionInner>,
1071 event: Event,
1072 guest_address: u64,
1073 datamatch: u64,
1074 len: u32,
1075 flags: u32,
1076}
1077
1078impl MshvDoorbellEntry {
1079 fn new(
1080 partition: &Arc<MshvPartitionInner>,
1081 guest_address: u64,
1082 value: Option<u64>,
1083 length: Option<u32>,
1084 fd: &Event,
1085 ) -> io::Result<MshvDoorbellEntry> {
1086 let flags = if value.is_some() {
1087 1 << mshv_bindings::MSHV_IOEVENTFD_BIT_DATAMATCH
1088 } else {
1089 0
1090 };
1091 let datamatch = value.unwrap_or(0);
1092 let len = length.unwrap_or(0);
1093 let event = fd.clone();
1094
1095 let ioeventfd = mshv_bindings::mshv_user_ioeventfd {
1096 datamatch,
1097 addr: guest_address,
1098 len,
1099 fd: event.as_fd().as_raw_fd(),
1100 flags,
1101 ..Default::default()
1102 };
1103 let ret = unsafe {
1107 libc::ioctl(
1108 partition.vmfd.as_raw_fd(),
1109 mshv_ioctls::MSHV_IOEVENTFD() as _,
1110 std::ptr::from_ref(&ioeventfd),
1111 )
1112 };
1113 if ret < 0 {
1114 return Err(io::Error::last_os_error());
1115 }
1116
1117 Ok(Self {
1118 partition: Arc::downgrade(partition),
1119 event,
1120 guest_address,
1121 datamatch,
1122 len,
1123 flags,
1124 })
1125 }
1126}
1127
1128impl Drop for MshvDoorbellEntry {
1129 fn drop(&mut self) {
1130 if let Some(partition) = self.partition.upgrade() {
1131 let ioeventfd = mshv_bindings::mshv_user_ioeventfd {
1132 datamatch: self.datamatch,
1133 addr: self.guest_address,
1134 len: self.len,
1135 fd: self.event.as_fd().as_raw_fd(),
1136 flags: self.flags | (1 << mshv_bindings::MSHV_IOEVENTFD_BIT_DEASSIGN),
1137 ..Default::default()
1138 };
1139 let ret = unsafe {
1143 libc::ioctl(
1144 partition.vmfd.as_raw_fd(),
1145 mshv_ioctls::MSHV_IOEVENTFD() as _,
1146 std::ptr::from_ref(&ioeventfd),
1147 )
1148 };
1149 assert!(
1150 ret >= 0,
1151 "failed to unregister doorbell at {:#x}: {}",
1152 self.guest_address,
1153 io::Error::last_os_error()
1154 );
1155 }
1156 }
1157}
1158
1159impl DoorbellRegistration for MshvPartition {
1160 fn register_doorbell(
1161 &self,
1162 guest_address: u64,
1163 value: Option<u64>,
1164 length: Option<u32>,
1165 fd: &Event,
1166 ) -> io::Result<Box<dyn Send + Sync>> {
1167 Ok(Box::new(MshvDoorbellEntry::new(
1168 &self.inner,
1169 guest_address,
1170 value,
1171 length,
1172 fd,
1173 )?))
1174 }
1175}
1176
1177impl virt::synic::Synic for MshvPartitionInner {
1178 fn port_map(&self) -> &virt::synic::SynicPortMap {
1179 &self.synic_ports
1180 }
1181
1182 fn post_message(&self, _vtl: Vtl, vp: VpIndex, sint: u8, typ: u32, payload: &[u8]) {
1183 self.post_message(vp, sint, &HvMessage::new(HvMessageType(typ), 0, payload));
1184 }
1185
1186 fn new_guest_event_port(
1187 self: Arc<Self>,
1188 _vtl: Vtl,
1189 vp: u32,
1190 sint: u8,
1191 flag: u16,
1192 ) -> Box<dyn GuestEventPort> {
1193 Box::new(MshvGuestEventPort {
1194 partition: Arc::downgrade(&self),
1195 params: Arc::new(Mutex::new(MshvEventPortParams {
1196 vp: VpIndex::new(vp),
1197 sint,
1198 flag,
1199 })),
1200 })
1201 }
1202
1203 fn prefer_os_events(&self) -> bool {
1204 false
1205 }
1206}
1207
1208#[derive(Debug, Clone)]
1210struct MshvGuestEventPort {
1211 partition: Weak<MshvPartitionInner>,
1212 params: Arc<Mutex<MshvEventPortParams>>,
1213}
1214
1215#[derive(Debug, Copy, Clone)]
1216struct MshvEventPortParams {
1217 vp: VpIndex,
1218 sint: u8,
1219 flag: u16,
1220}
1221
1222impl GuestEventPort for MshvGuestEventPort {
1223 fn interrupt(&self) -> Interrupt {
1224 let partition = self.partition.clone();
1225 let params = self.params.clone();
1226 Interrupt::from_fn(move || {
1227 let MshvEventPortParams { vp, sint, flag } = *params.lock();
1228 if let Some(partition) = partition.upgrade() {
1229 partition
1230 .signal_event_direct(vp.index(), sint, flag)
1231 .unwrap_or_else(|_| {
1232 panic!(
1233 "Failed signal synic sint {} on vp {:?} with flag {}",
1234 sint, vp, flag
1235 )
1236 });
1237 }
1238 })
1239 }
1240
1241 fn set_target_vp(&mut self, vp: u32) -> Result<(), vmcore::synic::HypervisorError> {
1242 self.params.lock().vp = VpIndex::new(vp);
1243 Ok(())
1244 }
1245}