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

[RFC] Add serde [de]serialization for KVM bindings #4

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ repository = "https://github.com/rust-vmm/kvm-bindings"
readme = "README.md"
keywords = ["kvm"]
license = "Apache-2.0"
build = "build.rs"

[features]
kvm-v4_14_0 = []
kvm-v4_20_0 = []
with_serde = ["libc", "serde", "serde_bytes"]

[dependencies]
libc = { version = ">=0.2.39", optional = true }
serde = { version = ">=1.0.27", optional = true }
serde_bytes = { version = ">=0.11.2", optional = true }
8 changes: 8 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

#[cfg(feature = "with_serde")]
include!("build_serde.rs");

#[cfg(not(feature = "with_serde"))]
fn main() {}
116 changes: 116 additions & 0 deletions build_serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::env;
use std::fs::{self, DirEntry, File, OpenOptions};
use std::io::{self, BufRead, BufReader, Read, Write};
use std::path::{Component, Path, PathBuf};

const SER_HDR: &str = "serialization/header.rs.txt";
const SER_SRC: &str = "serialization/src/";
const SER_TPL: &str = "serialization/template.rs.txt";

fn main() {
let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
add_serialization(&root);
}

fn add_serialization(root: &Path) {
let srcdir = root.join("src");
let archs = ["arm", "arm64", "x86"];

for arch in archs.into_iter().map(|a| srcdir.join(a)) {
visit_dirs(&arch, &add_serialization_to_srcfile).unwrap();
}
}

fn visit_dirs(dir: &Path, bindings_visitor: &dyn Fn(&DirEntry)) -> io::Result<()> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_file()
&& path
.file_name()
.unwrap()
.to_str()
.unwrap()
.starts_with("bindings_")
{
bindings_visitor(&entry)
}
}
}
Ok(())
}

fn is_relevant(line: &str) -> bool {
for prefix in vec!["pub struct", "pub union"].iter() {
if line.starts_with(prefix) {
for blacklisted in vec!["__BindgenBitfieldUnit", "__IncompleteArrayField"].iter() {
if line.contains(blacklisted) {
return false;
}
}
return true;
}
}
return false;
}

fn add_serialization_to_srcfile(srcfile: &DirEntry) {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let root = Path::new(&manifest_dir);

let serialization_header_file = root.join(SER_HDR);
let mut header = String::from("");
let _ = File::open(serialization_header_file)
.unwrap()
.read_to_string(&mut header);

let template_file = root.join(SER_TPL);
let mut template = String::from("");
let _ = File::open(template_file)
.unwrap()
.read_to_string(&mut template);

let srcpath = srcfile.path();
let mut components = srcpath.components().rev();

if let Some(Component::Normal(filename)) = components.next() {
if let Some(Component::Normal(arch)) = components.next() {
let serializer_fname = format!(
"serializers_{}",
&filename.to_str().unwrap()["bindings_".len()..]
);

let arch_dir_str = format!("{}/{}/{}", env::var("OUT_DIR").unwrap(), SER_SRC, arch.to_str().unwrap());
let arch_dir = Path::new(arch_dir_str.as_str());
if !arch_dir.exists() {
fs::create_dir_all(arch_dir).unwrap();
} else if !arch_dir.is_dir() {
panic!("{:?} exists and is not a directory", arch_dir);
}

let mut srcfile_out = OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(arch_dir.join(serializer_fname))
.unwrap();
srcfile_out.write_all(header.as_bytes()).unwrap();

let srcfile_in = File::open(srcfile.path()).unwrap();
let reader = BufReader::new(srcfile_in);
for line_res in reader.lines() {
if let Ok(line) = line_res {
if is_relevant(&line) {
let datastruct_name = line.split_whitespace().collect::<Vec<&str>>()[2];
let impl_text = template.replace("TYPENAME", datastruct_name);
srcfile_out.write_all(impl_text.as_bytes()).unwrap();
}
}
}
}
}
}
69 changes: 69 additions & 0 deletions serialization/header.rs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::mem;
use std::ptr;

use serde::de::{Deserialize, Deserializer};
use serde::{Serialize, Serializer};
use serde_bytes::ByteBuf;

