aboutsummaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
Diffstat (limited to 'rust')
-rw-r--r--rust/Cargo.lock347
-rw-r--r--rust/Cargo.toml30
-rw-r--r--rust/bindings/src/lib.rs (renamed from rust/qemu-api/src/bindings.rs)9
-rw-r--r--rust/bits/Cargo.toml19
-rw-r--r--rust/bits/meson.build16
-rw-r--r--rust/bits/src/lib.rs446
-rw-r--r--rust/bql/Cargo.toml (renamed from rust/qemu-api/Cargo.toml)12
l---------rust/bql/build.rs1
-rw-r--r--rust/bql/meson.build53
-rw-r--r--rust/bql/src/bindings.rs29
-rw-r--r--rust/bql/src/cell.rs (renamed from rust/qemu-api/src/cell.rs)340
-rw-r--r--rust/bql/src/lib.rs29
-rw-r--r--rust/bql/wrapper.h27
-rw-r--r--rust/chardev/Cargo.toml24
l---------rust/chardev/build.rs1
-rw-r--r--rust/chardev/meson.build42
-rw-r--r--rust/chardev/src/bindings.rs40
-rw-r--r--rust/chardev/src/chardev.rs (renamed from rust/qemu-api/src/chardev.rs)25
-rw-r--r--rust/chardev/src/lib.rs6
-rw-r--r--rust/chardev/wrapper.h28
-rw-r--r--rust/clippy.toml3
-rw-r--r--rust/common/Cargo.toml20
-rw-r--r--rust/common/meson.build36
-rw-r--r--rust/common/src/assertions.rs (renamed from rust/qemu-api/src/assertions.rs)24
-rw-r--r--rust/common/src/bitops.rs (renamed from rust/qemu-api/src/bitops.rs)3
-rw-r--r--rust/common/src/callbacks.rs (renamed from rust/qemu-api/src/callbacks.rs)37
-rw-r--r--rust/common/src/errno.rs (renamed from rust/qemu-api/src/errno.rs)19
-rw-r--r--rust/common/src/lib.rs22
-rw-r--r--rust/common/src/opaque.rs236
-rw-r--r--rust/common/src/uninit.rs85
-rw-r--r--rust/common/src/zeroable.rs18
-rw-r--r--rust/hw/char/pl011/Cargo.toml16
l---------rust/hw/char/pl011/build.rs1
-rw-r--r--rust/hw/char/pl011/meson.build51
-rw-r--r--rust/hw/char/pl011/src/bindings.rs32
-rw-r--r--rust/hw/char/pl011/src/device.rs273
-rw-r--r--rust/hw/char/pl011/src/device_class.rs103
-rw-r--r--rust/hw/char/pl011/src/lib.rs2
-rw-r--r--rust/hw/char/pl011/src/registers.rs41
-rw-r--r--rust/hw/char/pl011/wrapper.h51
-rw-r--r--rust/hw/core/Cargo.toml27
l---------rust/hw/core/build.rs1
-rw-r--r--rust/hw/core/meson.build81
-rw-r--r--rust/hw/core/src/bindings.rs44
-rw-r--r--rust/hw/core/src/irq.rs (renamed from rust/qemu-api/src/irq.rs)16
-rw-r--r--rust/hw/core/src/lib.rs15
-rw-r--r--rust/hw/core/src/qdev.rs (renamed from rust/qemu-api/src/qdev.rs)264
-rw-r--r--rust/hw/core/src/sysbus.rs (renamed from rust/qemu-api/src/sysbus.rs)24
-rw-r--r--rust/hw/core/tests/tests.rs (renamed from rust/qemu-api/tests/tests.rs)52
-rw-r--r--rust/hw/core/wrapper.h32
-rw-r--r--rust/hw/timer/hpet/Cargo.toml12
-rw-r--r--rust/hw/timer/hpet/meson.build12
-rw-r--r--rust/hw/timer/hpet/src/device.rs (renamed from rust/hw/timer/hpet/src/hpet.rs)358
-rw-r--r--rust/hw/timer/hpet/src/fw_cfg.rs15
-rw-r--r--rust/hw/timer/hpet/src/lib.rs4
-rw-r--r--rust/meson.build54
-rw-r--r--rust/migration/Cargo.toml21
l---------rust/migration/build.rs1
-rw-r--r--rust/migration/meson.build53
-rw-r--r--rust/migration/src/bindings.rs49
-rw-r--r--rust/migration/src/lib.rs6
-rw-r--r--rust/migration/src/vmstate.rs (renamed from rust/qemu-api/src/vmstate.rs)521
-rw-r--r--rust/migration/wrapper.h (renamed from rust/wrapper.h)17
-rw-r--r--rust/qemu-api-macros/meson.build27
-rw-r--r--rust/qemu-api-macros/src/lib.rs221
-rw-r--r--rust/qemu-api-macros/src/utils.rs26
-rw-r--r--rust/qemu-api/.gitignore2
-rw-r--r--rust/qemu-api/README.md19
-rw-r--r--rust/qemu-api/meson.build80
-rw-r--r--rust/qemu-api/src/lib.rs165
-rw-r--r--rust/qemu-api/src/prelude.rs29
-rw-r--r--rust/qemu-api/src/zeroable.rs37
-rw-r--r--rust/qemu-macros/Cargo.toml (renamed from rust/qemu-api-macros/Cargo.toml)3
-rw-r--r--rust/qemu-macros/meson.build23
-rw-r--r--rust/qemu-macros/src/bits.rs213
-rw-r--r--rust/qemu-macros/src/lib.rs407
-rw-r--r--rust/qemu-macros/src/tests.rs347
-rw-r--r--rust/qom/Cargo.toml24
l---------rust/qom/build.rs1
-rw-r--r--rust/qom/meson.build43
-rw-r--r--rust/qom/src/bindings.rs27
-rw-r--r--rust/qom/src/lib.rs13
-rw-r--r--rust/qom/src/prelude.rs12
-rw-r--r--rust/qom/src/qom.rs (renamed from rust/qemu-api/src/qom.rs)215
-rw-r--r--rust/qom/wrapper.h27
-rw-r--r--rust/system/Cargo.toml22
l---------rust/system/build.rs1
-rw-r--r--rust/system/meson.build43
-rw-r--r--rust/system/src/bindings.rs45
-rw-r--r--rust/system/src/lib.rs6
-rw-r--r--rust/system/src/memory.rs (renamed from rust/qemu-api/src/memory.rs)33
-rw-r--r--rust/system/wrapper.h29
-rw-r--r--rust/tests/Cargo.toml26
-rw-r--r--rust/tests/meson.build14
-rw-r--r--rust/tests/tests/vmstate_tests.rs (renamed from rust/qemu-api/tests/vmstate_tests.rs)162
-rw-r--r--rust/trace/Cargo.toml19
-rw-r--r--rust/trace/meson.build19
-rw-r--r--rust/trace/src/lib.rs39
-rw-r--r--rust/util/Cargo.toml23
-rw-r--r--rust/util/build.rs (renamed from rust/qemu-api/build.rs)18
-rw-r--r--rust/util/meson.build59
-rw-r--r--rust/util/src/bindings.rs27
-rw-r--r--rust/util/src/error.rs416
-rw-r--r--rust/util/src/lib.rs9
-rw-r--r--rust/util/src/log.rs151
-rw-r--r--rust/util/src/module.rs (renamed from rust/qemu-api/src/module.rs)2
-rw-r--r--rust/util/src/timer.rs (renamed from rust/qemu-api/src/timer.rs)18
-rw-r--r--rust/util/wrapper.h32
108 files changed, 5460 insertions, 1990 deletions
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 13d580c..1108513 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -3,12 +3,28 @@
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"
checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d"
[[package]]
+name = "attrs"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a207d40f43de65285f3de0509bb6cb16bc46098864fce957122bbacce327e5f"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
name = "bilge"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -32,17 +48,129 @@ dependencies = [
]
[[package]]
+name = "bits"
+version = "0.1.0"
+dependencies = [
+ "qemu_macros",
+]
+
+[[package]]
+name = "bql"
+version = "0.1.0"
+dependencies = [
+ "glib-sys",
+ "migration",
+]
+
+[[package]]
+name = "cfg-expr"
+version = "0.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a2c5f3bf25ec225351aa1c8e230d04d880d3bd89dea133537dafad4ae291e5c"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "chardev"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "common",
+ "glib-sys",
+ "migration",
+ "qom",
+ "util",
+]
+
+[[package]]
+name = "common"
+version = "0.1.0"
+dependencies = [
+ "libc",
+ "qemu_macros",
+]
+
+[[package]]
name = "either"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "foreign"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
name = "hpet"
version = "0.1.0"
dependencies = [
- "qemu_api",
- "qemu_api_macros",
+ "bql",
+ "common",
+ "hwcore",
+ "migration",
+ "qom",
+ "system",
+ "util",
+]
+
+[[package]]
+name = "hwcore"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "chardev",
+ "common",
+ "glib-sys",
+ "migration",
+ "qemu_macros",
+ "qom",
+ "system",
+ "util",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
+dependencies = [
+ "equivalent",
+ "hashbrown",
]
[[package]]
@@ -61,13 +189,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+
+[[package]]
+name = "migration"
+version = "0.1.0"
+dependencies = [
+ "common",
+ "glib-sys",
+ "util",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
name = "pl011"
version = "0.1.0"
dependencies = [
"bilge",
"bilge-impl",
- "qemu_api",
- "qemu_api_macros",
+ "bits",
+ "bql",
+ "chardev",
+ "common",
+ "glib-sys",
+ "hwcore",
+ "migration",
+ "qom",
+ "system",
+ "trace",
+ "util",
]
[[package]]
@@ -95,28 +253,33 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.84"
+version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
-name = "qemu_api"
+name = "qemu_macros"
version = "0.1.0"
dependencies = [
- "libc",
- "qemu_api_macros",
+ "attrs",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "qemu_api_macros"
+name = "qom"
version = "0.1.0"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "bql",
+ "common",
+ "glib-sys",
+ "migration",
+ "qemu_macros",
+ "util",
]
[[package]]
@@ -129,10 +292,54 @@ dependencies = [
]
[[package]]
+name = "serde"
+version = "1.0.226"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.226"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.226"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
name = "syn"
-version = "2.0.66"
+version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
@@ -140,13 +347,123 @@ dependencies = [
]
[[package]]
+name = "system"
+version = "0.1.0"
+dependencies = [
+ "common",
+ "glib-sys",
+ "qom",
+ "util",
+]
+
+[[package]]
+name = "system-deps"
+version = "7.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
+
+[[package]]
+name = "tests"
+version = "0.1.0"
+dependencies = [
+ "bql",
+ "chardev",
+ "common",
+ "hwcore",
+ "migration",
+ "qom",
+ "system",
+ "util",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "trace"
+version = "0.1.0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
+name = "util"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "common",
+ "foreign",
+ "glib-sys",
+ "libc",
+]
+
+[[package]]
+name = "version-compare"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+
+[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "winnow"
+version = "0.7.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
+dependencies = [
+ "memchr",
+]
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index d9faeec..783e626 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -1,10 +1,19 @@
[workspace]
resolver = "2"
members = [
- "qemu-api-macros",
- "qemu-api",
+ "bits",
+ "bql",
+ "common",
+ "migration",
+ "qemu-macros",
+ "qom",
+ "system",
+ "hw/core",
"hw/char/pl011",
"hw/timer/hpet",
+ "trace",
+ "util",
+ "tests",
]
[workspace.package]
@@ -12,12 +21,18 @@ edition = "2021"
homepage = "https://www.qemu.org"
license = "GPL-2.0-or-later"
repository = "https://gitlab.com/qemu-project/qemu/"
-rust-version = "1.77.0"
+# don't forget to update docs/devel/rust.rst msrv
+rust-version = "1.83.0"
+authors = ["The QEMU Project Developers <qemu-devel@nongnu.org>"]
+
+[workspace.dependencies]
+anyhow = "~1.0"
+foreign = "~0.3.1"
+libc = "0.2.162"
+glib-sys = { version = "0.21.2", features = ["v2_66"] }
[workspace.lints.rust]
-unexpected_cfgs = { level = "deny", check-cfg = [
- 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)',
-] }
+unexpected_cfgs = { level = "deny", check-cfg = ['cfg(MESON)'] }
# Occasionally, we may need to silence warnings and clippy lints that
# were only introduced in newer Rust compiler versions. Do not croak
@@ -52,7 +67,6 @@ as_ptr_cast_mut = "deny"
as_underscore = "deny"
assertions_on_result_states = "deny"
bool_to_int_with_if = "deny"
-borrow_as_ptr = "deny"
cast_lossless = "deny"
dbg_macro = "deny"
debug_assert_with_mut_call = "deny"
@@ -63,10 +77,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/qemu-api/src/bindings.rs b/rust/bindings/src/lib.rs
index 3c1d297..5bf03b1 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/bindings/src/lib.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
)]
@@ -53,3 +54,11 @@ unsafe impl Sync for VMStateField {}
unsafe impl Send for VMStateInfo {}
unsafe impl Sync for VMStateInfo {}
+
+// bindgen does not derive Default here
+#[allow(clippy::derivable_impls)]
+impl Default for VMStateFlags {
+ fn default() -> Self {
+ Self(0)
+ }
+}
diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml
new file mode 100644
index 0000000..7fce972
--- /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_macros = { path = "../qemu-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/bits/meson.build b/rust/bits/meson.build
new file mode 100644
index 0000000..359ca86
--- /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_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..d1141f7
--- /dev/null
+++ b/rust/bits/src/lib.rs
@@ -0,0 +1,446 @@
+// 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 const fn set(&mut self, rhs: Self) {
+ self.0 |= rhs.0;
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const fn clear(&mut self, rhs: Self) {
+ self.0 &= !rhs.0;
+ }
+
+ #[allow(dead_code)]
+ #[inline(always)]
+ pub const 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 } => {
+ $crate::bits_const_internal! { $type @ ($expr) }
+ };
+
+ { $type:ty as $int_type:ty: $expr:expr } => {
+ ($crate::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type
+ };
+}
+
+#[doc(hidden)]
+pub use qemu_macros::bits_const_internal;
+
+#[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/qemu-api/Cargo.toml b/rust/bql/Cargo.toml
index c96cf50..d5177e5 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/bql/Cargo.toml
@@ -1,12 +1,11 @@
[package]
-name = "qemu_api"
+name = "bql"
version = "0.1.0"
-authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
-description = "Rust bindings for QEMU"
-readme = "README.md"
+description = "Rust bindings for QEMU/BQL"
resolver = "2"
publish = false
+authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
@@ -14,12 +13,11 @@ repository.workspace = true
rust-version.workspace = true
[dependencies]
-qemu_api_macros = { path = "../qemu-api-macros" }
-libc = "0.2.162"
+migration = { path = "../migration" }
+glib-sys.workspace = true
[features]
default = ["debug_cell"]
-allocator = []
debug_cell = []
[lints]
diff --git a/rust/bql/build.rs b/rust/bql/build.rs
new file mode 120000
index 0000000..71a3167
--- /dev/null
+++ b/rust/bql/build.rs
@@ -0,0 +1 @@
+../util/build.rs \ No newline at end of file
diff --git a/rust/bql/meson.build b/rust/bql/meson.build
new file mode 100644
index 0000000..22d7c9b
--- /dev/null
+++ b/rust/bql/meson.build
@@ -0,0 +1,53 @@
+_bql_cfg = run_command(rustc_args,
+ '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+ capture: true, check: true).stdout().strip().splitlines()
+
+if get_option('debug_mutex')
+ _bql_cfg += ['--cfg', 'feature="debug_cell"']
+endif
+
+#
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_bql_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common,
+ c_args: bindgen_c_args,
+)
+
+_bql_rs = static_library(
+ 'bql',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/cell.rs',
+ ],
+ {'.': _bql_bindings_inc_rs}
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ rust_args: _bql_cfg,
+ link_with: [_migration_rs],
+ dependencies: [glib_sys_rs],
+)
+
+bql_rs = declare_dependency(link_with: [_bql_rs],
+ dependencies: [qemuutil])
+
+# 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-bql-rs-doctests',
+ _bql_rs,
+ dependencies: bql_rs,
+ suite: ['doc', 'rust'])
diff --git a/rust/bql/src/bindings.rs b/rust/bql/src/bindings.rs
new file mode 100644
index 0000000..8c70f3a
--- /dev/null
+++ b/rust/bql/src/bindings.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+use glib_sys::{
+ guint, GArray, GHashTable, GHashTableIter, GList, GPollFD, GPtrArray, GQueue, GSList, GSource,
+};
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/qemu-api/src/cell.rs b/rust/bql/src/cell.rs
index 05ce09f..24ab294 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/bql/src/cell.rs
@@ -75,10 +75,10 @@
//!
//! ### Example
//!
-//! ```
-//! # use qemu_api::prelude::*;
-//! # use qemu_api::{cell::BqlRefCell, irq::InterruptSource, irq::IRQState};
-//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField};
+//! ```ignore
+//! # use bql::BqlRefCell;
+//! # use qom::{Owned, ParentField};
+//! # use system::{InterruptSource, IRQState, SysBusDevice};
//! # const N_GPIOS: usize = 8;
//! # struct PL061Registers { /* ... */ }
//! # unsafe impl ObjectType for PL061State {
@@ -141,113 +141,17 @@
//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow),
//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The
//! thread will panic if these rules are violated or if the BQL is not held.
-//!
-//! ## Opaque wrappers
-//!
-//! The cell types from the previous section are useful at the boundaries
-//! of code that requires interior mutability. When writing glue code that
-//! interacts directly with C structs, however, it is useful to operate
-//! at a lower level.
-//!
-//! C functions often violate Rust's fundamental assumptions about memory
-//! safety by modifying memory even if it is shared. Furthermore, C structs
-//! often start their life uninitialized and may be populated lazily.
-//!
-//! For this reason, this module provides the [`Opaque<T>`] type to opt out
-//! of Rust's usual guarantees about the wrapped type. Access to the wrapped
-//! value is always through raw pointers, obtained via methods like
-//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These
-//! pointers can then be passed to C functions or dereferenced; both actions
-//! require `unsafe` blocks, making it clear where safety guarantees must be
-//! manually verified. For example
-//!
-//! ```ignore
-//! unsafe {
-//! let state = Opaque::<MyStruct>::uninit();
-//! qemu_struct_init(state.as_mut_ptr());
-//! }
-//! ```
-//!
-//! [`Opaque<T>`] will usually be wrapped one level further, so that
-//! bridge methods can be added to the wrapper:
-//!
-//! ```ignore
-//! pub struct MyStruct(Opaque<bindings::MyStruct>);
-//!
-//! impl MyStruct {
-//! fn new() -> Pin<Box<MyStruct>> {
-//! let result = Box::pin(unsafe { Opaque::uninit() });
-//! unsafe { qemu_struct_init(result.as_mut_ptr()) };
-//! result
-//! }
-//! }
-//! ```
-//!
-//! This pattern of wrapping bindgen-generated types in [`Opaque<T>`] provides
-//! several advantages:
-//!
-//! * The choice of traits to be implemented is not limited by the
-//! bindgen-generated code. For example, [`Drop`] can be added without
-//! disabling [`Copy`] on the underlying bindgen type
-//!
-//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper
-//! type rather than being automatically derived from the C struct's layout
-//!
-//! * Methods can be implemented in a separate crate from the bindgen-generated
-//! bindings
-//!
-//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display)
-//! implementations can be customized to be more readable than the raw C
-//! struct representation
-//!
-//! The [`Opaque<T>`] type does not include BQL validation; it is possible to
-//! assert in the code that the right lock is taken, to use it together
-//! with a custom lock guard type, or to let C code take the lock, as
-//! appropriate. It is also possible to use it with non-thread-safe
-//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`]
-//! it is neither `Sync` nor `Send`.
-//!
-//! While [`Opaque<T>`] is necessary for C interop, it should be used sparingly
-//! and only at FFI boundaries. For QEMU-specific types that need interior
-//! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
-
use std::{
cell::{Cell, UnsafeCell},
cmp::Ordering,
fmt,
- marker::{PhantomData, PhantomPinned},
- mem::{self, MaybeUninit},
+ marker::PhantomData,
+ mem,
ops::{Deref, DerefMut},
ptr::NonNull,
};
-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();
- }
- }
-}
-
-pub fn bql_locked() -> bool {
- // SAFETY: the function does nothing but return a thread-local bool
- !cfg!(MESON) || 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);
- }
- }
-}
+use migration::impl_vmstate_transparent;
/// A mutable memory location that is protected by the Big QEMU Lock.
///
@@ -327,8 +231,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlCell;
+ /// # bql::start_test();
///
/// let c = BqlCell::new(5);
/// ```
@@ -344,8 +248,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlCell;
+ /// # bql::start_test();
///
/// let c = BqlCell::new(5);
///
@@ -362,8 +266,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlCell;
+ /// # bql::start_test();
///
/// let cell = BqlCell::new(5);
/// assert_eq!(cell.get(), 5);
@@ -372,7 +276,7 @@ impl<T> BqlCell<T> {
/// ```
#[inline]
pub fn replace(&self, val: T) -> T {
- assert!(bql_locked());
+ assert!(crate::is_locked());
// SAFETY: This can cause data races if called from multiple threads,
// but it won't happen as long as C code accesses the value
// under BQL protection only.
@@ -384,8 +288,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlCell;
+ /// # bql::start_test();
///
/// let c = BqlCell::new(5);
/// let five = c.into_inner();
@@ -393,7 +297,7 @@ impl<T> BqlCell<T> {
/// assert_eq!(five, 5);
/// ```
pub fn into_inner(self) -> T {
- assert!(bql_locked());
+ assert!(crate::is_locked());
self.value.into_inner()
}
}
@@ -404,8 +308,8 @@ impl<T: Copy> BqlCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlCell;
+ /// # bql::start_test();
///
/// let c = BqlCell::new(5);
///
@@ -413,7 +317,7 @@ impl<T: Copy> BqlCell<T> {
/// ```
#[inline]
pub fn get(&self) -> T {
- assert!(bql_locked());
+ assert!(crate::is_locked());
// SAFETY: This can cause data races if called from multiple threads,
// but it won't happen as long as C code accesses the value
// under BQL protection only.
@@ -427,8 +331,8 @@ impl<T> BqlCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlCell;
+ /// # bql::start_test();
///
/// let c = BqlCell::new(5);
///
@@ -446,8 +350,8 @@ impl<T: Default> BqlCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlCell;
+ /// # bql::start_test();
///
/// let c = BqlCell::new(5);
/// let five = c.take();
@@ -460,6 +364,8 @@ impl<T: Default> BqlCell<T> {
}
}
+impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
+
/// A mutable memory location with dynamically checked borrow rules,
/// protected by the Big QEMU Lock.
///
@@ -516,7 +422,7 @@ impl<T> BqlRefCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlRefCell;
+ /// use bql::BqlRefCell;
///
/// let c = BqlRefCell::new(5);
/// ```
@@ -575,8 +481,8 @@ impl<T> BqlRefCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlRefCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlRefCell;
+ /// # bql::start_test();
///
/// let c = BqlRefCell::new(5);
///
@@ -587,8 +493,8 @@ impl<T> BqlRefCell<T> {
/// An example of panic:
///
/// ```should_panic
- /// use qemu_api::cell::BqlRefCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlRefCell;
+ /// # bql::start_test();
///
/// let c = BqlRefCell::new(5);
///
@@ -605,7 +511,7 @@ impl<T> BqlRefCell<T> {
self.borrowed_at.set(Some(std::panic::Location::caller()));
}
- bql_block_unlock(true);
+ crate::block_unlock(true);
// SAFETY: `BorrowRef` ensures that there is only immutable access
// to the value while borrowed.
@@ -629,8 +535,8 @@ impl<T> BqlRefCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlRefCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlRefCell;
+ /// # bql::start_test();
///
/// let c = BqlRefCell::new("hello".to_owned());
///
@@ -642,8 +548,8 @@ impl<T> BqlRefCell<T> {
/// An example of panic:
///
/// ```should_panic
- /// use qemu_api::cell::BqlRefCell;
- /// # qemu_api::cell::bql_start_test();
+ /// use bql::BqlRefCell;
+ /// # bql::start_test();
///
/// let c = BqlRefCell::new(5);
/// let m = c.borrow();
@@ -660,7 +566,7 @@ impl<T> BqlRefCell<T> {
}
// SAFETY: this only adjusts a counter
- bql_block_unlock(true);
+ crate::block_unlock(true);
// SAFETY: `BorrowRefMut` guarantees unique access.
let value = unsafe { NonNull::new_unchecked(self.value.get()) };
@@ -679,7 +585,7 @@ impl<T> BqlRefCell<T> {
/// # Examples
///
/// ```
- /// use qemu_api::cell::BqlRefCell;
+ /// use bql::BqlRefCell;
///
/// let c = BqlRefCell::new(5);
///
@@ -768,6 +674,8 @@ impl<T> From<T> for BqlRefCell<T> {
}
}
+impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
+
struct BorrowRef<'b> {
borrow: &'b Cell<BorrowFlag>,
}
@@ -804,7 +712,7 @@ impl Drop for BorrowRef<'_> {
let borrow = self.borrow.get();
debug_assert!(is_reading(borrow));
self.borrow.set(borrow - 1);
- bql_block_unlock(false)
+ crate::block_unlock(false)
}
}
@@ -894,7 +802,7 @@ impl Drop for BorrowRefMut<'_> {
let borrow = self.borrow.get();
debug_assert!(is_writing(borrow));
self.borrow.set(borrow + 1);
- bql_block_unlock(false)
+ crate::block_unlock(false)
}
}
@@ -939,167 +847,3 @@ impl<T: fmt::Display> fmt::Display for BqlRefMut<'_, T> {
(**self).fmt(f)
}
}
-
-/// Stores an opaque value that is shared with C code.
-///
-/// Often, C structs can changed when calling a C function even if they are
-/// behind a shared Rust reference, or they can be initialized lazily and have
-/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's
-/// strict aliasing rules, which normally prevent mutation through shared
-/// references.
-///
-/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
-/// assume the usual constraints that Rust structs require, and allows using
-/// shared references on the Rust side.
-///
-/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
-/// of `T`.
-#[repr(transparent)]
-pub struct Opaque<T> {
- value: UnsafeCell<MaybeUninit<T>>,
- // PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
- // one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
- // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
- _pin: PhantomPinned,
-}
-
-impl<T> Opaque<T> {
- /// Creates a new shared reference from a C pointer
- ///
- /// # Safety
- ///
- /// The pointer must be valid, though it need not point to a valid value.
- pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
- let ptr = NonNull::new(ptr).unwrap().cast::<Self>();
- // SAFETY: Self is a transparent wrapper over T
- unsafe { ptr.as_ref() }
- }
-
- /// Creates a new opaque object with uninitialized contents.
- ///
- /// # Safety
- ///
- /// Ultimately the pointer to the returned value will be dereferenced
- /// in another `unsafe` block, for example when passing it to a C function,
- /// but the functions containing the dereference are usually safe. The
- /// value returned from `uninit()` must be initialized and pinned before
- /// calling them.
- #[allow(clippy::missing_const_for_fn)]
- pub unsafe fn uninit() -> Self {
- Self {
- value: UnsafeCell::new(MaybeUninit::uninit()),
- _pin: PhantomPinned,
- }
- }
-
- /// Creates a new opaque object with zeroed contents.
- ///
- /// # Safety
- ///
- /// Ultimately the pointer to the returned value will be dereferenced
- /// in another `unsafe` block, for example when passing it to a C function,
- /// but the functions containing the dereference are usually safe. The
- /// value returned from `uninit()` must be pinned (and possibly initialized)
- /// before calling them.
- #[allow(clippy::missing_const_for_fn)]
- pub unsafe fn zeroed() -> Self {
- Self {
- value: UnsafeCell::new(MaybeUninit::zeroed()),
- _pin: PhantomPinned,
- }
- }
-
- /// Returns a raw mutable pointer to the opaque data.
- pub const fn as_mut_ptr(&self) -> *mut T {
- UnsafeCell::get(&self.value).cast()
- }
-
- /// Returns a raw pointer to the opaque data.
- pub const fn as_ptr(&self) -> *const T {
- self.as_mut_ptr().cast_const()
- }
-
- /// Returns a raw pointer to the opaque data that can be passed to a
- /// C function as `void *`.
- pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void {
- UnsafeCell::get(&self.value).cast()
- }
-
- /// Converts a raw pointer to the wrapped type.
- pub const fn raw_get(slot: *mut Self) -> *mut T {
- // Compare with Linux's raw_get method, which goes through an UnsafeCell
- // because it takes a *const Self instead.
- slot.cast()
- }
-}
-
-impl<T> fmt::Debug for Opaque<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut name: String = "Opaque<".to_string();
- name += std::any::type_name::<T>();
- name += ">";
- f.debug_tuple(&name).field(&self.as_ptr()).finish()
- }
-}
-
-impl<T: Default> Opaque<T> {
- /// Creates a new opaque object with default contents.
- ///
- /// # Safety
- ///
- /// Ultimately the pointer to the returned value will be dereferenced
- /// in another `unsafe` block, for example when passing it to a C function,
- /// but the functions containing the dereference are usually safe. The
- /// value returned from `uninit()` must be pinned before calling them.
- pub unsafe fn new() -> Self {
- Self {
- value: UnsafeCell::new(MaybeUninit::new(T::default())),
- _pin: PhantomPinned,
- }
- }
-}
-
-/// Annotates [`Self`] as a transparent wrapper for another type.
-///
-/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro.
-///
-/// # Examples
-///
-/// ```
-/// # use std::mem::ManuallyDrop;
-/// # use qemu_api::cell::Wrapper;
-/// #[repr(transparent)]
-/// pub struct Example {
-/// inner: ManuallyDrop<String>,
-/// }
-///
-/// unsafe impl Wrapper for Example {
-/// type Wrapped = String;
-/// }
-/// ```
-///
-/// # Safety
-///
-/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type,
-/// whether directly or indirectly.
-///
-/// # Methods
-///
-/// By convention, types that implement Wrapper also implement the following
-/// methods:
-///
-/// ```ignore
-/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self;
-/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped;
-/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped;
-/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped;
-/// ```
-///
-/// They are not defined here to allow them to be `const`.
-pub unsafe trait Wrapper {
- type Wrapped;
-}
-
-unsafe impl<T> Wrapper for Opaque<T> {
- type Wrapped = T;
-}
diff --git a/rust/bql/src/lib.rs b/rust/bql/src/lib.rs
new file mode 100644
index 0000000..ef08221
--- /dev/null
+++ b/rust/bql/src/lib.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+mod bindings;
+use bindings::{bql_block_unlock, bql_locked, rust_bql_mock_lock};
+
+mod cell;
+pub use cell::*;
+
+/// An internal function that is used by doctests.
+pub fn start_test() {
+ // 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 {
+ rust_bql_mock_lock();
+ }
+}
+
+pub fn is_locked() -> bool {
+ // SAFETY: the function does nothing but return a thread-local bool
+ unsafe { bql_locked() }
+}
+
+pub fn block_unlock(increase: bool) {
+ // SAFETY: this only adjusts a counter
+ unsafe {
+ bql_block_unlock(increase);
+ }
+}
diff --git a/rust/bql/wrapper.h b/rust/bql/wrapper.h
new file mode 100644
index 0000000..2ef9a96
--- /dev/null
+++ b/rust/bql/wrapper.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "qemu/main-loop.h"
diff --git a/rust/chardev/Cargo.toml b/rust/chardev/Cargo.toml
new file mode 100644
index 0000000..f105189
--- /dev/null
+++ b/rust/chardev/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "chardev"
+version = "0.1.0"
+description = "Rust bindings for QEMU/chardev"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+glib-sys = { workspace = true }
+common = { path = "../common" }
+bql = { path = "../bql" }
+migration = { path = "../migration" }
+qom = { path = "../qom" }
+util = { path = "../util" }
+
+[lints]
+workspace = true
diff --git a/rust/chardev/build.rs b/rust/chardev/build.rs
new file mode 120000
index 0000000..71a3167
--- /dev/null
+++ b/rust/chardev/build.rs
@@ -0,0 +1 @@
+../util/build.rs \ No newline at end of file
diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build
new file mode 100644
index 0000000..d365d8d
--- /dev/null
+++ b/rust/chardev/meson.build
@@ -0,0 +1,42 @@
+c_enums = [
+ 'QEMUChrEvent',
+]
+_chardev_bindgen_args = []
+foreach enum : c_enums
+ _chardev_bindgen_args += ['--rustified-enum', enum]
+endforeach
+
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_chardev_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common + _chardev_bindgen_args,
+ c_args: bindgen_c_args,
+)
+
+_chardev_rs = static_library(
+ 'chardev',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/chardev.rs',
+ ],
+ {'.': _chardev_bindings_inc_rs}
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs],
+ dependencies: [glib_sys_rs, common_rs, qemu_macros],
+)
+
+chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil])
diff --git a/rust/chardev/src/bindings.rs b/rust/chardev/src/bindings.rs
new file mode 100644
index 0000000..c95dc89
--- /dev/null
+++ b/rust/chardev/src/bindings.rs
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+use common::Zeroable;
+use glib_sys::{
+ gboolean, guint, GArray, GHashTable, GHashTableIter, GIOCondition, GList, GMainContext,
+ GPollFD, GPtrArray, GQueue, GSList, GSource, GSourceFunc,
+};
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+// SAFETY: these are implemented in C; the bindings need to assert that the
+// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
+// When bindings for character devices are introduced, this can be
+// moved to the Opaque<> wrapper in src/chardev.rs.
+unsafe impl Send for CharBackend {}
+unsafe impl Sync for CharBackend {}
+
+unsafe impl Zeroable for CharBackend {}
diff --git a/rust/qemu-api/src/chardev.rs b/rust/chardev/src/chardev.rs
index 6e0590d..2014479 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/chardev/src/chardev.rs
@@ -18,16 +18,15 @@ use std::{
slice,
};
-use crate::{
- bindings,
- callbacks::FnCall,
- cell::{BqlRefMut, Opaque},
- prelude::*,
-};
+use bql::{BqlRefCell, BqlRefMut};
+use common::{callbacks::FnCall, errno, Opaque};
+use qom::prelude::*;
+
+use crate::bindings;
/// A safe wrapper around [`bindings::Chardev`].
#[repr(transparent)]
-#[derive(qemu_api_macros::Wrapper)]
+#[derive(common::Wrapper)]
pub struct Chardev(Opaque<bindings::Chardev>);
pub type ChardevClass = bindings::ChardevClass;
@@ -43,13 +42,15 @@ pub struct CharBackend {
_pin: PhantomPinned,
}
-impl Write for BqlRefMut<'_, bindings::CharBackend> {
+pub struct CharBackendMut<'a>(BqlRefMut<'a, bindings::CharBackend>);
+
+impl Write for CharBackendMut<'_> {
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- let chr: &mut bindings::CharBackend = self;
+ let chr: &mut bindings::CharBackend = &mut self.0;
let len = buf.len().try_into().unwrap();
let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) };
@@ -57,7 +58,7 @@ impl Write for BqlRefMut<'_, bindings::CharBackend> {
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
- let chr: &mut bindings::CharBackend = self;
+ let chr: &mut bindings::CharBackend = &mut self.0;
let len = buf.len().try_into().unwrap();
let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) };
@@ -138,7 +139,7 @@ impl CharBackend {
F::call((owner, event))
}
- let _: () = CanReceiveFn::ASSERT_IS_SOME;
+ const { assert!(CanReceiveFn::IS_SOME) };
let receive_cb: Option<unsafe extern "C" fn(*mut c_void, *const u8, c_int)> =
if ReceiveFn::is_some() {
Some(rust_receive_cb::<T, ReceiveFn>)
@@ -197,7 +198,7 @@ impl CharBackend {
/// the big QEMU lock while the character device is borrowed, as
/// that might cause C code to write to the character device.
pub fn borrow_mut(&self) -> impl Write + '_ {
- self.inner.borrow_mut()
+ CharBackendMut(self.inner.borrow_mut())
}
/// Send a continuous stream of zero bits on the line if `enabled` is
diff --git a/rust/chardev/src/lib.rs b/rust/chardev/src/lib.rs
new file mode 100644
index 0000000..2e549f9
--- /dev/null
+++ b/rust/chardev/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+mod chardev;
+pub use chardev::*;
diff --git a/rust/chardev/wrapper.h b/rust/chardev/wrapper.h
new file mode 100644
index 0000000..65ede6ea
--- /dev/null
+++ b/rust/chardev/wrapper.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "chardev/char-fe.h"
+#include "chardev/char-serial.h"
diff --git a/rust/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/common/Cargo.toml b/rust/common/Cargo.toml
new file mode 100644
index 0000000..0e1b4fc
--- /dev/null
+++ b/rust/common/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "common"
+version = "0.1.0"
+description = "Rust common code for QEMU"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+libc.workspace = true
+qemu_macros = { path = "../qemu-macros" }
+
+[lints]
+workspace = true
diff --git a/rust/common/meson.build b/rust/common/meson.build
new file mode 100644
index 0000000..aff601d
--- /dev/null
+++ b/rust/common/meson.build
@@ -0,0 +1,36 @@
+_common_cfg = run_command(rustc_args,
+ '--config-headers', config_host_h, '--features', files('Cargo.toml'),
+ capture: true, check: true).stdout().strip().splitlines()
+
+_common_rs = static_library(
+ 'common',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/assertions.rs',
+ 'src/bitops.rs',
+ 'src/callbacks.rs',
+ 'src/errno.rs',
+ 'src/opaque.rs',
+ 'src/uninit.rs',
+ 'src/zeroable.rs',
+ ],
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ rust_args: _common_cfg,
+ dependencies: [libc_rs, qemu_macros],
+)
+
+common_rs = declare_dependency(link_with: [_common_rs])
+
+rust.test('rust-common-tests', _common_rs,
+ suite: ['unit', 'rust'])
+
+# 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-common-doctests',
+ _common_rs,
+ dependencies: common_rs,
+ suite: ['doc', 'rust'])
diff --git a/rust/qemu-api/src/assertions.rs b/rust/common/src/assertions.rs
index a2d38c8..91f83a5 100644
--- a/rust/qemu-api/src/assertions.rs
+++ b/rust/common/src/assertions.rs
@@ -8,7 +8,7 @@
//! types match the expectations of C code.
//!
//! Documentation is hidden because it only exposes macros, which
-//! are exported directly from `qemu_api`.
+//! are exported directly from `common`.
// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292
// (stackoverflow answers are released under MIT license).
@@ -27,7 +27,7 @@ impl<T> EqType for T {
/// # Examples
///
/// ```
-/// # use qemu_api::assert_same_type;
+/// # use common::assert_same_type;
/// # use std::ops::Deref;
/// assert_same_type!(u32, u32);
/// assert_same_type!(<Box<u32> as Deref>::Target, u32);
@@ -36,7 +36,7 @@ impl<T> EqType for T {
/// Different types will cause a compile failure
///
/// ```compile_fail
-/// # use qemu_api::assert_same_type;
+/// # use common::assert_same_type;
/// assert_same_type!(&Box<u32>, &u32);
/// ```
#[macro_export]
@@ -61,7 +61,7 @@ macro_rules! assert_same_type {
/// # Examples
///
/// ```
-/// # use qemu_api::assert_field_type;
+/// # use common::assert_field_type;
/// pub struct A {
/// field1: u32,
/// }
@@ -72,7 +72,7 @@ macro_rules! assert_same_type {
/// Different types will cause a compile failure
///
/// ```compile_fail
-/// # use qemu_api::assert_field_type;
+/// # use common::assert_field_type;
/// # pub struct A { field1: u32 }
/// assert_field_type!(A, field1, i32);
/// ```
@@ -81,8 +81,8 @@ macro_rules! assert_field_type {
(@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => {
const _: () = {
#[allow(unused)]
- fn assert_field_type($param_name: &$t) {
- fn types_must_be_equal<T, U>(_: &T)
+ const fn assert_field_type($param_name: &$t) {
+ const fn types_must_be_equal<T, U>(_: &T)
where
T: $crate::assertions::EqType<Itself = U>,
{
@@ -95,10 +95,6 @@ macro_rules! assert_field_type {
($t:ty, $i:tt, $ti:ty) => {
$crate::assert_field_type!(@internal v, $ti, $t, v.$i);
};
-
- ($t:ty, $i:tt, $ti:ty, num = $num:ident) => {
- $crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]);
- };
}
/// Assert that an expression matches a pattern. This can also be
@@ -107,7 +103,7 @@ macro_rules! assert_field_type {
/// # Examples
///
/// ```
-/// # use qemu_api::assert_match;
+/// # use common::assert_match;
/// // JoinHandle does not implement `Eq`, therefore the result
/// // does not either.
/// let result: Result<std::thread::JoinHandle<()>, u32> = Err(42);
@@ -136,12 +132,12 @@ macro_rules! assert_match {
/// # Examples
///
/// ```
-/// # use qemu_api::static_assert;
+/// # use common::static_assert;
/// static_assert!("abc".len() == 3);
/// ```
///
/// ```compile_fail
-/// # use qemu_api::static_assert;
+/// # use common::static_assert;
/// static_assert!("abc".len() == 2); // does not compile
/// ```
#[macro_export]
diff --git a/rust/qemu-api/src/bitops.rs b/rust/common/src/bitops.rs
index 023ec1a..06c78c3 100644
--- a/rust/qemu-api/src/bitops.rs
+++ b/rust/common/src/bitops.rs
@@ -1,9 +1,8 @@
// 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.
-//! It is usually included via the `qemu_api` prelude.
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
diff --git a/rust/qemu-api/src/callbacks.rs b/rust/common/src/callbacks.rs
index 9642a16..b8898fe 100644
--- a/rust/qemu-api/src/callbacks.rs
+++ b/rust/common/src/callbacks.rs
@@ -55,7 +55,7 @@ use std::{mem, ptr::NonNull};
/// # Examples
///
/// ```
-/// # use qemu_api::callbacks::FnCall;
+/// # use common::callbacks::FnCall;
/// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
/// F::call((s,))
/// }
@@ -71,7 +71,7 @@ use std::{mem, ptr::NonNull};
/// Attempting to pass a non-zero-sized closure causes a compile-time failure:
///
/// ```compile_fail
-/// # use qemu_api::callbacks::FnCall;
+/// # use common::callbacks::FnCall;
/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String {
/// # F::call((s,))
/// # }
@@ -82,7 +82,7 @@ use std::{mem, ptr::NonNull};
/// `()` can be used to indicate "no function":
///
/// ```
-/// # use qemu_api::callbacks::FnCall;
+/// # use common::callbacks::FnCall;
/// fn optional<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option<String> {
/// if F::IS_SOME {
/// Some(F::call((s,)))
@@ -97,7 +97,7 @@ use std::{mem, ptr::NonNull};
/// Invoking `F::call` will then be a run-time error.
///
/// ```should_panic
-/// # use qemu_api::callbacks::FnCall;
+/// # use common::callbacks::FnCall;
/// # fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
/// # F::call((s,))
/// # }
@@ -113,31 +113,6 @@ use std::{mem, ptr::NonNull};
/// This is always true for zero-capture closures and function pointers, as long
/// as the code is able to name the function in the first place.
pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
- /// Referring to this internal constant asserts that the `Self` type is
- /// zero-sized. Can be replaced by an inline const expression in
- /// Rust 1.79.0+.
- const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 0) };
-
- /// Referring to this constant asserts that the `Self` type is an actual
- /// function type, which can be used to catch incorrect use of `()`
- /// at compile time.
- ///
- /// # Examples
- ///
- /// ```compile_fail
- /// # use qemu_api::callbacks::FnCall;
- /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
- /// let _: () = F::ASSERT_IS_SOME;
- /// F::call((s,))
- /// }
- ///
- /// let s: String = call_it((), "hello world"); // does not compile
- /// ```
- ///
- /// Note that this can be more simply `const { assert!(F::IS_SOME) }` in
- /// Rust 1.79.0 or newer.
- const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) };
-
/// `true` if `Self` is an actual function type and not `()`.
///
/// # Examples
@@ -145,7 +120,7 @@ pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
/// You can use `IS_SOME` to catch this at compile time:
///
/// ```compile_fail
- /// # use qemu_api::callbacks::FnCall;
+ /// # use common::callbacks::FnCall;
/// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
/// const { assert!(F::IS_SOME) }
/// F::call((s,))
@@ -195,7 +170,7 @@ macro_rules! impl_call {
#[inline(always)]
fn call(a: ($($args,)*)) -> R {
- let _: () = Self::ASSERT_ZERO_SIZED;
+ const { assert!(mem::size_of::<Self>() == 0) };
// SAFETY: the safety of this method is the condition for implementing
// `FnCall`. As to the `NonNull` idiom to create a zero-sized type,
diff --git a/rust/qemu-api/src/errno.rs b/rust/common/src/errno.rs
index 18d1014..64b2933 100644
--- a/rust/qemu-api/src/errno.rs
+++ b/rust/common/src/errno.rs
@@ -7,7 +7,10 @@
//! convention. This module provides functions to portably convert an integer
//! into an [`io::Result`] and back.
-use std::{convert::TryFrom, io, io::ErrorKind};
+use std::{
+ convert::{self, TryFrom},
+ io::{self, ErrorKind},
+};
/// An `errno` value that can be converted into an [`io::Error`]
pub struct Errno(pub u16);
@@ -99,6 +102,12 @@ impl From<io::Error> for Errno {
}
}
+impl From<convert::Infallible> for Errno {
+ fn from(_value: convert::Infallible) -> Errno {
+ panic!("unreachable")
+ }
+}
+
/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`]
/// for the "right" set of types.
mod traits {
@@ -176,7 +185,7 @@ use traits::{GetErrno, MergeErrno};
/// are interpreted as negated `errno` and turned into an `Err`.
///
/// ```
-/// # use qemu_api::errno::into_io_result;
+/// # use common::errno::into_io_result;
/// # use std::io::ErrorKind;
/// let ok = into_io_result(1i32).unwrap();
/// assert_eq!(ok, 1u32);
@@ -192,7 +201,7 @@ use traits::{GetErrno, MergeErrno};
/// likely overflows and will panic:
///
/// ```should_panic
-/// # use qemu_api::errno::into_io_result;
+/// # use common::errno::into_io_result;
/// # #[allow(dead_code)]
/// let err = into_io_result(-0x1234_5678i32); // panic
/// ```
@@ -204,7 +213,7 @@ pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> {
/// values to report errors.
///
/// ```
-/// # use qemu_api::errno::into_neg_errno;
+/// # use common::errno::into_neg_errno;
/// # use std::io::{self, ErrorKind};
/// let ok: io::Result<()> = Ok(());
/// assert_eq!(into_neg_errno(ok), 0);
@@ -223,7 +232,7 @@ pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> {
/// positive:
///
/// ```should_panic
-/// # use qemu_api::errno::into_neg_errno;
+/// # use common::errno::into_neg_errno;
/// # use std::io;
/// let err: io::Result<u32> = Ok(0x8899_AABB);
/// into_neg_errno(err) // panic
diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs
new file mode 100644
index 0000000..8311bf9
--- /dev/null
+++ b/rust/common/src/lib.rs
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub use qemu_macros::{TryInto, Wrapper};
+
+pub mod assertions;
+
+pub mod bitops;
+
+pub mod callbacks;
+pub use callbacks::FnCall;
+
+pub mod errno;
+pub use errno::Errno;
+
+pub mod opaque;
+pub use opaque::{Opaque, Wrapper};
+
+pub mod uninit;
+pub use uninit::MaybeUninitField;
+
+pub mod zeroable;
+pub use zeroable::Zeroable;
diff --git a/rust/common/src/opaque.rs b/rust/common/src/opaque.rs
new file mode 100644
index 0000000..c941fb4
--- /dev/null
+++ b/rust/common/src/opaque.rs
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: MIT
+
+//! ## Opaque wrappers
+//!
+//! The cell types from the previous section are useful at the boundaries
+//! of code that requires interior mutability. When writing glue code that
+//! interacts directly with C structs, however, it is useful to operate
+//! at a lower level.
+//!
+//! C functions often violate Rust's fundamental assumptions about memory
+//! safety by modifying memory even if it is shared. Furthermore, C structs
+//! often start their life uninitialized and may be populated lazily.
+//!
+//! For this reason, this module provides the [`Opaque<T>`] type to opt out
+//! of Rust's usual guarantees about the wrapped type. Access to the wrapped
+//! value is always through raw pointers, obtained via methods like
+//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These
+//! pointers can then be passed to C functions or dereferenced; both actions
+//! require `unsafe` blocks, making it clear where safety guarantees must be
+//! manually verified. For example
+//!
+//! ```ignore
+//! unsafe {
+//! let state = Opaque::<MyStruct>::uninit();
+//! qemu_struct_init(state.as_mut_ptr());
+//! }
+//! ```
+//!
+//! [`Opaque<T>`] will usually be wrapped one level further, so that
+//! bridge methods can be added to the wrapper:
+//!
+//! ```ignore
+//! pub struct MyStruct(Opaque<bindings::MyStruct>);
+//!
+//! impl MyStruct {
+//! fn new() -> Pin<Box<MyStruct>> {
+//! let result = Box::pin(unsafe { Opaque::uninit() });
+//! unsafe { qemu_struct_init(result.as_mut_ptr()) };
+//! result
+//! }
+//! }
+//! ```
+//!
+//! This pattern of wrapping bindgen-generated types in [`Opaque<T>`] provides
+//! several advantages:
+//!
+//! * The choice of traits to be implemented is not limited by the
+//! bindgen-generated code. For example, [`Drop`] can be added without
+//! disabling [`Copy`] on the underlying bindgen type
+//!
+//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper
+//! type rather than being automatically derived from the C struct's layout
+//!
+//! * Methods can be implemented in a separate crate from the bindgen-generated
+//! bindings
+//!
+//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display)
+//! implementations can be customized to be more readable than the raw C
+//! struct representation
+//!
+//! The [`Opaque<T>`] type does not include BQL validation; it is possible to
+//! assert in the code that the right lock is taken, to use it together
+//! with a custom lock guard type, or to let C code take the lock, as
+//! appropriate. It is also possible to use it with non-thread-safe
+//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`]
+//! it is neither `Sync` nor `Send`.
+//!
+//! While [`Opaque<T>`] is necessary for C interop, it should be used sparingly
+//! and only at FFI boundaries. For QEMU-specific types that need interior
+//! mutability, prefer [`BqlCell`] or [`BqlRefCell`].
+//!
+//! [`BqlCell`]: ../../bql/cell/struct.BqlCell.html
+//! [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html
+use std::{cell::UnsafeCell, fmt, marker::PhantomPinned, mem::MaybeUninit, ptr::NonNull};
+
+/// Stores an opaque value that is shared with C code.
+///
+/// Often, C structs can changed when calling a C function even if they are
+/// behind a shared Rust reference, or they can be initialized lazily and have
+/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's
+/// strict aliasing rules, which normally prevent mutation through shared
+/// references.
+///
+/// Wrapping the struct with `Opaque<T>` ensures that the Rust compiler does not
+/// assume the usual constraints that Rust structs require, and allows using
+/// shared references on the Rust side.
+///
+/// `Opaque<T>` is `#[repr(transparent)]`, so that it matches the memory layout
+/// of `T`.
+#[repr(transparent)]
+pub struct Opaque<T> {
+ value: UnsafeCell<MaybeUninit<T>>,
+ // PhantomPinned also allows multiple references to the `Opaque<T>`, i.e.
+ // one `&mut Opaque<T>` can coexist with a `&mut T` or any number of `&T`;
+ // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/.
+ _pin: PhantomPinned,
+}
+
+impl<T> Opaque<T> {
+ /// Creates a new shared reference from a C pointer
+ ///
+ /// # Safety
+ ///
+ /// The pointer must be valid, though it need not point to a valid value.
+ pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self {
+ let ptr = NonNull::new(ptr).unwrap().cast::<Self>();
+ // SAFETY: Self is a transparent wrapper over T
+ unsafe { ptr.as_ref() }
+ }
+
+ /// Creates a new opaque object with uninitialized contents.
+ ///
+ /// # Safety
+ ///
+ /// Ultimately the pointer to the returned value will be dereferenced
+ /// in another `unsafe` block, for example when passing it to a C function,
+ /// but the functions containing the dereference are usually safe. The
+ /// value returned from `uninit()` must be initialized and pinned before
+ /// calling them.
+ pub const unsafe fn uninit() -> Self {
+ Self {
+ value: UnsafeCell::new(MaybeUninit::uninit()),
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Creates a new opaque object with zeroed contents.
+ ///
+ /// # Safety
+ ///
+ /// Ultimately the pointer to the returned value will be dereferenced
+ /// in another `unsafe` block, for example when passing it to a C function,
+ /// but the functions containing the dereference are usually safe. The
+ /// value returned from `uninit()` must be pinned (and possibly initialized)
+ /// before calling them.
+ pub const unsafe fn zeroed() -> Self {
+ Self {
+ value: UnsafeCell::new(MaybeUninit::zeroed()),
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns a raw mutable pointer to the opaque data.
+ pub const fn as_mut_ptr(&self) -> *mut T {
+ UnsafeCell::get(&self.value).cast()
+ }
+
+ /// Returns a raw pointer to the opaque data.
+ pub const fn as_ptr(&self) -> *const T {
+ self.as_mut_ptr().cast_const()
+ }
+
+ /// Returns a raw pointer to the opaque data that can be passed to a
+ /// C function as `void *`.
+ pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void {
+ UnsafeCell::get(&self.value).cast()
+ }
+
+ /// Converts a raw pointer to the wrapped type.
+ pub const fn raw_get(slot: *mut Self) -> *mut T {
+ // Compare with Linux's raw_get method, which goes through an UnsafeCell
+ // because it takes a *const Self instead.
+ slot.cast()
+ }
+}
+
+impl<T> fmt::Debug for Opaque<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut name: String = "Opaque<".to_string();
+ name += std::any::type_name::<T>();
+ name += ">";
+ f.debug_tuple(&name).field(&self.as_ptr()).finish()
+ }
+}
+
+impl<T: Default> Opaque<T> {
+ /// Creates a new opaque object with default contents.
+ ///
+ /// # Safety
+ ///
+ /// Ultimately the pointer to the returned value will be dereferenced
+ /// in another `unsafe` block, for example when passing it to a C function,
+ /// but the functions containing the dereference are usually safe. The
+ /// value returned from `uninit()` must be pinned before calling them.
+ pub unsafe fn new() -> Self {
+ Self {
+ value: UnsafeCell::new(MaybeUninit::new(T::default())),
+ _pin: PhantomPinned,
+ }
+ }
+}
+
+/// Annotates [`Self`] as a transparent wrapper for another type.
+///
+/// Usually defined via the [`crate::Wrapper`] derive macro.
+///
+/// # Examples
+///
+/// ```
+/// # use std::mem::ManuallyDrop;
+/// # use common::opaque::Wrapper;
+/// #[repr(transparent)]
+/// pub struct Example {
+/// inner: ManuallyDrop<String>,
+/// }
+///
+/// unsafe impl Wrapper for Example {
+/// type Wrapped = String;
+/// }
+/// ```
+///
+/// # Safety
+///
+/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type,
+/// whether directly or indirectly.
+///
+/// # Methods
+///
+/// By convention, types that implement Wrapper also implement the following
+/// methods:
+///
+/// ```ignore
+/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self;
+/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped;
+/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped;
+/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped;
+/// ```
+///
+/// They are not defined here to allow them to be `const`.
+pub unsafe trait Wrapper {
+ type Wrapped;
+}
+
+unsafe impl<T> Wrapper for Opaque<T> {
+ type Wrapped = T;
+}
diff --git a/rust/common/src/uninit.rs b/rust/common/src/uninit.rs
new file mode 100644
index 0000000..8d021b1
--- /dev/null
+++ b/rust/common/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 const 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 const 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 const fn parent_mut(f: &mut Self) -> *mut T {
+ f.parent.as_mut_ptr()
+ }
+}
+
+impl<T, U> Deref for MaybeUninitField<'_, 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<T, U> DerefMut for MaybeUninitField<'_, 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 common::{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/common/src/zeroable.rs b/rust/common/src/zeroable.rs
new file mode 100644
index 0000000..fd056de
--- /dev/null
+++ b/rust/common/src/zeroable.rs
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Defines a trait for structs that can be safely initialized with zero bytes.
+
+/// Encapsulates the requirement that
+/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
+/// behavior.
+///
+/// # Safety
+///
+/// Do not add this trait to a type unless all-zeroes is a valid value for the
+/// type. In particular, raw pointers can be zero, but references and
+/// `NonNull<T>` cannot.
+pub unsafe trait Zeroable: Default {
+ /// Return a value of Self whose memory representation consists of all
+ /// zeroes, with the possible exclusion of padding bytes.
+ const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() };
+}
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index a1f431a..5b31945 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -12,14 +12,20 @@ license.workspace = true
repository.workspace = true
rust-version.workspace = true
-[lib]
-crate-type = ["staticlib"]
-
[dependencies]
+glib-sys.workspace = true
bilge = { version = "0.2.0" }
bilge-impl = { version = "0.2.0" }
-qemu_api = { path = "../../../qemu-api" }
-qemu_api_macros = { path = "../../../qemu-api-macros" }
+bits = { path = "../../../bits" }
+common = { path = "../../../common" }
+util = { path = "../../../util" }
+bql = { path = "../../../bql" }
+migration = { path = "../../../migration" }
+qom = { path = "../../../qom" }
+chardev = { path = "../../../chardev" }
+system = { path = "../../../system" }
+hwcore = { path = "../../../hw/core" }
+trace = { path = "../../../trace" }
[lints]
workspace = true
diff --git a/rust/hw/char/pl011/build.rs b/rust/hw/char/pl011/build.rs
new file mode 120000
index 0000000..5f5060d
--- /dev/null
+++ b/rust/hw/char/pl011/build.rs
@@ -0,0 +1 @@
+../../../util/build.rs \ No newline at end of file
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 547cca5..33b91f2 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -1,26 +1,51 @@
-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')
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_libpl011_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common,
+ c_args: bindgen_c_args,
+)
_libpl011_rs = static_library(
'pl011',
- files('src/lib.rs'),
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/device.rs',
+ 'src/registers.rs',
+ ],
+ {'.' : _libpl011_bindings_inc_rs},
+ ),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
dependencies: [
- bilge_dep,
- bilge_impl_dep,
- qemu_api,
- qemu_api_macros,
+ bilge_rs,
+ bilge_impl_rs,
+ bits_rs,
+ common_rs,
+ glib_sys_rs,
+ util_rs,
+ migration_rs,
+ bql_rs,
+ qom_rs,
+ chardev_rs,
+ system_rs,
+ hwcore_rs,
+ trace_rs
],
)
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],
variables: {'crate': 'pl011'},
)])
diff --git a/rust/hw/char/pl011/src/bindings.rs b/rust/hw/char/pl011/src/bindings.rs
new file mode 100644
index 0000000..52a76d0
--- /dev/null
+++ b/rust/hw/char/pl011/src/bindings.rs
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+//! `bindgen`-generated declarations.
+
+use glib_sys::{
+ gboolean, guint, GArray, GByteArray, GHashTable, GHashTableIter, GIOCondition, GList,
+ GMainContext, GPollFD, GPtrArray, GQueue, GSList, GSource, GSourceFunc, GString,
+};
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index bde3be6..8889d6e 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -2,25 +2,26 @@
// 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 qemu_api::{
- chardev::{CharBackend, Chardev, Event},
- impl_vmstate_forward,
- irq::{IRQState, InterruptSource},
- memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
- prelude::*,
- qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
- qom::{ObjectImpl, Owned, ParentField},
- static_assert,
- sysbus::{SysBusDevice, SysBusDeviceImpl},
- vmstate::VMStateDescription,
+use std::{ffi::CStr, mem::size_of};
+
+use bql::BqlRefCell;
+use chardev::{CharBackend, Chardev, Event};
+use common::{static_assert, uninit_field_mut};
+use hwcore::{
+ Clock, ClockEvent, DeviceImpl, DeviceMethods, DeviceState, IRQState, InterruptSource,
+ ResetType, ResettablePhasesImpl, SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
};
-
-use crate::{
- device_class,
- registers::{self, Interrupt, RegisterOffset},
+use migration::{
+ self, impl_vmstate_forward, impl_vmstate_struct, vmstate_fields, vmstate_of,
+ vmstate_subsections, vmstate_unused, VMStateDescription, VMStateDescriptionBuilder,
};
+use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
+use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
+use util::{log::Log, log_mask_ln};
+
+use crate::registers::{self, Interrupt, RegisterOffset};
+
+::trace::include_trace!("hw_char");
// TODO: You must disable the UART before any of the control registers are
// reprogrammed. When the UART is disabled in the middle of transmission or
@@ -85,8 +86,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,
@@ -98,12 +99,13 @@ pub struct PL011Registers {
}
#[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qom::Object, hwcore::Device)]
/// PL011 Device Model in QEMU
pub struct PL011State {
pub parent_obj: ParentField<SysBusDevice>,
pub iomem: MemoryRegion,
#[doc(alias = "chr")]
+ #[property(rename = "chardev")]
pub char_backend: CharBackend,
pub regs: BqlRefCell<PL011Registers>,
/// QEMU interrupts
@@ -122,6 +124,7 @@ pub struct PL011State {
#[doc(alias = "clk")]
pub clock: Owned<Clock>,
#[doc(alias = "migrate_clk")]
+ #[property(rename = "migrate-clk", default = true)]
pub migrate_clock: bool,
}
@@ -129,7 +132,7 @@ pub struct PL011State {
// structs, so the size of the Rust version must not be any larger
// than the size of the C one. If this assert triggers you need to
// expand the padding_for_rust[] array in the C PL011State struct.
-static_assert!(size_of::<PL011State>() <= size_of::<qemu_api::bindings::PL011State>());
+static_assert!(size_of::<PL011State>() <= size_of::<crate::bindings::PL011State>());
qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
@@ -163,19 +166,14 @@ 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>;
}
impl DeviceImpl for PL011State {
- fn properties() -> &'static [Property] {
- &device_class::PL011_PROPERTIES
- }
- fn vmsd() -> Option<&'static VMStateDescription> {
- Some(&device_class::VMSTATE_PL011)
- }
- const REALIZE: Option<fn(&Self)> = Some(Self::realize);
+ const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_PL011);
+ const REALIZE: Option<fn(&Self) -> util::Result<()>> = Some(Self::realize);
}
impl ResettablePhasesImpl for PL011State {
@@ -199,9 +197,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
@@ -212,13 +210,7 @@ impl PL011Registers {
(update, result)
}
- pub(self) fn write(
- &mut self,
- offset: RegisterOffset,
- value: u32,
- char_backend: &CharBackend,
- ) -> bool {
- // eprintln!("write offset {offset} value {value}");
+ pub(self) fn write(&mut self, offset: RegisterOffset, value: u32, device: &PL011State) -> bool {
use RegisterOffset::*;
match offset {
DR => return self.write_data_register(value),
@@ -233,9 +225,11 @@ impl PL011Registers {
}
IBRD => {
self.ibrd = value;
+ device.trace_baudrate_change(self.ibrd, self.fbrd);
}
FBRD => {
self.fbrd = value;
+ device.trace_baudrate_change(self.ibrd, self.fbrd);
}
LCR_H => {
let new_val: registers::LineControl = value.into();
@@ -246,7 +240,7 @@ impl PL011Registers {
}
let update = (self.line_control.send_break() != new_val.send_break()) && {
let break_enable = new_val.send_break();
- let _ = char_backend.send_break(break_enable);
+ let _ = device.char_backend.send_break(break_enable);
self.loopback_break(break_enable)
};
self.line_control = new_val;
@@ -263,20 +257,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");
}
}
}
@@ -284,28 +277,36 @@ impl PL011Registers {
}
fn read_data_register(&mut self, update: &mut bool) -> u32 {
+ let depth = self.fifo_depth();
self.flags.set_receive_fifo_full(false);
let c = self.read_fifo[self.read_pos];
if self.read_count > 0 {
self.read_count -= 1;
- self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
+ self.read_pos = (self.read_pos + 1) & (depth - 1);
}
if self.read_count == 0 {
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;
}
+ trace::trace_pl011_read_fifo(self.read_count, depth);
self.receive_status_error_clear.set_from_data(c);
*update = true;
u32::from(c)
}
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 +362,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 +392,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;
@@ -446,21 +447,23 @@ impl PL011Registers {
self.read_fifo[slot] = value;
self.read_count += 1;
self.flags.set_receive_fifo_empty(false);
+ trace::trace_pl011_fifo_rx_put(value.into(), self.read_count, depth);
if self.read_count == depth {
+ trace::trace_pl011_fifo_rx_full();
self.flags.set_receive_fifo_full(true);
}
if self.read_count == self.read_trigger {
- self.int_level |= Interrupt::RX.0;
+ self.int_level |= Interrupt::RX;
return true;
}
false
}
- pub fn post_load(&mut self) -> Result<(), ()> {
+ pub fn post_load(&mut self) -> Result<(), migration::InvalidError> {
/* Sanity-check input state */
if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
- return Err(());
+ return Err(migration::InvalidError);
}
if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
@@ -480,15 +483,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,32 +499,44 @@ 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) {
- /* pl011_trace_baudrate_change(s); */
+ pub fn trace_baudrate_change(&self, ibrd: u32, fbrd: u32) {
+ let divider = 4.0 / f64::from(ibrd * (FBRD_MASK + 1) + fbrd);
+ let hz = self.clock.hz();
+ let rate = if ibrd == 0 {
+ 0
+ } else {
+ ((hz as f64) * divider) as u32
+ };
+ trace::trace_pl011_baudrate_change(rate, hz, ibrd, fbrd);
+ }
+
+ fn clock_update(&self, _event: ClockEvent) {
+ let regs = self.regs.borrow();
+ let (ibrd, fbrd) = (regs.ibrd, regs.fbrd);
+ self.trace_baudrate_change(ibrd, fbrd)
+ }
+
+ pub fn clock_needed(&self) -> bool {
+ self.migrate_clock
}
fn post_init(&self) {
@@ -538,11 +553,12 @@ 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) => {
let (update_irq, result) = self.regs.borrow_mut().read(field);
+ trace::trace_pl011_read(offset, result, c"");
if update_irq {
self.update();
self.char_backend.accept_input();
@@ -557,6 +573,7 @@ impl PL011State {
if let Ok(field) = RegisterOffset::try_from(offset) {
// qemu_chr_fe_write_all() calls into the can_receive
// callback, so handle writes before entering PL011Registers.
+ trace::trace_pl011_write(offset, value as u32, c"");
if field == RegisterOffset::DR {
// ??? Check if transmitter is enabled.
let ch: [u8; 1] = [value as u8];
@@ -565,12 +582,12 @@ impl PL011State {
let _ = self.char_backend.write_all(&ch);
}
- update_irq = self
- .regs
- .borrow_mut()
- .write(field, value as u32, &self.char_backend);
+ update_irq = self.regs.borrow_mut().write(field, value as u32, self);
} 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();
@@ -579,11 +596,19 @@ impl PL011State {
fn can_receive(&self) -> u32 {
let regs = self.regs.borrow();
- // trace_pl011_can_receive(s->lcr, s->read_count, r);
- regs.fifo_depth() - regs.read_count
+ let fifo_available = regs.fifo_depth() - regs.read_count;
+ trace::trace_pl011_can_receive(
+ regs.line_control.into(),
+ regs.read_count,
+ regs.fifo_depth(),
+ fifo_available,
+ );
+ fifo_available
}
fn receive(&self, buf: &[u8]) {
+ trace::trace_pl011_receive(buf.len());
+
let mut regs = self.regs.borrow_mut();
if regs.loopback_enabled() {
// In loopback mode, the RX input signal is internally disconnected
@@ -619,9 +644,10 @@ impl PL011State {
}
}
- fn realize(&self) {
+ fn realize(&self) -> util::Result<()> {
self.char_backend
.enable_handlers(self, Self::can_receive, Self::receive, Self::event);
+ Ok(())
}
fn reset_hold(&self, _type: ResetType) {
@@ -631,25 +657,25 @@ impl PL011State {
fn update(&self) {
let regs = self.regs.borrow();
let flags = regs.int_level & regs.int_enabled;
+ trace::trace_pl011_irq_state(flags != 0);
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
- irq.set(flags & i != 0);
+ irq.set(flags.any_set(i));
}
}
- pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
+ pub fn post_load(&self, _version_id: u8) -> Result<(), migration::InvalidError> {
self.regs.borrow_mut().post_load()
}
}
/// 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
@@ -680,7 +706,7 @@ pub unsafe extern "C" fn pl011_create(
}
#[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qom::Object, hwcore::Device)]
/// PL011 Luminary device model.
pub struct PL011Luminary {
parent_obj: ParentField<PL011State>,
@@ -706,3 +732,56 @@ impl PL011Impl for PL011Luminary {
impl DeviceImpl for PL011Luminary {}
impl ResettablePhasesImpl for PL011Luminary {}
impl SysBusDeviceImpl for PL011Luminary {}
+
+/// Migration subsection for [`PL011State`] clock.
+static VMSTATE_PL011_CLOCK: VMStateDescription<PL011State> =
+ VMStateDescriptionBuilder::<PL011State>::new()
+ .name(c"pl011/clock")
+ .version_id(1)
+ .minimum_version_id(1)
+ .needed(&PL011State::clock_needed)
+ .fields(vmstate_fields! {
+ vmstate_of!(PL011State, clock),
+ })
+ .build();
+
+impl_vmstate_struct!(
+ PL011Registers,
+ VMStateDescriptionBuilder::<PL011Registers>::new()
+ .name(c"pl011/regs")
+ .version_id(2)
+ .minimum_version_id(2)
+ .fields(vmstate_fields! {
+ vmstate_of!(PL011Registers, flags),
+ vmstate_of!(PL011Registers, line_control),
+ vmstate_of!(PL011Registers, receive_status_error_clear),
+ vmstate_of!(PL011Registers, control),
+ vmstate_of!(PL011Registers, dmacr),
+ vmstate_of!(PL011Registers, int_enabled),
+ vmstate_of!(PL011Registers, int_level),
+ vmstate_of!(PL011Registers, read_fifo),
+ vmstate_of!(PL011Registers, ilpr),
+ vmstate_of!(PL011Registers, ibrd),
+ vmstate_of!(PL011Registers, fbrd),
+ vmstate_of!(PL011Registers, ifl),
+ vmstate_of!(PL011Registers, read_pos),
+ vmstate_of!(PL011Registers, read_count),
+ vmstate_of!(PL011Registers, read_trigger),
+ })
+ .build()
+);
+
+pub const VMSTATE_PL011: VMStateDescription<PL011State> =
+ VMStateDescriptionBuilder::<PL011State>::new()
+ .name(c"pl011")
+ .version_id(2)
+ .minimum_version_id(2)
+ .post_load(&PL011State::post_load)
+ .fields(vmstate_fields! {
+ vmstate_unused!(core::mem::size_of::<u32>()),
+ vmstate_of!(PL011State, regs),
+ })
+ .subsections(vmstate_subsections! {
+ VMSTATE_PL011_CLOCK
+ })
+ .build();
diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs
deleted file mode 100644
index d328d84..0000000
--- a/rust/hw/char/pl011/src/device_class.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2024, Linaro Limited
-// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-use std::{
- ffi::{c_int, c_void},
- ptr::NonNull,
-};
-
-use qemu_api::{
- bindings::{qdev_prop_bool, qdev_prop_chr},
- prelude::*,
- vmstate::VMStateDescription,
- vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
- zeroable::Zeroable,
-};
-
-use crate::device::{PL011Registers, PL011State};
-
-extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
- let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
- unsafe { state.as_ref().migrate_clock }
-}
-
-/// Migration subsection for [`PL011State`] clock.
-static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
- name: c"pl011/clock".as_ptr(),
- version_id: 1,
- minimum_version_id: 1,
- needed: Some(pl011_clock_needed),
- fields: vmstate_fields! {
- vmstate_clock!(PL011State, clock),
- },
- ..Zeroable::ZERO
-};
-
-extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
- let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
- let result = unsafe { state.as_ref().post_load(version_id as u32) };
- if result.is_err() {
- -1
- } else {
- 0
- }
-}
-
-static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
- name: c"pl011/regs".as_ptr(),
- version_id: 2,
- minimum_version_id: 2,
- fields: vmstate_fields! {
- vmstate_of!(PL011Registers, flags),
- vmstate_of!(PL011Registers, line_control),
- vmstate_of!(PL011Registers, receive_status_error_clear),
- vmstate_of!(PL011Registers, control),
- vmstate_of!(PL011Registers, dmacr),
- vmstate_of!(PL011Registers, int_enabled),
- vmstate_of!(PL011Registers, int_level),
- vmstate_of!(PL011Registers, read_fifo),
- vmstate_of!(PL011Registers, ilpr),
- vmstate_of!(PL011Registers, ibrd),
- vmstate_of!(PL011Registers, fbrd),
- vmstate_of!(PL011Registers, ifl),
- vmstate_of!(PL011Registers, read_pos),
- vmstate_of!(PL011Registers, read_count),
- vmstate_of!(PL011Registers, read_trigger),
- },
- ..Zeroable::ZERO
-};
-
-pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
- name: c"pl011".as_ptr(),
- version_id: 2,
- minimum_version_id: 2,
- post_load: Some(pl011_post_load),
- fields: vmstate_fields! {
- vmstate_unused!(core::mem::size_of::<u32>()),
- vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>),
- },
- subsections: vmstate_subsections! {
- VMSTATE_PL011_CLOCK
- },
- ..Zeroable::ZERO
-};
-
-qemu_api::declare_properties! {
- PL011_PROPERTIES,
- qemu_api::define_property!(
- c"chardev",
- PL011State,
- char_backend,
- unsafe { &qdev_prop_chr },
- CharBackend
- ),
- qemu_api::define_property!(
- c"migrate-clk",
- PL011State,
- migrate_clock,
- unsafe { &qdev_prop_bool },
- bool,
- default = true
- ),
-}
diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs
index 5c4fbc9..0c19b70 100644
--- a/rust/hw/char/pl011/src/lib.rs
+++ b/rust/hw/char/pl011/src/lib.rs
@@ -12,8 +12,8 @@
//! See [`PL011State`](crate::device::PL011State) for the device model type and
//! the [`registers`] module for register types.
+mod bindings;
mod device;
-mod device_class;
mod registers;
pub use device::pl011_create;
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index 690feb6..0c3a4d7 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -9,13 +9,14 @@
// https://developer.arm.com/documentation/ddi0183/latest/
use bilge::prelude::*;
-use qemu_api::impl_vmstate_bitsized;
+use bits::bits;
+use migration::{impl_vmstate_bitsized, impl_vmstate_forward};
/// Offset of each register from the base memory address of the device.
#[doc(alias = "offset")]
#[allow(non_camel_case_types)]
#[repr(u64)]
-#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)]
+#[derive(Debug, Eq, PartialEq, common::TryInto)]
pub enum RegisterOffset {
/// Data Register
///
@@ -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/char/pl011/wrapper.h b/rust/hw/char/pl011/wrapper.h
new file mode 100644
index 0000000..87a5a58
--- /dev/null
+++ b/rust/hw/char/pl011/wrapper.h
@@ -0,0 +1,51 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2024 Linaro Ltd.
+ *
+ * Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+#include "hw/char/pl011.h"
diff --git a/rust/hw/core/Cargo.toml b/rust/hw/core/Cargo.toml
new file mode 100644
index 0000000..ecfb564
--- /dev/null
+++ b/rust/hw/core/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "hwcore"
+version = "0.1.0"
+description = "Rust bindings for QEMU/hwcore"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+glib-sys.workspace = true
+qemu_macros = { path = "../../qemu-macros" }
+common = { path = "../../common" }
+bql = { path = "../../bql" }
+qom = { path = "../../qom" }
+chardev = { path = "../../chardev" }
+migration = { path = "../../migration" }
+system = { path = "../../system" }
+util = { path = "../../util" }
+
+[lints]
+workspace = true
diff --git a/rust/hw/core/build.rs b/rust/hw/core/build.rs
new file mode 120000
index 0000000..2a79ee3
--- /dev/null
+++ b/rust/hw/core/build.rs
@@ -0,0 +1 @@
+../../util/build.rs \ No newline at end of file
diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build
new file mode 100644
index 0000000..1560dd2
--- /dev/null
+++ b/rust/hw/core/meson.build
@@ -0,0 +1,81 @@
+_hwcore_bindgen_args = []
+c_enums = [
+ 'DeviceCategory',
+ 'GpioPolarity',
+ 'MachineInitPhase',
+ 'ResetType',
+]
+foreach enum : c_enums
+ _hwcore_bindgen_args += ['--rustified-enum', enum]
+endforeach
+
+blocked_type = [
+ 'Chardev',
+ 'Error',
+ 'ObjectClass',
+ 'MemoryRegion',
+ 'VMStateDescription',
+]
+foreach type: blocked_type
+ _hwcore_bindgen_args += ['--blocklist-type', type]
+endforeach
+
+c_bitfields = [
+ 'ClockEvent',
+]
+foreach enum : c_bitfields
+ _hwcore_bindgen_args += ['--bitfield-enum', enum]
+endforeach
+
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_hwcore_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common + _hwcore_bindgen_args,
+ c_args: bindgen_c_args,
+)
+
+_hwcore_rs = static_library(
+ 'hwcore',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/irq.rs',
+ 'src/qdev.rs',
+ 'src/sysbus.rs',
+ ],
+ {'.': _hwcore_bindings_inc_rs}
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _system_rs, _util_rs],
+ dependencies: [glib_sys_rs, qemu_macros, common_rs],
+)
+
+hwcore_rs = declare_dependency(link_with: [_hwcore_rs],
+ dependencies: [qom_rs, hwcore])
+
+test('rust-hwcore-rs-integration',
+ executable(
+ 'rust-hwcore-rs-integration',
+ files('tests/tests.rs'),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_args: ['--test'],
+ install: false,
+ dependencies: [common_rs, hwcore_rs, bql_rs, migration_rs, util_rs]),
+ args: [
+ '--test', '--test-threads', '1',
+ '--format', 'pretty',
+ ],
+ protocol: 'rust',
+ suite: ['unit', 'rust'])
diff --git a/rust/hw/core/src/bindings.rs b/rust/hw/core/src/bindings.rs
new file mode 100644
index 0000000..65b9aae
--- /dev/null
+++ b/rust/hw/core/src/bindings.rs
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+use chardev::bindings::Chardev;
+use common::Zeroable;
+use glib_sys::{
+ GArray, GByteArray, GHashTable, GHashTableIter, GList, GPtrArray, GQueue, GSList, GString,
+};
+use migration::bindings::VMStateDescription;
+use qom::bindings::ObjectClass;
+use system::bindings::MemoryRegion;
+use util::bindings::Error;
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+unsafe impl Send for Property {}
+unsafe impl Sync for Property {}
+
+unsafe impl Send for TypeInfo {}
+unsafe impl Sync for TypeInfo {}
+
+unsafe impl Zeroable for Property__bindgen_ty_1 {}
+unsafe impl Zeroable for Property {}
diff --git a/rust/qemu-api/src/irq.rs b/rust/hw/core/src/irq.rs
index 1526e6f..e0d7784 100644
--- a/rust/qemu-api/src/irq.rs
+++ b/rust/hw/core/src/irq.rs
@@ -10,16 +10,15 @@ use std::{
ptr,
};
-use crate::{
- bindings::{self, qemu_set_irq},
- cell::Opaque,
- prelude::*,
- qom::ObjectClass,
-};
+use bql::BqlCell;
+use common::Opaque;
+use qom::{prelude::*, ObjectClass};
+
+use crate::bindings::{self, qemu_set_irq};
/// An opaque wrapper around [`bindings::IRQState`].
#[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
pub struct IRQState(Opaque<bindings::IRQState>);
/// Interrupt sources are used by devices to pass changes to a value (typically
@@ -34,7 +33,7 @@ pub struct IRQState(Opaque<bindings::IRQState>);
///
/// Interrupts are implemented as a pointer to the interrupt "sink", which has
/// type [`IRQState`]. A device exposes its source as a QOM link property using
-/// a function such as [`SysBusDeviceMethods::init_irq`], and
+/// a function such as [`crate::sysbus::SysBusDeviceMethods::init_irq`], and
/// initially leaves the pointer to a NULL value, representing an unconnected
/// interrupt. To connect it, whoever creates the device fills the pointer with
/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because
@@ -112,4 +111,5 @@ unsafe impl ObjectType for IRQState {
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) };
}
+
qom_isa!(IRQState: Object);
diff --git a/rust/hw/core/src/lib.rs b/rust/hw/core/src/lib.rs
new file mode 100644
index 0000000..b40801e
--- /dev/null
+++ b/rust/hw/core/src/lib.rs
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub use qemu_macros::Device;
+pub use qom;
+
+pub mod bindings;
+
+mod irq;
+pub use irq::*;
+
+mod qdev;
+pub use qdev::*;
+
+mod sysbus;
+pub use sysbus::*;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/hw/core/src/qdev.rs
index 1279d7a..c3097a2 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/hw/core/src/qdev.rs
@@ -6,25 +6,24 @@
use std::{
ffi::{c_int, c_void, CStr, CString},
- ptr::NonNull,
+ ptr::{addr_of, NonNull},
};
-pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
+use chardev::Chardev;
+use common::{callbacks::FnCall, Opaque};
+use migration::{impl_vmstate_c_struct, VMStateDescription};
+use qom::{prelude::*, ObjectClass, ObjectImpl, Owned, ParentInit};
+use util::{Error, Result};
+pub use crate::bindings::{ClockEvent, DeviceClass, Property, ResetType};
use crate::{
- bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass},
- callbacks::FnCall,
- cell::{bql_locked, Opaque},
- chardev::Chardev,
+ bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass},
irq::InterruptSource,
- prelude::*,
- qom::{ObjectClass, ObjectImpl, Owned},
- vmstate::VMStateDescription,
};
/// A safe wrapper around [`bindings::Clock`].
#[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
pub struct Clock(Opaque<bindings::Clock>);
unsafe impl Send for Clock {}
@@ -32,7 +31,7 @@ unsafe impl Sync for Clock {}
/// A safe wrapper around [`bindings::DeviceState`].
#[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
pub struct DeviceState(Opaque<bindings::DeviceState>);
unsafe impl Send for DeviceState {}
@@ -100,29 +99,92 @@ unsafe extern "C" fn rust_resettable_exit_fn<T: ResettablePhasesImpl>(
T::EXIT.unwrap()(unsafe { state.as_ref() }, typ);
}
+/// Helper trait to return pointer to a [`bindings::PropertyInfo`] for a type.
+///
+/// This trait is used by [`qemu_macros::Device`] derive macro.
+///
+/// Base types that already have `qdev_prop_*` globals in the QEMU API should
+/// use those values as exported by the [`bindings`] module, instead of
+/// redefining them.
+///
+/// # Safety
+///
+/// This trait is marked as `unsafe` because `BASE_INFO` and `BIT_INFO` must be
+/// valid raw references to [`bindings::PropertyInfo`].
+///
+/// Note we could not use a regular reference:
+///
+/// ```text
+/// const VALUE: &bindings::PropertyInfo = ...
+/// ```
+///
+/// because this results in the following compiler error:
+///
+/// ```text
+/// constructing invalid value: encountered reference to `extern` static in `const`
+/// ```
+///
+/// This is because the compiler generally might dereference a normal reference
+/// during const evaluation, but not in this case (if it did, it'd need to
+/// dereference the raw pointer so using a `*const` would also fail to compile).
+///
+/// It is the implementer's responsibility to provide a valid
+/// [`bindings::PropertyInfo`] pointer for the trait implementation to be safe.
+pub unsafe trait QDevProp {
+ const BASE_INFO: *const bindings::PropertyInfo;
+ const BIT_INFO: *const bindings::PropertyInfo = {
+ panic!("invalid type for bit property");
+ };
+}
+
+macro_rules! impl_qdev_prop {
+ ($type:ty,$info:ident$(, $bit_info:ident)?) => {
+ unsafe impl $crate::qdev::QDevProp for $type {
+ const BASE_INFO: *const $crate::bindings::PropertyInfo =
+ addr_of!($crate::bindings::$info);
+ $(const BIT_INFO: *const $crate::bindings::PropertyInfo =
+ addr_of!($crate::bindings::$bit_info);)?
+ }
+ };
+}
+
+impl_qdev_prop!(bool, qdev_prop_bool);
+impl_qdev_prop!(u8, qdev_prop_uint8);
+impl_qdev_prop!(u16, qdev_prop_uint16);
+impl_qdev_prop!(u32, qdev_prop_uint32, qdev_prop_bit);
+impl_qdev_prop!(u64, qdev_prop_uint64, qdev_prop_bit64);
+impl_qdev_prop!(usize, qdev_prop_usize);
+impl_qdev_prop!(i32, qdev_prop_int32);
+impl_qdev_prop!(i64, qdev_prop_int64);
+impl_qdev_prop!(chardev::CharBackend, qdev_prop_chr);
+
+/// Trait to define device properties.
+///
+/// # Safety
+///
+/// Caller is responsible for the validity of properties array.
+pub unsafe trait DevicePropertiesImpl {
+ /// An array providing the properties that the user can set on the
+ /// device.
+ const PROPERTIES: &'static [Property] = &[];
+}
+
/// Trait providing the contents of [`DeviceClass`].
-pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
+pub trait DeviceImpl:
+ ObjectImpl + ResettablePhasesImpl + DevicePropertiesImpl + IsA<DeviceState>
+{
/// _Realization_ is the second stage of device creation. It contains
/// all operations that depend on device properties and can fail (note:
/// this is not yet supported for Rust devices).
///
/// If not `None`, the parent class's `realize` method is overridden
/// with the function pointed to by `REALIZE`.
- const REALIZE: Option<fn(&Self)> = None;
-
- /// An array providing the properties that the user can set on the
- /// device. Not a `const` because referencing statics in constants
- /// is unstable until Rust 1.83.0.
- fn properties() -> &'static [Property] {
- &[]
- }
+ const REALIZE: Option<fn(&Self) -> Result<()>> = None;
/// A `VMStateDescription` providing the migration format for the device
/// Not a `const` because referencing statics in constants is unstable
/// until Rust 1.83.0.
- fn vmsd() -> Option<&'static VMStateDescription> {
- None
- }
+ const VMSTATE: Option<VMStateDescription<Self>> = None;
}
/// # Safety
@@ -134,10 +196,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 util::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 {
@@ -168,10 +233,10 @@ impl DeviceClass {
if <T as DeviceImpl>::REALIZE.is_some() {
self.realize = Some(rust_realize_fn::<T>);
}
- if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
- self.vmsd = vmsd;
+ if let Some(ref vmsd) = <T as DeviceImpl>::VMSTATE {
+ self.vmsd = vmsd.as_ref();
}
- let prop = <T as DeviceImpl>::properties();
+ let prop = <T as DevicePropertiesImpl>::PROPERTIES;
if !prop.is_empty() {
unsafe {
bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len());
@@ -183,75 +248,17 @@ impl DeviceClass {
}
}
-#[macro_export]
-macro_rules! define_property {
- ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => {
- $crate::bindings::Property {
- // use associated function syntax for type checking
- name: ::std::ffi::CStr::as_ptr($name),
- info: $prop,
- offset: ::std::mem::offset_of!($state, $field) as isize,
- bitnr: $bitnr,
- set_default: true,
- defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
- ..$crate::zeroable::Zeroable::ZERO
- }
- };
- ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => {
- $crate::bindings::Property {
- // use associated function syntax for type checking
- name: ::std::ffi::CStr::as_ptr($name),
- info: $prop,
- offset: ::std::mem::offset_of!($state, $field) as isize,
- set_default: true,
- defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 },
- ..$crate::zeroable::Zeroable::ZERO
- }
- };
- ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => {
- $crate::bindings::Property {
- // use associated function syntax for type checking
- name: ::std::ffi::CStr::as_ptr($name),
- info: $prop,
- offset: ::std::mem::offset_of!($state, $field) as isize,
- set_default: false,
- ..$crate::zeroable::Zeroable::ZERO
- }
- };
-}
-
-#[macro_export]
-macro_rules! declare_properties {
- ($ident:ident, $($prop:expr),*$(,)*) => {
- pub static $ident: [$crate::bindings::Property; {
- let mut len = 0;
- $({
- _ = stringify!($prop);
- len += 1;
- })*
- len
- }] = [
- $($prop),*,
- ];
- };
-}
-
unsafe impl ObjectType for DeviceState {
type Class = DeviceClass;
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) };
}
+
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,19 +269,22 @@ 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,
cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>,
events: ClockEvent,
) -> Owned<Clock> {
- assert!(bql_locked());
+ assert!(bql::is_locked());
// SAFETY: the clock is heap allocated, but qdev_init_clock_in()
// does not gift the reference to its caller; so use Owned::from to
@@ -283,10 +293,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 +313,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,18 +330,32 @@ 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());
+ assert!(bql::is_locked());
let c_propname = CString::new(propname).unwrap();
let chr: &Chardev = chr;
unsafe {
@@ -358,7 +382,7 @@ where
}
}
- let _: () = F::ASSERT_IS_SOME;
+ const { assert!(F::IS_SOME) };
unsafe extern "C" fn rust_irq_handler<T, F: for<'a> FnCall<(&'a T, u32, u32)>>(
opaque: *mut c_void,
line: c_int,
@@ -387,9 +411,45 @@ where
impl<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {}
+impl Clock {
+ pub const PERIOD_1SEC: u64 = bindings::CLOCK_PERIOD_1SEC;
+
+ pub const fn period_from_ns(ns: u64) -> u64 {
+ ns * Self::PERIOD_1SEC / 1_000_000_000
+ }
+
+ pub const fn period_from_hz(hz: u64) -> u64 {
+ if hz == 0 {
+ 0
+ } else {
+ Self::PERIOD_1SEC / hz
+ }
+ }
+
+ pub const fn period_to_hz(period: u64) -> u64 {
+ if period == 0 {
+ 0
+ } else {
+ Self::PERIOD_1SEC / period
+ }
+ }
+
+ pub const fn period(&self) -> u64 {
+ // SAFETY: Clock is returned by init_clock_in with zero value for period
+ unsafe { &*self.0.as_ptr() }.period
+ }
+
+ pub const fn hz(&self) -> u64 {
+ Self::period_to_hz(self.period())
+ }
+}
+
unsafe impl ObjectType for Clock {
type Class = ObjectClass;
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) };
}
+
qom_isa!(Clock: Object);
+
+impl_vmstate_c_struct!(Clock, bindings::vmstate_clock);
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/hw/core/src/sysbus.rs
index e92502a..282315f 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/hw/core/src/sysbus.rs
@@ -7,20 +7,19 @@
use std::{ffi::CStr, ptr::addr_of_mut};
pub use bindings::SysBusDeviceClass;
+use common::Opaque;
+use qom::{prelude::*, Owned};
+use system::MemoryRegion;
use crate::{
bindings,
- cell::{bql_locked, Opaque},
irq::{IRQState, InterruptSource},
- memory::MemoryRegion,
- prelude::*,
qdev::{DeviceImpl, DeviceState},
- qom::Owned,
};
/// A safe wrapper around [`bindings::SysBusDevice`].
#[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
pub struct SysBusDevice(Opaque<bindings::SysBusDevice>);
unsafe impl Send for SysBusDevice {}
@@ -31,6 +30,7 @@ unsafe impl ObjectType for SysBusDevice {
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) };
}
+
qom_isa!(SysBusDevice: DeviceState, Object);
// TODO: add virtual methods
@@ -55,7 +55,7 @@ where
/// region with a number that corresponds to the order of calls to
/// `init_mmio`.
fn init_mmio(&self, iomem: &MemoryRegion) {
- assert!(bql_locked());
+ assert!(bql::is_locked());
unsafe {
bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr());
}
@@ -66,7 +66,7 @@ where
/// whoever creates the sysbus device will refer to the interrupts with
/// a number that corresponds to the order of calls to `init_irq`.
fn init_irq(&self, irq: &InterruptSource) {
- assert!(bql_locked());
+ assert!(bql::is_locked());
unsafe {
bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr());
}
@@ -74,7 +74,7 @@ where
// TODO: do we want a type like GuestAddress here?
fn mmio_addr(&self, id: u32) -> Option<u64> {
- assert!(bql_locked());
+ assert!(bql::is_locked());
// SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and
// the SysBusDevice must be initialized to get an IsA<SysBusDevice>.
let sbd = unsafe { *self.upcast().as_ptr() };
@@ -88,7 +88,7 @@ where
// TODO: do we want a type like GuestAddress here?
fn mmio_map(&self, id: u32, addr: u64) {
- assert!(bql_locked());
+ assert!(bql::is_locked());
let id: i32 = id.try_into().unwrap();
unsafe {
bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr);
@@ -99,7 +99,7 @@ where
// object_property_set_link) adds a reference to the IRQState,
// which can prolong its life
fn connect_irq(&self, id: u32, irq: &Owned<IRQState>) {
- assert!(bql_locked());
+ assert!(bql::is_locked());
let id: i32 = id.try_into().unwrap();
let irq: &IRQState = irq;
unsafe {
@@ -109,11 +109,11 @@ where
fn sysbus_realize(&self) {
// TODO: return an Error
- assert!(bql_locked());
+ assert!(bql::is_locked());
unsafe {
bindings::sysbus_realize(
self.upcast().as_mut_ptr(),
- addr_of_mut!(bindings::error_fatal),
+ addr_of_mut!(util::bindings::error_fatal),
);
}
}
diff --git a/rust/qemu-api/tests/tests.rs b/rust/hw/core/tests/tests.rs
index a658a49..247d812 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/hw/core/tests/tests.rs
@@ -4,31 +4,23 @@
use std::{ffi::CStr, ptr::addr_of};
-use qemu_api::{
- bindings::{module_call_init, module_init_type, qdev_prop_bool},
- cell::{self, BqlCell},
- declare_properties, define_property,
- prelude::*,
- qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
- qom::{ObjectImpl, ParentField},
- sysbus::SysBusDevice,
- vmstate::VMStateDescription,
- zeroable::Zeroable,
-};
-
-mod vmstate_tests;
+use bql::BqlCell;
+use hwcore::{DeviceImpl, DeviceState, ResettablePhasesImpl, SysBusDevice};
+use migration::{VMStateDescription, VMStateDescriptionBuilder};
+use qom::{prelude::*, ObjectImpl, ParentField};
+use util::bindings::{module_call_init, module_init_type};
// Test that macros can compile.
-pub static VMSTATE: VMStateDescription = VMStateDescription {
- name: c"name".as_ptr(),
- unmigratable: true,
- ..Zeroable::ZERO
-};
+pub const VMSTATE: VMStateDescription<DummyState> = VMStateDescriptionBuilder::<DummyState>::new()
+ .name(c"name")
+ .unmigratable()
+ .build();
#[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qom::Object, hwcore::Device)]
pub struct DummyState {
parent: ParentField<DeviceState>,
+ #[property(rename = "migrate-clk", default = true)]
migrate_clock: bool,
}
@@ -44,17 +36,6 @@ impl DummyClass {
}
}
-declare_properties! {
- DUMMY_PROPERTIES,
- define_property!(
- c"migrate-clk",
- DummyState,
- migrate_clock,
- unsafe { &qdev_prop_bool },
- bool
- ),
-}
-
unsafe impl ObjectType for DummyState {
type Class = DummyClass;
const TYPE_NAME: &'static CStr = c"dummy";
@@ -69,16 +50,11 @@ impl ObjectImpl for DummyState {
impl ResettablePhasesImpl for DummyState {}
impl DeviceImpl for DummyState {
- fn properties() -> &'static [Property] {
- &DUMMY_PROPERTIES
- }
- fn vmsd() -> Option<&'static VMStateDescription> {
- Some(&VMSTATE)
- }
+ const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE);
}
#[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qom::Object, hwcore::Device)]
pub struct DummyChildState {
parent: ParentField<DummyState>,
}
@@ -112,7 +88,7 @@ impl DummyChildClass {
fn init_qom() {
static ONCE: BqlCell<bool> = BqlCell::new(false);
- cell::bql_start_test();
+ bql::start_test();
if !ONCE.get() {
unsafe {
module_call_init(module_init_type::MODULE_INIT_QOM);
diff --git a/rust/hw/core/wrapper.h b/rust/hw/core/wrapper.h
new file mode 100644
index 0000000..3bdbd12
--- /dev/null
+++ b/rust/hw/core/wrapper.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "hw/sysbus.h"
+#include "hw/clock.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/irq.h"
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 6f07502..f781b28 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -10,12 +10,14 @@ license.workspace = true
repository.workspace = true
rust-version.workspace = true
-[lib]
-crate-type = ["staticlib"]
-
[dependencies]
-qemu_api = { path = "../../../qemu-api" }
-qemu_api_macros = { path = "../../../qemu-api-macros" }
+common = { path = "../../../common" }
+util = { path = "../../../util" }
+migration = { path = "../../../migration" }
+bql = { path = "../../../bql" }
+qom = { path = "../../../qom" }
+system = { path = "../../../system" }
+hwcore = { path = "../../../hw/core" }
[lints]
workspace = true
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index c2d7c05..bb64b96 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -4,15 +4,17 @@ _libhpet_rs = static_library(
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
dependencies: [
- qemu_api,
- qemu_api_macros,
+ common_rs,
+ util_rs,
+ migration_rs,
+ bql_rs,
+ qom_rs,
+ system_rs,
+ hwcore_rs,
],
)
rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency(
link_whole: [_libhpet_rs],
- # Putting proc macro crates in `dependencies` is necessary for Meson to find
- # them when compiling the root per-target static rust lib.
- dependencies: [qemu_api_macros],
variables: {'crate': 'hpet'},
)])
diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/device.rs
index 779681d..86638c0 100644
--- a/rust/hw/timer/hpet/src/hpet.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -1,34 +1,31 @@
// 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},
+ ffi::CStr,
+ mem::MaybeUninit,
pin::Pin,
ptr::{addr_of_mut, null_mut, NonNull},
slice::from_ref,
};
-use qemu_api::{
- bindings::{
- address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
- qdev_prop_uint32, qdev_prop_uint8,
- },
- cell::{BqlCell, BqlRefCell},
- irq::InterruptSource,
- memory::{
- hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
- },
- prelude::*,
- qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl},
- qom::{ObjectImpl, ObjectType, ParentField},
- qom_isa,
- sysbus::{SysBusDevice, SysBusDeviceImpl},
- timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
- vmstate::VMStateDescription,
- vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate,
- zeroable::Zeroable,
+use bql::{BqlCell, BqlRefCell};
+use common::{bitops::IntegerExt, uninit_field_mut};
+use hwcore::{
+ DeviceImpl, DeviceMethods, DeviceState, InterruptSource, ResetType, ResettablePhasesImpl,
+ SysBusDevice, SysBusDeviceImpl, SysBusDeviceMethods,
};
+use migration::{
+ self, impl_vmstate_struct, vmstate_fields, vmstate_of, vmstate_subsections, vmstate_validate,
+ VMStateDescription, VMStateDescriptionBuilder,
+};
+use qom::{prelude::*, ObjectImpl, ParentField, ParentInit};
+use system::{
+ bindings::{address_space_memory, address_space_stl_le, hwaddr},
+ MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
+};
+use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
use crate::fw_cfg::HPETFwConfig;
@@ -36,9 +33,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;
@@ -99,7 +96,7 @@ const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15;
/// Timer N Interrupt Routing Capability (bits 32:63)
const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32;
-#[derive(qemu_api_macros::TryInto)]
+#[derive(common::TryInto)]
#[repr(u64)]
#[allow(non_camel_case_types)]
/// Timer registers, masked by 0x18
@@ -112,7 +109,7 @@ enum TimerRegister {
ROUTE = 16,
}
-#[derive(qemu_api_macros::TryInto)]
+#[derive(common::TryInto)]
#[repr(u64)]
#[allow(non_camel_case_types)]
/// Global registers
@@ -211,14 +208,18 @@ pub struct HPETTimer {
last: u64,
}
+// SAFETY: Sync is not automatically derived due to the `state` field,
+// which is always dereferenced to a shared reference.
+unsafe impl Sync for 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 +227,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 {
@@ -522,7 +519,7 @@ impl HPETTimer {
/// HPET Event Timer Block Abstraction
#[repr(C)]
-#[derive(qemu_api_macros::Object)]
+#[derive(qom::Object, hwcore::Device)]
pub struct HPETState {
parent_obj: ParentField<SysBusDevice>,
iomem: MemoryRegion,
@@ -542,10 +539,12 @@ pub struct HPETState {
// Internal state
/// Capabilities that QEMU HPET supports.
/// bit 0: MSI (or FSB) support.
+ #[property(rename = "msi", bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, default = false)]
flags: u32,
/// Offset of main counter relative to qemu clock.
hpet_offset: BqlCell<u64>,
+ #[property(rename = "hpet-offset-saved", default = true)]
hpet_offset_saved: bool,
irqs: [InterruptSource; HPET_NUM_IRQ_ROUTES],
@@ -557,12 +556,14 @@ pub struct HPETState {
/// the timers' interrupt can be routed, and is encoded in the
/// bits 32:64 of timer N's config register:
#[doc(alias = "intcap")]
+ #[property(rename = "hpet-intcap", default = 0)]
int_route_cap: u32,
/// 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],
+ #[property(rename = "timers", default = HPET_MIN_TIMERS)]
+ num_timers: usize,
num_timers_save: BqlCell<u8>,
/// Instance id (HPET timer block ID).
@@ -570,11 +571,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 +608,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 +641,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 +653,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 +676,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 +700,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 +710,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 +727,35 @@ impl HPETState {
}
}
- fn realize(&self) {
+ fn realize(&self) -> util::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());
-
- 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.hpet_id.set(HPETFwConfig::assign_hpet_id()?);
- 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 +774,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 +783,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))
@@ -843,7 +844,7 @@ impl HPETState {
}
}
- fn pre_save(&self) -> i32 {
+ fn pre_save(&self) -> Result<(), migration::Infallible> {
if self.is_hpet_enabled() {
self.counter.set(self.get_ticks());
}
@@ -853,12 +854,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());
- 0
+ self.num_timers_save.set(self.num_timers as u8);
+ Ok(())
}
- fn post_load(&self, _version_id: u8) -> i32 {
- for timer in self.timers.iter().take(self.get_num_timers()) {
+ fn post_load(&self, _version_id: u8) -> Result<(), migration::Infallible> {
+ 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);
@@ -871,7 +872,7 @@ impl HPETState {
.set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns());
}
- 0
+ Ok(())
}
fn is_rtc_irq_level_needed(&self) -> bool {
@@ -883,7 +884,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,151 +899,76 @@ 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>;
}
-// TODO: Make these properties user-configurable!
-qemu_api::declare_properties! {
- HPET_PROPERTIES,
- qemu_api::define_property!(
- c"timers",
- HPETState,
- num_timers,
- unsafe { &qdev_prop_uint8 },
- u8,
- default = HPET_MIN_TIMERS
- ),
- qemu_api::define_property!(
- c"msi",
- HPETState,
- flags,
- unsafe { &qdev_prop_bit },
- u32,
- bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8,
- default = false,
- ),
- qemu_api::define_property!(
- c"hpet-intcap",
- HPETState,
- int_route_cap,
- unsafe { &qdev_prop_uint32 },
- u32,
- default = 0
- ),
- qemu_api::define_property!(
- c"hpet-offset-saved",
- HPETState,
- hpet_offset_saved,
- unsafe { &qdev_prop_bool },
- bool,
- default = true
- ),
-}
-
-unsafe extern "C" fn hpet_rtc_irq_level_needed(opaque: *mut c_void) -> bool {
- // SAFETY:
- // the pointer is convertible to a reference
- let state: &HPETState = unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_ref() };
- state.is_rtc_irq_level_needed()
-}
-
-unsafe extern "C" fn hpet_offset_needed(opaque: *mut c_void) -> bool {
- // SAFETY:
- // the pointer is convertible to a reference
- let state: &HPETState = unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_ref() };
- state.is_offset_needed()
-}
-
-unsafe extern "C" fn hpet_pre_save(opaque: *mut c_void) -> c_int {
- // SAFETY:
- // the pointer is convertible to a reference
- let state: &mut HPETState =
- unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_mut() };
- state.pre_save() as c_int
-}
-
-unsafe extern "C" fn hpet_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
- // SAFETY:
- // the pointer is convertible to a reference
- let state: &mut HPETState =
- unsafe { NonNull::new(opaque.cast::<HPETState>()).unwrap().as_mut() };
- let version: u8 = version_id.try_into().unwrap();
- state.post_load(version) as c_int
-}
-
-static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription {
- name: c"hpet/rtc_irq_level".as_ptr(),
- version_id: 1,
- minimum_version_id: 1,
- needed: Some(hpet_rtc_irq_level_needed),
- fields: vmstate_fields! {
- vmstate_of!(HPETState, rtc_irq_level),
- },
- ..Zeroable::ZERO
-};
-
-static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription {
- name: c"hpet/offset".as_ptr(),
- version_id: 1,
- minimum_version_id: 1,
- needed: Some(hpet_offset_needed),
- fields: vmstate_fields! {
- vmstate_of!(HPETState, hpet_offset),
- },
- ..Zeroable::ZERO
-};
-
-static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription {
- name: c"hpet_timer".as_ptr(),
- version_id: 1,
- minimum_version_id: 1,
- fields: vmstate_fields! {
- vmstate_of!(HPETTimer, index),
- vmstate_of!(HPETTimer, config),
- vmstate_of!(HPETTimer, cmp),
- vmstate_of!(HPETTimer, fsb),
- vmstate_of!(HPETTimer, period),
- vmstate_of!(HPETTimer, wrap_flag),
- vmstate_of!(HPETTimer, qemu_timer),
- },
- ..Zeroable::ZERO
-};
+static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription<HPETState> =
+ VMStateDescriptionBuilder::<HPETState>::new()
+ .name(c"hpet/rtc_irq_level")
+ .version_id(1)
+ .minimum_version_id(1)
+ .needed(&HPETState::is_rtc_irq_level_needed)
+ .fields(vmstate_fields! {
+ vmstate_of!(HPETState, rtc_irq_level),
+ })
+ .build();
+
+static VMSTATE_HPET_OFFSET: VMStateDescription<HPETState> =
+ VMStateDescriptionBuilder::<HPETState>::new()
+ .name(c"hpet/offset")
+ .version_id(1)
+ .minimum_version_id(1)
+ .needed(&HPETState::is_offset_needed)
+ .fields(vmstate_fields! {
+ vmstate_of!(HPETState, hpet_offset),
+ })
+ .build();
+
+const VMSTATE_HPET_TIMER: VMStateDescription<HPETTimer> =
+ VMStateDescriptionBuilder::<HPETTimer>::new()
+ .name(c"hpet_timer")
+ .version_id(1)
+ .minimum_version_id(1)
+ .fields(vmstate_fields! {
+ vmstate_of!(HPETTimer, index),
+ vmstate_of!(HPETTimer, config),
+ vmstate_of!(HPETTimer, cmp),
+ vmstate_of!(HPETTimer, fsb),
+ vmstate_of!(HPETTimer, period),
+ vmstate_of!(HPETTimer, wrap_flag),
+ vmstate_of!(HPETTimer, qemu_timer),
+ })
+ .build();
+impl_vmstate_struct!(HPETTimer, VMSTATE_HPET_TIMER);
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,
- 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_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),
- },
- subsections: vmstate_subsections! {
- VMSTATE_HPET_RTC_IRQ_LEVEL,
- VMSTATE_HPET_OFFSET,
- },
- ..Zeroable::ZERO
-};
+const VMSTATE_HPET: VMStateDescription<HPETState> =
+ VMStateDescriptionBuilder::<HPETState>::new()
+ .name(c"hpet")
+ .version_id(2)
+ .minimum_version_id(2)
+ .pre_save(&HPETState::pre_save)
+ .post_load(&HPETState::post_load)
+ .fields(vmstate_fields! {
+ vmstate_of!(HPETState, config),
+ vmstate_of!(HPETState, int_status),
+ vmstate_of!(HPETState, counter),
+ vmstate_of!(HPETState, num_timers_save),
+ vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers),
+ vmstate_of!(HPETState, timers[0 .. num_timers_save], HPETState::validate_num_timers).with_version_id(0),
+ })
+ .subsections(vmstate_subsections!(
+ VMSTATE_HPET_RTC_IRQ_LEVEL,
+ VMSTATE_HPET_OFFSET,
+ ))
+ .build();
impl DeviceImpl for HPETState {
- fn properties() -> &'static [Property] {
- &HPET_PROPERTIES
- }
-
- fn vmsd() -> Option<&'static VMStateDescription> {
- Some(&VMSTATE_HPET)
- }
-
- const REALIZE: Option<fn(&Self)> = Some(Self::realize);
+ const VMSTATE: Option<VMStateDescription<Self>> = Some(VMSTATE_HPET);
+ const REALIZE: Option<fn(&Self) -> util::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..e569b57 100644
--- a/rust/hw/timer/hpet/src/fw_cfg.rs
+++ b/rust/hw/timer/hpet/src/fw_cfg.rs
@@ -1,10 +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::ptr::addr_of_mut;
-use qemu_api::{cell::bql_locked, zeroable::Zeroable};
+use common::Zeroable;
/// Each `HPETState` represents a Event Timer Block. The v1 spec supports
/// up to 8 blocks. QEMU only uses 1 block (in PC machine).
@@ -36,8 +36,8 @@ pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig {
};
impl HPETFwConfig {
- pub(crate) fn assign_hpet_id() -> usize {
- assert!(bql_locked());
+ pub(crate) fn assign_hpet_id() -> Result<usize, &'static str> {
+ assert!(bql::is_locked());
// SAFETY: all accesses go through these methods, which guarantee
// that the accesses are protected by the BQL.
let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) };
@@ -48,17 +48,16 @@ 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) {
- assert!(bql_locked());
+ assert!(bql::is_locked());
// SAFETY: all accesses go through these methods, which guarantee
// that the accesses are protected by the BQL.
let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) };
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..6ba075c 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -1,26 +1,50 @@
-subdir('qemu-api-macros')
-subdir('qemu-api')
+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('glib-sys-0.21-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')
+glib_sys_rs = dependency('glib-sys-0.21-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)
+subproject('attrs-0.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)
+attrs_rs_native = dependency('attrs-0.2-rs', native: true)
+
+genrs = []
+
+subdir('qemu-macros')
+
+subdir('common')
+subdir('bits')
+subdir('util')
+subdir('migration')
+subdir('bql')
+subdir('qom')
+subdir('system')
+subdir('chardev')
+subdir('hw/core')
+subdir('tests')
+subdir('trace')
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/migration/Cargo.toml b/rust/migration/Cargo.toml
new file mode 100644
index 0000000..94504f3
--- /dev/null
+++ b/rust/migration/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "migration"
+version = "0.1.0"
+description = "Rust bindings for QEMU/migration"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../common" }
+util = { path = "../util" }
+glib-sys.workspace = true
+
+[lints]
+workspace = true
diff --git a/rust/migration/build.rs b/rust/migration/build.rs
new file mode 120000
index 0000000..71a3167
--- /dev/null
+++ b/rust/migration/build.rs
@@ -0,0 +1 @@
+../util/build.rs \ No newline at end of file
diff --git a/rust/migration/meson.build b/rust/migration/meson.build
new file mode 100644
index 0000000..18be65c
--- /dev/null
+++ b/rust/migration/meson.build
@@ -0,0 +1,53 @@
+_migration_bindgen_args = []
+c_bitfields = [
+ 'MigrationPolicy',
+ 'MigrationPriority',
+ 'VMStateFlags',
+]
+foreach enum : c_bitfields
+ _migration_bindgen_args += ['--bitfield-enum', enum]
+endforeach
+#
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_migration_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common + _migration_bindgen_args,
+ c_args: bindgen_c_args,
+)
+
+_migration_rs = static_library(
+ 'migration',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/vmstate.rs',
+ ],
+ {'.' : _migration_bindings_inc_rs},
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ link_with: [_util_rs],
+ dependencies: [common_rs, glib_sys_rs],
+)
+
+migration_rs = declare_dependency(link_with: [_migration_rs],
+ dependencies: [migration, qemuutil])
+
+# 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-migration-rs-doctests',
+ _migration_rs,
+ dependencies: migration_rs,
+ suite: ['doc', 'rust'])
diff --git a/rust/migration/src/bindings.rs b/rust/migration/src/bindings.rs
new file mode 100644
index 0000000..24503eb
--- /dev/null
+++ b/rust/migration/src/bindings.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+use common::Zeroable;
+use glib_sys::{GHashTable, GHashTableIter, GList, GPtrArray, GQueue, GSList};
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+unsafe impl Send for VMStateDescription {}
+unsafe impl Sync for VMStateDescription {}
+
+unsafe impl Send for VMStateField {}
+unsafe impl Sync for VMStateField {}
+
+unsafe impl Send for VMStateInfo {}
+unsafe impl Sync for VMStateInfo {}
+
+// bindgen does not derive Default here
+#[allow(clippy::derivable_impls)]
+impl Default for VMStateFlags {
+ fn default() -> Self {
+ Self(0)
+ }
+}
+
+unsafe impl Zeroable for VMStateFlags {}
+unsafe impl Zeroable for VMStateField {}
+unsafe impl Zeroable for VMStateDescription {}
diff --git a/rust/migration/src/lib.rs b/rust/migration/src/lib.rs
new file mode 100644
index 0000000..5f51dde
--- /dev/null
+++ b/rust/migration/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+pub mod vmstate;
+pub use vmstate::*;
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/migration/src/vmstate.rs
index 9c8b239..e04b19b 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/migration/src/vmstate.rs
@@ -9,12 +9,13 @@
//! * [`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
-//! the definition of the [`VMState`] trait (respectively for transparent
-//! structs and for `bilge`-defined types)
+//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward),
+//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and
+//! [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the
+//! definition of the [`VMState`] trait (respectively for transparent structs,
+//! nested structs and `bilge`-defined types)
//!
//! * helper macros to declare a device model state struct, in particular
//! [`vmstate_subsections`](crate::vmstate_subsections) and
@@ -24,14 +25,25 @@
//! `include/migration/vmstate.h`. These are not type-safe and only provide
//! functionality that is missing from `vmstate_of!`.
-use core::{marker::PhantomData, mem, ptr::NonNull};
-use std::ffi::{c_int, c_void};
+pub use std::convert::Infallible;
+use std::{
+ error::Error,
+ ffi::{c_int, c_void, CStr},
+ fmt, io,
+ marker::PhantomData,
+ mem,
+ ptr::{addr_of, NonNull},
+};
-pub use crate::bindings::{VMStateDescription, VMStateField};
-use crate::{
- bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable,
+use common::{
+ callbacks::FnCall,
+ errno::{into_neg_errno, Errno},
+ Zeroable,
};
+use crate::bindings::{self, VMStateFlags};
+pub use crate::bindings::{MigrationPriority, VMStateField};
+
/// This macro is used to call a function with a generic argument bound
/// to the type of a field. The function must take a
/// [`PhantomData`]`<T>` argument; `T` is the type of
@@ -40,7 +52,7 @@ use crate::{
/// # Examples
///
/// ```
-/// # use qemu_api::call_func_with_field;
+/// # use migration::call_func_with_field;
/// # use core::marker::PhantomData;
/// const fn size_of_field<T>(_: PhantomData<T>) -> usize {
/// std::mem::size_of::<T>()
@@ -69,70 +81,6 @@ macro_rules! call_func_with_field {
};
}
-/// Workaround for lack of `const_refs_static`: references to global variables
-/// can be included in a `static`, but not in a `const`; unfortunately, this
-/// is exactly what would go in the `VMStateField`'s `info` member.
-///
-/// This enum contains the contents of the `VMStateField`'s `info` member,
-/// but as an `enum` instead of a pointer.
-#[allow(non_camel_case_types)]
-pub enum VMStateFieldType {
- null,
- vmstate_info_bool,
- vmstate_info_int8,
- vmstate_info_int16,
- vmstate_info_int32,
- vmstate_info_int64,
- vmstate_info_uint8,
- vmstate_info_uint16,
- vmstate_info_uint32,
- vmstate_info_uint64,
- vmstate_info_timer,
-}
-
-/// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType`
-/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`.
-#[macro_export]
-macro_rules! info_enum_to_ref {
- ($e:expr) => {
- unsafe {
- match $e {
- $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(),
- $crate::vmstate::VMStateFieldType::vmstate_info_bool => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_int8 => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_int16 => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_int32 => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_int64 => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64)
- }
- $crate::vmstate::VMStateFieldType::vmstate_info_timer => {
- ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer)
- }
- }
- }
- };
-}
-
/// A trait for types that can be included in a device's migration stream. It
/// provides the base contents of a `VMStateField` (minus the name and offset).
///
@@ -143,12 +91,6 @@ macro_rules! info_enum_to_ref {
/// to implement it except via macros that do it for you, such as
/// `impl_vmstate_bitsized!`.
pub unsafe trait VMState {
- /// The `info` member of a `VMStateField` is a pointer and as such cannot
- /// yet be included in the [`BASE`](VMState::BASE) associated constant;
- /// this is only allowed by Rust 1.83.0 and newer. For now, include the
- /// member as an enum which is stored in a separate constant.
- const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null;
-
/// The base contents of a `VMStateField` (minus the name and offset) for
/// the type that is implementing the trait.
const BASE: VMStateField;
@@ -163,12 +105,6 @@ pub unsafe trait VMState {
};
}
-/// Internal utility function to retrieve a type's `VMStateFieldType`;
-/// used by [`vmstate_of!`](crate::vmstate_of).
-pub const fn vmstate_scalar_type<T: VMState>(_: PhantomData<T>) -> VMStateFieldType {
- T::SCALAR_TYPE
-}
-
/// Internal utility function to retrieve a type's `VMStateField`;
/// used by [`vmstate_of!`](crate::vmstate_of).
pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField {
@@ -189,32 +125,31 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
/// * scalar types (integer and `bool`)
/// * the C struct `QEMUTimer`
/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`,
-/// [`BqlCell`], [`BqlRefCell`]
+/// [`BqlCell`], [`BqlRefCell`])
/// * a raw pointer to any of the above
/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above
/// * an array of any of the above
///
/// In order to support other types, the trait `VMState` must be implemented
-/// for them. The macros
-/// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized)
-/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this.
+/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward),
+/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and
+/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this.
+///
+/// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html
+/// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html
+/// [`Owned`]: ../../qom/qom/struct.Owned.html
#[macro_export]
macro_rules! vmstate_of {
($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
$crate::bindings::VMStateField {
name: ::core::concat!(::core::stringify!($field_name), "\0")
.as_bytes()
- .as_ptr() as *const ::std::os::raw::c_char,
+ .as_ptr().cast::<::std::os::raw::c_char>(),
offset: ::std::mem::offset_of!($struct_name, $field_name),
$(num_offset: ::std::mem::offset_of!($struct_name, $num),)?
$(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
// The calls to `call_func_with_field!` are the magic that
// computes most of the VMStateField from the type of the field.
- info: $crate::info_enum_to_ref!($crate::call_func_with_field!(
- $crate::vmstate::vmstate_scalar_type,
- $struct_name,
- $field_name
- )),
..$crate::call_func_with_field!(
$crate::vmstate::vmstate_base,
$struct_name,
@@ -228,7 +163,11 @@ macro_rules! vmstate_of {
};
}
-impl VMStateFlags {
+pub trait VMStateFlagsExt {
+ const VMS_VARRAY_FLAGS: VMStateFlags;
+}
+
+impl VMStateFlagsExt for VMStateFlags {
const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags(
VMStateFlags::VMS_VARRAY_INT32.0
| VMStateFlags::VMS_VARRAY_UINT8.0
@@ -274,7 +213,7 @@ impl VMStateField {
}
#[must_use]
- pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMStateField {
+ pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> Self {
self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0);
self.flags = VMStateFlags(self.flags.0 | flag.0);
self.num = 0; // varray uses num_offset instead of num.
@@ -283,13 +222,13 @@ impl VMStateField {
#[must_use]
#[allow(unused_mut)]
- pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField {
+ pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> Self {
assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0);
self.with_varray_flag_unchecked(flag)
}
#[must_use]
- pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField {
+ pub const fn with_varray_multiply(mut self, num: u32) -> Self {
assert!(num <= 0x7FFF_FFFFu32);
self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0);
self.num = num as i32;
@@ -304,7 +243,7 @@ impl VMStateField {
/// # Examples
///
/// ```
-/// # use qemu_api::impl_vmstate_forward;
+/// # use migration::impl_vmstate_forward;
/// pub struct Fifo([u8; 16]);
/// impl_vmstate_forward!(Fifo);
/// ```
@@ -315,8 +254,6 @@ macro_rules! impl_vmstate_forward {
// the first field of the tuple
($tuple:ty) => {
unsafe impl $crate::vmstate::VMState for $tuple {
- const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
- $crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0);
const BASE: $crate::bindings::VMStateField =
$crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0);
}
@@ -325,15 +262,15 @@ macro_rules! impl_vmstate_forward {
// Transparent wrappers: just use the internal type
+#[macro_export]
macro_rules! impl_vmstate_transparent {
($type:ty where $base:tt: VMState $($where:tt)*) => {
- unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
- const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE;
- const BASE: VMStateField = VMStateField {
+ unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
+ const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField {
size: mem::size_of::<$type>(),
- ..<$base as VMState>::BASE
+ ..<$base as $crate::vmstate::VMState>::BASE
};
- const VARRAY_FLAG: VMStateFlags = <$base as VMState>::VARRAY_FLAG;
+ const VARRAY_FLAG: $crate::bindings::VMStateFlags = <$base as $crate::vmstate::VMState>::VARRAY_FLAG;
}
};
}
@@ -341,18 +278,12 @@ macro_rules! impl_vmstate_transparent {
impl_vmstate_transparent!(std::cell::Cell<T> where T: VMState);
impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState);
impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState);
-impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState);
-impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState);
-impl_vmstate_transparent!(crate::cell::Opaque<T> where T: VMState);
+impl_vmstate_transparent!(common::Opaque<T> where T: VMState);
#[macro_export]
macro_rules! impl_vmstate_bitsized {
($type:ty) => {
unsafe impl $crate::vmstate::VMState for $type {
- const SCALAR_TYPE: $crate::vmstate::VMStateFieldType =
- <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
- as ::bilge::prelude::Number>::UnderlyingType
- as $crate::vmstate::VMState>::SCALAR_TYPE;
const BASE: $crate::bindings::VMStateField =
<<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt
as ::bilge::prelude::Number>::UnderlyingType
@@ -369,12 +300,12 @@ macro_rules! impl_vmstate_bitsized {
macro_rules! impl_vmstate_scalar {
($info:ident, $type:ty$(, $varray_flag:ident)?) => {
- unsafe impl VMState for $type {
- const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info;
- const BASE: VMStateField = VMStateField {
+ unsafe impl $crate::vmstate::VMState for $type {
+ const BASE: $crate::vmstate::VMStateField = $crate::vmstate::VMStateField {
+ info: addr_of!(bindings::$info),
size: mem::size_of::<$type>(),
- flags: VMStateFlags::VMS_SINGLE,
- ..Zeroable::ZERO
+ flags: $crate::vmstate::VMStateFlags::VMS_SINGLE,
+ ..::common::zeroable::Zeroable::ZERO
};
$(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)?
}
@@ -390,17 +321,31 @@ impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8);
impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16);
impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32);
impl_vmstate_scalar!(vmstate_info_uint64, u64);
-impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer);
+impl_vmstate_scalar!(vmstate_info_timer, util::timer::Timer);
+
+#[macro_export]
+macro_rules! impl_vmstate_c_struct {
+ ($type:ty, $vmsd:expr) => {
+ unsafe impl $crate::vmstate::VMState for $type {
+ const BASE: $crate::bindings::VMStateField = $crate::bindings::VMStateField {
+ vmsd: ::std::ptr::addr_of!($vmsd),
+ size: ::std::mem::size_of::<$type>(),
+ flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
+ ..::common::zeroable::Zeroable::ZERO
+ };
+ }
+ };
+}
// Pointer types using the underlying type's VMState plus VMS_POINTER
// Note that references are not supported, though references to cells
// could be allowed.
+#[macro_export]
macro_rules! impl_vmstate_pointer {
($type:ty where $base:tt: VMState $($where:tt)*) => {
- unsafe impl<$base> VMState for $type where $base: VMState $($where)* {
- const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
- const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag();
+ unsafe impl<$base> $crate::vmstate::VMState for $type where $base: $crate::vmstate::VMState $($where)* {
+ const BASE: $crate::vmstate::VMStateField = <$base as $crate::vmstate::VMState>::BASE.with_pointer_flag();
}
};
}
@@ -412,13 +357,11 @@ impl_vmstate_pointer!(NonNull<T> where T: VMState);
// Unlike C pointers, Box is always non-null therefore there is no need
// to specify VMS_ALLOC.
impl_vmstate_pointer!(Box<T> where T: VMState);
-impl_vmstate_pointer!(Owned<T> where T: VMState + ObjectType);
// Arrays using the underlying type's VMState plus
// VMS_ARRAY/VMS_ARRAY_OF_POINTER
unsafe impl<T: VMState, const N: usize> VMState for [T; N] {
- const SCALAR_TYPE: VMStateFieldType = <T as VMState>::SCALAR_TYPE;
const BASE: VMStateField = <T as VMState>::BASE.with_array_flag(N);
}
@@ -431,7 +374,7 @@ macro_rules! vmstate_unused {
size: $size,
info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) },
flags: $crate::bindings::VMStateFlags::VMS_BUFFER,
- ..$crate::zeroable::Zeroable::ZERO
+ ..::common::Zeroable::ZERO
}
}};
}
@@ -440,7 +383,7 @@ pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), b
opaque: *mut c_void,
version_id: c_int,
) -> bool {
- // SAFETY: the opaque was passed as a reference to `T`.
+ // SAFETY: the function is used in T's implementation of VMState
let owner: &T = unsafe { &*(opaque.cast::<T>()) };
let version: u8 = version_id.try_into().unwrap();
F::call((owner, version))
@@ -454,10 +397,10 @@ pub type VMSFieldExistCb = unsafe extern "C" fn(
#[macro_export]
macro_rules! vmstate_exist_fn {
($struct_name:ty, $test_fn:expr) => {{
- const fn test_cb_builder__<T, F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>>(
+ const fn test_cb_builder__<T, F: for<'a> ::common::FnCall<(&'a T, u8), bool>>(
_phantom: ::core::marker::PhantomData<F>,
) -> $crate::vmstate::VMSFieldExistCb {
- let _: () = F::ASSERT_IS_SOME;
+ const { assert!(F::IS_SOME) };
$crate::vmstate::rust_vms_test_field_exists::<T, F>
}
@@ -468,76 +411,6 @@ macro_rules! vmstate_exist_fn {
}};
}
-// FIXME: including the `vmsd` field in a `const` is not possible without
-// the const_refs_static feature (stabilized in Rust 1.83.0). Without it,
-// it is not possible to use VMS_STRUCT in a transparent manner using
-// `vmstate_of!`. While VMSTATE_CLOCK can at least try to be type-safe,
-// VMSTATE_STRUCT includes $type only for documentation purposes; it
-// is checked against $field_name and $struct_name, but not against $vmsd
-// which is what really would matter.
-#[doc(alias = "VMSTATE_STRUCT")]
-#[macro_export]
-macro_rules! vmstate_struct {
- ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(, $test_fn:expr)? $(,)?) => {
- $crate::bindings::VMStateField {
- name: ::core::concat!(::core::stringify!($field_name), "\0")
- .as_bytes()
- .as_ptr() as *const ::std::os::raw::c_char,
- $(num_offset: ::std::mem::offset_of!($struct_name, $num),)?
- offset: {
- $crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?);
- ::std::mem::offset_of!($struct_name, $field_name)
- },
- size: ::core::mem::size_of::<$type>(),
- flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
- vmsd: $vmsd,
- $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
- ..$crate::zeroable::Zeroable::ZERO
- } $(.with_varray_flag_unchecked(
- $crate::call_func_with_field!(
- $crate::vmstate::vmstate_varray_flag,
- $struct_name,
- $num
- )
- )
- $(.with_varray_multiply($factor))?)?
- };
-}
-
-#[doc(alias = "VMSTATE_CLOCK")]
-#[macro_export]
-macro_rules! vmstate_clock {
- ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{
- $crate::bindings::VMStateField {
- name: ::core::concat!(::core::stringify!($field_name), "\0")
- .as_bytes()
- .as_ptr() as *const ::std::os::raw::c_char,
- offset: {
- $crate::assert_field_type!(
- $struct_name,
- $field_name,
- $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)?
- );
- ::std::mem::offset_of!($struct_name, $field_name)
- },
- size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
- flags: $crate::bindings::VMStateFlags(
- $crate::bindings::VMStateFlags::VMS_STRUCT.0
- | $crate::bindings::VMStateFlags::VMS_POINTER.0,
- ),
- vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
- ..$crate::zeroable::Zeroable::ZERO
- } $(.with_varray_flag_unchecked(
- $crate::call_func_with_field!(
- $crate::vmstate::vmstate_varray_flag,
- $struct_name,
- $num
- )
- )
- $(.with_varray_multiply($factor))?)?
- }};
-}
-
/// Helper macro to declare a list of
/// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return
/// a pointer to the array of values it created.
@@ -548,7 +421,7 @@ macro_rules! vmstate_fields {
$($field),*,
$crate::bindings::VMStateField {
flags: $crate::bindings::VMStateFlags::VMS_END,
- ..$crate::zeroable::Zeroable::ZERO
+ ..::common::zeroable::Zeroable::ZERO
}
];
_FIELDS.as_ptr()
@@ -567,7 +440,31 @@ macro_rules! vmstate_validate {
| $crate::bindings::VMStateFlags::VMS_ARRAY.0,
),
num: 0, // 0 elements: no data, only run test_fn callback
- ..$crate::zeroable::Zeroable::ZERO
+ ..::common::zeroable::Zeroable::ZERO
+ }
+ };
+}
+
+/// Helper macro to allow using a struct in [`vmstate_of!`]
+///
+/// # Safety
+///
+/// The [`VMStateDescription`] constant `$vmsd` must be an accurate
+/// description of the struct.
+#[macro_export]
+macro_rules! impl_vmstate_struct {
+ ($type:ty, $vmsd:expr) => {
+ unsafe impl $crate::vmstate::VMState for $type {
+ const BASE: $crate::bindings::VMStateField = {
+ static VMSD: &$crate::bindings::VMStateDescription = $vmsd.as_ref();
+
+ $crate::bindings::VMStateField {
+ vmsd: ::core::ptr::addr_of!(*VMSD),
+ size: ::core::mem::size_of::<$type>(),
+ flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
+ ..common::Zeroable::ZERO
+ }
+ };
}
};
}
@@ -594,11 +491,225 @@ macro_rules! vmstate_subsections {
($($subsection:expr),*$(,)*) => {{
static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[
$({
- static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection;
+ static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection.get();
::core::ptr::addr_of!(_SUBSECTION)
}),*,
::core::ptr::null()
]);
- _SUBSECTIONS.0.as_ptr()
+ &_SUBSECTIONS
}}
}
+
+pub struct VMStateDescription<T>(bindings::VMStateDescription, PhantomData<fn(&T)>);
+
+// SAFETY: When a *const T is passed to the callbacks, the call itself
+// is done in a thread-safe manner. The invocation is okay as long as
+// T itself is `Sync`.
+unsafe impl<T: Sync> Sync for VMStateDescription<T> {}
+
+#[derive(Clone)]
+pub struct VMStateDescriptionBuilder<T>(bindings::VMStateDescription, PhantomData<fn(&T)>);
+
+#[derive(Debug)]
+pub struct InvalidError;
+
+impl Error for InvalidError {}
+
+impl std::fmt::Display for InvalidError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "invalid migration data")
+ }
+}
+
+impl From<InvalidError> for Errno {
+ fn from(_value: InvalidError) -> Errno {
+ io::ErrorKind::InvalidInput.into()
+ }
+}
+
+unsafe extern "C" fn vmstate_no_version_cb<
+ T,
+ F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>,
+>(
+ opaque: *mut c_void,
+) -> c_int {
+ // SAFETY: the function is used in T's implementation of VMState
+ let result = F::call((unsafe { &*(opaque.cast::<T>()) },));
+ into_neg_errno(result)
+}
+
+unsafe extern "C" fn vmstate_post_load_cb<
+ T,
+ F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>,
+>(
+ opaque: *mut c_void,
+ version_id: c_int,
+) -> c_int {
+ // SAFETY: the function is used in T's implementation of VMState
+ let owner: &T = unsafe { &*(opaque.cast::<T>()) };
+ let version: u8 = version_id.try_into().unwrap();
+ let result = F::call((owner, version));
+ into_neg_errno(result)
+}
+
+unsafe extern "C" fn vmstate_needed_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
+ opaque: *mut c_void,
+) -> bool {
+ // SAFETY: the function is used in T's implementation of VMState
+ F::call((unsafe { &*(opaque.cast::<T>()) },))
+}
+
+unsafe extern "C" fn vmstate_dev_unplug_pending_cb<T, F: for<'a> FnCall<(&'a T,), bool>>(
+ opaque: *mut c_void,
+) -> bool {
+ // SAFETY: the function is used in T's implementation of VMState
+ F::call((unsafe { &*(opaque.cast::<T>()) },))
+}
+
+impl<T> VMStateDescriptionBuilder<T> {
+ #[must_use]
+ pub const fn name(mut self, name_str: &CStr) -> Self {
+ self.0.name = ::std::ffi::CStr::as_ptr(name_str);
+ self
+ }
+
+ #[must_use]
+ pub const fn unmigratable(mut self) -> Self {
+ self.0.unmigratable = true;
+ self
+ }
+
+ #[must_use]
+ pub const fn early_setup(mut self) -> Self {
+ self.0.early_setup = true;
+ self
+ }
+
+ #[must_use]
+ pub const fn version_id(mut self, version: u8) -> Self {
+ self.0.version_id = version as c_int;
+ self
+ }
+
+ #[must_use]
+ pub const fn minimum_version_id(mut self, min_version: u8) -> Self {
+ self.0.minimum_version_id = min_version as c_int;
+ self
+ }
+
+ #[must_use]
+ pub const fn priority(mut self, priority: MigrationPriority) -> Self {
+ self.0.priority = priority;
+ self
+ }
+
+ #[must_use]
+ pub const fn pre_load<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
+ mut self,
+ _f: &F,
+ ) -> Self {
+ self.0.pre_load = if F::IS_SOME {
+ Some(vmstate_no_version_cb::<T, F>)
+ } else {
+ None
+ };
+ self
+ }
+
+ #[must_use]
+ pub const fn post_load<F: for<'a> FnCall<(&'a T, u8), Result<(), impl Into<Errno>>>>(
+ mut self,
+ _f: &F,
+ ) -> Self {
+ self.0.post_load = if F::IS_SOME {
+ Some(vmstate_post_load_cb::<T, F>)
+ } else {
+ None
+ };
+ self
+ }
+
+ #[must_use]
+ pub const fn pre_save<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
+ mut self,
+ _f: &F,
+ ) -> Self {
+ self.0.pre_save = if F::IS_SOME {
+ Some(vmstate_no_version_cb::<T, F>)
+ } else {
+ None
+ };
+ self
+ }
+
+ #[must_use]
+ pub const fn post_save<F: for<'a> FnCall<(&'a T,), Result<(), impl Into<Errno>>>>(
+ mut self,
+ _f: &F,
+ ) -> Self {
+ self.0.post_save = if F::IS_SOME {
+ Some(vmstate_no_version_cb::<T, F>)
+ } else {
+ None
+ };
+ self
+ }
+
+ #[must_use]
+ pub const fn needed<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self {
+ self.0.needed = if F::IS_SOME {
+ Some(vmstate_needed_cb::<T, F>)
+ } else {
+ None
+ };
+ self
+ }
+
+ #[must_use]
+ pub const fn unplug_pending<F: for<'a> FnCall<(&'a T,), bool>>(mut self, _f: &F) -> Self {
+ self.0.dev_unplug_pending = if F::IS_SOME {
+ Some(vmstate_dev_unplug_pending_cb::<T, F>)
+ } else {
+ None
+ };
+ self
+ }
+
+ #[must_use]
+ pub const fn fields(mut self, fields: *const VMStateField) -> Self {
+ self.0.fields = fields;
+ self
+ }
+
+ #[must_use]
+ pub const fn subsections(mut self, subs: &'static VMStateSubsectionsWrapper) -> Self {
+ self.0.subsections = subs.0.as_ptr();
+ self
+ }
+
+ #[must_use]
+ pub const fn build(self) -> VMStateDescription<T> {
+ VMStateDescription::<T>(self.0, PhantomData)
+ }
+
+ #[must_use]
+ pub const fn new() -> Self {
+ Self(bindings::VMStateDescription::ZERO, PhantomData)
+ }
+}
+
+impl<T> Default for VMStateDescriptionBuilder<T> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<T> VMStateDescription<T> {
+ pub const fn get(&self) -> bindings::VMStateDescription {
+ self.0
+ }
+
+ pub const fn as_ref(&self) -> &bindings::VMStateDescription {
+ &self.0
+ }
+}
diff --git a/rust/wrapper.h b/rust/migration/wrapper.h
index beddd9a..daf316a 100644
--- a/rust/wrapper.h
+++ b/rust/migration/wrapper.h
@@ -48,21 +48,4 @@ typedef enum memory_order {
#endif /* __CLANG_STDATOMIC_H */
#include "qemu/osdep.h"
-#include "qemu/module.h"
-#include "qemu-io.h"
-#include "system/system.h"
-#include "hw/sysbus.h"
-#include "system/memory.h"
-#include "chardev/char-fe.h"
-#include "hw/clock.h"
-#include "hw/qdev-clock.h"
-#include "hw/qdev-properties.h"
-#include "hw/qdev-properties-system.h"
-#include "hw/irq.h"
-#include "qapi/error.h"
#include "migration/vmstate.h"
-#include "chardev/char-serial.h"
-#include "exec/memattrs.h"
-#include "qemu/timer.h"
-#include "system/address-spaces.h"
-#include "hw/char/pl011.h"
diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build
deleted file mode 100644
index 6f94a4b..0000000
--- a/rust/qemu-api-macros/meson.build
+++ /dev/null
@@ -1,27 +0,0 @@
-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'),
- override_options: ['rust_std=2021', 'build.rust_std=2021'],
- rust_args: [
- '--cfg', 'use_fallback',
- '--cfg', 'feature="syn-error"',
- '--cfg', 'feature="proc-macro"',
- ],
- dependencies: [
- proc_macro2_dep,
- quote_dep,
- syn_dep,
- ],
-)
-
-qemu_api_macros = declare_dependency(
- link_with: _qemu_api_macros_rs,
-)
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
deleted file mode 100644
index f97449b..0000000
--- a/rust/qemu-api-macros/src/lib.rs
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2024, Linaro Limited
-// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-use proc_macro::TokenStream;
-use quote::quote;
-use syn::{
- parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
- DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant,
-};
-
-mod utils;
-use utils::MacroError;
-
-fn get_fields<'a>(
- input: &'a DeriveInput,
- msg: &str,
-) -> Result<&'a Punctuated<Field, Comma>, MacroError> {
- let Data::Struct(ref s) = &input.data else {
- return Err(MacroError::Message(
- format!("Struct required for {msg}"),
- input.ident.span(),
- ));
- };
- let Fields::Named(ref fs) = &s.fields else {
- return Err(MacroError::Message(
- format!("Named fields required for {msg}"),
- input.ident.span(),
- ));
- };
- Ok(&fs.named)
-}
-
-fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> {
- let Data::Struct(ref s) = &input.data else {
- return Err(MacroError::Message(
- format!("Struct required for {msg}"),
- input.ident.span(),
- ));
- };
- let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else {
- return Err(MacroError::Message(
- format!("Tuple struct required for {msg}"),
- s.fields.span(),
- ));
- };
- if unnamed.len() != 1 {
- return Err(MacroError::Message(
- format!("A single field is required for {msg}"),
- s.fields.span(),
- ));
- }
- Ok(&unnamed[0])
-}
-
-fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
- let expected = parse_quote! { #[repr(C)] };
-
- if input.attrs.iter().any(|attr| attr == &expected) {
- Ok(())
- } else {
- Err(MacroError::Message(
- format!("#[repr(C)] required for {msg}"),
- input.ident.span(),
- ))
- }
-}
-
-fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
- let expected = parse_quote! { #[repr(transparent)] };
-
- if input.attrs.iter().any(|attr| attr == &expected) {
- Ok(())
- } else {
- Err(MacroError::Message(
- format!("#[repr(transparent)] required for {msg}"),
- input.ident.span(),
- ))
- }
-}
-
-fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
- is_c_repr(&input, "#[derive(Object)]")?;
-
- let name = &input.ident;
- let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident;
-
- Ok(quote! {
- ::qemu_api::assert_field_type!(#name, #parent,
- ::qemu_api::qom::ParentField<<#name as ::qemu_api::qom::ObjectImpl>::ParentType>);
-
- ::qemu_api::module_init! {
- MODULE_INIT_QOM => unsafe {
- ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
- }
- }
- })
-}
-
-#[proc_macro_derive(Object)]
-pub fn derive_object(input: TokenStream) -> TokenStream {
- let input = parse_macro_input!(input as DeriveInput);
- let expanded = derive_object_or_error(input).unwrap_or_else(Into::into);
-
- TokenStream::from(expanded)
-}
-
-fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
- is_transparent_repr(&input, "#[derive(Wrapper)]")?;
-
- let name = &input.ident;
- let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?;
- let typ = &field.ty;
-
- // TODO: how to add "::qemu_api"? For now, this is only used in the
- // qemu_api crate so it's not a problem.
- Ok(quote! {
- unsafe impl crate::cell::Wrapper for #name {
- type Wrapped = <#typ as crate::cell::Wrapper>::Wrapped;
- }
- impl #name {
- pub unsafe fn from_raw<'a>(ptr: *mut <Self as crate::cell::Wrapper>::Wrapped) -> &'a Self {
- let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::<Self>();
- unsafe { ptr.as_ref() }
- }
-
- pub const fn as_mut_ptr(&self) -> *mut <Self as crate::cell::Wrapper>::Wrapped {
- self.0.as_mut_ptr()
- }
-
- pub const fn as_ptr(&self) -> *const <Self as crate::cell::Wrapper>::Wrapped {
- self.0.as_ptr()
- }
-
- pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void {
- self.0.as_void_ptr()
- }
-
- pub const fn raw_get(slot: *mut Self) -> *mut <Self as crate::cell::Wrapper>::Wrapped {
- slot.cast()
- }
- }
- })
-}
-
-#[proc_macro_derive(Wrapper)]
-pub fn derive_opaque(input: TokenStream) -> TokenStream {
- let input = parse_macro_input!(input as DeriveInput);
- let expanded = derive_opaque_or_error(input).unwrap_or_else(Into::into);
-
- TokenStream::from(expanded)
-}
-
-#[allow(non_snake_case)]
-fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> {
- let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
- if let Some(repr) = repr {
- let nested = repr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
- for meta in nested {
- match meta {
- Meta::Path(path) if path.is_ident("u8") => return Ok(path),
- Meta::Path(path) if path.is_ident("u16") => return Ok(path),
- Meta::Path(path) if path.is_ident("u32") => return Ok(path),
- Meta::Path(path) if path.is_ident("u64") => return Ok(path),
- _ => {}
- }
- }
- }
-
- Err(MacroError::Message(
- format!("#[repr(u8/u16/u32/u64) required for {msg}"),
- input.ident.span(),
- ))
-}
-
-fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, MacroError> {
- let Data::Enum(ref e) = &input.data else {
- return Err(MacroError::Message(
- "Cannot derive TryInto for union or struct.".to_string(),
- input.ident.span(),
- ));
- };
- if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) {
- return Err(MacroError::Message(
- "Cannot derive TryInto for enum with non-unit variants.".to_string(),
- v.fields.span(),
- ));
- }
- Ok(&e.variants)
-}
-
-#[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();
-
- Ok(quote! {
- 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),
- }
- }
- }
- })
-}
-
-#[proc_macro_derive(TryInto)]
-pub fn derive_tryinto(input: TokenStream) -> TokenStream {
- let input = parse_macro_input!(input as DeriveInput);
- let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into);
-
- TokenStream::from(expanded)
-}
diff --git a/rust/qemu-api-macros/src/utils.rs b/rust/qemu-api-macros/src/utils.rs
deleted file mode 100644
index 02c91ae..0000000
--- a/rust/qemu-api-macros/src/utils.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Procedural macro utilities.
-// Author(s): Paolo Bonzini <pbonzini@redhat.com>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-use proc_macro2::Span;
-use quote::quote_spanned;
-
-pub enum MacroError {
- Message(String, Span),
- ParseError(syn::Error),
-}
-
-impl From<syn::Error> for MacroError {
- fn from(err: syn::Error) -> Self {
- MacroError::ParseError(err)
- }
-}
-
-impl From<MacroError> for proc_macro2::TokenStream {
- fn from(err: MacroError) -> Self {
- match err {
- MacroError::Message(msg, span) => quote_spanned! { span => compile_error!(#msg); },
- MacroError::ParseError(err) => err.into_compile_error(),
- }
- }
-}
diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore
deleted file mode 100644
index df6c216..0000000
--- a/rust/qemu-api/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Ignore generated bindings file overrides.
-/src/bindings.inc.rs
diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md
deleted file mode 100644
index ed1b7ab..0000000
--- a/rust/qemu-api/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# QEMU bindings and API wrappers
-
-This library exports helper Rust types, Rust macros and C FFI bindings for internal QEMU APIs.
-
-The C bindings can be generated with `bindgen`, using this build target:
-
-```console
-$ make bindings.inc.rs
-```
-
-## Generate Rust documentation
-
-Common Cargo tasks can be performed from the QEMU build directory
-
-```console
-$ make clippy
-$ make rustfmt
-$ make rustdoc
-```
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
deleted file mode 100644
index 1696df7..0000000
--- a/rust/qemu-api/meson.build
+++ /dev/null
@@ -1,80 +0,0 @@
-_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
-
-_qemu_api_rs = static_library(
- 'qemu_api',
- structured_sources(
- [
- 'src/lib.rs',
- 'src/assertions.rs',
- 'src/bindings.rs',
- 'src/bitops.rs',
- 'src/callbacks.rs',
- 'src/cell.rs',
- 'src/chardev.rs',
- 'src/errno.rs',
- 'src/irq.rs',
- 'src/memory.rs',
- 'src/module.rs',
- 'src/prelude.rs',
- 'src/qdev.rs',
- 'src/qom.rs',
- 'src/sysbus.rs',
- 'src/timer.rs',
- 'src/vmstate.rs',
- 'src/zeroable.rs',
- ],
- {'.' : bindings_rs},
- ),
- override_options: ['rust_std=2021', 'build.rust_std=2021'],
- rust_abi: 'rust',
- rust_args: _qemu_api_cfg,
- dependencies: [libc_dep, qemu_api_macros],
-)
-
-rust.test('rust-qemu-api-tests', _qemu_api_rs,
- suite: ['unit', 'rust'])
-
-qemu_api = declare_dependency(link_with: _qemu_api_rs)
-
-# 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])
-
-test('rust-qemu-api-integration',
- executable(
- 'rust-qemu-api-integration',
- files('tests/tests.rs', 'tests/vmstate_tests.rs'),
- override_options: ['rust_std=2021', 'build.rust_std=2021'],
- rust_args: ['--test'],
- install: false,
- dependencies: [qemu_api, qemu_api_macros, rust_qemu_api_deps]),
- args: [
- '--test', '--test-threads', '1',
- '--format', 'pretty',
- ],
- protocol: 'rust',
- suite: ['unit', 'rust'])
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
deleted file mode 100644
index 234a94e..0000000
--- a/rust/qemu-api/src/lib.rs
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2024, Linaro Limited
-// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
-#![deny(clippy::missing_const_for_fn)]
-
-#[rustfmt::skip]
-pub mod bindings;
-
-// preserve one-item-per-"use" syntax, it is clearer
-// for prelude-like modules
-#[rustfmt::skip]
-pub mod prelude;
-
-pub mod assertions;
-pub mod bitops;
-pub mod callbacks;
-pub mod cell;
-pub mod chardev;
-pub mod errno;
-pub mod irq;
-pub mod memory;
-pub mod module;
-pub mod qdev;
-pub mod qom;
-pub mod sysbus;
-pub mod timer;
-pub mod vmstate;
-pub mod zeroable;
-
-use std::{
- alloc::{GlobalAlloc, Layout},
- ffi::c_void,
-};
-
-#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
-extern "C" {
- fn g_aligned_alloc0(
- n_blocks: bindings::gsize,
- n_block_bytes: bindings::gsize,
- alignment: bindings::gsize,
- ) -> bindings::gpointer;
- fn g_aligned_free(mem: bindings::gpointer);
-}
-
-#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
-extern "C" {
- fn qemu_memalign(alignment: usize, size: usize) -> *mut c_void;
- fn qemu_vfree(ptr: *mut c_void);
-}
-
-extern "C" {
- fn g_malloc0(n_bytes: bindings::gsize) -> bindings::gpointer;
- fn g_free(mem: bindings::gpointer);
-}
-
-/// An allocator that uses the same allocator as QEMU in C.
-///
-/// It is enabled by default with the `allocator` feature.
-///
-/// To set it up manually as a global allocator in your crate:
-///
-/// ```ignore
-/// use qemu_api::QemuAllocator;
-///
-/// #[global_allocator]
-/// static GLOBAL: QemuAllocator = QemuAllocator::new();
-/// ```
-#[derive(Clone, Copy, Debug)]
-#[repr(C)]
-pub struct QemuAllocator {
- _unused: [u8; 0],
-}
-
-#[cfg_attr(all(feature = "allocator", not(test)), global_allocator)]
-pub static GLOBAL: QemuAllocator = QemuAllocator::new();
-
-impl QemuAllocator {
- // From the glibc documentation, on GNU systems, malloc guarantees 16-byte
- // alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See
- // https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html.
- // This alignment guarantee also applies to Windows and Android. On Darwin
- // and OpenBSD, the alignment is 16 bytes on both 64-bit and 32-bit systems.
- #[cfg(all(
- target_pointer_width = "32",
- not(any(target_os = "macos", target_os = "openbsd"))
- ))]
- pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(8);
- #[cfg(all(
- target_pointer_width = "64",
- not(any(target_os = "macos", target_os = "openbsd"))
- ))]
- pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16);
- #[cfg(all(
- any(target_pointer_width = "32", target_pointer_width = "64"),
- any(target_os = "macos", target_os = "openbsd")
- ))]
- pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = Some(16);
- #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
- pub const DEFAULT_ALIGNMENT_BYTES: Option<usize> = None;
-
- pub const fn new() -> Self {
- Self { _unused: [] }
- }
-}
-
-impl Default for QemuAllocator {
- fn default() -> Self {
- Self::new()
- }
-}
-
-// Sanity check.
-const _: [(); 8] = [(); ::core::mem::size_of::<*mut c_void>()];
-
-unsafe impl GlobalAlloc for QemuAllocator {
- unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
- if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0))
- {
- // SAFETY: g_malloc0() is safe to call.
- unsafe { g_malloc0(layout.size().try_into().unwrap()).cast::<u8>() }
- } else {
- #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
- {
- // SAFETY: g_aligned_alloc0() is safe to call.
- unsafe {
- g_aligned_alloc0(
- layout.size().try_into().unwrap(),
- 1,
- layout.align().try_into().unwrap(),
- )
- .cast::<u8>()
- }
- }
- #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
- {
- // SAFETY: qemu_memalign() is safe to call.
- unsafe { qemu_memalign(layout.align(), layout.size()).cast::<u8>() }
- }
- }
- }
-
- unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
- if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0))
- {
- // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid
- // glib-allocated pointer, so `g_free`ing is safe.
- unsafe { g_free(ptr.cast::<_>()) }
- } else {
- #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)]
- {
- // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned
- // glib-allocated pointer, so `g_aligned_free`ing is safe.
- unsafe { g_aligned_free(ptr.cast::<_>()) }
- }
- #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))]
- {
- // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned
- // glib-allocated pointer, so `qemu_vfree`ing is safe.
- unsafe { qemu_vfree(ptr.cast::<_>()) }
- }
- }
- }
-}
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
deleted file mode 100644
index 43bfcd5..0000000
--- a/rust/qemu-api/src/prelude.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2024 Red Hat, Inc.
-// Author(s): Paolo Bonzini <pbonzini@redhat.com>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-//! Commonly used traits and types for QEMU.
-
-pub use crate::bitops::IntegerExt;
-
-pub use crate::cell::BqlCell;
-pub use crate::cell::BqlRefCell;
-
-pub use crate::errno;
-
-pub use crate::qdev::DeviceMethods;
-
-pub use crate::qom::InterfaceType;
-pub use crate::qom::IsA;
-pub use crate::qom::Object;
-pub use crate::qom::ObjectCast;
-pub use crate::qom::ObjectDeref;
-pub use crate::qom::ObjectClassMethods;
-pub use crate::qom::ObjectMethods;
-pub use crate::qom::ObjectType;
-
-pub use crate::qom_isa;
-
-pub use crate::sysbus::SysBusDeviceMethods;
-
-pub use crate::vmstate::VMState;
diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs
deleted file mode 100644
index d8239d0..0000000
--- a/rust/qemu-api/src/zeroable.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-//! Defines a trait for structs that can be safely initialized with zero bytes.
-
-/// Encapsulates the requirement that
-/// `MaybeUninit::<Self>::zeroed().assume_init()` does not cause undefined
-/// behavior.
-///
-/// # Safety
-///
-/// Do not add this trait to a type unless all-zeroes is a valid value for the
-/// type. In particular, raw pointers can be zero, but references and
-/// `NonNull<T>` cannot.
-pub unsafe trait Zeroable: Default {
- /// Return a value of Self whose memory representation consists of all
- /// zeroes, with the possible exclusion of padding bytes.
- const ZERO: Self = unsafe { ::core::mem::MaybeUninit::<Self>::zeroed().assume_init() };
-}
-
-// bindgen does not derive Default here
-#[allow(clippy::derivable_impls)]
-impl Default for crate::bindings::VMStateFlags {
- fn default() -> Self {
- Self(0)
- }
-}
-
-unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {}
-unsafe impl Zeroable for crate::bindings::Property {}
-unsafe impl Zeroable for crate::bindings::VMStateFlags {}
-unsafe impl Zeroable for crate::bindings::VMStateField {}
-unsafe impl Zeroable for crate::bindings::VMStateDescription {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {}
-unsafe impl Zeroable for crate::bindings::MemoryRegionOps {}
-unsafe impl Zeroable for crate::bindings::MemTxAttrs {}
-unsafe impl Zeroable for crate::bindings::CharBackend {}
diff --git a/rust/qemu-api-macros/Cargo.toml b/rust/qemu-macros/Cargo.toml
index 0cd40c8..c25b6c0 100644
--- a/rust/qemu-api-macros/Cargo.toml
+++ b/rust/qemu-macros/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "qemu_api_macros"
+name = "qemu_macros"
version = "0.1.0"
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
description = "Rust bindings for QEMU - Utility macros"
@@ -16,6 +16,7 @@ rust-version.workspace = true
proc-macro = true
[dependencies]
+attrs = "0.2.9"
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["extra-traits"] }
diff --git a/rust/qemu-macros/meson.build b/rust/qemu-macros/meson.build
new file mode 100644
index 0000000..0f27e0d
--- /dev/null
+++ b/rust/qemu-macros/meson.build
@@ -0,0 +1,23 @@
+_qemu_macros_rs = rust.proc_macro(
+ 'qemu_macros',
+ files('src/lib.rs'),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_args: [
+ '--cfg', 'use_fallback',
+ '--cfg', 'feature="syn-error"',
+ '--cfg', 'feature="proc-macro"',
+ ],
+ dependencies: [
+ attrs_rs_native,
+ proc_macro2_rs_native,
+ quote_rs_native,
+ syn_rs_native,
+ ],
+)
+
+qemu_macros = declare_dependency(
+ link_with: _qemu_macros_rs,
+)
+
+rust.test('rust-qemu-macros-tests', _qemu_macros_rs,
+ suite: ['unit', 'rust'])
diff --git a/rust/qemu-macros/src/bits.rs b/rust/qemu-macros/src/bits.rs
new file mode 100644
index 0000000..a80a3b9
--- /dev/null
+++ b/rust/qemu-macros/src/bits.rs
@@ -0,0 +1,213 @@
+// 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 syn::Error;
+
+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>, Error> {
+ let next = match tok {
+ TT::Group(ref g) => {
+ if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None {
+ return Err(Error::new(g.span(), "expected parenthesis"));
+ }
+ let mut stream = g.stream().into_iter();
+ let Some(first_tok) = stream.next() else {
+ return Err(Error::new(g.span(), "expected operand, found ')'"));
+ };
+ 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(Error::new(tok.span(), format!("unexpected token {tok}")));
+ }
+ 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(Error::new(p.span(), "expected operand"));
+ }
+ let Some(rhs_tok) = it.next() else {
+ return Err(Error::new(p.span(), "expected operand at end of input"));
+ };
+ let next = self.parse_primary(rhs_tok, it, out)?;
+ out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]);
+ next
+ }
+ _ => {
+ return Err(Error::new(tok.span(), "unexpected literal"));
+ }
+ };
+ Ok(next)
+ }
+
+ fn parse_binop<
+ F: Fn(
+ &Self,
+ TokenTree,
+ &mut dyn Iterator<Item = TokenTree>,
+ &mut TokenStream,
+ ) -> Result<Option<TokenTree>, Error>,
+ >(
+ &self,
+ tok: TokenTree,
+ it: &mut dyn Iterator<Item = TokenTree>,
+ out: &mut TokenStream,
+ ch: char,
+ f: F,
+ method: &'static str,
+ ) -> Result<Option<TokenTree>, Error> {
+ 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(Error::new(p.span(), "expected operand at end of input"));
+ };
+ 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>, Error> {
+ 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>, Error> {
+ 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>, Error> {
+ 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>, Error> {
+ self.parse_binop(tok, it, out, '|', Self::parse_xor, "union")
+ }
+
+ pub fn parse(
+ it: &mut dyn Iterator<Item = TokenTree>,
+ ) -> Result<proc_macro2::TokenStream, Error> {
+ 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(Error::new(
+ pos,
+ "expected expression, do not call this macro directly",
+ ));
+ };
+ let TT::Group(ref _group) = tok else {
+ return Err(Error::new(
+ tok.span(),
+ "expected parenthesis, do not call this macro directly",
+ ));
+ };
+ 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(Error::new(tok.span(), format!("unexpected token {tok}")));
+ }
+ Ok(out)
+ }
+}
diff --git a/rust/qemu-macros/src/lib.rs b/rust/qemu-macros/src/lib.rs
new file mode 100644
index 0000000..3e21b67
--- /dev/null
+++ b/rust/qemu-macros/src/lib.rs
@@ -0,0 +1,407 @@
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use proc_macro::TokenStream;
+use quote::{quote, quote_spanned};
+use syn::{
+ parse::{Parse, ParseStream},
+ parse_macro_input, parse_quote,
+ punctuated::Punctuated,
+ spanned::Spanned,
+ token::Comma,
+ Attribute, Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token,
+ Variant,
+};
+mod bits;
+use bits::BitsConstInternal;
+
+#[cfg(test)]
+mod tests;
+
+fn get_fields<'a>(
+ input: &'a DeriveInput,
+ msg: &str,
+) -> Result<&'a Punctuated<Field, Comma>, Error> {
+ let Data::Struct(ref s) = &input.data else {
+ return Err(Error::new(
+ input.ident.span(),
+ format!("Struct required for {msg}"),
+ ));
+ };
+ let Fields::Named(ref fs) = &s.fields else {
+ return Err(Error::new(
+ input.ident.span(),
+ format!("Named fields required for {msg}"),
+ ));
+ };
+ Ok(&fs.named)
+}
+
+fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, Error> {
+ let Data::Struct(ref s) = &input.data else {
+ return Err(Error::new(
+ input.ident.span(),
+ format!("Struct required for {msg}"),
+ ));
+ };
+ let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else {
+ return Err(Error::new(
+ s.fields.span(),
+ format!("Tuple struct required for {msg}"),
+ ));
+ };
+ if unnamed.len() != 1 {
+ return Err(Error::new(
+ s.fields.span(),
+ format!("A single field is required for {msg}"),
+ ));
+ }
+ Ok(&unnamed[0])
+}
+
+fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> {
+ let expected = parse_quote! { #[repr(C)] };
+
+ if input.attrs.iter().any(|attr| attr == &expected) {
+ Ok(())
+ } else {
+ Err(Error::new(
+ input.ident.span(),
+ format!("#[repr(C)] required for {msg}"),
+ ))
+ }
+}
+
+fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> {
+ let expected = parse_quote! { #[repr(transparent)] };
+
+ if input.attrs.iter().any(|attr| attr == &expected) {
+ Ok(())
+ } else {
+ Err(Error::new(
+ input.ident.span(),
+ format!("#[repr(transparent)] required for {msg}"),
+ ))
+ }
+}
+
+fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
+ is_c_repr(&input, "#[derive(Object)]")?;
+
+ let name = &input.ident;
+ let parent = &get_fields(&input, "#[derive(Object)]")?
+ .get(0)
+ .ok_or_else(|| {
+ Error::new(
+ input.ident.span(),
+ "#[derive(Object)] requires a parent field",
+ )
+ })?
+ .ident;
+
+ Ok(quote! {
+ ::common::assert_field_type!(#name, #parent,
+ ::qom::ParentField<<#name as ::qom::ObjectImpl>::ParentType>);
+
+ ::util::module_init! {
+ MODULE_INIT_QOM => unsafe {
+ ::qom::type_register_static(&<#name as ::qom::ObjectImpl>::TYPE_INFO);
+ }
+ }
+ })
+}
+
+#[proc_macro_derive(Object)]
+pub fn derive_object(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ derive_object_or_error(input)
+ .unwrap_or_else(syn::Error::into_compile_error)
+ .into()
+}
+
+fn derive_opaque_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
+ is_transparent_repr(&input, "#[derive(Wrapper)]")?;
+
+ let name = &input.ident;
+ let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?;
+ let typ = &field.ty;
+
+ Ok(quote! {
+ unsafe impl ::common::opaque::Wrapper for #name {
+ type Wrapped = <#typ as ::common::opaque::Wrapper>::Wrapped;
+ }
+ impl #name {
+ pub unsafe fn from_raw<'a>(ptr: *mut <Self as ::common::opaque::Wrapper>::Wrapped) -> &'a Self {
+ let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::<Self>();
+ unsafe { ptr.as_ref() }
+ }
+
+ pub const fn as_mut_ptr(&self) -> *mut <Self as ::common::opaque::Wrapper>::Wrapped {
+ self.0.as_mut_ptr()
+ }
+
+ pub const fn as_ptr(&self) -> *const <Self as ::common::opaque::Wrapper>::Wrapped {
+ self.0.as_ptr()
+ }
+
+ pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void {
+ self.0.as_void_ptr()
+ }
+
+ pub const fn raw_get(slot: *mut Self) -> *mut <Self as ::common::opaque::Wrapper>::Wrapped {
+ slot.cast()
+ }
+ }
+ })
+}
+
+#[derive(Debug)]
+enum DevicePropertyName {
+ CStr(syn::LitCStr),
+ Str(syn::LitStr),
+}
+
+impl Parse for DevicePropertyName {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
+ let lo = input.lookahead1();
+ if lo.peek(syn::LitStr) {
+ Ok(Self::Str(input.parse()?))
+ } else if lo.peek(syn::LitCStr) {
+ Ok(Self::CStr(input.parse()?))
+ } else {
+ Err(lo.error())
+ }
+ }
+}
+
+#[derive(Default, Debug)]
+struct DeviceProperty {
+ rename: Option<DevicePropertyName>,
+ bitnr: Option<syn::Expr>,
+ defval: Option<syn::Expr>,
+}
+
+impl DeviceProperty {
+ fn parse_from(&mut self, a: &Attribute) -> syn::Result<()> {
+ use attrs::{set, with, Attrs};
+ let mut parser = Attrs::new();
+ parser.once("rename", with::eq(set::parse(&mut self.rename)));
+ parser.once("bit", with::eq(set::parse(&mut self.bitnr)));
+ parser.once("default", with::eq(set::parse(&mut self.defval)));
+ a.parse_args_with(&mut parser)
+ }
+
+ fn parse(a: &Attribute) -> syn::Result<Self> {
+ let mut retval = Self::default();
+ retval.parse_from(a)?;
+ Ok(retval)
+ }
+}
+
+#[proc_macro_derive(Device, attributes(property))]
+pub fn derive_device(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ derive_device_or_error(input)
+ .unwrap_or_else(syn::Error::into_compile_error)
+ .into()
+}
+
+fn derive_device_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
+ is_c_repr(&input, "#[derive(Device)]")?;
+ let properties: Vec<(syn::Field, DeviceProperty)> = get_fields(&input, "#[derive(Device)]")?
+ .iter()
+ .flat_map(|f| {
+ f.attrs
+ .iter()
+ .filter(|a| a.path().is_ident("property"))
+ .map(|a| Ok((f.clone(), DeviceProperty::parse(a)?)))
+ })
+ .collect::<Result<Vec<_>, Error>>()?;
+ let name = &input.ident;
+ let mut properties_expanded = vec![];
+
+ for (field, prop) in properties {
+ let DeviceProperty {
+ rename,
+ bitnr,
+ defval,
+ } = prop;
+ let field_name = field.ident.unwrap();
+ macro_rules! str_to_c_str {
+ ($value:expr, $span:expr) => {{
+ let (value, span) = ($value, $span);
+ let cstr = std::ffi::CString::new(value.as_str()).map_err(|err| {
+ Error::new(
+ span,
+ format!(
+ "Property name `{value}` cannot be represented as a C string: {err}"
+ ),
+ )
+ })?;
+ let cstr_lit = syn::LitCStr::new(&cstr, span);
+ Ok(quote! { #cstr_lit })
+ }};
+ }
+
+ let prop_name = rename.map_or_else(
+ || str_to_c_str!(field_name.to_string(), field_name.span()),
+ |prop_rename| -> Result<proc_macro2::TokenStream, Error> {
+ match prop_rename {
+ DevicePropertyName::CStr(cstr_lit) => Ok(quote! { #cstr_lit }),
+ DevicePropertyName::Str(str_lit) => {
+ str_to_c_str!(str_lit.value(), str_lit.span())
+ }
+ }
+ },
+ )?;
+ let field_ty = field.ty.clone();
+ let qdev_prop = if bitnr.is_none() {
+ quote! { <#field_ty as ::hwcore::QDevProp>::BASE_INFO }
+ } else {
+ quote! { <#field_ty as ::hwcore::QDevProp>::BIT_INFO }
+ };
+ let bitnr = bitnr.unwrap_or(syn::Expr::Verbatim(quote! { 0 }));
+ let set_default = defval.is_some();
+ let defval = defval.unwrap_or(syn::Expr::Verbatim(quote! { 0 }));
+ properties_expanded.push(quote! {
+ ::hwcore::bindings::Property {
+ name: ::std::ffi::CStr::as_ptr(#prop_name),
+ info: #qdev_prop,
+ offset: ::core::mem::offset_of!(#name, #field_name) as isize,
+ bitnr: #bitnr,
+ set_default: #set_default,
+ defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: #defval as u64 },
+ ..::common::Zeroable::ZERO
+ }
+ });
+ }
+
+ Ok(quote_spanned! {input.span() =>
+ unsafe impl ::hwcore::DevicePropertiesImpl for #name {
+ const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
+ #(#properties_expanded),*
+ ];
+ }
+ })
+}
+
+#[proc_macro_derive(Wrapper)]
+pub fn derive_opaque(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ derive_opaque_or_error(input)
+ .unwrap_or_else(syn::Error::into_compile_error)
+ .into()
+}
+
+#[allow(non_snake_case)]
+fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, Error> {
+ let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
+ if let Some(repr) = repr {
+ let nested = repr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
+ for meta in nested {
+ match meta {
+ Meta::Path(path) if path.is_ident("u8") => return Ok(path),
+ Meta::Path(path) if path.is_ident("u16") => return Ok(path),
+ Meta::Path(path) if path.is_ident("u32") => return Ok(path),
+ Meta::Path(path) if path.is_ident("u64") => return Ok(path),
+ _ => {}
+ }
+ }
+ }
+
+ Err(Error::new(
+ input.ident.span(),
+ format!("#[repr(u8/u16/u32/u64) required for {msg}"),
+ ))
+}
+
+fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, Error> {
+ let Data::Enum(ref e) = &input.data else {
+ return Err(Error::new(
+ input.ident.span(),
+ "Cannot derive TryInto for union or struct.",
+ ));
+ };
+ if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) {
+ return Err(Error::new(
+ v.fields.span(),
+ "Cannot derive TryInto for enum with non-unit variants.",
+ ));
+ }
+ Ok(&e.variants)
+}
+
+#[rustfmt::skip::macros(quote)]
+fn derive_tryinto_body(
+ name: &Ident,
+ variants: &Punctuated<Variant, Comma>,
+ repr: &Path,
+) -> Result<proc_macro2::TokenStream, Error> {
+ 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, Error> {
+ let repr = get_repr_uN(&input, "#[derive(TryInto)]")?;
+ let name = &input.ident;
+ 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;
+
+ #[allow(ambiguous_associated_items)]
+ fn try_from(value: #repr) -> Result<Self, #repr> {
+ #body
+ }
+ }
+ })
+}
+
+#[proc_macro_derive(TryInto)]
+pub fn derive_tryinto(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ derive_tryinto_or_error(input)
+ .unwrap_or_else(syn::Error::into_compile_error)
+ .into()
+}
+
+#[proc_macro]
+pub fn bits_const_internal(ts: TokenStream) -> TokenStream {
+ let ts = proc_macro2::TokenStream::from(ts);
+ let mut it = ts.into_iter();
+
+ BitsConstInternal::parse(&mut it)
+ .unwrap_or_else(syn::Error::into_compile_error)
+ .into()
+}
diff --git a/rust/qemu-macros/src/tests.rs b/rust/qemu-macros/src/tests.rs
new file mode 100644
index 0000000..ac998d2
--- /dev/null
+++ b/rust/qemu-macros/src/tests.rs
@@ -0,0 +1,347 @@
+// Copyright 2025, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use quote::quote;
+
+use super::*;
+
+macro_rules! derive_compile_fail {
+ ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{
+ let input: proc_macro2::TokenStream = $input;
+ let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*];
+ let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> =
+ $derive_fn;
+
+ let input: syn::DeriveInput = syn::parse2(input).unwrap();
+ let result = derive_fn(input);
+ let err = result.unwrap_err().into_compile_error();
+ assert_eq!(
+ err.to_string(),
+ quote! { #(#error_msg)* }.to_string()
+ );
+ }};
+}
+
+macro_rules! derive_compile {
+ ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{
+ let input: proc_macro2::TokenStream = $input;
+ let expected: proc_macro2::TokenStream = $($expected)*;
+ let derive_fn: fn(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> =
+ $derive_fn;
+
+ let input: syn::DeriveInput = syn::parse2(input).unwrap();
+ let result = derive_fn(input).unwrap();
+ assert_eq!(result.to_string(), expected.to_string());
+ }};
+}
+
+#[test]
+fn test_derive_device() {
+ // Check that repr(C) is used
+ derive_compile_fail!(
+ derive_device_or_error,
+ quote! {
+ #[derive(Device)]
+ struct Foo {
+ _unused: [u8; 0],
+ }
+ },
+ "#[repr(C)] required for #[derive(Device)]"
+ );
+ // Check that invalid/misspelled attributes raise an error
+ derive_compile_fail!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ struct DummyState {
+ #[property(defalt = true)]
+ migrate_clock: bool,
+ }
+ },
+ "Expected one of `bit`, `default` or `rename`"
+ );
+ // Check that repeated attributes are not allowed:
+ derive_compile_fail!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ struct DummyState {
+ #[property(rename = "migrate-clk", rename = "migrate-clk", default = true)]
+ migrate_clock: bool,
+ }
+ },
+ "Duplicate argument",
+ "Already used here",
+ );
+ derive_compile_fail!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ struct DummyState {
+ #[property(default = true, default = true)]
+ migrate_clock: bool,
+ }
+ },
+ "Duplicate argument",
+ "Already used here",
+ );
+ derive_compile_fail!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ struct DummyState {
+ #[property(bit = 0, bit = 1)]
+ flags: u32,
+ }
+ },
+ "Duplicate argument",
+ "Already used here",
+ );
+ // Check that the field name is preserved when `rename` isn't used:
+ derive_compile!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ pub struct DummyState {
+ parent: ParentField<DeviceState>,
+ #[property(default = true)]
+ migrate_clock: bool,
+ }
+ },
+ quote! {
+ unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
+ const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
+ ::hwcore::bindings::Property {
+ name: ::std::ffi::CStr::as_ptr(c"migrate_clock"),
+ info: <bool as ::hwcore::QDevProp>::BASE_INFO,
+ offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
+ bitnr: 0,
+ set_default: true,
+ defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
+ ..::common::Zeroable::ZERO
+ }
+ ];
+ }
+ }
+ );
+ // Check that `rename` value is used for the property name when used:
+ derive_compile!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ pub struct DummyState {
+ parent: ParentField<DeviceState>,
+ #[property(rename = "migrate-clk", default = true)]
+ migrate_clock: bool,
+ }
+ },
+ quote! {
+ unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
+ const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
+ ::hwcore::bindings::Property {
+ name: ::std::ffi::CStr::as_ptr(c"migrate-clk"),
+ info: <bool as ::hwcore::QDevProp>::BASE_INFO,
+ offset: ::core::mem::offset_of!(DummyState, migrate_clock) as isize,
+ bitnr: 0,
+ set_default: true,
+ defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
+ ..::common::Zeroable::ZERO
+ }
+ ];
+ }
+ }
+ );
+ // Check that `bit` value is used for the bit property without default
+ // value (note: though C macro (e.g., DEFINE_PROP_BIT) always requires
+ // default value, Rust side allows to default this field to "0"):
+ derive_compile!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ pub struct DummyState {
+ parent: ParentField<DeviceState>,
+ #[property(bit = 3)]
+ flags: u32,
+ }
+ },
+ quote! {
+ unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
+ const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
+ ::hwcore::bindings::Property {
+ name: ::std::ffi::CStr::as_ptr(c"flags"),
+ info: <u32 as ::hwcore::QDevProp>::BIT_INFO,
+ offset: ::core::mem::offset_of!(DummyState, flags) as isize,
+ bitnr: 3,
+ set_default: false,
+ defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: 0 as u64 },
+ ..::common::Zeroable::ZERO
+ }
+ ];
+ }
+ }
+ );
+ // Check that `bit` value is used for the bit property when used:
+ derive_compile!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ pub struct DummyState {
+ parent: ParentField<DeviceState>,
+ #[property(bit = 3, default = true)]
+ flags: u32,
+ }
+ },
+ quote! {
+ unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
+ const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
+ ::hwcore::bindings::Property {
+ name: ::std::ffi::CStr::as_ptr(c"flags"),
+ info: <u32 as ::hwcore::QDevProp>::BIT_INFO,
+ offset: ::core::mem::offset_of!(DummyState, flags) as isize,
+ bitnr: 3,
+ set_default: true,
+ defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: true as u64 },
+ ..::common::Zeroable::ZERO
+ }
+ ];
+ }
+ }
+ );
+ // Check that `bit` value is used for the bit property with rename when used:
+ derive_compile!(
+ derive_device_or_error,
+ quote! {
+ #[repr(C)]
+ #[derive(Device)]
+ pub struct DummyState {
+ parent: ParentField<DeviceState>,
+ #[property(rename = "msi", bit = 3, default = false)]
+ flags: u64,
+ }
+ },
+ quote! {
+ unsafe impl ::hwcore::DevicePropertiesImpl for DummyState {
+ const PROPERTIES: &'static [::hwcore::bindings::Property] = &[
+ ::hwcore::bindings::Property {
+ name: ::std::ffi::CStr::as_ptr(c"msi"),
+ info: <u64 as ::hwcore::QDevProp>::BIT_INFO,
+ offset: ::core::mem::offset_of!(DummyState, flags) as isize,
+ bitnr: 3,
+ set_default: true,
+ defval: ::hwcore::bindings::Property__bindgen_ty_1 { u: false as u64 },
+ ..::common::Zeroable::ZERO
+ }
+ ];
+ }
+ }
+ );
+}
+
+#[test]
+fn test_derive_object() {
+ derive_compile_fail!(
+ derive_object_or_error,
+ quote! {
+ #[derive(Object)]
+ struct Foo {
+ _unused: [u8; 0],
+ }
+ },
+ "#[repr(C)] required for #[derive(Object)]"
+ );
+ derive_compile!(
+ derive_object_or_error,
+ quote! {
+ #[derive(Object)]
+ #[repr(C)]
+ struct Foo {
+ _unused: [u8; 0],
+ }
+ },
+ quote! {
+ ::common::assert_field_type!(
+ Foo,
+ _unused,
+ ::qom::ParentField<<Foo as ::qom::ObjectImpl>::ParentType>
+ );
+ ::util::module_init! {
+ MODULE_INIT_QOM => unsafe {
+ ::qom::type_register_static(&<Foo as ::qom::ObjectImpl>::TYPE_INFO);
+ }
+ }
+ }
+ );
+}
+
+#[test]
+fn test_derive_tryinto() {
+ derive_compile_fail!(
+ derive_tryinto_or_error,
+ quote! {
+ #[derive(TryInto)]
+ struct Foo {
+ _unused: [u8; 0],
+ }
+ },
+ "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]"
+ );
+ derive_compile!(
+ derive_tryinto_or_error,
+ quote! {
+ #[derive(TryInto)]
+ #[repr(u8)]
+ enum Foo {
+ First = 0,
+ Second,
+ }
+ },
+ quote! {
+ impl Foo {
+ #[allow(dead_code)]
+ pub const fn into_bits(self) -> u8 {
+ self as u8
+ }
+
+ #[allow(dead_code)]
+ pub const fn from_bits(value: u8) -> Self {
+ match ({
+ const First: u8 = Foo::First as u8;
+ const Second: u8 = Foo::Second as u8;
+ match value {
+ First => core::result::Result::Ok(Foo::First),
+ Second => core::result::Result::Ok(Foo::Second),
+ _ => core::result::Result::Err(value),
+ }
+ }) {
+ Ok(x) => x,
+ Err(_) => panic!("invalid value for Foo"),
+ }
+ }
+ }
+
+ impl core::convert::TryFrom<u8> for Foo {
+ type Error = u8;
+
+ #[allow(ambiguous_associated_items)]
+ fn try_from(value: u8) -> Result<Self, u8> {
+ const First: u8 = Foo::First as u8;
+ const Second: u8 = Foo::Second as u8;
+ match value {
+ First => core::result::Result::Ok(Foo::First),
+ Second => core::result::Result::Ok(Foo::Second),
+ _ => core::result::Result::Err(value),
+ }
+ }
+ }
+ }
+ );
+}
diff --git a/rust/qom/Cargo.toml b/rust/qom/Cargo.toml
new file mode 100644
index 0000000..4be3c25
--- /dev/null
+++ b/rust/qom/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "qom"
+version = "0.1.0"
+description = "Rust bindings for QEMU/QOM"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../common" }
+bql = { path = "../bql" }
+migration = { path = "../migration" }
+qemu_macros = { path = "../qemu-macros" }
+util = { path = "../util" }
+glib-sys.workspace = true
+
+[lints]
+workspace = true
diff --git a/rust/qom/build.rs b/rust/qom/build.rs
new file mode 120000
index 0000000..71a3167
--- /dev/null
+++ b/rust/qom/build.rs
@@ -0,0 +1 @@
+../util/build.rs \ No newline at end of file
diff --git a/rust/qom/meson.build b/rust/qom/meson.build
new file mode 100644
index 0000000..e50f418
--- /dev/null
+++ b/rust/qom/meson.build
@@ -0,0 +1,43 @@
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_qom_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common,
+ c_args: bindgen_c_args,
+)
+
+_qom_rs = static_library(
+ 'qom',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/prelude.rs',
+ 'src/qom.rs',
+ ],
+ {'.': _qom_bindings_inc_rs}
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ link_with: [_bql_rs, _migration_rs],
+ dependencies: [common_rs, glib_sys_rs, qemu_macros],
+)
+
+qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom])
+
+# Doctests are essentially integration tests, so they need the same dependencies.
+# Note that running them requires the object files for C code, so place them
+# in a separate suite that is run by the "build" CI jobs rather than "check".
+rust.doctest('rust-qom-rs-doctests',
+ _qom_rs,
+ dependencies: qom_rs,
+ suite: ['doc', 'rust'])
diff --git a/rust/qom/src/bindings.rs b/rust/qom/src/bindings.rs
new file mode 100644
index 0000000..91de42f
--- /dev/null
+++ b/rust/qom/src/bindings.rs
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+use glib_sys::{GHashTable, GHashTableIter, GList, GPtrArray, GQueue, GSList};
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/qom/src/lib.rs b/rust/qom/src/lib.rs
new file mode 100644
index 0000000..24c44fc
--- /dev/null
+++ b/rust/qom/src/lib.rs
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub use qemu_macros::Object;
+
+pub mod bindings;
+
+// preserve one-item-per-"use" syntax, it is clearer
+// for prelude-like modules
+#[rustfmt::skip]
+pub mod prelude;
+
+mod qom;
+pub use qom::*;
diff --git a/rust/qom/src/prelude.rs b/rust/qom/src/prelude.rs
new file mode 100644
index 0000000..00a6095
--- /dev/null
+++ b/rust/qom/src/prelude.rs
@@ -0,0 +1,12 @@
+//! Traits and essential types intended for blanket imports.
+
+pub use crate::qom::InterfaceType;
+pub use crate::qom::IsA;
+pub use crate::qom::Object;
+pub use crate::qom::ObjectCast;
+pub use crate::qom::ObjectClassMethods;
+pub use crate::qom::ObjectDeref;
+pub use crate::qom::ObjectMethods;
+pub use crate::qom::ObjectType;
+
+pub use crate::qom_isa;
diff --git a/rust/qemu-api/src/qom.rs b/rust/qom/src/qom.rs
index 41e5a5e..5808051 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qom/src/qom.rs
@@ -95,24 +95,24 @@
use std::{
ffi::{c_void, CStr},
fmt,
- mem::ManuallyDrop,
+ marker::PhantomData,
+ mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut},
ptr::NonNull,
};
-pub use bindings::ObjectClass;
+use common::Opaque;
+use migration::impl_vmstate_pointer;
-use crate::{
- bindings::{
- self, object_class_dynamic_cast, object_dynamic_cast, object_get_class,
- object_get_typename, object_new, object_ref, object_unref, TypeInfo,
- },
- cell::{bql_locked, Opaque},
+use crate::bindings::{
+ self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, object_get_typename,
+ object_new, object_ref, object_unref, TypeInfo,
};
+pub use crate::bindings::{type_register_static, ObjectClass};
/// A safe wrapper around [`bindings::Object`].
#[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
pub struct Object(Opaque<bindings::Object>);
unsafe impl Send for Object {}
@@ -146,7 +146,7 @@ macro_rules! qom_isa {
$(
// SAFETY: it is the caller responsibility to have $parent as the
// first field
- unsafe impl $crate::qom::IsA<$parent> for $struct {}
+ unsafe impl $crate::IsA<$parent> for $struct {}
impl AsRef<$parent> for $struct {
fn as_ref(&self) -> &$parent {
@@ -173,7 +173,7 @@ macro_rules! qom_isa {
///
/// ```ignore
/// #[repr(C)]
-/// #[derive(qemu_api_macros::Object)]
+/// #[derive(qom::Object)]
/// pub struct MyDevice {
/// parent: ParentField<DeviceState>,
/// ...
@@ -206,13 +206,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 const fn as_object_mut_ptr(&self) -> *mut bindings::Object {
+ self.as_object_ptr().cast_mut()
+ }
+
+ /// Return the receiver as a mutable raw pointer to Object.
+ ///
+ /// # Safety
+ ///
+ /// Fields beyond `Object` could be uninitialized and it's your
+ /// responsibility to avoid that they're used when the pointer is
+ /// dereferenced, either directly or through a cast.
+ pub const fn as_object_ptr(&self) -> *const bindings::Object {
+ self.0.as_ptr().cast()
+ }
+}
+
+impl<'a, T: ObjectImpl> ParentInit<'a, T> {
+ /// Convert from a derived type to one of its parent types, which
+ /// have already been initialized.
+ ///
+ /// # Safety
+ ///
+ /// Structurally this is always a safe operation; the [`IsA`] trait
+ /// provides static verification trait that `Self` dereferences to `U` or
+ /// a child of `U`, and only parent types of `T` are allowed.
+ ///
+ /// However, while the fields of the resulting reference are initialized,
+ /// calls might use uninitialized fields of the subclass. It is your
+ /// responsibility to avoid this.
+ pub const unsafe fn upcast<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 +468,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 +656,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
@@ -693,7 +870,7 @@ impl<T: ObjectType> ObjectDeref for Owned<T> {}
impl<T: ObjectType> Drop for Owned<T> {
fn drop(&mut self) {
- assert!(bql_locked());
+ assert!(bql::is_locked());
// SAFETY: creation method is unsafe, and whoever calls it has
// responsibility that the pointer is valid, and remains valid
// throughout the lifetime of the `Owned<T>` and its clones.
@@ -717,7 +894,7 @@ impl<T: IsA<Object>> fmt::Debug for Owned<T> {
pub trait ObjectClassMethods: IsA<Object> {
/// Return a new reference counted instance of this class
fn new() -> Owned<Self> {
- assert!(bql_locked());
+ assert!(bql::is_locked());
// SAFETY: the object created by object_new is allocated on
// the heap and has a reference count of 1
unsafe {
@@ -770,3 +947,5 @@ where
impl<T> ObjectClassMethods for T where T: IsA<Object> {}
impl<R: ObjectDeref> ObjectMethods for R where R::Target: IsA<Object> {}
+
+impl_vmstate_pointer!(Owned<T> where T: VMState + ObjectType);
diff --git a/rust/qom/wrapper.h b/rust/qom/wrapper.h
new file mode 100644
index 0000000..3b71bcd
--- /dev/null
+++ b/rust/qom/wrapper.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "qom/object.h"
diff --git a/rust/system/Cargo.toml b/rust/system/Cargo.toml
new file mode 100644
index 0000000..186ea00
--- /dev/null
+++ b/rust/system/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "system"
+version = "0.1.0"
+description = "Rust bindings for QEMU/system"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../common" }
+qom = { path = "../qom" }
+util = { path = "../util" }
+glib-sys.workspace = true
+
+[lints]
+workspace = true
diff --git a/rust/system/build.rs b/rust/system/build.rs
new file mode 120000
index 0000000..71a3167
--- /dev/null
+++ b/rust/system/build.rs
@@ -0,0 +1 @@
+../util/build.rs \ No newline at end of file
diff --git a/rust/system/meson.build b/rust/system/meson.build
new file mode 100644
index 0000000..73d6199
--- /dev/null
+++ b/rust/system/meson.build
@@ -0,0 +1,43 @@
+c_enums = [
+ 'device_endian',
+]
+_system_bindgen_args = []
+foreach enum : c_enums
+ _system_bindgen_args += ['--rustified-enum', enum]
+endforeach
+
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_system_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common + _system_bindgen_args,
+ c_args: bindgen_c_args,
+)
+
+_system_rs = static_library(
+ 'system',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/memory.rs',
+ ],
+ {'.': _system_bindings_inc_rs}
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs],
+ dependencies: [glib_sys_rs, common_rs, qemu_macros],
+)
+
+system_rs = declare_dependency(link_with: [_system_rs],
+ dependencies: [hwcore])
diff --git a/rust/system/src/bindings.rs b/rust/system/src/bindings.rs
new file mode 100644
index 0000000..6cbb588
--- /dev/null
+++ b/rust/system/src/bindings.rs
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+use common::Zeroable;
+use glib_sys::{
+ guint, GArray, GByteArray, GHashTable, GHashTableIter, GList, GPollFD, GPtrArray, GQueue,
+ GSList, GString,
+};
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+
+// SAFETY: these are constants and vtables; the Send and Sync requirements
+// are deferred to the unsafe callbacks that they contain
+unsafe impl Send for MemoryRegionOps {}
+unsafe impl Sync for MemoryRegionOps {}
+
+// SAFETY: this is a pure data struct
+unsafe impl Send for CoalescedMemoryRange {}
+unsafe impl Sync for CoalescedMemoryRange {}
+
+unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_1 {}
+unsafe impl Zeroable for MemoryRegionOps__bindgen_ty_2 {}
+unsafe impl Zeroable for MemoryRegionOps {}
+unsafe impl Zeroable for MemTxAttrs {}
diff --git a/rust/system/src/lib.rs b/rust/system/src/lib.rs
new file mode 100644
index 0000000..aafe9a8
--- /dev/null
+++ b/rust/system/src/lib.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+
+mod memory;
+pub use memory::*;
diff --git a/rust/qemu-api/src/memory.rs b/rust/system/src/memory.rs
index 9ef2694..4b3316b 100644
--- a/rust/qemu-api/src/memory.rs
+++ b/rust/system/src/memory.rs
@@ -9,15 +9,11 @@ use std::{
marker::PhantomData,
};
-pub use bindings::{hwaddr, MemTxAttrs};
-
-use crate::{
- bindings::{self, device_endian, memory_region_init_io},
- callbacks::FnCall,
- cell::Opaque,
- prelude::*,
- zeroable::Zeroable,
-};
+use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque};
+use qom::prelude::*;
+
+use crate::bindings::{self, device_endian, memory_region_init_io};
+pub use crate::bindings::{hwaddr, MemTxAttrs};
pub struct MemoryRegionOps<T>(
bindings::MemoryRegionOps,
@@ -133,21 +129,16 @@ impl<T> Default for MemoryRegionOpsBuilder<T> {
/// A safe wrapper around [`bindings::MemoryRegion`].
#[repr(transparent)]
-#[derive(qemu_api_macros::Wrapper)]
+#[derive(common::Wrapper)]
pub struct MemoryRegion(Opaque<bindings::MemoryRegion>);
unsafe impl Send for MemoryRegion {}
unsafe impl Sync for MemoryRegion {}
impl MemoryRegion {
- // inline to ensure that it is not included in tests, which only
- // link to hwcore and qom. FIXME: inlining is actually the opposite
- // of what we want, since this is the type-erased version of the
- // init_io function below. Look into splitting the qemu_api crate.
- #[inline(always)]
unsafe fn do_init_io(
slot: *mut bindings::MemoryRegion,
- owner: *mut Object,
+ owner: *mut bindings::Object,
ops: &'static bindings::MemoryRegionOps,
name: &'static str,
size: u64,
@@ -156,7 +147,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 +157,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,
@@ -189,6 +179,7 @@ unsafe impl ObjectType for MemoryRegion {
const TYPE_NAME: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) };
}
+
qom_isa!(MemoryRegion: Object);
/// A special `MemTxAttrs` constant, used to indicate that no memory
diff --git a/rust/system/wrapper.h b/rust/system/wrapper.h
new file mode 100644
index 0000000..48abde8
--- /dev/null
+++ b/rust/system/wrapper.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "system/system.h"
+#include "system/memory.h"
+#include "system/address-spaces.h"
diff --git a/rust/tests/Cargo.toml b/rust/tests/Cargo.toml
new file mode 100644
index 0000000..d47dc33
--- /dev/null
+++ b/rust/tests/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "tests"
+version = "0.1.0"
+description = "Rust integration tests for QEMU"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+common = { path = "../common" }
+chardev = { path = "../chardev" }
+hwcore = { path = "../hw/core" }
+migration = { path = "../migration" }
+util = { path = "../util" }
+bql = { path = "../bql" }
+qom = { path = "../qom" }
+system = { path = "../system" }
+
+[lints]
+workspace = true
diff --git a/rust/tests/meson.build b/rust/tests/meson.build
new file mode 100644
index 0000000..00688c6
--- /dev/null
+++ b/rust/tests/meson.build
@@ -0,0 +1,14 @@
+test('rust-integration',
+ executable(
+ 'rust-integration',
+ files('tests/vmstate_tests.rs'),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_args: ['--test'],
+ install: false,
+ dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs]),
+ args: [
+ '--test', '--test-threads', '1',
+ '--format', 'pretty',
+ ],
+ protocol: 'rust',
+ suite: ['unit', 'rust'])
diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/tests/tests/vmstate_tests.rs
index ad0fc5c..fa9bbd6 100644
--- a/rust/qemu-api/tests/vmstate_tests.rs
+++ b/rust/tests/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::{
@@ -9,16 +9,16 @@ use std::{
slice,
};
-use qemu_api::{
+use bql::BqlCell;
+use common::Opaque;
+use migration::{
bindings::{
vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8,
vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags,
},
- cell::{BqlCell, Opaque},
- impl_vmstate_forward,
- vmstate::{VMStateDescription, VMStateField},
- vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate,
- zeroable::Zeroable,
+ impl_vmstate_forward, impl_vmstate_struct,
+ vmstate::{VMStateDescription, VMStateDescriptionBuilder, VMStateField},
+ vmstate_fields, vmstate_of, vmstate_unused, vmstate_validate,
};
const FOO_ARRAY_MAX: usize = 3;
@@ -41,22 +41,24 @@ struct FooA {
elem: i8,
}
-static VMSTATE_FOOA: VMStateDescription = VMStateDescription {
- name: c"foo_a".as_ptr(),
- version_id: 1,
- minimum_version_id: 1,
- fields: vmstate_fields! {
+static VMSTATE_FOOA: VMStateDescription<FooA> = VMStateDescriptionBuilder::<FooA>::new()
+ .name(c"foo_a")
+ .version_id(1)
+ .minimum_version_id(1)
+ .fields(vmstate_fields! {
vmstate_of!(FooA, elem),
vmstate_unused!(size_of::<i64>()),
vmstate_of!(FooA, arr[0 .. num]).with_version_id(0),
vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]),
- },
- ..Zeroable::ZERO
-};
+ })
+ .build();
+
+impl_vmstate_struct!(FooA, VMSTATE_FOOA);
#[test]
fn test_vmstate_uint16() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) };
// 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16)
assert_eq!(
@@ -76,7 +78,8 @@ fn test_vmstate_uint16() {
#[test]
fn test_vmstate_unused() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) };
// 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED)
assert_eq!(
@@ -96,7 +99,8 @@ fn test_vmstate_unused() {
#[test]
fn test_vmstate_varray_uint16_unsafe() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) };
// 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to
// VMSTATE_VARRAY_UINT16_UNSAFE)
@@ -117,7 +121,8 @@ fn test_vmstate_varray_uint16_unsafe() {
#[test]
fn test_vmstate_varray_multiply() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOA.as_ref().fields, 5) };
// 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to
// VMSTATE_VARRAY_MULTIPLY)
@@ -171,24 +176,24 @@ fn validate_foob(_state: &FooB, _version_id: u8) -> bool {
true
}
-static VMSTATE_FOOB: VMStateDescription = VMStateDescription {
- name: c"foo_b".as_ptr(),
- version_id: 2,
- minimum_version_id: 1,
- fields: vmstate_fields! {
+static VMSTATE_FOOB: VMStateDescription<FooB> = VMStateDescriptionBuilder::<FooB>::new()
+ .name(c"foo_b")
+ .version_id(2)
+ .minimum_version_id(1)
+ .fields(vmstate_fields! {
vmstate_of!(FooB, val).with_version_id(2),
vmstate_of!(FooB, wrap),
- vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1),
- vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2),
+ vmstate_of!(FooB, arr_a[0 .. num_a]).with_version_id(1),
+ vmstate_of!(FooB, arr_a_mul[0 .. num_a_mul * 32]).with_version_id(2),
vmstate_of!(FooB, arr_i64),
- vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob),
- },
- ..Zeroable::ZERO
-};
+ vmstate_of!(FooB, arr_a_wrap[0 .. num_a_wrap], validate_foob),
+ })
+ .build();
#[test]
fn test_vmstate_bool_v() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
// 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V)
assert_eq!(
@@ -208,7 +213,8 @@ fn test_vmstate_bool_v() {
#[test]
fn test_vmstate_uint64() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
// 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64)
assert_eq!(
@@ -228,7 +234,8 @@ fn test_vmstate_uint64() {
#[test]
fn test_vmstate_struct_varray_uint8() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
// 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to
// VMSTATE_STRUCT_VARRAY_UINT8)
@@ -246,13 +253,14 @@ fn test_vmstate_struct_varray_uint8() {
foo_fields[2].flags.0,
VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0
);
- assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA);
+ assert_eq!(foo_fields[2].vmsd, VMSTATE_FOOA.as_ref());
assert!(foo_fields[2].field_exists.is_none());
}
#[test]
fn test_vmstate_struct_varray_uint32_multiply() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
// 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to
// (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32)
@@ -272,13 +280,14 @@ fn test_vmstate_struct_varray_uint32_multiply() {
| VMStateFlags::VMS_VARRAY_UINT32.0
| VMStateFlags::VMS_MULTIPLY_ELEMENTS.0
);
- assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA);
+ assert_eq!(foo_fields[3].vmsd, VMSTATE_FOOA.as_ref());
assert!(foo_fields[3].field_exists.is_none());
}
#[test]
fn test_vmstate_macro_array() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
// 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to
// VMSTATE_ARRAY)
@@ -299,7 +308,8 @@ fn test_vmstate_macro_array() {
#[test]
fn test_vmstate_struct_varray_uint8_wrapper() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOB.as_ref().fields, 7) };
let mut foo_b: FooB = Default::default();
let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::<c_void>();
@@ -335,26 +345,26 @@ struct FooC {
arr_ptr_wrap: FooCWrapper,
}
-static VMSTATE_FOOC: VMStateDescription = VMStateDescription {
- name: c"foo_c".as_ptr(),
- version_id: 3,
- minimum_version_id: 1,
- fields: vmstate_fields! {
+unsafe impl Sync for FooC {}
+
+static VMSTATE_FOOC: VMStateDescription<FooC> = VMStateDescriptionBuilder::<FooC>::new()
+ .name(c"foo_c")
+ .version_id(3)
+ .minimum_version_id(1)
+ .fields(vmstate_fields! {
vmstate_of!(FooC, ptr).with_version_id(2),
- // FIXME: Currently vmstate_struct doesn't support the pointer to structure.
- // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>)
- vmstate_unused!(size_of::<NonNull<FooA>>()),
+ vmstate_of!(FooC, ptr_a),
vmstate_of!(FooC, arr_ptr),
vmstate_of!(FooC, arr_ptr_wrap),
- },
- ..Zeroable::ZERO
-};
+ })
+ .build();
const PTR_SIZE: usize = size_of::<*mut ()>();
#[test]
fn test_vmstate_pointer() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
// 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER)
assert_eq!(
@@ -376,8 +386,34 @@ fn test_vmstate_pointer() {
}
#[test]
+fn test_vmstate_struct_pointer() {
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
+
+ // 2st VMStateField ("ptr_a") in VMSTATE_FOOC (corresponding to
+ // VMSTATE_STRUCT_POINTER)
+ assert_eq!(
+ unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(),
+ b"ptr_a\0"
+ );
+ assert_eq!(foo_fields[1].offset, PTR_SIZE);
+ assert_eq!(foo_fields[1].num_offset, 0);
+ assert_eq!(foo_fields[1].vmsd, VMSTATE_FOOA.as_ref());
+ assert_eq!(foo_fields[1].version_id, 0);
+ assert_eq!(foo_fields[1].size, size_of::<FooA>());
+ assert_eq!(foo_fields[1].num, 0);
+ assert_eq!(
+ foo_fields[1].flags.0,
+ VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0
+ );
+ assert!(foo_fields[1].info.is_null());
+ assert!(foo_fields[1].field_exists.is_none());
+}
+
+#[test]
fn test_vmstate_macro_array_of_pointer() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
// 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to
// VMSTATE_ARRAY_OF_POINTER)
@@ -401,7 +437,8 @@ fn test_vmstate_macro_array_of_pointer() {
#[test]
fn test_vmstate_macro_array_of_pointer_wrapped() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOC.as_ref().fields, 6) };
// 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to
// VMSTATE_ARRAY_OF_POINTER)
@@ -432,8 +469,7 @@ fn test_vmstate_macro_array_of_pointer_wrapped() {
// * VMSTATE_FOOD:
// - VMSTATE_VALIDATE
-// Add more member fields when vmstate_of/vmstate_struct support "test"
-// parameter.
+// Add more member fields when vmstate_of support "test" parameter.
struct FooD;
impl FooD {
@@ -450,21 +486,21 @@ fn validate_food_2(_state: &FooD, _version_id: u8) -> bool {
true
}
-static VMSTATE_FOOD: VMStateDescription = VMStateDescription {
- name: c"foo_d".as_ptr(),
- version_id: 3,
- minimum_version_id: 1,
- fields: vmstate_fields! {
+static VMSTATE_FOOD: VMStateDescription<FooD> = VMStateDescriptionBuilder::<FooD>::new()
+ .name(c"foo_d")
+ .version_id(3)
+ .minimum_version_id(1)
+ .fields(vmstate_fields! {
vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0),
vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1),
vmstate_validate!(FooD, c"foo_d_2", validate_food_2),
- },
- ..Zeroable::ZERO
-};
+ })
+ .build();
#[test]
fn test_vmstate_validate() {
- let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) };
+ let foo_fields: &[VMStateField] =
+ unsafe { slice::from_raw_parts(VMSTATE_FOOD.as_ref().fields, 4) };
let mut foo_d = FooD;
let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::<c_void>();
diff --git a/rust/trace/Cargo.toml b/rust/trace/Cargo.toml
new file mode 100644
index 0000000..fc81bce
--- /dev/null
+++ b/rust/trace/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "trace"
+version = "0.1.0"
+authors = ["Tanish Desai <tanishdesai37@gmail.com>"]
+description = "QEMU tracing infrastructure support"
+resolver = "2"
+publish = false
+
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+libc = { workspace = true }
+
+[lints]
+workspace = true
diff --git a/rust/trace/meson.build b/rust/trace/meson.build
new file mode 100644
index 0000000..adca57e
--- /dev/null
+++ b/rust/trace/meson.build
@@ -0,0 +1,19 @@
+rust = import('rust')
+
+lib_rs = configure_file(
+ input: 'src/lib.rs',
+ output: 'lib.rs',
+ configuration: {
+ 'MESON_BUILD_ROOT': meson.project_build_root(),
+ })
+
+_trace_rs = static_library(
+ 'trace', # Library name,
+ lib_rs,
+ trace_rs_targets, # List of generated `.rs` custom targets
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ dependencies: [libc_rs],
+ rust_abi: 'rust',
+)
+
+trace_rs = declare_dependency(link_with: _trace_rs)
diff --git a/rust/trace/src/lib.rs b/rust/trace/src/lib.rs
new file mode 100644
index 0000000..e03bce4
--- /dev/null
+++ b/rust/trace/src/lib.rs
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! This crate provides macros that aid in using QEMU's tracepoint
+//! functionality.
+
+#[doc(hidden)]
+/// Re-exported item to avoid adding libc as a dependency everywhere.
+pub use libc::{syslog, LOG_INFO};
+
+#[macro_export]
+/// Define the trace-points from the named directory (which should have slashes
+/// replaced by underscore characters) as functions in a module called `trace`.
+///
+/// ```ignore
+/// ::trace::include_trace!("hw_char");
+/// // ...
+/// trace::trace_pl011_read_fifo_rx_full();
+/// ```
+macro_rules! include_trace {
+ ($name:literal) => {
+ #[allow(
+ clippy::ptr_as_ptr,
+ clippy::cast_lossless,
+ clippy::used_underscore_binding
+ )]
+ mod trace {
+ #[cfg(not(MESON))]
+ include!(concat!(
+ env!("MESON_BUILD_ROOT"),
+ "/trace/trace-",
+ $name,
+ ".rs"
+ ));
+
+ #[cfg(MESON)]
+ include!(concat!("@MESON_BUILD_ROOT@/trace/trace-", $name, ".rs"));
+ }
+ };
+}
diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml
new file mode 100644
index 0000000..85f9143
--- /dev/null
+++ b/rust/util/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "util"
+version = "0.1.0"
+description = "Rust bindings for QEMU/util"
+resolver = "2"
+publish = false
+
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+anyhow = { workspace = true }
+foreign = { workspace = true }
+glib-sys = { workspace = true }
+libc = { workspace = true }
+common = { path = "../common" }
+
+[lints]
+workspace = true
diff --git a/rust/qemu-api/build.rs b/rust/util/build.rs
index 1e72064..5654d1d 100644
--- a/rust/qemu-api/build.rs
+++ b/rust/util/build.rs
@@ -9,12 +9,16 @@ use std::os::windows::fs::symlink_file;
use std::{env, fs::remove_file, io::Result, path::Path};
fn main() -> Result<()> {
- // Placing bindings.inc.rs in the source directory is supported
- // but not documented or encouraged.
- let path = env::var("MESON_BUILD_ROOT")
- .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR")));
+ let manifest_dir = env!("CARGO_MANIFEST_DIR");
+ let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") {
+ let sub = get_rust_subdir(manifest_dir).unwrap();
+ format!("{root}/{sub}/bindings.inc.rs")
+ } else {
+ // Placing bindings.inc.rs in the source directory is supported
+ // but not documented or encouraged.
+ format!("{manifest_dir}/src/bindings.inc.rs")
+ };
- let file = format!("{path}/bindings.inc.rs");
let file = Path::new(&file);
if !Path::new(&file).exists() {
panic!(concat!(
@@ -39,3 +43,7 @@ fn main() -> Result<()> {
println!("cargo:rerun-if-changed=build.rs");
Ok(())
}
+
+fn get_rust_subdir(path: &str) -> Option<&str> {
+ path.find("/rust").map(|index| &path[index + 1..])
+}
diff --git a/rust/util/meson.build b/rust/util/meson.build
new file mode 100644
index 0000000..b0b75e9
--- /dev/null
+++ b/rust/util/meson.build
@@ -0,0 +1,59 @@
+_util_bindgen_args = []
+c_enums = [
+ 'module_init_type',
+ 'QEMUClockType',
+]
+foreach enum : c_enums
+ _util_bindgen_args += ['--rustified-enum', enum]
+endforeach
+
+#
+# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+#
+# Rust bindings generation with `bindgen` might fail in some cases where the
+# detected `libclang` does not match the expected `clang` version/target. In
+# this case you must pass the path to `clang` and `libclang` to your build
+# command invocation using the environment variables CLANG_PATH and
+# LIBCLANG_PATH
+_util_bindings_inc_rs = rust.bindgen(
+ input: 'wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.inc.rs',
+ include_directories: bindings_incdir,
+ bindgen_version: ['>=0.60.0'],
+ args: bindgen_args_common + _util_bindgen_args,
+ c_args: bindgen_c_args,
+)
+
+_util_rs = static_library(
+ 'util',
+ structured_sources(
+ [
+ 'src/lib.rs',
+ 'src/bindings.rs',
+ 'src/error.rs',
+ 'src/log.rs',
+ 'src/module.rs',
+ 'src/timer.rs',
+ ],
+ {'.': _util_bindings_inc_rs}
+ ),
+ override_options: ['rust_std=2021', 'build.rust_std=2021'],
+ rust_abi: 'rust',
+ dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, qom, qemuutil],
+)
+
+util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom])
+
+rust.test('rust-util-tests', _util_rs,
+ dependencies: [qemuutil, qom],
+ suite: ['unit', 'rust'])
+
+# 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-util-rs-doctests',
+ _util_rs,
+ dependencies: util_rs,
+ suite: ['doc', 'rust']
+)
diff --git a/rust/util/src/bindings.rs b/rust/util/src/bindings.rs
new file mode 100644
index 0000000..c277a29
--- /dev/null
+++ b/rust/util/src/bindings.rs
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#![allow(
+ dead_code,
+ improper_ctypes_definitions,
+ improper_ctypes,
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unnecessary_transmutes,
+ unsafe_op_in_unsafe_fn,
+ clippy::pedantic,
+ clippy::restriction,
+ clippy::style,
+ clippy::missing_const_for_fn,
+ clippy::ptr_offset_with_cast,
+ clippy::useless_transmute,
+ clippy::missing_safety_doc,
+ clippy::too_many_arguments
+)]
+
+use glib_sys::{guint, GList, GPollFD, GQueue, GSList, GString};
+
+#[cfg(MESON)]
+include!("bindings.inc.rs");
+
+#[cfg(not(MESON))]
+include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
diff --git a/rust/util/src/error.rs b/rust/util/src/error.rs
new file mode 100644
index 0000000..bfa5a86
--- /dev/null
+++ b/rust/util/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
+//! [`utils::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 [`utils::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 common::assert_match;
+ use foreign::OwnedPointer;
+
+ use super::*;
+
+ #[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/util/src/lib.rs b/rust/util/src/lib.rs
new file mode 100644
index 0000000..16c89b9
--- /dev/null
+++ b/rust/util/src/lib.rs
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+pub mod bindings;
+pub mod error;
+pub mod log;
+pub mod module;
+pub mod timer;
+
+pub use error::{Error, Result};
diff --git a/rust/util/src/log.rs b/rust/util/src/log.rs
new file mode 100644
index 0000000..0a4bc42
--- /dev/null
+++ b/rust/util/src/log.rs
@@ -0,0 +1,151 @@
+// Copyright 2025 Bernhard Beschow <shentey@gmail.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Bindings for QEMU's logging infrastructure
+
+use std::{
+ io::{self, Write},
+ ptr::NonNull,
+};
+
+use common::errno;
+
+use crate::bindings;
+
+#[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 = bindings::LOG_GUEST_ERROR,
+
+ /// Log guest access of unimplemented functionality.
+ /// Corresponds to `LOG_UNIMP` in the C implementation.
+ Unimp = bindings::LOG_UNIMP,
+}
+
+/// A RAII guard for QEMU's logging infrastructure. Creating the guard
+/// locks the log file, and dropping it (letting it go out of scope) unlocks
+/// the file.
+///
+/// As long as the guard lives, it can be written to using [`std::io::Write`].
+///
+/// The locking is recursive, therefore owning a guard does not prevent
+/// using [`log_mask_ln!()`](crate::log_mask_ln).
+pub struct LogGuard(NonNull<bindings::FILE>);
+
+impl LogGuard {
+ /// Return a RAII guard that writes to QEMU's logging infrastructure.
+ /// The log file is locked while the guard exists, ensuring that there
+ /// is no tearing of the messages.
+ ///
+ /// Return `None` if the log file is closed and could not be opened.
+ /// Do *not* use `unwrap()` on the result; failure can be handled simply
+ /// by not logging anything.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use util::log::LogGuard;
+ /// # use std::io::Write;
+ /// if let Some(mut log) = LogGuard::new() {
+ /// writeln!(log, "test");
+ /// }
+ /// ```
+ pub fn new() -> Option<Self> {
+ let f = unsafe { bindings::qemu_log_trylock() }.cast();
+ NonNull::new(f).map(Self)
+ }
+
+ /// Writes a formatted string into the log, returning any error encountered.
+ ///
+ /// This method is primarily used by the
+ /// [`log_mask_ln!()`](crate::log_mask_ln) macro, and it is rare for it
+ /// to be called explicitly. It is public because it is the only way to
+ /// examine the error, which `log_mask_ln!()` ignores
+ ///
+ /// Unlike `log_mask_ln!()`, it does *not* append a newline at the end.
+ pub fn log_fmt(args: std::fmt::Arguments) -> io::Result<()> {
+ if let Some(mut log) = Self::new() {
+ log.write_fmt(args)?;
+ }
+ Ok(())
+ }
+}
+
+impl Write for LogGuard {
+ fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
+ let ret = unsafe {
+ bindings::rust_fwrite(bytes.as_ptr().cast(), 1, bytes.len(), self.0.as_ptr())
+ };
+ errno::into_io_result(ret)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ // Do nothing, dropping the guard takes care of flushing
+ Ok(())
+ }
+}
+
+impl Drop for LogGuard {
+ fn drop(&mut self) {
+ unsafe {
+ bindings::qemu_log_unlock(self.0.as_ptr());
+ }
+ }
+}
+
+/// 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.
+///
+/// Errors from writing to the log are ignored.
+///
+/// # 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 util::{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 util::{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 _: $crate::log::Log = $mask;
+
+ if unsafe {
+ ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_uint)) != 0
+ } {
+ _ = $crate::log::LogGuard::log_fmt(
+ format_args!("{}\n", format_args!($fmt $($args)*)));
+ }
+ }};
+}
diff --git a/rust/qemu-api/src/module.rs b/rust/util/src/module.rs
index fa5cea3..06c45fc 100644
--- a/rust/qemu-api/src/module.rs
+++ b/rust/util/src/module.rs
@@ -36,7 +36,7 @@ macro_rules! module_init {
// shortcut because it's quite common that $body needs unsafe {}
($type:ident => unsafe $body:block) => {
- $crate::module_init! {
+ ::util::module_init! {
$type => { unsafe { $body } }
}
};
diff --git a/rust/qemu-api/src/timer.rs b/rust/util/src/timer.rs
index 868bd88..c6b3e40 100644
--- a/rust/qemu-api/src/timer.rs
+++ b/rust/util/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::{
@@ -7,22 +7,22 @@ use std::{
pin::Pin,
};
-use crate::{
- bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType},
- callbacks::FnCall,
- cell::Opaque,
+use common::{callbacks::FnCall, Opaque};
+
+use crate::bindings::{
+ self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType,
};
/// A safe wrapper around [`bindings::QEMUTimer`].
#[repr(transparent)]
-#[derive(Debug, qemu_api_macros::Wrapper)]
+#[derive(Debug, common::Wrapper)]
pub struct Timer(Opaque<bindings::QEMUTimer>);
unsafe impl Send for Timer {}
unsafe impl Sync for Timer {}
#[repr(transparent)]
-#[derive(qemu_api_macros::Wrapper)]
+#[derive(common::Wrapper)]
pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>);
unsafe impl Send for TimerListGroup {}
@@ -39,7 +39,7 @@ impl Timer {
///
/// The timer must be initialized before it is armed with
/// [`modify`](Self::modify).
- pub unsafe fn new() -> Self {
+ pub const unsafe fn new() -> Self {
// SAFETY: requirements relayed to callers of Timer::new
Self(unsafe { Opaque::zeroed() })
}
@@ -56,7 +56,7 @@ impl Timer {
) where
F: for<'a> FnCall<(&'a T,)>,
{
- let _: () = F::ASSERT_IS_SOME;
+ const { assert!(F::IS_SOME) };
/// timer expiration callback
unsafe extern "C" fn rust_timer_handler<T, F: for<'a> FnCall<(&'a T,)>>(
diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h
new file mode 100644
index 0000000..b9ed68a
--- /dev/null
+++ b/rust/util/wrapper.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#ifndef __CLANG_STDATOMIC_H
+#define __CLANG_STDATOMIC_H
+/*
+ * Fix potential missing stdatomic.h error in case bindgen does not insert the
+ * correct libclang header paths on its own. We do not use stdatomic.h symbols
+ * in QEMU code, so it's fine to declare dummy types instead.
+ */
+typedef enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst,
+} memory_order;
+#endif /* __CLANG_STDATOMIC_H */
+
+#include "qemu/osdep.h"
+
+#include "qapi/error.h"
+#include "qapi/error-internal.h"
+#include "qemu/log-for-trace.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"