1#![cfg(all(target_os = "linux", guest_is_native, guest_arch = "x86_64"))]
7#![expect(missing_docs)]
8#![expect(unsafe_code)]
10
11mod vm_state;
12mod vp_state;
13
14use arrayvec::ArrayVec;
15use guestmem::DoorbellRegistration;
16use guestmem::GuestMemory;
17use hv1_emulator::message_queues::MessageQueues;
18use hv1_hypercall::X64RegisterIo;
19use hvdef::HV_PAGE_SHIFT;
20use hvdef::HvDeliverabilityNotificationsRegister;
21use hvdef::HvError;
22use hvdef::HvMessage;
23use hvdef::HvMessageType;
24use hvdef::HvX64RegisterName;
25use hvdef::HvX64VpExecutionState;
26use hvdef::Vtl;
27use hvdef::hypercall::HV_INTERCEPT_ACCESS_MASK_EXECUTE;
28use hvdef::hypercall::HvRegisterAssoc;
29use inspect::Inspect;
30use inspect::InspectMut;
31use mshv_bindings::MSHV_SET_MEM_BIT_EXECUTABLE;
32use mshv_bindings::MSHV_SET_MEM_BIT_WRITABLE;
33use mshv_bindings::hv_message;
34use mshv_bindings::hv_register_assoc;
35use mshv_bindings::hv_register_value;
36use mshv_bindings::hv_u128;
37use mshv_bindings::hv_x64_io_port_intercept_message;
38use mshv_bindings::hv_x64_memory_intercept_message;
39use mshv_bindings::hv_x64_segment_register;
40use mshv_bindings::mshv_install_intercept;
41use mshv_bindings::mshv_user_mem_region;
42use mshv_ioctls::InterruptRequest;
43use mshv_ioctls::Mshv;
44use mshv_ioctls::MshvError;
45use mshv_ioctls::VcpuFd;
46use mshv_ioctls::VmFd;
47use mshv_ioctls::set_bits;
48use mshv_ioctls::set_registers_64;
49use pal::unix::pthread::*;
50use pal_event::Event;
51use parking_lot::Mutex;
52use parking_lot::RwLock;
53use pci_core::msi::SignalMsi;
54use std::convert::Infallible;
55use std::io;
56use std::sync::Arc;
57use std::sync::Once;
58use std::sync::Weak;
59use thiserror::Error;
60use virt::Hv1;
61use virt::NeedsYield;
62use virt::PartitionAccessState;
63use virt::PartitionConfig;
64use virt::ProtoPartition;
65use virt::ProtoPartitionConfig;
66use virt::StopVp;
67use virt::VpHaltReason;
68use virt::VpIndex;
69use virt::io::CpuIo;
70use virt::irqcon::MsiRequest;
71use virt::x86::max_physical_address_size_from_cpuid;
72use virt_support_x86emu::emulate::EmuTranslateError;
73use virt_support_x86emu::emulate::EmuTranslateResult;
74use virt_support_x86emu::emulate::EmulatorSupport;
75use virt_support_x86emu::emulate::TranslateGvaSupport;
76use virt_support_x86emu::emulate::TranslateMode;
77use virt_support_x86emu::emulate::emulate_translate_gva;
78use virt_support_x86emu::translate::TranslationRegisters;
79use vmcore::interrupt::Interrupt;
80use vmcore::reference_time::GetReferenceTime;
81use vmcore::reference_time::ReferenceTimeResult;
82use vmcore::reference_time::ReferenceTimeSource;
83use vmcore::synic::GuestEventPort;
84use x86defs::RFlags;
85use x86defs::SegmentRegister;
86use zerocopy::IntoBytes;
87
88#[derive(Debug)]
89pub struct LinuxMshv;
90
91struct MshvEmuCache {
92 gps: [u64; 16],
94 segs: [SegmentRegister; 6],
96 rip: u64,
97 rflags: RFlags,
98
99 cr0: u64,
100 efer: u64,
101}
102
103impl virt::Hypervisor for LinuxMshv {
104 type ProtoPartition<'a> = MshvProtoPartition<'a>;
105 type Partition = MshvPartition;
106 type Error = Error;
107
108 fn new_partition<'a>(
109 &mut self,
110 config: ProtoPartitionConfig<'a>,
111 ) -> Result<MshvProtoPartition<'a>, Self::Error> {
112 if config.isolation.is_isolated() {
113 return Err(Error::IsolationNotSupported);
114 }
115
116 let mshv = Mshv::new().map_err(Error::OpenMshv)?;
118
119 let vmfd: VmFd;
125 loop {
126 match mshv.create_vm() {
127 Ok(fd) => vmfd = fd,
128 Err(e) => {
129 if e.errno() == libc::EINTR {
130 continue;
134 } else {
135 return Err(Error::CreateVMFailed);
136 }
137 }
138 }
139 break;
140 }
141
142 vmfd.initialize()
143 .map_err(|e| Error::CreateVMInitFailed(e.into()))?;
144
145 let mut vps: Vec<MshvVpInner> = Vec::new();
147 for vp in config.processor_topology.vps_arch() {
148 if vp.base.vp_index.index() != vp.apic_id {
149 return Err(Error::NotSupported);
151 }
152
153 let vcpufd = vmfd
154 .create_vcpu(vp.base.vp_index.index() as u8)
155 .map_err(Error::CreateVcpu)?;
156
157 vps.push(MshvVpInner {
158 vcpufd,
159 thread: RwLock::new(None),
160 needs_yield: NeedsYield::new(),
161 message_queues: MessageQueues::new(),
162 deliverability_notifications: Mutex::new(
163 HvDeliverabilityNotificationsRegister::new(),
164 ),
165 });
166 }
167
168 let intercept_args = 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 vmfd.install_intercept(intercept_args)
175 .map_err(Error::InstallIntercept)?;
176
177 static SIGNAL_HANDLER_INIT: Once = Once::new();
179 SIGNAL_HANDLER_INIT.call_once(|| unsafe {
182 signal_hook::low_level::register(libc::SIGRTMIN(), || {
183 })
186 .unwrap();
187 });
188
189 if let Some(hv_config) = &config.hv_config {
190 if hv_config.vtl2.is_some() {
191 return Err(Error::Vtl2NotSupported);
192 }
193 }
194
195 Ok(MshvProtoPartition { config, vmfd, vps })
196 }
197
198 fn is_available(&self) -> Result<bool, Self::Error> {
199 match std::fs::metadata("/dev/mshv") {
200 Ok(_) => Ok(true),
201 Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(false),
202 Err(err) => Err(Error::AvailableCheck(err)),
203 }
204 }
205}
206
207pub struct MshvProtoPartition<'a> {
209 config: ProtoPartitionConfig<'a>,
210 vmfd: VmFd,
211 vps: Vec<MshvVpInner>,
212}
213
214impl ProtoPartition for MshvProtoPartition<'_> {
215 type Partition = MshvPartition;
216 type ProcessorBinder = MshvProcessorBinder;
217 type Error = Error;
218
219 fn cpuid(&self, eax: u32, ecx: u32) -> [u32; 4] {
220 self.vps[0]
223 .vcpufd
224 .get_cpuid_values(eax, ecx, 0, 0)
225 .expect("cpuid should not fail")
226 }
227
228 fn max_physical_address_size(&self) -> u8 {
229 max_physical_address_size_from_cpuid(&|eax, ecx| self.cpuid(eax, ecx))
230 }
231
232 fn build(
233 self,
234 config: PartitionConfig<'_>,
235 ) -> Result<(Self::Partition, Vec<Self::ProcessorBinder>), Self::Error> {
236 let caps = virt::PartitionCapabilities::from_cpuid(
240 self.config.processor_topology,
241 &mut |function, index| {
242 self.vps[0]
243 .vcpufd
244 .get_cpuid_values(function, index, 0, 0)
245 .expect("cpuid should not fail")
246 },
247 )
248 .map_err(Error::Capabilities)?;
249
250 let partition = MshvPartition {
252 inner: Arc::new(MshvPartitionInner {
253 vmfd: self.vmfd,
254 memory: Default::default(),
255 gm: config.guest_memory.clone(),
256 vps: self.vps,
257 irq_routes: Default::default(),
258 caps,
259 }),
260 };
261
262 let vps = self
263 .config
264 .processor_topology
265 .vps()
266 .map(|vp| MshvProcessorBinder {
267 partition: partition.inner.clone(),
268 vpindex: vp.vp_index,
269 })
270 .collect();
271
272 Ok((partition, vps))
273 }
274}
275
276#[derive(Debug)]
278pub struct MshvPartition {
279 inner: Arc<MshvPartitionInner>,
280}
281
282#[derive(Debug)]
283struct MshvPartitionInner {
284 vmfd: VmFd,
285 memory: Mutex<MshvMemoryRangeState>,
286 gm: GuestMemory,
287 vps: Vec<MshvVpInner>,
288 irq_routes: virt::irqcon::IrqRoutes,
289 caps: virt::PartitionCapabilities,
290}
291
292#[derive(Debug)]
293struct MshvVpInner {
294 vcpufd: VcpuFd,
295 thread: RwLock<Option<Pthread>>,
296 needs_yield: NeedsYield,
297 message_queues: MessageQueues,
298 deliverability_notifications: Mutex<HvDeliverabilityNotificationsRegister>,
299}
300
301struct MshvVpInnerCleaner<'a> {
302 vpinner: &'a MshvVpInner,
303}
304
305impl Drop for MshvVpInnerCleaner<'_> {
306 fn drop(&mut self) {
307 self.vpinner.thread.write().take();
308 }
309}
310
311impl virt::Partition for MshvPartition {
312 fn supports_reset(&self) -> Option<&dyn virt::ResetPartition<Error = Error>> {
313 None
314 }
315
316 fn doorbell_registration(
317 self: &Arc<Self>,
318 _minimum_vtl: Vtl,
319 ) -> Option<Arc<dyn DoorbellRegistration>> {
320 Some(self.clone())
323 }
324
325 fn caps(&self) -> &virt::PartitionCapabilities {
326 &self.inner.caps
327 }
328
329 fn request_msi(&self, _vtl: Vtl, request: MsiRequest) {
330 self.inner.request_msi(request)
331 }
332
333 fn as_signal_msi(self: &Arc<Self>, _vtl: Vtl) -> Option<Arc<dyn SignalMsi>> {
334 Some(self.inner.clone())
335 }
336
337 fn request_yield(&self, vp_index: VpIndex) {
338 let vp = self.inner.vp(vp_index);
339 if vp.needs_yield.request_yield() {
340 let thread = vp.thread.read();
342 if let Some(thread) = *thread {
343 if thread != Pthread::current() {
344 thread
345 .signal(libc::SIGRTMIN())
346 .expect("thread cancel signal failed");
347 }
348 }
349 }
350 }
351}
352
353impl virt::X86Partition for MshvPartition {
354 fn ioapic_routing(&self) -> Arc<dyn virt::irqcon::IoApicRouting> {
355 self.inner.clone()
356 }
357
358 fn pulse_lint(&self, vp_index: VpIndex, vtl: Vtl, lint: u8) {
359 tracing::warn!(?vp_index, ?vtl, lint, "ignored lint pulse");
361 }
362}
363
364impl PartitionAccessState for MshvPartition {
365 type StateAccess<'a> = &'a MshvPartition;
366
367 fn access_state(&self, vtl: Vtl) -> Self::StateAccess<'_> {
368 assert_eq!(vtl, Vtl::Vtl0);
369
370 self
371 }
372}
373
374impl Hv1 for MshvPartition {
375 type Error = Error;
376 type Device = virt::UnimplementedDevice;
377
378 fn reference_time_source(&self) -> Option<ReferenceTimeSource> {
379 Some(ReferenceTimeSource::from(self.inner.clone() as Arc<_>))
380 }
381
382 fn new_virtual_device(
383 &self,
384 ) -> Option<&dyn virt::DeviceBuilder<Device = Self::Device, Error = Self::Error>> {
385 None
386 }
387}
388
389impl GetReferenceTime for MshvPartitionInner {
390 fn now(&self) -> ReferenceTimeResult {
391 let mut regs = [hv_register_assoc {
392 name: hvdef::HvAllArchRegisterName::TimeRefCount.0,
393 value: hv_register_value { reg64: 0 },
394 ..Default::default()
395 }];
396 self.vp(VpIndex::BSP).vcpufd.get_reg(&mut regs).unwrap();
397 let ref_time = unsafe { regs[0].value.reg64 };
399 ReferenceTimeResult {
400 ref_time,
401 system_time: None,
402 }
403 }
404}
405
406impl MshvPartitionInner {
407 fn vp(&self, vp_index: VpIndex) -> &MshvVpInner {
408 &self.vps[vp_index.index() as usize]
409 }
410
411 fn post_message(&self, vp_index: VpIndex, sint: u8, message: &HvMessage) {
412 let request_notification = self
413 .vp(vp_index)
414 .message_queues
415 .enqueue_message(sint, message);
416
417 if request_notification {
418 self.request_sint_notifications(vp_index, 1 << sint);
419 }
420 }
421
422 fn request_sint_notifications(&self, vp_index: VpIndex, sints: u16) {
423 let mut notifications = self.vp(vp_index).deliverability_notifications.lock();
424 if notifications.sints() != sints {
425 notifications.set_sints(sints);
426 self.vmfd
427 .register_deliverabilty_notifications(vp_index.index(), (*notifications).into())
428 .expect("Requesting deliverability is not a fallable operation");
429 }
430 }
431}
432
433pub struct MshvProcessorBinder {
434 partition: Arc<MshvPartitionInner>,
435 vpindex: VpIndex,
436}
437
438impl virt::BindProcessor for MshvProcessorBinder {
439 type Processor<'a>
440 = MshvProcessor<'a>
441 where
442 Self: 'a;
443 type Error = Error;
444
445 fn bind(&mut self) -> Result<Self::Processor<'_>, Self::Error> {
446 Ok(MshvProcessor {
447 partition: &self.partition,
448 inner: &self.partition.vps[self.vpindex.index() as usize],
449 vpindex: self.vpindex,
450 })
451 }
452}
453
454pub struct MshvProcessor<'a> {
455 partition: &'a MshvPartitionInner,
456 inner: &'a MshvVpInner,
457 vpindex: VpIndex,
458}
459
460impl MshvProcessor<'_> {
461 async fn emulate(
462 &self,
463 message: &hv_message,
464 devices: &impl CpuIo,
465 interruption_pending: bool,
466 ) -> Result<(), VpHaltReason> {
467 let cache = self.emulation_cache();
468 let emu_mem = virt_support_x86emu::emulate::EmulatorMemoryAccess {
469 gm: &self.partition.gm,
470 kx_gm: &self.partition.gm,
471 ux_gm: &self.partition.gm,
472 };
473
474 let mut support = MshvEmulationState {
475 partition: self.partition,
476 processor: self.inner,
477 vp_index: self.vpindex,
478 message,
479 interruption_pending,
480 cache,
481 };
482 virt_support_x86emu::emulate::emulate(&mut support, &emu_mem, devices).await
483 }
484
485 async fn handle_io_port_intercept(
486 &self,
487 message: &hv_message,
488 devices: &impl CpuIo,
489 ) -> Result<(), VpHaltReason> {
490 let info = message.to_ioport_info().unwrap();
491 let access_info = info.access_info;
492 let port_access_info = unsafe { access_info.__bindgen_anon_1 };
494
495 if port_access_info.string_op() != 0 || port_access_info.rep_prefix() != 0 {
496 let execution_state = info.header.execution_state;
497 let io_execution_state = unsafe { execution_state.__bindgen_anon_1 };
499 let interruption_pending = io_execution_state.interruption_pending() != 0;
500
501 self.emulate(message, devices, interruption_pending).await?
502 } else {
503 let mut ret_rax = info.rax;
504 virt_support_x86emu::emulate::emulate_io(
505 self.vpindex,
506 info.header.intercept_access_type == 1,
507 info.port_number,
508 &mut ret_rax,
509 port_access_info.access_size(),
510 devices,
511 )
512 .await;
513
514 let insn_len = info.header.instruction_length() as u64;
515
516 let arr_reg_name_value = [
518 (
519 mshv_bindings::hv_register_name_HV_X64_REGISTER_RIP,
520 info.header.rip + insn_len,
521 ),
522 (mshv_bindings::hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
523 ];
524
525 set_registers_64!(self.inner.vcpufd, arr_reg_name_value).unwrap();
526 }
527
528 Ok(())
529 }
530
531 async fn handle_mmio_intercept(
532 &self,
533 message: &hv_message,
534 devices: &impl CpuIo,
535 ) -> Result<(), VpHaltReason> {
536 let execution_state = message.to_memory_info().unwrap().header.execution_state;
537 let mmio_execution_state = unsafe { execution_state.__bindgen_anon_1 };
539 let interruption_pending = mmio_execution_state.interruption_pending() != 0;
540
541 self.emulate(message, devices, interruption_pending).await
542 }
543
544 fn handle_synic_deliverable_exit(&self, message: &hv_message, _devices: &impl CpuIo) {
545 let info = message.to_sint_deliverable_info().unwrap();
546 self.flush_messages(info.deliverable_sints);
547 }
548
549 fn handle_hypercall_intercept(&self, message: &hv_message, devices: &impl CpuIo) {
550 let info = message.to_hypercall_intercept_info().unwrap();
551 let execution_state = info.header.execution_state;
552 let vp_state = unsafe { HvX64VpExecutionState::from(execution_state.as_uint16) };
554 let is_64bit = vp_state.cr0_pe() && vp_state.efer_lma();
555 let mut hpc_context = MshvHypercallContext {
556 rax: info.rax,
557 rbx: info.rbx,
558 rcx: info.rcx,
559 rdx: info.rdx,
560 r8: info.r8,
561 rsi: info.rsi,
562 rdi: info.rdi,
563 xmm: info.xmmregisters,
564 };
565 let mut handler = MshvHypercallHandler {
566 bus: devices,
567 context: &mut hpc_context,
568 rip: info.header.rip,
569 rip_dirty: false,
570 xmm_dirty: false,
571 gp_dirty: false,
572 };
573
574 MshvHypercallHandler::DISPATCHER.dispatch(
575 &self.partition.gm,
576 X64RegisterIo::new(&mut handler, is_64bit),
577 );
578
579 let mut dirty_regs = ArrayVec::<hv_register_assoc, 14>::new();
580
581 if handler.gp_dirty {
582 dirty_regs.extend([
583 hv_register_assoc {
584 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_RAX,
585 value: hv_register_value {
586 reg64: handler.context.rax,
587 },
588 ..Default::default()
589 },
590 hv_register_assoc {
591 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_RBX,
592 value: hv_register_value {
593 reg64: handler.context.rbx,
594 },
595 ..Default::default()
596 },
597 hv_register_assoc {
598 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_RCX,
599 value: hv_register_value {
600 reg64: handler.context.rcx,
601 },
602 ..Default::default()
603 },
604 hv_register_assoc {
605 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_RDX,
606 value: hv_register_value {
607 reg64: handler.context.rdx,
608 },
609 ..Default::default()
610 },
611 hv_register_assoc {
612 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_R8,
613 value: hv_register_value {
614 reg64: handler.context.r8,
615 },
616 ..Default::default()
617 },
618 hv_register_assoc {
619 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_RSI,
620 value: hv_register_value {
621 reg64: handler.context.rsi,
622 },
623 ..Default::default()
624 },
625 hv_register_assoc {
626 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_RDI,
627 value: hv_register_value {
628 reg64: handler.context.rdi,
629 },
630 ..Default::default()
631 },
632 ]);
633 }
634
635 if handler.xmm_dirty {
636 dirty_regs.extend((0..5).map(|i| hv_register_assoc {
637 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_XMM0 + i,
638 value: hv_register_value {
639 reg128: handler.context.xmm[i as usize],
640 },
641 ..Default::default()
642 }));
643 }
644
645 if handler.rip_dirty {
646 dirty_regs.push(hv_register_assoc {
647 name: mshv_bindings::hv_register_name_HV_X64_REGISTER_RIP,
648 value: hv_register_value { reg64: handler.rip },
649 ..Default::default()
650 });
651 }
652
653 if !dirty_regs.is_empty() {
654 self.inner
655 .vcpufd
656 .set_reg(&dirty_regs)
657 .expect("RIP setting is not a fallable operation");
658 }
659 }
660
661 fn flush_messages(&self, deliverable_sints: u16) {
662 let nonempty_sints =
663 self.inner
664 .message_queues
665 .post_pending_messages(deliverable_sints, |sint, message| {
666 match self.partition.vmfd.post_message_direct(
667 self.vpindex.index(),
668 sint,
669 message.as_bytes(),
670 ) {
671 Ok(()) => {
672 tracing::trace!(sint, "sint message posted successfully");
673 Ok(())
674 }
675 Err(e) => {
676 tracing::trace!(error = %e, "dropping sint message");
678 Err(HvError::ObjectInUse)
679 }
680 }
681 });
682
683 {
684 let mut notifications = self.inner.deliverability_notifications.lock();
686 let remaining_sints = notifications.sints() & !deliverable_sints;
687 notifications.set_sints(remaining_sints);
688 }
689
690 if nonempty_sints != 0 {
691 self.partition
692 .request_sint_notifications(self.vpindex, nonempty_sints);
693 }
694 }
695
696 fn emulation_cache(&self) -> MshvEmuCache {
697 let regs = self.inner.vcpufd.get_regs().unwrap();
698 let gps = [
699 regs.rax, regs.rcx, regs.rdx, regs.rbx, regs.rsp, regs.rbp, regs.rsi, regs.rdi,
700 regs.r8, regs.r9, regs.r10, regs.r11, regs.r12, regs.r13, regs.r14, regs.r15,
701 ];
702 let rip = regs.rip;
703 let rflags = regs.rflags;
704
705 let sregs = self.inner.vcpufd.get_sregs().unwrap();
706 let segs = [
707 x86emu_sreg_from_mshv_sreg(sregs.es),
708 x86emu_sreg_from_mshv_sreg(sregs.cs),
709 x86emu_sreg_from_mshv_sreg(sregs.ss),
710 x86emu_sreg_from_mshv_sreg(sregs.ds),
711 x86emu_sreg_from_mshv_sreg(sregs.fs),
712 x86emu_sreg_from_mshv_sreg(sregs.gs),
713 ];
714 let cr0 = sregs.cr0;
715 let efer = sregs.efer;
716
717 MshvEmuCache {
718 gps,
719 segs,
720 rip,
721 rflags: rflags.into(),
722 cr0,
723 efer,
724 }
725 }
726}
727
728struct MshvEmulationState<'a> {
729 partition: &'a MshvPartitionInner,
730 processor: &'a MshvVpInner,
731 vp_index: VpIndex,
732 message: &'a hv_message,
733 interruption_pending: bool,
734 cache: MshvEmuCache,
735}
736
737impl EmulatorSupport for MshvEmulationState<'_> {
738 fn vp_index(&self) -> VpIndex {
739 self.vp_index
740 }
741
742 fn vendor(&self) -> x86defs::cpuid::Vendor {
743 self.partition.caps.vendor
744 }
745
746 fn gp(&mut self, reg: x86emu::Gp) -> u64 {
747 self.cache.gps[reg as usize]
748 }
749
750 fn set_gp(&mut self, reg: x86emu::Gp, v: u64) {
751 self.cache.gps[reg as usize] = v;
752 }
753
754 fn rip(&mut self) -> u64 {
755 self.cache.rip
756 }
757
758 fn set_rip(&mut self, v: u64) {
759 self.cache.rip = v;
760 }
761
762 fn segment(&mut self, reg: x86emu::Segment) -> SegmentRegister {
763 self.cache.segs[reg as usize]
764 }
765
766 fn efer(&mut self) -> u64 {
767 self.cache.efer
768 }
769
770 fn cr0(&mut self) -> u64 {
771 self.cache.cr0
772 }
773
774 fn rflags(&mut self) -> RFlags {
775 self.cache.rflags
776 }
777
778 fn set_rflags(&mut self, v: RFlags) {
779 self.cache.rflags = v;
780 }
781
782 fn xmm(&mut self, reg: usize) -> u128 {
783 assert!(reg < 16);
784 let name = HvX64RegisterName(HvX64RegisterName::Xmm0.0 + reg as u32);
785 let reg = unsafe {
787 std::mem::transmute::<HvRegisterAssoc, hv_register_assoc>(HvRegisterAssoc::from((
788 name, 0u128,
789 )))
790 };
791 let _ = self.processor.vcpufd.get_reg(&mut [reg]);
792 hvu128_to_u128(unsafe { ®.value.reg128 })
794 }
795
796 fn set_xmm(&mut self, reg: usize, value: u128) {
797 assert!(reg < 16);
798 let name = HvX64RegisterName(HvX64RegisterName::Xmm0.0 + reg as u32);
799 let reg = unsafe {
801 std::mem::transmute::<HvRegisterAssoc, hv_register_assoc>(HvRegisterAssoc::from((
802 name, value,
803 )))
804 };
805 self.processor.vcpufd.set_reg(&[reg]).unwrap();
806 }
807
808 fn flush(&mut self) {
809 let arr_reg_name_value = [
810 (
811 mshv_bindings::hv_register_name_HV_X64_REGISTER_RIP,
812 self.cache.rip,
813 ),
814 (
815 mshv_bindings::hv_register_name_HV_X64_REGISTER_RFLAGS,
816 self.cache.rflags.into(),
817 ),
818 (
819 mshv_bindings::hv_register_name_HV_X64_REGISTER_RAX,
820 self.cache.gps[0],
821 ),
822 (
823 mshv_bindings::hv_register_name_HV_X64_REGISTER_RCX,
824 self.cache.gps[1],
825 ),
826 (
827 mshv_bindings::hv_register_name_HV_X64_REGISTER_RDX,
828 self.cache.gps[2],
829 ),
830 (
831 mshv_bindings::hv_register_name_HV_X64_REGISTER_RBX,
832 self.cache.gps[3],
833 ),
834 (
835 mshv_bindings::hv_register_name_HV_X64_REGISTER_RSP,
836 self.cache.gps[4],
837 ),
838 (
839 mshv_bindings::hv_register_name_HV_X64_REGISTER_RBP,
840 self.cache.gps[5],
841 ),
842 (
843 mshv_bindings::hv_register_name_HV_X64_REGISTER_RSI,
844 self.cache.gps[6],
845 ),
846 (
847 mshv_bindings::hv_register_name_HV_X64_REGISTER_RDI,
848 self.cache.gps[7],
849 ),
850 (
851 mshv_bindings::hv_register_name_HV_X64_REGISTER_R8,
852 self.cache.gps[8],
853 ),
854 (
855 mshv_bindings::hv_register_name_HV_X64_REGISTER_R9,
856 self.cache.gps[9],
857 ),
858 (
859 mshv_bindings::hv_register_name_HV_X64_REGISTER_R10,
860 self.cache.gps[10],
861 ),
862 (
863 mshv_bindings::hv_register_name_HV_X64_REGISTER_R11,
864 self.cache.gps[11],
865 ),
866 (
867 mshv_bindings::hv_register_name_HV_X64_REGISTER_R12,
868 self.cache.gps[12],
869 ),
870 (
871 mshv_bindings::hv_register_name_HV_X64_REGISTER_R13,
872 self.cache.gps[13],
873 ),
874 (
875 mshv_bindings::hv_register_name_HV_X64_REGISTER_R14,
876 self.cache.gps[14],
877 ),
878 (
879 mshv_bindings::hv_register_name_HV_X64_REGISTER_R15,
880 self.cache.gps[15],
881 ),
882 ];
883
884 set_registers_64!(self.processor.vcpufd, arr_reg_name_value).unwrap();
885 }
886
887 fn instruction_bytes(&self) -> &[u8] {
888 match HvMessageType(self.message.header.message_type) {
889 HvMessageType::HvMessageTypeGpaIntercept
890 | HvMessageType::HvMessageTypeUnmappedGpa
891 | HvMessageType::HvMessageTypeUnacceptedGpa => {
892 unsafe {
894 let info = (&raw const self.message.u.payload)
895 .cast::<hv_x64_memory_intercept_message>();
896 let instruction_bytes = &raw const (*info).instruction_bytes;
897 let instruction_byte_count =
898 std::ptr::read_unaligned(&raw const (*info).instruction_byte_count);
899 std::slice::from_raw_parts(
900 instruction_bytes.cast(),
901 instruction_byte_count as usize,
902 )
903 }
904 }
905 HvMessageType::HvMessageTypeX64IoPortIntercept => {
906 unsafe {
908 let info = (&raw const self.message.u.payload)
909 .cast::<hv_x64_io_port_intercept_message>();
910 let instruction_bytes = &raw const (*info).instruction_bytes;
911 let instruction_byte_count =
912 std::ptr::read_unaligned(&raw const (*info).instruction_byte_count);
913 std::slice::from_raw_parts(
914 instruction_bytes.cast(),
915 instruction_byte_count as usize,
916 )
917 }
918 }
919 _ => unreachable!(),
920 }
921 }
922
923 fn physical_address(&self) -> Option<u64> {
924 if self.message.header.message_type == HvMessageType::HvMessageTypeGpaIntercept.0
925 || self.message.header.message_type == HvMessageType::HvMessageTypeUnmappedGpa.0
926 || self.message.header.message_type == HvMessageType::HvMessageTypeUnacceptedGpa.0
927 {
928 let info = self.message.to_memory_info().unwrap();
929 Some(info.guest_physical_address)
930 } else {
931 None
932 }
933 }
934
935 fn initial_gva_translation(
936 &mut self,
937 ) -> Option<virt_support_x86emu::emulate::InitialTranslation> {
938 if (self.message.header.message_type != HvMessageType::HvMessageTypeGpaIntercept.0)
939 && (self.message.header.message_type != HvMessageType::HvMessageTypeUnmappedGpa.0)
940 && (self.message.header.message_type != HvMessageType::HvMessageTypeUnacceptedGpa.0)
941 {
942 return None;
943 }
944
945 let message = self.message.to_memory_info().unwrap();
946
947 let memory_access_info =
950 unsafe { hvdef::HvX64MemoryAccessInfo::from(message.memory_access_info.as_uint8) };
951
952 if !memory_access_info.gva_gpa_valid() {
953 return None;
954 }
955
956 if let Ok(translate_mode) = TranslateMode::try_from(hvdef::HvInterceptAccessType(
957 message.header.intercept_access_type,
958 )) {
959 Some(virt_support_x86emu::emulate::InitialTranslation {
960 gva: message.guest_virtual_address,
961 gpa: message.guest_physical_address,
962 translate_mode,
963 })
964 } else {
965 None
966 }
967 }
968
969 fn interruption_pending(&self) -> bool {
970 self.interruption_pending
971 }
972
973 fn check_vtl_access(
974 &mut self,
975 _gpa: u64,
976 _mode: TranslateMode,
977 ) -> Result<(), virt_support_x86emu::emulate::EmuCheckVtlAccessError> {
978 Ok(())
980 }
981
982 fn translate_gva(
983 &mut self,
984 gva: u64,
985 mode: TranslateMode,
986 ) -> Result<EmuTranslateResult, EmuTranslateError> {
987 emulate_translate_gva(self, gva, mode)
988 }
989
990 fn inject_pending_event(&mut self, event_info: hvdef::HvX64PendingEvent) {
991 let reg = unsafe {
993 &[
994 std::mem::transmute::<HvRegisterAssoc, hv_register_assoc>(HvRegisterAssoc::from((
995 HvX64RegisterName::PendingEvent0,
996 u128::from(event_info.reg_0),
997 ))),
998 std::mem::transmute::<HvRegisterAssoc, hv_register_assoc>(HvRegisterAssoc::from((
999 HvX64RegisterName::PendingEvent1,
1000 u128::from(event_info.reg_1),
1001 ))),
1002 ]
1003 };
1004 self.processor.vcpufd.set_reg(reg).unwrap();
1005 }
1006
1007 fn is_gpa_mapped(&self, gpa: u64, write: bool) -> bool {
1008 self.partition
1009 .memory
1010 .lock()
1011 .ranges
1012 .iter()
1013 .flatten()
1014 .any(|range| {
1015 (range.guest_pfn..range.guest_pfn + range.size).contains(&gpa)
1016 && (!write
1017 || range.flags & set_bits!(u8, MSHV_SET_MEM_BIT_WRITABLE)
1018 == set_bits!(u8, MSHV_SET_MEM_BIT_WRITABLE))
1019 })
1020 }
1021
1022 fn lapic_base_address(&self) -> Option<u64> {
1023 None
1024 }
1025
1026 fn lapic_read(&mut self, _address: u64, _data: &mut [u8]) {
1027 unreachable!()
1028 }
1029
1030 fn lapic_write(&mut self, _address: u64, _data: &[u8]) {
1031 unreachable!()
1032 }
1033}
1034
1035impl TranslateGvaSupport for MshvEmulationState<'_> {
1036 fn guest_memory(&self) -> &GuestMemory {
1037 &self.partition.gm
1038 }
1039
1040 fn acquire_tlb_lock(&mut self) {
1041 }
1043
1044 fn registers(&mut self) -> TranslationRegisters {
1045 let mut reg = [
1046 HvX64RegisterName::Cr0,
1047 HvX64RegisterName::Cr4,
1048 HvX64RegisterName::Efer,
1049 HvX64RegisterName::Cr3,
1050 HvX64RegisterName::Rflags,
1051 HvX64RegisterName::Ss,
1052 ]
1053 .map(|n| HvRegisterAssoc::from((n, 0u64)));
1054
1055 unsafe {
1057 self.processor
1058 .vcpufd
1059 .get_reg(std::mem::transmute::<
1060 &mut [HvRegisterAssoc],
1061 &mut [hv_register_assoc],
1062 >(&mut reg[..]))
1063 .unwrap();
1064 }
1065
1066 let [cr0, cr4, efer, cr3, rflags, ss] = reg.map(|v| v.value);
1067
1068 TranslationRegisters {
1069 cr0: cr0.as_u64(),
1070 cr4: cr4.as_u64(),
1071 efer: efer.as_u64(),
1072 cr3: cr3.as_u64(),
1073 rflags: rflags.as_u64(),
1074 ss: from_seg(ss.as_segment()),
1075 encryption_mode: virt_support_x86emu::translate::EncryptionMode::None,
1076 }
1077 }
1078}
1079
1080#[derive(Error, Debug)]
1082pub enum Error {
1083 #[error("operation not supported")]
1084 NotSupported,
1085 #[error("create_vm failed")]
1086 CreateVMFailed,
1087 #[error("failed to initialize VM")]
1088 CreateVMInitFailed(#[source] anyhow::Error),
1089 #[error("failed to create VCPU")]
1090 CreateVcpu(#[source] MshvError),
1091 #[error("vtl2 not supported")]
1092 Vtl2NotSupported,
1093 #[error("isolation not supported")]
1094 IsolationNotSupported,
1095 #[error("failed to stat /dev/mshv")]
1096 AvailableCheck(#[source] io::Error),
1097 #[error("failed to open /dev/mshv")]
1098 OpenMshv(#[source] MshvError),
1099 #[error("register access error")]
1100 Register(#[source] MshvError),
1101 #[error("install instercept failed")]
1102 InstallIntercept(#[source] MshvError),
1103 #[error("host does not support required cpu capabilities")]
1104 Capabilities(virt::PartitionCapabilitiesError),
1105}
1106
1107impl MshvPartitionInner {
1108 fn request_msi(&self, request: MsiRequest) {
1109 let (address, data) = request.as_x86();
1110 let control = request.hv_x86_interrupt_control();
1111 let mshv_req = InterruptRequest {
1112 interrupt_type: control.interrupt_type().0,
1113 apic_id: address.virt_destination().into(),
1114 vector: data.vector().into(),
1115 level_triggered: control.x86_level_triggered(),
1116 logical_destination_mode: control.x86_logical_destination_mode(),
1117 long_mode: false,
1118 };
1119
1120 if let Err(err) = self.vmfd.request_virtual_interrupt(&mshv_req) {
1121 tracelimit::warn_ratelimited!(
1122 address = request.address,
1123 data = request.data,
1124 error = &err as &dyn std::error::Error,
1125 "failed to request msi"
1126 );
1127 }
1128 }
1129}
1130
1131impl SignalMsi for MshvPartitionInner {
1132 fn signal_msi(&self, _rid: u32, address: u64, data: u32) {
1133 self.request_msi(MsiRequest { address, data });
1134 }
1135}
1136
1137impl virt::irqcon::IoApicRouting for MshvPartitionInner {
1138 fn set_irq_route(&self, irq: u8, request: Option<MsiRequest>) {
1139 self.irq_routes.set_irq_route(irq, request)
1140 }
1141
1142 fn assert_irq(&self, irq: u8) {
1143 self.irq_routes
1144 .assert_irq(irq, |request| self.request_msi(request))
1145 }
1146}
1147
1148#[derive(Debug, Default)]
1149struct MshvMemoryRangeState {
1150 ranges: Vec<Option<mshv_user_mem_region>>,
1151}
1152
1153impl virt::PartitionMemoryMapper for MshvPartition {
1154 fn memory_mapper(&self, vtl: Vtl) -> Arc<dyn virt::PartitionMemoryMap> {
1155 assert_eq!(vtl, Vtl::Vtl0);
1156 self.inner.clone()
1157 }
1158}
1159
1160impl virt::PartitionMemoryMap for MshvPartitionInner {
1162 unsafe fn map_range(
1163 &self,
1164 data: *mut u8,
1165 size: usize,
1166 addr: u64,
1167 writable: bool,
1168 exec: bool,
1169 ) -> anyhow::Result<()> {
1170 let mut state = self.memory.lock();
1171
1172 let mut slot_to_use = None;
1175 for (slot, range) in state.ranges.iter_mut().enumerate() {
1176 match range {
1177 Some(range) if range.userspace_addr == data as u64 => {
1178 slot_to_use = Some(slot);
1179 break;
1180 }
1181 Some(_) => (),
1182 None => slot_to_use = Some(slot),
1183 }
1184 }
1185 if slot_to_use.is_none() {
1186 slot_to_use = Some(state.ranges.len());
1187 state.ranges.push(None);
1188 }
1189 let slot_to_use = slot_to_use.unwrap();
1190
1191 let mut flags = 0;
1192 if writable {
1193 flags |= set_bits!(u8, MSHV_SET_MEM_BIT_WRITABLE);
1194 }
1195 if exec {
1196 flags |= set_bits!(u8, MSHV_SET_MEM_BIT_EXECUTABLE);
1197 }
1198 let mem_region = mshv_user_mem_region {
1199 size: size as u64,
1200 guest_pfn: addr >> HV_PAGE_SHIFT,
1201 userspace_addr: data as u64,
1202 flags,
1203 rsvd: [0; 7],
1204 };
1205
1206 self.vmfd.map_user_memory(mem_region)?;
1207 state.ranges[slot_to_use] = Some(mem_region);
1208 Ok(())
1209 }
1210
1211 fn unmap_range(&self, addr: u64, size: u64) -> anyhow::Result<()> {
1212 let mut state = self.memory.lock();
1213 let (slot, range) = state
1214 .ranges
1215 .iter_mut()
1216 .enumerate()
1217 .find(|(_, range)| {
1218 range.as_ref().map(|r| (r.guest_pfn, r.size)) == Some((addr >> HV_PAGE_SHIFT, size))
1219 })
1220 .expect("can only unmap existing ranges of exact size");
1221
1222 self.vmfd.unmap_user_memory(range.unwrap())?;
1223 state.ranges[slot] = None;
1224 Ok(())
1225 }
1226}
1227
1228struct MshvDoorbellEntry;
1230
1231impl MshvDoorbellEntry {
1232 pub fn new(
1233 _guest_address: u64,
1234 _value: Option<u64>,
1235 _length: Option<u32>,
1236 _fd: &Event,
1237 ) -> io::Result<MshvDoorbellEntry> {
1238 Ok(Self)
1241 }
1242}
1243
1244impl DoorbellRegistration for MshvPartition {
1245 fn register_doorbell(
1246 &self,
1247 guest_address: u64,
1248 value: Option<u64>,
1249 length: Option<u32>,
1250 fd: &Event,
1251 ) -> io::Result<Box<dyn Send + Sync>> {
1252 Ok(Box::new(MshvDoorbellEntry::new(
1253 guest_address,
1254 value,
1255 length,
1256 fd,
1257 )?))
1258 }
1259}
1260
1261pub struct MshvHypercallContext {
1262 pub rax: u64,
1263 pub rbx: u64,
1264 pub rcx: u64,
1265 pub rdx: u64,
1266 pub r8: u64,
1267 pub rsi: u64,
1268 pub rdi: u64,
1269 pub xmm: [hv_u128; 6],
1270}
1271
1272impl<T> hv1_hypercall::X64RegisterState for MshvHypercallHandler<'_, T> {
1273 fn rip(&mut self) -> u64 {
1274 self.rip
1275 }
1276
1277 fn set_rip(&mut self, rip: u64) {
1278 self.rip = rip;
1279 self.rip_dirty = true;
1280 }
1281
1282 fn gp(&mut self, n: hv1_hypercall::X64HypercallRegister) -> u64 {
1283 match n {
1284 hv1_hypercall::X64HypercallRegister::Rax => self.context.rax,
1285 hv1_hypercall::X64HypercallRegister::Rcx => self.context.rcx,
1286 hv1_hypercall::X64HypercallRegister::Rdx => self.context.rdx,
1287 hv1_hypercall::X64HypercallRegister::Rbx => self.context.rbx,
1288 hv1_hypercall::X64HypercallRegister::Rsi => self.context.rsi,
1289 hv1_hypercall::X64HypercallRegister::Rdi => self.context.rdi,
1290 hv1_hypercall::X64HypercallRegister::R8 => self.context.r8,
1291 }
1292 }
1293
1294 fn set_gp(&mut self, n: hv1_hypercall::X64HypercallRegister, value: u64) {
1295 *match n {
1296 hv1_hypercall::X64HypercallRegister::Rax => &mut self.context.rax,
1297 hv1_hypercall::X64HypercallRegister::Rcx => &mut self.context.rcx,
1298 hv1_hypercall::X64HypercallRegister::Rdx => &mut self.context.rdx,
1299 hv1_hypercall::X64HypercallRegister::Rbx => &mut self.context.rbx,
1300 hv1_hypercall::X64HypercallRegister::Rsi => &mut self.context.rsi,
1301 hv1_hypercall::X64HypercallRegister::Rdi => &mut self.context.rdi,
1302 hv1_hypercall::X64HypercallRegister::R8 => &mut self.context.r8,
1303 } = value;
1304 self.gp_dirty = true;
1305 }
1306
1307 fn xmm(&mut self, n: usize) -> u128 {
1308 let r = &self.context.xmm[n];
1309 hvu128_to_u128(r)
1310 }
1311
1312 fn set_xmm(&mut self, n: usize, value: u128) {
1313 self.context.xmm[n] = u128_to_hvu128(value);
1314 self.xmm_dirty = true;
1315 }
1316}
1317
1318fn hvu128_to_u128(r: &hv_u128) -> u128 {
1319 (r.high_part as u128) << 64 | r.low_part as u128
1320}
1321
1322fn u128_to_hvu128(value: u128) -> hv_u128 {
1323 hv_u128 {
1324 high_part: (value >> 64) as u64,
1325 low_part: (value & (u64::MAX as u128)) as u64,
1326 }
1327}
1328
1329#[cfg(test)]
1330mod tests {
1331 use super::*;
1332
1333 #[test]
1334 fn u128_roundtrip() {
1335 let original = 0x0123_4567_89ab_cdef_fedc_ba98_7654_3210;
1336 let hv = u128_to_hvu128(original);
1337 let roundtrip = hvu128_to_u128(&hv);
1338 assert_eq!(roundtrip, original);
1339 }
1340}
1341
1342struct MshvHypercallHandler<'a, T> {
1343 bus: &'a T,
1344 context: &'a mut MshvHypercallContext,
1345 rip: u64,
1346 rip_dirty: bool,
1347 xmm_dirty: bool,
1348 gp_dirty: bool,
1349}
1350
1351impl<T: CpuIo> MshvHypercallHandler<'_, T> {
1352 const DISPATCHER: hv1_hypercall::Dispatcher<Self> = hv1_hypercall::dispatcher!(
1353 Self,
1354 [hv1_hypercall::HvPostMessage, hv1_hypercall::HvSignalEvent],
1355 );
1356}
1357
1358impl<T: CpuIo> hv1_hypercall::PostMessage for MshvHypercallHandler<'_, T> {
1359 fn post_message(&mut self, connection_id: u32, message: &[u8]) -> hvdef::HvResult<()> {
1360 self.bus
1361 .post_synic_message(Vtl::Vtl0, connection_id, false, message)
1362 }
1363}
1364
1365impl<T: CpuIo> hv1_hypercall::SignalEvent for MshvHypercallHandler<'_, T> {
1366 fn signal_event(&mut self, connection_id: u32, flag: u16) -> hvdef::HvResult<()> {
1367 self.bus.signal_synic_event(Vtl::Vtl0, connection_id, flag)
1368 }
1369}
1370
1371impl Inspect for MshvPartition {
1372 fn inspect(&self, req: inspect::Request<'_>) {
1373 req.respond();
1375 }
1376}
1377
1378impl InspectMut for MshvProcessor<'_> {
1379 fn inspect_mut(&mut self, req: inspect::Request<'_>) {
1380 req.respond();
1381 }
1382}
1383
1384impl virt::Processor for MshvProcessor<'_> {
1385 type StateAccess<'a>
1386 = &'a mut Self
1387 where
1388 Self: 'a;
1389
1390 fn set_debug_state(
1391 &mut self,
1392 _vtl: Vtl,
1393 _state: Option<&virt::x86::DebugState>,
1394 ) -> Result<(), <&mut Self as virt::vp::AccessVpState>::Error> {
1395 Err(Error::NotSupported)
1396 }
1397
1398 async fn run_vp(
1399 &mut self,
1400 stop: StopVp<'_>,
1401 dev: &impl CpuIo,
1402 ) -> Result<Infallible, VpHaltReason> {
1403 let vpinner = self.inner;
1404 let _cleaner = MshvVpInnerCleaner { vpinner };
1405 let vcpufd = &vpinner.vcpufd;
1406
1407 assert!(vpinner.thread.write().replace(Pthread::current()).is_none());
1410
1411 loop {
1412 vpinner.needs_yield.maybe_yield().await;
1413 stop.check()?;
1414
1415 match vcpufd.run() {
1416 Ok(exit) => match HvMessageType(exit.header.message_type) {
1417 HvMessageType::HvMessageTypeUnrecoverableException => {
1418 return Err(VpHaltReason::TripleFault { vtl: Vtl::Vtl0 });
1419 }
1420 HvMessageType::HvMessageTypeX64IoPortIntercept => {
1421 self.handle_io_port_intercept(&exit, dev).await?;
1422 }
1423 HvMessageType::HvMessageTypeUnmappedGpa
1424 | HvMessageType::HvMessageTypeGpaIntercept => {
1425 self.handle_mmio_intercept(&exit, dev).await?;
1426 }
1427 HvMessageType::HvMessageTypeSynicSintDeliverable => {
1428 tracing::trace!("SYNIC_SINT_DELIVERABLE");
1429 self.handle_synic_deliverable_exit(&exit, dev);
1430 }
1431 HvMessageType::HvMessageTypeHypercallIntercept => {
1432 tracing::trace!("HYPERCALL_INTERCEPT");
1433 self.handle_hypercall_intercept(&exit, dev);
1434 }
1435 exit => {
1436 panic!("Unhandled vcpu exit code {exit:?}");
1437 }
1438 },
1439
1440 Err(e) => match e.errno() {
1441 libc::EAGAIN | libc::EINTR => {}
1442 _ => tracing::error!(
1443 error = &e as &dyn std::error::Error,
1444 "vcpufd.run returned error"
1445 ),
1446 },
1447 }
1448 }
1449 }
1450
1451 fn flush_async_requests(&mut self) {}
1452
1453 fn access_state(&mut self, vtl: Vtl) -> Self::StateAccess<'_> {
1454 assert_eq!(vtl, Vtl::Vtl0);
1455 self
1456 }
1457}
1458
1459fn x86emu_sreg_from_mshv_sreg(reg: mshv_bindings::SegmentRegister) -> SegmentRegister {
1460 let reg: hv_x64_segment_register = hv_x64_segment_register::from(reg);
1461 let attributes: u16 = unsafe { reg.__bindgen_anon_1.attributes };
1463
1464 SegmentRegister {
1465 base: reg.base,
1466 limit: reg.limit,
1467 selector: reg.selector,
1468 attributes: attributes.into(),
1469 }
1470}
1471
1472fn from_seg(reg: hvdef::HvX64SegmentRegister) -> SegmentRegister {
1473 SegmentRegister {
1474 base: reg.base,
1475 limit: reg.limit,
1476 selector: reg.selector,
1477 attributes: reg.attributes.into(),
1478 }
1479}
1480
1481impl virt::Synic for MshvPartition {
1482 fn post_message(&self, _vtl: Vtl, vp: VpIndex, sint: u8, typ: u32, payload: &[u8]) {
1483 self.inner
1484 .post_message(vp, sint, &HvMessage::new(HvMessageType(typ), 0, payload));
1485 }
1486
1487 fn new_guest_event_port(
1488 &self,
1489 _vtl: Vtl,
1490 vp: u32,
1491 sint: u8,
1492 flag: u16,
1493 ) -> Box<dyn GuestEventPort> {
1494 Box::new(MshvGuestEventPort {
1495 partition: Arc::downgrade(&self.inner),
1496 params: Arc::new(Mutex::new(MshvEventPortParams {
1497 vp: VpIndex::new(vp),
1498 sint,
1499 flag,
1500 })),
1501 })
1502 }
1503
1504 fn prefer_os_events(&self) -> bool {
1505 false
1506 }
1507}
1508
1509#[derive(Debug, Clone)]
1511struct MshvGuestEventPort {
1512 partition: Weak<MshvPartitionInner>,
1513 params: Arc<Mutex<MshvEventPortParams>>,
1514}
1515
1516#[derive(Debug, Copy, Clone)]
1517struct MshvEventPortParams {
1518 vp: VpIndex,
1519 sint: u8,
1520 flag: u16,
1521}
1522
1523impl GuestEventPort for MshvGuestEventPort {
1524 fn interrupt(&self) -> Interrupt {
1525 let partition = self.partition.clone();
1526 let params = self.params.clone();
1527 Interrupt::from_fn(move || {
1528 let MshvEventPortParams { vp, sint, flag } = *params.lock();
1529 if let Some(partition) = partition.upgrade() {
1530 partition
1531 .vmfd
1532 .signal_event_direct(vp.index(), sint, flag)
1533 .unwrap_or_else(|_| {
1534 panic!(
1535 "Failed signal synic sint {} on vp {:?} with flag {}",
1536 sint, vp, flag
1537 )
1538 });
1539 }
1540 })
1541 }
1542
1543 fn set_target_vp(&mut self, vp: u32) -> Result<(), vmcore::synic::HypervisorError> {
1544 self.params.lock().vp = VpIndex::new(vp);
1545 Ok(())
1546 }
1547}