From a05b986d74d04ac47f5e4ef9cfa8a3ff160d5a82 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Jan 2026 18:18:57 +0100 Subject: [PATCH 01/13] rust: trace: libc does not have syslog on windows Reported-by: Thomas Huth Signed-off-by: Paolo Bonzini --- rust/trace/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/trace/src/lib.rs b/rust/trace/src/lib.rs index c2abe430a5..859d8d1b94 100644 --- a/rust/trace/src/lib.rs +++ b/rust/trace/src/lib.rs @@ -3,6 +3,7 @@ //! This crate provides macros that aid in using QEMU's tracepoint //! functionality. +#[cfg(not(windows))] #[doc(hidden)] /// Re-exported item to avoid adding libc as a dependency everywhere. pub use libc::{syslog, LOG_INFO}; From e07927260472cf4a24435f9e88f329d514501502 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 14 Jan 2026 09:51:27 +0100 Subject: [PATCH 02/13] rust: hwcore: add chardev symbols to integration tests Even though they are not used, rustc does not elide its symbols on msys2. This causes a linker error. Signed-off-by: Paolo Bonzini --- rust/hw/core/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index fa1765a230..942ee9cdac 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -73,7 +73,7 @@ test('rust-hwcore-rs-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]), + dependencies: [chardev_rs, common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', From f9bbbde1ed1d38a78b1c62b3295e436412592dd5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 14 Jan 2026 09:46:01 +0100 Subject: [PATCH 03/13] rust: move class_init to an extension trait MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for having ObjectClass, DeviceClass and SysbusDeviceClass defined outside the hwcore and qom crates. It then becomes impossible to add a method to them. Extracted from a patch by Marc-André Lureau. Signed-off-by: Paolo Bonzini --- rust/hw/core/src/prelude.rs | 3 +++ rust/hw/core/src/qdev.rs | 25 +++++++++++++++---------- rust/hw/core/src/sysbus.rs | 10 +++++++--- rust/qom/src/prelude.rs | 1 + rust/qom/src/qom.rs | 8 ++++++-- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/rust/hw/core/src/prelude.rs b/rust/hw/core/src/prelude.rs index c544c317b3..13f7dfc680 100644 --- a/rust/hw/core/src/prelude.rs +++ b/rust/hw/core/src/prelude.rs @@ -1,6 +1,8 @@ //! Essential types and traits intended for blanket imports. pub use crate::qdev::Clock; + +pub use crate::qdev::DeviceClassExt; pub use crate::qdev::DeviceState; pub use crate::qdev::DeviceImpl; pub use crate::qdev::DeviceMethods; @@ -8,6 +10,7 @@ pub use crate::qdev::ResetType; pub use crate::sysbus::SysBusDevice; +pub use crate::sysbus::SysBusDeviceClassExt; pub use crate::sysbus::SysBusDeviceImpl; pub use crate::sysbus::SysBusDeviceMethods; diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index 87232becba..f6037fbdca 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -15,9 +15,9 @@ use qom::{prelude::*, ObjectClass}; use util::{Error, Result}; -pub use crate::bindings::{ClockEvent, DeviceClass, Property, ResetType}; +pub use crate::bindings::{ClockEvent, ResetType}; use crate::{ - bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, + bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, DeviceClass, Property}, irq::InterruptSource, }; @@ -206,6 +206,9 @@ pub trait DeviceImpl: } } +#[repr(transparent)] +pub struct ResettableClass(bindings::ResettableClass); + unsafe impl InterfaceType for ResettableClass { const TYPE_NAME: &'static CStr = unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; @@ -214,23 +217,25 @@ unsafe impl InterfaceType for ResettableClass { impl ResettableClass { /// Fill in the virtual methods of `ResettableClass` based on the /// definitions in the `ResettablePhasesImpl` trait. - pub fn class_init(&mut self) { + fn class_init(&mut self) { if ::ENTER.is_some() { - self.phases.enter = Some(rust_resettable_enter_fn::); + self.0.phases.enter = Some(rust_resettable_enter_fn::); } if ::HOLD.is_some() { - self.phases.hold = Some(rust_resettable_hold_fn::); + self.0.phases.hold = Some(rust_resettable_hold_fn::); } if ::EXIT.is_some() { - self.phases.exit = Some(rust_resettable_exit_fn::); + self.0.phases.exit = Some(rust_resettable_exit_fn::); } } } -impl DeviceClass { - /// Fill in the virtual methods of `DeviceClass` based on the definitions in - /// the `DeviceImpl` trait. - pub fn class_init(&mut self) { +pub trait DeviceClassExt { + fn class_init(&mut self); +} + +impl DeviceClassExt for DeviceClass { + fn class_init(&mut self) { if ::REALIZE.is_some() { self.realize = Some(rust_realize_fn::); } diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs index 071fccff1e..81fab3f191 100644 --- a/rust/hw/core/src/sysbus.rs +++ b/rust/hw/core/src/sysbus.rs @@ -15,7 +15,7 @@ use crate::{ bindings, irq::{IRQState, InterruptSource}, - qdev::{DeviceImpl, DeviceState}, + qdev::{DeviceClassExt, DeviceImpl, DeviceState}, }; /// A safe wrapper around [`bindings::SysBusDevice`]. @@ -37,10 +37,14 @@ unsafe impl ObjectType for SysBusDevice { // TODO: add virtual methods pub trait SysBusDeviceImpl: DeviceImpl + IsA {} -impl SysBusDeviceClass { +pub trait SysBusDeviceClassExt { + fn class_init(&mut self); +} + +impl SysBusDeviceClassExt for SysBusDeviceClass { /// Fill in the virtual methods of `SysBusDeviceClass` based on the /// definitions in the `SysBusDeviceImpl` trait. - pub fn class_init(self: &mut SysBusDeviceClass) { + fn class_init(&mut self) { self.parent_class.class_init::(); } } diff --git a/rust/qom/src/prelude.rs b/rust/qom/src/prelude.rs index 6a1ecaef2a..1d1177f1e0 100644 --- a/rust/qom/src/prelude.rs +++ b/rust/qom/src/prelude.rs @@ -4,6 +4,7 @@ pub use crate::qom::IsA; pub use crate::qom::Object; pub use crate::qom::ObjectCast; +pub use crate::qom::ObjectClassExt; pub use crate::qom::ObjectClassMethods; pub use crate::qom::ObjectDeref; pub use crate::qom::ObjectImpl; diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs index 84455cea79..cc00ddcfc9 100644 --- a/rust/qom/src/qom.rs +++ b/rust/qom/src/qom.rs @@ -729,10 +729,14 @@ pub trait ObjectImpl: ObjectType + IsA { T::UNPARENT.unwrap()(unsafe { state.as_ref() }); } -impl ObjectClass { +pub trait ObjectClassExt { + fn class_init(&mut self); +} + +impl ObjectClassExt for ObjectClass { /// Fill in the virtual methods of `ObjectClass` based on the definitions in /// the `ObjectImpl` trait. - pub fn class_init(&mut self) { + fn class_init(&mut self) { if ::UNPARENT.is_some() { self.unparent = Some(rust_unparent_fn::); } From c899071b5a86fca3c59e5abe04deaa3c9d77edb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 3 Dec 2025 16:46:16 +0100 Subject: [PATCH 04/13] rust: move binding generation to bindings/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move raw FFI bindings generation to separate crates. This makes it possible to reuse bindgen declarations for a header file in its dependencies (this was not the case before this change), while keeping multiple -sys crates to avoid rebuilding all the code whenever something changes. Because the -sys crates are generated in dependency order, this also enforces that the crates are organized in something that resembles the dependencies between C headers. The meson.build for rust-safe crates becomes simpler, and it should be possible in the future to let Meson's cargo support handle most of it. Signed-off-by: Marc-André Lureau [General cleanup and Python script. - Paolo] Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 57 ++++++ meson.build | 3 +- rust/Cargo.lock | 68 ++++++++ rust/{util => bindings}/build.rs | 30 ++-- rust/bindings/chardev-sys/Cargo.toml | 28 +++ rust/bindings/chardev-sys/build.rs | 1 + .../chardev-sys/lib.rs} | 7 +- rust/bindings/chardev-sys/meson.build | 12 ++ rust/bindings/chardev-sys/wrapper.h | 12 ++ rust/bindings/generate_bindgen_args.py | 164 ++++++++++++++++++ rust/bindings/hwcore-sys/Cargo.toml | 32 ++++ rust/bindings/hwcore-sys/build.rs | 1 + .../hwcore-sys/lib.rs} | 17 +- rust/bindings/hwcore-sys/meson.build | 12 ++ rust/bindings/hwcore-sys/wrapper.h | 30 ++++ rust/bindings/meson.build | 37 ++++ rust/bindings/migration-sys/Cargo.toml | 28 +++ rust/bindings/migration-sys/build.rs | 1 + rust/bindings/migration-sys/lib.rs | 125 +++++++++++++ rust/bindings/migration-sys/meson.build | 12 ++ rust/bindings/migration-sys/wrapper.h | 10 ++ rust/bindings/qom-sys/Cargo.toml | 25 +++ rust/bindings/qom-sys/build.rs | 1 + .../bindings.rs => bindings/qom-sys/lib.rs} | 4 + rust/bindings/qom-sys/meson.build | 12 ++ rust/bindings/qom-sys/wrapper.h | 17 ++ rust/bindings/system-sys/Cargo.toml | 30 ++++ rust/bindings/system-sys/build.rs | 1 + .../system-sys/lib.rs} | 4 +- rust/bindings/system-sys/meson.build | 12 ++ rust/bindings/system-sys/wrapper.h | 21 +++ rust/bindings/util-sys/Cargo.toml | 25 +++ rust/bindings/util-sys/build.rs | 1 + .../bindings.rs => bindings/util-sys/lib.rs} | 2 +- rust/bindings/util-sys/meson.build | 12 ++ rust/bindings/util-sys/wrapper.h | 39 +++++ rust/bql/Cargo.toml | 1 + rust/bql/build.rs | 1 - rust/bql/meson.build | 30 +--- rust/bql/src/bindings.rs | 27 --- rust/bql/src/lib.rs | 3 +- rust/bql/wrapper.h | 27 --- rust/chardev/Cargo.toml | 1 + rust/chardev/build.rs | 1 - rust/chardev/meson.build | 37 +--- rust/chardev/src/lib.rs | 2 +- rust/chardev/wrapper.h | 28 --- rust/hw/char/pl011/build.rs | 2 +- rust/hw/char/pl011/meson.build | 21 +-- rust/hw/char/pl011/src/bindings.rs | 7 +- rust/hw/core/Cargo.toml | 1 + rust/hw/core/build.rs | 1 - rust/hw/core/meson.build | 60 +------ rust/hw/core/src/lib.rs | 3 +- rust/hw/core/src/qdev.rs | 6 +- rust/hw/core/src/sysbus.rs | 21 ++- rust/hw/core/wrapper.h | 32 ---- rust/meson.build | 1 + rust/migration/Cargo.toml | 1 + rust/migration/build.rs | 1 - rust/migration/meson.build | 40 +---- rust/migration/src/bindings.rs | 49 ------ rust/migration/src/lib.rs | 3 +- rust/migration/src/vmstate.rs | 73 -------- rust/migration/wrapper.h | 51 ------ rust/qom/Cargo.toml | 1 + rust/qom/build.rs | 1 - rust/qom/meson.build | 29 +--- rust/qom/src/lib.rs | 3 +- rust/qom/wrapper.h | 27 --- rust/system/Cargo.toml | 3 + rust/system/build.rs | 1 - rust/system/meson.build | 37 +--- rust/system/src/lib.rs | 2 +- rust/system/src/memory.rs | 2 +- rust/system/wrapper.h | 29 ---- rust/util/Cargo.toml | 1 + rust/util/meson.build | 42 +---- rust/util/src/lib.rs | 3 +- rust/util/wrapper.h | 32 ---- 80 files changed, 918 insertions(+), 719 deletions(-) rename rust/{util => bindings}/build.rs (55%) create mode 100644 rust/bindings/chardev-sys/Cargo.toml create mode 120000 rust/bindings/chardev-sys/build.rs rename rust/{chardev/src/bindings.rs => bindings/chardev-sys/lib.rs} (84%) create mode 100644 rust/bindings/chardev-sys/meson.build create mode 100644 rust/bindings/chardev-sys/wrapper.h create mode 100644 rust/bindings/generate_bindgen_args.py create mode 100644 rust/bindings/hwcore-sys/Cargo.toml create mode 120000 rust/bindings/hwcore-sys/build.rs rename rust/{hw/core/src/bindings.rs => bindings/hwcore-sys/lib.rs} (71%) create mode 100644 rust/bindings/hwcore-sys/meson.build create mode 100644 rust/bindings/hwcore-sys/wrapper.h create mode 100644 rust/bindings/meson.build create mode 100644 rust/bindings/migration-sys/Cargo.toml create mode 120000 rust/bindings/migration-sys/build.rs create mode 100644 rust/bindings/migration-sys/lib.rs create mode 100644 rust/bindings/migration-sys/meson.build create mode 100644 rust/bindings/migration-sys/wrapper.h create mode 100644 rust/bindings/qom-sys/Cargo.toml create mode 120000 rust/bindings/qom-sys/build.rs rename rust/{qom/src/bindings.rs => bindings/qom-sys/lib.rs} (85%) create mode 100644 rust/bindings/qom-sys/meson.build create mode 100644 rust/bindings/qom-sys/wrapper.h create mode 100644 rust/bindings/system-sys/Cargo.toml create mode 120000 rust/bindings/system-sys/build.rs rename rust/{system/src/bindings.rs => bindings/system-sys/lib.rs} (88%) create mode 100644 rust/bindings/system-sys/meson.build create mode 100644 rust/bindings/system-sys/wrapper.h create mode 100644 rust/bindings/util-sys/Cargo.toml create mode 120000 rust/bindings/util-sys/build.rs rename rust/{util/src/bindings.rs => bindings/util-sys/lib.rs} (88%) create mode 100644 rust/bindings/util-sys/meson.build create mode 100644 rust/bindings/util-sys/wrapper.h delete mode 120000 rust/bql/build.rs delete mode 100644 rust/bql/src/bindings.rs delete mode 100644 rust/bql/wrapper.h delete mode 120000 rust/chardev/build.rs delete mode 100644 rust/chardev/wrapper.h delete mode 120000 rust/hw/core/build.rs delete mode 100644 rust/hw/core/wrapper.h delete mode 120000 rust/migration/build.rs delete mode 100644 rust/migration/src/bindings.rs delete mode 100644 rust/migration/wrapper.h delete mode 120000 rust/qom/build.rs delete mode 100644 rust/qom/wrapper.h delete mode 120000 rust/system/build.rs delete mode 100644 rust/system/wrapper.h delete mode 100644 rust/util/wrapper.h diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 79c26d9d16..67ea84539a 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -339,6 +339,63 @@ Here are some things to keep in mind when working on the QEMU Rust crate. or a macro) where it can be documented and tested. If needed, include toy versions of the code in the documentation. +FFI Binding Generation +'''''''''''''''''''''' + +QEMU's Rust integration uses multiple ``*-sys`` crates that contain raw FFI +bindings to different QEMU subsystems. These crates mirror the dependency +structure that meson.build uses for C code, and which is reflected in +``static_library()`` declarations. For example: + +* util-sys: Basic utilities (no dependencies) +* qom-sys: QEMU Object Model (depends on util-sys) +* chardev-sys: Character devices (depends on qom-sys, util-sys) +* hwcore-sys: Hardware core (depends on qom-sys, util-sys) +* migration-sys: Migration (depends on util-sys) +* system-sys: System-level APIs (depends on all others) + +Having multiple crates avoids massive rebuilds of all Rust code when C headers +are changed. On the other hand, bindgen is not aware of how headers are split +across crates, and therefore it would generate declarations for dependencies +again. These duplicate declarations are not only large, they create distinct +types and therefore they are incompatible with each other. + +Bindgen Configuration +~~~~~~~~~~~~~~~~~~~~~ + +Bindgen options such as symbol blocklists or how to configure enums can be +defined in each crate's ``Cargo.toml`` via a ``[package.metadata.bindgen]`` section. +For example:: + + [package.metadata.bindgen] + header = "wrapper.h" # Main header file for this crate + rustified-enum = ["QEMUClockType"] # Enums to generate as Rust enums + bitfield-enum = ["VMStateFlags"] # Enums to treat as bitfields + blocklist-function = [ # Functions to exclude + "vmstate_register_ram", + "vmstate_unregister_ram" + ] + additional-files = [ # Extra files to allowlist + "include/system/memory_ldst.*" + ] + +All bindgen options are supported in the metadata section. The complete list +can be found in ``rust/bindings/generate_bindgen_args.py``. + +Dependency Management +~~~~~~~~~~~~~~~~~~~~~ + +By examining the dependency chain before bindgen creates the code for +the ``*-sys`` crates, the build system ensures that header files included in +one crate are blocked from appearing in dependent crates, thus avoiding +duplicate definitions. Dependent crates can import the definition via +"use" statements. + +This dependency-aware binding generation is handled automatically by +``rust/bindings/generate_bindgen_args.py``, which processes the Cargo.toml +files in dependency order and generates appropriate ``--allowlist-file`` and +``--blocklist-file`` arguments for bindgen. + Writing procedural macros ''''''''''''''''''''''''' diff --git a/meson.build b/meson.build index a84f14258b..668f1aa919 100644 --- a/meson.build +++ b/meson.build @@ -4169,9 +4169,8 @@ if have_rust '--with-derive-default', '--no-layout-tests', '--no-prepend-enum-name', - '--allowlist-file', meson.project_source_root() + '/include/.*', - '--allowlist-file', meson.project_build_root() + '/.*', '--blocklist-file', glib_pc.get_variable('includedir') + '/glib-2.0/.*', + '--blocklist-file', meson.project_source_root() + '/include/qemu/typedefs.h', '--blocklist-type', '.*_([a-z]*autoptr)$', ] if not rustfmt.found() diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 78452c3db9..801ac5f4f5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -59,6 +59,7 @@ name = "bql" version = "0.1.0" dependencies = [ "glib-sys", + "util-sys", ] [[package]] @@ -76,6 +77,7 @@ name = "chardev" version = "0.1.0" dependencies = [ "bql", + "chardev-sys", "common", "glib-sys", "migration", @@ -83,6 +85,16 @@ dependencies = [ "util", ] +[[package]] +name = "chardev-sys" +version = "0.1.0" +dependencies = [ + "common", + "glib-sys", + "qom-sys", + "util-sys", +] + [[package]] name = "common" version = "0.1.0" @@ -156,6 +168,7 @@ dependencies = [ "chardev", "common", "glib-sys", + "hwcore-sys", "migration", "qemu_macros", "qom", @@ -163,6 +176,19 @@ dependencies = [ "util", ] +[[package]] +name = "hwcore-sys" +version = "0.1.0" +dependencies = [ + "chardev-sys", + "common", + "glib-sys", + "migration-sys", + "qom-sys", + "system-sys", + "util-sys", +] + [[package]] name = "indexmap" version = "2.11.4" @@ -201,10 +227,20 @@ dependencies = [ "bql", "common", "glib-sys", + "migration-sys", "qemu_macros", "util", ] +[[package]] +name = "migration-sys" +version = "0.1.0" +dependencies = [ + "common", + "glib-sys", + "util-sys", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -287,9 +323,18 @@ dependencies = [ "glib-sys", "migration", "qemu_macros", + "qom-sys", "util", ] +[[package]] +name = "qom-sys" +version = "0.1.0" +dependencies = [ + "glib-sys", + "util-sys", +] + [[package]] name = "quote" version = "1.0.36" @@ -306,6 +351,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" dependencies = [ "serde_core", + "serde_derive", ] [[package]] @@ -358,9 +404,12 @@ dependencies = [ name = "system" version = "0.1.0" dependencies = [ + "bql", "common", "glib-sys", + "migration", "qom", + "system-sys", "util", ] @@ -377,6 +426,17 @@ dependencies = [ "version-compare", ] +[[package]] +name = "system-sys" +version = "0.1.0" +dependencies = [ + "common", + "glib-sys", + "migration-sys", + "qom-sys", + "util-sys", +] + [[package]] name = "target-lexicon" version = "0.13.2" @@ -454,6 +514,14 @@ dependencies = [ "foreign", "glib-sys", "libc", + "util-sys", +] + +[[package]] +name = "util-sys" +version = "0.1.0" +dependencies = [ + "glib-sys", ] [[package]] diff --git a/rust/util/build.rs b/rust/bindings/build.rs similarity index 55% rename from rust/util/build.rs rename to rust/bindings/build.rs index 5654d1d562..a466958d71 100644 --- a/rust/util/build.rs +++ b/rust/bindings/build.rs @@ -10,25 +10,25 @@ fn main() -> Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { - let sub = get_rust_subdir(manifest_dir).unwrap(); - format!("{root}/{sub}/bindings.inc.rs") - } else { - // Placing bindings.inc.rs in the source directory is supported - // but not documented or encouraged. - format!("{manifest_dir}/src/bindings.inc.rs") - }; + let root = env::var("MESON_BUILD_ROOT").expect(concat!( + "\n", + " MESON_BUILD_ROOT not found. Maybe you wanted one of\n", + " `make clippy`, `make rustfmt`, `make rustdoc`?\n", + "\n", + " For other uses of `cargo`, start a subshell with\n", + " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", + " the top of the build tree." + )); + let sub = get_rust_subdir(manifest_dir).unwrap(); + let file = format!("{root}/{sub}/bindings.inc.rs"); let file = Path::new(&file); - if !Path::new(&file).exists() { + + if !file.exists() { panic!(concat!( "\n", - " No generated C bindings found! Maybe you wanted one of\n", - " `make clippy`, `make rustfmt`, `make rustdoc`?\n", - "\n", - " For other uses of `cargo`, start a subshell with\n", - " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", - " the top of the build tree." + " No generated C bindings found! Run `make` first; or maybe you\n", + " wanted one of `make clippy`, `make rustfmt`, `make rustdoc`?\n", )); } diff --git a/rust/bindings/chardev-sys/Cargo.toml b/rust/bindings/chardev-sys/Cargo.toml new file mode 100644 index 0000000000..6aa0025ca6 --- /dev/null +++ b/rust/bindings/chardev-sys/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "chardev-sys" +version = "0.1.0" +description = "Rust sys bindings for QEMU/chardev" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +path = "lib.rs" + +[dependencies] +glib-sys = { workspace = true } +common = { path = "../../common" } +qom-sys = { path = "../qom-sys" } +util-sys = { path = "../util-sys" } + +[lints] +workspace = true + +[package.metadata.bindgen] +header = "wrapper.h" +rustified-enum = ["QEMUChrEvent"] diff --git a/rust/bindings/chardev-sys/build.rs b/rust/bindings/chardev-sys/build.rs new file mode 120000 index 0000000000..10238032f5 --- /dev/null +++ b/rust/bindings/chardev-sys/build.rs @@ -0,0 +1 @@ +../build.rs \ No newline at end of file diff --git a/rust/chardev/src/bindings.rs b/rust/bindings/chardev-sys/lib.rs similarity index 84% rename from rust/chardev/src/bindings.rs rename to rust/bindings/chardev-sys/lib.rs index 360b30d6a3..903f6fc492 100644 --- a/rust/chardev/src/bindings.rs +++ b/rust/bindings/chardev-sys/lib.rs @@ -19,10 +19,9 @@ )] use common::Zeroable; -use glib_sys::{ - gboolean, guint, GArray, GHashTable, GHashTableIter, GIOCondition, GMainContext, GPollFD, - GPtrArray, GSList, GSource, GSourceFunc, -}; +use glib_sys::{gboolean, guint, GIOCondition, GMainContext, GSource, GSourceFunc}; +use qom_sys::{Object, ObjectClass}; +use util_sys::{Error, IOCanReadHandler, IOReadHandler, QemuOpts}; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/bindings/chardev-sys/meson.build b/rust/bindings/chardev-sys/meson.build new file mode 100644 index 0000000000..458075b806 --- /dev/null +++ b/rust/bindings/chardev-sys/meson.build @@ -0,0 +1,12 @@ +_bindgen_chardev_rs = rust.bindgen( + args: bindgen_args_common + bindgen_args_data['chardev-sys'].split(), + kwargs: bindgen_kwargs) +_chardev_sys_rs = static_library( + 'chardev_sys', + structured_sources(['lib.rs', _bindgen_chardev_rs]), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [glib_sys_rs, common_rs, qom_sys_rs, util_sys_rs], +) + +chardev_sys_rs = declare_dependency(link_with: [_chardev_sys_rs]) diff --git a/rust/bindings/chardev-sys/wrapper.h b/rust/bindings/chardev-sys/wrapper.h new file mode 100644 index 0000000000..b8ddc361f7 --- /dev/null +++ b/rust/bindings/chardev-sys/wrapper.h @@ -0,0 +1,12 @@ +/* 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. + */ + +#include "qemu/osdep.h" + +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" diff --git a/rust/bindings/generate_bindgen_args.py b/rust/bindings/generate_bindgen_args.py new file mode 100644 index 0000000000..8e526d7e81 --- /dev/null +++ b/rust/bindings/generate_bindgen_args.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later + +""" +Generate bindgen arguments from Cargo.toml metadata for QEMU's Rust FFI bindings. + +Author: Paolo Bonzini + +Copyright (C) 2025 Red Hat, Inc. + +This script processes Cargo.toml file for QEMU's bindings crates (util-sys, +chardev-sys, qom-sys, etc.); it generates bindgen command lines that allow +easy customization and that export the right headers in each bindings crate. + +For detailed information, see docs/devel/rust.rst. +""" + +import os +import re +import sys +import argparse +from pathlib import Path +from dataclasses import dataclass +from typing import Iterable, List, Dict, Any + +try: + import tomllib +except ImportError: + import tomli as tomllib # type: ignore + +INCLUDE_RE = re.compile(r'^#include\s+"([^"]+)"') +OPTIONS = [ + "bitfield-enum", + "newtype-enum", + "newtype-global-enum", + "rustified-enum", + "rustified-non-exhaustive-enum", + "constified-enum", + "constified-enum-module", + "normal-alias", + "new-type-alias", + "new-type-alias-deref", + "bindgen-wrapper-union", + "manually-drop-union", + "blocklist-type", + "blocklist-function", + "blocklist-item", + "blocklist-file", + "blocklist-var", + "opaque-type", + "no-partialeq", + "no-copy", + "no-debug", + "no-default", + "no-hash", + "must-use-type", + "with-derive-custom", + "with-derive-custom-struct", + "with-derive-custom-enum", + "with-derive-custom-union", + "with-attribute-custom", + "with-attribute-custom-struct", + "with-attribute-custom-enum", + "with-attribute-custom-union", +] + + +@dataclass +class BindgenInfo: + cmd_args: List[str] + inputs: List[str] + + +def extract_includes(lines: Iterable[str]) -> List[str]: + """Extract #include directives from a file.""" + includes: List[str] = [] + for line in lines: + match = INCLUDE_RE.match(line.strip()) + if match: + includes.append(match.group(1)) + return includes + + +def build_bindgen_args(metadata: Dict[str, Any]) -> List[str]: + """Build command line arguments from [package.metadata.bindgen].""" + args: List[str] = [] + for key, values in metadata.items(): + if key in OPTIONS: + flag = f"--{key}" + assert isinstance(values, list) + for value in values: + args.append(flag) + args.append(value) + + return args + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Generate bindgen arguments from Cargo.toml metadata" + ) + parser.add_argument( + "directories", nargs="+", help="Directories containing Cargo.toml files" + ) + parser.add_argument( + "-I", + "--include-root", + default=None, + help="Base path for --allowlist-file/--blocklist-file", + ) + parser.add_argument("--source-dir", default=os.getcwd(), help="Source directory") + parser.add_argument("-o", "--output", required=True, help="Output file") + parser.add_argument("--dep-file", help="Dependency file to write") + args = parser.parse_args() + + prev_allowlist_files: Dict[str, object] = {} + bindgen_infos: Dict[str, BindgenInfo] = {} + + os.chdir(args.source_dir) + include_root = args.include_root or args.source_dir + for directory in args.directories: + cargo_path = Path(directory) / "Cargo.toml" + inputs = [str(Path(args.source_dir) / cargo_path)] + + with open(cargo_path, "rb") as f: + cargo_toml = tomllib.load(f) + + metadata = cargo_toml.get("package", {}).get("metadata", {}).get("bindgen", {}) + input_file = Path(directory) / metadata["header"] + inputs.append(str(Path(args.source_dir) / input_file)) + + cmd_args = build_bindgen_args(metadata) + + # Each include file is allowed for this file and blocked in the + # next ones + for blocklist_path in prev_allowlist_files: + cmd_args.extend(["--blocklist-file", blocklist_path]) + with open(input_file, "r", encoding="utf-8", errors="ignore") as f: + includes = extract_includes(f) + for allowlist_file in includes + metadata.get("additional-files", []): + allowlist_path = Path(include_root) / allowlist_file + cmd_args.extend(["--allowlist-file", str(allowlist_path)]) + prev_allowlist_files.setdefault(str(allowlist_path), True) + + bindgen_infos[directory] = BindgenInfo(cmd_args=cmd_args, inputs=inputs) + + # now write the output + with open(args.output, "w") as f: + for directory, info in bindgen_infos.items(): + args_sh = " ".join(info.cmd_args) + f.write(f"{directory}={args_sh}\n") + + if args.dep_file: + with open(args.dep_file, "w") as f: + deps: List[str] = [] + for info in bindgen_infos.values(): + deps += info.inputs + f.write(f"{os.path.basename(args.output)}: {' '.join(deps)}\n") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/rust/bindings/hwcore-sys/Cargo.toml b/rust/bindings/hwcore-sys/Cargo.toml new file mode 100644 index 0000000000..c20024e921 --- /dev/null +++ b/rust/bindings/hwcore-sys/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "hwcore-sys" +version = "0.1.0" +description = "Rust sys bindings for QEMU/hwcore" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +path = "lib.rs" + +[dependencies] +glib-sys = { workspace = true } +common = { path = "../../common" } +chardev-sys = { path = "../chardev-sys" } +qom-sys = { path = "../qom-sys" } +migration-sys = { path = "../migration-sys" } +util-sys = { path = "../util-sys" } +system-sys = { path = "../system-sys" } + +[lints] +workspace = true + +[package.metadata.bindgen] +header = "wrapper.h" +rustified-enum = ["DeviceCategory", "GpioPolarity", "MachineInitPhase", "ResetType"] +bitfield-enum = ["ClockEvent"] diff --git a/rust/bindings/hwcore-sys/build.rs b/rust/bindings/hwcore-sys/build.rs new file mode 120000 index 0000000000..10238032f5 --- /dev/null +++ b/rust/bindings/hwcore-sys/build.rs @@ -0,0 +1 @@ +../build.rs \ No newline at end of file diff --git a/rust/hw/core/src/bindings.rs b/rust/bindings/hwcore-sys/lib.rs similarity index 71% rename from rust/hw/core/src/bindings.rs rename to rust/bindings/hwcore-sys/lib.rs index db872d38bc..8cef4a7961 100644 --- a/rust/hw/core/src/bindings.rs +++ b/rust/bindings/hwcore-sys/lib.rs @@ -18,13 +18,15 @@ clippy::too_many_arguments )] -use chardev::bindings::Chardev; +use chardev_sys::Chardev; use common::Zeroable; -use glib_sys::{GHashTable, GHashTableIter, GList, GPtrArray, GSList}; -use migration::bindings::VMStateDescription; -use qom::bindings::ObjectClass; -use system::bindings::MemoryRegion; -use util::bindings::Error; +use glib_sys::GSList; +use migration_sys::VMStateDescription; +use qom_sys::{ + InterfaceClass, Object, ObjectClass, ObjectProperty, ObjectPropertyAccessor, + ObjectPropertyRelease, +}; +use util_sys::{Error, QDict, QList}; #[cfg(MESON)] include!("bindings.inc.rs"); @@ -35,8 +37,5 @@ unsafe impl Send for Property {} unsafe impl Sync for Property {} -unsafe impl Send for TypeInfo {} -unsafe impl Sync for TypeInfo {} - unsafe impl Zeroable for Property__bindgen_ty_1 {} unsafe impl Zeroable for Property {} diff --git a/rust/bindings/hwcore-sys/meson.build b/rust/bindings/hwcore-sys/meson.build new file mode 100644 index 0000000000..3d51947b4a --- /dev/null +++ b/rust/bindings/hwcore-sys/meson.build @@ -0,0 +1,12 @@ +_bindgen_hwcore_rs = rust.bindgen( + args: bindgen_args_common + bindgen_args_data['hwcore-sys'].split(), + kwargs: bindgen_kwargs) +_hwcore_sys_rs = static_library( + 'hwcore_sys', + structured_sources(['lib.rs', _bindgen_hwcore_rs]), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [common_rs, glib_sys_rs, qom_sys_rs, util_sys_rs, migration_sys_rs, chardev_sys_rs], +) + +hwcore_sys_rs = declare_dependency(link_with: [_hwcore_sys_rs]) diff --git a/rust/bindings/hwcore-sys/wrapper.h b/rust/bindings/hwcore-sys/wrapper.h new file mode 100644 index 0000000000..7c7c3c35f6 --- /dev/null +++ b/rust/bindings/hwcore-sys/wrapper.h @@ -0,0 +1,30 @@ +/* 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. + */ + +/* + * We block include/qemu/typedefs.h from bindgen, add here symbols + * that are needed as opaque types by other functions. + */ +typedef struct Clock Clock; +typedef struct DeviceState DeviceState; +typedef struct IRQState *qemu_irq; +typedef void (*qemu_irq_handler)(void *opaque, int n, int level); + +/* Once bindings exist, these could move to a different *-sys crate. */ +typedef struct BlockBackend BlockBackend; +typedef struct Monitor Monitor; +typedef struct NetClientState NetClientState; + +#include "qemu/osdep.h" + +#include "hw/core/clock.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-clock.h" +#include "hw/core/qdev.h" +#include "hw/core/qdev-properties-system.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/resettable.h" diff --git a/rust/bindings/meson.build b/rust/bindings/meson.build new file mode 100644 index 0000000000..c2798731a1 --- /dev/null +++ b/rust/bindings/meson.build @@ -0,0 +1,37 @@ +# Generate bindgen arguments from Cargo.toml metadata +# Sort these in dependency order, same as the subdir() +# invocations below. +bindgen_dirs = [ + 'util-sys', + 'migration-sys', + 'qom-sys', + 'chardev-sys', + 'hwcore-sys', + 'system-sys', +] +bindgen_args_file = configure_file( + command: [files('generate_bindgen_args.py'), + '-I', meson.project_source_root() / 'include', + '--source-dir', meson.current_source_dir(), + '-o', '@OUTPUT@', '--dep-file', '@DEPFILE@'] + bindgen_dirs, + output: 'bindgen_args.mak', + depfile: 'bindgen_args.d' +) + +# now generate all bindgen files +bindgen_args_data = keyval.load(bindgen_args_file) +bindgen_kwargs = { + 'input': 'wrapper.h', + 'dependencies': common_ss.all_dependencies(), + 'output': 'bindings.inc.rs', + 'include_directories': bindings_incdir, + 'bindgen_version': ['>=0.60.0'], + 'c_args': bindgen_c_args, +} + +subdir('util-sys') +subdir('migration-sys') +subdir('qom-sys') +subdir('chardev-sys') +subdir('hwcore-sys') +subdir('system-sys') diff --git a/rust/bindings/migration-sys/Cargo.toml b/rust/bindings/migration-sys/Cargo.toml new file mode 100644 index 0000000000..88d7742e6a --- /dev/null +++ b/rust/bindings/migration-sys/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "migration-sys" +version = "0.1.0" +description = "Rust sys bindings for QEMU/migration" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +path = "lib.rs" + +[dependencies] +glib-sys = { workspace = true } +common = { path = "../../common" } +util-sys = { path = "../util-sys" } + +[lints] +workspace = true + +[package.metadata.bindgen] +header = "wrapper.h" +bitfield-enum = ["MigrationPolicy", "MigrationPriority", "VMStateFlags"] +blocklist-function = ["vmstate_register_ram", "vmstate_register_ram_global", "vmstate_unregister_ram"] diff --git a/rust/bindings/migration-sys/build.rs b/rust/bindings/migration-sys/build.rs new file mode 120000 index 0000000000..10238032f5 --- /dev/null +++ b/rust/bindings/migration-sys/build.rs @@ -0,0 +1 @@ +../build.rs \ No newline at end of file diff --git a/rust/bindings/migration-sys/lib.rs b/rust/bindings/migration-sys/lib.rs new file mode 100644 index 0000000000..7ee30a3f7d --- /dev/null +++ b/rust/bindings/migration-sys/lib.rs @@ -0,0 +1,125 @@ +// 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; +use util_sys::{Error, JSONWriter, QEMUFile}; + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +unsafe impl Send for VMStateDescription {} +unsafe impl Sync for VMStateDescription {} + +unsafe impl Send for VMStateField {} +unsafe impl Sync for VMStateField {} + +unsafe impl Send for VMStateInfo {} +unsafe impl Sync for VMStateInfo {} + +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for VMStateFlags { + fn default() -> Self { + Self(0) + } +} + +unsafe impl Zeroable for VMStateFlags {} +unsafe impl Zeroable for VMStateField {} +unsafe impl Zeroable for VMStateDescription {} + +// The following higher-level helpers could be in "migration" +// crate when Rust has const trait impl. + +pub trait VMStateFlagsExt { + const VMS_VARRAY_FLAGS: VMStateFlags; +} + +impl VMStateFlagsExt for VMStateFlags { + const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( + VMStateFlags::VMS_VARRAY_INT32.0 + | VMStateFlags::VMS_VARRAY_UINT8.0 + | VMStateFlags::VMS_VARRAY_UINT16.0 + | VMStateFlags::VMS_VARRAY_UINT32.0, + ); +} + +// Add a couple builder-style methods to VMStateField, allowing +// easy derivation of VMStateField constants from other types. +impl VMStateField { + #[must_use] + pub const fn with_version_id(mut self, version_id: i32) -> Self { + assert!(version_id >= 0); + self.version_id = version_id; + self + } + + #[must_use] + pub const fn with_array_flag(mut self, num: usize) -> Self { + assert!(num <= 0x7FFF_FFFFusize); + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0); + assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0); + if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); + // VMS_ARRAY_OF_POINTER flag stores the size of pointer. + // FIXME: *const, *mut, NonNull and Box<> have the same size as usize. + // Resize if more smart pointers are supported. + self.size = std::mem::size_of::(); + } + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); + self.num = num as i32; + self + } + + #[must_use] + pub const fn with_pointer_flag(mut self) -> Self { + assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0); + self + } + + #[must_use] + pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> Self { + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); + self.flags = VMStateFlags(self.flags.0 | flag.0); + self.num = 0; // varray uses num_offset instead of num. + self + } + + #[must_use] + #[allow(unused_mut)] + pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> Self { + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + self.with_varray_flag_unchecked(flag) + } + + #[must_use] + pub const fn with_varray_multiply(mut self, num: u32) -> Self { + assert!(num <= 0x7FFF_FFFFu32); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); + self.num = num as i32; + self + } +} diff --git a/rust/bindings/migration-sys/meson.build b/rust/bindings/migration-sys/meson.build new file mode 100644 index 0000000000..9243acba30 --- /dev/null +++ b/rust/bindings/migration-sys/meson.build @@ -0,0 +1,12 @@ +_bindgen_migration_rs = rust.bindgen( + args: bindgen_args_common + bindgen_args_data['migration-sys'].split(), + kwargs: bindgen_kwargs) +_migration_sys_rs = static_library( + 'migration_sys', + structured_sources(['lib.rs', _bindgen_migration_rs]), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [glib_sys_rs, common_rs, util_sys_rs], +) + +migration_sys_rs = declare_dependency(link_with: [_migration_sys_rs]) diff --git a/rust/bindings/migration-sys/wrapper.h b/rust/bindings/migration-sys/wrapper.h new file mode 100644 index 0000000000..076ad79a21 --- /dev/null +++ b/rust/bindings/migration-sys/wrapper.h @@ -0,0 +1,10 @@ +/* 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. + */ + +#include "qemu/osdep.h" + +#include "migration/vmstate.h" diff --git a/rust/bindings/qom-sys/Cargo.toml b/rust/bindings/qom-sys/Cargo.toml new file mode 100644 index 0000000000..621bd468e6 --- /dev/null +++ b/rust/bindings/qom-sys/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "qom-sys" +version = "0.1.0" +description = "Rust sys bindings for QEMU/qom" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +path = "lib.rs" + +[dependencies] +glib-sys = { workspace = true } +util-sys = { path = "../util-sys" } + +[lints] +workspace = true + +[package.metadata.bindgen] +header = "wrapper.h" diff --git a/rust/bindings/qom-sys/build.rs b/rust/bindings/qom-sys/build.rs new file mode 120000 index 0000000000..10238032f5 --- /dev/null +++ b/rust/bindings/qom-sys/build.rs @@ -0,0 +1 @@ +../build.rs \ No newline at end of file diff --git a/rust/qom/src/bindings.rs b/rust/bindings/qom-sys/lib.rs similarity index 85% rename from rust/qom/src/bindings.rs rename to rust/bindings/qom-sys/lib.rs index e61259ec2e..464b929034 100644 --- a/rust/qom/src/bindings.rs +++ b/rust/bindings/qom-sys/lib.rs @@ -19,9 +19,13 @@ )] use glib_sys::{GHashTable, GHashTableIter, GPtrArray, GSList}; +use util_sys::{Error, QDict, QObject, Visitor}; #[cfg(MESON)] include!("bindings.inc.rs"); #[cfg(not(MESON))] include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +unsafe impl Send for TypeInfo {} +unsafe impl Sync for TypeInfo {} diff --git a/rust/bindings/qom-sys/meson.build b/rust/bindings/qom-sys/meson.build new file mode 100644 index 0000000000..8f8ae7d1bc --- /dev/null +++ b/rust/bindings/qom-sys/meson.build @@ -0,0 +1,12 @@ +_bindgen_qom_rs = rust.bindgen( + args: bindgen_args_common + bindgen_args_data['qom-sys'].split(), + kwargs: bindgen_kwargs) +_qom_sys_rs = static_library( + 'qom_sys', + structured_sources(['lib.rs', _bindgen_qom_rs]), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [glib_sys_rs, util_sys_rs], +) + +qom_sys_rs = declare_dependency(link_with: [_qom_sys_rs]) diff --git a/rust/bindings/qom-sys/wrapper.h b/rust/bindings/qom-sys/wrapper.h new file mode 100644 index 0000000000..18a5ea6d25 --- /dev/null +++ b/rust/bindings/qom-sys/wrapper.h @@ -0,0 +1,17 @@ +/* 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. + */ + +/* + * We block include/qemu/typedefs.h from bindgen, add here symbols + * that are needed as opaque types by other functions. + */ +typedef struct Object Object; +typedef struct ObjectClass ObjectClass; + +#include "qemu/osdep.h" + +#include "qom/object.h" diff --git a/rust/bindings/system-sys/Cargo.toml b/rust/bindings/system-sys/Cargo.toml new file mode 100644 index 0000000000..7e527130ff --- /dev/null +++ b/rust/bindings/system-sys/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "system-sys" +version = "0.1.0" +description = "Rust sys bindings for QEMU/system" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +path = "lib.rs" + +[dependencies] +glib-sys = { workspace = true } +common = { path = "../../common" } +migration-sys = { path = "../migration-sys" } +util-sys = { path = "../util-sys" } +qom-sys = { path = "../qom-sys" } + +[lints] +workspace = true + +[package.metadata.bindgen] +header = "wrapper.h" +rustified-enum = ["device_endian"] +additional-files = ["system/memory.*"] diff --git a/rust/bindings/system-sys/build.rs b/rust/bindings/system-sys/build.rs new file mode 120000 index 0000000000..10238032f5 --- /dev/null +++ b/rust/bindings/system-sys/build.rs @@ -0,0 +1 @@ +../build.rs \ No newline at end of file diff --git a/rust/system/src/bindings.rs b/rust/bindings/system-sys/lib.rs similarity index 88% rename from rust/system/src/bindings.rs rename to rust/bindings/system-sys/lib.rs index 7164c5219a..022fe65dd8 100644 --- a/rust/system/src/bindings.rs +++ b/rust/bindings/system-sys/lib.rs @@ -19,7 +19,9 @@ )] use common::Zeroable; -use glib_sys::{guint, GHashTable, GHashTableIter, GList, GPollFD, GPtrArray, GSList}; +use hwcore_sys::{qemu_irq, DeviceClass, DeviceState}; +use qom_sys::{InterfaceClass, Object, ObjectClass}; +use util_sys::{Error, EventNotifier, QEMUBH}; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/bindings/system-sys/meson.build b/rust/bindings/system-sys/meson.build new file mode 100644 index 0000000000..aa5e880114 --- /dev/null +++ b/rust/bindings/system-sys/meson.build @@ -0,0 +1,12 @@ +_bindgen_system_rs = rust.bindgen( + args: bindgen_args_common + bindgen_args_data['system-sys'].split(), + kwargs: bindgen_kwargs) +_system_sys_rs = static_library( + 'system_sys', + structured_sources(['lib.rs', _bindgen_system_rs]), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [common_rs, glib_sys_rs, hwcore_sys_rs, migration_sys_rs, qom_sys_rs, util_sys_rs], +) + +system_sys_rs = declare_dependency(link_with: [_system_sys_rs]) diff --git a/rust/bindings/system-sys/wrapper.h b/rust/bindings/system-sys/wrapper.h new file mode 100644 index 0000000000..0a8bf06a1f --- /dev/null +++ b/rust/bindings/system-sys/wrapper.h @@ -0,0 +1,21 @@ +/* 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. + */ + +/* + * We block include/qemu/typedefs.h from bindgen, add here symbols + * that are needed as opaque types by other functions. + */ +typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; +typedef struct MemoryRegion MemoryRegion; +typedef struct RAMBlock RAMBlock; + +#include "qemu/osdep.h" + +#include "exec/hwaddr.h" +#include "system/address-spaces.h" +#include "system/memory.h" +#include "hw/core/sysbus.h" diff --git a/rust/bindings/util-sys/Cargo.toml b/rust/bindings/util-sys/Cargo.toml new file mode 100644 index 0000000000..929a08ac4e --- /dev/null +++ b/rust/bindings/util-sys/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "util-sys" +version = "0.1.0" +description = "Rust sys bindings for QEMU/util" +publish = false + +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +path = "lib.rs" + +[dependencies] +glib-sys = { workspace = true } + +[lints] +workspace = true + +[package.metadata.bindgen] +header = "wrapper.h" +rustified-enum = ["module_init_type", "QEMUClockType"] diff --git a/rust/bindings/util-sys/build.rs b/rust/bindings/util-sys/build.rs new file mode 120000 index 0000000000..10238032f5 --- /dev/null +++ b/rust/bindings/util-sys/build.rs @@ -0,0 +1 @@ +../build.rs \ No newline at end of file diff --git a/rust/util/src/bindings.rs b/rust/bindings/util-sys/lib.rs similarity index 88% rename from rust/util/src/bindings.rs rename to rust/bindings/util-sys/lib.rs index 3514a66f5f..0212e94184 100644 --- a/rust/util/src/bindings.rs +++ b/rust/bindings/util-sys/lib.rs @@ -18,7 +18,7 @@ clippy::too_many_arguments )] -use glib_sys::{guint, GPollFD, GString}; +use glib_sys::{guint, GArray, GHashTable, GPollFD, GSList, GSource, GString}; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/bindings/util-sys/meson.build b/rust/bindings/util-sys/meson.build new file mode 100644 index 0000000000..c37f50a94b --- /dev/null +++ b/rust/bindings/util-sys/meson.build @@ -0,0 +1,12 @@ +_bindgen_util_rs = rust.bindgen( + args: bindgen_args_common + bindgen_args_data['util-sys'].split(), + kwargs: bindgen_kwargs) +_util_sys_rs = static_library( + 'util_sys', + structured_sources(['lib.rs', _bindgen_util_rs]), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [glib_sys_rs], +) + +util_sys_rs = declare_dependency(link_with: [_util_sys_rs]) diff --git a/rust/bindings/util-sys/wrapper.h b/rust/bindings/util-sys/wrapper.h new file mode 100644 index 0000000000..e9c433e7dc --- /dev/null +++ b/rust/bindings/util-sys/wrapper.h @@ -0,0 +1,39 @@ +/* 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. + */ + +/* + * We block include/qemu/typedefs.h from bindgen, add here symbols + * that are needed as opaque types by other functions. + */ +typedef struct QEMUBH QEMUBH; +typedef struct QEMUFile QEMUFile; +typedef struct QemuOpts QemuOpts; +typedef struct JSONWriter JSONWriter; +typedef struct Visitor Visitor; + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qapi/error-internal.h" +#include "qemu/event_notifier.h" +#include "qemu/main-loop.h" +#include "qemu/aio.h" +#include "qemu/log-for-trace.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/timer.h" +#include "qapi/visitor.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qobject.h" +#include "qobject/qstring.h" +#include "qobject/json-writer.h" diff --git a/rust/bql/Cargo.toml b/rust/bql/Cargo.toml index 8fd8131102..a27099e058 100644 --- a/rust/bql/Cargo.toml +++ b/rust/bql/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true [dependencies] glib-sys.workspace = true +util-sys = { path = "../bindings/util-sys" } [features] default = ["debug_cell"] diff --git a/rust/bql/build.rs b/rust/bql/build.rs deleted file mode 120000 index 71a3167885..0000000000 --- a/rust/bql/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/bql/meson.build b/rust/bql/meson.build index 728c9e4dac..de295d2983 100644 --- a/rust/bql/meson.build +++ b/rust/bql/meson.build @@ -6,37 +6,11 @@ if get_option('debug_mutex') _bql_cfg += ['--cfg', 'feature="debug_cell"'] endif -# -# 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 -_bql_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, - c_args: bindgen_c_args, -) - _bql_rs = static_library( 'bql', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/cell.rs', - 'src/prelude.rs', - ], - {'.': _bql_bindings_inc_rs} - ), + 'src/lib.rs', rust_args: _bql_cfg, - dependencies: [glib_sys_rs], + dependencies: [glib_sys_rs, util_sys_rs], ) bql_rs = declare_dependency(link_with: [_bql_rs], diff --git a/rust/bql/src/bindings.rs b/rust/bql/src/bindings.rs deleted file mode 100644 index c656cf1884..0000000000 --- a/rust/bql/src/bindings.rs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 glib_sys::{guint, GArray, GHashTable, GHashTableIter, GPollFD, GPtrArray, GSList, GSource}; - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); diff --git a/rust/bql/src/lib.rs b/rust/bql/src/lib.rs index d2fea5db1a..3aa3d46d76 100644 --- a/rust/bql/src/lib.rs +++ b/rust/bql/src/lib.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -mod bindings; -use bindings::{bql_block_unlock, bql_locked, rust_bql_mock_lock}; +use util_sys::{bql_block_unlock, bql_locked, rust_bql_mock_lock}; mod cell; pub use cell::*; diff --git a/rust/bql/wrapper.h b/rust/bql/wrapper.h deleted file mode 100644 index 2ef9a96e1d..0000000000 --- a/rust/bql/wrapper.h +++ /dev/null @@ -1,27 +0,0 @@ -/* 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 "qemu/main-loop.h" diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml index f105189dcc..208d44d0cd 100644 --- a/rust/chardev/Cargo.toml +++ b/rust/chardev/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true [dependencies] glib-sys = { workspace = true } +chardev-sys = { path = "../bindings/chardev-sys" } common = { path = "../common" } bql = { path = "../bql" } migration = { path = "../migration" } diff --git a/rust/chardev/build.rs b/rust/chardev/build.rs deleted file mode 120000 index 71a3167885..0000000000 --- a/rust/chardev/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index 2e4f4670bd..7b267fd23a 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -1,41 +1,8 @@ -c_enums = [ - 'QEMUChrEvent', -] -_chardev_bindgen_args = [] -foreach enum : c_enums - _chardev_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 -_chardev_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 + _chardev_bindgen_args, - c_args: bindgen_c_args, -) - _chardev_rs = static_library( 'chardev', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/chardev.rs', - 'src/prelude.rs', - ], - {'.': _chardev_bindings_inc_rs} - ), + 'src/lib.rs', link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [glib_sys_rs, common_rs, qemu_macros], + dependencies: [glib_sys_rs, common_rs, qemu_macros, chardev_sys_rs], ) chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil]) diff --git a/rust/chardev/src/lib.rs b/rust/chardev/src/lib.rs index 93a2872593..9f1a3a9c1c 100644 --- a/rust/chardev/src/lib.rs +++ b/rust/chardev/src/lib.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pub mod bindings; +pub use chardev_sys as bindings; mod chardev; pub use chardev::*; diff --git a/rust/chardev/wrapper.h b/rust/chardev/wrapper.h deleted file mode 100644 index 65ede6ea6d..0000000000 --- a/rust/chardev/wrapper.h +++ /dev/null @@ -1,28 +0,0 @@ -/* 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 "chardev/char-fe.h" -#include "chardev/char-serial.h" diff --git a/rust/hw/char/pl011/build.rs b/rust/hw/char/pl011/build.rs index 5f5060db35..e326242685 120000 --- a/rust/hw/char/pl011/build.rs +++ b/rust/hw/char/pl011/build.rs @@ -1 +1 @@ -../../../util/build.rs \ No newline at end of file +../../../bindings/build.rs \ No newline at end of file diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 9c0e8290e9..1a1a09e508 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -1,18 +1,11 @@ -# 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 _libpl011_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, - c_args: bindgen_c_args, + args: bindgen_args_common + [ + '--allowlist-file', meson.project_source_root() / 'include/hw/char/pl011.h', + '--blocklist-file', + meson.project_source_root() / + 'include/(block\|chardev/|exec/|hw/core/|qemu/|qom/|system/).*', + ], + kwargs: bindgen_kwargs, ) _libpl011_rs = static_library( diff --git a/rust/hw/char/pl011/src/bindings.rs b/rust/hw/char/pl011/src/bindings.rs index 68b9b22bbc..8e2f8863b1 100644 --- a/rust/hw/char/pl011/src/bindings.rs +++ b/rust/hw/char/pl011/src/bindings.rs @@ -20,10 +20,9 @@ //! `bindgen`-generated declarations. -use glib_sys::{ - gboolean, guint, GArray, GHashTable, GHashTableIter, GIOCondition, GList, GMainContext, - GPollFD, GPtrArray, GSList, GSource, GSourceFunc, -}; +use chardev::bindings::{CharFrontend, Chardev}; +use hwcore::bindings::{qemu_irq, Clock, DeviceState}; +use system::bindings::{hwaddr, MemoryRegion, SysBusDevice}; #[cfg(MESON)] include!("bindings.inc.rs"); diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml index ecfb564718..8cc514da20 100644 --- a/rust/hw/core/Cargo.toml +++ b/rust/hw/core/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true [dependencies] glib-sys.workspace = true +hwcore-sys = { path = "../../bindings/hwcore-sys" } qemu_macros = { path = "../../qemu-macros" } common = { path = "../../common" } bql = { path = "../../bql" } diff --git a/rust/hw/core/build.rs b/rust/hw/core/build.rs deleted file mode 120000 index 2a79ee31b8..0000000000 --- a/rust/hw/core/build.rs +++ /dev/null @@ -1 +0,0 @@ -../../util/build.rs \ No newline at end of file diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 942ee9cdac..28ea00cdb4 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -1,66 +1,10 @@ -_hwcore_bindgen_args = [] -c_enums = [ - 'DeviceCategory', - 'GpioPolarity', - 'MachineInitPhase', - 'ResetType', -] -foreach enum : c_enums - _hwcore_bindgen_args += ['--rustified-enum', enum] -endforeach - -blocked_type = [ - 'Chardev', - 'Error', - 'ObjectClass', - 'MemoryRegion', - 'VMStateDescription', -] -foreach type: blocked_type - _hwcore_bindgen_args += ['--blocklist-type', type] -endforeach - -c_bitfields = [ - 'ClockEvent', -] -foreach enum : c_bitfields - _hwcore_bindgen_args += ['--bitfield-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 -_hwcore_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 + _hwcore_bindgen_args, - c_args: bindgen_c_args, -) - _hwcore_rs = static_library( 'hwcore', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/irq.rs', - 'src/prelude.rs', - 'src/qdev.rs', - 'src/sysbus.rs', - ], - {'.': _hwcore_bindings_inc_rs} - ), + 'src/lib.rs', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs], - dependencies: [glib_sys_rs, qemu_macros, common_rs], + dependencies: [glib_sys_rs, qemu_macros, common_rs, hwcore_sys_rs], ) hwcore_rs = declare_dependency(link_with: [_hwcore_rs], diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs index 491743d2b9..76689fe7db 100644 --- a/rust/hw/core/src/lib.rs +++ b/rust/hw/core/src/lib.rs @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later +pub use hwcore_sys as bindings; pub use qemu_macros::Device; pub use qom; -pub mod bindings; - mod irq; pub use irq::*; diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index f6037fbdca..145e20a984 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -66,7 +66,7 @@ pub trait ResettablePhasesImpl { /// 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_resettable_enter_fn( - obj: *mut bindings::Object, + obj: *mut qom::bindings::Object, typ: ResetType, ) { let state = NonNull::new(obj).unwrap().cast::(); @@ -79,7 +79,7 @@ pub trait ResettablePhasesImpl { /// 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_resettable_hold_fn( - obj: *mut bindings::Object, + obj: *mut qom::bindings::Object, typ: ResetType, ) { let state = NonNull::new(obj).unwrap().cast::(); @@ -92,7 +92,7 @@ pub trait ResettablePhasesImpl { /// 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_resettable_exit_fn( - obj: *mut bindings::Object, + obj: *mut qom::bindings::Object, typ: ResetType, ) { let state = NonNull::new(obj).unwrap().cast::(); diff --git a/rust/hw/core/src/sysbus.rs b/rust/hw/core/src/sysbus.rs index 81fab3f191..7db09a82c6 100644 --- a/rust/hw/core/src/sysbus.rs +++ b/rust/hw/core/src/sysbus.rs @@ -6,22 +6,21 @@ use std::ffi::CStr; -pub use bindings::SysBusDeviceClass; use common::Opaque; use qom::prelude::*; use system::MemoryRegion; +pub use system_sys::SysBusDeviceClass; use util::{Error, Result}; use crate::{ - bindings, irq::{IRQState, InterruptSource}, qdev::{DeviceClassExt, DeviceImpl, DeviceState}, }; -/// A safe wrapper around [`bindings::SysBusDevice`]. +/// A safe wrapper around [`system_sys::SysBusDevice`]. #[repr(transparent)] #[derive(Debug, common::Wrapper)] -pub struct SysBusDevice(Opaque); +pub struct SysBusDevice(Opaque); unsafe impl Send for SysBusDevice {} unsafe impl Sync for SysBusDevice {} @@ -29,7 +28,7 @@ unsafe impl Sync for SysBusDevice {} unsafe impl ObjectType for SysBusDevice { type Class = SysBusDeviceClass; const TYPE_NAME: &'static CStr = - unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; + unsafe { CStr::from_bytes_with_nul_unchecked(system_sys::TYPE_SYS_BUS_DEVICE) }; } qom_isa!(SysBusDevice: DeviceState, Object); @@ -62,7 +61,7 @@ pub trait SysBusDeviceMethods: ObjectDeref fn init_mmio(&self, iomem: &MemoryRegion) { assert!(bql::is_locked()); unsafe { - bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); + system_sys::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); } } @@ -73,7 +72,7 @@ fn init_mmio(&self, iomem: &MemoryRegion) { fn init_irq(&self, irq: &InterruptSource) { assert!(bql::is_locked()); unsafe { - bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); + system_sys::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); } } @@ -82,7 +81,7 @@ fn mmio_addr(&self, id: u32) -> Option { assert!(bql::is_locked()); // SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and // the SysBusDevice must be initialized to get an IsA. - let sbd = unsafe { &*self.upcast().as_ptr() }; + let sbd = unsafe { &*self.upcast().as_mut_ptr() }; let id: usize = id.try_into().unwrap(); if sbd.mmio[id].memory.is_null() { None @@ -96,7 +95,7 @@ fn mmio_map(&self, id: u32, addr: u64) { assert!(bql::is_locked()); let id: i32 = id.try_into().unwrap(); unsafe { - bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); + system_sys::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); } } @@ -108,7 +107,7 @@ fn connect_irq(&self, id: u32, irq: &Owned) { let id: i32 = id.try_into().unwrap(); let irq: &IRQState = irq; unsafe { - bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr()); + system_sys::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr()); } } @@ -116,7 +115,7 @@ fn sysbus_realize(&self) -> Result<()> { assert!(bql::is_locked()); unsafe { Error::with_errp(|errp| { - bindings::sysbus_realize(self.upcast().as_mut_ptr(), errp); + system_sys::sysbus_realize(self.upcast().as_mut_ptr(), errp); }) } } diff --git a/rust/hw/core/wrapper.h b/rust/hw/core/wrapper.h deleted file mode 100644 index 8278738f3d..0000000000 --- a/rust/hw/core/wrapper.h +++ /dev/null @@ -1,32 +0,0 @@ -/* 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 "hw/core/sysbus.h" -#include "hw/core/clock.h" -#include "hw/core/qdev-clock.h" -#include "hw/core/qdev-properties.h" -#include "hw/core/qdev-properties-system.h" -#include "hw/core/irq.h" diff --git a/rust/meson.build b/rust/meson.build index bacb787910..3a3e10d7b9 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -35,6 +35,7 @@ genrs = [] subdir('qemu-macros') subdir('common') +subdir('bindings') subdir('bits') subdir('util') diff --git a/rust/migration/Cargo.toml b/rust/migration/Cargo.toml index 415457496d..cf10601b44 100644 --- a/rust/migration/Cargo.toml +++ b/rust/migration/Cargo.toml @@ -17,6 +17,7 @@ bql = { path = "../bql" } common = { path = "../common" } qemu_macros = { path = "../qemu-macros" } util = { path = "../util" } +migration-sys = { path = "../bindings/migration-sys" } glib-sys.workspace = true [lints] diff --git a/rust/migration/build.rs b/rust/migration/build.rs deleted file mode 120000 index 71a3167885..0000000000 --- a/rust/migration/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/migration/meson.build b/rust/migration/meson.build index 76d86b0ac0..9626689777 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -1,44 +1,8 @@ -_migration_bindgen_args = [] -c_bitfields = [ - 'MigrationPolicy', - 'MigrationPriority', - 'VMStateFlags', -] -foreach enum : c_bitfields - _migration_bindgen_args += ['--bitfield-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 -_migration_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 + _migration_bindgen_args, - c_args: bindgen_c_args, -) - _migration_rs = static_library( 'migration', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/migratable.rs', - 'src/prelude.rs', - 'src/vmstate.rs', - ], - {'.' : _migration_bindings_inc_rs}, - ), + 'src/lib.rs', link_with: [_util_rs, _bql_rs], - dependencies: [common_rs, glib_sys_rs, qemu_macros], + dependencies: [common_rs, glib_sys_rs, qemu_macros, migration_sys_rs], ) migration_rs = declare_dependency(link_with: [_migration_rs], diff --git a/rust/migration/src/bindings.rs b/rust/migration/src/bindings.rs deleted file mode 100644 index e9c058386c..0000000000 --- a/rust/migration/src/bindings.rs +++ /dev/null @@ -1,49 +0,0 @@ -// 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; -use glib_sys::{GHashTable, GHashTableIter, GPtrArray, GSList}; - -#[cfg(MESON)] -include!("bindings.inc.rs"); - -#[cfg(not(MESON))] -include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); - -unsafe impl Send for VMStateDescription {} -unsafe impl Sync for VMStateDescription {} - -unsafe impl Send for VMStateField {} -unsafe impl Sync for VMStateField {} - -unsafe impl Send for VMStateInfo {} -unsafe impl Sync for VMStateInfo {} - -// bindgen does not derive Default here -#[allow(clippy::derivable_impls)] -impl Default for VMStateFlags { - fn default() -> Self { - Self(0) - } -} - -unsafe impl Zeroable for VMStateFlags {} -unsafe impl Zeroable for VMStateField {} -unsafe impl Zeroable for VMStateDescription {} diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs index 32e182e716..6d819cc155 100644 --- a/rust/migration/src/lib.rs +++ b/rust/migration/src/lib.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pub mod bindings; - +pub use migration_sys as bindings; pub use qemu_macros::ToMigrationState; pub mod migratable; diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 595e7e9cd7..edc7c70265 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -165,79 +165,6 @@ macro_rules! vmstate_of { }; } -pub trait VMStateFlagsExt { - const VMS_VARRAY_FLAGS: VMStateFlags; -} - -impl VMStateFlagsExt for VMStateFlags { - const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( - VMStateFlags::VMS_VARRAY_INT32.0 - | VMStateFlags::VMS_VARRAY_UINT8.0 - | VMStateFlags::VMS_VARRAY_UINT16.0 - | VMStateFlags::VMS_VARRAY_UINT32.0, - ); -} - -// Add a couple builder-style methods to VMStateField, allowing -// easy derivation of VMStateField constants from other types. -impl VMStateField { - #[must_use] - pub const fn with_version_id(mut self, version_id: i32) -> Self { - assert!(version_id >= 0); - self.version_id = version_id; - self - } - - #[must_use] - pub const fn with_array_flag(mut self, num: usize) -> Self { - assert!(num <= 0x7FFF_FFFFusize); - assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0); - assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0); - if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { - self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); - // VMS_ARRAY_OF_POINTER flag stores the size of pointer. - // FIXME: *const, *mut, NonNull and Box<> have the same size as usize. - // Resize if more smart pointers are supported. - self.size = std::mem::size_of::(); - } - self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); - self.num = num as i32; - self - } - - #[must_use] - pub const fn with_pointer_flag(mut self) -> Self { - assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0); - self - } - - #[must_use] - pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> Self { - self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); - self.flags = VMStateFlags(self.flags.0 | flag.0); - self.num = 0; // varray uses num_offset instead of num. - self - } - - #[must_use] - #[allow(unused_mut)] - pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> Self { - assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); - self.with_varray_flag_unchecked(flag) - } - - #[must_use] - pub const fn with_varray_multiply(mut self, num: u32) -> Self { - assert!(num <= 0x7FFF_FFFFu32); - self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); - self.num = num as i32; - self - } -} - /// This macro can be used (by just passing it a type) to forward the `VMState` /// trait to the first field of a tuple. This is a workaround for lack of /// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0. diff --git a/rust/migration/wrapper.h b/rust/migration/wrapper.h deleted file mode 100644 index daf316aed4..0000000000 --- a/rust/migration/wrapper.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2024 Linaro Ltd. - * - * Authors: Manos Pitsidianakis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -/* - * 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 "migration/vmstate.h" diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml index 4be3c2541b..aed8af5474 100644 --- a/rust/qom/Cargo.toml +++ b/rust/qom/Cargo.toml @@ -18,6 +18,7 @@ bql = { path = "../bql" } migration = { path = "../migration" } qemu_macros = { path = "../qemu-macros" } util = { path = "../util" } +qom-sys = { path = "../bindings/qom-sys" } glib-sys.workspace = true [lints] diff --git a/rust/qom/build.rs b/rust/qom/build.rs deleted file mode 120000 index 71a3167885..0000000000 --- a/rust/qom/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/qom/meson.build b/rust/qom/meson.build index dda26c3f98..9865da280c 100644 --- a/rust/qom/meson.build +++ b/rust/qom/meson.build @@ -1,33 +1,8 @@ -# 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, - c_args: bindgen_c_args, -) - _qom_rs = static_library( 'qom', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/prelude.rs', - 'src/qom.rs', - ], - {'.': _qom_bindings_inc_rs} - ), + 'src/lib.rs', link_with: [_bql_rs, _migration_rs], - dependencies: [common_rs, glib_sys_rs, qemu_macros], + dependencies: [common_rs, glib_sys_rs, qemu_macros, qom_sys_rs], ) qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom, qemuutil]) diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs index 24c44fc2af..1bed1cfe23 100644 --- a/rust/qom/src/lib.rs +++ b/rust/qom/src/lib.rs @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later pub use qemu_macros::Object; - -pub mod bindings; +pub use qom_sys as bindings; // preserve one-item-per-"use" syntax, it is clearer // for prelude-like modules diff --git a/rust/qom/wrapper.h b/rust/qom/wrapper.h deleted file mode 100644 index 3b71bcd3f5..0000000000 --- a/rust/qom/wrapper.h +++ /dev/null @@ -1,27 +0,0 @@ -/* 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" diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml index 186ea00bff..f7fde9782e 100644 --- a/rust/system/Cargo.toml +++ b/rust/system/Cargo.toml @@ -14,6 +14,9 @@ rust-version.workspace = true [dependencies] common = { path = "../common" } +system-sys = { path = "../bindings/system-sys" } +bql = { path = "../bql" } +migration = { path = "../migration" } qom = { path = "../qom" } util = { path = "../util" } glib-sys.workspace = true diff --git a/rust/system/build.rs b/rust/system/build.rs deleted file mode 120000 index 71a3167885..0000000000 --- a/rust/system/build.rs +++ /dev/null @@ -1 +0,0 @@ -../util/build.rs \ No newline at end of file diff --git a/rust/system/meson.build b/rust/system/meson.build index e9f36ed855..4cbd63cbbd 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -1,41 +1,8 @@ -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, - c_args: bindgen_c_args, -) - _system_rs = static_library( 'system', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/memory.rs', - 'src/prelude.rs', - ], - {'.': _system_bindings_inc_rs} - ), + 'src/lib.rs', link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [glib_sys_rs, common_rs, qemu_macros], + dependencies: [glib_sys_rs, common_rs, qemu_macros, system_sys_rs], ) system_rs = declare_dependency(link_with: [_system_rs], diff --git a/rust/system/src/lib.rs b/rust/system/src/lib.rs index 5fb83b65d8..10741e0ee0 100644 --- a/rust/system/src/lib.rs +++ b/rust/system/src/lib.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pub mod bindings; +pub use system_sys as bindings; mod memory; pub use memory::*; diff --git a/rust/system/src/memory.rs b/rust/system/src/memory.rs index 4e06c16a0b..4c258201ba 100644 --- a/rust/system/src/memory.rs +++ b/rust/system/src/memory.rs @@ -132,7 +132,7 @@ unsafe impl Sync for MemoryRegion {} impl MemoryRegion { unsafe fn do_init_io( slot: *mut bindings::MemoryRegion, - owner: *mut bindings::Object, + owner: *mut qom::bindings::Object, ops: &'static bindings::MemoryRegionOps, name: &'static str, size: u64, diff --git a/rust/system/wrapper.h b/rust/system/wrapper.h deleted file mode 100644 index 48abde8505..0000000000 --- a/rust/system/wrapper.h +++ /dev/null @@ -1,29 +0,0 @@ -/* 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" diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 85f9143654..2ad5940dac 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -18,6 +18,7 @@ foreign = { workspace = true } glib-sys = { workspace = true } libc = { workspace = true } common = { path = "../common" } +util-sys = { path = "../bindings/util-sys" } [lints] workspace = true diff --git a/rust/util/meson.build b/rust/util/meson.build index 98629394af..6d175ae0b0 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -1,45 +1,7 @@ -_util_bindgen_args = [] -c_enums = [ - 'module_init_type', - 'QEMUClockType', -] -foreach enum : c_enums - _util_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 -_util_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 + _util_bindgen_args, - c_args: bindgen_c_args, -) - _util_rs = static_library( 'util', - structured_sources( - [ - 'src/lib.rs', - 'src/bindings.rs', - 'src/error.rs', - 'src/log.rs', - 'src/module.rs', - 'src/prelude.rs', - 'src/timer.rs', - ], - {'.': _util_bindings_inc_rs} - ), - dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs], + 'src/lib.rs', + dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, util_sys_rs], ) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) diff --git a/rust/util/src/lib.rs b/rust/util/src/lib.rs index 7d2de3ed81..436c67e139 100644 --- a/rust/util/src/lib.rs +++ b/rust/util/src/lib.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pub mod bindings; +pub use util_sys as bindings; + pub mod error; pub mod log; pub mod module; diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h deleted file mode 100644 index b9ed68a01d..0000000000 --- a/rust/util/wrapper.h +++ /dev/null @@ -1,32 +0,0 @@ -/* 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 "qapi/error.h" -#include "qapi/error-internal.h" -#include "qemu/log-for-trace.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qemu/timer.h" From 1713498c0d7f41ce81387e47d60db0d8420f6233 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 14 Jan 2026 09:44:36 +0100 Subject: [PATCH 05/13] rust: move hwcore::sysbus to system crate This inverts the dependency from hwcore to system, replacing it with a dependency from system to hwcore. It also matches how hw/core/sysbus.h is part of the system-sys crate, and hw/core/sysbus.c is part of system_ss on the C side. This fixes a linker error in hwcore integration tests on msys2. Signed-off-by: Paolo Bonzini --- rust/hw/core/Cargo.toml | 1 - rust/hw/core/meson.build | 2 +- rust/hw/core/src/irq.rs | 8 ++++---- rust/hw/core/src/lib.rs | 3 --- rust/hw/core/src/prelude.rs | 5 ----- rust/hw/core/tests/tests.rs | 4 ++-- rust/meson.build | 2 +- rust/system/Cargo.toml | 1 + rust/system/meson.build | 2 +- rust/system/src/lib.rs | 3 +++ rust/system/src/prelude.rs | 5 +++++ rust/{hw/core => system}/src/sysbus.rs | 7 ++----- 12 files changed, 20 insertions(+), 23 deletions(-) rename rust/{hw/core => system}/src/sysbus.rs (96%) diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml index 8cc514da20..d1ff71d5c3 100644 --- a/rust/hw/core/Cargo.toml +++ b/rust/hw/core/Cargo.toml @@ -21,7 +21,6 @@ bql = { path = "../../bql" } qom = { path = "../../qom" } chardev = { path = "../../chardev" } migration = { path = "../../migration" } -system = { path = "../../system" } util = { path = "../../util" } [lints] diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 28ea00cdb4..6d1bfd4d20 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -3,7 +3,7 @@ _hwcore_rs = static_library( 'src/lib.rs', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', - link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs], + link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _util_rs], dependencies: [glib_sys_rs, qemu_macros, common_rs, hwcore_sys_rs], ) diff --git a/rust/hw/core/src/irq.rs b/rust/hw/core/src/irq.rs index e0d7784d97..13ace92182 100644 --- a/rust/hw/core/src/irq.rs +++ b/rust/hw/core/src/irq.rs @@ -33,10 +33,10 @@ /// /// 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 [`crate::sysbus::SysBusDeviceMethods::init_irq`], and +/// a function such as [`crate::DeviceMethods::init_gpio_out`], 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 +/// the sink's `IRQState *`, for example using `qdev_connect_gpio_out`. Because /// devices are generally shared objects, interrupt sources are an example of /// the interior mutability pattern. /// @@ -87,11 +87,11 @@ pub fn set(&self, level: T) { } } - pub(crate) const fn as_ptr(&self) -> *mut *mut bindings::IRQState { + pub const fn as_ptr(&self) -> *mut *mut bindings::IRQState { self.cell.as_ptr() } - pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState { + pub const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState { assert!(!slice.is_empty()); slice[0].as_ptr() } diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs index 76689fe7db..6701dc52b6 100644 --- a/rust/hw/core/src/lib.rs +++ b/rust/hw/core/src/lib.rs @@ -14,6 +14,3 @@ mod qdev; pub use qdev::*; - -mod sysbus; -pub use sysbus::*; diff --git a/rust/hw/core/src/prelude.rs b/rust/hw/core/src/prelude.rs index 13f7dfc680..45e86e178b 100644 --- a/rust/hw/core/src/prelude.rs +++ b/rust/hw/core/src/prelude.rs @@ -9,9 +9,4 @@ pub use crate::qdev::ResettablePhasesImpl; pub use crate::qdev::ResetType; -pub use crate::sysbus::SysBusDevice; -pub use crate::sysbus::SysBusDeviceClassExt; -pub use crate::sysbus::SysBusDeviceImpl; -pub use crate::sysbus::SysBusDeviceMethods; - pub use crate::irq::InterruptSource; diff --git a/rust/hw/core/tests/tests.rs b/rust/hw/core/tests/tests.rs index 115dd7a860..05aa5d4a37 100644 --- a/rust/hw/core/tests/tests.rs +++ b/rust/hw/core/tests/tests.rs @@ -142,7 +142,7 @@ fn test_cast() { let obj_ref: &Object = p_ref.upcast(); assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); - let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); + let sbd_ref: Option<&DummyChildState> = obj_ref.dynamic_cast(); assert!(sbd_ref.is_none()); let dev_ref: Option<&DeviceState> = obj_ref.downcast(); @@ -150,7 +150,7 @@ fn test_cast() { // SAFETY: the cast is wrong, but the value is only used for comparison unsafe { - let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); + let sbd_ref: &DummyChildState = obj_ref.unsafe_cast(); assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); } } diff --git a/rust/meson.build b/rust/meson.build index 3a3e10d7b9..b6711fe77d 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -42,9 +42,9 @@ subdir('util') subdir('bql') subdir('migration') subdir('qom') -subdir('system') subdir('chardev') subdir('hw/core') +subdir('system') subdir('tests') subdir('trace') subdir('hw') diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml index f7fde9782e..d621737031 100644 --- a/rust/system/Cargo.toml +++ b/rust/system/Cargo.toml @@ -16,6 +16,7 @@ rust-version.workspace = true common = { path = "../common" } system-sys = { path = "../bindings/system-sys" } bql = { path = "../bql" } +hwcore = { path = "../hw/core" } migration = { path = "../migration" } qom = { path = "../qom" } util = { path = "../util" } diff --git a/rust/system/meson.build b/rust/system/meson.build index 4cbd63cbbd..89c1f2b84d 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -1,7 +1,7 @@ _system_rs = static_library( 'system', 'src/lib.rs', - link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], + link_with: [_bql_rs, _hwcore_rs, _migration_rs, _qom_rs, _util_rs], dependencies: [glib_sys_rs, common_rs, qemu_macros, system_sys_rs], ) diff --git a/rust/system/src/lib.rs b/rust/system/src/lib.rs index 10741e0ee0..3b1666e62c 100644 --- a/rust/system/src/lib.rs +++ b/rust/system/src/lib.rs @@ -9,3 +9,6 @@ // for prelude-like modules #[rustfmt::skip] pub mod prelude; + +mod sysbus; +pub use sysbus::*; diff --git a/rust/system/src/prelude.rs b/rust/system/src/prelude.rs index 2d98524c36..5a8688ca34 100644 --- a/rust/system/src/prelude.rs +++ b/rust/system/src/prelude.rs @@ -6,3 +6,8 @@ pub use crate::memory::MemoryRegionOps; pub use crate::memory::MemoryRegionOpsBuilder; pub use crate::memory::MemTxAttrs; + +pub use crate::sysbus::SysBusDevice; +pub use crate::sysbus::SysBusDeviceClassExt; +pub use crate::sysbus::SysBusDeviceImpl; +pub use crate::sysbus::SysBusDeviceMethods; diff --git a/rust/hw/core/src/sysbus.rs b/rust/system/src/sysbus.rs similarity index 96% rename from rust/hw/core/src/sysbus.rs rename to rust/system/src/sysbus.rs index 7db09a82c6..3c9aff51be 100644 --- a/rust/hw/core/src/sysbus.rs +++ b/rust/system/src/sysbus.rs @@ -7,15 +7,12 @@ use std::ffi::CStr; use common::Opaque; +use hwcore::{prelude::*, IRQState}; use qom::prelude::*; -use system::MemoryRegion; pub use system_sys::SysBusDeviceClass; use util::{Error, Result}; -use crate::{ - irq::{IRQState, InterruptSource}, - qdev::{DeviceClassExt, DeviceImpl, DeviceState}, -}; +use crate::MemoryRegion; /// A safe wrapper around [`system_sys::SysBusDevice`]. #[repr(transparent)] From fe367cbe398a526fc70693fe8bebee1ead325deb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 14 Jan 2026 17:34:10 +0100 Subject: [PATCH 06/13] qdev: add hw/core/gpio.c to libhwcore While GPIO functionality is not used by user-mode emulation, it is conceptually part of qdev - GPIO pins are included in DeviceState independent of whether this is system or user-mode emulation. For the Rust bindings, having GPIO functionality in system_ss causes a problem because, for simplicity, all methods of DeviceState are included in the Rust hwcore crate. On Windows, rustc is not able to do dead code elimination as well as on other OSes and this causes an undefined symbol error, because gpio.c is not linked into the rust/hw/core/rust-hwcore-rs-integration test binary. To fix it, move gpio.c out of system_ss and into libhwcore. Alternatively, it would be possible to define some DeviceState methods also in the system crate, using for example a separate trace SystemDeviceMethods. For now, keep all the methods in one crate and link 200 unused lines of code into user-mode emulators. Signed-off-by: Paolo Bonzini --- hw/core/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/meson.build b/hw/core/meson.build index b5a545a0ed..8a96567de8 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -1,6 +1,7 @@ # core qdev-related obj files, also used by *-user and unit tests hwcore_ss.add(files( 'bus.c', + 'gpio.c', 'qdev-properties.c', 'qdev.c', 'resetcontainer.c', @@ -29,7 +30,6 @@ system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutl system_ss.add(files( 'cpu-system.c', 'fw-path-provider.c', - 'gpio.c', 'hotplug.c', 'loader.c', 'machine-hmp-cmds.c', From 2eb8d9734355ed86e162dce2a3f265ffee4005ed Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 Jan 2026 16:42:19 +0100 Subject: [PATCH 07/13] target/i386/tcg: fix a few instructions that do not support VEX.L=1 Match the contents of table 2-17 ("#UD Exception and VEX.L Field Encoding") in the SDM, for instruction in exception class 5. They were incorrectly accepting 256-bit versions that do not exist. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 09e3d8884c..e44b92710c 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -628,7 +628,7 @@ static void decode_0F7E(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui static const X86OpEntry opcodes_0F7E[4] = { X86_OP_ENTRY3(MOVD_from, E,y, None,None, P,y, vex5 mmx), X86_OP_ENTRY3(MOVD_from, E,y, None,None, V,y, vex5), - X86_OP_ENTRY3(MOVQ, V,x, None,None, W,q, vex5), /* wrong dest Vy on SDM! */ + X86_OP_ENTRY3(MOVQ, V,dq,None,None, W,q, vex5), /* wrong dest Vq on SDM! */ {}, }; *entry = *decode_by_prefix(s, opcodes_0F7E); @@ -693,7 +693,7 @@ static void decode_0FD6(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui { static const X86OpEntry movq[4] = { {}, - X86_OP_ENTRY3(MOVQ, W,x, None, None, V,q, vex5), + X86_OP_ENTRY3(MOVQ, W,dq, None, None, V,q, vex5), X86_OP_ENTRY3(MOVq_dq, V,dq, None, None, N,q), X86_OP_ENTRY3(MOVq_dq, P,q, None, None, U,q), }; @@ -1102,7 +1102,7 @@ static void decode_0F12(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui }; static const X86OpEntry opcodes_0F12_reg[4] = { X86_OP_ENTRY3(VMOVHLPS, V,dq, H,dq, U,dq, vex7), - X86_OP_ENTRY3(VMOVLPx, W,x, H,x, U,q, vex5), /* MOVLPD */ + X86_OP_ENTRY3(VMOVLPx, W,dq, H,dq, U,q, vex5), /* MOVLPD */ X86_OP_ENTRY3(VMOVSLDUP, V,x, None,None, U,x, vex4 cpuid(SSE3)), X86_OP_ENTRY3(VMOVDDUP, V,x, None,None, U,x, vex5 cpuid(SSE3)), }; @@ -1465,7 +1465,7 @@ static const X86OpEntry opcodes_0F[256] = { [0x6b] = X86_OP_ENTRY3(PACKSSDW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), [0x6c] = X86_OP_ENTRY3(PUNPCKLQDQ, V,x, H,x, W,x, vex4 p_66 avx2_256), [0x6d] = X86_OP_ENTRY3(PUNPCKHQDQ, V,x, H,x, W,x, vex4 p_66 avx2_256), - [0x6e] = X86_OP_ENTRY3(MOVD_to, V,x, None,None, E,y, vex5 mmx p_00_66), /* wrong dest Vy on SDM! */ + [0x6e] = X86_OP_ENTRY3(MOVD_to, V,dq,None,None, E,y, vex5 mmx p_00_66), /* wrong dest Vy on SDM! */ [0x6f] = X86_OP_GROUP0(0F6F), [0x78] = X86_OP_GROUP0(0F78), From d66532600f81ae032b0eba04daa4936fb28df928 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 7 Jan 2026 16:45:00 +0100 Subject: [PATCH 08/13] target/i386/tcg: fix typo in dpps/dppd instructions Their gen_* functions were incorrectly named gen_VDDPS and gen_VDDPD. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 4 ++-- target/i386/tcg/emit.c.inc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index e44b92710c..b00ea3e86e 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -977,8 +977,8 @@ static const X86OpEntry opcodes_0F3A[256] = { [0x21] = X86_OP_GROUP0(VINSERTPS), [0x22] = X86_OP_ENTRY4(PINSR, V,dq, H,dq, E,y, vex5 cpuid(SSE41) p_66), - [0x40] = X86_OP_ENTRY4(VDDPS, V,x, H,x, W,x, vex2 cpuid(SSE41) p_66), - [0x41] = X86_OP_ENTRY4(VDDPD, V,dq, H,dq, W,dq, vex2 cpuid(SSE41) p_66), + [0x40] = X86_OP_ENTRY4(VDPPS, V,x, H,x, W,x, vex2 cpuid(SSE41) p_66), + [0x41] = X86_OP_ENTRY4(VDPPD, V,dq, H,dq, W,dq, vex2 cpuid(SSE41) p_66), [0x42] = X86_OP_ENTRY4(VMPSADBW, V,x, H,x, W,x, vex2 cpuid(SSE41) avx2_256 p_66), [0x44] = X86_OP_ENTRY4(PCLMULQDQ, V,dq, H,dq, W,dq, vex4 cpuid(PCLMULQDQ) p_66), [0x46] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 639a1eb638..f23c401189 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -788,9 +788,9 @@ static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) BINARY_IMM_SSE(VBLENDPD, blendpd) BINARY_IMM_SSE(VBLENDPS, blendps) BINARY_IMM_SSE(VPBLENDW, pblendw) -BINARY_IMM_SSE(VDDPS, dpps) +BINARY_IMM_SSE(VDPPS, dpps) #define gen_helper_dppd_ymm NULL -BINARY_IMM_SSE(VDDPD, dppd) +BINARY_IMM_SSE(VDPPD, dppd) BINARY_IMM_SSE(VMPSADBW, mpsadbw) BINARY_IMM_SSE(PCLMULQDQ, pclmulqdq) From 30ebb65f8385c8bcd0738ae94c9b1ea04e3378bd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 9 Jan 2026 15:30:36 +0100 Subject: [PATCH 09/13] target/i386/tcg: remove dead constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NB_OP_SIZES has been dead since the conversion to TCG, REG_L_OFFSET since 2015, the others somewhere in the middle. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 460848e422..20aa94347b 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -374,30 +374,6 @@ static void gen_update_cc_op(DisasContext *s) } } -#ifdef TARGET_X86_64 - -#define NB_OP_SIZES 4 - -#else /* !TARGET_X86_64 */ - -#define NB_OP_SIZES 3 - -#endif /* !TARGET_X86_64 */ - -#if HOST_BIG_ENDIAN -#define REG_B_OFFSET (sizeof(target_ulong) - 1) -#define REG_H_OFFSET (sizeof(target_ulong) - 2) -#define REG_W_OFFSET (sizeof(target_ulong) - 2) -#define REG_L_OFFSET (sizeof(target_ulong) - 4) -#define REG_LH_OFFSET (sizeof(target_ulong) - 8) -#else -#define REG_B_OFFSET 0 -#define REG_H_OFFSET 1 -#define REG_W_OFFSET 0 -#define REG_L_OFFSET 0 -#define REG_LH_OFFSET 4 -#endif - /* In instruction encodings for byte register accesses the * register number usually indicates "low 8 bits of register N"; * however there are some special cases where N 4..7 indicates From a1356c5677d0868c7427f7bbee8b0af056cc849d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Dec 2025 17:55:59 +0100 Subject: [PATCH 10/13] target/i386/tcg: merge decode_modrm and decode_modrm_address split Unlike the older code in translate.c, mod=11b *is* filtered out earlier by decode_modrm, and it would have returned bogus code. Since the register case is so simple, just inline decode_modrm_address into its caller instead of removing the "if". Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 64 ++++++++++++-------------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index b00ea3e86e..662d1d707d 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2007,33 +2007,34 @@ static void decode_root(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui *entry = opcodes_root[*b]; } -/* Decompose an address. */ -static AddressParts decode_modrm_address(CPUX86State *env, DisasContext *s, - int modrm, bool is_vsib) +/* Decode the MODRM and SIB bytes into a register or memory operand. */ +static void decode_modrm(DisasContext *s, CPUX86State *env, + X86DecodedInsn *decode, X86DecodedOp *op) { - int def_seg, base, index, scale, mod, rm; - target_long disp; - bool havesib; - - def_seg = R_DS; - index = -1; - scale = 0; - disp = 0; - - mod = (modrm >> 6) & 3; - rm = modrm & 7; - base = rm | REX_B(s); + int modrm = get_modrm(s, env); + int mod = (modrm >> 6) & 3; + int rm = modrm & 7; + bool is_vsib = decode->e.vex_class == 12; + bool havesib = false; if (mod == 3) { - /* Normally filtered out earlier, but including this path - simplifies multi-byte nop, as well as bndcl, bndcu, bndcn. */ - goto done; + op->n = rm; + if (op->unit != X86_OP_MMX) { + op->n |= REX_B(s); + } + return; } + /* Decompose an address. */ + int def_seg = R_DS; + int base = rm | REX_B(s); + int index = -1; + int scale = 0; + target_ulong disp = 0; + switch (s->aflag) { case MO_64: case MO_32: - havesib = 0; if (rm == 4) { int code = x86_ldub_code(env, s); scale = (code >> 6) & 3; @@ -2042,7 +2043,7 @@ static AddressParts decode_modrm_address(CPUX86State *env, DisasContext *s, index = -1; /* no index */ } base = (code & 7) | REX_B(s); - havesib = 1; + havesib = true; } switch (mod) { @@ -2127,26 +2128,9 @@ static AddressParts decode_modrm_address(CPUX86State *env, DisasContext *s, g_assert_not_reached(); } - done: - return (AddressParts){ def_seg, base, index, scale, disp }; -} - -static int decode_modrm(DisasContext *s, CPUX86State *env, - X86DecodedInsn *decode, X86DecodedOp *op) -{ - int modrm = get_modrm(s, env); - if ((modrm >> 6) == 3) { - op->n = (modrm & 7); - if (op->unit != X86_OP_MMX) { - op->n |= REX_B(s); - } - } else { - op->has_ea = true; - op->n = -1; - decode->mem = decode_modrm_address(env, s, get_modrm(s, env), - decode->e.vex_class == 12); - } - return modrm; + op->has_ea = true; + op->n = -1; + decode->mem = (AddressParts){ def_seg, base, index, scale, disp }; } static bool decode_op_size(DisasContext *s, X86OpEntry *e, X86OpSize size, MemOp *ot) From 5585d072c6be36848d6b2c20718bde1d29415b1e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 9 Jan 2026 12:29:29 +0100 Subject: [PATCH 11/13] target/i386/tcg: replace havesib variable with the SIB byte itself Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 662d1d707d..086a3bcec1 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2015,7 +2015,7 @@ static void decode_modrm(DisasContext *s, CPUX86State *env, int mod = (modrm >> 6) & 3; int rm = modrm & 7; bool is_vsib = decode->e.vex_class == 12; - bool havesib = false; + int sib = -1; if (mod == 3) { op->n = rm; @@ -2036,14 +2036,13 @@ static void decode_modrm(DisasContext *s, CPUX86State *env, case MO_64: case MO_32: if (rm == 4) { - int code = x86_ldub_code(env, s); - scale = (code >> 6) & 3; - index = ((code >> 3) & 7) | REX_X(s); + sib = x86_ldub_code(env, s); + scale = (sib >> 6) & 3; + index = ((sib >> 3) & 7) | REX_X(s); if (index == 4 && !is_vsib) { index = -1; /* no index */ } - base = (code & 7) | REX_B(s); - havesib = true; + base = (sib & 7) | REX_B(s); } switch (mod) { @@ -2051,7 +2050,7 @@ static void decode_modrm(DisasContext *s, CPUX86State *env, if ((base & 7) == 5) { base = -1; disp = (int32_t)x86_ldl_code(env, s); - if (CODE64(s) && !havesib) { + if (CODE64(s) && sib == -1) { base = -2; disp += s->pc + s->rip_offset; } From 2a52067b0678c911190593155f05b92e6b5025b9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 9 Jan 2026 15:42:59 +0100 Subject: [PATCH 12/13] target/i386/tcg: cleanup #ifdef TARGET_X86_64 32-bit TCG opcodes produced for the i386 target usually looks the same as 64-bit TCG opcodes produced for the x86_64. The special one that needs extensions is 32-bit TCG opcodes produced for the x86_64 target. Make all #ifdefs look the same, like this: case MO_32: #ifdef TARGET_X86_64 /* code using 32-bit opcodes */ case MO_64: #endif /* code using target_long opcodes */ default: g_assert_not_reached(); Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 18 ++++++++++++------ target/i386/tcg/translate.c | 11 ++++++----- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index f23c401189..ce636b6c56 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1236,8 +1236,8 @@ static void gen_ADCOX(DisasContext *s, X86DecodedInsn *decode, int cc_op) } switch (ot) { -#ifdef TARGET_X86_64 case MO_32: +#ifdef TARGET_X86_64 /* If TL is 64-bit just do everything in 64-bit arithmetic. */ tcg_gen_ext32u_tl(s->T0, s->T0); tcg_gen_ext32u_tl(s->T1, s->T1); @@ -1245,12 +1245,16 @@ static void gen_ADCOX(DisasContext *s, X86DecodedInsn *decode, int cc_op) tcg_gen_add_i64(s->T0, s->T0, carry_in); tcg_gen_shri_i64(*carry_out, s->T0, 32); break; + + case MO_64: #endif - default: zero = tcg_constant_tl(0); tcg_gen_add2_tl(s->T0, *carry_out, s->T0, zero, carry_in, zero); tcg_gen_add2_tl(s->T0, *carry_out, s->T0, *carry_out, s->T1, zero); break; + + default: + g_assert_not_reached(); } } @@ -1991,7 +1995,6 @@ static void gen_DIV(DisasContext *s, X86DecodedInsn *decode) case MO_16: gen_helper_divw_AX(tcg_env, s->T0); break; - default: case MO_32: gen_helper_divl_EAX(tcg_env, s->T0); break; @@ -2000,6 +2003,8 @@ static void gen_DIV(DisasContext *s, X86DecodedInsn *decode) gen_helper_divq_EAX(tcg_env, s->T0); break; #endif + default: + g_assert_not_reached(); } } @@ -2065,7 +2070,6 @@ static void gen_IDIV(DisasContext *s, X86DecodedInsn *decode) case MO_16: gen_helper_idivw_AX(tcg_env, s->T0); break; - default: case MO_32: gen_helper_idivl_EAX(tcg_env, s->T0); break; @@ -2074,6 +2078,8 @@ static void gen_IDIV(DisasContext *s, X86DecodedInsn *decode) gen_helper_idivq_EAX(tcg_env, s->T0); break; #endif + default: + g_assert_not_reached(); } } @@ -2876,7 +2882,7 @@ static inline void gen_pextr(DisasContext *s, X86DecodedInsn *decode, MemOp ot) tcg_gen_ld_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); break; default: - abort(); + g_assert_not_reached(); } } @@ -2923,7 +2929,7 @@ static inline void gen_pinsr(DisasContext *s, X86DecodedInsn *decode, MemOp ot) tcg_gen_st_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); break; default: - abort(); + g_assert_not_reached(); } } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 20aa94347b..7186517239 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -430,17 +430,15 @@ static TCGv gen_op_deposit_reg_v(DisasContext *s, MemOp ot, int reg, TCGv dest, tcg_gen_deposit_tl(dest, cpu_regs[reg], t0, 0, 16); break; case MO_32: - /* For x86_64, this sets the higher half of register to zero. - For i386, this is equivalent to a mov. */ +#ifdef TARGET_X86_64 dest = dest ? dest : cpu_regs[reg]; tcg_gen_ext32u_tl(dest, t0); break; -#ifdef TARGET_X86_64 case MO_64: +#endif dest = dest ? dest : cpu_regs[reg]; tcg_gen_mov_tl(dest, t0); break; -#endif default: g_assert_not_reached(); } @@ -1585,8 +1583,8 @@ static TCGv gen_shiftd_rm_T1(DisasContext *s, MemOp ot, tcg_gen_shri_i64(s->T0, s->T0, 32); } break; + case MO_64: #endif - default: hishift = tcg_temp_new(); tcg_gen_subi_tl(tmp, count, 1); if (is_right) { @@ -1615,6 +1613,9 @@ static TCGv gen_shiftd_rm_T1(DisasContext *s, MemOp ot, tcg_constant_tl(0), s->T1); tcg_gen_or_tl(s->T0, s->T0, s->T1); break; + + default: + g_assert_not_reached(); } return cc_src; From df8dfc3b6080713c2dbd7ef37c2c85a78e502cd6 Mon Sep 17 00:00:00 2001 From: Mohd Kashif Khan Date: Tue, 27 Jan 2026 02:39:10 +0530 Subject: [PATCH 13/13] rust/hpet: remove stale TODO comment The irq binding is already implemented in the following line, so the TODO comment is obsolete. Signed-off-by: Mohd Kashif Khan Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20260126210910.28889-1-kashif04x@gmail.com Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/device.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 4757bf5fd5..ebf715d399 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -725,7 +725,6 @@ fn set_cfg_reg(&self, regs: &mut HPETRegisters, shift: u32, len: u32, val: u64) self.irqs[0].lower(); self.irqs[RTC_ISA_IRQ].lower(); } else if deactivating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { - // TODO: Add irq binding: qemu_irq_lower(s->irqs[0]) self.irqs[0].lower(); self.pit_enabled.set(true); self.irqs[RTC_ISA_IRQ].set(self.rtc_irq_level.get() != 0);