Skip to main content

virt_mshv_vtl/processor/mshv/
x64.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! X64 Processor support for Microsoft hypervisor-backed partitions.
5
6#![cfg(guest_arch = "x86_64")]
7
8type VpRegisterName = HvX64RegisterName;
9
10use super::super::BackingParams;
11use super::super::BackingPrivate;
12use super::super::UhEmulationState;
13use super::super::signal_mnf;
14use super::super::vp_state;
15use super::super::vp_state::UhVpStateAccess;
16use super::MshvRunVpError;
17use super::VbsIsolatedVtl1State;
18use crate::BackingShared;
19use crate::Error;
20use crate::GuestVsmState;
21use crate::GuestVtl;
22use crate::UhPartitionNewParams;
23use crate::processor::BackingSharedParams;
24use crate::processor::SidecarExitReason;
25use crate::processor::SidecarRemoveExit;
26use crate::processor::UhHypercallHandler;
27use crate::processor::UhProcessor;
28use crate::validate_vtl_gpa_flags;
29use hcl::ioctl;
30use hcl::ioctl::ApplyVtlProtectionsError;
31use hcl::ioctl::x64::MshvX64;
32use hcl::protocol;
33use hv1_emulator::hv::ProcessorVtlHv;
34use hv1_hypercall::HvRepResult;
35use hv1_structs::VtlSet;
36use hvdef::HV_PAGE_SIZE;
37use hvdef::HvDeliverabilityNotificationsRegister;
38use hvdef::HvError;
39use hvdef::HvInterceptAccessType;
40use hvdef::HvMapGpaFlags;
41use hvdef::HvMessageType;
42use hvdef::HvRegisterValue;
43use hvdef::HvRegisterVsmPartitionConfig;
44use hvdef::HvX64InterceptMessageHeader;
45use hvdef::HvX64PendingEvent;
46use hvdef::HvX64PendingInterruptionType;
47use hvdef::HvX64RegisterName;
48use hvdef::Vtl;
49use hvdef::hypercall;
50use inspect::Inspect;
51use inspect::InspectMut;
52use inspect_counters::Counter;
53use parking_lot::RwLock;
54use std::sync::atomic::Ordering::Relaxed;
55use virt::EmulatorMonitorSupport;
56use virt::StopVp;
57use virt::VpHaltReason;
58use virt::VpIndex;
59use virt::io::CpuIo;
60use virt::state::HvRegisterState;
61use virt::state::StateElement;
62use virt::vp;
63use virt::vp::AccessVpState;
64use virt::x86::MsrError;
65use virt_support_x86emu::emulate::EmuCheckVtlAccessError;
66use virt_support_x86emu::emulate::EmuTranslateError;
67use virt_support_x86emu::emulate::EmuTranslateResult;
68use virt_support_x86emu::emulate::EmulatorSupport;
69use x86defs::RFlags;
70use x86defs::SegmentRegister;
71use x86defs::xsave::Fxsave;
72use x86defs::xsave::XFEATURE_SSE;
73use x86defs::xsave::XFEATURE_X87;
74use x86defs::xsave::XsaveHeader;
75use zerocopy::FromZeros;
76use zerocopy::Immutable;
77use zerocopy::IntoBytes;
78use zerocopy::KnownLayout;
79
80/// A backing for hypervisor-backed partitions (non-isolated and
81/// software-isolated).
82#[derive(InspectMut)]
83pub struct HypervisorBackedX86 {
84    // VTL0 only, used for synic message and extint readiness notifications.
85    // We do not currently support synic message ports or extint interrupts for VTL1.
86    #[inspect(hex, with = "|&x| u64::from(x)")]
87    deliverability_notifications: HvDeliverabilityNotificationsRegister,
88    /// Next set of deliverability notifications. See register definition for details.
89    #[inspect(hex, with = "|&x| u64::from(x)")]
90    pub(super) next_deliverability_notifications: HvDeliverabilityNotificationsRegister,
91    stats: ProcessorStatsX86,
92    /// Fallback for hypervisors that don't support setting the startup suspend
93    /// state explicitly via the internal activity register: send an INIT to
94    /// VTL0 before running the VP to simulate setting startup suspend. Prefer
95    /// the synchronous register write where possible; see `init` and
96    /// `set_vtl0_startup_suspend`.
97    deferred_init: bool,
98}
99
100/// Partition-wide shared data for hypervisor backed VMs.
101#[derive(Inspect)]
102pub struct HypervisorBackedX86Shared {
103    pub(crate) guest_vsm: RwLock<GuestVsmState<VbsIsolatedVtl1State>>,
104}
105
106impl HypervisorBackedX86Shared {
107    /// Creates a new partition-shared data structure for hypervisor backed VMs.
108    pub(crate) fn new(
109        _partition_params: &UhPartitionNewParams<'_>,
110        params: BackingSharedParams<'_>,
111    ) -> Result<Self, Error> {
112        Ok(Self {
113            guest_vsm: RwLock::new(GuestVsmState::from_availability(params.guest_vsm_available)),
114        })
115    }
116}
117
118#[derive(Inspect, Default)]
119struct ProcessorStatsX86 {
120    io_port: Counter,
121    mmio: Counter,
122    unaccepted_gpa: Counter,
123    hypercall: Counter,
124    synic_deliverable: Counter,
125    interrupt_deliverable: Counter,
126    cpuid: Counter,
127    msr: Counter,
128    eoi: Counter,
129    unrecoverable_exception: Counter,
130    halt: Counter,
131    exception_intercept: Counter,
132}
133
134pub struct MshvEmulationCache {
135    rsp: u64,
136    es: SegmentRegister,
137    ds: SegmentRegister,
138    fs: SegmentRegister,
139    gs: SegmentRegister,
140    ss: SegmentRegister,
141    cr0: u64,
142    efer: u64,
143    rip: u64,
144    rflags: RFlags,
145}
146
147#[expect(private_interfaces)]
148impl BackingPrivate for HypervisorBackedX86 {
149    type HclBacking<'mshv> = MshvX64<'mshv>;
150    type Shared = HypervisorBackedX86Shared;
151    type EmulationCache = MshvEmulationCache;
152
153    fn shared(shared: &BackingShared) -> &Self::Shared {
154        let BackingShared::Hypervisor(shared) = shared else {
155            unreachable!()
156        };
157        shared
158    }
159
160    fn new(
161        params: BackingParams<'_, '_, Self>,
162        _shared: &HypervisorBackedX86Shared,
163    ) -> Result<Self, Error> {
164        // Initialize shared register state to architectural state. The kernel
165        // zero initializes this.
166        //
167        // When restoring, this will be overwritten, but it's not expensive
168        // enough to bother skipping.
169        let regs = vp::Registers::at_reset(&params.partition.caps, params.vp_info);
170        *params.runner.cpu_context_mut() = protocol::hcl_cpu_context_x64 {
171            gps_no_rsp: [
172                regs.rax, regs.rcx, regs.rdx, regs.rbx, 0, /* cr2 */
173                regs.rbp, regs.rsi, regs.rdi, regs.r8, regs.r9, regs.r10, regs.r11, regs.r12,
174                regs.r13, regs.r14, regs.r15,
175            ],
176            fx_state: vp::Xsave::at_reset(&params.partition.caps, params.vp_info).fxsave(),
177            reserved: [0; 384],
178        };
179
180        Ok(Self {
181            deliverability_notifications: Default::default(),
182            next_deliverability_notifications: Default::default(),
183            stats: Default::default(),
184            deferred_init: false,
185        })
186    }
187
188    fn init(this: &mut UhProcessor<'_, Self>) {
189        // The hypervisor initializes startup suspend to false. Application
190        // processors must instead come up in the wait-for-SIPI state, so set
191        // the architectural default of startup suspend for them.
192        //
193        // Prefer setting the startup suspend state synchronously by writing the
194        // internal activity register directly. The alternative--sending a
195        // deferred INIT (see `pre_run_vp`)--is asynchronous and races against
196        // the guest's own INIT-SIPI-SIPI sequence: if the hypervisor delivers
197        // the deferred INIT after the guest has already sent a SIPI, it resets
198        // the AP and discards the applied SIPI vector, stranding the VP in the
199        // wait-for-SIPI state. Fall back to the deferred INIT only on
200        // hypervisors that don't support setting the register directly.
201        if !this.vp_index().is_bsp() {
202            this.backing.deferred_init = match this.set_vtl0_startup_suspend(true) {
203                Ok(()) => false,
204                Err(err) => {
205                    tracelimit::warn_ratelimited!(
206                        error = &err as &dyn std::error::Error,
207                        vp = this.vp_index().index(),
208                        "unable to set internal activity register, falling back to deferred init"
209                    );
210                    true
211                }
212            };
213        }
214    }
215
216    type StateAccess<'p, 'a>
217        = UhVpStateAccess<'a, 'p, Self>
218    where
219        Self: 'a + 'p,
220        'p: 'a;
221
222    fn access_vp_state<'a, 'p>(
223        this: &'a mut UhProcessor<'p, Self>,
224        vtl: GuestVtl,
225    ) -> Self::StateAccess<'p, 'a> {
226        UhVpStateAccess::new(this, vtl)
227    }
228
229    fn pre_run_vp(this: &mut UhProcessor<'_, Self>) {
230        if std::mem::take(&mut this.backing.deferred_init) {
231            tracing::debug!(
232                vp = this.vp_index().index(),
233                "sending deferred INIT to set startup suspend"
234            );
235            this.partition.request_msi(
236                GuestVtl::Vtl0,
237                virt::irqcon::MsiRequest::new_x86(
238                    virt::irqcon::DeliveryMode::INIT,
239                    this.inner.vp_info.apic_id,
240                    false,
241                    0,
242                    true,
243                ),
244            );
245        }
246    }
247
248    async fn run_vp(
249        this: &mut UhProcessor<'_, Self>,
250        dev: &impl CpuIo,
251        stop: &mut StopVp<'_>,
252    ) -> Result<(), VpHaltReason> {
253        if this.backing.deliverability_notifications
254            != this.backing.next_deliverability_notifications
255        {
256            let notifications = this.backing.next_deliverability_notifications;
257            tracing::trace!(?notifications, "setting notifications");
258            this.runner
259                .set_vp_register(
260                    // TODO GUEST VSM
261                    GuestVtl::Vtl0,
262                    VpRegisterName::DeliverabilityNotifications,
263                    u64::from(notifications).into(),
264                )
265                .expect("requesting deliverability is not a fallable operation");
266            this.backing.deliverability_notifications =
267                this.backing.next_deliverability_notifications;
268        }
269
270        let intercepted = if this.runner.is_sidecar() {
271            let mut run = this
272                .runner
273                .run_sidecar()
274                .map_err(|e| dev.fatal_error(e.into()))?;
275            match stop.until_stop(run.wait()).await {
276                Ok(r) => r,
277                Err(stop) => {
278                    run.cancel();
279                    let r = run.wait().await;
280                    if matches!(r, Ok(false)) {
281                        // No intercept, so stop the VP.
282                        return Err(stop.into());
283                    }
284                    r
285                }
286            }
287            .map_err(|e| dev.fatal_error(ioctl::Error::Sidecar(e).into()))?
288        } else {
289            this.unlock_tlb_lock(Vtl::Vtl2);
290            this.runner
291                .run()
292                .map_err(|e| dev.fatal_error(MshvRunVpError(e).into()))?
293        };
294
295        if intercepted {
296            let message_type = this.runner.exit_message().header.typ;
297
298            let mut intercept_handler =
299                InterceptHandler::new(this).map_err(|e| dev.fatal_error(e.into()))?;
300
301            let stat = match message_type {
302                HvMessageType::HvMessageTypeX64IoPortIntercept => {
303                    intercept_handler.handle_io_port_exit(dev).await?;
304                    &mut this.backing.stats.io_port
305                }
306                HvMessageType::HvMessageTypeUnmappedGpa
307                | HvMessageType::HvMessageTypeGpaIntercept => {
308                    intercept_handler.handle_mmio_exit(dev).await?;
309                    &mut this.backing.stats.mmio
310                }
311                HvMessageType::HvMessageTypeUnacceptedGpa => {
312                    intercept_handler
313                        .handle_unaccepted_gpa_intercept(dev)
314                        .await?;
315                    &mut this.backing.stats.unaccepted_gpa
316                }
317                HvMessageType::HvMessageTypeHypercallIntercept => {
318                    intercept_handler.handle_hypercall_exit();
319                    &mut this.backing.stats.hypercall
320                }
321                HvMessageType::HvMessageTypeSynicSintDeliverable => {
322                    intercept_handler.handle_synic_deliverable_exit();
323                    &mut this.backing.stats.synic_deliverable
324                }
325                HvMessageType::HvMessageTypeX64InterruptionDeliverable => {
326                    intercept_handler.handle_interrupt_deliverable_exit(dev)?;
327                    &mut this.backing.stats.interrupt_deliverable
328                }
329                HvMessageType::HvMessageTypeX64CpuidIntercept => {
330                    intercept_handler.handle_cpuid_intercept();
331                    &mut this.backing.stats.cpuid
332                }
333                HvMessageType::HvMessageTypeMsrIntercept => {
334                    intercept_handler.handle_msr_intercept();
335                    &mut this.backing.stats.msr
336                }
337                HvMessageType::HvMessageTypeX64ApicEoi => {
338                    intercept_handler.handle_eoi(dev);
339                    &mut this.backing.stats.eoi
340                }
341                HvMessageType::HvMessageTypeUnrecoverableException => {
342                    intercept_handler.handle_unrecoverable_exception()?;
343                    &mut this.backing.stats.unrecoverable_exception
344                }
345                HvMessageType::HvMessageTypeExceptionIntercept => {
346                    intercept_handler.handle_exception(dev)?;
347                    &mut this.backing.stats.exception_intercept
348                }
349                reason => unreachable!("unknown exit reason: {:#x?}", reason),
350            };
351            stat.increment();
352
353            if this.runner.is_sidecar()
354                && !this.signaled_sidecar_exit
355                && !this.partition.no_sidecar_hotplug.load(Relaxed)
356            {
357                // We got and handled an exit and this is a sidecar VP. Cancel
358                // the run so that we can move the sidecar VP over to the main
359                // kernel and handle future exits there.
360                //
361                // This is not strictly necessary--we can continue to run the VP
362                // in the sidecar kernel. But since we have received at least
363                // one exit, we can expect that we will receive more, and
364                // handling the exits remotely introduces jitter.
365                let message = this.runner.exit_message();
366                this.inner
367                    .set_sidecar_exit_reason(SidecarExitReason::Exit(parse_sidecar_exit(message)));
368                this.signaled_sidecar_exit = true;
369                return Err(VpHaltReason::Cancel);
370            }
371        }
372        Ok(())
373    }
374
375    fn poll_apic(_this: &mut UhProcessor<'_, Self>, _vtl: GuestVtl, _scan_irr: bool) {}
376
377    fn process_interrupts(
378        _this: &mut UhProcessor<'_, Self>,
379        _scan_irr: hv1_structs::VtlArray<bool, 2>,
380        _first_scan_irr: &mut bool,
381        _dev: &impl CpuIo,
382    ) -> bool {
383        false
384    }
385
386    fn request_extint_readiness(this: &mut UhProcessor<'_, Self>) {
387        this.backing
388            .next_deliverability_notifications
389            .set_interrupt_notification(true);
390    }
391
392    fn request_untrusted_sint_readiness(this: &mut UhProcessor<'_, Self>, sints: u16) {
393        this.backing
394            .next_deliverability_notifications
395            .set_sints(this.backing.next_deliverability_notifications.sints() | sints);
396    }
397
398    fn hv(&self, _vtl: GuestVtl) -> Option<&ProcessorVtlHv> {
399        None
400    }
401
402    fn hv_mut(&mut self, _vtl: GuestVtl) -> Option<&mut ProcessorVtlHv> {
403        None
404    }
405
406    fn handle_vp_start_enable_vtl_wake(_this: &mut UhProcessor<'_, Self>, _vtl: GuestVtl) {
407        unimplemented!()
408    }
409
410    fn vtl1_inspectable(_this: &UhProcessor<'_, Self>) -> bool {
411        // TODO: Use the VsmVpStatus register to query the hypervisor for
412        // whether VTL 1 is enabled on the vp (this can be cached).
413        false
414    }
415}
416
417fn parse_sidecar_exit(message: &hvdef::HvMessage) -> SidecarRemoveExit {
418    match message.header.typ {
419        HvMessageType::HvMessageTypeX64IoPortIntercept => {
420            let message = message.as_message::<hvdef::HvX64IoPortInterceptMessage>();
421            SidecarRemoveExit::Io {
422                port: message.port_number,
423                write: message.header.intercept_access_type == HvInterceptAccessType::WRITE,
424            }
425        }
426        HvMessageType::HvMessageTypeUnmappedGpa | HvMessageType::HvMessageTypeGpaIntercept => {
427            let message = message.as_message::<hvdef::HvX64MemoryInterceptMessage>();
428            SidecarRemoveExit::Mmio {
429                gpa: message.guest_physical_address,
430                write: message.header.intercept_access_type == HvInterceptAccessType::WRITE,
431            }
432        }
433        HvMessageType::HvMessageTypeHypercallIntercept => {
434            let message = message.as_message::<hvdef::HvX64HypercallInterceptMessage>();
435            let is_64bit = message.header.execution_state.cr0_pe()
436                && message.header.execution_state.efer_lma();
437            let control = if is_64bit {
438                message.rcx
439            } else {
440                (message.rdx << 32) | (message.rax as u32 as u64)
441            };
442            SidecarRemoveExit::Hypercall {
443                code: hvdef::HypercallCode(hypercall::Control::from(control).code()),
444            }
445        }
446        HvMessageType::HvMessageTypeX64CpuidIntercept => {
447            let message = message.as_message::<hvdef::HvX64CpuidInterceptMessage>();
448            SidecarRemoveExit::Cpuid {
449                leaf: message.rax as u32,
450                subleaf: message.rcx as u32,
451            }
452        }
453        HvMessageType::HvMessageTypeMsrIntercept => {
454            let message = message.as_message::<hvdef::HvX64MsrInterceptMessage>();
455            SidecarRemoveExit::Msr {
456                msr: message.msr_number,
457                value: (message.header.intercept_access_type == HvInterceptAccessType::WRITE)
458                    .then_some((message.rdx << 32) | message.rax as u32 as u64),
459            }
460        }
461        typ => SidecarRemoveExit::Hypervisor { message: typ },
462    }
463}
464
465fn next_rip(value: &HvX64InterceptMessageHeader) -> u64 {
466    value.rip.wrapping_add(value.instruction_len() as u64)
467}
468
469struct InterceptHandler<'a, 'b> {
470    vp: &'a mut UhProcessor<'b, HypervisorBackedX86>,
471    intercepted_vtl: GuestVtl,
472}
473
474#[derive(Debug, Error)]
475#[error("invalid intercepted vtl {0:?}")]
476struct InvalidInterceptedVtl(u8);
477
478#[derive(Debug, Error)]
479#[error("guest accessed unaccepted gpa {0}")]
480struct UnacceptedMemoryAccess(u64);
481
482impl<'a, 'b> InterceptHandler<'a, 'b> {
483    fn new(
484        vp: &'a mut UhProcessor<'b, HypervisorBackedX86>,
485    ) -> Result<Self, InvalidInterceptedVtl> {
486        let message_type = vp.runner.exit_message().header.typ;
487
488        let intercepted_vtl = match vp.runner.reg_page_vtl() {
489            Ok(vtl) => vtl,
490            Err(ioctl::x64::RegisterPageVtlError::InvalidVtl(vtl)) => {
491                return Err(InvalidInterceptedVtl(vtl));
492            }
493            Err(ioctl::x64::RegisterPageVtlError::NoRegisterPage) => {
494                if matches!(&message_type, &HvMessageType::HvMessageTypeX64ApicEoi) {
495                    // At the moment this is only used for the ioapic, so assume
496                    // that this is targeting VTL 0 for now. TODO: fix
497                    GuestVtl::Vtl0
498                } else {
499                    let message_header = match &message_type {
500                        &HvMessageType::HvMessageTypeX64IoPortIntercept => {
501                            &vp.runner
502                                .exit_message()
503                                .as_message::<hvdef::HvX64IoPortInterceptMessage>()
504                                .header
505                        }
506                        &HvMessageType::HvMessageTypeUnmappedGpa
507                        | &HvMessageType::HvMessageTypeGpaIntercept => {
508                            &vp.runner
509                                .exit_message()
510                                .as_message::<hvdef::HvX64MemoryInterceptMessage>()
511                                .header
512                        }
513                        &HvMessageType::HvMessageTypeUnacceptedGpa => {
514                            &vp.runner
515                                .exit_message()
516                                .as_message::<hvdef::HvX64MemoryInterceptMessage>()
517                                .header
518                        }
519                        &HvMessageType::HvMessageTypeHypercallIntercept => {
520                            &vp.runner
521                                .exit_message()
522                                .as_message::<hvdef::HvX64HypercallInterceptMessage>()
523                                .header
524                        }
525                        &HvMessageType::HvMessageTypeSynicSintDeliverable => {
526                            &vp.runner
527                                .exit_message()
528                                .as_message::<hvdef::HvX64SynicSintDeliverableMessage>()
529                                .header
530                        }
531                        &HvMessageType::HvMessageTypeX64InterruptionDeliverable => {
532                            &vp.runner
533                                .exit_message()
534                                .as_message::<hvdef::HvX64InterruptionDeliverableMessage>()
535                                .header
536                        }
537                        &HvMessageType::HvMessageTypeX64CpuidIntercept => {
538                            &vp.runner
539                                .exit_message()
540                                .as_message::<hvdef::HvX64CpuidInterceptMessage>()
541                                .header
542                        }
543                        &HvMessageType::HvMessageTypeMsrIntercept => {
544                            &vp.runner
545                                .exit_message()
546                                .as_message::<hvdef::HvX64MsrInterceptMessage>()
547                                .header
548                        }
549                        &HvMessageType::HvMessageTypeUnrecoverableException => {
550                            &vp.runner
551                                .exit_message()
552                                .as_message::<hvdef::HvX64UnrecoverableExceptionMessage>()
553                                .header
554                        }
555                        &HvMessageType::HvMessageTypeX64Halt => {
556                            &vp.runner
557                                .exit_message()
558                                .as_message::<hvdef::HvX64HaltMessage>()
559                                .header
560                        }
561                        &HvMessageType::HvMessageTypeExceptionIntercept => {
562                            &vp.runner
563                                .exit_message()
564                                .as_message::<hvdef::HvX64ExceptionInterceptMessage>()
565                                .header
566                        }
567                        reason => unreachable!("unknown exit reason: {:#x?}", reason),
568                    };
569
570                    message_header
571                        .execution_state
572                        .vtl()
573                        .try_into()
574                        .map_err(|hcl::UnsupportedGuestVtl(vtl)| InvalidInterceptedVtl(vtl))?
575                }
576            }
577        };
578
579        Ok(Self {
580            vp,
581            intercepted_vtl,
582        })
583    }
584
585    fn handle_interrupt_deliverable_exit(&mut self, bus: &impl CpuIo) -> Result<(), VpHaltReason> {
586        let message = self
587            .vp
588            .runner
589            .exit_message()
590            .as_message::<hvdef::HvX64InterruptionDeliverableMessage>();
591
592        assert_eq!(
593            message.deliverable_type,
594            HvX64PendingInterruptionType::HV_X64_PENDING_INTERRUPT
595        );
596
597        self.vp
598            .backing
599            .deliverability_notifications
600            .set_interrupt_notification(false);
601
602        self.vp
603            .backing
604            .next_deliverability_notifications
605            .set_interrupt_notification(false);
606
607        if let Some(vector) = bus.acknowledge_pic_interrupt() {
608            let event = hvdef::HvX64PendingExtIntEvent::new()
609                .with_event_pending(true)
610                .with_event_type(hvdef::HV_X64_PENDING_EVENT_EXT_INT)
611                .with_vector(vector);
612
613            self.vp
614                .runner
615                .set_vp_register(
616                    self.intercepted_vtl,
617                    HvX64RegisterName::PendingEvent0,
618                    u128::from(event).into(),
619                )
620                .unwrap();
621        }
622
623        Ok(())
624    }
625
626    fn handle_synic_deliverable_exit(&mut self) {
627        let message = self
628            .vp
629            .runner
630            .exit_message()
631            .as_message::<hvdef::HvX64SynicSintDeliverableMessage>();
632
633        tracing::trace!(
634            deliverable_sints = message.deliverable_sints,
635            "sint deliverable"
636        );
637
638        self.vp.backing.deliverability_notifications.set_sints(
639            self.vp.backing.deliverability_notifications.sints() & !message.deliverable_sints,
640        );
641
642        // This is updated by `deliver_synic_messages below`, so clear it here.
643        self.vp
644            .backing
645            .next_deliverability_notifications
646            .set_sints(0);
647
648        // These messages are always delivered to VTL0, as VTL1 does not own any VMBUS channels.
649        self.vp
650            .deliver_synic_messages(GuestVtl::Vtl0, message.deliverable_sints);
651    }
652
653    fn handle_hypercall_exit(&mut self) {
654        let message = self
655            .vp
656            .runner
657            .exit_message()
658            .as_message::<hvdef::HvX64HypercallInterceptMessage>();
659
660        tracing::trace!(msg = %format_args!("{:x?}", message), "hypercall");
661
662        let is_64bit =
663            message.header.execution_state.cr0_pe() && message.header.execution_state.efer_lma();
664
665        let guest_memory = &self.vp.partition.gm[self.intercepted_vtl];
666        let handler = UhHypercallHandler {
667            vp: self.vp,
668            trusted: false,
669            intercepted_vtl: self.intercepted_vtl,
670        };
671        UhHypercallHandler::MSHV_DISPATCHER.dispatch(
672            guest_memory,
673            hv1_hypercall::X64RegisterIo::new(handler, is_64bit, true),
674        );
675    }
676
677    async fn handle_mmio_exit(&mut self, dev: &impl CpuIo) -> Result<(), VpHaltReason> {
678        let message = self
679            .vp
680            .runner
681            .exit_message()
682            .as_message::<hvdef::HvX64MemoryInterceptMessage>();
683
684        tracing::trace!(msg = %format_args!("{:x?}", message), "mmio");
685
686        let interruption_pending = message.header.execution_state.interruption_pending();
687
688        // Fast path for monitor page writes.
689        if Some(message.guest_physical_address & !(HV_PAGE_SIZE - 1))
690            == self.vp.partition.monitor_page.gpa()
691            && message.header.intercept_access_type == HvInterceptAccessType::WRITE
692        {
693            let tlb_lock_held = message.memory_access_info.gva_gpa_valid()
694                || message.memory_access_info.tlb_locked();
695            let guest_memory = &self.vp.partition.gm[self.intercepted_vtl];
696            let cache = self.vp.emulation_cache(self.intercepted_vtl);
697            let mut emulation_state = UhEmulationState {
698                vp: &mut *self.vp,
699                interruption_pending,
700                devices: dev,
701                vtl: self.intercepted_vtl,
702                cache,
703            };
704            if let Some(bit) = virt_support_x86emu::emulate::emulate_mnf_write_fast_path(
705                &mut emulation_state,
706                guest_memory,
707                dev,
708                interruption_pending,
709                tlb_lock_held,
710            ) {
711                if let Some(connection_id) = self.vp.partition.monitor_page.write_bit(bit) {
712                    signal_mnf(&self.vp.partition.synic_ports, connection_id);
713                }
714                return Ok(());
715            }
716        }
717
718        let cache = self.vp.emulation_cache(self.intercepted_vtl);
719        self.vp
720            .emulate(dev, interruption_pending, self.intercepted_vtl, cache)
721            .await
722    }
723
724    async fn handle_io_port_exit(&mut self, dev: &impl CpuIo) -> Result<(), VpHaltReason> {
725        let message = self
726            .vp
727            .runner
728            .exit_message()
729            .as_message::<hvdef::HvX64IoPortInterceptMessage>();
730
731        tracing::trace!(msg = %format_args!("{:x?}", message), "io_port");
732
733        assert_eq!(
734            message.rax,
735            self.vp.runner.cpu_context().gps_no_rsp[protocol::RAX]
736        );
737
738        let interruption_pending = message.header.execution_state.interruption_pending();
739
740        if message.access_info.string_op() || message.access_info.rep_prefix() {
741            let cache = self.vp.emulation_cache(self.intercepted_vtl);
742            self.vp
743                .emulate(dev, interruption_pending, self.intercepted_vtl, cache)
744                .await
745        } else {
746            let next_rip = next_rip(&message.header);
747            let access_size = message.access_info.access_size();
748            virt_support_x86emu::emulate::emulate_io(
749                self.vp.vp_index(),
750                message.header.intercept_access_type == HvInterceptAccessType::WRITE,
751                message.port_number,
752                &mut self.vp.runner.cpu_context_mut().gps_no_rsp[protocol::RAX],
753                access_size,
754                dev,
755            )
756            .await;
757            self.vp.set_rip(self.intercepted_vtl, next_rip);
758            Ok(())
759        }
760    }
761
762    async fn handle_unaccepted_gpa_intercept(
763        &mut self,
764        dev: &impl CpuIo,
765    ) -> Result<(), VpHaltReason> {
766        let gpa = self
767            .vp
768            .runner
769            .exit_message()
770            .as_message::<hvdef::HvX64MemoryInterceptMessage>()
771            .guest_physical_address;
772
773        if self.vp.partition.is_gpa_lower_vtl_ram(gpa) {
774            // The host may have moved the page to an unaccepted state, so fail
775            // here. This does not apply to VTL 2 memory - for unaccepted pages,
776            // the intercept goes to host VTL0.
777            //
778            // Note: SGX memory should be included in this check, so if SGX is
779            // no longer included in the lower_vtl_memory_layout, make sure the
780            // appropriate changes are reflected here.
781            Err(dev.fatal_error(UnacceptedMemoryAccess(gpa).into()))
782        } else {
783            self.handle_mmio_exit(dev).await
784        }
785    }
786
787    fn handle_cpuid_intercept(&mut self) {
788        let message = self
789            .vp
790            .runner
791            .exit_message()
792            .as_message::<hvdef::HvX64CpuidInterceptMessage>();
793
794        let default_result = [
795            message.default_result_rax as u32,
796            message.default_result_rbx as u32,
797            message.default_result_rcx as u32,
798            message.default_result_rdx as u32,
799        ];
800
801        tracing::trace!(msg = %format_args!("{:x?}", message), "cpuid");
802
803        let [eax, ebx, ecx, edx] =
804            self.vp
805                .partition
806                .cpuid
807                .result(message.rax as u32, message.rcx as u32, &default_result);
808
809        let next_rip = next_rip(&message.header);
810        self.vp.runner.cpu_context_mut().gps_no_rsp[protocol::RAX] = eax.into();
811        self.vp.runner.cpu_context_mut().gps_no_rsp[protocol::RBX] = ebx.into();
812        self.vp.runner.cpu_context_mut().gps_no_rsp[protocol::RCX] = ecx.into();
813        self.vp.runner.cpu_context_mut().gps_no_rsp[protocol::RDX] = edx.into();
814
815        self.vp.set_rip(self.intercepted_vtl, next_rip);
816    }
817
818    fn handle_msr_intercept(&mut self) {
819        let message = self
820            .vp
821            .runner
822            .exit_message()
823            .as_message::<hvdef::HvX64MsrInterceptMessage>();
824        let rip = next_rip(&message.header);
825
826        tracing::trace!(msg = %format_args!("{:x?}", message), "msr");
827
828        let msr = message.msr_number;
829        match message.header.intercept_access_type {
830            HvInterceptAccessType::READ => {
831                // Only supported MSRs are the crash MSRs.
832                let value = match self.vp.read_crash_msr(msr, self.intercepted_vtl) {
833                    Ok(v) => v,
834                    Err(MsrError::Unknown) => {
835                        tracing::trace!(msr, "unknown msr read");
836                        0
837                    }
838                    Err(MsrError::InvalidAccess) => {
839                        self.vp.inject_gpf(self.intercepted_vtl);
840                        // Do not advance RIP.
841                        return;
842                    }
843                };
844
845                self.vp.runner.cpu_context_mut().gps_no_rsp[protocol::RAX] = value & 0xffff_ffff;
846                self.vp.runner.cpu_context_mut().gps_no_rsp[protocol::RDX] = value >> 32;
847            }
848            HvInterceptAccessType::WRITE => {
849                let value = (message.rax & 0xffff_ffff) | (message.rdx << 32);
850                // Only supported MSRs are the crash MSRs.
851                match self.vp.write_crash_msr(msr, value, self.intercepted_vtl) {
852                    Ok(()) => {}
853                    Err(MsrError::Unknown) => {
854                        tracing::trace!(msr, value, "unknown msr write");
855                    }
856                    Err(MsrError::InvalidAccess) => {
857                        self.vp.inject_gpf(self.intercepted_vtl);
858                        // Do not advance RIP.
859                        return;
860                    }
861                }
862            }
863            _ => unreachable!(),
864        }
865
866        self.vp.set_rip(self.intercepted_vtl, rip);
867    }
868
869    fn handle_eoi(&self, dev: &impl CpuIo) {
870        let message = self
871            .vp
872            .runner
873            .exit_message()
874            .as_message::<hvdef::HvX64ApicEoiMessage>();
875
876        tracing::trace!(msg = %format_args!("{:x?}", message), "eoi");
877
878        dev.handle_eoi(message.interrupt_vector);
879    }
880
881    fn handle_unrecoverable_exception(&self) -> Result<(), VpHaltReason> {
882        Err(VpHaltReason::TripleFault {
883            vtl: self.intercepted_vtl.into(),
884        })
885    }
886
887    fn handle_exception(&mut self, dev: &impl CpuIo) -> Result<(), VpHaltReason> {
888        let message = self
889            .vp
890            .runner
891            .exit_message()
892            .as_message::<hvdef::HvX64ExceptionInterceptMessage>();
893
894        match x86defs::Exception(message.vector as u8) {
895            x86defs::Exception::DEBUG if cfg!(feature = "gdb") => {
896                self.vp.handle_debug_exception(dev, self.intercepted_vtl)?
897            }
898            _ => tracing::error!("unexpected exception type {:#x?}", message.vector),
899        }
900        Ok(())
901    }
902}
903
904impl UhProcessor<'_, HypervisorBackedX86> {
905    fn set_rip(&mut self, vtl: GuestVtl, rip: u64) {
906        self.runner
907            .set_vp_register(vtl, HvX64RegisterName::Rip, rip.into())
908            .unwrap();
909    }
910
911    fn inject_gpf(&mut self, vtl: GuestVtl) {
912        let exception_event = hvdef::HvX64PendingExceptionEvent::new()
913            .with_event_pending(true)
914            .with_event_type(hvdef::HV_X64_PENDING_EVENT_EXCEPTION)
915            .with_vector(x86defs::Exception::GENERAL_PROTECTION_FAULT.0.into())
916            .with_deliver_error_code(true)
917            .with_error_code(0);
918
919        self.runner
920            .set_vp_register(
921                vtl,
922                HvX64RegisterName::PendingEvent0,
923                u128::from(exception_event).into(),
924            )
925            .expect("set_vp_register should succeed for pending event");
926    }
927
928    fn set_vsm_partition_config(
929        &mut self,
930        vtl: GuestVtl,
931        value: HvRegisterVsmPartitionConfig,
932    ) -> Result<(), HvError> {
933        if vtl != GuestVtl::Vtl1 {
934            return Err(HvError::InvalidParameter);
935        }
936
937        assert!(self.partition.isolation.is_isolated());
938
939        let status = self
940            .partition
941            .vsm_status()
942            .expect("cannot fail to query vsm status");
943
944        let vtl1_enabled = VtlSet::from(status.enabled_vtl_set()).is_set(GuestVtl::Vtl1);
945        if !vtl1_enabled {
946            return Err(HvError::InvalidVtlState);
947        }
948
949        let mut guest_vsm_lock = self.shared.guest_vsm.write();
950
951        // Initialize partition.guest_vsm state if necessary.
952        match *guest_vsm_lock {
953            GuestVsmState::NotPlatformSupported => {
954                return Err(HvError::AccessDenied);
955            }
956            GuestVsmState::NotGuestEnabled => {
957                // TODO: check status
958                *guest_vsm_lock = GuestVsmState::Enabled {
959                    vtl1: Default::default(),
960                };
961            }
962            GuestVsmState::Enabled { .. } => {}
963        }
964
965        let GuestVsmState::Enabled { vtl1 } = &mut *guest_vsm_lock else {
966            unreachable!()
967        };
968        let protections = HvMapGpaFlags::from(value.default_vtl_protection_mask() as u32);
969
970        if value.reserved() != 0 {
971            return Err(HvError::InvalidRegisterValue);
972        }
973
974        // VTL protection cannot be disabled once enabled.
975        //
976        // The hypervisor should intercept only the case where the lower VTL is
977        // setting the enable_vtl_protection bit when it was previously
978        // disabled; other cases are handled directly by the hypervisor.
979        if !value.enable_vtl_protection() {
980            if vtl1.enable_vtl_protection {
981                // A malicious guest could change its hypercall parameters in
982                // memory while the intercept is being handled; this case
983                // explicitly handles that situation.
984                return Err(HvError::InvalidRegisterValue);
985            } else {
986                panic!("unexpected SetVpRegisters intercept");
987            }
988        }
989
990        // For VBS-isolated VMs, protections apply to VTLs lower than the one specified when
991        // setting VsmPartitionConfig.
992        let mbec_enabled = VtlSet::from(status.mbec_enabled_vtl_set()).is_set(GuestVtl::Vtl0);
993        let shadow_supervisor_stack_enabled =
994            VtlSet::from(status.supervisor_shadow_stack_enabled_vtl_set() as u16)
995                .is_set(GuestVtl::Vtl0);
996
997        if !validate_vtl_gpa_flags(protections, mbec_enabled, shadow_supervisor_stack_enabled) {
998            return Err(HvError::InvalidRegisterValue);
999        }
1000
1001        // Default VTL protection mask must include read and write.
1002        if !(protections.readable() && protections.writable()) {
1003            return Err(HvError::InvalidRegisterValue);
1004        }
1005
1006        // Don't allow changing existing protections once set.
1007        if let Some(current_protections) = vtl1.default_vtl_protections {
1008            if protections != current_protections {
1009                return Err(HvError::InvalidRegisterValue);
1010            }
1011        }
1012        vtl1.default_vtl_protections = Some(protections);
1013
1014        for ram_range in self.partition.lower_vtl_memory_layout.ram().iter() {
1015            self.partition
1016                .hcl
1017                .modify_vtl_protection_mask(ram_range.range, protections, vtl.into())
1018                .map_err(|e| match e {
1019                    ApplyVtlProtectionsError::Hypervisor {
1020                        range: _,
1021                        output: _,
1022                        hv_error,
1023                        vtl: _,
1024                    } => hv_error,
1025                    _ => unreachable!(),
1026                })?;
1027        }
1028
1029        let hc_regs = [(HvX64RegisterName::VsmPartitionConfig, u64::from(value))];
1030        self.runner.set_vp_registers_hvcall(vtl.into(), hc_regs)?;
1031        vtl1.enable_vtl_protection = true;
1032
1033        Ok(())
1034    }
1035
1036    ///Eagerly load registers for emulation
1037    ///Typically we load expensive registers lazily, however some registers will always be used,
1038    ///and the underlying ioctl supports batching multiple register retrievals into a single call
1039    fn emulation_cache(&mut self, vtl: GuestVtl) -> MshvEmulationCache {
1040        const NAMES: &[HvX64RegisterName] = &[
1041            HvX64RegisterName::Rsp,
1042            HvX64RegisterName::Es,
1043            HvX64RegisterName::Ds,
1044            HvX64RegisterName::Fs,
1045            HvX64RegisterName::Gs,
1046            HvX64RegisterName::Ss,
1047            HvX64RegisterName::Cr0,
1048            HvX64RegisterName::Efer,
1049        ];
1050        let mut values = [FromZeros::new_zeroed(); NAMES.len()];
1051        self.runner
1052            .get_vp_registers(vtl, NAMES, &mut values)
1053            .expect("register query should not fail");
1054
1055        let [rsp, es, ds, fs, gs, ss, cr0, efer] = values;
1056
1057        let header = self
1058            .runner
1059            .exit_message()
1060            .as_message::<HvX64InterceptMessageHeader>();
1061
1062        MshvEmulationCache {
1063            rsp: rsp.as_u64(),
1064            es: from_seg(es.into()),
1065            ds: from_seg(ds.into()),
1066            fs: from_seg(fs.into()),
1067            gs: from_seg(gs.into()),
1068            ss: from_seg(ss.into()),
1069            cr0: cr0.as_u64(),
1070            efer: efer.as_u64(),
1071            rip: header.rip,
1072            rflags: header.rflags.into(),
1073        }
1074    }
1075}
1076
1077fn from_seg(reg: hvdef::HvX64SegmentRegister) -> SegmentRegister {
1078    SegmentRegister {
1079        base: reg.base,
1080        limit: reg.limit,
1081        selector: reg.selector,
1082        attributes: reg.attributes.into(),
1083    }
1084}
1085
1086impl<T: CpuIo> EmulatorSupport for UhEmulationState<'_, '_, T, HypervisorBackedX86> {
1087    fn flush(&mut self) {
1088        self.vp
1089            .runner
1090            .set_vp_registers(
1091                self.vtl,
1092                [
1093                    (HvX64RegisterName::Rip, self.cache.rip),
1094                    (HvX64RegisterName::Rflags, self.cache.rflags.into()),
1095                    (HvX64RegisterName::Rsp, self.cache.rsp),
1096                ],
1097            )
1098            .unwrap();
1099    }
1100
1101    fn vp_index(&self) -> VpIndex {
1102        self.vp.vp_index()
1103    }
1104
1105    fn vendor(&self) -> x86defs::cpuid::Vendor {
1106        self.vp.partition.caps.vendor
1107    }
1108
1109    fn gp(&mut self, reg: x86emu::Gp) -> u64 {
1110        match reg {
1111            x86emu::Gp::RSP => self.cache.rsp,
1112            _ => self.vp.runner.cpu_context().gps_no_rsp[reg as usize],
1113        }
1114    }
1115
1116    fn set_gp(&mut self, reg: x86emu::Gp, v: u64) {
1117        match reg {
1118            x86emu::Gp::RSP => self.cache.rsp = v,
1119            _ => self.vp.runner.cpu_context_mut().gps_no_rsp[reg as usize] = v,
1120        }
1121    }
1122
1123    fn xmm(&mut self, index: usize) -> u128 {
1124        u128::from_le_bytes(self.vp.runner.cpu_context().fx_state.xmm[index])
1125    }
1126
1127    fn set_xmm(&mut self, index: usize, v: u128) {
1128        self.vp.runner.cpu_context_mut().fx_state.xmm[index] = v.to_le_bytes();
1129    }
1130
1131    fn rip(&mut self) -> u64 {
1132        self.cache.rip
1133    }
1134
1135    fn set_rip(&mut self, v: u64) {
1136        self.cache.rip = v;
1137    }
1138
1139    fn segment(&mut self, index: x86emu::Segment) -> SegmentRegister {
1140        match index {
1141            x86emu::Segment::CS => {
1142                let header = self
1143                    .vp
1144                    .runner
1145                    .exit_message()
1146                    .as_message::<HvX64InterceptMessageHeader>();
1147                from_seg(header.cs_segment)
1148            }
1149            x86emu::Segment::ES => self.cache.es,
1150            x86emu::Segment::SS => self.cache.ss,
1151            x86emu::Segment::DS => self.cache.ds,
1152            x86emu::Segment::FS => self.cache.fs,
1153            x86emu::Segment::GS => self.cache.gs,
1154        }
1155    }
1156
1157    fn efer(&mut self) -> u64 {
1158        self.cache.efer
1159    }
1160
1161    fn cr0(&mut self) -> u64 {
1162        self.cache.cr0
1163    }
1164
1165    fn rflags(&mut self) -> RFlags {
1166        self.cache.rflags
1167    }
1168
1169    fn set_rflags(&mut self, v: RFlags) {
1170        self.cache.rflags = v;
1171    }
1172
1173    fn instruction_bytes(&self) -> &[u8] {
1174        let message = self.vp.runner.exit_message();
1175        match message.header.typ {
1176            HvMessageType::HvMessageTypeGpaIntercept
1177            | HvMessageType::HvMessageTypeUnmappedGpa
1178            | HvMessageType::HvMessageTypeUnacceptedGpa => {
1179                let message = message.as_message::<hvdef::HvX64MemoryInterceptMessage>();
1180                &message.instruction_bytes[..message.instruction_byte_count as usize]
1181            }
1182            HvMessageType::HvMessageTypeX64IoPortIntercept => {
1183                let message = message.as_message::<hvdef::HvX64IoPortInterceptMessage>();
1184                &message.instruction_bytes[..message.instruction_byte_count as usize]
1185            }
1186            _ => unreachable!(),
1187        }
1188    }
1189
1190    fn physical_address(&self) -> Option<u64> {
1191        let message = self.vp.runner.exit_message();
1192        match message.header.typ {
1193            HvMessageType::HvMessageTypeGpaIntercept
1194            | HvMessageType::HvMessageTypeUnmappedGpa
1195            | HvMessageType::HvMessageTypeUnacceptedGpa => {
1196                let message = message.as_message::<hvdef::HvX64MemoryInterceptMessage>();
1197                Some(message.guest_physical_address)
1198            }
1199            _ => None,
1200        }
1201    }
1202
1203    fn initial_gva_translation(
1204        &mut self,
1205    ) -> Option<virt_support_x86emu::emulate::InitialTranslation> {
1206        if (self.vp.runner.exit_message().header.typ != HvMessageType::HvMessageTypeGpaIntercept)
1207            && (self.vp.runner.exit_message().header.typ != HvMessageType::HvMessageTypeUnmappedGpa)
1208            && (self.vp.runner.exit_message().header.typ
1209                != HvMessageType::HvMessageTypeUnacceptedGpa)
1210        {
1211            return None;
1212        }
1213
1214        let message = self
1215            .vp
1216            .runner
1217            .exit_message()
1218            .as_message::<hvdef::HvX64MemoryInterceptMessage>();
1219
1220        if !message.memory_access_info.gva_gpa_valid() {
1221            tracing::trace!(?message.guest_virtual_address, ?message.guest_physical_address, "gva gpa not valid {:?}", self.vp.runner.exit_message().payload());
1222            return None;
1223        }
1224
1225        let translate_mode = virt_support_x86emu::emulate::TranslateMode::try_from(
1226            message.header.intercept_access_type,
1227        )
1228        .expect("unexpected intercept access type");
1229
1230        let translation = virt_support_x86emu::emulate::InitialTranslation {
1231            gva: message.guest_virtual_address,
1232            gpa: message.guest_physical_address,
1233            translate_mode,
1234        };
1235
1236        tracing::trace!(?translation, "initial translation");
1237
1238        // If we have a valid translation, the hypervisor must have set the TLB lock
1239        // so the translation remains valid for the duration of this exit.
1240        // Update our local cache appropriately.
1241        self.vp.mark_tlb_locked(Vtl::Vtl2, self.vtl);
1242
1243        Some(translation)
1244    }
1245
1246    fn interruption_pending(&self) -> bool {
1247        self.interruption_pending
1248    }
1249
1250    fn check_vtl_access(
1251        &mut self,
1252        gpa: u64,
1253        mode: virt_support_x86emu::emulate::TranslateMode,
1254    ) -> Result<(), EmuCheckVtlAccessError> {
1255        // Underhill currently doesn't set VTL 2 protections against execute exclusively, it removes
1256        // all permissions from a page. So for VTL 1, no need to check the permissions; if VTL 1
1257        // doesn't have permissions to a page, Underhill should appropriately fail when it tries
1258        // to read or write to that page on VTL 1's behalf.
1259        //
1260        // For VTL 0, the alias map guards for read and write permissions, so only check VTL execute
1261        // permissions. Because VTL 2 will not restrict execute exclusively, only VTL 1 execute
1262        // permissions need to be checked and therefore only check permissions once VTL 1
1263        // protections are enabled. However on non-isolated partitions we don't intercept
1264        // VTL 1 enablement, so we just check if VTL 1 is supported at all.
1265        //
1266        // Note: the restriction to VTL 1 support also means that for WHP, which doesn't support VTL 1
1267        // the HvCheckSparseGpaPageVtlAccess hypercall--which is unimplemented in whp--will never be made.
1268        if mode == virt_support_x86emu::emulate::TranslateMode::Execute
1269            && self.vtl == GuestVtl::Vtl0
1270            && !matches!(
1271                *self.vp.shared.guest_vsm.read(),
1272                GuestVsmState::NotPlatformSupported,
1273            )
1274        {
1275            // Should always be called after translate gva with the tlb lock flag
1276            // or with an initial translation.
1277            debug_assert!(self.vp.is_tlb_locked(Vtl::Vtl2, self.vtl));
1278
1279            // An intercept can report a gpa that is unmapped or even outside the
1280            // partition's address space, and the hypervisor fails the whole
1281            // hypercall for those rather than reporting a per-page result. Only
1282            // mapped lower VTL RAM can carry VTL protections anyway.
1283            if !self.vp.partition.is_gpa_lower_vtl_ram(gpa) {
1284                return Ok(());
1285            }
1286
1287            let mbec_user_execute = self
1288                .vp
1289                .runner
1290                .get_vp_register(self.vtl, HvX64RegisterName::InstructionEmulationHints)
1291                .unwrap();
1292
1293            let flags =
1294                if hvdef::HvInstructionEmulatorHintsRegister::from(mbec_user_execute.as_u64())
1295                    .mbec_user_execute_control()
1296                {
1297                    HvMapGpaFlags::new().with_user_executable(true)
1298                } else {
1299                    HvMapGpaFlags::new().with_kernel_executable(true)
1300                };
1301
1302            let access_result = self
1303                .vp
1304                .partition
1305                .hcl
1306                .check_vtl_access(gpa, self.vtl, flags)
1307                .unwrap();
1308
1309            if let Some(ioctl::CheckVtlAccessResult { vtl, denied_flags }) = access_result {
1310                return Err(EmuCheckVtlAccessError::AccessDenied { vtl, denied_flags });
1311            };
1312        }
1313
1314        Ok(())
1315    }
1316
1317    fn translate_gva(
1318        &mut self,
1319        gva: u64,
1320        mode: virt_support_x86emu::emulate::TranslateMode,
1321    ) -> Result<EmuTranslateResult, EmuTranslateError> {
1322        let mut control_flags = hypercall::TranslateGvaControlFlagsX64::new();
1323        match mode {
1324            virt_support_x86emu::emulate::TranslateMode::Read => {
1325                control_flags.set_validate_read(true)
1326            }
1327            virt_support_x86emu::emulate::TranslateMode::Write => {
1328                control_flags.set_validate_read(true);
1329                control_flags.set_validate_write(true);
1330            }
1331            virt_support_x86emu::emulate::TranslateMode::Execute => {
1332                control_flags.set_validate_execute(true)
1333            }
1334        };
1335
1336        // The translation will be used, so set the appropriate page table bits
1337        // (the access/dirty bit).
1338        //
1339        // Prevent flushes in order to make sure that translation of this GVA
1340        // remains usable until the VP is resumed back to direct execution.
1341        control_flags.set_set_page_table_bits(true);
1342        control_flags.set_tlb_flush_inhibit(true);
1343
1344        // In case we're not running ring 0, check privileges against VP state
1345        // as of when the original intercept came in - since the emulator
1346        // doesn't support instructions that change ring level, the ring level
1347        // will remain the same as it was in the VP state as of when the
1348        // original intercept came in. The privilege exempt flag should
1349        // not be set.
1350        assert!(!control_flags.privilege_exempt());
1351
1352        // Do the translation using the current VTL.
1353        control_flags.set_input_vtl(self.vtl.into());
1354
1355        match self
1356            .vp
1357            .runner
1358            .translate_gva_to_gpa(gva, control_flags)
1359            .unwrap()
1360        {
1361            Ok(ioctl::TranslateResult {
1362                gpa_page,
1363                overlay_page,
1364            }) => {
1365                self.vp.mark_tlb_locked(Vtl::Vtl2, self.vtl);
1366                Ok(EmuTranslateResult {
1367                    gpa: (gpa_page << hvdef::HV_PAGE_SHIFT) + (gva & (HV_PAGE_SIZE - 1)),
1368                    overlay_page: Some(overlay_page),
1369                })
1370            }
1371            Err(ioctl::x64::TranslateErrorX64 { code, event_info }) => Err(EmuTranslateError {
1372                code: hypercall::TranslateGvaResultCode(code),
1373                event_info: Some(event_info),
1374            }),
1375        }
1376    }
1377
1378    fn inject_pending_event(&mut self, event_info: HvX64PendingEvent) {
1379        let regs = [
1380            (
1381                HvX64RegisterName::PendingEvent0,
1382                u128::from(event_info.reg_0),
1383            ),
1384            (
1385                HvX64RegisterName::PendingEvent1,
1386                u128::from(event_info.reg_1),
1387            ),
1388        ];
1389
1390        self.vp
1391            .runner
1392            .set_vp_registers_hvcall(self.vtl.into(), regs)
1393            .expect("set_vp_registers hypercall for setting pending event should not fail");
1394    }
1395
1396    fn monitor_support(&self) -> Option<&dyn EmulatorMonitorSupport> {
1397        Some(self)
1398    }
1399
1400    fn is_gpa_mapped(&self, gpa: u64, write: bool) -> bool {
1401        self.vp.partition.is_gpa_mapped(gpa, write)
1402    }
1403
1404    fn lapic_base_address(&self) -> Option<u64> {
1405        None
1406    }
1407
1408    fn lapic_read(&mut self, _address: u64, _data: &mut [u8]) {
1409        unimplemented!()
1410    }
1411
1412    fn lapic_write(&mut self, _address: u64, _data: &[u8]) {
1413        unimplemented!()
1414    }
1415}
1416
1417impl UhHypercallHandler<'_, '_, HypervisorBackedX86> {
1418    const MSHV_DISPATCHER: hv1_hypercall::Dispatcher<Self> = hv1_hypercall::dispatcher!(
1419        Self,
1420        [
1421            hv1_hypercall::HvPostMessage,
1422            hv1_hypercall::HvSignalEvent,
1423            hv1_hypercall::HvRetargetDeviceInterrupt,
1424            hv1_hypercall::HvGetVpIndexFromApicId,
1425            hv1_hypercall::HvSetVpRegisters,
1426            hv1_hypercall::HvModifyVtlProtectionMask,
1427            hv1_hypercall::HvRestorePartitionTime,
1428        ]
1429    );
1430}
1431
1432impl hv1_hypercall::X64RegisterState for UhHypercallHandler<'_, '_, HypervisorBackedX86> {
1433    fn rip(&mut self) -> u64 {
1434        self.vp
1435            .runner
1436            .exit_message()
1437            .as_message::<HvX64InterceptMessageHeader>()
1438            .rip
1439    }
1440
1441    fn set_rip(&mut self, rip: u64) {
1442        self.vp.set_rip(self.intercepted_vtl, rip)
1443    }
1444
1445    fn gp(&mut self, n: hv1_hypercall::X64HypercallRegister) -> u64 {
1446        // Note that RSP is not a hypercall register, so this is OK.
1447        self.vp.runner.cpu_context().gps_no_rsp[n as usize]
1448    }
1449
1450    fn set_gp(&mut self, n: hv1_hypercall::X64HypercallRegister, value: u64) {
1451        // Note that RSP is not a hypercall register, so this is OK.
1452        self.vp.runner.cpu_context_mut().gps_no_rsp[n as usize] = value;
1453    }
1454
1455    fn xmm(&mut self, n: usize) -> u128 {
1456        u128::from_ne_bytes(self.vp.runner.cpu_context().fx_state.xmm[n])
1457    }
1458
1459    fn set_xmm(&mut self, n: usize, value: u128) {
1460        self.vp.runner.cpu_context_mut().fx_state.xmm[n] = value.to_ne_bytes();
1461    }
1462}
1463
1464trait ToVpRegisterName: 'static + Copy + std::fmt::Debug {
1465    fn to_vp_reg_name(self) -> VpRegisterName;
1466}
1467
1468impl ToVpRegisterName for VpRegisterName {
1469    fn to_vp_reg_name(self) -> VpRegisterName {
1470        self
1471    }
1472}
1473
1474impl UhVpStateAccess<'_, '_, HypervisorBackedX86> {
1475    fn set_register_state<T, R: ToVpRegisterName, const N: usize>(
1476        &mut self,
1477        regs: &T,
1478    ) -> Result<(), vp_state::Error>
1479    where
1480        T: HvRegisterState<R, N>,
1481    {
1482        let names = regs.names().map(|r| r.to_vp_reg_name());
1483        let mut values = [HvRegisterValue::new_zeroed(); N];
1484        regs.get_values(values.iter_mut());
1485        self.vp
1486            .runner
1487            .set_vp_registers(self.vtl, names.iter().copied().zip(values))
1488            .map_err(vp_state::Error::SetRegisters)?;
1489        Ok(())
1490    }
1491
1492    fn get_register_state<T, R: ToVpRegisterName, const N: usize>(
1493        &mut self,
1494    ) -> Result<T, vp_state::Error>
1495    where
1496        T: HvRegisterState<R, N>,
1497    {
1498        let mut regs = T::default();
1499        let names = regs.names().map(|r| r.to_vp_reg_name());
1500        let mut values = [HvRegisterValue::new_zeroed(); N];
1501        self.vp
1502            .runner
1503            .get_vp_registers(self.vtl, &names, &mut values)
1504            .map_err(vp_state::Error::GetRegisters)?;
1505
1506        regs.set_values(values.into_iter());
1507        Ok(regs)
1508    }
1509}
1510
1511impl AccessVpState for UhVpStateAccess<'_, '_, HypervisorBackedX86> {
1512    type Error = vp_state::Error;
1513
1514    fn caps(&self) -> &virt::x86::X86PartitionCapabilities {
1515        &self.vp.partition.caps
1516    }
1517
1518    fn commit(&mut self) -> Result<(), Self::Error> {
1519        Ok(())
1520    }
1521
1522    fn registers(&mut self) -> Result<vp::Registers, Self::Error> {
1523        self.get_register_state()
1524    }
1525
1526    fn set_registers(&mut self, value: &vp::Registers) -> Result<(), Self::Error> {
1527        self.set_register_state(value)
1528    }
1529
1530    fn activity(&mut self) -> Result<vp::Activity, Self::Error> {
1531        let activity: vp::Activity = self.get_register_state()?;
1532
1533        // TODO: Get the NMI pending bit from the APIC.
1534        // let apic = self.vp.whp(self.vtl).get_apic()?;
1535        // activity.nmi_pending = hv_apic_nmi_pending(&apic);
1536        Ok(activity)
1537    }
1538
1539    fn set_activity(&mut self, value: &vp::Activity) -> Result<(), Self::Error> {
1540        self.set_register_state(value)?;
1541
1542        // TODO: Set the NMI pending bit via the APIC.
1543        // let mut apic = self.vp.whp(self.vtl).get_apic()?;
1544        // set_hv_apic_nmi_pending(&mut apic, value.nmi_pending);
1545        // self.vp.whp(self.vtl).set_apic(&apic)?;
1546        Ok(())
1547    }
1548
1549    fn xsave(&mut self) -> Result<vp::Xsave, Self::Error> {
1550        // TODO: get the rest of the xsave state, not just the legacy FP state.
1551        //
1552        // This is just used for debugging, so this should not be a problem.
1553        #[repr(C)]
1554        #[derive(IntoBytes, Immutable, KnownLayout)]
1555        struct XsaveStandard {
1556            fxsave: Fxsave,
1557            xsave_header: XsaveHeader,
1558        }
1559        let state = XsaveStandard {
1560            fxsave: self.vp.runner.cpu_context().fx_state.clone(),
1561            xsave_header: XsaveHeader {
1562                xstate_bv: XFEATURE_X87 | XFEATURE_SSE,
1563                ..FromZeros::new_zeroed()
1564            },
1565        };
1566        Ok(vp::Xsave::from_standard(state.as_bytes(), self.caps()))
1567    }
1568
1569    fn set_xsave(&mut self, _value: &vp::Xsave) -> Result<(), Self::Error> {
1570        Err(vp_state::Error::Unimplemented("xsave"))
1571    }
1572
1573    fn apic(&mut self) -> Result<vp::Apic, Self::Error> {
1574        Err(vp_state::Error::Unimplemented("apic"))
1575    }
1576
1577    fn set_apic(&mut self, _value: &vp::Apic) -> Result<(), Self::Error> {
1578        Err(vp_state::Error::Unimplemented("apic"))
1579    }
1580
1581    fn xcr(&mut self) -> Result<vp::Xcr0, Self::Error> {
1582        self.get_register_state()
1583    }
1584
1585    fn set_xcr(&mut self, value: &vp::Xcr0) -> Result<(), Self::Error> {
1586        self.set_register_state(value)
1587    }
1588
1589    fn xss(&mut self) -> Result<vp::Xss, Self::Error> {
1590        self.get_register_state()
1591    }
1592
1593    fn set_xss(&mut self, value: &vp::Xss) -> Result<(), Self::Error> {
1594        self.set_register_state(value)
1595    }
1596
1597    fn mtrrs(&mut self) -> Result<vp::Mtrrs, Self::Error> {
1598        self.get_register_state()
1599    }
1600
1601    fn set_mtrrs(&mut self, cc: &vp::Mtrrs) -> Result<(), Self::Error> {
1602        self.set_register_state(cc)
1603    }
1604
1605    fn pat(&mut self) -> Result<vp::Pat, Self::Error> {
1606        self.get_register_state()
1607    }
1608
1609    fn set_pat(&mut self, value: &vp::Pat) -> Result<(), Self::Error> {
1610        self.set_register_state(value)
1611    }
1612
1613    fn virtual_msrs(&mut self) -> Result<vp::VirtualMsrs, Self::Error> {
1614        self.get_register_state()
1615    }
1616
1617    fn set_virtual_msrs(&mut self, msrs: &vp::VirtualMsrs) -> Result<(), Self::Error> {
1618        self.set_register_state(msrs)
1619    }
1620
1621    fn debug_regs(&mut self) -> Result<vp::DebugRegisters, Self::Error> {
1622        self.get_register_state()
1623    }
1624
1625    fn set_debug_regs(&mut self, value: &vp::DebugRegisters) -> Result<(), Self::Error> {
1626        self.set_register_state(value)
1627    }
1628
1629    fn tsc(&mut self) -> Result<vp::Tsc, Self::Error> {
1630        self.get_register_state()
1631    }
1632
1633    fn set_tsc(&mut self, value: &vp::Tsc) -> Result<(), Self::Error> {
1634        self.set_register_state(value)
1635    }
1636
1637    fn cet(&mut self) -> Result<vp::Cet, Self::Error> {
1638        self.get_register_state()
1639    }
1640
1641    fn set_cet(&mut self, value: &vp::Cet) -> Result<(), Self::Error> {
1642        self.set_register_state(value)
1643    }
1644
1645    fn cet_ss(&mut self) -> Result<vp::CetSs, Self::Error> {
1646        self.get_register_state()
1647    }
1648
1649    fn set_cet_ss(&mut self, value: &vp::CetSs) -> Result<(), Self::Error> {
1650        self.set_register_state(value)
1651    }
1652
1653    fn tsc_aux(&mut self) -> Result<vp::TscAux, Self::Error> {
1654        self.get_register_state()
1655    }
1656
1657    fn set_tsc_aux(&mut self, value: &vp::TscAux) -> Result<(), Self::Error> {
1658        self.set_register_state(value)
1659    }
1660
1661    fn synic_msrs(&mut self) -> Result<vp::SyntheticMsrs, Self::Error> {
1662        self.get_register_state()
1663    }
1664
1665    fn set_synic_msrs(&mut self, value: &vp::SyntheticMsrs) -> Result<(), Self::Error> {
1666        self.set_register_state(value)
1667    }
1668
1669    fn synic_timers(&mut self) -> Result<vp::SynicTimers, Self::Error> {
1670        Err(vp_state::Error::Unimplemented("synic_timers"))
1671    }
1672
1673    fn set_synic_timers(&mut self, _value: &vp::SynicTimers) -> Result<(), Self::Error> {
1674        Err(vp_state::Error::Unimplemented("synic_timers"))
1675    }
1676
1677    fn synic_message_queues(&mut self) -> Result<vp::SynicMessageQueues, Self::Error> {
1678        Ok(self.vp.inner.message_queues[self.vtl].save())
1679    }
1680
1681    fn set_synic_message_queues(
1682        &mut self,
1683        value: &vp::SynicMessageQueues,
1684    ) -> Result<(), Self::Error> {
1685        self.vp.inner.message_queues[self.vtl].restore(value);
1686        Ok(())
1687    }
1688
1689    fn synic_message_page(&mut self) -> Result<vp::SynicMessagePage, Self::Error> {
1690        Err(vp_state::Error::Unimplemented("synic_message_page"))
1691    }
1692
1693    fn set_synic_message_page(&mut self, _value: &vp::SynicMessagePage) -> Result<(), Self::Error> {
1694        Err(vp_state::Error::Unimplemented("synic_message_page"))
1695    }
1696
1697    fn synic_event_flags_page(&mut self) -> Result<vp::SynicEventFlagsPage, Self::Error> {
1698        Err(vp_state::Error::Unimplemented("synic_event_flags_page"))
1699    }
1700
1701    fn set_synic_event_flags_page(
1702        &mut self,
1703        _value: &vp::SynicEventFlagsPage,
1704    ) -> Result<(), Self::Error> {
1705        Err(vp_state::Error::Unimplemented("synic_event_flags_page"))
1706    }
1707
1708    fn nested_state(&mut self) -> Result<vp::NestedState, Self::Error> {
1709        Err(vp_state::Error::Unimplemented("nested_state"))
1710    }
1711
1712    fn set_nested_state(&mut self, _value: &vp::NestedState) -> Result<(), Self::Error> {
1713        Err(vp_state::Error::Unimplemented("nested_state"))
1714    }
1715}
1716
1717impl hv1_hypercall::RetargetDeviceInterrupt for UhHypercallHandler<'_, '_, HypervisorBackedX86> {
1718    fn retarget_interrupt(
1719        &mut self,
1720        device_id: u64,
1721        address: u64,
1722        data: u32,
1723        params: hv1_hypercall::HvInterruptParameters<'_>,
1724    ) -> hvdef::HvResult<()> {
1725        self.retarget_virtual_interrupt(
1726            device_id,
1727            address,
1728            data,
1729            params.vector,
1730            params.multicast,
1731            params.target_processors,
1732        )
1733    }
1734}
1735
1736impl hv1_hypercall::SetVpRegisters for UhHypercallHandler<'_, '_, HypervisorBackedX86> {
1737    fn set_vp_registers(
1738        &mut self,
1739        partition_id: u64,
1740        vp_index: u32,
1741        vtl: Option<Vtl>,
1742        registers: &[hypercall::HvRegisterAssoc],
1743    ) -> HvRepResult {
1744        if partition_id != hvdef::HV_PARTITION_ID_SELF {
1745            return Err((HvError::AccessDenied, 0));
1746        }
1747
1748        if vp_index != hvdef::HV_VP_INDEX_SELF && vp_index != self.vp.vp_index().index() {
1749            return Err((HvError::InvalidVpIndex, 0));
1750        }
1751
1752        let target_vtl = self
1753            .target_vtl_no_higher(vtl.unwrap_or(self.intercepted_vtl.into()))
1754            .map_err(|e| (e, 0))?;
1755
1756        for (i, reg) in registers.iter().enumerate() {
1757            if reg.name == HvX64RegisterName::VsmPartitionConfig.into() {
1758                let value = HvRegisterVsmPartitionConfig::from(reg.value.as_u64());
1759                self.vp
1760                    .set_vsm_partition_config(target_vtl, value)
1761                    .map_err(|e| (e, i))?;
1762            } else {
1763                return Err((HvError::InvalidParameter, i));
1764            }
1765        }
1766
1767        Ok(())
1768    }
1769}
1770
1771impl hv1_hypercall::ModifyVtlProtectionMask for UhHypercallHandler<'_, '_, HypervisorBackedX86> {
1772    fn modify_vtl_protection_mask(
1773        &mut self,
1774        partition_id: u64,
1775        _map_flags: HvMapGpaFlags,
1776        target_vtl: Option<Vtl>,
1777        gpa_pages: &[u64],
1778    ) -> HvRepResult {
1779        if partition_id != hvdef::HV_PARTITION_ID_SELF {
1780            return Err((HvError::AccessDenied, 0));
1781        }
1782
1783        let target_vtl = self
1784            .target_vtl_no_higher(target_vtl.unwrap_or(self.intercepted_vtl.into()))
1785            .map_err(|e| (e, 0))?;
1786        if target_vtl == GuestVtl::Vtl0 {
1787            return Err((HvError::InvalidParameter, 0));
1788        }
1789
1790        // A VTL cannot change its own VTL permissions until it has enabled VTL protection and
1791        // configured default permissions. Higher VTLs are not under this restriction (as they may
1792        // need to apply default permissions before VTL protection is enabled).
1793        if target_vtl == self.intercepted_vtl
1794            && !matches!(
1795                *self.vp.shared.guest_vsm.read(),
1796                GuestVsmState::Enabled {
1797                    vtl1: VbsIsolatedVtl1State {
1798                        enable_vtl_protection: true,
1799                        default_vtl_protections: Some(_),
1800                    },
1801                }
1802            )
1803        {
1804            return Err((HvError::AccessDenied, 0));
1805        }
1806
1807        // TODO VBS GUEST VSM: verify this logic is correct
1808        // TODO VBS GUEST VSM: validation on map_flags, similar to default
1809        // protections mask changes
1810        // Can receive an intercept on adjust permissions, and for isolated
1811        // VMs if the page is unaccepted
1812        if self.vp.partition.isolation.is_isolated() {
1813            return Err((HvError::OperationDenied, 0));
1814        } else {
1815            if !gpa_pages.is_empty() {
1816                if !self.vp.partition.is_gpa_lower_vtl_ram(gpa_pages[0]) {
1817                    return Err((HvError::OperationDenied, 0));
1818                } else {
1819                    panic!("Should not be handling this hypercall for guest ram");
1820                }
1821            }
1822        }
1823
1824        Ok(())
1825    }
1826}
1827
1828mod save_restore {
1829    use super::HypervisorBackedX86;
1830    use super::UhProcessor;
1831    use anyhow::Context;
1832    use hcl::GuestVtl;
1833    use hvdef::HV_X64_MSR_GUEST_CRASH_CTL;
1834    use hvdef::HvInternalActivityRegister;
1835    use hvdef::HvX64RegisterName;
1836    use hvdef::Vtl;
1837    use virt::Processor;
1838    use virt::vp::AccessVpState;
1839    use virt::vp::Mtrrs;
1840    use vmcore::save_restore::RestoreError;
1841    use vmcore::save_restore::SaveError;
1842    use vmcore::save_restore::SaveRestore;
1843    use zerocopy::FromZeros;
1844    use zerocopy::IntoBytes;
1845
1846    mod state {
1847        use mesh::payload::Protobuf;
1848        use vmcore::save_restore::SavedStateRoot;
1849
1850        #[derive(Protobuf, SavedStateRoot)]
1851        #[mesh(package = "underhill.partition")]
1852        pub struct ProcessorSavedState {
1853            #[mesh(1)]
1854            pub(super) rax: u64,
1855            #[mesh(2)]
1856            pub(super) rcx: u64,
1857            #[mesh(3)]
1858            pub(super) rdx: u64,
1859            #[mesh(4)]
1860            pub(super) rbx: u64,
1861            #[mesh(5)]
1862            pub(super) cr2: u64,
1863            #[mesh(6)]
1864            pub(super) rbp: u64,
1865            #[mesh(7)]
1866            pub(super) rsi: u64,
1867            #[mesh(8)]
1868            pub(super) rdi: u64,
1869            #[mesh(9)]
1870            pub(super) r8: u64,
1871            #[mesh(10)]
1872            pub(super) r9: u64,
1873            #[mesh(11)]
1874            pub(super) r10: u64,
1875            #[mesh(12)]
1876            pub(super) r11: u64,
1877            #[mesh(13)]
1878            pub(super) r12: u64,
1879            #[mesh(14)]
1880            pub(super) r13: u64,
1881            #[mesh(15)]
1882            pub(super) r14: u64,
1883            #[mesh(16)]
1884            pub(super) r15: u64,
1885            #[mesh(17)]
1886            pub(super) fx_state: Vec<u8>,
1887            #[mesh(18)]
1888            pub(super) dr0: u64,
1889            #[mesh(19)]
1890            pub(super) dr1: u64,
1891            #[mesh(20)]
1892            pub(super) dr2: u64,
1893            #[mesh(21)]
1894            pub(super) dr3: u64,
1895
1896            /// Only set when the DR6_SHARED capability is present
1897            #[mesh(22)]
1898            pub(super) dr6: Option<u64>,
1899
1900            /// If VTL0 should be in the startup suspend state. Older underhill
1901            /// versions do not save this property, so maintain the old buggy
1902            /// behavior for those cases its not present in the saved state.
1903            #[mesh(23)]
1904            pub(super) startup_suspend: Option<bool>,
1905
1906            #[mesh(24)]
1907            pub(super) crash_reg: Option<[u64; 5]>,
1908
1909            /// This value is ignored going forward, but may still be read by downlevel
1910            /// versions.
1911            #[mesh(25)]
1912            pub(super) crash_control: u64,
1913
1914            #[mesh(26)]
1915            pub(super) msr_mtrr_def_type: u64,
1916            #[mesh(27)]
1917            pub(super) fixed_mtrrs: Option<[u64; 11]>,
1918            #[mesh(28)]
1919            pub(super) variable_mtrrs: Option<[u64; 16]>,
1920            #[mesh(29)]
1921            pub(super) per_vtl: Vec<ProcessorVtlSavedState>,
1922        }
1923
1924        #[derive(Protobuf, SavedStateRoot)]
1925        #[mesh(package = "underhill.partition")]
1926        pub struct ProcessorVtlSavedState {
1927            #[mesh(1)]
1928            pub(super) message_queue: virt::vp::SynicMessageQueues,
1929        }
1930    }
1931
1932    const SHARED_REGISTERS: &[HvX64RegisterName] = &[
1933        HvX64RegisterName::Dr0,
1934        HvX64RegisterName::Dr1,
1935        HvX64RegisterName::Dr2,
1936        HvX64RegisterName::Dr3,
1937        HvX64RegisterName::Dr6, // must be last
1938    ];
1939
1940    impl SaveRestore for UhProcessor<'_, HypervisorBackedX86> {
1941        type SavedState = state::ProcessorSavedState;
1942
1943        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
1944            // Ensure all async requests are reflected in the saved state.
1945            self.flush_async_requests();
1946
1947            let dr6_shared = self.partition.hcl.dr6_shared();
1948            let mut values = [FromZeros::new_zeroed(); SHARED_REGISTERS.len()];
1949            let len = if dr6_shared {
1950                SHARED_REGISTERS.len()
1951            } else {
1952                SHARED_REGISTERS.len() - 1
1953            };
1954
1955            self.runner
1956                // All these registers are shared, so the VTL we ask for doesn't matter
1957                .get_vp_registers(GuestVtl::Vtl0, &SHARED_REGISTERS[..len], &mut values[..len])
1958                .context("failed to get shared registers")
1959                .map_err(SaveError::Other)?;
1960
1961            let [
1962                rax,
1963                rcx,
1964                rdx,
1965                rbx,
1966                cr2,
1967                rbp,
1968                rsi,
1969                rdi,
1970                r8,
1971                r9,
1972                r10,
1973                r11,
1974                r12,
1975                r13,
1976                r14,
1977                r15,
1978            ] = self.runner.cpu_context().gps_no_rsp;
1979
1980            // We are responsible for saving shared MSRs too, but other than
1981            // the MTRRs all shared MSRs are read-only. So this is all we need.
1982            let Mtrrs {
1983                msr_mtrr_def_type,
1984                fixed: fixed_mtrrs,
1985                variable: variable_mtrrs,
1986            } = self
1987                // MTRRs are shared, so it doesn't matter which VTL we ask for.
1988                .access_state(Vtl::Vtl0)
1989                .mtrrs()
1990                .context("failed to get MTRRs")
1991                .map_err(SaveError::Other)?;
1992
1993            // This value is ignored during restore, but may still be read by downlevel
1994            // versions. Set it to the correct hardcoded read value as a best effort for them.
1995            let crash_control = self
1996                .read_crash_msr(HV_X64_MSR_GUEST_CRASH_CTL, GuestVtl::Vtl0)
1997                .unwrap();
1998
1999            let UhProcessor {
2000                _not_send,
2001                inner:
2002                    crate::UhVpInner {
2003                        // Saved
2004                        message_queues,
2005                        // Sidecar state is reset during servicing
2006                        sidecar_exit_reason: _,
2007                        // Will be cleared by flush_async_requests above
2008                        wake_reasons: _,
2009                        // Runtime glue
2010                        waker: _,
2011                        // Topology information
2012                        vp_info: _,
2013                        cpu_index: _,
2014                    },
2015                // Saved
2016                crash_reg,
2017                // Runtime glue
2018                partition: _,
2019                idle_control: _,
2020                vmtime: _,
2021                timer: _,
2022                // This field is only used in dev/test scenarios
2023                force_exit_sidecar: _,
2024                signaled_sidecar_exit: _,
2025                // Just caching the hypervisor value, let it handle saving
2026                vtls_tlb_locked: _,
2027                // Statistic that should reset to 0 on restore
2028                kernel_returns: _,
2029                // Shared state should be handled by the backing
2030                shared: _,
2031                // The runner doesn't hold anything needing saving
2032                runner: _,
2033                backing:
2034                    HypervisorBackedX86 {
2035                        deliverability_notifications: _,
2036                        next_deliverability_notifications: _,
2037                        stats: _,
2038                        deferred_init,
2039                    },
2040                // Currently only meaningful for CVMs
2041                exit_activities: _,
2042            } = *self;
2043
2044            // Non-VTL0 VPs should never be in startup suspend, so we only need to check VTL0.
2045            // The hypervisor handles halt and idle for us.
2046            let startup_suspend = if deferred_init {
2047                Some(true)
2048            } else {
2049                let internal_activity = self
2050                    .runner
2051                    .get_vp_register(GuestVtl::Vtl0, HvX64RegisterName::InternalActivityState)
2052                    .inspect_err(|e| {
2053                        // The ioctl get_vp_register path does not tell us
2054                        // hv_status directly, so just log if it failed for any
2055                        // reason.
2056                        tracing::warn!(
2057                            error = e as &dyn std::error::Error,
2058                            "unable to query startup suspend, unable to save VTL0 startup suspend state"
2059                        );
2060                    })
2061                    .ok();
2062
2063                internal_activity
2064                    .map(|a| HvInternalActivityRegister::from(a.as_u64()).startup_suspend())
2065            };
2066
2067            let per_vtl = [GuestVtl::Vtl0, GuestVtl::Vtl1]
2068                .map(|vtl| state::ProcessorVtlSavedState {
2069                    message_queue: message_queues[vtl].save(),
2070                })
2071                .into();
2072
2073            let state = state::ProcessorSavedState {
2074                rax,
2075                rcx,
2076                rdx,
2077                rbx,
2078                cr2,
2079                rbp,
2080                rsi,
2081                rdi,
2082                r8,
2083                r9,
2084                r10,
2085                r11,
2086                r12,
2087                r13,
2088                r14,
2089                r15,
2090                fx_state: self.runner.cpu_context().fx_state.as_bytes().to_vec(),
2091                dr0: values[0].as_u64(),
2092                dr1: values[1].as_u64(),
2093                dr2: values[2].as_u64(),
2094                dr3: values[3].as_u64(),
2095                dr6: dr6_shared.then(|| values[4].as_u64()),
2096                startup_suspend,
2097                crash_reg: Some(crash_reg),
2098                crash_control,
2099                msr_mtrr_def_type,
2100                fixed_mtrrs: Some(fixed_mtrrs),
2101                variable_mtrrs: Some(variable_mtrrs),
2102                per_vtl,
2103            };
2104
2105            Ok(state)
2106        }
2107
2108        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
2109            let state::ProcessorSavedState {
2110                rax,
2111                rcx,
2112                rdx,
2113                rbx,
2114                cr2,
2115                rbp,
2116                rsi,
2117                rdi,
2118                r8,
2119                r9,
2120                r10,
2121                r11,
2122                r12,
2123                r13,
2124                r14,
2125                r15,
2126                fx_state,
2127                dr0,
2128                dr1,
2129                dr2,
2130                dr3,
2131                dr6,
2132                startup_suspend,
2133                crash_reg,
2134                crash_control: _crash_control,
2135                msr_mtrr_def_type,
2136                fixed_mtrrs,
2137                variable_mtrrs,
2138                per_vtl,
2139            } = state;
2140
2141            let dr6_shared = self.partition.hcl.dr6_shared();
2142            self.runner.cpu_context_mut().gps_no_rsp = [
2143                rax, rcx, rdx, rbx, cr2, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15,
2144            ];
2145            if fx_state.len() != self.runner.cpu_context_mut().fx_state.as_bytes().len() {
2146                return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
2147                    "invalid fpu state"
2148                )));
2149            }
2150            if dr6_shared != state.dr6.is_some() {
2151                return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
2152                    "dr6 state mismatch"
2153                )));
2154            }
2155
2156            let len = if dr6_shared {
2157                SHARED_REGISTERS.len()
2158            } else {
2159                SHARED_REGISTERS.len() - 1
2160            };
2161
2162            let values = [dr0, dr1, dr2, dr3, dr6.unwrap_or(0)];
2163            self.runner
2164                .set_vp_registers(
2165                    GuestVtl::Vtl0,
2166                    SHARED_REGISTERS[..len].iter().copied().zip(values),
2167                )
2168                .context("failed to set shared registers")
2169                .map_err(RestoreError::Other)?;
2170
2171            self.runner
2172                .cpu_context_mut()
2173                .fx_state
2174                .as_mut_bytes()
2175                .copy_from_slice(&fx_state);
2176
2177            self.crash_reg = crash_reg.unwrap_or_default();
2178
2179            // Previous versions of Underhill did not save the MTRRs.
2180            // If we get a restore state with them missing then assume they weren't
2181            // saved and don't zero out whatever the system already has.
2182            if let (Some(fixed), Some(variable)) = (fixed_mtrrs, variable_mtrrs) {
2183                let mut access = self.access_state(Vtl::Vtl0);
2184                access
2185                    .set_mtrrs(&Mtrrs {
2186                        msr_mtrr_def_type,
2187                        fixed,
2188                        variable,
2189                    })
2190                    .context("failed to set MTRRs")
2191                    .map_err(RestoreError::Other)?;
2192            }
2193
2194            for (per, vtl) in per_vtl.into_iter().zip(0u8..) {
2195                let vtl = GuestVtl::try_from(vtl)
2196                    .context("too many vtls")
2197                    .map_err(RestoreError::Other)?;
2198                self.inner.message_queues[vtl].restore(&per.message_queue);
2199            }
2200
2201            let startup_suspend = match startup_suspend {
2202                Some(true) => {
2203                    // When Underhill brings up APs during a servicing update
2204                    // via hypercall, this clears the VTL0 startup suspend
2205                    // state and makes the VP runnable. Like the cold boot path,
2206                    // we need to put the AP back into the startup suspend state
2207                    // in order to not start running the VP incorrectly.
2208                    true
2209                }
2210                None if !self.vp_index().is_bsp() => {
2211                    // Previous versions of Underhill did not save this value,
2212                    // which means the VM could be in a bad state if it's being
2213                    // serviced before VTL0 brings up APs. Log this state to
2214                    // note that.
2215                    const NAMES: [HvX64RegisterName; 4] = [
2216                        HvX64RegisterName::Rip,
2217                        HvX64RegisterName::Rflags,
2218                        HvX64RegisterName::Cr0,
2219                        HvX64RegisterName::Efer,
2220                    ];
2221                    let mut values = [FromZeros::new_zeroed(); NAMES.len()];
2222                    self.runner
2223                        // Non-VTL0 VPs should never be in startup suspend, so we only need to handle VTL0.
2224                        .get_vp_registers(GuestVtl::Vtl0, &NAMES, &mut values)
2225                        .context("failed to get VP registers for startup suspend log")
2226                        .map_err(RestoreError::Other)?;
2227                    let [rip, rflags, cr0, efer] = values.map(|reg| reg.as_u64());
2228
2229                    tracing::error!(
2230                        vp_index = self.vp_index().index(),
2231                        rip,
2232                        rflags,
2233                        cr0,
2234                        efer,
2235                        "previous version of underhill did not save startup_suspend state"
2236                    );
2237
2238                    false
2239                }
2240                Some(false) | None => false,
2241            };
2242
2243            self.backing.deferred_init = match self.set_vtl0_startup_suspend(startup_suspend) {
2244                Ok(()) => false,
2245                Err(e) => {
2246                    if startup_suspend {
2247                        tracing::warn!(
2248                            error = &e as &dyn std::error::Error,
2249                            "unable to set internal activity register, falling back to deferred init"
2250                        );
2251                    }
2252                    startup_suspend
2253                }
2254            };
2255            Ok(())
2256        }
2257    }
2258}