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}