Skip to content

Commit b526040

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 d55b2e5 commit b526040

File tree

2 files changed

+124
-1
lines changed

2 files changed

+124
-1
lines changed

kvm-ioctls/src/ioctls/vcpu.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
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+
use std::num::NonZeroUsize;
1318
use std::os::unix::io::{AsRawFd, RawFd};
1419

1520
use crate::ioctls::{KvmCoalescedIoRing, KvmRunWrapper, Result};
@@ -1983,6 +1988,94 @@ impl VcpuFd {
19831988
}
19841989
}
19851990

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+
19862079
/// Queues an NMI on the thread's vcpu. Only usable if `KVM_CAP_USER_NMI`
19872080
/// is available.
19882081
///
@@ -3609,4 +3702,34 @@ mod tests {
36093702
assert_eq!(addr, ADDR);
36103703
assert_eq!(data, (DATA as u16).to_le_bytes());
36113704
}
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+
}
36123735
}

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)