aboutsummaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
Diffstat (limited to 'rust')
-rw-r--r--rust/Cargo.lock25
-rw-r--r--rust/Cargo.toml3
-rw-r--r--rust/bits/Cargo.toml19
-rw-r--r--rust/bits/meson.build16
-rw-r--r--rust/bits/src/lib.rs443
-rw-r--r--rust/clippy.toml3
-rw-r--r--rust/hw/char/pl011/Cargo.toml1
-rw-r--r--rust/hw/char/pl011/meson.build13
-rw-r--r--rust/hw/char/pl011/src/device.rs112
-rw-r--r--rust/hw/char/pl011/src/registers.rs39
-rw-r--r--rust/hw/timer/hpet/src/device.rs (renamed from rust/hw/timer/hpet/src/hpet.rs)122
-rw-r--r--rust/hw/timer/hpet/src/fw_cfg.rs9
-rw-r--r--rust/hw/timer/hpet/src/lib.rs4
-rw-r--r--rust/meson.build39
-rw-r--r--rust/qemu-api-macros/meson.build14
-rw-r--r--rust/qemu-api-macros/src/bits.rs229
-rw-r--r--rust/qemu-api-macros/src/lib.rs59
-rw-r--r--rust/qemu-api/Cargo.toml2
-rw-r--r--rust/qemu-api/build.rs2
-rw-r--r--rust/qemu-api/meson.build82
-rw-r--r--rust/qemu-api/src/bindings.rs1
-rw-r--r--rust/qemu-api/src/bitops.rs2
-rw-r--r--rust/qemu-api/src/cell.rs22
-rw-r--r--rust/qemu-api/src/error.rs416
-rw-r--r--rust/qemu-api/src/lib.rs5
-rw-r--r--rust/qemu-api/src/log.rs73
-rw-r--r--rust/qemu-api/src/memory.rs12
-rw-r--r--rust/qemu-api/src/prelude.rs2
-rw-r--r--rust/qemu-api/src/qdev.rs61
-rw-r--r--rust/qemu-api/src/qom.rs190
-rw-r--r--rust/qemu-api/src/timer.rs2
-rw-r--r--rust/qemu-api/src/uninit.rs85
-rw-r--r--rust/qemu-api/src/vmstate.rs2
-rw-r--r--rust/qemu-api/tests/vmstate_tests.rs2
-rw-r--r--rust/qemu-api/wrapper.h (renamed from rust/wrapper.h)3
35 files changed, 1851 insertions, 263 deletions
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 13d580c..b785c71 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -3,6 +3,12 @@
version = 3
[[package]]
+name = "anyhow"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
name = "arbitrary-int"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -32,12 +38,28 @@ dependencies = [
]
[[package]]
+name = "bits"
+version = "0.1.0"
+dependencies = [
+ "qemu_api_macros",
+]
+
+[[package]]
name = "either"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
[[package]]
+name = "foreign"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "hpet"
version = "0.1.0"
dependencies = [
@@ -66,6 +88,7 @@ version = "0.1.0"
dependencies = [
"bilge",
"bilge-impl",
+ "bits",
"qemu_api",
"qemu_api_macros",
]
@@ -106,6 +129,8 @@ dependencies = [
name = "qemu_api"
version = "0.1.0"
dependencies = [
+ "anyhow",
+ "foreign",
"libc",
"qemu_api_macros",
]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index d9faeec..0868e1b 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
+ "bits",
"qemu-api-macros",
"qemu-api",
"hw/char/pl011",
@@ -63,10 +64,10 @@ ignored_unit_patterns = "deny"
implicit_clone = "deny"
macro_use_imports = "deny"
missing_safety_doc = "deny"
-multiple_crate_versions = "deny"
mut_mut = "deny"
needless_bitwise_bool = "deny"
needless_pass_by_ref_mut = "deny"
+needless_update = "deny"
no_effect_underscore_binding = "deny"
option_option = "deny"
or_fun_call = "deny"
diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml
new file mode 100644
index 0000000..1ff38a4
--- /dev/null
+++ b/rust/bits/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "bits"
+version = "0.1.0"
+authors = ["Paolo Bonzini <pbonzini@redhat.com>"]
+description = "const-friendly bit flags"
+resolver = "2"
+publish = false
+
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+qemu_api_macros = { path = "../qemu-api-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/bits/meson.build b/rust/bits/meson.build
new file mode 100644
index 0000000..2a41e13
--- /dev/null
+++ b/rust/bits/meson.build
@@ -0,0 +1,16 @@
+_bits_rs = static_library(
+ 'bits',
+ 'src/lib.rs',
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ dependencies: [qemu_api_macros],
+)
+
+bits_rs = declare_dependency(link_with: _bits_rs)
+
+rust.test('rust-bits-tests', _bits_rs,
+ suite: ['unit', 'rust'])
+
+rust.doctest('rust-bits-doctests', _bits_rs,
+ dependencies: bits_rs,
+ suite: ['doc', 'rust'])
diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs
new file mode 100644
index 0000000..d485d6b
--- /dev/null
+++ b/rust/bits/src/lib.rs
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later
+
+/// # Definition entry point
+///
+/// Define a struct with a single field of type $type. Include public constants
+/// for each element listed in braces.
+///
+/// The unnamed element at the end, if present, can be used to enlarge the set
+/// of valid bits. Bits that are valid but not listed are treated normally for
+/// the purpose of arithmetic operations, and are printed with their hexadecimal
+/// value.
+///
+/// The struct implements the following traits: [`BitAnd`](std::ops::BitAnd),
+/// [`BitOr`](std::ops::BitOr), [`BitXor`](std::ops::BitXor),
+/// [`Not`](std::ops::Not), [`Sub`](std::ops::Sub); [`Debug`](std::fmt::Debug),
+/// [`Display`](std::fmt::Display), [`Binary`](std::fmt::Binary),
+/// [`Octal`](std::fmt::Octal), [`LowerHex`](std::fmt::LowerHex),
+/// [`UpperHex`](std::fmt::UpperHex); [`From`]`<type>`/[`Into`]`<type>` where
+/// type is the type specified in the definition.
+///
+/// ## Example
+///
+/// ```
+/// # use bits::bits;
+/// bits! {
+/// pub struct Colors(u8) {
+/// BLACK = 0,
+/// RED = 1,
+/// GREEN = 1 << 1,
+/// BLUE = 1 << 2,
+/// WHITE = (1 << 0) | (1 << 1) | (1 << 2),
+/// }
+/// }
+/// ```
+///
+/// ```
+/// # use bits::bits;
+/// # bits! { pub struct Colors(u8) { BLACK = 0, RED = 1, GREEN = 1 << 1, BLUE = 1 << 2, } }
+///
+/// bits! {
+/// pub struct Colors8(u8) {
+/// BLACK = 0,
+/// RED = 1,
+/// GREEN = 1 << 1,
+/// BLUE = 1 << 2,
+/// WHITE = (1 << 0) | (1 << 1) | (1 << 2),
+///
+/// _ = 255,
+/// }
+/// }
+///
+/// // The previously defined struct ignores bits not explicitly defined.
+/// assert_eq!(
+/// Colors::from(255).into_bits(),
+/// (Colors::RED | Colors::GREEN | Colors::BLUE).into_bits()
+/// );
+///
+/// // Adding "_ = 255" makes it retain other bits as well.
+/// assert_eq!(Colors8::from(255).into_bits(), 255);
+///
+/// // all() does not include the additional bits, valid_bits() does
+/// assert_eq!(Colors8::all().into_bits(), Colors::all().into_bits());
+/// assert_eq!(Colors8::valid_bits().into_bits(), 255);
+/// ```
+///
+/// # Evaluation entry point
+///
+/// Return a constant corresponding to the boolean expression `$expr`.
+/// Identifiers in the expression correspond to values defined for the
+/// type `$type`. Supported operators are `!` (unary), `-`, `&`, `^`, `|`.
+///
+/// ## Examples
+///
+/// ```
+/// # use bits::bits;
+/// bits! {
+/// pub struct Colors(u8) {
+/// BLACK = 0,
+/// RED = 1,
+/// GREEN = 1 << 1,
+/// BLUE = 1 << 2,
+/// // same as "WHITE = 7",
+/// WHITE = bits!(Self as u8: RED | GREEN | BLUE),
+/// }
+/// }
+///
+/// let rgb = bits! { Colors: RED | GREEN | BLUE };
+/// assert_eq!(rgb, Colors::WHITE);
+/// ```
+#[macro_export]
+macro_rules! bits {
+ {
+ $(#[$struct_meta:meta])*
+ $struct_vis:vis struct $struct_name:ident($field_vis:vis $type:ty) {
+ $($(#[$const_meta:meta])* $const:ident = $val:expr),+
+ $(,_ = $mask:expr)?
+ $(,)?
+ }
+ } => {
+ $(#[$struct_meta])*
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ #[repr(transparent)]
+ $struct_vis struct $struct_name($field_vis $type);
+
+ impl $struct_name {
+ $( #[allow(dead_code)] $(#[$const_meta])*
+ pub const $const: $struct_name = $struct_name($val); )+
+
+ #[doc(hidden)]
+ const VALID__: $type = $( Self::$const.0 )|+ $(|$mask)?;
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn empty() -> Self {
+ Self(0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn all() -> Self {
+ Self($( Self::$const.0 )|+)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn valid_bits() -> Self {
+ Self(Self::VALID__)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn valid(val: $type) -> bool {
+ (val & !Self::VALID__) == 0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn any_set(self, mask: Self) -> bool {
+ (self.0 & mask.0) != 0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn all_set(self, mask: Self) -> bool {
+ (self.0 & mask.0) == mask.0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn none_set(self, mask: Self) -> bool {
+ (self.0 & mask.0) == 0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn from_bits(value: $type) -> Self {
+ $struct_name(value)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn into_bits(self) -> $type {
+ self.0
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub fn set(&mut self, rhs: Self) {
+ self.0 |= rhs.0;
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub fn clear(&mut self, rhs: Self) {
+ self.0 &= !rhs.0;
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub fn toggle(&mut self, rhs: Self) {
+ self.0 ^= rhs.0;
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn intersection(self, rhs: Self) -> Self {
+ $struct_name(self.0 & rhs.0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn difference(self, rhs: Self) -> Self {
+ $struct_name(self.0 & !rhs.0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn symmetric_difference(self, rhs: Self) -> Self {
+ $struct_name(self.0 ^ rhs.0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn union(self, rhs: Self) -> Self {
+ $struct_name(self.0 | rhs.0)
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn invert(self) -> Self {
+ $struct_name(self.0 ^ Self::VALID__)
+ }
+ }
+
+ impl ::std::fmt::Binary for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ // If no width, use the highest valid bit
+ let width = f.width().unwrap_or((Self::VALID__.ilog2() + 1) as usize);
+ write!(f, "{:0>width$.precision$b}", self.0,
+ width = width,
+ precision = f.precision().unwrap_or(width))
+ }
+ }
+
+ impl ::std::fmt::LowerHex for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ <$type as ::std::fmt::LowerHex>::fmt(&self.0, f)
+ }
+ }
+
+ impl ::std::fmt::Octal for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ <$type as ::std::fmt::Octal>::fmt(&self.0, f)
+ }
+ }
+
+ impl ::std::fmt::UpperHex for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ <$type as ::std::fmt::UpperHex>::fmt(&self.0, f)
+ }
+ }
+
+ impl ::std::fmt::Debug for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ write!(f, "{}({})", stringify!($struct_name), self)
+ }
+ }
+
+ impl ::std::fmt::Display for $struct_name {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ use ::std::fmt::Display;
+ let mut first = true;
+ let mut left = self.0;
+ $(if Self::$const.0.is_power_of_two() && (self & Self::$const).0 != 0 {
+ if first { first = false } else { Display::fmt(&'|', f)?; }
+ Display::fmt(stringify!($const), f)?;
+ left -= Self::$const.0;
+ })+
+ if first {
+ Display::fmt(&'0', f)
+ } else if left != 0 {
+ write!(f, "|{left:#x}")
+ } else {
+ Ok(())
+ }
+ }
+ }
+
+ impl ::std::cmp::PartialEq<$type> for $struct_name {
+ fn eq(&self, rhs: &$type) -> bool {
+ self.0 == *rhs
+ }
+ }
+
+ impl ::std::ops::BitAnd<$struct_name> for &$struct_name {
+ type Output = $struct_name;
+ fn bitand(self, rhs: $struct_name) -> Self::Output {
+ $struct_name(self.0 & rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitAndAssign<$struct_name> for $struct_name {
+ fn bitand_assign(&mut self, rhs: $struct_name) {
+ self.0 = self.0 & rhs.0
+ }
+ }
+
+ impl ::std::ops::BitXor<$struct_name> for &$struct_name {
+ type Output = $struct_name;
+ fn bitxor(self, rhs: $struct_name) -> Self::Output {
+ $struct_name(self.0 ^ rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitXorAssign<$struct_name> for $struct_name {
+ fn bitxor_assign(&mut self, rhs: $struct_name) {
+ self.0 = self.0 ^ rhs.0
+ }
+ }
+
+ impl ::std::ops::BitOr<$struct_name> for &$struct_name {
+ type Output = $struct_name;
+ fn bitor(self, rhs: $struct_name) -> Self::Output {
+ $struct_name(self.0 | rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitOrAssign<$struct_name> for $struct_name {
+ fn bitor_assign(&mut self, rhs: $struct_name) {
+ self.0 = self.0 | rhs.0
+ }
+ }
+
+ impl ::std::ops::Sub<$struct_name> for &$struct_name {
+ type Output = $struct_name;
+ fn sub(self, rhs: $struct_name) -> Self::Output {
+ $struct_name(self.0 & !rhs.0)
+ }
+ }
+
+ impl ::std::ops::SubAssign<$struct_name> for $struct_name {
+ fn sub_assign(&mut self, rhs: $struct_name) {
+ self.0 = self.0 - rhs.0
+ }
+ }
+
+ impl ::std::ops::Not for &$struct_name {
+ type Output = $struct_name;
+ fn not(self) -> Self::Output {
+ $struct_name(self.0 ^ $struct_name::VALID__)
+ }
+ }
+
+ impl ::std::ops::BitAnd<$struct_name> for $struct_name {
+ type Output = Self;
+ fn bitand(self, rhs: Self) -> Self::Output {
+ $struct_name(self.0 & rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitXor<$struct_name> for $struct_name {
+ type Output = Self;
+ fn bitxor(self, rhs: Self) -> Self::Output {
+ $struct_name(self.0 ^ rhs.0)
+ }
+ }
+
+ impl ::std::ops::BitOr<$struct_name> for $struct_name {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self::Output {
+ $struct_name(self.0 | rhs.0)
+ }
+ }
+
+ impl ::std::ops::Sub<$struct_name> for $struct_name {
+ type Output = Self;
+ fn sub(self, rhs: Self) -> Self::Output {
+ $struct_name(self.0 & !rhs.0)
+ }
+ }
+
+ impl ::std::ops::Not for $struct_name {
+ type Output = Self;
+ fn not(self) -> Self::Output {
+ $struct_name(self.0 ^ Self::VALID__)
+ }
+ }
+
+ impl From<$struct_name> for $type {
+ fn from(x: $struct_name) -> $type {
+ x.0
+ }
+ }
+
+ impl From<$type> for $struct_name {
+ fn from(x: $type) -> Self {
+ $struct_name(x & Self::VALID__)
+ }
+ }
+ };
+
+ { $type:ty: $expr:expr } => {
+ ::qemu_api_macros::bits_const_internal! { $type @ ($expr) }
+ };
+
+ { $type:ty as $int_type:ty: $expr:expr } => {
+ (::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
+ };
+}
+
+#[cfg(test)]
+mod test {
+ bits! {
+ pub struct InterruptMask(u32) {
+ OE = 1 << 10,
+ BE = 1 << 9,
+ PE = 1 << 8,
+ FE = 1 << 7,
+ RT = 1 << 6,
+ TX = 1 << 5,
+ RX = 1 << 4,
+ DSR = 1 << 3,
+ DCD = 1 << 2,
+ CTS = 1 << 1,
+ RI = 1 << 0,
+
+ E = bits!(Self as u32: OE | BE | PE | FE),
+ MS = bits!(Self as u32: RI | DSR | DCD | CTS),
+ }
+ }
+
+ #[test]
+ pub fn test_not() {
+ assert_eq!(
+ !InterruptMask::from(InterruptMask::RT.0),
+ InterruptMask::E | InterruptMask::MS | InterruptMask::TX | InterruptMask::RX
+ );
+ }
+
+ #[test]
+ pub fn test_and() {
+ assert_eq!(
+ InterruptMask::from(0),
+ InterruptMask::MS & InterruptMask::OE
+ )
+ }
+
+ #[test]
+ pub fn test_or() {
+ assert_eq!(
+ InterruptMask::E,
+ InterruptMask::OE | InterruptMask::BE | InterruptMask::PE | InterruptMask::FE
+ );
+ }
+
+ #[test]
+ pub fn test_xor() {
+ assert_eq!(
+ InterruptMask::E ^ InterruptMask::BE,
+ InterruptMask::OE | InterruptMask::PE | InterruptMask::FE
+ );
+ }
+}
diff --git a/rust/clippy.toml b/rust/clippy.toml
deleted file mode 100644
index 58a62c0..0000000
--- a/rust/clippy.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-doc-valid-idents = ["PrimeCell", ".."]
-allow-mixed-uninlined-format-args = false
-msrv = "1.77.0"
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index a1f431a..003ef96 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -18,6 +18,7 @@ crate-type = ["staticlib"]
[dependencies]
bilge = { version = "0.2.0" }
bilge-impl = { version = "0.2.0" }
+bits = { path = "../../../bits" }
qemu_api = { path = "../../../qemu-api" }
qemu_api_macros = { path = "../../../qemu-api-macros" }
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 547cca5..2a1be32 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -1,17 +1,12 @@
-subproject('bilge-0.2-rs', required: true)
-subproject('bilge-impl-0.2-rs', required: true)
-
-bilge_dep = dependency('bilge-0.2-rs')
-bilge_impl_dep = dependency('bilge-impl-0.2-rs')
-
_libpl011_rs = static_library(
'pl011',
files('src/lib.rs'),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
dependencies: [
- bilge_dep,
- bilge_impl_dep,
+ bilge_rs,
+ bilge_impl_rs,
+ bits_rs,
qemu_api,
qemu_api_macros,
],
@@ -21,6 +16,6 @@ rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency(
link_whole: [_libpl011_rs],
# Putting proc macro crates in `dependencies` is necessary for Meson to find
# them when compiling the root per-target static rust lib.
- dependencies: [bilge_impl_dep, qemu_api_macros],
+ dependencies: [bilge_impl_rs, qemu_api_macros],
variables: {'crate': 'pl011'},
)])
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index bde3be6..5b53f26 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -2,18 +2,21 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::{ffi::CStr, mem::size_of, ptr::addr_of_mut};
+use std::{ffi::CStr, mem::size_of};
use qemu_api::{
chardev::{CharBackend, Chardev, Event},
impl_vmstate_forward,
irq::{IRQState, InterruptSource},
+ log::Log,
+ log_mask_ln,
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
prelude::*,
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
- qom::{ObjectImpl, Owned, ParentField},
+ qom::{ObjectImpl, Owned, ParentField, ParentInit},
static_assert,
sysbus::{SysBusDevice, SysBusDeviceImpl},
+ uninit_field_mut,
vmstate::VMStateDescription,
};
@@ -85,8 +88,8 @@ pub struct PL011Registers {
#[doc(alias = "cr")]
pub control: registers::Control,
pub dmacr: u32,
- pub int_enabled: u32,
- pub int_level: u32,
+ pub int_enabled: Interrupt,
+ pub int_level: Interrupt,
pub read_fifo: Fifo,
pub ilpr: u32,
pub ibrd: u32,
@@ -163,7 +166,7 @@ impl PL011Impl for PL011State {
impl ObjectImpl for PL011State {
type ParentType = SysBusDevice;
- const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
+ const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = Some(Self::init);
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
}
@@ -175,7 +178,7 @@ impl DeviceImpl for PL011State {
fn vmsd() -> Option<&'static VMStateDescription> {
Some(&device_class::VMSTATE_PL011)
}
- const REALIZE: Option<fn(&Self)> = Some(Self::realize);
+ const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
}
impl ResettablePhasesImpl for PL011State {
@@ -199,9 +202,9 @@ impl PL011Registers {
LCR_H => u32::from(self.line_control),
CR => u32::from(self.control),
FLS => self.ifl,
- IMSC => self.int_enabled,
- RIS => self.int_level,
- MIS => self.int_level & self.int_enabled,
+ IMSC => u32::from(self.int_enabled),
+ RIS => u32::from(self.int_level),
+ MIS => u32::from(self.int_level & self.int_enabled),
ICR => {
// "The UARTICR Register is the interrupt clear register and is write-only"
// Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR
@@ -263,20 +266,19 @@ impl PL011Registers {
self.set_read_trigger();
}
IMSC => {
- self.int_enabled = value;
+ self.int_enabled = Interrupt::from(value);
return true;
}
RIS => {}
MIS => {}
ICR => {
- self.int_level &= !value;
+ self.int_level &= !Interrupt::from(value);
return true;
}
DMACR => {
self.dmacr = value;
if value & 3 > 0 {
- // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
- eprintln!("pl011: DMA not implemented");
+ log_mask_ln!(Log::Unimp, "pl011: DMA not implemented");
}
}
}
@@ -295,7 +297,7 @@ impl PL011Registers {
self.flags.set_receive_fifo_empty(true);
}
if self.read_count + 1 == self.read_trigger {
- self.int_level &= !Interrupt::RX.0;
+ self.int_level &= !Interrupt::RX;
}
self.receive_status_error_clear.set_from_data(c);
*update = true;
@@ -303,9 +305,15 @@ impl PL011Registers {
}
fn write_data_register(&mut self, value: u32) -> bool {
+ if !self.control.enable_uart() {
+ log_mask_ln!(Log::GuestError, "PL011 data written to disabled UART");
+ }
+ if !self.control.enable_transmit() {
+ log_mask_ln!(Log::GuestError, "PL011 data written to disabled TX UART");
+ }
// interrupts always checked
let _ = self.loopback_tx(value.into());
- self.int_level |= Interrupt::TX.0;
+ self.int_level |= Interrupt::TX;
true
}
@@ -361,19 +369,19 @@ impl PL011Registers {
// Change interrupts based on updated FR
let mut il = self.int_level;
- il &= !Interrupt::MS.0;
+ il &= !Interrupt::MS;
if self.flags.data_set_ready() {
- il |= Interrupt::DSR.0;
+ il |= Interrupt::DSR;
}
if self.flags.data_carrier_detect() {
- il |= Interrupt::DCD.0;
+ il |= Interrupt::DCD;
}
if self.flags.clear_to_send() {
- il |= Interrupt::CTS.0;
+ il |= Interrupt::CTS;
}
if self.flags.ring_indicator() {
- il |= Interrupt::RI.0;
+ il |= Interrupt::RI;
}
self.int_level = il;
true
@@ -391,8 +399,8 @@ impl PL011Registers {
self.line_control.reset();
self.receive_status_error_clear.reset();
self.dmacr = 0;
- self.int_enabled = 0;
- self.int_level = 0;
+ self.int_enabled = 0.into();
+ self.int_level = 0.into();
self.ilpr = 0;
self.ibrd = 0;
self.fbrd = 0;
@@ -451,7 +459,7 @@ impl PL011Registers {
}
if self.read_count == self.read_trigger {
- self.int_level |= Interrupt::RX.0;
+ self.int_level |= Interrupt::RX;
return true;
}
false
@@ -480,15 +488,15 @@ impl PL011Registers {
}
impl PL011State {
- /// Initializes a pre-allocated, unitialized instance of `PL011State`.
+ /// Initializes a pre-allocated, uninitialized instance of `PL011State`.
///
/// # Safety
///
/// `self` must point to a correctly sized and aligned location for the
/// `PL011State` type. It must not be called more than once on the same
- /// location/instance. All its fields are expected to hold unitialized
+ /// location/instance. All its fields are expected to hold uninitialized
/// values with the sole exception of `parent_obj`.
- unsafe fn init(&mut self) {
+ unsafe fn init(mut this: ParentInit<Self>) {
static PL011_OPS: MemoryRegionOps<PL011State> = MemoryRegionOpsBuilder::<PL011State>::new()
.read(&PL011State::read)
.write(&PL011State::write)
@@ -496,28 +504,23 @@ impl PL011State {
.impl_sizes(4, 4)
.build();
- // SAFETY:
- //
- // self and self.iomem are guaranteed to be valid at this point since callers
- // must make sure the `self` reference is valid.
+ // SAFETY: this and this.iomem are guaranteed to be valid at this point
MemoryRegion::init_io(
- unsafe { &mut *addr_of_mut!(self.iomem) },
- addr_of_mut!(*self),
+ &mut uninit_field_mut!(*this, iomem),
&PL011_OPS,
"pl011",
0x1000,
);
- self.regs = Default::default();
+ uninit_field_mut!(*this, regs).write(Default::default());
- // SAFETY:
- //
- // self.clock is not initialized at this point; but since `Owned<_>` is
- // not Drop, we can overwrite the undefined value without side effects;
- // it's not sound but, because for all PL011State instances are created
- // by QOM code which calls this function to initialize the fields, at
- // leastno code is able to access an invalid self.clock value.
- self.clock = self.init_clock_in("clk", &Self::clock_update, ClockEvent::ClockUpdate);
+ let clock = DeviceState::init_clock_in(
+ &mut this,
+ "clk",
+ &Self::clock_update,
+ ClockEvent::ClockUpdate,
+ );
+ uninit_field_mut!(*this, clock).write(clock);
}
const fn clock_update(&self, _event: ClockEvent) {
@@ -538,7 +541,7 @@ impl PL011State {
u64::from(device_id[(offset - 0xfe0) >> 2])
}
Err(_) => {
- // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
+ log_mask_ln!(Log::GuestError, "PL011State::read: Bad offset {offset}");
0
}
Ok(field) => {
@@ -570,7 +573,10 @@ impl PL011State {
.borrow_mut()
.write(field, value as u32, &self.char_backend);
} else {
- eprintln!("write bad offset {offset} value {value}");
+ log_mask_ln!(
+ Log::GuestError,
+ "PL011State::write: Bad offset {offset} value {value}"
+ );
}
if update_irq {
self.update();
@@ -619,9 +625,10 @@ impl PL011State {
}
}
- fn realize(&self) {
+ fn realize(&self) -> qemu_api::Result<()> {
self.char_backend
.enable_handlers(self, Self::can_receive, Self::receive, Self::event);
+ Ok(())
}
fn reset_hold(&self, _type: ResetType) {
@@ -632,7 +639,7 @@ impl PL011State {
let regs = self.regs.borrow();
let flags = regs.int_level & regs.int_enabled;
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
- irq.set(flags & i != 0);
+ irq.set(flags.any_set(i));
}
}
@@ -642,14 +649,13 @@ impl PL011State {
}
/// Which bits in the interrupt status matter for each outbound IRQ line ?
-const IRQMASK: [u32; 6] = [
- /* combined IRQ */
- Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0,
- Interrupt::RX.0,
- Interrupt::TX.0,
- Interrupt::RT.0,
- Interrupt::MS.0,
- Interrupt::E.0,
+const IRQMASK: [Interrupt; 6] = [
+ Interrupt::all(),
+ Interrupt::RX,
+ Interrupt::TX,
+ Interrupt::RT,
+ Interrupt::MS,
+ Interrupt::E,
];
/// # Safety
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index 690feb6..7ececd3 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -9,7 +9,8 @@
// https://developer.arm.com/documentation/ddi0183/latest/
use bilge::prelude::*;
-use qemu_api::impl_vmstate_bitsized;
+use bits::bits;
+use qemu_api::{impl_vmstate_bitsized, impl_vmstate_forward};
/// Offset of each register from the base memory address of the device.
#[doc(alias = "offset")]
@@ -326,22 +327,24 @@ impl Default for Control {
}
}
-/// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
-pub struct Interrupt(pub u32);
+bits! {
+ /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
+ #[derive(Default)]
+ pub struct Interrupt(u32) {
+ OE = 1 << 10,
+ BE = 1 << 9,
+ PE = 1 << 8,
+ FE = 1 << 7,
+ RT = 1 << 6,
+ TX = 1 << 5,
+ RX = 1 << 4,
+ DSR = 1 << 3,
+ DCD = 1 << 2,
+ CTS = 1 << 1,
+ RI = 1 << 0,
-impl Interrupt {
- pub const OE: Self = Self(1 << 10);
- pub const BE: Self = Self(1 << 9);
- pub const PE: Self = Self(1 << 8);
- pub const FE: Self = Self(1 << 7);
- pub const RT: Self = Self(1 << 6);
- pub const TX: Self = Self(1 << 5);
- pub const RX: Self = Self(1 << 4);
- pub const DSR: Self = Self(1 << 3);
- pub const DCD: Self = Self(1 << 2);
- pub const CTS: Self = Self(1 << 1);
- pub const RI: Self = Self(1 << 0);
-
- pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0);
- pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0);
+ E = bits!(Self as u32: OE | BE | PE | FE),
+ MS = bits!(Self as u32: RI | DSR | DCD | CTS),
+ }
}
+impl_vmstate_forward!(Interrupt);
diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/device.rs
index 779681d..acf7251 100644
--- a/rust/hw/timer/hpet/src/hpet.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -1,9 +1,10 @@
// Copyright (C) 2024 Intel Corporation.
-// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{
ffi::{c_int, c_void, CStr},
+ mem::MaybeUninit,
pin::Pin,
ptr::{addr_of_mut, null_mut, NonNull},
slice::from_ref,
@@ -12,7 +13,7 @@ use std::{
use qemu_api::{
bindings::{
address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
- qdev_prop_uint32, qdev_prop_uint8,
+ qdev_prop_uint32, qdev_prop_usize,
},
cell::{BqlCell, BqlRefCell},
irq::InterruptSource,
@@ -20,11 +21,12 @@ use qemu_api::{
hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
},
prelude::*,
- qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl},
- qom::{ObjectImpl, ObjectType, ParentField},
+ qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
+ qom::{ObjectImpl, ObjectType, ParentField, ParentInit},
qom_isa,
sysbus::{SysBusDevice, SysBusDeviceImpl},
timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
+ uninit_field_mut,
vmstate::VMStateDescription,
vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
zeroable::Zeroable,
@@ -36,9 +38,9 @@ use crate::fw_cfg::HPETFwConfig;
const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes
/// Minimum recommended hardware implementation.
-const HPET_MIN_TIMERS: u8 = 3;
+const HPET_MIN_TIMERS: usize = 3;
/// Maximum timers in each timer block.
-const HPET_MAX_TIMERS: u8 = 32;
+const HPET_MAX_TIMERS: usize = 32;
/// Flags that HPETState.flags supports.
const HPET_FLAG_MSI_SUPPORT_SHIFT: usize = 0;
@@ -212,13 +214,13 @@ pub struct HPETTimer {
}
impl HPETTimer {
- fn init(&mut self, index: u8, state: &HPETState) {
- *self = HPETTimer {
+ fn new(index: u8, state: *const HPETState) -> HPETTimer {
+ HPETTimer {
index,
// SAFETY: the HPETTimer will only be used after the timer
// is initialized below.
qemu_timer: unsafe { Timer::new() },
- state: NonNull::new((state as *const HPETState).cast_mut()).unwrap(),
+ state: NonNull::new(state.cast_mut()).unwrap(),
config: 0,
cmp: 0,
fsb: 0,
@@ -226,19 +228,15 @@ impl HPETTimer {
period: 0,
wrap_flag: 0,
last: 0,
- };
+ }
+ }
+ fn init_timer_with_cell(cell: &BqlRefCell<Self>) {
+ let mut timer = cell.borrow_mut();
// SAFETY: HPETTimer is only used as part of HPETState, which is
// always pinned.
- let qemu_timer = unsafe { Pin::new_unchecked(&mut self.qemu_timer) };
- qemu_timer.init_full(
- None,
- CLOCK_VIRTUAL,
- Timer::NS,
- 0,
- timer_handler,
- &state.timers[self.index as usize],
- )
+ let qemu_timer = unsafe { Pin::new_unchecked(&mut timer.qemu_timer) };
+ qemu_timer.init_full(None, CLOCK_VIRTUAL, Timer::NS, 0, timer_handler, cell);
}
fn get_state(&self) -> &HPETState {
@@ -561,8 +559,8 @@ pub struct HPETState {
/// HPET timer array managed by this timer block.
#[doc(alias = "timer")]
- timers: [BqlRefCell<HPETTimer>; HPET_MAX_TIMERS as usize],
- num_timers: BqlCell<u8>,
+ timers: [BqlRefCell<HPETTimer>; HPET_MAX_TIMERS],
+ num_timers: usize,
num_timers_save: BqlCell<u8>,
/// Instance id (HPET timer block ID).
@@ -570,11 +568,6 @@ pub struct HPETState {
}
impl HPETState {
- // Get num_timers with `usize` type, which is useful to play with array index.
- fn get_num_timers(&self) -> usize {
- self.num_timers.get().into()
- }
-
const fn has_msi_flag(&self) -> bool {
self.flags & (1 << HPET_FLAG_MSI_SUPPORT_SHIFT) != 0
}
@@ -612,9 +605,18 @@ impl HPETState {
}
}
- fn init_timer(&self) {
- for (index, timer) in self.timers.iter().enumerate() {
- timer.borrow_mut().init(index.try_into().unwrap(), self);
+ fn init_timers(this: &mut MaybeUninit<Self>) {
+ let state = this.as_ptr();
+ for index in 0..HPET_MAX_TIMERS {
+ let mut timer = uninit_field_mut!(*this, timers[index]);
+
+ // Initialize in two steps, to avoid calling Timer::init_full on a
+ // temporary that can be moved.
+ let timer = timer.write(BqlRefCell::new(HPETTimer::new(
+ index.try_into().unwrap(),
+ state,
+ )));
+ HPETTimer::init_timer_with_cell(timer);
}
}
@@ -636,7 +638,7 @@ impl HPETState {
self.hpet_offset
.set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns());
- for timer in self.timers.iter().take(self.get_num_timers()) {
+ for timer in self.timers.iter().take(self.num_timers) {
let mut t = timer.borrow_mut();
if t.is_int_enabled() && t.is_int_active() {
@@ -648,7 +650,7 @@ impl HPETState {
// Halt main counter and disable interrupt generation.
self.counter.set(self.get_ticks());
- for timer in self.timers.iter().take(self.get_num_timers()) {
+ for timer in self.timers.iter().take(self.num_timers) {
timer.borrow_mut().del_timer();
}
}
@@ -671,7 +673,7 @@ impl HPETState {
let new_val = val << shift;
let cleared = new_val & self.int_status.get();
- for (index, timer) in self.timers.iter().take(self.get_num_timers()).enumerate() {
+ for (index, timer) in self.timers.iter().take(self.num_timers).enumerate() {
if cleared & (1 << index) != 0 {
timer.borrow_mut().update_irq(false);
}
@@ -695,7 +697,7 @@ impl HPETState {
.set(self.counter.get().deposit(shift, len, val));
}
- unsafe fn init(&mut self) {
+ unsafe fn init(mut this: ParentInit<Self>) {
static HPET_RAM_OPS: MemoryRegionOps<HPETState> =
MemoryRegionOpsBuilder::<HPETState>::new()
.read(&HPETState::read)
@@ -705,16 +707,14 @@ impl HPETState {
.impl_sizes(4, 8)
.build();
- // SAFETY:
- // self and self.iomem are guaranteed to be valid at this point since callers
- // must make sure the `self` reference is valid.
MemoryRegion::init_io(
- unsafe { &mut *addr_of_mut!(self.iomem) },
- addr_of_mut!(*self),
+ &mut uninit_field_mut!(*this, iomem),
&HPET_RAM_OPS,
"hpet",
HPET_REG_SPACE_LEN,
);
+
+ Self::init_timers(&mut this);
}
fn post_init(&self) {
@@ -724,37 +724,35 @@ impl HPETState {
}
}
- fn realize(&self) {
+ fn realize(&self) -> qemu_api::Result<()> {
+ if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS {
+ Err(format!(
+ "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}"
+ ))?;
+ }
if self.int_route_cap == 0 {
- // TODO: Add error binding: warn_report()
- println!("Hpet's hpet-intcap property not initialized");
+ Err("hpet.hpet-intcap property not initialized")?;
}
- self.hpet_id.set(HPETFwConfig::assign_hpet_id());
+ self.hpet_id.set(HPETFwConfig::assign_hpet_id()?);
- if self.num_timers.get() < HPET_MIN_TIMERS {
- self.num_timers.set(HPET_MIN_TIMERS);
- } else if self.num_timers.get() > HPET_MAX_TIMERS {
- self.num_timers.set(HPET_MAX_TIMERS);
- }
-
- self.init_timer();
// 64-bit General Capabilities and ID Register; LegacyReplacementRoute.
self.capability.set(
HPET_CAP_REV_ID_VALUE << HPET_CAP_REV_ID_SHIFT |
1 << HPET_CAP_COUNT_SIZE_CAP_SHIFT |
1 << HPET_CAP_LEG_RT_CAP_SHIFT |
HPET_CAP_VENDER_ID_VALUE << HPET_CAP_VENDER_ID_SHIFT |
- ((self.get_num_timers() - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer
+ ((self.num_timers - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer
(HPET_CLK_PERIOD * FS_PER_NS) << HPET_CAP_CNT_CLK_PERIOD_SHIFT, // 10 ns
);
self.init_gpio_in(2, HPETState::handle_legacy_irq);
self.init_gpio_out(from_ref(&self.pit_enabled));
+ Ok(())
}
fn reset_hold(&self, _type: ResetType) {
- for timer in self.timers.iter().take(self.get_num_timers()) {
+ for timer in self.timers.iter().take(self.num_timers) {
timer.borrow_mut().reset();
}
@@ -773,7 +771,7 @@ impl HPETState {
self.rtc_irq_level.set(0);
}
- fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode {
+ fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode<'_> {
let shift = ((addr & 4) * 8) as u32;
let len = std::cmp::min(size * 8, 64 - shift);
@@ -782,7 +780,7 @@ impl HPETState {
GlobalRegister::try_from(addr).map(HPETRegister::Global)
} else {
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
- if timer_id <= self.get_num_timers() {
+ if timer_id < self.num_timers {
// TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id)
TimerRegister::try_from(addr & 0x18)
.map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg))
@@ -853,12 +851,12 @@ impl HPETState {
* also added to the migration stream. Check that it matches the value
* that was configured.
*/
- self.num_timers_save.set(self.num_timers.get());
+ self.num_timers_save.set(self.num_timers as u8);
0
}
fn post_load(&self, _version_id: u8) -> i32 {
- for timer in self.timers.iter().take(self.get_num_timers()) {
+ for timer in self.timers.iter().take(self.num_timers) {
let mut t = timer.borrow_mut();
t.cmp64 = t.calculate_cmp64(t.get_state().counter.get(), t.cmp);
@@ -883,7 +881,7 @@ impl HPETState {
}
fn validate_num_timers(&self, _version_id: u8) -> bool {
- self.num_timers.get() == self.num_timers_save.get()
+ self.num_timers == self.num_timers_save.get().into()
}
}
@@ -898,7 +896,7 @@ unsafe impl ObjectType for HPETState {
impl ObjectImpl for HPETState {
type ParentType = SysBusDevice;
- const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
+ const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = Some(Self::init);
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
}
@@ -910,7 +908,7 @@ qemu_api::declare_properties! {
c"timers",
HPETState,
num_timers,
- unsafe { &qdev_prop_uint8 },
+ unsafe { &qdev_prop_usize },
u8,
default = HPET_MIN_TIMERS
),
@@ -1015,16 +1013,16 @@ const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match";
static VMSTATE_HPET: VMStateDescription = VMStateDescription {
name: c"hpet".as_ptr(),
version_id: 2,
- minimum_version_id: 1,
+ minimum_version_id: 2,
pre_save: Some(hpet_pre_save),
post_load: Some(hpet_post_load),
fields: vmstate_fields! {
vmstate_of!(HPETState, config),
vmstate_of!(HPETState, int_status),
vmstate_of!(HPETState, counter),
- vmstate_of!(HPETState, num_timers_save).with_version_id(2),
+ vmstate_of!(HPETState, num_timers_save),
vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers),
- vmstate_struct!(HPETState, timers[0 .. num_timers], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0),
+ vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell<HPETTimer>, HPETState::validate_num_timers).with_version_id(0),
},
subsections: vmstate_subsections! {
VMSTATE_HPET_RTC_IRQ_LEVEL,
@@ -1042,7 +1040,7 @@ impl DeviceImpl for HPETState {
Some(&VMSTATE_HPET)
}
- const REALIZE: Option<fn(&Self)> = Some(Self::realize);
+ const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
}
impl ResettablePhasesImpl for HPETState {
diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs
index aa08d28..619d662 100644
--- a/rust/hw/timer/hpet/src/fw_cfg.rs
+++ b/rust/hw/timer/hpet/src/fw_cfg.rs
@@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
-// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::ptr::addr_of_mut;
@@ -36,7 +36,7 @@ pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig {
};
impl HPETFwConfig {
- pub(crate) fn assign_hpet_id() -> usize {
+ pub(crate) fn assign_hpet_id() -> Result<usize, &'static str> {
assert!(bql_locked());
// SAFETY: all accesses go through these methods, which guarantee
// that the accesses are protected by the BQL.
@@ -48,13 +48,12 @@ impl HPETFwConfig {
}
if fw_cfg.count == 8 {
- // TODO: Add error binding: error_setg()
- panic!("Only 8 instances of HPET is allowed");
+ Err("Only 8 instances of HPET are allowed")?;
}
let id: usize = fw_cfg.count.into();
fw_cfg.count += 1;
- id
+ Ok(id)
}
pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) {
diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs
index 1954584..a95cf14 100644
--- a/rust/hw/timer/hpet/src/lib.rs
+++ b/rust/hw/timer/hpet/src/lib.rs
@@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
-// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
//! # HPET QEMU Device Model
@@ -7,7 +7,7 @@
//! This library implements a device model for the IA-PC HPET (High
//! Precision Event Timers) device in QEMU.
+pub mod device;
pub mod fw_cfg;
-pub mod hpet;
pub const TYPE_HPET: &::std::ffi::CStr = c"hpet";
diff --git a/rust/meson.build b/rust/meson.build
index 91e52b8..331f11b 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -1,4 +1,29 @@
+subproject('anyhow-1-rs', required: true)
+subproject('bilge-0.2-rs', required: true)
+subproject('bilge-impl-0.2-rs', required: true)
+subproject('foreign-0.3-rs', required: true)
+subproject('libc-0.2-rs', required: true)
+
+anyhow_rs = dependency('anyhow-1-rs')
+bilge_rs = dependency('bilge-0.2-rs')
+bilge_impl_rs = dependency('bilge-impl-0.2-rs')
+foreign_rs = dependency('foreign-0.3-rs')
+libc_rs = dependency('libc-0.2-rs')
+
+subproject('proc-macro2-1-rs', required: true)
+subproject('quote-1-rs', required: true)
+subproject('syn-2-rs', required: true)
+
+quote_rs_native = dependency('quote-1-rs', native: true)
+syn_rs_native = dependency('syn-2-rs', native: true)
+proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true)
+
+qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true)
+
+genrs = []
+
subdir('qemu-api-macros')
+subdir('bits')
subdir('qemu-api')
subdir('hw')
@@ -6,21 +31,9 @@ subdir('hw')
cargo = find_program('cargo', required: false)
if cargo.found()
- run_target('clippy',
- command: [config_host['MESON'], 'devenv',
- '--workdir', '@CURRENT_SOURCE_DIR@',
- cargo, 'clippy', '--tests'],
- depends: bindings_rs)
-
run_target('rustfmt',
command: [config_host['MESON'], 'devenv',
'--workdir', '@CURRENT_SOURCE_DIR@',
cargo, 'fmt'],
- depends: bindings_rs)
-
- run_target('rustdoc',
- command: [config_host['MESON'], 'devenv',
- '--workdir', '@CURRENT_SOURCE_DIR@',
- cargo, 'doc', '--no-deps', '--document-private-items'],
- depends: bindings_rs)
+ depends: genrs)
endif
diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build
index 6f94a4b..8610ce1 100644
--- a/rust/qemu-api-macros/meson.build
+++ b/rust/qemu-api-macros/meson.build
@@ -1,11 +1,3 @@
-subproject('proc-macro2-1-rs', required: true)
-subproject('quote-1-rs', required: true)
-subproject('syn-2-rs', required: true)
-
-quote_dep = dependency('quote-1-rs', native: true)
-syn_dep = dependency('syn-2-rs', native: true)
-proc_macro2_dep = dependency('proc-macro2-1-rs', native: true)
-
_qemu_api_macros_rs = rust.proc_macro(
'qemu_api_macros',
files('src/lib.rs'),
@@ -16,9 +8,9 @@ _qemu_api_macros_rs = rust.proc_macro(
'--cfg', 'feature="proc-macro"',
],
dependencies: [
- proc_macro2_dep,
- quote_dep,
- syn_dep,
+ proc_macro2_rs_native,
+ quote_rs_native,
+ syn_rs_native,
],
)
diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-api-macros/src/bits.rs
new file mode 100644
index 0000000..5ba8475
--- /dev/null
+++ b/rust/qemu-api-macros/src/bits.rs
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later
+
+// shadowing is useful together with "if let"
+#![allow(clippy::shadow_unrelated)]
+
+use proc_macro2::{
+ Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT,
+};
+
+use crate::utils::MacroError;
+
+pub struct BitsConstInternal {
+ typ: TokenTree,
+}
+
+fn paren(ts: TokenStream) -> TokenTree {
+ TT::Group(Group::new(Delimiter::Parenthesis, ts))
+}
+
+fn ident(s: &'static str) -> TokenTree {
+ TT::Ident(Ident::new(s, Span::call_site()))
+}
+
+fn punct(ch: char) -> TokenTree {
+ TT::Punct(Punct::new(ch, Spacing::Alone))
+}
+
+/// Implements a recursive-descent parser that translates Boolean expressions on
+/// bitmasks to invocations of `const` functions defined by the `bits!` macro.
+impl BitsConstInternal {
+ // primary ::= '(' or ')'
+ // | ident
+ // | '!' ident
+ fn parse_primary(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ let next = match tok {
+ TT::Group(ref g) => {
+ if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None {
+ return Err(MacroError::Message("expected parenthesis".into(), g.span()));
+ }
+ let mut stream = g.stream().into_iter();
+ let Some(first_tok) = stream.next() else {
+ return Err(MacroError::Message(
+ "expected operand, found ')'".into(),
+ g.span(),
+ ));
+ };
+ let mut output = TokenStream::new();
+ // start from the lowest precedence
+ let next = self.parse_or(first_tok, &mut stream, &mut output)?;
+ if let Some(tok) = next {
+ return Err(MacroError::Message(
+ format!("unexpected token {tok}"),
+ tok.span(),
+ ));
+ }
+ out.extend(Some(paren(output)));
+ it.next()
+ }
+ TT::Ident(_) => {
+ let mut output = TokenStream::new();
+ output.extend([
+ self.typ.clone(),
+ TT::Punct(Punct::new(':', Spacing::Joint)),
+ TT::Punct(Punct::new(':', Spacing::Joint)),
+ tok,
+ ]);
+ out.extend(Some(paren(output)));
+ it.next()
+ }
+ TT::Punct(ref p) => {
+ if p.as_char() != '!' {
+ return Err(MacroError::Message("expected operand".into(), p.span()));
+ }
+ let Some(rhs_tok) = it.next() else {
+ return Err(MacroError::Message(
+ "expected operand at end of input".into(),
+ p.span(),
+ ));
+ };
+ let next = self.parse_primary(rhs_tok, it, out)?;
+ out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]);
+ next
+ }
+ _ => {
+ return Err(MacroError::Message("unexpected literal".into(), tok.span()));
+ }
+ };
+ Ok(next)
+ }
+
+ fn parse_binop<
+ F: Fn(
+ &Self,
+ TokenTree,
+ &mut dyn Iterator<Item = TokenTree>,
+ &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError>,
+ >(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ch: char,
+ f: F,
+ method: &'static str,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ let mut next = f(self, tok, it, out)?;
+ while next.is_some() {
+ let op = next.as_ref().unwrap();
+ let TT::Punct(ref p) = op else { break };
+ if p.as_char() != ch {
+ break;
+ }
+
+ let Some(rhs_tok) = it.next() else {
+ return Err(MacroError::Message(
+ "expected operand at end of input".into(),
+ p.span(),
+ ));
+ };
+ let mut rhs = TokenStream::new();
+ next = f(self, rhs_tok, it, &mut rhs)?;
+ out.extend([punct('.'), ident(method), paren(rhs)]);
+ }
+ Ok(next)
+ }
+
+ // sub ::= primary ('-' primary)*
+ pub fn parse_sub(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference")
+ }
+
+ // and ::= sub ('&' sub)*
+ fn parse_and(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection")
+ }
+
+ // xor ::= and ('&' and)*
+ fn parse_xor(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference")
+ }
+
+ // or ::= xor ('|' xor)*
+ pub fn parse_or(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ) -> Result<Option<TokenTree>, MacroError> {
+ self.parse_binop(tok, it, out, '|', Self::parse_xor, "union")
+ }
+
+ pub fn parse(
+ it: &mut dyn Iterator<Item = TokenTree>,
+ ) -> Result<proc_macro2::TokenStream, MacroError> {
+ let mut pos = Span::call_site();
+ let mut typ = proc_macro2::TokenStream::new();
+
+ // Gobble everything up to an `@` sign, which is followed by a
+ // parenthesized expression; that is, all token trees except the
+ // last two form the type.
+ let next = loop {
+ let tok = it.next();
+ if let Some(ref t) = tok {
+ pos = t.span();
+ }
+ match tok {
+ None => break None,
+ Some(TT::Punct(ref p)) if p.as_char() == '@' => {
+ let tok = it.next();
+ if let Some(ref t) = tok {
+ pos = t.span();
+ }
+ break tok;
+ }
+ Some(x) => typ.extend(Some(x)),
+ }
+ };
+
+ let Some(tok) = next else {
+ return Err(MacroError::Message(
+ "expected expression, do not call this macro directly".into(),
+ pos,
+ ));
+ };
+ let TT::Group(ref _group) = tok else {
+ return Err(MacroError::Message(
+ "expected parenthesis, do not call this macro directly".into(),
+ tok.span(),
+ ));
+ };
+ let mut out = TokenStream::new();
+ let state = Self {
+ typ: TT::Group(Group::new(Delimiter::None, typ)),
+ };
+
+ let next = state.parse_primary(tok, it, &mut out)?;
+
+ // A parenthesized expression is a single production of the grammar,
+ // so the input must have reached the last token.
+ if let Some(tok) = next {
+ return Err(MacroError::Message(
+ format!("unexpected token {tok}"),
+ tok.span(),
+ ));
+ }
+ Ok(out)
+ }
+}
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index f97449b..c18bb4e 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -12,6 +12,9 @@ use syn::{
mod utils;
use utils::MacroError;
+mod bits;
+use bits::BitsConstInternal;
+
fn get_fields<'a>(
input: &'a DeriveInput,
msg: &str,
@@ -190,23 +193,52 @@ fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, Macr
}
#[rustfmt::skip::macros(quote)]
+fn derive_tryinto_body(
+ name: &Ident,
+ variants: &Punctuated<Variant, Comma>,
+ repr: &Path,
+) -> Result<proc_macro2::TokenStream, MacroError> {
+ let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect();
+
+ Ok(quote! {
+ #(const #discriminants: #repr = #name::#discriminants as #repr;)*;
+ match value {
+ #(#discriminants => core::result::Result::Ok(#name::#discriminants),)*
+ _ => core::result::Result::Err(value),
+ }
+ })
+}
+
+#[rustfmt::skip::macros(quote)]
fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
let repr = get_repr_uN(&input, "#[derive(TryInto)]")?;
-
let name = &input.ident;
- let variants = get_variants(&input)?;
- let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect();
+ let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?;
+ let errmsg = format!("invalid value for {name}");
Ok(quote! {
+ impl #name {
+ #[allow(dead_code)]
+ pub const fn into_bits(self) -> #repr {
+ self as #repr
+ }
+
+ #[allow(dead_code)]
+ pub const fn from_bits(value: #repr) -> Self {
+ match ({
+ #body
+ }) {
+ Ok(x) => x,
+ Err(_) => panic!(#errmsg)
+ }
+ }
+ }
impl core::convert::TryFrom<#repr> for #name {
type Error = #repr;
- fn try_from(value: #repr) -> Result<Self, Self::Error> {
- #(const #discriminants: #repr = #name::#discriminants as #repr;)*;
- match value {
- #(#discriminants => Ok(Self::#discriminants),)*
- _ => Err(value),
- }
+ #[allow(ambiguous_associated_items)]
+ fn try_from(value: #repr) -> Result<Self, #repr> {
+ #body
}
}
})
@@ -219,3 +251,12 @@ pub fn derive_tryinto(input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}
+
+#[proc_macro]
+pub fn bits_const_internal(ts: TokenStream) -> TokenStream {
+ let ts = proc_macro2::TokenStream::from(ts);
+ let mut it = ts.into_iter();
+
+ let expanded = BitsConstInternal::parse(&mut it).unwrap_or_else(Into::into);
+ TokenStream::from(expanded)
+}
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index c96cf50..db7000d 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -15,7 +15,9 @@ rust-version.workspace = true
[dependencies]
qemu_api_macros = { path = "../qemu-api-macros" }
+anyhow = "~1.0"
libc = "0.2.162"
+foreign = "~0.3.1"
[features]
default = ["debug_cell"]
diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
index 1e72064..7849486 100644
--- a/rust/qemu-api/build.rs
+++ b/rust/qemu-api/build.rs
@@ -14,7 +14,7 @@ fn main() -> Result<()> {
let path = env::var("MESON_BUILD_ROOT")
.unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR")));
- let file = format!("{path}/bindings.inc.rs");
+ let file = format!("{path}/rust/qemu-api/bindings.inc.rs");
let file = Path::new(&file);
if !Path::new(&file).exists() {
panic!(concat!(
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 1696df7..a090297 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -2,13 +2,52 @@ _qemu_api_cfg = run_command(rustc_args,
'--config-headers', config_host_h, '--features', files('Cargo.toml'),
capture: true, check: true).stdout().strip().splitlines()
-libc_dep = dependency('libc-0.2-rs')
-
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
if get_option('debug_mutex')
_qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
endif
+c_enums = [
+ 'DeviceCategory',
+ 'GpioPolarity',
+ 'MachineInitPhase',
+ 'MemoryDeviceInfoKind',
+ 'MigrationPolicy',
+ 'MigrationPriority',
+ 'QEMUChrEvent',
+ 'QEMUClockType',
+ 'ResetType',
+ 'device_endian',
+ 'module_init_type',
+]
+_qemu_api_bindgen_args = []
+foreach enum : c_enums
+ _qemu_api_bindgen_args += ['--rustified-enum', enum]
+endforeach
+c_bitfields = [
+ 'ClockEvent',
+ 'VMStateFlags',
+]
+foreach enum : c_bitfields
+ _qemu_api_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
+_qemu_api_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 + _qemu_api_bindgen_args,
+ )
+
_qemu_api_rs = static_library(
'qemu_api',
structured_sources(
@@ -21,7 +60,9 @@ _qemu_api_rs = static_library(
'src/cell.rs',
'src/chardev.rs',
'src/errno.rs',
+ 'src/error.rs',
'src/irq.rs',
+ 'src/log.rs',
'src/memory.rs',
'src/module.rs',
'src/prelude.rs',
@@ -29,40 +70,33 @@ _qemu_api_rs = static_library(
'src/qom.rs',
'src/sysbus.rs',
'src/timer.rs',
+ 'src/uninit.rs',
'src/vmstate.rs',
'src/zeroable.rs',
],
- {'.' : bindings_rs},
+ {'.' : _qemu_api_bindings_inc_rs},
),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _qemu_api_cfg,
- dependencies: [libc_dep, qemu_api_macros],
+ dependencies: [anyhow_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs,
+ qom, hwcore, chardev, migration],
)
rust.test('rust-qemu-api-tests', _qemu_api_rs,
suite: ['unit', 'rust'])
-qemu_api = declare_dependency(link_with: _qemu_api_rs)
+qemu_api = declare_dependency(link_with: [_qemu_api_rs],
+ dependencies: [qemu_api_macros, qom, hwcore, chardev, migration])
-# Rust executables do not support objects, so add an intermediate step.
-rust_qemu_api_objs = static_library(
- 'rust_qemu_api_objs',
- objects: [libqom.extract_all_objects(recursive: false),
- libhwcore.extract_all_objects(recursive: false),
- libchardev.extract_all_objects(recursive: false),
- libcrypto.extract_all_objects(recursive: false),
- libauthz.extract_all_objects(recursive: false),
- libio.extract_all_objects(recursive: false),
- libmigration.extract_all_objects(recursive: false)])
-rust_qemu_api_deps = declare_dependency(
- dependencies: [
- qom_ss.dependencies(),
- chardev_ss.dependencies(),
- crypto_ss.dependencies(),
- authz_ss.dependencies(),
- io_ss.dependencies()],
- link_whole: [rust_qemu_api_objs, libqemuutil])
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-qemu-api-doctests',
+ _qemu_api_rs,
+ protocol: 'rust',
+ dependencies: qemu_api,
+ suite: ['doc', 'rust'])
test('rust-qemu-api-integration',
executable(
@@ -71,7 +105,7 @@ test('rust-qemu-api-integration',
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_args: ['--test'],
install: false,
- dependencies: [qemu_api, qemu_api_macros, rust_qemu_api_deps]),
+ dependencies: [qemu_api]),
args: [
'--test', '--test-threads', '1',
'--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index 3c1d297..057de4b 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -11,6 +11,7 @@
clippy::restriction,
clippy::style,
clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
clippy::useless_transmute,
clippy::missing_safety_doc
)]
diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs
index 023ec1a..b1e3a53 100644
--- a/rust/qemu-api/src/bitops.rs
+++ b/rust/qemu-api/src/bitops.rs
@@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
-// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
//! This module provides bit operation extensions to integer types.
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index 05ce09f..27063b0 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -225,27 +225,23 @@ use crate::bindings;
/// An internal function that is used by doctests.
pub fn bql_start_test() {
- if cfg!(MESON) {
- // SAFETY: integration tests are run with --test-threads=1, while
- // unit tests and doctests are not multithreaded and do not have
- // any BQL-protected data. Just set bql_locked to true.
- unsafe {
- bindings::rust_bql_mock_lock();
- }
+ // SAFETY: integration tests are run with --test-threads=1, while
+ // unit tests and doctests are not multithreaded and do not have
+ // any BQL-protected data. Just set bql_locked to true.
+ unsafe {
+ bindings::rust_bql_mock_lock();
}
}
pub fn bql_locked() -> bool {
// SAFETY: the function does nothing but return a thread-local bool
- !cfg!(MESON) || unsafe { bindings::bql_locked() }
+ unsafe { bindings::bql_locked() }
}
fn bql_block_unlock(increase: bool) {
- if cfg!(MESON) {
- // SAFETY: this only adjusts a counter
- unsafe {
- bindings::bql_block_unlock(increase);
- }
+ // SAFETY: this only adjusts a counter
+ unsafe {
+ bindings::bql_block_unlock(increase);
}
}
diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs
new file mode 100644
index 0000000..e114fc4
--- /dev/null
+++ b/rust/qemu-api/src/error.rs
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Error propagation for QEMU Rust code
+//!
+//! This module contains [`Error`], the bridge between Rust errors and
+//! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error)
+//! struct.
+//!
+//! For FFI code, [`Error`] provides functions to simplify conversion between
+//! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions:
+//!
+//! * [`ok_or_propagate`](crate::Error::ok_or_propagate),
+//! [`bool_or_propagate`](crate::Error::bool_or_propagate),
+//! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build
+//! a C return value while also propagating an error condition
+//!
+//! * [`err_or_else`](crate::Error::err_or_else) and
+//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result`
+//!
+//! This module is most commonly used at the boundary between C and Rust code;
+//! other code will usually access it through the
+//! [`qemu_api::Result`](crate::Result) type alias, and will use the
+//! [`std::error::Error`] interface to let C errors participate in Rust's error
+//! handling functionality.
+//!
+//! Rust code can also create use this module to create an error object that
+//! will be passed up to C code, though in most cases this will be done
+//! transparently through the `?` operator. Errors can be constructed from a
+//! simple error string, from an [`anyhow::Error`] to pass any other Rust error
+//! type up to C code, or from a combination of the two.
+//!
+//! The third case, corresponding to [`Error::with_error`], is the only one that
+//! requires mentioning [`qemu_api::Error`](crate::Error) explicitly. Similar
+//! to how QEMU's C code handles errno values, the string and the
+//! `anyhow::Error` object will be concatenated with `:` as the separator.
+
+use std::{
+ borrow::Cow,
+ ffi::{c_char, c_int, c_void, CStr},
+ fmt::{self, Display},
+ panic, ptr,
+};
+
+use foreign::{prelude::*, OwnedPointer};
+
+use crate::bindings;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug)]
+pub struct Error {
+ msg: Option<Cow<'static, str>>,
+ /// Appends the print string of the error to the msg if not None
+ cause: Option<anyhow::Error>,
+ file: &'static str,
+ line: u32,
+}
+
+impl std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.cause.as_ref().map(AsRef::as_ref)
+ }
+
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ self.msg
+ .as_deref()
+ .or_else(|| self.cause.as_deref().map(std::error::Error::description))
+ .expect("no message nor cause?")
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut prefix = "";
+ if let Some(ref msg) = self.msg {
+ write!(f, "{msg}")?;
+ prefix = ": ";
+ }
+ if let Some(ref cause) = self.cause {
+ write!(f, "{prefix}{cause}")?;
+ } else if prefix.is_empty() {
+ panic!("no message nor cause?");
+ }
+ Ok(())
+ }
+}
+
+impl From<String> for Error {
+ #[track_caller]
+ fn from(msg: String) -> Self {
+ let location = panic::Location::caller();
+ Error {
+ msg: Some(Cow::Owned(msg)),
+ cause: None,
+ file: location.file(),
+ line: location.line(),
+ }
+ }
+}
+
+impl From<&'static str> for Error {
+ #[track_caller]
+ fn from(msg: &'static str) -> Self {
+ let location = panic::Location::caller();
+ Error {
+ msg: Some(Cow::Borrowed(msg)),
+ cause: None,
+ file: location.file(),
+ line: location.line(),
+ }
+ }
+}
+
+impl From<anyhow::Error> for Error {
+ #[track_caller]
+ fn from(error: anyhow::Error) -> Self {
+ let location = panic::Location::caller();
+ Error {
+ msg: None,
+ cause: Some(error),
+ file: location.file(),
+ line: location.line(),
+ }
+ }
+}
+
+impl Error {
+ /// Create a new error, prepending `msg` to the
+ /// description of `cause`
+ #[track_caller]
+ pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self {
+ let location = panic::Location::caller();
+ Error {
+ msg: Some(msg.into()),
+ cause: Some(cause.into()),
+ file: location.file(),
+ line: location.line(),
+ }
+ }
+
+ /// Consume a result, returning `false` if it is an error and
+ /// `true` if it is successful. The error is propagated into
+ /// `errp` like the C API `error_propagate` would do.
+ ///
+ /// # Safety
+ ///
+ /// `errp` must be a valid argument to `error_propagate`;
+ /// typically it is received from C code and need not be
+ /// checked further at the Rust↔C boundary.
+ pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool {
+ // SAFETY: caller guarantees errp is valid
+ unsafe { Self::ok_or_propagate(result, errp) }.is_some()
+ }
+
+ /// Consume a result, returning a `NULL` pointer if it is an error and
+ /// a C representation of the contents if it is successful. This is
+ /// similar to the C API `error_propagate`, but it panics if `*errp`
+ /// is not `NULL`.
+ ///
+ /// # Safety
+ ///
+ /// `errp` must be a valid argument to `error_propagate`;
+ /// typically it is received from C code and need not be
+ /// checked further at the Rust↔C boundary.
+ ///
+ /// See [`propagate`](Error::propagate) for more information.
+ #[must_use]
+ pub unsafe fn ptr_or_propagate<T: CloneToForeign>(
+ result: Result<T>,
+ errp: *mut *mut bindings::Error,
+ ) -> *mut T::Foreign {
+ // SAFETY: caller guarantees errp is valid
+ unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr()
+ }
+
+ /// Consume a result in the same way as `self.ok()`, but also propagate
+ /// a possible error into `errp`. This is similar to the C API
+ /// `error_propagate`, but it panics if `*errp` is not `NULL`.
+ ///
+ /// # Safety
+ ///
+ /// `errp` must be a valid argument to `error_propagate`;
+ /// typically it is received from C code and need not be
+ /// checked further at the Rust↔C boundary.
+ ///
+ /// See [`propagate`](Error::propagate) for more information.
+ pub unsafe fn ok_or_propagate<T>(
+ result: Result<T>,
+ errp: *mut *mut bindings::Error,
+ ) -> Option<T> {
+ result.map_err(|err| unsafe { err.propagate(errp) }).ok()
+ }
+
+ /// Equivalent of the C function `error_propagate`. Fill `*errp`
+ /// with the information container in `self` if `errp` is not NULL;
+ /// then consume it.
+ ///
+ /// This is similar to the C API `error_propagate`, but it panics if
+ /// `*errp` is not `NULL`.
+ ///
+ /// # Safety
+ ///
+ /// `errp` must be a valid argument to `error_propagate`; it can be
+ /// `NULL` or it can point to any of:
+ /// * `error_abort`
+ /// * `error_fatal`
+ /// * a local variable of (C) type `Error *`
+ ///
+ /// Typically `errp` is received from C code and need not be
+ /// checked further at the Rust↔C boundary.
+ pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) {
+ if errp.is_null() {
+ return;
+ }
+
+ // SAFETY: caller guarantees that errp and *errp are valid
+ unsafe {
+ assert_eq!(*errp, ptr::null_mut());
+ bindings::error_propagate(errp, self.clone_to_foreign_ptr());
+ }
+ }
+
+ /// Convert a C `Error*` into a Rust `Result`, using
+ /// `Ok(())` if `c_error` is NULL. Free the `Error*`.
+ ///
+ /// # Safety
+ ///
+ /// `c_error` must be `NULL` or valid; typically it was initialized
+ /// with `ptr::null_mut()` and passed by reference to a C function.
+ pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> {
+ // SAFETY: caller guarantees c_error is valid
+ unsafe { Self::err_or_else(c_error, || ()) }
+ }
+
+ /// Convert a C `Error*` into a Rust `Result`, calling `f()` to
+ /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`.
+ ///
+ /// # Safety
+ ///
+ /// `c_error` must be `NULL` or point to a valid C [`struct
+ /// Error`](bindings::Error); typically it was initialized with
+ /// `ptr::null_mut()` and passed by reference to a C function.
+ pub unsafe fn err_or_else<T, F: FnOnce() -> T>(
+ c_error: *mut bindings::Error,
+ f: F,
+ ) -> Result<T> {
+ // SAFETY: caller guarantees c_error is valid
+ let err = unsafe { Option::<Self>::from_foreign(c_error) };
+ match err {
+ None => Ok(f()),
+ Some(err) => Err(err),
+ }
+ }
+}
+
+impl FreeForeign for Error {
+ type Foreign = bindings::Error;
+
+ unsafe fn free_foreign(p: *mut bindings::Error) {
+ // SAFETY: caller guarantees p is valid
+ unsafe {
+ bindings::error_free(p);
+ }
+ }
+}
+
+impl CloneToForeign for Error {
+ fn clone_to_foreign(&self) -> OwnedPointer<Self> {
+ // SAFETY: all arguments are controlled by this function
+ unsafe {
+ let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>());
+ let err: &mut bindings::Error = &mut *err.cast();
+ *err = bindings::Error {
+ msg: format!("{self}").clone_to_foreign_ptr(),
+ err_class: bindings::ERROR_CLASS_GENERIC_ERROR,
+ src_len: self.file.len() as c_int,
+ src: self.file.as_ptr().cast::<c_char>(),
+ line: self.line as c_int,
+ func: ptr::null_mut(),
+ hint: ptr::null_mut(),
+ };
+ OwnedPointer::new(err)
+ }
+ }
+}
+
+impl FromForeign for Error {
+ unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self {
+ // SAFETY: caller guarantees c_error is valid
+ unsafe {
+ let error = &*c_error;
+ let file = if error.src_len < 0 {
+ // NUL-terminated
+ CStr::from_ptr(error.src).to_str()
+ } else {
+ // Can become str::from_utf8 with Rust 1.87.0
+ std::str::from_utf8(std::slice::from_raw_parts(
+ &*error.src.cast::<u8>(),
+ error.src_len as usize,
+ ))
+ };
+
+ Error {
+ msg: FromForeign::cloned_from_foreign(error.msg),
+ cause: None,
+ file: file.unwrap(),
+ line: error.line as u32,
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::ffi::CStr;
+
+ use anyhow::anyhow;
+ use foreign::OwnedPointer;
+
+ use super::*;
+ use crate::{assert_match, bindings};
+
+ #[track_caller]
+ fn error_for_test(msg: &CStr) -> OwnedPointer<Error> {
+ // SAFETY: all arguments are controlled by this function
+ let location = panic::Location::caller();
+ unsafe {
+ let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>());
+ let err: &mut bindings::Error = &mut *err.cast();
+ *err = bindings::Error {
+ msg: msg.clone_to_foreign_ptr(),
+ err_class: bindings::ERROR_CLASS_GENERIC_ERROR,
+ src_len: location.file().len() as c_int,
+ src: location.file().as_ptr().cast::<c_char>(),
+ line: location.line() as c_int,
+ func: ptr::null_mut(),
+ hint: ptr::null_mut(),
+ };
+ OwnedPointer::new(err)
+ }
+ }
+
+ unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr {
+ unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) }
+ }
+
+ #[test]
+ #[allow(deprecated)]
+ fn test_description() {
+ use std::error::Error;
+
+ assert_eq!(super::Error::from("msg").description(), "msg");
+ assert_eq!(super::Error::from("msg".to_owned()).description(), "msg");
+ }
+
+ #[test]
+ fn test_display() {
+ assert_eq!(&*format!("{}", Error::from("msg")), "msg");
+ assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg");
+ assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg");
+
+ assert_eq!(
+ &*format!("{}", Error::with_error("msg", anyhow!("cause"))),
+ "msg: cause"
+ );
+ }
+
+ #[test]
+ fn test_bool_or_propagate() {
+ unsafe {
+ let mut local_err: *mut bindings::Error = ptr::null_mut();
+
+ assert!(Error::bool_or_propagate(Ok(()), &mut local_err));
+ assert_eq!(local_err, ptr::null_mut());
+
+ let my_err = Error::from("msg");
+ assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err));
+ assert_ne!(local_err, ptr::null_mut());
+ assert_eq!(error_get_pretty(local_err), c"msg");
+ bindings::error_free(local_err);
+ }
+ }
+
+ #[test]
+ fn test_ptr_or_propagate() {
+ unsafe {
+ let mut local_err: *mut bindings::Error = ptr::null_mut();
+
+ let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err);
+ assert_eq!(String::from_foreign(ret), "abc");
+ assert_eq!(local_err, ptr::null_mut());
+
+ let my_err = Error::from("msg");
+ assert_eq!(
+ Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err),
+ ptr::null_mut()
+ );
+ assert_ne!(local_err, ptr::null_mut());
+ assert_eq!(error_get_pretty(local_err), c"msg");
+ bindings::error_free(local_err);
+ }
+ }
+
+ #[test]
+ fn test_err_or_unit() {
+ unsafe {
+ let result = Error::err_or_unit(ptr::null_mut());
+ assert_match!(result, Ok(()));
+
+ let err = error_for_test(c"msg");
+ let err = Error::err_or_unit(err.into_inner()).unwrap_err();
+ assert_eq!(&*format!("{err}"), "msg");
+ }
+ }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 234a94e..86dcd8e 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -19,13 +19,16 @@ pub mod callbacks;
pub mod cell;
pub mod chardev;
pub mod errno;
+pub mod error;
pub mod irq;
+pub mod log;
pub mod memory;
pub mod module;
pub mod qdev;
pub mod qom;
pub mod sysbus;
pub mod timer;
+pub mod uninit;
pub mod vmstate;
pub mod zeroable;
@@ -34,6 +37,8 @@ use std::{
ffi::c_void,
};
+pub use error::{Error, Result};
+
#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
extern "C" {
fn g_aligned_alloc0(
diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs
new file mode 100644
index 0000000..d6c3d6c
--- /dev/null
+++ b/rust/qemu-api/src/log.rs
@@ -0,0 +1,73 @@
+// Copyright 2025 Bernhard Beschow <shentey@gmail.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Bindings for QEMU's logging infrastructure
+
+#[repr(u32)]
+/// Represents specific error categories within QEMU's logging system.
+///
+/// The `Log` enum provides a Rust abstraction for logging errors, corresponding
+/// to a subset of the error categories defined in the C implementation.
+pub enum Log {
+ /// Log invalid access caused by the guest.
+ /// Corresponds to `LOG_GUEST_ERROR` in the C implementation.
+ GuestError = crate::bindings::LOG_GUEST_ERROR,
+
+ /// Log guest access of unimplemented functionality.
+ /// Corresponds to `LOG_UNIMP` in the C implementation.
+ Unimp = crate::bindings::LOG_UNIMP,
+}
+
+/// A macro to log messages conditionally based on a provided mask.
+///
+/// The `log_mask_ln` macro checks whether the given mask matches the current
+/// log level and, if so, formats and logs the message. It is the Rust
+/// counterpart of the `qemu_log_mask()` macro in the C implementation.
+///
+/// # Parameters
+///
+/// - `$mask`: A log level mask. This should be a variant of the `Log` enum.
+/// - `$fmt`: A format string following the syntax and rules of the `format!`
+/// macro. It specifies the structure of the log message.
+/// - `$args`: Optional arguments to be interpolated into the format string.
+///
+/// # Example
+///
+/// ```
+/// use qemu_api::{log::Log, log_mask_ln};
+///
+/// let error_address = 0xbad;
+/// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range");
+/// ```
+///
+/// It is also possible to use printf-style formatting, as well as having a
+/// trailing `,`:
+///
+/// ```
+/// use qemu_api::{log::Log, log_mask_ln};
+///
+/// let error_address = 0xbad;
+/// log_mask_ln!(
+/// Log::GuestError,
+/// "Address 0x{:x} out of range",
+/// error_address,
+/// );
+/// ```
+#[macro_export]
+macro_rules! log_mask_ln {
+ ($mask:expr, $fmt:tt $($args:tt)*) => {{
+ // Type assertion to enforce type `Log` for $mask
+ let _: Log = $mask;
+
+ if unsafe {
+ (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0
+ } {
+ let formatted_string = format!("{}\n", format_args!($fmt $($args)*));
+ let c_string = std::ffi::CString::new(formatted_string).unwrap();
+
+ unsafe {
+ ::qemu_api::bindings::qemu_log(c_string.as_ptr());
+ }
+ }
+ }};
+}
diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs
index 9ef2694..e40fad6 100644
--- a/rust/qemu-api/src/memory.rs
+++ b/rust/qemu-api/src/memory.rs
@@ -16,6 +16,7 @@ use crate::{
callbacks::FnCall,
cell::Opaque,
prelude::*,
+ uninit::MaybeUninitField,
zeroable::Zeroable,
};
@@ -147,7 +148,7 @@ impl MemoryRegion {
#[inline(always)]
unsafe fn do_init_io(
slot: *mut bindings::MemoryRegion,
- owner: *mut Object,
+ owner: *mut bindings::Object,
ops: &'static bindings::MemoryRegionOps,
name: &'static str,
size: u64,
@@ -156,7 +157,7 @@ impl MemoryRegion {
let cstr = CString::new(name).unwrap();
memory_region_init_io(
slot,
- owner.cast::<bindings::Object>(),
+ owner,
ops,
owner.cast::<c_void>(),
cstr.as_ptr(),
@@ -166,16 +167,15 @@ impl MemoryRegion {
}
pub fn init_io<T: IsA<Object>>(
- &mut self,
- owner: *mut T,
+ this: &mut MaybeUninitField<'_, T, Self>,
ops: &'static MemoryRegionOps<T>,
name: &'static str,
size: u64,
) {
unsafe {
Self::do_init_io(
- self.0.as_mut_ptr(),
- owner.cast::<Object>(),
+ this.as_mut_ptr().cast(),
+ MaybeUninitField::parent_mut(this).cast(),
&ops.0,
name,
size,
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 43bfcd5..8f9e23e 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -11,6 +11,8 @@ pub use crate::cell::BqlRefCell;
pub use crate::errno;
+pub use crate::log_mask_ln;
+
pub use crate::qdev::DeviceMethods;
pub use crate::qom::InterfaceType;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index 1279d7a..36f02fb 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -12,13 +12,14 @@ use std::{
pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
use crate::{
- bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass},
+ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
callbacks::FnCall,
cell::{bql_locked, Opaque},
chardev::Chardev,
+ error::{Error, Result},
irq::InterruptSource,
prelude::*,
- qom::{ObjectClass, ObjectImpl, Owned},
+ qom::{ObjectClass, ObjectImpl, Owned, ParentInit},
vmstate::VMStateDescription,
};
@@ -108,7 +109,7 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
///
/// If not `None`, the parent class's `realize` method is overridden
/// with the function pointed to by `REALIZE`.
- const REALIZE: Option<fn(&Self)> = None;
+ const REALIZE: Option<fn(&Self) -> Result<()>> = None;
/// An array providing the properties that the user can set on the
/// device. Not a `const` because referencing statics in constants
@@ -134,10 +135,13 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
/// readable/writeable from one thread at any time.
unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(
dev: *mut bindings::DeviceState,
- _errp: *mut *mut Error,
+ errp: *mut *mut bindings::Error,
) {
let state = NonNull::new(dev).unwrap().cast::<T>();
- T::REALIZE.unwrap()(unsafe { state.as_ref() });
+ let result = T::REALIZE.unwrap()(unsafe { state.as_ref() });
+ unsafe {
+ Error::ok_or_propagate(result, errp);
+ }
}
unsafe impl InterfaceType for ResettableClass {
@@ -243,15 +247,9 @@ unsafe impl ObjectType for DeviceState {
}
qom_isa!(DeviceState: Object);
-/// Trait for methods exposed by the [`DeviceState`] class. The methods can be
-/// called on all objects that have the trait `IsA<DeviceState>`.
-///
-/// The trait should only be used through the blanket implementation,
-/// which guarantees safety via `IsA`.
-pub trait DeviceMethods: ObjectDeref
-where
- Self::Target: IsA<DeviceState>,
-{
+/// Initialization methods take a [`ParentInit`] and can be called as
+/// associated functions.
+impl DeviceState {
/// Add an input clock named `name`. Invoke the callback with
/// `self` as the first parameter for the events that are requested.
///
@@ -262,12 +260,15 @@ where
/// which Rust code has a reference to a child object) it would be
/// possible for this function to return a `&Clock` too.
#[inline]
- fn init_clock_in<F: for<'a> FnCall<(&'a Self::Target, ClockEvent)>>(
- &self,
+ pub fn init_clock_in<T: DeviceImpl, F: for<'a> FnCall<(&'a T, ClockEvent)>>(
+ this: &mut ParentInit<T>,
name: &str,
_cb: &F,
events: ClockEvent,
- ) -> Owned<Clock> {
+ ) -> Owned<Clock>
+ where
+ T::ParentType: IsA<DeviceState>,
+ {
fn do_init_clock_in(
dev: &DeviceState,
name: &str,
@@ -283,10 +284,10 @@ where
unsafe {
let cstr = CString::new(name).unwrap();
let clk = bindings::qdev_init_clock_in(
- dev.as_mut_ptr(),
+ dev.0.as_mut_ptr(),
cstr.as_ptr(),
cb,
- dev.as_void_ptr(),
+ dev.0.as_void_ptr(),
events.0,
);
@@ -303,12 +304,12 @@ where
// SAFETY: the opaque is "this", which is indeed a pointer to T
F::call((unsafe { &*(opaque.cast::<T>()) }, event))
}
- Some(rust_clock_cb::<Self::Target, F>)
+ Some(rust_clock_cb::<T, F>)
} else {
None
};
- do_init_clock_in(self.upcast(), name, cb, events)
+ do_init_clock_in(unsafe { this.upcast_mut() }, name, cb, events)
}
/// Add an output clock named `name`.
@@ -320,16 +321,30 @@ where
/// which Rust code has a reference to a child object) it would be
/// possible for this function to return a `&Clock` too.
#[inline]
- fn init_clock_out(&self, name: &str) -> Owned<Clock> {
+ pub fn init_clock_out<T: DeviceImpl>(this: &mut ParentInit<T>, name: &str) -> Owned<Clock>
+ where
+ T::ParentType: IsA<DeviceState>,
+ {
unsafe {
let cstr = CString::new(name).unwrap();
- let clk = bindings::qdev_init_clock_out(self.upcast().as_mut_ptr(), cstr.as_ptr());
+ let dev: &mut DeviceState = this.upcast_mut();
+ let clk = bindings::qdev_init_clock_out(dev.0.as_mut_ptr(), cstr.as_ptr());
let clk: &Clock = Clock::from_raw(clk);
Owned::from(clk)
}
}
+}
+/// Trait for methods exposed by the [`DeviceState`] class. The methods can be
+/// called on all objects that have the trait `IsA<DeviceState>`.
+///
+/// The trait should only be used through the blanket implementation,
+/// which guarantees safety via `IsA`.
+pub trait DeviceMethods: ObjectDeref
+where
+ Self::Target: IsA<DeviceState>,
+{
fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
assert!(bql_locked());
let c_propname = CString::new(propname).unwrap();
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 41e5a5e..e20ee01 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -95,7 +95,8 @@
use std::{
ffi::{c_void, CStr},
fmt,
- mem::ManuallyDrop,
+ marker::PhantomData,
+ mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut},
ptr::NonNull,
};
@@ -206,13 +207,190 @@ impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> {
}
}
+/// This struct knows that the superclasses of the object have already been
+/// initialized.
+///
+/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a
+/// technique that is found in several crates, the main ones probably being
+/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/))
+/// and `generativity`.
+///
+/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to
+/// the lifetime argument `'init`. This, together with the `for<'...>` in
+/// `[ParentInit::with]`, block any attempt of the compiler to be creative when
+/// operating on types of type `ParentInit` and to extend their lifetimes. In
+/// particular, it ensures that the `ParentInit` cannot be made to outlive the
+/// `rust_instance_init()` function that creates it, and therefore that the
+/// `&'init T` reference is valid.
+///
+/// This implementation of the same concept, without the QOM baggage, can help
+/// understanding the effect:
+///
+/// ```
+/// use std::marker::PhantomData;
+///
+/// #[derive(PartialEq, Eq)]
+/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData<fn(&'closure ()) -> &'closure ()>);
+///
+/// impl<'closure, T: Copy> Jail<'closure, T> {
+/// fn get(&self) -> T {
+/// *self.0
+/// }
+///
+/// #[inline]
+/// fn with<U>(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U {
+/// let parent_init = Jail(&v, PhantomData);
+/// f(parent_init)
+/// }
+/// }
+/// ```
+///
+/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the
+/// closure:
+///
+/// ```ignore
+/// let x = 42;
+/// let escape = Jail::with(&x, |token1| {
+/// println!("{}", token1.get());
+/// // fails to compile...
+/// token1
+/// });
+/// // ... so you cannot do this:
+/// println!("{}", escape.get());
+/// ```
+///
+/// Likewise, in the QOM case the `ParentInit` cannot be moved out of
+/// `instance_init()`. Without this trick it would be possible to stash a
+/// `ParentInit` and use it later to access uninitialized memory.
+///
+/// Here is another example, showing how separately-created "identities" stay
+/// isolated:
+///
+/// ```ignore
+/// impl<'closure, T: Copy> Clone for Jail<'closure, T> {
+/// fn clone(&self) -> Jail<'closure, T> {
+/// Jail(self.0, PhantomData)
+/// }
+/// }
+///
+/// fn main() {
+/// Jail::with(42, |token1| {
+/// // this works and returns true: the clone has the same "identity"
+/// println!("{}", token1 == token1.clone());
+/// Jail::with(42, |token2| {
+/// // here the outer token remains accessible...
+/// println!("{}", token1.get());
+/// // ... but the two are separate: this fails to compile:
+/// println!("{}", token1 == token2);
+/// });
+/// });
+/// }
+/// ```
+pub struct ParentInit<'init, T>(
+ &'init mut MaybeUninit<T>,
+ PhantomData<fn(&'init ()) -> &'init ()>,
+);
+
+impl<'init, T> ParentInit<'init, T> {
+ #[inline]
+ pub fn with(obj: &'init mut MaybeUninit<T>, f: impl for<'id> FnOnce(ParentInit<'id, T>)) {
+ let parent_init = ParentInit(obj, PhantomData);
+ f(parent_init)
+ }
+}
+
+impl<T: ObjectType> ParentInit<'_, T> {
+ /// Return the receiver as a mutable raw pointer to Object.
+ ///
+ /// # Safety
+ ///
+ /// Fields beyond `Object` could be uninitialized and it's your
+ /// responsibility to avoid that they're used when the pointer is
+ /// dereferenced, either directly or through a cast.
+ pub fn as_object_mut_ptr(&self) -> *mut bindings::Object {
+ self.as_object_ptr().cast_mut()
+ }
+
+ /// Return the receiver as a mutable raw pointer to Object.
+ ///
+ /// # Safety
+ ///
+ /// Fields beyond `Object` could be uninitialized and it's your
+ /// responsibility to avoid that they're used when the pointer is
+ /// dereferenced, either directly or through a cast.
+ pub fn as_object_ptr(&self) -> *const bindings::Object {
+ self.0.as_ptr().cast()
+ }
+}
+
+impl<'a, T: ObjectImpl> ParentInit<'a, T> {
+ /// Convert from a derived type to one of its parent types, which
+ /// have already been initialized.
+ ///
+ /// # Safety
+ ///
+ /// Structurally this is always a safe operation; the [`IsA`] trait
+ /// provides static verification trait that `Self` dereferences to `U` or
+ /// a child of `U`, and only parent types of `T` are allowed.
+ ///
+ /// However, while the fields of the resulting reference are initialized,
+ /// calls might use uninitialized fields of the subclass. It is your
+ /// responsibility to avoid this.
+ pub unsafe fn upcast<U: ObjectType>(&self) -> &'a U
+ where
+ T::ParentType: IsA<U>,
+ {
+ // SAFETY: soundness is declared via IsA<U>, which is an unsafe trait;
+ // the parent has been initialized before `instance_init `is called
+ unsafe { &*(self.0.as_ptr().cast::<U>()) }
+ }
+
+ /// Convert from a derived type to one of its parent types, which
+ /// have already been initialized.
+ ///
+ /// # Safety
+ ///
+ /// Structurally this is always a safe operation; the [`IsA`] trait
+ /// provides static verification trait that `Self` dereferences to `U` or
+ /// a child of `U`, and only parent types of `T` are allowed.
+ ///
+ /// However, while the fields of the resulting reference are initialized,
+ /// calls might use uninitialized fields of the subclass. It is your
+ /// responsibility to avoid this.
+ pub unsafe fn upcast_mut<U: ObjectType>(&mut self) -> &'a mut U
+ where
+ T::ParentType: IsA<U>,
+ {
+ // SAFETY: soundness is declared via IsA<U>, which is an unsafe trait;
+ // the parent has been initialized before `instance_init `is called
+ unsafe { &mut *(self.0.as_mut_ptr().cast::<U>()) }
+ }
+}
+
+impl<T> Deref for ParentInit<'_, T> {
+ type Target = MaybeUninit<T>;
+
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+
+impl<T> DerefMut for ParentInit<'_, T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.0
+ }
+}
+
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut bindings::Object) {
- let mut state = NonNull::new(obj).unwrap().cast::<T>();
+ let mut state = NonNull::new(obj).unwrap().cast::<MaybeUninit<T>>();
+
// SAFETY: obj is an instance of T, since rust_instance_init<T>
// is called from QOM core as the instance_init function
// for class T
unsafe {
- T::INSTANCE_INIT.unwrap()(state.as_mut());
+ ParentInit::with(state.as_mut(), |parent_init| {
+ T::INSTANCE_INIT.unwrap()(parent_init);
+ });
}
}
@@ -291,7 +469,7 @@ pub unsafe trait ObjectType: Sized {
}
/// Return the receiver as a const raw pointer to Object.
- /// This is preferrable to `as_object_mut_ptr()` if a C
+ /// This is preferable to `as_object_mut_ptr()` if a C
/// function only needs a `const Object *`.
fn as_object_ptr(&self) -> *const bindings::Object {
self.as_object().as_ptr()
@@ -479,13 +657,13 @@ pub trait ObjectImpl: ObjectType + IsA<Object> {
///
/// FIXME: The argument is not really a valid reference. `&mut
/// MaybeUninit<Self>` would be a better description.
- const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = None;
+ const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = None;
/// Function that is called to finish initialization of an object, once
/// `INSTANCE_INIT` functions have been called.
const INSTANCE_POST_INIT: Option<fn(&Self)> = None;
- /// Called on descendent classes after all parent class initialization
+ /// Called on descendant classes after all parent class initialization
/// has occurred, but before the class itself is initialized. This
/// is only useful if a class is not a leaf, and can be used to undo
/// the effects of copying the contents of the parent's class struct
diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs
index 868bd88..0a2d111 100644
--- a/rust/qemu-api/src/timer.rs
+++ b/rust/qemu-api/src/timer.rs
@@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
-// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{
diff --git a/rust/qemu-api/src/uninit.rs b/rust/qemu-api/src/uninit.rs
new file mode 100644
index 0000000..04123b4
--- /dev/null
+++ b/rust/qemu-api/src/uninit.rs
@@ -0,0 +1,85 @@
+//! Access fields of a [`MaybeUninit`]
+
+use std::{
+ mem::MaybeUninit,
+ ops::{Deref, DerefMut},
+};
+
+pub struct MaybeUninitField<'a, T, U> {
+ parent: &'a mut MaybeUninit<T>,
+ child: *mut U,
+}
+
+impl<'a, T, U> MaybeUninitField<'a, T, U> {
+ #[doc(hidden)]
+ pub fn new(parent: &'a mut MaybeUninit<T>, child: *mut U) -> Self {
+ MaybeUninitField { parent, child }
+ }
+
+ /// Return a constant pointer to the containing object of the field.
+ ///
+ /// Because the `MaybeUninitField` remembers the containing object,
+ /// it is possible to use it in foreign APIs that initialize the
+ /// child.
+ pub fn parent(f: &Self) -> *const T {
+ f.parent.as_ptr()
+ }
+
+ /// Return a mutable pointer to the containing object.
+ ///
+ /// Because the `MaybeUninitField` remembers the containing object,
+ /// it is possible to use it in foreign APIs that initialize the
+ /// child.
+ pub fn parent_mut(f: &mut Self) -> *mut T {
+ f.parent.as_mut_ptr()
+ }
+}
+
+impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> {
+ type Target = MaybeUninit<U>;
+
+ fn deref(&self) -> &MaybeUninit<U> {
+ // SAFETY: self.child was obtained by dereferencing a valid mutable
+ // reference; the content of the memory may be invalid or uninitialized
+ // but MaybeUninit<_> makes no assumption on it
+ unsafe { &*(self.child.cast()) }
+ }
+}
+
+impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> {
+ fn deref_mut(&mut self) -> &mut MaybeUninit<U> {
+ // SAFETY: self.child was obtained by dereferencing a valid mutable
+ // reference; the content of the memory may be invalid or uninitialized
+ // but MaybeUninit<_> makes no assumption on it
+ unsafe { &mut *(self.child.cast()) }
+ }
+}
+
+/// ```
+/// #[derive(Debug)]
+/// struct S {
+/// x: u32,
+/// y: u32,
+/// }
+///
+/// # use std::mem::MaybeUninit;
+/// # use qemu_api::{assert_match, uninit_field_mut};
+///
+/// let mut s: MaybeUninit<S> = MaybeUninit::zeroed();
+/// uninit_field_mut!(s, x).write(5);
+/// let s = unsafe { s.assume_init() };
+/// assert_match!(s, S { x: 5, y: 0 });
+/// ```
+#[macro_export]
+macro_rules! uninit_field_mut {
+ ($container:expr, $($field:tt)+) => {{
+ let container__: &mut ::std::mem::MaybeUninit<_> = &mut $container;
+ let container_ptr__ = container__.as_mut_ptr();
+
+ // SAFETY: the container is not used directly, only through a MaybeUninit<>,
+ // so the safety is delegated to the caller and to final invocation of
+ // assume_init()
+ let target__ = unsafe { std::ptr::addr_of_mut!((*container_ptr__).$($field)+) };
+ $crate::uninit::MaybeUninitField::new(container__, target__)
+ }};
+}
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 9c8b239..812f390 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -9,7 +9,7 @@
//! * [`vmstate_unused!`](crate::vmstate_unused) and
//! [`vmstate_of!`](crate::vmstate_of), which are used to express the
//! migration format for a struct. This is based on the [`VMState`] trait,
-//! which is defined by all migrateable types.
+//! which is defined by all migratable types.
//!
//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and
//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs
index ad0fc5c..bded836 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/qemu-api/tests/vmstate_tests.rs
@@ -1,5 +1,5 @@
// Copyright (C) 2025 Intel Corporation.
-// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{
diff --git a/rust/wrapper.h b/rust/qemu-api/wrapper.h
index beddd9a..15a1b19 100644
--- a/rust/wrapper.h
+++ b/rust/qemu-api/wrapper.h
@@ -48,6 +48,8 @@ typedef enum memory_order {
#endif /* __CLANG_STDATOMIC_H */
#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/log-for-trace.h"
#include "qemu/module.h"
#include "qemu-io.h"
#include "system/system.h"
@@ -60,6 +62,7 @@ typedef enum memory_order {
#include "hw/qdev-properties-system.h"
#include "hw/irq.h"
#include "qapi/error.h"
+#include "qapi/error-internal.h"
#include "migration/vmstate.h"
#include "chardev/char-serial.h"
#include "exec/memattrs.h"