From 593c408a6a8cd8b0af9bf60c7c3625da7910a737 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:48 +0200 Subject: rust: split Rust-only "common" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-6-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/memory.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'rust/qemu-api/src/memory.rs') diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index e40fad6cf1..f790cb5fd2 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -10,14 +10,11 @@ use std::{ }; pub use bindings::{hwaddr, MemTxAttrs}; +use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; use crate::{ bindings::{self, device_endian, memory_region_init_io}, - callbacks::FnCall, - cell::Opaque, prelude::*, - uninit::MaybeUninitField, - zeroable::Zeroable, }; pub struct MemoryRegionOps( -- cgit 1.4.1 From fcf4c00b4d73185db9239b1a6f03289f6211e142 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:53 +0200 Subject: rust: split "qom" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250827104147.717203-13-marcandre.lureau@redhat.com Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 14 + rust/Cargo.toml | 1 + rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 2 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 3 +- rust/meson.build | 1 + rust/migration/src/vmstate.rs | 2 +- rust/qemu-api-macros/src/lib.rs | 4 +- rust/qemu-api-macros/src/tests.rs | 4 +- rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 15 +- rust/qemu-api/src/bindings.rs | 1 + rust/qemu-api/src/chardev.rs | 3 +- rust/qemu-api/src/irq.rs | 10 +- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/memory.rs | 7 +- rust/qemu-api/src/prelude.rs | 11 - rust/qemu-api/src/qdev.rs | 5 +- rust/qemu-api/src/qom.rs | 951 -------------------------------------- rust/qemu-api/src/sysbus.rs | 4 +- rust/qemu-api/tests/tests.rs | 3 +- rust/qom/Cargo.toml | 23 + rust/qom/build.rs | 1 + rust/qom/meson.build | 43 ++ rust/qom/src/bindings.rs | 25 + rust/qom/src/lib.rs | 11 + rust/qom/src/prelude.rs | 12 + rust/qom/src/qom.rs | 951 ++++++++++++++++++++++++++++++++++++++ rust/qom/wrapper.h | 27 ++ 33 files changed, 1148 insertions(+), 993 deletions(-) delete mode 100644 rust/qemu-api/src/qom.rs create mode 100644 rust/qom/Cargo.toml create mode 120000 rust/qom/build.rs create mode 100644 rust/qom/meson.build create mode 100644 rust/qom/src/bindings.rs create mode 100644 rust/qom/src/lib.rs create mode 100644 rust/qom/src/prelude.rs create mode 100644 rust/qom/src/qom.rs create mode 100644 rust/qom/wrapper.h (limited to 'rust/qemu-api/src/memory.rs') diff --git a/MAINTAINERS b/MAINTAINERS index a55d5c95d7..c7bd02aef1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3520,6 +3520,7 @@ F: rust/common/ F: rust/migration/ F: rust/qemu-api F: rust/qemu-api-macros +F: rust/qom/ F: rust/rustfmt.toml F: rust/util/ F: scripts/get-wraps-from-cargo-registry.py diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 73ca9582a5..442eadf08f 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -82,6 +82,7 @@ dependencies = [ "migration", "qemu_api", "qemu_api_macros", + "qom", "util", ] @@ -121,6 +122,7 @@ dependencies = [ "migration", "qemu_api", "qemu_api_macros", + "qom", "util", ] @@ -164,6 +166,7 @@ dependencies = [ "common", "migration", "qemu_api_macros", + "qom", "util", ] @@ -176,6 +179,17 @@ dependencies = [ "syn", ] +[[package]] +name = "qom" +version = "0.1.0" +dependencies = [ + "bql", + "common", + "migration", + "qemu_api_macros", + "util", +] + [[package]] name = "quote" version = "1.0.36" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8be90da8ff..0516c16591 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,6 +7,7 @@ members = [ "migration", "qemu-api-macros", "qemu-api", + "qom", "hw/char/pl011", "hw/timer/hpet", "util", diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 1a1d4ba715..da89f78727 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -20,6 +20,7 @@ common = { path = "../../../common" } util = { path = "../../../util" } bql = { path = "../../../bql" } migration = { path = "../../../migration" } +qom = { path = "../../../qom" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 8561c4c14a..af9393c9da 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -13,6 +13,7 @@ _libpl011_rs = static_library( migration_rs, bql_rs, qemu_api_macros, + qom_rs, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 00ae432825..63651b9dcd 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -16,9 +16,9 @@ use qemu_api::{ memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, - qom::{ObjectImpl, Owned, ParentField, ParentInit}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; +use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 9fcec38bfa..19456ec72b 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -15,6 +15,7 @@ common = { path = "../../../common" } util = { path = "../../../util" } migration = { path = "../../../migration" } bql = { path = "../../../bql" } +qom = { path = "../../../qom" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 43a62db0d0..50ccdee4a9 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -10,6 +10,7 @@ _libhpet_rs = static_library( migration_rs, bql_rs, qemu_api_macros, + qom_rs, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 9658e071c2..404569aa2d 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -27,10 +27,9 @@ use qemu_api::{ }, prelude::*, qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, - qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; +use qom::{prelude::*, ObjectImpl, ParentField, ParentInit}; use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; use crate::fw_cfg::HPETFwConfig; diff --git a/rust/meson.build b/rust/meson.build index 2ba1ea2280..043603d416 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -28,6 +28,7 @@ subdir('bits') subdir('util') subdir('migration') subdir('bql') +subdir('qom') subdir('qemu-api') subdir('hw') diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index d714aacb7e..c05c4a1fd6 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -137,7 +137,7 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// /// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html /// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html -/// [`Owned`]: ../../qemu_api/qom/struct.Owned.html +/// [`Owned`]: ../../qom/qom/struct.Owned.html #[macro_export] macro_rules! vmstate_of { ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 67650a9a26..e643e57ebd 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -98,11 +98,11 @@ fn derive_object_or_error(input: DeriveInput) -> Result::ParentType>); + ::qom::ParentField<<#name as ::qom::ObjectImpl>::ParentType>); ::util::module_init! { MODULE_INIT_QOM => unsafe { - ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); + ::qom::type_register_static(&<#name as ::qom::ObjectImpl>::TYPE_INFO); } } }) diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index 8e71ac6e67..76e6c57479 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -168,11 +168,11 @@ fn test_derive_object() { ::common::assert_field_type!( Foo, _unused, - ::qemu_api::qom::ParentField<::ParentType> + ::qom::ParentField<::ParentType> ); ::util::module_init! { MODULE_INIT_QOM => unsafe { - ::qemu_api::bindings::type_register_static(&::TYPE_INFO); + ::qom::type_register_static(&::TYPE_INFO); } } } diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 6e9427f80c..9d11becb28 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -18,6 +18,7 @@ common = { path = "../common" } migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } +qom = { path = "../qom" } qemu_api_macros = { path = "../qemu-api-macros" } [lints] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a47ee6c1a3..11e43bb646 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -22,9 +22,15 @@ foreach enum : c_bitfields _qemu_api_bindgen_args += ['--bitfield-enum', enum] endforeach -_qemu_api_bindgen_args += ['--blocklist-type', 'VMStateDescription'] +blocked_type = [ + 'ObjectClass', + 'VMStateDescription', + 'Error', +] +foreach type: blocked_type + _qemu_api_bindgen_args += ['--blocklist-type', type] +endforeach -_qemu_api_bindgen_args += ['--blocklist-type', 'Error'] # TODO: Remove this comment when the clang/libclang mismatch issue is solved. # # Rust bindings generation with `bindgen` might fail in some cases where the @@ -52,7 +58,6 @@ _qemu_api_rs = static_library( 'src/memory.rs', 'src/prelude.rs', 'src/qdev.rs', - 'src/qom.rs', 'src/sysbus.rs', ], {'.' : _qemu_api_bindings_inc_rs}, @@ -61,7 +66,7 @@ _qemu_api_rs = static_library( rust_abi: 'rust', rust_args: _qemu_api_cfg, dependencies: [anyhow_rs, bql_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, - util_rs, qom, hwcore, chardev], + qom_rs, util_rs, hwcore, chardev], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], @@ -74,7 +79,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [bql_rs, common_rs, util_rs, migration_rs, qemu_api_rs]), + dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, qemu_api_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index ce00a6e0e4..525f136ae2 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -22,6 +22,7 @@ use common::Zeroable; use migration::bindings::VMStateDescription; +use qom::bindings::ObjectClass; use util::bindings::Error; #[cfg(MESON)] diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs index 2ec90cc0b2..072d806e4a 100644 --- a/rust/qemu-api/src/chardev.rs +++ b/rust/qemu-api/src/chardev.rs @@ -20,8 +20,9 @@ use std::{ use bql::{BqlRefCell, BqlRefMut}; use common::{callbacks::FnCall, errno, Opaque}; +use qom::prelude::*; -use crate::{bindings, prelude::*}; +use crate::bindings; /// A safe wrapper around [`bindings::Chardev`]. #[repr(transparent)] diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index 3063fbe97a..fead2bbe8e 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -12,12 +12,9 @@ use std::{ use bql::BqlCell; use common::Opaque; +use qom::{prelude::*, ObjectClass}; -use crate::{ - bindings::{self, qemu_set_irq}, - prelude::*, - qom::ObjectClass, -}; +use crate::bindings::{self, qemu_set_irq}; /// An opaque wrapper around [`bindings::IRQState`]. #[repr(transparent)] @@ -36,7 +33,7 @@ pub struct IRQState(Opaque); /// /// Interrupts are implemented as a pointer to the interrupt "sink", which has /// type [`IRQState`]. A device exposes its source as a QOM link property using -/// a function such as [`SysBusDeviceMethods::init_irq`], and +/// a function such as [`crate::sysbus::SysBusDeviceMethods::init_irq`], and /// initially leaves the pointer to a NULL value, representing an unconnected /// interrupt. To connect it, whoever creates the device fills the pointer with /// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because @@ -114,4 +111,5 @@ unsafe impl ObjectType for IRQState { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) }; } + qom_isa!(IRQState: Object); diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 6cd9e5b990..0541050e66 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -17,7 +17,6 @@ pub mod chardev; pub mod irq; pub mod memory; pub mod qdev; -pub mod qom; pub mod sysbus; // Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index f790cb5fd2..ecbbd9b604 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -11,11 +11,9 @@ use std::{ pub use bindings::{hwaddr, MemTxAttrs}; use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; +use qom::prelude::*; -use crate::{ - bindings::{self, device_endian, memory_region_init_io}, - prelude::*, -}; +use crate::bindings::{self, device_endian, memory_region_init_io}; pub struct MemoryRegionOps( bindings::MemoryRegionOps, @@ -186,6 +184,7 @@ unsafe impl ObjectType for MemoryRegion { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; } + qom_isa!(MemoryRegion: Object); /// A special `MemTxAttrs` constant, used to indicate that no memory diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 9da7313016..9e9d1c8247 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -6,15 +6,4 @@ pub use crate::qdev::DeviceMethods; -pub use crate::qom::InterfaceType; -pub use crate::qom::IsA; -pub use crate::qom::Object; -pub use crate::qom::ObjectCast; -pub use crate::qom::ObjectClassMethods; -pub use crate::qom::ObjectDeref; -pub use crate::qom::ObjectMethods; -pub use crate::qom::ObjectType; - -pub use crate::qom_isa; - pub use crate::sysbus::SysBusDeviceMethods; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 74a82b8710..3daf9dda2b 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -12,14 +12,13 @@ use std::{ pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use common::{callbacks::FnCall, Opaque}; use migration::{impl_vmstate_c_struct, VMStateDescription}; +use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit}; use util::{Error, Result}; use crate::{ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, chardev::Chardev, irq::InterruptSource, - prelude::*, - qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, }; /// A safe wrapper around [`bindings::Clock`]. @@ -291,6 +290,7 @@ unsafe impl ObjectType for DeviceState { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; } + qom_isa!(DeviceState: Object); /// Initialization methods take a [`ParentInit`] and can be called as @@ -453,6 +453,7 @@ unsafe impl ObjectType for Clock { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; } + qom_isa!(Clock: Object); impl_vmstate_c_struct!(Clock, bindings::vmstate_clock); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs deleted file mode 100644 index 032701af65..0000000000 --- a/rust/qemu-api/src/qom.rs +++ /dev/null @@ -1,951 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings to access QOM functionality from Rust. -//! -//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU -//! devices. This module makes QOM's features available in Rust through three -//! main mechanisms: -//! -//! * Automatic creation and registration of `TypeInfo` for classes that are -//! written in Rust, as well as mapping between Rust traits and QOM vtables. -//! -//! * Type-safe casting between parent and child classes, through the [`IsA`] -//! trait and methods such as [`upcast`](ObjectCast::upcast) and -//! [`downcast`](ObjectCast::downcast). -//! -//! * Automatic delegation of parent class methods to child classes. When a -//! trait uses [`IsA`] as a bound, its contents become available to all child -//! classes through blanket implementations. This works both for class methods -//! and for instance methods accessed through references or smart pointers. -//! -//! # Structure of a class -//! -//! A leaf class only needs a struct holding instance state. The struct must -//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl` -//! traits that exist for its superclasses. -//! -//! If a class has subclasses, it will also provide a struct for instance data, -//! with the same characteristics as for concrete classes, but it also needs -//! additional components to support virtual methods: -//! -//! * a struct for class data, for example `DeviceClass`. This corresponds to -//! the C "class struct" and holds the vtable that is used by instances of the -//! class and its subclasses. It must start with its parent's class struct. -//! -//! * a trait for virtual method implementations, for example `DeviceImpl`. -//! Child classes implement this trait to provide their own behavior for -//! virtual methods. The trait's methods take `&self` to access instance data. -//! The traits have the appropriate specialization of `IsA<>` as a supertrait, -//! for example `IsA` for `DeviceImpl`. -//! -//! * a trait for instance methods, for example `DeviceMethods`. This trait is -//! automatically implemented for any reference or smart pointer to a device -//! instance. It calls into the vtable provides access across all subclasses -//! to methods defined for the class. -//! -//! * optionally, a trait for class methods, for example `DeviceClassMethods`. -//! This provides access to class-wide functionality that doesn't depend on -//! instance data. Like instance methods, these are automatically inherited by -//! child classes. -//! -//! # Class structures -//! -//! Each QOM class that has virtual methods describes them in a -//! _class struct_. Class structs include a parent field corresponding -//! to the vtable of the parent class, all the way up to [`ObjectClass`]. -//! -//! As mentioned above, virtual methods are defined via traits such as -//! `DeviceImpl`. Class structs do not define any trait but, conventionally, -//! all of them have a `class_init` method to initialize the virtual methods -//! based on the trait and then call the same method on the superclass. -//! -//! ```ignore -//! impl YourSubclassClass -//! { -//! pub fn class_init(&mut self) { -//! ... -//! klass.parent_class::class_init(); -//! } -//! } -//! ``` -//! -//! If a class implements a QOM interface. In that case, the function must -//! contain, for each interface, an extra forwarding call as follows: -//! -//! ```ignore -//! ResettableClass::cast::(self).class_init::(); -//! ``` -//! -//! These `class_init` functions are methods on the class rather than a trait, -//! because the bound on `T` (`DeviceImpl` in this case), will change for every -//! class struct. The functions are pointed to by the -//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default -//! implementation, in most cases it will be enough to write it as follows: -//! -//! ```ignore -//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::; -//! ``` -//! -//! This design incurs a small amount of code duplication but, by not using -//! traits, it allows the flexibility of implementing bindings in any crate, -//! without incurring into violations of orphan rules for traits. - -use std::{ - ffi::{c_void, CStr}, - fmt, - marker::PhantomData, - mem::{ManuallyDrop, MaybeUninit}, - ops::{Deref, DerefMut}, - ptr::NonNull, -}; - -pub use bindings::ObjectClass; -use common::Opaque; -use migration::impl_vmstate_pointer; - -use crate::bindings::{ - self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, - object_new, object_ref, object_unref, TypeInfo, -}; - -/// A safe wrapper around [`bindings::Object`]. -#[repr(transparent)] -#[derive(Debug, qemu_api_macros::Wrapper)] -pub struct Object(Opaque); - -unsafe impl Send for Object {} -unsafe impl Sync for Object {} - -/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct -/// or indirect parent of `Self`). -/// -/// # Safety -/// -/// The struct `Self` must be `#[repr(C)]` and must begin, directly or -/// indirectly, with a field of type `P`. This ensures that invalid casts, -/// which rely on `IsA<>` for static checking, are rejected at compile time. -pub unsafe trait IsA: ObjectType {} - -// SAFETY: it is always safe to cast to your own type -unsafe impl IsA for T {} - -/// Macro to mark superclasses of QOM classes. This enables type-safe -/// up- and downcasting. -/// -/// # Safety -/// -/// This macro is a thin wrapper around the [`IsA`] trait and performs -/// no checking whatsoever of what is declared. It is the caller's -/// responsibility to have $struct begin, directly or indirectly, with -/// a field of type `$parent`. -#[macro_export] -macro_rules! qom_isa { - ($struct:ty : $($parent:ty),* ) => { - $( - // SAFETY: it is the caller responsibility to have $parent as the - // first field - unsafe impl $crate::qom::IsA<$parent> for $struct {} - - impl AsRef<$parent> for $struct { - fn as_ref(&self) -> &$parent { - // SAFETY: follows the same rules as for IsA, which is - // declared above. - let ptr: *const Self = self; - unsafe { &*ptr.cast::<$parent>() } - } - } - )* - }; -} - -/// This is the same as [`ManuallyDrop`](std::mem::ManuallyDrop), though -/// it hides the standard methods of `ManuallyDrop`. -/// -/// The first field of an `ObjectType` must be of type `ParentField`. -/// (Technically, this is only necessary if there is at least one Rust -/// superclass in the hierarchy). This is to ensure that the parent field is -/// dropped after the subclass; this drop order is enforced by the C -/// `object_deinit` function. -/// -/// # Examples -/// -/// ```ignore -/// #[repr(C)] -/// #[derive(qemu_api_macros::Object)] -/// pub struct MyDevice { -/// parent: ParentField, -/// ... -/// } -/// ``` -#[derive(Debug)] -#[repr(transparent)] -pub struct ParentField(std::mem::ManuallyDrop); - -impl Deref for ParentField { - type Target = T; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for ParentField { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl fmt::Display for ParentField { - #[inline(always)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0.fmt(f) - } -} - -/// This struct knows that the superclasses of the object have already been -/// initialized. -/// -/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a -/// technique that is found in several crates, the main ones probably being -/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/)) -/// and `generativity`. -/// -/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to -/// the lifetime argument `'init`. This, together with the `for<'...>` in -/// `[ParentInit::with]`, block any attempt of the compiler to be creative when -/// operating on types of type `ParentInit` and to extend their lifetimes. In -/// particular, it ensures that the `ParentInit` cannot be made to outlive the -/// `rust_instance_init()` function that creates it, and therefore that the -/// `&'init T` reference is valid. -/// -/// This implementation of the same concept, without the QOM baggage, can help -/// understanding the effect: -/// -/// ``` -/// use std::marker::PhantomData; -/// -/// #[derive(PartialEq, Eq)] -/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData &'closure ()>); -/// -/// impl<'closure, T: Copy> Jail<'closure, T> { -/// fn get(&self) -> T { -/// *self.0 -/// } -/// -/// #[inline] -/// fn with(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { -/// let parent_init = Jail(&v, PhantomData); -/// f(parent_init) -/// } -/// } -/// ``` -/// -/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the -/// closure: -/// -/// ```ignore -/// let x = 42; -/// let escape = Jail::with(&x, |token1| { -/// println!("{}", token1.get()); -/// // fails to compile... -/// token1 -/// }); -/// // ... so you cannot do this: -/// println!("{}", escape.get()); -/// ``` -/// -/// Likewise, in the QOM case the `ParentInit` cannot be moved out of -/// `instance_init()`. Without this trick it would be possible to stash a -/// `ParentInit` and use it later to access uninitialized memory. -/// -/// Here is another example, showing how separately-created "identities" stay -/// isolated: -/// -/// ```ignore -/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { -/// fn clone(&self) -> Jail<'closure, T> { -/// Jail(self.0, PhantomData) -/// } -/// } -/// -/// fn main() { -/// Jail::with(42, |token1| { -/// // this works and returns true: the clone has the same "identity" -/// println!("{}", token1 == token1.clone()); -/// Jail::with(42, |token2| { -/// // here the outer token remains accessible... -/// println!("{}", token1.get()); -/// // ... but the two are separate: this fails to compile: -/// println!("{}", token1 == token2); -/// }); -/// }); -/// } -/// ``` -pub struct ParentInit<'init, T>( - &'init mut MaybeUninit, - PhantomData &'init ()>, -); - -impl<'init, T> ParentInit<'init, T> { - #[inline] - pub fn with(obj: &'init mut MaybeUninit, f: impl for<'id> FnOnce(ParentInit<'id, T>)) { - let parent_init = ParentInit(obj, PhantomData); - f(parent_init) - } -} - -impl ParentInit<'_, T> { - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// Fields beyond `Object` could be uninitialized and it's your - /// responsibility to avoid that they're used when the pointer is - /// dereferenced, either directly or through a cast. - pub const fn as_object_mut_ptr(&self) -> *mut bindings::Object { - self.as_object_ptr().cast_mut() - } - - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// Fields beyond `Object` could be uninitialized and it's your - /// responsibility to avoid that they're used when the pointer is - /// dereferenced, either directly or through a cast. - pub const fn as_object_ptr(&self) -> *const bindings::Object { - self.0.as_ptr().cast() - } -} - -impl<'a, T: ObjectImpl> ParentInit<'a, T> { - /// Convert from a derived type to one of its parent types, which - /// have already been initialized. - /// - /// # Safety - /// - /// Structurally this is always a safe operation; the [`IsA`] trait - /// provides static verification trait that `Self` dereferences to `U` or - /// a child of `U`, and only parent types of `T` are allowed. - /// - /// However, while the fields of the resulting reference are initialized, - /// calls might use uninitialized fields of the subclass. It is your - /// responsibility to avoid this. - pub const unsafe fn upcast(&self) -> &'a U - where - T::ParentType: IsA, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait; - // the parent has been initialized before `instance_init `is called - unsafe { &*(self.0.as_ptr().cast::()) } - } - - /// Convert from a derived type to one of its parent types, which - /// have already been initialized. - /// - /// # Safety - /// - /// Structurally this is always a safe operation; the [`IsA`] trait - /// provides static verification trait that `Self` dereferences to `U` or - /// a child of `U`, and only parent types of `T` are allowed. - /// - /// However, while the fields of the resulting reference are initialized, - /// calls might use uninitialized fields of the subclass. It is your - /// responsibility to avoid this. - pub unsafe fn upcast_mut(&mut self) -> &'a mut U - where - T::ParentType: IsA, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait; - // the parent has been initialized before `instance_init `is called - unsafe { &mut *(self.0.as_mut_ptr().cast::()) } - } -} - -impl Deref for ParentInit<'_, T> { - type Target = MaybeUninit; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl DerefMut for ParentInit<'_, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } -} - -unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { - let mut state = NonNull::new(obj).unwrap().cast::>(); - - // SAFETY: obj is an instance of T, since rust_instance_init - // is called from QOM core as the instance_init function - // for class T - unsafe { - ParentInit::with(state.as_mut(), |parent_init| { - T::INSTANCE_INIT.unwrap()(parent_init); - }); - } -} - -unsafe extern "C" fn rust_instance_post_init(obj: *mut bindings::Object) { - let state = NonNull::new(obj).unwrap().cast::(); - // SAFETY: obj is an instance of T, since rust_instance_post_init - // is called from QOM core as the instance_post_init function - // for class T - T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); -} - -unsafe extern "C" fn rust_class_init( - klass: *mut ObjectClass, - _data: *const c_void, -) { - let mut klass = NonNull::new(klass) - .unwrap() - .cast::<::Class>(); - // SAFETY: klass is a T::Class, since rust_class_init - // is called from QOM core as the class_init function - // for class T - ::CLASS_INIT(unsafe { klass.as_mut() }) -} - -unsafe extern "C" fn drop_object(obj: *mut bindings::Object) { - // SAFETY: obj is an instance of T, since drop_object is called - // from the QOM core function object_deinit() as the instance_finalize - // function for class T. Note that while object_deinit() will drop the - // superclass field separately after this function returns, `T` must - // implement the unsafe trait ObjectType; the safety rules for the - // trait mandate that the parent field is manually dropped. - unsafe { std::ptr::drop_in_place(obj.cast::()) } -} - -/// Trait exposed by all structs corresponding to QOM objects. -/// -/// # Safety -/// -/// For classes declared in C: -/// -/// - `Class` and `TYPE` must match the data in the `TypeInfo`; -/// -/// - the first field of the struct must be of the instance type corresponding -/// to the superclass, as declared in the `TypeInfo` -/// -/// - likewise, the first field of the `Class` struct must be of the class type -/// corresponding to the superclass -/// -/// For classes declared in Rust and implementing [`ObjectImpl`]: -/// -/// - the struct must be `#[repr(C)]`; -/// -/// - the first field of the struct must be of type -/// [`ParentField`](ParentField), where `T` is the parent type -/// [`ObjectImpl::ParentType`] -/// -/// - the first field of the `Class` must be of the class struct corresponding -/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField` -/// is not needed here. -/// -/// In both cases, having a separate class type is not necessary if the subclass -/// does not add any field. -pub unsafe trait ObjectType: Sized { - /// The QOM class object corresponding to this struct. This is used - /// to automatically generate a `class_init` method. - type Class; - - /// The name of the type, which can be passed to `object_new()` to - /// generate an instance of this type. - const TYPE_NAME: &'static CStr; - - /// Return the receiver as an Object. This is always safe, even - /// if this type represents an interface. - fn as_object(&self) -> &Object { - unsafe { &*self.as_ptr().cast() } - } - - /// Return the receiver as a const raw pointer to Object. - /// This is preferable to `as_object_mut_ptr()` if a C - /// function only needs a `const Object *`. - fn as_object_ptr(&self) -> *const bindings::Object { - self.as_object().as_ptr() - } - - /// Return the receiver as a mutable raw pointer to Object. - /// - /// # Safety - /// - /// This cast is always safe, but because the result is mutable - /// and the incoming reference is not, this should only be used - /// for calls to C functions, and only if needed. - unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object { - self.as_object().as_mut_ptr() - } -} - -/// Trait exposed by all structs corresponding to QOM interfaces. -/// Unlike `ObjectType`, it is implemented on the class type (which provides -/// the vtable for the interfaces). -/// -/// # Safety -/// -/// `TYPE` must match the contents of the `TypeInfo` as found in the C code; -/// right now, interfaces can only be declared in C. -pub unsafe trait InterfaceType: Sized { - /// The name of the type, which can be passed to - /// `object_class_dynamic_cast()` to obtain the pointer to the vtable - /// for this interface. - const TYPE_NAME: &'static CStr; - - /// Return the vtable for the interface; `U` is the type that - /// lists the interface in its `TypeInfo`. - /// - /// # Examples - /// - /// This function is usually called by a `class_init` method in `U::Class`. - /// For example, `DeviceClass::class_init` initializes its `Resettable` - /// interface as follows: - /// - /// ```ignore - /// ResettableClass::cast::(self).class_init::(); - /// ``` - /// - /// where `T` is the concrete subclass that is being initialized. - /// - /// # Panics - /// - /// Panic if the incoming argument if `T` does not implement the interface. - fn cast(klass: &mut U::Class) -> &mut Self { - unsafe { - // SAFETY: upcasting to ObjectClass is always valid, and the - // return type is either NULL or the argument itself - let result: *mut Self = object_class_dynamic_cast( - (klass as *mut U::Class).cast(), - Self::TYPE_NAME.as_ptr(), - ) - .cast(); - result.as_mut().unwrap() - } - } -} - -/// This trait provides safe casting operations for QOM objects to raw pointers, -/// to be used for example for FFI. The trait can be applied to any kind of -/// reference or smart pointers, and enforces correctness through the [`IsA`] -/// trait. -pub trait ObjectDeref: Deref -where - Self::Target: ObjectType, -{ - /// Convert to a const Rust pointer, to be used for example for FFI. - /// The target pointer type must be the type of `self` or a superclass - fn as_ptr(&self) -> *const U - where - Self::Target: IsA, - { - let ptr: *const Self::Target = self.deref(); - ptr.cast::() - } - - /// Convert to a mutable Rust pointer, to be used for example for FFI. - /// The target pointer type must be the type of `self` or a superclass. - /// Used to implement interior mutability for objects. - /// - /// # Safety - /// - /// This method is safe because only the actual dereference of the pointer - /// has to be unsafe. Bindings to C APIs will use it a lot, but care has - /// to be taken because it overrides the const-ness of `&self`. - fn as_mut_ptr(&self) -> *mut U - where - Self::Target: IsA, - { - #[allow(clippy::as_ptr_cast_mut)] - { - self.as_ptr::().cast_mut() - } - } -} - -/// Trait that adds extra functionality for `&T` where `T` is a QOM -/// object type. Allows conversion to/from C objects in generic code. -pub trait ObjectCast: ObjectDeref + Copy -where - Self::Target: ObjectType, -{ - /// Safely convert from a derived type to one of its parent types. - /// - /// This is always safe; the [`IsA`] trait provides static verification - /// trait that `Self` dereferences to `U` or a child of `U`. - fn upcast<'a, U: ObjectType>(self) -> &'a U - where - Self::Target: IsA, - Self: 'a, - { - // SAFETY: soundness is declared via IsA, which is an unsafe trait - unsafe { self.unsafe_cast::() } - } - - /// Attempt to convert to a derived type. - /// - /// Returns `None` if the object is not actually of type `U`. This is - /// verified at runtime by checking the object's type information. - fn downcast<'a, U: IsA>(self) -> Option<&'a U> - where - Self: 'a, - { - self.dynamic_cast::() - } - - /// Attempt to convert between any two types in the QOM hierarchy. - /// - /// Returns `None` if the object is not actually of type `U`. This is - /// verified at runtime by checking the object's type information. - fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U> - where - Self: 'a, - { - unsafe { - // SAFETY: upcasting to Object is always valid, and the - // return type is either NULL or the argument itself - let result: *const U = - object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); - - result.as_ref() - } - } - - /// Convert to any QOM type without verification. - /// - /// # Safety - /// - /// What safety? You need to know yourself that the cast is correct; only - /// use when performance is paramount. It is still better than a raw - /// pointer `cast()`, which does not even check that you remain in the - /// realm of QOM `ObjectType`s. - /// - /// `unsafe_cast::()` is always safe. - unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U - where - Self: 'a, - { - unsafe { &*(self.as_ptr::().cast::()) } - } -} - -impl ObjectDeref for &T {} -impl ObjectCast for &T {} - -impl ObjectDeref for &mut T {} - -/// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: ObjectType + IsA { - /// The parent of the type. This should match the first field of the - /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. - type ParentType: ObjectType; - - /// Whether the object can be instantiated - const ABSTRACT: bool = false; - - /// Function that is called to initialize an object. The parent class will - /// have already been initialized so the type is only responsible for - /// initializing its own members. - /// - /// FIXME: The argument is not really a valid reference. `&mut - /// MaybeUninit` would be a better description. - const INSTANCE_INIT: Option)> = None; - - /// Function that is called to finish initialization of an object, once - /// `INSTANCE_INIT` functions have been called. - const INSTANCE_POST_INIT: Option = None; - - /// Called on descendant classes after all parent class initialization - /// has occurred, but before the class itself is initialized. This - /// is only useful if a class is not a leaf, and can be used to undo - /// the effects of copying the contents of the parent's class struct - /// to the descendants. - const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *const c_void), - > = None; - - const TYPE_INFO: TypeInfo = TypeInfo { - name: Self::TYPE_NAME.as_ptr(), - parent: Self::ParentType::TYPE_NAME.as_ptr(), - instance_size: core::mem::size_of::(), - instance_align: core::mem::align_of::(), - instance_init: match Self::INSTANCE_INIT { - None => None, - Some(_) => Some(rust_instance_init::), - }, - instance_post_init: match Self::INSTANCE_POST_INIT { - None => None, - Some(_) => Some(rust_instance_post_init::), - }, - instance_finalize: Some(drop_object::), - abstract_: Self::ABSTRACT, - class_size: core::mem::size_of::(), - class_init: Some(rust_class_init::), - class_base_init: Self::CLASS_BASE_INIT, - class_data: core::ptr::null(), - interfaces: core::ptr::null(), - }; - - // methods on ObjectClass - const UNPARENT: Option = None; - - /// Store into the argument the virtual method implementations - /// for `Self`. On entry, the virtual method pointers are set to - /// the default values coming from the parent classes; the function - /// can change them to override virtual methods of a parent class. - /// - /// Usually defined simply as `Self::Class::class_init::`; - /// however a default implementation cannot be included here, because the - /// bounds that the `Self::Class::class_init` method places on `Self` are - /// not known in advance. - /// - /// # Safety - /// - /// While `klass`'s parent class is initialized on entry, the other fields - /// are all zero; it is therefore assumed that all fields in `T` can be - /// zeroed, otherwise it would not be possible to provide the class as a - /// `&mut T`. TODO: it may be possible to add an unsafe trait that checks - /// that all fields *after the parent class* (but not the parent class - /// itself) are Zeroable. This unsafe trait can be added via a derive - /// macro. - const CLASS_INIT: fn(&mut Self::Class); -} - -/// # Safety -/// -/// We expect the FFI user of this function to pass a valid pointer that -/// can be downcasted to type `T`. We also expect the device is -/// readable/writeable from one thread at any time. -unsafe extern "C" fn rust_unparent_fn(dev: *mut bindings::Object) { - let state = NonNull::new(dev).unwrap().cast::(); - T::UNPARENT.unwrap()(unsafe { state.as_ref() }); -} - -impl ObjectClass { - /// Fill in the virtual methods of `ObjectClass` based on the definitions in - /// the `ObjectImpl` trait. - pub fn class_init(&mut self) { - if ::UNPARENT.is_some() { - self.unparent = Some(rust_unparent_fn::); - } - } -} - -unsafe impl ObjectType for Object { - type Class = ObjectClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; -} - -/// A reference-counted pointer to a QOM object. -/// -/// `Owned` wraps `T` with automatic reference counting. It increases the -/// reference count when created via [`Owned::from`] or cloned, and decreases -/// it when dropped. This ensures that the reference count remains elevated -/// as long as any `Owned` references to it exist. -/// -/// `Owned` can be used for two reasons: -/// * because the lifetime of the QOM object is unknown and someone else could -/// take a reference (similar to `Arc`, for example): in this case, the -/// object can escape and outlive the Rust struct that contains the `Owned` -/// field; -/// -/// * to ensure that the object stays alive until after `Drop::drop` is called -/// on the Rust struct: in this case, the object will always die together with -/// the Rust struct that contains the `Owned` field. -/// -/// Child properties are an example of the second case: in C, an object that -/// is created with `object_initialize_child` will die *before* -/// `instance_finalize` is called, whereas Rust expects the struct to have valid -/// contents when `Drop::drop` is called. Therefore Rust structs that have -/// child properties need to keep a reference to the child object. Right now -/// this can be done with `Owned`; in the future one might have a separate -/// `Child<'parent, T>` smart pointer that keeps a reference to a `T`, like -/// `Owned`, but does not allow cloning. -/// -/// Note that dropping an `Owned` requires the big QEMU lock to be taken. -#[repr(transparent)] -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Owned(NonNull); - -// The following rationale for safety is taken from Linux's kernel::sync::Arc. - -// SAFETY: It is safe to send `Owned` to another thread when the underlying -// `T` is `Sync` because it effectively means sharing `&T` (which is safe -// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any -// thread that has an `Owned` may ultimately access `T` using a -// mutable reference when the reference count reaches zero and `T` is dropped. -unsafe impl Send for Owned {} - -// SAFETY: It is safe to send `&Owned` to another thread when the underlying -// `T` is `Sync` because it effectively means sharing `&T` (which is safe -// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any -// thread that has a `&Owned` may clone it and get an `Owned` on that -// thread, so the thread may ultimately access `T` using a mutable reference -// when the reference count reaches zero and `T` is dropped. -unsafe impl Sync for Owned {} - -impl Owned { - /// Convert a raw C pointer into an owned reference to the QOM - /// object it points to. The object's reference count will be - /// decreased when the `Owned` is dropped. - /// - /// # Panics - /// - /// Panics if `ptr` is NULL. - /// - /// # Safety - /// - /// The caller must indeed own a reference to the QOM object. - /// The object must not be embedded in another unless the outer - /// object is guaranteed to have a longer lifetime. - /// - /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed - /// back to `from_raw()` (assuming the original `Owned` was valid!), - /// since the owned reference remains there between the calls to - /// `into_raw()` and `from_raw()`. - pub unsafe fn from_raw(ptr: *const T) -> Self { - // SAFETY NOTE: while NonNull requires a mutable pointer, only - // Deref is implemented so the pointer passed to from_raw - // remains const - Owned(NonNull::new(ptr.cast_mut()).unwrap()) - } - - /// Obtain a raw C pointer from a reference. `src` is consumed - /// and the reference is leaked. - #[allow(clippy::missing_const_for_fn)] - pub fn into_raw(src: Owned) -> *mut T { - let src = ManuallyDrop::new(src); - src.0.as_ptr() - } - - /// Increase the reference count of a QOM object and return - /// a new owned reference to it. - /// - /// # Safety - /// - /// The object must not be embedded in another, unless the outer - /// object is guaranteed to have a longer lifetime. - pub unsafe fn from(obj: &T) -> Self { - unsafe { - object_ref(obj.as_object_mut_ptr().cast::()); - - // SAFETY NOTE: while NonNull requires a mutable pointer, only - // Deref is implemented so the reference passed to from_raw - // remains shared - Owned(NonNull::new_unchecked(obj.as_mut_ptr())) - } - } -} - -impl Clone for Owned { - fn clone(&self) -> Self { - // SAFETY: creation method is unsafe; whoever calls it has - // responsibility that the pointer is valid, and remains valid - // throughout the lifetime of the `Owned` and its clones. - unsafe { Owned::from(self.deref()) } - } -} - -impl Deref for Owned { - type Target = T; - - fn deref(&self) -> &Self::Target { - // SAFETY: creation method is unsafe; whoever calls it has - // responsibility that the pointer is valid, and remains valid - // throughout the lifetime of the `Owned` and its clones. - // With that guarantee, reference counting ensures that - // the object remains alive. - unsafe { &*self.0.as_ptr() } - } -} -impl ObjectDeref for Owned {} - -impl Drop for Owned { - fn drop(&mut self) { - assert!(bql::is_locked()); - // SAFETY: creation method is unsafe, and whoever calls it has - // responsibility that the pointer is valid, and remains valid - // throughout the lifetime of the `Owned` and its clones. - unsafe { - object_unref(self.as_object_mut_ptr().cast::()); - } - } -} - -impl> fmt::Debug for Owned { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.deref().debug_fmt(f) - } -} - -/// Trait for class methods exposed by the Object class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA` -pub trait ObjectClassMethods: IsA { - /// Return a new reference counted instance of this class - fn new() -> Owned { - assert!(bql::is_locked()); - // SAFETY: the object created by object_new is allocated on - // the heap and has a reference count of 1 - unsafe { - let raw_obj = object_new(Self::TYPE_NAME.as_ptr()); - let obj = Object::from_raw(raw_obj).unsafe_cast::(); - Owned::from_raw(obj) - } - } -} - -/// Trait for methods exposed by the Object class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA` -pub trait ObjectMethods: ObjectDeref -where - Self::Target: IsA, -{ - /// Return the name of the type of `self` - fn typename(&self) -> std::borrow::Cow<'_, str> { - let obj = self.upcast::(); - // SAFETY: safety of this is the requirement for implementing IsA - // The result of the C API has static lifetime - unsafe { - let p = object_get_typename(obj.as_mut_ptr()); - CStr::from_ptr(p).to_string_lossy() - } - } - - fn get_class(&self) -> &'static ::Class { - let obj = self.upcast::(); - - // SAFETY: all objects can call object_get_class; the actual class - // type is guaranteed by the implementation of `ObjectType` and - // `ObjectImpl`. - let klass: &'static ::Class = - unsafe { &*object_get_class(obj.as_mut_ptr()).cast() }; - - klass - } - - /// Convenience function for implementing the Debug trait - fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple(&self.typename()) - .field(&(self as *const Self)) - .finish() - } -} - -impl ObjectClassMethods for T where T: IsA {} -impl ObjectMethods for R where R::Target: IsA {} - -impl_vmstate_pointer!(Owned where T: VMState + ObjectType); diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index b21883246e..b883d7eaf1 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -8,14 +8,13 @@ use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::SysBusDeviceClass; use common::Opaque; +use qom::{prelude::*, Owned}; use crate::{ bindings, irq::{IRQState, InterruptSource}, memory::MemoryRegion, - prelude::*, qdev::{DeviceImpl, DeviceState}, - qom::Owned, }; /// A safe wrapper around [`bindings::SysBusDevice`]. @@ -31,6 +30,7 @@ unsafe impl ObjectType for SysBusDevice { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; } + qom_isa!(SysBusDevice: DeviceState, Object); // TODO: add virtual methods diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index e72ba08aef..f2e5eb9f4f 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -7,11 +7,10 @@ use std::{ffi::CStr, ptr::addr_of}; use bql::BqlCell; use migration::{VMStateDescription, VMStateDescriptionBuilder}; use qemu_api::{ - prelude::*, qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl}, - qom::{ObjectImpl, ParentField}, sysbus::SysBusDevice, }; +use qom::{prelude::*, ObjectImpl, ParentField}; use util::bindings::{module_call_init, module_init_type}; mod vmstate_tests; diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml new file mode 100644 index 0000000000..46bbf7c7fe --- /dev/null +++ b/rust/qom/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "qom" +version = "0.1.0" +description = "Rust bindings for QEMU/QOM" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +common = { path = "../common" } +bql = { path = "../bql" } +migration = { path = "../migration" } +qemu_api_macros = { path = "../qemu-api-macros" } +util = { path = "../util" } + +[lints] +workspace = true diff --git a/rust/qom/build.rs b/rust/qom/build.rs new file mode 120000 index 0000000000..71a3167885 --- /dev/null +++ b/rust/qom/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/qom/meson.build b/rust/qom/meson.build new file mode 100644 index 0000000000..84a65cb737 --- /dev/null +++ b/rust/qom/meson.build @@ -0,0 +1,43 @@ +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_qom_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common, +) + +_qom_rs = static_library( + 'qom', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/prelude.rs', + 'src/qom.rs', + ], + {'.': _qom_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _migration_rs], + dependencies: [common_rs, qemu_api_macros], +) + +qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_api_macros, qom]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-qom-rs-doctests', + _qom_rs, + protocol: 'rust', + dependencies: qom_rs, + suite: ['doc', 'rust']) diff --git a/rust/qom/src/bindings.rs b/rust/qom/src/bindings.rs new file mode 100644 index 0000000000..9ffff12cde --- /dev/null +++ b/rust/qom/src/bindings.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs new file mode 100644 index 0000000000..204c6fea2f --- /dev/null +++ b/rust/qom/src/lib.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; + +// preserve one-item-per-"use" syntax, it is clearer +// for prelude-like modules +#[rustfmt::skip] +pub mod prelude; + +mod qom; +pub use qom::*; diff --git a/rust/qom/src/prelude.rs b/rust/qom/src/prelude.rs new file mode 100644 index 0000000000..00a6095977 --- /dev/null +++ b/rust/qom/src/prelude.rs @@ -0,0 +1,12 @@ +//! Traits and essential types intended for blanket imports. + +pub use crate::qom::InterfaceType; +pub use crate::qom::IsA; +pub use crate::qom::Object; +pub use crate::qom::ObjectCast; +pub use crate::qom::ObjectClassMethods; +pub use crate::qom::ObjectDeref; +pub use crate::qom::ObjectMethods; +pub use crate::qom::ObjectType; + +pub use crate::qom_isa; diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs new file mode 100644 index 0000000000..3ea1ad9c5b --- /dev/null +++ b/rust/qom/src/qom.rs @@ -0,0 +1,951 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings to access QOM functionality from Rust. +//! +//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU +//! devices. This module makes QOM's features available in Rust through three +//! main mechanisms: +//! +//! * Automatic creation and registration of `TypeInfo` for classes that are +//! written in Rust, as well as mapping between Rust traits and QOM vtables. +//! +//! * Type-safe casting between parent and child classes, through the [`IsA`] +//! trait and methods such as [`upcast`](ObjectCast::upcast) and +//! [`downcast`](ObjectCast::downcast). +//! +//! * Automatic delegation of parent class methods to child classes. When a +//! trait uses [`IsA`] as a bound, its contents become available to all child +//! classes through blanket implementations. This works both for class methods +//! and for instance methods accessed through references or smart pointers. +//! +//! # Structure of a class +//! +//! A leaf class only needs a struct holding instance state. The struct must +//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl` +//! traits that exist for its superclasses. +//! +//! If a class has subclasses, it will also provide a struct for instance data, +//! with the same characteristics as for concrete classes, but it also needs +//! additional components to support virtual methods: +//! +//! * a struct for class data, for example `DeviceClass`. This corresponds to +//! the C "class struct" and holds the vtable that is used by instances of the +//! class and its subclasses. It must start with its parent's class struct. +//! +//! * a trait for virtual method implementations, for example `DeviceImpl`. +//! Child classes implement this trait to provide their own behavior for +//! virtual methods. The trait's methods take `&self` to access instance data. +//! The traits have the appropriate specialization of `IsA<>` as a supertrait, +//! for example `IsA` for `DeviceImpl`. +//! +//! * a trait for instance methods, for example `DeviceMethods`. This trait is +//! automatically implemented for any reference or smart pointer to a device +//! instance. It calls into the vtable provides access across all subclasses +//! to methods defined for the class. +//! +//! * optionally, a trait for class methods, for example `DeviceClassMethods`. +//! This provides access to class-wide functionality that doesn't depend on +//! instance data. Like instance methods, these are automatically inherited by +//! child classes. +//! +//! # Class structures +//! +//! Each QOM class that has virtual methods describes them in a +//! _class struct_. Class structs include a parent field corresponding +//! to the vtable of the parent class, all the way up to [`ObjectClass`]. +//! +//! As mentioned above, virtual methods are defined via traits such as +//! `DeviceImpl`. Class structs do not define any trait but, conventionally, +//! all of them have a `class_init` method to initialize the virtual methods +//! based on the trait and then call the same method on the superclass. +//! +//! ```ignore +//! impl YourSubclassClass +//! { +//! pub fn class_init(&mut self) { +//! ... +//! klass.parent_class::class_init(); +//! } +//! } +//! ``` +//! +//! If a class implements a QOM interface. In that case, the function must +//! contain, for each interface, an extra forwarding call as follows: +//! +//! ```ignore +//! ResettableClass::cast::(self).class_init::(); +//! ``` +//! +//! These `class_init` functions are methods on the class rather than a trait, +//! because the bound on `T` (`DeviceImpl` in this case), will change for every +//! class struct. The functions are pointed to by the +//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default +//! implementation, in most cases it will be enough to write it as follows: +//! +//! ```ignore +//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::; +//! ``` +//! +//! This design incurs a small amount of code duplication but, by not using +//! traits, it allows the flexibility of implementing bindings in any crate, +//! without incurring into violations of orphan rules for traits. + +use std::{ + ffi::{c_void, CStr}, + fmt, + marker::PhantomData, + mem::{ManuallyDrop, MaybeUninit}, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; + +use common::Opaque; +use migration::impl_vmstate_pointer; + +use crate::bindings::{ + self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename, + object_new, object_ref, object_unref, TypeInfo, +}; +pub use crate::bindings::{type_register_static, ObjectClass}; + +/// A safe wrapper around [`bindings::Object`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Object(Opaque); + +unsafe impl Send for Object {} +unsafe impl Sync for Object {} + +/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct +/// or indirect parent of `Self`). +/// +/// # Safety +/// +/// The struct `Self` must be `#[repr(C)]` and must begin, directly or +/// indirectly, with a field of type `P`. This ensures that invalid casts, +/// which rely on `IsA<>` for static checking, are rejected at compile time. +pub unsafe trait IsA: ObjectType {} + +// SAFETY: it is always safe to cast to your own type +unsafe impl IsA for T {} + +/// Macro to mark superclasses of QOM classes. This enables type-safe +/// up- and downcasting. +/// +/// # Safety +/// +/// This macro is a thin wrapper around the [`IsA`] trait and performs +/// no checking whatsoever of what is declared. It is the caller's +/// responsibility to have $struct begin, directly or indirectly, with +/// a field of type `$parent`. +#[macro_export] +macro_rules! qom_isa { + ($struct:ty : $($parent:ty),* ) => { + $( + // SAFETY: it is the caller responsibility to have $parent as the + // first field + unsafe impl $crate::IsA<$parent> for $struct {} + + impl AsRef<$parent> for $struct { + fn as_ref(&self) -> &$parent { + // SAFETY: follows the same rules as for IsA, which is + // declared above. + let ptr: *const Self = self; + unsafe { &*ptr.cast::<$parent>() } + } + } + )* + }; +} + +/// This is the same as [`ManuallyDrop`](std::mem::ManuallyDrop), though +/// it hides the standard methods of `ManuallyDrop`. +/// +/// The first field of an `ObjectType` must be of type `ParentField`. +/// (Technically, this is only necessary if there is at least one Rust +/// superclass in the hierarchy). This is to ensure that the parent field is +/// dropped after the subclass; this drop order is enforced by the C +/// `object_deinit` function. +/// +/// # Examples +/// +/// ```ignore +/// #[repr(C)] +/// #[derive(qemu_api_macros::Object)] +/// pub struct MyDevice { +/// parent: ParentField, +/// ... +/// } +/// ``` +#[derive(Debug)] +#[repr(transparent)] +pub struct ParentField(std::mem::ManuallyDrop); + +impl Deref for ParentField { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ParentField { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Display for ParentField { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +/// This struct knows that the superclasses of the object have already been +/// initialized. +/// +/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a +/// technique that is found in several crates, the main ones probably being +/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/)) +/// and `generativity`. +/// +/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to +/// the lifetime argument `'init`. This, together with the `for<'...>` in +/// `[ParentInit::with]`, block any attempt of the compiler to be creative when +/// operating on types of type `ParentInit` and to extend their lifetimes. In +/// particular, it ensures that the `ParentInit` cannot be made to outlive the +/// `rust_instance_init()` function that creates it, and therefore that the +/// `&'init T` reference is valid. +/// +/// This implementation of the same concept, without the QOM baggage, can help +/// understanding the effect: +/// +/// ``` +/// use std::marker::PhantomData; +/// +/// #[derive(PartialEq, Eq)] +/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData &'closure ()>); +/// +/// impl<'closure, T: Copy> Jail<'closure, T> { +/// fn get(&self) -> T { +/// *self.0 +/// } +/// +/// #[inline] +/// fn with(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { +/// let parent_init = Jail(&v, PhantomData); +/// f(parent_init) +/// } +/// } +/// ``` +/// +/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the +/// closure: +/// +/// ```ignore +/// let x = 42; +/// let escape = Jail::with(&x, |token1| { +/// println!("{}", token1.get()); +/// // fails to compile... +/// token1 +/// }); +/// // ... so you cannot do this: +/// println!("{}", escape.get()); +/// ``` +/// +/// Likewise, in the QOM case the `ParentInit` cannot be moved out of +/// `instance_init()`. Without this trick it would be possible to stash a +/// `ParentInit` and use it later to access uninitialized memory. +/// +/// Here is another example, showing how separately-created "identities" stay +/// isolated: +/// +/// ```ignore +/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { +/// fn clone(&self) -> Jail<'closure, T> { +/// Jail(self.0, PhantomData) +/// } +/// } +/// +/// fn main() { +/// Jail::with(42, |token1| { +/// // this works and returns true: the clone has the same "identity" +/// println!("{}", token1 == token1.clone()); +/// Jail::with(42, |token2| { +/// // here the outer token remains accessible... +/// println!("{}", token1.get()); +/// // ... but the two are separate: this fails to compile: +/// println!("{}", token1 == token2); +/// }); +/// }); +/// } +/// ``` +pub struct ParentInit<'init, T>( + &'init mut MaybeUninit, + PhantomData &'init ()>, +); + +impl<'init, T> ParentInit<'init, T> { + #[inline] + pub fn with(obj: &'init mut MaybeUninit, f: impl for<'id> FnOnce(ParentInit<'id, T>)) { + let parent_init = ParentInit(obj, PhantomData); + f(parent_init) + } +} + +impl ParentInit<'_, T> { + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub const fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object_ptr().cast_mut() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub const fn as_object_ptr(&self) -> *const bindings::Object { + self.0.as_ptr().cast() + } +} + +impl<'a, T: ObjectImpl> ParentInit<'a, T> { + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub const unsafe fn upcast(&self) -> &'a U + where + T::ParentType: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &*(self.0.as_ptr().cast::()) } + } + + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub unsafe fn upcast_mut(&mut self) -> &'a mut U + where + T::ParentType: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &mut *(self.0.as_mut_ptr().cast::()) } + } +} + +impl Deref for ParentInit<'_, T> { + type Target = MaybeUninit; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl DerefMut for ParentInit<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { + let mut state = NonNull::new(obj).unwrap().cast::>(); + + // SAFETY: obj is an instance of T, since rust_instance_init + // is called from QOM core as the instance_init function + // for class T + unsafe { + ParentInit::with(state.as_mut(), |parent_init| { + T::INSTANCE_INIT.unwrap()(parent_init); + }); + } +} + +unsafe extern "C" fn rust_instance_post_init(obj: *mut bindings::Object) { + let state = NonNull::new(obj).unwrap().cast::(); + // SAFETY: obj is an instance of T, since rust_instance_post_init + // is called from QOM core as the instance_post_init function + // for class T + T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); +} + +unsafe extern "C" fn rust_class_init( + klass: *mut ObjectClass, + _data: *const c_void, +) { + let mut klass = NonNull::new(klass) + .unwrap() + .cast::<::Class>(); + // SAFETY: klass is a T::Class, since rust_class_init + // is called from QOM core as the class_init function + // for class T + ::CLASS_INIT(unsafe { klass.as_mut() }) +} + +unsafe extern "C" fn drop_object(obj: *mut bindings::Object) { + // SAFETY: obj is an instance of T, since drop_object is called + // from the QOM core function object_deinit() as the instance_finalize + // function for class T. Note that while object_deinit() will drop the + // superclass field separately after this function returns, `T` must + // implement the unsafe trait ObjectType; the safety rules for the + // trait mandate that the parent field is manually dropped. + unsafe { std::ptr::drop_in_place(obj.cast::()) } +} + +/// Trait exposed by all structs corresponding to QOM objects. +/// +/// # Safety +/// +/// For classes declared in C: +/// +/// - `Class` and `TYPE` must match the data in the `TypeInfo`; +/// +/// - the first field of the struct must be of the instance type corresponding +/// to the superclass, as declared in the `TypeInfo` +/// +/// - likewise, the first field of the `Class` struct must be of the class type +/// corresponding to the superclass +/// +/// For classes declared in Rust and implementing [`ObjectImpl`]: +/// +/// - the struct must be `#[repr(C)]`; +/// +/// - the first field of the struct must be of type +/// [`ParentField`](ParentField), where `T` is the parent type +/// [`ObjectImpl::ParentType`] +/// +/// - the first field of the `Class` must be of the class struct corresponding +/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField` +/// is not needed here. +/// +/// In both cases, having a separate class type is not necessary if the subclass +/// does not add any field. +pub unsafe trait ObjectType: Sized { + /// The QOM class object corresponding to this struct. This is used + /// to automatically generate a `class_init` method. + type Class; + + /// The name of the type, which can be passed to `object_new()` to + /// generate an instance of this type. + const TYPE_NAME: &'static CStr; + + /// Return the receiver as an Object. This is always safe, even + /// if this type represents an interface. + fn as_object(&self) -> &Object { + unsafe { &*self.as_ptr().cast() } + } + + /// Return the receiver as a const raw pointer to Object. + /// This is preferable to `as_object_mut_ptr()` if a C + /// function only needs a `const Object *`. + fn as_object_ptr(&self) -> *const bindings::Object { + self.as_object().as_ptr() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// This cast is always safe, but because the result is mutable + /// and the incoming reference is not, this should only be used + /// for calls to C functions, and only if needed. + unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object().as_mut_ptr() + } +} + +/// Trait exposed by all structs corresponding to QOM interfaces. +/// Unlike `ObjectType`, it is implemented on the class type (which provides +/// the vtable for the interfaces). +/// +/// # Safety +/// +/// `TYPE` must match the contents of the `TypeInfo` as found in the C code; +/// right now, interfaces can only be declared in C. +pub unsafe trait InterfaceType: Sized { + /// The name of the type, which can be passed to + /// `object_class_dynamic_cast()` to obtain the pointer to the vtable + /// for this interface. + const TYPE_NAME: &'static CStr; + + /// Return the vtable for the interface; `U` is the type that + /// lists the interface in its `TypeInfo`. + /// + /// # Examples + /// + /// This function is usually called by a `class_init` method in `U::Class`. + /// For example, `DeviceClass::class_init` initializes its `Resettable` + /// interface as follows: + /// + /// ```ignore + /// ResettableClass::cast::(self).class_init::(); + /// ``` + /// + /// where `T` is the concrete subclass that is being initialized. + /// + /// # Panics + /// + /// Panic if the incoming argument if `T` does not implement the interface. + fn cast(klass: &mut U::Class) -> &mut Self { + unsafe { + // SAFETY: upcasting to ObjectClass is always valid, and the + // return type is either NULL or the argument itself + let result: *mut Self = object_class_dynamic_cast( + (klass as *mut U::Class).cast(), + Self::TYPE_NAME.as_ptr(), + ) + .cast(); + result.as_mut().unwrap() + } + } +} + +/// This trait provides safe casting operations for QOM objects to raw pointers, +/// to be used for example for FFI. The trait can be applied to any kind of +/// reference or smart pointers, and enforces correctness through the [`IsA`] +/// trait. +pub trait ObjectDeref: Deref +where + Self::Target: ObjectType, +{ + /// Convert to a const Rust pointer, to be used for example for FFI. + /// The target pointer type must be the type of `self` or a superclass + fn as_ptr(&self) -> *const U + where + Self::Target: IsA, + { + let ptr: *const Self::Target = self.deref(); + ptr.cast::() + } + + /// Convert to a mutable Rust pointer, to be used for example for FFI. + /// The target pointer type must be the type of `self` or a superclass. + /// Used to implement interior mutability for objects. + /// + /// # Safety + /// + /// This method is safe because only the actual dereference of the pointer + /// has to be unsafe. Bindings to C APIs will use it a lot, but care has + /// to be taken because it overrides the const-ness of `&self`. + fn as_mut_ptr(&self) -> *mut U + where + Self::Target: IsA, + { + #[allow(clippy::as_ptr_cast_mut)] + { + self.as_ptr::().cast_mut() + } + } +} + +/// Trait that adds extra functionality for `&T` where `T` is a QOM +/// object type. Allows conversion to/from C objects in generic code. +pub trait ObjectCast: ObjectDeref + Copy +where + Self::Target: ObjectType, +{ + /// Safely convert from a derived type to one of its parent types. + /// + /// This is always safe; the [`IsA`] trait provides static verification + /// trait that `Self` dereferences to `U` or a child of `U`. + fn upcast<'a, U: ObjectType>(self) -> &'a U + where + Self::Target: IsA, + Self: 'a, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { self.unsafe_cast::() } + } + + /// Attempt to convert to a derived type. + /// + /// Returns `None` if the object is not actually of type `U`. This is + /// verified at runtime by checking the object's type information. + fn downcast<'a, U: IsA>(self) -> Option<&'a U> + where + Self: 'a, + { + self.dynamic_cast::() + } + + /// Attempt to convert between any two types in the QOM hierarchy. + /// + /// Returns `None` if the object is not actually of type `U`. This is + /// verified at runtime by checking the object's type information. + fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U> + where + Self: 'a, + { + unsafe { + // SAFETY: upcasting to Object is always valid, and the + // return type is either NULL or the argument itself + let result: *const U = + object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); + + result.as_ref() + } + } + + /// Convert to any QOM type without verification. + /// + /// # Safety + /// + /// What safety? You need to know yourself that the cast is correct; only + /// use when performance is paramount. It is still better than a raw + /// pointer `cast()`, which does not even check that you remain in the + /// realm of QOM `ObjectType`s. + /// + /// `unsafe_cast::()` is always safe. + unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U + where + Self: 'a, + { + unsafe { &*(self.as_ptr::().cast::()) } + } +} + +impl ObjectDeref for &T {} +impl ObjectCast for &T {} + +impl ObjectDeref for &mut T {} + +/// Trait a type must implement to be registered with QEMU. +pub trait ObjectImpl: ObjectType + IsA { + /// The parent of the type. This should match the first field of the + /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. + type ParentType: ObjectType; + + /// Whether the object can be instantiated + const ABSTRACT: bool = false; + + /// Function that is called to initialize an object. The parent class will + /// have already been initialized so the type is only responsible for + /// initializing its own members. + /// + /// FIXME: The argument is not really a valid reference. `&mut + /// MaybeUninit` would be a better description. + const INSTANCE_INIT: Option)> = None; + + /// Function that is called to finish initialization of an object, once + /// `INSTANCE_INIT` functions have been called. + const INSTANCE_POST_INIT: Option = None; + + /// Called on descendant classes after all parent class initialization + /// has occurred, but before the class itself is initialized. This + /// is only useful if a class is not a leaf, and can be used to undo + /// the effects of copying the contents of the parent's class struct + /// to the descendants. + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *const c_void), + > = None; + + const TYPE_INFO: TypeInfo = TypeInfo { + name: Self::TYPE_NAME.as_ptr(), + parent: Self::ParentType::TYPE_NAME.as_ptr(), + instance_size: core::mem::size_of::(), + instance_align: core::mem::align_of::(), + instance_init: match Self::INSTANCE_INIT { + None => None, + Some(_) => Some(rust_instance_init::), + }, + instance_post_init: match Self::INSTANCE_POST_INIT { + None => None, + Some(_) => Some(rust_instance_post_init::), + }, + instance_finalize: Some(drop_object::), + abstract_: Self::ABSTRACT, + class_size: core::mem::size_of::(), + class_init: Some(rust_class_init::), + class_base_init: Self::CLASS_BASE_INIT, + class_data: core::ptr::null(), + interfaces: core::ptr::null(), + }; + + // methods on ObjectClass + const UNPARENT: Option = None; + + /// Store into the argument the virtual method implementations + /// for `Self`. On entry, the virtual method pointers are set to + /// the default values coming from the parent classes; the function + /// can change them to override virtual methods of a parent class. + /// + /// Usually defined simply as `Self::Class::class_init::`; + /// however a default implementation cannot be included here, because the + /// bounds that the `Self::Class::class_init` method places on `Self` are + /// not known in advance. + /// + /// # Safety + /// + /// While `klass`'s parent class is initialized on entry, the other fields + /// are all zero; it is therefore assumed that all fields in `T` can be + /// zeroed, otherwise it would not be possible to provide the class as a + /// `&mut T`. TODO: it may be possible to add an unsafe trait that checks + /// that all fields *after the parent class* (but not the parent class + /// itself) are Zeroable. This unsafe trait can be added via a derive + /// macro. + const CLASS_INIT: fn(&mut Self::Class); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_unparent_fn(dev: *mut bindings::Object) { + let state = NonNull::new(dev).unwrap().cast::(); + T::UNPARENT.unwrap()(unsafe { state.as_ref() }); +} + +impl ObjectClass { + /// Fill in the virtual methods of `ObjectClass` based on the definitions in + /// the `ObjectImpl` trait. + pub fn class_init(&mut self) { + if ::UNPARENT.is_some() { + self.unparent = Some(rust_unparent_fn::); + } + } +} + +unsafe impl ObjectType for Object { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; +} + +/// A reference-counted pointer to a QOM object. +/// +/// `Owned` wraps `T` with automatic reference counting. It increases the +/// reference count when created via [`Owned::from`] or cloned, and decreases +/// it when dropped. This ensures that the reference count remains elevated +/// as long as any `Owned` references to it exist. +/// +/// `Owned` can be used for two reasons: +/// * because the lifetime of the QOM object is unknown and someone else could +/// take a reference (similar to `Arc`, for example): in this case, the +/// object can escape and outlive the Rust struct that contains the `Owned` +/// field; +/// +/// * to ensure that the object stays alive until after `Drop::drop` is called +/// on the Rust struct: in this case, the object will always die together with +/// the Rust struct that contains the `Owned` field. +/// +/// Child properties are an example of the second case: in C, an object that +/// is created with `object_initialize_child` will die *before* +/// `instance_finalize` is called, whereas Rust expects the struct to have valid +/// contents when `Drop::drop` is called. Therefore Rust structs that have +/// child properties need to keep a reference to the child object. Right now +/// this can be done with `Owned`; in the future one might have a separate +/// `Child<'parent, T>` smart pointer that keeps a reference to a `T`, like +/// `Owned`, but does not allow cloning. +/// +/// Note that dropping an `Owned` requires the big QEMU lock to be taken. +#[repr(transparent)] +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Owned(NonNull); + +// The following rationale for safety is taken from Linux's kernel::sync::Arc. + +// SAFETY: It is safe to send `Owned` to another thread when the underlying +// `T` is `Sync` because it effectively means sharing `&T` (which is safe +// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any +// thread that has an `Owned` may ultimately access `T` using a +// mutable reference when the reference count reaches zero and `T` is dropped. +unsafe impl Send for Owned {} + +// SAFETY: It is safe to send `&Owned` to another thread when the underlying +// `T` is `Sync` because it effectively means sharing `&T` (which is safe +// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any +// thread that has a `&Owned` may clone it and get an `Owned` on that +// thread, so the thread may ultimately access `T` using a mutable reference +// when the reference count reaches zero and `T` is dropped. +unsafe impl Sync for Owned {} + +impl Owned { + /// Convert a raw C pointer into an owned reference to the QOM + /// object it points to. The object's reference count will be + /// decreased when the `Owned` is dropped. + /// + /// # Panics + /// + /// Panics if `ptr` is NULL. + /// + /// # Safety + /// + /// The caller must indeed own a reference to the QOM object. + /// The object must not be embedded in another unless the outer + /// object is guaranteed to have a longer lifetime. + /// + /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed + /// back to `from_raw()` (assuming the original `Owned` was valid!), + /// since the owned reference remains there between the calls to + /// `into_raw()` and `from_raw()`. + pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the pointer passed to from_raw + // remains const + Owned(NonNull::new(ptr.cast_mut()).unwrap()) + } + + /// Obtain a raw C pointer from a reference. `src` is consumed + /// and the reference is leaked. + #[allow(clippy::missing_const_for_fn)] + pub fn into_raw(src: Owned) -> *mut T { + let src = ManuallyDrop::new(src); + src.0.as_ptr() + } + + /// Increase the reference count of a QOM object and return + /// a new owned reference to it. + /// + /// # Safety + /// + /// The object must not be embedded in another, unless the outer + /// object is guaranteed to have a longer lifetime. + pub unsafe fn from(obj: &T) -> Self { + unsafe { + object_ref(obj.as_object_mut_ptr().cast::()); + + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the reference passed to from_raw + // remains shared + Owned(NonNull::new_unchecked(obj.as_mut_ptr())) + } + } +} + +impl Clone for Owned { + fn clone(&self) -> Self { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + unsafe { Owned::from(self.deref()) } + } +} + +impl Deref for Owned { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + // With that guarantee, reference counting ensures that + // the object remains alive. + unsafe { &*self.0.as_ptr() } + } +} +impl ObjectDeref for Owned {} + +impl Drop for Owned { + fn drop(&mut self) { + assert!(bql::is_locked()); + // SAFETY: creation method is unsafe, and whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + unsafe { + object_unref(self.as_object_mut_ptr().cast::()); + } + } +} + +impl> fmt::Debug for Owned { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.deref().debug_fmt(f) + } +} + +/// Trait for class methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectClassMethods: IsA { + /// Return a new reference counted instance of this class + fn new() -> Owned { + assert!(bql::is_locked()); + // SAFETY: the object created by object_new is allocated on + // the heap and has a reference count of 1 + unsafe { + let raw_obj = object_new(Self::TYPE_NAME.as_ptr()); + let obj = Object::from_raw(raw_obj).unsafe_cast::(); + Owned::from_raw(obj) + } + } +} + +/// Trait for methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectMethods: ObjectDeref +where + Self::Target: IsA, +{ + /// Return the name of the type of `self` + fn typename(&self) -> std::borrow::Cow<'_, str> { + let obj = self.upcast::(); + // SAFETY: safety of this is the requirement for implementing IsA + // The result of the C API has static lifetime + unsafe { + let p = object_get_typename(obj.as_mut_ptr()); + CStr::from_ptr(p).to_string_lossy() + } + } + + fn get_class(&self) -> &'static ::Class { + let obj = self.upcast::(); + + // SAFETY: all objects can call object_get_class; the actual class + // type is guaranteed by the implementation of `ObjectType` and + // `ObjectImpl`. + let klass: &'static ::Class = + unsafe { &*object_get_class(obj.as_mut_ptr()).cast() }; + + klass + } + + /// Convenience function for implementing the Debug trait + fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple(&self.typename()) + .field(&(self as *const Self)) + .finish() + } +} + +impl ObjectClassMethods for T where T: IsA {} +impl ObjectMethods for R where R::Target: IsA {} + +impl_vmstate_pointer!(Owned where T: VMState + ObjectType); diff --git a/rust/qom/wrapper.h b/rust/qom/wrapper.h new file mode 100644 index 0000000000..3b71bcd3f5 --- /dev/null +++ b/rust/qom/wrapper.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "qom/object.h" -- cgit 1.4.1 From ee4ffbf239cbd9de8c6b6cc33283b7a64a95a956 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Mon, 8 Sep 2025 12:49:55 +0200 Subject: rust: split "system" crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250827104147.717203-15-marcandre.lureau@redhat.com Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + rust/Cargo.lock | 13 +++ rust/Cargo.toml | 1 + rust/bql/src/cell.rs | 5 +- rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 2 +- rust/hw/timer/hpet/Cargo.toml | 1 + rust/hw/timer/hpet/meson.build | 1 + rust/hw/timer/hpet/src/device.rs | 12 +-- rust/meson.build | 1 + rust/qemu-api/Cargo.toml | 1 + rust/qemu-api/meson.build | 7 +- rust/qemu-api/src/bindings.rs | 14 +-- rust/qemu-api/src/lib.rs | 1 - rust/qemu-api/src/memory.rs | 200 --------------------------------------- rust/qemu-api/src/sysbus.rs | 2 +- rust/qemu-api/wrapper.h | 3 - rust/system/Cargo.toml | 22 +++++ rust/system/build.rs | 1 + rust/system/meson.build | 42 ++++++++ rust/system/src/bindings.rs | 41 ++++++++ rust/system/src/lib.rs | 6 ++ rust/system/src/memory.rs | 200 +++++++++++++++++++++++++++++++++++++++ rust/system/wrapper.h | 29 ++++++ 25 files changed, 376 insertions(+), 232 deletions(-) delete mode 100644 rust/qemu-api/src/memory.rs create mode 100644 rust/system/Cargo.toml create mode 120000 rust/system/build.rs create mode 100644 rust/system/meson.build create mode 100644 rust/system/src/bindings.rs create mode 100644 rust/system/src/lib.rs create mode 100644 rust/system/src/memory.rs create mode 100644 rust/system/wrapper.h (limited to 'rust/qemu-api/src/memory.rs') diff --git a/MAINTAINERS b/MAINTAINERS index cac6dcdc65..432ed51354 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3523,6 +3523,7 @@ F: rust/qemu-api F: rust/qemu-api-macros F: rust/qom/ F: rust/rustfmt.toml +F: rust/system/ F: rust/util/ F: scripts/get-wraps-from-cargo-registry.py diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ae852c5550..e6b75f30be 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -95,6 +95,7 @@ dependencies = [ "qemu_api", "qemu_api_macros", "qom", + "system", "util", ] @@ -136,6 +137,7 @@ dependencies = [ "qemu_api", "qemu_api_macros", "qom", + "system", "util", ] @@ -181,6 +183,7 @@ dependencies = [ "migration", "qemu_api_macros", "qom", + "system", "util", ] @@ -224,6 +227,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system" +version = "0.1.0" +dependencies = [ + "common", + "qemu_api_macros", + "qom", + "util", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0516c16591..8e210d277a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -8,6 +8,7 @@ members = [ "qemu-api-macros", "qemu-api", "qom", + "system", "hw/char/pl011", "hw/timer/hpet", "util", diff --git a/rust/bql/src/cell.rs b/rust/bql/src/cell.rs index 25007427ed..24ab294b60 100644 --- a/rust/bql/src/cell.rs +++ b/rust/bql/src/cell.rs @@ -77,9 +77,8 @@ //! //! ```ignore //! # use bql::BqlRefCell; -//! # use qemu_api::prelude::*; -//! # use qemu_api::{irq::InterruptSource, irq::IRQState}; -//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField}; +//! # use qom::{Owned, ParentField}; +//! # use system::{InterruptSource, IRQState, SysBusDevice}; //! # const N_GPIOS: usize = 8; //! # struct PL061Registers { /* ... */ } //! # unsafe impl ObjectType for PL061State { diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index f7ad5f8e08..e4b1c3f1eb 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -22,6 +22,7 @@ bql = { path = "../../../bql" } migration = { path = "../../../migration" } qom = { path = "../../../qom" } chardev = { path = "../../../chardev" } +system = { path = "../../../system" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index aaf911c5f4..fae6e1b9c9 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -15,6 +15,7 @@ _libpl011_rs = static_library( qemu_api_macros, qom_rs, chardev_rs, + system_rs, ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bc64061fb3..c65db5a517 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -13,12 +13,12 @@ use migration::{ }; use qemu_api::{ irq::{IRQState, InterruptSource}, - memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit}; +use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}; use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 19456ec72b..a95b1271c6 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -16,6 +16,7 @@ util = { path = "../../../util" } migration = { path = "../../../migration" } bql = { path = "../../../bql" } qom = { path = "../../../qom" } +system = { path = "../../../system" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 50ccdee4a9..c4ffe020f6 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -11,6 +11,7 @@ _libhpet_rs = static_library( bql_rs, qemu_api_macros, qom_rs, + system_rs, ], ) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 404569aa2d..841c2ba337 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -17,19 +17,17 @@ use migration::{ VMStateDescription, VMStateDescriptionBuilder, }; use qemu_api::{ - bindings::{ - address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, - qdev_prop_uint32, qdev_prop_usize, - }, + bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize}, irq::InterruptSource, - memory::{ - hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, - }, prelude::*, qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, sysbus::{SysBusDevice, SysBusDeviceImpl}, }; use qom::{prelude::*, ObjectImpl, ParentField, ParentInit}; +use system::{ + bindings::{address_space_memory, address_space_stl_le, hwaddr}, + MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, +}; use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; use crate::fw_cfg::HPETFwConfig; diff --git a/rust/meson.build b/rust/meson.build index 4d9e291223..d8b71f5506 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -29,6 +29,7 @@ subdir('util') subdir('migration') subdir('bql') subdir('qom') +subdir('system') subdir('chardev') subdir('qemu-api') diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 3bf2dafa6d..2884c1d460 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -20,6 +20,7 @@ migration = { path = "../migration" } util = { path = "../util" } bql = { path = "../bql" } qom = { path = "../qom" } +system = { path = "../system" } qemu_api_macros = { path = "../qemu-api-macros" } [lints] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a47f178b69..92e2581a64 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -8,7 +8,6 @@ c_enums = [ 'MachineInitPhase', 'MemoryDeviceInfoKind', 'ResetType', - 'device_endian', ] _qemu_api_bindgen_args = [] foreach enum : c_enums @@ -24,8 +23,11 @@ endforeach blocked_type = [ 'Chardev', 'Error', + 'MemTxAttrs', + 'MemoryRegion', 'ObjectClass', 'VMStateDescription', + 'device_endian', ] foreach type: blocked_type _qemu_api_bindgen_args += ['--blocklist-type', type] @@ -54,7 +56,6 @@ _qemu_api_rs = static_library( 'src/lib.rs', 'src/bindings.rs', 'src/irq.rs', - 'src/memory.rs', 'src/prelude.rs', 'src/qdev.rs', 'src/sysbus.rs', @@ -65,7 +66,7 @@ _qemu_api_rs = static_library( rust_abi: 'rust', rust_args: _qemu_api_cfg, dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros, - qom_rs, util_rs, hwcore], + qom_rs, system_rs, util_rs, hwcore], ) qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs], diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 526bcf8e31..63b805c76e 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -24,6 +24,7 @@ use chardev::bindings::Chardev; use common::Zeroable; use migration::bindings::VMStateDescription; use qom::bindings::ObjectClass; +use system::bindings::{device_endian, MemTxAttrs, MemoryRegion}; use util::bindings::Error; #[cfg(MESON)] @@ -32,15 +33,6 @@ include!("bindings.inc.rs"); #[cfg(not(MESON))] include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); -// SAFETY: this is a pure data struct -unsafe impl Send for CoalescedMemoryRange {} -unsafe impl Sync for CoalescedMemoryRange {} - -// SAFETY: these are constants and vtables; the Send and Sync requirements -// are deferred to the unsafe callbacks that they contain -unsafe impl Send for MemoryRegionOps {} -unsafe impl Sync for MemoryRegionOps {} - unsafe impl Send for Property {} unsafe impl Sync for Property {} @@ -49,7 +41,3 @@ unsafe impl Sync for TypeInfo {} unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} unsafe impl Zeroable for crate::bindings::Property {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} -unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} -unsafe impl Zeroable for crate::bindings::MemTxAttrs {} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index d96096899d..8d57440478 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -14,7 +14,6 @@ pub mod bindings; pub mod prelude; pub mod irq; -pub mod memory; pub mod qdev; pub mod sysbus; diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs deleted file mode 100644 index ecbbd9b604..0000000000 --- a/rust/qemu-api/src/memory.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2024 Red Hat, Inc. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs` - -use std::{ - ffi::{c_uint, c_void, CStr, CString}, - marker::PhantomData, -}; - -pub use bindings::{hwaddr, MemTxAttrs}; -use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; -use qom::prelude::*; - -use crate::bindings::{self, device_endian, memory_region_init_io}; - -pub struct MemoryRegionOps( - bindings::MemoryRegionOps, - // Note: quite often you'll see PhantomData mentioned when discussing - // covariance and contravariance; you don't need any of those to understand - // this usage of PhantomData. Quite simply, MemoryRegionOps *logically* - // holds callbacks that take an argument of type &T, except the type is erased - // before the callback is stored in the bindings::MemoryRegionOps field. - // The argument of PhantomData is a function pointer in order to represent - // that relationship; while that will also provide desirable and safe variance - // for T, variance is not the point but just a consequence. - PhantomData, -); - -// SAFETY: When a *const T is passed to the callbacks, the call itself -// is done in a thread-safe manner. The invocation is okay as long as -// T itself is `Sync`. -unsafe impl Sync for MemoryRegionOps {} - -#[derive(Clone)] -pub struct MemoryRegionOpsBuilder(bindings::MemoryRegionOps, PhantomData); - -unsafe extern "C" fn memory_region_ops_read_cb FnCall<(&'a T, hwaddr, u32), u64>>( - opaque: *mut c_void, - addr: hwaddr, - size: c_uint, -) -> u64 { - F::call((unsafe { &*(opaque.cast::()) }, addr, size)) -} - -unsafe extern "C" fn memory_region_ops_write_cb FnCall<(&'a T, hwaddr, u64, u32)>>( - opaque: *mut c_void, - addr: hwaddr, - data: u64, - size: c_uint, -) { - F::call((unsafe { &*(opaque.cast::()) }, addr, data, size)) -} - -impl MemoryRegionOpsBuilder { - #[must_use] - pub const fn read FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self { - self.0.read = Some(memory_region_ops_read_cb::); - self - } - - #[must_use] - pub const fn write FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self { - self.0.write = Some(memory_region_ops_write_cb::); - self - } - - #[must_use] - pub const fn big_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_BIG_ENDIAN; - self - } - - #[must_use] - pub const fn little_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN; - self - } - - #[must_use] - pub const fn native_endian(mut self) -> Self { - self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; - self - } - - #[must_use] - pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { - self.0.valid.min_access_size = min; - self.0.valid.max_access_size = max; - self - } - - #[must_use] - pub const fn valid_unaligned(mut self) -> Self { - self.0.valid.unaligned = true; - self - } - - #[must_use] - pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { - self.0.impl_.min_access_size = min; - self.0.impl_.max_access_size = max; - self - } - - #[must_use] - pub const fn impl_unaligned(mut self) -> Self { - self.0.impl_.unaligned = true; - self - } - - #[must_use] - pub const fn build(self) -> MemoryRegionOps { - MemoryRegionOps::(self.0, PhantomData) - } - - #[must_use] - pub const fn new() -> Self { - Self(bindings::MemoryRegionOps::ZERO, PhantomData) - } -} - -impl Default for MemoryRegionOpsBuilder { - fn default() -> Self { - Self::new() - } -} - -/// A safe wrapper around [`bindings::MemoryRegion`]. -#[repr(transparent)] -#[derive(qemu_api_macros::Wrapper)] -pub struct MemoryRegion(Opaque); - -unsafe impl Send for MemoryRegion {} -unsafe impl Sync for MemoryRegion {} - -impl MemoryRegion { - // inline to ensure that it is not included in tests, which only - // link to hwcore and qom. FIXME: inlining is actually the opposite - // of what we want, since this is the type-erased version of the - // init_io function below. Look into splitting the qemu_api crate. - #[inline(always)] - unsafe fn do_init_io( - slot: *mut bindings::MemoryRegion, - owner: *mut bindings::Object, - ops: &'static bindings::MemoryRegionOps, - name: &'static str, - size: u64, - ) { - unsafe { - let cstr = CString::new(name).unwrap(); - memory_region_init_io( - slot, - owner, - ops, - owner.cast::(), - cstr.as_ptr(), - size, - ); - } - } - - pub fn init_io>( - this: &mut MaybeUninitField<'_, T, Self>, - ops: &'static MemoryRegionOps, - name: &'static str, - size: u64, - ) { - unsafe { - Self::do_init_io( - this.as_mut_ptr().cast(), - MaybeUninitField::parent_mut(this).cast(), - &ops.0, - name, - size, - ); - } - } -} - -unsafe impl ObjectType for MemoryRegion { - type Class = bindings::MemoryRegionClass; - const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; -} - -qom_isa!(MemoryRegion: Object); - -/// A special `MemTxAttrs` constant, used to indicate that no memory -/// attributes are specified. -/// -/// Bus masters which don't specify any attributes will get this, -/// which has all attribute bits clear except the topmost one -/// (so that we can distinguish "all attributes deliberately clear" -/// from "didn't specify" if necessary). -pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs { - unspecified: true, - ..Zeroable::ZERO -}; diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index b883d7eaf1..dda71ebda7 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -9,11 +9,11 @@ use std::{ffi::CStr, ptr::addr_of_mut}; pub use bindings::SysBusDeviceClass; use common::Opaque; use qom::{prelude::*, Owned}; +use system::MemoryRegion; use crate::{ bindings, irq::{IRQState, InterruptSource}, - memory::MemoryRegion, qdev::{DeviceImpl, DeviceState}, }; diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h index 07dbc9987a..564733b903 100644 --- a/rust/qemu-api/wrapper.h +++ b/rust/qemu-api/wrapper.h @@ -49,14 +49,11 @@ typedef enum memory_order { #include "qemu/osdep.h" #include "qemu-io.h" -#include "system/system.h" #include "hw/sysbus.h" -#include "system/memory.h" #include "hw/clock.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/irq.h" #include "exec/memattrs.h" -#include "system/address-spaces.h" #include "hw/char/pl011.h" diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml new file mode 100644 index 0000000000..6803895e08 --- /dev/null +++ b/rust/system/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "system" +version = "0.1.0" +description = "Rust bindings for QEMU/system" +resolver = "2" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +common = { path = "../common" } +qom = { path = "../qom" } +util = { path = "../util" } +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/system/build.rs b/rust/system/build.rs new file mode 120000 index 0000000000..71a3167885 --- /dev/null +++ b/rust/system/build.rs @@ -0,0 +1 @@ +../util/build.rs \ No newline at end of file diff --git a/rust/system/meson.build b/rust/system/meson.build new file mode 100644 index 0000000000..ae9b932d29 --- /dev/null +++ b/rust/system/meson.build @@ -0,0 +1,42 @@ +c_enums = [ + 'device_endian', +] +_system_bindgen_args = [] +foreach enum : c_enums + _system_bindgen_args += ['--rustified-enum', enum] +endforeach + +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_system_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _system_bindgen_args, +) + +_system_rs = static_library( + 'system', + structured_sources( + [ + 'src/lib.rs', + 'src/bindings.rs', + 'src/memory.rs', + ], + {'.': _system_bindings_inc_rs} + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], + dependencies: [common_rs, qemu_api_macros], +) + +system_rs = declare_dependency(link_with: [_system_rs], + dependencies: [qemu_api_macros, hwcore]) diff --git a/rust/system/src/bindings.rs b/rust/system/src/bindings.rs new file mode 100644 index 0000000000..43edd98807 --- /dev/null +++ b/rust/system/src/bindings.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] + +use common::Zeroable; + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +// SAFETY: these are constants and vtables; the Send and Sync requirements +// are deferred to the unsafe callbacks that they contain +unsafe impl Send for MemoryRegionOps {} +unsafe impl Sync for MemoryRegionOps {} + +// SAFETY: this is a pure data struct +unsafe impl Send for CoalescedMemoryRange {} +unsafe impl Sync for CoalescedMemoryRange {} + +unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_1 {} +unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_2 {} +unsafe impl Zeroable for MemoryRegionOps {} +unsafe impl Zeroable for MemTxAttrs {} diff --git a/rust/system/src/lib.rs b/rust/system/src/lib.rs new file mode 100644 index 0000000000..aafe9a866c --- /dev/null +++ b/rust/system/src/lib.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pub mod bindings; + +mod memory; +pub use memory::*; diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs new file mode 100644 index 0000000000..29568ed767 --- /dev/null +++ b/rust/system/src/memory.rs @@ -0,0 +1,200 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs` + +use std::{ + ffi::{c_uint, c_void, CStr, CString}, + marker::PhantomData, +}; + +use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque}; +use qom::prelude::*; + +use crate::bindings::{self, device_endian, memory_region_init_io}; +pub use crate::bindings::{hwaddr, MemTxAttrs}; + +pub struct MemoryRegionOps( + bindings::MemoryRegionOps, + // Note: quite often you'll see PhantomData mentioned when discussing + // covariance and contravariance; you don't need any of those to understand + // this usage of PhantomData. Quite simply, MemoryRegionOps *logically* + // holds callbacks that take an argument of type &T, except the type is erased + // before the callback is stored in the bindings::MemoryRegionOps field. + // The argument of PhantomData is a function pointer in order to represent + // that relationship; while that will also provide desirable and safe variance + // for T, variance is not the point but just a consequence. + PhantomData, +); + +// SAFETY: When a *const T is passed to the callbacks, the call itself +// is done in a thread-safe manner. The invocation is okay as long as +// T itself is `Sync`. +unsafe impl Sync for MemoryRegionOps {} + +#[derive(Clone)] +pub struct MemoryRegionOpsBuilder(bindings::MemoryRegionOps, PhantomData); + +unsafe extern "C" fn memory_region_ops_read_cb FnCall<(&'a T, hwaddr, u32), u64>>( + opaque: *mut c_void, + addr: hwaddr, + size: c_uint, +) -> u64 { + F::call((unsafe { &*(opaque.cast::()) }, addr, size)) +} + +unsafe extern "C" fn memory_region_ops_write_cb FnCall<(&'a T, hwaddr, u64, u32)>>( + opaque: *mut c_void, + addr: hwaddr, + data: u64, + size: c_uint, +) { + F::call((unsafe { &*(opaque.cast::()) }, addr, data, size)) +} + +impl MemoryRegionOpsBuilder { + #[must_use] + pub const fn read FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self { + self.0.read = Some(memory_region_ops_read_cb::); + self + } + + #[must_use] + pub const fn write FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self { + self.0.write = Some(memory_region_ops_write_cb::); + self + } + + #[must_use] + pub const fn big_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_BIG_ENDIAN; + self + } + + #[must_use] + pub const fn little_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN; + self + } + + #[must_use] + pub const fn native_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; + self + } + + #[must_use] + pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { + self.0.valid.min_access_size = min; + self.0.valid.max_access_size = max; + self + } + + #[must_use] + pub const fn valid_unaligned(mut self) -> Self { + self.0.valid.unaligned = true; + self + } + + #[must_use] + pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { + self.0.impl_.min_access_size = min; + self.0.impl_.max_access_size = max; + self + } + + #[must_use] + pub const fn impl_unaligned(mut self) -> Self { + self.0.impl_.unaligned = true; + self + } + + #[must_use] + pub const fn build(self) -> MemoryRegionOps { + MemoryRegionOps::(self.0, PhantomData) + } + + #[must_use] + pub const fn new() -> Self { + Self(bindings::MemoryRegionOps::ZERO, PhantomData) + } +} + +impl Default for MemoryRegionOpsBuilder { + fn default() -> Self { + Self::new() + } +} + +/// A safe wrapper around [`bindings::MemoryRegion`]. +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct MemoryRegion(Opaque); + +unsafe impl Send for MemoryRegion {} +unsafe impl Sync for MemoryRegion {} + +impl MemoryRegion { + // inline to ensure that it is not included in tests, which only + // link to hwcore and qom. FIXME: inlining is actually the opposite + // of what we want, since this is the type-erased version of the + // init_io function below. Look into splitting the qemu_api crate. + #[inline(always)] + unsafe fn do_init_io( + slot: *mut bindings::MemoryRegion, + owner: *mut bindings::Object, + ops: &'static bindings::MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + let cstr = CString::new(name).unwrap(); + memory_region_init_io( + slot, + owner, + ops, + owner.cast::(), + cstr.as_ptr(), + size, + ); + } + } + + pub fn init_io>( + this: &mut MaybeUninitField<'_, T, Self>, + ops: &'static MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + Self::do_init_io( + this.as_mut_ptr().cast(), + MaybeUninitField::parent_mut(this).cast(), + &ops.0, + name, + size, + ); + } + } +} + +unsafe impl ObjectType for MemoryRegion { + type Class = bindings::MemoryRegionClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; +} + +qom_isa!(MemoryRegion: Object); + +/// A special `MemTxAttrs` constant, used to indicate that no memory +/// attributes are specified. +/// +/// Bus masters which don't specify any attributes will get this, +/// which has all attribute bits clear except the topmost one +/// (so that we can distinguish "all attributes deliberately clear" +/// from "didn't specify" if necessary). +pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs { + unspecified: true, + ..Zeroable::ZERO +}; diff --git a/rust/system/wrapper.h b/rust/system/wrapper.h new file mode 100644 index 0000000000..48abde8505 --- /dev/null +++ b/rust/system/wrapper.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" + +#include "system/system.h" +#include "system/memory.h" +#include "system/address-spaces.h" -- cgit 1.4.1