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