@@ -24,6 +24,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue;
2424use hyperlight_common:: flatbuffer_wrappers:: guest_error:: GuestError ;
2525use hyperlight_common:: flatbuffer_wrappers:: guest_log_data:: GuestLogData ;
2626use hyperlight_common:: flatbuffer_wrappers:: host_function_details:: HostFunctionDetails ;
27+ use hyperlight_common:: mem:: PAGES_IN_BLOCK ;
2728use tracing:: { Span , instrument} ;
2829
2930use super :: exe:: ExeInfo ;
@@ -33,8 +34,9 @@ use super::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryReg
3334use super :: ptr:: { GuestPtr , RawPtr } ;
3435use super :: ptr_offset:: Offset ;
3536use super :: shared_mem:: { ExclusiveSharedMemory , GuestSharedMemory , HostSharedMemory , SharedMemory } ;
36- use super :: shared_mem_snapshot:: SharedMemorySnapshot ;
37- use crate :: HyperlightError :: NoMemorySnapshot ;
37+ use super :: shared_memory_snapshot_manager:: SharedMemorySnapshotManager ;
38+ use crate :: mem:: bitmap:: { bitmap_union, new_page_bitmap} ;
39+ use crate :: mem:: dirty_page_tracking:: DirtyPageTracker ;
3840use crate :: sandbox:: SandboxConfiguration ;
3941use crate :: sandbox:: uninitialized:: GuestBlob ;
4042use crate :: { Result , log_then_return, new_error} ;
@@ -75,9 +77,8 @@ pub(crate) struct SandboxMemoryManager<S> {
7577 pub ( crate ) entrypoint_offset : Offset ,
7678 /// How many memory regions were mapped after sandbox creation
7779 pub ( crate ) mapped_rgns : u64 ,
78- /// A vector of memory snapshots that can be used to save and restore the state of the memory
79- /// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API)
80- snapshots : Arc < Mutex < Vec < SharedMemorySnapshot > > > ,
80+ /// Shared memory snapshots that can be used to save and restore the state of the memory
81+ snapshot_manager : Arc < Mutex < Option < SharedMemorySnapshotManager > > > ,
8182}
8283
8384impl < S > SandboxMemoryManager < S >
9899 load_addr,
99100 entrypoint_offset,
100101 mapped_rgns : 0 ,
101- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
102+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
102103 }
103104 }
104105
@@ -107,17 +108,12 @@ where
107108 & mut self . shared_mem
108109 }
109110
110- /// Set up the hypervisor partition in the given `SharedMemory` parameter
111- /// `shared_mem`, with the given memory size `mem_size`
111+ /// Set up the page tables in the shared memory
112112 // TODO: This should perhaps happen earlier and use an
113113 // ExclusiveSharedMemory from the beginning.
114114 #[ instrument( err( Debug ) , skip_all, parent = Span :: current( ) , level= "Trace" ) ]
115115 #[ cfg( feature = "init-paging" ) ]
116- pub ( crate ) fn set_up_shared_memory (
117- & mut self ,
118- mem_size : u64 ,
119- regions : & mut [ MemoryRegion ] ,
120- ) -> Result < u64 > {
116+ pub ( crate ) fn set_up_page_tables ( & mut self , regions : & mut [ MemoryRegion ] ) -> Result < u64 > {
121117 let rsp: u64 = self . layout . get_top_of_user_stack_offset ( ) as u64
122118 + SandboxMemoryLayout :: BASE_ADDRESS as u64
123119 + self . layout . stack_size as u64
@@ -126,6 +122,7 @@ where
126122 // test from `sandbox_host_tests` fails. We should investigate this further.
127123 // See issue #498 for more details.
128124 - 0x28 ;
125+ let mem_size = self . shared_mem . mem_size ( ) ;
129126
130127 self . shared_mem . with_exclusivity ( |shared_mem| {
131128 // Create PDL4 table with only 1 PML4E
@@ -154,8 +151,6 @@ where
154151 // We can use the memory size to calculate the number of PTs we need
155152 // We round up mem_size/2MB
156153
157- let mem_size = usize:: try_from ( mem_size) ?;
158-
159154 let num_pages: usize = mem_size. div_ceil ( AMOUNT_OF_MEMORY_PER_PT ) ;
160155
161156 // Create num_pages PT with 512 PTEs
@@ -265,54 +260,100 @@ where
265260 }
266261 }
267262
268- /// this function will create a memory snapshot and push it onto the stack of snapshots
269- /// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state
270- pub ( crate ) fn push_state ( & mut self ) -> Result < ( ) > {
271- let snapshot = SharedMemorySnapshot :: new ( & mut self . shared_mem , self . mapped_rgns ) ?;
272- self . snapshots
263+ /// this function will create an initial snapshot and then create the SnapshotManager
264+ pub ( crate ) fn create_initial_snapshot (
265+ & mut self ,
266+ vm_dirty_bitmap : & [ u64 ] ,
267+ host_dirty_page_idx : & [ usize ] ,
268+ layout : & SandboxMemoryLayout ,
269+ ) -> Result < ( ) > {
270+ let mut existing_snapshot_manager = self
271+ . snapshot_manager
273272 . try_lock ( )
274- . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
275- . push ( snapshot) ;
273+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
274+
275+ if existing_snapshot_manager. is_some ( ) {
276+ log_then_return ! ( "Snapshot manager already initialized, not creating a new one" ) ;
277+ }
278+
279+ // covert vec of page indices to bitmap
280+ let mut res = new_page_bitmap ( self . shared_mem . mem_size ( ) , false ) ?;
281+ for page_idx in host_dirty_page_idx {
282+ let block_idx = page_idx / PAGES_IN_BLOCK ;
283+ let bit_idx = page_idx % PAGES_IN_BLOCK ;
284+ res[ block_idx] |= 1 << bit_idx;
285+ }
286+
287+ // merge the host dirty page map into the dirty bitmap
288+ let merged = bitmap_union ( & res, vm_dirty_bitmap) ;
289+
290+ let snapshot_manager = SharedMemorySnapshotManager :: new (
291+ & mut self . shared_mem ,
292+ & merged,
293+ layout,
294+ self . mapped_rgns ,
295+ ) ?;
296+ existing_snapshot_manager. replace ( snapshot_manager) ;
276297 Ok ( ( ) )
277298 }
278299
279300 /// this function restores a memory snapshot from the last snapshot in the list but does not pop the snapshot
280301 /// off the stack
281302 /// It should be used when you want to restore the state of the memory to a previous state but still want to
282303 /// retain that state, for example after calling a function in the guest
283- ///
284- /// Returns the number of memory regions mapped into the sandbox
285- /// that need to be unmapped in order for the restore to be
286- /// completed.
287- pub ( crate ) fn restore_state_from_last_snapshot ( & mut self ) -> Result < u64 > {
288- let mut snapshots = self
289- . snapshots
304+ pub ( crate ) fn restore_state_from_last_snapshot ( & mut self , dirty_bitmap : & [ u64 ] ) -> Result < u64 > {
305+ let mut snapshot_manager = self
306+ . snapshot_manager
290307 . try_lock ( )
291308 . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
292- let last = snapshots. last_mut ( ) ;
293- if last. is_none ( ) {
294- log_then_return ! ( NoMemorySnapshot ) ;
309+
310+ match snapshot_manager. as_mut ( ) {
311+ None => {
312+ log_then_return ! ( "Snapshot manager not initialized" ) ;
313+ }
314+ Some ( snapshot_manager) => {
315+ snapshot_manager. restore_from_snapshot ( & mut self . shared_mem , dirty_bitmap)
316+ }
295317 }
296- #[ allow( clippy:: unwrap_used) ] // We know that last is not None because we checked it above
297- let snapshot = last. unwrap ( ) ;
298- let old_rgns = self . mapped_rgns ;
299- self . mapped_rgns = snapshot. restore_from_snapshot ( & mut self . shared_mem ) ?;
300- Ok ( old_rgns - self . mapped_rgns )
301318 }
302319
303320 /// this function pops the last snapshot off the stack and restores the memory to the previous state
304321 /// It should be used when you want to restore the state of the memory to a previous state and do not need to retain that state
305322 /// for example when devolving a sandbox to a previous state.
306- pub ( crate ) fn pop_and_restore_state_from_snapshot ( & mut self ) -> Result < u64 > {
307- let last = self
308- . snapshots
323+ pub ( crate ) fn pop_and_restore_state_from_snapshot (
324+ & mut self ,
325+ dirty_bitmap : & [ u64 ] ,
326+ ) -> Result < u64 > {
327+ let mut snapshot_manager = self
328+ . snapshot_manager
329+ . try_lock ( )
330+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
331+
332+ match snapshot_manager. as_mut ( ) {
333+ None => {
334+ log_then_return ! ( "Snapshot manager not initialized" ) ;
335+ }
336+ Some ( snapshot_manager) => snapshot_manager
337+ . pop_and_restore_state_from_snapshot ( & mut self . shared_mem , dirty_bitmap) ,
338+ }
339+ }
340+
341+ pub ( crate ) fn push_state ( & mut self , dirty_bitmap : & [ u64 ] ) -> Result < ( ) > {
342+ let mut snapshot_manager = self
343+ . snapshot_manager
309344 . try_lock ( )
310- . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
311- . pop ( ) ;
312- if last. is_none ( ) {
313- log_then_return ! ( NoMemorySnapshot ) ;
345+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
346+
347+ match snapshot_manager. as_mut ( ) {
348+ None => {
349+ log_then_return ! ( "Snapshot manager not initialized" ) ;
350+ }
351+ Some ( snapshot_manager) => snapshot_manager. create_new_snapshot (
352+ & mut self . shared_mem ,
353+ dirty_bitmap,
354+ self . mapped_rgns ,
355+ ) ,
314356 }
315- self . restore_state_from_last_snapshot ( )
316357 }
317358
318359 /// Sets `addr` to the correct offset in the memory referenced by
@@ -347,7 +388,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
347388 cfg : SandboxConfiguration ,
348389 exe_info : & mut ExeInfo ,
349390 guest_blob : Option < & GuestBlob > ,
350- ) -> Result < Self > {
391+ ) -> Result < ( Self , DirtyPageTracker ) > {
351392 let guest_blob_size = guest_blob. map ( |b| b. data . len ( ) ) . unwrap_or ( 0 ) ;
352393 let guest_blob_mem_flags = guest_blob. map ( |b| b. permissions ) ;
353394
@@ -360,6 +401,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
360401 guest_blob_mem_flags,
361402 ) ?;
362403 let mut shared_mem = ExclusiveSharedMemory :: new ( layout. get_memory_size ( ) ?) ?;
404+ let tracker = shared_mem. start_tracking_dirty_pages ( ) ?;
363405
364406 let load_addr: RawPtr = RawPtr :: try_from ( layout. get_guest_code_address ( ) ) ?;
365407
@@ -378,7 +420,10 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
378420 & mut shared_mem. as_mut_slice ( ) [ layout. get_guest_code_offset ( ) ..] ,
379421 ) ?;
380422
381- Ok ( Self :: new ( layout, shared_mem, load_addr, entrypoint_offset) )
423+ Ok ( (
424+ Self :: new ( layout, shared_mem, load_addr, entrypoint_offset) ,
425+ tracker,
426+ ) )
382427 }
383428
384429 /// Writes host function details to memory
@@ -440,15 +485,15 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
440485 load_addr : self . load_addr . clone ( ) ,
441486 entrypoint_offset : self . entrypoint_offset ,
442487 mapped_rgns : 0 ,
443- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
488+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
444489 } ,
445490 SandboxMemoryManager {
446491 shared_mem : gshm,
447492 layout : self . layout ,
448493 load_addr : self . load_addr . clone ( ) ,
449494 entrypoint_offset : self . entrypoint_offset ,
450495 mapped_rgns : 0 ,
451- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
496+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
452497 } ,
453498 )
454499 }
0 commit comments