Skip to content

Commit 68269dd

Browse files
committed
Add MultiUseSandbox from_snapshot
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent e8ce0d3 commit 68269dd

1 file changed

Lines changed: 150 additions & 0 deletions

File tree

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,156 @@ impl MultiUseSandbox {
145145
self.pt_root_finder = Some(finder);
146146
}
147147

148+
/// Create a `MultiUseSandbox` directly from a [`Snapshot`],
149+
/// bypassing [`UninitializedSandbox`](crate::UninitializedSandbox)
150+
/// and [`evolve()`](crate::UninitializedSandbox::evolve).
151+
///
152+
/// This is useful for fast sandbox creation when a snapshot of
153+
/// an already-initialized guest is available, either saved to disk
154+
/// or captured in memory from another sandbox.
155+
///
156+
/// The provided [`HostFunctions`] must include every host function
157+
/// that was registered on the sandbox at the time the snapshot was
158+
/// taken (matched by name and signature). Additional host functions
159+
/// not present in the snapshot are allowed.
160+
///
161+
/// # Examples
162+
///
163+
/// From a snapshot taken on another sandbox:
164+
///
165+
/// ```no_run
166+
/// # use std::sync::Arc;
167+
/// # use hyperlight_host::{HostFunctions, MultiUseSandbox, UninitializedSandbox, GuestBinary};
168+
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
169+
/// // Create and initialize a sandbox the normal way
170+
/// let mut sandbox: MultiUseSandbox = UninitializedSandbox::new(
171+
/// GuestBinary::FilePath("guest.bin".into()),
172+
/// None,
173+
/// )?.evolve()?;
174+
///
175+
/// // Capture a snapshot of the initialized state
176+
/// let snapshot = sandbox.snapshot()?;
177+
///
178+
/// // Create a new sandbox directly from the snapshot
179+
/// let mut sandbox2 = MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default())?;
180+
/// let result: i32 = sandbox2.call("GetValue", ())?;
181+
/// # Ok(())
182+
/// # }
183+
/// ```
184+
///
185+
/// From a snapshot loaded from disk:
186+
///
187+
/// ```no_run
188+
/// # use std::sync::Arc;
189+
/// # use hyperlight_host::{HostFunctions, MultiUseSandbox};
190+
/// # use hyperlight_host::sandbox::snapshot::Snapshot;
191+
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
192+
/// let snapshot = Arc::new(Snapshot::from_file("guest_snapshot.hls")?);
193+
/// let mut sandbox = MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default())?;
194+
/// let result: String = sandbox.call("Echo", "hello".to_string())?;
195+
/// # Ok(())
196+
/// # }
197+
/// ```
198+
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
199+
pub fn from_snapshot(
200+
snapshot: Arc<Snapshot>,
201+
host_funcs: crate::HostFunctions,
202+
) -> Result<Self> {
203+
use rand::RngExt;
204+
205+
use crate::mem::ptr::RawPtr;
206+
use crate::sandbox::uninitialized_evolve::set_up_hypervisor_partition;
207+
208+
// Validate that the provided host functions are a superset of
209+
// those required by the snapshot.
210+
snapshot.validate_host_functions(&host_funcs)?;
211+
212+
let host_funcs = Arc::new(Mutex::new(host_funcs.into_inner()));
213+
214+
let stack_top_gva = snapshot.stack_top_gva();
215+
let mut config = crate::sandbox::SandboxConfiguration::default();
216+
config.set_input_data_size(snapshot.layout().input_data_size);
217+
config.set_output_data_size(snapshot.layout().output_data_size);
218+
config.set_heap_size(snapshot.layout().heap_size as u64);
219+
config.set_scratch_size(snapshot.layout().get_scratch_size());
220+
let load_info = snapshot.load_info();
221+
222+
let mgr = crate::mem::mgr::SandboxMemoryManager::from_snapshot(&snapshot)?;
223+
let (mut hshm, gshm) = mgr.build()?;
224+
225+
let page_size = u32::try_from(page_size::get())? as usize;
226+
227+
#[cfg(target_os = "linux")]
228+
crate::signal_handlers::setup_signal_handlers(&config)?;
229+
230+
let mut vm = set_up_hypervisor_partition(
231+
gshm,
232+
&config,
233+
stack_top_gva,
234+
page_size,
235+
#[cfg(any(crashdump, gdb))]
236+
Default::default(),
237+
load_info,
238+
)?;
239+
240+
let seed = {
241+
let mut rng = rand::rng();
242+
rng.random::<u64>()
243+
};
244+
let peb_addr = RawPtr::from(u64::try_from(hshm.layout.peb_address())?);
245+
246+
#[cfg(gdb)]
247+
let dbg_mem_access_hdl = Arc::new(Mutex::new(hshm.clone()));
248+
249+
vm.initialise(
250+
peb_addr,
251+
seed,
252+
page_size as u32,
253+
&mut hshm,
254+
&host_funcs,
255+
None,
256+
#[cfg(gdb)]
257+
dbg_mem_access_hdl,
258+
)
259+
.map_err(crate::hypervisor::hyperlight_vm::HyperlightVmError::Initialize)?;
260+
261+
// If the snapshot was taken from an already-initialized guest
262+
// (NextAction::Call), apply the captured special registers so
263+
// the guest resumes in the correct CPU state.
264+
#[cfg(not(feature = "i686-guest"))]
265+
if matches!(snapshot.entrypoint(), super::snapshot::NextAction::Call(_)) {
266+
let sregs = snapshot.sregs().cloned().unwrap_or_else(|| {
267+
crate::hypervisor::regs::CommonSpecialRegisters::standard_64bit_defaults(
268+
hshm.layout.get_pt_base_gpa(),
269+
)
270+
});
271+
vm.apply_sregs(hshm.layout.get_pt_base_gpa(), &sregs)
272+
.map_err(|e| {
273+
crate::HyperlightError::HyperlightVmError(
274+
crate::hypervisor::hyperlight_vm::HyperlightVmError::Restore(e),
275+
)
276+
})?;
277+
}
278+
279+
#[cfg(gdb)]
280+
let dbg_mem_wrapper = Arc::new(Mutex::new(hshm.clone()));
281+
282+
let mut sbox = MultiUseSandbox::from_uninit(
283+
host_funcs,
284+
hshm,
285+
vm,
286+
#[cfg(gdb)]
287+
dbg_mem_wrapper,
288+
);
289+
// Use the snapshot's sandbox_id so that restore() back to this
290+
// snapshot is permitted.
291+
// TODO: this is buggy where 2 different sandboxes can now have same id.
292+
// Ids are inter-process, and should not be saved to disk.
293+
// We should get rid of the ids entirely and instead check layout for equality.
294+
sbox.id = snapshot.sandbox_id();
295+
Ok(sbox)
296+
}
297+
148298
/// Creates a snapshot of the sandbox's current memory state.
149299
///
150300
/// The snapshot is tied to this specific sandbox instance and can only be

0 commit comments

Comments
 (0)