Skip to content

Commit 26ce672

Browse files
phip1611SteelCrab
authored andcommitted
kvm-bindings: init KvmNestedStateBuffer helper
This type is a helper, making the use of get_nested_state() and set_nested_state(), which are added in a following commit, much more convenient. Note that this type's name uses UpperCamelCase as it is not just a plain old data type but actually contains some logic: the `size` field is properly initialized. Effectively, KVM expects a dynamic buffer with a header reporting the size to either store the nested state in or load it from. As such data structures with a certain alignment are challenging to work with (note that Vec<u8> always have an alignment of 1 but we need 4), this type sacrifices a little memory overhead in some cases for better UX; copying 8K once is cheap anyway. Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de> On-behalf-of: SAP philipp.schuster@sap.com
1 parent 9ea14db commit 26ce672

File tree

5 files changed

+164
-1
lines changed

5 files changed

+164
-1
lines changed

kvm-bindings/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ extern crate serde;
1818
#[cfg(feature = "serde")]
1919
extern crate zerocopy;
2020

21+
extern crate core;
22+
2123
#[cfg(feature = "serde")]
2224
#[macro_use]
2325
mod serialize;

kvm-bindings/src/x86_64/bindings.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,6 +2022,10 @@ impl Default for kvm_vmx_nested_state_data {
20222022
}
20232023
#[repr(C)]
20242024
#[derive(Debug, Default, Copy, Clone, PartialEq)]
2025+
#[cfg_attr(
2026+
feature = "serde",
2027+
derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes)
2028+
)]
20252029
pub struct kvm_vmx_nested_state_hdr {
20262030
pub vmxon_pa: __u64,
20272031
pub vmcs12_pa: __u64,
@@ -2032,6 +2036,10 @@ pub struct kvm_vmx_nested_state_hdr {
20322036
}
20332037
#[repr(C)]
20342038
#[derive(Debug, Default, Copy, Clone, PartialEq)]
2039+
#[cfg_attr(
2040+
feature = "serde",
2041+
derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes)
2042+
)]
20352043
pub struct kvm_vmx_nested_state_hdr__bindgen_ty_1 {
20362044
pub flags: __u16,
20372045
}
@@ -2088,6 +2096,10 @@ impl Default for kvm_svm_nested_state_data {
20882096
}
20892097
#[repr(C)]
20902098
#[derive(Debug, Default, Copy, Clone, PartialEq)]
2099+
#[cfg_attr(
2100+
feature = "serde",
2101+
derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes)
2102+
)]
20912103
pub struct kvm_svm_nested_state_hdr {
20922104
pub vmcb_pa: __u64,
20932105
}
@@ -2110,6 +2122,7 @@ pub struct kvm_nested_state {
21102122
}
21112123
#[repr(C)]
21122124
#[derive(Copy, Clone)]
2125+
#[cfg_attr(feature = "serde", derive(zerocopy::Immutable, zerocopy::FromBytes))]
21132126
pub union kvm_nested_state__bindgen_ty_1 {
21142127
pub vmx: kvm_vmx_nested_state_hdr,
21152128
pub svm: kvm_svm_nested_state_hdr,

kvm-bindings/src/x86_64/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub mod bindings;
66
#[cfg(feature = "fam-wrappers")]
77
pub mod fam_wrappers;
88

9+
pub mod nested;
10+
911
#[cfg(feature = "serde")]
1012
mod serialize;
1113

kvm-bindings/src/x86_64/nested.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//! Higher-level abstractions for working with nested state.
2+
//!
3+
//! Getting and setting the nested KVM state is helpful if nested virtualization
4+
//! is used and the state needs to be serialized, e.g., for live-migration or
5+
//! state save/resume. See [`KvmNestedStateBuffer`].
6+
7+
use core::mem;
8+
use KVM_STATE_NESTED_SVM_VMCB_SIZE;
9+
use {kvm_nested_state__bindgen_ty_1, KVM_STATE_NESTED_VMX_VMCS_SIZE};
10+
11+
/// Non-zero variant of the bindgen data union.
12+
///
13+
/// Please note that on SVM, this type wastes one page as the VMX state is
14+
/// larger.
15+
#[derive(Clone, Copy)]
16+
#[cfg_attr(feature = "serde", derive(zerocopy::Immutable, zerocopy::FromBytes))]
17+
#[repr(C)]
18+
pub union kvm_nested_state__data {
19+
pub vmx: kvm_vmx_nested_state_data,
20+
pub svm: kvm_svm_nested_state_data,
21+
}
22+
23+
impl Default for kvm_nested_state__data {
24+
fn default() -> Self {
25+
// SAFETY: Every bit pattern is valid.
26+
unsafe { mem::zeroed() }
27+
}
28+
}
29+
30+
#[derive(Clone, Copy)]
31+
#[cfg_attr(
32+
feature = "serde",
33+
derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes)
34+
)]
35+
#[repr(C)]
36+
pub struct kvm_vmx_nested_state_data {
37+
pub vmcs12: [u8; KVM_STATE_NESTED_VMX_VMCS_SIZE as usize],
38+
pub shadow_vmcs12: [u8; KVM_STATE_NESTED_VMX_VMCS_SIZE as usize],
39+
}
40+
41+
#[derive(Clone, Copy)]
42+
#[cfg_attr(
43+
feature = "serde",
44+
derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes)
45+
)]
46+
#[repr(C)]
47+
pub struct kvm_svm_nested_state_data {
48+
pub vmcb12: [u8; KVM_STATE_NESTED_SVM_VMCB_SIZE as usize],
49+
}
50+
51+
/// A stack-allocated buffer for nested KVM state including the mandatory
52+
/// header with meta-information.
53+
///
54+
/// KVM uses a dynamically sized buffer structure (with a header reporting the
55+
/// size of the buffer/state). This helper type makes working with
56+
/// `get_nested_state()` and `set_nested_state`() significantly more convenient
57+
/// at the cost of a slightly higher memory footprint in some cases.
58+
///
59+
/// # Type Size
60+
///
61+
/// On Intel VMX, the actual state requires `128 + 8192 == 8320` bytes, on
62+
/// AMD SVM, the actual state requires `128 + 4096 == 4224` bytes. This type
63+
/// doesn't make a differentiation and unifies the required memory. By
64+
/// sacrificing a few more bytes on VMX, this type is generally convenient to
65+
/// use.
66+
#[derive(Clone, Copy)]
67+
#[cfg_attr(
68+
feature = "serde",
69+
derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes)
70+
)]
71+
#[repr(C)]
72+
#[non_exhaustive] // Prevent constructor bypass in public API.
73+
pub struct KvmNestedStateBuffer {
74+
pub flags: u16,
75+
pub format: u16,
76+
pub size: u32,
77+
pub hdr: kvm_nested_state__bindgen_ty_1,
78+
pub data: kvm_nested_state__data,
79+
}
80+
81+
impl KvmNestedStateBuffer {
82+
/// Creates a new empty buffer, ready for nested state to be stored in by KVM.
83+
///
84+
/// The `size` property will report the size of the buffer to KVM.
85+
pub fn empty() -> Self {
86+
// SAFETY: Every bit pattern is valid.
87+
let mut this: KvmNestedStateBuffer = unsafe { mem::zeroed() };
88+
// This way, KVM knows the size of the buffer to store state into.
89+
// See: https://elixir.bootlin.com/linux/v6.12/source/arch/x86/kvm/x86.c#L6193
90+
this.size = size_of::<Self>() as u32;
91+
this
92+
}
93+
}
94+
95+
impl Default for KvmNestedStateBuffer {
96+
fn default() -> Self {
97+
Self::empty()
98+
}
99+
}
100+
101+
#[cfg(test)]
102+
mod tests {
103+
use super::*;
104+
105+
use crate::kvm_nested_state as kvm_nested_state_raw_binding;
106+
107+
#[test]
108+
fn test_layout() {
109+
assert_eq!(
110+
align_of::<kvm_nested_state_raw_binding>(),
111+
align_of::<KvmNestedStateBuffer>()
112+
);
113+
assert!(size_of::<KvmNestedStateBuffer>() > size_of::<kvm_nested_state_raw_binding>());
114+
// When this fails/changes, we should re-evaluate the overall types and API
115+
assert_eq!(size_of::<KvmNestedStateBuffer>(), 8320);
116+
}
117+
}

