Skip to content

Commit 5a5110d

Browse files
epilysjunjiemao1bonzini
committed
rust: add crate to expose bindings and interfaces
Add rust/qemu-api, which exposes rust-bindgen generated FFI bindings and provides some declaration macros for symbols visible to the rest of QEMU. Co-authored-by: Junjie Mao <[email protected]> Co-authored-by: Paolo Bonzini <[email protected]> Signed-off-by: Junjie Mao <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]> Signed-off-by: Manos Pitsidianakis <[email protected]> Link: https://lore.kernel.org/r/0fb23fbe211761b263aacec03deaf85c0cc39995.1727961605.git.manos.pitsidianakis@linaro.org Signed-off-by: Paolo Bonzini <[email protected]>
1 parent dc43b18 commit 5a5110d

File tree

13 files changed

+541
-0
lines changed

13 files changed

+541
-0
lines changed

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3291,6 +3291,12 @@ F: hw/core/register.c
32913291
F: include/hw/register.h
32923292
F: include/hw/registerfields.h
32933293

3294+
Rust
3295+
M: Manos Pitsidianakis <[email protected]>
3296+
S: Maintained
3297+
F: rust/qemu-api
3298+
F: rust/rustfmt.toml
3299+
32943300
SLIRP
32953301
M: Samuel Thibault <[email protected]>
32963302
S: Maintained

rust/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
subdir('qemu-api')

rust/qemu-api/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ignore generated bindings file overrides.
2+
src/bindings.rs

rust/qemu-api/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/qemu-api/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "qemu_api"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["Manos Pitsidianakis <[email protected]>"]
6+
license = "GPL-2.0-or-later"
7+
readme = "README.md"
8+
homepage = "https://www.qemu.org"
9+
description = "Rust bindings for QEMU"
10+
repository = "https://gitlab.com/qemu-project/qemu/"
11+
resolver = "2"
12+
publish = false
13+
keywords = []
14+
categories = []
15+
16+
[dependencies]
17+
18+
[features]
19+
default = []
20+
allocator = []
21+
22+
# Do not include in any global workspace
23+
[workspace]
24+
25+
[lints.rust]
26+
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)'] }

rust/qemu-api/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# QEMU bindings and API wrappers
2+
3+
This library exports helper Rust types, Rust macros and C FFI bindings for internal QEMU APIs.
4+
5+
The C bindings can be generated with `bindgen`, using this build target:
6+
7+
```console
8+
$ ninja bindings.rs
9+
```
10+
11+
## Generate Rust documentation
12+
13+
To generate docs for this crate, including private items:
14+
15+
```sh
16+
cargo doc --no-deps --document-private-items
17+
```

