1#![expect(unsafe_code)]
8#![expect(missing_docs)]
9
10pub mod ranges;
11
12use guestmem_core::ranges::PagedRange;
13use inspect::Inspect;
14use pal_event::Event;
15use sparse_mmap::AsMappableRef;
16use std::any::Any;
17use std::fmt::Debug;
18use std::future::Future;
19use std::io;
20use std::ops::Deref;
21use std::ops::DerefMut;
22use std::ops::Range;
23use std::ptr::NonNull;
24use std::sync::Arc;
25use std::sync::atomic::AtomicU8;
26use thiserror::Error;
27use zerocopy::FromBytes;
28use zerocopy::Immutable;
29use zerocopy::IntoBytes;
30use zerocopy::KnownLayout;
31
32pub use guestmem_core::AccessError;
33pub use guestmem_core::GuestMemoryBackingError;
34pub use guestmem_core::GuestMemoryError;
35pub use guestmem_core::GuestMemoryErrorKind;
36pub use guestmem_core::GuestMemoryOperation;
37pub use guestmem_core::InvalidGpn;
38pub use guestmem_core::Limit;
39pub use guestmem_core::MemoryRead;
40pub use guestmem_core::MemoryWrite;
41pub use guestmem_core::PAGE_SIZE;
42pub use guestmem_core::Page;
43pub use guestmem_core::PageFaultError;
44
45use guestmem_core::gpn_to_gpa;
46
47const PAGE_SIZE64: u64 = 4096;
48
49#[derive(Debug, Error)]
50#[error("no memory at address")]
51struct OutOfRange;
52
53#[derive(Debug, Error)]
54#[error("memory not lockable")]
55struct NotLockable;
56
57#[derive(Debug, Error)]
58#[error("no fallback for this operation")]
59struct NoFallback;
60
61#[derive(Debug, Error)]
62#[error("the specified page is not mapped")]
63struct NotMapped;
64
65#[derive(Debug, Error)]
66#[error("page inaccessible in bitmap")]
67struct BitmapFailure;
68
69pub trait LinearGuestMemory: GuestMemoryAccess {}
78
79unsafe impl GuestMemoryAccess for sparse_mmap::alloc::SharedMem {
81 fn mapping(&self) -> Option<NonNull<u8>> {
82 NonNull::new(self.as_ptr().cast_mut().cast())
83 }
84
85 fn max_address(&self) -> u64 {
86 self.len() as u64
87 }
88}
89
90impl LinearGuestMemory for sparse_mmap::alloc::SharedMem {}
91
92pub struct AlignedHeapMemory {
94 pages: Box<[AlignedPage]>,
95}
96
97impl Debug for AlignedHeapMemory {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 f.debug_struct("AlignedHeapMemory")
100 .field("len", &self.len())
101 .finish()
102 }
103}
104
105#[repr(C, align(4096))]
106struct AlignedPage([AtomicU8; PAGE_SIZE]);
107
108impl AlignedHeapMemory {
109 pub fn new(size: usize) -> Self {
111 #[expect(clippy::declare_interior_mutable_const)] const ZERO: AtomicU8 = AtomicU8::new(0);
113 #[expect(clippy::declare_interior_mutable_const)]
114 const ZERO_PAGE: AlignedPage = AlignedPage([ZERO; PAGE_SIZE]);
115 let mut pages = Vec::new();
116 pages.resize_with(size.div_ceil(PAGE_SIZE), || ZERO_PAGE);
117 Self {
118 pages: pages.into(),
119 }
120 }
121
122 pub fn len(&self) -> usize {
124 self.pages.len() * PAGE_SIZE
125 }
126
127 pub fn as_bytes(&mut self) -> &[u8] {
132 self.as_mut()
133 }
134
135 pub fn as_mut_bytes(&mut self) -> &mut [u8] {
137 self.as_mut()
138 }
139}
140
141impl Deref for AlignedHeapMemory {
142 type Target = [AtomicU8];
143
144 fn deref(&self) -> &Self::Target {
145 unsafe { std::slice::from_raw_parts(self.pages.as_ptr().cast(), self.len()) }
147 }
148}
149
150impl DerefMut for AlignedHeapMemory {
151 fn deref_mut(&mut self) -> &mut Self::Target {
152 unsafe { std::slice::from_raw_parts_mut(self.pages.as_mut_ptr().cast(), self.len()) }
154 }
155}
156
157impl AsRef<[AtomicU8]> for AlignedHeapMemory {
158 fn as_ref(&self) -> &[AtomicU8] {
159 self
160 }
161}
162
163impl AsMut<[AtomicU8]> for AlignedHeapMemory {
164 fn as_mut(&mut self) -> &mut [AtomicU8] {
165 self
166 }
167}
168
169impl AsMut<[u8]> for AlignedHeapMemory {
170 fn as_mut(&mut self) -> &mut [u8] {
171 unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr().cast(), self.len()) }
175 }
176}
177
178unsafe impl GuestMemoryAccess for AlignedHeapMemory {
181 fn mapping(&self) -> Option<NonNull<u8>> {
182 NonNull::new(self.pages.as_ptr().cast_mut().cast())
183 }
184
185 fn max_address(&self) -> u64 {
186 (self.pages.len() * PAGE_SIZE) as u64
187 }
188}
189
190impl LinearGuestMemory for AlignedHeapMemory {}
191
192pub struct ShareableRegion {
203 pub guest_address: u64,
205 pub size: u64,
207 pub file: Arc<sparse_mmap::Mappable>,
209 pub file_offset: u64,
211}
212
213pub type ShareableRegionError = Box<dyn std::error::Error + Send + Sync>;
215
216pub struct GuestMemorySharing {
230 inner: Box<dyn DynProvideShareableRegions>,
231}
232
233impl GuestMemorySharing {
234 pub fn new(inner: impl ProvideShareableRegions + 'static) -> Self {
237 Self {
238 inner: Box::new(inner),
239 }
240 }
241
242 pub async fn get_regions(&self) -> Result<Vec<ShareableRegion>, ShareableRegionError> {
244 self.inner.get_regions().await
245 }
246}
247
248pub trait ProvideShareableRegions: Send + Sync {
259 fn get_regions(
261 &self,
262 ) -> impl Future<Output = Result<Vec<ShareableRegion>, ShareableRegionError>> + Send + '_;
263}
264
265trait DynProvideShareableRegions: Send + Sync {
267 fn get_regions(
268 &self,
269 ) -> std::pin::Pin<
270 Box<dyn Future<Output = Result<Vec<ShareableRegion>, ShareableRegionError>> + Send + '_>,
271 >;
272}
273
274impl<T: ProvideShareableRegions> DynProvideShareableRegions for T {
275 fn get_regions(
276 &self,
277 ) -> std::pin::Pin<
278 Box<dyn Future<Output = Result<Vec<ShareableRegion>, ShareableRegionError>> + Send + '_>,
279 > {
280 Box::pin(ProvideShareableRegions::get_regions(self))
281 }
282}
283
284pub unsafe trait GuestMemoryAccess: 'static + Send + Sync {
300 fn mapping(&self) -> Option<NonNull<u8>>;
307
308 fn max_address(&self) -> u64;
311
312 #[cfg(feature = "bitmap")]
330 fn access_bitmap(&self) -> Option<BitmapInfo> {
331 None
332 }
333
334 fn subrange(
337 &self,
338 offset: u64,
339 len: u64,
340 allow_preemptive_locking: bool,
341 ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
342 let _ = (offset, len, allow_preemptive_locking);
343 Ok(None)
344 }
345
346 fn page_fault(
356 &self,
357 address: u64,
358 len: usize,
359 write: bool,
360 bitmap_failure: bool,
361 ) -> PageFaultAction {
362 let _ = (address, len, write);
363 let err = if bitmap_failure {
364 PageFaultError::other(BitmapFailure)
365 } else {
366 PageFaultError::other(NotMapped)
367 };
368 PageFaultAction::Fail(err)
369 }
370
371 unsafe fn read_fallback(
384 &self,
385 addr: u64,
386 dest: *mut u8,
387 len: usize,
388 ) -> Result<(), GuestMemoryBackingError> {
389 let _ = (dest, len);
390 Err(GuestMemoryBackingError::other(addr, NoFallback))
391 }
392
393 unsafe fn write_fallback(
403 &self,
404 addr: u64,
405 src: *const u8,
406 len: usize,
407 ) -> Result<(), GuestMemoryBackingError> {
408 let _ = (src, len);
409 Err(GuestMemoryBackingError::other(addr, NoFallback))
410 }
411
412 fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
417 let _ = (val, len);
418 Err(GuestMemoryBackingError::other(addr, NoFallback))
419 }
420
421 fn compare_exchange_fallback(
428 &self,
429 addr: u64,
430 current: &mut [u8],
431 new: &[u8],
432 ) -> Result<bool, GuestMemoryBackingError> {
433 let _ = (current, new);
434 Err(GuestMemoryBackingError::other(addr, NoFallback))
435 }
436
437 fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
443 let _ = (address, len);
444 Ok(())
445 }
446
447 fn base_iova(&self) -> Option<u64> {
453 None
454 }
455
456 fn lock_gpns(&self, gpns: &[u64]) -> Result<bool, GuestMemoryBackingError> {
461 let _ = gpns;
462 Ok(false)
463 }
464
465 fn unlock_gpns(&self, gpns: &[u64]) {
471 let _ = gpns;
472 }
473
474 fn sharing(&self) -> Option<GuestMemorySharing> {
480 None
481 }
482
483 fn supports_locking(&self) -> bool {
497 self.mapping().is_some()
498 }
499}
500
501trait DynGuestMemoryAccess: 'static + Send + Sync + Any {
502 fn subrange(
503 &self,
504 offset: u64,
505 len: u64,
506 allow_preemptive_locking: bool,
507 ) -> Result<Option<GuestMemory>, GuestMemoryBackingError>;
508
509 fn page_fault(
510 &self,
511 address: u64,
512 len: usize,
513 write: bool,
514 bitmap_failure: bool,
515 ) -> PageFaultAction;
516
517 unsafe fn read_fallback(
520 &self,
521 addr: u64,
522 dest: *mut u8,
523 len: usize,
524 ) -> Result<(), GuestMemoryBackingError>;
525
526 unsafe fn write_fallback(
529 &self,
530 addr: u64,
531 src: *const u8,
532 len: usize,
533 ) -> Result<(), GuestMemoryBackingError>;
534
535 fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError>;
536
537 fn compare_exchange_fallback(
538 &self,
539 addr: u64,
540 current: &mut [u8],
541 new: &[u8],
542 ) -> Result<bool, GuestMemoryBackingError>;
543
544 fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError>;
545
546 fn lock_gpns(&self, gpns: &[u64]) -> Result<bool, GuestMemoryBackingError>;
547
548 fn unlock_gpns(&self, gpns: &[u64]);
549
550 fn sharing(&self) -> Option<GuestMemorySharing>;
551}
552
553impl<T: GuestMemoryAccess> DynGuestMemoryAccess for T {
554 fn subrange(
555 &self,
556 offset: u64,
557 len: u64,
558 allow_preemptive_locking: bool,
559 ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
560 self.subrange(offset, len, allow_preemptive_locking)
561 }
562
563 fn page_fault(
564 &self,
565 address: u64,
566 len: usize,
567 write: bool,
568 bitmap_failure: bool,
569 ) -> PageFaultAction {
570 self.page_fault(address, len, write, bitmap_failure)
571 }
572
573 unsafe fn read_fallback(
574 &self,
575 addr: u64,
576 dest: *mut u8,
577 len: usize,
578 ) -> Result<(), GuestMemoryBackingError> {
579 unsafe { self.read_fallback(addr, dest, len) }
581 }
582
583 unsafe fn write_fallback(
584 &self,
585 addr: u64,
586 src: *const u8,
587 len: usize,
588 ) -> Result<(), GuestMemoryBackingError> {
589 unsafe { self.write_fallback(addr, src, len) }
591 }
592
593 fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
594 self.fill_fallback(addr, val, len)
595 }
596
597 fn compare_exchange_fallback(
598 &self,
599 addr: u64,
600 current: &mut [u8],
601 new: &[u8],
602 ) -> Result<bool, GuestMemoryBackingError> {
603 self.compare_exchange_fallback(addr, current, new)
604 }
605
606 fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
607 self.expose_va(address, len)
608 }
609
610 fn lock_gpns(&self, gpns: &[u64]) -> Result<bool, GuestMemoryBackingError> {
611 self.lock_gpns(gpns)
612 }
613
614 fn unlock_gpns(&self, gpns: &[u64]) {
615 self.unlock_gpns(gpns)
616 }
617
618 fn sharing(&self) -> Option<GuestMemorySharing> {
619 self.sharing()
620 }
621}
622
623pub enum PageFaultAction {
626 Fail(PageFaultError),
628 Retry,
630 Fallback,
632}
633
634#[cfg(feature = "bitmap")]
636pub struct BitmapInfo {
637 pub read_bitmap: NonNull<u8>,
639 pub write_bitmap: NonNull<u8>,
641 pub bit_offset: u8,
646}
647
648unsafe impl<T: GuestMemoryAccess> GuestMemoryAccess for Arc<T> {
650 fn mapping(&self) -> Option<NonNull<u8>> {
651 self.as_ref().mapping()
652 }
653
654 fn max_address(&self) -> u64 {
655 self.as_ref().max_address()
656 }
657
658 #[cfg(feature = "bitmap")]
659 fn access_bitmap(&self) -> Option<BitmapInfo> {
660 self.as_ref().access_bitmap()
661 }
662
663 fn subrange(
664 &self,
665 offset: u64,
666 len: u64,
667 allow_preemptive_locking: bool,
668 ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
669 self.as_ref()
670 .subrange(offset, len, allow_preemptive_locking)
671 }
672
673 fn page_fault(
674 &self,
675 addr: u64,
676 len: usize,
677 write: bool,
678 bitmap_failure: bool,
679 ) -> PageFaultAction {
680 self.as_ref().page_fault(addr, len, write, bitmap_failure)
681 }
682
683 unsafe fn read_fallback(
684 &self,
685 addr: u64,
686 dest: *mut u8,
687 len: usize,
688 ) -> Result<(), GuestMemoryBackingError> {
689 unsafe { self.as_ref().read_fallback(addr, dest, len) }
691 }
692
693 unsafe fn write_fallback(
694 &self,
695 addr: u64,
696 src: *const u8,
697 len: usize,
698 ) -> Result<(), GuestMemoryBackingError> {
699 unsafe { self.as_ref().write_fallback(addr, src, len) }
701 }
702
703 fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
704 self.as_ref().fill_fallback(addr, val, len)
705 }
706
707 fn compare_exchange_fallback(
708 &self,
709 addr: u64,
710 current: &mut [u8],
711 new: &[u8],
712 ) -> Result<bool, GuestMemoryBackingError> {
713 self.as_ref().compare_exchange_fallback(addr, current, new)
714 }
715
716 fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
717 self.as_ref().expose_va(address, len)
718 }
719
720 fn base_iova(&self) -> Option<u64> {
721 self.as_ref().base_iova()
722 }
723
724 fn sharing(&self) -> Option<GuestMemorySharing> {
725 self.as_ref().sharing()
726 }
727}
728
729unsafe impl GuestMemoryAccess for sparse_mmap::SparseMapping {
731 fn mapping(&self) -> Option<NonNull<u8>> {
732 NonNull::new(self.as_ptr().cast())
733 }
734
735 fn max_address(&self) -> u64 {
736 self.len() as u64
737 }
738}
739
740struct GuestMemoryAccessRange {
742 base: Arc<GuestMemoryInner>,
743 offset: u64,
744 len: u64,
745 region: usize,
746}
747
748impl GuestMemoryAccessRange {
749 fn adjust_range(&self, address: u64, len: u64) -> Result<u64, GuestMemoryBackingError> {
750 if address <= self.len && len <= self.len - address {
751 Ok(self.offset + address)
752 } else {
753 Err(GuestMemoryBackingError::new(
754 GuestMemoryErrorKind::OutOfRange,
755 address,
756 OutOfRange,
757 ))
758 }
759 }
760}
761
762unsafe impl GuestMemoryAccess for GuestMemoryAccessRange {
764 fn mapping(&self) -> Option<NonNull<u8>> {
765 let region = &self.base.regions[self.region];
766 region.mapping.and_then(|mapping| {
767 let offset = self.offset & self.base.region_def.region_mask;
768 assert!(region.len >= offset + self.len);
770 NonNull::new(unsafe { mapping.0.as_ptr().add(offset as usize) })
773 })
774 }
775
776 fn max_address(&self) -> u64 {
777 self.len
778 }
779
780 #[cfg(feature = "bitmap")]
781 fn access_bitmap(&self) -> Option<BitmapInfo> {
782 let region = &self.base.regions[self.region];
783 region.bitmaps.map(|bitmaps| {
784 let offset = self.offset & self.base.region_def.region_mask;
785 let bit_offset = region.bitmap_start as u64 + offset / PAGE_SIZE64;
786 let [read_bitmap, write_bitmap] = bitmaps.map(|SendPtrU8(ptr)| {
787 NonNull::new(unsafe { ptr.as_ptr().add((bit_offset / 8) as usize) }).unwrap()
790 });
791 let bitmap_start = (bit_offset % 8) as u8;
792 BitmapInfo {
793 read_bitmap,
794 write_bitmap,
795 bit_offset: bitmap_start,
796 }
797 })
798 }
799
800 fn subrange(
801 &self,
802 offset: u64,
803 len: u64,
804 _allow_preemptive_locking: bool,
805 ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
806 let address = self.adjust_range(offset, len)?;
807 Ok(Some(GuestMemory::new(
808 self.base.debug_name.clone(),
809 GuestMemoryAccessRange {
810 base: self.base.clone(),
811 offset: address,
812 len,
813 region: self.region,
814 },
815 )))
816 }
817
818 fn page_fault(
819 &self,
820 address: u64,
821 len: usize,
822 write: bool,
823 bitmap_failure: bool,
824 ) -> PageFaultAction {
825 let address = self
826 .adjust_range(address, len as u64)
827 .expect("the caller should have validated the range was in the mapping");
828
829 self.base
830 .imp
831 .page_fault(address, len, write, bitmap_failure)
832 }
833
834 unsafe fn write_fallback(
835 &self,
836 address: u64,
837 src: *const u8,
838 len: usize,
839 ) -> Result<(), GuestMemoryBackingError> {
840 let address = self.adjust_range(address, len as u64)?;
841 unsafe { self.base.imp.write_fallback(address, src, len) }
843 }
844
845 fn fill_fallback(
846 &self,
847 address: u64,
848 val: u8,
849 len: usize,
850 ) -> Result<(), GuestMemoryBackingError> {
851 let address = self.adjust_range(address, len as u64)?;
852 self.base.imp.fill_fallback(address, val, len)
853 }
854
855 fn compare_exchange_fallback(
856 &self,
857 addr: u64,
858 current: &mut [u8],
859 new: &[u8],
860 ) -> Result<bool, GuestMemoryBackingError> {
861 let address = self.adjust_range(addr, new.len() as u64)?;
862 self.base
863 .imp
864 .compare_exchange_fallback(address, current, new)
865 }
866
867 unsafe fn read_fallback(
868 &self,
869 address: u64,
870 dest: *mut u8,
871 len: usize,
872 ) -> Result<(), GuestMemoryBackingError> {
873 let address = self.adjust_range(address, len as u64)?;
874 unsafe { self.base.imp.read_fallback(address, dest, len) }
876 }
877
878 fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
879 let address = self.adjust_range(address, len)?;
880 self.base.imp.expose_va(address, len)
881 }
882
883 fn base_iova(&self) -> Option<u64> {
884 let region = &self.base.regions[self.region];
885 Some(region.base_iova? + (self.offset & self.base.region_def.region_mask))
886 }
887}
888
889fn create_memory_subrange(
892 base: Arc<GuestMemoryInner>,
893 offset: u64,
894 len: u64,
895 _allow_preemptive_locking: bool,
896) -> Result<GuestMemory, GuestMemoryBackingError> {
897 let (_, _, region) = base.region(offset, len)?;
898 Ok(GuestMemory::new(
899 base.debug_name.clone(),
900 GuestMemoryAccessRange {
901 base,
902 offset,
903 len,
904 region,
905 },
906 ))
907}
908
909struct MultiRegionGuestMemoryAccess<T> {
910 imps: Vec<Option<T>>,
911 region_def: RegionDefinition,
912}
913
914impl<T> MultiRegionGuestMemoryAccess<T> {
915 fn region(&self, gpa: u64, len: u64) -> Result<(&T, u64), GuestMemoryBackingError> {
916 let (i, offset) = self.region_def.region(gpa, len)?;
917 let imp = self.imps[i].as_ref().ok_or(GuestMemoryBackingError::new(
918 GuestMemoryErrorKind::OutOfRange,
919 gpa,
920 OutOfRange,
921 ))?;
922 Ok((imp, offset))
923 }
924}
925
926impl<T: GuestMemoryAccess> DynGuestMemoryAccess for MultiRegionGuestMemoryAccess<T> {
928 fn subrange(
929 &self,
930 offset: u64,
931 len: u64,
932 allow_preemptive_locking: bool,
933 ) -> Result<Option<GuestMemory>, GuestMemoryBackingError> {
934 let (region, offset_in_region) = self.region(offset, len)?;
935 region.subrange(offset_in_region, len, allow_preemptive_locking)
936 }
937
938 unsafe fn read_fallback(
939 &self,
940 addr: u64,
941 dest: *mut u8,
942 len: usize,
943 ) -> Result<(), GuestMemoryBackingError> {
944 let (region, offset_in_region) = self.region(addr, len as u64)?;
945 unsafe { region.read_fallback(offset_in_region, dest, len) }
947 }
948
949 unsafe fn write_fallback(
950 &self,
951 addr: u64,
952 src: *const u8,
953 len: usize,
954 ) -> Result<(), GuestMemoryBackingError> {
955 let (region, offset_in_region) = self.region(addr, len as u64)?;
956 unsafe { region.write_fallback(offset_in_region, src, len) }
958 }
959
960 fn fill_fallback(&self, addr: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
961 let (region, offset_in_region) = self.region(addr, len as u64)?;
962 region.fill_fallback(offset_in_region, val, len)
963 }
964
965 fn compare_exchange_fallback(
966 &self,
967 addr: u64,
968 current: &mut [u8],
969 new: &[u8],
970 ) -> Result<bool, GuestMemoryBackingError> {
971 let (region, offset_in_region) = self.region(addr, new.len() as u64)?;
972 region.compare_exchange_fallback(offset_in_region, current, new)
973 }
974
975 fn expose_va(&self, address: u64, len: u64) -> Result<(), GuestMemoryBackingError> {
976 let (region, offset_in_region) = self.region(address, len)?;
977 region.expose_va(offset_in_region, len)
978 }
979
980 fn page_fault(
981 &self,
982 address: u64,
983 len: usize,
984 write: bool,
985 bitmap_failure: bool,
986 ) -> PageFaultAction {
987 match self.region(address, len as u64) {
988 Ok((region, offset_in_region)) => {
989 region.page_fault(offset_in_region, len, write, bitmap_failure)
990 }
991 Err(err) => PageFaultAction::Fail(PageFaultError {
992 kind: err.kind,
993 err: err.err,
994 }),
995 }
996 }
997
998 fn lock_gpns(&self, gpns: &[u64]) -> Result<bool, GuestMemoryBackingError> {
999 let mut ret = false;
1000 for gpn in gpns {
1001 let (region, offset_in_region) = self.region(gpn * PAGE_SIZE64, PAGE_SIZE64)?;
1002 ret |= region.lock_gpns(&[offset_in_region / PAGE_SIZE64])?;
1003 }
1004 Ok(ret)
1005 }
1006
1007 fn unlock_gpns(&self, gpns: &[u64]) {
1008 for gpn in gpns {
1009 let (region, offset_in_region) = self.region(gpn * PAGE_SIZE64, PAGE_SIZE64).unwrap();
1010 region.unlock_gpns(&[offset_in_region / PAGE_SIZE64]);
1011 }
1012 }
1013
1014 fn sharing(&self) -> Option<GuestMemorySharing> {
1015 None
1021 }
1022}
1023
1024#[derive(Debug, Clone, Inspect)]
1030#[inspect(skip)]
1031pub struct GuestMemory {
1032 inner: Arc<GuestMemoryInner>,
1033}
1034
1035struct GuestMemoryInner<T: ?Sized = dyn DynGuestMemoryAccess> {
1036 region_def: RegionDefinition,
1037 regions: Vec<MemoryRegion>,
1038 debug_name: Arc<str>,
1039 allocated: bool,
1040 supports_locking: bool,
1043 imp: T,
1044}
1045
1046impl<T: ?Sized> Debug for GuestMemoryInner<T> {
1047 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1048 f.debug_struct("GuestMemoryInner")
1049 .field("region_def", &self.region_def)
1050 .field("regions", &self.regions)
1051 .finish()
1052 }
1053}
1054
1055#[derive(Debug, Copy, Clone, Default)]
1056struct MemoryRegion {
1057 mapping: Option<SendPtrU8>,
1058 #[cfg(feature = "bitmap")]
1059 bitmaps: Option<[SendPtrU8; 2]>,
1060 #[cfg(feature = "bitmap")]
1061 bitmap_start: u8,
1062 len: u64,
1063 base_iova: Option<u64>,
1064}
1065
1066#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1071pub enum AccessType {
1072 Read = 0,
1074 Write = 1,
1076}
1077
1078#[derive(Debug, Copy, Clone)]
1090struct SendPtrU8(NonNull<u8>);
1091
1092unsafe impl Send for SendPtrU8 {}
1094unsafe impl Sync for SendPtrU8 {}
1096
1097impl MemoryRegion {
1098 fn new(imp: &impl GuestMemoryAccess) -> Self {
1099 #[cfg(feature = "bitmap")]
1100 let (bitmaps, bitmap_start) = {
1101 let bitmap_info = imp.access_bitmap();
1102 let bitmaps = bitmap_info
1103 .as_ref()
1104 .map(|bm| [SendPtrU8(bm.read_bitmap), SendPtrU8(bm.write_bitmap)]);
1105 let bitmap_start = bitmap_info.map_or(0, |bi| bi.bit_offset);
1106 (bitmaps, bitmap_start)
1107 };
1108 Self {
1109 mapping: imp.mapping().map(SendPtrU8),
1110 #[cfg(feature = "bitmap")]
1111 bitmaps,
1112 #[cfg(feature = "bitmap")]
1113 bitmap_start,
1114 len: imp.max_address(),
1115 base_iova: imp.base_iova(),
1116 }
1117 }
1118
1119 unsafe fn check_access(
1125 &self,
1126 access_type: AccessType,
1127 offset: u64,
1128 len: u64,
1129 ) -> Result<(), u64> {
1130 debug_assert!(self.len >= offset + len);
1131 #[cfg(not(feature = "bitmap"))]
1132 let _ = access_type;
1133
1134 #[cfg(feature = "bitmap")]
1135 if len == 0 {
1136 return Ok(());
1137 } else if let Some(bitmaps) = &self.bitmaps {
1138 let SendPtrU8(bitmap) = bitmaps[access_type as usize];
1139 let start = offset / PAGE_SIZE64;
1140 let end = (offset + len - 1) / PAGE_SIZE64;
1141 for gpn in start..=end {
1144 let bit_offset = self.bitmap_start as u64 + gpn;
1145 let bit = unsafe {
1148 (*bitmap
1149 .as_ptr()
1150 .cast_const()
1151 .cast::<AtomicU8>()
1152 .add(bit_offset as usize / 8))
1153 .load(std::sync::atomic::Ordering::Relaxed)
1154 & (1 << (bit_offset % 8))
1155 };
1156 if bit == 0 {
1157 return Err((gpn * PAGE_SIZE64).saturating_sub(offset));
1158 }
1159 }
1160 }
1161
1162 Ok(())
1163 }
1164}
1165
1166impl Default for GuestMemory {
1168 fn default() -> Self {
1169 Self::empty()
1170 }
1171}
1172
1173struct Empty;
1174
1175unsafe impl GuestMemoryAccess for Empty {
1177 fn mapping(&self) -> Option<NonNull<u8>> {
1178 None
1179 }
1180
1181 fn max_address(&self) -> u64 {
1182 0
1183 }
1184
1185 fn supports_locking(&self) -> bool {
1186 true
1189 }
1190}
1191
1192#[derive(Debug, Error)]
1193pub enum MultiRegionError {
1194 #[error("region size {0:#x} is not a power of 2")]
1195 NotPowerOfTwo(u64),
1196 #[error("region size {0:#x} is smaller than a page")]
1197 RegionSizeTooSmall(u64),
1198 #[error(
1199 "too many regions ({region_count}) for region size {region_size:#x}; max is {max_region_count}"
1200 )]
1201 TooManyRegions {
1202 region_count: usize,
1203 max_region_count: usize,
1204 region_size: u64,
1205 },
1206 #[error("backing size {backing_size:#x} is too large for region size {region_size:#x}")]
1207 BackingTooLarge { backing_size: u64, region_size: u64 },
1208}
1209
1210#[cfg(feature = "bitmap")]
1218pub fn rcu() -> minircu::RcuDomain {
1219 minircu::global()
1221}
1222
1223impl GuestMemory {
1224 pub fn new(debug_name: impl Into<Arc<str>>, imp: impl GuestMemoryAccess) -> Self {
1229 if imp.mapping().is_some() && !cfg!(miri) {
1234 trycopy::initialize_try_copy();
1235 }
1236 Self::new_inner(debug_name.into(), imp, false)
1237 }
1238
1239 fn new_inner(debug_name: Arc<str>, imp: impl GuestMemoryAccess, allocated: bool) -> Self {
1240 let regions = vec![MemoryRegion::new(&imp)];
1241 let supports_locking = imp.supports_locking();
1242 Self {
1243 inner: Arc::new(GuestMemoryInner {
1244 imp,
1245 debug_name,
1246 region_def: RegionDefinition {
1247 invalid_mask: 1 << 63,
1248 region_mask: !0 >> 1,
1249 region_bits: 63, },
1251 regions,
1252 allocated,
1253 supports_locking,
1254 }),
1255 }
1256 }
1257
1258 pub fn new_multi_region(
1274 debug_name: impl Into<Arc<str>>,
1275 region_size: u64,
1276 mut imps: Vec<Option<impl GuestMemoryAccess>>,
1277 ) -> Result<Self, MultiRegionError> {
1278 trycopy::initialize_try_copy();
1280
1281 if !region_size.is_power_of_two() {
1282 return Err(MultiRegionError::NotPowerOfTwo(region_size));
1283 }
1284 if region_size < PAGE_SIZE64 {
1285 return Err(MultiRegionError::RegionSizeTooSmall(region_size));
1286 }
1287 let region_bits = region_size.trailing_zeros();
1288
1289 let max_region_count = 1 << (63 - region_bits);
1290
1291 let region_count = imps.len().next_power_of_two();
1292 if region_count > max_region_count {
1293 return Err(MultiRegionError::TooManyRegions {
1294 region_count,
1295 max_region_count,
1296 region_size,
1297 });
1298 }
1299
1300 let valid_bits = region_bits + region_count.trailing_zeros();
1301 assert!(valid_bits < 64);
1302 let invalid_mask = !0 << valid_bits;
1303
1304 let mut regions = vec![MemoryRegion::default(); region_count];
1305 for (imp, region) in imps.iter().zip(&mut regions) {
1306 let Some(imp) = imp else { continue };
1307 let backing_size = imp.max_address();
1308 if backing_size > region_size {
1309 return Err(MultiRegionError::BackingTooLarge {
1310 backing_size,
1311 region_size,
1312 });
1313 }
1314 *region = MemoryRegion::new(imp);
1315 }
1316
1317 let region_def = RegionDefinition {
1318 invalid_mask,
1319 region_mask: region_size - 1,
1320 region_bits,
1321 };
1322
1323 imps.resize_with(region_count, || None);
1324 let supports_locking = imps
1326 .iter()
1327 .flatten()
1328 .all(GuestMemoryAccess::supports_locking);
1329 let imp = MultiRegionGuestMemoryAccess { imps, region_def };
1330
1331 let inner = GuestMemoryInner {
1332 debug_name: debug_name.into(),
1333 region_def,
1334 regions,
1335 imp,
1336 allocated: false,
1337 supports_locking,
1338 };
1339
1340 Ok(Self {
1341 inner: Arc::new(inner),
1342 })
1343 }
1344
1345 pub fn allocate(size: usize) -> Self {
1355 Self::new_inner("heap".into(), AlignedHeapMemory::new(size), true)
1356 }
1357
1358 pub fn into_inner_buf(self) -> Result<AlignedHeapMemory, Self> {
1364 if !self.inner.allocated {
1365 return Err(self);
1366 }
1367 let inner = unsafe {
1370 Arc::<GuestMemoryInner<AlignedHeapMemory>>::from_raw(Arc::into_raw(self.inner).cast())
1371 };
1372 let inner = Arc::try_unwrap(inner).map_err(|inner| Self { inner })?;
1373 Ok(inner.imp)
1374 }
1375
1376 pub fn inner_buf(&self) -> Option<&[AtomicU8]> {
1379 if !self.inner.allocated {
1380 return None;
1381 }
1382 let inner = unsafe { &*core::ptr::from_ref(&self.inner.imp).cast::<AlignedHeapMemory>() };
1385 Some(inner)
1386 }
1387
1388 pub fn inner_buf_mut(&mut self) -> Option<&mut [u8]> {
1392 if !self.inner.allocated {
1393 return None;
1394 }
1395 let inner = Arc::get_mut(&mut self.inner)?;
1396 let imp = unsafe { &mut *core::ptr::from_mut(&mut inner.imp).cast::<AlignedHeapMemory>() };
1399 Some(imp.as_mut())
1400 }
1401
1402 pub fn empty() -> Self {
1404 GuestMemory::new("empty", Empty)
1405 }
1406
1407 fn wrap_err(
1408 &self,
1409 gpa_len: Option<(u64, u64)>,
1410 op: GuestMemoryOperation,
1411 err: GuestMemoryBackingError,
1412 ) -> GuestMemoryError {
1413 let range = gpa_len.map(|(gpa, len)| gpa..gpa.wrapping_add(len));
1414 GuestMemoryError::new(&self.inner.debug_name, range, op, err)
1415 }
1416
1417 fn with_op<T>(
1418 &self,
1419 gpa_len: Option<(u64, u64)>,
1420 op: GuestMemoryOperation,
1421 f: impl FnOnce() -> Result<T, GuestMemoryBackingError>,
1422 ) -> Result<T, GuestMemoryError> {
1423 f().map_err(|err| self.wrap_err(gpa_len, op, err))
1424 }
1425
1426 pub fn subrange(
1431 &self,
1432 offset: u64,
1433 len: u64,
1434 allow_preemptive_locking: bool,
1435 ) -> Result<GuestMemory, GuestMemoryError> {
1436 self.with_op(Some((offset, len)), GuestMemoryOperation::Subrange, || {
1437 if let Some(guest_memory) =
1438 self.inner
1439 .imp
1440 .subrange(offset, len, allow_preemptive_locking)?
1441 {
1442 Ok(guest_memory)
1443 } else {
1444 create_memory_subrange(self.inner.clone(), offset, len, allow_preemptive_locking)
1445 }
1446 })
1447 }
1448
1449 pub fn lockable_subrange(
1451 &self,
1452 offset: u64,
1453 len: u64,
1454 ) -> Result<GuestMemory, GuestMemoryError> {
1455 self.subrange(offset, len, true)
1457 }
1458
1459 pub fn full_mapping(&self) -> Option<(*mut u8, usize)> {
1464 if let [region] = self.inner.regions.as_slice() {
1465 #[cfg(feature = "bitmap")]
1466 if region.bitmaps.is_some() {
1467 return None;
1468 }
1469 region
1470 .mapping
1471 .map(|SendPtrU8(ptr)| (ptr.as_ptr(), region.len as usize))
1472 } else {
1473 None
1474 }
1475 }
1476
1477 pub fn iova(&self, gpa: u64) -> Option<u64> {
1480 let (region, offset, _) = self.inner.region(gpa, 1).ok()?;
1481 Some(region.base_iova? + offset)
1482 }
1483
1484 pub fn sharing(&self) -> Option<GuestMemorySharing> {
1487 self.inner.imp.sharing()
1488 }
1489
1490 pub fn supports_locking(&self) -> bool {
1499 self.inner.supports_locking
1500 }
1501
1502 fn mapping_range(
1507 &self,
1508 access_type: AccessType,
1509 gpa: u64,
1510 len: usize,
1511 ) -> Result<Option<*mut u8>, GuestMemoryBackingError> {
1512 let (region, offset, _) = self.inner.region(gpa, len as u64)?;
1513 if let Some(SendPtrU8(ptr)) = region.mapping {
1514 loop {
1515 let fault_offset = unsafe {
1517 match region.check_access(access_type, offset, len as u64) {
1518 Ok(()) => return Ok(Some(ptr.as_ptr().add(offset as usize))),
1519 Err(n) => n,
1520 }
1521 };
1522
1523 match self.inner.imp.page_fault(
1525 gpa + fault_offset,
1526 len - fault_offset as usize,
1527 access_type == AccessType::Write,
1528 true,
1529 ) {
1530 PageFaultAction::Fail(err) => {
1531 return Err(GuestMemoryBackingError::new(
1532 err.kind,
1533 gpa + fault_offset,
1534 err.err,
1535 ));
1536 }
1537 PageFaultAction::Retry => {}
1538 PageFaultAction::Fallback => break,
1539 }
1540 }
1541 }
1542 Ok(None)
1543 }
1544
1545 fn run_on_mapping<T, P>(
1552 &self,
1553 access_type: AccessType,
1554 gpa: u64,
1555 len: usize,
1556 mut param: P,
1557 mut f: impl FnMut(&mut P, *mut u8) -> Result<T, trycopy::MemoryError>,
1558 fallback: impl FnOnce(&mut P) -> Result<T, GuestMemoryBackingError>,
1559 ) -> Result<T, GuestMemoryBackingError> {
1560 let op = || {
1561 let Some(mapping) = self.mapping_range(access_type, gpa, len)? else {
1562 return fallback(&mut param);
1563 };
1564
1565 loop {
1567 match f(&mut param, mapping) {
1568 Ok(t) => return Ok(t),
1569 Err(fault) => {
1570 match self.inner.imp.page_fault(
1571 gpa + fault.offset() as u64,
1572 len - fault.offset(),
1573 access_type == AccessType::Write,
1574 false,
1575 ) {
1576 PageFaultAction::Fail(err) => {
1577 return Err(GuestMemoryBackingError::new(
1578 err.kind,
1579 gpa + fault.offset() as u64,
1580 err.err,
1581 ));
1582 }
1583 PageFaultAction::Retry => {}
1584 PageFaultAction::Fallback => return fallback(&mut param),
1585 }
1586 }
1587 }
1588 }
1589 };
1590 #[cfg(feature = "bitmap")]
1594 return rcu().run(op);
1595 #[cfg(not(feature = "bitmap"))]
1596 op()
1597 }
1598
1599 unsafe fn write_ptr(
1603 &self,
1604 gpa: u64,
1605 src: *const u8,
1606 len: usize,
1607 ) -> Result<(), GuestMemoryBackingError> {
1608 if len == 0 {
1609 return Ok(());
1610 }
1611 self.run_on_mapping(
1612 AccessType::Write,
1613 gpa,
1614 len,
1615 (),
1616 |(), dest| {
1617 unsafe { trycopy::try_copy(src, dest, len) }
1621 },
1622 |()| {
1623 unsafe { self.inner.imp.write_fallback(gpa, src, len) }
1626 },
1627 )
1628 }
1629
1630 pub fn write_at(&self, gpa: u64, src: &[u8]) -> Result<(), GuestMemoryError> {
1632 self.with_op(
1633 Some((gpa, src.len() as u64)),
1634 GuestMemoryOperation::Write,
1635 || self.write_at_inner(gpa, src),
1636 )
1637 }
1638
1639 fn write_at_inner(&self, gpa: u64, src: &[u8]) -> Result<(), GuestMemoryBackingError> {
1640 unsafe { self.write_ptr(gpa, src.as_ptr(), src.len()) }
1642 }
1643
1644 pub fn write_from_atomic(&self, gpa: u64, src: &[AtomicU8]) -> Result<(), GuestMemoryError> {
1646 self.with_op(
1647 Some((gpa, src.len() as u64)),
1648 GuestMemoryOperation::Write,
1649 || {
1650 unsafe { self.write_ptr(gpa, src.as_ptr().cast(), src.len()) }
1652 },
1653 )
1654 }
1655
1656 pub fn fill_at(&self, gpa: u64, val: u8, len: usize) -> Result<(), GuestMemoryError> {
1658 self.with_op(Some((gpa, len as u64)), GuestMemoryOperation::Fill, || {
1659 self.fill_at_inner(gpa, val, len)
1660 })
1661 }
1662
1663 fn fill_at_inner(&self, gpa: u64, val: u8, len: usize) -> Result<(), GuestMemoryBackingError> {
1664 if len == 0 {
1665 return Ok(());
1666 }
1667 self.run_on_mapping(
1668 AccessType::Write,
1669 gpa,
1670 len,
1671 (),
1672 |(), dest| {
1673 unsafe { trycopy::try_write_bytes(dest, val, len) }
1675 },
1676 |()| self.inner.imp.fill_fallback(gpa, val, len),
1677 )
1678 }
1679
1680 unsafe fn read_ptr(
1685 &self,
1686 gpa: u64,
1687 dest: *mut u8,
1688 len: usize,
1689 ) -> Result<(), GuestMemoryBackingError> {
1690 if len == 0 {
1691 return Ok(());
1692 }
1693 self.run_on_mapping(
1694 AccessType::Read,
1695 gpa,
1696 len,
1697 (),
1698 |(), src| {
1699 unsafe { trycopy::try_copy(src, dest, len) }
1703 },
1704 |()| {
1705 unsafe { self.inner.imp.read_fallback(gpa, dest, len) }
1708 },
1709 )
1710 }
1711
1712 fn read_at_inner(&self, gpa: u64, dest: &mut [u8]) -> Result<(), GuestMemoryBackingError> {
1713 unsafe { self.read_ptr(gpa, dest.as_mut_ptr(), dest.len()) }
1715 }
1716
1717 pub fn read_at(&self, gpa: u64, dest: &mut [u8]) -> Result<(), GuestMemoryError> {
1719 self.with_op(
1720 Some((gpa, dest.len() as u64)),
1721 GuestMemoryOperation::Read,
1722 || self.read_at_inner(gpa, dest),
1723 )
1724 }
1725
1726 pub fn read_to_atomic(&self, gpa: u64, dest: &[AtomicU8]) -> Result<(), GuestMemoryError> {
1728 self.with_op(
1729 Some((gpa, dest.len() as u64)),
1730 GuestMemoryOperation::Read,
1731 || unsafe { self.read_ptr(gpa, dest.as_ptr() as *mut u8, dest.len()) },
1733 )
1734 }
1735
1736 pub fn write_plain<T: IntoBytes + Immutable + KnownLayout>(
1748 &self,
1749 gpa: u64,
1750 b: &T,
1751 ) -> Result<(), GuestMemoryError> {
1752 let len = size_of::<T>();
1754 self.with_op(Some((gpa, len as u64)), GuestMemoryOperation::Write, || {
1755 self.run_on_mapping(
1756 AccessType::Write,
1757 gpa,
1758 len,
1759 (),
1760 |(), dest| {
1761 unsafe { trycopy::try_write_volatile(dest.cast(), b) }
1764 },
1765 |()| {
1766 unsafe {
1768 self.inner
1769 .imp
1770 .write_fallback(gpa, b.as_bytes().as_ptr(), len)
1771 }
1772 },
1773 )
1774 })
1775 }
1776
1777 pub fn compare_exchange<T: IntoBytes + FromBytes + Immutable + KnownLayout + Copy>(
1779 &self,
1780 gpa: u64,
1781 current: T,
1782 new: T,
1783 ) -> Result<Result<T, T>, GuestMemoryError> {
1784 const {
1785 assert!(matches!(size_of::<T>(), 1 | 2 | 4 | 8));
1786 assert!(align_of::<T>() >= size_of::<T>());
1787 };
1788 let len = size_of_val(&new);
1789 self.with_op(
1790 Some((gpa, len as u64)),
1791 GuestMemoryOperation::CompareExchange,
1792 || {
1793 self.run_on_mapping(
1795 AccessType::Write,
1796 gpa,
1797 len,
1798 (),
1799 |(), dest| {
1800 unsafe { trycopy::try_compare_exchange(dest.cast(), current, new) }
1803 },
1804 |()| {
1805 let mut current = current;
1806 let success = self.inner.imp.compare_exchange_fallback(
1807 gpa,
1808 current.as_mut_bytes(),
1809 new.as_bytes(),
1810 )?;
1811
1812 Ok(if success { Ok(new) } else { Err(current) })
1813 },
1814 )
1815 },
1816 )
1817 }
1818
1819 pub fn read_plain<T: FromBytes + Immutable + KnownLayout>(
1831 &self,
1832 gpa: u64,
1833 ) -> Result<T, GuestMemoryError> {
1834 self.with_op(
1835 Some((gpa, size_of::<T>() as u64)),
1836 GuestMemoryOperation::Read,
1837 || self.read_plain_inner(gpa),
1838 )
1839 }
1840
1841 fn read_plain_inner<T: FromBytes + Immutable + KnownLayout>(
1842 &self,
1843 gpa: u64,
1844 ) -> Result<T, GuestMemoryBackingError> {
1845 let len = size_of::<T>();
1846 self.run_on_mapping(
1847 AccessType::Read,
1848 gpa,
1849 len,
1850 (),
1851 |(), src| {
1852 unsafe { trycopy::try_read_volatile(src.cast::<T>()) }
1855 },
1856 |()| {
1857 let mut obj = std::mem::MaybeUninit::<T>::zeroed();
1858 unsafe {
1861 self.inner
1862 .imp
1863 .read_fallback(gpa, obj.as_mut_ptr().cast(), len)?;
1864 }
1865 Ok(unsafe { obj.assume_init() })
1867 },
1868 )
1869 }
1870
1871 fn probe_page_for_lock(
1872 &self,
1873 access: AccessType,
1874 with_kernel_access: bool,
1875 gpa: u64,
1876 ) -> Result<*const AtomicU8, GuestMemoryBackingError> {
1877 let (region, offset, _) = self.inner.region(gpa, 1)?;
1878 let Some(SendPtrU8(ptr)) = region.mapping else {
1879 return Err(GuestMemoryBackingError::other(gpa, NotLockable));
1880 };
1881 if with_kernel_access {
1883 self.inner.imp.expose_va(gpa, 1)?;
1884 }
1885 match access {
1895 AccessType::Read => {
1896 self.read_plain_inner::<u8>(gpa)?;
1897 }
1898 AccessType::Write => {
1899 self.probe_mapped_page_writable(gpa)?;
1900 }
1901 }
1902 let page = unsafe { ptr.as_ptr().add(offset as usize) };
1905 Ok(page.cast())
1906 }
1907
1908 fn probe_mapped_page_writable(&self, gpa: u64) -> Result<(), GuestMemoryBackingError> {
1928 self.run_on_mapping(
1929 AccessType::Write,
1930 gpa,
1931 1,
1932 (),
1933 |(), dest| {
1934 unsafe { trycopy::try_compare_exchange::<u8>(dest.cast(), 0, 0).map(|_| ()) }
1936 },
1937 |()| {
1938 Err(GuestMemoryBackingError::other(gpa, NotLockable))
1944 },
1945 )
1946 }
1947
1948 pub fn lock_gpns(
1957 &self,
1958 access: AccessType,
1959 with_kernel_access: bool,
1960 gpns: &[u64],
1961 ) -> Result<LockedPages, GuestMemoryError> {
1962 self.with_op(None, GuestMemoryOperation::Lock, || {
1963 if !self.inner.supports_locking {
1964 let gpa = gpns.first().map_or(0, |&gpn| gpn.wrapping_mul(PAGE_SIZE64));
1965 return Err(GuestMemoryBackingError::other(gpa, NotLockable));
1966 }
1967 let mut pages = Vec::with_capacity(gpns.len());
1968 for &gpn in gpns {
1969 let gpa = gpn_to_gpa(gpn).map_err(GuestMemoryBackingError::gpn)?;
1970 let page = self.probe_page_for_lock(access, with_kernel_access, gpa)?;
1971 pages.push(PagePtr(page));
1972 }
1973 let store_gpns = self.inner.imp.lock_gpns(gpns)?;
1974 Ok(LockedPages {
1975 pages: pages.into_boxed_slice(),
1976 gpns: store_gpns.then(|| gpns.to_vec().into_boxed_slice()),
1977 mem: self.inner.clone(),
1978 })
1979 })
1980 }
1981
1982 pub fn probe_gpns(&self, gpns: &[u64]) -> Result<(), GuestMemoryError> {
1983 self.with_op(None, GuestMemoryOperation::Probe, || {
1984 for &gpn in gpns {
1985 self.read_plain_inner::<u8>(
1986 gpn_to_gpa(gpn).map_err(GuestMemoryBackingError::gpn)?,
1987 )?;
1988 }
1989 Ok(())
1990 })
1991 }
1992
1993 pub fn probe_gpa_readable(&self, gpa: u64) -> Result<(), GuestMemoryErrorKind> {
1995 let mut b = [0];
1996 self.read_at_inner(gpa, &mut b).map_err(|err| err.kind)
1997 }
1998
1999 pub fn probe_gpa_writable(&self, gpa: u64) -> Result<(), GuestMemoryErrorKind> {
2001 let _ = self
2002 .compare_exchange(gpa, 0u8, 0)
2003 .map_err(|err| err.kind())?;
2004 Ok(())
2005 }
2006
2007 fn dangerous_access_pre_locked_memory(&self, gpa: u64, len: usize) -> &[AtomicU8] {
2019 let addr = self
2020 .mapping_range(AccessType::Write, gpa, len)
2021 .unwrap()
2022 .unwrap();
2023 unsafe { std::slice::from_raw_parts(addr.cast(), len) }
2028 }
2029
2030 fn op_range<F: FnMut(u64, Range<usize>) -> Result<(), GuestMemoryBackingError>>(
2031 &self,
2032 op: GuestMemoryOperation,
2033 range: &PagedRange<'_>,
2034 mut f: F,
2035 ) -> Result<(), GuestMemoryError> {
2036 self.with_op(None, op, || {
2037 let gpns = range.gpns();
2038 let offset = range.offset();
2039
2040 let mut byte_index = 0;
2044 let mut len = range.len();
2045 let mut page = 0;
2046 if !offset.is_multiple_of(PAGE_SIZE) {
2047 let head_len = std::cmp::min(len, PAGE_SIZE - (offset % PAGE_SIZE));
2048 let addr = gpn_to_gpa(gpns[page]).map_err(GuestMemoryBackingError::gpn)?
2049 + offset as u64 % PAGE_SIZE64;
2050 f(addr, byte_index..byte_index + head_len)?;
2051 byte_index += head_len;
2052 len -= head_len;
2053 page += 1;
2054 }
2055 while len >= PAGE_SIZE {
2056 f(
2057 gpn_to_gpa(gpns[page]).map_err(GuestMemoryBackingError::gpn)?,
2058 byte_index..byte_index + PAGE_SIZE,
2059 )?;
2060 byte_index += PAGE_SIZE;
2061 len -= PAGE_SIZE;
2062 page += 1;
2063 }
2064 if len > 0 {
2065 f(
2066 gpn_to_gpa(gpns[page]).map_err(GuestMemoryBackingError::gpn)?,
2067 byte_index..byte_index + len,
2068 )?;
2069 }
2070
2071 Ok(())
2072 })
2073 }
2074
2075 pub fn write_range(&self, range: &PagedRange<'_>, data: &[u8]) -> Result<(), GuestMemoryError> {
2076 assert!(data.len() == range.len());
2077 self.op_range(GuestMemoryOperation::Write, range, move |addr, r| {
2078 self.write_at_inner(addr, &data[r])
2079 })
2080 }
2081
2082 pub fn fill_range(&self, range: &PagedRange<'_>, val: u8) -> Result<(), GuestMemoryError> {
2083 self.op_range(GuestMemoryOperation::Fill, range, move |addr, r| {
2084 self.fill_at_inner(addr, val, r.len())
2085 })
2086 }
2087
2088 pub fn zero_range(&self, range: &PagedRange<'_>) -> Result<(), GuestMemoryError> {
2089 self.op_range(GuestMemoryOperation::Fill, range, move |addr, r| {
2090 self.fill_at_inner(addr, 0, r.len())
2091 })
2092 }
2093
2094 pub fn read_range(
2095 &self,
2096 range: &PagedRange<'_>,
2097 data: &mut [u8],
2098 ) -> Result<(), GuestMemoryError> {
2099 assert!(data.len() == range.len());
2100 self.op_range(GuestMemoryOperation::Read, range, move |addr, r| {
2101 self.read_at_inner(addr, &mut data[r])
2102 })
2103 }
2104
2105 pub fn write_range_from_atomic(
2106 &self,
2107 range: &PagedRange<'_>,
2108 data: &[AtomicU8],
2109 ) -> Result<(), GuestMemoryError> {
2110 assert!(data.len() == range.len());
2111 self.op_range(GuestMemoryOperation::Write, range, move |addr, r| {
2112 let src = &data[r];
2113 unsafe { self.write_ptr(addr, src.as_ptr().cast(), src.len()) }
2115 })
2116 }
2117
2118 pub fn read_range_to_atomic(
2119 &self,
2120 range: &PagedRange<'_>,
2121 data: &[AtomicU8],
2122 ) -> Result<(), GuestMemoryError> {
2123 assert!(data.len() == range.len());
2124 self.op_range(GuestMemoryOperation::Read, range, move |addr, r| {
2125 let dest = &data[r];
2126 unsafe { self.read_ptr(addr, dest.as_ptr().cast_mut().cast(), dest.len()) }
2128 })
2129 }
2130
2131 pub fn lock_range<'a, T: LockedRange<'a>>(
2143 &'a self,
2144 access: AccessType,
2145 paged_range: PagedRange<'_>,
2146 mut locked_range: T,
2147 ) -> Result<LockedRangeImpl<'a, T>, GuestMemoryError> {
2148 self.with_op(None, GuestMemoryOperation::Lock, || {
2149 let gpns = paged_range.gpns();
2150 if !self.inner.supports_locking {
2151 let gpa = gpns.first().map_or(0, |&gpn| gpn.wrapping_mul(PAGE_SIZE64));
2152 return Err(GuestMemoryBackingError::other(gpa, NotLockable));
2153 }
2154 for &gpn in gpns {
2155 let gpa = gpn_to_gpa(gpn).map_err(GuestMemoryBackingError::gpn)?;
2156 self.probe_page_for_lock(access, true, gpa)?;
2157 }
2158 for range in paged_range.ranges() {
2159 let range = range.map_err(GuestMemoryBackingError::gpn)?;
2160 locked_range.push_sub_range(
2161 self.dangerous_access_pre_locked_memory(range.start, range.len() as usize),
2162 );
2163 }
2164 let store_gpns = self.inner.imp.lock_gpns(paged_range.gpns())?;
2165 Ok(LockedRangeImpl {
2166 mem: &self.inner,
2167 gpns: store_gpns.then(|| paged_range.gpns().to_vec().into_boxed_slice()),
2168 inner: locked_range,
2169 })
2170 })
2171 }
2172}
2173
2174#[derive(Debug, Copy, Clone, Default)]
2175struct RegionDefinition {
2176 invalid_mask: u64,
2177 region_mask: u64,
2178 region_bits: u32,
2179}
2180
2181impl RegionDefinition {
2182 fn region(&self, gpa: u64, len: u64) -> Result<(usize, u64), GuestMemoryBackingError> {
2183 if (gpa | len) & self.invalid_mask != 0 {
2184 return Err(GuestMemoryBackingError::new(
2185 GuestMemoryErrorKind::OutOfRange,
2186 gpa,
2187 OutOfRange,
2188 ));
2189 }
2190 let offset = gpa & self.region_mask;
2191 if offset.wrapping_add(len) & !self.region_mask != 0 {
2192 return Err(GuestMemoryBackingError::new(
2193 GuestMemoryErrorKind::OutOfRange,
2194 gpa,
2195 OutOfRange,
2196 ));
2197 }
2198 let index = (gpa >> self.region_bits) as usize;
2199 Ok((index, offset))
2200 }
2201}
2202
2203impl GuestMemoryInner {
2204 fn region(
2205 &self,
2206 gpa: u64,
2207 len: u64,
2208 ) -> Result<(&MemoryRegion, u64, usize), GuestMemoryBackingError> {
2209 let (index, offset) = self.region_def.region(gpa, len)?;
2210 let region = &self.regions[index];
2211 if offset + len > region.len {
2212 return Err(GuestMemoryBackingError::new(
2213 GuestMemoryErrorKind::OutOfRange,
2214 gpa,
2215 OutOfRange,
2216 ));
2217 }
2218 Ok((&self.regions[index], offset, index))
2219 }
2220}
2221
2222pub struct LockedPages {
2223 pages: Box<[PagePtr]>,
2224 gpns: Option<Box<[u64]>>,
2225 mem: Arc<GuestMemoryInner>,
2227}
2228
2229impl Drop for LockedPages {
2230 fn drop(&mut self) {
2231 if let Some(gpns) = &self.gpns {
2232 self.mem.imp.unlock_gpns(gpns);
2233 }
2234 }
2235}
2236
2237impl Debug for LockedPages {
2238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2239 f.debug_struct("LockedPages")
2240 .field("page_count", &self.pages.len())
2241 .finish()
2242 }
2243}
2244
2245#[derive(Copy, Clone, Debug)]
2246struct PagePtr(#[expect(dead_code)] *const AtomicU8);
2248
2249unsafe impl Send for PagePtr {}
2252unsafe impl Sync for PagePtr {}
2254
2255impl LockedPages {
2256 #[inline]
2257 pub fn pages(&self) -> &[&Page] {
2258 unsafe { std::slice::from_raw_parts(self.pages.as_ptr().cast::<&Page>(), self.pages.len()) }
2262 }
2263}
2264
2265impl<'a> AsRef<[&'a Page]> for &'a LockedPages {
2266 fn as_ref(&self) -> &[&'a Page] {
2267 self.pages()
2268 }
2269}
2270
2271pub trait LockedRange<'a> {
2276 fn push_sub_range(&mut self, sub_range: &'a [AtomicU8]);
2278}
2279
2280pub struct LockedRangeImpl<'a, T: LockedRange<'a>> {
2281 mem: &'a GuestMemoryInner,
2282 gpns: Option<Box<[u64]>>,
2283 inner: T,
2284}
2285
2286impl<'a, T: LockedRange<'a>> LockedRangeImpl<'a, T> {
2287 pub fn get(&self) -> &T {
2288 &self.inner
2289 }
2290
2291 pub fn get_mut(&mut self) -> &mut T {
2292 &mut self.inner
2293 }
2294}
2295
2296impl<'a, T: LockedRange<'a>> Drop for LockedRangeImpl<'a, T> {
2297 fn drop(&mut self) {
2298 if let Some(gpns) = &self.gpns {
2299 self.mem.imp.unlock_gpns(gpns);
2300 }
2301 }
2302}
2303
2304pub trait MappableGuestMemory: Send + Sync {
2307 fn map_to_guest(&mut self, gpa: u64, writable: bool) -> io::Result<()>;
2313
2314 fn unmap_from_guest(&mut self);
2315}
2316
2317pub trait MappedMemoryRegion: Send + Sync {
2320 fn map(
2324 &self,
2325 offset: usize,
2326 section: &dyn AsMappableRef,
2327 file_offset: u64,
2328 len: usize,
2329 writable: bool,
2330 ) -> io::Result<()>;
2331
2332 fn unmap(&self, offset: usize, len: usize) -> io::Result<()>;
2334}
2335
2336pub trait MemoryMapper: Send + Sync {
2338 fn new_region(
2343 &self,
2344 len: usize,
2345 debug_name: String,
2346 ) -> io::Result<(Box<dyn MappableGuestMemory>, Arc<dyn MappedMemoryRegion>)>;
2347}
2348
2349pub trait DoorbellRegistration: Send + Sync {
2351 fn register_doorbell(
2353 &self,
2354 guest_address: u64,
2355 value: Option<u64>,
2356 length: Option<u32>,
2357 event: &Event,
2358 ) -> io::Result<Box<dyn Send + Sync>>;
2359}
2360
2361pub trait MapRom: Send + Sync {
2363 fn map_rom(&self, gpa: u64, offset: u64, len: u64) -> io::Result<Box<dyn UnmapRom>>;
2367
2368 fn len(&self) -> u64;
2370}
2371
2372pub trait UnmapRom: Send + Sync {
2374 fn unmap_rom(self);
2376}
2377
2378#[cfg(test)]
2379#[expect(clippy::undocumented_unsafe_blocks)]
2380mod tests {
2381 use crate::GuestMemory;
2382 use crate::PAGE_SIZE64;
2383 use crate::PageFaultAction;
2384 use crate::PageFaultError;
2385
2386 use sparse_mmap::SparseMapping;
2387 use std::ptr::NonNull;
2388 use std::sync::Arc;
2389 use thiserror::Error;
2390
2391 pub struct GuestMemoryMapping {
2396 mapping: SparseMapping,
2397 #[cfg(feature = "bitmap")]
2398 bitmap: Option<Vec<u8>>,
2399 }
2400
2401 unsafe impl crate::GuestMemoryAccess for GuestMemoryMapping {
2402 fn mapping(&self) -> Option<NonNull<u8>> {
2403 NonNull::new(self.mapping.as_ptr().cast())
2404 }
2405
2406 fn max_address(&self) -> u64 {
2407 self.mapping.len() as u64
2408 }
2409
2410 #[cfg(feature = "bitmap")]
2411 fn access_bitmap(&self) -> Option<crate::BitmapInfo> {
2412 self.bitmap.as_ref().map(|bm| crate::BitmapInfo {
2413 read_bitmap: NonNull::new(bm.as_ptr().cast_mut()).unwrap(),
2414 write_bitmap: NonNull::new(bm.as_ptr().cast_mut()).unwrap(),
2415 bit_offset: 0,
2416 })
2417 }
2418 }
2419
2420 const PAGE_SIZE: usize = 4096;
2421 const SIZE_1MB: usize = 1048576;
2422
2423 fn create_test_mapping() -> GuestMemoryMapping {
2430 let mapping = SparseMapping::new(SIZE_1MB * 4).unwrap();
2431 mapping.alloc(0, SIZE_1MB).unwrap();
2432 mapping.alloc(2 * SIZE_1MB, SIZE_1MB).unwrap();
2433 mapping
2434 .alloc(3 * SIZE_1MB + PAGE_SIZE, SIZE_1MB - PAGE_SIZE)
2435 .unwrap();
2436
2437 GuestMemoryMapping {
2438 mapping,
2439 #[cfg(feature = "bitmap")]
2440 bitmap: None,
2441 }
2442 }
2443
2444 #[test]
2445 fn test_basic_read_write() {
2446 let mapping = create_test_mapping();
2447 let gm = GuestMemory::new("test", mapping);
2448
2449 let addr = 0;
2451 let result = gm.read_plain::<u8>(addr);
2452 assert_eq!(result.unwrap(), 0);
2453
2454 let write_buffer = [1, 2, 3, 4, 5];
2456 let mut read_buffer = [0; 5];
2457 gm.write_at(0, &write_buffer).unwrap();
2458 gm.read_at(0, &mut read_buffer).unwrap();
2459 assert_eq!(write_buffer, read_buffer);
2460 assert_eq!(gm.read_plain::<u8>(0).unwrap(), 1);
2461 assert_eq!(gm.read_plain::<u8>(1).unwrap(), 2);
2462 assert_eq!(gm.read_plain::<u8>(2).unwrap(), 3);
2463 assert_eq!(gm.read_plain::<u8>(3).unwrap(), 4);
2464 assert_eq!(gm.read_plain::<u8>(4).unwrap(), 5);
2465
2466 let addr = 2 * SIZE_1MB as u64;
2468 let write_buffer: Vec<u8> = (0..PAGE_SIZE).map(|x| x as u8).collect();
2469 let mut read_buffer: Vec<u8> = (0..PAGE_SIZE).map(|_| 0).collect();
2470 gm.write_at(addr, write_buffer.as_slice()).unwrap();
2471 gm.read_at(addr, read_buffer.as_mut_slice()).unwrap();
2472 assert_eq!(write_buffer, read_buffer);
2473
2474 let write_buffer: Vec<u8> = (0..SIZE_1MB).map(|x| x as u8).collect();
2476 let mut read_buffer: Vec<u8> = (0..SIZE_1MB).map(|_| 0).collect();
2477 gm.write_at(addr, write_buffer.as_slice()).unwrap();
2478 gm.read_at(addr, read_buffer.as_mut_slice()).unwrap();
2479 assert_eq!(write_buffer, read_buffer);
2480
2481 let addr = SIZE_1MB as u64;
2483 let result = gm.read_plain::<u8>(addr);
2484 assert!(result.is_err());
2485 }
2486
2487 #[test]
2488 fn test_multi() {
2489 let len = SIZE_1MB * 4;
2490 let mapping = SparseMapping::new(len).unwrap();
2491 mapping.alloc(0, len).unwrap();
2492 let mapping = Arc::new(GuestMemoryMapping {
2493 mapping,
2494 #[cfg(feature = "bitmap")]
2495 bitmap: None,
2496 });
2497 let region_len = 1 << 30;
2498 let gm = GuestMemory::new_multi_region(
2499 "test",
2500 region_len,
2501 vec![Some(mapping.clone()), None, Some(mapping.clone())],
2502 )
2503 .unwrap();
2504
2505 let mut b = [0];
2506 let len = len as u64;
2507 gm.read_at(0, &mut b).unwrap();
2508 gm.read_at(len, &mut b).unwrap_err();
2509 gm.read_at(region_len, &mut b).unwrap_err();
2510 gm.read_at(2 * region_len, &mut b).unwrap();
2511 gm.read_at(2 * region_len + len, &mut b).unwrap_err();
2512 gm.read_at(3 * region_len, &mut b).unwrap_err();
2513 }
2514
2515 #[cfg(feature = "bitmap")]
2516 #[test]
2517 fn test_bitmap() {
2518 let len = PAGE_SIZE * 4;
2519 let mapping = SparseMapping::new(len).unwrap();
2520 mapping.alloc(0, len).unwrap();
2521 let bitmap = vec![0b0101];
2522 let mapping = Arc::new(GuestMemoryMapping {
2523 mapping,
2524 bitmap: Some(bitmap),
2525 });
2526 let gm = GuestMemory::new("test", mapping);
2527
2528 gm.read_plain::<[u8; 1]>(0).unwrap();
2529 gm.read_plain::<[u8; 1]>(PAGE_SIZE64 - 1).unwrap();
2530 gm.read_plain::<[u8; 2]>(PAGE_SIZE64 - 1).unwrap_err();
2531 gm.read_plain::<[u8; 1]>(PAGE_SIZE64).unwrap_err();
2532 gm.read_plain::<[u8; 1]>(PAGE_SIZE64 * 2).unwrap();
2533 gm.read_plain::<[u8; PAGE_SIZE * 2]>(0).unwrap_err();
2534 }
2535
2536 struct FaultingMapping {
2537 mapping: SparseMapping,
2538 }
2539
2540 #[derive(Debug, Error)]
2541 #[error("fault")]
2542 struct Fault;
2543
2544 unsafe impl crate::GuestMemoryAccess for FaultingMapping {
2545 fn mapping(&self) -> Option<NonNull<u8>> {
2546 NonNull::new(self.mapping.as_ptr().cast())
2547 }
2548
2549 fn max_address(&self) -> u64 {
2550 self.mapping.len() as u64
2551 }
2552
2553 fn page_fault(
2554 &self,
2555 address: u64,
2556 _len: usize,
2557 write: bool,
2558 bitmap_failure: bool,
2559 ) -> PageFaultAction {
2560 assert!(!bitmap_failure);
2561 let qlen = self.mapping.len() as u64 / 4;
2562 if address < qlen || address >= 3 * qlen {
2563 return PageFaultAction::Fail(PageFaultError::other(Fault));
2564 }
2565 let page_address = (address as usize) & !(PAGE_SIZE - 1);
2566 if address >= 2 * qlen {
2567 if write {
2568 return PageFaultAction::Fail(PageFaultError::other(Fault));
2569 }
2570 self.mapping.map_zero(page_address, PAGE_SIZE).unwrap();
2571 } else {
2572 self.mapping.alloc(page_address, PAGE_SIZE).unwrap();
2573 }
2574 PageFaultAction::Retry
2575 }
2576 }
2577
2578 impl FaultingMapping {
2579 fn new(len: usize) -> Self {
2580 let mapping = SparseMapping::new(len).unwrap();
2581 FaultingMapping { mapping }
2582 }
2583 }
2584
2585 #[test]
2586 fn test_fault() {
2587 let len = PAGE_SIZE * 4;
2588 let mapping = FaultingMapping::new(len);
2589 let gm = GuestMemory::new("test", mapping);
2590
2591 gm.write_plain::<u8>(0, &0).unwrap_err();
2592 gm.read_plain::<u8>(PAGE_SIZE64 - 1).unwrap_err();
2593 gm.read_plain::<u8>(PAGE_SIZE64).unwrap();
2594 gm.write_plain::<u8>(PAGE_SIZE64, &0).unwrap();
2595 gm.write_plain::<u16>(PAGE_SIZE64 * 3 - 1, &0).unwrap_err();
2596 gm.read_plain::<u16>(PAGE_SIZE64 * 3 - 1).unwrap_err();
2597 gm.read_plain::<u8>(PAGE_SIZE64 * 3 - 1).unwrap();
2598 gm.write_plain::<u8>(PAGE_SIZE64 * 3 - 1, &0).unwrap_err();
2599 }
2600
2601 #[cfg(feature = "bitmap")]
2602 #[test]
2603 fn test_zero_length_access_at_offset_zero() {
2604 let len = PAGE_SIZE * 4;
2610 let mapping = SparseMapping::new(len).unwrap();
2611 mapping.alloc(0, len).unwrap();
2612 let bitmap = vec![0b0000]; let mapping = Arc::new(GuestMemoryMapping {
2614 mapping,
2615 bitmap: Some(bitmap),
2616 });
2617 let gm = GuestMemory::new("test", mapping);
2618
2619 gm.read_plain::<()>(0).unwrap();
2623 gm.write_plain::<()>(0, &()).unwrap();
2624 }
2625
2626 #[test]
2627 fn test_allocated() {
2628 let mut gm = GuestMemory::allocate(0x10000);
2629 let pattern = [0x42; 0x10000];
2630 gm.write_at(0, &pattern).unwrap();
2631 assert_eq!(gm.inner_buf_mut().unwrap(), &pattern);
2632 gm.inner_buf().unwrap();
2633 let gm2 = gm.clone();
2634 assert!(gm.inner_buf_mut().is_none());
2635 gm.inner_buf().unwrap();
2636 let mut gm = gm.into_inner_buf().unwrap_err();
2637 drop(gm2);
2638 assert_eq!(gm.inner_buf_mut().unwrap(), &pattern);
2639 gm.into_inner_buf().unwrap();
2640 }
2641
2642 struct ToggleLockMapping {
2646 mapping: SparseMapping,
2647 lockable: bool,
2648 }
2649
2650 impl ToggleLockMapping {
2651 fn new(size: usize, lockable: bool) -> Self {
2652 let mapping = SparseMapping::new(size).unwrap();
2653 mapping.alloc(0, size).unwrap();
2654 Self { mapping, lockable }
2655 }
2656 }
2657
2658 unsafe impl crate::GuestMemoryAccess for ToggleLockMapping {
2660 fn mapping(&self) -> Option<NonNull<u8>> {
2661 NonNull::new(self.mapping.as_ptr().cast())
2662 }
2663
2664 fn max_address(&self) -> u64 {
2665 self.mapping.len() as u64
2666 }
2667
2668 fn supports_locking(&self) -> bool {
2669 self.lockable
2670 }
2671 }
2672
2673 #[test]
2674 fn test_supports_locking() {
2675 let gm = GuestMemory::allocate(0x10000);
2677 assert!(gm.supports_locking());
2678
2679 let gm = GuestMemory::new("nolock", ToggleLockMapping::new(SIZE_1MB, false));
2684 assert!(!gm.supports_locking());
2685 assert!(gm.lock_gpns(crate::AccessType::Write, false, &[0]).is_err());
2686
2687 let gm = GuestMemory::new_multi_region(
2690 "multi-lockable",
2691 SIZE_1MB as u64,
2692 vec![
2693 Some(ToggleLockMapping::new(SIZE_1MB / 2, true)),
2694 Some(ToggleLockMapping::new(SIZE_1MB / 2, true)),
2695 ],
2696 )
2697 .unwrap();
2698 assert!(gm.supports_locking());
2699
2700 let gm = GuestMemory::new_multi_region(
2701 "multi-mixed",
2702 SIZE_1MB as u64,
2703 vec![
2704 Some(ToggleLockMapping::new(SIZE_1MB / 2, true)),
2705 Some(ToggleLockMapping::new(SIZE_1MB / 2, false)),
2706 ],
2707 )
2708 .unwrap();
2709 assert!(!gm.supports_locking());
2710 }
2711}