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