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::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#[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 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 #[inspect(skip)]
122 runner: ProcessorRunner<'a, T::HclBacking<'a>>,
123 #[inspect(mut, safe)]
124 backing: T,
125}
126
127#[derive(Inspect)]
128struct VtlsTlbLocked {
129 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 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 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 fn poll_apic(this: &mut UhProcessor<'_, Self>, vtl: GuestVtl, scan_irr: bool);
245
246 fn request_extint_readiness(this: &mut UhProcessor<'_, Self>);
251
252 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
269pub 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#[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#[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))]
322struct 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#[cfg_attr(not(guest_arch = "x86_64"), expect(dead_code))]
423pub(crate) trait HardwareIsolatedBacking: Backing {
424 fn cvm_state(&self) -> &crate::UhCvmVpState;
426 fn cvm_state_mut(&mut self) -> &mut crate::UhCvmVpState;
428 fn cvm_partition_state(shared: &Self::Shared) -> &crate::UhCvmPartitionState;
430 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 fn switch_vtl(this: &mut UhProcessor<'_, Self>, source_vtl: GuestVtl, target_vtl: GuestVtl);
446 fn translation_registers(
448 &self,
449 this: &UhProcessor<'_, Self>,
450 vtl: GuestVtl,
451 ) -> TranslationRegisters;
452 fn pending_event_vector(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> Option<u8>;
455 fn is_interrupt_pending(
458 this: &mut UhProcessor<'_, Self>,
459 vtl: GuestVtl,
460 check_rflags: bool,
461 dev: &impl CpuIo,
462 ) -> bool;
463 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 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 fn update_deadline(this: &mut UhProcessor<'_, Self>, ref_time_now: u64, next_ref_time: u64);
493
494 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 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 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 match hcl::stats::vp_stats() {
595 Err(err) => {
596 resp.field("error", inspect::AsDebug(&err));
597 }
598 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 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 const BREAKPOINT_INDEX_OFFSET: usize = 4;
658 let i = debug_regs.dr6.trailing_zeros() as usize;
659 if i >= BREAKPOINT_INDEX_OFFSET {
660 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 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(®isters)?;
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!(¤t, CpuSet::new().set(self.inner.cpu_index));
759
760 nice::nice(1);
763 }
764
765 let mut last_waker = None;
766
767 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 poll_fn(|cx| {
782 loop {
783 stop.check()?;
784
785 self.runner.clear_cancel();
787
788 self.vmtime.cancel_timeout();
790
791 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 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 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 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 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 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 let pending_sints = self.inner.message_queues[vtl].pending_sints();
939 let mut masked_sints = 0;
940
941 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 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 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 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 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 #[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 #[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 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 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 self.partition.fill_device_vectors(vtl, &mut irr_bits);
1171
1172 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
1189async fn yield_now() {
1191 let mut yielded = false;
1192 poll_fn(|cx| {
1193 if !yielded {
1194 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 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 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}