@@ -20,7 +20,11 @@ use arch::aarch64::gic::GICDevice;
2020#[ cfg( target_arch = "x86_64" ) ]
2121use cpuid:: { c3, filter_cpuid, t2, VmSpec } ;
2222#[ cfg( target_arch = "x86_64" ) ]
23- use kvm_bindings:: { kvm_pit_config, CpuId , KVM_MAX_CPUID_ENTRIES , KVM_PIT_SPEAKER_DUMMY } ;
23+ use kvm_bindings:: {
24+ kvm_clock_data, kvm_irqchip, kvm_pit_config, kvm_pit_state2, CpuId , MsrList ,
25+ KVM_CLOCK_TSC_STABLE , KVM_IRQCHIP_IOAPIC , KVM_IRQCHIP_PIC_MASTER , KVM_IRQCHIP_PIC_SLAVE ,
26+ KVM_MAX_CPUID_ENTRIES , KVM_PIT_SPEAKER_DUMMY ,
27+ } ;
2428use kvm_bindings:: { kvm_userspace_memory_region, KVM_API_VERSION } ;
2529use kvm_ioctls:: * ;
2630use logger:: { Metric , METRICS } ;
@@ -110,6 +114,24 @@ pub enum Error {
110114 VcpuUnhandledKvmExit ,
111115 /// Cannot open the VM file descriptor.
112116 VmFd ( kvm_ioctls:: Error ) ,
117+ #[ cfg( target_arch = "x86_64" ) ]
118+ /// Failed to get KVM vm pit state.
119+ VmGetPit2 ( kvm_ioctls:: Error ) ,
120+ #[ cfg( target_arch = "x86_64" ) ]
121+ /// Failed to get KVM vm clock.
122+ VmGetClock ( kvm_ioctls:: Error ) ,
123+ #[ cfg( target_arch = "x86_64" ) ]
124+ /// Failed to get KVM vm irqchip.
125+ VmGetIrqChip ( kvm_ioctls:: Error ) ,
126+ #[ cfg( target_arch = "x86_64" ) ]
127+ /// Failed to set KVM vm pit state.
128+ VmSetPit2 ( kvm_ioctls:: Error ) ,
129+ #[ cfg( target_arch = "x86_64" ) ]
130+ /// Failed to set KVM vm clock.
131+ VmSetClock ( kvm_ioctls:: Error ) ,
132+ #[ cfg( target_arch = "x86_64" ) ]
133+ /// Failed to set KVM vm irqchip.
134+ VmSetIrqChip ( kvm_ioctls:: Error ) ,
113135 /// Cannot configure the microvm.
114136 VmSetup ( kvm_ioctls:: Error ) ,
115137}
@@ -280,6 +302,74 @@ impl Vm {
280302 pub fn fd ( & self ) -> & VmFd {
281303 & self . fd
282304 }
305+
306+ #[ allow( unused) ]
307+ #[ cfg( target_arch = "x86_64" ) ]
308+ /// Saves and returns the Kvm Vm state.
309+ pub fn save_state ( & self ) -> Result < VmState > {
310+ let pitstate = self . fd . get_pit2 ( ) . map_err ( Error :: VmGetPit2 ) ?;
311+
312+ let mut clock = self . fd . get_clock ( ) . map_err ( Error :: VmGetClock ) ?;
313+ // This bit is not accepted in SET_CLOCK, clear it.
314+ clock. flags &= !KVM_CLOCK_TSC_STABLE ;
315+
316+ let mut pic_master = kvm_irqchip:: default ( ) ;
317+ pic_master. chip_id = KVM_IRQCHIP_PIC_MASTER ;
318+ self . fd
319+ . get_irqchip ( & mut pic_master)
320+ . map_err ( Error :: VmGetIrqChip ) ?;
321+
322+ let mut pic_slave = kvm_irqchip:: default ( ) ;
323+ pic_slave. chip_id = KVM_IRQCHIP_PIC_SLAVE ;
324+ self . fd
325+ . get_irqchip ( & mut pic_slave)
326+ . map_err ( Error :: VmGetIrqChip ) ?;
327+
328+ let mut ioapic = kvm_irqchip:: default ( ) ;
329+ ioapic. chip_id = KVM_IRQCHIP_IOAPIC ;
330+ self . fd
331+ . get_irqchip ( & mut ioapic)
332+ . map_err ( Error :: VmGetIrqChip ) ?;
333+
334+ Ok ( VmState {
335+ pitstate,
336+ clock,
337+ pic_master,
338+ pic_slave,
339+ ioapic,
340+ } )
341+ }
342+
343+ #[ allow( unused) ]
344+ #[ cfg( target_arch = "x86_64" ) ]
345+ /// Restores the Kvm Vm state.
346+ pub fn restore_state ( & self , state : & VmState ) -> Result < ( ) > {
347+ self . fd
348+ . set_pit2 ( & state. pitstate )
349+ . map_err ( Error :: VmSetPit2 ) ?;
350+ self . fd . set_clock ( & state. clock ) . map_err ( Error :: VmSetClock ) ?;
351+ self . fd
352+ . set_irqchip ( & state. pic_master )
353+ . map_err ( Error :: VmSetIrqChip ) ?;
354+ self . fd
355+ . set_irqchip ( & state. pic_slave )
356+ . map_err ( Error :: VmSetIrqChip ) ?;
357+ self . fd
358+ . set_irqchip ( & state. ioapic )
359+ . map_err ( Error :: VmSetIrqChip ) ?;
360+ Ok ( ( ) )
361+ }
362+ }
363+
364+ #[ allow( unused) ]
365+ #[ cfg( target_arch = "x86_64" ) ]
366+ /// Structure holding VM kvm state.
367+ pub struct VmState {
368+ pitstate : kvm_pit_state2 ,
369+ clock : kvm_clock_data ,
370+ pic_master : kvm_irqchip ,
371+ pic_slave : kvm_irqchip ,
372+ ioapic : kvm_irqchip ,
283373}
284374
285375// Using this for easier explicit type-casting to help IDEs interpret the code.
@@ -1285,4 +1375,27 @@ mod tests {
12851375 fn test_vcpu_rtsig_offset ( ) {
12861376 assert ! ( validate_signal_num( sigrtmin( ) + VCPU_RTSIG_OFFSET ) . is_ok( ) ) ;
12871377 }
1378+
1379+ #[ cfg( target_arch = "x86_64" ) ]
1380+ #[ test]
1381+ fn test_vm_save_restore_state ( ) {
1382+ let kvm_fd = Kvm :: new ( ) . unwrap ( ) ;
1383+ let vm = Vm :: new ( & kvm_fd) . expect ( "new vm failed" ) ;
1384+ // Irqchips, clock and pitstate are not configured so trying to save state should fail.
1385+ assert ! ( vm. save_state( ) . is_err( ) ) ;
1386+
1387+ let ( vm, _) = setup_vcpu ( 0x1000 ) ;
1388+ let vm_state = vm. save_state ( ) . unwrap ( ) ;
1389+ assert_eq ! (
1390+ vm_state. pitstate. flags | KVM_PIT_SPEAKER_DUMMY ,
1391+ KVM_PIT_SPEAKER_DUMMY
1392+ ) ;
1393+ assert_eq ! ( vm_state. clock. flags & KVM_CLOCK_TSC_STABLE , 0 ) ;
1394+ assert_eq ! ( vm_state. pic_master. chip_id, KVM_IRQCHIP_PIC_MASTER ) ;
1395+ assert_eq ! ( vm_state. pic_slave. chip_id, KVM_IRQCHIP_PIC_SLAVE ) ;
1396+ assert_eq ! ( vm_state. ioapic. chip_id, KVM_IRQCHIP_IOAPIC ) ;
1397+
1398+ let ( vm, _) = setup_vcpu ( 0x1000 ) ;
1399+ assert ! ( vm. restore_state( & vm_state) . is_ok( ) ) ;
1400+ }
12881401}
0 commit comments