@@ -18,12 +18,19 @@ use std::path::Path;
1818use std:: sync:: Arc ;
1919
2020use anyhow:: Context ;
21+ use bhyve_api:: {
22+ vdi_field_entry_v1, vdi_time_info_v1, ApiVersion , VAI_BOOT_HRTIME ,
23+ VDC_VMM_ARCH , VDC_VMM_TIME ,
24+ } ;
25+ use propolis:: vmm:: data as vmm_data;
2126use propolis:: {
2227 chardev:: UDSock ,
2328 common:: { GuestAddr , GuestRegion } ,
2429 inventory:: Order ,
2530 migrate:: { MigrateCtx , Migrator } ,
31+ vmm:: VmmHdl ,
2632} ;
33+
2734use slog:: { info, warn} ;
2835use tokio:: {
2936 fs:: File ,
@@ -57,9 +64,9 @@ pub(crate) async fn save(
5764
5865 info ! ( log, "Serializing global VM state" ) ;
5966 let global_state = {
60- let global_state =
61- hdl . export ( ) . context ( "Failed to export global VM state" ) ?;
62- serde_json:: to_vec ( & global_state ) ?
67+ let output =
68+ export_global ( & hdl ) . context ( "Failed to export global VM state" ) ?;
69+ serde_json:: to_vec ( & output ) ?
6370 } ;
6471
6572 info ! ( log, "Serializing VM device state" ) ;
@@ -223,13 +230,12 @@ pub(crate) async fn restore(
223230 let state_len = file. read_u64 ( ) . await ?;
224231 let mut global_state = vec ! [ 0 ; state_len. try_into( ) ?] ;
225232 file. read_exact ( & mut global_state) . await ?;
226- let mut deserializer =
227- serde_json:: Deserializer :: from_slice ( & global_state) ;
228- let deserializer =
229- & mut <dyn erased_serde:: Deserializer >:: erase ( & mut deserializer) ;
230233
231- // Restore it
232- hdl. import ( deserializer) . context ( "Failed to import global VM state" ) ?;
234+ let data: VmGlobalState = serde_json:: from_slice ( & global_state)
235+ . context ( "Failed to deserialize global VM state" ) ?;
236+
237+ import_global ( & hdl, & data)
238+ . context ( "failed to import global VM state" ) ?;
233239 }
234240
235241 // Next are the devices
@@ -333,3 +339,48 @@ pub(crate) async fn restore(
333339 drop ( inst_inner) ;
334340 Ok ( ( inst, com1_sock) )
335341}
342+
343+ #[ derive( serde:: Serialize , serde:: Deserialize ) ]
344+ pub struct VmGlobalState {
345+ // Just using the raw boot_hrtime leaves room for all sorts of failures,
346+ // especially if a saved state file is used after a subsequent reboot of the
347+ // host. These problems can be addressed later.
348+ pub boot_hrtime : i64 ,
349+ // Fixing up the guest TSC is left as an exercise for later
350+ }
351+
352+ fn export_global ( hdl : & VmmHdl ) -> std:: io:: Result < VmGlobalState > {
353+ if hdl. api_version ( ) ? > ApiVersion :: V11 . into ( ) {
354+ let info: vdi_time_info_v1 = vmm_data:: read ( hdl, -1 , VDC_VMM_TIME , 1 ) ?;
355+
356+ Ok ( VmGlobalState { boot_hrtime : info. vt_boot_hrtime } )
357+ } else {
358+ let arch_entries: Vec < bhyve_api:: vdi_field_entry_v1 > =
359+ vmm_data:: read_many ( hdl, -1 , VDC_VMM_ARCH , 1 ) ?;
360+ let boot_ent = arch_entries
361+ . iter ( )
362+ . find ( |ent| ent. vfe_ident == VAI_BOOT_HRTIME )
363+ . expect ( "VAI_BOOT_HRTIME should be present" ) ;
364+
365+ Ok ( VmGlobalState { boot_hrtime : boot_ent. vfe_value as i64 } )
366+ }
367+ }
368+ fn import_global ( hdl : & VmmHdl , state : & VmGlobalState ) -> std:: io:: Result < ( ) > {
369+ if hdl. api_version ( ) ? > ApiVersion :: V11 . into ( ) {
370+ let mut info: vdi_time_info_v1 =
371+ vmm_data:: read ( hdl, -1 , VDC_VMM_TIME , 1 ) ?;
372+
373+ info. vt_boot_hrtime = state. boot_hrtime ;
374+ vmm_data:: write ( hdl, -1 , VDC_VMM_TIME , 1 , info) ?;
375+
376+ Ok ( ( ) )
377+ } else {
378+ let arch_entry = vdi_field_entry_v1 {
379+ vfe_ident : VAI_BOOT_HRTIME ,
380+ vfe_value : state. boot_hrtime as u64 ,
381+ ..Default :: default ( )
382+ } ;
383+ vmm_data:: write ( hdl, -1 , VDC_VMM_ARCH , 1 , arch_entry) ?;
384+ Ok ( ( ) )
385+ }
386+ }
0 commit comments