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