Skip to content

Commit de5e85a

Browse files
committed
kvm-ioctls: add get_nested_state() and set_nested_state()
These calls are relevant for live-migration and state save/resume when nested virtualization is used. I tested everything with a nested guest in Cloud Hypervisor, but these patches are not yet upstream. Signed-off-by: Philipp Schuster <[email protected]> On-behalf-of: SAP [email protected]
1 parent 3d000de commit de5e85a

File tree

2 files changed

+126
-2
lines changed

2 files changed

+126
-2
lines changed

kvm-ioctls/src/ioctls/vcpu.rs

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
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+
1014
use kvm_bindings::*;
1115
use libc::EINVAL;
1216
use std::fs::File;
@@ -17,7 +21,10 @@ use crate::kvm_ioctls::*;
1721
use vmm_sys_util::errno;
1822
use 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
}

kvm-ioctls/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ pub use ioctls::vcpu::reg_size;
249249
pub use ioctls::vcpu::{HypercallExit, VcpuExit, VcpuFd};
250250

251251
#[cfg(target_arch = "x86_64")]
252-
pub use ioctls::vcpu::{MsrExitReason, ReadMsrExit, SyncReg, WriteMsrExit};
252+
pub use ioctls::vcpu::{KvmNestedStateBuffer, MsrExitReason, ReadMsrExit, SyncReg, WriteMsrExit};
253253

254254
pub use ioctls::vm::{IoEventAddress, NoDatamatch, VmFd};
255255
// The following example is used to verify that our public

0 commit comments

Comments
 (0)