Skip to main content

virtio_media/
mmap.rs

1// Copyright 2024 The ChromiumOS Authors
2
3use std::os::fd::BorrowedFd;
4
5use thiserror::Error;
6
7use crate::VirtioMediaHostMemoryMapper;
8
9#[derive(Debug, PartialEq, Eq)]
10pub struct MmapBufferMapping {
11    /// Number of times `mmap` has been performed for this buffer.
12    num_mappings: usize,
13    /// Guest address at which the buffer is currently mapped.
14    guest_addr: u64,
15    /// Whether the mapping of this buffer is read-only or read-write.
16    rw: bool,
17}
18
19/// Information about a MMAP buffer.
20#[derive(Debug, PartialEq, Eq)]
21pub struct MmapBuffer {
22    /// Start offset in the MMAP range of this buffer.
23    offset: u32,
24    /// Size of the buffer.
25    size: u32,
26    /// Whether this buffer is still registered, i.e. hasn't been deleted by the driver.
27    /// Unregistered buffers can still have active mappings, and are kept alive until their mapping
28    /// count reaches zero. However such buffers cannot be mapped anymore and take no space in the
29    /// MMAP range.
30    registered: bool,
31    /// Mapping information about this buffer, if the buffer is currently mapped into the guest.
32    mapping: Option<MmapBufferMapping>,
33}
34
35impl MmapBuffer {
36    /// Returns a new instance of `MmapBuffer` with the given parameters, zero mappings and
37    /// a registered status.
38    fn new(offset: u32, size: u32) -> Self {
39        Self {
40            offset,
41            size,
42            registered: true,
43            mapping: None,
44        }
45    }
46}
47
48/// Range manager for MMAP buffers, using a host memory mapper.
49///
50/// Devices that allocate MMAP buffers can register a buffer using [`Self::register_buffer`] and
51/// unregister them with [`Self::unregister_buffer`]. Registered buffers can then be mapped into the
52/// guest address space by calling [`Self::create_mapping`] on their offset. This will return the address
53/// of their guest mapping, which can then be accessed or used to unmap them with [`Self::remove_mapping`].
54pub struct MmapMappingManager<M: VirtioMediaHostMemoryMapper> {
55    /// Sorted MMAP space of the device. Each registered MMAP buffer takes the `[offset, size - 1]`
56    /// range in this space, which is sorted in order to be binary-searchable.
57    ///
58    /// Buffers that are unregistered but still mapped are still kept here, but do not take space
59    /// in the MMAP range (i.e. they are skipped during the binary search).
60    buffers: Vec<MmapBuffer>,
61    /// Memory mapper used to create buffer mappings.
62    mapper: M,
63}
64
65impl<M: VirtioMediaHostMemoryMapper> From<M> for MmapMappingManager<M> {
66    fn from(mapper: M) -> Self {
67        Self {
68            buffers: Vec::new(),
69            mapper,
70        }
71    }
72}
73
74#[derive(Debug, Error, PartialEq, Eq)]
75pub enum RegisterBufferError {
76    #[error("insufficient free space in the MMAP range")]
77    NoFreeSpace,
78    #[error("requested offset is already occupied")]
79    OffsetOccupied,
80    #[error("buffers of size 0 cannot be registered")]
81    EmptyBuffer,
82    #[error("buffer offset must be a multiple of the memory page size")]
83    UnalignedOffset,
84}
85
86#[derive(Debug, Error, PartialEq, Eq)]
87pub enum CreateMappingError {
88    #[error("no buffer registered at the requested offset")]
89    InvalidOffset,
90    #[error("cannot create new mappings for unregistered buffers")]
91    UnregisteredBuffer,
92    #[error("requested mapping range goes outside the buffer")]
93    SizeOutOfBounds,
94    #[error("error while cloning the FD for the buffer")]
95    FdCloneFailure(std::io::ErrorKind),
96    #[error("error while mapping the buffer: {0}")]
97    MappingFailure(i32),
98    #[error("mapping requested with different permission from the old one")]
99    NonMatchingPermissions,
100}
101
102#[derive(Debug, Error, PartialEq, Eq)]
103pub enum RemoveMappingError {
104    #[error("no buffer registered at the requested offset")]
105    InvalidOffset,
106}
107
108const PAGE_SIZE: u32 = 0x1000;
109const PAGE_MASK: u32 = !(PAGE_SIZE - 1);
110
111impl<M: VirtioMediaHostMemoryMapper> MmapMappingManager<M> {
112    /// Registers a new buffer at `offset`. If `offset` if `None`, then an offset is allocated and
113    /// returned.
114    ///
115    /// This method fails if the range is full, or if `offset` is `Some` and the requested offset
116    /// if already used by some other buffer. If `offset` is `Some` and the function succeed, then
117    /// the returned value is guaranteed to be the passed offset.
118    ///
119    /// Note that the ranges automatically allocated are of fixed size: only the offset of a
120    /// buffer is relevant when mapping it, not its size. Real V4L2 drivers also use this trick of
121    /// allocating ranges such that buffers appear to overlap. This is useful as the address space
122    /// is technically 32-bit, and we might need to use buffers which added size would not fit.
123    ///
124    /// TODO: we should recycle offsets, and further type `MmapMappingManager` so that only one
125    /// allocation type can be used per instance (fixed or dynamic).
126    pub fn register_buffer(
127        &mut self,
128        offset: Option<u32>,
129        size: u32,
130    ) -> Result<u32, RegisterBufferError> {
131        let offset = offset.unwrap_or_else(|| {
132            self.buffers
133                .last()
134                // Align the start offset to the next page, or `register_buffer_by_offset` will
135                // fail.
136                .map(|b| (b.offset + 1).next_multiple_of(PAGE_SIZE))
137                .unwrap_or(0)
138        });
139
140        self.register_buffer_by_offset(offset, size)
141            .map(|()| offset)
142    }
143
144    /// Unregisters the buffer previously registered at `offset`. Returns `true` if a buffer was
145    /// indeed registered as starting at `offset`, `false` otherwise.
146    pub fn unregister_buffer(&mut self, offset: u32) -> bool {
147        match self.buffers.binary_search_by_key(&offset, |b| b.offset) {
148            Err(_) => false,
149            Ok(index) => {
150                let buffer = &mut self.buffers[index];
151
152                buffer.registered = false;
153                // If there is no mapping then the buffer can be removed from the MMAP range.
154                if buffer.mapping.is_none() {
155                    self.buffers.remove(index);
156                }
157
158                true
159            }
160        }
161    }
162
163    // Register a new buffer of `size` at `offset`. Returns an error if `offset` is` already
164    // occupied by another buffer.
165    //
166    // `size` must be greater than `0` and `offset` must be a multiple of `PAGE_SIZE`.
167    fn register_buffer_by_offset(
168        &mut self,
169        offset: u32,
170        size: u32,
171    ) -> Result<(), RegisterBufferError> {
172        if size == 0 {
173            return Err(RegisterBufferError::EmptyBuffer);
174        }
175        if offset & PAGE_MASK != offset {
176            return Err(RegisterBufferError::UnalignedOffset);
177        }
178
179        // Check that `offset` is actually available.
180        match self.buffers.binary_search_by_key(&offset, |b| b.offset) {
181            // Already have a registered buffer at that very offset.
182            Ok(_) => Err(RegisterBufferError::OffsetOccupied),
183            Err(index) => {
184                self.buffers.insert(index, MmapBuffer::new(offset, size));
185                Ok(())
186            }
187        }
188    }
189
190    /// Create a new mapping for the buffer registered at `offset`. `rw` indicates whether the
191    /// mapping is read-only or read-write. Returns the guest address at which the buffer is
192    /// mapped, and the size of the mapping, which should be equal to the size of the buffer.
193    ///
194    /// This method can be called several times and will reuse the prior mapping if it exists. The
195    /// mapping will also persist until an identical number of calls to [`Self::remove_mapping`]
196    /// are performed.
197    ///
198    /// Note however that requiring the same active mapping with different `rw` permissions will
199    /// result in a `EPERM` error.
200    pub fn create_mapping(
201        &mut self,
202        offset: u32,
203        fd: BorrowedFd,
204        rw: bool,
205    ) -> Result<(u64, u64), CreateMappingError> {
206        let buffer = self
207            .buffers
208            .binary_search_by_key(&offset, |b| b.offset)
209            .map(|i| &mut self.buffers[i])
210            .map_err(|_| CreateMappingError::InvalidOffset)?;
211        let last_buffer_address = buffer
212            .offset
213            .checked_add(buffer.size - 1)
214            .ok_or(CreateMappingError::InvalidOffset)?;
215
216        // Cannot create additional mappings for buffers that have been destroyed on the guest side.
217        if !buffer.registered {
218            return Err(CreateMappingError::UnregisteredBuffer);
219        }
220
221        // Check that we are not requiring more mapping than the buffer can cover.
222        if last_buffer_address > buffer.offset + (buffer.size - 1) {
223            return Err(CreateMappingError::SizeOutOfBounds);
224        }
225
226        let guest_addr = match &mut buffer.mapping {
227            None => {
228                let guest_addr = self
229                    .mapper
230                    .add_mapping(
231                        fd,
232                        // Always map the full buffer so we can reuse the mapping even with different
233                        // sizes.
234                        buffer.size as u64,
235                        buffer.offset as u64,
236                        rw,
237                    )
238                    .map_err(CreateMappingError::MappingFailure)?;
239
240                buffer.mapping = Some(MmapBufferMapping {
241                    num_mappings: 1,
242                    rw,
243                    guest_addr,
244                });
245
246                // TODO: need to be able to lookup the buffer back by guest address - add a
247                // guest_addr -> offset table?
248                guest_addr
249            }
250            Some(mapping) => {
251                if mapping.rw != rw {
252                    return Err(CreateMappingError::NonMatchingPermissions);
253                }
254                mapping.num_mappings += 1;
255                mapping.guest_addr
256            }
257        };
258
259        Ok((guest_addr, buffer.size as u64))
260    }
261
262    /// Returns `true` if the buffer still has other mappings, `false` if this was the last mapping.
263    pub fn remove_mapping(&mut self, guest_addr: u64) -> Result<bool, RemoveMappingError> {
264        // TODO: use a guest_addr -> offset table to avoid O(n) here?
265        for (i, buffer) in self.buffers.iter_mut().enumerate() {
266            match &mut buffer.mapping {
267                Some(mapping) if mapping.guest_addr == guest_addr => {
268                    mapping.num_mappings -= 1;
269                    if mapping.num_mappings == 0 {
270                        if let Err(e) = self.mapper.remove_mapping(guest_addr) {
271                            log::error!("error while unmapping MMAP buffer: {:#}", e);
272                        }
273                        buffer.mapping = None;
274                        // If this was the last dangling mapping then the buffer can be removed
275                        // from the MMAP range.
276                        if !buffer.registered {
277                            self.buffers.remove(i);
278                        }
279                        return Ok(false);
280                    } else {
281                        return Ok(true);
282                    }
283                }
284                _ => (),
285            }
286        }
287
288        Err(RemoveMappingError::InvalidOffset)
289    }
290    /// Returns `true` if the buffer registered at `offset` is already mapped.
291    pub fn is_mapped(&self, offset: u64) -> bool {
292        let Ok(offset) = u32::try_from(offset) else {
293            return false;
294        };
295
296        match self.buffers.binary_search_by_key(&offset, |b| b.offset) {
297            Err(_) => false,
298            Ok(index) => self.buffers[index].mapping.is_some(),
299        }
300    }
301
302    /// Consume the mapping manager and return the mapper it has been constructed from.
303    pub fn into_mapper(self) -> M {
304        self.mapper
305    }
306}
307
308#[cfg(test)]
309mod tests {
310    use std::fs::File;
311    use std::os::fd::AsFd;
312    use std::os::fd::BorrowedFd;
313    use std::os::fd::FromRawFd;
314
315    use crate::VirtioMediaHostMemoryMapper;
316
317    use super::CreateMappingError;
318    use super::MmapBuffer;
319    use super::MmapBufferMapping;
320    use super::MmapMappingManager;
321    use super::RegisterBufferError;
322    use super::RemoveMappingError;
323
324    struct DummyHostMemoryMapper;
325
326    impl VirtioMediaHostMemoryMapper for DummyHostMemoryMapper {
327        fn add_mapping(
328            &mut self,
329            _buffer: BorrowedFd,
330            _length: u64,
331            offset: u64,
332            _rw: bool,
333        ) -> Result<u64, i32> {
334            Ok(offset | 0x8000_0000)
335        }
336
337        fn remove_mapping(&mut self, _guest_addr: u64) -> Result<(), i32> {
338            Ok(())
339        }
340    }
341
342    #[test]
343    fn mmap_manager_register_by_offset() {
344        let mut mm = MmapMappingManager::from(DummyHostMemoryMapper);
345        assert_eq!(mm.buffers, vec![]);
346
347        assert_eq!(mm.register_buffer_by_offset(0x0, 0x1000), Ok(()));
348        assert_eq!(mm.buffers, vec![MmapBuffer::new(0x0, 0x1000)]);
349
350        assert_eq!(mm.register_buffer_by_offset(0x1000, 0x5000), Ok(()));
351        assert_eq!(
352            mm.buffers,
353            vec![
354                MmapBuffer::new(0x0, 0x1000),
355                MmapBuffer::new(0x1000, 0x5000),
356            ]
357        );
358
359        assert_eq!(mm.register_buffer_by_offset(0xa000, 0x1000), Ok(()));
360        assert_eq!(
361            mm.buffers,
362            vec![
363                MmapBuffer::new(0x0, 0x1000),
364                MmapBuffer::new(0x1000, 0x5000),
365                MmapBuffer::new(0xa000, 0x1000),
366            ]
367        );
368
369        assert_eq!(mm.register_buffer_by_offset(0x6000, 0x2000), Ok(()));
370        assert_eq!(
371            mm.buffers,
372            vec![
373                MmapBuffer::new(0x0, 0x1000),
374                MmapBuffer::new(0x1000, 0x5000),
375                MmapBuffer::new(0x6000, 0x2000),
376                MmapBuffer::new(0xa000, 0x1000),
377            ]
378        );
379
380        assert_eq!(
381            mm.register_buffer_by_offset(0x8000, 0x0),
382            Err(RegisterBufferError::EmptyBuffer)
383        );
384
385        assert_eq!(
386            mm.register_buffer_by_offset(0x8100, 0x1000),
387            Err(RegisterBufferError::UnalignedOffset)
388        );
389
390        assert_eq!(
391            mm.register_buffer_by_offset(0x0, 0x1000),
392            Err(RegisterBufferError::OffsetOccupied)
393        );
394
395        assert_eq!(
396            mm.register_buffer_by_offset(0x1000, 0x1000),
397            Err(RegisterBufferError::OffsetOccupied)
398        );
399
400        assert_eq!(mm.register_buffer_by_offset(0x2000, 0x1000), Ok(()));
401        assert_eq!(
402            mm.buffers,
403            vec![
404                MmapBuffer::new(0x0, 0x1000),
405                MmapBuffer::new(0x1000, 0x5000),
406                MmapBuffer::new(0x2000, 0x1000),
407                MmapBuffer::new(0x6000, 0x2000),
408                MmapBuffer::new(0xa000, 0x1000),
409            ]
410        );
411
412        assert_eq!(mm.register_buffer_by_offset(0x7000, 0x2000), Ok(()));
413        assert_eq!(
414            mm.buffers,
415            vec![
416                MmapBuffer::new(0x0, 0x1000),
417                MmapBuffer::new(0x1000, 0x5000),
418                MmapBuffer::new(0x2000, 0x1000),
419                MmapBuffer::new(0x6000, 0x2000),
420                MmapBuffer::new(0x7000, 0x2000),
421                MmapBuffer::new(0xa000, 0x1000),
422            ]
423        );
424
425        assert_eq!(mm.register_buffer_by_offset(0x8000, 0x2000), Ok(()));
426        assert_eq!(
427            mm.buffers,
428            vec![
429                MmapBuffer::new(0x0, 0x1000),
430                MmapBuffer::new(0x1000, 0x5000),
431                MmapBuffer::new(0x2000, 0x1000),
432                MmapBuffer::new(0x6000, 0x2000),
433                MmapBuffer::new(0x7000, 0x2000),
434                MmapBuffer::new(0x8000, 0x2000),
435                MmapBuffer::new(0xa000, 0x1000),
436            ]
437        );
438
439        assert_eq!(mm.register_buffer_by_offset(0xffff_f000, 0x1000), Ok(()));
440        assert_eq!(
441            mm.buffers,
442            vec![
443                MmapBuffer::new(0x0, 0x1000),
444                MmapBuffer::new(0x1000, 0x5000),
445                MmapBuffer::new(0x2000, 0x1000),
446                MmapBuffer::new(0x6000, 0x2000),
447                MmapBuffer::new(0x7000, 0x2000),
448                MmapBuffer::new(0x8000, 0x2000),
449                MmapBuffer::new(0xa000, 0x1000),
450                MmapBuffer::new(0xffff_f000, 0x1000),
451            ]
452        );
453
454        assert!(mm.unregister_buffer(0xffff_f000));
455        assert_eq!(
456            mm.buffers,
457            vec![
458                MmapBuffer::new(0x0, 0x1000),
459                MmapBuffer::new(0x1000, 0x5000),
460                MmapBuffer::new(0x2000, 0x1000),
461                MmapBuffer::new(0x6000, 0x2000),
462                MmapBuffer::new(0x7000, 0x2000),
463                MmapBuffer::new(0x8000, 0x2000),
464                MmapBuffer::new(0xa000, 0x1000),
465            ]
466        );
467
468        assert!(mm.unregister_buffer(0x6000));
469        assert_eq!(
470            mm.buffers,
471            vec![
472                MmapBuffer::new(0x0, 0x1000),
473                MmapBuffer::new(0x1000, 0x5000),
474                MmapBuffer::new(0x2000, 0x1000),
475                MmapBuffer::new(0x7000, 0x2000),
476                MmapBuffer::new(0x8000, 0x2000),
477                MmapBuffer::new(0xa000, 0x1000),
478            ]
479        );
480
481        assert!(!mm.unregister_buffer(0x6000));
482        assert_eq!(
483            mm.buffers,
484            vec![
485                MmapBuffer::new(0x0, 0x1000),
486                MmapBuffer::new(0x1000, 0x5000),
487                MmapBuffer::new(0x2000, 0x1000),
488                MmapBuffer::new(0x7000, 0x2000),
489                MmapBuffer::new(0x8000, 0x2000),
490                MmapBuffer::new(0xa000, 0x1000),
491            ]
492        );
493
494        assert!(!mm.unregister_buffer(0x8100));
495        assert_eq!(
496            mm.buffers,
497            vec![
498                MmapBuffer::new(0x0, 0x1000),
499                MmapBuffer::new(0x1000, 0x5000),
500                MmapBuffer::new(0x2000, 0x1000),
501                MmapBuffer::new(0x7000, 0x2000),
502                MmapBuffer::new(0x8000, 0x2000),
503                MmapBuffer::new(0xa000, 0x1000),
504            ]
505        );
506
507        assert!(mm.unregister_buffer(0x0));
508        assert_eq!(
509            mm.buffers,
510            vec![
511                MmapBuffer::new(0x1000, 0x5000),
512                MmapBuffer::new(0x2000, 0x1000),
513                MmapBuffer::new(0x7000, 0x2000),
514                MmapBuffer::new(0x8000, 0x2000),
515                MmapBuffer::new(0xa000, 0x1000),
516            ]
517        );
518
519        assert!(mm.unregister_buffer(0xa000));
520        assert_eq!(
521            mm.buffers,
522            vec![
523                MmapBuffer::new(0x1000, 0x5000),
524                MmapBuffer::new(0x2000, 0x1000),
525                MmapBuffer::new(0x7000, 0x2000),
526                MmapBuffer::new(0x8000, 0x2000),
527            ]
528        );
529
530        assert!(mm.unregister_buffer(0x1000));
531        assert_eq!(
532            mm.buffers,
533            vec![
534                MmapBuffer::new(0x2000, 0x1000),
535                MmapBuffer::new(0x7000, 0x2000),
536                MmapBuffer::new(0x8000, 0x2000),
537            ]
538        );
539
540        assert!(mm.unregister_buffer(0x8000));
541        assert_eq!(
542            mm.buffers,
543            vec![
544                MmapBuffer::new(0x2000, 0x1000),
545                MmapBuffer::new(0x7000, 0x2000),
546            ]
547        );
548
549        assert_eq!(
550            mm.buffers,
551            vec![
552                MmapBuffer::new(0x2000, 0x1000),
553                MmapBuffer::new(0x7000, 0x2000),
554            ]
555        );
556    }
557
558    #[test]
559    fn mmap_manager_register() {
560        let mut mm = MmapMappingManager::from(DummyHostMemoryMapper);
561
562        assert_eq!(mm.register_buffer(None, 0x1000), Ok(0x0));
563        assert_eq!(mm.buffers, vec![MmapBuffer::new(0x0, 0x1000)]);
564
565        assert_eq!(mm.register_buffer(None, 0x5000), Ok(0x1000));
566        assert_eq!(
567            mm.buffers,
568            vec![
569                MmapBuffer::new(0x0, 0x1000),
570                MmapBuffer::new(0x1000, 0x5000),
571            ]
572        );
573
574        assert_eq!(mm.register_buffer(None, 0xffff_a000), Ok(0x2000));
575        assert_eq!(
576            mm.buffers,
577            vec![
578                MmapBuffer::new(0x0, 0x1000),
579                MmapBuffer::new(0x1000, 0x5000),
580                MmapBuffer::new(0x2000, 0xffff_a000),
581            ]
582        );
583
584        assert!(mm.unregister_buffer(0x2000));
585        assert_eq!(
586            mm.buffers,
587            vec![
588                MmapBuffer::new(0x0, 0x1000),
589                MmapBuffer::new(0x1000, 0x5000),
590            ]
591        );
592
593        assert_eq!(mm.register_buffer(None, 0xffff_b000), Ok(0x2000));
594        assert_eq!(
595            mm.buffers,
596            vec![
597                MmapBuffer::new(0x0, 0x1000),
598                MmapBuffer::new(0x1000, 0x5000),
599                MmapBuffer::new(0x2000, 0xffff_b000),
600            ]
601        );
602    }
603
604    #[test]
605    fn mmap_manager_mapping() {
606        let mut mm = MmapMappingManager::from(DummyHostMemoryMapper);
607
608        assert_eq!(mm.register_buffer(None, 0x1000), Ok(0x0));
609        assert_eq!(mm.register_buffer(None, 0x5000), Ok(0x1000));
610        assert_eq!(
611            mm.buffers,
612            vec![
613                MmapBuffer::new(0x0, 0x1000),
614                MmapBuffer::new(0x1000, 0x5000),
615            ]
616        );
617
618        let file = unsafe { File::from_raw_fd(0) };
619
620        // Single mapping
621        assert_eq!(
622            mm.create_mapping(0x1000, file.as_fd(), false),
623            Ok((0x8000_1000, 0x5000))
624        );
625        assert_eq!(mm.remove_mapping(0x8000_1000), Ok(false));
626        assert_eq!(
627            mm.remove_mapping(0x8000_1000),
628            Err(RemoveMappingError::InvalidOffset)
629        );
630
631        // Multiple mappings
632        assert_eq!(
633            mm.create_mapping(0x1000, file.as_fd(), false),
634            Ok((0x8000_1000, 0x5000))
635        );
636        assert_eq!(
637            mm.create_mapping(0x1000, file.as_fd(), false),
638            Ok((0x8000_1000, 0x5000))
639        );
640        assert_eq!(mm.remove_mapping(0x8000_1000), Ok(true));
641        assert_eq!(mm.remove_mapping(0x8000_1000), Ok(false));
642        assert_eq!(
643            mm.remove_mapping(0x8000_1000),
644            Err(RemoveMappingError::InvalidOffset)
645        );
646
647        // Mapping at non-existing offset
648        assert_eq!(
649            mm.create_mapping(0x2000, file.as_fd(), false),
650            Err(CreateMappingError::InvalidOffset)
651        );
652
653        // Requesting same mapping with different access
654        assert_eq!(
655            mm.create_mapping(0x1000, file.as_fd(), false),
656            Ok((0x8000_1000, 0x5000))
657        );
658        assert_eq!(
659            mm.create_mapping(0x1000, file.as_fd(), true),
660            Err(CreateMappingError::NonMatchingPermissions)
661        );
662        assert_eq!(mm.remove_mapping(0x8000_1000), Ok(false));
663
664        // Mappings must survive a buffer's deregistration
665        assert_eq!(
666            mm.create_mapping(0x1000, file.as_fd(), false),
667            Ok((0x8000_1000, 0x5000))
668        );
669        assert!(mm.unregister_buffer(0x1000));
670        assert_eq!(
671            mm.buffers,
672            vec![
673                MmapBuffer::new(0x0, 0x1000),
674                MmapBuffer {
675                    offset: 0x1000,
676                    size: 0x5000,
677                    registered: false,
678                    mapping: Some(MmapBufferMapping {
679                        num_mappings: 1,
680                        guest_addr: 0x8000_1000,
681                        rw: false
682                    })
683                }
684            ]
685        );
686        // ... but un-registered buffers are removed alongside their last mapping.
687        assert_eq!(mm.remove_mapping(0x8000_1000), Ok(false));
688        assert_eq!(mm.buffers, vec![MmapBuffer::new(0x0, 0x1000),])
689    }
690}