fn serialize_ffi<T>(something: &T) -> ByteBuf {
let mut serialized_self: Vec<u8> = vec![0; mem::size_of::<T>()];
unsafe {
libc::memcpy(
serialized_self.as_mut_ptr() as *mut libc::c_void,
something as *const T as *const libc::c_void,
mem::size_of::<T>(),
);
}
ByteBuf::from(serialized_self)
}

fn deserialize_ffi<T>(serialized: ByteBuf) -> T {
unsafe { ptr::read(serialized.into_vec().as_ptr() as *const T) }
}

impl<Storage, Align> Serialize for __BindgenBitfieldUnit<Storage, Align>
where
Storage: AsRef<[u8]> + AsMut<[u8]>,
{
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = serialize_ffi::<__BindgenBitfieldUnit<Storage, Align>>(&self);
bytes.serialize(serializer)
}
}

impl<'de, Storage, Align> Deserialize<'de> for __BindgenBitfieldUnit<Storage, Align>
where
Storage: AsRef<[u8]> + AsMut<[u8]>,
{
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let v: ByteBuf = ByteBuf::deserialize::<D>(deserializer)?;
Ok(deserialize_ffi::<__BindgenBitfieldUnit<Storage, Align>>(v))
}
}

impl<T> Serialize for __IncompleteArrayField<T> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
[0u8; 0].serialize(serializer)
}
}

impl<'de, T> Deserialize<'de> for __IncompleteArrayField<T> {
fn deserialize<D>(_: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(__IncompleteArrayField::new())
}
}
19 changes: 19 additions & 0 deletions serialization/template.rs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
impl Serialize for TYPENAME {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = serialize_ffi::<TYPENAME>(&self);
bytes.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for TYPENAME {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let v: ByteBuf = ByteBuf::deserialize::<D>(deserializer)?;
Ok(deserialize_ffi::<TYPENAME>(v))
}
}
32 changes: 32 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

#[cfg(feature = "with_serde")]
extern crate libc;
#[cfg(feature = "with_serde")]
extern crate serde;
#[cfg(feature = "with_serde")]
extern crate serde_bytes;

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod x86;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Expand All @@ -19,3 +26,28 @@ pub use self::arm::bindings::*;
mod arm64;
#[cfg(target_arch = "aarch64")]
pub use self::arm64::bindings::*;


// Serializers for x86 and x86_64.
#[cfg(all(feature = "with_serde", feature = "kvm-v4_14_0", any(target_arch = "x86", target_arch = "x86_64")))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/x86/serializers_v4_14_0.rs"));
#[cfg(all(feature = "with_serde", feature = "kvm-v4_20_0", any(target_arch = "x86", target_arch = "x86_64")))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/x86/serializers_v4_20_0.rs"));
#[cfg(all(feature = "with_serde", not(feature = "kvm-v4_14_0"), not(feature = "kvm-v4_20_0"), any(target_arch = "x86", target_arch = "x86_64")))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/x86/serializers_v4_20_0.rs"));

// Serializers for aarch.
#[cfg(all(feature = "with_serde", feature = "kvm-v4_14_0", target_arch = "aarch"))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/arm/serializers_v4_14_0.rs"));
#[cfg(all(feature = "with_serde", feature = "kvm-v4_20_0", target_arch = "aarch"))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/arm/serializers_v4_20_0.rs"));
#[cfg(all(feature = "with_serde", not(feature = "kvm-v4_14_0"), not(feature = "kvm-v4_20_0"), target_arch = "aarch"))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/arm/serializers_v4_20_0.rs"));

// Serializers for aarch64.
#[cfg(all(feature = "with_serde", feature = "kvm-v4_14_0", target_arch = "aarch64"))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/arm64/serializers_v4_14_0.rs"));
#[cfg(all(feature = "with_serde", feature = "kvm-v4_20_0", target_arch = "aarch64"))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/arm64/serializers_v4_20_0.rs"));
#[cfg(all(feature = "with_serde", not(feature = "kvm-v4_14_0"), not(feature = "kvm-v4_20_0"), target_arch = "aarch64"))]
include!(concat!(env!("OUT_DIR"), "/serialization/src/arm64/serializers_v4_20_0.rs"));