microsoft/openvmm

Public

mirrored fromhttps://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
044a1588c83070552bc8d547fc011779c82b1892

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

openhcl/virt_mshv_vtl/src/processor/mod.rs

1447lines · modecode

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