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