rust: split "hwcore" crate

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20250827104147.717203-16-marcandre.lureau@redhat.com
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Marc-André Lureau 2025-09-08 12:49:56 +02:00 committed by Paolo Bonzini
parent ee4ffbf239
commit 5e588c9d08
29 changed files with 318 additions and 81 deletions

View file

@ -3518,6 +3518,7 @@ S: Maintained
F: rust/bql/
F: rust/chardev/
F: rust/common/
F: rust/hw/core/
F: rust/migration/
F: rust/qemu-api
F: rust/qemu-api-macros

17
rust/Cargo.lock generated
View file

@ -91,6 +91,7 @@ version = "0.1.0"
dependencies = [
"bql",
"common",
"hwcore",
"migration",
"qemu_api",
"qemu_api_macros",
@ -99,6 +100,20 @@ dependencies = [
"util",
]
[[package]]
name = "hwcore"
version = "0.1.0"
dependencies = [
"bql",
"chardev",
"common",
"migration",
"qemu_api_macros",
"qom",
"system",
"util",
]
[[package]]
name = "itertools"
version = "0.11.0"
@ -133,6 +148,7 @@ dependencies = [
"bql",
"chardev",
"common",
"hwcore",
"migration",
"qemu_api",
"qemu_api_macros",
@ -180,6 +196,7 @@ dependencies = [
"bql",
"chardev",
"common",
"hwcore",
"migration",
"qemu_api_macros",
"qom",

View file

@ -9,6 +9,7 @@ members = [
"qemu-api",
"qom",
"system",
"hw/core",
"hw/char/pl011",
"hw/timer/hpet",
"util",

64
rust/bindings/src/lib.rs Normal file
View file

@ -0,0 +1,64 @@
// 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,
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
)]
//! `bindgen`-generated declarations.
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
// SAFETY: these are implemented in C; the bindings need to assert that the
// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
// When bindings for character devices are introduced, this can be
// moved to the Opaque<> wrapper in src/chardev.rs.
unsafe impl Send for CharBackend {}
unsafe impl Sync for CharBackend {}
// SAFETY: this is a pure data struct
unsafe impl Send for CoalescedMemoryRange {}
unsafe impl Sync for CoalescedMemoryRange {}
// SAFETY: these are constants and vtables; the Send and Sync requirements
// are deferred to the unsafe callbacks that they contain
unsafe impl Send for MemoryRegionOps {}
unsafe impl Sync for MemoryRegionOps {}
unsafe impl Send for Property {}
unsafe impl Sync for Property {}
unsafe impl Send for TypeInfo {}
unsafe impl Sync for TypeInfo {}
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)
}
}

View file

@ -23,6 +23,7 @@ migration = { path = "../../../migration" }
qom = { path = "../../../qom" }
chardev = { path = "../../../chardev" }
system = { path = "../../../system" }
hwcore = { path = "../../../hw/core" }
qemu_api = { path = "../../../qemu-api" }
qemu_api_macros = { path = "../../../qemu-api-macros" }

View file

@ -16,6 +16,7 @@ _libpl011_rs = static_library(
qom_rs,
chardev_rs,
system_rs,
hwcore_rs,
],
)

View file

@ -7,16 +7,14 @@ use std::{ffi::CStr, mem::size_of};
use bql::BqlRefCell;
use chardev::{CharBackend, Chardev, Event};
use common::{static_assert, uninit_field_mut};
use hwcore::{
Clock, ClockEvent, DeviceImpl, DeviceMethods, DeviceState, IRQState, InterruptSource,
ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
};
use migration::{
self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of,
vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder,
};
use qemu_api::{
irq::{IRQState, InterruptSource},
prelude::*,
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, ResetType, ResettablePhasesImpl},
sysbus::{SysBusDevice, SysBusDeviceImpl},
};
use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
use util::{log::Log, log_mask_ln};

26
rust/hw/core/Cargo.toml Normal file
View file

