@@ -13,6 +13,7 @@
bindings::{self, *},
c_str,
definitions::ObjectImpl,
+ with_offsets,
};
use crate::{
@@ -28,50 +29,52 @@
/// QEMU sourced constant.
pub const PL011_FIFO_DEPTH: usize = 16_usize;
-#[repr(C)]
-#[derive(Debug, qemu_api_macros::Object)]
-/// PL011 Device Model in QEMU
-pub struct PL011State {
- pub parent_obj: SysBusDevice,
- pub iomem: MemoryRegion,
- #[doc(alias = "fr")]
- pub flags: registers::Flags,
- #[doc(alias = "lcr")]
- pub line_control: registers::LineControl,
- #[doc(alias = "rsr")]
- pub receive_status_error_clear: registers::ReceiveStatusErrorClear,
- #[doc(alias = "cr")]
- pub control: registers::Control,
- pub dmacr: u32,
- pub int_enabled: u32,
- pub int_level: u32,
- pub read_fifo: [u32; PL011_FIFO_DEPTH],
- pub ilpr: u32,
- pub ibrd: u32,
- pub fbrd: u32,
- pub ifl: u32,
- pub read_pos: usize,
- pub read_count: usize,
- pub read_trigger: usize,
- #[doc(alias = "chr")]
- pub char_backend: CharBackend,
- /// QEMU interrupts
- ///
- /// ```text
- /// * sysbus MMIO region 0: device registers
- /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line)
- /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line)
- /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line)
- /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line)
- /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line)
- /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
- /// ```
- #[doc(alias = "irq")]
- pub interrupts: [qemu_irq; 6usize],
- #[doc(alias = "clk")]
- pub clock: NonNull<Clock>,
- #[doc(alias = "migrate_clk")]
- pub migrate_clock: bool,
+with_offsets! {
+ #[repr(C)]
+ #[derive(Debug, qemu_api_macros::Object)]
+ /// PL011 Device Model in QEMU
+ pub struct PL011State {
+ pub parent_obj: SysBusDevice,
+ pub iomem: MemoryRegion,
+ #[doc(alias = "fr")]
+ pub flags: registers::Flags,
+ #[doc(alias = "lcr")]
+ pub line_control: registers::LineControl,
+ #[doc(alias = "rsr")]
+ pub receive_status_error_clear: registers::ReceiveStatusErrorClear,
+ #[doc(alias = "cr")]
+ pub control: registers::Control,
+ pub dmacr: u32,
+ pub int_enabled: u32,
+ pub int_level: u32,
+ pub read_fifo: [u32; PL011_FIFO_DEPTH],
+ pub ilpr: u32,
+ pub ibrd: u32,
+ pub fbrd: u32,
+ pub ifl: u32,
+ pub read_pos: usize,
+ pub read_count: usize,
+ pub read_trigger: usize,
+ #[doc(alias = "chr")]
+ pub char_backend: CharBackend,
+ /// QEMU interrupts
+ ///
+ /// ```text
+ /// * sysbus MMIO region 0: device registers
+ /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line)
+ /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line)
+ /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line)
+ /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line)
+ /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line)
+ /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
+ /// ```
+ #[doc(alias = "irq")]
+ pub interrupts: [qemu_irq; 6usize],
+ #[doc(alias = "clk")]
+ pub clock: NonNull<Clock>,
+ #[doc(alias = "migrate_clk")]
+ pub migrate_clock: bool,
+ }
}
impl ObjectImpl for PL011State {
@@ -1,3 +1,9 @@
+_qemu_api_cfg = ['--cfg', 'MESON']
+# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
+if rustc.version().version_compare('>=1.77.0')
+ _qemu_api_cfg += ['--cfg', 'has_offset_of']
+endif
+
_qemu_api_rs = static_library(
'qemu_api',
structured_sources(
@@ -6,16 +12,14 @@ _qemu_api_rs = static_library(
'src/c_str.rs',
'src/definitions.rs',
'src/device_class.rs',
+ 'src/offset_of.rs',
'src/tests.rs',
],
{'.' : bindings_rs},
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
- rust_args: [
- '--cfg', 'MESON',
- # '--cfg', 'feature="allocator"',
- ],
+ rust_args: _qemu_api_cfg,
dependencies: [
qemu_api_macros,
],
@@ -26,7 +26,7 @@ macro_rules! device_class_init {
#[macro_export]
macro_rules! define_property {
- ($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr, default = $defval:expr$(,)*) => {
+ ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr, default = $defval:expr$(,)*) => {
$crate::bindings::Property {
name: {
#[used]
@@ -34,7 +34,7 @@ macro_rules! define_property {
_TEMP.as_ptr()
},
info: $prop,
- offset: ::core::mem::offset_of!($state, $field)
+ offset: $crate::offset_of!($state, $field)
.try_into()
.expect("Could not fit offset value to type"),
bitnr: 0,
@@ -47,7 +47,7 @@ macro_rules! define_property {
link_type: ::core::ptr::null(),
}
};
- ($name:expr, $state:ty, $field:expr, $prop:expr, $type:expr$(,)*) => {
+ ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr$(,)*) => {
$crate::bindings::Property {
name: {
#[used]
@@ -55,7 +55,7 @@ macro_rules! define_property {
_TEMP.as_ptr()
},
info: $prop,
- offset: ::core::mem::offset_of!($state, $field)
+ offset: $crate::offset_of!($state, $field)
.try_into()
.expect("Could not fit offset value to type"),
bitnr: 0,
@@ -30,6 +30,7 @@ unsafe impl Sync for bindings::VMStateDescription {}
pub mod c_str;
pub mod definitions;
pub mod device_class;
+pub mod offset_of;
#[cfg(test)]
mod tests;
@@ -167,3 +168,6 @@ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
}
}
}
+
+#[cfg(has_offset_of)]
+pub use std::mem::offset_of;
new file mode 100644
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: MIT
+
+/// This macro provides the same functionality as `core::mem::offset_of`,
+/// except that only one level of field access is supported. The declaration
+/// of the struct must be wrapped with `with_offsets! { }`.
+///
+/// It is needed because `offset_of!` was only stabilized in Rust 1.77.
+#[cfg(not(has_offset_of))]
+#[macro_export]
+macro_rules! offset_of {
+ ($Container:ty, $field:ident) => {
+ <$Container>::offset_to.$field
+ };
+}
+
+/// A wrapper for struct declarations, that allows using `offset_of!` in
+/// versions of Rust prior to 1.77
+#[macro_export]
+macro_rules! with_offsets {
+ // source: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df
+ // used under MIT license with permission of Yandros aka Daniel Henry-Mantilla
+ (
+ #[repr(C)]
+ $(#[$struct_meta:meta])*
+ $struct_vis:vis
+ struct $StructName:ident {
+ $(
+ $(#[$field_meta:meta])*
+ $field_vis:vis
+ $field_name:ident : $field_ty:ty
+ ),*
+ $(,)?
+ }
+ ) => (
+ #[repr(C)]
+ $(#[$struct_meta])*
+ $struct_vis
+ struct $StructName {
+ $(
+ $(#[$field_meta])*
+ $field_vis
+ $field_name : $field_ty ,
+ )*
+ }
+
+ #[cfg(not(has_offset_of))]
+ #[allow(nonstandard_style)]
+ const _: () = {
+ pub
+ struct StructOffsets {
+ $(
+ $field_vis
+ $field_name: usize,
+ )*
+ }
+ struct Helper;
+ impl $StructName {
+ pub
+ const offset_to: StructOffsets = StructOffsets {
+ $(
+ $field_name: Helper::$field_name,
+ )*
+ };
+ }
+ const END_OF_PREV_FIELD: usize = 0;
+ $crate::with_offsets! {
+ @names [ $($field_name)* ]
+ @tys [ $($field_ty ,)*]
+ }
+ };
+ );
+
+ (
+ @names []
+ @tys []
+ ) => ();
+
+ (
+ @names [$field_name:ident $($other_names:tt)*]
+ @tys [$field_ty:ty , $($other_tys:tt)*]
+ ) => (
+ impl Helper {
+ const $field_name: usize = {
+ let align =
+ std::mem::align_of::<$field_ty>()
+ ;
+ let trail =
+ END_OF_PREV_FIELD % align
+ ;
+ 0 + END_OF_PREV_FIELD
+ + (align - trail)
+ * [1, 0][(trail == 0) as usize]
+ };
+ }
+ const _: () = {
+ const END_OF_PREV_FIELD: usize =
+ Helper::$field_name +
+ std::mem::size_of::<$field_ty>()
+ ;
+ $crate::with_offsets! {
+ @names [$($other_names)*]
+ @tys [$($other_tys)*]
+ }
+ };
+ );
+}
@@ -4,6 +4,7 @@
use crate::{
bindings::*, c_str, declare_properties, define_property, device_class_init, vm_state_description,
+ with_offsets,
};
#[test]
@@ -15,10 +16,12 @@ fn test_device_decl_macros() {
unmigratable: true,
}
- #[repr(C)]
- pub struct DummyState {
- pub char_backend: CharBackend,
- pub migrate_clock: bool,
+ with_offsets! {
+ #[repr(C)]
+ pub struct DummyState {
+ pub char_backend: CharBackend,
+ pub migrate_clock: bool,
+ }
}
declare_properties! {
offset_of! was stabilized in Rust 1.77.0. Use an alternative implemenation that was found on the Rust forums, and whose author agreed to license as MIT for use in QEMU. The alternative allows only one level of field access, but apart from this can be used just by replacing core::mem::offset_of! with qemu_api::offset_of!. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- rust/hw/char/pl011/src/device.rs | 91 ++++++++++++------------- rust/qemu-api/meson.build | 12 ++-- rust/qemu-api/src/device_class.rs | 8 +-- rust/qemu-api/src/lib.rs | 4 ++ rust/qemu-api/src/offset_of.rs | 106 ++++++++++++++++++++++++++++++ rust/qemu-api/src/tests.rs | 11 ++-- 6 files changed, 176 insertions(+), 56 deletions(-) create mode 100644 rust/qemu-api/src/offset_of.rs