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::{self, Read, Write};
47use std::ops::{BitAnd, BitOr, Deref};
48use std::rc::Rc;
49use std::sync::atomic::Ordering;
50use std::sync::Arc;
51
52use crate::address::{Address, AddressValue};
53use crate::bitmap::{Bitmap, BS, MS};
54use crate::bytes::{AtomicAccess, Bytes};
55use crate::io::{ReadVolatile, WriteVolatile};
56use crate::volatile_memory::{self, VolatileSlice};
57use crate::GuestMemoryError;
58
59static MAX_ACCESS_CHUNK: usize = 4096;
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/// Represents a continuous region of guest physical memory.
165#[allow(clippy::len_without_is_empty)]
166pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
167 /// Type used for dirty memory tracking.
168 type B: Bitmap;
169
170 /// Returns the size of the region.
171 fn len(&self) -> GuestUsize;
172
173 /// Returns the minimum (inclusive) address managed by the region.
174 fn start_addr(&self) -> GuestAddress;
175
176 /// Returns the maximum (inclusive) address managed by the region.
177 fn last_addr(&self) -> GuestAddress {
178 // unchecked_add is safe as the region bounds were checked when it was created.
179 self.start_addr().unchecked_add(self.len() - 1)
180 }
181
182 /// Borrow the associated `Bitmap` object.
183 fn bitmap(&self) -> &Self::B;
184
185 /// Returns the given address if it is within this region.
186 fn check_address(&self, addr: MemoryRegionAddress) -> Option<MemoryRegionAddress> {
187 if self.address_in_range(addr) {
188 Some(addr)
189 } else {
190 None
191 }
192 }
193
194 /// Returns `true` if the given address is within this region.
195 fn address_in_range(&self, addr: MemoryRegionAddress) -> bool {
196 addr.raw_value() < self.len()
197 }
198
199 /// Returns the address plus the offset if it is in this region.
200 fn checked_offset(
201 &self,
202 base: MemoryRegionAddress,
203 offset: usize,
204 ) -> Option<MemoryRegionAddress> {
205 base.checked_add(offset as u64)
206 .and_then(|addr| self.check_address(addr))
207 }
208
209 /// Tries to convert an absolute address to a relative address within this region.
210 ///
211 /// Returns `None` if `addr` is out of the bounds of this region.
212 fn to_region_addr(&self, addr: GuestAddress) -> Option<MemoryRegionAddress> {
213 addr.checked_offset_from(self.start_addr())
214 .and_then(|offset| self.check_address(MemoryRegionAddress(offset)))
215 }
216
217 /// Returns the host virtual address corresponding to the region address.
218 ///
219 /// Some [`GuestMemory`](trait.GuestMemory.html) implementations, like `GuestMemoryMmap`,
220 /// have the capability to mmap guest address range into host virtual address space for
221 /// direct access, so the corresponding host virtual address may be passed to other subsystems.
222 ///
223 /// # Note
224 /// The underlying guest memory is not protected from memory aliasing, which breaks the
225 /// Rust memory safety model. It's the caller's responsibility to ensure that there's no
226 /// concurrent accesses to the underlying guest memory.
227 fn get_host_address(&self, _addr: MemoryRegionAddress) -> Result<*mut u8> {
228 Err(Error::HostAddressNotAvailable)
229 }
230
231 /// Returns information regarding the file and offset backing this memory region.
232 fn file_offset(&self) -> Option<&FileOffset> {
233 None
234 }
235
236 /// Returns a slice corresponding to the data in the region.
237 ///
238 /// Returns `None` if the region does not support slice-based access.
239 ///
240 /// # Safety
241 ///
242 /// Unsafe because of possible aliasing.
243 #[deprecated = "It is impossible to use this function for accessing memory of a running virtual \
244 machine without violating aliasing rules "]
245 unsafe fn as_slice(&self) -> Option<&[u8]> {
246 None
247 }
248
249 /// Returns a mutable slice corresponding to the data in the region.
250 ///
251 /// Returns `None` if the region does not support slice-based access.
252 ///
253 /// # Safety
254 ///
255 /// Unsafe because of possible aliasing. Mutable accesses performed through the
256 /// returned slice are not visible to the dirty bitmap tracking functionality of
257 /// the region, and must be manually recorded using the associated bitmap object.
258 #[deprecated = "It is impossible to use this function for accessing memory of a running virtual \
259 machine without violating aliasing rules "]
260 unsafe fn as_mut_slice(&self) -> Option<&mut [u8]> {
261 None
262 }
263
264 /// Returns a [`VolatileSlice`](struct.VolatileSlice.html) of `count` bytes starting at
265 /// `offset`.
266 #[allow(unused_variables)]
267 fn get_slice(
268 &self,
269 offset: MemoryRegionAddress,
270 count: usize,
271 ) -> Result<VolatileSlice<BS<Self::B>>> {
272 Err(Error::HostAddressNotAvailable)
273 }
274
275 /// Gets a slice of memory for the entire region that supports volatile access.
276 ///
277 /// # Examples (uses the `backend-mmap` feature)
278 ///
279 /// ```
280 /// # #[cfg(feature = "backend-mmap")]
281 /// # {
282 /// # use vm_memory::{GuestAddress, MmapRegion, GuestRegionMmap, GuestMemoryRegion};
283 /// # use vm_memory::volatile_memory::{VolatileMemory, VolatileSlice, VolatileRef};
284 /// #
285 /// let region = GuestRegionMmap::<()>::from_range(GuestAddress(0x0), 0x400, None)
286 /// .expect("Could not create guest memory");
287 /// let slice = region
288 /// .as_volatile_slice()
289 /// .expect("Could not get volatile slice");
290 ///
291 /// let v = 42u32;
292 /// let r = slice
293 /// .get_ref::<u32>(0x200)
294 /// .expect("Could not get reference");
295 /// r.store(v);
296 /// assert_eq!(r.load(), v);
297 /// # }
298 /// ```
299 fn as_volatile_slice(&self) -> Result<VolatileSlice<BS<Self::B>>> {
300 self.get_slice(MemoryRegionAddress(0), self.len() as usize)
301 }
302
303 /// Show if the region is based on the `HugeTLBFS`.
304 /// Returns Some(true) if the region is backed by hugetlbfs.
305 /// None represents that no information is available.
306 ///
307 /// # Examples (uses the `backend-mmap` feature)
308 ///
309 /// ```
310 /// # #[cfg(feature = "backend-mmap")]
311 /// # {
312 /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap, GuestRegionMmap};
313 /// let addr = GuestAddress(0x1000);
314 /// let mem = GuestMemoryMmap::<()>::from_ranges(&[(addr, 0x1000)]).unwrap();
315 /// let r = mem.find_region(addr).unwrap();
316 /// assert_eq!(r.is_hugetlbfs(), None);
317 /// # }
318 /// ```
319 #[cfg(target_os = "linux")]
320 fn is_hugetlbfs(&self) -> Option<bool> {
321 None
322 }
323}
324
325/// `GuestAddressSpace` provides a way to retrieve a `GuestMemory` object.
326/// The vm-memory crate already provides trivial implementation for
327/// references to `GuestMemory` or reference-counted `GuestMemory` objects,
328/// but the trait can also be implemented by any other struct in order
329/// to provide temporary access to a snapshot of the memory map.
330///
331/// In order to support generic mutable memory maps, devices (or other things
332/// that access memory) should store the memory as a `GuestAddressSpace<M>`.
333/// This example shows that references can also be used as the `GuestAddressSpace`
334/// implementation, providing a zero-cost abstraction whenever immutable memory
335/// maps are sufficient.
336///
337/// # Examples (uses the `backend-mmap` and `backend-atomic` features)
338///
339/// ```
340/// # #[cfg(feature = "backend-mmap")]
341/// # {
342/// # use std::sync::Arc;
343/// # use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryMmap};
344/// #
345/// pub struct VirtioDevice<AS: GuestAddressSpace> {
346/// mem: Option<AS>,
347/// }
348///
349/// impl<AS: GuestAddressSpace> VirtioDevice<AS> {
350/// fn new() -> Self {
351/// VirtioDevice { mem: None }
352/// }
353/// fn activate(&mut self, mem: AS) {
354/// self.mem = Some(mem)
355/// }
356/// }
357///
358/// fn get_mmap() -> GuestMemoryMmap<()> {
359/// let start_addr = GuestAddress(0x1000);
360/// GuestMemoryMmap::from_ranges(&vec![(start_addr, 0x400)])
361/// .expect("Could not create guest memory")
362/// }
363///
364/// // Using `VirtioDevice` with an immutable GuestMemoryMmap:
365/// let mut for_immutable_mmap = VirtioDevice::<&GuestMemoryMmap<()>>::new();
366/// let mmap = get_mmap();
367/// for_immutable_mmap.activate(&mmap);
368/// let mut another = VirtioDevice::<&GuestMemoryMmap<()>>::new();
369/// another.activate(&mmap);
370///
371/// # #[cfg(feature = "backend-atomic")]
372/// # {
373/// # use vm_memory::GuestMemoryAtomic;
374/// // Using `VirtioDevice` with a mutable GuestMemoryMmap:
375/// let mut for_mutable_mmap = VirtioDevice::<GuestMemoryAtomic<GuestMemoryMmap<()>>>::new();
376/// let atomic = GuestMemoryAtomic::new(get_mmap());
377/// for_mutable_mmap.activate(atomic.clone());
378/// let mut another = VirtioDevice::<GuestMemoryAtomic<GuestMemoryMmap<()>>>::new();
379/// another.activate(atomic.clone());
380///
381/// // atomic can be modified here...
382/// # }
383/// # }
384/// ```
385pub trait GuestAddressSpace {
386 /// The type that will be used to access guest memory.
387 type M: GuestMemory;
388
389 /// A type that provides access to the memory.
390 type T: Clone + Deref<Target = Self::M>;
391
392 /// Return an object (e.g. a reference or guard) that can be used
393 /// to access memory through this address space. The object provides
394 /// a consistent snapshot of the memory map.
395 fn memory(&self) -> Self::T;
396}
397
398impl<M: GuestMemory> GuestAddressSpace for &M {
399 type M = M;
400 type T = Self;
401
402 fn memory(&self) -> Self {
403 self
404 }
405}
406
407impl<M: GuestMemory> GuestAddressSpace for Rc<M> {
408 type M = M;
409 type T = Self;
410
411 fn memory(&self) -> Self {
412 self.clone()
413 }
414}
415
416impl<M: GuestMemory> GuestAddressSpace for Arc<M> {
417 type M = M;
418 type T = Self;
419
420 fn memory(&self) -> Self {
421 self.clone()
422 }
423}
424
425/// `GuestMemory` represents a container for an *immutable* collection of
426/// `GuestMemoryRegion` objects. `GuestMemory` provides the `Bytes<GuestAddress>`
427/// trait to hide the details of accessing guest memory by physical address.
428/// Interior mutability is not allowed for implementations of `GuestMemory` so
429/// that they always provide a consistent view of the memory map.
430///
431/// The task of the `GuestMemory` trait are:
432/// - map a request address to a `GuestMemoryRegion` object and relay the request to it.
433/// - handle cases where an access request spanning two or more `GuestMemoryRegion` objects.
434pub trait GuestMemory {
435 /// Type of objects hosted by the address space.
436 type R: GuestMemoryRegion;
437
438 /// Returns the number of regions in the collection.
439 fn num_regions(&self) -> usize;
440
441 /// Returns the region containing the specified address or `None`.
442 fn find_region(&self, addr: GuestAddress) -> Option<&Self::R>;
443
444 /// Perform the specified action on each region.
445 ///
446 /// It only walks children of current region and does not step into sub regions.
447 #[deprecated(since = "0.6.0", note = "Use `.iter()` instead")]
448 fn with_regions<F, E>(&self, cb: F) -> std::result::Result<(), E>
449 where
450 F: Fn(usize, &Self::R) -> std::result::Result<(), E>,
451 {
452 for (index, region) in self.iter().enumerate() {
453 cb(index, region)?;
454 }
455 Ok(())
456 }
457
458 /// Perform the specified action on each region mutably.
459 ///
460 /// It only walks children of current region and does not step into sub regions.
461 #[deprecated(since = "0.6.0", note = "Use `.iter()` instead")]
462 fn with_regions_mut<F, E>(&self, mut cb: F) -> std::result::Result<(), E>
463 where
464 F: FnMut(usize, &Self::R) -> std::result::Result<(), E>,
465 {
466 for (index, region) in self.iter().enumerate() {
467 cb(index, region)?;
468 }
469 Ok(())
470 }
471
472 /// Gets an iterator over the entries in the collection.
473 ///
474 /// # Examples
475 ///
476 /// * Compute the total size of all memory mappings in KB by iterating over the memory regions
477 /// and dividing their sizes to 1024, then summing up the values in an accumulator. (uses the
478 /// `backend-mmap` feature)
479 ///
480 /// ```
481 /// # #[cfg(feature = "backend-mmap")]
482 /// # {
483 /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryRegion, GuestMemoryMmap};
484 /// #
485 /// let start_addr1 = GuestAddress(0x0);
486 /// let start_addr2 = GuestAddress(0x400);
487 /// let gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr1, 1024), (start_addr2, 2048)])
488 /// .expect("Could not create guest memory");
489 ///
490 /// let total_size = gm
491 /// .iter()
492 /// .map(|region| region.len() / 1024)
493 /// .fold(0, |acc, size| acc + size);
494 /// assert_eq!(3, total_size)
495 /// # }
496 /// ```
497 fn iter(&self) -> impl Iterator<Item = &Self::R>;
498
499 /// Applies two functions, specified as callbacks, on the inner memory regions.
500 ///
501 /// # Arguments
502 /// * `init` - Starting value of the accumulator for the `foldf` function.
503 /// * `mapf` - "Map" function, applied to all the inner memory regions. It returns an array of
504 /// the same size as the memory regions array, containing the function's results
505 /// for each region.
506 /// * `foldf` - "Fold" function, applied to the array returned by `mapf`. It acts as an
507 /// operator, applying itself to the `init` value and to each subsequent elemnent
508 /// in the array returned by `mapf`.
509 ///
510 /// # Examples
511 ///
512 /// * Compute the total size of all memory mappings in KB by iterating over the memory regions
513 /// and dividing their sizes to 1024, then summing up the values in an accumulator. (uses the
514 /// `backend-mmap` feature)
515 ///
516 /// ```
517 /// # #[cfg(feature = "backend-mmap")]
518 /// # {
519 /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryRegion, GuestMemoryMmap};
520 /// #
521 /// let start_addr1 = GuestAddress(0x0);
522 /// let start_addr2 = GuestAddress(0x400);
523 /// let gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr1, 1024), (start_addr2, 2048)])
524 /// .expect("Could not create guest memory");
525 ///
526 /// let total_size = gm.map_and_fold(0, |(_, region)| region.len() / 1024, |acc, size| acc + size);
527 /// assert_eq!(3, total_size)
528 /// # }
529 /// ```
530 #[deprecated(since = "0.6.0", note = "Use `.iter()` instead")]
531 fn map_and_fold<F, G, T>(&self, init: T, mapf: F, foldf: G) -> T
532 where
533 F: Fn((usize, &Self::R)) -> T,
534 G: Fn(T, T) -> T,
535 {
536 self.iter().enumerate().map(mapf).fold(init, foldf)
537 }
538
539 /// Returns the maximum (inclusive) address managed by the
540 /// [`GuestMemory`](trait.GuestMemory.html).
541 ///
542 /// # Examples (uses the `backend-mmap` feature)
543 ///
544 /// ```
545 /// # #[cfg(feature = "backend-mmap")]
546 /// # {
547 /// # use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap};
548 /// #
549 /// let start_addr = GuestAddress(0x1000);
550 /// let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)])
551 /// .expect("Could not create guest memory");
552 ///
553 /// assert_eq!(start_addr.checked_add(0x3ff), Some(gm.last_addr()));
554 /// # }
555 /// ```
556 fn last_addr(&self) -> GuestAddress {
557 self.iter()
558 .map(GuestMemoryRegion::last_addr)
559 .fold(GuestAddress(0), std::cmp::max)
560 }
561
562 /// Tries to convert an absolute address to a relative address within the corresponding region.
563 ///
564 /// Returns `None` if `addr` isn't present within the memory of the guest.
565 fn to_region_addr(&self, addr: GuestAddress) -> Option<(&Self::R, MemoryRegionAddress)> {
566 self.find_region(addr)
567 .map(|r| (r, r.to_region_addr(addr).unwrap()))
568 }
569
570 /// Returns `true` if the given address is present within the memory of the guest.
571 fn address_in_range(&self, addr: GuestAddress) -> bool {
572 self.find_region(addr).is_some()
573 }
574
575 /// Returns the given address if it is present within the memory of the guest.
576 fn check_address(&self, addr: GuestAddress) -> Option<GuestAddress> {
577 self.find_region(addr).map(|_| addr)
578 }
579
580 /// Check whether the range [base, base + len) is valid.
581 fn check_range(&self, base: GuestAddress, len: usize) -> bool {
582 match self.try_access(len, base, |_, count, _, _| -> Result<usize> { Ok(count) }) {
583 Ok(count) => count == len,
584 _ => false,
585 }
586 }
587
588 /// Returns the address plus the offset if it is present within the memory of the guest.
589 fn checked_offset(&self, base: GuestAddress, offset: usize) -> Option<GuestAddress> {
590 base.checked_add(offset as u64)
591 .and_then(|addr| self.check_address(addr))
592 }
593
594 /// Invokes callback `f` to handle data in the address range `[addr, addr + count)`.
595 ///
596 /// The address range `[addr, addr + count)` may span more than one
597 /// [`GuestMemoryRegion`](trait.GuestMemoryRegion.html) object, or even have holes in it.
598 /// So [`try_access()`](trait.GuestMemory.html#method.try_access) invokes the callback 'f'
599 /// for each [`GuestMemoryRegion`](trait.GuestMemoryRegion.html) object involved and returns:
600 /// - the error code returned by the callback 'f'
601 /// - the size of the already handled data when encountering the first hole
602 /// - the size of the already handled data when the whole range has been handled
603 fn try_access<F>(&self, count: usize, addr: GuestAddress, mut f: F) -> Result<usize>
604 where
605 F: FnMut(usize, usize, MemoryRegionAddress, &Self::R) -> Result<usize>,
606 {
607 let mut cur = addr;
608 let mut total = 0;
609 while let Some(region) = self.find_region(cur) {
610 let start = region.to_region_addr(cur).unwrap();
611 let cap = region.len() - start.raw_value();
612 let len = std::cmp::min(cap, (count - total) as GuestUsize);
613 match f(total, len as usize, start, region) {
614 // no more data
615 Ok(0) => return Ok(total),
616 // made some progress
617 Ok(len) => {
618 total = match total.checked_add(len) {
619 Some(x) if x < count => x,
620 Some(x) if x == count => return Ok(x),
621 _ => return Err(Error::CallbackOutOfRange),
622 };
623 cur = match cur.overflowing_add(len as GuestUsize) {
624 (x @ GuestAddress(0), _) | (x, false) => x,
625 (_, true) => return Err(Error::GuestAddressOverflow),
626 };
627 }
628 // error happened
629 e => return e,
630 }
631 }
632 if total == 0 {
633 Err(Error::InvalidGuestAddress(addr))
634 } else {
635 Ok(total)
636 }
637 }
638
639 /// Reads up to `count` bytes from an object and writes them into guest memory at `addr`.
640 ///
641 /// Returns the number of bytes written into guest memory.
642 ///
643 /// # Arguments
644 /// * `addr` - Begin writing at this address.
645 /// * `src` - Copy from `src` into the container.
646 /// * `count` - Copy `count` bytes from `src` into the container.
647 ///
648 /// # Examples
649 ///
650 /// * Read bytes from /dev/urandom (uses the `backend-mmap` feature)
651 ///
652 /// ```
653 /// # #[cfg(feature = "backend-mmap")]
654 /// # {
655 /// # use vm_memory::{Address, GuestMemory, Bytes, GuestAddress, GuestMemoryMmap};
656 /// # use std::fs::File;
657 /// # use std::path::Path;
658 /// #
659 /// # let start_addr = GuestAddress(0x1000);
660 /// # let gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)])
661 /// # .expect("Could not create guest memory");
662 /// # let addr = GuestAddress(0x1010);
663 /// # let mut file = if cfg!(unix) {
664 /// let mut file = File::open(Path::new("/dev/urandom")).expect("Could not open /dev/urandom");
665 /// # file
666 /// # } else {
667 /// # File::open(Path::new("c:\\Windows\\system32\\ntoskrnl.exe"))
668 /// # .expect("Could not open c:\\Windows\\system32\\ntoskrnl.exe")
669 /// # };
670 ///
671 /// gm.read_volatile_from(addr, &mut file, 128)
672 /// .expect("Could not read from /dev/urandom into guest memory");
673 ///
674 /// let read_addr = addr.checked_add(8).expect("Could not compute read address");
675 /// let rand_val: u32 = gm
676 /// .read_obj(read_addr)
677 /// .expect("Could not read u32 val from /dev/urandom");
678 /// # }
679 /// ```
680 fn read_volatile_from<F>(&self, addr: GuestAddress, src: &mut F, count: usize) -> Result<usize>
681 where
682 F: ReadVolatile,
683 {
684 self.try_access(count, addr, |offset, len, caddr, region| -> Result<usize> {
685 // Check if something bad happened before doing unsafe things.
686 assert!(offset <= count);
687
688 let mut vslice = region.get_slice(caddr, len)?;
689
690 src.read_volatile(&mut vslice)
691 .map_err(GuestMemoryError::from)
692 })
693 }
694
695 /// Reads up to `count` bytes from guest memory at `addr` and writes them it into an object.
696 ///
697 /// Returns the number of bytes copied from guest memory.
698 ///
699 /// # Arguments
700 /// * `addr` - Begin reading from this address.
701 /// * `dst` - Copy from guest memory to `dst`.
702 /// * `count` - Copy `count` bytes from guest memory to `dst`.
703 fn write_volatile_to<F>(&self, addr: GuestAddress, dst: &mut F, count: usize) -> Result<usize>
704 where
705 F: WriteVolatile,
706 {
707 self.try_access(count, addr, |offset, len, caddr, region| -> Result<usize> {
708 // Check if something bad happened before doing unsafe things.
709 assert!(offset <= count);
710
711 let vslice = region.get_slice(caddr, len)?;
712
713 // For a non-RAM region, reading could have side effects, so we
714 // must use write_all().
715 dst.write_all_volatile(&vslice)?;
716
717 Ok(len)
718 })
719 }
720
721 /// Reads exactly `count` bytes from an object and writes them into guest memory at `addr`.
722 ///
723 /// # Errors
724 ///
725 /// Returns an error if `count` bytes couldn't have been copied from `src` to guest memory.
726 /// Part of the data may have been copied nevertheless.
727 ///
728 /// # Arguments
729 /// * `addr` - Begin writing at this address.
730 /// * `src` - Copy from `src` into guest memory.
731 /// * `count` - Copy exactly `count` bytes from `src` into guest memory.
732 fn read_exact_volatile_from<F>(
733 &self,
734 addr: GuestAddress,
735 src: &mut F,
736 count: usize,
737 ) -> Result<()>
738 where
739 F: ReadVolatile,
740 {
741 let res = self.read_volatile_from(addr, src, count)?;
742 if res != count {
743 return Err(Error::PartialBuffer {
744 expected: count,
745 completed: res,
746 });
747 }
748 Ok(())
749 }
750
751 /// Reads exactly `count` bytes from guest memory at `addr` and writes them into an object.
752 ///
753 /// # Errors
754 ///
755 /// Returns an error if `count` bytes couldn't have been copied from guest memory to `dst`.
756 /// Part of the data may have been copied nevertheless.
757 ///
758 /// # Arguments
759 /// * `addr` - Begin reading from this address.
760 /// * `dst` - Copy from guest memory to `dst`.
761 /// * `count` - Copy exactly `count` bytes from guest memory to `dst`.
762 fn write_all_volatile_to<F>(&self, addr: GuestAddress, dst: &mut F, count: usize) -> Result<()>
763 where
764 F: WriteVolatile,
765 {
766 let res = self.write_volatile_to(addr, dst, count)?;
767 if res != count {
768 return Err(Error::PartialBuffer {
769 expected: count,
770 completed: res,
771 });
772 }
773 Ok(())
774 }
775
776 /// Get the host virtual address corresponding to the guest address.
777 ///
778 /// Some [`GuestMemory`](trait.GuestMemory.html) implementations, like `GuestMemoryMmap`,
779 /// have the capability to mmap the guest address range into virtual address space of the host
780 /// for direct access, so the corresponding host virtual address may be passed to other
781 /// subsystems.
782 ///
783 /// # Note
784 /// The underlying guest memory is not protected from memory aliasing, which breaks the
785 /// Rust memory safety model. It's the caller's responsibility to ensure that there's no
786 /// concurrent accesses to the underlying guest memory.
787 ///
788 /// # Arguments
789 /// * `addr` - Guest address to convert.
790 ///
791 /// # Examples (uses the `backend-mmap` feature)
792 ///
793 /// ```
794 /// # #[cfg(feature = "backend-mmap")]
795 /// # {
796 /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
797 /// #
798 /// # let start_addr = GuestAddress(0x1000);
799 /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x500)])
800 /// # .expect("Could not create guest memory");
801 /// #
802 /// let addr = gm
803 /// .get_host_address(GuestAddress(0x1200))
804 /// .expect("Could not get host address");
805 /// println!("Host address is {:p}", addr);
806 /// # }
807 /// ```
808 fn get_host_address(&self, addr: GuestAddress) -> Result<*mut u8> {
809 self.to_region_addr(addr)
810 .ok_or(Error::InvalidGuestAddress(addr))
811 .and_then(|(r, addr)| r.get_host_address(addr))
812 }
813
814 /// Returns a [`VolatileSlice`](struct.VolatileSlice.html) of `count` bytes starting at
815 /// `addr`.
816 fn get_slice(&self, addr: GuestAddress, count: usize) -> Result<VolatileSlice<MS<Self>>> {
817 self.to_region_addr(addr)
818 .ok_or(Error::InvalidGuestAddress(addr))
819 .and_then(|(r, addr)| r.get_slice(addr, count))
820 }
821}
822
823impl<T: GuestMemory + ?Sized> Bytes<GuestAddress> for T {
824 type E = Error;
825
826 fn write(&self, buf: &[u8], addr: GuestAddress) -> Result<usize> {
827 self.try_access(
828 buf.len(),
829 addr,
830 |offset, _count, caddr, region| -> Result<usize> {
831 region.write(&buf[offset..], caddr)
832 },
833 )
834 }
835
836 fn read(&self, buf: &mut [u8], addr: GuestAddress) -> Result<usize> {
837 self.try_access(
838 buf.len(),
839 addr,
840 |offset, _count, caddr, region| -> Result<usize> {
841 region.read(&mut buf[offset..], caddr)
842 },
843 )
844 }
845
846 /// # Examples
847 ///
848 /// * Write a slice at guestaddress 0x1000. (uses the `backend-mmap` feature)
849 ///
850 /// ```
851 /// # #[cfg(feature = "backend-mmap")]
852 /// # {
853 /// # use vm_memory::{Bytes, GuestAddress, mmap::GuestMemoryMmap};
854 /// #
855 /// # let start_addr = GuestAddress(0x1000);
856 /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)])
857 /// # .expect("Could not create guest memory");
858 /// #
859 /// gm.write_slice(&[1, 2, 3, 4, 5], start_addr)
860 /// .expect("Could not write slice to guest memory");
861 /// # }
862 /// ```
863 fn write_slice(&self, buf: &[u8], addr: GuestAddress) -> Result<()> {
864 let res = self.write(buf, addr)?;
865 if res != buf.len() {
866 return Err(Error::PartialBuffer {
867 expected: buf.len(),
868 completed: res,
869 });
870 }
871 Ok(())
872 }
873
874 /// # Examples
875 ///
876 /// * Read a slice of length 16 at guestaddress 0x1000. (uses the `backend-mmap` feature)
877 ///
878 /// ```
879 /// # #[cfg(feature = "backend-mmap")]
880 /// # {
881 /// # use vm_memory::{Bytes, GuestAddress, mmap::GuestMemoryMmap};
882 /// #
883 /// let start_addr = GuestAddress(0x1000);
884 /// let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)])
885 /// .expect("Could not create guest memory");
886 /// let buf = &mut [0u8; 16];
887 ///
888 /// gm.read_slice(buf, start_addr)
889 /// .expect("Could not read slice from guest memory");
890 /// # }
891 /// ```
892 fn read_slice(&self, buf: &mut [u8], addr: GuestAddress) -> Result<()> {
893 let res = self.read(buf, addr)?;
894 if res != buf.len() {
895 return Err(Error::PartialBuffer {
896 expected: buf.len(),
897 completed: res,
898 });
899 }
900 Ok(())
901 }
902
903 /// # Examples
904 ///
905 /// * Read bytes from /dev/urandom (uses the `backend-mmap` feature)
906 ///
907 /// ```
908 /// # #[cfg(feature = "backend-mmap")]
909 /// # {
910 /// # use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryMmap};
911 /// # use std::fs::File;
912 /// # use std::path::Path;
913 /// #
914 /// # let start_addr = GuestAddress(0x1000);
915 /// # let gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)])
916 /// # .expect("Could not create guest memory");
917 /// # let addr = GuestAddress(0x1010);
918 /// # let mut file = if cfg!(unix) {
919 /// let mut file = File::open(Path::new("/dev/urandom")).expect("Could not open /dev/urandom");
920 /// # file
921 /// # } else {
922 /// # File::open(Path::new("c:\\Windows\\system32\\ntoskrnl.exe"))
923 /// # .expect("Could not open c:\\Windows\\system32\\ntoskrnl.exe")
924 /// # };
925 ///
926 /// gm.read_from(addr, &mut file, 128)
927 /// .expect("Could not read from /dev/urandom into guest memory");
928 ///
929 /// let read_addr = addr.checked_add(8).expect("Could not compute read address");
930 /// let rand_val: u32 = gm
931 /// .read_obj(read_addr)
932 /// .expect("Could not read u32 val from /dev/urandom");
933 /// # }
934 /// ```
935 fn read_from<F>(&self, addr: GuestAddress, src: &mut F, count: usize) -> Result<usize>
936 where
937 F: Read,
938 {
939 self.try_access(count, addr, |offset, len, caddr, region| -> Result<usize> {
940 // Check if something bad happened before doing unsafe things.
941 assert!(offset <= count);
942
943 let len = std::cmp::min(len, MAX_ACCESS_CHUNK);
944 let mut buf = vec![0u8; len].into_boxed_slice();
945
946 loop {
947 match src.read(&mut buf[..]) {
948 Ok(bytes_read) => {
949 // We don't need to update the dirty bitmap manually here because it's
950 // expected to be handled by the logic within the `Bytes`
951 // implementation for the region object.
952 let bytes_written = region.write(&buf[0..bytes_read], caddr)?;
953 assert_eq!(bytes_written, bytes_read);
954 break Ok(bytes_read);
955 }
956 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
957 Err(e) => break Err(Error::IOError(e)),
958 }
959 }
960 })
961 }
962
963 fn read_exact_from<F>(&self, addr: GuestAddress, src: &mut F, count: usize) -> Result<()>
964 where
965 F: Read,
966 {
967 #[allow(deprecated)] // this function itself is deprecated
968 let res = self.read_from(addr, src, count)?;
969 if res != count {
970 return Err(Error::PartialBuffer {
971 expected: count,
972 completed: res,
973 });
974 }
975 Ok(())
976 }
977
978 /// # Examples
979 ///
980 /// * Write 128 bytes to /dev/null (uses the `backend-mmap` feature)
981 ///
982 /// ```
983 /// # #[cfg(not(unix))]
984 /// # extern crate vmm_sys_util;
985 /// # #[cfg(feature = "backend-mmap")]
986 /// # {
987 /// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
988 /// #
989 /// # let start_addr = GuestAddress(0x1000);
990 /// # let gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 1024)])
991 /// # .expect("Could not create guest memory");
992 /// # let mut file = if cfg!(unix) {
993 /// # use std::fs::OpenOptions;
994 /// let mut file = OpenOptions::new()
995 /// .write(true)
996 /// .open("/dev/null")
997 /// .expect("Could not open /dev/null");
998 /// # file
999 /// # } else {
1000 /// # use vmm_sys_util::tempfile::TempFile;
1001 /// # TempFile::new().unwrap().into_file()
1002 /// # };
1003 ///
1004 /// gm.write_to(start_addr, &mut file, 128)
1005 /// .expect("Could not write 128 bytes to the provided address");
1006 /// # }
1007 /// ```
1008 fn write_to<F>(&self, addr: GuestAddress, dst: &mut F, count: usize) -> Result<usize>
1009 where
1010 F: Write,
1011 {
1012 self.try_access(count, addr, |offset, len, caddr, region| -> Result<usize> {
1013 // Check if something bad happened before doing unsafe things.
1014 assert!(offset <= count);
1015
1016 let len = std::cmp::min(len, MAX_ACCESS_CHUNK);
1017 let mut buf = vec![0u8; len].into_boxed_slice();
1018 let bytes_read = region.read(&mut buf, caddr)?;
1019 assert_eq!(bytes_read, len);
1020 // For a non-RAM region, reading could have side effects, so we
1021 // must use write_all().
1022 dst.write_all(&buf).map_err(Error::IOError)?;
1023 Ok(len)
1024 })
1025 }
1026
1027 /// # Examples
1028 ///
1029 /// * Write 128 bytes to /dev/null (uses the `backend-mmap` feature)
1030 ///
1031 /// ```
1032 /// # #[cfg(not(unix))]
1033 /// # extern crate vmm_sys_util;
1034 /// # #[cfg(feature = "backend-mmap")]
1035 /// # {
1036 /// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
1037 /// #
1038 /// # let start_addr = GuestAddress(0x1000);
1039 /// # let gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 1024)])
1040 /// # .expect("Could not create guest memory");
1041 /// # let mut file = if cfg!(unix) {
1042 /// # use std::fs::OpenOptions;
1043 /// let mut file = OpenOptions::new()
1044 /// .write(true)
1045 /// .open("/dev/null")
1046 /// .expect("Could not open /dev/null");
1047 /// # file
1048 /// # } else {
1049 /// # use vmm_sys_util::tempfile::TempFile;
1050 /// # TempFile::new().unwrap().into_file()
1051 /// # };
1052 ///
1053 /// gm.write_all_to(start_addr, &mut file, 128)
1054 /// .expect("Could not write 128 bytes to the provided address");
1055 /// # }
1056 /// ```
1057 fn write_all_to<F>(&self, addr: GuestAddress, dst: &mut F, count: usize) -> Result<()>
1058 where
1059 F: Write,
1060 {
1061 #[allow(deprecated)] // this function itself is deprecated
1062 let res = self.write_to(addr, dst, count)?;
1063 if res != count {
1064 return Err(Error::PartialBuffer {
1065 expected: count,
1066 completed: res,
1067 });
1068 }
1069 Ok(())
1070 }
1071
1072 fn store<O: AtomicAccess>(&self, val: O, addr: GuestAddress, order: Ordering) -> Result<()> {
1073 // `find_region` should really do what `to_region_addr` is doing right now, except
1074 // it should keep returning a `Result`.
1075 self.to_region_addr(addr)
1076 .ok_or(Error::InvalidGuestAddress(addr))
1077 .and_then(|(region, region_addr)| region.store(val, region_addr, order))
1078 }
1079
1080 fn load<O: AtomicAccess>(&self, addr: GuestAddress, order: Ordering) -> Result<O> {
1081 self.to_region_addr(addr)
1082 .ok_or(Error::InvalidGuestAddress(addr))
1083 .and_then(|(region, region_addr)| region.load(region_addr, order))
1084 }
1085}
1086
1087#[cfg(test)]
1088mod tests {
1089 #![allow(clippy::undocumented_unsafe_blocks)]
1090 use super::*;
1091 #[cfg(feature = "backend-mmap")]
1092 use crate::bytes::ByteValued;
1093 #[cfg(feature = "backend-mmap")]
1094 use crate::GuestAddress;
1095 #[cfg(feature = "backend-mmap")]
1096 use std::time::{Duration, Instant};
1097
1098 use vmm_sys_util::tempfile::TempFile;
1099
1100 #[cfg(feature = "backend-mmap")]
1101 type GuestMemoryMmap = crate::GuestMemoryMmap<()>;
1102
1103 #[cfg(feature = "backend-mmap")]
1104 fn make_image(size: u8) -> Vec<u8> {
1105 let mut image: Vec<u8> = Vec::with_capacity(size as usize);
1106 for i in 0..size {
1107 image.push(i);
1108 }
1109 image
1110 }
1111
1112 #[test]
1113 fn test_file_offset() {
1114 let file = TempFile::new().unwrap().into_file();
1115 let start = 1234;
1116 let file_offset = FileOffset::new(file, start);
1117 assert_eq!(file_offset.start(), start);
1118 assert_eq!(
1119 file_offset.file() as *const File,
1120 file_offset.arc().as_ref() as *const File
1121 );
1122 }
1123
1124 #[cfg(feature = "backend-mmap")]
1125 #[test]
1126 fn checked_read_from() {
1127 let start_addr1 = GuestAddress(0x0);
1128 let start_addr2 = GuestAddress(0x40);
1129 let mem = GuestMemoryMmap::from_ranges(&[(start_addr1, 64), (start_addr2, 64)]).unwrap();
1130 let image = make_image(0x80);
1131 let offset = GuestAddress(0x30);
1132 let count: usize = 0x20;
1133 assert_eq!(
1134 0x20_usize,
1135 mem.read_volatile_from(offset, &mut image.as_slice(), count)
1136 .unwrap()
1137 );
1138 }
1139
1140 // Runs the provided closure in a loop, until at least `duration` time units have elapsed.
1141 #[cfg(feature = "backend-mmap")]
1142 fn loop_timed<F>(duration: Duration, mut f: F)
1143 where
1144 F: FnMut(),
1145 {
1146 // We check the time every `CHECK_PERIOD` iterations.
1147 const CHECK_PERIOD: u64 = 1_000_000;
1148 let start_time = Instant::now();
1149
1150 loop {
1151 for _ in 0..CHECK_PERIOD {
1152 f();
1153 }
1154 if start_time.elapsed() >= duration {
1155 break;
1156 }
1157 }
1158 }
1159
1160 // Helper method for the following test. It spawns a writer and a reader thread, which
1161 // simultaneously try to access an object that is placed at the junction of two memory regions.
1162 // The part of the object that's continuously accessed is a member of type T. The writer
1163 // flips all the bits of the member with every write, while the reader checks that every byte
1164 // has the same value (and thus it did not do a non-atomic access). The test succeeds if
1165 // no mismatch is detected after performing accesses for a pre-determined amount of time.
1166 #[cfg(feature = "backend-mmap")]
1167 #[cfg(not(miri))] // This test simulates a race condition between guest and vmm
1168 fn non_atomic_access_helper<T>()
1169 where
1170 T: ByteValued
1171 + std::fmt::Debug
1172 + From<u8>
1173 + Into<u128>
1174 + std::ops::Not<Output = T>
1175 + PartialEq,
1176 {
1177 use std::mem;
1178 use std::thread;
1179
1180 // A dummy type that's always going to have the same alignment as the first member,
1181 // and then adds some bytes at the end.
1182 #[derive(Clone, Copy, Debug, Default, PartialEq)]
1183 struct Data<T> {
1184 val: T,
1185 some_bytes: [u8; 8],
1186 }
1187
1188 // Some sanity checks.
1189 assert_eq!(mem::align_of::<T>(), mem::align_of::<Data<T>>());
1190 assert_eq!(mem::size_of::<T>(), mem::align_of::<T>());
1191
1192 // There must be no padding bytes, as otherwise implementing ByteValued is UB
1193 assert_eq!(mem::size_of::<Data<T>>(), mem::size_of::<T>() + 8);
1194
1195 unsafe impl<T: ByteValued> ByteValued for Data<T> {}
1196
1197 // Start of first guest memory region.
1198 let start = GuestAddress(0);
1199 let region_len = 1 << 12;
1200
1201 // The address where we start writing/reading a Data<T> value.
1202 let data_start = GuestAddress((region_len - mem::size_of::<T>()) as u64);
1203
1204 let mem = GuestMemoryMmap::from_ranges(&[
1205 (start, region_len),
1206 (start.unchecked_add(region_len as u64), region_len),
1207 ])
1208 .unwrap();
1209
1210 // Need to clone this and move it into the new thread we create.
1211 let mem2 = mem.clone();
1212 // Just some bytes.
1213 let some_bytes = [1u8, 2, 4, 16, 32, 64, 128, 255];
1214
1215 let mut data = Data {
1216 val: T::from(0u8),
1217 some_bytes,
1218 };
1219
1220 // Simple check that cross-region write/read is ok.
1221 mem.write_obj(data, data_start).unwrap();
1222 let read_data = mem.read_obj::<Data<T>>(data_start).unwrap();
1223 assert_eq!(read_data, data);
1224
1225 let t = thread::spawn(move || {
1226 let mut count: u64 = 0;
1227
1228 loop_timed(Duration::from_secs(3), || {
1229 let data = mem2.read_obj::<Data<T>>(data_start).unwrap();
1230
1231 // Every time data is written to memory by the other thread, the value of
1232 // data.val alternates between 0 and T::MAX, so the inner bytes should always
1233 // have the same value. If they don't match, it means we read a partial value,
1234 // so the access was not atomic.
1235 let bytes = data.val.into().to_le_bytes();
1236 for i in 1..mem::size_of::<T>() {
1237 if bytes[0] != bytes[i] {
1238 panic!(
1239 "val bytes don't match {:?} after {} iterations",
1240 &bytes[..mem::size_of::<T>()],
1241 count
1242 );
1243 }
1244 }
1245 count += 1;
1246 });
1247 });
1248
1249 // Write the object while flipping the bits of data.val over and over again.
1250 loop_timed(Duration::from_secs(3), || {
1251 mem.write_obj(data, data_start).unwrap();
1252 data.val = !data.val;
1253 });
1254
1255 t.join().unwrap()
1256 }
1257
1258 #[cfg(feature = "backend-mmap")]
1259 #[test]
1260 #[cfg(not(miri))]
1261 fn test_non_atomic_access() {
1262 non_atomic_access_helper::<u16>()
1263 }
1264
1265 #[cfg(feature = "backend-mmap")]
1266 #[test]
1267 fn test_zero_length_accesses() {
1268 #[derive(Default, Clone, Copy)]
1269 #[repr(C)]
1270 struct ZeroSizedStruct {
1271 dummy: [u32; 0],
1272 }
1273
1274 unsafe impl ByteValued for ZeroSizedStruct {}
1275
1276 let addr = GuestAddress(0x1000);
1277 let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap();
1278 let obj = ZeroSizedStruct::default();
1279 let mut image = make_image(0x80);
1280
1281 assert_eq!(mem.write(&[], addr).unwrap(), 0);
1282 assert_eq!(mem.read(&mut [], addr).unwrap(), 0);
1283
1284 assert!(mem.write_slice(&[], addr).is_ok());
1285 assert!(mem.read_slice(&mut [], addr).is_ok());
1286
1287 assert!(mem.write_obj(obj, addr).is_ok());
1288 assert!(mem.read_obj::<ZeroSizedStruct>(addr).is_ok());
1289
1290 assert_eq!(
1291 mem.read_volatile_from(addr, &mut image.as_slice(), 0)
1292 .unwrap(),
1293 0
1294 );
1295
1296 assert!(mem
1297 .read_exact_volatile_from(addr, &mut image.as_slice(), 0)
1298 .is_ok());
1299
1300 assert_eq!(
1301 mem.write_volatile_to(addr, &mut image.as_mut_slice(), 0)
1302 .unwrap(),
1303 0
1304 );
1305
1306 assert!(mem
1307 .write_all_volatile_to(addr, &mut image.as_mut_slice(), 0)
1308 .is_ok());
1309 }
1310
1311 #[cfg(feature = "backend-mmap")]
1312 #[test]
1313 fn test_atomic_accesses() {
1314 let addr = GuestAddress(0x1000);
1315 let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap();
1316 let bad_addr = addr.unchecked_add(0x1000);
1317
1318 crate::bytes::tests::check_atomic_accesses(mem, addr, bad_addr);
1319 }
1320
1321 #[cfg(feature = "backend-mmap")]
1322 #[cfg(target_os = "linux")]
1323 #[test]
1324 fn test_guest_memory_mmap_is_hugetlbfs() {
1325 let addr = GuestAddress(0x1000);
1326 let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap();
1327 let r = mem.find_region(addr).unwrap();
1328 assert_eq!(r.is_hugetlbfs(), None);
1329 }
1330}