vm_memory/
guest_memory.rs

1// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
2//
3// Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4//
5// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
6// Use of this source code is governed by a BSD-style license that can be
7// found in the LICENSE-BSD-3-Clause file.
8//
9// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
10
11//! Traits to track and access the physical memory of the guest.
12//!
13//! To make the abstraction as generic as possible, all the core traits declared here only define
14//! methods to access guest's memory, and never define methods to manage (create, delete, insert,
15//! remove etc) guest's memory. This way, the guest memory consumers (virtio device drivers,
16//! vhost drivers and boot loaders etc) may be decoupled from the guest memory provider (typically
17//! a hypervisor).
18//!
19//! Traits and Structs
20//! - [`GuestAddress`](struct.GuestAddress.html): represents a guest physical address (GPA).
21//! - [`MemoryRegionAddress`](struct.MemoryRegionAddress.html): represents an offset inside a
22//!   region.
23//! - [`GuestMemoryRegion`](trait.GuestMemoryRegion.html): represent a continuous region of guest's
24//!   physical memory.
25//! - [`GuestMemory`](trait.GuestMemory.html): represent a collection of `GuestMemoryRegion`
26//!   objects.
27//!   The main responsibilities of the `GuestMemory` trait are:
28//!     - hide the detail of accessing guest's physical address.
29//!     - map a request address to a `GuestMemoryRegion` object and relay the request to it.
30//!     - handle cases where an access request spanning two or more `GuestMemoryRegion` objects.
31//!
32//! Whenever a collection of `GuestMemoryRegion` objects is mutable,
33//! [`GuestAddressSpace`](trait.GuestAddressSpace.html) should be implemented
34//! for clients to obtain a [`GuestMemory`] reference or smart pointer.
35//!
36//! The `GuestMemoryRegion` trait has an associated `B: Bitmap` type which is used to handle
37//! dirty bitmap tracking. Backends are free to define the granularity (or whether tracking is
38//! actually performed at all). Those that do implement tracking functionality are expected to
39//! ensure the correctness of the underlying `Bytes` implementation. The user has to explicitly
40//! record (using the handle returned by `GuestRegionMmap::bitmap`) write accesses performed
41//! via pointers, references, or slices returned by methods of `GuestMemory`,`GuestMemoryRegion`,
42//! `VolatileSlice`, `VolatileRef`, or `VolatileArrayRef`.
43
44use std::convert::From;
45use std::fs::File;
46use std::io;
47use std::iter::FusedIterator;
48use std::mem::size_of;
49use std::ops::{BitAnd, BitOr, Deref};
50use std::rc::Rc;
51use std::sync::atomic::Ordering;
52use std::sync::Arc;
53
54use crate::address::{Address, AddressValue};
55use crate::bitmap::MS;
56use crate::bytes::{AtomicAccess, Bytes};
57use crate::io::{ReadVolatile, WriteVolatile};
58use crate::volatile_memory::{self, VolatileSlice};
59use crate::GuestMemoryRegion;
60
61/// Errors associated with handling guest memory accesses.
62#[allow(missing_docs)]
63#[derive(Debug, thiserror::Error)]
64pub enum Error {
65    /// Failure in finding a guest address in any memory regions mapped by this guest.
66    #[error("Guest memory error: invalid guest address {}",.0.raw_value())]
67    InvalidGuestAddress(GuestAddress),
68    /// Couldn't read/write from the given source.
69    #[error("Guest memory error: {0}")]
70    IOError(io::Error),
71    /// Incomplete read or write.
72    #[error("Guest memory error: only used {completed} bytes in {expected} long buffer")]
73    PartialBuffer { expected: usize, completed: usize },
74    /// Requested backend address is out of range.
75    #[error("Guest memory error: invalid backend address")]
76    InvalidBackendAddress,
77    /// Host virtual address not available.
78    #[error("Guest memory error: host virtual address not available")]
79    HostAddressNotAvailable,
80    /// The length returned by the callback passed to `try_access` is outside the address range.
81    #[error(
82        "The length returned by the callback passed to `try_access` is outside the address range."
83    )]
84    CallbackOutOfRange,
85    /// The address to be read by `try_access` is outside the address range.
86    #[error("The address to be read by `try_access` is outside the address range")]
87    GuestAddressOverflow,
88}
89
90impl From<volatile_memory::Error> for Error {
91    fn from(e: volatile_memory::Error) -> Self {
92        match e {
93            volatile_memory::Error::OutOfBounds { .. } => Error::InvalidBackendAddress,
94            volatile_memory::Error::Overflow { .. } => Error::InvalidBackendAddress,
95            volatile_memory::Error::TooBig { .. } => Error::InvalidBackendAddress,
96            volatile_memory::Error::Misaligned { .. } => Error::InvalidBackendAddress,
97            volatile_memory::Error::IOError(e) => Error::IOError(e),
98            volatile_memory::Error::PartialBuffer {
99                expected,
100                completed,
101            } => Error::PartialBuffer {
102                expected,
103                completed,
104            },
105        }
106    }
107}
108
109/// Result of guest memory operations.
110pub type Result<T> = std::result::Result<T, Error>;
111
112/// Represents a guest physical address (GPA).
113///
114/// # Notes:
115/// On ARM64, a 32-bit hypervisor may be used to support a 64-bit guest. For simplicity,
116/// `u64` is used to store the the raw value no matter if the guest a 32-bit or 64-bit virtual
117/// machine.
118#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
119pub struct GuestAddress(pub u64);
120impl_address_ops!(GuestAddress, u64);
121
122/// Represents an offset inside a region.
123#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
124pub struct MemoryRegionAddress(pub u64);
125impl_address_ops!(MemoryRegionAddress, u64);
126
127/// Type of the raw value stored in a `GuestAddress` object.
128pub type GuestUsize = <GuestAddress as AddressValue>::V;
129
130/// Represents the start point within a `File` that backs a `GuestMemoryRegion`.
131#[derive(Clone, Debug)]
132pub struct FileOffset {
133    file: Arc<File>,
134    start: u64,
135}
136
137impl FileOffset {
138    /// Creates a new `FileOffset` object.
139    pub fn new(file: File, start: u64) -> Self {
140        FileOffset::from_arc(Arc::new(file), start)
141    }
142
143    /// Creates a new `FileOffset` object based on an exiting `Arc<File>`.
144    pub fn from_arc(file: Arc<File>, start: u64) -> Self {
145        FileOffset { file, start }
146    }
147
148    /// Returns a reference to the inner `File` object.
149    pub fn file(&self) -> &File {
150        self.file.as_ref()
151    }
152
153    /// Return a reference to the inner `Arc<File>` object.
154    pub fn arc(&self) -> &Arc<File> {
155        &self.file
156    }
157
158    /// Returns the start offset within the file.
159    pub fn start(&self) -> u64 {
160        self.start
161    }
162}
163
164/// `GuestAddressSpace` provides a way to retrieve a `GuestMemory` object.
165/// The vm-memory crate already provides trivial implementation for
166/// references to `GuestMemory` or reference-counted `GuestMemory` objects,
167/// but the trait can also be implemented by any other struct in order
168/// to provide temporary access to a snapshot of the memory map.
169///
170/// In order to support generic mutable memory maps, devices (or other things
171/// that access memory) should store the memory as a `GuestAddressSpace<M>`.
172/// This example shows that references can also be used as the `GuestAddressSpace`
173/// implementation, providing a zero-cost abstraction whenever immutable memory
174/// maps are sufficient.
175///
176/// # Examples (uses the `backend-mmap` and `backend-atomic` features)
177///
178/// ```
179/// # #[cfg(feature = "backend-mmap")]
180/// # {
181/// # use std::sync::Arc;
182/// # use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryMmap};
183/// #
184/// pub struct VirtioDevice<AS: GuestAddressSpace> {
185///     mem: Option<AS>,
186/// }
187///
188/// impl<AS: GuestAddressSpace> VirtioDevice<AS> {
189///     fn new() -> Self {
190///         VirtioDevice { mem: None }
191///     }
192///     fn activate(&mut self, mem: AS) {
193///         self.mem = Some(mem)
194///     }
195/// }
196///
197/// fn get_mmap() -> GuestMemoryMmap<()> {
198///     let start_addr = GuestAddress(0x1000);
199///     GuestMemoryMmap::from_ranges(&vec![(start_addr, 0x400)])
200///         .expect("Could not create guest memory")
201/// }
202///
203/// // Using `VirtioDevice` with an immutable GuestMemoryMmap:
204/// let mut for_immutable_mmap = VirtioDevice::<&GuestMemoryMmap<()>>::new();
205/// let mmap = get_mmap();
206/// for_immutable_mmap.activate(&mmap);
207/// let mut another = VirtioDevice::<&GuestMemoryMmap<()>>::new();
208/// another.activate(&mmap);
209///
210/// # #[cfg(feature = "backend-atomic")]
211/// # {
212/// # use vm_memory::GuestMemoryAtomic;
213/// // Using `VirtioDevice` with a mutable GuestMemoryMmap:
214/// let mut for_mutable_mmap = VirtioDevice::<GuestMemoryAtomic<GuestMemoryMmap<()>>>::new();
215/// let atomic = GuestMemoryAtomic::new(get_mmap());
216/// for_mutable_mmap.activate(atomic.clone());
217/// let mut another = VirtioDevice::<GuestMemoryAtomic<GuestMemoryMmap<()>>>::new();
218/// another.activate(atomic.clone());
219///
220/// // atomic can be modified here...
221/// # }
222/// # }
223/// ```
224pub trait GuestAddressSpace: Clone {
225    /// The type that will be used to access guest memory.
226    type M: GuestMemory;
227
228    /// A type that provides access to the memory.
229    type T: Clone + Deref<Target = Self::M>;
230
231    /// Return an object (e.g. a reference or guard) that can be used
232    /// to access memory through this address space.  The object provides
233    /// a consistent snapshot of the memory map.
234    fn memory(&self) -> Self::T;
235}
236
237impl<M: GuestMemory> GuestAddressSpace for &M {
238    type M = M;
239    type T = Self;
240
241    fn memory(&self) -> Self {
242        self
243    }
244}
245
246impl<M: GuestMemory> GuestAddressSpace for Rc<M> {
247    type M = M;
248    type T = Self;
249
250    fn memory(&self) -> Self {
251        self.clone()
252    }
253}
254
255impl<M: GuestMemory> GuestAddressSpace for Arc<M> {
256    type M = M;
257    type T = Self;
258
259    fn memory(&self) -> Self {
260        self.clone()
261    }
262}
263
264/// `GuestMemory` represents a container for an *immutable* collection of
265/// `GuestMemoryRegion` objects.  `GuestMemory` provides the `Bytes<GuestAddress>`
266/// trait to hide the details of accessing guest memory by physical address.
267/// Interior mutability is not allowed for implementations of `GuestMemory` so
268/// that they always provide a consistent view of the memory map.
269///
270/// The task of the `GuestMemory` trait are:
271/// - map a request address to a `GuestMemoryRegion` object and relay the request to it.
272/// - handle cases where an access request spanning two or more `GuestMemoryRegion` objects.
273pub trait GuestMemory {
274    /// Type of objects hosted by the address space.
275    type R: GuestMemoryRegion;
276
277    /// Returns the number of regions in the collection.
278    fn num_regions(&self) -> usize {
279        self.iter().count()
280    }
281
282    /// Returns the region containing the specified address or `None`.
283    fn find_region(&self, addr: GuestAddress) -> Option<&Self::R> {
284        self.iter()
285            .find(|region| addr >= region.start_addr() && addr <= region.last_addr())
286    }
287
288    /// Gets an iterator over the entries in the collection.
289    ///
290    /// # Examples
291    ///
292    /// * Compute the total size of all memory mappings in KB by iterating over the memory regions
293    ///   and dividing their sizes to 1024, then summing up the values in an accumulator. (uses the
294    ///   `backend-mmap` feature)
295    ///
296    /// ```
297    /// # #[cfg(feature = "backend-mmap")]
298    /// # {
299    /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryRegion, GuestMemoryMmap};
300    /// #
301    /// let start_addr1 = GuestAddress(0x0);
302    /// let start_addr2 = GuestAddress(0x400);
303    /// let gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr1, 1024), (start_addr2, 2048)])
304    ///     .expect("Could not create guest memory");
305    ///
306    /// let total_size = gm
307    ///     .iter()
308    ///     .map(|region| region.len() / 1024)
309    ///     .fold(0, |acc, size| acc + size);
310    /// assert_eq!(3, total_size)
311    /// # }
312    /// ```
313    fn iter(&self) -> impl Iterator<Item = &Self::R>;
314
315    /// Returns the maximum (inclusive) address managed by the
316    /// [`GuestMemory`](trait.GuestMemory.html).
317    ///
318    /// # Examples (uses the `backend-mmap` feature)
319    ///
320    /// ```
321    /// # #[cfg(feature = "backend-mmap")]
322    /// # {
323    /// # use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap};
324    /// #
325    /// let start_addr = GuestAddress(0x1000);
326    /// let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)])
327    ///     .expect("Could not create guest memory");
328    ///
329    /// assert_eq!(start_addr.checked_add(0x3ff), Some(gm.last_addr()));
330    /// # }
331    /// ```
332    fn last_addr(&self) -> GuestAddress {
333        self.iter()
334            .map(GuestMemoryRegion::last_addr)
335            .fold(GuestAddress(0), std::cmp::max)
336    }
337
338    /// Tries to convert an absolute address to a relative address within the corresponding region.
339    ///
340    /// Returns `None` if `addr` isn't present within the memory of the guest.
341    fn to_region_addr(&self, addr: GuestAddress) -> Option<(&Self::R, MemoryRegionAddress)> {
342        self.find_region(addr)
343            .map(|r| (r, r.to_region_addr(addr).unwrap()))
344    }
345
346    /// Returns `true` if the given address is present within the memory of the guest.
347    fn address_in_range(&self, addr: GuestAddress) -> bool {
348        self.find_region(addr).is_some()
349    }
350
351    /// Returns the given address if it is present within the memory of the guest.
352    fn check_address(&self, addr: GuestAddress) -> Option<GuestAddress> {
353        self.find_region(addr).map(|_| addr)
354    }
355
356    /// Check whether the range [base, base + len) is valid.
357    fn check_range(&self, base: GuestAddress, len: usize) -> bool {
358        // get_slices() ensures that if no error happens, the cumulative length of all slices
359        // equal `len`.
360        self.get_slices(base, len).all(|r| r.is_ok())
361    }
362
363    /// Returns the address plus the offset if it is present within the memory of the guest.
364    fn checked_offset(&self, base: GuestAddress, offset: usize) -> Option<GuestAddress> {
365        base.checked_add(offset as u64)
366            .and_then(|addr| self.check_address(addr))
367    }
368
369    /// Invokes callback `f` to handle data in the address range `[addr, addr + count)`.
370    ///
371    /// The address range `[addr, addr + count)` may span more than one
372    /// [`GuestMemoryRegion`](trait.GuestMemoryRegion.html) object, or even have holes in it.
373    /// So [`try_access()`](trait.GuestMemory.html#method.try_access) invokes the callback 'f'
374    /// for each [`GuestMemoryRegion`](trait.GuestMemoryRegion.html) object involved and returns:
375    /// - the error code returned by the callback 'f'
376    /// - the size of the already handled data when encountering the first hole
377    /// - the size of the already handled data when the whole range has been handled
378    #[deprecated(
379        since = "0.17.0",
380        note = "supplemented by external iterator `get_slices()`"
381    )]
382    fn try_access<F>(&self, count: usize, addr: GuestAddress, mut f: F) -> Result<usize>
383    where
384        F: FnMut(usize, usize, MemoryRegionAddress, &Self::R) -> Result<usize>,
385    {
386        let mut cur = addr;
387        let mut total = 0;
388        while let Some(region) = self.find_region(cur) {
389            let start = region.to_region_addr(cur).unwrap();
390            let cap = region.len() - start.raw_value();
391            let len = std::cmp::min(cap, (count - total) as GuestUsize);
392            match f(total, len as usize, start, region) {
393                // no more data
394                Ok(0) => return Ok(total),
395                // made some progress
396                Ok(len) => {
397                    total = match total.checked_add(len) {
398                        Some(x) if x < count => x,
399                        Some(x) if x == count => return Ok(x),
400                        _ => return Err(Error::CallbackOutOfRange),
401                    };
402                    cur = match cur.overflowing_add(len as GuestUsize) {
403                        (x @ GuestAddress(0), _) | (x, false) => x,
404                        (_, true) => return Err(Error::GuestAddressOverflow),
405                    };
406                }
407                // error happened
408                e => return e,
409            }
410        }
411        if total == 0 {
412            Err(Error::InvalidGuestAddress(addr))
413        } else {
414            Ok(total)
415        }
416    }
417
418    /// Get the host virtual address corresponding to the guest address.
419    ///
420    /// Some [`GuestMemory`](trait.GuestMemory.html) implementations, like `GuestMemoryMmap`,
421    /// have the capability to mmap the guest address range into virtual address space of the host
422    /// for direct access, so the corresponding host virtual address may be passed to other
423    /// subsystems.
424    ///
425    /// # Note
426    /// The underlying guest memory is not protected from memory aliasing, which breaks the
427    /// Rust memory safety model. It's the caller's responsibility to ensure that there's no
428    /// concurrent accesses to the underlying guest memory.
429    ///
430    /// # Arguments
431    /// * `addr` - Guest address to convert.
432    ///
433    /// # Examples (uses the `backend-mmap` feature)
434    ///
435    /// ```
436    /// # #[cfg(feature = "backend-mmap")]
437    /// # {
438    /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
439    /// #
440    /// # let start_addr = GuestAddress(0x1000);
441    /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x500)])
442    /// #    .expect("Could not create guest memory");
443    /// #
444    /// let addr = gm
445    ///     .get_host_address(GuestAddress(0x1200))
446    ///     .expect("Could not get host address");
447    /// println!("Host address is {:p}", addr);
448    /// # }
449    /// ```
450    fn get_host_address(&self, addr: GuestAddress) -> Result<*mut u8> {
451        self.to_region_addr(addr)
452            .ok_or(Error::InvalidGuestAddress(addr))
453            .and_then(|(r, addr)| r.get_host_address(addr))
454    }
455
456    /// Returns a [`VolatileSlice`](struct.VolatileSlice.html) of `count` bytes starting at
457    /// `addr`.
458    fn get_slice(&self, addr: GuestAddress, count: usize) -> Result<VolatileSlice<MS<Self>>> {
459        self.to_region_addr(addr)
460            .ok_or(Error::InvalidGuestAddress(addr))
461            .and_then(|(r, addr)| r.get_slice(addr, count))
462    }
463
464    /// Returns an iterator over [`VolatileSlice`](struct.VolatileSlice.html)s, together covering
465    /// `count` bytes starting at `addr`.
466    ///
467    /// Iterating in this way is necessary because the given address range may be fragmented across
468    /// multiple [`GuestMemoryRegion`]s.
469    ///
470    /// The iterator’s items are wrapped in [`Result`], i.e. errors are reported on individual
471    /// items.  If there is no such error, the cumulative length of all items will be equal to
472    /// `count`.  If `count` is 0, an empty iterator will be returned.
473    fn get_slices<'a>(
474        &'a self,
475        addr: GuestAddress,
476        count: usize,
477    ) -> GuestMemorySliceIterator<'a, Self> {
478        GuestMemorySliceIterator {
479            mem: self,
480            addr,
481            count,
482        }
483    }
484}
485
486/// Iterates over [`VolatileSlice`]s that together form a guest memory area.
487///
488/// Returned by [`GuestMemory::get_slices()`].
489#[derive(Debug)]
490pub struct GuestMemorySliceIterator<'a, M: GuestMemory + ?Sized> {
491    /// Underlying memory
492    mem: &'a M,
493    /// Next address in the guest memory area
494    addr: GuestAddress,
495    /// Remaining bytes in the guest memory area
496    count: usize,
497}
498
499impl<'a, M: GuestMemory + ?Sized> GuestMemorySliceIterator<'a, M> {
500    /// Helper function for [`<Self as Iterator>::next()`](GuestMemorySliceIterator::next).
501    ///
502    /// Get the next slice (i.e. the one starting from `self.addr` with a length up to
503    /// `self.count`) and update the internal state.
504    ///
505    /// # Safety
506    ///
507    /// This function does not reset to `self.count` to 0 in case of error, i.e. will not stop
508    /// iterating.  Actual behavior after an error is ill-defined, so the caller must check the
509    /// return value, and in case of an error, reset `self.count` to 0.
510    ///
511    /// (This is why this function exists, so this resetting can be done in a single central
512    /// location.)
513    unsafe fn do_next(&mut self) -> Option<Result<VolatileSlice<'a, MS<'a, M>>>> {
514        if self.count == 0 {
515            return None;
516        }
517
518        let Some((region, start)) = self.mem.to_region_addr(self.addr) else {
519            return Some(Err(Error::InvalidGuestAddress(self.addr)));
520        };
521
522        let cap = region.len() - start.raw_value();
523        let len = std::cmp::min(cap as usize, self.count);
524
525        self.count -= len;
526        self.addr = match self.addr.overflowing_add(len as GuestUsize) {
527            (x @ GuestAddress(0), _) | (x, false) => x,
528            (_, true) => return Some(Err(Error::GuestAddressOverflow)),
529        };
530
531        Some(region.get_slice(start, len).inspect(|s| {
532            assert_eq!(
533                s.len(),
534                len,
535                "get_slice() returned a slice with wrong length"
536            )
537        }))
538    }
539
540    /// Adapts this [`GuestMemorySliceIterator`] to return `None` (e.g. gracefully terminate)
541    /// when it encounters an error after successfully producing at least one slice.
542    /// Return an error if requesting the first slice returns an error.
543    pub fn stop_on_error(self) -> Result<impl Iterator<Item = VolatileSlice<'a, MS<'a, M>>>> {
544        let mut peek = self.peekable();
545        if let Some(err) = peek.next_if(Result::is_err) {
546            return Err(err.unwrap_err());
547        }
548        Ok(peek.filter_map(Result::ok))
549    }
550}
551
552impl<'a, M: GuestMemory + ?Sized> Iterator for GuestMemorySliceIterator<'a, M> {
553    type Item = Result<VolatileSlice<'a, MS<'a, M>>>;
554
555    fn next(&mut self) -> Option<Self::Item> {
556        // SAFETY:
557        // We reset `self.count` to 0 on error
558        match unsafe { self.do_next() } {
559            Some(Ok(slice)) => Some(Ok(slice)),
560            other => {
561                // On error (or end), reset to 0 so iteration remains stopped
562                self.count = 0;
563                other
564            }
565        }
566    }
567}
568
569/// This iterator continues to return `None` when exhausted.
570///
571/// [`<Self as Iterator>::next()`](GuestMemorySliceIterator::next) sets `self.count` to 0 when
572/// returning `None`, ensuring that it will only return `None` from that point on.
573impl<M: GuestMemory + ?Sized> FusedIterator for GuestMemorySliceIterator<'_, M> {}
574
575impl<T: GuestMemory + ?Sized> Bytes<GuestAddress> for T {
576    type E = Error;
577
578    fn write(&self, buf: &[u8], addr: GuestAddress) -> Result<usize> {
579        self.get_slices(addr, buf.len())
580            .stop_on_error()?
581            .try_fold(0, |acc, slice| Ok(acc + slice.write(&buf[acc..], 0)?))
582    }
583
584    fn read(&self, buf: &mut [u8], addr: GuestAddress) -> Result<usize> {
585        self.get_slices(addr, buf.len())
586            .stop_on_error()?
587            .try_fold(0, |acc, slice| Ok(acc + slice.read(&mut buf[acc..], 0)?))
588    }
589
590    /// # Examples
591    ///
592    /// * Write a slice at guestaddress 0x1000. (uses the `backend-mmap` feature)
593    ///
594    /// ```
595    /// # #[cfg(feature = "backend-mmap")]
596    /// # {
597    /// # use vm_memory::{Bytes, GuestAddress, mmap::GuestMemoryMmap};
598    /// #
599    /// # let start_addr = GuestAddress(0x1000);
600    /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)])
601    /// #    .expect("Could not create guest memory");
602    /// #
603    /// gm.write_slice(&[1, 2, 3, 4, 5], start_addr)
604    ///     .expect("Could not write slice to guest memory");
605    /// # }
606    /// ```
607    fn write_slice(&self, buf: &[u8], addr: GuestAddress) -> Result<()> {
608        let res = self.write(buf, addr)?;
609        if res != buf.len() {
610            return Err(Error::PartialBuffer {
611                expected: buf.len(),
612                completed: res,
613            });
614        }
615        Ok(())
616    }
617
618    /// # Examples
619    ///
620    /// * Read a slice of length 16 at guestaddress 0x1000. (uses the `backend-mmap` feature)
621    ///
622    /// ```
623    /// # #[cfg(feature = "backend-mmap")]
624    /// # {
625    /// # use vm_memory::{Bytes, GuestAddress, mmap::GuestMemoryMmap};
626    /// #
627    /// let start_addr = GuestAddress(0x1000);
628    /// let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)])
629    ///     .expect("Could not create guest memory");
630    /// let buf = &mut [0u8; 16];
631    ///
632    /// gm.read_slice(buf, start_addr)
633    ///     .expect("Could not read slice from guest memory");
634    /// # }
635    /// ```
636    fn read_slice(&self, buf: &mut [u8], addr: GuestAddress) -> Result<()> {
637        let res = self.read(buf, addr)?;
638        if res != buf.len() {
639            return Err(Error::PartialBuffer {
640                expected: buf.len(),
641                completed: res,
642            });
643        }
644        Ok(())
645    }
646
647    fn read_volatile_from<F>(&self, addr: GuestAddress, src: &mut F, count: usize) -> Result<usize>
648    where
649        F: ReadVolatile,
650    {
651        self.get_slices(addr, count)
652            .stop_on_error()?
653            .try_fold(0, |acc, slice| {
654                Ok(acc + slice.read_volatile_from(0, src, slice.len())?)
655            })
656    }
657
658    fn read_exact_volatile_from<F>(
659        &self,
660        addr: GuestAddress,
661        src: &mut F,
662        count: usize,
663    ) -> Result<()>
664    where
665        F: ReadVolatile,
666    {
667        let res = self.read_volatile_from(addr, src, count)?;
668        if res != count {
669            return Err(Error::PartialBuffer {
670                expected: count,
671                completed: res,
672            });
673        }
674        Ok(())
675    }
676
677    fn write_volatile_to<F>(&self, addr: GuestAddress, dst: &mut F, count: usize) -> Result<usize>
678    where
679        F: WriteVolatile,
680    {
681        self.get_slices(addr, count)
682            .stop_on_error()?
683            .try_fold(0, |acc, slice| {
684                // For a non-RAM region, reading could have side effects, so we
685                // must use write_all().
686                slice.write_all_volatile_to(0, dst, slice.len())?;
687                Ok(acc + slice.len())
688            })
689    }
690
691    fn write_all_volatile_to<F>(&self, addr: GuestAddress, dst: &mut F, count: usize) -> Result<()>
692    where
693        F: WriteVolatile,
694    {
695        let res = self.write_volatile_to(addr, dst, count)?;
696        if res != count {
697            return Err(Error::PartialBuffer {
698                expected: count,
699                completed: res,
700            });
701        }
702        Ok(())
703    }
704
705    fn store<O: AtomicAccess>(&self, val: O, addr: GuestAddress, order: Ordering) -> Result<()> {
706        // No need to check past the first iterator item: It either has the size of `O`, then there
707        // can be no further items; or it does not, and then `VolatileSlice::store()` will fail.
708        self.get_slices(addr, size_of::<O>())
709            .next()
710            .unwrap()? // count > 0 never produces an empty iterator
711            .store(val, 0, order)
712            .map_err(Into::into)
713    }
714
715    fn load<O: AtomicAccess>(&self, addr: GuestAddress, order: Ordering) -> Result<O> {
716        // No need to check past the first iterator item: It either has the size of `O`, then there
717        // can be no further items; or it does not, and then `VolatileSlice::store()` will fail.
718        self.get_slices(addr, size_of::<O>())
719            .next()
720            .unwrap()? // count > 0 never produces an empty iterator
721            .load(0, order)
722            .map_err(Into::into)
723    }
724}
725
726#[cfg(test)]
727mod tests {
728    #![allow(clippy::undocumented_unsafe_blocks)]
729    use super::*;
730    #[cfg(feature = "backend-mmap")]
731    use crate::bytes::ByteValued;
732    #[cfg(feature = "backend-mmap")]
733    use crate::GuestAddress;
734    #[cfg(feature = "backend-mmap")]
735    use std::time::{Duration, Instant};
736
737    use vmm_sys_util::tempfile::TempFile;
738
739    #[cfg(feature = "backend-mmap")]
740    type GuestMemoryMmap = crate::GuestMemoryMmap<()>;
741
742    #[cfg(feature = "backend-mmap")]
743    fn make_image(size: u8) -> Vec<u8> {
744        let mut image: Vec<u8> = Vec::with_capacity(size as usize);
745        for i in 0..size {
746            image.push(i);
747        }
748        image
749    }
750
751    #[test]
752    fn test_file_offset() {
753        let file = TempFile::new().unwrap().into_file();
754        let start = 1234;
755        let file_offset = FileOffset::new(file, start);
756        assert_eq!(file_offset.start(), start);
757        assert_eq!(
758            file_offset.file() as *const File,
759            file_offset.arc().as_ref() as *const File
760        );
761    }
762
763    #[cfg(feature = "backend-mmap")]
764    #[test]
765    fn checked_read_from() {
766        let start_addr1 = GuestAddress(0x0);
767        let start_addr2 = GuestAddress(0x40);
768        let mem = GuestMemoryMmap::from_ranges(&[(start_addr1, 64), (start_addr2, 64)]).unwrap();
769        let image = make_image(0x80);
770        let offset = GuestAddress(0x30);
771        let count: usize = 0x20;
772        assert_eq!(
773            0x20_usize,
774            mem.read_volatile_from(offset, &mut image.as_slice(), count)
775                .unwrap()
776        );
777    }
778
779    // Runs the provided closure in a loop, until at least `duration` time units have elapsed.
780    #[cfg(feature = "backend-mmap")]
781    fn loop_timed<F>(duration: Duration, mut f: F)
782    where
783        F: FnMut(),
784    {
785        // We check the time every `CHECK_PERIOD` iterations.
786        const CHECK_PERIOD: u64 = 1_000_000;
787        let start_time = Instant::now();
788
789        loop {
790            for _ in 0..CHECK_PERIOD {
791                f();
792            }
793            if start_time.elapsed() >= duration {
794                break;
795            }
796        }
797    }
798
799    // Helper method for the following test. It spawns a writer and a reader thread, which
800    // simultaneously try to access an object that is placed at the junction of two memory regions.
801    // The part of the object that's continuously accessed is a member of type T. The writer
802    // flips all the bits of the member with every write, while the reader checks that every byte
803    // has the same value (and thus it did not do a non-atomic access). The test succeeds if
804    // no mismatch is detected after performing accesses for a pre-determined amount of time.
805    #[cfg(feature = "backend-mmap")]
806    #[cfg(not(miri))] // This test simulates a race condition between guest and vmm
807    fn non_atomic_access_helper<T>()
808    where
809        T: ByteValued
810            + std::fmt::Debug
811            + From<u8>
812            + Into<u128>
813            + std::ops::Not<Output = T>
814            + PartialEq,
815    {
816        use std::mem;
817        use std::thread;
818
819        // A dummy type that's always going to have the same alignment as the first member,
820        // and then adds some bytes at the end.
821        #[derive(Clone, Copy, Debug, Default, PartialEq)]
822        struct Data<T> {
823            val: T,
824            some_bytes: [u8; 8],
825        }
826
827        // Some sanity checks.
828        assert_eq!(mem::align_of::<T>(), mem::align_of::<Data<T>>());
829        assert_eq!(mem::size_of::<T>(), mem::align_of::<T>());
830
831        // There must be no padding bytes, as otherwise implementing ByteValued is UB
832        assert_eq!(mem::size_of::<Data<T>>(), mem::size_of::<T>() + 8);
833
834        unsafe impl<T: ByteValued> ByteValued for Data<T> {}
835
836        // Start of first guest memory region.
837        let start = GuestAddress(0);
838        let region_len = 1 << 12;
839
840        // The address where we start writing/reading a Data<T> value.
841        let data_start = GuestAddress((region_len - mem::size_of::<T>()) as u64);
842
843        let mem = GuestMemoryMmap::from_ranges(&[
844            (start, region_len),
845            (start.unchecked_add(region_len as u64), region_len),
846        ])
847        .unwrap();
848
849        // Need to clone this and move it into the new thread we create.
850        let mem2 = mem.clone();
851        // Just some bytes.
852        let some_bytes = [1u8, 2, 4, 16, 32, 64, 128, 255];
853
854        let mut data = Data {
855            val: T::from(0u8),
856            some_bytes,
857        };
858
859        // Simple check that cross-region write/read is ok.
860        mem.write_obj(data, data_start).unwrap();
861        let read_data = mem.read_obj::<Data<T>>(data_start).unwrap();
862        assert_eq!(read_data, data);
863
864        let t = thread::spawn(move || {
865            let mut count: u64 = 0;
866
867            loop_timed(Duration::from_secs(3), || {
868                let data = mem2.read_obj::<Data<T>>(data_start).unwrap();
869
870                // Every time data is written to memory by the other thread, the value of
871                // data.val alternates between 0 and T::MAX, so the inner bytes should always
872                // have the same value. If they don't match, it means we read a partial value,
873                // so the access was not atomic.
874                let bytes = data.val.into().to_le_bytes();
875                for i in 1..mem::size_of::<T>() {
876                    if bytes[0] != bytes[i] {
877                        panic!(
878                            "val bytes don't match {:?} after {} iterations",
879                            &bytes[..mem::size_of::<T>()],
880                            count
881                        );
882                    }
883                }
884                count += 1;
885            });
886        });
887
888        // Write the object while flipping the bits of data.val over and over again.
889        loop_timed(Duration::from_secs(3), || {
890            mem.write_obj(data, data_start).unwrap();
891            data.val = !data.val;
892        });
893
894        t.join().unwrap()
895    }
896
897    #[cfg(feature = "backend-mmap")]
898    #[test]
899    #[cfg(not(miri))]
900    fn test_non_atomic_access() {
901        non_atomic_access_helper::<u16>()
902    }
903
904    #[cfg(feature = "backend-mmap")]
905    #[test]
906    fn test_zero_length_accesses() {
907        #[derive(Default, Clone, Copy)]
908        #[repr(C)]
909        struct ZeroSizedStruct {
910            dummy: [u32; 0],
911        }
912
913        unsafe impl ByteValued for ZeroSizedStruct {}
914
915        let addr = GuestAddress(0x1000);
916        let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap();
917        let obj = ZeroSizedStruct::default();
918        let mut image = make_image(0x80);
919
920        assert_eq!(mem.write(&[], addr).unwrap(), 0);
921        assert_eq!(mem.read(&mut [], addr).unwrap(), 0);
922
923        assert!(mem.write_slice(&[], addr).is_ok());
924        assert!(mem.read_slice(&mut [], addr).is_ok());
925
926        assert!(mem.write_obj(obj, addr).is_ok());
927        assert!(mem.read_obj::<ZeroSizedStruct>(addr).is_ok());
928
929        assert_eq!(
930            mem.read_volatile_from(addr, &mut image.as_slice(), 0)
931                .unwrap(),
932            0
933        );
934
935        assert!(mem
936            .read_exact_volatile_from(addr, &mut image.as_slice(), 0)
937            .is_ok());
938
939        assert_eq!(
940            mem.write_volatile_to(addr, &mut image.as_mut_slice(), 0)
941                .unwrap(),
942            0
943        );
944
945        assert!(mem
946            .write_all_volatile_to(addr, &mut image.as_mut_slice(), 0)
947            .is_ok());
948    }
949
950    #[cfg(feature = "backend-mmap")]
951    #[test]
952    fn test_atomic_accesses() {
953        let addr = GuestAddress(0x1000);
954        let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap();
955        let bad_addr = addr.unchecked_add(0x1000);
956
957        crate::bytes::tests::check_atomic_accesses(mem, addr, bad_addr);
958    }
959
960    #[cfg(feature = "backend-mmap")]
961    #[cfg(target_os = "linux")]
962    #[test]
963    fn test_guest_memory_mmap_is_hugetlbfs() {
964        let addr = GuestAddress(0x1000);
965        let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap();
966        let r = mem.find_region(addr).unwrap();
967        assert_eq!(r.is_hugetlbfs(), None);
968    }
969}