From aa6956ed27bc39475bddae4bc08bda5237d25220 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 14 Mar 2025 10:17:11 +0000 Subject: [PATCH 01/13] refactor(xen): allow extracting guest_base from `MmapXen` Currently, on xen the guest base in tracked in two different places. GuestRegionMmap, defined in mmap.rs, and (almost) each of the implemented of XenMmapTrait. As a first step for eliminating this duplication, add getters so that the guest base stored in the XenMmapTrait trait object stored in MmapXen can be extracted. Signed-off-by: Patrick Roy --- src/mmap/xen.rs | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/mmap/xen.rs b/src/mmap/xen.rs index 3ffe3acb..ff0f39b7 100644 --- a/src/mmap/xen.rs +++ b/src/mmap/xen.rs @@ -508,11 +508,12 @@ fn validate_file(file_offset: &Option) -> Result<(i32, u64)> { trait MmapXenTrait: std::fmt::Debug { fn mmap_slice(&self, addr: *const u8, prot: i32, len: usize) -> Result; fn addr(&self) -> *mut u8; + fn guest_base(&self) -> GuestAddress; } // Standard Unix memory mapping for testing other crates. #[derive(Clone, Debug, PartialEq)] -struct MmapXenUnix(MmapUnix); +struct MmapXenUnix(MmapUnix, GuestAddress); impl MmapXenUnix { fn new(range: &MmapRange) -> Result { @@ -522,13 +523,16 @@ impl MmapXenUnix { (-1, 0) }; - Ok(Self(MmapUnix::new( - range.size, - range.prot.ok_or(Error::UnexpectedError)?, - range.flags.ok_or(Error::UnexpectedError)?, - fd, - offset, - )?)) + Ok(Self( + MmapUnix::new( + range.size, + range.prot.ok_or(Error::UnexpectedError)?, + range.flags.ok_or(Error::UnexpectedError)?, + fd, + offset, + )?, + range.addr, + )) } } @@ -541,6 +545,10 @@ impl MmapXenTrait for MmapXenUnix { fn addr(&self) -> *mut u8 { self.0.addr() } + + fn guest_base(&self) -> GuestAddress { + self.1 + } } // Privcmd mmap batch v2 command @@ -651,6 +659,10 @@ impl MmapXenTrait for MmapXenForeign { fn addr(&self) -> *mut u8 { self.unix_mmap.addr() } + + fn guest_base(&self) -> GuestAddress { + self.guest_base + } } // Xen Grant memory mapping interface. @@ -886,6 +898,10 @@ impl MmapXenTrait for MmapXenGrant { null_mut() } } + + fn guest_base(&self) -> GuestAddress { + self.guest_base + } } impl Drop for MmapXenGrant { From 135a42423af0dfd90af3907d33351a0b4d50abc7 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 14 Mar 2025 12:44:55 +0000 Subject: [PATCH 02/13] xen: implement GuestMemoryRegion for mmap_xen::MmapRegion Xen's MmapRegion struct already stores a `GuestAddress` inside of it, so there's no need to wrap it into a tuple of `(MmapRegion, GuestAddress)` to implement this trait on (which is essentially what happens in `mmap.rs`) - in fact, duplicating the GuestAddress seems wrong to be because I don't see any mechanism for keeping them in-sync. Do not duplication over the functions defined on mmap::GuestRegionMmap, because there already exist functions with colliding names in mmap_xen. Signed-off-by: Patrick Roy --- src/lib.rs | 3 +++ src/mmap/mod.rs | 2 +- src/mmap/xen.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3d4ad1e9..71ef1a36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,6 +72,9 @@ pub use mmap::{GuestMemoryMmap, GuestRegionMmap, MmapRegion}; #[cfg(all(feature = "backend-mmap", feature = "xen", target_family = "unix"))] pub use mmap::{MmapRange, MmapXenFlags}; +#[cfg(all(feature = "xen", unix))] +pub use mmap::xen::MmapRegion as MmapRegionXen; + pub mod volatile_memory; pub use volatile_memory::{ Error as VolatileMemoryError, Result as VolatileMemoryResult, VolatileArrayRef, VolatileMemory, diff --git a/src/mmap/mod.rs b/src/mmap/mod.rs index 7d133bdf..cd92bdfc 100644 --- a/src/mmap/mod.rs +++ b/src/mmap/mod.rs @@ -151,7 +151,7 @@ impl GuestMemoryRegion for GuestRegionMmap { offset: MemoryRegionAddress, count: usize, ) -> guest_memory::Result>> { - let slice = self.mapping.get_slice(offset.raw_value() as usize, count)?; + let slice = VolatileMemory::get_slice(&self.mapping, offset.raw_value() as usize, count)?; Ok(slice) } diff --git a/src/mmap/xen.rs b/src/mmap/xen.rs index ff0f39b7..afa67bdf 100644 --- a/src/mmap/xen.rs +++ b/src/mmap/xen.rs @@ -27,6 +27,10 @@ use tests::ioctl_with_ref; use crate::bitmap::{Bitmap, NewBitmap, BS}; use crate::guest_memory::{FileOffset, GuestAddress}; use crate::volatile_memory::{self, VolatileMemory, VolatileSlice}; +use crate::{ + guest_memory, Address, GuestMemoryRegion, GuestMemoryRegionBytes, GuestUsize, + MemoryRegionAddress, +}; /// Error conditions that may arise when creating a new `MmapRegion` object. #[derive(Debug, thiserror::Error)] @@ -151,6 +155,50 @@ pub struct MmapRegion { mmap: MmapXen, } +impl GuestMemoryRegion for MmapRegion { + type B = B; + + fn len(&self) -> GuestUsize { + self.size as GuestUsize + } + + fn start_addr(&self) -> GuestAddress { + self.mmap.mmap.guest_base() + } + + fn bitmap(&self) -> BS<'_, Self::B> { + self.bitmap.slice_at(0) + } + + // TODO: MmapRegion::as_ptr states that it should only be used for passing pointers to ioctls. Should this function then just remain the default implementation of returning Err(InvalidHostAddress)? + fn get_host_address(&self, addr: MemoryRegionAddress) -> crate::guest_memory::Result<*mut u8> { + self.check_address(addr) + .ok_or(guest_memory::Error::InvalidBackendAddress) + .map(|addr| self.as_ptr().wrapping_offset(addr.raw_value() as isize)) + } + + fn file_offset(&self) -> Option<&FileOffset> { + self.file_offset.as_ref() + } + + fn get_slice( + &self, + offset: MemoryRegionAddress, + count: usize, + ) -> crate::guest_memory::Result>> { + VolatileMemory::get_slice(self, offset.raw_value() as usize, count).map_err(Into::into) + } + + // TODO: does this make sense in the context of Xen, or should it just return None, as the default implementation does? + // (and if running on Xen, will target_os="linux" even be true?) + #[cfg(target_os = "linux")] + fn is_hugetlbfs(&self) -> Option { + self.hugetlbfs + } +} + +impl GuestMemoryRegionBytes for MmapRegion {} + // SAFETY: Send and Sync aren't automatically inherited for the raw address pointer. // Accessing that pointer is only done through the stateless interface which // allows the object to be shared by multiple threads without a decrease in @@ -308,8 +356,8 @@ impl MmapRegion { if f_off1.file().as_raw_fd() == f_off2.file().as_raw_fd() { let s1 = f_off1.start(); let s2 = f_off2.start(); - let l1 = self.len() as u64; - let l2 = other.len() as u64; + let l1 = self.size as u64; + let l2 = other.size as u64; if s1 < s2 { return s1 + l1 > s2; From 6fb2b8ce850add52692361ff6e253d1dd3569000 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 14 Mar 2025 13:24:50 +0000 Subject: [PATCH 03/13] xen: Introduce GuestMemoryXen and use it in doc tests This is a xen specific version of `GuestMemoryMmap`. This makes the xen module independent of the cfg'd types in mmap.rs, meaning we are almost ready to allow enabling both Mmap and Xen support at the same time. Signed-off-by: Patrick Roy --- src/lib.rs | 2 +- src/mmap/xen.rs | 32 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 71ef1a36..bf4da403 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,7 @@ pub use mmap::{GuestMemoryMmap, GuestRegionMmap, MmapRegion}; pub use mmap::{MmapRange, MmapXenFlags}; #[cfg(all(feature = "xen", unix))] -pub use mmap::xen::MmapRegion as MmapRegionXen; +pub use mmap::xen::{GuestMemoryXen, MmapRegion as MmapRegionXen}; pub mod volatile_memory; pub use volatile_memory::{ diff --git a/src/mmap/xen.rs b/src/mmap/xen.rs index afa67bdf..35177c19 100644 --- a/src/mmap/xen.rs +++ b/src/mmap/xen.rs @@ -28,8 +28,8 @@ use crate::bitmap::{Bitmap, NewBitmap, BS}; use crate::guest_memory::{FileOffset, GuestAddress}; use crate::volatile_memory::{self, VolatileMemory, VolatileSlice}; use crate::{ - guest_memory, Address, GuestMemoryRegion, GuestMemoryRegionBytes, GuestUsize, - MemoryRegionAddress, + guest_memory, Address, GuestMemoryRegion, GuestMemoryRegionBytes, GuestRegionCollection, + GuestUsize, MemoryRegionAddress, }; /// Error conditions that may arise when creating a new `MmapRegion` object. @@ -199,6 +199,12 @@ impl GuestMemoryRegion for MmapRegion { impl GuestMemoryRegionBytes for MmapRegion {} +/// A collection of Xen guest memory regions. +/// +/// Represents the entire physical memory of the guest by tracking all its memory regions. +/// Each region is an instance of [`MmapRegionXen`]. +pub type GuestMemoryXen = GuestRegionCollection>; + // SAFETY: Send and Sync aren't automatically inherited for the raw address pointer. // Accessing that pointer is only done through the stateless interface which // allows the object to be shared by multiple threads without a decrease in @@ -220,8 +226,7 @@ impl MmapRegion { /// use std::fs::File; /// use std::path::Path; /// use vm_memory::{ - /// Bytes, FileOffset, GuestAddress, GuestMemoryMmap, GuestRegionMmap, MmapRange, MmapRegion, - /// MmapXenFlags, + /// Bytes, FileOffset, GuestAddress, GuestMemoryXen, MmapRange, MmapRegionXen, MmapXenFlags, /// }; /// # use vmm_sys_util::tempfile::TempFile; /// @@ -237,13 +242,9 @@ impl MmapRegion { /// # // We need a UNIX mapping for tests to succeed. /// # let range = MmapRange::new_unix(0x400, None, addr); /// - /// let r = GuestRegionMmap::new( - /// MmapRegion::<()>::from_range(range).expect("Could not create mmap region"), - /// addr, - /// ) - /// .expect("Could not create guest region"); + /// let r = MmapRegionXen::<()>::from_range(range).expect("Could not create mmap region"); /// - /// let mut gm = GuestMemoryMmap::from_regions(vec![r]).expect("Could not create guest memory"); + /// let mut gm = GuestMemoryXen::from_regions(vec![r]).expect("Could not create guest memory"); /// let res = gm /// .write(&[1, 2, 3, 4, 5], GuestAddress(0x1200)) /// .expect("Could not write to guest memory"); @@ -256,8 +257,7 @@ impl MmapRegion { /// use std::fs::File; /// use std::path::Path; /// use vm_memory::{ - /// Bytes, FileOffset, GuestAddress, GuestMemoryMmap, GuestRegionMmap, MmapRange, MmapRegion, - /// MmapXenFlags, + /// Bytes, FileOffset, GuestAddress, GuestMemoryXen, MmapRange, MmapRegionXen, MmapXenFlags, /// }; /// # use vmm_sys_util::tempfile::TempFile; /// @@ -273,13 +273,9 @@ impl MmapRegion { /// # // We need a UNIX mapping for tests to succeed. /// # let range = MmapRange::new_unix(0x400, None, addr); /// - /// let r = GuestRegionMmap::new( - /// MmapRegion::<()>::from_range(range).expect("Could not create mmap region"), - /// addr, - /// ) - /// .expect("Could not create guest region"); + /// let r = MmapRegionXen::<()>::from_range(range).expect("Could not create mmap region"); /// - /// let mut gm = GuestMemoryMmap::from_regions(vec![r]).expect("Could not create guest memory"); + /// let mut gm = GuestMemoryXen::from_regions(vec![r]).expect("Could not create guest memory"); /// let res = gm /// .write(&[1, 2, 3, 4, 5], GuestAddress(0x1200)) /// .expect("Could not write to guest memory"); From 39f346a8323e3fa9c61d07be056b2622293b0e1b Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 14 Mar 2025 16:22:34 +0000 Subject: [PATCH 04/13] test: run mmap.rs tests with all available backends Currently, these tests are run either with the Xen backend, or the mmap backend, depending on which cargo features are enabled. But since the xen feature disables mmap_unix, effectively this means if we do cargo test --all-features, only the xen backend is tested. Refactor our testing story to support running tests with _all_ available backends, so that in a world where both the xen and non-xen backends can coexist, tests will be run for both if --all-features is passed. This won't win beauty contests, but it works for now. Signed-off-by: Patrick Roy --- src/mmap/mod.rs | 545 +++++++++++++++++++++++++++--------------------- 1 file changed, 307 insertions(+), 238 deletions(-) diff --git a/src/mmap/mod.rs b/src/mmap/mod.rs index cd92bdfc..db95e2bb 100644 --- a/src/mmap/mod.rs +++ b/src/mmap/mod.rs @@ -226,28 +226,173 @@ mod tests { use crate::{Bytes, GuestMemory, GuestMemoryError}; use std::io::Write; + use std::sync::Arc; #[cfg(feature = "rawfd")] use std::{fs::File, path::Path}; use vmm_sys_util::tempfile::TempFile; use matches::assert_matches; - type GuestRegionMmap = super::GuestRegionMmap<()>; - type GuestMemoryMmap = super::GuestRegionCollection; - type MmapRegion = super::MmapRegion<()>; + macro_rules! any_backend { + ($($(#[$attr:meta])* $backend: ident[$region: path]), *) => { + #[derive(Debug)] + enum AnyRegion { + $( + $(#[$attr])* $backend($region) + ),* + } + + type AnyBackend = $crate::GuestRegionCollection; + + impl $crate::GuestMemoryRegion for AnyRegion { + type B = (); + + fn len(&self) -> GuestUsize { + match self { + $($(#[$attr])* AnyRegion::$backend(inner) => $crate::GuestMemoryRegion::len(inner)),* + } + } + + fn start_addr(&self) -> GuestAddress { + match self { + $($(#[$attr])* AnyRegion::$backend(inner) => inner.start_addr()),* + } + } + + fn bitmap(&self) { } + + fn get_host_address(&self, addr: MemoryRegionAddress) -> guest_memory::Result<*mut u8> { + match self { + $($(#[$attr])* AnyRegion::$backend(inner) => inner.get_host_address(addr)),* + } + } + + fn file_offset(&self) -> Option<&FileOffset> { + match self { + $($(#[$attr])* AnyRegion::$backend(inner) => inner.file_offset()),* + } + } + + fn get_slice(&self, offset: MemoryRegionAddress, count: usize) -> guest_memory::Result>> { + match self { + $($(#[$attr])* AnyRegion::$backend(inner) => $crate::GuestMemoryRegion::get_slice(inner, offset, count)),* + } + } + } + + impl GuestMemoryRegionBytes for AnyRegion {} + }; + } + + any_backend! { + #[cfg(all(windows, feature = "backend-mmap"))] + Windows[GuestRegionMmap<()>], + #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] + Mmap[GuestRegionMmap<()>], + #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] + Xen[crate::mmap::xen::MmapRegion] + } + + // The cfgs make using vec![...] instead more unreadable, so suppress the lint here. + #[allow(clippy::vec_init_then_push)] + impl AnyRegion { + fn all_with_file(addr: GuestAddress, size: usize, f_off: &FileOffset) -> Vec { + let mut regions = Vec::new(); + #[cfg(all(windows, feature = "backend-mmap"))] + regions.push(AnyRegion::Windows( + GuestRegionMmap::new(MmapRegion::from_file(f_off.clone(), size).unwrap(), addr) + .unwrap(), + )); + #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] + regions.push(AnyRegion::Mmap( + GuestRegionMmap::new(MmapRegion::from_file(f_off.clone(), size).unwrap(), addr) + .unwrap(), + )); + #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] + regions.push(AnyRegion::Xen( + MmapRegion::from_range(MmapRange::new_unix(size, Some(f_off.clone()), addr)) + .unwrap(), + )); + regions + } + + fn all(addr: GuestAddress, size: usize, file: &Option) -> Vec { + let mut regions = if let Some(file) = file { + Self::all_with_file(addr, size, file) + } else { + Vec::new() + }; + #[cfg(all(windows, feature = "backend-mmap"))] + regions.push(AnyRegion::Windows( + GuestRegionMmap::new(MmapRegion::new(size).unwrap(), addr).unwrap(), + )); + #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] + regions.push(AnyRegion::Mmap( + GuestRegionMmap::new(MmapRegion::new(size).unwrap(), addr).unwrap(), + )); + #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] + regions.push(AnyRegion::Xen( + MmapRegion::from_range(MmapRange::new_unix(size, None, addr)).unwrap(), + )); + regions + } + + fn as_ptr(&self) -> *mut u8 { + self.get_host_address(MemoryRegionAddress(0)).unwrap() + } + } + + fn transpose(striped: Vec>) -> Vec { + assert!(!striped.is_empty(), "No test cases found!"); + + let mut backends = (0..striped[0].len()) + .map(|_| AnyBackend::default()) + .collect::>(); + for stripe in striped { + backends + .iter_mut() + .zip(stripe) + .for_each(|(backend, region)| { + *backend = backend.insert_region(Arc::new(region)).unwrap(); + }); + } + backends + } + + impl AnyBackend { + fn all_with_file(regions: &[(GuestAddress, usize, FileOffset)]) -> Vec { + let striped = regions + .iter() + .map(|(addr, size, file)| AnyRegion::all_with_file(*addr, *size, file)) + .collect::>(); + + transpose(striped) + } + + fn all(regions: &[(GuestAddress, usize, Option)]) -> Vec { + let striped = regions + .iter() + .map(|(addr, size, file)| AnyRegion::all(*addr, *size, file)) + .collect::>(); + + transpose(striped) + } + } #[test] fn basic_map() { - let m = MmapRegion::new(1024).unwrap(); - assert_eq!(1024, m.size()); + for m in AnyRegion::all(GuestAddress(0), 1024, &None) { + assert_eq!(1024, m.len()); + } } #[test] fn slice_addr() { - let m = GuestRegionMmap::from_range(GuestAddress(0), 5, None).unwrap(); - let s = m.get_slice(MemoryRegionAddress(2), 3).unwrap(); - let guard = s.ptr_guard(); - assert_eq!(guard.as_ptr(), unsafe { m.as_ptr().offset(2) }); + for m in AnyRegion::all(GuestAddress(0), 5, &None) { + let s = m.get_slice(MemoryRegionAddress(2), 3).unwrap(); + let guard = s.ptr_guard(); + assert_eq!(guard.as_ptr(), unsafe { m.as_ptr().offset(2) }); + } } #[test] @@ -257,14 +402,15 @@ mod tests { let sample_buf = &[1, 2, 3, 4, 5]; assert!(f.write_all(sample_buf).is_ok()); - let file = Some(FileOffset::new(f, 0)); - let mem_map = GuestRegionMmap::from_range(GuestAddress(0), sample_buf.len(), file).unwrap(); - let buf = &mut [0u8; 16]; - assert_eq!( - mem_map.as_volatile_slice().unwrap().read(buf, 0).unwrap(), - sample_buf.len() - ); - assert_eq!(buf[0..sample_buf.len()], sample_buf[..]); + let file = FileOffset::new(f, 0); + for mem_map in AnyRegion::all_with_file(GuestAddress(0), sample_buf.len(), &file) { + let buf = &mut [0u8; 16]; + assert_eq!( + mem_map.as_volatile_slice().unwrap().read(buf, 0).unwrap(), + sample_buf.len() + ); + assert_eq!(buf[0..sample_buf.len()], sample_buf[..]); + } } #[test] @@ -276,20 +422,14 @@ mod tests { let start_addr1 = GuestAddress(0x0); let start_addr2 = GuestAddress(0x800); - let guest_mem = - GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap(); - let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[ + for guest_mem in AnyBackend::all(&[ (start_addr1, 0x400, Some(FileOffset::new(f1, 0))), (start_addr2, 0x400, Some(FileOffset::new(f2, 0))), - ]) - .unwrap(); - - let guest_mem_list = [guest_mem, guest_mem_backed_by_file]; - for guest_mem in guest_mem_list.iter() { + ]) { assert!(guest_mem.to_region_addr(GuestAddress(0x600)).is_none()); let (r0, addr0) = guest_mem.to_region_addr(GuestAddress(0x800)).unwrap(); let (r1, addr1) = guest_mem.to_region_addr(GuestAddress(0xa00)).unwrap(); - assert!(r0.as_ptr() == r1.as_ptr()); + assert_eq!(r0.as_ptr(), r1.as_ptr()); assert_eq!(addr0, MemoryRegionAddress(0)); assert_eq!(addr1, MemoryRegionAddress(0x200)); } @@ -304,16 +444,10 @@ mod tests { let start_addr1 = GuestAddress(0x0); let start_addr2 = GuestAddress(0x800); - let guest_mem = - GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap(); - let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[ + for guest_mem in AnyBackend::all(&[ (start_addr1, 0x400, Some(FileOffset::new(f1, 0))), (start_addr2, 0x400, Some(FileOffset::new(f2, 0))), - ]) - .unwrap(); - - let guest_mem_list = [guest_mem, guest_mem_backed_by_file]; - for guest_mem in guest_mem_list.iter() { + ]) { assert!(guest_mem.get_host_address(GuestAddress(0x600)).is_err()); let ptr0 = guest_mem.get_host_address(GuestAddress(0x800)).unwrap(); let ptr1 = guest_mem.get_host_address(GuestAddress(0xa00)).unwrap(); @@ -330,22 +464,21 @@ mod tests { let start_addr1 = GuestAddress(0); let start_addr2 = GuestAddress(0x800); let start_addr3 = GuestAddress(0xc00); - let guest_mem = GuestMemoryMmap::from_ranges(&[ - (start_addr1, 0x400), - (start_addr2, 0x400), - (start_addr3, 0x400), - ]) - .unwrap(); - - assert!(guest_mem.check_range(start_addr1, 0x0)); - assert!(guest_mem.check_range(start_addr1, 0x200)); - assert!(guest_mem.check_range(start_addr1, 0x400)); - assert!(!guest_mem.check_range(start_addr1, 0xa00)); - assert!(guest_mem.check_range(start_addr2, 0x7ff)); - assert!(guest_mem.check_range(start_addr2, 0x800)); - assert!(!guest_mem.check_range(start_addr2, 0x801)); - assert!(!guest_mem.check_range(start_addr2, 0xc00)); - assert!(!guest_mem.check_range(start_addr1, usize::MAX)); + for guest_mem in AnyBackend::all(&[ + (start_addr1, 0x400, None), + (start_addr2, 0x400, None), + (start_addr3, 0x400, None), + ]) { + assert!(guest_mem.check_range(start_addr1, 0x0)); + assert!(guest_mem.check_range(start_addr1, 0x200)); + assert!(guest_mem.check_range(start_addr1, 0x400)); + assert!(!guest_mem.check_range(start_addr1, 0xa00)); + assert!(guest_mem.check_range(start_addr2, 0x7ff)); + assert!(guest_mem.check_range(start_addr2, 0x800)); + assert!(!guest_mem.check_range(start_addr2, 0x801)); + assert!(!guest_mem.check_range(start_addr2, 0xc00)); + assert!(!guest_mem.check_range(start_addr1, usize::MAX)); + } } #[test] @@ -354,16 +487,7 @@ mod tests { f.set_len(0x400).unwrap(); let start_addr = GuestAddress(0x0); - let guest_mem = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap(); - let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[( - start_addr, - 0x400, - Some(FileOffset::new(f, 0)), - )]) - .unwrap(); - - let guest_mem_list = [guest_mem, guest_mem_backed_by_file]; - for guest_mem in guest_mem_list.iter() { + for guest_mem in AnyBackend::all(&[(start_addr, 0x400, Some(FileOffset::new(f, 0)))]) { let sample_buf = &[1, 2, 3, 4, 5]; assert_eq!(guest_mem.write(sample_buf, start_addr).unwrap(), 5); @@ -392,16 +516,10 @@ mod tests { let bad_addr2 = GuestAddress(0x1ffc); let max_addr = GuestAddress(0x2000); - let gm = - GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap(); - let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[ + for gm in AnyBackend::all(&[ (start_addr1, 0x1000, Some(FileOffset::new(f1, 0))), (start_addr2, 0x1000, Some(FileOffset::new(f2, 0))), - ]) - .unwrap(); - - let gm_list = [gm, gm_backed_by_file]; - for gm in gm_list.iter() { + ]) { let val1: u64 = 0xaa55_aa55_aa55_aa55; let val2: u64 = 0x55aa_55aa_55aa_55aa; assert_matches!( @@ -427,16 +545,7 @@ mod tests { f.set_len(0x400).unwrap(); let mut start_addr = GuestAddress(0x1000); - let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap(); - let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[( - start_addr, - 0x400, - Some(FileOffset::new(f, 0)), - )]) - .unwrap(); - - let gm_list = [gm, gm_backed_by_file]; - for gm in gm_list.iter() { + for gm in AnyBackend::all(&[(start_addr, 0x400, Some(FileOffset::new(f, 0)))]) { let sample_buf = &[1, 2, 3, 4, 5]; assert_eq!(gm.write(sample_buf, start_addr).unwrap(), 5); @@ -462,22 +571,9 @@ mod tests { let f = TempFile::new().unwrap().into_file(); f.set_len(0x400).unwrap(); - let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x1000), 0x400)]).unwrap(); - let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[( - GuestAddress(0x1000), - 0x400, - Some(FileOffset::new(f, 0)), - )]) - .unwrap(); - - let gm_list = [gm, gm_backed_by_file]; - for gm in gm_list.iter() { + for gm in AnyBackend::all(&[(GuestAddress(0x1000), 0x400, Some(FileOffset::new(f, 0)))]) { let addr = GuestAddress(0x1010); - let mut file = if cfg!(target_family = "unix") { - File::open(Path::new("/dev/zero")).unwrap() - } else { - File::open(Path::new("c:\\Windows\\system32\\ntoskrnl.exe")).unwrap() - }; + let mut file = File::open(Path::new("/dev/zero")).unwrap(); gm.write_obj(!0u32, addr).unwrap(); gm.read_exact_volatile_from(addr, &mut file, mem::size_of::()) .unwrap(); @@ -491,11 +587,7 @@ mod tests { let mut sink = vec![0; mem::size_of::()]; gm.write_all_volatile_to(addr, &mut sink.as_mut_slice(), mem::size_of::()) .unwrap(); - if cfg!(target_family = "unix") { - assert_eq!(sink, vec![0; mem::size_of::()]); - } else { - assert_eq!(sink, vec![0x4d, 0x5a, 0x90, 0x00]); - }; + assert_eq!(sink, vec![0; mem::size_of::()]); } } @@ -508,16 +600,10 @@ mod tests { let start_addr1 = GuestAddress(0x0); let start_addr2 = GuestAddress(0x1000); - let gm = - GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap(); - let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[ + for gm in AnyBackend::all(&[ (start_addr1, 0x1000, Some(FileOffset::new(f1, 0))), (start_addr2, 0x1000, Some(FileOffset::new(f2, 0))), - ]) - .unwrap(); - - let gm_list = [gm, gm_backed_by_file]; - for gm in gm_list.iter() { + ]) { let sample_buf = &[1, 2, 3, 4, 5]; assert_eq!(gm.write(sample_buf, GuestAddress(0xffc)).unwrap(), 5); let buf = &mut [0u8; 5]; @@ -532,20 +618,11 @@ mod tests { f.set_len(0x400).unwrap(); let start_addr = GuestAddress(0x0); - let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap(); - assert!(gm.find_region(start_addr).is_some()); - let region = gm.find_region(start_addr).unwrap(); - assert!(region.file_offset().is_none()); - - let gm = GuestMemoryMmap::from_ranges_with_files(&[( - start_addr, - 0x400, - Some(FileOffset::new(f, 0)), - )]) - .unwrap(); - assert!(gm.find_region(start_addr).is_some()); - let region = gm.find_region(start_addr).unwrap(); - assert!(region.file_offset().is_some()); + for gm in AnyBackend::all_with_file(&[(start_addr, 0x400, FileOffset::new(f, 0))]) { + assert!(gm.find_region(start_addr).is_some()); + let region = gm.find_region(start_addr).unwrap(); + assert!(region.file_offset().is_some()); + } } // Windows needs a dedicated test where it will retrieve the allocation @@ -561,91 +638,84 @@ mod tests { let offset = 0x1000; let start_addr = GuestAddress(0x0); - let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap(); - assert!(gm.find_region(start_addr).is_some()); - let region = gm.find_region(start_addr).unwrap(); - assert!(region.file_offset().is_none()); - - let gm = GuestMemoryMmap::from_ranges_with_files(&[( - start_addr, - 0x400, - Some(FileOffset::new(f, offset)), - )]) - .unwrap(); - assert!(gm.find_region(start_addr).is_some()); - let region = gm.find_region(start_addr).unwrap(); - assert!(region.file_offset().is_some()); - assert_eq!(region.file_offset().unwrap().start(), offset); + + for gm in AnyBackend::all_with_file(&[(start_addr, 0x400, FileOffset::new(f, offset))]) { + assert!(gm.find_region(start_addr).is_some()); + let region = gm.find_region(start_addr).unwrap(); + assert!(region.file_offset().is_some()); + assert_eq!(region.file_offset().unwrap().start(), offset); + } } #[test] fn test_guest_memory_mmap_get_slice() { - let region = GuestRegionMmap::from_range(GuestAddress(0), 0x400, None).unwrap(); - - // Normal case. - let slice_addr = MemoryRegionAddress(0x100); - let slice_size = 0x200; - let slice = region.get_slice(slice_addr, slice_size).unwrap(); - assert_eq!(slice.len(), slice_size); - - // Empty slice. - let slice_addr = MemoryRegionAddress(0x200); - let slice_size = 0x0; - let slice = region.get_slice(slice_addr, slice_size).unwrap(); - assert!(slice.is_empty()); - - // Error case when slice_size is beyond the boundary. - let slice_addr = MemoryRegionAddress(0x300); - let slice_size = 0x200; - assert!(region.get_slice(slice_addr, slice_size).is_err()); + for region in AnyRegion::all(GuestAddress(0), 0x400, &None) { + // Normal case. + let slice_addr = MemoryRegionAddress(0x100); + let slice_size = 0x200; + let slice = region.get_slice(slice_addr, slice_size).unwrap(); + assert_eq!(slice.len(), slice_size); + + // Empty slice. + let slice_addr = MemoryRegionAddress(0x200); + let slice_size = 0x0; + let slice = region.get_slice(slice_addr, slice_size).unwrap(); + assert!(slice.is_empty()); + + // Error case when slice_size is beyond the boundary. + let slice_addr = MemoryRegionAddress(0x300); + let slice_size = 0x200; + assert!(region.get_slice(slice_addr, slice_size).is_err()); + } } #[test] fn test_guest_memory_mmap_as_volatile_slice() { let region_size = 0x400; - let region = GuestRegionMmap::from_range(GuestAddress(0), region_size, None).unwrap(); - // Test slice length. - let slice = region.as_volatile_slice().unwrap(); - assert_eq!(slice.len(), region_size); + for region in AnyRegion::all(GuestAddress(0), region_size, &None) { + // Test slice length. + let slice = region.as_volatile_slice().unwrap(); + assert_eq!(slice.len(), region_size); - // Test slice data. - let v = 0x1234_5678u32; - let r = slice.get_ref::(0x200).unwrap(); - r.store(v); - assert_eq!(r.load(), v); + // Test slice data. + let v = 0x1234_5678u32; + let r = slice.get_ref::(0x200).unwrap(); + r.store(v); + assert_eq!(r.load(), v); + } } #[test] fn test_guest_memory_get_slice() { let start_addr1 = GuestAddress(0); let start_addr2 = GuestAddress(0x800); - let guest_mem = - GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap(); - - // Normal cases. - let slice_size = 0x200; - let slice = guest_mem - .get_slice(GuestAddress(0x100), slice_size) - .unwrap(); - assert_eq!(slice.len(), slice_size); - - let slice_size = 0x400; - let slice = guest_mem - .get_slice(GuestAddress(0x800), slice_size) - .unwrap(); - assert_eq!(slice.len(), slice_size); - - // Empty slice. - assert!(guest_mem - .get_slice(GuestAddress(0x900), 0) - .unwrap() - .is_empty()); - - // Error cases, wrong size or base address. - assert!(guest_mem.get_slice(GuestAddress(0), 0x500).is_err()); - assert!(guest_mem.get_slice(GuestAddress(0x600), 0x100).is_err()); - assert!(guest_mem.get_slice(GuestAddress(0xc00), 0x100).is_err()); + for guest_mem in AnyBackend::all(&[(start_addr1, 0x400, None), (start_addr2, 0x400, None)]) + { + // Normal cases. + let slice_size = 0x200; + let slice = guest_mem + .get_slice(GuestAddress(0x100), slice_size) + .unwrap(); + assert_eq!(slice.len(), slice_size); + + let slice_size = 0x400; + let slice = guest_mem + .get_slice(GuestAddress(0x800), slice_size) + .unwrap(); + assert_eq!(slice.len(), slice_size); + + // Empty slice. + assert!(guest_mem + .get_slice(GuestAddress(0x900), 0) + .unwrap() + .is_empty()); + + // Error cases, wrong size or base address. + assert!(guest_mem.get_slice(GuestAddress(0), 0x500).is_err()); + assert!(guest_mem.get_slice(GuestAddress(0x600), 0x100).is_err()); + assert!(guest_mem.get_slice(GuestAddress(0xc00), 0x100).is_err()); + } } #[test] @@ -653,60 +723,59 @@ mod tests { let start_addr1 = GuestAddress(0); let start_addr2 = GuestAddress(0x800); let start_addr3 = GuestAddress(0xc00); - let guest_mem = GuestMemoryMmap::from_ranges(&[ - (start_addr1, 0x400), - (start_addr2, 0x400), - (start_addr3, 0x400), - ]) - .unwrap(); - - // Same cases as `test_guest_memory_get_slice()`, just with `get_slices()`. - let slice_size = 0x200; - let mut slices = guest_mem.get_slices(GuestAddress(0x100), slice_size); - let slice = slices.next().unwrap().unwrap(); - assert!(slices.next().is_none()); - assert_eq!(slice.len(), slice_size); - - let slice_size = 0x400; - let mut slices = guest_mem.get_slices(GuestAddress(0x800), slice_size); - let slice = slices.next().unwrap().unwrap(); - assert!(slices.next().is_none()); - assert_eq!(slice.len(), slice_size); - - // Empty iterator. - assert!(guest_mem - .get_slices(GuestAddress(0x900), 0) - .next() - .is_none()); - - // Error cases, wrong size or base address. - let mut slices = guest_mem.get_slices(GuestAddress(0), 0x500); - assert_eq!(slices.next().unwrap().unwrap().len(), 0x400); - assert!(slices.next().unwrap().is_err()); - assert!(slices.next().is_none()); - let mut slices = guest_mem.get_slices(GuestAddress(0x600), 0x100); - assert!(slices.next().unwrap().is_err()); - assert!(slices.next().is_none()); - let mut slices = guest_mem.get_slices(GuestAddress(0x1000), 0x100); - assert!(slices.next().unwrap().is_err()); - assert!(slices.next().is_none()); - - // Test fragmented case - let mut slices = guest_mem.get_slices(GuestAddress(0xa00), 0x400); - assert_eq!(slices.next().unwrap().unwrap().len(), 0x200); - assert_eq!(slices.next().unwrap().unwrap().len(), 0x200); - assert!(slices.next().is_none()); + for guest_mem in AnyBackend::all(&[ + (start_addr1, 0x400, None), + (start_addr2, 0x400, None), + (start_addr3, 0x400, None), + ]) { + // Same cases as `test_guest_memory_get_slice()`, just with `get_slices()`. + let slice_size = 0x200; + let mut slices = guest_mem.get_slices(GuestAddress(0x100), slice_size); + let slice = slices.next().unwrap().unwrap(); + assert!(slices.next().is_none()); + assert_eq!(slice.len(), slice_size); + + let slice_size = 0x400; + let mut slices = guest_mem.get_slices(GuestAddress(0x800), slice_size); + let slice = slices.next().unwrap().unwrap(); + assert!(slices.next().is_none()); + assert_eq!(slice.len(), slice_size); + + // Empty iterator. + assert!(guest_mem + .get_slices(GuestAddress(0x900), 0) + .next() + .is_none()); + + // Error cases, wrong size or base address. + let mut slices = guest_mem.get_slices(GuestAddress(0), 0x500); + assert_eq!(slices.next().unwrap().unwrap().len(), 0x400); + assert!(slices.next().unwrap().is_err()); + assert!(slices.next().is_none()); + let mut slices = guest_mem.get_slices(GuestAddress(0x600), 0x100); + assert!(slices.next().unwrap().is_err()); + assert!(slices.next().is_none()); + let mut slices = guest_mem.get_slices(GuestAddress(0x1000), 0x100); + assert!(slices.next().unwrap().is_err()); + assert!(slices.next().is_none()); + + // Test fragmented case + let mut slices = guest_mem.get_slices(GuestAddress(0xa00), 0x400); + assert_eq!(slices.next().unwrap().unwrap().len(), 0x200); + assert_eq!(slices.next().unwrap().unwrap().len(), 0x200); + assert!(slices.next().is_none()); + } } #[test] fn test_atomic_accesses() { - let region = GuestRegionMmap::from_range(GuestAddress(0), 0x1000, None).unwrap(); - - crate::bytes::tests::check_atomic_accesses( - region, - MemoryRegionAddress(0), - MemoryRegionAddress(0x1000), - ); + for region in AnyRegion::all(GuestAddress(0), 0x1000, &None) { + crate::bytes::tests::check_atomic_accesses( + region, + MemoryRegionAddress(0), + MemoryRegionAddress(0x1000), + ); + } } #[test] From 705a13ea59ceadc84d9b5e43c133ebf38e03efcd Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 14 Mar 2025 16:29:31 +0000 Subject: [PATCH 05/13] test: remove duplicated test `test_atomic_accesses` in `guest_memory.rs` is exactly the same as `test_atomic_accesses` in `mmap.rs`, so delete the former. Signed-off-by: Patrick Roy --- src/guest_memory.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/guest_memory.rs b/src/guest_memory.rs index 7136db35..9b155c35 100644 --- a/src/guest_memory.rs +++ b/src/guest_memory.rs @@ -947,16 +947,6 @@ mod tests { .is_ok()); } - #[cfg(feature = "backend-mmap")] - #[test] - fn test_atomic_accesses() { - let addr = GuestAddress(0x1000); - let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap(); - let bad_addr = addr.unchecked_add(0x1000); - - crate::bytes::tests::check_atomic_accesses(mem, addr, bad_addr); - } - #[cfg(feature = "backend-mmap")] #[cfg(target_os = "linux")] #[test] From 217b29ecf3d973aa446f0a21779177831cac343a Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 14 Mar 2025 16:36:40 +0000 Subject: [PATCH 06/13] refactor(test): use new text fixtures in guest_memory.rs There are a lot of backend-mmap dependent tests in guest_memory.rs, so have them also use the new capabilities to run with all supported backends. Signed-off-by: Patrick Roy --- src/guest_memory.rs | 210 ++++++++++++++++++++++---------------------- src/mmap/mod.rs | 10 ++- 2 files changed, 111 insertions(+), 109 deletions(-) diff --git a/src/guest_memory.rs b/src/guest_memory.rs index 9b155c35..1ca8c0bf 100644 --- a/src/guest_memory.rs +++ b/src/guest_memory.rs @@ -726,27 +726,16 @@ impl Bytes for T { #[cfg(test)] mod tests { #![allow(clippy::undocumented_unsafe_blocks)] + use super::*; #[cfg(feature = "backend-mmap")] - use crate::bytes::ByteValued; - #[cfg(feature = "backend-mmap")] - use crate::GuestAddress; - #[cfg(feature = "backend-mmap")] use std::time::{Duration, Instant}; - use vmm_sys_util::tempfile::TempFile; - #[cfg(feature = "backend-mmap")] - type GuestMemoryMmap = crate::GuestMemoryMmap<()>; - + use crate::mmap::tests::AnyBackend; #[cfg(feature = "backend-mmap")] - fn make_image(size: u8) -> Vec { - let mut image: Vec = Vec::with_capacity(size as usize); - for i in 0..size { - image.push(i); - } - image - } + use crate::ByteValued; + use vmm_sys_util::tempfile::TempFile; #[test] fn test_file_offset() { @@ -761,19 +750,29 @@ mod tests { } #[cfg(feature = "backend-mmap")] + fn make_image(size: u8) -> Vec { + let mut image: Vec = Vec::with_capacity(size as usize); + for i in 0..size { + image.push(i); + } + image + } + #[test] + #[cfg(feature = "backend-mmap")] fn checked_read_from() { let start_addr1 = GuestAddress(0x0); let start_addr2 = GuestAddress(0x40); - let mem = GuestMemoryMmap::from_ranges(&[(start_addr1, 64), (start_addr2, 64)]).unwrap(); - let image = make_image(0x80); - let offset = GuestAddress(0x30); - let count: usize = 0x20; - assert_eq!( - 0x20_usize, - mem.read_volatile_from(offset, &mut image.as_slice(), count) - .unwrap() - ); + for mem in AnyBackend::all(&[(start_addr1, 64, None), (start_addr2, 64, None)]) { + let image = make_image(0x80); + let offset = GuestAddress(0x30); + let count: usize = 0x20; + assert_eq!( + 0x20_usize, + mem.read_volatile_from(offset, &mut image.as_slice(), count) + .unwrap() + ); + } } // Runs the provided closure in a loop, until at least `duration` time units have elapsed. @@ -802,8 +801,8 @@ mod tests { // flips all the bits of the member with every write, while the reader checks that every byte // has the same value (and thus it did not do a non-atomic access). The test succeeds if // no mismatch is detected after performing accesses for a pre-determined amount of time. - #[cfg(feature = "backend-mmap")] #[cfg(not(miri))] // This test simulates a race condition between guest and vmm + #[cfg(feature = "backend-mmap")] fn non_atomic_access_helper() where T: ByteValued @@ -840,69 +839,68 @@ mod tests { // The address where we start writing/reading a Data value. let data_start = GuestAddress((region_len - mem::size_of::()) as u64); - let mem = GuestMemoryMmap::from_ranges(&[ - (start, region_len), - (start.unchecked_add(region_len as u64), region_len), - ]) - .unwrap(); - - // Need to clone this and move it into the new thread we create. - let mem2 = mem.clone(); - // Just some bytes. - let some_bytes = [1u8, 2, 4, 16, 32, 64, 128, 255]; - - let mut data = Data { - val: T::from(0u8), - some_bytes, - }; - - // Simple check that cross-region write/read is ok. - mem.write_obj(data, data_start).unwrap(); - let read_data = mem.read_obj::>(data_start).unwrap(); - assert_eq!(read_data, data); - - let t = thread::spawn(move || { - let mut count: u64 = 0; - - loop_timed(Duration::from_secs(3), || { - let data = mem2.read_obj::>(data_start).unwrap(); - - // Every time data is written to memory by the other thread, the value of - // data.val alternates between 0 and T::MAX, so the inner bytes should always - // have the same value. If they don't match, it means we read a partial value, - // so the access was not atomic. - let bytes = data.val.into().to_le_bytes(); - for i in 1..mem::size_of::() { - if bytes[0] != bytes[i] { - panic!( - "val bytes don't match {:?} after {} iterations", - &bytes[..mem::size_of::()], - count - ); + for mem in AnyBackend::all(&[ + (start, region_len, None), + (start.unchecked_add(region_len as u64), region_len, None), + ]) { + // Need to clone this and move it into the new thread we create. + let mem2 = mem.clone(); + // Just some bytes. + let some_bytes = [1u8, 2, 4, 16, 32, 64, 128, 255]; + + let mut data = Data { + val: T::from(0u8), + some_bytes, + }; + + // Simple check that cross-region write/read is ok. + mem.write_obj(data, data_start).unwrap(); + let read_data = mem.read_obj::>(data_start).unwrap(); + assert_eq!(read_data, data); + + let t = thread::spawn(move || { + let mut count: u64 = 0; + + loop_timed(Duration::from_secs(3), || { + let data = mem2.read_obj::>(data_start).unwrap(); + + // Every time data is written to memory by the other thread, the value of + // data.val alternates between 0 and T::MAX, so the inner bytes should always + // have the same value. If they don't match, it means we read a partial value, + // so the access was not atomic. + let bytes = data.val.into().to_le_bytes(); + for i in 1..mem::size_of::() { + if bytes[0] != bytes[i] { + panic!( + "val bytes don't match {:?} after {} iterations", + &bytes[..mem::size_of::()], + count + ); + } } - } - count += 1; + count += 1; + }); }); - }); - // Write the object while flipping the bits of data.val over and over again. - loop_timed(Duration::from_secs(3), || { - mem.write_obj(data, data_start).unwrap(); - data.val = !data.val; - }); + // Write the object while flipping the bits of data.val over and over again. + loop_timed(Duration::from_secs(3), || { + mem.write_obj(data, data_start).unwrap(); + data.val = !data.val; + }); - t.join().unwrap() + t.join().unwrap() + } } - #[cfg(feature = "backend-mmap")] #[test] #[cfg(not(miri))] + #[cfg(feature = "backend-mmap")] fn test_non_atomic_access() { non_atomic_access_helper::() } - #[cfg(feature = "backend-mmap")] #[test] + #[cfg(feature = "backend-mmap")] fn test_zero_length_accesses() { #[derive(Default, Clone, Copy)] #[repr(C)] @@ -913,47 +911,49 @@ mod tests { unsafe impl ByteValued for ZeroSizedStruct {} let addr = GuestAddress(0x1000); - let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap(); - let obj = ZeroSizedStruct::default(); - let mut image = make_image(0x80); + for mem in AnyBackend::all(&[(addr, 0x1000, None)]) { + let obj = ZeroSizedStruct::default(); + let mut image = make_image(0x80); - assert_eq!(mem.write(&[], addr).unwrap(), 0); - assert_eq!(mem.read(&mut [], addr).unwrap(), 0); + assert_eq!(mem.write(&[], addr).unwrap(), 0); + assert_eq!(mem.read(&mut [], addr).unwrap(), 0); - assert!(mem.write_slice(&[], addr).is_ok()); - assert!(mem.read_slice(&mut [], addr).is_ok()); + assert!(mem.write_slice(&[], addr).is_ok()); + assert!(mem.read_slice(&mut [], addr).is_ok()); - assert!(mem.write_obj(obj, addr).is_ok()); - assert!(mem.read_obj::(addr).is_ok()); + assert!(mem.write_obj(obj, addr).is_ok()); + assert!(mem.read_obj::(addr).is_ok()); - assert_eq!( - mem.read_volatile_from(addr, &mut image.as_slice(), 0) - .unwrap(), - 0 - ); - - assert!(mem - .read_exact_volatile_from(addr, &mut image.as_slice(), 0) - .is_ok()); + assert_eq!( + mem.read_volatile_from(addr, &mut image.as_slice(), 0) + .unwrap(), + 0 + ); - assert_eq!( - mem.write_volatile_to(addr, &mut image.as_mut_slice(), 0) - .unwrap(), - 0 - ); + assert!(mem + .read_exact_volatile_from(addr, &mut image.as_slice(), 0) + .is_ok()); - assert!(mem - .write_all_volatile_to(addr, &mut image.as_mut_slice(), 0) - .is_ok()); + assert_eq!( + mem.write_volatile_to(addr, &mut image.as_mut_slice(), 0) + .unwrap(), + 0 + ); + + assert!(mem + .write_all_volatile_to(addr, &mut image.as_mut_slice(), 0) + .is_ok()); + } } - #[cfg(feature = "backend-mmap")] #[cfg(target_os = "linux")] #[test] + #[cfg(feature = "backend-mmap")] fn test_guest_memory_mmap_is_hugetlbfs() { let addr = GuestAddress(0x1000); - let mem = GuestMemoryMmap::from_ranges(&[(addr, 0x1000)]).unwrap(); - let r = mem.find_region(addr).unwrap(); - assert_eq!(r.is_hugetlbfs(), None); + for mem in AnyBackend::all(&[(addr, 0x1000, None)]) { + let r = mem.find_region(addr).unwrap(); + assert_eq!(r.is_hugetlbfs(), None); + } } } diff --git a/src/mmap/mod.rs b/src/mmap/mod.rs index db95e2bb..6f0d2774 100644 --- a/src/mmap/mod.rs +++ b/src/mmap/mod.rs @@ -215,7 +215,7 @@ impl GuestMemoryMmap { } #[cfg(test)] -mod tests { +pub(crate) mod tests { #![allow(clippy::undocumented_unsafe_blocks)] extern crate vmm_sys_util; @@ -236,13 +236,13 @@ mod tests { macro_rules! any_backend { ($($(#[$attr:meta])* $backend: ident[$region: path]), *) => { #[derive(Debug)] - enum AnyRegion { + pub enum AnyRegion { $( $(#[$attr])* $backend($region) ),* } - type AnyBackend = $crate::GuestRegionCollection; + pub type AnyBackend = $crate::GuestRegionCollection; impl $crate::GuestMemoryRegion for AnyRegion { type B = (); @@ -369,7 +369,9 @@ mod tests { transpose(striped) } - fn all(regions: &[(GuestAddress, usize, Option)]) -> Vec { + pub(crate) fn all( + regions: &[(GuestAddress, usize, Option)], + ) -> Vec { let striped = regions .iter() .map(|(addr, size, file)| AnyRegion::all(*addr, *size, file)) From 320da5cb5b09172402b6ebad3aa17221908c8b05 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 14 Mar 2025 17:15:09 +0000 Subject: [PATCH 07/13] refactor: scatter mmap.rs contents to unix/windows modules This is some duplication, but it once and for all eliminates all cfg fuckery and code that expects precisely one module to be defined. Admittedly, for windows/unix we could have stuck with the cfg stuff, but I wanted a clean slate for now. The windows parts are compile-tested only, as I do not have a windows system available. The handling of various from_range functions is based on the assumption that these were only used in test code and nowhere else (hence all the preceding refactors of test code). In unit tests, their use has been eliminated, and doc tests now got a cfg(target_family="unix", not(feature="xen")) so that they only run when mmap_unix is available. This is fine, because they are examples of how to use specific functions, and not any serious tests (e.g. dont need to run for all supported backends). Signed-off-by: Patrick Roy --- benches/guest_memory.rs | 2 +- benches/main.rs | 8 +- benches/mmap/mod.rs | 2 +- src/bitmap/mod.rs | 6 +- src/bytes.rs | 2 +- src/guest_memory.rs | 12 +-- src/lib.rs | 14 ++- src/mmap/mod.rs | 234 +++++----------------------------------- src/mmap/unix.rs | 161 ++++++++++++++++++++++++++- src/mmap/windows.rs | 80 ++++++++++++++ src/region.rs | 12 +-- 11 files changed, 302 insertions(+), 231 deletions(-) diff --git a/benches/guest_memory.rs b/benches/guest_memory.rs index 4fdce1f5..35242def 100644 --- a/benches/guest_memory.rs +++ b/benches/guest_memory.rs @@ -1,7 +1,7 @@ // Copyright (C) 2020 Alibaba Cloud Computing. All rights reserved. // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause -#![cfg(feature = "backend-mmap")] +#![cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_family = "windows")))] use core::hint::black_box; pub use criterion::Criterion; diff --git a/benches/main.rs b/benches/main.rs index 83d50f2a..eb854c10 100644 --- a/benches/main.rs +++ b/benches/main.rs @@ -5,7 +5,7 @@ extern crate criterion; pub use criterion::{criterion_group, criterion_main, Criterion}; -#[cfg(feature = "backend-mmap")] +#[cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_os = "windows")))] use vm_memory::{GuestAddress, GuestMemoryMmap}; mod guest_memory; @@ -14,7 +14,7 @@ mod volatile; use volatile::benchmark_for_volatile; -#[cfg(feature = "backend-mmap")] +#[cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_os = "windows")))] // Use this function with caution. It does not check against overflows // and `GuestMemoryMmap::from_ranges` errors. fn create_guest_memory_mmap(size: usize, count: u64) -> GuestMemoryMmap<()> { @@ -27,12 +27,12 @@ fn create_guest_memory_mmap(size: usize, count: u64) -> GuestMemoryMmap<()> { } pub fn criterion_benchmark(_c: &mut Criterion) { - #[cfg(feature = "backend-mmap")] + #[cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_os = "windows")))] mmap::benchmark_for_mmap(_c); } pub fn benchmark_guest_memory(_c: &mut Criterion) { - #[cfg(feature = "backend-mmap")] + #[cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_os = "windows")))] guest_memory::benchmark_for_guest_memory(_c) } diff --git a/benches/mmap/mod.rs b/benches/mmap/mod.rs index 4fdb8001..df3ff6e5 100644 --- a/benches/mmap/mod.rs +++ b/benches/mmap/mod.rs @@ -1,7 +1,7 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause -#![cfg(feature = "backend-mmap")] +#![cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_family = "windows")))] #![allow(clippy::undocumented_unsafe_blocks)] extern crate criterion; diff --git a/src/bitmap/mod.rs b/src/bitmap/mod.rs index 352b970c..ec81ce6d 100644 --- a/src/bitmap/mod.rs +++ b/src/bitmap/mod.rs @@ -125,7 +125,7 @@ pub(crate) mod tests { use std::sync::atomic::Ordering; use crate::{Bytes, VolatileMemory}; - #[cfg(feature = "backend-mmap")] + #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] use crate::{GuestAddress, MemoryRegionAddress}; // Helper method to check whether a specified range is clean. @@ -219,7 +219,7 @@ pub(crate) mod tests { // them to test the mmap-based backend implementations for now. Going forward, the generic // test functions defined here can be placed in a separate module (i.e. `test_utilities`) // which is gated by a feature and can be used for testing purposes by other crates as well. - #[cfg(feature = "backend-mmap")] + #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] fn test_guest_memory_region(region: &R) { let dirty_addr = MemoryRegionAddress(0x0); let val = 123u64; @@ -253,7 +253,7 @@ pub(crate) mod tests { ); } - #[cfg(feature = "backend-mmap")] + #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] // Assumptions about M generated by f ... pub fn test_guest_memory_and_region(f: F) where diff --git a/src/bytes.rs b/src/bytes.rs index 2b48dbbf..c44593ad 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -331,7 +331,7 @@ pub trait Bytes { /// * Read bytes from /dev/urandom (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", feature = "rawfd"))] + /// # #[cfg(all(feature = "backend-mmap", feature = "rawfd", target_family = "unix", not(feature = "xen")))] /// # { /// # use vm_memory::{Address, GuestMemory, Bytes, GuestAddress, GuestMemoryMmap}; /// # use std::fs::File; diff --git a/src/guest_memory.rs b/src/guest_memory.rs index 1ca8c0bf..1a11a943 100644 --- a/src/guest_memory.rs +++ b/src/guest_memory.rs @@ -176,7 +176,7 @@ impl FileOffset { /// # Examples (uses the `backend-mmap` and `backend-atomic` features) /// /// ``` -/// # #[cfg(feature = "backend-mmap")] +/// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # use std::sync::Arc; /// # use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryMmap}; @@ -294,7 +294,7 @@ pub trait GuestMemory { /// `backend-mmap` feature) /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryRegion, GuestMemoryMmap}; /// # @@ -318,7 +318,7 @@ pub trait GuestMemory { /// # Examples (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap}; /// # @@ -433,7 +433,7 @@ pub trait GuestMemory { /// # Examples (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap}; /// # @@ -592,7 +592,7 @@ impl Bytes for T { /// * Write a slice at guestaddress 0x1000. (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # use vm_memory::{Bytes, GuestAddress, mmap::GuestMemoryMmap}; /// # @@ -620,7 +620,7 @@ impl Bytes for T { /// * Read a slice of length 16 at guestaddress 0x1000. (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # use vm_memory::{Bytes, GuestAddress, mmap::GuestMemoryMmap}; /// # diff --git a/src/lib.rs b/src/lib.rs index bf4da403..597b325c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,14 +67,22 @@ pub use io::{ReadVolatile, WriteVolatile}; #[cfg(feature = "backend-mmap")] pub mod mmap; -#[cfg(feature = "backend-mmap")] -pub use mmap::{GuestMemoryMmap, GuestRegionMmap, MmapRegion}; #[cfg(all(feature = "backend-mmap", feature = "xen", target_family = "unix"))] pub use mmap::{MmapRange, MmapXenFlags}; -#[cfg(all(feature = "xen", unix))] +#[cfg(all(feature = "xen", target_family = "unix"))] pub use mmap::xen::{GuestMemoryXen, MmapRegion as MmapRegionXen}; +#[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] +pub use mmap::unix::{Error as MmapRegionError, GuestMemoryMmap, GuestRegionMmap, MmapRegion}; + +#[cfg(all(feature = "backend-mmap", target_family = "windows"))] +pub use crate::mmap::windows::{ + GuestMemoryWindows as GuestMemoryMmap, GuestRegionWindows as GuestRegionMmap, MmapRegion, +}; // rename for backwards compat +#[cfg(all(feature = "backend-mmap", target_family = "windows"))] +pub use std::io::Error as MmapRegionError; + pub mod volatile_memory; pub use volatile_memory::{ Error as VolatileMemoryError, Result as VolatileMemoryResult, VolatileArrayRef, VolatileMemory, diff --git a/src/mmap/mod.rs b/src/mmap/mod.rs index 6f0d2774..a0924b50 100644 --- a/src/mmap/mod.rs +++ b/src/mmap/mod.rs @@ -12,29 +12,17 @@ //! //! This implementation is mmap-ing the memory of the guest into the current process. -use std::borrow::Borrow; -use std::ops::Deref; -use std::result; - -use crate::address::Address; -use crate::bitmap::{Bitmap, BS}; -use crate::guest_memory::{self, FileOffset, GuestAddress, GuestUsize, MemoryRegionAddress}; -use crate::region::{ - GuestMemoryRegion, GuestMemoryRegionBytes, GuestRegionCollection, GuestRegionCollectionError, -}; -use crate::volatile_memory::{VolatileMemory, VolatileSlice}; - // re-export for backward compat, as the trait used to be defined in mmap.rs pub use crate::bitmap::NewBitmap; #[cfg(all(not(feature = "xen"), target_family = "unix"))] -mod unix; +pub(super) mod unix; #[cfg(all(feature = "xen", target_family = "unix"))] -pub(crate) mod xen; +pub(super) mod xen; #[cfg(target_family = "windows")] -mod windows; +pub(super) mod windows; #[cfg(all(not(feature = "xen"), target_family = "unix"))] pub use unix::{Error as MmapRegionError, MmapRegion, MmapRegionBuilder}; @@ -47,183 +35,17 @@ pub use std::io::Error as MmapRegionError; #[cfg(target_family = "windows")] pub use windows::MmapRegion; -/// [`GuestMemoryRegion`](trait.GuestMemoryRegion.html) implementation that mmaps the guest's -/// memory region in the current process. -/// -/// Represents a continuous region of the guest's physical memory that is backed by a mapping -/// in the virtual address space of the calling process. -#[derive(Debug)] -pub struct GuestRegionMmap { - mapping: MmapRegion, - guest_base: GuestAddress, -} - -impl Deref for GuestRegionMmap { - type Target = MmapRegion; - - fn deref(&self) -> &MmapRegion { - &self.mapping - } -} - -impl GuestRegionMmap { - /// Create a new memory-mapped memory region for the guest's physical memory. - /// - /// Returns `None` if `guest_base` + `mapping.len()` would overflow. - pub fn new(mapping: MmapRegion, guest_base: GuestAddress) -> Option { - guest_base - .0 - .checked_add(mapping.size() as u64) - .map(|_| Self { - mapping, - guest_base, - }) - } -} - -#[cfg(not(feature = "xen"))] -impl GuestRegionMmap { - /// Create a new memory-mapped memory region from guest's physical memory, size and file. - pub fn from_range( - addr: GuestAddress, - size: usize, - file: Option, - ) -> result::Result { - let region = if let Some(ref f_off) = file { - MmapRegion::from_file(f_off.clone(), size)? - } else { - MmapRegion::new(size)? - }; - - Self::new(region, addr).ok_or(FromRangesError::InvalidGuestRegion) - } -} - -#[cfg(feature = "xen")] -impl GuestRegionMmap { - /// Create a new Unix memory-mapped memory region from guest's physical memory, size and file. - /// This must only be used for tests, doctests, benches and is not designed for end consumers. - pub fn from_range( - addr: GuestAddress, - size: usize, - file: Option, - ) -> result::Result { - let range = MmapRange::new_unix(size, file, addr); - - let region = MmapRegion::from_range(range)?; - Self::new(region, addr).ok_or(FromRangesError::InvalidGuestRegion) - } -} - -impl GuestMemoryRegion for GuestRegionMmap { - type B = B; - - fn len(&self) -> GuestUsize { - self.mapping.size() as GuestUsize - } - - fn start_addr(&self) -> GuestAddress { - self.guest_base - } - - fn bitmap(&self) -> BS<'_, Self::B> { - self.mapping.bitmap().slice_at(0) - } - - fn get_host_address(&self, addr: MemoryRegionAddress) -> guest_memory::Result<*mut u8> { - // Not sure why wrapping_offset is not unsafe. Anyway this - // is safe because we've just range-checked addr using check_address. - self.check_address(addr) - .ok_or(guest_memory::Error::InvalidBackendAddress) - .map(|addr| { - self.mapping - .as_ptr() - .wrapping_offset(addr.raw_value() as isize) - }) - } - - fn file_offset(&self) -> Option<&FileOffset> { - self.mapping.file_offset() - } - - fn get_slice( - &self, - offset: MemoryRegionAddress, - count: usize, - ) -> guest_memory::Result>> { - let slice = VolatileMemory::get_slice(&self.mapping, offset.raw_value() as usize, count)?; - Ok(slice) - } - - #[cfg(target_os = "linux")] - fn is_hugetlbfs(&self) -> Option { - self.mapping.is_hugetlbfs() - } -} - -impl GuestMemoryRegionBytes for GuestRegionMmap {} - -/// [`GuestMemory`](trait.GuestMemory.html) implementation that mmaps the guest's memory -/// in the current process. -/// -/// Represents the entire physical memory of the guest by tracking all its memory regions. -/// Each region is an instance of `GuestRegionMmap`, being backed by a mapping in the -/// virtual address space of the calling process. -pub type GuestMemoryMmap = GuestRegionCollection>; - -/// Errors that can happen during [`GuestMemoryMmap::from_ranges`] and related functions. -#[derive(Debug, thiserror::Error)] -pub enum FromRangesError { - /// Error during construction of [`GuestMemoryMmap`] - #[error("Error constructing guest region collection: {0}")] - Collection(#[from] GuestRegionCollectionError), - /// Error while allocating raw mmap region - #[error("Error setting up raw memory for guest region: {0}")] - MmapRegion(#[from] MmapRegionError), - /// A combination of region length and guest address would overflow. - #[error("Combination of guest address and region length invalid (would overflow)")] - InvalidGuestRegion, -} - -impl GuestMemoryMmap { - /// Creates a container and allocates anonymous memory for guest memory regions. - /// - /// Valid memory regions are specified as a slice of (Address, Size) tuples sorted by Address. - pub fn from_ranges(ranges: &[(GuestAddress, usize)]) -> result::Result { - Self::from_ranges_with_files(ranges.iter().map(|r| (r.0, r.1, None))) - } - - /// Creates a container and allocates anonymous memory for guest memory regions. - /// - /// Valid memory regions are specified as a sequence of (Address, Size, [`Option`]) - /// tuples sorted by Address. - pub fn from_ranges_with_files(ranges: T) -> result::Result - where - A: Borrow<(GuestAddress, usize, Option)>, - T: IntoIterator, - { - Self::from_regions( - ranges - .into_iter() - .map(|x| { - GuestRegionMmap::from_range(x.borrow().0, x.borrow().1, x.borrow().2.clone()) - }) - .collect::, _>>()?, - ) - .map_err(Into::into) - } -} - #[cfg(test)] pub(crate) mod tests { #![allow(clippy::undocumented_unsafe_blocks)] extern crate vmm_sys_util; - use super::*; - - #[cfg(feature = "backend-bitmap")] - use crate::bitmap::AtomicBitmap; - use crate::{Bytes, GuestMemory, GuestMemoryError}; + use crate::bitmap::BS; + use crate::{ + guest_memory, Address, Bytes, FileOffset, GuestAddress, GuestMemory, GuestMemoryError, + GuestMemoryRegion, GuestMemoryRegionBytes, GuestUsize, MemoryRegionAddress, VolatileMemory, + VolatileSlice, + }; use std::io::Write; use std::sync::Arc; @@ -259,7 +81,7 @@ pub(crate) mod tests { } } - fn bitmap(&self) { } + fn bitmap(&self) {} fn get_host_address(&self, addr: MemoryRegionAddress) -> guest_memory::Result<*mut u8> { match self { @@ -286,9 +108,9 @@ pub(crate) mod tests { any_backend! { #[cfg(all(windows, feature = "backend-mmap"))] - Windows[GuestRegionMmap<()>], + Windows[crate::mmap::windows::GuestRegionWindows<()>], #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] - Mmap[GuestRegionMmap<()>], + Mmap[crate::mmap::unix::GuestRegionMmap<()>], #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] Xen[crate::mmap::xen::MmapRegion] } @@ -300,13 +122,19 @@ pub(crate) mod tests { let mut regions = Vec::new(); #[cfg(all(windows, feature = "backend-mmap"))] regions.push(AnyRegion::Windows( - GuestRegionMmap::new(MmapRegion::from_file(f_off.clone(), size).unwrap(), addr) - .unwrap(), + crate::mmap::windows::GuestRegionWindows::new( + crate::mmap::windows::MmapRegion::from_file(f_off.clone(), size).unwrap(), + addr, + ) + .unwrap(), )); #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] regions.push(AnyRegion::Mmap( - GuestRegionMmap::new(MmapRegion::from_file(f_off.clone(), size).unwrap(), addr) - .unwrap(), + crate::mmap::unix::GuestRegionMmap::new( + MmapRegion::from_file(f_off.clone(), size).unwrap(), + addr, + ) + .unwrap(), )); #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] regions.push(AnyRegion::Xen( @@ -324,11 +152,16 @@ pub(crate) mod tests { }; #[cfg(all(windows, feature = "backend-mmap"))] regions.push(AnyRegion::Windows( - GuestRegionMmap::new(MmapRegion::new(size).unwrap(), addr).unwrap(), + crate::mmap::windows::GuestRegionWindows::new( + crate::mmap::windows::MmapRegion::new(size).unwrap(), + addr, + ) + .unwrap(), )); #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] regions.push(AnyRegion::Mmap( - GuestRegionMmap::new(MmapRegion::new(size).unwrap(), addr).unwrap(), + crate::mmap::unix::GuestRegionMmap::new(MmapRegion::new(size).unwrap(), addr) + .unwrap(), )); #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] regions.push(AnyRegion::Xen( @@ -779,13 +612,4 @@ pub(crate) mod tests { ); } } - - #[test] - #[cfg(feature = "backend-bitmap")] - fn test_dirty_tracking() { - crate::bitmap::tests::test_guest_memory_and_region(|| { - crate::GuestMemoryMmap::::from_ranges(&[(GuestAddress(0), 0x1_0000)]) - .unwrap() - }); - } } diff --git a/src/mmap/unix.rs b/src/mmap/unix.rs index eea4788e..6898fc60 100644 --- a/src/mmap/unix.rs +++ b/src/mmap/unix.rs @@ -10,7 +10,9 @@ //! Helper structure for working with mmaped memory regions in Unix. +use std::borrow::Borrow; use std::io; +use std::ops::Deref; use std::os::unix::io::AsRawFd; use std::ptr::null_mut; use std::result; @@ -18,6 +20,10 @@ use std::result; use crate::bitmap::{Bitmap, NewBitmap, BS}; use crate::guest_memory::FileOffset; use crate::volatile_memory::{self, VolatileMemory, VolatileSlice}; +use crate::{ + guest_memory, Address, GuestAddress, GuestMemoryRegion, GuestMemoryRegionBytes, + GuestRegionCollection, GuestRegionCollectionError, GuestUsize, MemoryRegionAddress, +}; /// Error conditions that may arise when creating a new `MmapRegion` object. #[derive(Debug, thiserror::Error)] @@ -432,6 +438,154 @@ impl Drop for MmapRegion { } } +/// [`GuestMemoryRegion`](trait.GuestMemoryRegion.html) implementation that mmaps the guest's +/// memory region in the current process. +/// +/// Represents a continuous region of the guest's physical memory that is backed by a mapping +/// in the virtual address space of the calling process. +#[derive(Debug)] +pub struct GuestRegionMmap { + mapping: MmapRegion, + guest_base: GuestAddress, +} + +impl Deref for GuestRegionMmap { + type Target = MmapRegion; + + fn deref(&self) -> &MmapRegion { + &self.mapping + } +} + +impl GuestRegionMmap { + /// Create a new memory-mapped memory region for the guest's physical memory. + pub fn new(mapping: MmapRegion, guest_base: GuestAddress) -> Option { + guest_base + .0 + .checked_add(mapping.size() as u64) + .map(|_| GuestRegionMmap { + mapping, + guest_base, + }) + } +} + +impl GuestRegionMmap { + /// Create a new memory-mapped memory region from guest's physical memory, size and file. + pub fn from_range( + addr: GuestAddress, + size: usize, + file: Option, + ) -> result::Result { + let region = if let Some(ref f_off) = file { + MmapRegion::from_file(f_off.clone(), size)? + } else { + MmapRegion::new(size)? + }; + + Self::new(region, addr).ok_or(FromRangesError::InvalidGuestRegion) + } +} + +impl GuestMemoryRegion for GuestRegionMmap { + type B = B; + + fn len(&self) -> GuestUsize { + self.mapping.size() as GuestUsize + } + + fn start_addr(&self) -> GuestAddress { + self.guest_base + } + + fn bitmap(&self) -> BS<'_, Self::B> { + self.mapping.bitmap().slice_at(0) + } + + fn get_host_address(&self, addr: MemoryRegionAddress) -> guest_memory::Result<*mut u8> { + // Not sure why wrapping_offset is not unsafe. Anyway this + // is safe because we've just range-checked addr using check_address. + self.check_address(addr) + .ok_or(guest_memory::Error::InvalidBackendAddress) + .map(|addr| { + self.mapping + .as_ptr() + .wrapping_offset(addr.raw_value() as isize) + }) + } + + fn file_offset(&self) -> Option<&FileOffset> { + self.mapping.file_offset() + } + + fn get_slice( + &self, + offset: MemoryRegionAddress, + count: usize, + ) -> guest_memory::Result>> { + let slice = VolatileMemory::get_slice(&self.mapping, offset.raw_value() as usize, count)?; + Ok(slice) + } + + #[cfg(target_os = "linux")] + fn is_hugetlbfs(&self) -> Option { + self.mapping.is_hugetlbfs() + } +} + +impl GuestMemoryRegionBytes for GuestRegionMmap {} + +/// [`GuestMemory`](trait.GuestMemory.html) implementation that mmaps the guest's memory +/// in the current process. +/// +/// Represents the entire physical memory of the guest by tracking all its memory regions. +/// Each region is an instance of `GuestRegionMmap`, being backed by a mapping in the +/// virtual address space of the calling process. +pub type GuestMemoryMmap = GuestRegionCollection>; + +/// Errors that can happen during [`GuestMemoryMmap::from_ranges`] and related functions. +#[derive(Debug, thiserror::Error)] +pub enum FromRangesError { + /// Error during construction of [`GuestMemoryMmap`] + #[error("Error constructing guest region collection: {0}")] + Collection(#[from] GuestRegionCollectionError), + /// Error while allocating raw mmap region + #[error("Error setting up raw memory for guest region: {0}")] + MmapRegion(#[from] Error), + /// A combination of region length and guest address would overflow. + #[error("Combination of guest address and region length invalid (would overflow)")] + InvalidGuestRegion, +} + +impl GuestMemoryMmap { + /// Creates a container and allocates anonymous memory for guest memory regions. + /// + /// Valid memory regions are specified as a slice of (Address, Size) tuples sorted by Address. + pub fn from_ranges(ranges: &[(GuestAddress, usize)]) -> result::Result { + Self::from_ranges_with_files(ranges.iter().map(|r| (r.0, r.1, None))) + } + + /// Creates a container and allocates anonymous memory for guest memory regions. + /// + /// Valid memory regions are specified as a sequence of (Address, Size, [`Option`]) + /// tuples sorted by Address. + pub fn from_ranges_with_files(ranges: T) -> result::Result + where + A: Borrow<(GuestAddress, usize, Option)>, + T: IntoIterator, + { + Self::from_regions( + ranges + .into_iter() + .map(|x| { + GuestRegionMmap::from_range(x.borrow().0, x.borrow().1, x.borrow().2.clone()) + }) + .collect::, _>>()?, + ) + .map_err(Into::into) + } +} + #[cfg(test)] mod tests { #![allow(clippy::undocumented_unsafe_blocks)] @@ -649,11 +803,16 @@ mod tests { } #[test] - #[cfg(feature = "backend-bitmap")] + #[cfg(all(feature = "backend-bitmap", target_family = "unix"))] fn test_dirty_tracking() { // Using the `crate` prefix because we aliased `MmapRegion` to `MmapRegion<()>` for // the rest of the unit tests above. let m = crate::MmapRegion::::new(0x1_0000).unwrap(); crate::bitmap::tests::test_volatile_memory(&m); + + crate::bitmap::tests::test_guest_memory_and_region(|| { + crate::GuestMemoryMmap::::from_ranges(&[(GuestAddress(0), 0x1_0000)]) + .unwrap() + }); } } diff --git a/src/mmap/windows.rs b/src/mmap/windows.rs index 6d723917..7cfa912e 100644 --- a/src/mmap/windows.rs +++ b/src/mmap/windows.rs @@ -12,9 +12,14 @@ use libc::{c_void, size_t}; use winapi::um::errhandlingapi::GetLastError; +use crate::address::Address; use crate::bitmap::{Bitmap, NewBitmap, BS}; use crate::guest_memory::FileOffset; use crate::volatile_memory::{self, compute_offset, VolatileMemory, VolatileSlice}; +use crate::{ + guest_memory, GuestAddress, GuestMemoryRegion, GuestMemoryRegionBytes, GuestRegionCollection, + GuestUsize, MemoryRegionAddress, +}; #[allow(non_snake_case)] #[link(name = "kernel32")] @@ -241,6 +246,81 @@ impl Drop for MmapRegion { } } +/// [`GuestMemoryRegion`](trait.GuestMemoryRegion.html) implementation that mmaps the guest's +/// memory region in the current process. +/// +/// Represents a continuous region of the guest's physical memory that is backed by a mapping +/// in the virtual address space of the calling process. +#[derive(Debug)] +pub struct GuestRegionWindows { + mapping: MmapRegion, + guest_base: GuestAddress, +} + +impl GuestRegionWindows { + /// Create a new memory-mapped memory region for the guest's physical memory. + pub fn new(mapping: MmapRegion, guest_base: GuestAddress) -> Option { + guest_base + .0 + .checked_add(mapping.size() as u64) + .map(|_| GuestRegionWindows { + mapping, + guest_base, + }) + } +} + +impl GuestMemoryRegion for GuestRegionWindows { + type B = B; + + fn len(&self) -> GuestUsize { + self.mapping.size() as GuestUsize + } + + fn start_addr(&self) -> GuestAddress { + self.guest_base + } + + fn bitmap(&self) -> BS<'_, Self::B> { + self.mapping.bitmap().slice_at(0) + } + + fn get_host_address(&self, addr: MemoryRegionAddress) -> guest_memory::Result<*mut u8> { + // Not sure why wrapping_offset is not unsafe. Anyway this + // is safe because we've just range-checked addr using check_address. + self.check_address(addr) + .ok_or(guest_memory::Error::InvalidBackendAddress) + .map(|addr| { + self.mapping + .as_ptr() + .wrapping_offset(addr.raw_value() as isize) + }) + } + + fn file_offset(&self) -> Option<&FileOffset> { + self.mapping.file_offset() + } + + fn get_slice( + &self, + offset: MemoryRegionAddress, + count: usize, + ) -> guest_memory::Result>> { + let slice = VolatileMemory::get_slice(&self.mapping, offset.raw_value() as usize, count)?; + Ok(slice) + } +} + +impl GuestMemoryRegionBytes for GuestRegionWindows {} + +/// [`GuestMemory`](trait.GuestMemory.html) implementation that mmaps the guest's memory +/// in the current process. +/// +/// Represents the entire physical memory of the guest by tracking all its memory regions. +/// Each region is an instance of `GuestRegionMmap`, being backed by a mapping in the +/// virtual address space of the calling process. +pub type GuestMemoryWindows = GuestRegionCollection>; + #[cfg(test)] mod tests { use std::os::windows::io::FromRawHandle; diff --git a/src/region.rs b/src/region.rs index d3ca4350..f9715889 100644 --- a/src/region.rs +++ b/src/region.rs @@ -124,7 +124,7 @@ pub trait GuestMemoryRegion: Bytes { /// # Examples (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # use vm_memory::{GuestAddress, MmapRegion, GuestRegionMmap, GuestMemoryRegion}; /// # use vm_memory::volatile_memory::{VolatileMemory, VolatileSlice, VolatileRef}; @@ -154,7 +154,7 @@ pub trait GuestMemoryRegion: Bytes { /// # Examples (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap, GuestRegionMmap}; /// let addr = GuestAddress(0x1000); @@ -335,10 +335,10 @@ impl Bytes for R { /// * Write a slice at guest address 0x1200. /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; /// # - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # let start_addr = GuestAddress(0x1000); /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)]) @@ -361,10 +361,10 @@ impl Bytes for R { /// * Read a slice of length 16 at guestaddress 0x1200. /// /// ``` - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; /// # - /// # #[cfg(feature = "backend-mmap")] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] /// # { /// # let start_addr = GuestAddress(0x1000); /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)]) From 9f11cc1aa86425ecbdd393481655bc814a9d8fe4 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 14 Mar 2025 17:31:22 +0000 Subject: [PATCH 08/13] Make mmap_unix.rs and mmap_xen.rs not mutually exclusive Now that all tension between these two modules is resolves (e.g. no more code depends on exactly one of these being defined, and tests can support iterating over multiple supported backends), drop all the `cfg(not(feature = "xen"))` from across the code base. Signed-off-by: Patrick Roy --- CHANGELOG.md | 3 +++ benches/guest_memory.rs | 2 +- benches/main.rs | 8 ++++---- benches/mmap/mod.rs | 2 +- src/bitmap/mod.rs | 6 +++--- src/bytes.rs | 2 +- src/guest_memory.rs | 16 ++++++++-------- src/lib.rs | 11 +++++------ src/mmap/mod.rs | 28 ++++++++++++++++++---------- src/region.rs | 12 ++++++------ 10 files changed, 50 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b7467a3..0e05bcf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ Change return type of `GuestRegionMmap::new` from `Result` to `Option`. - \[#324](https:////github.com/rust-vmm/vm-memory/pull/324)\] `GuestMemoryRegion::bitmap()` now returns a `BitmapSlice`. Accessing the full bitmap is now possible only if the type of the memory region is know, for example with `MmapRegion::bitmap()`. - \[[#339](https://github.com/rust-vmm/vm-memory/pull/339)\] Implement `Bytes::load()` and `Bytes::store()` with `get_slices()` instead of `to_region_addr()` +- \[[#317](https://github.com/rust-vmm/vm-memory/pull/317)\]: Make `xen` feature additive, meaning enabling it + no longer disables the `mmap_unix` module at compile time. For this, various Xen-related structs had to be + renamed due to naming conflicts. ### Removed diff --git a/benches/guest_memory.rs b/benches/guest_memory.rs index 35242def..813cc132 100644 --- a/benches/guest_memory.rs +++ b/benches/guest_memory.rs @@ -1,7 +1,7 @@ // Copyright (C) 2020 Alibaba Cloud Computing. All rights reserved. // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause -#![cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_family = "windows")))] +#![cfg(all(feature = "backend-mmap", not(target_family = "windows")))] use core::hint::black_box; pub use criterion::Criterion; diff --git a/benches/main.rs b/benches/main.rs index eb854c10..f1283e7d 100644 --- a/benches/main.rs +++ b/benches/main.rs @@ -5,7 +5,7 @@ extern crate criterion; pub use criterion::{criterion_group, criterion_main, Criterion}; -#[cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_os = "windows")))] +#[cfg(all(feature = "backend-mmap", not(target_os = "windows")))] use vm_memory::{GuestAddress, GuestMemoryMmap}; mod guest_memory; @@ -14,7 +14,7 @@ mod volatile; use volatile::benchmark_for_volatile; -#[cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_os = "windows")))] +#[cfg(all(feature = "backend-mmap", not(target_os = "windows")))] // Use this function with caution. It does not check against overflows // and `GuestMemoryMmap::from_ranges` errors. fn create_guest_memory_mmap(size: usize, count: u64) -> GuestMemoryMmap<()> { @@ -27,12 +27,12 @@ fn create_guest_memory_mmap(size: usize, count: u64) -> GuestMemoryMmap<()> { } pub fn criterion_benchmark(_c: &mut Criterion) { - #[cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_os = "windows")))] + #[cfg(all(feature = "backend-mmap", not(target_os = "windows")))] mmap::benchmark_for_mmap(_c); } pub fn benchmark_guest_memory(_c: &mut Criterion) { - #[cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_os = "windows")))] + #[cfg(all(feature = "backend-mmap", not(target_os = "windows")))] guest_memory::benchmark_for_guest_memory(_c) } diff --git a/benches/mmap/mod.rs b/benches/mmap/mod.rs index df3ff6e5..fb3da445 100644 --- a/benches/mmap/mod.rs +++ b/benches/mmap/mod.rs @@ -1,7 +1,7 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause -#![cfg(all(feature = "backend-mmap", not(feature = "xen"), not(target_family = "windows")))] +#![cfg(all(feature = "backend-mmap", not(target_family = "windows")))] #![allow(clippy::undocumented_unsafe_blocks)] extern crate criterion; diff --git a/src/bitmap/mod.rs b/src/bitmap/mod.rs index ec81ce6d..4c98d495 100644 --- a/src/bitmap/mod.rs +++ b/src/bitmap/mod.rs @@ -125,7 +125,7 @@ pub(crate) mod tests { use std::sync::atomic::Ordering; use crate::{Bytes, VolatileMemory}; - #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + #[cfg(all(feature = "backend-mmap", target_family = "unix"))] use crate::{GuestAddress, MemoryRegionAddress}; // Helper method to check whether a specified range is clean. @@ -219,7 +219,7 @@ pub(crate) mod tests { // them to test the mmap-based backend implementations for now. Going forward, the generic // test functions defined here can be placed in a separate module (i.e. `test_utilities`) // which is gated by a feature and can be used for testing purposes by other crates as well. - #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + #[cfg(all(feature = "backend-mmap", target_family = "unix"))] fn test_guest_memory_region(region: &R) { let dirty_addr = MemoryRegionAddress(0x0); let val = 123u64; @@ -253,7 +253,7 @@ pub(crate) mod tests { ); } - #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + #[cfg(all(feature = "backend-mmap", target_family = "unix"))] // Assumptions about M generated by f ... pub fn test_guest_memory_and_region(f: F) where diff --git a/src/bytes.rs b/src/bytes.rs index c44593ad..b60087df 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -331,7 +331,7 @@ pub trait Bytes { /// * Read bytes from /dev/urandom (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", feature = "rawfd", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", feature = "rawfd", target_family = "unix"))] /// # { /// # use vm_memory::{Address, GuestMemory, Bytes, GuestAddress, GuestMemoryMmap}; /// # use std::fs::File; diff --git a/src/guest_memory.rs b/src/guest_memory.rs index 1a11a943..7e4da01e 100644 --- a/src/guest_memory.rs +++ b/src/guest_memory.rs @@ -176,7 +176,7 @@ impl FileOffset { /// # Examples (uses the `backend-mmap` and `backend-atomic` features) /// /// ``` -/// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] +/// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { /// # use std::sync::Arc; /// # use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryMmap}; @@ -294,7 +294,7 @@ pub trait GuestMemory { /// `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryRegion, GuestMemoryMmap}; /// # @@ -318,7 +318,7 @@ pub trait GuestMemory { /// # Examples (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { /// # use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap}; /// # @@ -433,7 +433,7 @@ pub trait GuestMemory { /// # Examples (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap}; /// # @@ -592,9 +592,9 @@ impl Bytes for T { /// * Write a slice at guestaddress 0x1000. (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { - /// # use vm_memory::{Bytes, GuestAddress, mmap::GuestMemoryMmap}; + /// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; /// # /// # let start_addr = GuestAddress(0x1000); /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)]) @@ -620,9 +620,9 @@ impl Bytes for T { /// * Read a slice of length 16 at guestaddress 0x1000. (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { - /// # use vm_memory::{Bytes, GuestAddress, mmap::GuestMemoryMmap}; + /// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; /// # /// let start_addr = GuestAddress(0x1000); /// let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)]) diff --git a/src/lib.rs b/src/lib.rs index 597b325c..91ef92dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,14 +67,13 @@ pub use io::{ReadVolatile, WriteVolatile}; #[cfg(feature = "backend-mmap")] pub mod mmap; -#[cfg(all(feature = "backend-mmap", feature = "xen", target_family = "unix"))] -pub use mmap::{MmapRange, MmapXenFlags}; - #[cfg(all(feature = "xen", target_family = "unix"))] -pub use mmap::xen::{GuestMemoryXen, MmapRegion as MmapRegionXen}; +pub use mmap::xen::{GuestMemoryXen, MmapRange, MmapRegion as MmapRegionXen, MmapXenFlags}; -#[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] -pub use mmap::unix::{Error as MmapRegionError, GuestMemoryMmap, GuestRegionMmap, MmapRegion}; +#[cfg(all(feature = "backend-mmap", target_family = "unix"))] +pub use mmap::unix::{ + Error as MmapRegionError, GuestMemoryMmap, GuestRegionMmap, MmapRegion, MmapRegionBuilder, +}; #[cfg(all(feature = "backend-mmap", target_family = "windows"))] pub use crate::mmap::windows::{ diff --git a/src/mmap/mod.rs b/src/mmap/mod.rs index a0924b50..065b7cf8 100644 --- a/src/mmap/mod.rs +++ b/src/mmap/mod.rs @@ -15,7 +15,7 @@ // re-export for backward compat, as the trait used to be defined in mmap.rs pub use crate::bitmap::NewBitmap; -#[cfg(all(not(feature = "xen"), target_family = "unix"))] +#[cfg(target_family = "unix")] pub(super) mod unix; #[cfg(all(feature = "xen", target_family = "unix"))] @@ -109,7 +109,7 @@ pub(crate) mod tests { any_backend! { #[cfg(all(windows, feature = "backend-mmap"))] Windows[crate::mmap::windows::GuestRegionWindows<()>], - #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] + #[cfg(all(unix, feature = "backend-mmap"))] Mmap[crate::mmap::unix::GuestRegionMmap<()>], #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] Xen[crate::mmap::xen::MmapRegion] @@ -128,18 +128,22 @@ pub(crate) mod tests { ) .unwrap(), )); - #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] + #[cfg(all(unix, feature = "backend-mmap"))] regions.push(AnyRegion::Mmap( crate::mmap::unix::GuestRegionMmap::new( - MmapRegion::from_file(f_off.clone(), size).unwrap(), + crate::MmapRegion::from_file(f_off.clone(), size).unwrap(), addr, ) .unwrap(), )); #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] regions.push(AnyRegion::Xen( - MmapRegion::from_range(MmapRange::new_unix(size, Some(f_off.clone()), addr)) - .unwrap(), + crate::MmapRegionXen::from_range(crate::MmapRange::new_unix( + size, + Some(f_off.clone()), + addr, + )) + .unwrap(), )); regions } @@ -158,14 +162,18 @@ pub(crate) mod tests { ) .unwrap(), )); - #[cfg(all(unix, feature = "backend-mmap", not(feature = "xen")))] + #[cfg(all(unix, feature = "backend-mmap"))] regions.push(AnyRegion::Mmap( - crate::mmap::unix::GuestRegionMmap::new(MmapRegion::new(size).unwrap(), addr) - .unwrap(), + crate::mmap::unix::GuestRegionMmap::new( + crate::MmapRegion::new(size).unwrap(), + addr, + ) + .unwrap(), )); #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] regions.push(AnyRegion::Xen( - MmapRegion::from_range(MmapRange::new_unix(size, None, addr)).unwrap(), + crate::MmapRegionXen::from_range(crate::MmapRange::new_unix(size, None, addr)) + .unwrap(), )); regions } diff --git a/src/region.rs b/src/region.rs index f9715889..2883f3f8 100644 --- a/src/region.rs +++ b/src/region.rs @@ -124,7 +124,7 @@ pub trait GuestMemoryRegion: Bytes { /// # Examples (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { /// # use vm_memory::{GuestAddress, MmapRegion, GuestRegionMmap, GuestMemoryRegion}; /// # use vm_memory::volatile_memory::{VolatileMemory, VolatileSlice, VolatileRef}; @@ -154,7 +154,7 @@ pub trait GuestMemoryRegion: Bytes { /// # Examples (uses the `backend-mmap` feature) /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { /// # use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap, GuestRegionMmap}; /// let addr = GuestAddress(0x1000); @@ -335,10 +335,10 @@ impl Bytes for R { /// * Write a slice at guest address 0x1200. /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; /// # - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { /// # let start_addr = GuestAddress(0x1000); /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)]) @@ -361,10 +361,10 @@ impl Bytes for R { /// * Read a slice of length 16 at guestaddress 0x1200. /// /// ``` - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; /// # - /// # #[cfg(all(feature = "backend-mmap", target_family = "unix", not(feature = "xen")))] + /// # #[cfg(all(feature = "backend-mmap", target_family = "unix"))] /// # { /// # let start_addr = GuestAddress(0x1000); /// # let mut gm = GuestMemoryMmap::<()>::from_ranges(&vec![(start_addr, 0x400)]) From ffd6e536e1b28f71b13992c646b938619381f5e2 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Mon, 17 Mar 2025 09:56:23 +0000 Subject: [PATCH 09/13] xen: fix clippy warning It seems that now with all our cargo features compatible, clippy is checking more code in CI. Fix a warning about an error variant being suffixed with the enum name in mmap_xen.rs that only popped up now. Signed-off-by: Patrick Roy --- src/mmap/xen.rs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/mmap/xen.rs b/src/mmap/xen.rs index 35177c19..b594c54e 100644 --- a/src/mmap/xen.rs +++ b/src/mmap/xen.rs @@ -61,7 +61,7 @@ pub enum Error { Fam(FamError), /// Unexpected error. #[error("Unexpected error")] - UnexpectedError, + Unexpected, } type Result = result::Result; @@ -302,8 +302,8 @@ impl MmapRegion { Ok(MmapRegion { bitmap: B::with_len(range.size), size: range.size, - prot: range.prot.ok_or(Error::UnexpectedError)?, - flags: range.flags.ok_or(Error::UnexpectedError)?, + prot: range.prot.ok_or(Error::Unexpected)?, + flags: range.flags.ok_or(Error::Unexpected)?, file_offset: range.file_offset, hugetlbfs: range.hugetlbfs, mmap, @@ -570,8 +570,8 @@ impl MmapXenUnix { Ok(Self( MmapUnix::new( range.size, - range.prot.ok_or(Error::UnexpectedError)?, - range.flags.ok_or(Error::UnexpectedError)?, + range.prot.ok_or(Error::Unexpected)?, + range.flags.ok_or(Error::Unexpected)?, fd, offset, )?, @@ -647,8 +647,8 @@ impl MmapXenForeign { let unix_mmap = MmapUnix::new( size, - range.prot.ok_or(Error::UnexpectedError)?, - range.flags.ok_or(Error::UnexpectedError)? | MAP_SHARED, + range.prot.ok_or(Error::Unexpected)?, + range.flags.ok_or(Error::Unexpected)? | MAP_SHARED, fd, f_offset, )?; @@ -861,7 +861,7 @@ impl MmapXenGrant { guest_base: range.addr, unix_mmap: None, file_offset: range.file_offset.as_ref().unwrap().clone(), - flags: range.flags.ok_or(Error::UnexpectedError)?, + flags: range.flags.ok_or(Error::Unexpected)?, size: 0, index: 0, domid: range.mmap_data, @@ -870,11 +870,8 @@ impl MmapXenGrant { // Region can't be mapped in advance, partial mapping will be done later via // `MmapXenSlice`. if mmap_flags.mmap_in_advance() { - let (unix_mmap, index) = grant.mmap_range( - range.addr, - range.size, - range.prot.ok_or(Error::UnexpectedError)?, - )?; + let (unix_mmap, index) = + grant.mmap_range(range.addr, range.size, range.prot.ok_or(Error::Unexpected)?)?; grant.unix_mmap = Some(unix_mmap); grant.index = index; @@ -1173,12 +1170,12 @@ mod tests { range.file_offset = Some(FileOffset::new(TempFile::new().unwrap().into_file(), 0)); range.prot = None; let r = MmapXenForeign::new(&range); - assert_matches!(r.unwrap_err(), Error::UnexpectedError); + assert_matches!(r.unwrap_err(), Error::Unexpected); let mut range = MmapRange::initialized(true); range.flags = None; let r = MmapXenForeign::new(&range); - assert_matches!(r.unwrap_err(), Error::UnexpectedError); + assert_matches!(r.unwrap_err(), Error::Unexpected); let mut range = MmapRange::initialized(true); range.file_offset = Some(FileOffset::new(TempFile::new().unwrap().into_file(), 1)); @@ -1205,7 +1202,7 @@ mod tests { let mut range = MmapRange::initialized(true); range.prot = None; let r = MmapXenGrant::new(&range, MmapXenFlags::empty()); - assert_matches!(r.unwrap_err(), Error::UnexpectedError); + assert_matches!(r.unwrap_err(), Error::Unexpected); let mut range = MmapRange::initialized(true); range.prot = None; @@ -1215,7 +1212,7 @@ mod tests { let mut range = MmapRange::initialized(true); range.flags = None; let r = MmapXenGrant::new(&range, MmapXenFlags::NO_ADVANCE_MAP); - assert_matches!(r.unwrap_err(), Error::UnexpectedError); + assert_matches!(r.unwrap_err(), Error::Unexpected); let mut range = MmapRange::initialized(true); range.file_offset = Some(FileOffset::new(TempFile::new().unwrap().into_file(), 1)); From 9e670260bfc865e695817aa7610c63dbf2a7e0a8 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Mon, 17 Mar 2025 10:48:49 +0000 Subject: [PATCH 10/13] ci: add xen to features for coverage Now that all features are additive, collect coverage with the xen feature enabled. Signed-off-by: Patrick Roy --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 1c8745c5..ff08b215 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { "coverage_score": 90.82, "exclude_path": "mmap_windows.rs", - "crate_features": "backend-mmap,backend-atomic,backend-bitmap" + "crate_features": "backend-mmap,backend-atomic,backend-bitmap,xen" } From d597f75a8a94851f8c1dd0452cc02b454d088f1d Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 4 Jul 2025 15:43:04 +0100 Subject: [PATCH 11/13] fix: eliminate unneeded clone Signed-off-by: Patrick Roy --- src/mmap/unix.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mmap/unix.rs b/src/mmap/unix.rs index 6898fc60..c8236375 100644 --- a/src/mmap/unix.rs +++ b/src/mmap/unix.rs @@ -477,8 +477,8 @@ impl GuestRegionMmap { size: usize, file: Option, ) -> result::Result { - let region = if let Some(ref f_off) = file { - MmapRegion::from_file(f_off.clone(), size)? + let region = if let Some(f_off) = file { + MmapRegion::from_file(f_off, size)? } else { MmapRegion::new(size)? }; From e6b0c184f91793698602014a3d97042dc6fa5f14 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Fri, 4 Jul 2025 15:55:12 +0100 Subject: [PATCH 12/13] refactor(xen): Reuse MmapRegion from unix module Now that xen and non-xen definitions of all the mapping structure can co-exist, we can reuse stuff from the `mmap::unix` module in `mmap::xen`, and eliminate a few of the structs and functions that are copy-pasted between the two. Signed-off-by: Patrick Roy --- src/mmap/unix.rs | 2 +- src/mmap/xen.rs | 137 +++++++++++++++++------------------------------ 2 files changed, 49 insertions(+), 90 deletions(-) diff --git a/src/mmap/unix.rs b/src/mmap/unix.rs index c8236375..b6bceab5 100644 --- a/src/mmap/unix.rs +++ b/src/mmap/unix.rs @@ -217,7 +217,7 @@ impl MmapRegionBuilder { /// When running a 64-bit virtual machine on a 32-bit hypervisor, only part of the guest's /// physical memory may be mapped into the current process due to the limited virtual address /// space size of the process. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct MmapRegion { addr: *mut u8, size: usize, diff --git a/src/mmap/xen.rs b/src/mmap/xen.rs index b594c54e..cb560889 100644 --- a/src/mmap/xen.rs +++ b/src/mmap/xen.rs @@ -9,8 +9,8 @@ use bitflags::bitflags; use libc::{c_int, c_void, MAP_SHARED, _SC_PAGESIZE}; +use std::sync::Arc; use std::{io, mem::size_of, os::raw::c_ulong, os::unix::io::AsRawFd, ptr::null_mut, result}; - use vmm_sys_util::{ fam::{Error as FamError, FamStruct, FamStructWrapper}, generate_fam_struct_impl, @@ -26,28 +26,23 @@ use tests::ioctl_with_ref; use crate::bitmap::{Bitmap, NewBitmap, BS}; use crate::guest_memory::{FileOffset, GuestAddress}; +use crate::mmap::unix::MmapRegion as MmapUnix; use crate::volatile_memory::{self, VolatileMemory, VolatileSlice}; use crate::{ guest_memory, Address, GuestMemoryRegion, GuestMemoryRegionBytes, GuestRegionCollection, - GuestUsize, MemoryRegionAddress, + GuestUsize, MemoryRegionAddress, MmapRegionBuilder, }; /// Error conditions that may arise when creating a new `MmapRegion` object. #[derive(Debug, thiserror::Error)] pub enum Error { - /// The specified file offset and length cause overflow when added. - #[error("The specified file offset and length cause overflow when added")] - InvalidOffsetLength, /// The forbidden `MAP_FIXED` flag was specified. #[error("The forbidden `MAP_FIXED` flag was specified")] MapFixed, - /// A mapping with offset + length > EOF was attempted. - #[error("The specified file offset and length is greater then file length")] - MappingPastEof, /// The `mmap` call returned an error. #[error("{0}")] Mmap(io::Error), - /// Invalid file offset. + /// Invalid file offset (non-zero of missing altogether). #[error("Invalid file offset")] InvalidFileOffset, /// Memory mapped in advance. @@ -62,6 +57,9 @@ pub enum Error { /// Unexpected error. #[error("Unexpected error")] Unexpected, + /// Error establishing normal unix mapping + #[error["{0}"]] + Unix(#[from] crate::mmap::unix::Error), } type Result = result::Result; @@ -427,45 +425,6 @@ impl VolatileMemory for MmapRegion { } } -#[derive(Clone, Debug, PartialEq)] -struct MmapUnix { - addr: *mut u8, - size: usize, -} - -impl MmapUnix { - fn new(size: usize, prot: i32, flags: i32, fd: i32, f_offset: u64) -> Result { - let addr = - // SAFETY: This is safe because we're not allowing MAP_FIXED, and invalid parameters - // cannot break Rust safety guarantees (things may change if we're mapping /dev/mem or - // some wacky file). - unsafe { libc::mmap(null_mut(), size, prot, flags, fd, f_offset as libc::off_t) }; - - if addr == libc::MAP_FAILED { - return Err(Error::Mmap(io::Error::last_os_error())); - } - - Ok(Self { - addr: addr as *mut u8, - size, - }) - } - - fn addr(&self) -> *mut u8 { - self.addr - } -} - -impl Drop for MmapUnix { - fn drop(&mut self) { - // SAFETY: This is safe because we mmap the area at addr ourselves, and nobody - // else is holding a reference to it. - unsafe { - libc::munmap(self.addr as *mut libc::c_void, self.size); - } - } -} - // Bit mask for the vhost-user xen mmap message. bitflags! { /// Flags for the Xen mmap message. @@ -531,21 +490,18 @@ fn pages(size: usize) -> (usize, usize) { (num, page_size * num) } -fn validate_file(file_offset: &Option) -> Result<(i32, u64)> { +fn validate_file(file_offset: Option) -> Result { let file_offset = match file_offset { Some(f) => f, None => return Err(Error::InvalidFileOffset), }; - let fd = file_offset.file().as_raw_fd(); - let f_offset = file_offset.start(); - // We don't allow file offsets with Xen foreign mappings. - if f_offset != 0 { - return Err(Error::InvalidOffsetLength); + if file_offset.start() != 0 { + return Err(Error::InvalidFileOffset); } - Ok((fd, f_offset)) + Ok(file_offset) } // Xen Foreign memory mapping interface. @@ -556,27 +512,22 @@ trait MmapXenTrait: std::fmt::Debug { } // Standard Unix memory mapping for testing other crates. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] struct MmapXenUnix(MmapUnix, GuestAddress); impl MmapXenUnix { fn new(range: &MmapRange) -> Result { - let (fd, offset) = if let Some(ref f_off) = range.file_offset { - (f_off.file().as_raw_fd(), f_off.start()) - } else { - (-1, 0) - }; + let mut builder = MmapRegionBuilder::new(range.size) + .with_mmap_prot(range.prot.ok_or(Error::Unexpected)?) + .with_mmap_flags(range.flags.ok_or(Error::Unexpected)?); - Ok(Self( - MmapUnix::new( - range.size, - range.prot.ok_or(Error::Unexpected)?, - range.flags.ok_or(Error::Unexpected)?, - fd, - offset, - )?, - range.addr, - )) + if let Some(ref file_offset) = range.file_offset { + builder = builder.with_file_offset(file_offset.clone()); + } + + let mmap_unix = builder.build()?; + + Ok(MmapXenUnix(mmap_unix, range.addr)) } } @@ -587,7 +538,7 @@ impl MmapXenTrait for MmapXenUnix { } fn addr(&self) -> *mut u8 { - self.0.addr() + self.0.as_ptr() } fn guest_base(&self) -> GuestAddress { @@ -626,7 +577,7 @@ fn ioctl_privcmd_mmapbatch_v2() -> c_ulong { } // Xen foreign memory specific implementation. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] struct MmapXenForeign { domid: u32, guest_base: GuestAddress, @@ -642,16 +593,15 @@ impl AsRawFd for MmapXenForeign { impl MmapXenForeign { fn new(range: &MmapRange) -> Result { - let (fd, f_offset) = validate_file(&range.file_offset)?; let (count, size) = pages(range.size); + let file_offset = validate_file(range.file_offset.clone())?; + let fd = file_offset.file().as_raw_fd(); - let unix_mmap = MmapUnix::new( - size, - range.prot.ok_or(Error::Unexpected)?, - range.flags.ok_or(Error::Unexpected)? | MAP_SHARED, - fd, - f_offset, - )?; + let unix_mmap = MmapRegionBuilder::new(size) + .with_mmap_prot(range.prot.ok_or(Error::Unexpected)?) + .with_mmap_flags(range.flags.ok_or(Error::Unexpected)? | MAP_SHARED) + .with_file_offset(file_offset) + .build()?; let foreign = Self { domid: range.mmap_data, @@ -701,7 +651,7 @@ impl MmapXenTrait for MmapXenForeign { } fn addr(&self) -> *mut u8 { - self.unix_mmap.addr() + self.unix_mmap.as_ptr() } fn guest_base(&self) -> GuestAddress { @@ -855,12 +805,12 @@ impl AsRawFd for MmapXenGrant { impl MmapXenGrant { fn new(range: &MmapRange, mmap_flags: MmapXenFlags) -> Result { - validate_file(&range.file_offset)?; + let file_offset = validate_file(range.file_offset.clone())?; let mut grant = Self { guest_base: range.addr, unix_mmap: None, - file_offset: range.file_offset.as_ref().unwrap().clone(), + file_offset, flags: range.flags.ok_or(Error::Unexpected)?, size: 0, index: 0, @@ -884,7 +834,15 @@ impl MmapXenGrant { fn mmap_range(&self, addr: GuestAddress, size: usize, prot: i32) -> Result<(MmapUnix, u64)> { let (count, size) = pages(size); let index = self.mmap_ioctl(addr, count)?; - let unix_mmap = MmapUnix::new(size, prot, self.flags, self.as_raw_fd(), index)?; + + let unix_mmap = MmapRegionBuilder::new(size) + .with_mmap_prot(prot) + .with_mmap_flags(self.flags) + .with_file_offset(FileOffset::from_arc( + Arc::clone(self.file_offset.arc()), + index, + )) + .build()?; Ok((unix_mmap, index)) } @@ -934,7 +892,7 @@ impl MmapXenTrait for MmapXenGrant { fn addr(&self) -> *mut u8 { if let Some(ref unix_mmap) = self.unix_mmap { - unix_mmap.addr() + unix_mmap.as_ptr() } else { null_mut() } @@ -983,7 +941,7 @@ impl MmapXenSlice { let (unix_mmap, index) = grant.mmap_range(GuestAddress(addr), size, prot)?; // SAFETY: We have already mapped the range including offset. - let addr = unsafe { unix_mmap.addr().add(offset) }; + let addr = unsafe { unix_mmap.as_ptr().add(offset) }; Ok(Self { grant: Some(grant), @@ -1086,6 +1044,7 @@ mod tests { fn raw_os_error(&self) -> i32 { match self { Error::Mmap(e) => e.raw_os_error().unwrap(), + Error::Unix(crate::mmap::unix::Error::Mmap(e)) => e.raw_os_error().unwrap(), _ => i32::MIN, } } @@ -1180,7 +1139,7 @@ mod tests { let mut range = MmapRange::initialized(true); range.file_offset = Some(FileOffset::new(TempFile::new().unwrap().into_file(), 1)); let r = MmapXenForeign::new(&range); - assert_matches!(r.unwrap_err(), Error::InvalidOffsetLength); + assert_matches!(r.unwrap_err(), Error::InvalidFileOffset); let mut range = MmapRange::initialized(true); range.size = 0; @@ -1217,7 +1176,7 @@ mod tests { let mut range = MmapRange::initialized(true); range.file_offset = Some(FileOffset::new(TempFile::new().unwrap().into_file(), 1)); let r = MmapXenGrant::new(&range, MmapXenFlags::NO_ADVANCE_MAP); - assert_matches!(r.unwrap_err(), Error::InvalidOffsetLength); + assert_matches!(r.unwrap_err(), Error::InvalidFileOffset); let mut range = MmapRange::initialized(true); range.size = 0; From 4757d1573013f69e0ad800e884c10a023662ce07 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Tue, 30 Sep 2025 10:35:20 +0100 Subject: [PATCH 13/13] xen: rename MmapRegion to GuestRegionXen It plays the same role as GuestRegionMmap for unix, so update the name to provide some consistency between the two. Signed-off-by: Patrick Roy --- src/lib.rs | 2 +- src/mmap/mod.rs | 8 ++++---- src/mmap/xen.rs | 35 ++++++++++++++++++----------------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 91ef92dc..8002da05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,7 @@ pub use io::{ReadVolatile, WriteVolatile}; pub mod mmap; #[cfg(all(feature = "xen", target_family = "unix"))] -pub use mmap::xen::{GuestMemoryXen, MmapRange, MmapRegion as MmapRegionXen, MmapXenFlags}; +pub use mmap::xen::{GuestMemoryXen, GuestRegionXen, MmapRange, MmapXenFlags}; #[cfg(all(feature = "backend-mmap", target_family = "unix"))] pub use mmap::unix::{ diff --git a/src/mmap/mod.rs b/src/mmap/mod.rs index 065b7cf8..2d3dd61c 100644 --- a/src/mmap/mod.rs +++ b/src/mmap/mod.rs @@ -28,7 +28,7 @@ pub(super) mod windows; pub use unix::{Error as MmapRegionError, MmapRegion, MmapRegionBuilder}; #[cfg(all(feature = "xen", target_family = "unix"))] -pub use xen::{Error as MmapRegionError, MmapRange, MmapRegion, MmapXenFlags}; +pub use xen::{Error as MmapRegionError, GuestRegionXen, MmapRange, MmapXenFlags}; #[cfg(target_family = "windows")] pub use std::io::Error as MmapRegionError; @@ -112,7 +112,7 @@ pub(crate) mod tests { #[cfg(all(unix, feature = "backend-mmap"))] Mmap[crate::mmap::unix::GuestRegionMmap<()>], #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] - Xen[crate::mmap::xen::MmapRegion] + Xen[crate::mmap::xen::GuestRegionXen] } // The cfgs make using vec![...] instead more unreadable, so suppress the lint here. @@ -138,7 +138,7 @@ pub(crate) mod tests { )); #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] regions.push(AnyRegion::Xen( - crate::MmapRegionXen::from_range(crate::MmapRange::new_unix( + crate::GuestRegionXen::from_range(crate::MmapRange::new_unix( size, Some(f_off.clone()), addr, @@ -172,7 +172,7 @@ pub(crate) mod tests { )); #[cfg(all(unix, feature = "backend-mmap", feature = "xen"))] regions.push(AnyRegion::Xen( - crate::MmapRegionXen::from_range(crate::MmapRange::new_unix(size, None, addr)) + crate::GuestRegionXen::from_range(crate::MmapRange::new_unix(size, None, addr)) .unwrap(), )); regions diff --git a/src/mmap/xen.rs b/src/mmap/xen.rs index cb560889..8bd24fc8 100644 --- a/src/mmap/xen.rs +++ b/src/mmap/xen.rs @@ -143,7 +143,7 @@ impl MmapRange { /// physical memory may be mapped into the current process due to the limited virtual address /// space size of the process. #[derive(Debug)] -pub struct MmapRegion { +pub struct GuestRegionXen { bitmap: B, size: usize, prot: i32, @@ -153,7 +153,7 @@ pub struct MmapRegion { mmap: MmapXen, } -impl GuestMemoryRegion for MmapRegion { +impl GuestMemoryRegion for GuestRegionXen { type B = B; fn len(&self) -> GuestUsize { @@ -195,23 +195,23 @@ impl GuestMemoryRegion for MmapRegion { } } -impl GuestMemoryRegionBytes for MmapRegion {} +impl GuestMemoryRegionBytes for GuestRegionXen {} /// A collection of Xen guest memory regions. /// /// Represents the entire physical memory of the guest by tracking all its memory regions. -/// Each region is an instance of [`MmapRegionXen`]. -pub type GuestMemoryXen = GuestRegionCollection>; +/// Each region is an instance of [`GuestRegionXen`]. +pub type GuestMemoryXen = GuestRegionCollection>; // SAFETY: Send and Sync aren't automatically inherited for the raw address pointer. // Accessing that pointer is only done through the stateless interface which // allows the object to be shared by multiple threads without a decrease in // safety. -unsafe impl Send for MmapRegion {} +unsafe impl Send for GuestRegionXen {} // SAFETY: See comment above. -unsafe impl Sync for MmapRegion {} +unsafe impl Sync for GuestRegionXen {} -impl MmapRegion { +impl GuestRegionXen { /// Creates a shared anonymous mapping of `size` bytes. /// /// # Arguments @@ -224,7 +224,7 @@ impl MmapRegion { /// use std::fs::File; /// use std::path::Path; /// use vm_memory::{ - /// Bytes, FileOffset, GuestAddress, GuestMemoryXen, MmapRange, MmapRegionXen, MmapXenFlags, + /// Bytes, FileOffset, GuestAddress, GuestMemoryXen, GuestRegionXen, MmapRange, MmapXenFlags, /// }; /// # use vmm_sys_util::tempfile::TempFile; /// @@ -240,7 +240,7 @@ impl MmapRegion { /// # // We need a UNIX mapping for tests to succeed. /// # let range = MmapRange::new_unix(0x400, None, addr); /// - /// let r = MmapRegionXen::<()>::from_range(range).expect("Could not create mmap region"); + /// let r = GuestRegionXen::<()>::from_range(range).expect("Could not create mmap region"); /// /// let mut gm = GuestMemoryXen::from_regions(vec![r]).expect("Could not create guest memory"); /// let res = gm @@ -255,7 +255,7 @@ impl MmapRegion { /// use std::fs::File; /// use std::path::Path; /// use vm_memory::{ - /// Bytes, FileOffset, GuestAddress, GuestMemoryXen, MmapRange, MmapRegionXen, MmapXenFlags, + /// Bytes, FileOffset, GuestAddress, GuestMemoryXen, GuestRegionXen, MmapRange, MmapXenFlags, /// }; /// # use vmm_sys_util::tempfile::TempFile; /// @@ -271,7 +271,7 @@ impl MmapRegion { /// # // We need a UNIX mapping for tests to succeed. /// # let range = MmapRange::new_unix(0x400, None, addr); /// - /// let r = MmapRegionXen::<()>::from_range(range).expect("Could not create mmap region"); + /// let r = GuestRegionXen::<()>::from_range(range).expect("Could not create mmap region"); /// /// let mut gm = GuestMemoryXen::from_regions(vec![r]).expect("Could not create guest memory"); /// let res = gm @@ -297,7 +297,7 @@ impl MmapRegion { let mmap = MmapXen::new(&range)?; - Ok(MmapRegion { + Ok(GuestRegionXen { bitmap: B::with_len(range.size), size: range.size, prot: range.prot.ok_or(Error::Unexpected)?, @@ -309,7 +309,7 @@ impl MmapRegion { } } -impl MmapRegion { +impl GuestRegionXen { /// Returns a pointer to the beginning of the memory region. Mutable accesses performed /// using the resulting pointer are not automatically accounted for by the dirty bitmap /// tracking functionality. @@ -344,7 +344,7 @@ impl MmapRegion { /// /// This is mostly a sanity check available for convenience, as different file descriptors /// can alias the same file. - pub fn fds_overlap(&self, other: &MmapRegion) -> bool { + pub fn fds_overlap(&self, other: &GuestRegionXen) -> bool { if let Some(f_off1) = self.file_offset() { if let Some(f_off2) = other.file_offset() { if f_off1.file().as_raw_fd() == f_off2.file().as_raw_fd() { @@ -390,7 +390,7 @@ impl MmapRegion { } } -impl VolatileMemory for MmapRegion { +impl VolatileMemory for GuestRegionXen { type B = B; fn len(&self) -> usize { @@ -1035,6 +1035,7 @@ mod tests { #![allow(clippy::undocumented_unsafe_blocks)] use super::*; + use crate::mmap::GuestRegionXen; use matches::assert_matches; use vmm_sys_util::tempfile::TempFile; @@ -1071,7 +1072,7 @@ mod tests { } } - impl MmapRegion { + impl GuestRegionXen { /// Create an `MmapRegion` with specified `size` at GuestAdress(0) pub fn new(size: usize) -> Result { let range = MmapRange::new_unix(size, None, GuestAddress(0));