@ -0,0 +1,26 @@
[package]
name = "hwcore"
version = "0.1.0"
description = "Rust bindings for QEMU/hwcore"
resolver = "2"
publish = false
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies]
common = { path = "../../common" }
bql = { path = "../../bql" }
qom = { path = "../../qom" }
chardev = { path = "../../chardev" }
migration = { path = "../../migration" }
system = { path = "../../system" }
util = { path = "../../util" }
qemu_api_macros = { path = "../../qemu-api-macros" }
[lints]
workspace = true

1
rust/hw/core/build.rs Symbolic link
View file

@ -0,0 +1 @@
../../util/build.rs

80
rust/hw/core/meson.build Normal file
View file

@ -0,0 +1,80 @@
_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,
)
_hwcore_rs = static_library(
'hwcore',
structured_sources(
[
'src/lib.rs',
'src/bindings.rs',
'src/irq.rs',
'src/qdev.rs',
'src/sysbus.rs',
],
{'.': _hwcore_bindings_inc_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: [qemu_api_macros, common_rs],
)
hwcore_rs = declare_dependency(link_with: [_hwcore_rs],
dependencies: [qom_rs, hwcore])
test('rust-hwcore-rs-integration',
executable(
'rust-hwcore-rs-integration',
files('tests/tests.rs'),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_args: ['--test'],
install: false,
dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, qemu_api_macros, util_rs]),
args: [
'--test', '--test-threads', '1',
'--format', 'pretty',
],
protocol: 'rust',
suite: ['unit', 'rust'])

View file

@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#![allow(
dead_code,
improper_ctypes_definitions,
improper_ctypes,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unnecessary_transmutes,
unsafe_op_in_unsafe_fn,
clippy::pedantic,
clippy::restriction,
clippy::style,
clippy::missing_const_for_fn,
clippy::ptr_offset_with_cast,
clippy::useless_transmute,
clippy::missing_safety_doc,
clippy::too_many_arguments
)]
use chardev::bindings::Chardev;
use common::Zeroable;
use migration::bindings::VMStateDescription;
use qom::bindings::ObjectClass;
use system::bindings::MemoryRegion;
use util::bindings::Error;
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
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 {}

14
rust/hw/core/src/lib.rs Normal file
View file

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pub use qom;
pub mod bindings;
mod irq;
pub use irq::*;
mod qdev;
pub use qdev::*;
mod sysbus;
pub use sysbus::*;

View file

