@@ -24,6 +24,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue;
24
24
use hyperlight_common:: flatbuffer_wrappers:: guest_error:: GuestError ;
25
25
use hyperlight_common:: flatbuffer_wrappers:: guest_log_data:: GuestLogData ;
26
26
use hyperlight_common:: flatbuffer_wrappers:: host_function_details:: HostFunctionDetails ;
27
+ use hyperlight_common:: mem:: PAGES_IN_BLOCK ;
27
28
use tracing:: { Span , instrument} ;
28
29
29
30
use super :: exe:: ExeInfo ;
@@ -33,8 +34,9 @@ use super::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryReg
33
34
use super :: ptr:: { GuestPtr , RawPtr } ;
34
35
use super :: ptr_offset:: Offset ;
35
36
use 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 ;
38
40
use crate :: sandbox:: SandboxConfiguration ;
39
41
use crate :: sandbox:: uninitialized:: GuestBlob ;
40
42
use crate :: { Result , log_then_return, new_error} ;
@@ -75,9 +77,8 @@ pub(crate) struct SandboxMemoryManager<S> {
75
77
pub ( crate ) entrypoint_offset : Offset ,
76
78
/// How many memory regions were mapped after sandbox creation
77
79
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 > > > ,
81
82
}
82
83
83
84
impl < S > SandboxMemoryManager < S >
98
99
load_addr,
99
100
entrypoint_offset,
100
101
mapped_rgns : 0 ,
101
- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
102
+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
102
103
}
103
104
}
104
105
@@ -107,17 +108,12 @@ where
107
108
& mut self . shared_mem
108
109
}
109
110
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
112
112
// TODO: This should perhaps happen earlier and use an
113
113
// ExclusiveSharedMemory from the beginning.
114
114
#[ instrument( err( Debug ) , skip_all, parent = Span :: current( ) , level= "Trace" ) ]
115
115
#[ 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 > {
121
117
let rsp: u64 = self . layout . get_top_of_user_stack_offset ( ) as u64
122
118
+ SandboxMemoryLayout :: BASE_ADDRESS as u64
123
119
+ self . layout . stack_size as u64
@@ -126,6 +122,7 @@ where
126
122
// test from `sandbox_host_tests` fails. We should investigate this further.
127
123
// See issue #498 for more details.
128
124
- 0x28 ;
125
+ let mem_size = self . shared_mem . mem_size ( ) ;
129
126
130
127
self . shared_mem . with_exclusivity ( |shared_mem| {
131
128
// Create PDL4 table with only 1 PML4E
@@ -154,8 +151,6 @@ where
154
151
// We can use the memory size to calculate the number of PTs we need
155
152
// We round up mem_size/2MB
156
153
157
- let mem_size = usize:: try_from ( mem_size) ?;
158
-
159
154
let num_pages: usize = mem_size. div_ceil ( AMOUNT_OF_MEMORY_PER_PT ) ;
160
155
161
156
// Create num_pages PT with 512 PTEs
@@ -265,54 +260,100 @@ where
265
260
}
266
261
}
267
262
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
273
272
. 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) ;
276
297
Ok ( ( ) )
277
298
}
278
299
279
300
/// this function restores a memory snapshot from the last snapshot in the list but does not pop the snapshot
280
301
/// off the stack
281
302
/// It should be used when you want to restore the state of the memory to a previous state but still want to
282
303
/// 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
290
307
. try_lock ( )
291
308
. 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
+ }
295
317
}
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 )
301
318
}
302
319
303
320
/// this function pops the last snapshot off the stack and restores the memory to the previous state
304
321
/// 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
305
322
/// 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
309
344
. 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
+ ) ,
314
356
}
315
- self . restore_state_from_last_snapshot ( )
316
357
}
317
358
318
359
/// Sets `addr` to the correct offset in the memory referenced by
@@ -347,7 +388,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
347
388
cfg : SandboxConfiguration ,
348
389
exe_info : & mut ExeInfo ,
349
390
guest_blob : Option < & GuestBlob > ,
350
- ) -> Result < Self > {
391
+ ) -> Result < ( Self , DirtyPageTracker ) > {
351
392
let guest_blob_size = guest_blob. map ( |b| b. data . len ( ) ) . unwrap_or ( 0 ) ;
352
393
let guest_blob_mem_flags = guest_blob. map ( |b| b. permissions ) ;
353
394
@@ -360,6 +401,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
360
401
guest_blob_mem_flags,
361
402
) ?;
362
403
let mut shared_mem = ExclusiveSharedMemory :: new ( layout. get_memory_size ( ) ?) ?;
404
+ let tracker = shared_mem. start_tracking_dirty_pages ( ) ?;
363
405
364
406
let load_addr: RawPtr = RawPtr :: try_from ( layout. get_guest_code_address ( ) ) ?;
365
407
@@ -378,7 +420,10 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
378
420
& mut shared_mem. as_mut_slice ( ) [ layout. get_guest_code_offset ( ) ..] ,
379
421
) ?;
380
422
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
+ ) )
382
427
}
383
428
384
429
/// Writes host function details to memory
@@ -440,15 +485,15 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
440
485
load_addr : self . load_addr . clone ( ) ,
441
486
entrypoint_offset : self . entrypoint_offset ,
442
487
mapped_rgns : 0 ,
443
- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
488
+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
444
489
} ,
445
490
SandboxMemoryManager {
446
491
shared_mem : gshm,
447
492
layout : self . layout ,
448
493
load_addr : self . load_addr . clone ( ) ,
449
494
entrypoint_offset : self . entrypoint_offset ,
450
495
mapped_rgns : 0 ,
451
- snapshots : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
496
+ snapshot_manager : Arc :: new ( Mutex :: new ( None ) ) ,
452
497
} ,
453
498
)
454
499
}
0 commit comments