Skip to content

Commit bc07f2c

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 02b62c8 commit bc07f2c

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

kvm-ioctls/src/ioctls/vcpu.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,126 @@ impl VcpuFd {
19831983
}
19841984
}
19851985

1986+
/// Returns the nested guest state using the `KVM_GET_NESTED_STATE` ioctl.
1987+
///
1988+
/// This only works when `KVM_CAP_NESTED_STATE` is available.
1989+
///
1990+
/// # Arguments
1991+
///
1992+
/// - `state_buffer`: The buffer to be filled with the new [`kvm_nested_state`]. For example,
1993+
/// this can be a properly aligned buffer with the exact needed length as reported by
1994+
/// [`Kvm::check_extension_int`], or the helper type [`KvmNestedState`].
1995+
///
1996+
/// # Return Value
1997+
/// If this returns `None`, KVM doesn't has nested state. Otherwise, the length of
1998+
/// the actual state is returned.
1999+
///
2000+
/// # Example
2001+
///
2002+
/// ```rust
2003+
/// # use kvm_ioctls::{Kvm, Cap, KvmNestedStateBuffer};
2004+
/// let kvm = Kvm::new().unwrap();
2005+
/// let vm = kvm.create_vm().unwrap();
2006+
/// let vcpu = vm.create_vcpu(0).unwrap();
2007+
/// let mut state_buffer = KvmNestedStateBuffer::new_empty();
2008+
/// if kvm.check_extension(Cap::NestedState) {
2009+
/// vcpu.get_nested_state(&mut state_buffer).unwrap();
2010+
/// // Next, serialize the actual state.
2011+
/// let _actual_state = state_buffer.as_actual_raw_state();
2012+
/// }
2013+
/// ```
2014+
///
2015+
/// [`Kvm::check_extension_int`]: kvm_ioctls::Kvm::check_extension_int
2016+
#[cfg(target_arch = "x86_64")]
2017+
pub fn get_nested_state<D: AsMut<[u8]>>(
2018+
&self,
2019+
mut buffer: D,
2020+
) -> Result<Option<usize /* length of state */>> {
2021+
let buffer = buffer.as_mut();
2022+
2023+
// Check alignment
2024+
assert_eq!(
2025+
buffer.as_ptr().align_offset(align_of::<kvm_nested_state>()),
2026+
0
2027+
);
2028+
assert!(
2029+
buffer.len() > size_of::<kvm_nested_state>(),
2030+
"Buffer must have capacity for payload"
2031+
);
2032+
2033+
// SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel.
2034+
let ret = unsafe { ioctl_with_mut_ptr(self, KVM_GET_NESTED_STATE(), buffer.as_mut_ptr()) };
2035+
match ret {
2036+
0 => {
2037+
// SAFETY: We know the data is initialized and we asserted the size.
2038+
let hdr = unsafe { buffer.as_ptr().cast::<kvm_nested_state>().as_ref().unwrap() };
2039+
let size = hdr.size as usize;
2040+
if size == size_of::<kvm_nested_state>() {
2041+
Ok(None)
2042+
} else {
2043+
Ok(Some(size))
2044+
}
2045+
}
2046+
_ => Err(errno::Error::last()),
2047+
}
2048+
}
2049+
2050+
/// Sets the nested guest state using the `KVM_SET_NESTED_STATE` ioctl.
2051+
///
2052+
/// This only works when `KVM_CAP_NESTED_STATE` is available.
2053+
///
2054+
/// # Arguments
2055+
///
2056+
/// - `state`: The new [`kvm_nested_state`]. The header must report the
2057+
/// `size` of the state to KVM accordingly.
2058+
///
2059+
/// # Example
2060+
///
2061+
/// ```rust
2062+
/// # use kvm_ioctls::{Kvm, Cap, KvmNestedStateBuffer};
2063+
/// let kvm = Kvm::new().unwrap();
2064+
/// let vm = kvm.create_vm().unwrap();
2065+
/// let vcpu = vm.create_vcpu(0).unwrap();
2066+
/// if kvm.check_extension(Cap::NestedState) {
2067+
/// let mut state_buffer = KvmNestedStateBuffer::new_empty();
2068+
/// vcpu.get_nested_state(&mut state_buffer).unwrap();
2069+
/// let old_state: &[u8] = state_buffer.as_actual_raw_state();
2070+
///
2071+
/// // now assume we transfer the state to a new location
2072+
/// // and load it back into kvm:
2073+
/// vcpu.set_nested_state(old_state).unwrap();
2074+
/// }
2075+
/// ```
2076+
#[cfg(target_arch = "x86_64")]
2077+
pub fn set_nested_state<D: AsRef<[u8]>>(&self, state: D) -> Result<()> {
2078+
let state = state.as_ref();
2079+
2080+
// Check header
2081+
{
2082+
// Check alignment
2083+
assert_eq!(
2084+
state.as_ptr().align_offset(align_of::<kvm_nested_state>()),
2085+
0
2086+
);
2087+
assert!(
2088+
state.len() >= size_of::<kvm_nested_state>(),
2089+
"Buffer must have capacity for payload"
2090+
);
2091+
2092+
// SAFETY: We know the data is initialized and we asserted the size.
2093+
let hdr = unsafe { state.as_ptr().cast::<kvm_nested_state>().as_ref().unwrap() };
2094+
assert!(hdr.size as usize >= size_of::<kvm_nested_state>());
2095+
assert!(hdr.size as usize <= state.len());
2096+
}
2097+
2098+
// SAFETY: Safe because we call this with a Vcpu fd and we trust the kernel.
2099+
let ret = unsafe { ioctl_with_ptr(self, KVM_SET_NESTED_STATE(), state.as_ptr()) };
2100+
match ret {
2101+
0 => Ok(()),
2102+
_ => Err(errno::Error::last()),
2103+
}
2104+
}
2105+
19862106
/// Queues an NMI on the thread's vcpu. Only usable if `KVM_CAP_USER_NMI`
19872107
/// is available.
19882108
///
@@ -2087,6 +2207,7 @@ mod tests {
20872207
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
20882208
use crate::cap::Cap;
20892209
use crate::ioctls::system::Kvm;
2210+
use crate::KvmNestedStateBuffer;
20902211
use std::ptr::NonNull;
20912212

20922213
// Helper function for memory mapping `size` bytes of anonymous memory.
@@ -3931,4 +4052,41 @@ mod tests {
39314052
assert_eq!(addr, ADDR);
39324053
assert_eq!(data, (DATA as u16).to_le_bytes());
39334054
}
4055+
4056+
#[test]
4057+
#[cfg(target_arch = "x86_64")]
4058+
fn test_get_and_set_nested_state() {
4059+
assert_eq!(
4060+
align_of::<KvmNestedStateBuffer>(),
4061+
align_of::<kvm_nested_state>()
4062+
);
4063+
// The type holding all payload must be bigger than just the header
4064+
assert!(size_of::<KvmNestedStateBuffer>() > size_of::<kvm_nested_state>());
4065+
4066+
let kvm = Kvm::new().unwrap();
4067+
let vm = kvm.create_vm().unwrap();
4068+
let vcpu = vm.create_vcpu(0).unwrap();
4069+
4070+
// Ensure that KVM also during runtime never wants more memory than we have pre-allocated
4071+
// by the helper type. KVM is expected to report:
4072+
// - 128+4096==4224 on SVM
4073+
// - 128+8192==8320 on VMX
4074+
let kvm_nested_state_size = kvm.check_extension_int(Cap::NestedState) as usize;
4075+
assert!(kvm_nested_state_size <= size_of::<KvmNestedStateBuffer>());
4076+
4077+
let mut state_buffer = KvmNestedStateBuffer::default();
4078+
// Ensure that header shows full buffer length.
4079+
assert_eq!(
4080+
state_buffer.header.size as usize,
4081+
size_of::<KvmNestedStateBuffer>()
4082+
);
4083+
4084+
vcpu.get_nested_state(&mut state_buffer).unwrap();
4085+
let old_state = state_buffer;
4086+
// Currently there is no nested guest, so there is no payload.
4087+
assert_eq!(
4088+
old_state.header.size as usize,
4089+
size_of::<kvm_nested_state>()
4090+
);
4091+
}
39344092
}

0 commit comments

Comments
 (0)