virt/
generic.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4mod partition_memory_map;
5
6pub use partition_memory_map::PartitionMemoryMap;
7pub use vm_topology::processor::VpIndex;
8
9use crate::CpuidLeaf;
10use crate::PartitionCapabilities;
11use crate::io::CpuIo;
12use crate::irqcon::ControlGic;
13use crate::irqcon::IoApicRouting;
14use crate::irqcon::MsiRequest;
15use crate::x86::DebugState;
16use crate::x86::HardwareBreakpoint;
17use guestmem::DoorbellRegistration;
18use guestmem::GuestMemory;
19use hvdef::Vtl;
20use inspect::Inspect;
21use inspect::InspectMut;
22use memory_range::MemoryRange;
23use pci_core::msi::MsiInterruptTarget;
24use std::cell::Cell;
25use std::convert::Infallible;
26use std::fmt::Debug;
27use std::future::Future;
28use std::future::poll_fn;
29use std::pin::pin;
30use std::sync::Arc;
31use std::sync::atomic::AtomicBool;
32use std::sync::atomic::Ordering;
33use std::task::Poll;
34use std::task::Waker;
35use vm_topology::memory::MemoryLayout;
36use vm_topology::processor::ProcessorTopology;
37use vmcore::monitor::MonitorId;
38use vmcore::reference_time::ReferenceTimeSource;
39use vmcore::synic::GuestEventPort;
40use vmcore::vmtime::VmTimeSource;
41use vmcore::vpci_msi::MsiAddressData;
42use vmcore::vpci_msi::RegisterInterruptError;
43use vmcore::vpci_msi::VpciInterruptMapper;
44use vmcore::vpci_msi::VpciInterruptParameters;
45
46pub type Error = anyhow::Error;
47
48pub trait Hypervisor: 'static {
49    /// The prototype partition type.
50    type ProtoPartition<'a>: ProtoPartition<Partition = Self::Partition>;
51    /// The partition type.
52    type Partition;
53    /// The error type when creating the partition.
54    type Error: std::error::Error + Send + Sync + 'static;
55
56    /// Returns whether this hypervisor is available on this machine.
57    fn is_available(&self) -> Result<bool, Self::Error>;
58
59    /// Returns a new prototype partition from the given configuration.
60    fn new_partition<'a>(
61        &'a mut self,
62        config: ProtoPartitionConfig<'a>,
63    ) -> Result<Self::ProtoPartition<'a>, Self::Error>;
64}
65
66/// Isolation type for a partition.
67#[derive(Eq, PartialEq, Debug, Copy, Clone, Inspect)]
68pub enum IsolationType {
69    /// No isolation.
70    None,
71    /// Hypervisor based isolation.
72    Vbs,
73    /// Secure nested paging (AMD SEV-SNP) - hardware based isolation.
74    Snp,
75    /// Trust domain extensions (Intel TDX) - hardware based isolation.
76    Tdx,
77}
78
79impl IsolationType {
80    /// Returns true if the isolation type is not `None`.
81    pub fn is_isolated(&self) -> bool {
82        !matches!(self, Self::None)
83    }
84
85    /// Returns whether the isolation type is hardware-backed.
86    pub fn is_hardware_isolated(&self) -> bool {
87        matches!(self, Self::Snp | Self::Tdx)
88    }
89}
90
91/// An unexpected isolation type was provided.
92#[derive(Debug)]
93pub struct UnexpectedIsolationType;
94
95impl IsolationType {
96    pub const fn from_hv(
97        value: hvdef::HvPartitionIsolationType,
98    ) -> Result<Self, UnexpectedIsolationType> {
99        match value {
100            hvdef::HvPartitionIsolationType::NONE => Ok(IsolationType::None),
101            hvdef::HvPartitionIsolationType::VBS => Ok(IsolationType::Vbs),
102            hvdef::HvPartitionIsolationType::SNP => Ok(IsolationType::Snp),
103            hvdef::HvPartitionIsolationType::TDX => Ok(IsolationType::Tdx),
104            _ => Err(UnexpectedIsolationType),
105        }
106    }
107
108    pub const fn to_hv(self) -> hvdef::HvPartitionIsolationType {
109        match self {
110            IsolationType::None => hvdef::HvPartitionIsolationType::NONE,
111            IsolationType::Vbs => hvdef::HvPartitionIsolationType::VBS,
112            IsolationType::Snp => hvdef::HvPartitionIsolationType::SNP,
113            IsolationType::Tdx => hvdef::HvPartitionIsolationType::TDX,
114        }
115    }
116}
117
118/// Page visibility types for isolated partitions.
119#[derive(Eq, PartialEq, Debug, Copy, Clone, Inspect)]
120pub enum PageVisibility {
121    /// The guest has exclusive access to the page, and no access from the host.
122    Exclusive,
123    /// The page has shared access with the guest and host.
124    Shared,
125}
126
127/// Prototype partition creation configuration.
128pub struct ProtoPartitionConfig<'a> {
129    /// The set of VPs to create.
130    pub processor_topology: &'a ProcessorTopology,
131    /// Microsoft hypervisor guest interface configuration.
132    pub hv_config: Option<HvConfig>,
133    /// VM time access.
134    pub vmtime: &'a VmTimeSource,
135    /// Use the user-mode APIC emulator, if supported.
136    pub user_mode_apic: bool,
137    /// Isolation type for this partition.
138    pub isolation: IsolationType,
139}
140
141/// Partition creation configuration.
142pub struct PartitionConfig<'a> {
143    /// The guest memory layout.
144    pub mem_layout: &'a MemoryLayout,
145    /// Guest memory access.
146    pub guest_memory: &'a GuestMemory,
147    /// Cpuid leaves to add to the default CPUID results.
148    pub cpuid: &'a [CpuidLeaf],
149    /// The offset of the VTL0 alias map. This maps VTL0's view of memory into
150    /// VTL2 at the specified offset (which must be a power of 2).
151    pub vtl0_alias_map: Option<u64>,
152}
153
154/// Trait for a prototype partition, one that is partially created but still
155/// needs final configuration.
156///
157/// This is separate from the partition so that it can be queried to determine
158/// the final partition configuration.
159pub trait ProtoPartition {
160    /// The partition type.
161    type Partition: Partition;
162    /// The VP binder type.
163    type ProcessorBinder: 'static + BindProcessor + Send;
164    /// The error type when creating the partition.
165    type Error: std::error::Error + Send + Sync + 'static;
166
167    /// Gets the default guest cpuid value for inputs `eax` and `ecx`.
168    #[cfg(guest_arch = "x86_64")]
169    fn cpuid(&self, eax: u32, ecx: u32) -> [u32; 4];
170
171    /// The maximum physical address width that processors and devices for this
172    /// partition can access.
173    ///
174    /// This may be smaller than what is reported to the guest via architectural
175    /// interfaces by default, and it may be larger or smaller than what the VMM
176    /// ultimately chooses to report to the guest.
177    fn max_physical_address_size(&self) -> u8;
178
179    /// Constructs the full partition.
180    fn build(
181        self,
182        config: PartitionConfig<'_>,
183    ) -> Result<(Self::Partition, Vec<Self::ProcessorBinder>), Self::Error>;
184}
185
186/// Trait used to bind a processor to the current thread.
187pub trait BindProcessor {
188    /// The processor object.
189    type Processor<'a>: Processor
190    where
191        Self: 'a;
192
193    /// A binding error.
194    type Error: std::error::Error + Send + Sync + 'static;
195
196    /// Binds the processor to the current thread.
197    fn bind(&mut self) -> Result<Self::Processor<'_>, Self::Error>;
198}
199
200/// Policy for the partition when mapping VTL0 memory late.
201#[derive(Eq, PartialEq, Debug, Copy, Clone)]
202pub enum LateMapVtl0MemoryPolicy {
203    /// Halt execution of the VP if VTL0 memory is accessed.
204    Halt,
205    /// Log the error but emulate the access with the instruction emulator.
206    Log,
207    /// Inject an exception into the guest.
208    InjectException,
209}
210
211/// Which ranges VTL2 is allowed to access before VTL0 ram is mapped.
212#[derive(Debug, Clone)]
213pub enum LateMapVtl0AllowedRanges {
214    /// Ask the memory layout what the vtl2_ram ranges are.
215    MemoryLayout,
216    /// These specific ranges are allowed.
217    Ranges(Vec<MemoryRange>),
218}
219
220/// Config used to determine late mapping VTL0 memory.
221#[derive(Debug, Clone)]
222pub struct LateMapVtl0MemoryConfig {
223    /// What ranges VTL2 are allowed to access before VTL0 memory is mapped.
224    /// Generally this consists of the ranges representing VTL2 ram.
225    pub allowed_ranges: LateMapVtl0AllowedRanges,
226    /// The policy for the partition mapping VTL0 memory late.
227    pub policy: LateMapVtl0MemoryPolicy,
228}
229
230/// VTL2 configuration.
231#[derive(Debug)]
232pub struct Vtl2Config {
233    /// If set, map VTL0 memory late after VTL2 has started. The current
234    /// heuristic is to defer mapping VTL0 memory until the first
235    /// [`hvdef::HypercallCode::HvCallModifyVtlProtectionMask`] hypercall is
236    /// made.
237    ///
238    /// Accesses before memory is mapped is determined by the specified config.
239    pub late_map_vtl0_memory: Option<LateMapVtl0MemoryConfig>,
240}
241
242/// Hypervisor configuration.
243#[derive(Debug)]
244pub struct HvConfig {
245    /// Use the hypervisor's in-built enlightenment support if available.
246    pub offload_enlightenments: bool,
247    /// Allow device assignment on the partition.
248    pub allow_device_assignment: bool,
249    /// Enable VTL2 support if set. Additional options are described by
250    /// [Vtl2Config].
251    pub vtl2: Option<Vtl2Config>,
252}
253
254/// Methods for manipulating a VM partition.
255pub trait Partition: 'static + Hv1 + Inspect + Send + Sync {
256    /// Returns a trait object to accept pages on behalf of the guest during the
257    /// initial start import flow.
258    fn supports_initial_accept_pages(
259        &self,
260    ) -> Option<&dyn AcceptInitialPages<Error = <Self as Hv1>::Error>> {
261        None
262    }
263
264    /// Returns a trait object to reset the partition, if supported.
265    fn supports_reset(&self) -> Option<&dyn ResetPartition<Error = <Self as Hv1>::Error>>;
266
267    /// Returns a trait object to reset VTL state, if supported.
268    fn supports_vtl_scrub(&self) -> Option<&dyn ScrubVtl<Error = <Self as Hv1>::Error>> {
269        None
270    }
271
272    /// Returns an interface for registering MMIO doorbells for this partition.
273    ///
274    /// Not all partitions support this.
275    fn doorbell_registration(
276        self: &Arc<Self>,
277        minimum_vtl: Vtl,
278    ) -> Option<Arc<dyn DoorbellRegistration>> {
279        let _ = minimum_vtl;
280        None
281    }
282
283    /// Requests an MSI for the specified VTL.
284    ///
285    /// On x86, the MSI format is the architectural APIC format.
286    ///
287    /// On ARM64, the MSI format is currently not defined, since we only support
288    /// Hyper-V-style VMs (which use synthetic MSIs via VPCI). In the future, we
289    /// may want to support either or both SPI- and ITS+LPI-based MSIs.
290    fn request_msi(&self, vtl: Vtl, request: MsiRequest);
291
292    /// Returns an MSI interrupt target for this partition, which can be used to
293    /// create MSI interrupts.
294    ///
295    /// Not all partitions support this.
296    fn msi_interrupt_target(self: &Arc<Self>, vtl: Vtl) -> Option<Arc<dyn MsiInterruptTarget>> {
297        let _ = vtl;
298        None
299    }
300
301    /// Get the partition capabilities for this partition.
302    fn caps(&self) -> &PartitionCapabilities;
303
304    /// Forces the run_vp call to yield to the scheduler (i.e. return
305    /// Poll::Pending).
306    fn request_yield(&self, vp_index: VpIndex);
307}
308
309/// X86-specific partition methods.
310pub trait X86Partition: Partition {
311    /// Gets the IO-APIC routing control for VTL0.
312    fn ioapic_routing(&self) -> Arc<dyn IoApicRouting>;
313
314    /// Pulses the specified APIC's local interrupt line (0 or 1).
315    fn pulse_lint(&self, vp_index: VpIndex, vtl: Vtl, lint: u8);
316}
317
318/// ARM64-specific partition methods.
319pub trait Aarch64Partition: Partition {
320    /// Returns an interface for accessing the GIC interrupt controller for `vtl`.
321    fn control_gic(&self, vtl: Vtl) -> Arc<dyn ControlGic>;
322}
323
324/// Extension trait for accepting initial pages.
325pub trait AcceptInitialPages {
326    type Error: std::error::Error;
327
328    /// Accepts initial pages on behalf of the guest.
329    ///
330    /// This can only be used during the load path during partition start to
331    /// accept pages on behalf of the guest that were set as part of the load
332    /// process. The host virtstack cannot accept pages on behalf of the guest
333    /// once it has started running.
334    fn accept_initial_pages(
335        &self,
336        pages: &[(MemoryRange, PageVisibility)],
337    ) -> Result<(), Self::Error>;
338}
339
340/// Extension trait for resetting the partition.
341pub trait ResetPartition {
342    type Error: std::error::Error;
343
344    /// Resets the partition, restoring all partition state to the initial
345    /// state.
346    ///
347    /// The caller must ensure that no VPs are running when this is called.
348    ///
349    /// If this fails, the partition is in a bad state and cannot be resumed
350    /// until a subsequent reset call succeeds.
351    fn reset(&self) -> Result<(), Self::Error>;
352}
353
354/// Extension trait for scrubbing higher VTL state while leaving lower VTLs
355/// untouched.
356pub trait ScrubVtl {
357    type Error: std::error::Error;
358
359    /// Scrubs partition and VP state for `vtl`. This is useful for servicing
360    /// and restarting a higher VTL without touching the lower VTL.
361    ///
362    /// The caller must ensure that no VPs are running when this is called.
363    ///
364    /// Note that this does not reset page protections. This is necessary
365    /// because there may be devices assigned to lower VTLs, and they should not
366    /// be able to DMA to higher VTL memory during servicing.
367    fn scrub(&self, vtl: Vtl) -> Result<(), Self::Error>;
368}
369
370/// Provides access to partition state for save, restore, and reset.
371///
372/// This is not part of [`Partition`] because some scenarios do not require such
373/// access.
374pub trait PartitionAccessState {
375    type StateAccess<'a>: crate::vm::AccessVmState
376    where
377        Self: 'a;
378
379    /// Returns an object to access VM state for the specified VTL.
380    fn access_state(&self, vtl: Vtl) -> Self::StateAccess<'_>;
381}
382
383/// Change memory protections for lower VTLs. This can be used to share memory
384/// with a lower VTL or make memory accesses trigger an intercept. This is
385/// intended for dynamic state as initial memory protections are applied at VM
386/// start.
387pub trait VtlMemoryProtection {
388    /// Sets lower VTL permissions on a physical page.
389    ///
390    /// TODO: To remain generic may want to replace hvdef::HvMapGpaFlags with
391    ///       something else.
392    fn modify_vtl_page_setting(&self, pfn: u64, flags: hvdef::HvMapGpaFlags) -> anyhow::Result<()>;
393}
394
395pub trait Processor: InspectMut {
396    type Error: std::error::Error + Send + Sync + 'static;
397    type RunVpError: std::error::Error + Send + Sync + 'static;
398    type StateAccess<'a>: crate::vp::AccessVpState
399    where
400        Self: 'a;
401
402    /// Sets the debug state: conditions under which the VP should exit for
403    /// debugging the guest. This including single stepping and hardware
404    /// breakpoints.
405    ///
406    /// TODO: generalize for non-x86 architectures.
407    fn set_debug_state(&mut self, vtl: Vtl, state: Option<&DebugState>) -> Result<(), Self::Error>;
408
409    /// Runs the VP.
410    ///
411    /// Although this is an async function, it may block synchronously until
412    /// [`Partition::request_yield`] is called for this VP. Then its future must
413    /// return [`Poll::Pending`] at least once.
414    ///
415    /// Returns when an error occurs, the VP halts, or the VP is requested to
416    /// stop via `stop`.
417    #[expect(async_fn_in_trait)] // don't need or want Send bound
418    async fn run_vp(
419        &mut self,
420        stop: StopVp<'_>,
421        dev: &impl CpuIo,
422    ) -> Result<Infallible, VpHaltReason<Self::RunVpError>>;
423
424    /// Without running the VP, flushes any asynchronous requests from other
425    /// processors or objects that might affect this state, so that the object
426    /// can be saved/restored correctly.
427    fn flush_async_requests(&mut self) -> Result<(), Self::RunVpError>;
428
429    /// Returns whether the specified VTL can be inspected on this processor.
430    ///
431    /// VTL0 is always inspectable.
432    fn vtl_inspectable(&self, vtl: Vtl) -> bool {
433        vtl == Vtl::Vtl0
434    }
435
436    fn access_state(&mut self, vtl: Vtl) -> Self::StateAccess<'_>;
437}
438
439/// A source for [`StopVp`].
440pub struct StopVpSource {
441    stop: Cell<bool>,
442    waker: Cell<Option<Waker>>,
443}
444
445impl StopVpSource {
446    /// Creates a new source.
447    pub fn new() -> Self {
448        Self {
449            stop: Cell::new(false),
450            waker: Cell::new(None),
451        }
452    }
453
454    /// Returns an object to wait for stops.
455    pub fn checker(&self) -> StopVp<'_> {
456        StopVp { source: self }
457    }
458
459    /// Initiates a VP stop.
460    ///
461    /// After this, calls to [`StopVp::check`] or [`StopVp::until_stop`] will
462    /// fail.
463    pub fn stop(&self) {
464        self.stop.set(true);
465        if let Some(waker) = self.waker.take() {
466            waker.wake();
467        }
468    }
469
470    /// Returns whether [`Self::stop`] has been called.
471    pub fn is_stopping(&self) -> bool {
472        self.stop.get()
473    }
474}
475
476/// Object to check for VP stop requests.
477pub struct StopVp<'a> {
478    source: &'a StopVpSource,
479}
480
481/// An error result that the VP stopped due to request.
482#[derive(Debug)]
483pub struct VpStopped(());
484
485impl StopVp<'_> {
486    /// Returns `Err(VpStopped(_))` if the VP should stop.
487    pub fn check(&self) -> Result<(), VpStopped> {
488        if self.source.stop.get() {
489            Err(VpStopped(()))
490        } else {
491            Ok(())
492        }
493    }
494
495    /// Runs `fut` until it completes or the VP should stop.
496    pub async fn until_stop<Fut: Future>(&mut self, fut: Fut) -> Result<Fut::Output, VpStopped> {
497        let mut fut = pin!(fut);
498        poll_fn(|cx| match fut.as_mut().poll(cx) {
499            Poll::Ready(r) => Poll::Ready(Ok(r)),
500            Poll::Pending => {
501                self.check()?;
502                self.source.waker.set(Some(cx.waker().clone()));
503                Poll::Pending
504            }
505        })
506        .await
507    }
508}
509
510/// An object that can be polled to see if a yield has been requested.
511#[derive(Debug)]
512pub struct NeedsYield {
513    yield_requested: AtomicBool,
514}
515
516impl NeedsYield {
517    /// Creates a new object.
518    pub fn new() -> Self {
519        Self {
520            yield_requested: false.into(),
521        }
522    }
523
524    /// Requests a yield.
525    ///
526    /// Returns whether a signal is necessary to ensure that the task yields
527    /// soon.
528    pub fn request_yield(&self) -> bool {
529        !self.yield_requested.swap(true, Ordering::Release)
530    }
531
532    /// Yields execution to the executor if `request_yield` has been called
533    /// since the last call to `maybe_yield`.
534    pub async fn maybe_yield(&self) {
535        poll_fn(|cx| {
536            if self.yield_requested.load(Ordering::Acquire) {
537                // Wake this task again to ensure it runs again.
538                cx.waker().wake_by_ref();
539                self.yield_requested.store(false, Ordering::Relaxed);
540                Poll::Pending
541            } else {
542                Poll::Ready(())
543            }
544        })
545        .await
546    }
547}
548
549/// The reason that [`Processor::run_vp`] returned.
550#[derive(Debug)]
551pub enum VpHaltReason<E = anyhow::Error> {
552    /// The processor was requested to stop.
553    Stop(VpStopped),
554    /// The processor task should be restarted, possibly on a different thread.
555    Cancel,
556    /// The processor initiated a power off.
557    PowerOff,
558    /// The processor initiated a reboot.
559    Reset,
560    /// The processor triple faulted.
561    TripleFault {
562        /// The faulting VTL.
563        // FUTURE: move VTL state into `AccessVpState``.
564        vtl: Vtl,
565    },
566    /// The VM's state (e.g. registers, memory) is invalid.
567    InvalidVmState(E),
568    /// Emulation failed.
569    EmulationFailure(Box<dyn std::error::Error + Send + Sync>),
570    /// The underlying hypervisor failed.
571    Hypervisor(E),
572    /// Debugger single step.
573    SingleStep,
574    /// Debugger hardware breakpoint.
575    HwBreak(HardwareBreakpoint),
576}
577
578impl<E> From<VpStopped> for VpHaltReason<E> {
579    fn from(stop: VpStopped) -> Self {
580        Self::Stop(stop)
581    }
582}
583
584pub trait PartitionMemoryMapper {
585    /// Returns a memory mapper for the partition backing `vtl`.
586    fn memory_mapper(&self, vtl: Vtl) -> Arc<dyn PartitionMemoryMap>;
587}
588
589pub trait Hv1 {
590    type Error: std::error::Error + Send + Sync + 'static;
591    type Device: VpciInterruptMapper + MsiInterruptTarget;
592
593    fn reference_time_source(&self) -> Option<ReferenceTimeSource>;
594
595    fn new_virtual_device(
596        &self,
597    ) -> Option<&dyn DeviceBuilder<Device = Self::Device, Error = Self::Error>>;
598}
599
600pub trait DeviceBuilder: Hv1 {
601    fn build(&self, vtl: Vtl, device_id: u64) -> Result<Self::Device, Self::Error>;
602}
603
604pub enum UnimplementedDevice {}
605
606impl VpciInterruptMapper for UnimplementedDevice {
607    fn register_interrupt(
608        &self,
609        _vector_count: u32,
610        _params: &VpciInterruptParameters<'_>,
611    ) -> Result<MsiAddressData, RegisterInterruptError> {
612        match *self {}
613    }
614
615    fn unregister_interrupt(&self, _address: u64, _data: u32) {
616        match *self {}
617    }
618}
619
620impl MsiInterruptTarget for UnimplementedDevice {
621    fn new_interrupt(&self) -> Box<dyn pci_core::msi::MsiControl> {
622        match *self {}
623    }
624}
625
626pub trait Synic: Send + Sync {
627    /// Adds a fast path to signal `event` when the guest signals
628    /// `connection_id` from VTL >= `minimum_vtl`.
629    ///
630    /// Returns Ok(None) if this acceleration is not supported.
631    fn new_host_event_port(
632        &self,
633        connection_id: u32,
634        minimum_vtl: Vtl,
635        event: &pal_event::Event,
636    ) -> Result<Option<Box<dyn Sync + Send>>, vmcore::synic::Error> {
637        let _ = (connection_id, minimum_vtl, event);
638        Ok(None)
639    }
640
641    /// Posts a message to the guest.
642    fn post_message(&self, vtl: Vtl, vp: VpIndex, sint: u8, typ: u32, payload: &[u8]);
643
644    /// Creates a [`GuestEventPort`] for signaling VMBus channels in the guest.
645    fn new_guest_event_port(
646        &self,
647        vtl: Vtl,
648        vp: u32,
649        sint: u8,
650        flag: u16,
651    ) -> Box<dyn GuestEventPort>;
652
653    /// Returns whether callers should pass an OS event when creating event
654    /// ports, as opposed to passing a function to call.
655    ///
656    /// This is true when the hypervisor can more quickly dispatch an OS event
657    /// and resume the VP than it can take an intercept into user mode and call
658    /// a function.
659    fn prefer_os_events(&self) -> bool;
660
661    /// Returns an object for manipulating the monitor page, or None if monitor pages aren't
662    /// supported.
663    fn monitor_support(&self) -> Option<&dyn SynicMonitor> {
664        None
665    }
666}
667
668/// Provides monitor page functionality for a `Synic` implementation.
669pub trait SynicMonitor: Synic {
670    /// Registers a monitored interrupt. The returned struct will unregister the ID when dropped.
671    ///
672    /// # Panics
673    ///
674    /// Panics if monitor_id is already in use.
675    fn register_monitor(&self, monitor_id: MonitorId, connection_id: u32) -> Box<dyn Sync + Send>;
676
677    /// Sets the GPA of the monitor page currently in use.
678    fn set_monitor_page(&self, vtl: Vtl, gpa: Option<u64>) -> anyhow::Result<()>;
679}