Skip to content

Commit cb80843

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 58449cd commit cb80843

File tree

2 files changed

+120
-1
lines changed

2 files changed

+120
-1
lines changed

kvm-ioctls/src/ioctls/vcpu.rs

Lines changed: 119 additions & 0 deletions
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::KvmNestedState;
13+
1014
use kvm_bindings::*;
1115
use libc::EINVAL;
1216
use std::fs::File;
@@ -1983,6 +1987,94 @@ impl VcpuFd {
19831987
}
19841988
}
19851989

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

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::{KvmNestedState, 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)