Skip to content

Commit 3b398bc

Browse files
committed
Install DirtyPageTracker immediately after allocating shared memory. Uninstall DirtyPageTracker immediately before setting up VM and mapping memory into VM. Use dirty pages to create snapshots with new snapshotmanager.
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Move setting up page tables out of set_up_hypervisor_partition Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 5d71e2a commit 3b398bc

File tree

8 files changed

+191
-194
lines changed

8 files changed

+191
-194
lines changed

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,11 +565,33 @@ pub(crate) mod tests {
565565
UninitializedSandbox::new(GuestBinary::FilePath(filename.clone()), Some(config))?;
566566
let tracker = sandbox.tracker.unwrap();
567567
let (_hshm, mut gshm) = sandbox.mgr.build();
568-
// we need to undo the mprotect(readonly) before mapping memory into vm, and that is done by getting dirty pages
568+
569+
let mut regions = gshm.layout.get_memory_regions(&gshm.shared_mem)?;
570+
571+
// Set up shared memory to calculate rsp_ptr
572+
#[cfg(feature = "init-paging")]
573+
let rsp_ptr = {
574+
use crate::mem::ptr::GuestPtr;
575+
let rsp_u64 = gshm.set_up_page_tables(&mut regions)?;
576+
let rsp_raw = RawPtr::from(rsp_u64);
577+
GuestPtr::try_from(rsp_raw)
578+
}?;
579+
#[cfg(not(feature = "init-paging"))]
580+
let rsp_ptr = {
581+
use crate::mem::ptr::GuestPtr;
582+
use crate::mem::ptr_offset::Offset;
583+
GuestPtr::try_from(Offset::from(0))
584+
}?;
585+
586+
// we need to undo the mprotect(readonly) before mapping memory into vm.
587+
// This is currently done by consuming the dirty page tracker by getting the dirty pages
569588
let _ = tracker.get_dirty_pages()?;
589+
570590
let mut vm = set_up_hypervisor_partition(
571591
&mut gshm,
572592
&config,
593+
rsp_ptr,
594+
regions,
573595
#[cfg(any(crashdump, gdb))]
574596
&rt_cfg,
575597
)?;

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 94 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue;
2424
use hyperlight_common::flatbuffer_wrappers::guest_error::GuestError;
2525
use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData;
2626
use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails;
27+
use hyperlight_common::mem::PAGES_IN_BLOCK;
2728
use tracing::{Span, instrument};
2829

2930
use super::exe::ExeInfo;
@@ -33,8 +34,9 @@ use super::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryReg
3334
use super::ptr::{GuestPtr, RawPtr};
3435
use super::ptr_offset::Offset;
3536
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;
3840
use crate::sandbox::SandboxConfiguration;
3941
use crate::sandbox::uninitialized::GuestBlob;
4042
use 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

8384
impl<S> SandboxMemoryManager<S>
@@ -98,7 +99,7 @@ where
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
}

src/hyperlight_host/src/mem/mod.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ pub mod ptr_offset;
4343
/// A wrapper around unsafe functionality to create and initialize
4444
/// a memory region for a guest running in a sandbox.
4545
pub mod shared_mem;
46-
/// A wrapper around a `SharedMemory` and a snapshot in time
47-
/// of the memory therein
48-
pub mod shared_mem_snapshot;
4946
/// Utilities for writing shared memory tests
5047
#[cfg(test)]
5148
pub(crate) mod shared_mem_tests;

0 commit comments

Comments
 (0)