77// Use of this source code is governed by a BSD-style license that can be
88// found in the THIRD-PARTY file.
99
10+ // Part of public API
11+ #[ cfg( target_arch = "x86_64" ) ]
12+ pub use kvm_bindings:: nested:: KvmNestedStateBuffer ;
13+
1014use kvm_bindings:: * ;
1115use libc:: EINVAL ;
1216use std:: fs:: File ;
@@ -17,7 +21,10 @@ use crate::kvm_ioctls::*;
1721use vmm_sys_util:: errno;
1822use vmm_sys_util:: ioctl:: { ioctl, ioctl_with_mut_ref, ioctl_with_ref} ;
1923#[ 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+ } ;
2128
2229/// Helper method to obtain the size of the register through its id
2330#[ cfg( any( target_arch = "aarch64" , target_arch = "riscv64" ) ) ]
@@ -1983,6 +1990,93 @@ impl VcpuFd {
19831990 }
19841991 }
19851992
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+
19862080 /// Queues an NMI on the thread's vcpu. Only usable if `KVM_CAP_USER_NMI`
19872081 /// is available.
19882082 ///
@@ -3609,4 +3703,34 @@ mod tests {
36093703 assert_eq ! ( addr, ADDR ) ;
36103704 assert_eq ! ( data, ( DATA as u16 ) . to_le_bytes( ) ) ;
36113705 }
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+ }
36123736}
0 commit comments