|
| 1 | +// Copyright © 2025 Cyberus Technology GmbH |
| 2 | +// |
| 3 | +// SPDX-License-Identifier: Apache-2.0 OR MIT |
| 4 | + |
| 5 | +//! Module for working with nested KVM state. |
| 6 | +//! |
| 7 | +//! Getting and setting the nested KVM state is helpful if nested virtualization |
| 8 | +//! is used and the state needs to be serialized, e.g., for live-migration or |
| 9 | +//! state save/resume. The main export is [`KvmNestedStateBuffer`]. |
| 10 | +
|
| 11 | +use kvm_bindings::{kvm_nested_state, kvm_svm_nested_state_data, kvm_vmx_nested_state_data}; |
| 12 | +use std::fmt::{Debug, Formatter}; |
| 13 | +use std::{cmp, mem}; |
| 14 | + |
| 15 | +/// Helper type for [`KvmNestedStateBuffer`] unifying the actual state according |
| 16 | +/// to KVM bindings. |
| 17 | +/// |
| 18 | +/// Please note that on SVM, this type wastes one page as the VMX state is |
| 19 | +/// larger. |
| 20 | +#[cfg(target_arch = "x86_64")] |
| 21 | +#[repr(C)] |
| 22 | +#[derive(Copy, Clone)] |
| 23 | +pub(crate) union KvmNestedStateData { |
| 24 | + pub vmx_state: kvm_vmx_nested_state_data, |
| 25 | + pub svm_state: kvm_svm_nested_state_data, |
| 26 | +} |
| 27 | + |
| 28 | +impl Debug for KvmNestedStateData { |
| 29 | + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 30 | + write!(f, "KvmNestedStateData({} bytes)", size_of::<Self>()) |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +/// A stack-allocated buffer for nested KVM state. |
| 35 | +/// |
| 36 | +/// KVM uses a dynamically sized buffer structure (with a header reporting the |
| 37 | +/// size of the buffer/state) making it cumbersome to work with. This helper |
| 38 | +/// type makes working with [`get_nested_state`] and [`set_nested_state`] |
| 39 | +/// significantly more convenient at the cost of a slightly higher memory |
| 40 | +/// footprint in some cases. Unlike the fixed header [`kvm_nested_state`], |
| 41 | +/// this type acts as a stack buffer capable of holding all possible state. |
| 42 | +/// |
| 43 | +/// The implementations for [`AsRef<[u8]>`] and [`AsMut<[u8]>`] refer to the |
| 44 | +/// entire buffer. To only get the state size as reported by the header, use |
| 45 | +/// [`Self::as_raw_state`]. |
| 46 | +/// |
| 47 | +/// # Type Size |
| 48 | +/// |
| 49 | +/// On Intel VMX, the actual state requires `128 + 8192 == 8320` bytes, on |
| 50 | +/// AMD SVM, the actual state requires `128 + 4096 == 4224` bytes. This type |
| 51 | +/// doesn't make a differentiation and unifies the required memory. By |
| 52 | +/// sacrificing a few more bytes on VMX, this type is more convenient to use. |
| 53 | +/// |
| 54 | +/// [`get_nested_state`]: crate::VcpuFd::get_nested_state |
| 55 | +/// [`set_nested_state`]: crate::VcpuFd::set_nested_state |
| 56 | +#[repr(C)] |
| 57 | +#[derive(Debug)] |
| 58 | +pub struct KvmNestedStateBuffer { |
| 59 | + /// The fixed header from the KVM binding. |
| 60 | + pub(crate) header: kvm_nested_state, |
| 61 | + /// The actual payload. |
| 62 | + pub(crate) data: KvmNestedStateData, |
| 63 | +} |
| 64 | + |
| 65 | +impl KvmNestedStateBuffer { |
| 66 | + /// Creates a new type which acts as empty ready-to-use buffer for |
| 67 | + /// [`get_nested_state`]. |
| 68 | + /// |
| 69 | + /// [`get_nested_state`]: crate::VcpuFd::get_nested_state |
| 70 | + pub fn new_empty() -> Self { |
| 71 | + // SAFETY: properly initialized `u8` values to zero |
| 72 | + let mut base = unsafe { mem::zeroed::<KvmNestedStateBuffer>() }; |
| 73 | + // This is a sane default as KVM uses this field to know how many bytes |
| 74 | + // are allocated for it. |
| 75 | + base.header.size = size_of::<Self>() as _; |
| 76 | + base |
| 77 | + } |
| 78 | + |
| 79 | + /// Returns the actual complete raw state, suited for serialization. |
| 80 | + /// |
| 81 | + /// This is useful to serialize the actual state without wasting unused |
| 82 | + /// buffer capacity on SVM. On VMX, this is as good as [`Self::as_ref::<[u8]>`] |
| 83 | + /// |
| 84 | + /// This is supposed to be called **after** [`Vcpu::get_nested_state`] |
| 85 | + /// properly stored the actual state into the buffer. Then, when the |
| 86 | + /// header is properly set, this method also respects the length as |
| 87 | + /// reported by the header, effectively not returning unused memory |
| 88 | + /// for VMX. |
| 89 | + pub fn as_actual_raw_state(&self) -> &[u8] { |
| 90 | + let len = self.header.size as usize; |
| 91 | + |
| 92 | + // Ensure there is at least the header. |
| 93 | + // It is okay if this is empty |
| 94 | + // -> When no nested nirtualization was used. |
| 95 | + assert!(len >= size_of::<kvm_nested_state>()); |
| 96 | + // Ensure there is no invalid size |
| 97 | + assert!(len <= size_of::<Self>()); |
| 98 | + |
| 99 | + unsafe { |
| 100 | + let ptr = core::ptr::addr_of!(*self); |
| 101 | + core::slice::from_raw_parts(ptr.cast::<u8>(), len) |
| 102 | + } |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +impl Clone for KvmNestedStateBuffer { |
| 107 | + fn clone(&self) -> Self { |
| 108 | + let mut header_clone: kvm_nested_state = unsafe { mem::zeroed() }; |
| 109 | + // SAFETY: header is initialized and sized |
| 110 | + unsafe { core::ptr::copy_nonoverlapping(&self.header, &mut header_clone, 1) }; |
| 111 | + Self { |
| 112 | + header: header_clone, |
| 113 | + data: self.data.clone(), |
| 114 | + } |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +#[cfg(target_arch = "x86_64")] |
| 119 | +impl Default for KvmNestedStateBuffer { |
| 120 | + fn default() -> Self { |
| 121 | + Self::new_empty() |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +impl AsRef<[u8]> for KvmNestedStateBuffer { |
| 126 | + fn as_ref(&self) -> &[u8] { |
| 127 | + let ptr = core::ptr::addr_of!(*self); |
| 128 | + let len = cmp::min(size_of::<Self>(), self.header.size as usize); |
| 129 | + // SAFETY: The reference is initialized and we checked the length. |
| 130 | + unsafe { core::slice::from_raw_parts(ptr.cast::<u8>(), len) } |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +impl AsMut<[u8]> for KvmNestedStateBuffer { |
| 135 | + fn as_mut(&mut self) -> &mut [u8] { |
| 136 | + let ptr = core::ptr::addr_of_mut!(*self); |
| 137 | + let len = cmp::min(size_of::<Self>(), self.header.size as usize); |
| 138 | + // SAFETY: The reference is initialized and we checked the length. |
| 139 | + unsafe { core::slice::from_raw_parts_mut(ptr.cast::<u8>(), len) } |
| 140 | + } |
| 141 | +} |
0 commit comments