7
7
// Use of this source code is governed by a BSD-style license that can be
8
8
// found in the THIRD-PARTY file.
9
9
10
+ // Part of public API
11
+ #[ cfg( target_arch = "x86_64" ) ]
12
+ pub use kvm_bindings:: nested:: KvmNestedStateBuffer ;
13
+
10
14
use kvm_bindings:: * ;
11
15
use libc:: EINVAL ;
12
16
use std:: fs:: File ;
@@ -17,7 +21,10 @@ use crate::kvm_ioctls::*;
17
21
use vmm_sys_util:: errno;
18
22
use vmm_sys_util:: ioctl:: { ioctl, ioctl_with_mut_ref, ioctl_with_ref} ;
19
23
#[ cfg( target_arch = "x86_64" ) ]
20
- use vmm_sys_util:: ioctl:: { ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val} ;
24
+ use {
25
+ std:: num:: NonZeroUsize ,
26
+ vmm_sys_util:: ioctl:: { ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val} ,
27
+ } ;
21
28
22
29
/// Helper method to obtain the size of the register through its id
23
30
#[ cfg( any( target_arch = "aarch64" , target_arch = "riscv64" ) ) ]
@@ -1983,6 +1990,93 @@ impl VcpuFd {
1983
1990
}
1984
1991
}
1985
1992
1993
+ /// Returns the nested guest state using the `KVM_GET_NESTED_STATE` ioctl.
1994
+ ///
1995
+ /// This only works when `KVM_CAP_NESTED_STATE` is available.
1996
+ ///
1997
+ /// # Arguments
1998
+ ///
1999
+ /// - `buffer`: The buffer to be filled with the new nested state.
2000
+ ///
2001
+ /// # Return Value
2002
+ /// If this returns `None`, KVM doesn't have nested state. Otherwise, the
2003
+ /// actual length of the state is returned.
2004
+ ///
2005
+ /// # Example
2006
+ ///
2007
+ /// ```rust
2008
+ /// # use kvm_ioctls::{Kvm, Cap, KvmNestedStateBuffer};
2009
+ /// let kvm = Kvm::new().unwrap();
2010
+ /// let vm = kvm.create_vm().unwrap();
2011
+ /// let vcpu = vm.create_vcpu(0).unwrap();
2012
+ /// let mut state_buffer = KvmNestedStateBuffer::empty();
2013
+ /// if kvm.check_extension(Cap::NestedState) {
2014
+ /// vcpu.get_nested_state(&mut state_buffer).unwrap();
2015
+ /// // Next, serialize the actual state into a file or so.
2016
+ /// }
2017
+ /// ```
2018
+ ///
2019
+ /// [`Kvm::check_extension_int`]: kvm_ioctls::Kvm::check_extension_int
2020
+ #[ cfg( target_arch = "x86_64" ) ]
2021
+ pub fn get_nested_state (
2022
+ & self ,
2023
+ buffer : & mut KvmNestedStateBuffer ,
2024
+ ) -> Result < Option < NonZeroUsize /* actual length of state */ > > {
2025
+ assert_ne ! ( buffer. size, 0 , "buffer should not report a size of zero" ) ;
2026
+
2027
+ // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel.
2028
+ let ret = unsafe { ioctl_with_mut_ref ( self , KVM_GET_NESTED_STATE ( ) , buffer) } ;
2029
+ match ret {
2030
+ 0 => {
2031
+ let size = buffer. size as usize ;
2032
+ if size == size_of :: < kvm_nested_state /* just the empty header */ > ( ) {
2033
+ Ok ( None )
2034
+ } else {
2035
+ Ok ( Some ( NonZeroUsize :: new ( size) . unwrap ( ) ) )
2036
+ }
2037
+ }
2038
+ _ => Err ( errno:: Error :: last ( ) ) ,
2039
+ }
2040
+ }
2041
+
2042
+ /// Sets the nested guest state using the `KVM_SET_NESTED_STATE` ioctl.
2043
+ ///
2044
+ /// This only works when `KVM_CAP_NESTED_STATE` is available.
2045
+ ///
2046
+ /// # Arguments
2047
+ ///
2048
+ /// - `state`: The new state to be put into KVM. The header must report the
2049
+ /// `size` of the state properly. The state must be retrieved first using
2050
+ /// [`Self::get_nested_state`].
2051
+ ///
2052
+ /// # Example
2053
+ ///
2054
+ /// ```rust
2055
+ /// # use kvm_ioctls::{Kvm, Cap, KvmNestedStateBuffer};
2056
+ /// let kvm = Kvm::new().unwrap();
2057
+ /// let vm = kvm.create_vm().unwrap();
2058
+ /// let vcpu = vm.create_vcpu(0).unwrap();
2059
+ /// if kvm.check_extension(Cap::NestedState) {
2060
+ /// let mut state_buffer = KvmNestedStateBuffer::empty();
2061
+ /// vcpu.get_nested_state(&mut state_buffer).unwrap();
2062
+ /// // Rename the variable to better reflect the role.
2063
+ /// let old_state = state_buffer;
2064
+ ///
2065
+ /// // now assume we transfer the state to a new location
2066
+ /// // and load it back into kvm:
2067
+ /// vcpu.set_nested_state(&old_state).unwrap();
2068
+ /// }
2069
+ /// ```
2070
+ #[ cfg( target_arch = "x86_64" ) ]
2071
+ pub fn set_nested_state ( & self , state : & KvmNestedStateBuffer ) -> Result < ( ) > {
2072
+ // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel.
2073
+ let ret = unsafe { ioctl_with_ref ( self , KVM_SET_NESTED_STATE ( ) , state) } ;
2074
+ match ret {
2075
+ 0 => Ok ( ( ) ) ,
2076
+ _ => Err ( errno:: Error :: last ( ) ) ,
2077
+ }
2078
+ }
2079
+
1986
2080
/// Queues an NMI on the thread's vcpu. Only usable if `KVM_CAP_USER_NMI`
1987
2081
/// is available.
1988
2082
///
@@ -3609,4 +3703,34 @@ mod tests {
3609
3703
assert_eq ! ( addr, ADDR ) ;
3610
3704
assert_eq ! ( data, ( DATA as u16 ) . to_le_bytes( ) ) ;
3611
3705
}
3706
+
3707
+ #[ test]
3708
+ #[ cfg( target_arch = "x86_64" ) ]
3709
+ fn test_get_and_set_nested_state ( ) {
3710
+ let kvm = Kvm :: new ( ) . unwrap ( ) ;
3711
+ let vm = kvm. create_vm ( ) . unwrap ( ) ;
3712
+ let vcpu = vm. create_vcpu ( 0 ) . unwrap ( ) ;
3713
+
3714
+ // Ensure that KVM also during runtime never wants more memory than we have pre-allocated
3715
+ // by the helper type. KVM is expected to report:
3716
+ // - 128+4096==4224 on SVM
3717
+ // - 128+8192==8320 on VMX
3718
+ let kvm_nested_state_size = kvm. check_extension_int ( Cap :: NestedState ) as usize ;
3719
+ assert ! ( kvm_nested_state_size <= size_of:: <KvmNestedStateBuffer >( ) ) ;
3720
+
3721
+ let mut state_buffer = KvmNestedStateBuffer :: default ( ) ;
3722
+ // Ensure that header shows full buffer length.
3723
+ assert_eq ! (
3724
+ state_buffer. size as usize ,
3725
+ size_of:: <KvmNestedStateBuffer >( )
3726
+ ) ;
3727
+
3728
+ vcpu. get_nested_state ( & mut state_buffer) . unwrap ( ) ;
3729
+ let old_state = state_buffer;
3730
+
3731
+ // There is no nested guest in this test, so there is no payload.
3732
+ assert_eq ! ( state_buffer. size as usize , size_of:: <kvm_nested_state>( ) ) ;
3733
+
3734
+ vcpu. set_nested_state ( & old_state) . unwrap ( ) ;
3735
+ }
3612
3736
}
0 commit comments