@ -9,13 +9,13 @@ use std::{
ptr::NonNull,
};
pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
use chardev::Chardev;
use common::{callbacks::FnCall, Opaque};
use migration::{impl_vmstate_c_struct, VMStateDescription};
use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit};
use util::{Error, Result};
pub use crate::bindings::{ClockEvent, DeviceClass, Property, ResetType};
use crate::{
bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
irq::InterruptSource,

View file

@ -5,16 +5,11 @@
use std::{ffi::CStr, ptr::addr_of};
use bql::BqlCell;
use hwcore::{DeviceImpl, DeviceState, ResettablePhasesImpl, SysBusDevice};
use migration::{VMStateDescription, VMStateDescriptionBuilder};
use qemu_api::{
qdev::{DeviceImpl, DeviceState, ResettablePhasesImpl},
sysbus::SysBusDevice,
};
use qom::{prelude::*, ObjectImpl, ParentField};
use util::bindings::{module_call_init, module_init_type};
mod vmstate_tests;
// Test that macros can compile.
pub const VMSTATE: VMStateDescription<DummyState> = VMStateDescriptionBuilder::<DummyState>::new()
.name(c"name")

32
rust/hw/core/wrapper.h Normal file
View file

@ -0,0 +1,32 @@
/* 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/sysbus.h"
#include "hw/clock.h"
#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/irq.h"

View file

@ -19,6 +19,7 @@ qom = { path = "../../../qom" }
system = { path = "../../../system" }
qemu_api = { path = "../../../qemu-api" }
qemu_api_macros = { path = "../../../qemu-api-macros" }
hwcore = { path = "../../../hw/core" }
[lints]
workspace = true

View file

@ -12,6 +12,7 @@ _libhpet_rs = static_library(
qemu_api_macros,
qom_rs,
system_rs,
hwcore_rs,
],
)

View file

@ -12,17 +12,15 @@ use std::{
use bql::{BqlCell, BqlRefCell};
use common::{bitops::IntegerExt, uninit_field_mut};
use hwcore::{
bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize},
declare_properties, define_property, DeviceImpl, DeviceMethods, DeviceState, InterruptSource,
Property, ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
};
use migration::{
self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
VMStateDescription, VMStateDescriptionBuilder,
};
use qemu_api::{
bindings::{qdev_prop_bit, qdev_prop_bool, qdev_prop_uint32, qdev_prop_usize},
irq::InterruptSource,
prelude::*,
qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
sysbus::{SysBusDevice, SysBusDeviceImpl},
};
use qom::{prelude::*, ObjectImpl, ParentField, ParentInit};
use system::{
bindings::{address_space_memory, address_space_stl_le, hwaddr},
@ -904,9 +902,9 @@ impl ObjectImpl for HPETState {
}
// TODO: Make these properties user-configurable!
qemu_api::declare_properties! {
declare_properties! {
HPET_PROPERTIES,
qemu_api::define_property!(
define_property!(
c"timers",
HPETState,
num_timers,
@ -914,7 +912,7 @@ qemu_api::declare_properties! {
u8,
default = HPET_MIN_TIMERS
),
qemu_api::define_property!(
define_property!(
c"msi",
HPETState,
flags,
@ -923,7 +921,7 @@ qemu_api::declare_properties! {
bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8,
default = false,
),
qemu_api::define_property!(
define_property!(
c"hpet-intcap",
HPETState,
int_route_cap,
@ -931,7 +929,7 @@ qemu_api::declare_properties! {
u32,
default = 0
),
qemu_api::define_property!(
define_property!(
c"hpet-offset-saved",
HPETState,
hpet_offset_saved,
@ -1004,8 +1002,8 @@ const VMSTATE_HPET: VMStateDescription<HPETState> =
.build();
// SAFETY: HPET_PROPERTIES is a valid Property array constructed with the
// qemu_api::declare_properties macro.
unsafe impl qemu_api::qdev::DevicePropertiesImpl for HPETState {
// hwcore::declare_properties macro.
unsafe impl hwcore::DevicePropertiesImpl for HPETState {
const PROPERTIES: &'static [Property] = &HPET_PROPERTIES;
}

View file

@ -31,6 +31,7 @@ subdir('bql')
subdir('qom')
subdir('system')
subdir('chardev')
subdir('hw/core')
subdir('qemu-api')
subdir('hw')

View file

@ -272,24 +272,24 @@ fn derive_device_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream
},
)?;
let field_ty = field.ty.clone();
let qdev_prop = quote! { <#field_ty as ::qemu_api::qdev::QDevProp>::VALUE };
let qdev_prop = quote! { <#field_ty as ::hwcore::QDevProp>::VALUE };
let set_default = defval.is_some();
let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 }));
properties_expanded.push(quote! {
::qemu_api::bindings::Property {
::hwcore::bindings::Property {
name: ::std::ffi::CStr::as_ptr(#prop_name),
info: #qdev_prop ,
offset: ::core::mem::offset_of!(#name, #field_name) as isize,
set_default: #set_default,
defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: #defval as u64 },
defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 },
..::common::Zeroable::ZERO
}
});
}
Ok(quote_spanned! {input.span() =>
unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for #name {
const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
unsafe impl ::hwcore::DevicePropertiesImpl for #name {
const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
#(#properties_expanded),*
];
}

View file

@ -100,14 +100,14 @@ fn test_derive_device() {
}
},
quote! {
unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
::qemu_api::bindings::Property {
unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
::hwcore::bindings::Property {
name: ::std::ffi::CStr::as_ptr(c"migrate_clock"),
info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
info: <bool as ::hwcore::QDevProp>::VALUE,
offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
set_default: true,
defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
..::common::Zeroable::ZERO
}
];
@ -127,14 +127,14 @@ fn test_derive_device() {
}
},
quote! {
unsafe impl ::qemu_api::qdev::DevicePropertiesImpl for DummyState {
const PROPERTIES: &'static [::qemu_api::bindings::Property] = &[
::qemu_api::bindings::Property {
unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
::hwcore::bindings::Property {
name: ::std::ffi::CStr::as_ptr(c"migrate-clk"),
info: <bool as ::qemu_api::qdev::QDevProp>::VALUE,
info: <bool as ::hwcore::QDevProp>::VALUE,
offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
set_default: true,
defval: ::qemu_api::bindings::Property__bindgen_ty_1 { u: true as u64 },
defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
..::common::Zeroable::ZERO
}
];

View file

@ -16,6 +16,7 @@ rust-version.workspace = true
[dependencies]
common = { path = "../common" }
chardev = { path = "../chardev" }
hwcore = { path = "../hw/core" }
migration = { path = "../migration" }
util = { path = "../util" }
bql = { path = "../bql" }

View file

@ -3,22 +3,12 @@ _qemu_api_cfg = run_command(rustc_args,
capture: true, check: true).stdout().strip().splitlines()
c_enums = [
'DeviceCategory',
'GpioPolarity',
'MachineInitPhase',
'MemoryDeviceInfoKind',
'ResetType',
]
_qemu_api_bindgen_args = []
foreach enum : c_enums
_qemu_api_bindgen_args += ['--rustified-enum', enum]
endforeach
c_bitfields = [
'ClockEvent',
]
foreach enum : c_bitfields
_qemu_api_bindgen_args += ['--bitfield-enum', enum]
endforeach
blocked_type = [
'Chardev',
@ -55,17 +45,14 @@ _qemu_api_rs = static_library(
[
'src/lib.rs',
'src/bindings.rs',
'src/irq.rs',
'src/prelude.rs',
'src/qdev.rs',
'src/sysbus.rs',
],
{'.' : _qemu_api_bindings_inc_rs},
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _qemu_api_cfg,
dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, libc_rs, migration_rs, qemu_api_macros,
dependencies: [anyhow_rs, bql_rs, chardev_rs, common_rs, foreign_rs, hwcore_rs, libc_rs, migration_rs, qemu_api_macros,
qom_rs, system_rs, util_rs, hwcore],
)
@ -75,7 +62,7 @@ qemu_api_rs = declare_dependency(link_with: [_qemu_api_rs],
test('rust-qemu-api-integration',
executable(
'rust-qemu-api-integration',
files('tests/tests.rs', 'tests/vmstate_tests.rs'),
files('tests/vmstate_tests.rs'),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_args: ['--test'],
install: false,

View file

@ -21,7 +21,6 @@
//! `bindgen`-generated declarations.
use chardev::bindings::Chardev;
use common::Zeroable;
use migration::bindings::VMStateDescription;
use qom::bindings::ObjectClass;
use system::bindings::{device_endian, MemTxAttrs, MemoryRegion};
@ -32,12 +31,3 @@ include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
unsafe impl Send for Property {}
unsafe impl Sync for Property {}
unsafe impl Send for TypeInfo {}
unsafe impl Sync for TypeInfo {}
unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {}
unsafe impl Zeroable for crate::bindings::Property {}

View file

@ -13,10 +13,6 @@ pub mod bindings;
#[rustfmt::skip]
pub mod prelude;
pub mod irq;
pub mod qdev;
pub mod sysbus;
// Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this
// crate).
extern crate self as qemu_api;

View file

@ -3,7 +3,3 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//! Commonly used traits and types for QEMU.
pub use crate::qdev::DeviceMethods;
pub use crate::sysbus::SysBusDeviceMethods;

View file

@ -49,11 +49,5 @@ typedef enum memory_order {
#include "qemu/osdep.h"
#include "qemu-io.h"
#include "hw/sysbus.h"
#include "hw/clock.h"
#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/irq.h"
#include "exec/memattrs.h"
#include "hw/char/pl011.h"