Skip to main content

virt_mshv_vtl/processor/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! This module contains Underhill specific functionality and implementations of required traits
5//! in order to plug into the rest of the common code.
6
7pub mod mshv;
8mod nice;
9mod vp_state;
10
11cfg_if::cfg_if! {
12    if #[cfg(guest_arch = "x86_64")] {
13        mod hardware_cvm;
14        pub mod snp;
15        pub mod tdx;
16
17        use crate::VtlCrash;
18        use bitvec::prelude::BitArray;
19        use bitvec::prelude::Lsb0;
20        use hv1_emulator::synic::ProcessorSynic;
21        use hvdef::HvRegisterCrInterceptControl;
22        use hvdef::HvX64RegisterName;
23        use virt::vp::MpState;
24        use virt::x86::MsrError;
25        use virt_support_apic::LocalApic;
26        use virt_support_x86emu::translate::TranslationRegisters;
27        use virt::vp::AccessVpState;
28        use zerocopy::IntoBytes;
29    } else if #[cfg(guest_arch = "aarch64")] {
30        pub mod cca;
31        use hv1_hypercall::Arm64RegisterState;
32        use hvdef::HvArm64RegisterName;
33        use virt_support_aarch64emu::translate::TranslationRegisters;
34        use hvdef::HvRegisterCrInterceptControl;
35        use hv1_emulator::synic::ProcessorSynic;
36    } else {
37        compile_error!("unsupported guest architecture");
38    }
39}
40
41use super::Error;
42use super::UhPartitionInner;
43use super::UhVpInner;
44use crate::ExitActivity;
45use crate::GuestVtl;
46use crate::TlbFlushLockAccess;
47use crate::WakeReason;
48use cvm_tracing::CVM_ALLOWED;
49use cvm_tracing::CVM_CONFIDENTIAL;
50use hcl::ioctl::Hcl;
51use hcl::ioctl::ProcessorRunner;
52use hv1_emulator::message_queues::MessageQueues;
53use hv1_hypercall::HvRepResult;
54use hv1_structs::ProcessorSet;
55use hv1_structs::VtlArray;
56use hvdef::HvError;
57use hvdef::HvMessage;
58use hvdef::HvSynicSint;
59use hvdef::NUM_SINTS;
60use hvdef::Vtl;
61use inspect::Inspect;
62use inspect::InspectMut;
63use pal::unix::affinity;
64use pal::unix::affinity::CpuSet;
65use pal_async::driver::Driver;
66use pal_async::driver::PollImpl;
67use pal_async::timer::PollTimer;
68use pal_uring::IdleControl;
69use private::BackingPrivate;
70use std::convert::Infallible;
71use std::future::poll_fn;
72use std::marker::PhantomData;
73use std::sync::Arc;
74use std::sync::atomic::Ordering;
75use std::task::Poll;
76use virt::EmulatorMonitorSupport;
77use virt::Processor;
78use virt::StopVp;
79use virt::VpHaltReason;
80use virt::VpIndex;
81use virt::io::CpuIo;
82use vm_topology::processor::TargetVpInfo;
83use vmcore::vmtime::VmTimeAccess;
84
85/// An object to run lower VTLs and to access processor state.
86///
87/// This is not [`Send`] and can only be instantiated from
88/// [`crate::UhProcessorBox::bind_processor`]. This ensures that it can only be used
89/// from a thread that is affinitized to the VP, since it is only possible to
90/// access lower VTL processor state from the same processor.
91#[derive(InspectMut)]
92#[inspect(extra = "UhProcessor::inspect_extra", bound = "T: Backing")]
93pub struct UhProcessor<'a, T: Backing> {
94    _not_send: PhantomData<*mut ()>,
95
96    #[inspect(flatten)]
97    inner: &'a UhVpInner,
98    #[inspect(skip)]
99    partition: &'a UhPartitionInner,
100    #[inspect(skip)]
101    idle_control: Option<&'a mut IdleControl>,
102    kernel_returns: u64,
103    #[inspect(hex, iter_by_index)]
104    crash_reg: [u64; hvdef::HV_X64_GUEST_CRASH_PARAMETER_MSRS],
105    vmtime: VmTimeAccess,
106    #[inspect(skip)]
107    timer: PollImpl<dyn PollTimer>,
108    #[inspect(mut)]
109    force_exit_sidecar: bool,
110    signaled_sidecar_exit: bool,
111    /// The VTLs on this VP that are currently locked, per requesting VTL.
112    vtls_tlb_locked: VtlsTlbLocked,
113    #[inspect(skip)]
114    shared: &'a T::Shared,
115    #[inspect(hex, with = "|x| inspect::iter_by_index(x.iter()).map_value(|a| a.0)")]
116    exit_activities: VtlArray<ExitActivity, 2>,
117
118    // Put the runner and backing at the end so that monomorphisms of functions
119    // that don't access backing-specific state are more likely to be folded
120    // together by the compiler.
121    #[inspect(skip)]
122    runner: ProcessorRunner<'a, T::HclBacking<'a>>,
123    #[inspect(mut, safe)]
124    backing: T,
125}
126
127#[derive(Inspect)]
128struct VtlsTlbLocked {
129    // vtl0: VtlArray<bool, 0>,
130    vtl1: VtlArray<bool, 1>,
131    vtl2: VtlArray<bool, 2>,
132}
133
134impl VtlsTlbLocked {
135    fn get(&self, requesting_vtl: Vtl, target_vtl: GuestVtl) -> bool {
136        match requesting_vtl {
137            Vtl::Vtl0 => unreachable!(),
138            Vtl::Vtl1 => self.vtl1[target_vtl],
139            Vtl::Vtl2 => self.vtl2[target_vtl],
140        }
141    }
142
143    fn set(&mut self, requesting_vtl: Vtl, target_vtl: GuestVtl, value: bool) {
144        match requesting_vtl {
145            Vtl::Vtl0 => unreachable!(),
146            Vtl::Vtl1 => self.vtl1[target_vtl] = value,
147            Vtl::Vtl2 => self.vtl2[target_vtl] = value,
148        }
149    }
150
151    fn fill(&mut self, requesting_vtl: Vtl, value: bool) {
152        match requesting_vtl {
153            Vtl::Vtl0 => unreachable!(),
154            Vtl::Vtl1 => self.vtl1.fill(value),
155            Vtl::Vtl2 => self.vtl2.fill(value),
156        }
157    }
158}
159
160#[cfg(guest_arch = "x86_64")]
161#[derive(Inspect)]
162pub(crate) struct LapicState {
163    #[inspect(safe)]
164    lapic: LocalApic,
165    activity: MpState,
166    nmi_pending: bool,
167}
168
169#[cfg(guest_arch = "x86_64")]
170impl LapicState {
171    pub fn new(lapic: LocalApic, activity: MpState) -> Self {
172        Self {
173            lapic,
174            activity,
175            nmi_pending: false,
176        }
177    }
178}
179
180struct BackingParams<'a, 'b, T: Backing> {
181    partition: &'a UhPartitionInner,
182    vp_info: &'a TargetVpInfo,
183    runner: &'a mut ProcessorRunner<'b, T::HclBacking<'b>>,
184}
185
186mod private {
187    use super::BackingParams;
188    use super::vp_state;
189    use crate::BackingShared;
190    use crate::Error;
191    use crate::GuestVtl;
192    use crate::processor::UhProcessor;
193    use hv1_emulator::hv::ProcessorVtlHv;
194    use hv1_structs::VtlArray;
195    use inspect::InspectMut;
196    use std::future::Future;
197    use virt::StopVp;
198    use virt::VpHaltReason;
199    use virt::io::CpuIo;
200    use virt::vp::AccessVpState;
201
202    #[expect(private_interfaces)]
203    pub trait BackingPrivate: 'static + Sized + InspectMut + Sized {
204        type HclBacking<'b>: hcl::ioctl::Backing<'b>;
205        type EmulationCache;
206        type Shared;
207
208        fn shared(shared: &BackingShared) -> &Self::Shared;
209
210        fn new(params: BackingParams<'_, '_, Self>, shared: &Self::Shared) -> Result<Self, Error>;
211
212        type StateAccess<'p, 'a>: AccessVpState<Error = vp_state::Error>
213        where
214            Self: 'a + 'p,
215            'p: 'a;
216
217        fn init(this: &mut UhProcessor<'_, Self>);
218
219        fn access_vp_state<'a, 'p>(
220            this: &'a mut UhProcessor<'p, Self>,
221            vtl: GuestVtl,
222        ) -> Self::StateAccess<'p, 'a>;
223
224        /// Called before the first `run_vp` call to allow the backing to
225        /// perform any pre-run tasks. Must be idempotent--may be called again
226        /// before some subsequent `run_vp` calls.
227        fn pre_run_vp(_this: &mut UhProcessor<'_, Self>) {}
228
229        fn run_vp(
230            this: &mut UhProcessor<'_, Self>,
231            dev: &impl CpuIo,
232            stop: &mut StopVp<'_>,
233        ) -> impl Future<Output = Result<(), VpHaltReason>>;
234
235        /// Process any pending interrupts. Returns true if reprocessing is required.
236        fn process_interrupts(
237            this: &mut UhProcessor<'_, Self>,
238            scan_irr: VtlArray<bool, 2>,
239            first_scan_irr: &mut bool,
240            dev: &impl CpuIo,
241        ) -> bool;
242
243        /// Process any pending APIC work.
244        fn poll_apic(this: &mut UhProcessor<'_, Self>, vtl: GuestVtl, scan_irr: bool);
245
246        /// Requests the VP to exit when an external interrupt is ready to be
247        /// delivered.
248        ///
249        /// Only used when the hypervisor implements the APIC.
250        fn request_extint_readiness(this: &mut UhProcessor<'_, Self>);
251
252        /// Requests the VP to exit when any of the specified SINTs have a free
253        /// message slot.
254        ///
255        /// This is used for hypervisor-managed and untrusted SINTs.
256        fn request_untrusted_sint_readiness(this: &mut UhProcessor<'_, Self>, sints: u16);
257
258        fn handle_vp_start_enable_vtl_wake(_this: &mut UhProcessor<'_, Self>, _vtl: GuestVtl);
259
260        fn inspect_extra(_this: &mut UhProcessor<'_, Self>, _resp: &mut inspect::Response<'_>) {}
261
262        fn hv(&self, vtl: GuestVtl) -> Option<&ProcessorVtlHv>;
263        fn hv_mut(&mut self, vtl: GuestVtl) -> Option<&mut ProcessorVtlHv>;
264
265        fn vtl1_inspectable(this: &UhProcessor<'_, Self>) -> bool;
266    }
267}
268
269/// Processor backing.
270pub trait Backing: BackingPrivate {}
271
272impl<T: BackingPrivate> Backing for T {}
273
274#[cfg_attr(not(guest_arch = "x86_64"), expect(dead_code))]
275pub(crate) struct BackingSharedParams<'a> {
276    pub cvm_state: Option<crate::UhCvmPartitionState>,
277    #[cfg(guest_arch = "x86_64")]
278    pub cpuid: &'a virt::CpuidLeafSet,
279    pub hcl: &'a Hcl,
280    pub guest_vsm_available: bool,
281    pub lower_vtl_timer_virt_available: bool,
282}
283
284/// Supported intercept message types.
285#[cfg_attr(guest_arch = "aarch64", expect(dead_code))]
286enum InterceptMessageType {
287    #[cfg(guest_arch = "x86_64")]
288    Register {
289        reg: HvX64RegisterName,
290        value: u64,
291    },
292    Msr {
293        msr: u32,
294    },
295    #[cfg(guest_arch = "x86_64")]
296    IoPort {
297        port_number: u16,
298        access_size: u8,
299        string_access: bool,
300        rep_access: bool,
301    },
302}
303
304/// Per-arch state required to generate an intercept message.
305#[cfg_attr(guest_arch = "aarch64", expect(dead_code))]
306pub(crate) struct InterceptMessageState {
307    instruction_length_and_cr8: u8,
308    cpl: u8,
309    efer_lma: bool,
310    cs: hvdef::HvX64SegmentRegister,
311    rip: u64,
312    rflags: u64,
313    rax: u64,
314    rdx: u64,
315    rcx: u64,
316    rsi: u64,
317    rdi: u64,
318    optional: Option<InterceptMessageOptionalState>,
319}
320
321#[cfg_attr(guest_arch = "aarch64", expect(dead_code))]
322/// Additional per-arch state required to generate an intercept message. Used
323/// for state that is not common across the intercept message types and that
324/// might be slower to retrieve on certain architectures.
325struct InterceptMessageOptionalState {
326    ds: hvdef::HvX64SegmentRegister,
327    es: hvdef::HvX64SegmentRegister,
328}
329
330impl InterceptMessageType {
331    #[cfg(guest_arch = "x86_64")]
332    fn generate_hv_message(
333        &self,
334        vp_index: VpIndex,
335        vtl: GuestVtl,
336        state: InterceptMessageState,
337        is_read: bool,
338    ) -> HvMessage {
339        let header = hvdef::HvX64InterceptMessageHeader {
340            vp_index: vp_index.index(),
341            instruction_length_and_cr8: state.instruction_length_and_cr8,
342            intercept_access_type: if is_read {
343                hvdef::HvInterceptAccessType::READ
344            } else {
345                hvdef::HvInterceptAccessType::WRITE
346            },
347            execution_state: hvdef::HvX64VpExecutionState::new()
348                .with_cpl(state.cpl)
349                .with_vtl(vtl.into())
350                .with_efer_lma(state.efer_lma),
351            cs_segment: state.cs,
352            rip: state.rip,
353            rflags: state.rflags,
354        };
355        match self {
356            InterceptMessageType::Register { reg, value } => {
357                let intercept_message = hvdef::HvX64RegisterInterceptMessage {
358                    header,
359                    flags: hvdef::HvX64RegisterInterceptMessageFlags::new(),
360                    rsvd: 0,
361                    rsvd2: 0,
362                    register_name: *reg,
363                    access_info: hvdef::HvX64RegisterAccessInfo::new_source_value(
364                        hvdef::HvRegisterValue::from(*value),
365                    ),
366                };
367                HvMessage::new(
368                    hvdef::HvMessageType::HvMessageTypeRegisterIntercept,
369                    0,
370                    intercept_message.as_bytes(),
371                )
372            }
373            InterceptMessageType::Msr { msr } => {
374                let intercept_message = hvdef::HvX64MsrInterceptMessage {
375                    header,
376                    msr_number: *msr,
377                    rax: state.rax,
378                    rdx: state.rdx,
379                    reserved: 0,
380                };
381
382                HvMessage::new(
383                    hvdef::HvMessageType::HvMessageTypeMsrIntercept,
384                    0,
385                    intercept_message.as_bytes(),
386                )
387            }
388            InterceptMessageType::IoPort {
389                port_number,
390                access_size,
391                string_access,
392                rep_access,
393            } => {
394                let access_info =
395                    hvdef::HvX64IoPortAccessInfo::new(*access_size, *string_access, *rep_access);
396                let intercept_message = hvdef::HvX64IoPortInterceptMessage {
397                    header,
398                    port_number: *port_number,
399                    access_info,
400                    instruction_byte_count: 0,
401                    reserved: 0,
402                    rax: state.rax,
403                    instruction_bytes: [0u8; 16],
404                    ds_segment: state.optional.as_ref().unwrap().ds,
405                    es_segment: state.optional.as_ref().unwrap().es,
406                    rcx: state.rcx,
407                    rsi: state.rsi,
408                    rdi: state.rdi,
409                };
410
411                HvMessage::new(
412                    hvdef::HvMessageType::HvMessageTypeX64IoPortIntercept,
413                    0,
414                    intercept_message.as_bytes(),
415                )
416            }
417        }
418    }
419}
420
421/// Trait for processor backings that have hardware isolation support.
422#[cfg_attr(not(guest_arch = "x86_64"), expect(dead_code))]
423pub(crate) trait HardwareIsolatedBacking: Backing {
424    /// Gets CVM specific VP state.
425    fn cvm_state(&self) -> &crate::UhCvmVpState;
426    /// Gets CVM specific VP state.
427    fn cvm_state_mut(&mut self) -> &mut crate::UhCvmVpState;
428    /// Gets CVM specific partition state.
429    fn cvm_partition_state(shared: &Self::Shared) -> &crate::UhCvmPartitionState;
430    /// Gets a struct that can be used to interact with TLB flushing and
431    /// locking.
432    ///
433    /// If a `vp_index` is provided, it will be used to cause the specified VP
434    /// to wait for all TLB locks to be released before returning to a lower VTL.
435    //
436    // FUTURE: This probably shouldn't be optional, but right now MNF doesn't know
437    // what VP it's set from and probably doesn't need it?
438    fn tlb_flush_lock_access<'a>(
439        vp_index: Option<VpIndex>,
440        partition: &'a UhPartitionInner,
441        shared: &'a Self::Shared,
442    ) -> impl TlbFlushLockAccess + 'a;
443    /// Copies shared registers (per VSM TLFS spec) from the source VTL to
444    /// the target VTL that will become active, and set the exit vtl
445    fn switch_vtl(this: &mut UhProcessor<'_, Self>, source_vtl: GuestVtl, target_vtl: GuestVtl);
446    /// Gets registers needed for gva to gpa translation
447    fn translation_registers(
448        &self,
449        this: &UhProcessor<'_, Self>,
450        vtl: GuestVtl,
451    ) -> TranslationRegisters;
452    /// Vector of the event that is pending injection into the guest state, if
453    /// valid.
454    fn pending_event_vector(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> Option<u8>;
455    /// Check if an interrupt of appropriate priority, or an NMI, is pending for
456    /// the given VTL. `check_rflags` specifies whether RFLAGS.IF should be checked.
457    fn is_interrupt_pending(
458        this: &mut UhProcessor<'_, Self>,
459        vtl: GuestVtl,
460        check_rflags: bool,
461        dev: &impl CpuIo,
462    ) -> bool;
463    /// Sets the pending exception for the guest state.
464    ///
465    /// Note that this will overwrite any existing pending exception. It will
466    /// not handle merging any existing pending exception with the new one.
467    fn set_pending_exception(
468        this: &mut UhProcessor<'_, Self>,
469        vtl: GuestVtl,
470        event: hvdef::HvX64PendingExceptionEvent,
471    );
472
473    fn intercept_message_state(
474        this: &UhProcessor<'_, Self>,
475        vtl: GuestVtl,
476        include_optional_state: bool,
477    ) -> InterceptMessageState;
478
479    /// Individual register for CPUID and crx intercept handling, since
480    /// AccessVpState::registers is relatively slow on TDX.
481    fn cr0(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> u64;
482    fn cr4(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> u64;
483
484    fn cr_intercept_registration(
485        this: &mut UhProcessor<'_, Self>,
486        intercept_control: HvRegisterCrInterceptControl,
487    );
488
489    fn untrusted_synic_mut(&mut self) -> Option<&mut ProcessorSynic>;
490
491    /// Updates the timer deadline.
492    fn update_deadline(this: &mut UhProcessor<'_, Self>, ref_time_now: u64, next_ref_time: u64);
493
494    /// Clears any pending timer deadline.
495    fn clear_deadline(this: &mut UhProcessor<'_, Self>);
496}
497
498#[cfg_attr(guest_arch = "aarch64", expect(dead_code))]
499#[derive(Inspect, Debug)]
500#[inspect(tag = "reason")]
501pub(crate) enum SidecarExitReason {
502    #[inspect(transparent)]
503    Exit(SidecarRemoveExit),
504    #[inspect(transparent)]
505    TaskRequest(Arc<str>),
506    ManualRequest,
507}
508
509#[cfg_attr(guest_arch = "aarch64", expect(dead_code))]
510#[derive(Inspect, Debug)]
511#[inspect(tag = "exit")]
512pub(crate) enum SidecarRemoveExit {
513    Msr {
514        #[inspect(hex)]
515        msr: u32,
516        value: Option<u64>,
517    },
518    Io {
519        #[inspect(hex)]
520        port: u16,
521        write: bool,
522    },
523    Mmio {
524        #[inspect(hex)]
525        gpa: u64,
526        write: bool,
527    },
528    Hypercall {
529        #[inspect(debug)]
530        code: hvdef::HypercallCode,
531    },
532    Cpuid {
533        #[inspect(hex)]
534        leaf: u32,
535        #[inspect(hex)]
536        subleaf: u32,
537    },
538    Hypervisor {
539        #[inspect(debug)]
540        message: hvdef::HvMessageType,
541    },
542}
543
544impl UhVpInner {
545    // Create a new vp's state.
546    pub fn new(cpu_index: u32, vp_info: TargetVpInfo) -> Self {
547        Self {
548            wake_reasons: Default::default(),
549            message_queues: VtlArray::from_fn(|_| MessageQueues::new()),
550            waker: Default::default(),
551            cpu_index,
552            vp_info,
553            sidecar_exit_reason: Default::default(),
554        }
555    }
556
557    /// Queues a message for sending, optionally alerting the hypervisor if the queue is empty.
558    pub fn post_message(&self, vtl: GuestVtl, sint: u8, message: &HvMessage) {
559        if self.message_queues[vtl].enqueue_message(sint, message) {
560            self.wake(vtl, WakeReason::MESSAGE_QUEUES);
561        }
562    }
563
564    pub fn wake(&self, vtl: GuestVtl, reason: WakeReason) {
565        let reason = u64::from(reason.0) << (vtl as u8 * 32);
566        if self.wake_reasons.fetch_or(reason, Ordering::Release) & reason == 0 {
567            if let Some(waker) = &*self.waker.read() {
568                waker.wake_by_ref();
569            }
570        }
571    }
572
573    pub fn wake_vtl2(&self) {
574        if let Some(waker) = &*self.waker.read() {
575            waker.wake_by_ref();
576        }
577    }
578
579    pub fn set_sidecar_exit_reason(&self, reason: SidecarExitReason) {
580        self.sidecar_exit_reason.lock().get_or_insert_with(|| {
581            tracing::info!(CVM_ALLOWED, "sidecar exit");
582            tracing::info!(CVM_CONFIDENTIAL, ?reason, "sidecar exit");
583            reason
584        });
585    }
586}
587
588impl<T: Backing> UhProcessor<'_, T> {
589    fn inspect_extra(&mut self, resp: &mut inspect::Response<'_>) {
590        resp.child("stats", |req| {
591            let mut resp = req.respond();
592            // Get all the VP stats and just grab this VP's. Note that the
593            // kernel indexes these by Linux CPU number, not VP index.
594            match hcl::stats::vp_stats() {
595                Err(err) => {
596                    resp.field("error", inspect::AsDebug(&err));
597                }
598                // The kernel omits offline CPUs, so report nothing rather than
599                // a misleading zero.
600                Ok(stats) => match stats
601                    .get(self.inner.cpu_index as usize)
602                    .and_then(Option::as_ref)
603                {
604                    None => {
605                        resp.field("error", "no stats for this cpu");
606                    }
607                    Some(stats) => {
608                        resp.counter("vtl_transitions", stats.vtl_transitions)
609                            .counter(
610                                "spurious_exits",
611                                stats.vtl_transitions.saturating_sub(self.kernel_returns),
612                            );
613                    }
614                },
615            }
616        })
617        .field(
618            "last_enter_modes",
619            self.runner
620                .enter_mode()
621                .map(|&mut v| inspect::AsHex(u8::from(v))),
622        )
623        .field("sidecar", self.runner.is_sidecar())
624        .field(
625            "sidecar_base_cpu",
626            self.partition.hcl.sidecar_base_cpu(self.vp_index().index()),
627        );
628
629        T::inspect_extra(self, resp);
630    }
631
632    #[cfg(guest_arch = "x86_64")]
633    fn handle_debug_exception(
634        &mut self,
635        dev: &impl CpuIo,
636        vtl: GuestVtl,
637    ) -> Result<(), VpHaltReason> {
638        // FUTURE: Underhill does not yet support VTL1 so this is only tested with VTL0.
639        if vtl == GuestVtl::Vtl0 {
640            let debug_regs: virt::x86::vp::DebugRegisters = self
641                .access_state(Vtl::Vtl0)
642                .debug_regs()
643                .expect("register query should not fail");
644
645            let dr = [
646                debug_regs.dr0,
647                debug_regs.dr1,
648                debug_regs.dr2,
649                debug_regs.dr3,
650            ];
651
652            if debug_regs.dr6 & x86defs::DR6_SINGLE_STEP != 0 {
653                return Err(VpHaltReason::SingleStep);
654            }
655
656            // Last four bits of DR6 indicate which breakpoint was triggered.
657            const BREAKPOINT_INDEX_OFFSET: usize = 4;
658            let i = debug_regs.dr6.trailing_zeros() as usize;
659            if i >= BREAKPOINT_INDEX_OFFSET {
660                // Received a debug exception not triggered by a breakpoint or single step.
661                return Err(dev.fatal_error(
662                    UnexpectedDebugException {
663                        dr6: debug_regs.dr6,
664                    }
665                    .into(),
666                ));
667            }
668            let bp = virt::x86::HardwareBreakpoint::from_dr7(debug_regs.dr7, dr[i], i);
669
670            return Err(VpHaltReason::HwBreak(bp));
671        }
672
673        panic!("unexpected debug exception in VTL {:?}", vtl);
674    }
675}
676
677#[cfg(guest_arch = "x86_64")]
678#[derive(Debug, Error)]
679#[error("unexpected debug exception with dr6 value {dr6:#x}")]
680struct UnexpectedDebugException {
681    dr6: u64,
682}
683
684impl<'p, T: Backing> Processor for UhProcessor<'p, T> {
685    type StateAccess<'a>
686        = T::StateAccess<'p, 'a>
687    where
688        Self: 'a;
689
690    #[cfg(guest_arch = "aarch64")]
691    fn set_debug_state(
692        &mut self,
693        _vtl: Vtl,
694        _state: Option<&virt::x86::DebugState>,
695    ) -> Result<(), <T::StateAccess<'p, '_> as virt::vp::AccessVpState>::Error> {
696        unimplemented!()
697    }
698
699    #[cfg(guest_arch = "x86_64")]
700    fn set_debug_state(
701        &mut self,
702        vtl: Vtl,
703        state: Option<&virt::x86::DebugState>,
704    ) -> Result<(), <T::StateAccess<'p, '_> as AccessVpState>::Error> {
705        // FUTURE: Underhill does not yet support VTL1 so this is only tested with VTL0.
706        if vtl == Vtl::Vtl0 {
707            let mut db: [u64; 4] = [0; 4];
708            let mut access_state = self.access_state(vtl);
709            let mut registers = access_state.registers()?;
710            let mut rflags = x86defs::RFlags::from(registers.rflags);
711            let mut dr7: u64 = 0;
712
713            if let Some(state) = state {
714                rflags.set_trap(state.single_step);
715                for (i, bp) in state.breakpoints.iter().enumerate() {
716                    if let Some(bp) = bp {
717                        db[i] = bp.address;
718                        dr7 |= bp.dr7_bits(i);
719                    }
720                }
721            }
722
723            let debug_registers = virt::x86::vp::DebugRegisters {
724                dr0: db[0],
725                dr1: db[1],
726                dr2: db[2],
727                dr3: db[3],
728                dr6: 0,
729                dr7,
730            };
731            access_state.set_debug_regs(&debug_registers)?;
732
733            registers.rflags = rflags.into();
734            access_state.set_registers(&registers)?;
735            return Ok(());
736        }
737
738        panic!("unexpected set debug state in VTL {:?}", vtl);
739    }
740
741    async fn run_vp(
742        &mut self,
743        mut stop: StopVp<'_>,
744        dev: &impl CpuIo,
745    ) -> Result<Infallible, VpHaltReason> {
746        T::pre_run_vp(self);
747
748        if self.runner.is_sidecar() {
749            if self.force_exit_sidecar && !self.signaled_sidecar_exit {
750                self.inner
751                    .set_sidecar_exit_reason(SidecarExitReason::ManualRequest);
752                self.signaled_sidecar_exit = true;
753                return Err(VpHaltReason::Cancel);
754            }
755        } else {
756            let mut current = Default::default();
757            affinity::get_current_thread_affinity(&mut current).unwrap();
758            assert_eq!(&current, CpuSet::new().set(self.inner.cpu_index));
759
760            // Lower the priority of this VP thread so that the VM does not return
761            // to VTL0 while there is still outstanding VTL2 work to do.
762            nice::nice(1);
763        }
764
765        let mut last_waker = None;
766
767        // Force deliverability notifications to be reevaluated.
768        let vtl0_wakes = WakeReason::new()
769            .with_message_queues(true)
770            .with_intcon(true);
771        let vtl1_wakes = WakeReason::new().with_message_queues(true);
772        self.inner.wake_reasons.fetch_or(
773            ((vtl1_wakes.0 as u64) << 32) | (vtl0_wakes.0 as u64),
774            Ordering::Relaxed,
775        );
776
777        let mut first_scan_irr = true;
778
779        loop {
780            // Process VP activity and wait for the VP to be ready.
781            poll_fn(|cx| {
782                loop {
783                    stop.check()?;
784
785                    // Clear the run VP cancel request.
786                    self.runner.clear_cancel();
787
788                    // Cancel any pending timer.
789                    self.vmtime.cancel_timeout();
790
791                    // Ensure the waker is set.
792                    if !last_waker
793                        .as_ref()
794                        .is_some_and(|waker| cx.waker().will_wake(waker))
795                    {
796                        last_waker = Some(cx.waker().clone());
797                        self.inner.waker.write().clone_from(&last_waker);
798                    }
799
800                    // Process wakes.
801                    let scan_irr = if self.inner.wake_reasons.load(Ordering::Relaxed) != 0 {
802                        self.handle_wake()
803                    } else {
804                        [false, false].into()
805                    };
806
807                    if T::process_interrupts(self, scan_irr, &mut first_scan_irr, dev) {
808                        continue;
809                    }
810
811                    // Arm the timer.
812                    if let Some(timeout) = self.vmtime.get_timeout() {
813                        let deadline = self.vmtime.host_time(timeout);
814                        if self.timer.poll_timer(cx, deadline).is_ready() {
815                            continue;
816                        }
817                    }
818
819                    return <Result<_, VpHaltReason>>::Ok(()).into();
820                }
821            })
822            .await?;
823
824            // Yield if the thread pool is not ready to block.
825            if let Some(idle_control) = &mut self.idle_control {
826                if !idle_control.pre_block() {
827                    yield_now().await;
828                    continue;
829                }
830            }
831
832            if let Some(mode) = self.runner.enter_mode() {
833                *mode = self
834                    .partition
835                    .enter_modes_atomic
836                    .load(Ordering::Relaxed)
837                    .into();
838            }
839
840            // Quiesce RCU before running the VP to avoid having to synchronize with
841            // this CPU during memory protection updates.
842            minircu::global().quiesce();
843
844            T::run_vp(self, dev, &mut stop).await?;
845            self.kernel_returns += 1;
846        }
847    }
848
849    fn flush_async_requests(&mut self) {
850        if self.inner.wake_reasons.load(Ordering::Relaxed) != 0 {
851            let scan_irr = self.handle_wake();
852            for vtl in [GuestVtl::Vtl1, GuestVtl::Vtl0] {
853                if scan_irr[vtl] {
854                    T::poll_apic(self, vtl, true);
855                }
856            }
857        }
858        self.runner.flush_deferred_state();
859    }
860
861    fn access_state(&mut self, vtl: Vtl) -> Self::StateAccess<'_> {
862        T::access_vp_state(self, vtl.try_into().unwrap())
863    }
864
865    fn vtl_inspectable(&self, vtl: Vtl) -> bool {
866        match vtl {
867            Vtl::Vtl0 => true,
868            Vtl::Vtl1 => T::vtl1_inspectable(self),
869            Vtl::Vtl2 => false,
870        }
871    }
872}
873
874impl<'a, T: Backing> UhProcessor<'a, T> {
875    pub(super) fn new(
876        driver: &impl Driver,
877        partition: &'a UhPartitionInner,
878        vp_info: TargetVpInfo,
879        idle_control: Option<&'a mut IdleControl>,
880    ) -> Result<Self, Error> {
881        let inner = partition.vp(vp_info.base.vp_index).unwrap();
882        let mut runner = partition
883            .hcl
884            .runner(inner.vp_index().index(), idle_control.is_none())
885            .unwrap();
886
887        let backing_shared = T::shared(&partition.backing_shared);
888
889        let backing = T::new(
890            BackingParams {
891                partition,
892                vp_info: &vp_info,
893                runner: &mut runner,
894            },
895            backing_shared,
896        )?;
897
898        let mut vp = Self {
899            partition,
900            inner,
901            runner,
902            idle_control,
903            kernel_returns: 0,
904            crash_reg: [0; hvdef::HV_X64_GUEST_CRASH_PARAMETER_MSRS],
905            _not_send: PhantomData,
906            backing,
907            shared: backing_shared,
908            vmtime: partition
909                .vmtime
910                .access(format!("vp-{}", vp_info.base.vp_index.index())),
911            timer: driver.new_dyn_timer(),
912            force_exit_sidecar: false,
913            signaled_sidecar_exit: false,
914            vtls_tlb_locked: VtlsTlbLocked {
915                vtl1: VtlArray::new(false),
916                vtl2: VtlArray::new(false),
917            },
918            exit_activities: Default::default(),
919        };
920
921        T::init(&mut vp);
922
923        Ok(vp)
924    }
925
926    /// Returns true if the interrupt controller has work to do.
927    fn handle_wake(&mut self) -> VtlArray<bool, 2> {
928        let wake_reasons_raw = self.inner.wake_reasons.swap(0, Ordering::SeqCst);
929        let wake_reasons_vtl: [WakeReason; 2] = zerocopy::transmute!(wake_reasons_raw);
930        for (vtl, wake_reasons) in [
931            (GuestVtl::Vtl1, wake_reasons_vtl[1]),
932            (GuestVtl::Vtl0, wake_reasons_vtl[0]),
933        ] {
934            if wake_reasons.message_queues() {
935                let pending_sints = self.inner.message_queues[vtl].pending_sints();
936                if pending_sints != 0 {
937                    // Set SINT interest.
938                    let pending_sints = self.inner.message_queues[vtl].pending_sints();
939                    let mut masked_sints = 0;
940
941                    // Determine which of the pending sints are masked.
942                    for sint in 0..NUM_SINTS as u8 {
943                        if pending_sints & (1 << sint) == 0 {
944                            continue;
945                        }
946                        let sint_msr = if let Some(hv) = self.backing.hv(vtl).as_ref() {
947                            hv.synic.sint(sint)
948                        } else {
949                            #[cfg(guest_arch = "x86_64")]
950                            let sint_reg =
951                                HvX64RegisterName(HvX64RegisterName::Sint0.0 + sint as u32);
952                            #[cfg(guest_arch = "aarch64")]
953                            let sint_reg =
954                                HvArm64RegisterName(HvArm64RegisterName::Sint0.0 + sint as u32);
955                            self.runner.get_vp_register(vtl, sint_reg).unwrap().as_u64()
956                        };
957                        masked_sints |= (HvSynicSint::from(sint_msr).masked() as u16) << sint;
958                    }
959
960                    // Drain the queues for all masked SINTs.
961                    self.inner.message_queues[vtl].post_pending_messages(masked_sints, |_, _| {
962                        Err(HvError::InvalidSynicState)
963                    });
964
965                    self.request_sint_notifications(vtl, pending_sints & !masked_sints);
966                }
967            }
968
969            if wake_reasons.extint() {
970                T::request_extint_readiness(self);
971            }
972
973            #[cfg(guest_arch = "x86_64")]
974            if wake_reasons.hv_start_enable_vtl_vp() {
975                T::handle_vp_start_enable_vtl_wake(self, vtl);
976            }
977
978            #[cfg(guest_arch = "x86_64")]
979            if wake_reasons.update_proxy_irr_filter() {
980                // update `proxy_irr_blocked` filter
981                debug_assert!(self.partition.isolation.is_hardware_isolated());
982                self.update_proxy_irr_filter(vtl);
983            }
984        }
985
986        wake_reasons_vtl.map(|w| w.intcon()).into()
987    }
988
989    fn request_sint_notifications(&mut self, vtl: GuestVtl, sints: u16) {
990        if sints == 0 {
991            return;
992        }
993
994        // Send the SINT notifications to the local synic for non-proxied SINTs.
995        let untrusted_sints = if let Some(hv) = self.backing.hv_mut(vtl).as_mut() {
996            let proxied_sints = hv.synic.proxied_sints();
997            hv.synic.request_sint_readiness(sints & !proxied_sints);
998            proxied_sints
999        } else {
1000            !0
1001        };
1002
1003        if sints & untrusted_sints != 0 {
1004            assert_eq!(vtl, GuestVtl::Vtl0);
1005            T::request_untrusted_sint_readiness(self, sints & untrusted_sints);
1006        }
1007    }
1008
1009    fn vp_index(&self) -> VpIndex {
1010        self.inner.vp_index()
1011    }
1012
1013    #[cfg(guest_arch = "x86_64")]
1014    fn write_crash_msr(&mut self, msr: u32, value: u64, vtl: GuestVtl) -> Result<(), MsrError> {
1015        match msr {
1016            hvdef::HV_X64_MSR_GUEST_CRASH_CTL => {
1017                let crash = VtlCrash {
1018                    vp_index: self.vp_index(),
1019                    last_vtl: vtl,
1020                    control: hvdef::GuestCrashCtl::from(value),
1021                    parameters: self.crash_reg,
1022                };
1023                tracelimit::warn_ratelimited!(
1024                    CVM_ALLOWED,
1025                    ?crash,
1026                    "Guest has reported system crash"
1027                );
1028
1029                if crash.control.crash_message() {
1030                    let message_gpa = crash.parameters[3];
1031                    let message_size = std::cmp::min(crash.parameters[4], hvdef::HV_PAGE_SIZE);
1032                    let mut message = vec![0; message_size as usize];
1033                    match self.partition.gm[vtl].read_at(message_gpa, &mut message) {
1034                        Ok(()) => {
1035                            let message = String::from_utf8_lossy(&message).into_owned();
1036                            tracelimit::warn_ratelimited!(
1037                                CVM_CONFIDENTIAL,
1038                                message,
1039                                "Guest has reported a system crash message"
1040                            );
1041                        }
1042                        Err(e) => {
1043                            tracelimit::warn_ratelimited!(
1044                                CVM_ALLOWED,
1045                                ?e,
1046                                "Failed to read crash message"
1047                            );
1048                        }
1049                    }
1050                }
1051
1052                self.partition.crash_notification_send.send(crash);
1053            }
1054            hvdef::HV_X64_MSR_GUEST_CRASH_P0
1055            | hvdef::HV_X64_MSR_GUEST_CRASH_P1
1056            | hvdef::HV_X64_MSR_GUEST_CRASH_P2
1057            | hvdef::HV_X64_MSR_GUEST_CRASH_P3
1058            | hvdef::HV_X64_MSR_GUEST_CRASH_P4 => {
1059                self.crash_reg[(msr - hvdef::HV_X64_MSR_GUEST_CRASH_P0) as usize] = value;
1060            }
1061            _ => return Err(MsrError::Unknown),
1062        }
1063        Ok(())
1064    }
1065
1066    #[cfg(guest_arch = "x86_64")]
1067    fn read_crash_msr(&self, msr: u32, _vtl: GuestVtl) -> Result<u64, MsrError> {
1068        let v = match msr {
1069            // Reads of CRASH_CTL report our supported capabilities, not any
1070            // previously written value.
1071            hvdef::HV_X64_MSR_GUEST_CRASH_CTL => hvdef::GuestCrashCtl::new()
1072                .with_crash_notify(true)
1073                .with_crash_message(true)
1074                .with_no_crash_dump(true)
1075                .with_pre_os_id(0b111)
1076                .into(),
1077            hvdef::HV_X64_MSR_GUEST_CRASH_P0 => self.crash_reg[0],
1078            hvdef::HV_X64_MSR_GUEST_CRASH_P1 => self.crash_reg[1],
1079            hvdef::HV_X64_MSR_GUEST_CRASH_P2 => self.crash_reg[2],
1080            hvdef::HV_X64_MSR_GUEST_CRASH_P3 => self.crash_reg[3],
1081            hvdef::HV_X64_MSR_GUEST_CRASH_P4 => self.crash_reg[4],
1082            _ => return Err(MsrError::Unknown),
1083        };
1084        Ok(v)
1085    }
1086
1087    /// Emulates an instruction due to a memory access exit.
1088    #[cfg(guest_arch = "x86_64")]
1089    async fn emulate<D: CpuIo>(
1090        &mut self,
1091        devices: &D,
1092        interruption_pending: bool,
1093        vtl: GuestVtl,
1094        cache: T::EmulationCache,
1095    ) -> Result<(), VpHaltReason>
1096    where
1097        for<'b> UhEmulationState<'b, 'a, D, T>: virt_support_x86emu::emulate::EmulatorSupport,
1098    {
1099        let guest_memory = &self.partition.gm[vtl];
1100        let (kx_guest_memory, ux_guest_memory) = match vtl {
1101            GuestVtl::Vtl0 => (
1102                &self.partition.vtl0_kernel_exec_gm,
1103                &self.partition.vtl0_user_exec_gm,
1104            ),
1105            GuestVtl::Vtl1 => (guest_memory, guest_memory),
1106        };
1107        let emu_mem = virt_support_x86emu::emulate::EmulatorMemoryAccess {
1108            gm: guest_memory,
1109            kx_gm: kx_guest_memory,
1110            ux_gm: ux_guest_memory,
1111        };
1112        let mut emulation_state = UhEmulationState {
1113            vp: &mut *self,
1114            interruption_pending,
1115            devices,
1116            vtl,
1117            cache,
1118        };
1119
1120        virt_support_x86emu::emulate::emulate(&mut emulation_state, &emu_mem, devices).await
1121    }
1122
1123    /// Emulates an instruction due to a memory access exit.
1124    #[cfg(guest_arch = "aarch64")]
1125    async fn emulate<D: CpuIo>(
1126        &mut self,
1127        devices: &D,
1128        intercept_state: &aarch64emu::InterceptState,
1129        vtl: GuestVtl,
1130        cache: T::EmulationCache,
1131    ) -> Result<(), VpHaltReason>
1132    where
1133        for<'b> UhEmulationState<'b, 'a, D, T>: virt_support_aarch64emu::emulate::EmulatorSupport,
1134    {
1135        let guest_memory = &self.partition.gm[vtl];
1136        virt_support_aarch64emu::emulate::emulate(
1137            &mut UhEmulationState {
1138                vp: &mut *self,
1139                interruption_pending: intercept_state.interruption_pending,
1140                devices,
1141                vtl,
1142                cache,
1143            },
1144            intercept_state,
1145            guest_memory,
1146            devices,
1147        )
1148        .await
1149    }
1150
1151    #[cfg(guest_arch = "x86_64")]
1152    fn update_proxy_irr_filter(&mut self, vtl: GuestVtl) {
1153        assert_eq!(vtl, GuestVtl::Vtl0);
1154        let mut irr_bits: BitArray<[u32; 8], Lsb0> = BitArray::new(Default::default());
1155
1156        // Get all not masked && proxy SINT vectors
1157        if let Some(hv) = self.backing.hv(vtl).as_ref() {
1158            for sint in 0..NUM_SINTS as u8 {
1159                let sint_msr = hv.synic.sint(sint);
1160                let hv_sint = HvSynicSint::from(sint_msr);
1161                // When vmbus relay is active, then SINT will not be proxied
1162                // in case of non-relay, guest will setup proxied sint
1163                if (hv_sint.proxy() || self.partition.vmbus_relay) && !hv_sint.masked() {
1164                    irr_bits.set(hv_sint.vector() as usize, true);
1165                }
1166            }
1167        }
1168
1169        // Get all device vectors
1170        self.partition.fill_device_vectors(vtl, &mut irr_bits);
1171
1172        // Update `proxy_irr_blocked` filter in run page
1173        self.runner
1174            .update_proxy_irr_filter_vtl0(&irr_bits.into_inner());
1175    }
1176}
1177
1178fn signal_mnf(synic_ports: &virt::synic::SynicPortMap, connection_id: u32) {
1179    if let Err(err) = synic_ports.handle_signal_event(Vtl::Vtl0, connection_id, 0) {
1180        tracelimit::warn_ratelimited!(
1181            CVM_ALLOWED,
1182            error = &err as &dyn std::error::Error,
1183            connection_id,
1184            "failed to signal mnf"
1185        );
1186    }
1187}
1188
1189/// Yields execution back to the executor.
1190async fn yield_now() {
1191    let mut yielded = false;
1192    poll_fn(|cx| {
1193        if !yielded {
1194            // Wake the waker so that this task gets to run again.
1195            cx.waker().wake_by_ref();
1196            yielded = true;
1197            Poll::Pending
1198        } else {
1199            Poll::Ready(())
1200        }
1201    })
1202    .await;
1203}
1204
1205struct UhEmulationState<'a, 'b, T: CpuIo, U: Backing> {
1206    vp: &'a mut UhProcessor<'b, U>,
1207    interruption_pending: bool,
1208    #[cfg_attr(guest_arch = "aarch64", expect(dead_code))]
1209    devices: &'a T,
1210    vtl: GuestVtl,
1211    cache: U::EmulationCache,
1212}
1213
1214impl<T: CpuIo, U: Backing> EmulatorMonitorSupport for UhEmulationState<'_, '_, T, U> {
1215    fn check_write(&self, gpa: u64, bytes: &[u8]) -> bool {
1216        self.vp
1217            .partition
1218            .monitor_page
1219            .check_write(gpa, bytes, |connection_id| {
1220                signal_mnf(&self.vp.partition.synic_ports, connection_id);
1221            })
1222    }
1223
1224    fn check_read(&self, gpa: u64, bytes: &mut [u8]) -> bool {
1225        self.vp.partition.monitor_page.check_read(gpa, bytes)
1226    }
1227}
1228
1229struct UhHypercallHandler<'a, 'b, B: Backing> {
1230    vp: &'a mut UhProcessor<'b, B>,
1231    /// Indicates if the handler is for trusted hypercalls in case hardware isolation is in use. A
1232    /// hypercall is trusted if it was made by the guest using a regular vmcall instruction, without
1233    /// using any host-visible mechanisms. An untrusted hypercall was intercepted from the
1234    /// hypervisor, such as one made by the guest using an isolated mechanism such as tdcall or
1235    /// GHCB.
1236    ///
1237    /// This should always be false if hardware isolation is not in use, as the distinction does
1238    /// not exist in that case.
1239    trusted: bool,
1240    intercepted_vtl: GuestVtl,
1241}
1242
1243impl<B: Backing> UhHypercallHandler<'_, '_, B> {
1244    fn target_vtl_no_higher(&self, target_vtl: Vtl) -> Result<GuestVtl, HvError> {
1245        if Vtl::from(self.intercepted_vtl) < target_vtl {
1246            return Err(HvError::AccessDenied);
1247        }
1248        Ok(target_vtl.try_into().unwrap())
1249    }
1250}
1251
1252impl<B: Backing> hv1_hypercall::GetVpIndexFromApicId for UhHypercallHandler<'_, '_, B> {
1253    fn get_vp_index_from_apic_id(
1254        &mut self,
1255        partition_id: u64,
1256        target_vtl: Vtl,
1257        apic_ids: &[u32],
1258        vp_indices: &mut [u32],
1259    ) -> HvRepResult {
1260        tracing::debug!(partition_id, ?target_vtl, "HvGetVpIndexFromApicId");
1261
1262        if partition_id != hvdef::HV_PARTITION_ID_SELF {
1263            return Err((HvError::InvalidPartitionId, 0));
1264        }
1265
1266        let _target_vtl = self.target_vtl_no_higher(target_vtl).map_err(|e| (e, 0))?;
1267
1268        #[cfg(guest_arch = "aarch64")]
1269        if true {
1270            let _ = apic_ids;
1271            let _ = vp_indices;
1272            todo!("AARCH64_TODO");
1273        }
1274
1275        #[cfg(guest_arch = "x86_64")]
1276        for (i, (&apic_id, vp_index)) in apic_ids.iter().zip(vp_indices).enumerate() {
1277            *vp_index = self
1278                .vp
1279                .partition
1280                .vps
1281                .iter()
1282                .find(|vp| vp.vp_info.apic_id == apic_id)
1283                .ok_or((HvError::InvalidParameter, i))?
1284                .vp_info
1285                .base
1286                .vp_index
1287                .index()
1288        }
1289
1290        Ok(())
1291    }
1292}
1293
1294#[cfg(guest_arch = "aarch64")]
1295impl<B: Backing> Arm64RegisterState for UhHypercallHandler<'_, '_, B> {
1296    fn pc(&mut self) -> u64 {
1297        self.vp
1298            .runner
1299            .get_vp_register(self.intercepted_vtl, HvArm64RegisterName::XPc)
1300            .expect("get vp register cannot fail")
1301            .as_u64()
1302    }
1303
1304    fn set_pc(&mut self, pc: u64) {
1305        self.vp
1306            .runner
1307            .set_vp_register(self.intercepted_vtl, HvArm64RegisterName::XPc, pc.into())
1308            .expect("set vp register cannot fail");
1309    }
1310
1311    fn x(&mut self, n: u8) -> u64 {
1312        self.vp
1313            .runner
1314            .get_vp_register(
1315                self.intercepted_vtl,
1316                HvArm64RegisterName(HvArm64RegisterName::X0.0 + n as u32),
1317            )
1318            .expect("get vp register cannot fail")
1319            .as_u64()
1320    }
1321
1322    fn set_x(&mut self, n: u8, v: u64) {
1323        self.vp
1324            .runner
1325            .set_vp_register(
1326                self.intercepted_vtl,
1327                HvArm64RegisterName(HvArm64RegisterName::X0.0 + n as u32),
1328                v.into(),
1329            )
1330            .expect("set vp register cannot fail")
1331    }
1332}
1333
1334impl<B: Backing> hv1_hypercall::PostMessage for UhHypercallHandler<'_, '_, B> {
1335    fn post_message(&mut self, connection_id: u32, message: &[u8]) -> hvdef::HvResult<()> {
1336        tracing::trace!(
1337            connection_id,
1338            self.trusted,
1339            "handling post message intercept"
1340        );
1341
1342        self.vp.partition.synic_ports.handle_post_message(
1343            self.intercepted_vtl.into(),
1344            connection_id,
1345            self.trusted,
1346            message,
1347        )
1348    }
1349}
1350
1351impl<B: Backing> hv1_hypercall::SignalEvent for UhHypercallHandler<'_, '_, B> {
1352    fn signal_event(&mut self, connection_id: u32, flag: u16) -> hvdef::HvResult<()> {
1353        tracing::trace!(connection_id, "handling signal event intercept");
1354
1355        self.vp.partition.synic_ports.handle_signal_event(
1356            self.intercepted_vtl.into(),
1357            connection_id,
1358            flag,
1359        )
1360    }
1361}
1362
1363impl<B: Backing> UhHypercallHandler<'_, '_, B> {
1364    fn retarget_virtual_interrupt(
1365        &mut self,
1366        device_id: u64,
1367        address: u64,
1368        data: u32,
1369        vector: u32,
1370        multicast: bool,
1371        target_processors: ProcessorSet<'_>,
1372    ) -> hvdef::HvResult<()> {
1373        let target_processors = Vec::from_iter(target_processors);
1374        let vpci_params = vmcore::vpci_msi::VpciInterruptParameters {
1375            vector,
1376            multicast,
1377            target_processors: &target_processors,
1378        };
1379
1380        self.vp
1381            .partition
1382            .software_devices
1383            .as_ref()
1384            .expect("should exist if this intercept is registered or this is a CVM")
1385            .retarget_interrupt(device_id, address, data, &vpci_params)
1386    }
1387}
1388
1389impl<B: Backing> hv1_hypercall::ExtendedQueryCapabilities for UhHypercallHandler<'_, '_, B> {
1390    fn query_extended_capabilities(&mut self) -> hvdef::HvResult<u64> {
1391        // This capability is not actually supported. However Windows may unconditionally issue this
1392        // hypercall. Return InvalidHypercallCode as the error status. This is the same as not
1393        // implementing this at all, but has the advantage of not causing generating error messages.
1394        Err(HvError::InvalidHypercallCode)
1395    }
1396}
1397
1398impl<B: Backing> hv1_hypercall::RestorePartitionTime for UhHypercallHandler<'_, '_, B> {
1399    fn restore_partition_time(
1400        &mut self,
1401        partition_id: u64,
1402        tsc_sequence: u32,
1403        reference_time_in_100_ns: u64,
1404        tsc: u64,
1405    ) -> hvdef::HvResult<()> {
1406        tracelimit::info_ratelimited!(
1407            partition_id,
1408            tsc_sequence,
1409            reference_time_in_100_ns,
1410            tsc,
1411            "handling restore partition time intercept"
1412        );
1413        if partition_id != hvdef::HV_PARTITION_ID_SELF {
1414            return Err(HvError::InvalidParameter);
1415        }
1416
1417        if let Err(e) = self.vp.partition.hcl.restore_partition_time(
1418            tsc_sequence,
1419            reference_time_in_100_ns,
1420            tsc,
1421        ) {
1422            tracelimit::error_ratelimited!(
1423                error = &e as &dyn std::error::Error,
1424                "failed to restore partition time"
1425            );
1426            return Err(HvError::InvalidParameter);
1427        }
1428        Ok(())
1429    }
1430}