@@ -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