rust/qemu-api/build.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2024, Linaro Limited
2+
// Author(s): Manos Pitsidianakis <[email protected]>
3+
// SPDX-License-Identifier: GPL-2.0-or-later
4+
5+
use std::path::Path;
6+
7+
fn main() {
8+
if !Path::new("src/bindings.rs").exists() {
9+
panic!(
10+
"No generated C bindings found! Either build them manually with bindgen or with meson \
11+
(`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson."
12+
);
13+
}
14+
}

rust/qemu-api/meson.build

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
_qemu_api_rs = static_library(
2+
'qemu_api',
3+
structured_sources(
4+
[
5+
'src/lib.rs',
6+
'src/definitions.rs',
7+
'src/device_class.rs',
8+
],
9+
{'.' : bindings_rs},
10+
),
11+
override_options: ['rust_std=2021', 'build.rust_std=2021'],
12+
rust_abi: 'rust',
13+
rust_args: rustc_args + [
14+
'--cfg', 'MESON',
15+
# '--cfg', 'feature="allocator"',
16+
],
17+
)
18+
19+
qemu_api = declare_dependency(
20+
link_with: _qemu_api_rs,
21+
)

rust/qemu-api/src/definitions.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2024, Linaro Limited
2+
// Author(s): Manos Pitsidianakis <[email protected]>
3+
// SPDX-License-Identifier: GPL-2.0-or-later
4+
5+
//! Definitions required by QEMU when registering a device.
6+
7+
use ::core::ffi::{c_void, CStr};
8+
9+
use crate::bindings::{Object, ObjectClass, TypeInfo};
10+
11+
/// Trait a type must implement to be registered with QEMU.
12+
pub trait ObjectImpl {
13+
type Class;
14+
const TYPE_INFO: TypeInfo;
15+
const TYPE_NAME: &'static CStr;
16+
const PARENT_TYPE_NAME: Option<&'static CStr>;
17+
const ABSTRACT: bool;
18+
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)>;
19+
const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)>;
20+
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)>;
21+
}
22+
23+
pub trait Class {
24+
const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)>;
25+
const CLASS_BASE_INIT: Option<
26+
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
27+
>;
28+
}
29+
30+
#[macro_export]
31+
macro_rules! module_init {
32+
($func:expr, $type:expr) => {
33+
#[used]
34+
#[cfg_attr(target_os = "linux", link_section = ".ctors")]
35+
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
36+
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
37+
pub static LOAD_MODULE: extern "C" fn() = {
38+
extern "C" fn __load() {
39+
unsafe {
40+
$crate::bindings::register_module_init(Some($func), $type);
41+
}
42+
}
43+
44+
__load
45+
};
46+
};
47+
(qom: $func:ident => $body:block) => {
48+
// NOTE: To have custom identifiers for the ctor func we need to either supply
49+
// them directly as a macro argument or create them with a proc macro.
50+
#[used]
51+
#[cfg_attr(target_os = "linux", link_section = ".ctors")]
52+
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
53+
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
54+
pub static LOAD_MODULE: extern "C" fn() = {
55+
extern "C" fn __load() {
56+
#[no_mangle]
57+
unsafe extern "C" fn $func() {
58+
$body
59+
}
60+
61+
unsafe {
62+
$crate::bindings::register_module_init(
63+
Some($func),
64+
$crate::bindings::module_init_type::MODULE_INIT_QOM,
65+
);
66+
}
67+
}
68+
69+
__load
70+
};
71+
};
72+
}
73+
74+
#[macro_export]
75+
macro_rules! type_info {
76+
($t:ty) => {
77+
$crate::bindings::TypeInfo {
78+
name: <$t as $crate::definitions::ObjectImpl>::TYPE_NAME.as_ptr(),
79+
parent: if let Some(pname) = <$t as $crate::definitions::ObjectImpl>::PARENT_TYPE_NAME {
80+
pname.as_ptr()
81+
} else {
82+
::core::ptr::null_mut()
83+
},
84+
instance_size: ::core::mem::size_of::<$t>() as $crate::bindings::size_t,
85+
instance_align: ::core::mem::align_of::<$t>() as $crate::bindings::size_t,
86+
instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT,
87+
instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT,
88+
instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE,
89+
abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT,
90+
class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>() as $crate::bindings::size_t,
91+
class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_INIT,
92+
class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT,
93+
class_data: ::core::ptr::null_mut(),
94+
interfaces: ::core::ptr::null_mut(),
95+
};
96+
}
97+
}

rust/qemu-api/src/device_class.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright 2024, Linaro Limited
2+
// Author(s): Manos Pitsidianakis <[email protected]>
3+
// SPDX-License-Identifier: GPL-2.0-or-later
4+
5+
use std::sync::OnceLock;
6+
7+
use crate::bindings::Property;
8+
9+
#[macro_export]
10+
macro_rules! device_class_init {
11+
($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => {
12+
#[no_mangle]
13+
pub unsafe extern "C" fn $func(
14+
klass: *mut $crate::bindings::ObjectClass,
15+
_: *mut ::core::ffi::c_void,
16+
) {
17+
let mut dc =
18+
::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap();
19+
dc.as_mut().realize = $realize_fn;
20+
dc.as_mut().vmsd = &$vmsd;
21+
$crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn);
22+
$crate::bindings::device_class_set_props(dc.as_mut(), $props.as_mut_ptr());
23+
}
24+
};
25+
}
26+
27+
#[macro_export]
28+
macro_rules! define_property {
29+
($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr, default = $defval:expr$(,)*) => {
30+
$crate::bindings::Property {
31+
name: {
32+
#[used]
33+
static _TEMP: &::core::ffi::CStr = $name;
34+
_TEMP.as_ptr()
35+
},
36+
info: $prop,
37+
offset: ::core::mem::offset_of!($state, $field)
38+
.try_into()
39+
.expect("Could not fit offset value to type"),
40+
bitnr: 0,
41+
bitmask: 0,
42+
set_default: true,
43+
defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval.into() },
44+
arrayoffset: 0,
45+
arrayinfo: ::core::ptr::null(),
46+
arrayfieldsize: 0,
47+
link_type: ::core::ptr::null(),
48+
}
49+
};
50+
($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr$(,)*) => {
51+
$crate::bindings::Property {
52+
name: {
53+
#[used]
54+
static _TEMP: &::core::ffi::CStr = $name;
55+
_TEMP.as_ptr()
56+
},
57+
info: $prop,
58+
offset: ::core::mem::offset_of!($state, $field)
59+
.try_into()
60+
.expect("Could not fit offset value to type"),
61+
bitnr: 0,
62+
bitmask: 0,
63+
set_default: false,
64+
defval: $crate::bindings::Property__bindgen_ty_1 { i: 0 },
65+
arrayoffset: 0,
66+
arrayinfo: ::core::ptr::null(),
67+
arrayfieldsize: 0,
68+
link_type: ::core::ptr::null(),
69+
}
70+
};
71+
}
72+
73+
#[repr(C)]
74+
pub struct Properties<const N: usize>(pub OnceLock<[Property; N]>, pub fn() -> [Property; N]);
75+
76+
impl<const N: usize> Properties<N> {
77+
pub fn as_mut_ptr(&mut self) -> *mut Property {
78+
_ = self.0.get_or_init(self.1);
79+
self.0.get_mut().unwrap().as_mut_ptr()
80+
}
81+
}
82+
83+
#[macro_export]
84+
macro_rules! declare_properties {
85+
($ident:ident, $($prop:expr),*$(,)*) => {
86+
87+
const fn _calc_prop_len() -> usize {
88+
let mut len = 1;
89+
$({
90+
_ = stringify!($prop);
91+
len += 1;
92+
})*
93+
len
94+
}
95+
const PROP_LEN: usize = _calc_prop_len();
96+
97+
fn _make_properties() -> [$crate::bindings::Property; PROP_LEN] {
98+
[
99+
$($prop),*,
100+
unsafe { ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init() },
101+
]
102+
}
103+
104+
#[no_mangle]
105+
pub static mut $ident: $crate::device_class::Properties<PROP_LEN> = $crate::device_class::Properties(::std::sync::OnceLock::new(), _make_properties);
106+
};
107+
}
108+
109+
#[macro_export]
110+
macro_rules! vm_state_description {
111+
($(#[$outer:meta])*
112+
$name:ident,
113+
$(name: $vname:expr,)*
114+
$(unmigratable: $um_val:expr,)*
115+
) => {
116+
#[used]
117+
$(#[$outer])*
118+
pub static $name: $crate::bindings::VMStateDescription = $crate::bindings::VMStateDescription {
119+
$(name: {
120+
#[used]
121+
static VMSTATE_NAME: &::core::ffi::CStr = $vname;
122+
$vname.as_ptr()
123+
},)*
124+
unmigratable: true,
125+
..unsafe { ::core::mem::MaybeUninit::<$crate::bindings::VMStateDescription>::zeroed().assume_init() }
126+
};
127+
}
128+
}

0 commit comments

Comments
 (0)