Skip to content
This repository was archived by the owner on Nov 6, 2024. It is now read-only.

Commit de0c7b2

Browse files
committed
Add serde support for various bindings
Add implementations of `serde::{Serialize, Deserialize}` for kvm-bindings structures commonly used for live migration/snapshotting. The selections of structures to add the derives to was determined by finding the minimal set that allows both Firecracker and CloudHypervisor to compile against this branch. Serialization happens as opaque binary blobs via the `zerocopy` crate. This is to support live migration across different versions of kvm-bindings (which can potentially be using bindings generated from different versions of the kernel headers). Since the kernel's ABI is backward-compatible, the only changes that can happen are: - "reserved" areas of structures acquire meaning. This is a change purely at the representational level, and has no impact on the underlying binary representation. Thus, our serialization is roboust to these changes (whereas simply deriving the serde traits would cause incompatibilities here). - Adding new fields to the end of a structure. When deserializating, we compare the length of the deserialized data with the size of the struct, and pad with zeros/truncate as appropriate. This allows seamless live update. Signed-off-by: Patrick Roy <[email protected]>
1 parent 33a89cc commit de0c7b2

File tree

10 files changed

+394
-5
lines changed

10 files changed

+394
-5
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ rustdoc-args = ["--cfg", "docsrs"]
1414

1515
[features]
1616
fam-wrappers = ["vmm-sys-util"]
17+
# It is not needed to enable the `with-serde` feature of `vmm-sys-util` here, because due to how cargo merges features,
18+
# if a downstream crate enables vmm-sys-util in its Cargo.toml, it will get enabled globally.
19+
with-serde = ["dep:serde", "serde/derive", "dep:zerocopy"]
20+
1721

