Skip to main content

guestmem/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Interfaces to read and write guest memory.
5
6// UNSAFETY: This crate's whole purpose is manual memory mapping and management.
7#![expect(unsafe_code)]
8#![expect(missing_docs)]
9
10pub mod ranges;
11
12use guestmem_core::ranges::PagedRange;
13use inspect::Inspect;
14use pal_event::Event;
15use sparse_mmap::AsMappableRef;
16use std::any::Any;
17use std::fmt::Debug;
18use std::future::Future;
19use std::io;
20use std::ops::Deref;
21use std::ops::DerefMut;
22use std::ops::Range;
23use std::ptr::NonNull;
24use std::sync::Arc;
25use std::sync::atomic::AtomicU8;
26use thiserror::Error;
27use zerocopy::FromBytes;
28use zerocopy::Immutable;
29use zerocopy::IntoBytes;
30use zerocopy::KnownLayout;
31
32pub use guestmem_core::AccessError;
33pub use guestmem_core::GuestMemoryBackingError;
34pub use guestmem_core::GuestMemoryError;
35pub use guestmem_core::GuestMemoryErrorKind;
36pub use guestmem_core::GuestMemoryOperation;
37pub use guestmem_core::InvalidGpn;
38pub use guestmem_core::Limit;
39pub use guestmem_core::MemoryRead;
40pub use guestmem_core::MemoryWrite;
41pub use guestmem_core::PAGE_SIZE;
42pub use guestmem_core::Page;
43pub use guestmem_core::PageFaultError;
44
45use guestmem_core::gpn_to_gpa;
46
47const PAGE_SIZE64: u64 = 4096;
48
49#[derive(Debug, Error)]
50#[error("no memory at address")]
51struct OutOfRange;
52
53#[derive(Debug, Error)]
54#[error("memory not lockable")]
55struct NotLockable;
56
57#[derive(Debug, Error)]
58#[error("no fallback for this operation")]
59struct NoFallback;
60
61#[derive(Debug, Error)]
62#[error("the specified page is not mapped")]
63struct NotMapped;
64
65#[derive(Debug, Error)]
66#[error("page inaccessible in bitmap")]
67struct BitmapFailure;
68
69/// A trait for a guest memory backing that is fully available via a virtual
70/// address mapping, as opposed to the fallback functions such as
71/// [`GuestMemoryAccess::read_fallback`].
72///
73/// By implementing this trait, a type guarantees that its
74/// [`GuestMemoryAccess::mapping`] will return `Some(_)` and that all of its
75/// memory can be accessed through that mapping, without needing to call the
76/// fallback functions.
77pub trait LinearGuestMemory: GuestMemoryAccess {}
78
79// SAFETY: the allocation will stay valid for the lifetime of the object.
80unsafe impl GuestMemoryAccess for sparse_mmap::alloc::SharedMem {
81    fn mapping(&self) -> Option<NonNull<u8>> {
82        NonNull::new(self.as_ptr().cast_mut().cast())
83    }
84
85    fn max_address(&self) -> u64 {
86        self.len() as u64
87    }
88}
89
90impl LinearGuestMemory for sparse_mmap::alloc::SharedMem {}
91
92/// A page-aligned heap allocation for use with [`GuestMemory`].
93pub struct AlignedHeapMemory {
94    pages: Box<[AlignedPage]>,
95}
96
97impl Debug for AlignedHeapMemory {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        f.debug_struct("AlignedHeapMemory")
100            .field("len", &self.len())
101            .finish()
102    }
103}
104
105#[repr(C, align(4096))]
106struct AlignedPage([AtomicU8; PAGE_SIZE]);
107
108impl AlignedHeapMemory {
109    /// Allocates a new memory of `size` bytes, rounded up to a page size.
110    pub fn new(size: usize) -> Self {
111        #[expect(clippy::declare_interior_mutable_const)] // <https://github.com/rust-lang/rust-clippy/issues/7665>
112        const ZERO: AtomicU8 = AtomicU8::new(0);
113        #[expect(clippy::declare_interior_mutable_const)]
114        const ZERO_PAGE: AlignedPage = AlignedPage([ZERO; PAGE_SIZE]);
115        let mut pages = Vec::new();
116        pages.resize_with(size.div_ceil(PAGE_SIZE), || ZERO_PAGE);
117        Self {
118            pages: pages.into(),
119        }
120    }
121
122    /// Returns the length of the memory in bytes.
123    pub fn len(&self) -> usize {
124        self.pages.len() * PAGE_SIZE
125    }
126
127    /// Returns an immutable slice of bytes.
128    ///
129    /// This must take `&mut self` since the buffer is mutable via interior
130    /// mutability with just `&self`.
131    pub fn as_bytes(&mut self) -> &[u8] {
132        self.as_mut()
133    }
134
135    /// Returns a mutable slice of bytes.
136    pub fn as_mut_bytes(&mut self) -> &mut [u8] {
137        self.as_mut()
138    }
139}
140
141impl Deref for AlignedHeapMemory {
142    type Target = [AtomicU8];
143
144    fn deref(&self) -> &Self::Target {
145        // SAFETY: the buffer has the correct size and validity.
146        unsafe { std::slice::from_raw_parts(self.pages.as_ptr().cast(), self.len()) }
147    }
148}
149
150impl DerefMut for AlignedHeapMemory {
151    fn deref_mut(&mut self) -> &mut Self::Target {
152        // SAFETY: the buffer is unaliased and valid.
153        unsafe { std::slice::from_raw_parts_mut(self.pages.as_mut_ptr().cast(), self.len()) }
154    }
155}
156
157impl AsRef<[AtomicU8]> for AlignedHeapMemory {
158    fn as_ref(&self) -> &[AtomicU8] {
159        self
160    }
161}
162
163impl AsMut<[AtomicU8]> for AlignedHeapMemory {
164    fn as_mut(&mut self) -> &mut [AtomicU8] {
165        self
166    }
167}
168
169impl AsMut<[u8]> for AlignedHeapMemory {
170    fn as_mut(&mut self) -> &mut [u8] {
171        // FUTURE: use AtomicU8::get_mut_slice once stabilized.
172        // SAFETY: the buffer is unaliased, so it is fine to cast away the atomicness of the
173        // slice.
174        unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr().cast(), self.len()) }
175    }
176}
177
178// SAFETY: the allocation remains alive and valid for the lifetime of the
179// object.
180unsafe impl GuestMemoryAccess for AlignedHeapMemory {
181    fn mapping(&self) -> Option<NonNull<u8>> {
182        NonNull::new(self.pages.as_ptr().cast_mut().cast())
183    }
184
185    fn max_address(&self) -> u64 {
186        (self.pages.len() * PAGE_SIZE) as u64
187    }
188}
189
190impl LinearGuestMemory for AlignedHeapMemory {}
191
192/// A shareable region of guest memory backed by a file (Unix) or
193/// section handle (Windows).
194///
195/// The backing file must already contain committed data for the region —
196/// the consumer will map it directly, without any guestmem-managed lazy
197/// commitment or fault handling. All bytes in the range must be accessible
198/// without triggering SIGSEGV or SIGBUS due to missing backing. Normal OS
199/// demand paging and minor faults on first access are still expected; this
200/// requirement is specifically incompatible with bitmap-gated access or
201/// lazy fault-in schemes.
202pub struct ShareableRegion {
203    /// Guest physical address of this region.
204    pub guest_address: u64,
205    /// Size in bytes.
206    pub size: u64,
207    /// Backing file/handle, shared via `Arc` to avoid OS-level `dup()`.
208    pub file: Arc<sparse_mmap::Mappable>,
209    /// Offset into `file` where this region starts.
210    pub file_offset: u64,
211}
212
213/// Error type for [`ProvideShareableRegions::get_regions`].
214pub type ShareableRegionError = Box<dyn std::error::Error + Send + Sync>;
215
216/// Opaque control object for accessing the shareable backing of guest
217/// memory. Not all `GuestMemory` instances support this — those backed
218/// by private memory or heap allocations return `None`.
219///
220/// # Contract
221///
222/// * The regions returned by [`get_regions`](Self::get_regions) must have
223///   fully committed backing — the consumer will map them directly,
224///   without guestmem-managed fault handling.
225/// * The set of regions is currently static for the lifetime of the VM.
226///   Hotplug and hot-remove of shareable regions are not yet supported;
227///   once they are, additional methods will be added here to notify
228///   consumers of changes.
229pub struct GuestMemorySharing {
230    inner: Box<dyn DynProvideShareableRegions>,
231}
232
233impl GuestMemorySharing {
234    /// Construct from a trait implementation. Called by `GuestMemoryAccess`
235    /// implementations (e.g., `VaMapper` in membacking).
236    pub fn new(inner: impl ProvideShareableRegions + 'static) -> Self {
237        Self {
238            inner: Box::new(inner),
239        }
240    }
241
242    /// Return the current set of shareable backing regions.
243    pub async fn get_regions(&self) -> Result<Vec<ShareableRegion>, ShareableRegionError> {
244        self.inner.get_regions().await
245    }
246}
247
248/// Trait for providing shareable region information.
249///
250/// Implementors must return regions whose backing files have fully
251/// committed data — consumers will map them directly without
252/// guestmem-managed fault handling. The region set is currently static;
253/// dynamic updates (hotplug / hot-remove) are not yet supported.
254///
255/// This trait must be public so that crates like `membacking` can
256/// implement it, but callers should interact with
257/// [`GuestMemorySharing`]'s methods rather than this trait directly.
258pub trait ProvideShareableRegions: Send + Sync {
259    /// Return the current set of shareable backing regions.
260    fn get_regions(
261        &self,
262    ) -> impl Future<Output = Result<Vec<ShareableRegion>, ShareableRegionError>> + Send + '_;
263}
264
265/// Dyn-compatible version of [`ProvideShareableRegions`].
266trait DynProvideShareableRegions: Send + Sync {
267    fn get_regions(
268        &self,
269    ) -> std::pin::Pin<
270        Box<dyn Future<Output = Result<Vec<ShareableRegion>, ShareableRegionError>> + Send + '_>,
271    >;
272}
273
274impl<T: ProvideShareableRegions> DynProvideShareableRegions for T {
275    fn get_regions(
276        &self,
277    ) -> std::pin::Pin<
278        Box<dyn Future<Output = Result<Vec<ShareableRegion>, ShareableRegionError>> + Send + '_>,
279    > {
280        Box::pin(ProvideShareableRegions::get_regions(self))
281    }
282}
283
284/// A trait for a guest memory backing.
285///
286/// Guest memory may be backed by a virtual memory mapping, in which case this
287/// trait can provide the VA and length of that mapping. Alternatively, it may
288/// be backed by some other means, in which case this trait can provide fallback
289/// methods for reading and writing memory.
290///
291/// Memory access should first be attempted via the virtual address mapping. If
292/// this fails or is not present, the caller should fall back to `read_fallback`
293/// or `write_fallback`. This allows an implementation to have a fast path using
294/// the mapping, and a slow path using the fallback functions.
295///
296/// # Safety
297///
298/// The implementor must follow the contract for each method.
299pub unsafe trait GuestMemoryAccess: 'static + Send + Sync {
300    /// Returns a stable VA mapping for guest memory.
301    ///
302    /// The size of the mapping is the same as `max_address`.
303    ///
304    /// The VA is guaranteed to remain reserved, but individual ranges may be
305    /// uncommitted.
306    fn mapping(&self) -> Option<NonNull<u8>>;
307
308    /// The maximum address that can be passed to the `*_fallback` methods, as
309    /// well as the maximum offset into the VA range described by `mapping`.
310    fn max_address(&self) -> u64;
311
312    /// The bitmaps to check for validity, one bit per page. If a bit is set,
313    /// then the page is valid to access via the mapping; if it is clear, then
314    /// the page will not be accessed.
315    ///
316    /// The bitmaps must be at least `ceil(bitmap_start + max_address() /
317    /// PAGE_SIZE)` bits long, and they must be valid for atomic read access for
318    /// the lifetime of this object from any thread.
319    ///
320    /// The bitmaps are only checked if there is a mapping. If the bitmap check
321    /// fails, then the associated `*_fallback` routine is called to handle the
322    /// error.
323    ///
324    /// Bitmap checks are performed under the [`rcu()`] RCU domain, with relaxed
325    /// accesses. After a thread updates the bitmap to be more restrictive, it
326    /// must call [minircu::RcuDomain::synchronize()] on [`minircu::global()`]
327    /// to ensure that all threads see the update before taking any action that
328    /// depends on the bitmap update being visible.
329    #[cfg(feature = "bitmap")]
330    fn access_bitmap(&self) -> Option<BitmapInfo> {
331        None
332    }
333
334    // Returns an accessor for a subrange, or `None` to use the default
335    // implementation.
336    fn subrange(
337        &self,
338        offset: u64,
339        len: u64,
340        allow_preemptive_locking: bool,
341    ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
342        let _ = (offset, len, allow_preemptive_locking);
343        Ok(None)
344    }
345
346    /// Called when access to memory via the mapped range fails, either due to a
347    /// bitmap failure or due to a failure when accessing the virtual address.
348    ///
349    /// `address` is the address where the access failed. `len` is the remainder
350    /// of the access; it is not necessarily the case that all `len` bytes are
351    /// inaccessible in the bitmap or mapping.
352    ///
353    /// Returns whether the faulting operation should be retried, failed, or that
354    /// one of the fallback operations (e.g. `read_fallback`) should be called.
355    fn page_fault(
356        &self,
357        address: u64,
358        len: usize,
359        write: bool,
360        bitmap_failure: bool,
361    ) -> PageFaultAction {
362        let _ = (address, len, write);
363        let err = if bitmap_failure {
364            PageFaultError::other(BitmapFailure)
365        } else {
366            PageFaultError::other(NotMapped)
367        };
368        PageFaultAction::Fail(err)
369    }
370
371    /// Fallback called if a read fails via direct access to `mapped_range`.
372    ///
373    /// This is only called if `mapping()` returns `None` or if `page_fault()`
374    /// returns `PageFaultAction::Fallback`.
375    ///
376    /// Implementors must ensure that `dest[..len]` is fully initialized on
377    /// successful return.
378    ///
379    /// # Safety
380    /// The caller must ensure that `dest[..len]` is valid for write. Note,
381    /// however, that `dest` might be aliased by other threads, the guest, or
382    /// the kernel.
383    unsafe fn read_fallback(
384        &self,
385        addr: u64,
386        dest: *mut u8,
387        len: usize,
388    ) -> Result<(), GuestMemoryBackingError> {
389        let _ = (dest, len);
390        Err(GuestMemoryBackingError::other(addr, NoFallback))
391    }
392
393    /// Fallback called if a write fails via direct access to `mapped_range`.
394    ///
395    /// This is only called if `mapping()` returns `None` or if `page_fault()`
396    /// returns `PageFaultAction::Fallback`.
397    ///
398    /// # Safety
399    /// The caller must ensure that `src[..len]` is valid for read. Note,
400    /// however, that `src` might be aliased by other threads, the guest, or
401    /// the kernel.
402    unsafe fn write_fallback(
403        &self,
404        addr: u64,
405        src: *const u8,
406        len: usize,
407    ) -> Result<(), GuestMemoryBackingError> {
408        let _ = (src, len);
409        Err(GuestMemoryBackingError::other(addr, NoFallback))
410    }
411
412    /// Fallback called if a fill fails via direct access to `mapped_range`.
413    ///
414    /// This is only called if `mapping()` returns `None` or if `page_fault()`
415    /// returns `PageFaultAction::Fallback`.
416    fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
417        let _ = (val, len);
418        Err(GuestMemoryBackingError::other(addr, NoFallback))
419    }
420
421    /// Fallback called if a compare exchange fails via direct access to `mapped_range`.
422    ///
423    /// On compare failure, returns `Ok(false)` and updates `current`.
424    ///
425    /// This is only called if `mapping()` returns `None` or if `page_fault()`
426    /// returns `PageFaultAction::Fallback`.
427    fn compare_exchange_fallback(
428        &self,
429        addr: u64,
430        current: &mut [u8],
431        new: &[u8],
432    ) -> Result<bool, GuestMemoryBackingError> {
433        let _ = (current, new);
434        Err(GuestMemoryBackingError::other(addr, NoFallback))
435    }
436
437    /// Prepares a guest page for having its virtual address exposed as part of
438    /// a lock call.
439    ///
440    /// This is useful to ensure that the address is mapped in a way that it can
441    /// be passed to the kernel for DMA.
442    fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
443        let _ = (address, len);
444        Ok(())
445    }
446
447    /// Returns the base IO virtual address for the mapping.
448    ///
449    /// This is the base address that should be used for DMA from a user-mode
450    /// device driver whose device is not otherwise configured to go through an
451    /// IOMMU.
452    fn base_iova(&self) -> Option<u64> {
453        None
454    }
455
456    /// Locks the specified guest physical pages (GPNs), preventing any mapping
457    /// or permission changes until they are unlocked.
458    ///
459    /// Returns a boolean indicating whether unlocking is required.
460    fn lock_gpns(&self, gpns: &[u64]) -> Result<bool, GuestMemoryBackingError> {
461        let _ = gpns;
462        Ok(false)
463    }
464
465    /// Unlocks the specified guest physical pages (GPNs) after exclusive access.
466    ///
467    /// Panics if asked to unlock a page that was not previously locked. The
468    /// caller must ensure that the given slice has the same ordering as the
469    /// one passed to `lock_gpns`.
470    fn unlock_gpns(&self, gpns: &[u64]) {
471        let _ = gpns;
472    }
473
474    /// Return a sharing control object if this memory backing supports
475    /// file-based sharing (e.g., memfd on Linux, section on Windows).
476    ///
477    /// Returns `None` for private memory, heap-backed test memory, or
478    /// other non-shareable backings.
479    fn sharing(&self) -> Option<GuestMemorySharing> {
480        None
481    }
482
483    /// Returns whether this backing supports locking pages via
484    /// [`lock_gpns`](Self::lock_gpns).
485    ///
486    /// Locking requires a stable host mapping (see
487    /// [`mapping`](Self::mapping)), so the default returns whether a mapping
488    /// is present. Backings that translate each access on demand (e.g., memory
489    /// behind an emulated IOMMU) have no mapping and thus report `false`.
490    /// Callers that use locking as a zero-copy fast path should check this and
491    /// fall back to a copying path when it returns `false`.
492    ///
493    /// This is authoritative: when it returns `false`, the corresponding
494    /// [`GuestMemory`] locking APIs fail without invoking
495    /// [`lock_gpns`](Self::lock_gpns).
496    fn supports_locking(&self) -> bool {
497        self.mapping().is_some()
498    }
499}
500
501trait DynGuestMemoryAccess: 'static + Send + Sync + Any {
502    fn subrange(
503        &self,
504        offset: u64,
505        len: u64,
506        allow_preemptive_locking: bool,
507    ) -> Result<Option<GuestMemory>, GuestMemoryBackingError>;
508
509    fn page_fault(
510        &self,
511        address: u64,
512        len: usize,
513        write: bool,
514        bitmap_failure: bool,
515    ) -> PageFaultAction;
516
517    /// # Safety
518    /// See [`GuestMemoryAccess::read_fallback`].
519    unsafe fn read_fallback(
520        &self,
521        addr: u64,
522        dest: *mut u8,
523        len: usize,
524    ) -> Result<(), GuestMemoryBackingError>;
525
526    /// # Safety
527    /// See [`GuestMemoryAccess::write_fallback`].
528    unsafe fn write_fallback(
529        &self,
530        addr: u64,
531        src: *const u8,
532        len: usize,
533    ) -> Result<(), GuestMemoryBackingError>;
534
535    fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError>;
536
537    fn compare_exchange_fallback(
538        &self,
539        addr: u64,
540        current: &mut [u8],
541        new: &[u8],
542    ) -> Result<bool, GuestMemoryBackingError>;
543
544    fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError>;
545
546    fn lock_gpns(&self, gpns: &[u64]) -> Result<bool, GuestMemoryBackingError>;
547
548    fn unlock_gpns(&self, gpns: &[u64]);
549
550    fn sharing(&self) -> Option<GuestMemorySharing>;
551}
552
553impl<T: GuestMemoryAccess> DynGuestMemoryAccess for T {
554    fn subrange(
555        &self,
556        offset: u64,
557        len: u64,
558        allow_preemptive_locking: bool,
559    ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
560        self.subrange(offset, len, allow_preemptive_locking)
561    }
562
563    fn page_fault(
564        &self,
565        address: u64,
566        len: usize,
567        write: bool,
568        bitmap_failure: bool,
569    ) -> PageFaultAction {
570        self.page_fault(address, len, write, bitmap_failure)
571    }
572
573    unsafe fn read_fallback(
574        &self,
575        addr: u64,
576        dest: *mut u8,
577        len: usize,
578    ) -> Result<(), GuestMemoryBackingError> {
579        // SAFETY: guaranteed by caller.
580        unsafe { self.read_fallback(addr, dest, len) }
581    }
582
583    unsafe fn write_fallback(
584        &self,
585        addr: u64,
586        src: *const u8,
587        len: usize,
588    ) -> Result<(), GuestMemoryBackingError> {
589        // SAFETY: guaranteed by caller.
590        unsafe { self.write_fallback(addr, src, len) }
591    }
592
593    fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
594        self.fill_fallback(addr, val, len)
595    }
596
597    fn compare_exchange_fallback(
598        &self,
599        addr: u64,
600        current: &mut [u8],
601        new: &[u8],
602    ) -> Result<bool, GuestMemoryBackingError> {
603        self.compare_exchange_fallback(addr, current, new)
604    }
605
606    fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
607        self.expose_va(address, len)
608    }
609
610    fn lock_gpns(&self, gpns: &[u64]) -> Result<bool, GuestMemoryBackingError> {
611        self.lock_gpns(gpns)
612    }
613
614    fn unlock_gpns(&self, gpns: &[u64]) {
615        self.unlock_gpns(gpns)
616    }
617
618    fn sharing(&self) -> Option<GuestMemorySharing> {
619        self.sharing()
620    }
621}
622
623/// The action to take after [`GuestMemoryAccess::page_fault`] returns to
624/// continue the operation.
625pub enum PageFaultAction {
626    /// Fail the operation.
627    Fail(PageFaultError),
628    /// Retry the operation.
629    Retry,
630    /// Use the fallback method to access the memory.
631    Fallback,
632}
633
634/// Returned by [`GuestMemoryAccess::access_bitmap`].
635#[cfg(feature = "bitmap")]
636pub struct BitmapInfo {
637    /// A pointer to the bitmap for read access.
638    pub read_bitmap: NonNull<u8>,
639    /// A pointer to the bitmap for write access.
640    pub write_bitmap: NonNull<u8>,
641    /// The bit offset of the beginning of the bitmap.
642    ///
643    /// Typically this is zero, but it is needed to support subranges that are
644    /// not 8-page multiples.
645    pub bit_offset: u8,
646}
647
648// SAFETY: passing through guarantees from `T`.
649unsafe impl<T: GuestMemoryAccess> GuestMemoryAccess for Arc<T> {
650    fn mapping(&self) -> Option<NonNull<u8>> {
651        self.as_ref().mapping()
652    }
653
654    fn max_address(&self) -> u64 {
655        self.as_ref().max_address()
656    }
657
658    #[cfg(feature = "bitmap")]
659    fn access_bitmap(&self) -> Option<BitmapInfo> {
660        self.as_ref().access_bitmap()
661    }
662
663    fn subrange(
664        &self,
665        offset: u64,
666        len: u64,
667        allow_preemptive_locking: bool,
668    ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
669        self.as_ref()
670            .subrange(offset, len, allow_preemptive_locking)
671    }
672
673    fn page_fault(
674        &self,
675        addr: u64,
676        len: usize,
677        write: bool,
678        bitmap_failure: bool,
679    ) -> PageFaultAction {
680        self.as_ref().page_fault(addr, len, write, bitmap_failure)
681    }
682
683    unsafe fn read_fallback(
684        &self,
685        addr: u64,
686        dest: *mut u8,
687        len: usize,
688    ) -> Result<(), GuestMemoryBackingError> {
689        // SAFETY: passing through guarantees from caller.
690        unsafe { self.as_ref().read_fallback(addr, dest, len) }
691    }
692
693    unsafe fn write_fallback(
694        &self,
695        addr: u64,
696        src: *const u8,
697        len: usize,
698    ) -> Result<(), GuestMemoryBackingError> {
699        // SAFETY: passing through guarantees from caller.
700        unsafe { self.as_ref().write_fallback(addr, src, len) }
701    }
702
703    fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
704        self.as_ref().fill_fallback(addr, val, len)
705    }
706
707    fn compare_exchange_fallback(
708        &self,
709        addr: u64,
710        current: &mut [u8],
711        new: &[u8],
712    ) -> Result<bool, GuestMemoryBackingError> {
713        self.as_ref().compare_exchange_fallback(addr, current, new)
714    }
715
716    fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
717        self.as_ref().expose_va(address, len)
718    }
719
720    fn base_iova(&self) -> Option<u64> {
721        self.as_ref().base_iova()
722    }
723
724    fn sharing(&self) -> Option<GuestMemorySharing> {
725        self.as_ref().sharing()
726    }
727}
728
729// SAFETY: the allocation will stay valid for the lifetime of the object.
730unsafe impl GuestMemoryAccess for sparse_mmap::SparseMapping {
731    fn mapping(&self) -> Option<NonNull<u8>> {
732        NonNull::new(self.as_ptr().cast())
733    }
734
735    fn max_address(&self) -> u64 {
736        self.len() as u64
737    }
738}
739
740/// Default guest memory range type, enforcing access boundaries.
741struct GuestMemoryAccessRange {
742    base: Arc<GuestMemoryInner>,
743    offset: u64,
744    len: u64,
745    region: usize,
746}
747
748impl GuestMemoryAccessRange {
749    fn adjust_range(&self, address: u64, len: u64) -> Result<u64, GuestMemoryBackingError> {
750        if address <= self.len && len <= self.len - address {
751            Ok(self.offset + address)
752        } else {
753            Err(GuestMemoryBackingError::new(
754                GuestMemoryErrorKind::OutOfRange,
755                address,
756                OutOfRange,
757            ))
758        }
759    }
760}
761
762// SAFETY: `mapping()` is guaranteed to be valid for the lifetime of the object.
763unsafe impl GuestMemoryAccess for GuestMemoryAccessRange {
764    fn mapping(&self) -> Option<NonNull<u8>> {
765        let region = &self.base.regions[self.region];
766        region.mapping.and_then(|mapping| {
767            let offset = self.offset & self.base.region_def.region_mask;
768            // This is guaranteed by construction.
769            assert!(region.len >= offset + self.len);
770            // SAFETY: this mapping is guaranteed to be within range by
771            // construction (and validated again via the assertion above).
772            NonNull::new(unsafe { mapping.0.as_ptr().add(offset as usize) })
773        })
774    }
775
776    fn max_address(&self) -> u64 {
777        self.len
778    }
779
780    #[cfg(feature = "bitmap")]
781    fn access_bitmap(&self) -> Option<BitmapInfo> {
782        let region = &self.base.regions[self.region];
783        region.bitmaps.map(|bitmaps| {
784            let offset = self.offset & self.base.region_def.region_mask;
785            let bit_offset = region.bitmap_start as u64 + offset / PAGE_SIZE64;
786            let [read_bitmap, write_bitmap] = bitmaps.map(|SendPtrU8(ptr)| {
787                // SAFETY: the bitmap is guaranteed to be big enough for the region
788                // by construction.
789                NonNull::new(unsafe { ptr.as_ptr().add((bit_offset / 8) as usize) }).unwrap()
790            });
791            let bitmap_start = (bit_offset % 8) as u8;
792            BitmapInfo {
793                read_bitmap,
794                write_bitmap,
795                bit_offset: bitmap_start,
796            }
797        })
798    }
799
800    fn subrange(
801        &self,
802        offset: u64,
803        len: u64,
804        _allow_preemptive_locking: bool,
805    ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
806        let address = self.adjust_range(offset, len)?;
807        Ok(Some(GuestMemory::new(
808            self.base.debug_name.clone(),
809            GuestMemoryAccessRange {
810                base: self.base.clone(),
811                offset: address,
812                len,
813                region: self.region,
814            },
815        )))
816    }
817
818    fn page_fault(
819        &self,
820        address: u64,
821        len: usize,
822        write: bool,
823        bitmap_failure: bool,
824    ) -> PageFaultAction {
825        let address = self
826            .adjust_range(address, len as u64)
827            .expect("the caller should have validated the range was in the mapping");
828
829        self.base
830            .imp
831            .page_fault(address, len, write, bitmap_failure)
832    }
833
834    unsafe fn write_fallback(
835        &self,
836        address: u64,
837        src: *const u8,
838        len: usize,
839    ) -> Result<(), GuestMemoryBackingError> {
840        let address = self.adjust_range(address, len as u64)?;
841        // SAFETY: guaranteed by caller.
842        unsafe { self.base.imp.write_fallback(address, src, len) }
843    }
844
845    fn fill_fallback(
846        &self,
847        address: u64,
848        val: u8,
849        len: usize,
850    ) -> Result<(), GuestMemoryBackingError> {
851        let address = self.adjust_range(address, len as u64)?;
852        self.base.imp.fill_fallback(address, val, len)
853    }
854
855    fn compare_exchange_fallback(
856        &self,
857        addr: u64,
858        current: &mut [u8],
859        new: &[u8],
860    ) -> Result<bool, GuestMemoryBackingError> {
861        let address = self.adjust_range(addr, new.len() as u64)?;
862        self.base
863            .imp
864            .compare_exchange_fallback(address, current, new)
865    }
866
867    unsafe fn read_fallback(
868        &self,
869        address: u64,
870        dest: *mut u8,
871        len: usize,
872    ) -> Result<(), GuestMemoryBackingError> {
873        let address = self.adjust_range(address, len as u64)?;
874        // SAFETY: guaranteed by caller.
875        unsafe { self.base.imp.read_fallback(address, dest, len) }
876    }
877
878    fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
879        let address = self.adjust_range(address, len)?;
880        self.base.imp.expose_va(address, len)
881    }
882
883    fn base_iova(&self) -> Option<u64> {
884        let region = &self.base.regions[self.region];
885        Some(region.base_iova? + (self.offset & self.base.region_def.region_mask))
886    }
887}
888
889/// Create a default guest memory subrange that verifies range limits and calls
890/// back into the base implementation.
891fn create_memory_subrange(
892    base: Arc<GuestMemoryInner>,
893    offset: u64,
894    len: u64,
895    _allow_preemptive_locking: bool,
896) -> Result<GuestMemory, GuestMemoryBackingError> {
897    let (_, _, region) = base.region(offset, len)?;
898    Ok(GuestMemory::new(
899        base.debug_name.clone(),
900        GuestMemoryAccessRange {
901            base,
902            offset,
903            len,
904            region,
905        },
906    ))
907}
908
909struct MultiRegionGuestMemoryAccess<T> {
910    imps: Vec<Option<T>>,
911    region_def: RegionDefinition,
912}
913
914impl<T> MultiRegionGuestMemoryAccess<T> {
915    fn region(&self, gpa: u64, len: u64) -> Result<(&T, u64), GuestMemoryBackingError> {
916        let (i, offset) = self.region_def.region(gpa, len)?;
917        let imp = self.imps[i].as_ref().ok_or(GuestMemoryBackingError::new(
918            GuestMemoryErrorKind::OutOfRange,
919            gpa,
920            OutOfRange,
921        ))?;
922        Ok((imp, offset))
923    }
924}
925
926// SAFETY: `mapping()` is unreachable and panics if called.
927impl<T: GuestMemoryAccess> DynGuestMemoryAccess for MultiRegionGuestMemoryAccess<T> {
928    fn subrange(
929        &self,
930        offset: u64,
931        len: u64,
932        allow_preemptive_locking: bool,
933    ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
934        let (region, offset_in_region) = self.region(offset, len)?;
935        region.subrange(offset_in_region, len, allow_preemptive_locking)
936    }
937
938    unsafe fn read_fallback(
939        &self,
940        addr: u64,
941        dest: *mut u8,
942        len: usize,
943    ) -> Result<(), GuestMemoryBackingError> {
944        let (region, offset_in_region) = self.region(addr, len as u64)?;
945        // SAFETY: guaranteed by caller.
946        unsafe { region.read_fallback(offset_in_region, dest, len) }
947    }
948
949    unsafe fn write_fallback(
950        &self,
951        addr: u64,
952        src: *const u8,
953        len: usize,
954    ) -> Result<(), GuestMemoryBackingError> {
955        let (region, offset_in_region) = self.region(addr, len as u64)?;
956        // SAFETY: guaranteed by caller.
957        unsafe { region.write_fallback(offset_in_region, src, len) }
958    }
959
960    fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
961        let (region, offset_in_region) = self.region(addr, len as u64)?;
962        region.fill_fallback(offset_in_region, val, len)
963    }
964
965    fn compare_exchange_fallback(
966        &self,
967        addr: u64,
968        current: &mut [u8],
969        new: &[u8],
970    ) -> Result<bool, GuestMemoryBackingError> {
971        let (region, offset_in_region) = self.region(addr, new.len() as u64)?;
972        region.compare_exchange_fallback(offset_in_region, current, new)
973    }
974
975    fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
976        let (region, offset_in_region) = self.region(address, len)?;
977        region.expose_va(offset_in_region, len)
978    }
979
980    fn page_fault(
981        &self,
982        address: u64,
983        len: usize,
984        write: bool,
985        bitmap_failure: bool,
986    ) -> PageFaultAction {
987        match self.region(address, len as u64) {
988            Ok((region, offset_in_region)) => {
989                region.page_fault(offset_in_region, len, write, bitmap_failure)
990            }
991            Err(err) => PageFaultAction::Fail(PageFaultError {
992                kind: err.kind,
993                err: err.err,
994            }),
995        }
996    }
997
998    fn lock_gpns(&self, gpns: &[u64]) -> Result<bool, GuestMemoryBackingError> {
999        let mut ret = false;
1000        for gpn in gpns {
1001            let (region, offset_in_region) = self.region(gpn * PAGE_SIZE64, PAGE_SIZE64)?;
1002            ret |= region.lock_gpns(&[offset_in_region / PAGE_SIZE64])?;
1003        }
1004        Ok(ret)
1005    }
1006
1007    fn unlock_gpns(&self, gpns: &[u64]) {
1008        for gpn in gpns {
1009            let (region, offset_in_region) = self.region(gpn * PAGE_SIZE64, PAGE_SIZE64).unwrap();
1010            region.unlock_gpns(&[offset_in_region / PAGE_SIZE64]);
1011        }
1012    }
1013
1014    fn sharing(&self) -> Option<GuestMemorySharing> {
1015        // FUTURE: multi-region setups could aggregate shareable regions from
1016        // their sub-regions. For now, sharing is only supported for
1017        // single-region guest memory (the common case). If a VM uses
1018        // MultiRegionGuestMemoryAccess with vhost-user, this will return
1019        // None and the vhost-user backend will fail to initialize.
1020        None
1021    }
1022}
1023
1024/// A wrapper around a `GuestMemoryAccess` that provides methods for safely
1025/// reading and writing guest memory.
1026// NOTE: this type uses `inspect(skip)`, as it end up being a dependency of
1027// _many_ objects, and littering the inspect graph with references to the same
1028// node would be silly.
1029#[derive(Debug, Clone, Inspect)]
1030#[inspect(skip)]
1031pub struct GuestMemory {
1032    inner: Arc<GuestMemoryInner>,
1033}
1034
1035struct GuestMemoryInner<T: ?Sized = dyn DynGuestMemoryAccess> {
1036    region_def: RegionDefinition,
1037    regions: Vec<MemoryRegion>,
1038    debug_name: Arc<str>,
1039    allocated: bool,
1040    /// Cached result of [`GuestMemoryAccess::supports_locking`], since it is
1041    /// queried on hot zero-copy paths and never changes for a given backing.
1042    supports_locking: bool,
1043    imp: T,
1044}
1045
1046impl<T: ?Sized> Debug for GuestMemoryInner<T> {
1047    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1048        f.debug_struct("GuestMemoryInner")
1049            .field("region_def", &self.region_def)
1050            .field("regions", &self.regions)
1051            .finish()
1052    }
1053}
1054
1055#[derive(Debug, Copy, Clone, Default)]
1056struct MemoryRegion {
1057    mapping: Option<SendPtrU8>,
1058    #[cfg(feature = "bitmap")]
1059    bitmaps: Option<[SendPtrU8; 2]>,
1060    #[cfg(feature = "bitmap")]
1061    bitmap_start: u8,
1062    len: u64,
1063    base_iova: Option<u64>,
1064}
1065
1066/// The type of access that guest memory will be used for.
1067///
1068/// The discriminants correspond to bitmap indexes (read = 0, write = 1) and
1069/// must not be reordered.
1070#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1071pub enum AccessType {
1072    /// Read access.
1073    Read = 0,
1074    /// Write access.
1075    Write = 1,
1076}
1077
1078/// `NonNull<u8>` that implements `Send+Sync`.
1079///
1080/// Rust makes pointers `!Send+!Sync` by default to force you to think about the
1081/// ownership model and thread safety of types using pointers--there is nothing
1082/// safety-related about `Send`/`Sync` on pointers by themselves since all such
1083/// accesses to pointers require `unsafe` blocks anyway.
1084///
1085/// However, in practice, this leads to spurious manual `Send+Sync` impls on
1086/// types containing pointers, especially those containing generics. Define a
1087/// wrapping pointer type that implements `Send+Sync` so that the normal auto
1088/// trait rules apply to types containing these pointers.
1089#[derive(Debug, Copy, Clone)]
1090struct SendPtrU8(NonNull<u8>);
1091
1092// SAFETY: see type description.
1093unsafe impl Send for SendPtrU8 {}
1094// SAFETY: see type description.
1095unsafe impl Sync for SendPtrU8 {}
1096
1097impl MemoryRegion {
1098    fn new(imp: &impl GuestMemoryAccess) -> Self {
1099        #[cfg(feature = "bitmap")]
1100        let (bitmaps, bitmap_start) = {
1101            let bitmap_info = imp.access_bitmap();
1102            let bitmaps = bitmap_info
1103                .as_ref()
1104                .map(|bm| [SendPtrU8(bm.read_bitmap), SendPtrU8(bm.write_bitmap)]);
1105            let bitmap_start = bitmap_info.map_or(0, |bi| bi.bit_offset);
1106            (bitmaps, bitmap_start)
1107        };
1108        Self {
1109            mapping: imp.mapping().map(SendPtrU8),
1110            #[cfg(feature = "bitmap")]
1111            bitmaps,
1112            #[cfg(feature = "bitmap")]
1113            bitmap_start,
1114            len: imp.max_address(),
1115            base_iova: imp.base_iova(),
1116        }
1117    }
1118
1119    /// # Safety
1120    ///
1121    /// The caller must ensure that `offset + len` fits in this region, and that
1122    /// the object bitmap is currently valid for atomic read access from this
1123    /// thread.
1124    unsafe fn check_access(
1125        &self,
1126        access_type: AccessType,
1127        offset: u64,
1128        len: u64,
1129    ) -> Result<(), u64> {
1130        debug_assert!(self.len >= offset + len);
1131        #[cfg(not(feature = "bitmap"))]
1132        let _ = access_type;
1133
1134        #[cfg(feature = "bitmap")]
1135        if len == 0 {
1136            return Ok(());
1137        } else if let Some(bitmaps) = &self.bitmaps {
1138            let SendPtrU8(bitmap) = bitmaps[access_type as usize];
1139            let start = offset / PAGE_SIZE64;
1140            let end = (offset + len - 1) / PAGE_SIZE64;
1141            // FUTURE: consider optimizing this separately for multi-page and
1142            // single-page accesses.
1143            for gpn in start..=end {
1144                let bit_offset = self.bitmap_start as u64 + gpn;
1145                // SAFETY: the caller ensures that the bitmap is big enough and
1146                // valid for atomic read access from this thread.
1147                let bit = unsafe {
1148                    (*bitmap
1149                        .as_ptr()
1150                        .cast_const()
1151                        .cast::<AtomicU8>()
1152                        .add(bit_offset as usize / 8))
1153                    .load(std::sync::atomic::Ordering::Relaxed)
1154                        & (1 << (bit_offset % 8))
1155                };
1156                if bit == 0 {
1157                    return Err((gpn * PAGE_SIZE64).saturating_sub(offset));
1158                }
1159            }
1160        }
1161
1162        Ok(())
1163    }
1164}
1165
1166/// The default implementation is [`GuestMemory::empty`].
1167impl Default for GuestMemory {
1168    fn default() -> Self {
1169        Self::empty()
1170    }
1171}
1172
1173struct Empty;
1174
1175// SAFETY: the mapping is empty, so all requirements are trivially satisfied.
1176unsafe impl GuestMemoryAccess for Empty {
1177    fn mapping(&self) -> Option<NonNull<u8>> {
1178        None
1179    }
1180
1181    fn max_address(&self) -> u64 {
1182        0
1183    }
1184
1185    fn supports_locking(&self) -> bool {
1186        // This implementation trivially supports locking since there are no
1187        // pages to lock.
1188        true
1189    }
1190}
1191
1192#[derive(Debug, Error)]
1193pub enum MultiRegionError {
1194    #[error("region size {0:#x} is not a power of 2")]
1195    NotPowerOfTwo(u64),
1196    #[error("region size {0:#x} is smaller than a page")]
1197    RegionSizeTooSmall(u64),
1198    #[error(
1199        "too many regions ({region_count}) for region size {region_size:#x}; max is {max_region_count}"
1200    )]
1201    TooManyRegions {
1202        region_count: usize,
1203        max_region_count: usize,
1204        region_size: u64,
1205    },
1206    #[error("backing size {backing_size:#x} is too large for region size {region_size:#x}")]
1207    BackingTooLarge { backing_size: u64, region_size: u64 },
1208}
1209
1210/// The RCU domain memory accesses occur under. Updates to any memory access
1211/// bitmaps must be synchronized under this domain.
1212///
1213/// See [`GuestMemoryAccess::access_bitmap`] for more details.
1214///
1215/// This is currently the global domain, but this is reexported here to make
1216/// calling code clearer.
1217#[cfg(feature = "bitmap")]
1218pub fn rcu() -> minircu::RcuDomain {
1219    // Use the global domain unless we find a reason to do something else.
1220    minircu::global()
1221}
1222
1223impl GuestMemory {
1224    /// Returns a new instance using `imp` as the backing.
1225    ///
1226    /// `debug_name` is used to specify which guest memory is being accessed in
1227    /// error messages.
1228    pub fn new(debug_name: impl Into<Arc<str>>, imp: impl GuestMemoryAccess) -> Self {
1229        // Install signal handlers on unix if a mapping is present.
1230        //
1231        // Skip this on miri even when there is a mapping, since the mapping may
1232        // never be accessed by the code under test.
1233        if imp.mapping().is_some() && !cfg!(miri) {
1234            trycopy::initialize_try_copy();
1235        }
1236        Self::new_inner(debug_name.into(), imp, false)
1237    }
1238
1239    fn new_inner(debug_name: Arc<str>, imp: impl GuestMemoryAccess, allocated: bool) -> Self {
1240        let regions = vec![MemoryRegion::new(&imp)];
1241        let supports_locking = imp.supports_locking();
1242        Self {
1243            inner: Arc::new(GuestMemoryInner {
1244                imp,
1245                debug_name,
1246                region_def: RegionDefinition {
1247                    invalid_mask: 1 << 63,
1248                    region_mask: !0 >> 1,
1249                    region_bits: 63, // right shift of 64 isn't valid, so restrict the space
1250                },
1251                regions,
1252                allocated,
1253                supports_locking,
1254            }),
1255        }
1256    }
1257
1258    /// Creates a new multi-region guest memory, made up of multiple mappings.
1259    /// This allows you to create a very large sparse layout (up to the limits
1260    /// of the VM's physical address space) without having to allocate an
1261    /// enormous amount of virtual address space.
1262    ///
1263    /// Each region will be `region_size` bytes and will start immediately after
1264    /// the last one. This must be a power of two, be at least a page in size,
1265    /// and cannot fill the full 64-bit address space.
1266    ///
1267    /// `imps` must be a list of [`GuestMemoryAccess`] implementations, one for
1268    /// each region. Use `None` if the corresponding region is empty.
1269    ///
1270    /// A region's mapping cannot fully fill the region. This is necessary to
1271    /// avoid callers expecting to be able to access a memory range that spans
1272    /// two regions.
1273    pub fn new_multi_region(
1274        debug_name: impl Into<Arc<str>>,
1275        region_size: u64,
1276        mut imps: Vec<Option<impl GuestMemoryAccess>>,
1277    ) -> Result<Self, MultiRegionError> {
1278        // Install signal handlers.
1279        trycopy::initialize_try_copy();
1280
1281        if !region_size.is_power_of_two() {
1282            return Err(MultiRegionError::NotPowerOfTwo(region_size));
1283        }
1284        if region_size < PAGE_SIZE64 {
1285            return Err(MultiRegionError::RegionSizeTooSmall(region_size));
1286        }
1287        let region_bits = region_size.trailing_zeros();
1288
1289        let max_region_count = 1 << (63 - region_bits);
1290
1291        let region_count = imps.len().next_power_of_two();
1292        if region_count > max_region_count {
1293            return Err(MultiRegionError::TooManyRegions {
1294                region_count,
1295                max_region_count,
1296                region_size,
1297            });
1298        }
1299
1300        let valid_bits = region_bits + region_count.trailing_zeros();
1301        assert!(valid_bits < 64);
1302        let invalid_mask = !0 << valid_bits;
1303
1304        let mut regions = vec![MemoryRegion::default(); region_count];
1305        for (imp, region) in imps.iter().zip(&mut regions) {
1306            let Some(imp) = imp else { continue };
1307            let backing_size = imp.max_address();
1308            if backing_size > region_size {
1309                return Err(MultiRegionError::BackingTooLarge {
1310                    backing_size,
1311                    region_size,
1312                });
1313            }
1314            *region = MemoryRegion::new(imp);
1315        }
1316
1317        let region_def = RegionDefinition {
1318            invalid_mask,
1319            region_mask: region_size - 1,
1320            region_bits,
1321        };
1322
1323        imps.resize_with(region_count, || None);
1324        // Locking is only supported if every backing region supports it.
1325        let supports_locking = imps
1326            .iter()
1327            .flatten()
1328            .all(GuestMemoryAccess::supports_locking);
1329        let imp = MultiRegionGuestMemoryAccess { imps, region_def };
1330
1331        let inner = GuestMemoryInner {
1332            debug_name: debug_name.into(),
1333            region_def,
1334            regions,
1335            imp,
1336            allocated: false,
1337            supports_locking,
1338        };
1339
1340        Ok(Self {
1341            inner: Arc::new(inner),
1342        })
1343    }
1344
1345    /// Allocates a guest memory object on the heap with the given size in
1346    /// bytes.
1347    ///
1348    /// `size` will be rounded up to the page size. The backing buffer will be
1349    /// page aligned.
1350    ///
1351    /// The debug name in errors will be "heap". If you want to provide a
1352    /// different debug name, manually use `GuestMemory::new` with
1353    /// [`AlignedHeapMemory`].
1354    pub fn allocate(size: usize) -> Self {
1355        Self::new_inner("heap".into(), AlignedHeapMemory::new(size), true)
1356    }
1357
1358    /// If this memory is unaliased and was created via
1359    /// [`GuestMemory::allocate`], returns the backing buffer.
1360    ///
1361    /// Returns `Err(self)` if there are other references to this memory (via
1362    /// `clone()`).
1363    pub fn into_inner_buf(self) -> Result<AlignedHeapMemory, Self> {
1364        if !self.inner.allocated {
1365            return Err(self);
1366        }
1367        // FUTURE: consider using `Any` and `Arc::downcast` once trait upcasting is stable.
1368        // SAFETY: the inner implementation is guaranteed to be a `AlignedHeapMemory`.
1369        let inner = unsafe {
1370            Arc::<GuestMemoryInner<AlignedHeapMemory>>::from_raw(Arc::into_raw(self.inner).cast())
1371        };
1372        let inner = Arc::try_unwrap(inner).map_err(|inner| Self { inner })?;
1373        Ok(inner.imp)
1374    }
1375
1376    /// If this memory was created via [`GuestMemory::allocate`], returns a slice to
1377    /// the allocated buffer.
1378    pub fn inner_buf(&self) -> Option<&[AtomicU8]> {
1379        if !self.inner.allocated {
1380            return None;
1381        }
1382        // FUTURE: consider using `<dyn Any>::downcast` once trait upcasting is stable.
1383        // SAFETY: the inner implementation is guaranteed to be a `AlignedHeapMemory`.
1384        let inner = unsafe { &*core::ptr::from_ref(&self.inner.imp).cast::<AlignedHeapMemory>() };
1385        Some(inner)
1386    }
1387
1388    /// If this memory was created via [`GuestMemory::allocate`] and there are
1389    /// no other references to it, returns a mutable slice to the backing
1390    /// buffer.
1391    pub fn inner_buf_mut(&mut self) -> Option<&mut [u8]> {
1392        if !self.inner.allocated {
1393            return None;
1394        }
1395        let inner = Arc::get_mut(&mut self.inner)?;
1396        // FUTURE: consider using `<dyn Any>::downcast` once trait upcasting is stable.
1397        // SAFETY: the inner implementation is guaranteed to be a `AlignedHeapMemory`.
1398        let imp = unsafe { &mut *core::ptr::from_mut(&mut inner.imp).cast::<AlignedHeapMemory>() };
1399        Some(imp.as_mut())
1400    }
1401
1402    /// Returns an empty guest memory, which fails every operation.
1403    pub fn empty() -> Self {
1404        GuestMemory::new("empty", Empty)
1405    }
1406
1407    fn wrap_err(
1408        &self,
1409        gpa_len: Option<(u64, u64)>,
1410        op: GuestMemoryOperation,
1411        err: GuestMemoryBackingError,
1412    ) -> GuestMemoryError {
1413        let range = gpa_len.map(|(gpa, len)| gpa..gpa.wrapping_add(len));
1414        GuestMemoryError::new(&self.inner.debug_name, range, op, err)
1415    }
1416
1417    fn with_op<T>(
1418        &self,
1419        gpa_len: Option<(u64, u64)>,
1420        op: GuestMemoryOperation,
1421        f: impl FnOnce() -> Result<T, GuestMemoryBackingError>,
1422    ) -> Result<T, GuestMemoryError> {
1423        f().map_err(|err| self.wrap_err(gpa_len, op, err))
1424    }
1425
1426    /// Creates a smaller view into guest memory, constraining accesses within the new boundaries. For smaller ranges,
1427    /// some memory implementations (e.g. HDV) may choose to lock the pages into memory for faster access. Locking
1428    /// random guest memory may cause issues, so only opt in to this behavior when the range can be considered "owned"
1429    /// by the caller.
1430    pub fn subrange(
1431        &self,
1432        offset: u64,
1433        len: u64,
1434        allow_preemptive_locking: bool,
1435    ) -> Result<GuestMemory, GuestMemoryError> {
1436        self.with_op(Some((offset, len)), GuestMemoryOperation::Subrange, || {
1437            if let Some(guest_memory) =
1438                self.inner
1439                    .imp
1440                    .subrange(offset, len, allow_preemptive_locking)?
1441            {
1442                Ok(guest_memory)
1443            } else {
1444                create_memory_subrange(self.inner.clone(), offset, len, allow_preemptive_locking)
1445            }
1446        })
1447    }
1448
1449    /// Returns a subrange where pages from the subrange can be locked.
1450    pub fn lockable_subrange(
1451        &self,
1452        offset: u64,
1453        len: u64,
1454    ) -> Result<GuestMemory, GuestMemoryError> {
1455        // TODO: Enforce subrange is actually lockable.
1456        self.subrange(offset, len, true)
1457    }
1458
1459    /// Returns the mapping for all of guest memory.
1460    ///
1461    /// Returns `None` if there is more than one region or if the memory is not
1462    /// mapped.
1463    pub fn full_mapping(&self) -> Option<(*mut u8, usize)> {
1464        if let [region] = self.inner.regions.as_slice() {
1465            #[cfg(feature = "bitmap")]
1466            if region.bitmaps.is_some() {
1467                return None;
1468            }
1469            region
1470                .mapping
1471                .map(|SendPtrU8(ptr)| (ptr.as_ptr(), region.len as usize))
1472        } else {
1473            None
1474        }
1475    }
1476
1477    /// Gets the IO address for DMAing to `gpa` from a user-mode driver not
1478    /// going through an IOMMU.
1479    pub fn iova(&self, gpa: u64) -> Option<u64> {
1480        let (region, offset, _) = self.inner.region(gpa, 1).ok()?;
1481        Some(region.base_iova? + offset)
1482    }
1483
1484    /// Returns a sharing object if this memory supports
1485    /// file-based sharing. See [`GuestMemorySharing`].
1486    pub fn sharing(&self) -> Option<GuestMemorySharing> {
1487        self.inner.imp.sharing()
1488    }
1489
1490    /// Returns whether this memory supports locking pages via
1491    /// [`lock_gpns`](Self::lock_gpns) and [`lock_range`](Self::lock_range).
1492    ///
1493    /// Memory behind an emulated IOMMU has no stable host mapping and cannot
1494    /// be locked; zero-copy callers should check this and fall back to a
1495    /// copying path when it returns `false`. This is authoritative: when it
1496    /// returns `false`, [`lock_gpns`](Self::lock_gpns) and
1497    /// [`lock_range`](Self::lock_range) fail with a `NotLockable` error.
1498    pub fn supports_locking(&self) -> bool {
1499        self.inner.supports_locking
1500    }
1501
1502    /// Gets a pointer to the VA range for `gpa..gpa+len`.
1503    ///
1504    /// Returns `Ok(None)` if there is no mapping. Returns `Err(_)` if the
1505    /// memory is out of range.
1506    fn mapping_range(
1507        &self,
1508        access_type: AccessType,
1509        gpa: u64,
1510        len: usize,
1511    ) -> Result<Option<*mut u8>, GuestMemoryBackingError> {
1512        let (region, offset, _) = self.inner.region(gpa, len as u64)?;
1513        if let Some(SendPtrU8(ptr)) = region.mapping {
1514            loop {
1515                // SAFETY: offset + len is checked by `region()` to be inside the VA range.
1516                let fault_offset = unsafe {
1517                    match region.check_access(access_type, offset, len as u64) {
1518                        Ok(()) => return Ok(Some(ptr.as_ptr().add(offset as usize))),
1519                        Err(n) => n,
1520                    }
1521                };
1522
1523                // Resolve the fault and try again.
1524                match self.inner.imp.page_fault(
1525                    gpa + fault_offset,
1526                    len - fault_offset as usize,
1527                    access_type == AccessType::Write,
1528                    true,
1529                ) {
1530                    PageFaultAction::Fail(err) => {
1531                        return Err(GuestMemoryBackingError::new(
1532                            err.kind,
1533                            gpa + fault_offset,
1534                            err.err,
1535                        ));
1536                    }
1537                    PageFaultAction::Retry => {}
1538                    PageFaultAction::Fallback => break,
1539                }
1540            }
1541        }
1542        Ok(None)
1543    }
1544
1545    /// Runs `f` with a pointer to the mapped memory. If `f` fails, tries to
1546    /// resolve the fault (failing on error), then loops.
1547    ///
1548    /// If there is no mapping for the memory, or if the fault handler requests
1549    /// it, call `fallback` instead. `fallback` will not be called unless `gpa`
1550    /// and `len` are in range.
1551    fn run_on_mapping<T, P>(
1552        &self,
1553        access_type: AccessType,
1554        gpa: u64,
1555        len: usize,
1556        mut param: P,
1557        mut f: impl FnMut(&mut P, *mut u8) -> Result<T, trycopy::MemoryError>,
1558        fallback: impl FnOnce(&mut P) -> Result<T, GuestMemoryBackingError>,
1559    ) -> Result<T, GuestMemoryBackingError> {
1560        let op = || {
1561            let Some(mapping) = self.mapping_range(access_type, gpa, len)? else {
1562                return fallback(&mut param);
1563            };
1564
1565            // Try until the fault fails to resolve.
1566            loop {
1567                match f(&mut param, mapping) {
1568                    Ok(t) => return Ok(t),
1569                    Err(fault) => {
1570                        match self.inner.imp.page_fault(
1571                            gpa + fault.offset() as u64,
1572                            len - fault.offset(),
1573                            access_type == AccessType::Write,
1574                            false,
1575                        ) {
1576                            PageFaultAction::Fail(err) => {
1577                                return Err(GuestMemoryBackingError::new(
1578                                    err.kind,
1579                                    gpa + fault.offset() as u64,
1580                                    err.err,
1581                                ));
1582                            }
1583                            PageFaultAction::Retry => {}
1584                            PageFaultAction::Fallback => return fallback(&mut param),
1585                        }
1586                    }
1587                }
1588            }
1589        };
1590        // If the `bitmap` feature is enabled, run the function in an RCU
1591        // critical section. This will allow callers to flush concurrent
1592        // accesses after bitmap updates.
1593        #[cfg(feature = "bitmap")]
1594        return rcu().run(op);
1595        #[cfg(not(feature = "bitmap"))]
1596        op()
1597    }
1598
1599    /// # Safety
1600    ///
1601    /// The caller must ensure that `src`..`src + len` is a valid buffer for reads.
1602    unsafe fn write_ptr(
1603        &self,
1604        gpa: u64,
1605        src: *const u8,
1606        len: usize,
1607    ) -> Result<(), GuestMemoryBackingError> {
1608        if len == 0 {
1609            return Ok(());
1610        }
1611        self.run_on_mapping(
1612            AccessType::Write,
1613            gpa,
1614            len,
1615            (),
1616            |(), dest| {
1617                // SAFETY: dest..dest+len is guaranteed to point to a reserved VA
1618                // range, and src..src+len is guaranteed by the caller to be a valid
1619                // buffer for reads.
1620                unsafe { trycopy::try_copy(src, dest, len) }
1621            },
1622            |()| {
1623                // SAFETY: src..src+len is guaranteed by the caller to point to a valid
1624                // buffer for reads.
1625                unsafe { self.inner.imp.write_fallback(gpa, src, len) }
1626            },
1627        )
1628    }
1629
1630    /// Writes `src` into guest memory at address `gpa`.
1631    pub fn write_at(&self, gpa: u64, src: &[u8]) -> Result<(), GuestMemoryError> {
1632        self.with_op(
1633            Some((gpa, src.len() as u64)),
1634            GuestMemoryOperation::Write,
1635            || self.write_at_inner(gpa, src),
1636        )
1637    }
1638
1639    fn write_at_inner(&self, gpa: u64, src: &[u8]) -> Result<(), GuestMemoryBackingError> {
1640        // SAFETY: `src` is a valid buffer for reads.
1641        unsafe { self.write_ptr(gpa, src.as_ptr(), src.len()) }
1642    }
1643
1644    /// Writes `src` into guest memory at address `gpa`.
1645    pub fn write_from_atomic(&self, gpa: u64, src: &[AtomicU8]) -> Result<(), GuestMemoryError> {
1646        self.with_op(
1647            Some((gpa, src.len() as u64)),
1648            GuestMemoryOperation::Write,
1649            || {
1650                // SAFETY: `src` is a valid buffer for reads.
1651                unsafe { self.write_ptr(gpa, src.as_ptr().cast(), src.len()) }
1652            },
1653        )
1654    }
1655
1656    /// Writes `len` bytes of `val` into guest memory at address `gpa`.
1657    pub fn fill_at(&self, gpa: u64, val: u8, len: usize) -> Result<(), GuestMemoryError> {
1658        self.with_op(Some((gpa, len as u64)), GuestMemoryOperation::Fill, || {
1659            self.fill_at_inner(gpa, val, len)
1660        })
1661    }
1662
1663    fn fill_at_inner(&self, gpa: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
1664        if len == 0 {
1665            return Ok(());
1666        }
1667        self.run_on_mapping(
1668            AccessType::Write,
1669            gpa,
1670            len,
1671            (),
1672            |(), dest| {
1673                // SAFETY: dest..dest+len is guaranteed to point to a reserved VA range.
1674                unsafe { trycopy::try_write_bytes(dest, val, len) }
1675            },
1676            |()| self.inner.imp.fill_fallback(gpa, val, len),
1677        )
1678    }
1679
1680    /// Reads from guest memory into `dest..dest+len`.
1681    ///
1682    /// # Safety
1683    /// The caller must ensure dest..dest+len is a valid buffer for writes.
1684    unsafe fn read_ptr(
1685        &self,
1686        gpa: u64,
1687        dest: *mut u8,
1688        len: usize,
1689    ) -> Result<(), GuestMemoryBackingError> {
1690        if len == 0 {
1691            return Ok(());
1692        }
1693        self.run_on_mapping(
1694            AccessType::Read,
1695            gpa,
1696            len,
1697            (),
1698            |(), src| {
1699                // SAFETY: src..src+len is guaranteed to point to a reserved VA
1700                // range, and dest..dest+len is guaranteed by the caller to be a
1701                // valid buffer for writes.
1702                unsafe { trycopy::try_copy(src, dest, len) }
1703            },
1704            |()| {
1705                // SAFETY: dest..dest+len is guaranteed by the caller to point to a
1706                // valid buffer for writes.
1707                unsafe { self.inner.imp.read_fallback(gpa, dest, len) }
1708            },
1709        )
1710    }
1711
1712    fn read_at_inner(&self, gpa: u64, dest: &mut [u8]) -> Result<(), GuestMemoryBackingError> {
1713        // SAFETY: `dest` is a valid buffer for writes.
1714        unsafe { self.read_ptr(gpa, dest.as_mut_ptr(), dest.len()) }
1715    }
1716
1717    /// Reads from guest memory address `gpa` into `dest`.
1718    pub fn read_at(&self, gpa: u64, dest: &mut [u8]) -> Result<(), GuestMemoryError> {
1719        self.with_op(
1720            Some((gpa, dest.len() as u64)),
1721            GuestMemoryOperation::Read,
1722            || self.read_at_inner(gpa, dest),
1723        )
1724    }
1725
1726    /// Reads from guest memory address `gpa` into `dest`.
1727    pub fn read_to_atomic(&self, gpa: u64, dest: &[AtomicU8]) -> Result<(), GuestMemoryError> {
1728        self.with_op(
1729            Some((gpa, dest.len() as u64)),
1730            GuestMemoryOperation::Read,
1731            // SAFETY: `dest` is a valid buffer for writes.
1732            || unsafe { self.read_ptr(gpa, dest.as_ptr() as *mut u8, dest.len()) },
1733        )
1734    }
1735
1736    /// Writes an object to guest memory at address `gpa`.
1737    ///
1738    /// If the object is 1, 2, 4, or 8 bytes and the address is naturally
1739    /// aligned, then the write will be performed atomically. Here, this means
1740    /// that concurrent readers (via `read_plain`) cannot observe a torn write
1741    /// but will observe either the old or new value.
1742    ///
1743    /// The memory ordering of the write is unspecified.
1744    ///
1745    /// FUTURE: once we are on Rust 1.79, add a method specifically for atomic
1746    /// accesses that const asserts that the size is appropriate.
1747    pub fn write_plain<T: IntoBytes + Immutable + KnownLayout>(
1748        &self,
1749        gpa: u64,
1750        b: &T,
1751    ) -> Result<(), GuestMemoryError> {
1752        // Note that this is const, so the match below will compile out.
1753        let len = size_of::<T>();
1754        self.with_op(Some((gpa, len as u64)), GuestMemoryOperation::Write, || {
1755            self.run_on_mapping(
1756                AccessType::Write,
1757                gpa,
1758                len,
1759                (),
1760                |(), dest| {
1761                    // SAFETY: dest..dest+len is guaranteed to point to
1762                    // a reserved VA range.
1763                    unsafe { trycopy::try_write_volatile(dest.cast(), b) }
1764                },
1765                |()| {
1766                    // SAFETY: b is a valid buffer for reads.
1767                    unsafe {
1768                        self.inner
1769                            .imp
1770                            .write_fallback(gpa, b.as_bytes().as_ptr(), len)
1771                    }
1772                },
1773            )
1774        })
1775    }
1776
1777    /// Attempts a sequentially-consistent compare exchange of the value at `gpa`.
1778    pub fn compare_exchange<T: IntoBytes + FromBytes + Immutable + KnownLayout + Copy>(
1779        &self,
1780        gpa: u64,
1781        current: T,
1782        new: T,
1783    ) -> Result<Result<T, T>, GuestMemoryError> {
1784        const {
1785            assert!(matches!(size_of::<T>(), 1 | 2 | 4 | 8));
1786            assert!(align_of::<T>() >= size_of::<T>());
1787        };
1788        let len = size_of_val(&new);
1789        self.with_op(
1790            Some((gpa, len as u64)),
1791            GuestMemoryOperation::CompareExchange,
1792            || {
1793                // Assume that if write is allowed, then read is allowed.
1794                self.run_on_mapping(
1795                    AccessType::Write,
1796                    gpa,
1797                    len,
1798                    (),
1799                    |(), dest| {
1800                        // SAFETY: dest..dest+len is guaranteed by the caller to be a valid
1801                        // buffer for writes.
1802                        unsafe { trycopy::try_compare_exchange(dest.cast(), current, new) }
1803                    },
1804                    |()| {
1805                        let mut current = current;
1806                        let success = self.inner.imp.compare_exchange_fallback(
1807                            gpa,
1808                            current.as_mut_bytes(),
1809                            new.as_bytes(),
1810                        )?;
1811
1812                        Ok(if success { Ok(new) } else { Err(current) })
1813                    },
1814                )
1815            },
1816        )
1817    }
1818
1819    /// Reads an object from guest memory at address `gpa`.
1820    ///
1821    /// If the object is 1, 2, 4, or 8 bytes and the address is naturally
1822    /// aligned, then the read will be performed atomically. Here, this means
1823    /// that when there is a concurrent writer, callers will observe either the
1824    /// old or new value, but not a torn read.
1825    ///
1826    /// The memory ordering of the read is unspecified.
1827    ///
1828    /// FUTURE: once we are on Rust 1.79, add a method specifically for atomic
1829    /// accesses that const asserts that the size is appropriate.
1830    pub fn read_plain<T: FromBytes + Immutable + KnownLayout>(
1831        &self,
1832        gpa: u64,
1833    ) -> Result<T, GuestMemoryError> {
1834        self.with_op(
1835            Some((gpa, size_of::<T>() as u64)),
1836            GuestMemoryOperation::Read,
1837            || self.read_plain_inner(gpa),
1838        )
1839    }
1840
1841    fn read_plain_inner<T: FromBytes + Immutable + KnownLayout>(
1842        &self,
1843        gpa: u64,
1844    ) -> Result<T, GuestMemoryBackingError> {
1845        let len = size_of::<T>();
1846        self.run_on_mapping(
1847            AccessType::Read,
1848            gpa,
1849            len,
1850            (),
1851            |(), src| {
1852                // SAFETY: src..src+len is guaranteed to point to a reserved VA
1853                // range.
1854                unsafe { trycopy::try_read_volatile(src.cast::<T>()) }
1855            },
1856            |()| {
1857                let mut obj = std::mem::MaybeUninit::<T>::zeroed();
1858                // SAFETY: dest..dest+len is guaranteed by the caller to point to a
1859                // valid buffer for writes.
1860                unsafe {
1861                    self.inner
1862                        .imp
1863                        .read_fallback(gpa, obj.as_mut_ptr().cast(), len)?;
1864                }
1865                // SAFETY: `obj` was fully initialized by `read_fallback`.
1866                Ok(unsafe { obj.assume_init() })
1867            },
1868        )
1869    }
1870
1871    fn probe_page_for_lock(
1872        &self,
1873        access: AccessType,
1874        with_kernel_access: bool,
1875        gpa: u64,
1876    ) -> Result<*const AtomicU8, GuestMemoryBackingError> {
1877        let (region, offset, _) = self.inner.region(gpa, 1)?;
1878        let Some(SendPtrU8(ptr)) = region.mapping else {
1879            return Err(GuestMemoryBackingError::other(gpa, NotLockable));
1880        };
1881        // Ensure the virtual address can be exposed.
1882        if with_kernel_access {
1883            self.inner.imp.expose_va(gpa, 1)?;
1884        }
1885        // Fault the page in for the access the caller will perform through the
1886        // returned pointer. A write lock must fault for *write* so that a
1887        // read-only-until-write backing (e.g. Windows soft large pages, which
1888        // map guest RAM read-only until the first write raises it to
1889        // read-write) is made writable before the caller writes; otherwise the
1890        // write would hit a read-only page and access-violate. A read-only lock
1891        // only faults for read, so it neither forces writability (which
1892        // genuinely read-only memory would reject) nor needlessly promotes soft
1893        // large pages.
1894        match access {
1895            AccessType::Read => {
1896                self.read_plain_inner::<u8>(gpa)?;
1897            }
1898            AccessType::Write => {
1899                self.probe_mapped_page_writable(gpa)?;
1900            }
1901        }
1902        // SAFETY: the read_at call includes a check that ensures that
1903        // `gpa` is in the VA range.
1904        let page = unsafe { ptr.as_ptr().add(offset as usize) };
1905        Ok(page.cast())
1906    }
1907
1908    /// Faults a single **mapped** guest page in for write without changing its
1909    /// contents, so that a read-only-until-write backing (e.g. Windows soft
1910    /// large pages) is raised to read-write before a write lock hands out a
1911    /// pointer to it.
1912    ///
1913    /// This is a helper for the locking path and requires the page to be backed
1914    /// by a host mapping. It works by performing a no-op compare-exchange
1915    /// against the mapping: on a read-only page the locked read-modify-write
1916    /// triggers the write fault handler (raising the window to read-write), and
1917    /// once writable the exchange leaves the value unchanged (writing back the
1918    /// same value on a match and nothing on a mismatch).
1919    ///
1920    /// It deliberately does **not** fall back to
1921    /// [`GuestMemoryAccess::compare_exchange_fallback`]: the write-probe is
1922    /// meaningless without a mapping (there is nothing to fault in), and not all
1923    /// backings implement that fallback. Callers must only reach this after
1924    /// confirming the page is mapped, as the lock path does via
1925    /// [`GuestMemory::supports_locking`]. A non-mapping backing therefore fails
1926    /// with a clear error rather than silently taking an unsupported path.
1927    fn probe_mapped_page_writable(&self, gpa: u64) -> Result<(), GuestMemoryBackingError> {
1928        self.run_on_mapping(
1929            AccessType::Write,
1930            gpa,
1931            1,
1932            (),
1933            |(), dest| {
1934                // SAFETY: dest points to a reserved VA range of at least one byte.
1935                unsafe { trycopy::try_compare_exchange::<u8>(dest.cast(), 0, 0).map(|_| ()) }
1936            },
1937            |()| {
1938                // Only reachable without a mapping (or if a backing's
1939                // `page_fault` requests the fallback). The write-probe only
1940                // makes sense for mapping-based backings, so fail clearly here
1941                // instead of invoking `compare_exchange_fallback`, which many
1942                // backings do not implement.
1943                Err(GuestMemoryBackingError::other(gpa, NotLockable))
1944            },
1945        )
1946    }
1947
1948    /// Locks the specified guest pages (by GPN), returning handles that expose
1949    /// their host VA for zero-copy access.
1950    ///
1951    /// `access` selects whether the pages will be read from or written to: a
1952    /// write lock faults each page in for write so that a
1953    /// read-only-until-write backing (e.g. Windows soft large pages) is raised
1954    /// to read-write before the caller writes through the returned pointer,
1955    /// while a read-only lock (e.g. read-only DMA) only faults for read.
1956    pub fn lock_gpns(
1957        &self,
1958        access: AccessType,
1959        with_kernel_access: bool,
1960        gpns: &[u64],
1961    ) -> Result<LockedPages, GuestMemoryError> {
1962        self.with_op(None, GuestMemoryOperation::Lock, || {
1963            if !self.inner.supports_locking {
1964                let gpa = gpns.first().map_or(0, |&gpn| gpn.wrapping_mul(PAGE_SIZE64));
1965                return Err(GuestMemoryBackingError::other(gpa, NotLockable));
1966            }
1967            let mut pages = Vec::with_capacity(gpns.len());
1968            for &gpn in gpns {
1969                let gpa = gpn_to_gpa(gpn).map_err(GuestMemoryBackingError::gpn)?;
1970                let page = self.probe_page_for_lock(access, with_kernel_access, gpa)?;
1971                pages.push(PagePtr(page));
1972            }
1973            let store_gpns = self.inner.imp.lock_gpns(gpns)?;
1974            Ok(LockedPages {
1975                pages: pages.into_boxed_slice(),
1976                gpns: store_gpns.then(|| gpns.to_vec().into_boxed_slice()),
1977                mem: self.inner.clone(),
1978            })
1979        })
1980    }
1981
1982    pub fn probe_gpns(&self, gpns: &[u64]) -> Result<(), GuestMemoryError> {
1983        self.with_op(None, GuestMemoryOperation::Probe, || {
1984            for &gpn in gpns {
1985                self.read_plain_inner::<u8>(
1986                    gpn_to_gpa(gpn).map_err(GuestMemoryBackingError::gpn)?,
1987                )?;
1988            }
1989            Ok(())
1990        })
1991    }
1992
1993    /// Check if a given GPA is readable or not.
1994    pub fn probe_gpa_readable(&self, gpa: u64) -> Result<(), GuestMemoryErrorKind> {
1995        let mut b = [0];
1996        self.read_at_inner(gpa, &mut b).map_err(|err| err.kind)
1997    }
1998
1999    /// Check if a given GPA is writeable or not.
2000    pub fn probe_gpa_writable(&self, gpa: u64) -> Result<(), GuestMemoryErrorKind> {
2001        let _ = self
2002            .compare_exchange(gpa, 0u8, 0)
2003            .map_err(|err| err.kind())?;
2004        Ok(())
2005    }
2006
2007    /// Gets a slice of guest memory assuming the memory was already locked via
2008    /// [`GuestMemory::lock_gpns`].
2009    ///
2010    /// This is dangerous--if the pages have not been locked, then it could
2011    /// cause an access violation or guest memory corruption.
2012    ///
2013    /// Note that this is not `unsafe` since this cannot cause memory corruption
2014    /// in this process. Even if there is an access violation, the underlying VA
2015    /// space is known to be reserved.
2016    ///
2017    /// Panics if the requested buffer is out of range.
2018    fn dangerous_access_pre_locked_memory(&self, gpa: u64, len: usize) -> &[AtomicU8] {
2019        let addr = self
2020            .mapping_range(AccessType::Write, gpa, len)
2021            .unwrap()
2022            .unwrap();
2023        // SAFETY: addr..addr+len is checked above to be a valid VA range. It's
2024        // possible some of the pages aren't mapped and will cause AVs at
2025        // runtime when accessed, but, as discussed above, at a language level
2026        // this cannot cause any safety issues.
2027        unsafe { std::slice::from_raw_parts(addr.cast(), len) }
2028    }
2029
2030    fn op_range<F: FnMut(u64, Range<usize>) -> Result<(), GuestMemoryBackingError>>(
2031        &self,
2032        op: GuestMemoryOperation,
2033        range: &PagedRange<'_>,
2034        mut f: F,
2035    ) -> Result<(), GuestMemoryError> {
2036        self.with_op(None, op, || {
2037            let gpns = range.gpns();
2038            let offset = range.offset();
2039
2040            // Perform the operation in three phases: the first page (if it is not a
2041            // full page), the full pages, and the last page (if it is not a full
2042            // page).
2043            let mut byte_index = 0;
2044            let mut len = range.len();
2045            let mut page = 0;
2046            if !offset.is_multiple_of(PAGE_SIZE) {
2047                let head_len = std::cmp::min(len, PAGE_SIZE - (offset % PAGE_SIZE));
2048                let addr = gpn_to_gpa(gpns[page]).map_err(GuestMemoryBackingError::gpn)?
2049                    + offset as u64 % PAGE_SIZE64;
2050                f(addr, byte_index..byte_index + head_len)?;
2051                byte_index += head_len;
2052                len -= head_len;
2053                page += 1;
2054            }
2055            while len >= PAGE_SIZE {
2056                f(
2057                    gpn_to_gpa(gpns[page]).map_err(GuestMemoryBackingError::gpn)?,
2058                    byte_index..byte_index + PAGE_SIZE,
2059                )?;
2060                byte_index += PAGE_SIZE;
2061                len -= PAGE_SIZE;
2062                page += 1;
2063            }
2064            if len > 0 {
2065                f(
2066                    gpn_to_gpa(gpns[page]).map_err(GuestMemoryBackingError::gpn)?,
2067                    byte_index..byte_index + len,
2068                )?;
2069            }
2070
2071            Ok(())
2072        })
2073    }
2074
2075    pub fn write_range(&self, range: &PagedRange<'_>, data: &[u8]) -> Result<(), GuestMemoryError> {
2076        assert!(data.len() == range.len());
2077        self.op_range(GuestMemoryOperation::Write, range, move |addr, r| {
2078            self.write_at_inner(addr, &data[r])
2079        })
2080    }
2081
2082    pub fn fill_range(&self, range: &PagedRange<'_>, val: u8) -> Result<(), GuestMemoryError> {
2083        self.op_range(GuestMemoryOperation::Fill, range, move |addr, r| {
2084            self.fill_at_inner(addr, val, r.len())
2085        })
2086    }
2087
2088    pub fn zero_range(&self, range: &PagedRange<'_>) -> Result<(), GuestMemoryError> {
2089        self.op_range(GuestMemoryOperation::Fill, range, move |addr, r| {
2090            self.fill_at_inner(addr, 0, r.len())
2091        })
2092    }
2093
2094    pub fn read_range(
2095        &self,
2096        range: &PagedRange<'_>,
2097        data: &mut [u8],
2098    ) -> Result<(), GuestMemoryError> {
2099        assert!(data.len() == range.len());
2100        self.op_range(GuestMemoryOperation::Read, range, move |addr, r| {
2101            self.read_at_inner(addr, &mut data[r])
2102        })
2103    }
2104
2105    pub fn write_range_from_atomic(
2106        &self,
2107        range: &PagedRange<'_>,
2108        data: &[AtomicU8],
2109    ) -> Result<(), GuestMemoryError> {
2110        assert!(data.len() == range.len());
2111        self.op_range(GuestMemoryOperation::Write, range, move |addr, r| {
2112            let src = &data[r];
2113            // SAFETY: `src` is a valid buffer for reads.
2114            unsafe { self.write_ptr(addr, src.as_ptr().cast(), src.len()) }
2115        })
2116    }
2117
2118    pub fn read_range_to_atomic(
2119        &self,
2120        range: &PagedRange<'_>,
2121        data: &[AtomicU8],
2122    ) -> Result<(), GuestMemoryError> {
2123        assert!(data.len() == range.len());
2124        self.op_range(GuestMemoryOperation::Read, range, move |addr, r| {
2125            let dest = &data[r];
2126            // SAFETY: `dest` is a valid buffer for writes.
2127            unsafe { self.read_ptr(addr, dest.as_ptr().cast_mut().cast(), dest.len()) }
2128        })
2129    }
2130
2131    /// Locks the guest pages spanned by the specified `PagedRange`.
2132    ///
2133    /// # Arguments
2134    /// * 'access' - Whether the locked pages will be read from or written to.
2135    ///   A write lock faults each page in for write so that a
2136    ///   read-only-until-write backing (e.g. Windows soft large pages) is
2137    ///   raised to read-write before the caller writes through the returned
2138    ///   VA; a read-only lock (e.g. read-only DMA) only faults for read.
2139    /// * 'paged_range' - The guest memory range to lock.
2140    /// * 'locked_range' - Receives a list of VA ranges to which each contiguous physical sub-range in `paged_range`
2141    ///   has been mapped. Must be initially empty.
2142    pub fn lock_range<'a, T: LockedRange<'a>>(
2143        &'a self,
2144        access: AccessType,
2145        paged_range: PagedRange<'_>,
2146        mut locked_range: T,
2147    ) -> Result<LockedRangeImpl<'a, T>, GuestMemoryError> {
2148        self.with_op(None, GuestMemoryOperation::Lock, || {
2149            let gpns = paged_range.gpns();
2150            if !self.inner.supports_locking {
2151                let gpa = gpns.first().map_or(0, |&gpn| gpn.wrapping_mul(PAGE_SIZE64));
2152                return Err(GuestMemoryBackingError::other(gpa, NotLockable));
2153            }
2154            for &gpn in gpns {
2155                let gpa = gpn_to_gpa(gpn).map_err(GuestMemoryBackingError::gpn)?;
2156                self.probe_page_for_lock(access, true, gpa)?;
2157            }
2158            for range in paged_range.ranges() {
2159                let range = range.map_err(GuestMemoryBackingError::gpn)?;
2160                locked_range.push_sub_range(
2161                    self.dangerous_access_pre_locked_memory(range.start, range.len() as usize),
2162                );
2163            }
2164            let store_gpns = self.inner.imp.lock_gpns(paged_range.gpns())?;
2165            Ok(LockedRangeImpl {
2166                mem: &self.inner,
2167                gpns: store_gpns.then(|| paged_range.gpns().to_vec().into_boxed_slice()),
2168                inner: locked_range,
2169            })
2170        })
2171    }
2172}
2173
2174#[derive(Debug, Copy, Clone, Default)]
2175struct RegionDefinition {
2176    invalid_mask: u64,
2177    region_mask: u64,
2178    region_bits: u32,
2179}
2180
2181impl RegionDefinition {
2182    fn region(&self, gpa: u64, len: u64) -> Result<(usize, u64), GuestMemoryBackingError> {
2183        if (gpa | len) & self.invalid_mask != 0 {
2184            return Err(GuestMemoryBackingError::new(
2185                GuestMemoryErrorKind::OutOfRange,
2186                gpa,
2187                OutOfRange,
2188            ));
2189        }
2190        let offset = gpa & self.region_mask;
2191        if offset.wrapping_add(len) & !self.region_mask != 0 {
2192            return Err(GuestMemoryBackingError::new(
2193                GuestMemoryErrorKind::OutOfRange,
2194                gpa,
2195                OutOfRange,
2196            ));
2197        }
2198        let index = (gpa >> self.region_bits) as usize;
2199        Ok((index, offset))
2200    }
2201}
2202
2203impl GuestMemoryInner {
2204    fn region(
2205        &self,
2206        gpa: u64,
2207        len: u64,
2208    ) -> Result<(&MemoryRegion, u64, usize), GuestMemoryBackingError> {
2209        let (index, offset) = self.region_def.region(gpa, len)?;
2210        let region = &self.regions[index];
2211        if offset + len > region.len {
2212            return Err(GuestMemoryBackingError::new(
2213                GuestMemoryErrorKind::OutOfRange,
2214                gpa,
2215                OutOfRange,
2216            ));
2217        }
2218        Ok((&self.regions[index], offset, index))
2219    }
2220}
2221
2222pub struct LockedPages {
2223    pages: Box<[PagePtr]>,
2224    gpns: Option<Box<[u64]>>,
2225    // maintain a reference to the backing memory
2226    mem: Arc<GuestMemoryInner>,
2227}
2228
2229impl Drop for LockedPages {
2230    fn drop(&mut self) {
2231        if let Some(gpns) = &self.gpns {
2232            self.mem.imp.unlock_gpns(gpns);
2233        }
2234    }
2235}
2236
2237impl Debug for LockedPages {
2238    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2239        f.debug_struct("LockedPages")
2240            .field("page_count", &self.pages.len())
2241            .finish()
2242    }
2243}
2244
2245#[derive(Copy, Clone, Debug)]
2246// Field is read via slice transmute and pointer casts, not actually dead.
2247struct PagePtr(#[expect(dead_code)] *const AtomicU8);
2248
2249// SAFETY: PagePtr is just a pointer with no methods and has no inherent safety
2250// constraints.
2251unsafe impl Send for PagePtr {}
2252// SAFETY: see above comment
2253unsafe impl Sync for PagePtr {}
2254
2255impl LockedPages {
2256    #[inline]
2257    pub fn pages(&self) -> &[&Page] {
2258        // SAFETY: PagePtr is just a pointer to a Page. The pages are kept alive by
2259        // the reference in _mem, and the lifetimes here ensure the LockedPages outlives
2260        // the slice.
2261        unsafe { std::slice::from_raw_parts(self.pages.as_ptr().cast::<&Page>(), self.pages.len()) }
2262    }
2263}
2264
2265impl<'a> AsRef<[&'a Page]> for &'a LockedPages {
2266    fn as_ref(&self) -> &[&'a Page] {
2267        self.pages()
2268    }
2269}
2270
2271/// Represents a range of locked guest pages as an ordered list of the VA sub-ranges
2272/// to which the guest pages are mapped.
2273/// The range may only partially span the first and last page and must fully span all
2274/// intermediate pages.
2275pub trait LockedRange<'a> {
2276    /// Adds a sub-range to this range.
2277    fn push_sub_range(&mut self, sub_range: &'a [AtomicU8]);
2278}
2279
2280pub struct LockedRangeImpl<'a, T: LockedRange<'a>> {
2281    mem: &'a GuestMemoryInner,
2282    gpns: Option<Box<[u64]>>,
2283    inner: T,
2284}
2285
2286impl<'a, T: LockedRange<'a>> LockedRangeImpl<'a, T> {
2287    pub fn get(&self) -> &T {
2288        &self.inner
2289    }
2290
2291    pub fn get_mut(&mut self) -> &mut T {
2292        &mut self.inner
2293    }
2294}
2295
2296impl<'a, T: LockedRange<'a>> Drop for LockedRangeImpl<'a, T> {
2297    fn drop(&mut self) {
2298        if let Some(gpns) = &self.gpns {
2299            self.mem.imp.unlock_gpns(gpns);
2300        }
2301    }
2302}
2303
2304/// Trait implemented to allow mapping and unmapping a region of memory at
2305/// a particular guest address.
2306pub trait MappableGuestMemory: Send + Sync {
2307    /// Maps the memory into the guest.
2308    ///
2309    /// `writable` specifies whether the guest can write to the memory region.
2310    /// If a guest tries to write to a non-writable region, the virtual
2311    /// processor will exit for MMIO handling.
2312    fn map_to_guest(&mut self, gpa: u64, writable: bool) -> io::Result<()>;
2313
2314    fn unmap_from_guest(&mut self);
2315}
2316
2317/// Trait implemented for a region of memory that can have memory mapped into
2318/// it.
2319pub trait MappedMemoryRegion: Send + Sync {
2320    /// Maps an object at `offset` in the region.
2321    ///
2322    /// Behaves like mmap--overwrites and splits existing mappings.
2323    fn map(
2324        &self,
2325        offset: usize,
2326        section: &dyn AsMappableRef,
2327        file_offset: u64,
2328        len: usize,
2329        writable: bool,
2330    ) -> io::Result<()>;
2331
2332    /// Unmaps any mappings in the specified range within the region.
2333    fn unmap(&self, offset: usize, len: usize) -> io::Result<()>;
2334}
2335
2336/// Trait implemented to allow the creation of memory regions.
2337pub trait MemoryMapper: Send + Sync {
2338    /// Creates a new memory region that can later be mapped into the guest.
2339    ///
2340    /// Returns both an interface for mapping/unmapping the region and for
2341    /// adding internal mappings.
2342    fn new_region(
2343        &self,
2344        len: usize,
2345        debug_name: String,
2346    ) -> io::Result<(Box<dyn MappableGuestMemory>, Arc<dyn MappedMemoryRegion>)>;
2347}
2348
2349/// Doorbell provides a mechanism to register for notifications on writes to specific addresses in guest memory.
2350pub trait DoorbellRegistration: Send + Sync {
2351    /// Register a doorbell event.
2352    fn register_doorbell(
2353        &self,
2354        guest_address: u64,
2355        value: Option<u64>,
2356        length: Option<u32>,
2357        event: &Event,
2358    ) -> io::Result<Box<dyn Send + Sync>>;
2359}
2360
2361/// Trait to map a ROM at one or more locations in guest memory.
2362pub trait MapRom: Send + Sync {
2363    /// Maps the specified portion of the ROM into guest memory at `gpa`.
2364    ///
2365    /// The returned object will implicitly unmap the ROM when dropped.
2366    fn map_rom(&self, gpa: u64, offset: u64, len: u64) -> io::Result<Box<dyn UnmapRom>>;
2367
2368    /// Returns the length of the ROM in bytes.
2369    fn len(&self) -> u64;
2370}
2371
2372/// Trait to unmap a ROM from guest memory.
2373pub trait UnmapRom: Send + Sync {
2374    /// Unmaps the ROM from guest memory.
2375    fn unmap_rom(self);
2376}
2377
2378#[cfg(test)]
2379#[expect(clippy::undocumented_unsafe_blocks)]
2380mod tests {
2381    use crate::GuestMemory;
2382    use crate::PAGE_SIZE64;
2383    use crate::PageFaultAction;
2384    use crate::PageFaultError;
2385
2386    use sparse_mmap::SparseMapping;
2387    use std::ptr::NonNull;
2388    use std::sync::Arc;
2389    use thiserror::Error;
2390
2391    /// An implementation of a GuestMemoryAccess trait that expects all of
2392    /// guest memory to be mapped at a given base, with mmap or the Windows
2393    /// equivalent. Pages that are not backed by RAM will return failure
2394    /// when attempting to access them.
2395    pub struct GuestMemoryMapping {
2396        mapping: SparseMapping,
2397        #[cfg(feature = "bitmap")]
2398        bitmap: Option<Vec<u8>>,
2399    }
2400
2401    unsafe impl crate::GuestMemoryAccess for GuestMemoryMapping {
2402        fn mapping(&self) -> Option<NonNull<u8>> {
2403            NonNull::new(self.mapping.as_ptr().cast())
2404        }
2405
2406        fn max_address(&self) -> u64 {
2407            self.mapping.len() as u64
2408        }
2409
2410        #[cfg(feature = "bitmap")]
2411        fn access_bitmap(&self) -> Option<crate::BitmapInfo> {
2412            self.bitmap.as_ref().map(|bm| crate::BitmapInfo {
2413                read_bitmap: NonNull::new(bm.as_ptr().cast_mut()).unwrap(),
2414                write_bitmap: NonNull::new(bm.as_ptr().cast_mut()).unwrap(),
2415                bit_offset: 0,
2416            })
2417        }
2418    }
2419
2420    const PAGE_SIZE: usize = 4096;
2421    const SIZE_1MB: usize = 1048576;
2422
2423    /// Create a test guest layout:
2424    /// 0           -> 1MB          RAM
2425    /// 1MB         -> 2MB          empty
2426    /// 2MB         -> 3MB          RAM
2427    /// 3MB         -> 3MB + 4K     empty
2428    /// 3MB + 4K    -> 4MB          RAM
2429    fn create_test_mapping() -> GuestMemoryMapping {
2430        let mapping = SparseMapping::new(SIZE_1MB * 4).unwrap();
2431        mapping.alloc(0, SIZE_1MB).unwrap();
2432        mapping.alloc(2 * SIZE_1MB, SIZE_1MB).unwrap();
2433        mapping
2434            .alloc(3 * SIZE_1MB + PAGE_SIZE, SIZE_1MB - PAGE_SIZE)
2435            .unwrap();
2436
2437        GuestMemoryMapping {
2438            mapping,
2439            #[cfg(feature = "bitmap")]
2440            bitmap: None,
2441        }
2442    }
2443
2444    #[test]
2445    fn test_basic_read_write() {
2446        let mapping = create_test_mapping();
2447        let gm = GuestMemory::new("test", mapping);
2448
2449        // Test reading at 0.
2450        let addr = 0;
2451        let result = gm.read_plain::<u8>(addr);
2452        assert_eq!(result.unwrap(), 0);
2453
2454        // Test read/write to first page
2455        let write_buffer = [1, 2, 3, 4, 5];
2456        let mut read_buffer = [0; 5];
2457        gm.write_at(0, &write_buffer).unwrap();
2458        gm.read_at(0, &mut read_buffer).unwrap();
2459        assert_eq!(write_buffer, read_buffer);
2460        assert_eq!(gm.read_plain::<u8>(0).unwrap(), 1);
2461        assert_eq!(gm.read_plain::<u8>(1).unwrap(), 2);
2462        assert_eq!(gm.read_plain::<u8>(2).unwrap(), 3);
2463        assert_eq!(gm.read_plain::<u8>(3).unwrap(), 4);
2464        assert_eq!(gm.read_plain::<u8>(4).unwrap(), 5);
2465
2466        // Test read/write to page at 2MB
2467        let addr = 2 * SIZE_1MB as u64;
2468        let write_buffer: Vec<u8> = (0..PAGE_SIZE).map(|x| x as u8).collect();
2469        let mut read_buffer: Vec<u8> = (0..PAGE_SIZE).map(|_| 0).collect();
2470        gm.write_at(addr, write_buffer.as_slice()).unwrap();
2471        gm.read_at(addr, read_buffer.as_mut_slice()).unwrap();
2472        assert_eq!(write_buffer, read_buffer);
2473
2474        // Test read/write to first 1MB
2475        let write_buffer: Vec<u8> = (0..SIZE_1MB).map(|x| x as u8).collect();
2476        let mut read_buffer: Vec<u8> = (0..SIZE_1MB).map(|_| 0).collect();
2477        gm.write_at(addr, write_buffer.as_slice()).unwrap();
2478        gm.read_at(addr, read_buffer.as_mut_slice()).unwrap();
2479        assert_eq!(write_buffer, read_buffer);
2480
2481        // Test bad read at 1MB
2482        let addr = SIZE_1MB as u64;
2483        let result = gm.read_plain::<u8>(addr);
2484        assert!(result.is_err());
2485    }
2486
2487    #[test]
2488    fn test_multi() {
2489        let len = SIZE_1MB * 4;
2490        let mapping = SparseMapping::new(len).unwrap();
2491        mapping.alloc(0, len).unwrap();
2492        let mapping = Arc::new(GuestMemoryMapping {
2493            mapping,
2494            #[cfg(feature = "bitmap")]
2495            bitmap: None,
2496        });
2497        let region_len = 1 << 30;
2498        let gm = GuestMemory::new_multi_region(
2499            "test",
2500            region_len,
2501            vec![Some(mapping.clone()), None, Some(mapping.clone())],
2502        )
2503        .unwrap();
2504
2505        let mut b = [0];
2506        let len = len as u64;
2507        gm.read_at(0, &mut b).unwrap();
2508        gm.read_at(len, &mut b).unwrap_err();
2509        gm.read_at(region_len, &mut b).unwrap_err();
2510        gm.read_at(2 * region_len, &mut b).unwrap();
2511        gm.read_at(2 * region_len + len, &mut b).unwrap_err();
2512        gm.read_at(3 * region_len, &mut b).unwrap_err();
2513    }
2514
2515    #[cfg(feature = "bitmap")]
2516    #[test]
2517    fn test_bitmap() {
2518        let len = PAGE_SIZE * 4;
2519        let mapping = SparseMapping::new(len).unwrap();
2520        mapping.alloc(0, len).unwrap();
2521        let bitmap = vec![0b0101];
2522        let mapping = Arc::new(GuestMemoryMapping {
2523            mapping,
2524            bitmap: Some(bitmap),
2525        });
2526        let gm = GuestMemory::new("test", mapping);
2527
2528        gm.read_plain::<[u8; 1]>(0).unwrap();
2529        gm.read_plain::<[u8; 1]>(PAGE_SIZE64 - 1).unwrap();
2530        gm.read_plain::<[u8; 2]>(PAGE_SIZE64 - 1).unwrap_err();
2531        gm.read_plain::<[u8; 1]>(PAGE_SIZE64).unwrap_err();
2532        gm.read_plain::<[u8; 1]>(PAGE_SIZE64 * 2).unwrap();
2533        gm.read_plain::<[u8; PAGE_SIZE * 2]>(0).unwrap_err();
2534    }
2535
2536    struct FaultingMapping {
2537        mapping: SparseMapping,
2538    }
2539
2540    #[derive(Debug, Error)]
2541    #[error("fault")]
2542    struct Fault;
2543
2544    unsafe impl crate::GuestMemoryAccess for FaultingMapping {
2545        fn mapping(&self) -> Option<NonNull<u8>> {
2546            NonNull::new(self.mapping.as_ptr().cast())
2547        }
2548
2549        fn max_address(&self) -> u64 {
2550            self.mapping.len() as u64
2551        }
2552
2553        fn page_fault(
2554            &self,
2555            address: u64,
2556            _len: usize,
2557            write: bool,
2558            bitmap_failure: bool,
2559        ) -> PageFaultAction {
2560            assert!(!bitmap_failure);
2561            let qlen = self.mapping.len() as u64 / 4;
2562            if address < qlen || address >= 3 * qlen {
2563                return PageFaultAction::Fail(PageFaultError::other(Fault));
2564            }
2565            let page_address = (address as usize) & !(PAGE_SIZE - 1);
2566            if address >= 2 * qlen {
2567                if write {
2568                    return PageFaultAction::Fail(PageFaultError::other(Fault));
2569                }
2570                self.mapping.map_zero(page_address, PAGE_SIZE).unwrap();
2571            } else {
2572                self.mapping.alloc(page_address, PAGE_SIZE).unwrap();
2573            }
2574            PageFaultAction::Retry
2575        }
2576    }
2577
2578    impl FaultingMapping {
2579        fn new(len: usize) -> Self {
2580            let mapping = SparseMapping::new(len).unwrap();
2581            FaultingMapping { mapping }
2582        }
2583    }
2584
2585    #[test]
2586    fn test_fault() {
2587        let len = PAGE_SIZE * 4;
2588        let mapping = FaultingMapping::new(len);
2589        let gm = GuestMemory::new("test", mapping);
2590
2591        gm.write_plain::<u8>(0, &0).unwrap_err();
2592        gm.read_plain::<u8>(PAGE_SIZE64 - 1).unwrap_err();
2593        gm.read_plain::<u8>(PAGE_SIZE64).unwrap();
2594        gm.write_plain::<u8>(PAGE_SIZE64, &0).unwrap();
2595        gm.write_plain::<u16>(PAGE_SIZE64 * 3 - 1, &0).unwrap_err();
2596        gm.read_plain::<u16>(PAGE_SIZE64 * 3 - 1).unwrap_err();
2597        gm.read_plain::<u8>(PAGE_SIZE64 * 3 - 1).unwrap();
2598        gm.write_plain::<u8>(PAGE_SIZE64 * 3 - 1, &0).unwrap_err();
2599    }
2600
2601    #[cfg(feature = "bitmap")]
2602    #[test]
2603    fn test_zero_length_access_at_offset_zero() {
2604        // Regression test for a fuzzing-reported subtract-with-overflow panic
2605        // in `check_access`: a zero-length access at offset 0 underflowed while
2606        // computing the index of the last accessed page (`offset + len - 1`).
2607        // A zero-length access touches no pages and so must succeed without
2608        // consulting the bitmap, even when the page is marked inaccessible.
2609        let len = PAGE_SIZE * 4;
2610        let mapping = SparseMapping::new(len).unwrap();
2611        mapping.alloc(0, len).unwrap();
2612        let bitmap = vec![0b0000]; // every page marked inaccessible
2613        let mapping = Arc::new(GuestMemoryMapping {
2614            mapping,
2615            bitmap: Some(bitmap),
2616        });
2617        let gm = GuestMemory::new("test", mapping);
2618
2619        // Zero-sized plain accesses reach `check_access` with offset 0 and
2620        // len 0; these previously panicked with "attempt to subtract with
2621        // overflow".
2622        gm.read_plain::<()>(0).unwrap();
2623        gm.write_plain::<()>(0, &()).unwrap();
2624    }
2625
2626    #[test]
2627    fn test_allocated() {
2628        let mut gm = GuestMemory::allocate(0x10000);
2629        let pattern = [0x42; 0x10000];
2630        gm.write_at(0, &pattern).unwrap();
2631        assert_eq!(gm.inner_buf_mut().unwrap(), &pattern);
2632        gm.inner_buf().unwrap();
2633        let gm2 = gm.clone();
2634        assert!(gm.inner_buf_mut().is_none());
2635        gm.inner_buf().unwrap();
2636        let mut gm = gm.into_inner_buf().unwrap_err();
2637        drop(gm2);
2638        assert_eq!(gm.inner_buf_mut().unwrap(), &pattern);
2639        gm.into_inner_buf().unwrap();
2640    }
2641
2642    /// A backing whose locking support can be toggled, used to exercise
2643    /// [`GuestMemory::supports_locking`] aggregation. Backed by a real mapping
2644    /// so it can participate in single- and multi-region construction.
2645    struct ToggleLockMapping {
2646        mapping: SparseMapping,
2647        lockable: bool,
2648    }
2649
2650    impl ToggleLockMapping {
2651        fn new(size: usize, lockable: bool) -> Self {
2652            let mapping = SparseMapping::new(size).unwrap();
2653            mapping.alloc(0, size).unwrap();
2654            Self { mapping, lockable }
2655        }
2656    }
2657
2658    // SAFETY: the mapping is valid for the full range reported by `max_address`.
2659    unsafe impl crate::GuestMemoryAccess for ToggleLockMapping {
2660        fn mapping(&self) -> Option<NonNull<u8>> {
2661            NonNull::new(self.mapping.as_ptr().cast())
2662        }
2663
2664        fn max_address(&self) -> u64 {
2665            self.mapping.len() as u64
2666        }
2667
2668        fn supports_locking(&self) -> bool {
2669            self.lockable
2670        }
2671    }
2672
2673    #[test]
2674    fn test_supports_locking() {
2675        // A mapping-backed backing supports locking by default.
2676        let gm = GuestMemory::allocate(0x10000);
2677        assert!(gm.supports_locking());
2678
2679        // A backing that reports no locking support (e.g. on-demand
2680        // translation behind an emulated IOMMU) does not support locking, and
2681        // `supports_locking` is authoritative: locking fails without touching
2682        // the backing's `lock_gpns`.
2683        let gm = GuestMemory::new("nolock", ToggleLockMapping::new(SIZE_1MB, false));
2684        assert!(!gm.supports_locking());
2685        assert!(gm.lock_gpns(crate::AccessType::Write, false, &[0]).is_err());
2686
2687        // Multi-region: locking is supported only when every present backing
2688        // supports it.
2689        let gm = GuestMemory::new_multi_region(
2690            "multi-lockable",
2691            SIZE_1MB as u64,
2692            vec![
2693                Some(ToggleLockMapping::new(SIZE_1MB / 2, true)),
2694                Some(ToggleLockMapping::new(SIZE_1MB / 2, true)),
2695            ],
2696        )
2697        .unwrap();
2698        assert!(gm.supports_locking());
2699
2700        let gm = GuestMemory::new_multi_region(
2701            "multi-mixed",
2702            SIZE_1MB as u64,
2703            vec![
2704                Some(ToggleLockMapping::new(SIZE_1MB / 2, true)),
2705                Some(ToggleLockMapping::new(SIZE_1MB / 2, false)),
2706            ],
2707        )
2708        .unwrap();
2709        assert!(!gm.supports_locking());
2710    }
2711}