diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index d6142552..481a0395 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 89.00, + "coverage_score": 88.17, "exclude_path": ".*bindings\\.rs", "crate_features": "fam-wrappers,serde" } diff --git a/kvm-bindings/CHANGELOG.md b/kvm-bindings/CHANGELOG.md index 679a0471..4916a1ae 100644 --- a/kvm-bindings/CHANGELOG.md +++ b/kvm-bindings/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] +## [0.12.1] + +### Added + +- Added `KvmNestedStateBuffer` + ### Added ### Changed diff --git a/kvm-bindings/Cargo.toml b/kvm-bindings/Cargo.toml index c5aabf79..ec946d7f 100644 --- a/kvm-bindings/Cargo.toml +++ b/kvm-bindings/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kvm-bindings" -version = "0.12.0" +version = "0.12.1" authors = ["Amazon firecracker team "] description = "Rust FFI bindings to KVM generated using bindgen." repository = "https://github.com/rust-vmm/kvm" diff --git a/kvm-bindings/src/lib.rs b/kvm-bindings/src/lib.rs index c44c98e6..dbe11cd8 100644 --- a/kvm-bindings/src/lib.rs +++ b/kvm-bindings/src/lib.rs @@ -18,6 +18,8 @@ extern crate serde; #[cfg(feature = "serde")] extern crate zerocopy; +extern crate core; + #[cfg(feature = "serde")] #[macro_use] mod serialize; diff --git a/kvm-bindings/src/x86_64/bindings.rs b/kvm-bindings/src/x86_64/bindings.rs index 87799058..5dc56977 100644 --- a/kvm-bindings/src/x86_64/bindings.rs +++ b/kvm-bindings/src/x86_64/bindings.rs @@ -1999,6 +1999,10 @@ impl Default for kvm_vmx_nested_state_data { } #[repr(C)] #[derive(Debug, Default, Copy, Clone, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes) +)] pub struct kvm_vmx_nested_state_hdr { pub vmxon_pa: __u64, pub vmcs12_pa: __u64, @@ -2009,6 +2013,10 @@ pub struct kvm_vmx_nested_state_hdr { } #[repr(C)] #[derive(Debug, Default, Copy, Clone, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes) +)] pub struct kvm_vmx_nested_state_hdr__bindgen_ty_1 { pub flags: __u16, } @@ -2065,6 +2073,10 @@ impl Default for kvm_svm_nested_state_data { } #[repr(C)] #[derive(Debug, Default, Copy, Clone, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes) +)] pub struct kvm_svm_nested_state_hdr { pub vmcb_pa: __u64, } @@ -2087,6 +2099,7 @@ pub struct kvm_nested_state { } #[repr(C)] #[derive(Copy, Clone)] +#[cfg_attr(feature = "serde", derive(zerocopy::Immutable, zerocopy::FromBytes))] pub union kvm_nested_state__bindgen_ty_1 { pub vmx: kvm_vmx_nested_state_hdr, pub svm: kvm_svm_nested_state_hdr, diff --git a/kvm-bindings/src/x86_64/mod.rs b/kvm-bindings/src/x86_64/mod.rs index 08e1bab8..85c8dfe0 100644 --- a/kvm-bindings/src/x86_64/mod.rs +++ b/kvm-bindings/src/x86_64/mod.rs @@ -6,6 +6,8 @@ pub mod bindings; #[cfg(feature = "fam-wrappers")] pub mod fam_wrappers; +pub mod nested; + #[cfg(feature = "serde")] mod serialize; diff --git a/kvm-bindings/src/x86_64/nested.rs b/kvm-bindings/src/x86_64/nested.rs new file mode 100644 index 00000000..f53be53d --- /dev/null +++ b/kvm-bindings/src/x86_64/nested.rs @@ -0,0 +1,117 @@ +//! Higher-level abstractions for working with nested state. +//! +//! Getting and setting the nested KVM state is helpful if nested virtualization +//! is used and the state needs to be serialized, e.g., for live-migration or +//! state save/resume. See [`KvmNestedStateBuffer`]. + +use core::mem; +use KVM_STATE_NESTED_SVM_VMCB_SIZE; +use {kvm_nested_state__bindgen_ty_1, KVM_STATE_NESTED_VMX_VMCS_SIZE}; + +/// Non-zero variant of the bindgen data union. +/// +/// Please note that on SVM, this type wastes one page as the VMX state is +/// larger. +#[derive(Clone, Copy)] +#[cfg_attr(feature = "serde", derive(zerocopy::Immutable, zerocopy::FromBytes))] +#[repr(C)] +pub union kvm_nested_state__data { + pub vmx: kvm_vmx_nested_state_data, + pub svm: kvm_svm_nested_state_data, +} + +impl Default for kvm_nested_state__data { + fn default() -> Self { + // SAFETY: Every bit pattern is valid. + unsafe { mem::zeroed() } + } +} + +#[derive(Clone, Copy)] +#[cfg_attr( + feature = "serde", + derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes) +)] +#[repr(C)] +pub struct kvm_vmx_nested_state_data { + pub vmcs12: [u8; KVM_STATE_NESTED_VMX_VMCS_SIZE as usize], + pub shadow_vmcs12: [u8; KVM_STATE_NESTED_VMX_VMCS_SIZE as usize], +} + +#[derive(Clone, Copy)] +#[cfg_attr( + feature = "serde", + derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes) +)] +#[repr(C)] +pub struct kvm_svm_nested_state_data { + pub vmcb12: [u8; KVM_STATE_NESTED_SVM_VMCB_SIZE as usize], +} + +/// A stack-allocated buffer for nested KVM state including the mandatory +/// header with meta-information. +/// +/// KVM uses a dynamically sized buffer structure (with a header reporting the +/// size of the buffer/state). This helper type makes working with +/// `get_nested_state()` and `set_nested_state`() significantly more convenient +/// at the cost of a slightly higher memory footprint in some cases. +/// +/// # Type Size +/// +/// On Intel VMX, the actual state requires `128 + 8192 == 8320` bytes, on +/// AMD SVM, the actual state requires `128 + 4096 == 4224` bytes. This type +/// doesn't make a differentiation and unifies the required memory. By +/// sacrificing a few more bytes on VMX, this type is generally convenient to +/// use. +#[derive(Clone, Copy)] +#[cfg_attr( + feature = "serde", + derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::FromBytes) +)] +#[repr(C)] +#[non_exhaustive] // Prevent constructor bypass in public API. +pub struct KvmNestedStateBuffer { + pub flags: u16, + pub format: u16, + pub size: u32, + pub hdr: kvm_nested_state__bindgen_ty_1, + pub data: kvm_nested_state__data, +} + +impl KvmNestedStateBuffer { + /// Creates a new empty buffer, ready for nested state to be stored in by KVM. + /// + /// The `size` property will report the size of the buffer to KVM. + pub fn empty() -> Self { + // SAFETY: Every bit pattern is valid. + let mut this: KvmNestedStateBuffer = unsafe { mem::zeroed() }; + // This way, KVM knows the size of the buffer to store state into. + // See: https://elixir.bootlin.com/linux/v6.12/source/arch/x86/kvm/x86.c#L6193 + this.size = size_of::() as u32; + this + } +} + +impl Default for KvmNestedStateBuffer { + fn default() -> Self { + Self::empty() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::kvm_nested_state as kvm_nested_state_raw_binding; + + #[test] + fn test_layout() { + assert_eq!( + align_of::(), + align_of::() + ); + assert!(size_of::() > size_of::()); + // When this fails/changes, we should re-evaluate the overall types and API + assert_eq!(size_of::(), 8320); + } +} diff --git a/kvm-bindings/src/x86_64/serialize.rs b/kvm-bindings/src/x86_64/serialize.rs index f42d5026..9aeaf7eb 100644 --- a/kvm-bindings/src/x86_64/serialize.rs +++ b/kvm-bindings/src/x86_64/serialize.rs @@ -10,6 +10,8 @@ use bindings::{ kvm_xcr, kvm_xcrs, kvm_xsave, }; use fam_wrappers::kvm_xsave2; +use kvm_nested_state__bindgen_ty_1; +use nested::{kvm_nested_state__data, KvmNestedStateBuffer}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use zerocopy::{transmute, FromBytes, FromZeros, Immutable, IntoBytes}; @@ -35,7 +37,8 @@ serde_impls!( kvm_xsave2, kvm_irqchip, kvm_irq_routing, - kvm_irq_routing_entry + kvm_irq_routing_entry, + KvmNestedStateBuffer ); // 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 { } } +// SAFETY: zerocopy's derives explicitly disallow deriving for unions where +// the fields have different sizes, due to the smaller fields having padding. +// Miri however does not complain about these implementations (e.g. about +// reading the "padding" for one union field as valid data for a bigger one) +unsafe impl IntoBytes for kvm_nested_state__bindgen_ty_1 { + fn only_derive_is_allowed_to_implement_this_trait() + where + Self: Sized, + { + } +} + +// SAFETY: zerocopy's derives explicitly disallow deriving for unions where +// the fields have different sizes, due to the smaller fields having padding. +// Miri however does not complain about these implementations (e.g. about +// reading the "padding" for one union field as valid data for a bigger one) +unsafe impl IntoBytes for kvm_nested_state__data { + fn only_derive_is_allowed_to_implement_this_trait() + where + Self: Sized, + { + } +} + #[cfg(test)] mod tests { use super::*; @@ -182,6 +209,7 @@ mod tests { is_serde::(); is_serde::(); is_serde::(); + is_serde::(); } fn is_serde_json Deserialize<'de> + Default>() { @@ -216,5 +244,6 @@ mod tests { is_serde_json::(); is_serde_json::(); is_serde_json::(); + is_serde_json::(); } }