Skip to main content

vm_memory/mmap/
mod.rs

1// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
2//
3// Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4//
5// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
6// Use of this source code is governed by a BSD-style license that can be
7// found in the LICENSE-BSD-3-Clause file.
8//
9// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
10
11//! The default implementation for the [`GuestMemoryBackend`](trait.GuestMemoryBackend.html) trait.
12//!
13//! This implementation is mmap-ing the memory of the guest into the current process.
14
15use std::borrow::Borrow;
16use std::ops::Deref;
17use std::result;
18use std::sync::Arc;
19
20use crate::address::Address;
21use crate::bitmap::{Bitmap, BS};
22use crate::guest_memory::{self, FileOffset, GuestAddress, GuestUsize, MemoryRegionAddress};
23use crate::region::{
24    GuestMemoryRegion, GuestMemoryRegionBytes, GuestRegionCollection, GuestRegionCollectionError,
25};
26use crate::volatile_memory::{VolatileMemory, VolatileSlice};
27
28// re-export for backward compat, as the trait used to be defined in mmap.rs
29pub use crate::bitmap::NewBitmap;
30
31#[cfg(all(not(feature = "xen"), target_family = "unix"))]
32mod unix;
33
34#[cfg(all(feature = "xen", target_family = "unix"))]
35pub(crate) mod xen;
36
37#[cfg(target_family = "windows")]
38mod windows;
39
40#[cfg(all(not(feature = "xen"), target_family = "unix"))]
41pub use unix::{Error as MmapRegionError, MmapRegion, MmapRegionBuilder};
42
43#[cfg(all(feature = "xen", target_family = "unix"))]
44pub use xen::{Error as MmapRegionError, MmapRange, MmapRegion, MmapXenFlags};
45
46#[cfg(target_family = "windows")]
47pub use std::io::Error as MmapRegionError;
48#[cfg(target_family = "windows")]
49pub use windows::MmapRegion;
50
51/// [`GuestMemoryRegion`](trait.GuestMemoryRegion.html) implementation that mmaps the guest's
52/// memory region in the current process.
53///
54/// Represents a continuous region of the guest's physical memory that is backed by a mapping
55/// in the virtual address space of the calling process.
56#[derive(Debug)]
57pub struct GuestRegionMmap<B = ()> {
58    mapping: Arc<MmapRegion<B>>,
59    guest_base: GuestAddress,
60}
61
62impl<B> Deref for GuestRegionMmap<B> {
63    type Target = MmapRegion<B>;
64
65    fn deref(&self) -> &MmapRegion<B> {
66        self.mapping.as_ref()
67    }
68}
69
70impl<B: Bitmap> GuestRegionMmap<B> {
71    /// Create a new memory-mapped memory region for the guest's physical memory.
72    ///
73    /// Returns `None` if `guest_base` + `mapping.len()` would overflow.
74    pub fn new(mapping: MmapRegion<B>, guest_base: GuestAddress) -> Option<Self> {
75        Self::with_arc(Arc::new(mapping), guest_base)
76    }
77
78    /// Same as [`Self::new()`], but takes an `Arc`-wrapped `mapping`.
79    pub fn with_arc(mapping: Arc<MmapRegion<B>>, guest_base: GuestAddress) -> Option<Self> {
80        guest_base
81            .0
82            .checked_add(mapping.size() as u64)
83            .map(|_| Self {
84                mapping,
85                guest_base,
86            })
87    }
88
89    /// Return a clone of the inner `Arc<MmapRegion>` (as opposed to [`.deref()`](Self::deref()),
90    /// which bypasses the `Arc`).
91    ///
92    /// The returned reference can be used to construct a new `GuestRegionMmap` with a different
93    /// base address (e.g. when switching between memory address spaces based on the guest physical
94    /// address vs. the VMM userspace virtual address).
95    pub fn get_mmap(&self) -> Arc<MmapRegion<B>> {
96        Arc::clone(&self.mapping)
97    }
98}
99
100#[cfg(not(feature = "xen"))]
101impl<B: NewBitmap> GuestRegionMmap<B> {
102    /// Create a new memory-mapped memory region from guest's physical memory, size and file.
103    pub fn from_range(
104        addr: GuestAddress,
105        size: usize,
106        file: Option<FileOffset>,
107    ) -> result::Result<Self, FromRangesError> {
108        let region = if let Some(ref f_off) = file {
109            MmapRegion::from_file(f_off.clone(), size)?
110        } else {
111            MmapRegion::new(size)?
112        };
113
114        Self::new(region, addr).ok_or(FromRangesError::InvalidGuestRegion)
115    }
116}
117
118#[cfg(feature = "xen")]
119impl<B: NewBitmap> GuestRegionMmap<B> {
120    /// Create a new Unix memory-mapped memory region from guest's physical memory, size and file.
121    /// This must only be used for tests, doctests, benches and is not designed for end consumers.
122    pub fn from_range(
123        addr: GuestAddress,
124        size: usize,
125        file: Option<FileOffset>,
126    ) -> result::Result<Self, FromRangesError> {
127        let range = MmapRange::new_unix(size, file, addr);
128
129        let region = MmapRegion::from_range(range)?;
130        Self::new(region, addr).ok_or(FromRangesError::InvalidGuestRegion)
131    }
132}
133
134impl<B: Bitmap> GuestMemoryRegion for GuestRegionMmap<B> {
135    type B = B;
136
137    fn len(&self) -> GuestUsize {
138        self.mapping.size() as GuestUsize
139    }
140
141    fn start_addr(&self) -> GuestAddress {
142        self.guest_base
143    }
144
145    fn bitmap(&self) -> BS<'_, Self::B> {
146        self.mapping.bitmap().slice_at(0)
147    }
148
149    fn get_host_address(&self, addr: MemoryRegionAddress) -> guest_memory::Result<*mut u8> {
150        // Not sure why wrapping_offset is not unsafe.  Anyway this
151        // is safe because we've just range-checked addr using check_address.
152        self.check_address(addr)
153            .ok_or(guest_memory::Error::InvalidBackendAddress)
154            .map(|addr| {
155                self.mapping
156                    .as_ptr()
157                    .wrapping_offset(addr.raw_value() as isize)
158            })
159    }
160
161    fn file_offset(&self) -> Option<&FileOffset> {
162        self.mapping.file_offset()
163    }
164
165    fn get_slice(
166        &self,
167        offset: MemoryRegionAddress,
168        count: usize,
169    ) -> guest_memory::Result<VolatileSlice<'_, BS<'_, B>>> {
170        let slice = self.mapping.get_slice(offset.raw_value() as usize, count)?;
171        Ok(slice)
172    }
173
174    #[cfg(target_os = "linux")]
175    fn is_hugetlbfs(&self) -> Option<bool> {
176        self.mapping.is_hugetlbfs()
177    }
178}
179
180impl<B: Bitmap> GuestMemoryRegionBytes for GuestRegionMmap<B> {}
181
182/// [`GuestMemoryBackend`](trait.GuestMemoryBackend.html) implementation that mmaps the guest's memory
183/// in the current process.
184///
185/// Represents the entire physical memory of the guest by tracking all its memory regions.
186/// Each region is an instance of `GuestRegionMmap`, being backed by a mapping in the
187/// virtual address space of the calling process.
188pub type GuestMemoryMmap<B = ()> = GuestRegionCollection<GuestRegionMmap<B>>;
189
190/// Errors that can happen during [`GuestMemoryMmap::from_ranges`] and related functions.
191#[derive(Debug, thiserror::Error)]
192pub enum FromRangesError {
193    /// Error during construction of [`GuestMemoryMmap`]
194    #[error("Error constructing guest region collection: {0}")]
195    Collection(#[from] GuestRegionCollectionError),
196    /// Error while allocating raw mmap region
197    #[error("Error setting up raw memory for guest region: {0}")]
198    MmapRegion(#[from] MmapRegionError),
199    /// A combination of region length and guest address would overflow.
200    #[error("Combination of guest address and region length invalid (would overflow)")]
201    InvalidGuestRegion,
202}
203
204impl<B: NewBitmap> GuestMemoryMmap<B> {
205    /// Creates a container and allocates anonymous memory for guest memory regions.
206    ///
207    /// Valid memory regions are specified as a slice of (Address, Size) tuples sorted by Address.
208    pub fn from_ranges(ranges: &[(GuestAddress, usize)]) -> result::Result<Self, FromRangesError> {
209        Self::from_ranges_with_files(ranges.iter().map(|r| (r.0, r.1, None)))
210    }
211
212    /// Creates a container and allocates anonymous memory for guest memory regions.
213    ///
214    /// Valid memory regions are specified as a sequence of (Address, Size, [`Option<FileOffset>`])
215    /// tuples sorted by Address.
216    pub fn from_ranges_with_files<A, T>(ranges: T) -> result::Result<Self, FromRangesError>
217    where
218        A: Borrow<(GuestAddress, usize, Option<FileOffset>)>,
219        T: IntoIterator<Item = A>,
220    {
221        Self::from_regions(
222            ranges
223                .into_iter()
224                .map(|x| {
225                    GuestRegionMmap::from_range(x.borrow().0, x.borrow().1, x.borrow().2.clone())
226                })
227                .collect::<Result<Vec<_>, _>>()?,
228        )
229        .map_err(Into::into)
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    #![allow(clippy::undocumented_unsafe_blocks)]
236    extern crate vmm_sys_util;
237
238    use super::*;
239
240    #[cfg(feature = "backend-bitmap")]
241    use crate::bitmap::AtomicBitmap;
242    use crate::{Bytes, GuestMemoryBackend, GuestMemoryError};
243
244    use std::io::Write;
245    #[cfg(feature = "rawfd")]
246    use std::{fs::File, path::Path};
247    use vmm_sys_util::tempfile::TempFile;
248
249    use matches::assert_matches;
250
251    type GuestRegionMmap = super::GuestRegionMmap<()>;
252    type GuestMemoryMmap = super::GuestRegionCollection<GuestRegionMmap>;
253    type MmapRegion = super::MmapRegion<()>;
254
255    #[test]
256    fn basic_map() {
257        let m = MmapRegion::new(1024).unwrap();
258        assert_eq!(1024, m.size());
259    }
260
261    #[test]
262    fn slice_addr() {
263        let m = GuestRegionMmap::from_range(GuestAddress(0), 5, None).unwrap();
264        let s = m.get_slice(MemoryRegionAddress(2), 3).unwrap();
265        let guard = s.ptr_guard();
266        assert_eq!(guard.as_ptr(), unsafe { m.as_ptr().offset(2) });
267    }
268
269    #[test]
270    #[cfg(not(miri))] // Miri cannot mmap files
271    fn mapped_file_read() {
272        let mut f = TempFile::new().unwrap().into_file();
273        let sample_buf = &[1, 2, 3, 4, 5];
274        f.write_all(sample_buf).unwrap();
275
276        let file = Some(FileOffset::new(f, 0));
277        let mem_map = GuestRegionMmap::from_range(GuestAddress(0), sample_buf.len(), file).unwrap();
278        let buf = &mut [0u8; 16];
279        assert_eq!(
280            mem_map.as_volatile_slice().unwrap().read(buf, 0).unwrap(),
281            sample_buf.len()
282        );
283        assert_eq!(buf[0..sample_buf.len()], sample_buf[..]);
284    }
285
286    #[test]
287    fn test_to_region_addr() {
288        let f1 = TempFile::new().unwrap().into_file();
289        f1.set_len(0x400).unwrap();
290        let f2 = TempFile::new().unwrap().into_file();
291        f2.set_len(0x400).unwrap();
292
293        let start_addr1 = GuestAddress(0x0);
294        let start_addr2 = GuestAddress(0x800);
295        let guest_mem =
296            GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap();
297        let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
298            (start_addr1, 0x400, Some(FileOffset::new(f1, 0))),
299            (start_addr2, 0x400, Some(FileOffset::new(f2, 0))),
300        ])
301        .unwrap();
302
303        let guest_mem_list = [guest_mem, guest_mem_backed_by_file];
304        for guest_mem in guest_mem_list.iter() {
305            assert!(guest_mem.to_region_addr(GuestAddress(0x600)).is_none());
306            let (r0, addr0) = guest_mem.to_region_addr(GuestAddress(0x800)).unwrap();
307            let (r1, addr1) = guest_mem.to_region_addr(GuestAddress(0xa00)).unwrap();
308            assert!(r0.as_ptr() == r1.as_ptr());
309            assert_eq!(addr0, MemoryRegionAddress(0));
310            assert_eq!(addr1, MemoryRegionAddress(0x200));
311        }
312    }
313
314    #[test]
315    fn test_get_host_address() {
316        let f1 = TempFile::new().unwrap().into_file();
317        f1.set_len(0x400).unwrap();
318        let f2 = TempFile::new().unwrap().into_file();
319        f2.set_len(0x400).unwrap();
320
321        let start_addr1 = GuestAddress(0x0);
322        let start_addr2 = GuestAddress(0x800);
323        let guest_mem =
324            GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap();
325        let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
326            (start_addr1, 0x400, Some(FileOffset::new(f1, 0))),
327            (start_addr2, 0x400, Some(FileOffset::new(f2, 0))),
328        ])
329        .unwrap();
330
331        let guest_mem_list = [guest_mem, guest_mem_backed_by_file];
332        for guest_mem in guest_mem_list.iter() {
333            assert_matches!(
334                guest_mem.get_host_address(GuestAddress(0x600)).unwrap_err(),
335                GuestMemoryError::InvalidGuestAddress(GuestAddress(0x600))
336            );
337            let ptr0 = guest_mem.get_host_address(GuestAddress(0x800)).unwrap();
338            let ptr1 = guest_mem.get_host_address(GuestAddress(0xa00)).unwrap();
339            assert_eq!(
340                ptr0,
341                guest_mem.find_region(GuestAddress(0x800)).unwrap().as_ptr()
342            );
343            assert_eq!(unsafe { ptr0.offset(0x200) }, ptr1);
344        }
345    }
346
347    #[test]
348    fn test_check_range() {
349        let start_addr1 = GuestAddress(0);
350        let start_addr2 = GuestAddress(0x800);
351        let start_addr3 = GuestAddress(0xc00);
352        let guest_mem = GuestMemoryMmap::from_ranges(&[
353            (start_addr1, 0x400),
354            (start_addr2, 0x400),
355            (start_addr3, 0x400),
356        ])
357        .unwrap();
358
359        assert!(guest_mem.check_range(start_addr1, 0x0));
360        assert!(guest_mem.check_range(start_addr1, 0x200));
361        assert!(guest_mem.check_range(start_addr1, 0x400));
362        assert!(!guest_mem.check_range(start_addr1, 0xa00));
363        assert!(guest_mem.check_range(start_addr2, 0x7ff));
364        assert!(guest_mem.check_range(start_addr2, 0x800));
365        assert!(!guest_mem.check_range(start_addr2, 0x801));
366        assert!(!guest_mem.check_range(start_addr2, 0xc00));
367        assert!(!guest_mem.check_range(start_addr1, usize::MAX));
368    }
369
370    #[test]
371    fn test_deref() {
372        let f = TempFile::new().unwrap().into_file();
373        f.set_len(0x400).unwrap();
374
375        let start_addr = GuestAddress(0x0);
376        let guest_mem = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap();
377        let guest_mem_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[(
378            start_addr,
379            0x400,
380            Some(FileOffset::new(f, 0)),
381        )])
382        .unwrap();
383
384        let guest_mem_list = [guest_mem, guest_mem_backed_by_file];
385        for guest_mem in guest_mem_list.iter() {
386            let sample_buf = &[1, 2, 3, 4, 5];
387
388            assert_eq!(guest_mem.write(sample_buf, start_addr).unwrap(), 5);
389            let slice = guest_mem
390                .find_region(GuestAddress(0))
391                .unwrap()
392                .as_volatile_slice()
393                .unwrap();
394
395            let buf = &mut [0, 0, 0, 0, 0];
396            assert_eq!(slice.read(buf, 0).unwrap(), 5);
397            assert_eq!(buf, sample_buf);
398        }
399    }
400
401    #[test]
402    fn test_read_u64() {
403        let f1 = TempFile::new().unwrap().into_file();
404        f1.set_len(0x1000).unwrap();
405        let f2 = TempFile::new().unwrap().into_file();
406        f2.set_len(0x1000).unwrap();
407
408        let start_addr1 = GuestAddress(0x0);
409        let start_addr2 = GuestAddress(0x1000);
410        let bad_addr = GuestAddress(0x2001);
411        let bad_addr2 = GuestAddress(0x1ffc);
412        let max_addr = GuestAddress(0x2000);
413
414        let gm =
415            GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
416        let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
417            (start_addr1, 0x1000, Some(FileOffset::new(f1, 0))),
418            (start_addr2, 0x1000, Some(FileOffset::new(f2, 0))),
419        ])
420        .unwrap();
421
422        let gm_list = [gm, gm_backed_by_file];
423        for gm in gm_list.iter() {
424            let val1: u64 = 0xaa55_aa55_aa55_aa55;
425            let val2: u64 = 0x55aa_55aa_55aa_55aa;
426            assert_matches!(
427                gm.write_obj(val1, bad_addr).unwrap_err(),
428                GuestMemoryError::InvalidGuestAddress(addr) if addr == bad_addr
429            );
430            assert_matches!(
431                gm.write_obj(val1, bad_addr2).unwrap_err(),
432                GuestMemoryError::PartialBuffer { expected, completed } if expected == size_of::<u64>() && completed == max_addr.checked_offset_from(bad_addr2).unwrap() as usize);
433
434            gm.write_obj(val1, GuestAddress(0x500)).unwrap();
435            gm.write_obj(val2, GuestAddress(0x1000 + 32)).unwrap();
436            let num1: u64 = gm.read_obj(GuestAddress(0x500)).unwrap();
437            let num2: u64 = gm.read_obj(GuestAddress(0x1000 + 32)).unwrap();
438            assert_eq!(val1, num1);
439            assert_eq!(val2, num2);
440        }
441    }
442
443    #[test]
444    fn write_and_read() {
445        let f = TempFile::new().unwrap().into_file();
446        f.set_len(0x400).unwrap();
447
448        let mut start_addr = GuestAddress(0x1000);
449        let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap();
450        let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[(
451            start_addr,
452            0x400,
453            Some(FileOffset::new(f, 0)),
454        )])
455        .unwrap();
456
457        let gm_list = [gm, gm_backed_by_file];
458        for gm in gm_list.iter() {
459            let sample_buf = &[1, 2, 3, 4, 5];
460
461            assert_eq!(gm.write(sample_buf, start_addr).unwrap(), 5);
462
463            let buf = &mut [0u8; 5];
464            assert_eq!(gm.read(buf, start_addr).unwrap(), 5);
465            assert_eq!(buf, sample_buf);
466
467            start_addr = GuestAddress(0x13ff);
468            assert_eq!(gm.write(sample_buf, start_addr).unwrap(), 1);
469            assert_eq!(gm.read(buf, start_addr).unwrap(), 1);
470            assert_eq!(buf[0], sample_buf[0]);
471            start_addr = GuestAddress(0x1000);
472        }
473    }
474
475    #[test]
476    #[cfg(feature = "rawfd")]
477    #[cfg(not(miri))]
478    fn read_to_and_write_from_mem() {
479        use std::mem;
480
481        let f = TempFile::new().unwrap().into_file();
482        f.set_len(0x400).unwrap();
483
484        let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x1000), 0x400)]).unwrap();
485        let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[(
486            GuestAddress(0x1000),
487            0x400,
488            Some(FileOffset::new(f, 0)),
489        )])
490        .unwrap();
491
492        let gm_list = [gm, gm_backed_by_file];
493        for gm in gm_list.iter() {
494            let addr = GuestAddress(0x1010);
495            let mut file = if cfg!(target_family = "unix") {
496                File::open(Path::new("/dev/zero")).unwrap()
497            } else {
498                File::open(Path::new("c:\\Windows\\system32\\ntoskrnl.exe")).unwrap()
499            };
500            gm.write_obj(!0u32, addr).unwrap();
501            gm.read_exact_volatile_from(addr, &mut file, mem::size_of::<u32>())
502                .unwrap();
503            let value: u32 = gm.read_obj(addr).unwrap();
504            if cfg!(target_family = "unix") {
505                assert_eq!(value, 0);
506            } else {
507                assert_eq!(value, 0x0090_5a4d);
508            }
509
510            let mut sink = vec![0; mem::size_of::<u32>()];
511            gm.write_all_volatile_to(addr, &mut sink.as_mut_slice(), mem::size_of::<u32>())
512                .unwrap();
513            if cfg!(target_family = "unix") {
514                assert_eq!(sink, vec![0; mem::size_of::<u32>()]);
515            } else {
516                assert_eq!(sink, vec![0x4d, 0x5a, 0x90, 0x00]);
517            };
518        }
519    }
520
521    #[test]
522    fn test_access_cross_boundary() {
523        let f1 = TempFile::new().unwrap().into_file();
524        f1.set_len(0x1000).unwrap();
525        let f2 = TempFile::new().unwrap().into_file();
526        f2.set_len(0x1000).unwrap();
527
528        let start_addr1 = GuestAddress(0x0);
529        let start_addr2 = GuestAddress(0x1000);
530        let gm =
531            GuestMemoryMmap::from_ranges(&[(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
532        let gm_backed_by_file = GuestMemoryMmap::from_ranges_with_files(&[
533            (start_addr1, 0x1000, Some(FileOffset::new(f1, 0))),
534            (start_addr2, 0x1000, Some(FileOffset::new(f2, 0))),
535        ])
536        .unwrap();
537
538        let gm_list = [gm, gm_backed_by_file];
539        for gm in gm_list.iter() {
540            let sample_buf = &[1, 2, 3, 4, 5];
541            assert_eq!(gm.write(sample_buf, GuestAddress(0xffc)).unwrap(), 5);
542            let buf = &mut [0u8; 5];
543            assert_eq!(gm.read(buf, GuestAddress(0xffc)).unwrap(), 5);
544            assert_eq!(buf, sample_buf);
545        }
546    }
547
548    #[test]
549    fn test_retrieve_fd_backing_memory_region() {
550        let f = TempFile::new().unwrap().into_file();
551        f.set_len(0x400).unwrap();
552
553        let start_addr = GuestAddress(0x0);
554        let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap();
555        assert!(gm.find_region(start_addr).is_some());
556        let region = gm.find_region(start_addr).unwrap();
557        assert!(region.file_offset().is_none());
558
559        let gm = GuestMemoryMmap::from_ranges_with_files(&[(
560            start_addr,
561            0x400,
562            Some(FileOffset::new(f, 0)),
563        )])
564        .unwrap();
565        assert!(gm.find_region(start_addr).is_some());
566        let region = gm.find_region(start_addr).unwrap();
567        assert!(region.file_offset().is_some());
568    }
569
570    // Windows needs a dedicated test where it will retrieve the allocation
571    // granularity to determine a proper offset (other than 0) that can be
572    // used for the backing file. Refer to Microsoft docs here:
573    // https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-mapviewoffile
574    #[test]
575    #[cfg(target_family = "unix")]
576    fn test_retrieve_offset_from_fd_backing_memory_region() {
577        let f = TempFile::new().unwrap().into_file();
578        f.set_len(0x1400).unwrap();
579        // Needs to be aligned on 4k, otherwise mmap will fail.
580        let offset = 0x1000;
581
582        let start_addr = GuestAddress(0x0);
583        let gm = GuestMemoryMmap::from_ranges(&[(start_addr, 0x400)]).unwrap();
584        assert!(gm.find_region(start_addr).is_some());
585        let region = gm.find_region(start_addr).unwrap();
586        assert!(region.file_offset().is_none());
587
588        let gm = GuestMemoryMmap::from_ranges_with_files(&[(
589            start_addr,
590            0x400,
591            Some(FileOffset::new(f, offset)),
592        )])
593        .unwrap();
594        assert!(gm.find_region(start_addr).is_some());
595        let region = gm.find_region(start_addr).unwrap();
596        assert!(region.file_offset().is_some());
597        assert_eq!(region.file_offset().unwrap().start(), offset);
598    }
599
600    #[test]
601    fn test_guest_memory_mmap_get_slice() {
602        let region = GuestRegionMmap::from_range(GuestAddress(0), 0x400, None).unwrap();
603
604        // Normal case.
605        let slice_addr = MemoryRegionAddress(0x100);
606        let slice_size = 0x200;
607        let slice = region.get_slice(slice_addr, slice_size).unwrap();
608        assert_eq!(slice.len(), slice_size);
609
610        // Empty slice.
611        let slice_addr = MemoryRegionAddress(0x200);
612        let slice_size = 0x0;
613        let slice = region.get_slice(slice_addr, slice_size).unwrap();
614        assert!(slice.is_empty());
615
616        // Error case when slice_size is beyond the boundary.
617        let slice_addr = MemoryRegionAddress(0x300);
618        let slice_size = 0x200;
619        assert_matches!(
620            region.get_slice(slice_addr, slice_size).unwrap_err(),
621            GuestMemoryError::InvalidBackendAddress
622        );
623    }
624
625    #[test]
626    fn test_guest_memory_mmap_as_volatile_slice() {
627        let region_size = 0x400;
628        let region = GuestRegionMmap::from_range(GuestAddress(0), region_size, None).unwrap();
629
630        // Test slice length.
631        let slice = region.as_volatile_slice().unwrap();
632        assert_eq!(slice.len(), region_size);
633
634        // Test slice data.
635        let v = 0x1234_5678u32;
636        let r = slice.get_ref::<u32>(0x200).unwrap();
637        r.store(v);
638        assert_eq!(r.load(), v);
639    }
640
641    #[test]
642    fn test_guest_memory_get_slice() {
643        let start_addr1 = GuestAddress(0);
644        let start_addr2 = GuestAddress(0x800);
645        let guest_mem =
646            GuestMemoryMmap::from_ranges(&[(start_addr1, 0x400), (start_addr2, 0x400)]).unwrap();
647
648        // Normal cases.
649        let slice_size = 0x200;
650        let slice = guest_mem
651            .get_slice(GuestAddress(0x100), slice_size)
652            .unwrap();
653        assert_eq!(slice.len(), slice_size);
654
655        let slice_size = 0x400;
656        let slice = guest_mem
657            .get_slice(GuestAddress(0x800), slice_size)
658            .unwrap();
659        assert_eq!(slice.len(), slice_size);
660
661        // Empty slice.
662        assert!(guest_mem
663            .get_slice(GuestAddress(0x900), 0)
664            .unwrap()
665            .is_empty());
666
667        // Error cases, wrong size or base address.
668        assert_matches!(
669            guest_mem.get_slice(GuestAddress(0), 0x500).unwrap_err(),
670            GuestMemoryError::InvalidBackendAddress
671        );
672        assert_matches!(
673            guest_mem.get_slice(GuestAddress(0x600), 0x100).unwrap_err(),
674            GuestMemoryError::InvalidGuestAddress(GuestAddress(0x600))
675        );
676        assert_matches!(
677            guest_mem.get_slice(GuestAddress(0xc00), 0x100).unwrap_err(),
678            GuestMemoryError::InvalidGuestAddress(GuestAddress(0xc00))
679        );
680    }
681
682    #[test]
683    fn test_guest_memory_get_slices() {
684        let start_addr1 = GuestAddress(0);
685        let start_addr2 = GuestAddress(0x800);
686        let start_addr3 = GuestAddress(0xc00);
687        let guest_mem = GuestMemoryMmap::from_ranges(&[
688            (start_addr1, 0x400),
689            (start_addr2, 0x400),
690            (start_addr3, 0x400),
691        ])
692        .unwrap();
693
694        // Same cases as `test_guest_memory_get_slice()`, just with `get_slices()`.
695        let slice_size = 0x200;
696        let mut slices = guest_mem.get_slices(GuestAddress(0x100), slice_size);
697        let slice = slices.next().unwrap().unwrap();
698        assert!(slices.next().is_none());
699        assert_eq!(slice.len(), slice_size);
700
701        let slice_size = 0x400;
702        let mut slices = guest_mem.get_slices(GuestAddress(0x800), slice_size);
703        let slice = slices.next().unwrap().unwrap();
704        assert!(slices.next().is_none());
705        assert_eq!(slice.len(), slice_size);
706
707        // Empty iterator.
708        assert!(guest_mem
709            .get_slices(GuestAddress(0x900), 0)
710            .next()
711            .is_none());
712
713        // Error cases, wrong size or base address.
714        let mut slices = guest_mem.get_slices(GuestAddress(0), 0x500);
715        assert_eq!(slices.next().unwrap().unwrap().len(), 0x400);
716        assert_matches!(
717            slices.next().unwrap().unwrap_err(),
718            GuestMemoryError::InvalidGuestAddress(GuestAddress(0x400))
719        );
720        assert!(slices.next().is_none());
721        let mut slices = guest_mem.get_slices(GuestAddress(0x600), 0x100);
722        assert_matches!(
723            slices.next().unwrap().unwrap_err(),
724            GuestMemoryError::InvalidGuestAddress(GuestAddress(0x600))
725        );
726        assert!(slices.next().is_none());
727        let mut slices = guest_mem.get_slices(GuestAddress(0x1000), 0x100);
728        assert_matches!(
729            slices.next().unwrap().unwrap_err(),
730            GuestMemoryError::InvalidGuestAddress(GuestAddress(0x1000))
731        );
732        assert!(slices.next().is_none());
733
734        // Test fragmented case
735        let mut slices = guest_mem.get_slices(GuestAddress(0xa00), 0x400);
736        assert_eq!(slices.next().unwrap().unwrap().len(), 0x200);
737        assert_eq!(slices.next().unwrap().unwrap().len(), 0x200);
738        assert!(slices.next().is_none());
739    }
740
741    #[test]
742    fn test_atomic_accesses() {
743        let region = GuestRegionMmap::from_range(GuestAddress(0), 0x1000, None).unwrap();
744
745        crate::bytes::tests::check_atomic_accesses(
746            region,
747            MemoryRegionAddress(0),
748            MemoryRegionAddress(0x1000),
749        );
750    }
751
752    #[test]
753    #[cfg(feature = "backend-bitmap")]
754    fn test_dirty_tracking() {
755        crate::bitmap::tests::test_guest_memory_and_region(|| {
756            crate::GuestMemoryMmap::<AtomicBitmap>::from_ranges(&[(GuestAddress(0), 0x1_0000)])
757                .unwrap()
758        });
759    }
760
761    #[test]
762    fn test_change_region_addr() {
763        let addr1 = GuestAddress(0x1000);
764        let addr2 = GuestAddress(0x2000);
765        let gm = GuestMemoryMmap::from_ranges(&[(addr1, 0x1000)]).unwrap();
766
767        assert!(gm.find_region(addr1).is_some());
768        assert!(gm.find_region(addr2).is_none());
769
770        let (gm, region) = gm.remove_region(addr1, 0x1000).unwrap();
771
772        assert!(gm.find_region(addr1).is_none());
773        assert!(gm.find_region(addr2).is_none());
774
775        // Note that the `region` returned by `remove_region` is an `Arc<_>`, so users generally
776        // cannot mutate it (change its base address).  In this test, we can (we could unwrap the
777        // `Arc<_>`), but our users generally cannot, hence why this interface exists.
778        let region = GuestRegionMmap::with_arc(region.get_mmap(), addr2).unwrap();
779
780        let gm = gm.insert_region(Arc::new(region)).unwrap();
781
782        assert!(gm.find_region(addr1).is_none());
783        assert!(gm.find_region(addr2).is_some());
784    }
785}