1pub 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#[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 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 #[inspect(skip)]
119 runner: ProcessorRunner<'a, T::HclBacking<'a>>,
120 #[inspect(mut)]
121 backing: T,
122}
123
124#[derive(Inspect)]
125struct VtlsTlbLocked {
126 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 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 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 fn poll_apic(this: &mut UhProcessor<'_, Self>, vtl: GuestVtl, scan_irr: bool);
241
242 fn request_extint_readiness(this: &mut UhProcessor<'_, Self>);
247
248 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
265pub 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#[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#[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))]
318struct 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#[cfg(guest_arch = "x86_64")]
419pub(crate) trait HardwareIsolatedBacking: Backing {
420 fn cvm_state(&self) -> &crate::UhCvmVpState;
422 fn cvm_state_mut(&mut self) -> &mut crate::UhCvmVpState;
424 fn cvm_partition_state(shared: &Self::Shared) -> &crate::UhCvmPartitionState;
426 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 fn switch_vtl(this: &mut UhProcessor<'_, Self>, source_vtl: GuestVtl, target_vtl: GuestVtl);
442 fn translation_registers(
444 &self,
445 this: &UhProcessor<'_, Self>,
446 vtl: GuestVtl,
447 ) -> TranslationRegisters;
448 fn pending_event_vector(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> Option<u8>;
451 fn is_interrupt_pending(
454 this: &mut UhProcessor<'_, Self>,
455 vtl: GuestVtl,
456 check_rflags: bool,
457 dev: &impl CpuIo,
458 ) -> bool;
459 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 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 fn update_deadline(this: &mut UhProcessor<'_, Self>, ref_time_now: u64, next_ref_time: u64);
489
490 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 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 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 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 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 const BREAKPOINT_INDEX_OFFSET: usize = 4;
639 let i = debug_regs.dr6.trailing_zeros() as usize;
640 if i >= BREAKPOINT_INDEX_OFFSET {
641 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 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(®isters)?;
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!(¤t, CpuSet::new().set(self.inner.cpu_index));
740
741 nice::nice(1);
744 }
745
746 let mut last_waker = None;
747
748 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 poll_fn(|cx| {
763 loop {
764 stop.check()?;
765
766 self.runner.clear_cancel();
768
769 self.vmtime.cancel_timeout();
771
772 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 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 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 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 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 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 let pending_sints = self.inner.message_queues[vtl].pending_sints();
920 let mut masked_sints = 0;
921
922 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 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 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 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 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 #[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 #[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 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 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 self.partition.fill_device_vectors(vtl, &mut irr_bits);
1150
1151 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
1168async fn yield_now() {
1170 let mut yielded = false;
1171 poll_fn(|cx| {
1172 if !yielded {
1173 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 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 Err(HvError::InvalidHypercallCode)
1371 }
1372}