From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
To: Junjie Mao <junjie.mao@intel.com>, qemu-devel@nongnu.org
Cc: "Paolo Bonzini" <pbonzini@redhat.com>,
"Marc-André Lureau" <marcandre.lureau@redhat.com>,
"Daniel P. Berrangé " <berrange@redhat.com>,
"Thomas Huth" <thuth@redhat.com>,
"Philippe Mathieu-Daudé " <philmd@linaro.org>,
"Pierrick Bouvier" <pierrick.bouvier@linaro.org>,
"Richard Henderson" <richard.henderson@linaro.org>,
"Gustavo Romero" <gustavo.romero@linaro.org>,
"Alex Benné e" <alex.bennee@linaro.org>,
"Peter Maydell" <peter.maydell@linaro.org>,
"Zhao Liu" <zhao1.liu@intel.com>
Subject: Re: [PATCH v8 6/8] rust: add crate to expose bindings and interfaces
Date: Mon, 26 Aug 2024 09:12:28 +0300 [thread overview]
Message-ID: <itblf.by425lac4ow@linaro.org> (raw)
In-Reply-To: <8570704d-7cc8-460f-940e-4bf626972465@intel.com>
On Mon, 26 Aug 2024 08:03, Junjie Mao <junjie.mao@intel.com> wrote:
>Hi Manos,
>
>On 8/23/2024 4:11 PM, Manos Pitsidianakis wrote:
>> 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 <junjie.mao@intel.com>
>> Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
>> Signed-off-by: Junjie Mao <junjie.mao@intel.com>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
>> ---
>> MAINTAINERS | 6 ++
>> rust/meson.build | 1 +
>> rust/qemu-api/.gitignore | 2 +
>> rust/qemu-api/Cargo.lock | 7 +++
>> rust/qemu-api/Cargo.toml | 26 ++++++++
>> rust/qemu-api/README.md | 17 +++++
>> rust/qemu-api/build.rs | 14 +++++
>> rust/qemu-api/meson.build | 20 ++++++
>> rust/qemu-api/rustfmt.toml | 1 +
>> rust/qemu-api/src/definitions.rs | 109 ++++++++++++++++++++++++++++++++
>> rust/qemu-api/src/device_class.rs | 128 ++++++++++++++++++++++++++++++++++++++
>> rust/qemu-api/src/lib.rs | 102 ++++++++++++++++++++++++++++++
>> rust/qemu-api/src/tests.rs | 49 +++++++++++++++
>> rust/rustfmt.toml | 7 +++
>> 14 files changed, 489 insertions(+)
>>
>[snip]
>> diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md
>> new file mode 100644
>> index 0000000000..7588fa29ef
>> --- /dev/null
>> +++ b/rust/qemu-api/README.md
>> @@ -0,0 +1,17 @@
>> +# QEMU bindings and API wrappers
>> +
>> +This library exports helper Rust types, Rust macros and C FFI bindings for internal QEMU APIs.
>> +
>> +The C bindings can be generated with `bindgen`, using this build target:
>> +
>> +```console
>> +$ ninja bindings.rs
>> +```
>> +
>
>I suggest mentioning here that cargo test requires --no-default-features.
Right. I will make #[global_allocator] depend on both the `allocator`
feature being on, and `test` off.
>
>> +## Generate Rust documentation
>> +
>> +To generate docs for this crate, including private items:
>> +
>> +```sh
>> +cargo doc --no-deps --document-private-items
>> +```
>[snip]
>> diff --git a/rust/qemu-api/rustfmt.toml b/rust/qemu-api/rustfmt.toml
>> new file mode 120000
>> index 0000000000..39f97b043b
>> --- /dev/null
>> +++ b/rust/qemu-api/rustfmt.toml
>> @@ -0,0 +1 @@
>> +../rustfmt.toml
>> \ No newline at end of file
>
>This symbolic link is unnecessary. rustfmt will recursively search the parent
>directories for rustfmt.toml [1].
>
>[1] https://github.com/rust-lang/rustfmt?tab=readme-ov-file#configuring-rustfmt
Good to know, will remove it.
>> diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs
>> new file mode 100644
>> index 0000000000..4abd0253bd
>> --- /dev/null
>> +++ b/rust/qemu-api/src/definitions.rs
>> @@ -0,0 +1,109 @@
>> +// Copyright 2024, Linaro Limited
>> +// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +
>> +//! Definitions required by QEMU when registering a device.
>> +
>> +/// Trait a type must implement to be registered with QEMU.
>> +pub trait ObjectImpl {
>> + type Class;
>> + const TYPE_INFO: crate::bindings::TypeInfo;
>> + const TYPE_NAME: &'static ::core::ffi::CStr;
>> + const PARENT_TYPE_NAME: Option<&'static ::core::ffi::CStr>;
>> + const INSTANCE_INIT: ::core::option::Option<
>> + unsafe extern "C" fn(obj: *mut crate::bindings::Object),
>> + >;
>> + const INSTANCE_POST_INIT: ::core::option::Option<
>> + unsafe extern "C" fn(obj: *mut crate::bindings::Object),
>> + >;
>> + const INSTANCE_FINALIZE: ::core::option::Option<
>> + unsafe extern "C" fn(obj: *mut crate::bindings::Object),
>> + >;
>> + const ABSTRACT: bool;
>> +}
>> +
>> +pub trait Class {
>> + const CLASS_INIT: ::core::option::Option<
>> + unsafe extern "C" fn(
>> + klass: *mut crate::bindings::ObjectClass,
>> + data: *mut core::ffi::c_void,
>> + ),
>> + >;
>> + const CLASS_BASE_INIT: ::core::option::Option<
>> + unsafe extern "C" fn(
>> + klass: *mut crate::bindings::ObjectClass,
>> + data: *mut core::ffi::c_void,
>> + ),
>> + >;
>> +}
>> +
>> +#[macro_export]
>> +macro_rules! module_init {
>> + ($func:expr, $type:expr) => {
>> + #[used]
>> + #[cfg_attr(target_os = "linux", link_section = ".ctors")]
>> + #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
>> + #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
>> + pub static LOAD_MODULE: extern "C" fn() = {
>> + assert!($type < $crate::bindings::module_init_type_MODULE_INIT_MAX);
>> +
>> + extern "C" fn __load() {
>> + unsafe {
>> + $crate::bindings::register_module_init(Some($func), $type);
>> + }
>> + }
>> +
>> + __load
>> + };
>> + };
>> + (qom: $func:ident => $body:block) => {
>
>This arm looks duplicating what #[derive(Object)] is for, while both have their
>strengths and limitations: module_init!() provides more flexibility on the
>registration function body, and #[derive(Object)] is much more convenient to use.
>
>Complex registration functions are not rare, and thus the Rust APIs should
>ideally have both strengths: clean type declaration in most cases, and full
>flexibility when needed. In the current codebase, we have ~1080 uses of
>type_init(), with 750 of them having a registration function as simple as a
>single call to type_register_static() (disclaimer: those numbers are collected
>via brute-force searches and may not be accurate). More complex cases include:
>
>1. Registering multiple types (e.g., multiple models of same device) that share
>the same data structure, e.g., hw/misc/aspeed_xdma.c and hw/xtensa/xtfpga.c.
>There are ~200 uses of this kind in the codebase.
>
>2. Use domain-specific registration function, e.g., ui/egl-headless.c,
>audio/ossaudio.c and hw/virtio/virtio-net-pci.c.
>
>3. Other device-specific operations, e.g., hw/net/spapr_llan.c.
>
This is why I left the decl macro, I was prototyping this series with a
second Rust device (that is not included in the patches) and I needed
more logic in the module init.
>My rough idea is to define a proc macro around an impl block to collect
>constants (type names, parent names, etc.) as tokens and callbacks (class init,
>instance init, etc.) as functions, from which we generate TypeInfo and
>(optionally) type registration code. As an example:
Do you think we should not use a trait to define type info at all by the
way?
>
> pub struct PL011State {
> ...
> }
>
> #[qemu_type(name = "pl011", parent = TYPE_SYS_BUS_DEVICE, (abstract)*)]
> impl PL011State {
> #[class_init]
> pub fn class_init(klass: *mut ObjectClass, data: *mut core::ffi::c_void) {
> ...
> }
>
> #[instance_init]
> pub fn init(obj: *mut Object) { ... }
>
> ...
> }
>
>The proc macro then generates a TypeInfo instance named TYPE_INFO_pl011, with
>optional callbacks being None when not given. A registration function will also
>be generated unless qemu_type! has a no_register token.
Maybe this too can be a trait method people can override with a blank
implementation to avoid registration...
>Devices can still use module_init! to define their own registration
>function.
>
>The class_init callback is specified together with instance_init because it is
>common for multi-model devices to provide a different class_init even they share
>the same class structure. Refer to hw/misc/aspeed_xdma.c for an example.
Thanks I will take a look. QEMU Classes are a bit complex indeed.
>
>What do you think? It is still preliminary and the example can have grammatical
>issues, but I can try drafting if you think that is a good direction.
In my plan I wanted to eventually have all these callbacks available to
Rust code via trait methods which only take rust references instead of
pointers. Then the proc macros would generate extern "C" wrappers for
each of them, make a typeinfo declaration, set everything up. I like
your approach too. Should we wait until we have an actual device that
requires redesigning this? We're free to change things anyway.
>
>---
>Best Regards
>Junjie Mao
>
>> + // NOTE: To have custom identifiers for the ctor func we need to either supply
>> + // them directly as a macro argument or create them with a proc macro.
>> + #[used]
>> + #[cfg_attr(target_os = "linux", link_section = ".ctors")]
>> + #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
>> + #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
>> + pub static LOAD_MODULE: extern "C" fn() = {
>> + extern "C" fn __load() {
>> + #[no_mangle]
>> + unsafe extern "C" fn $func() {
>> + $body
>> + }
>> +
>> + unsafe {
>> + $crate::bindings::register_module_init(
>> + Some($func),
>> + $crate::bindings::module_init_type_MODULE_INIT_QOM,
>> + );
>> + }
>> + }
>> +
>> + __load
>> + };
>> + };
>> +}
>> +
>> +#[macro_export]
>> +macro_rules! type_info {
>> + ($t:ty) => {
>> + $crate::bindings::TypeInfo {
>> + name: <$t as $crate::definitions::ObjectImpl>::TYPE_NAME.as_ptr(),
>> + parent: if let Some(pname) = <$t as $crate::definitions::ObjectImpl>::PARENT_TYPE_NAME {
>> + pname.as_ptr()
>> + } else {
>> + ::core::ptr::null_mut()
>> + },
>> + instance_size: ::core::mem::size_of::<$t>(),
>> + instance_align: ::core::mem::align_of::<$t>(),
>> + instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT,
>> + instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT,
>> + instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE,
>> + abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT,
>> + class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>(),
>> + class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_INIT,
>> + class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT,
>> + class_data: ::core::ptr::null_mut(),
>> + interfaces: ::core::ptr::null_mut(),
>> + };
>> + }
>> +}
next prev parent reply other threads:[~2024-08-26 6:35 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-08-23 8:11 [PATCH v8 0/8] Add Rust build support, ARM PL011 device impl Manos Pitsidianakis
2024-08-23 8:11 ` [PATCH v8 1/8] Require meson version 1.5.0 Manos Pitsidianakis
2024-08-23 8:11 ` [PATCH v8 2/8] build-sys: Add rust feature option Manos Pitsidianakis
2024-08-23 8:11 ` [PATCH v8 3/8] configure, meson: detect Rust toolchain Manos Pitsidianakis
2024-08-23 8:11 ` [PATCH v8 4/8] rust: add bindgen step as a meson dependency Manos Pitsidianakis
2024-08-23 8:11 ` [PATCH v8 5/8] .gitattributes: add Rust diff and merge attributes Manos Pitsidianakis
2024-08-23 8:11 ` [PATCH v8 6/8] rust: add crate to expose bindings and interfaces Manos Pitsidianakis
2024-08-26 5:03 ` Junjie Mao
2024-08-26 6:12 ` Manos Pitsidianakis [this message]
2024-08-26 8:41 ` Junjie Mao
2024-08-26 5:31 ` Junjie Mao
2024-08-26 6:41 ` Manos Pitsidianakis
2024-08-26 7:45 ` Junjie Mao
2024-08-26 8:24 ` Thomas Huth
2024-08-26 11:29 ` Manos Pitsidianakis
2024-08-23 8:11 ` [PATCH v8 7/8] rust: add utility procedural macro crate Manos Pitsidianakis
2024-08-26 5:15 ` Junjie Mao
2024-08-26 6:02 ` Manos Pitsidianakis
2024-08-23 8:11 ` [PATCH v8 8/8] rust: add PL011 device model Manos Pitsidianakis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=itblf.by425lac4ow@linaro.org \
--to=manos.pitsidianakis@linaro.org \
--cc=alex.bennee@linaro.org \
--cc=berrange@redhat.com \
--cc=gustavo.romero@linaro.org \
--cc=junjie.mao@intel.com \
--cc=marcandre.lureau@redhat.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=philmd@linaro.org \
--cc=pierrick.bouvier@linaro.org \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=thuth@redhat.com \
--cc=zhao1.liu@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).