@@ -1983,6 +1983,126 @@ impl VcpuFd {
1983
1983
}
1984
1984
}
1985
1985
1986
+ /// Returns the nested guest state using the `KVM_GET_NESTED_STATE` ioctl.
1987
+ ///
1988
+ /// This only works when `KVM_CAP_NESTED_STATE` is available.
1989
+ ///
1990
+ /// # Arguments
1991
+ ///
1992
+ /// - `state_buffer`: The buffer to be filled with the new [`kvm_nested_state`]. For example,
1993
+ /// this can be a properly aligned buffer with the exact needed length as reported by
1994
+ /// [`Kvm::check_extension_int`], or the helper type [`KvmNestedState`].
1995
+ ///
1996
+ /// # Return Value
1997
+ /// If this returns `None`, KVM doesn't has nested state. Otherwise, the length of
1998
+ /// the actual state is returned.
1999
+ ///
2000
+ /// # Example
2001
+ ///
2002
+ /// ```rust
2003
+ /// # use kvm_ioctls::{Kvm, Cap, KvmNestedStateBuffer};
2004
+ /// let kvm = Kvm::new().unwrap();
2005
+ /// let vm = kvm.create_vm().unwrap();
2006
+ /// let vcpu = vm.create_vcpu(0).unwrap();
2007
+ /// let mut state_buffer = KvmNestedStateBuffer::new_empty();
2008
+ /// if kvm.check_extension(Cap::NestedState) {
2009
+ /// vcpu.get_nested_state(&mut state_buffer).unwrap();
2010
+ /// // Next, serialize the actual state.
2011
+ /// let _actual_state = state_buffer.as_actual_raw_state();
2012
+ /// }
2013
+ /// ```
2014
+ ///
2015
+ /// [`Kvm::check_extension_int`]: kvm_ioctls::Kvm::check_extension_int
2016
+ #[ cfg( target_arch = "x86_64" ) ]
2017
+ pub fn get_nested_state < D : AsMut < [ u8 ] > > (
2018
+ & self ,
2019
+ mut buffer : D ,
2020
+ ) -> Result < Option < usize /* length of state */ > > {
2021
+ let buffer = buffer. as_mut ( ) ;
2022
+
2023
+ // Check alignment
2024
+ assert_eq ! (
2025
+ buffer. as_ptr( ) . align_offset( align_of:: <kvm_nested_state>( ) ) ,
2026
+ 0
2027
+ ) ;
2028
+ assert ! (
2029
+ buffer. len( ) > size_of:: <kvm_nested_state>( ) ,
2030
+ "Buffer must have capacity for payload"
2031
+ ) ;
2032
+
2033
+ // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel.
2034
+ let ret = unsafe { ioctl_with_mut_ptr ( self , KVM_GET_NESTED_STATE ( ) , buffer. as_mut_ptr ( ) ) } ;
2035
+ match ret {
2036
+ 0 => {
2037
+ // SAFETY: We know the data is initialized and we asserted the size.
2038
+ let hdr = unsafe { buffer. as_ptr ( ) . cast :: < kvm_nested_state > ( ) . as_ref ( ) . unwrap ( ) } ;
2039
+ let size = hdr. size as usize ;
2040
+ if size == size_of :: < kvm_nested_state > ( ) {
2041
+ Ok ( None )
2042
+ } else {
2043
+ Ok ( Some ( size) )
2044
+ }
2045
+ }
2046
+ _ => Err ( errno:: Error :: last ( ) ) ,
2047
+ }
2048
+ }
2049
+
2050
+ /// Sets the nested guest state using the `KVM_SET_NESTED_STATE` ioctl.
2051
+ ///
2052
+ /// This only works when `KVM_CAP_NESTED_STATE` is available.
2053
+ ///
2054
+ /// # Arguments
2055
+ ///
2056
+ /// - `state`: The new [`kvm_nested_state`]. The header must report the
2057
+ /// `size` of the state to KVM accordingly.
2058
+ ///
2059
+ /// # Example
2060
+ ///
2061
+ /// ```rust
2062
+ /// # use kvm_ioctls::{Kvm, Cap, KvmNestedStateBuffer};
2063
+ /// let kvm = Kvm::new().unwrap();
2064
+ /// let vm = kvm.create_vm().unwrap();
2065
+ /// let vcpu = vm.create_vcpu(0).unwrap();
2066
+ /// if kvm.check_extension(Cap::NestedState) {
2067
+ /// let mut state_buffer = KvmNestedStateBuffer::new_empty();
2068
+ /// vcpu.get_nested_state(&mut state_buffer).unwrap();
2069
+ /// let old_state: &[u8] = state_buffer.as_actual_raw_state();
2070
+ ///
2071
+ /// // now assume we transfer the state to a new location
2072
+ /// // and load it back into kvm:
2073
+ /// vcpu.set_nested_state(old_state).unwrap();
2074
+ /// }
2075
+ /// ```
2076
+ #[ cfg( target_arch = "x86_64" ) ]
2077
+ pub fn set_nested_state < D : AsRef < [ u8 ] > > ( & self , state : D ) -> Result < ( ) > {
2078
+ let state = state. as_ref ( ) ;
2079
+
2080
+ // Check header
2081
+ {
2082
+ // Check alignment
2083
+ assert_eq ! (
2084
+ state. as_ptr( ) . align_offset( align_of:: <kvm_nested_state>( ) ) ,
2085
+ 0
2086
+ ) ;
2087
+ assert ! (
2088
+ state. len( ) >= size_of:: <kvm_nested_state>( ) ,
2089
+ "Buffer must have capacity for payload"
2090
+ ) ;
2091
+
2092
+ // SAFETY: We know the data is initialized and we asserted the size.
2093
+ let hdr = unsafe { state. as_ptr ( ) . cast :: < kvm_nested_state > ( ) . as_ref ( ) . unwrap ( ) } ;
2094
+ assert ! ( hdr. size as usize >= size_of:: <kvm_nested_state>( ) ) ;
2095
+ assert ! ( hdr. size as usize <= state. len( ) ) ;
2096
+ }
2097
+
2098
+ // SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel.
2099
+ let ret = unsafe { ioctl_with_ptr ( self , KVM_SET_NESTED_STATE ( ) , state. as_ptr ( ) ) } ;
2100
+ match ret {
2101
+ 0 => Ok ( ( ) ) ,
2102
+ _ => Err ( errno:: Error :: last ( ) ) ,
2103
+ }
2104
+ }
2105
+
1986
2106
/// Queues an NMI on the thread's vcpu. Only usable if `KVM_CAP_USER_NMI`
1987
2107
/// is available.
1988
2108
///
@@ -2087,6 +2207,7 @@ mod tests {
2087
2207
#[ cfg( any( target_arch = "x86_64" , target_arch = "aarch64" ) ) ]
2088
2208
use crate :: cap:: Cap ;
2089
2209
use crate :: ioctls:: system:: Kvm ;
2210
+ use crate :: KvmNestedStateBuffer ;
2090
2211
use std:: ptr:: NonNull ;
2091
2212
2092
2213
// Helper function for memory mapping `size` bytes of anonymous memory.
@@ -3931,4 +4052,41 @@ mod tests {
3931
4052
assert_eq ! ( addr, ADDR ) ;
3932
4053
assert_eq ! ( data, ( DATA as u16 ) . to_le_bytes( ) ) ;
3933
4054
}
4055
+
4056
+ #[ test]
4057
+ #[ cfg( target_arch = "x86_64" ) ]
4058
+ fn test_get_and_set_nested_state ( ) {
4059
+ assert_eq ! (
4060
+ align_of:: <KvmNestedStateBuffer >( ) ,
4061
+ align_of:: <kvm_nested_state>( )
4062
+ ) ;
4063
+ // The type holding all payload must be bigger than just the header
4064
+ assert ! ( size_of:: <KvmNestedStateBuffer >( ) > size_of:: <kvm_nested_state>( ) ) ;
4065
+
4066
+ let kvm = Kvm :: new ( ) . unwrap ( ) ;
4067
+ let vm = kvm. create_vm ( ) . unwrap ( ) ;
4068
+ let vcpu = vm. create_vcpu ( 0 ) . unwrap ( ) ;
4069
+
4070
+ // Ensure that KVM also during runtime never wants more memory than we have pre-allocated
4071
+ // by the helper type. KVM is expected to report:
4072
+ // - 128+4096==4224 on SVM
4073
+ // - 128+8192==8320 on VMX
4074
+ let kvm_nested_state_size = kvm. check_extension_int ( Cap :: NestedState ) as usize ;
4075
+ assert ! ( kvm_nested_state_size <= size_of:: <KvmNestedStateBuffer >( ) ) ;
4076
+
4077
+ let mut state_buffer = KvmNestedStateBuffer :: default ( ) ;
4078
+ // Ensure that header shows full buffer length.
4079
+ assert_eq ! (
4080
+ state_buffer. header. size as usize ,
4081
+ size_of:: <KvmNestedStateBuffer >( )
4082
+ ) ;
4083
+
4084
+ vcpu. get_nested_state ( & mut state_buffer) . unwrap ( ) ;
4085
+ let old_state = state_buffer;
4086
+ // Currently there is no nested guest, so there is no payload.
4087
+ assert_eq ! (
4088
+ old_state. header. size as usize ,
4089
+ size_of:: <kvm_nested_state>( )
4090
+ ) ;
4091
+ }
3934
4092
}
0 commit comments