kvm-bindings/src/x86_64/serialize.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use bindings::{
1010
kvm_xcr, kvm_xcrs, kvm_xsave,
1111
};
1212
use fam_wrappers::kvm_xsave2;
13+
use kvm_nested_state__bindgen_ty_1;
14+
use nested::{kvm_nested_state__data, KvmNestedStateBuffer};
1315
use serde::{Deserialize, Deserializer, Serialize, Serializer};
1416
use zerocopy::{transmute, FromBytes, FromZeros, Immutable, IntoBytes};
1517

@@ -35,7 +37,8 @@ serde_impls!(
3537
kvm_xsave2,
3638
kvm_irqchip,
3739
kvm_irq_routing,
38-
kvm_irq_routing_entry
40+
kvm_irq_routing_entry,
41+
KvmNestedStateBuffer
3942
);
4043

4144
// SAFETY: zerocopy's derives explicitly disallow deriving for unions where
@@ -122,6 +125,30 @@ unsafe impl IntoBytes for kvm_irq_routing_entry__bindgen_ty_1 {
122125
}
123126
}
124127

128+
// SAFETY: zerocopy's derives explicitly disallow deriving for unions where
129+
// the fields have different sizes, due to the smaller fields having padding.
130+
// Miri however does not complain about these implementations (e.g. about
131+
// reading the "padding" for one union field as valid data for a bigger one)
132+
unsafe impl IntoBytes for kvm_nested_state__bindgen_ty_1 {
133+
fn only_derive_is_allowed_to_implement_this_trait()
134+
where
135+
Self: Sized,
136+
{
137+
}
138+
}
139+
140+
// SAFETY: zerocopy's derives explicitly disallow deriving for unions where
141+
// the fields have different sizes, due to the smaller fields having padding.
142+
// Miri however does not complain about these implementations (e.g. about
143+
// reading the "padding" for one union field as valid data for a bigger one)
144+
unsafe impl IntoBytes for kvm_nested_state__data {
145+
fn only_derive_is_allowed_to_implement_this_trait()
146+
where
147+
Self: Sized,
148+
{
149+
}
150+
}
151+
125152
#[cfg(test)]
126153
mod tests {
127154
use super::*;
@@ -182,6 +209,7 @@ mod tests {
182209
is_serde::<kvm_mp_state>();
183210
is_serde::<kvm_irq_routing>();
184211
is_serde::<kvm_irq_routing_entry>();
212+
is_serde::<KvmNestedStateBuffer>();
185213
}
186214

187215
fn is_serde_json<T: Serialize + for<'de> Deserialize<'de> + Default>() {
@@ -216,5 +244,6 @@ mod tests {
216244
is_serde_json::<kvm_mp_state>();
217245
is_serde_json::<kvm_irq_routing>();
218246
is_serde_json::<kvm_irq_routing_entry>();
247+
is_serde_json::<KvmNestedStateBuffer>();
219248
}
220249
}

0 commit comments

Comments
 (0)