@@ -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:: { PAGE_SIZE_USIZE , 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} ;
@@ -73,9 +75,8 @@ pub(crate) struct SandboxMemoryManager<S> {
7375 pub ( crate ) load_addr : RawPtr ,
7476 /// Offset for the execution entrypoint from `load_addr`
7577 pub ( crate ) entrypoint_offset : Offset ,
76- /// A vector of memory snapshots that can be used to save and restore the state of the memory
77- /// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API)
78- snapshots : Arc < Mutex < Vec < SharedMemorySnapshot > > > ,
78+ /// Shared memory snapshots that can be used to save and restore the state of the memory
79+ snapshot_manager : Arc < Mutex < Option < SharedMemorySnapshotManager > > > ,
7980}
8081
8182impl < S > SandboxMemoryManager < S >
9596 shared_mem,
9697 load_addr,
9798 entrypoint_offset,
98- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
99+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
99100 }
100101 }
101102
@@ -262,48 +263,108 @@ where
262263 }
263264 }
264265
265- /// this function will create a memory snapshot and push it onto the stack of snapshots
266- /// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state
267- pub ( crate ) fn push_state ( & mut self ) -> Result < ( ) > {
268- let snapshot = SharedMemorySnapshot :: new ( & mut self . shared_mem ) ?;
269- self . snapshots
266+ /// this function will create an initial snapshot and then create the SnapshotManager
267+ pub ( crate ) fn create_initial_snapshot (
268+ & mut self ,
269+ dirty_bitmap : Option < & Vec < u64 > > ,
270+ layout : & SandboxMemoryLayout ,
271+ ) -> Result < ( ) > {
272+ let mut existing_snapshot_manager = self
273+ . snapshot_manager
270274 . try_lock ( )
271- . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
272- . push ( snapshot) ;
275+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
276+
277+ if existing_snapshot_manager. is_some ( ) {
278+ log_then_return ! ( "Snapshot manager already initialized, not creating a new one" ) ;
279+ }
280+ let merged_page_map = if let Some ( host_dirty_page_map) = self
281+ . shared_mem
282+ . with_exclusivity ( |e| e. get_and_clear_host_dirty_page_map ( ) ) ??
283+ {
284+ // covert vec of page indices to vec of blocks
285+ // memory size is the size of the shared memory minus the 2 guard pages
286+ let mut res = new_page_bitmap ( self . shared_mem . mem_size ( ) - 2 * PAGE_SIZE_USIZE , false ) ?;
287+ for page_idx in host_dirty_page_map {
288+ let block_idx = page_idx / PAGES_IN_BLOCK ;
289+ let bit_idx = page_idx % PAGES_IN_BLOCK ;
290+ res[ block_idx] |= 1 << bit_idx;
291+ }
292+
293+ // merge the host dirty page map into the dirty bitmap
294+ let len = res. len ( ) ;
295+ let merged = bitmap_union ( & res, dirty_bitmap. unwrap_or ( & vec ! [ 0 ; len] ) ) ;
296+ Some ( merged)
297+ } else {
298+ None
299+ } ;
300+
301+ let dirty_page_map = if let Some ( ref dirty_page_map) = merged_page_map {
302+ Some ( dirty_page_map)
303+ } else {
304+ dirty_bitmap
305+ } ;
306+
307+ let snapshot_manager =
308+ SharedMemorySnapshotManager :: new ( & mut self . shared_mem , dirty_page_map, layout) ?;
309+ existing_snapshot_manager. replace ( snapshot_manager) ;
273310 Ok ( ( ) )
274311 }
275312
276313 /// this function restores a memory snapshot from the last snapshot in the list but does not pop the snapshot
277314 /// off the stack
278315 /// It should be used when you want to restore the state of the memory to a previous state but still want to
279316 /// retain that state, for example after calling a function in the guest
280- pub ( crate ) fn restore_state_from_last_snapshot ( & mut self ) -> Result < ( ) > {
281- let mut snapshots = self
282- . snapshots
317+ pub ( crate ) fn restore_state_from_last_snapshot ( & mut self , dirty_bitmap : & [ u64 ] ) -> Result < ( ) > {
318+ let mut snapshot_manager = self
319+ . snapshot_manager
283320 . try_lock ( )
284321 . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
285- let last = snapshots. last_mut ( ) ;
286- if last. is_none ( ) {
287- log_then_return ! ( NoMemorySnapshot ) ;
322+
323+ match snapshot_manager. as_mut ( ) {
324+ None => {
325+ log_then_return ! ( "Snapshot manager not initialized" ) ;
326+ }
327+ Some ( snapshot_manager) => {
328+ snapshot_manager. restore_from_snapshot ( & mut self . shared_mem , dirty_bitmap)
329+ }
288330 }
289- #[ allow( clippy:: unwrap_used) ] // We know that last is not None because we checked it above
290- let snapshot = last. unwrap ( ) ;
291- snapshot. restore_from_snapshot ( & mut self . shared_mem )
292331 }
293332
294333 /// this function pops the last snapshot off the stack and restores the memory to the previous state
295334 /// 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
296335 /// for example when devolving a sandbox to a previous state.
297- pub ( crate ) fn pop_and_restore_state_from_snapshot ( & mut self ) -> Result < ( ) > {
298- let last = self
299- . snapshots
336+ pub ( crate ) fn pop_and_restore_state_from_snapshot (
337+ & mut self ,
338+ dirty_bitmap : & [ u64 ] ,
339+ ) -> Result < ( ) > {
340+ let mut snapshot_manager = self
341+ . snapshot_manager
300342 . try_lock ( )
301- . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?
302- . pop ( ) ;
303- if last. is_none ( ) {
304- log_then_return ! ( NoMemorySnapshot ) ;
343+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
344+
345+ match snapshot_manager. as_mut ( ) {
346+ None => {
347+ log_then_return ! ( "Snapshot manager not initialized" ) ;
348+ }
349+ Some ( snapshot_manager) => snapshot_manager
350+ . pop_and_restore_state_from_snapshot ( & mut self . shared_mem , dirty_bitmap) ,
351+ }
352+ }
353+
354+ pub ( crate ) fn push_state ( & mut self , dirty_bitmap : Option < & Vec < u64 > > ) -> Result < ( ) > {
355+ let mut snapshot_manager = self
356+ . snapshot_manager
357+ . try_lock ( )
358+ . map_err ( |e| new_error ! ( "Error locking at {}:{}: {}" , file!( ) , line!( ) , e) ) ?;
359+
360+ match snapshot_manager. as_mut ( ) {
361+ None => {
362+ log_then_return ! ( "Snapshot manager not initialized" ) ;
363+ }
364+ Some ( snapshot_manager) => {
365+ snapshot_manager. create_new_snapshot ( & mut self . shared_mem , dirty_bitmap)
366+ }
305367 }
306- self . restore_state_from_last_snapshot ( )
307368 }
308369
309370 /// Sets `addr` to the correct offset in the memory referenced by
@@ -338,7 +399,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
338399 cfg : SandboxConfiguration ,
339400 exe_info : & mut ExeInfo ,
340401 guest_blob : Option < & GuestBlob > ,
341- ) -> Result < Self > {
402+ ) -> Result < ( Self , DirtyPageTracker ) > {
342403 let guest_blob_size = guest_blob. map ( |b| b. data . len ( ) ) . unwrap_or ( 0 ) ;
343404 let guest_blob_mem_flags = guest_blob. map ( |b| b. permissions ) ;
344405
@@ -351,6 +412,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
351412 guest_blob_mem_flags,
352413 ) ?;
353414 let mut shared_mem = ExclusiveSharedMemory :: new ( layout. get_memory_size ( ) ?) ?;
415+ let tracker = shared_mem. start_tracking_dirty_pages ( ) ?;
354416
355417 let load_addr: RawPtr = RawPtr :: try_from ( layout. get_guest_code_address ( ) ) ?;
356418
@@ -369,7 +431,10 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
369431 & mut shared_mem. as_mut_slice ( ) [ layout. get_guest_code_offset ( ) ..] ,
370432 ) ?;
371433
372- Ok ( Self :: new ( layout, shared_mem, load_addr, entrypoint_offset) )
434+ Ok ( (
435+ Self :: new ( layout, shared_mem, load_addr, entrypoint_offset) ,
436+ tracker,
437+ ) )
373438 }
374439
375440 /// Writes host function details to memory
@@ -405,6 +470,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
405470 host_function_call_buffer. as_slice ( ) ,
406471 self . layout . host_function_definitions_buffer_offset ,
407472 ) ?;
473+
408474 Ok ( ( ) )
409475 }
410476
@@ -430,14 +496,14 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
430496 layout : self . layout ,
431497 load_addr : self . load_addr . clone ( ) ,
432498 entrypoint_offset : self . entrypoint_offset ,
433- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
499+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
434500 } ,
435501 SandboxMemoryManager {
436502 shared_mem : gshm,
437503 layout : self . layout ,
438504 load_addr : self . load_addr . clone ( ) ,
439505 entrypoint_offset : self . entrypoint_offset ,
440- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
506+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
441507 } ,
442508 )
443509 }
0 commit comments