66// found in the THIRD-PARTY file.
77
88use std:: fs:: File ;
9- use std:: io:: SeekFrom ;
9+ use std:: io:: { Read , Seek , SeekFrom , Write } ;
1010use std:: mem:: ManuallyDrop ;
11+ use std:: os:: fd:: AsFd ;
1112use std:: sync:: Arc ;
1213
1314use kvm_bindings:: { KVM_MEM_LOG_DIRTY_PAGES , kvm_userspace_memory_region} ;
@@ -20,7 +21,10 @@ pub use vm_memory::{
2021 Address , ByteValued , Bytes , FileOffset , GuestAddress , GuestMemory , GuestMemoryRegion ,
2122 GuestUsize , MemoryRegionAddress , MmapRegion , address,
2223} ;
23- use vm_memory:: { Error as VmMemoryError , GuestMemoryError , VolatileSlice , WriteVolatile } ;
24+ use vm_memory:: {
25+ Error as VmMemoryError , GuestMemoryError , ReadVolatile , VolatileMemoryError , VolatileSlice ,
26+ WriteVolatile ,
27+ } ;
2428use vmm_sys_util:: errno;
2529
2630use crate :: DirtyBitmap ;
@@ -53,6 +57,61 @@ pub enum MemoryError {
5357 OffsetTooLarge ,
5458}
5559
60+ /// Newtype that implements [`ReadVolatile`] and [`WriteVolatile`] if `T` implements `Read` or
61+ /// `Write` respectively, by reading/writing using a bounce buffer, and memcpy-ing into the
62+ /// [`VolatileSlice`].
63+ #[ derive( Debug ) ]
64+ pub struct Bounce < T > ( pub T , pub bool ) ;
65+
66+ // FIXME: replace AsFd with ReadVolatile once &File: ReadVolatile in vm-memory.
67+ impl < T : Read + AsFd > ReadVolatile for Bounce < T > {
68+ fn read_volatile < B : BitmapSlice > (
69+ & mut self ,
70+ buf : & mut VolatileSlice < B > ,
71+ ) -> Result < usize , VolatileMemoryError > {
72+ if self . 1 {
73+ let mut bbuf = vec ! [ 0 ; buf. len( ) ] ;
74+ let n = self
75+ . 0
76+ . read ( bbuf. as_mut_slice ( ) )
77+ . map_err ( VolatileMemoryError :: IOError ) ?;
78+ buf. copy_from ( & bbuf[ ..n] ) ;
79+ Ok ( n)
80+ } else {
81+ self . 0 . as_fd ( ) . read_volatile ( buf)
82+ }
83+ }
84+ }
85+
86+ impl < T : Write + AsFd > WriteVolatile for Bounce < T > {
87+ fn write_volatile < B : BitmapSlice > (
88+ & mut self ,
89+ buf : & VolatileSlice < B > ,
90+ ) -> Result < usize , VolatileMemoryError > {
91+ if self . 1 {
92+ let mut bbuf = vec ! [ 0 ; buf. len( ) ] ;
93+ buf. copy_to ( bbuf. as_mut_slice ( ) ) ;
94+ self . 0
95+ . write ( bbuf. as_slice ( ) )
96+ . map_err ( VolatileMemoryError :: IOError )
97+ } else {
98+ self . 0 . as_fd ( ) . write_volatile ( buf)
99+ }
100+ }
101+ }
102+
103+ impl < R : Read > Read for Bounce < R > {
104+ fn read ( & mut self , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
105+ self . 0 . read ( buf)
106+ }
107+ }
108+
109+ impl < S : Seek > Seek for Bounce < S > {
110+ fn seek ( & mut self , pos : SeekFrom ) -> std:: io:: Result < u64 > {
111+ self . 0 . seek ( pos)
112+ }
113+ }
114+
56115/// A memory region, described in terms of `kvm_userspace_memory_region`
57116#[ derive( Debug ) ]
58117pub struct KvmRegion {
@@ -475,6 +534,7 @@ mod tests {
475534 use std:: collections:: HashMap ;
476535 use std:: io:: { Read , Seek } ;
477536
537+ use itertools:: Itertools ;
478538 use vmm_sys_util:: tempfile:: TempFile ;
479539
480540 use super :: * ;
@@ -842,4 +902,35 @@ mod tests {
842902 seals. insert ( memfd:: FileSeal :: SealGrow ) ;
843903 memfd. add_seals ( & seals) . unwrap_err ( ) ;
844904 }
905+
906+ #[ test]
907+ fn test_bounce ( ) {
908+ let file_direct = TempFile :: new ( ) . unwrap ( ) ;
909+ let file_bounced = TempFile :: new ( ) . unwrap ( ) ;
910+
911+ let mut data = ( 0 ..=255 ) . collect_vec ( ) ;
912+
913+ Bounce ( file_direct. as_file ( ) , false )
914+ . write_all_volatile ( & VolatileSlice :: from ( data. as_mut_slice ( ) ) )
915+ . unwrap ( ) ;
916+ Bounce ( file_bounced. as_file ( ) , true )
917+ . write_all_volatile ( & VolatileSlice :: from ( data. as_mut_slice ( ) ) )
918+ . unwrap ( ) ;
919+
920+ let mut data_direct = vec ! [ 0u8 ; 256 ] ;
921+ let mut data_bounced = vec ! [ 0u8 ; 256 ] ;
922+
923+ file_direct. as_file ( ) . seek ( SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
924+ file_bounced. as_file ( ) . seek ( SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
925+
926+ Bounce ( file_direct. as_file ( ) , false )
927+ . read_exact_volatile ( & mut VolatileSlice :: from ( data_direct. as_mut_slice ( ) ) )
928+ . unwrap ( ) ;
929+ Bounce ( file_bounced. as_file ( ) , true )
930+ . read_exact_volatile ( & mut VolatileSlice :: from ( data_bounced. as_mut_slice ( ) ) )
931+ . unwrap ( ) ;
932+
933+ assert_eq ! ( data_direct, data_bounced) ;
934+ assert_eq ! ( data_direct, data) ;
935+ }
845936}
0 commit comments