1822
[dependencies]
1923
vmm-sys-util = { version = "0.12.1", optional = true }
24+
serde = { version = "1.0.0", optional = true, features = ["derive"] }
25+
zerocopy = { version = "0.7.32", optional = true, features = ["derive"] }

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ Rust FFI bindings to KVM, generated using
55
[bindgen](https://crates.io/crates/bindgen). It currently has support for the
66
following target architectures:
77
- x86_64
8-
- arm
98
- arm64
109

1110
The bindings exported by this crate are statically generated using header files
@@ -17,7 +16,7 @@ kernel version they are using. For example, the `immediate_exit` field from the
1716
capability is available. Using invalid fields or features may lead to undefined
1817
behaviour.
1918

20-
# Usage
19+
## Usage
2120
First, add the following to your `Cargo.toml`:
2221
```toml
2322
kvm-bindings = "0.3"
@@ -35,7 +34,14 @@ this crate. Example:
3534
kvm-bindings = { version = "0.3", features = ["fam-wrappers"]}
3635
```
3736

38-
# Dependencies
37+
## Dependencies
3938
The crate has an `optional` dependency to
4039
[vmm-sys-util](https://crates.io/crates/vmm-sys-util) when enabling the
4140
`fam-wrappers` feature.
41+
42+
It also has an optional dependency on [`serde`](serde.rs) when enabling the
43+
`with-serde` feature, to allow serialization of bindings. Serialization of
44+
bindings happens as opaque binary blobs via [`zerocopy`](https://google.github.io/comprehensive-rust/bare-metal/useful-crates/zerocopy.html).
45+
Due to the kernel's ABI compatibility, this means that bindings serialized
46+
in version `x` of `kvm-bindings` can be deserialized in version `y` of the
47+
crate, even if the bindings have had been regenerated in the meantime.

src/arm64/bindings.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,10 @@ pub type __wsum = __u32;
955955
pub type __poll_t = ::std::os::raw::c_uint;
956956
#[repr(C)]
957957
#[derive(Debug, Default, Copy, Clone, PartialEq)]
958+
#[cfg_attr(
959+
feature = "with-serde",
960+
derive(zerocopy::AsBytes, zerocopy::FromBytes, zerocopy::FromZeroes)
961+
)]
958962
pub struct user_pt_regs {
959963
pub regs: [__u64; 31usize],
960964
pub sp: __u64,
@@ -1019,6 +1023,10 @@ fn bindgen_test_layout_user_pt_regs() {
10191023
#[repr(C)]
10201024
#[repr(align(16))]
10211025
#[derive(Debug, Default, Copy, Clone, PartialEq)]
1026+
#[cfg_attr(
1027+
feature = "with-serde",
1028+
derive(zerocopy::AsBytes, zerocopy::FromBytes, zerocopy::FromZeroes)
1029+
)]
10221030
pub struct user_fpsimd_state {
10231031
pub vregs: [__uint128_t; 32usize],
10241032
pub fpsr: __u32,
@@ -1499,6 +1507,10 @@ fn bindgen_test_layout_user_za_header() {
14991507
#[repr(C)]
15001508
#[repr(align(16))]
15011509
#[derive(Debug, Default, Copy, Clone, PartialEq)]
1510+
#[cfg_attr(
1511+
feature = "with-serde",
1512+
derive(zerocopy::AsBytes, zerocopy::FromBytes, zerocopy::FromZeroes)
1513+
)]
15021514
pub struct kvm_regs {
15031515
pub regs: user_pt_regs,
15041516
pub sp_el1: __u64,
@@ -1574,6 +1586,10 @@ fn bindgen_test_layout_kvm_regs() {
15741586
}
15751587
#[repr(C)]
15761588
#[derive(Debug, Default, Copy, Clone, PartialEq)]
1589+
#[cfg_attr(
1590+
feature = "with-serde",
1591+
derive(zerocopy::AsBytes, zerocopy::FromBytes, zerocopy::FromZeroes)
1592+
)]
15771593
pub struct kvm_vcpu_init {
15781594
pub target: __u32,
15791595
pub features: [__u32; 7usize],
@@ -6511,6 +6527,10 @@ fn bindgen_test_layout_kvm_vapic_addr() {
65116527
}
65126528
#[repr(C)]
65136529
#[derive(Debug, Default, Copy, Clone, PartialEq)]
6530+
#[cfg_attr(
6531+
feature = "with-serde",
6532+
derive(zerocopy::AsBytes, zerocopy::FromBytes, zerocopy::FromZeroes)
6533+
)]
65146534
pub struct kvm_mp_state {
65156535
pub mp_state: __u32,
65166536
}
@@ -8786,6 +8806,10 @@ fn bindgen_test_layout_kvm_reg_list() {
87868806
}
87878807
#[repr(C)]
87888808
#[derive(Debug, Default, Copy, Clone, PartialEq)]
8809+
#[cfg_attr(
8810+
feature = "with-serde",
8811+
derive(zerocopy::AsBytes, zerocopy::FromBytes, zerocopy::FromZeroes)
8812+
)]
87898813
pub struct kvm_one_reg {
87908814
pub id: __u64,
87918815
pub addr: __u64,

src/arm64/mod.rs

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

10+
#[cfg(feature = "with-serde")]
11+
mod serialize;
12+
1013
pub use self::bindings::*;
1114
#[cfg(feature = "fam-wrappers")]
1215
pub use self::fam_wrappers::*;

src/arm64/serialize.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use bindings::{
2+
kvm_mp_state, kvm_one_reg, kvm_regs, kvm_vcpu_init, user_fpsimd_state, user_pt_regs,
3+
};
4+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
5+
use zerocopy::{transmute, AsBytes};
6+
7+
serde_impls! {
8+
user_pt_regs,
9+
user_fpsimd_state,
10+
kvm_regs,
11+
kvm_vcpu_init,
12+
kvm_mp_state,
13+
kvm_one_reg
14+
}
15+
16+
#[cfg(test)]
17+
mod tests {
18+
use bindings::*;
19+
use serde::{Deserialize, Serialize};
20+
21+
fn is_serde<T: Serialize + for<'de> Deserialize<'de>>() {}
22+
23+
#[test]
24+
fn static_assert_serde_implementations() {
25+
// This test statically (= at compile-time) asserts that various bindgen generated
26+
// structures implement serde's `Serialize` and `Deserialize` traits.
27+
// This is to make sure that we do not accidentally remove those implementations
28+
// when regenerating bindings. If this test fails to compile, please add
29+
//
30+
// #[cfg_attr(
31+
// feature = "with-serde",
32+
// derive(zerocopy::AsBytes, zerocopy::FromBytes, zerocopy::FromZeroes)
33+
// )]
34+
//
35+
// to all structures causing compilation errors (we need the zerocopy traits, as the
36+
// `Serialize` and `Deserialize` implementations are provided by the `serde_impls!` macro
37+
// above, which implements serialization based on zerocopy's `FromBytes` and `AsBytes`
38+
// traits that it expects to be derived).
39+
//
40+
// NOTE: This only include "top-level" items, and does not list out bindgen-anonymous types
41+
// (e.g. types like `kvm_vcpu_events__bindgen_ty_5`). These types can change name across
42+
// bindgen versions. If after re-adding the derives to all the below items you can compile
43+
// errors about anonymous types not implementing `Serialize`/`Deserialize`, please also add
44+
// the derives to all anonymous types references in the definitions of the below items.
45+
46+
is_serde::<user_pt_regs>();
47+
is_serde::<user_fpsimd_state>();
48+
is_serde::<kvm_regs>();
49+
is_serde::<kvm_vcpu_init>();
50+
is_serde::<kvm_mp_state>();
51+
is_serde::<kvm_one_reg>();
52+
}
53+
}

src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
#[cfg(feature = "fam-wrappers")]
1313
extern crate vmm_sys_util;
1414

15+
#[cfg(feature = "with-serde")]
16+
extern crate serde;
17+
18+
#[cfg(feature = "with-serde")]
19+
extern crate zerocopy;
20+
21+
#[cfg(feature = "with-serde")]
22+
#[macro_use]
23+
mod serialize;
24+
1525
#[cfg(target_arch = "x86_64")]
1626
mod x86_64;
1727
#[cfg(target_arch = "x86_64")]

src/serialize.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! Module containing serialization utilities
4+
5+
/// Macro that generates serde::Serialize and serde::Deserialize implementations for the given types.
6+
/// This macro assumes that the types implement zerocopy::FromBytes and zerocopy::AsBytes, and uses
7+
/// these implementations to serialize as opaque byte arrays. During deserialization, it will
8+
/// try to deserialize as a `Vec`. If this deserialized `Vec` has a length that equals `size_of::<T>`,
9+
/// it will transmute to `T` (using zerocopy), otherwise the `Vec` will either be zero-padded, or truncated.
10+
/// This will hopefully allow live update of bindings across kernel versions even if the kernel adds
11+
/// new fields to the end of some struct (we heavily rely on the kernel not making ABI breaking changes here).
12+
macro_rules! serde_impls {
13+
($($typ: ty),*) => {
14+
$(
15+
impl Serialize for $typ {
16+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
17+
where
18+
S: Serializer
19+
{
20+
let bytes = self.as_bytes();
21+
serializer.serialize_bytes(bytes)
22+
}
23+
}
24+
25+
impl<'de> Deserialize<'de> for $typ {
26+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
27+
where
28+
D: Deserializer<'de>
29+
{
30+
let bytes = Vec::<u8>::deserialize(deserializer)?;
31+
32+
let mut backing = [0u8; std::mem::size_of::<$typ>()];
33+
34+
let limit = bytes.len().min(backing.len());
35+
36+
backing[..limit].copy_from_slice(&bytes[..limit]);
37+
38+
Ok(transmute!(backing))
39+
}
40+
}
41+
)*
42+
}
43+
}

0 commit comments

Comments
 (0)