aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-03-10 13:40:05 +0800
committerStefan Hajnoczi <stefanha@redhat.com>2025-03-10 13:40:05 +0800
commit1843a0c01d06049f517fea7e155e5236e7287276 (patch)
tree2f520c4c409e5c24f377af7372e92678d57482fc
parentd9a4282c4b690e45d25c2b933f318bb41eeb271d (diff)
parent816945364f698ae750aa665fce3d121c98e37a6f (diff)
downloadqemu-1843a0c01d06049f517fea7e155e5236e7287276.zip
qemu-1843a0c01d06049f517fea7e155e5236e7287276.tar.gz
qemu-1843a0c01d06049f517fea7e155e5236e7287276.tar.bz2
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* scripts: dump stdin on meson-buildoptions error * rust: introduce qemu_api::cell::Opaque<> * rust: express pinning requirements for timers * rust: hpet: decode HPET registers into enums * rust: cell: add full example of declaring a SysBusDevice * rust: qom: remove operations on &mut # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmfNbXwUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroNjpwf+ODnG0XzHt7LSag695zs5fVLK353m # vLAHJ0bsmHoR4V+jEc+eaY7esDx5TLB9SRX/NvDsumJ9xnGYxXVn8Ti5GNHpa/xd # qSReB6X3E8fqG5e3AffUJGJnxrD8dHJ733RsyJBZqJc9sWkUnSiEBb5lGu7br6oC # fFyfiGweYboQ4AsiQUDtEN+tQsTWNkdThYEzq+dpnZrDJHNnw5e/rRwmqCUnEsLU # PfwhrOGJ3OkIUtdgHStuNfiN9sqjXV5DXmZVa9L2We8FEQdkhBzg3TC0ez0gFG/1 # W0P6JwfWk9Z+y/ERxkaycSXmabM0zUiFF1UJNgKEXp5iuPnRFC82OtRSUg== # =de1b # -----END PGP SIGNATURE----- # gpg: Signature made Sun 09 Mar 2025 18:29:16 HKT # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (25 commits) rust: pl011: Allow NULL chardev argument to pl011_create() meson.build: default to -gsplit-dwarf for debug info rust: qom: remove operations on &mut rust: cell: add full example of declaring a SysBusDevice rust: hpet: decode HPET registers into enums rust: pl011: pass around registers::Data rust: pl011: switch to safe chardev operation rust: pl011: clean up visibilities of callbacks rust: pl011: move register definitions out of lib.rs rust: chardev: provide basic bindings to character devices rust: bindings: remove more unnecessary Send/Sync impls rust: chardev: wrap Chardev with Opaque<> rust: memory: wrap MemoryRegion with Opaque<> rust: sysbus: wrap SysBusDevice with Opaque<> rust: hpet: do not access fields of SysBusDevice rust: qdev: wrap Clock and DeviceState with Opaque<> rust: qom: wrap Object with Opaque<> rust: irq: wrap IRQState with Opaque<> rust: timer: wrap QEMUTimer with Opaque<> and express pinning requirements rust: hpet: embed Timer without the Option and Box indirection ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r--docs/devel/rust.rst38
-rw-r--r--meson.build15
-rw-r--r--meson_options.txt2
-rw-r--r--rust/Cargo.toml2
-rw-r--r--rust/hw/char/pl011/src/device.rs155
-rw-r--r--rust/hw/char/pl011/src/lib.rs511
-rw-r--r--rust/hw/char/pl011/src/registers.rs506
-rw-r--r--rust/hw/timer/hpet/src/hpet.rs277
-rw-r--r--rust/qemu-api-macros/src/lib.rs90
-rw-r--r--rust/qemu-api/meson.build24
-rw-r--r--rust/qemu-api/src/bindings.rs26
-rw-r--r--rust/qemu-api/src/cell.rs277
-rw-r--r--rust/qemu-api/src/chardev.rs248
-rw-r--r--rust/qemu-api/src/irq.rs15
-rw-r--r--rust/qemu-api/src/memory.rs32
-rw-r--r--rust/qemu-api/src/prelude.rs1
-rw-r--r--rust/qemu-api/src/qdev.rs75
-rw-r--r--rust/qemu-api/src/qom.rs118
-rw-r--r--rust/qemu-api/src/sysbus.rs40
-rw-r--r--rust/qemu-api/src/timer.rs47
-rw-r--r--rust/qemu-api/src/vmstate.rs3
-rw-r--r--rust/qemu-api/src/zeroable.rs1
-rw-r--r--rust/qemu-api/tests/tests.rs34
-rw-r--r--scripts/meson-buildoptions.py10
-rw-r--r--scripts/meson-buildoptions.sh2
25 files changed, 1545 insertions, 1004 deletions
diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst
index 5d8aa3a..88bdec1 100644
--- a/docs/devel/rust.rst
+++ b/docs/devel/rust.rst
@@ -296,15 +296,35 @@ of ``&mut self``; access to internal fields must use *interior mutability*
to go from a shared reference to a ``&mut``.
Whenever C code provides you with an opaque ``void *``, avoid converting it
-to a Rust mutable reference, and use a shared reference instead. Rust code
-will then have to use QEMU's ``BqlRefCell`` and ``BqlCell`` type, which
-enforce that locking rules for the "Big QEMU Lock" are respected. These cell
-types are also known to the ``vmstate`` crate, which is able to "look inside"
-them when building an in-memory representation of a ``struct``'s layout.
-Note that the same is not true of a ``RefCell`` or ``Mutex``.
-
-In the future, similar cell types might also be provided for ``AioContext``-based
-locking as well.
+to a Rust mutable reference, and use a shared reference instead. The
+``qemu_api::cell`` module provides wrappers that can be used to tell the
+Rust compiler about interior mutability, and optionally to enforce locking
+rules for the "Big QEMU Lock". In the future, similar cell types might
+also be provided for ``AioContext``-based locking as well.
+
+In particular, device code will usually rely on the ``BqlRefCell`` and
+``BqlCell`` type to ensure that data is accessed correctly under the
+"Big QEMU Lock". These cell types are also known to the ``vmstate``
+crate, which is able to "look inside" them when building an in-memory
+representation of a ``struct``'s layout. Note that the same is not true
+of a ``RefCell`` or ``Mutex``.
+
+Bindings code instead will usually use the ``Opaque`` type, which hides
+the contents of the underlying struct and can be easily converted to
+a raw pointer, for use in calls to C functions. It can be used for
+example as follows::
+
+ #[repr(transparent)]
+ #[derive(Debug, qemu_api_macros::Wrapper)]
+ pub struct Object(Opaque<bindings::Object>);
+
+where the special ``derive`` macro provides useful methods such as
+``from_raw``, ``as_ptr`, ``as_mut_ptr`` and ``raw_get``. The bindings will
+then manually check for the big QEMU lock with assertions, which allows
+the wrapper to be declared thread-safe::
+
+ unsafe impl Send for Object {}
+ unsafe impl Sync for Object {}
Writing bindings to C code
''''''''''''''''''''''''''
diff --git a/meson.build b/meson.build
index 8b9fda4..4899d89 100644
--- a/meson.build
+++ b/meson.build
@@ -601,6 +601,10 @@ if get_option('tsan')
qemu_ldflags = ['-fsanitize=thread'] + qemu_ldflags
endif
+if get_option('debug') and get_option('split_debug')
+ qemu_cflags += '-gsplit-dwarf'
+endif
+
# Detect support for PT_GNU_RELRO + DT_BIND_NOW.
# The combination is known as "full relro", because .got.plt is read-only too.
qemu_ldflags += cc.get_supported_link_arguments('-Wl,-z,relro', '-Wl,-z,now')
@@ -4015,7 +4019,7 @@ libchardev = static_library('chardev', chardev_ss.sources() + genh,
build_by_default: false)
chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: false),
- dependencies: chardev_ss.dependencies())
+ dependencies: [chardev_ss.dependencies(), io])
hwcore_ss = hwcore_ss.apply({})
libhwcore = static_library('hwcore', sources: hwcore_ss.sources() + genh,
@@ -4100,13 +4104,6 @@ if have_rust
foreach enum : c_bitfields
bindgen_args += ['--bitfield-enum', enum]
endforeach
- c_nocopy = [
- 'QEMUTimer',
- ]
- # Used to customize Drop trait
- foreach struct : c_nocopy
- bindgen_args += ['--no-copy', struct]
- endforeach
# TODO: Remove this comment when the clang/libclang mismatch issue is solved.
#
@@ -4590,6 +4587,8 @@ if have_rust
summary_info += {'bindgen': bindgen.full_path()}
summary_info += {'bindgen version': bindgen.version()}
endif
+# option_cflags is purely for the summary display, meson will pass
+# -g/-O options directly
option_cflags = (get_option('debug') ? ['-g'] : [])
if get_option('optimization') != 'plain'
option_cflags += ['-O' + get_option('optimization')]
diff --git a/meson_options.txt b/meson_options.txt
index 59d973b..3432123 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -362,6 +362,8 @@ option('debug_mutex', type: 'boolean', value: false,
description: 'mutex debugging support')
option('debug_stack_usage', type: 'boolean', value: false,
description: 'measure coroutine stack usage')
+option('split_debug', type: 'boolean', value: true,
+ description: 'split debug info from object files')
option('qom_cast_debug', type: 'boolean', value: true,
description: 'cast debugging support')
option('slirp_smbd', type : 'feature', value : 'auto',
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 5041d62..ab1185a 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -37,6 +37,8 @@ result_unit_err = "allow"
should_implement_trait = "deny"
# can be for a reason, e.g. in callbacks
unused_self = "allow"
+# common in device crates
+upper_case_acronyms = "allow"
# default-allow lints
as_ptr_cast_mut = "deny"
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index d0857b4..f137b49 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -2,18 +2,10 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::{
- ffi::CStr,
- os::raw::{c_int, c_void},
- ptr::{addr_of, addr_of_mut, NonNull},
-};
+use std::{ffi::CStr, ptr::addr_of_mut};
use qemu_api::{
- bindings::{
- qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers,
- qemu_chr_fe_write_all, CharBackend, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK,
- },
- chardev::Chardev,
+ chardev::{CharBackend, Chardev, Event},
impl_vmstate_forward,
irq::{IRQState, InterruptSource},
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
@@ -26,10 +18,13 @@ use qemu_api::{
use crate::{
device_class,
- registers::{self, Interrupt},
- RegisterOffset,
+ registers::{self, Interrupt, RegisterOffset},
};
+// 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
+// reception, it completes the current character before stopping
+
/// Integer Baud Rate Divider, `UARTIBRD`
const IBRD_MASK: u32 = 0xffff;
@@ -232,14 +227,14 @@ impl PL011Registers {
&mut self,
offset: RegisterOffset,
value: u32,
- char_backend: *mut CharBackend,
+ char_backend: &CharBackend,
) -> bool {
// eprintln!("write offset {offset} value {value}");
use RegisterOffset::*;
match offset {
DR => {
// interrupts always checked
- let _ = self.loopback_tx(value);
+ let _ = self.loopback_tx(value.into());
self.int_level |= Interrupt::TX.0;
return true;
}
@@ -266,17 +261,9 @@ impl PL011Registers {
self.reset_tx_fifo();
}
let update = (self.line_control.send_break() != new_val.send_break()) && {
- let mut break_enable: c_int = new_val.send_break().into();
- // SAFETY: self.char_backend is a valid CharBackend instance after it's been
- // initialized in realize().
- unsafe {
- qemu_chr_fe_ioctl(
- char_backend,
- CHR_IOCTL_SERIAL_SET_BREAK as i32,
- addr_of_mut!(break_enable).cast::<c_void>(),
- );
- }
- self.loopback_break(break_enable > 0)
+ let break_enable = new_val.send_break();
+ let _ = char_backend.send_break(break_enable);
+ self.loopback_break(break_enable)
};
self.line_control = new_val;
self.set_read_trigger();
@@ -314,7 +301,7 @@ impl PL011Registers {
#[inline]
#[must_use]
- fn loopback_tx(&mut self, value: u32) -> bool {
+ fn loopback_tx(&mut self, value: registers::Data) -> bool {
// Caveat:
//
// In real hardware, TX loopback happens at the serial-bit level
@@ -383,7 +370,7 @@ impl PL011Registers {
}
fn loopback_break(&mut self, enable: bool) -> bool {
- enable && self.loopback_tx(registers::Data::BREAK.into())
+ enable && self.loopback_tx(registers::Data::BREAK)
}
fn set_read_trigger(&mut self) {
@@ -442,11 +429,11 @@ impl PL011Registers {
}
#[must_use]
- pub fn put_fifo(&mut self, value: u32) -> bool {
+ pub fn put_fifo(&mut self, value: registers::Data) -> bool {
let depth = self.fifo_depth();
assert!(depth > 0);
let slot = (self.read_pos + self.read_count) & (depth - 1);
- self.read_fifo[slot] = registers::Data::from(value);
+ self.read_fifo[slot] = value;
self.read_count += 1;
self.flags.set_receive_fifo_empty(false);
if self.read_count == depth {
@@ -534,7 +521,7 @@ impl PL011State {
}
}
- pub fn read(&self, offset: hwaddr, _size: u32) -> u64 {
+ fn read(&self, offset: hwaddr, _size: u32) -> u64 {
match RegisterOffset::try_from(offset) {
Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
let device_id = self.get_class().device_id;
@@ -548,37 +535,30 @@ impl PL011State {
let (update_irq, result) = self.regs.borrow_mut().read(field);
if update_irq {
self.update();
- unsafe {
- qemu_chr_fe_accept_input(addr_of!(self.char_backend) as *mut _);
- }
+ self.char_backend.accept_input();
}
result.into()
}
}
}
- pub fn write(&self, offset: hwaddr, value: u64, _size: u32) {
+ fn write(&self, offset: hwaddr, value: u64, _size: u32) {
let mut update_irq = false;
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.
if field == RegisterOffset::DR {
// ??? Check if transmitter is enabled.
- let ch: u8 = value as u8;
- // SAFETY: char_backend is a valid CharBackend instance after it's been
- // initialized in realize().
+ let ch: [u8; 1] = [value as u8];
// XXX this blocks entire thread. Rewrite to use
// qemu_chr_fe_write and background I/O callbacks
- unsafe {
- qemu_chr_fe_write_all(addr_of!(self.char_backend) as *mut _, &ch, 1);
- }
+ let _ = self.char_backend.write_all(&ch);
}
- update_irq = self.regs.borrow_mut().write(
- field,
- value as u32,
- addr_of!(self.char_backend) as *mut _,
- );
+ update_irq = self
+ .regs
+ .borrow_mut()
+ .write(field, value as u32, &self.char_backend);
} else {
eprintln!("write bad offset {offset} value {value}");
}
@@ -587,15 +567,19 @@ impl PL011State {
}
}
- pub fn can_receive(&self) -> bool {
- // trace_pl011_can_receive(s->lcr, s->read_count, r);
+ fn can_receive(&self) -> u32 {
let regs = self.regs.borrow();
- regs.read_count < regs.fifo_depth()
+ // trace_pl011_can_receive(s->lcr, s->read_count, r);
+ u32::from(regs.read_count < regs.fifo_depth())
}
- pub fn receive(&self, ch: u32) {
+ fn receive(&self, buf: &[u8]) {
+ if buf.is_empty() {
+ return;
+ }
let mut regs = self.regs.borrow_mut();
- let update_irq = !regs.loopback_enabled() && regs.put_fifo(ch);
+ let c: u32 = buf[0].into();
+ let update_irq = !regs.loopback_enabled() && regs.put_fifo(c.into());
// Release the BqlRefCell before calling self.update()
drop(regs);
@@ -604,11 +588,11 @@ impl PL011State {
}
}
- pub fn event(&self, event: QEMUChrEvent) {
+ fn event(&self, event: Event) {
let mut update_irq = false;
let mut regs = self.regs.borrow_mut();
- if event == QEMUChrEvent::CHR_EVENT_BREAK && !regs.loopback_enabled() {
- update_irq = regs.put_fifo(registers::Data::BREAK.into());
+ if event == Event::CHR_EVENT_BREAK && !regs.loopback_enabled() {
+ update_irq = regs.put_fifo(registers::Data::BREAK);
}
// Release the BqlRefCell before calling self.update()
drop(regs);
@@ -618,28 +602,16 @@ impl PL011State {
}
}
- pub fn realize(&self) {
- // SAFETY: self.char_backend has the correct size and alignment for a
- // CharBackend object, and its callbacks are of the correct types.
- unsafe {
- qemu_chr_fe_set_handlers(
- addr_of!(self.char_backend) as *mut CharBackend,
- Some(pl011_can_receive),
- Some(pl011_receive),
- Some(pl011_event),
- None,
- addr_of!(*self).cast::<c_void>() as *mut c_void,
- core::ptr::null_mut(),
- true,
- );
- }
+ fn realize(&self) {
+ self.char_backend
+ .enable_handlers(self, Self::can_receive, Self::receive, Self::event);
}
- pub fn reset_hold(&self, _type: ResetType) {
+ fn reset_hold(&self, _type: ResetType) {
self.regs.borrow_mut().reset();
}
- pub fn update(&self) {
+ fn update(&self) {
let regs = self.regs.borrow();
let flags = regs.int_level & regs.int_enabled;
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
@@ -665,43 +637,6 @@ const IRQMASK: [u32; 6] = [
/// # Safety
///
-/// We expect the FFI user of this function to pass a valid pointer, that has
-/// the same size as [`PL011State`]. We also expect the device is
-/// readable/writeable from one thread at any time.
-pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int {
- let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
- unsafe { state.as_ref().can_receive().into() }
-}
-
-/// # Safety
-///
-/// We expect the FFI user of this function to pass a valid pointer, that has
-/// the same size as [`PL011State`]. We also expect the device is
-/// readable/writeable from one thread at any time.
-///
-/// The buffer and size arguments must also be valid.
-pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) {
- let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
- unsafe {
- if size > 0 {
- debug_assert!(!buf.is_null());
- state.as_ref().receive(u32::from(buf.read_volatile()));
- }
- }
-}
-
-/// # Safety
-///
-/// We expect the FFI user of this function to pass a valid pointer, that has
-/// the same size as [`PL011State`]. We also expect the device is
-/// readable/writeable from one thread at any time.
-pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) {
- let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
- unsafe { state.as_ref().event(event) }
-}
-
-/// # Safety
-///
/// We expect the FFI user of this function to pass a valid pointer for `chr`
/// and `irq`.
#[no_mangle]
@@ -713,10 +648,12 @@ pub unsafe extern "C" fn pl011_create(
// SAFETY: The callers promise that they have owned references.
// They do not gift them to pl011_create, so use `Owned::from`.
let irq = unsafe { Owned::<IRQState>::from(&*irq) };
- let chr = unsafe { Owned::<Chardev>::from(&*chr) };
let dev = PL011State::new();
- dev.prop_set_chr("chardev", &chr);
+ if !chr.is_null() {
+ let chr = unsafe { Owned::<Chardev>::from(&*chr) };
+ dev.prop_set_chr("chardev", &chr);
+ }
dev.sysbus_realize();
dev.mmio_map(0, addr);
dev.connect_irq(0, &irq);
diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs
index 1bf46c6..dbae769 100644
--- a/rust/hw/char/pl011/src/lib.rs
+++ b/rust/hw/char/pl011/src/lib.rs
@@ -12,522 +12,13 @@
//! See [`PL011State`](crate::device::PL011State) for the device model type and
//! the [`registers`] module for register types.
-#![allow(clippy::upper_case_acronyms)]
-
use qemu_api::c_str;
mod device;
mod device_class;
+mod registers;
pub use device::pl011_create;
pub const TYPE_PL011: &::std::ffi::CStr = c_str!("pl011");
pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary");
-
-/// Offset of each register from the base memory address of the device.
-///
-/// # Source
-/// ARM DDI 0183G, Table 3-1 p.3-3
-#[doc(alias = "offset")]
-#[allow(non_camel_case_types)]
-#[repr(u64)]
-#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)]
-enum RegisterOffset {
- /// Data Register
- ///
- /// A write to this register initiates the actual data transmission
- #[doc(alias = "UARTDR")]
- DR = 0x000,
- /// Receive Status Register or Error Clear Register
- #[doc(alias = "UARTRSR")]
- #[doc(alias = "UARTECR")]
- RSR = 0x004,
- /// Flag Register
- ///
- /// A read of this register shows if transmission is complete
- #[doc(alias = "UARTFR")]
- FR = 0x018,
- /// Fractional Baud Rate Register
- ///
- /// responsible for baud rate speed
- #[doc(alias = "UARTFBRD")]
- FBRD = 0x028,
- /// `IrDA` Low-Power Counter Register
- #[doc(alias = "UARTILPR")]
- ILPR = 0x020,
- /// Integer Baud Rate Register
- ///
- /// Responsible for baud rate speed
- #[doc(alias = "UARTIBRD")]
- IBRD = 0x024,
- /// line control register (data frame format)
- #[doc(alias = "UARTLCR_H")]
- LCR_H = 0x02C,
- /// Toggle UART, transmission or reception
- #[doc(alias = "UARTCR")]
- CR = 0x030,
- /// Interrupt FIFO Level Select Register
- #[doc(alias = "UARTIFLS")]
- FLS = 0x034,
- /// Interrupt Mask Set/Clear Register
- #[doc(alias = "UARTIMSC")]
- IMSC = 0x038,
- /// Raw Interrupt Status Register
- #[doc(alias = "UARTRIS")]
- RIS = 0x03C,
- /// Masked Interrupt Status Register
- #[doc(alias = "UARTMIS")]
- MIS = 0x040,
- /// Interrupt Clear Register
- #[doc(alias = "UARTICR")]
- ICR = 0x044,
- /// DMA control Register
- #[doc(alias = "UARTDMACR")]
- DMACR = 0x048,
- ///// Reserved, offsets `0x04C` to `0x07C`.
- //Reserved = 0x04C,
-}
-
-mod registers {
- //! Device registers exposed as typed structs which are backed by arbitrary
- //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
- use bilge::prelude::*;
- use qemu_api::impl_vmstate_bitsized;
-
- /// Receive Status Register / Data Register common error bits
- ///
- /// The `UARTRSR` register is updated only when a read occurs
- /// from the `UARTDR` register with the same status information
- /// that can also be obtained by reading the `UARTDR` register
- #[bitsize(8)]
- #[derive(Clone, Copy, Default, DebugBits, FromBits)]
- pub struct Errors {
- pub framing_error: bool,
- pub parity_error: bool,
- pub break_error: bool,
- pub overrun_error: bool,
- _reserved_unpredictable: u4,
- }
-
- // TODO: FIFO Mode has different semantics
- /// Data Register, `UARTDR`
- ///
- /// The `UARTDR` register is the data register.
- ///
- /// For words to be transmitted:
- ///
- /// - if the FIFOs are enabled, data written to this location is pushed onto
- /// the transmit
- /// FIFO
- /// - if the FIFOs are not enabled, data is stored in the transmitter
- /// holding register (the
- /// bottom word of the transmit FIFO).
- ///
- /// The write operation initiates transmission from the UART. The data is
- /// prefixed with a start bit, appended with the appropriate parity bit
- /// (if parity is enabled), and a stop bit. The resultant word is then
- /// transmitted.
- ///
- /// For received words:
- ///
- /// - if the FIFOs are enabled, the data byte and the 4-bit status (break,
- /// frame, parity,
- /// and overrun) is pushed onto the 12-bit wide receive FIFO
- /// - if the FIFOs are not enabled, the data byte and status are stored in
- /// the receiving
- /// holding register (the bottom word of the receive FIFO).
- ///
- /// The received data byte is read by performing reads from the `UARTDR`
- /// register along with the corresponding status information. The status
- /// information can also be read by a read of the `UARTRSR/UARTECR`
- /// register.
- ///
- /// # Note
- ///
- /// You must disable the UART before any of the control registers are
- /// reprogrammed. When the UART is disabled in the middle of
- /// transmission or reception, it completes the current character before
- /// stopping.
- ///
- /// # Source
- /// ARM DDI 0183G 3.3.1 Data Register, UARTDR
- #[bitsize(32)]
- #[derive(Clone, Copy, Default, DebugBits, FromBits)]
- #[doc(alias = "UARTDR")]
- pub struct Data {
- pub data: u8,
- pub errors: Errors,
- _reserved: u16,
- }
- impl_vmstate_bitsized!(Data);
-
- impl Data {
- // bilge is not very const-friendly, unfortunately
- pub const BREAK: Self = Self { value: 1 << 10 };
- }
-
- // TODO: FIFO Mode has different semantics
- /// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR`
- ///
- /// The UARTRSR/UARTECR register is the receive status register/error clear
- /// register. Receive status can also be read from the `UARTRSR`
- /// register. If the status is read from this register, then the status
- /// information for break, framing and parity corresponds to the
- /// data character read from the [Data register](Data), `UARTDR` prior to
- /// reading the UARTRSR register. The status information for overrun is
- /// set immediately when an overrun condition occurs.
- ///
- ///
- /// # Note
- /// The received data character must be read first from the [Data
- /// Register](Data), `UARTDR` before reading the error status associated
- /// with that data character from the `UARTRSR` register. This read
- /// sequence cannot be reversed, because the `UARTRSR` register is
- /// updated only when a read occurs from the `UARTDR` register. However,
- /// the status information can also be obtained by reading the `UARTDR`
- /// register
- ///
- /// # Source
- /// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register,
- /// UARTRSR/UARTECR
- #[bitsize(32)]
- #[derive(Clone, Copy, DebugBits, FromBits)]
- pub struct ReceiveStatusErrorClear {
- pub errors: Errors,
- _reserved_unpredictable: u24,
- }
- impl_vmstate_bitsized!(ReceiveStatusErrorClear);
-
- impl ReceiveStatusErrorClear {
- pub fn set_from_data(&mut self, data: Data) {
- self.set_errors(data.errors());
- }
-
- pub fn reset(&mut self) {
- // All the bits are cleared to 0 on reset.
- *self = Self::default();
- }
- }
-
- impl Default for ReceiveStatusErrorClear {
- fn default() -> Self {
- 0.into()
- }
- }
-
- #[bitsize(32)]
- #[derive(Clone, Copy, DebugBits, FromBits)]
- /// Flag Register, `UARTFR`
- #[doc(alias = "UARTFR")]
- pub struct Flags {
- /// CTS Clear to send. This bit is the complement of the UART clear to
- /// send, `nUARTCTS`, modem status input. That is, the bit is 1
- /// when `nUARTCTS` is LOW.
- pub clear_to_send: bool,
- /// DSR Data set ready. This bit is the complement of the UART data set
- /// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when
- /// `nUARTDSR` is LOW.
- pub data_set_ready: bool,
- /// DCD Data carrier detect. This bit is the complement of the UART data
- /// carrier detect, `nUARTDCD`, modem status input. That is, the bit is
- /// 1 when `nUARTDCD` is LOW.
- pub data_carrier_detect: bool,
- /// BUSY UART busy. If this bit is set to 1, the UART is busy
- /// transmitting data. This bit remains set until the complete
- /// byte, including all the stop bits, has been sent from the
- /// shift register. This bit is set as soon as the transmit FIFO
- /// becomes non-empty, regardless of whether the UART is enabled
- /// or not.
- pub busy: bool,
- /// RXFE Receive FIFO empty. The meaning of this bit depends on the
- /// state of the FEN bit in the UARTLCR_H register. If the FIFO
- /// is disabled, this bit is set when the receive holding
- /// register is empty. If the FIFO is enabled, the RXFE bit is
- /// set when the receive FIFO is empty.
- pub receive_fifo_empty: bool,
- /// TXFF Transmit FIFO full. The meaning of this bit depends on the
- /// state of the FEN bit in the UARTLCR_H register. If the FIFO
- /// is disabled, this bit is set when the transmit holding
- /// register is full. If the FIFO is enabled, the TXFF bit is
- /// set when the transmit FIFO is full.
- pub transmit_fifo_full: bool,
- /// RXFF Receive FIFO full. The meaning of this bit depends on the state
- /// of the FEN bit in the UARTLCR_H register. If the FIFO is
- /// disabled, this bit is set when the receive holding register
- /// is full. If the FIFO is enabled, the RXFF bit is set when
- /// the receive FIFO is full.
- pub receive_fifo_full: bool,
- /// Transmit FIFO empty. The meaning of this bit depends on the state of
- /// the FEN bit in the [Line Control register](LineControl),
- /// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the
- /// transmit holding register is empty. If the FIFO is enabled,
- /// the TXFE bit is set when the transmit FIFO is empty. This
- /// bit does not indicate if there is data in the transmit shift
- /// register.
- pub transmit_fifo_empty: bool,
- /// `RI`, is `true` when `nUARTRI` is `LOW`.
- pub ring_indicator: bool,
- _reserved_zero_no_modify: u23,
- }
- impl_vmstate_bitsized!(Flags);
-
- impl Flags {
- pub fn reset(&mut self) {
- *self = Self::default();
- }
- }
-
- impl Default for Flags {
- fn default() -> Self {
- let mut ret: Self = 0.into();
- // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1
- ret.set_receive_fifo_empty(true);
- ret.set_transmit_fifo_empty(true);
- ret
- }
- }
-
- #[bitsize(32)]
- #[derive(Clone, Copy, DebugBits, FromBits)]
- /// Line Control Register, `UARTLCR_H`
- #[doc(alias = "UARTLCR_H")]
- pub struct LineControl {
- /// BRK Send break.
- ///
- /// If this bit is set to `1`, a low-level is continually output on the
- /// `UARTTXD` output, after completing transmission of the
- /// current character. For the proper execution of the break command,
- /// the software must set this bit for at least two complete
- /// frames. For normal use, this bit must be cleared to `0`.
- pub send_break: bool,
- /// 1 PEN Parity enable:
- ///
- /// - 0 = parity is disabled and no parity bit added to the data frame
- /// - 1 = parity checking and generation is enabled.
- ///
- /// See Table 3-11 on page 3-14 for the parity truth table.
- pub parity_enabled: bool,
- /// EPS Even parity select. Controls the type of parity the UART uses
- /// during transmission and reception:
- /// - 0 = odd parity. The UART generates or checks for an odd number of
- /// 1s in the data and parity bits.
- /// - 1 = even parity. The UART generates or checks for an even number
- /// of 1s in the data and parity bits.
- /// This bit has no effect when the `PEN` bit disables parity checking
- /// and generation. See Table 3-11 on page 3-14 for the parity
- /// truth table.
- pub parity: Parity,
- /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits
- /// are transmitted at the end of the frame. The receive
- /// logic does not check for two stop bits being received.
- pub two_stops_bits: bool,
- /// FEN Enable FIFOs:
- /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become
- /// 1-byte-deep holding registers 1 = transmit and receive FIFO
- /// buffers are enabled (FIFO mode).
- pub fifos_enabled: Mode,
- /// WLEN Word length. These bits indicate the number of data bits
- /// transmitted or received in a frame as follows: b11 = 8 bits
- /// b10 = 7 bits
- /// b01 = 6 bits
- /// b00 = 5 bits.
- pub word_length: WordLength,
- /// 7 SPS Stick parity select.
- /// 0 = stick parity is disabled
- /// 1 = either:
- /// • if the EPS bit is 0 then the parity bit is transmitted and checked
- /// as a 1 • if the EPS bit is 1 then the parity bit is
- /// transmitted and checked as a 0. This bit has no effect when
- /// the PEN bit disables parity checking and generation. See Table 3-11
- /// on page 3-14 for the parity truth table.
- pub sticky_parity: bool,
- /// 31:8 - Reserved, do not modify, read as zero.
- _reserved_zero_no_modify: u24,
- }
- impl_vmstate_bitsized!(LineControl);
-
- impl LineControl {
- pub fn reset(&mut self) {
- // All the bits are cleared to 0 when reset.
- *self = 0.into();
- }
- }
-
- impl Default for LineControl {
- fn default() -> Self {
- 0.into()
- }
- }
-
- #[bitsize(1)]
- #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
- /// `EPS` "Even parity select", field of [Line Control
- /// register](LineControl).
- pub enum Parity {
- /// - 0 = odd parity. The UART generates or checks for an odd number of
- /// 1s in the data and parity bits.
- Odd = 0,
- /// - 1 = even parity. The UART generates or checks for an even number
- /// of 1s in the data and parity bits.
- Even = 1,
- }
-
- #[bitsize(1)]
- #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
- /// `FEN` "Enable FIFOs" or Device mode, field of [Line Control
- /// register](LineControl).
- pub enum Mode {
- /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become
- /// 1-byte-deep holding registers
- Character = 0,
- /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
- FIFO = 1,
- }
-
- #[bitsize(2)]
- #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
- /// `WLEN` Word length, field of [Line Control register](LineControl).
- ///
- /// These bits indicate the number of data bits transmitted or received in a
- /// frame as follows:
- pub enum WordLength {
- /// b11 = 8 bits
- _8Bits = 0b11,
- /// b10 = 7 bits
- _7Bits = 0b10,
- /// b01 = 6 bits
- _6Bits = 0b01,
- /// b00 = 5 bits.
- _5Bits = 0b00,
- }
-
- /// Control Register, `UARTCR`
- ///
- /// The `UARTCR` register is the control register. All the bits are cleared
- /// to `0` on reset except for bits `9` and `8` that are set to `1`.
- ///
- /// # Source
- /// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12
- #[bitsize(32)]
- #[doc(alias = "UARTCR")]
- #[derive(Clone, Copy, DebugBits, FromBits)]
- pub struct Control {
- /// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled
- /// in the middle of transmission or reception, it completes the current
- /// character before stopping. 1 = the UART is enabled. Data
- /// transmission and reception occurs for either UART signals or SIR
- /// signals depending on the setting of the SIREN bit.
- pub enable_uart: bool,
- /// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT`
- /// remains LOW (no light pulse generated), and signal transitions on
- /// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is
- /// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH,
- /// in the marking state. Signal transitions on UARTRXD or modem status
- /// inputs have no effect. This bit has no effect if the UARTEN bit
- /// disables the UART.
- pub enable_sir: bool,
- /// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding
- /// mode. If this bit is cleared to 0, low-level bits are transmitted as
- /// an active high pulse with a width of 3/ 16th of the bit period. If
- /// this bit is set to 1, low-level bits are transmitted with a pulse
- /// width that is 3 times the period of the IrLPBaud16 input signal,
- /// regardless of the selected bit rate. Setting this bit uses less
- /// power, but might reduce transmission distances.
- pub sir_lowpower_irda_mode: u1,
- /// Reserved, do not modify, read as zero.
- _reserved_zero_no_modify: u4,
- /// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is
- /// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR
- /// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed
- /// through to the SIRIN path. The SIRTEST bit in the test register must
- /// be set to 1 to override the normal half-duplex SIR operation. This
- /// must be the requirement for accessing the test registers during
- /// normal operation, and SIRTEST must be cleared to 0 when loopback
- /// testing is finished. This feature reduces the amount of external
- /// coupling required during system test. If this bit is set to 1, and
- /// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the
- /// UARTRXD path. In either SIR mode or UART mode, when this bit is set,
- /// the modem outputs are also fed through to the modem inputs. This bit
- /// is cleared to 0 on reset, to disable loopback.
- pub enable_loopback: bool,
- /// `TXE` Transmit enable. If this bit is set to 1, the transmit section
- /// of the UART is enabled. Data transmission occurs for either UART
- /// signals, or SIR signals depending on the setting of the SIREN bit.
- /// When the UART is disabled in the middle of transmission, it
- /// completes the current character before stopping.
- pub enable_transmit: bool,
- /// `RXE` Receive enable. If this bit is set to 1, the receive section
- /// of the UART is enabled. Data reception occurs for either UART
- /// signals or SIR signals depending on the setting of the SIREN bit.
- /// When the UART is disabled in the middle of reception, it completes
- /// the current character before stopping.
- pub enable_receive: bool,
- /// `DTR` Data transmit ready. This bit is the complement of the UART
- /// data transmit ready, `nUARTDTR`, modem status output. That is, when
- /// the bit is programmed to a 1 then `nUARTDTR` is LOW.
- pub data_transmit_ready: bool,
- /// `RTS` Request to send. This bit is the complement of the UART
- /// request to send, `nUARTRTS`, modem status output. That is, when the
- /// bit is programmed to a 1 then `nUARTRTS` is LOW.
- pub request_to_send: bool,
- /// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`)
- /// modem status output. That is, when the bit is programmed to a 1 the
- /// output is 0. For DTE this can be used as Data Carrier Detect (DCD).
- pub out_1: bool,
- /// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`)
- /// modem status output. That is, when the bit is programmed to a 1, the
- /// output is 0. For DTE this can be used as Ring Indicator (RI).
- pub out_2: bool,
- /// `RTSEn` RTS hardware flow control enable. If this bit is set to 1,
- /// RTS hardware flow control is enabled. Data is only requested when
- /// there is space in the receive FIFO for it to be received.
- pub rts_hardware_flow_control_enable: bool,
- /// `CTSEn` CTS hardware flow control enable. If this bit is set to 1,
- /// CTS hardware flow control is enabled. Data is only transmitted when
- /// the `nUARTCTS` signal is asserted.
- pub cts_hardware_flow_control_enable: bool,
- /// 31:16 - Reserved, do not modify, read as zero.
- _reserved_zero_no_modify2: u16,
- }
- impl_vmstate_bitsized!(Control);
-
- impl Control {
- pub fn reset(&mut self) {
- *self = 0.into();
- self.set_enable_receive(true);
- self.set_enable_transmit(true);
- }
- }
-
- impl Default for Control {
- fn default() -> Self {
- let mut ret: Self = 0.into();
- ret.reset();
- ret
- }
- }
-
- /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
- pub struct Interrupt(pub u32);
-
- 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);
- }
-}
-
-// 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
-// reception, it completes the current character before stopping
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
new file mode 100644
index 0000000..cd92fa2
--- /dev/null
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -0,0 +1,506 @@
+// Copyright 2024, Linaro Limited
+// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Device registers exposed as typed structs which are backed by arbitrary
+//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
+
+use bilge::prelude::*;
+use qemu_api::impl_vmstate_bitsized;
+
+/// Offset of each register from the base memory address of the device.
+///
+/// # Source
+/// ARM DDI 0183G, Table 3-1 p.3-3
+#[doc(alias = "offset")]
+#[allow(non_camel_case_types)]
+#[repr(u64)]
+#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)]
+pub enum RegisterOffset {
+ /// Data Register
+ ///
+ /// A write to this register initiates the actual data transmission
+ #[doc(alias = "UARTDR")]
+ DR = 0x000,
+ /// Receive Status Register or Error Clear Register
+ #[doc(alias = "UARTRSR")]
+ #[doc(alias = "UARTECR")]
+ RSR = 0x004,
+ /// Flag Register
+ ///
+ /// A read of this register shows if transmission is complete
+ #[doc(alias = "UARTFR")]
+ FR = 0x018,
+ /// Fractional Baud Rate Register
+ ///
+ /// responsible for baud rate speed
+ #[doc(alias = "UARTFBRD")]
+ FBRD = 0x028,
+ /// `IrDA` Low-Power Counter Register
+ #[doc(alias = "UARTILPR")]
+ ILPR = 0x020,
+ /// Integer Baud Rate Register
+ ///
+ /// Responsible for baud rate speed
+ #[doc(alias = "UARTIBRD")]
+ IBRD = 0x024,
+ /// line control register (data frame format)
+ #[doc(alias = "UARTLCR_H")]
+ LCR_H = 0x02C,
+ /// Toggle UART, transmission or reception
+ #[doc(alias = "UARTCR")]
+ CR = 0x030,
+ /// Interrupt FIFO Level Select Register
+ #[doc(alias = "UARTIFLS")]
+ FLS = 0x034,
+ /// Interrupt Mask Set/Clear Register
+ #[doc(alias = "UARTIMSC")]
+ IMSC = 0x038,
+ /// Raw Interrupt Status Register
+ #[doc(alias = "UARTRIS")]
+ RIS = 0x03C,
+ /// Masked Interrupt Status Register
+ #[doc(alias = "UARTMIS")]
+ MIS = 0x040,
+ /// Interrupt Clear Register
+ #[doc(alias = "UARTICR")]
+ ICR = 0x044,
+ /// DMA control Register
+ #[doc(alias = "UARTDMACR")]
+ DMACR = 0x048,
+ ///// Reserved, offsets `0x04C` to `0x07C`.
+ //Reserved = 0x04C,
+}
+
+/// Receive Status Register / Data Register common error bits
+///
+/// The `UARTRSR` register is updated only when a read occurs
+/// from the `UARTDR` register with the same status information
+/// that can also be obtained by reading the `UARTDR` register
+#[bitsize(8)]
+#[derive(Clone, Copy, Default, DebugBits, FromBits)]
+pub struct Errors {
+ pub framing_error: bool,
+ pub parity_error: bool,
+ pub break_error: bool,
+ pub overrun_error: bool,
+ _reserved_unpredictable: u4,
+}
+
+// TODO: FIFO Mode has different semantics
+/// Data Register, `UARTDR`
+///
+/// The `UARTDR` register is the data register.
+///
+/// For words to be transmitted:
+///
+/// - if the FIFOs are enabled, data written to this location is pushed onto the
+/// transmit
+/// FIFO
+/// - if the FIFOs are not enabled, data is stored in the transmitter holding
+/// register (the
+/// bottom word of the transmit FIFO).
+///
+/// The write operation initiates transmission from the UART. The data is
+/// prefixed with a start bit, appended with the appropriate parity bit
+/// (if parity is enabled), and a stop bit. The resultant word is then
+/// transmitted.
+///
+/// For received words:
+///
+/// - if the FIFOs are enabled, the data byte and the 4-bit status (break,
+/// frame, parity,
+/// and overrun) is pushed onto the 12-bit wide receive FIFO
+/// - if the FIFOs are not enabled, the data byte and status are stored in the
+/// receiving
+/// holding register (the bottom word of the receive FIFO).
+///
+/// The received data byte is read by performing reads from the `UARTDR`
+/// register along with the corresponding status information. The status
+/// information can also be read by a read of the `UARTRSR/UARTECR`
+/// register.
+///
+/// # Note
+///
+/// You must disable the UART before any of the control registers are
+/// reprogrammed. When the UART is disabled in the middle of
+/// transmission or reception, it completes the current character before
+/// stopping.
+///
+/// # Source
+/// ARM DDI 0183G 3.3.1 Data Register, UARTDR
+#[bitsize(32)]
+#[derive(Clone, Copy, Default, DebugBits, FromBits)]
+#[doc(alias = "UARTDR")]
+pub struct Data {
+ pub data: u8,
+ pub errors: Errors,
+ _reserved: u16,
+}
+impl_vmstate_bitsized!(Data);
+
+impl Data {
+ // bilge is not very const-friendly, unfortunately
+ pub const BREAK: Self = Self { value: 1 << 10 };
+}
+
+// TODO: FIFO Mode has different semantics
+/// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR`
+///
+/// The UARTRSR/UARTECR register is the receive status register/error clear
+/// register. Receive status can also be read from the `UARTRSR`
+/// register. If the status is read from this register, then the status
+/// information for break, framing and parity corresponds to the
+/// data character read from the [Data register](Data), `UARTDR` prior to
+/// reading the UARTRSR register. The status information for overrun is
+/// set immediately when an overrun condition occurs.
+///
+///
+/// # Note
+/// The received data character must be read first from the [Data
+/// Register](Data), `UARTDR` before reading the error status associated
+/// with that data character from the `UARTRSR` register. This read
+/// sequence cannot be reversed, because the `UARTRSR` register is
+/// updated only when a read occurs from the `UARTDR` register. However,
+/// the status information can also be obtained by reading the `UARTDR`
+/// register
+///
+/// # Source
+/// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register,
+/// UARTRSR/UARTECR
+#[bitsize(32)]
+#[derive(Clone, Copy, DebugBits, FromBits)]
+pub struct ReceiveStatusErrorClear {
+ pub errors: Errors,
+ _reserved_unpredictable: u24,
+}
+impl_vmstate_bitsized!(ReceiveStatusErrorClear);
+
+impl ReceiveStatusErrorClear {
+ pub fn set_from_data(&mut self, data: Data) {
+ self.set_errors(data.errors());
+ }
+
+ pub fn reset(&mut self) {
+ // All the bits are cleared to 0 on reset.
+ *self = Self::default();
+ }
+}
+
+impl Default for ReceiveStatusErrorClear {
+ fn default() -> Self {
+ 0.into()
+ }
+}
+
+#[bitsize(32)]
+#[derive(Clone, Copy, DebugBits, FromBits)]
+/// Flag Register, `UARTFR`
+#[doc(alias = "UARTFR")]
+pub struct Flags {
+ /// CTS Clear to send. This bit is the complement of the UART clear to
+ /// send, `nUARTCTS`, modem status input. That is, the bit is 1
+ /// when `nUARTCTS` is LOW.
+ pub clear_to_send: bool,
+ /// DSR Data set ready. This bit is the complement of the UART data set
+ /// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when
+ /// `nUARTDSR` is LOW.
+ pub data_set_ready: bool,
+ /// DCD Data carrier detect. This bit is the complement of the UART data
+ /// carrier detect, `nUARTDCD`, modem status input. That is, the bit is
+ /// 1 when `nUARTDCD` is LOW.
+ pub data_carrier_detect: bool,
+ /// BUSY UART busy. If this bit is set to 1, the UART is busy
+ /// transmitting data. This bit remains set until the complete
+ /// byte, including all the stop bits, has been sent from the
+ /// shift register. This bit is set as soon as the transmit FIFO
+ /// becomes non-empty, regardless of whether the UART is enabled
+ /// or not.
+ pub busy: bool,
+ /// RXFE Receive FIFO empty. The meaning of this bit depends on the
+ /// state of the FEN bit in the UARTLCR_H register. If the FIFO
+ /// is disabled, this bit is set when the receive holding
+ /// register is empty. If the FIFO is enabled, the RXFE bit is
+ /// set when the receive FIFO is empty.
+ pub receive_fifo_empty: bool,
+ /// TXFF Transmit FIFO full. The meaning of this bit depends on the
+ /// state of the FEN bit in the UARTLCR_H register. If the FIFO
+ /// is disabled, this bit is set when the transmit holding
+ /// register is full. If the FIFO is enabled, the TXFF bit is
+ /// set when the transmit FIFO is full.
+ pub transmit_fifo_full: bool,
+ /// RXFF Receive FIFO full. The meaning of this bit depends on the state
+ /// of the FEN bit in the UARTLCR_H register. If the FIFO is
+ /// disabled, this bit is set when the receive holding register
+ /// is full. If the FIFO is enabled, the RXFF bit is set when
+ /// the receive FIFO is full.
+ pub receive_fifo_full: bool,
+ /// Transmit FIFO empty. The meaning of this bit depends on the state of
+ /// the FEN bit in the [Line Control register](LineControl),
+ /// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the
+ /// transmit holding register is empty. If the FIFO is enabled,
+ /// the TXFE bit is set when the transmit FIFO is empty. This
+ /// bit does not indicate if there is data in the transmit shift
+ /// register.
+ pub transmit_fifo_empty: bool,
+ /// `RI`, is `true` when `nUARTRI` is `LOW`.
+ pub ring_indicator: bool,
+ _reserved_zero_no_modify: u23,
+}
+impl_vmstate_bitsized!(Flags);
+
+impl Flags {
+ pub fn reset(&mut self) {
+ *self = Self::default();
+ }
+}
+
+impl Default for Flags {
+ fn default() -> Self {
+ let mut ret: Self = 0.into();
+ // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1
+ ret.set_receive_fifo_empty(true);
+ ret.set_transmit_fifo_empty(true);
+ ret
+ }
+}
+
+#[bitsize(32)]
+#[derive(Clone, Copy, DebugBits, FromBits)]
+/// Line Control Register, `UARTLCR_H`
+#[doc(alias = "UARTLCR_H")]
+pub struct LineControl {
+ /// BRK Send break.
+ ///
+ /// If this bit is set to `1`, a low-level is continually output on the
+ /// `UARTTXD` output, after completing transmission of the
+ /// current character. For the proper execution of the break command,
+ /// the software must set this bit for at least two complete
+ /// frames. For normal use, this bit must be cleared to `0`.
+ pub send_break: bool,
+ /// 1 PEN Parity enable:
+ ///
+ /// - 0 = parity is disabled and no parity bit added to the data frame
+ /// - 1 = parity checking and generation is enabled.
+ ///
+ /// See Table 3-11 on page 3-14 for the parity truth table.
+ pub parity_enabled: bool,
+ /// EPS Even parity select. Controls the type of parity the UART uses
+ /// during transmission and reception:
+ /// - 0 = odd parity. The UART generates or checks for an odd number of 1s
+ /// in the data and parity bits.
+ /// - 1 = even parity. The UART generates or checks for an even number of 1s
+ /// in the data and parity bits.
+ /// This bit has no effect when the `PEN` bit disables parity checking
+ /// and generation. See Table 3-11 on page 3-14 for the parity
+ /// truth table.
+ pub parity: Parity,
+ /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits
+ /// are transmitted at the end of the frame. The receive
+ /// logic does not check for two stop bits being received.
+ pub two_stops_bits: bool,
+ /// FEN Enable FIFOs:
+ /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become
+ /// 1-byte-deep holding registers 1 = transmit and receive FIFO
+ /// buffers are enabled (FIFO mode).
+ pub fifos_enabled: Mode,
+ /// WLEN Word length. These bits indicate the number of data bits
+ /// transmitted or received in a frame as follows: b11 = 8 bits
+ /// b10 = 7 bits
+ /// b01 = 6 bits
+ /// b00 = 5 bits.
+ pub word_length: WordLength,
+ /// 7 SPS Stick parity select.
+ /// 0 = stick parity is disabled
+ /// 1 = either:
+ /// • if the EPS bit is 0 then the parity bit is transmitted and checked
+ /// as a 1 • if the EPS bit is 1 then the parity bit is
+ /// transmitted and checked as a 0. This bit has no effect when
+ /// the PEN bit disables parity checking and generation. See Table 3-11
+ /// on page 3-14 for the parity truth table.
+ pub sticky_parity: bool,
+ /// 31:8 - Reserved, do not modify, read as zero.
+ _reserved_zero_no_modify: u24,
+}
+impl_vmstate_bitsized!(LineControl);
+
+impl LineControl {
+ pub fn reset(&mut self) {
+ // All the bits are cleared to 0 when reset.
+ *self = 0.into();
+ }
+}
+
+impl Default for LineControl {
+ fn default() -> Self {
+ 0.into()
+ }
+}
+
+#[bitsize(1)]
+#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
+/// `EPS` "Even parity select", field of [Line Control
+/// register](LineControl).
+pub enum Parity {
+ /// - 0 = odd parity. The UART generates or checks for an odd number of 1s
+ /// in the data and parity bits.
+ Odd = 0,
+ /// - 1 = even parity. The UART generates or checks for an even number of 1s
+ /// in the data and parity bits.
+ Even = 1,
+}
+
+#[bitsize(1)]
+#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
+/// `FEN` "Enable FIFOs" or Device mode, field of [Line Control
+/// register](LineControl).
+pub enum Mode {
+ /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become
+ /// 1-byte-deep holding registers
+ Character = 0,
+ /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
+ FIFO = 1,
+}
+
+#[bitsize(2)]
+#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)]
+/// `WLEN` Word length, field of [Line Control register](LineControl).
+///
+/// These bits indicate the number of data bits transmitted or received in a
+/// frame as follows:
+pub enum WordLength {
+ /// b11 = 8 bits
+ _8Bits = 0b11,
+ /// b10 = 7 bits
+ _7Bits = 0b10,
+ /// b01 = 6 bits
+ _6Bits = 0b01,
+ /// b00 = 5 bits.
+ _5Bits = 0b00,
+}
+
+/// Control Register, `UARTCR`
+///
+/// The `UARTCR` register is the control register. All the bits are cleared
+/// to `0` on reset except for bits `9` and `8` that are set to `1`.
+///
+/// # Source
+/// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12
+#[bitsize(32)]
+#[doc(alias = "UARTCR")]
+#[derive(Clone, Copy, DebugBits, FromBits)]
+pub struct Control {
+ /// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled
+ /// in the middle of transmission or reception, it completes the current
+ /// character before stopping. 1 = the UART is enabled. Data
+ /// transmission and reception occurs for either UART signals or SIR
+ /// signals depending on the setting of the SIREN bit.
+ pub enable_uart: bool,
+ /// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT`
+ /// remains LOW (no light pulse generated), and signal transitions on
+ /// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is
+ /// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH,
+ /// in the marking state. Signal transitions on UARTRXD or modem status
+ /// inputs have no effect. This bit has no effect if the UARTEN bit
+ /// disables the UART.
+ pub enable_sir: bool,
+ /// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding
+ /// mode. If this bit is cleared to 0, low-level bits are transmitted as
+ /// an active high pulse with a width of 3/ 16th of the bit period. If
+ /// this bit is set to 1, low-level bits are transmitted with a pulse
+ /// width that is 3 times the period of the IrLPBaud16 input signal,
+ /// regardless of the selected bit rate. Setting this bit uses less
+ /// power, but might reduce transmission distances.
+ pub sir_lowpower_irda_mode: u1,
+ /// Reserved, do not modify, read as zero.
+ _reserved_zero_no_modify: u4,
+ /// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is
+ /// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR
+ /// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed
+ /// through to the SIRIN path. The SIRTEST bit in the test register must
+ /// be set to 1 to override the normal half-duplex SIR operation. This
+ /// must be the requirement for accessing the test registers during
+ /// normal operation, and SIRTEST must be cleared to 0 when loopback
+ /// testing is finished. This feature reduces the amount of external
+ /// coupling required during system test. If this bit is set to 1, and
+ /// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the
+ /// UARTRXD path. In either SIR mode or UART mode, when this bit is set,
+ /// the modem outputs are also fed through to the modem inputs. This bit
+ /// is cleared to 0 on reset, to disable loopback.
+ pub enable_loopback: bool,
+ /// `TXE` Transmit enable. If this bit is set to 1, the transmit section
+ /// of the UART is enabled. Data transmission occurs for either UART
+ /// signals, or SIR signals depending on the setting of the SIREN bit.
+ /// When the UART is disabled in the middle of transmission, it
+ /// completes the current character before stopping.
+ pub enable_transmit: bool,
+ /// `RXE` Receive enable. If this bit is set to 1, the receive section
+ /// of the UART is enabled. Data reception occurs for either UART
+ /// signals or SIR signals depending on the setting of the SIREN bit.
+ /// When the UART is disabled in the middle of reception, it completes
+ /// the current character before stopping.
+ pub enable_receive: bool,
+ /// `DTR` Data transmit ready. This bit is the complement of the UART
+ /// data transmit ready, `nUARTDTR`, modem status output. That is, when
+ /// the bit is programmed to a 1 then `nUARTDTR` is LOW.
+ pub data_transmit_ready: bool,
+ /// `RTS` Request to send. This bit is the complement of the UART
+ /// request to send, `nUARTRTS`, modem status output. That is, when the
+ /// bit is programmed to a 1 then `nUARTRTS` is LOW.
+ pub request_to_send: bool,
+ /// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`)
+ /// modem status output. That is, when the bit is programmed to a 1 the
+ /// output is 0. For DTE this can be used as Data Carrier Detect (DCD).
+ pub out_1: bool,
+ /// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`)
+ /// modem status output. That is, when the bit is programmed to a 1, the
+ /// output is 0. For DTE this can be used as Ring Indicator (RI).
+ pub out_2: bool,
+ /// `RTSEn` RTS hardware flow control enable. If this bit is set to 1,
+ /// RTS hardware flow control is enabled. Data is only requested when
+ /// there is space in the receive FIFO for it to be received.
+ pub rts_hardware_flow_control_enable: bool,
+ /// `CTSEn` CTS hardware flow control enable. If this bit is set to 1,
+ /// CTS hardware flow control is enabled. Data is only transmitted when
+ /// the `nUARTCTS` signal is asserted.
+ pub cts_hardware_flow_control_enable: bool,
+ /// 31:16 - Reserved, do not modify, read as zero.
+ _reserved_zero_no_modify2: u16,
+}
+impl_vmstate_bitsized!(Control);
+
+impl Control {
+ pub fn reset(&mut self) {
+ *self = 0.into();
+ self.set_enable_receive(true);
+ self.set_enable_transmit(true);
+ }
+}
+
+impl Default for Control {
+ fn default() -> Self {
+ let mut ret: Self = 0.into();
+ ret.reset();
+ ret
+ }
+}
+
+/// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
+pub struct Interrupt(pub u32);
+
+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);
+}
diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs
index be27eb0..20e0afd 100644
--- a/rust/hw/timer/hpet/src/hpet.rs
+++ b/rust/hw/timer/hpet/src/hpet.rs
@@ -4,6 +4,7 @@
use std::{
ffi::CStr,
+ pin::Pin,
ptr::{addr_of_mut, null_mut, NonNull},
slice::from_ref,
};
@@ -47,8 +48,6 @@ const RTC_ISA_IRQ: usize = 8;
const HPET_CLK_PERIOD: u64 = 10; // 10 ns
const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns
-/// General Capabilities and ID Register
-const HPET_CAP_REG: u64 = 0x000;
/// Revision ID (bits 0:7). Revision 1 is implemented (refer to v1.0a spec).
const HPET_CAP_REV_ID_VALUE: u64 = 0x1;
const HPET_CAP_REV_ID_SHIFT: usize = 0;
@@ -64,8 +63,6 @@ const HPET_CAP_VENDER_ID_SHIFT: usize = 16;
/// Main Counter Tick Period (bits 32:63)
const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32;
-/// General Configuration Register
-const HPET_CFG_REG: u64 = 0x010;
/// Overall Enable (bit 0)
const HPET_CFG_ENABLE_SHIFT: usize = 0;
/// Legacy Replacement Route (bit 1)
@@ -73,14 +70,6 @@ const HPET_CFG_LEG_RT_SHIFT: usize = 1;
/// Other bits are reserved.
const HPET_CFG_WRITE_MASK: u64 = 0x003;
-/// General Interrupt Status Register
-const HPET_INT_STATUS_REG: u64 = 0x020;
-
-/// Main Counter Value Register
-const HPET_COUNTER_REG: u64 = 0x0f0;
-
-/// Timer N Configuration and Capability Register (masked by 0x18)
-const HPET_TN_CFG_REG: u64 = 0x000;
/// bit 0, 7, and bits 16:31 are reserved.
/// bit 4, 5, 15, and bits 32:64 are read-only.
const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e;
@@ -108,11 +97,51 @@ 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;
-/// Timer N Comparator Value Register (masked by 0x18)
-const HPET_TN_CMP_REG: u64 = 0x008;
+#[derive(qemu_api_macros::TryInto)]
+#[repr(u64)]
+#[allow(non_camel_case_types)]
+/// Timer registers, masked by 0x18
+enum TimerRegister {
+ /// Timer N Configuration and Capability Register
+ CFG = 0,
+ /// Timer N Comparator Value Register
+ CMP = 8,
+ /// Timer N FSB Interrupt Route Register
+ ROUTE = 16,
+}
+
+#[derive(qemu_api_macros::TryInto)]
+#[repr(u64)]
+#[allow(non_camel_case_types)]
+/// Global registers
+enum GlobalRegister {
+ /// General Capabilities and ID Register
+ CAP = 0,
+ /// General Configuration Register
+ CFG = 0x10,
+ /// General Interrupt Status Register
+ INT_STATUS = 0x20,
+ /// Main Counter Value Register
+ COUNTER = 0xF0,
+}
-/// Timer N FSB Interrupt Route Register (masked by 0x18)
-const HPET_TN_FSB_ROUTE_REG: u64 = 0x010;
+enum HPETRegister<'a> {
+ /// Global register in the range from `0` to `0xff`
+ Global(GlobalRegister),
+
+ /// Register in the timer block `0x100`...`0x3ff`
+ Timer(&'a BqlRefCell<HPETTimer>, TimerRegister),
+
+ /// Invalid address
+ #[allow(dead_code)]
+ Unknown(hwaddr),
+}
+
+struct HPETAddrDecode<'a> {
+ shift: u32,
+ len: u32,
+ reg: HPETRegister<'a>,
+}
const fn hpet_next_wrap(cur_tick: u64) -> u64 {
(cur_tick | 0xffffffff) + 1
@@ -151,14 +180,14 @@ fn timer_handler(timer_cell: &BqlRefCell<HPETTimer>) {
/// HPET Timer Abstraction
#[repr(C)]
-#[derive(Debug, Default, qemu_api_macros::offsets)]
+#[derive(Debug, qemu_api_macros::offsets)]
pub struct HPETTimer {
/// timer N index within the timer block (`HPETState`)
#[doc(alias = "tn")]
index: usize,
- qemu_timer: Option<Box<Timer>>,
+ qemu_timer: Timer,
/// timer block abstraction containing this timer
- state: Option<NonNull<HPETState>>,
+ state: NonNull<HPETState>,
// Memory-mapped, software visible timer registers
/// Timer N Configuration and Capability Register
@@ -181,32 +210,39 @@ pub struct HPETTimer {
}
impl HPETTimer {
- fn init(&mut self, index: usize, state_ptr: *mut HPETState) -> &mut Self {
- *self = HPETTimer::default();
- self.index = index;
- self.state = NonNull::new(state_ptr);
- self
- }
-
- fn init_timer_with_state(&mut self) {
- self.qemu_timer = Some(Box::new({
- let mut t = Timer::new();
- t.init_full(
- None,
- CLOCK_VIRTUAL,
- Timer::NS,
- 0,
- timer_handler,
- &self.get_state().timers[self.index],
- );
- t
- }));
+ fn init(&mut self, index: usize, state: &HPETState) {
+ *self = 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 _ as *mut _).unwrap(),
+ config: 0,
+ cmp: 0,
+ fsb: 0,
+ cmp64: 0,
+ period: 0,
+ wrap_flag: 0,
+ last: 0,
+ };
+
+ // 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],
+ )
}
fn get_state(&self) -> &HPETState {
// SAFETY:
// the pointer is convertible to a reference
- unsafe { self.state.unwrap().as_ref() }
+ unsafe { self.state.as_ref() }
}
fn is_int_active(&self) -> bool {
@@ -330,7 +366,7 @@ impl HPETTimer {
}
self.last = ns;
- self.qemu_timer.as_ref().unwrap().modify(self.last);
+ self.qemu_timer.modify(self.last);
}
fn set_timer(&mut self) {
@@ -353,7 +389,7 @@ impl HPETTimer {
fn del_timer(&mut self) {
// Just remove the timer from the timer_list without destroying
// this timer instance.
- self.qemu_timer.as_ref().unwrap().delete();
+ self.qemu_timer.delete();
if self.is_int_active() {
// For level-triggered interrupt, this leaves interrupt status
@@ -463,33 +499,21 @@ impl HPETTimer {
self.update_irq(true);
}
- const fn read(&self, addr: hwaddr, _size: u32) -> u64 {
- let shift: u64 = (addr & 4) * 8;
-
- match addr & !4 {
- HPET_TN_CFG_REG => self.config >> shift, // including interrupt capabilities
- HPET_TN_CMP_REG => self.cmp >> shift, // comparator register
- HPET_TN_FSB_ROUTE_REG => self.fsb >> shift,
- _ => {
- // TODO: Add trace point - trace_hpet_ram_read_invalid()
- // Reserved.
- 0
- }
+ const fn read(&self, reg: TimerRegister) -> u64 {
+ use TimerRegister::*;
+ match reg {
+ CFG => self.config, // including interrupt capabilities
+ CMP => self.cmp, // comparator register
+ ROUTE => self.fsb,
}
}
- fn write(&mut self, addr: hwaddr, value: u64, size: u32) {
- let shift = ((addr & 4) * 8) as u32;
- let len = std::cmp::min(size * 8, 64 - shift);
-
- match addr & !4 {
- HPET_TN_CFG_REG => self.set_tn_cfg_reg(shift, len, value),
- HPET_TN_CMP_REG => self.set_tn_cmp_reg(shift, len, value),
- HPET_TN_FSB_ROUTE_REG => self.set_tn_fsb_route_reg(shift, len, value),
- _ => {
- // TODO: Add trace point - trace_hpet_ram_write_invalid()
- // Reserved.
- }
+ fn write(&mut self, reg: TimerRegister, value: u64, shift: u32, len: u32) {
+ use TimerRegister::*;
+ match reg {
+ CFG => self.set_tn_cfg_reg(shift, len, value),
+ CMP => self.set_tn_cmp_reg(shift, len, value),
+ ROUTE => self.set_tn_fsb_route_reg(shift, len, value),
}
}
}
@@ -581,13 +605,8 @@ impl HPETState {
}
fn init_timer(&self) {
- let raw_ptr: *mut HPETState = self as *const HPETState as *mut HPETState;
-
for (index, timer) in self.timers.iter().enumerate() {
- timer
- .borrow_mut()
- .init(index, raw_ptr)
- .init_timer_with_state();
+ timer.borrow_mut().init(index, self);
}
}
@@ -727,8 +746,6 @@ impl HPETState {
}
fn reset_hold(&self, _type: ResetType) {
- let sbd = self.upcast::<SysBusDevice>();
-
for timer in self.timers.iter().take(self.num_timers.get()) {
timer.borrow_mut().reset();
}
@@ -741,83 +758,79 @@ impl HPETState {
HPETFwConfig::update_hpet_cfg(
self.hpet_id.get(),
self.capability.get() as u32,
- sbd.mmio[0].addr,
+ self.mmio_addr(0).unwrap(),
);
// to document that the RTC lowers its output on reset as well
self.rtc_irq_level.set(0);
}
- fn timer_and_addr(&self, addr: hwaddr) -> Option<(&BqlRefCell<HPETTimer>, hwaddr)> {
- let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
+ 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);
- // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id)
- if timer_id > self.num_timers.get() {
- // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id)
- None
+ addr &= !4;
+ let reg = if (0..=0xff).contains(&addr) {
+ GlobalRegister::try_from(addr).map(HPETRegister::Global)
} else {
- // Keep the complete address so that HPETTimer's read and write could
- // detect the invalid access.
- Some((&self.timers[timer_id], addr & 0x1F))
- }
+ let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
+ if timer_id <= self.num_timers.get() {
+ // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id)
+ TimerRegister::try_from(addr)
+ .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg))
+ } else {
+ // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id)
+ Err(addr)
+ }
+ };
+
+ // reg is now a Result<HPETRegister, hwaddr>
+ // convert the Err case into HPETRegister as well
+ let reg = reg.unwrap_or_else(HPETRegister::Unknown);
+ HPETAddrDecode { shift, len, reg }
}
fn read(&self, addr: hwaddr, size: u32) -> u64 {
- let shift: u64 = (addr & 4) * 8;
-
- // address range of all TN regs
// TODO: Add trace point - trace_hpet_ram_read(addr)
- if (0x100..=0x3ff).contains(&addr) {
- match self.timer_and_addr(addr) {
- None => 0, // Reserved,
- Some((timer, tn_addr)) => timer.borrow_mut().read(tn_addr, size),
- }
- } else {
- match addr & !4 {
- HPET_CAP_REG => self.capability.get() >> shift, /* including HPET_PERIOD 0x004 */
- // (CNT_CLK_PERIOD field)
- HPET_CFG_REG => self.config.get() >> shift,
- HPET_COUNTER_REG => {
- let cur_tick: u64 = if self.is_hpet_enabled() {
- self.get_ticks()
- } else {
- self.counter.get()
- };
-
- // TODO: Add trace point - trace_hpet_ram_read_reading_counter(addr & 4,
- // cur_tick)
- cur_tick >> shift
- }
- HPET_INT_STATUS_REG => self.int_status.get() >> shift,
- _ => {
- // TODO: Add trace point- trace_hpet_ram_read_invalid()
- // Reserved.
- 0
+ let HPETAddrDecode { shift, reg, .. } = self.decode(addr, size);
+
+ use GlobalRegister::*;
+ use HPETRegister::*;
+ (match reg {
+ Timer(timer, tn_reg) => timer.borrow_mut().read(tn_reg),
+ Global(CAP) => self.capability.get(), /* including HPET_PERIOD 0x004 */
+ Global(CFG) => self.config.get(),
+ Global(INT_STATUS) => self.int_status.get(),
+ Global(COUNTER) => {
+ // TODO: Add trace point
+ // trace_hpet_ram_read_reading_counter(addr & 4, cur_tick)
+ if self.is_hpet_enabled() {
+ self.get_ticks()
+ } else {
+ self.counter.get()
}
}
- }
+ Unknown(_) => {
+ // TODO: Add trace point- trace_hpet_ram_read_invalid()
+ 0
+ }
+ }) >> shift
}
fn write(&self, addr: hwaddr, value: u64, size: u32) {
- let shift = ((addr & 4) * 8) as u32;
- let len = std::cmp::min(size * 8, 64 - shift);
+ let HPETAddrDecode { shift, len, reg } = self.decode(addr, size);
// TODO: Add trace point - trace_hpet_ram_write(addr, value)
- if (0x100..=0x3ff).contains(&addr) {
- match self.timer_and_addr(addr) {
- None => (), // Reserved.
- Some((timer, tn_addr)) => timer.borrow_mut().write(tn_addr, value, size),
- }
- } else {
- match addr & !0x4 {
- HPET_CAP_REG => {} // General Capabilities and ID Register: Read Only
- HPET_CFG_REG => self.set_cfg_reg(shift, len, value),
- HPET_INT_STATUS_REG => self.set_int_status_reg(shift, len, value),
- HPET_COUNTER_REG => self.set_counter_reg(shift, len, value),
- _ => {
- // TODO: Add trace point - trace_hpet_ram_write_invalid()
- // Reserved.
- }
+ use GlobalRegister::*;
+ use HPETRegister::*;
+ match reg {
+ Timer(timer, tn_reg) => timer.borrow_mut().write(tn_reg, value, shift, len),
+ Global(CAP) => {} // General Capabilities and ID Register: Read Only
+ Global(CFG) => self.set_cfg_reg(shift, len, value),
+ Global(INT_STATUS) => self.set_int_status_reg(shift, len, value),
+ Global(COUNTER) => self.set_counter_reg(shift, len, value),
+ Unknown(_) => {
+ // TODO: Add trace point - trace_hpet_ram_write_invalid()
}
}
}
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index 7ec2182..eda0d46 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -6,7 +6,7 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
- DeriveInput, Field, Fields, Ident, Meta, Path, Token, Type, Variant, Visibility,
+ DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Type, Variant, Visibility,
};
mod utils;
@@ -33,6 +33,35 @@ fn get_fields<'a>(
}
}
+fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> {
+ if let Data::Struct(s) = &input.data {
+ let unnamed = match &s.fields {
+ Fields::Unnamed(FieldsUnnamed {
+ unnamed: ref fields,
+ ..
+ }) => fields,
+ _ => {
+ 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])
+ } else {
+ Err(MacroError::Message(
+ format!("Struct required for {}", msg),
+ input.ident.span(),
+ ))
+ }
+}
+
fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
let expected = parse_quote! { #[repr(C)] };
@@ -46,6 +75,19 @@ fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
}
}
+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)]")?;
@@ -72,6 +114,52 @@ pub fn derive_object(input: TokenStream) -> TokenStream {
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)
+}
+
#[rustfmt::skip::macros(quote)]
fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
is_c_repr(&input, "#[derive(offsets)]")?;
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index bcf1cf7..a3f226c 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -42,22 +42,31 @@ _qemu_api_rs = static_library(
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _qemu_api_cfg,
- dependencies: libc_dep,
+ 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,
- dependencies: qemu_api_macros,
-)
+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)])
+ 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)])
+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(
@@ -66,8 +75,7 @@ test('rust-qemu-api-integration',
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_args: ['--test'],
install: false,
- dependencies: [qemu_api, qemu_api_macros],
- link_whole: [rust_qemu_api_objs, libqemuutil]),
+ dependencies: [qemu_api, qemu_api_macros, rust_qemu_api_deps]),
args: [
'--test', '--test-threads', '1',
'--format', 'pretty',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index d286863..3c1d297 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -25,33 +25,11 @@ 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`.
-unsafe impl Send for BusState {}
-unsafe impl Sync for BusState {}
-
+// 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 Send for Chardev {}
-unsafe impl Sync for Chardev {}
-
-unsafe impl Send for Clock {}
-unsafe impl Sync for Clock {}
-
-unsafe impl Send for DeviceState {}
-unsafe impl Sync for DeviceState {}
-
-unsafe impl Send for MemoryRegion {}
-unsafe impl Sync for MemoryRegion {}
-
-unsafe impl Send for ObjectClass {}
-unsafe impl Sync for ObjectClass {}
-
-unsafe impl Send for Object {}
-unsafe impl Sync for Object {}
-
-unsafe impl Send for SysBusDevice {}
-unsafe impl Sync for SysBusDevice {}
-
// SAFETY: this is a pure data struct
unsafe impl Send for CoalescedMemoryRange {}
unsafe impl Sync for CoalescedMemoryRange {}
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index eae4e2c..ab0785a 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -27,7 +27,7 @@
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
-//! BQL-protected mutable containers.
+//! QEMU-specific mutable containers
//!
//! Rust memory safety is based on this rule: Given an object `T`, it is only
//! possible to have one of the following:
@@ -43,8 +43,10 @@
//! usually have their pointer shared with the "outside world very early in
//! their lifetime", for example when they create their
//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual
-//! parts of a device must be made mutable in a controlled manner through the
-//! use of cell types.
+//! parts of a device must be made mutable in a controlled manner; this module
+//! provides the tools to do so.
+//!
+//! ## Cell types
//!
//! [`BqlCell<T>`] and [`BqlRefCell<T>`] allow doing this via the Big QEMU Lock.
//! While they are essentially the same single-threaded primitives that are
@@ -71,7 +73,35 @@
//! QEMU device implementations is usually incorrect and can lead to
//! thread-safety issues.
//!
-//! ## `BqlCell<T>`
+//! ### Example
+//!
+//! ```
+//! # use qemu_api::prelude::*;
+//! # use qemu_api::{c_str, cell::BqlRefCell, irq::InterruptSource, irq::IRQState};
+//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField};
+//! # const N_GPIOS: usize = 8;
+//! # struct PL061Registers { /* ... */ }
+//! # unsafe impl ObjectType for PL061State {
+//! # type Class = <SysBusDevice as ObjectType>::Class;
+//! # const TYPE_NAME: &'static std::ffi::CStr = c_str!("pl061");
+//! # }
+//! struct PL061State {
+//! parent_obj: ParentField<SysBusDevice>,
+//!
+//! // Configuration is read-only after initialization
+//! pullups: u32,
+//! pulldowns: u32,
+//!
+//! // Single values shared with C code use BqlCell, in this case via InterruptSource
+//! out: [InterruptSource; N_GPIOS],
+//! interrupt: InterruptSource,
+//!
+//! // Larger state accessed by device methods uses BqlRefCell or Mutex
+//! registers: BqlRefCell<PL061Registers>,
+//! }
+//! ```
+//!
+//! ### `BqlCell<T>`
//!
//! [`BqlCell<T>`] implements interior mutability by moving values in and out of
//! the cell. That is, an `&mut T` to the inner value can never be obtained as
@@ -91,7 +121,7 @@
//! - [`set`](BqlCell::set): this method replaces the interior value,
//! dropping the replaced value.
//!
-//! ## `BqlRefCell<T>`
+//! ### `BqlRefCell<T>`
//!
//! [`BqlRefCell<T>`] uses Rust's lifetimes to implement "dynamic borrowing", a
//! process whereby one can claim temporary, exclusive, mutable access to the
@@ -111,13 +141,82 @@
//! 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,
- mem,
+ marker::{PhantomData, PhantomPinned},
+ mem::{self, MaybeUninit},
ops::{Deref, DerefMut},
ptr::NonNull,
};
@@ -840,3 +939,167 @@ 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() as *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/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs
index 74cfb63..11e6c45 100644
--- a/rust/qemu-api/src/chardev.rs
+++ b/rust/qemu-api/src/chardev.rs
@@ -3,13 +3,255 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//! Bindings for character devices
+//!
+//! Character devices in QEMU can run under the big QEMU lock or in a separate
+//! `GMainContext`. Here we only support the former, because the bindings
+//! enforce that the BQL is taken whenever the functions in [`CharBackend`] are
+//! called.
-use std::ffi::CStr;
+use std::{
+ ffi::CStr,
+ fmt::{self, Debug},
+ io::{self, ErrorKind, Write},
+ marker::PhantomPinned,
+ os::raw::{c_int, c_void},
+ ptr::addr_of_mut,
+ slice,
+};
-use crate::{bindings, prelude::*};
+use crate::{
+ bindings,
+ callbacks::FnCall,
+ cell::{BqlRefMut, Opaque},
+ prelude::*,
+};
+
+/// A safe wrapper around [`bindings::Chardev`].
+#[repr(transparent)]
+#[derive(qemu_api_macros::Wrapper)]
+pub struct Chardev(Opaque<bindings::Chardev>);
-pub type Chardev = bindings::Chardev;
pub type ChardevClass = bindings::ChardevClass;
+pub type Event = bindings::QEMUChrEvent;
+
+/// A safe wrapper around [`bindings::CharBackend`], denoting the character
+/// back-end that is used for example by a device. Compared to the
+/// underlying C struct it adds BQL protection, and is marked as pinned
+/// because the QOM object ([`bindings::Chardev`]) contains a pointer to
+/// the `CharBackend`.
+pub struct CharBackend {
+ inner: BqlRefCell<bindings::CharBackend>,
+ _pin: PhantomPinned,
+}
+
+impl Write for BqlRefMut<'_, bindings::CharBackend> {
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let chr: &mut bindings::CharBackend = self;
+
+ let len = buf.len().try_into().unwrap();
+ let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) };
+ errno::into_io_result(r).map(|cnt| cnt as usize)
+ }
+
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ let chr: &mut bindings::CharBackend = self;
+
+ let len = buf.len().try_into().unwrap();
+ let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) };
+ errno::into_io_result(r).and_then(|cnt| {
+ if cnt as usize == buf.len() {
+ Ok(())
+ } else {
+ Err(ErrorKind::WriteZero.into())
+ }
+ })
+ }
+}
+
+impl Debug for CharBackend {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // SAFETY: accessed just to print the values
+ let chr = self.inner.as_ptr();
+ Debug::fmt(unsafe { &*chr }, f)
+ }
+}
+
+// FIXME: use something like PinnedDrop from the pinned_init crate
+impl Drop for CharBackend {
+ fn drop(&mut self) {
+ self.disable_handlers();
+ }
+}
+
+impl CharBackend {
+ /// Enable the front-end's character device handlers, if there is an
+ /// associated `Chardev`.
+ pub fn enable_handlers<
+ 'chardev,
+ 'owner: 'chardev,
+ T,
+ CanReceiveFn: for<'a> FnCall<(&'a T,), u32>,
+ ReceiveFn: for<'a, 'b> FnCall<(&'a T, &'b [u8])>,
+ EventFn: for<'a> FnCall<(&'a T, Event)>,
+ >(
+ // When "self" is dropped, the handlers are automatically disabled.
+ // However, this is not necessarily true if the owner is dropped.
+ // So require the owner to outlive the character device.
+ &'chardev self,
+ owner: &'owner T,
+ _can_receive: CanReceiveFn,
+ _receive: ReceiveFn,
+ _event: EventFn,
+ ) {
+ unsafe extern "C" fn rust_can_receive_cb<T, F: for<'a> FnCall<(&'a T,), u32>>(
+ opaque: *mut c_void,
+ ) -> c_int {
+ // SAFETY: the values are safe according to the contract of
+ // enable_handlers() and qemu_chr_fe_set_handlers()
+ let owner: &T = unsafe { &*(opaque.cast::<T>()) };
+ let r = F::call((owner,));
+ r.try_into().unwrap()
+ }
+
+ unsafe extern "C" fn rust_receive_cb<T, F: for<'a, 'b> FnCall<(&'a T, &'b [u8])>>(
+ opaque: *mut c_void,
+ buf: *const u8,
+ size: c_int,
+ ) {
+ // SAFETY: the values are safe according to the contract of
+ // enable_handlers() and qemu_chr_fe_set_handlers()
+ let owner: &T = unsafe { &*(opaque.cast::<T>()) };
+ let buf = unsafe { slice::from_raw_parts(buf, size.try_into().unwrap()) };
+ F::call((owner, buf))
+ }
+
+ unsafe extern "C" fn rust_event_cb<T, F: for<'a> FnCall<(&'a T, Event)>>(
+ opaque: *mut c_void,
+ event: Event,
+ ) {
+ // SAFETY: the values are safe according to the contract of
+ // enable_handlers() and qemu_chr_fe_set_handlers()
+ let owner: &T = unsafe { &*(opaque.cast::<T>()) };
+ F::call((owner, event))
+ }
+
+ let _: () = CanReceiveFn::ASSERT_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>)
+ } else {
+ None
+ };
+ let event_cb: Option<unsafe extern "C" fn(*mut c_void, Event)> = if EventFn::is_some() {
+ Some(rust_event_cb::<T, EventFn>)
+ } else {
+ None
+ };
+
+ let mut chr = self.inner.borrow_mut();
+ // SAFETY: the borrow promises that the BQL is taken
+ unsafe {
+ bindings::qemu_chr_fe_set_handlers(
+ addr_of_mut!(*chr),
+ Some(rust_can_receive_cb::<T, CanReceiveFn>),
+ receive_cb,
+ event_cb,
+ None,
+ (owner as *const T as *mut T).cast::<c_void>(),
+ core::ptr::null_mut(),
+ true,
+ );
+ }
+ }
+
+ /// Disable the front-end's character device handlers.
+ pub fn disable_handlers(&self) {
+ let mut chr = self.inner.borrow_mut();
+ // SAFETY: the borrow promises that the BQL is taken
+ unsafe {
+ bindings::qemu_chr_fe_set_handlers(
+ addr_of_mut!(*chr),
+ None,
+ None,
+ None,
+ None,
+ core::ptr::null_mut(),
+ core::ptr::null_mut(),
+ true,
+ );
+ }
+ }
+
+ /// Notify that the frontend is ready to receive data.
+ pub fn accept_input(&self) {
+ let mut chr = self.inner.borrow_mut();
+ // SAFETY: the borrow promises that the BQL is taken
+ unsafe { bindings::qemu_chr_fe_accept_input(addr_of_mut!(*chr)) }
+ }
+
+ /// Temporarily borrow the character device, allowing it to be used
+ /// as an implementor of `Write`. Note that it is not valid to drop
+ /// 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()
+ }
+
+ /// Send a continuous stream of zero bits on the line if `enabled` is
+ /// true, or a short stream if `enabled` is false.
+ pub fn send_break(&self, long: bool) -> io::Result<()> {
+ let mut chr = self.inner.borrow_mut();
+ let mut duration: c_int = long.into();
+ // SAFETY: the borrow promises that the BQL is taken
+ let r = unsafe {
+ bindings::qemu_chr_fe_ioctl(
+ addr_of_mut!(*chr),
+ bindings::CHR_IOCTL_SERIAL_SET_BREAK as i32,
+ addr_of_mut!(duration).cast::<c_void>(),
+ )
+ };
+
+ errno::into_io_result(r).map(|_| ())
+ }
+
+ /// Write data to a character backend from the front end. This function
+ /// will send data from the front end to the back end. Unlike
+ /// `write`, this function will block if the back end cannot
+ /// consume all of the data attempted to be written.
+ ///
+ /// Returns the number of bytes consumed (0 if no associated Chardev) or an
+ /// error.
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ let len = buf.len().try_into().unwrap();
+ // SAFETY: qemu_chr_fe_write is thread-safe
+ let r = unsafe { bindings::qemu_chr_fe_write(self.inner.as_ptr(), buf.as_ptr(), len) };
+ errno::into_io_result(r).map(|cnt| cnt as usize)
+ }
+
+ /// Write data to a character backend from the front end. This function
+ /// will send data from the front end to the back end. Unlike
+ /// `write`, this function will block if the back end cannot
+ /// consume all of the data attempted to be written.
+ ///
+ /// Returns the number of bytes consumed (0 if no associated Chardev) or an
+ /// error.
+ pub fn write_all(&self, buf: &[u8]) -> io::Result<()> {
+ let len = buf.len().try_into().unwrap();
+ // SAFETY: qemu_chr_fe_write_all is thread-safe
+ let r = unsafe { bindings::qemu_chr_fe_write_all(self.inner.as_ptr(), buf.as_ptr(), len) };
+ errno::into_io_result(r).and_then(|cnt| {
+ if cnt as usize == buf.len() {
+ Ok(())
+ } else {
+ Err(ErrorKind::WriteZero.into())
+ }
+ })
+ }
+}
unsafe impl ObjectType for Chardev {
type Class = ChardevClass;
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
index 34c1926..1222d4f 100644
--- a/rust/qemu-api/src/irq.rs
+++ b/rust/qemu-api/src/irq.rs
@@ -8,10 +8,16 @@ use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr};
use crate::{
bindings::{self, qemu_set_irq},
+ cell::Opaque,
prelude::*,
qom::ObjectClass,
};
+/// An opaque wrapper around [`bindings::IRQState`].
+#[repr(transparent)]
+#[derive(Debug, qemu_api_macros::Wrapper)]
+pub struct IRQState(Opaque<bindings::IRQState>);
+
/// Interrupt sources are used by devices to pass changes to a value (typically
/// a boolean). The interrupt sink is usually an interrupt controller or
/// GPIO controller.
@@ -21,8 +27,7 @@ use crate::{
/// method sends a `true` value to the sink. If the guest has to see a
/// different polarity, that change is performed by the board between the
/// device and the interrupt controller.
-pub type IRQState = 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
@@ -40,7 +45,7 @@ pub struct InterruptSource<T = bool>
where
c_int: From<T>,
{
- cell: BqlCell<*mut IRQState>,
+ cell: BqlCell<*mut bindings::IRQState>,
_marker: PhantomData<T>,
}
@@ -79,11 +84,11 @@ where
}
}
- pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState {
+ pub(crate) const fn as_ptr(&self) -> *mut *mut bindings::IRQState {
self.cell.as_ptr()
}
- pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut IRQState {
+ pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState {
assert!(!slice.is_empty());
slice[0].as_ptr()
}
diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs
index 682951a..fdb1ea1 100644
--- a/rust/qemu-api/src/memory.rs
+++ b/rust/qemu-api/src/memory.rs
@@ -6,9 +6,8 @@
use std::{
ffi::{CStr, CString},
- marker::{PhantomData, PhantomPinned},
+ marker::PhantomData,
os::raw::{c_uint, c_void},
- ptr::addr_of,
};
pub use bindings::{hwaddr, MemTxAttrs};
@@ -16,6 +15,7 @@ pub use bindings::{hwaddr, MemTxAttrs};
use crate::{
bindings::{self, device_endian, memory_region_init_io},
callbacks::FnCall,
+ cell::Opaque,
prelude::*,
zeroable::Zeroable,
};
@@ -132,13 +132,13 @@ impl<T> Default for MemoryRegionOpsBuilder<T> {
}
}
-/// A safe wrapper around [`bindings::MemoryRegion`]. Compared to the
-/// underlying C struct it is marked as pinned because the QOM tree
-/// contains a pointer to it.
-pub struct MemoryRegion {
- inner: bindings::MemoryRegion,
- _pin: PhantomPinned,
-}
+/// A safe wrapper around [`bindings::MemoryRegion`].
+#[repr(transparent)]
+#[derive(qemu_api_macros::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
@@ -157,7 +157,7 @@ impl MemoryRegion {
let cstr = CString::new(name).unwrap();
memory_region_init_io(
slot,
- owner.cast::<Object>(),
+ owner.cast::<bindings::Object>(),
ops,
owner.cast::<c_void>(),
cstr.as_ptr(),
@@ -174,13 +174,15 @@ impl MemoryRegion {
size: u64,
) {
unsafe {
- Self::do_init_io(&mut self.inner, owner.cast::<Object>(), &ops.0, name, size);
+ Self::do_init_io(
+ self.0.as_mut_ptr(),
+ owner.cast::<Object>(),
+ &ops.0,
+ name,
+ size,
+ );
}
}
-
- pub(crate) const fn as_mut_ptr(&self) -> *mut bindings::MemoryRegion {
- addr_of!(self.inner) as *mut _
- }
}
unsafe impl ObjectType for MemoryRegion {
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 634acf3..43bfcd5 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -17,7 +17,6 @@ pub use crate::qom::InterfaceType;
pub use crate::qom::IsA;
pub use crate::qom::Object;
pub use crate::qom::ObjectCast;
-pub use crate::qom::ObjectCastMut;
pub use crate::qom::ObjectDeref;
pub use crate::qom::ObjectClassMethods;
pub use crate::qom::ObjectMethods;
diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs
index c136457..18b4a9b 100644
--- a/rust/qemu-api/src/qdev.rs
+++ b/rust/qemu-api/src/qdev.rs
@@ -10,12 +10,12 @@ use std::{
ptr::NonNull,
};
-pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType};
+pub use bindings::{ClockEvent, DeviceClass, Property, ResetType};
use crate::{
bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass},
callbacks::FnCall,
- cell::bql_locked,
+ cell::{bql_locked, Opaque},
chardev::Chardev,
irq::InterruptSource,
prelude::*,
@@ -23,6 +23,22 @@ use crate::{
vmstate::VMStateDescription,
};
+/// A safe wrapper around [`bindings::Clock`].
+#[repr(transparent)]
+#[derive(Debug, qemu_api_macros::Wrapper)]
+pub struct Clock(Opaque<bindings::Clock>);
+
+unsafe impl Send for Clock {}
+unsafe impl Sync for Clock {}
+
+/// A safe wrapper around [`bindings::DeviceState`].
+#[repr(transparent)]
+#[derive(Debug, qemu_api_macros::Wrapper)]
+pub struct DeviceState(Opaque<bindings::DeviceState>);
+
+unsafe impl Send for DeviceState {}
+unsafe impl Sync for DeviceState {}
+
/// Trait providing the contents of the `ResettablePhases` struct,
/// which is part of the QOM `Resettable` interface.
pub trait ResettablePhasesImpl {
@@ -52,7 +68,7 @@ pub trait ResettablePhasesImpl {
/// can be downcasted to type `T`. We also expect the device is
/// readable/writeable from one thread at any time.
unsafe extern "C" fn rust_resettable_enter_fn<T: ResettablePhasesImpl>(
- obj: *mut Object,
+ obj: *mut bindings::Object,
typ: ResetType,
) {
let state = NonNull::new(obj).unwrap().cast::<T>();
@@ -65,7 +81,7 @@ unsafe extern "C" fn rust_resettable_enter_fn<T: ResettablePhasesImpl>(
/// can be downcasted to type `T`. We also expect the device is
/// readable/writeable from one thread at any time.
unsafe extern "C" fn rust_resettable_hold_fn<T: ResettablePhasesImpl>(
- obj: *mut Object,
+ obj: *mut bindings::Object,
typ: ResetType,
) {
let state = NonNull::new(obj).unwrap().cast::<T>();
@@ -78,7 +94,7 @@ unsafe extern "C" fn rust_resettable_hold_fn<T: ResettablePhasesImpl>(
/// can be downcasted to type `T`. We also expect the device is
/// readable/writeable from one thread at any time.
unsafe extern "C" fn rust_resettable_exit_fn<T: ResettablePhasesImpl>(
- obj: *mut Object,
+ obj: *mut bindings::Object,
typ: ResetType,
) {
let state = NonNull::new(obj).unwrap().cast::<T>();
@@ -117,7 +133,10 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> {
/// We expect the FFI user of this function to pass a valid pointer that
/// can be downcasted to type `T`. We also expect the device is
/// readable/writeable from one thread at any time.
-unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp: *mut *mut Error) {
+unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(
+ dev: *mut bindings::DeviceState,
+ _errp: *mut *mut Error,
+) {
let state = NonNull::new(dev).unwrap().cast::<T>();
T::REALIZE.unwrap()(unsafe { state.as_ref() });
}
@@ -251,7 +270,7 @@ where
events: ClockEvent,
) -> Owned<Clock> {
fn do_init_clock_in(
- dev: *mut DeviceState,
+ dev: &DeviceState,
name: &str,
cb: Option<unsafe extern "C" fn(*mut c_void, ClockEvent)>,
events: ClockEvent,
@@ -265,14 +284,15 @@ where
unsafe {
let cstr = CString::new(name).unwrap();
let clk = bindings::qdev_init_clock_in(
- dev,
+ dev.as_mut_ptr(),
cstr.as_ptr(),
cb,
- dev.cast::<c_void>(),
+ dev.as_void_ptr(),
events.0,
);
- Owned::from(&*clk)
+ let clk: &Clock = Clock::from_raw(clk);
+ Owned::from(clk)
}
}
@@ -289,7 +309,7 @@ where
None
};
- do_init_clock_in(self.as_mut_ptr(), name, cb, events)
+ do_init_clock_in(self.upcast(), name, cb, events)
}
/// Add an output clock named `name`.
@@ -304,17 +324,23 @@ where
fn init_clock_out(&self, name: &str) -> Owned<Clock> {
unsafe {
let cstr = CString::new(name).unwrap();
- let clk = bindings::qdev_init_clock_out(self.as_mut_ptr(), cstr.as_ptr());
+ let clk = bindings::qdev_init_clock_out(self.upcast().as_mut_ptr(), cstr.as_ptr());
- Owned::from(&*clk)
+ let clk: &Clock = Clock::from_raw(clk);
+ Owned::from(clk)
}
}
fn prop_set_chr(&self, propname: &str, chr: &Owned<Chardev>) {
assert!(bql_locked());
let c_propname = CString::new(propname).unwrap();
+ let chr: &Chardev = chr;
unsafe {
- bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr());
+ bindings::qdev_prop_set_chr(
+ self.upcast().as_mut_ptr(),
+ c_propname.as_ptr(),
+ chr.as_mut_ptr(),
+ );
}
}
@@ -323,8 +349,17 @@ where
num_lines: u32,
_cb: F,
) {
- let _: () = F::ASSERT_IS_SOME;
+ fn do_init_gpio_in(
+ dev: &DeviceState,
+ num_lines: u32,
+ gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int),
+ ) {
+ unsafe {
+ qdev_init_gpio_in(dev.as_mut_ptr(), Some(gpio_in_cb), num_lines as c_int);
+ }
+ }
+ let _: () = F::ASSERT_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,
@@ -337,19 +372,13 @@ where
let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) =
rust_irq_handler::<Self::Target, F>;
- unsafe {
- qdev_init_gpio_in(
- self.as_mut_ptr::<DeviceState>(),
- Some(gpio_in_cb),
- num_lines as c_int,
- );
- }
+ do_init_gpio_in(self.upcast(), num_lines, gpio_in_cb);
}
fn init_gpio_out(&self, pins: &[InterruptSource]) {
unsafe {
qdev_init_gpio_out(
- self.as_mut_ptr::<DeviceState>(),
+ self.upcast().as_mut_ptr(),
InterruptSource::slice_as_ptr(pins),
pins.len() as c_int,
);
diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
index 5488643..34d7bc0 100644
--- a/rust/qemu-api/src/qom.rs
+++ b/rust/qemu-api/src/qom.rs
@@ -101,16 +101,24 @@ use std::{
ptr::NonNull,
};
-pub use bindings::{Object, ObjectClass};
+pub use bindings::ObjectClass;
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,
+ cell::{bql_locked, Opaque},
};
+/// A safe wrapper around [`bindings::Object`].
+#[repr(transparent)]
+#[derive(Debug, qemu_api_macros::Wrapper)]
+pub struct Object(Opaque<bindings::Object>);
+
+unsafe impl Send for Object {}
+unsafe impl Sync for Object {}
+
/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct
/// or indirect parent of `Self`).
///
@@ -199,7 +207,7 @@ impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> {
}
}
-unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
+unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut bindings::Object) {
let mut state = NonNull::new(obj).unwrap().cast::<T>();
// SAFETY: obj is an instance of T, since rust_instance_init<T>
// is called from QOM core as the instance_init function
@@ -209,7 +217,7 @@ unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
}
}
-unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) {
+unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut bindings::Object) {
let state = NonNull::new(obj).unwrap().cast::<T>();
// SAFETY: obj is an instance of T, since rust_instance_post_init<T>
// is called from QOM core as the instance_post_init function
@@ -230,7 +238,7 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ObjectImpl>(
<T as ObjectImpl>::CLASS_INIT(unsafe { klass.as_mut() })
}
-unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
+unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut bindings::Object) {
// SAFETY: obj is an instance of T, since drop_object<T> is called
// from the QOM core function object_deinit() as the instance_finalize
// function for class T. Note that while object_deinit() will drop the
@@ -280,14 +288,14 @@ pub unsafe trait ObjectType: Sized {
/// Return the receiver as an Object. This is always safe, even
/// if this type represents an interface.
fn as_object(&self) -> &Object {
- unsafe { &*self.as_object_ptr() }
+ unsafe { &*self.as_ptr().cast() }
}
/// Return the receiver as a const raw pointer to Object.
/// This is preferrable to `as_object_mut_ptr()` if a C
/// function only needs a `const Object *`.
- fn as_object_ptr(&self) -> *const Object {
- self.as_ptr().cast()
+ fn as_object_ptr(&self) -> *const bindings::Object {
+ self.as_object().as_ptr()
}
/// Return the receiver as a mutable raw pointer to Object.
@@ -297,8 +305,8 @@ pub unsafe trait ObjectType: Sized {
/// This cast is always safe, but because the result is mutable
/// and the incoming reference is not, this should only be used
/// for calls to C functions, and only if needed.
- unsafe fn as_object_mut_ptr(&self) -> *mut Object {
- self.as_object_ptr() as *mut _
+ unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object {
+ self.as_object().as_mut_ptr()
}
}
@@ -455,90 +463,7 @@ where
impl<T: ObjectType> ObjectDeref for &T {}
impl<T: ObjectType> ObjectCast for &T {}
-/// Trait for mutable type casting operations in the QOM hierarchy.
-///
-/// This trait provides the mutable counterparts to [`ObjectCast`]'s conversion
-/// functions. Unlike `ObjectCast`, this trait returns `Result` for fallible
-/// conversions to preserve the original smart pointer if the cast fails. This
-/// is necessary because mutable references cannot be copied, so a failed cast
-/// must return ownership of the original reference. For example:
-///
-/// ```ignore
-/// let mut dev = get_device();
-/// // If this fails, we need the original `dev` back to try something else
-/// match dev.dynamic_cast_mut::<FooDevice>() {
-/// Ok(foodev) => /* use foodev */,
-/// Err(dev) => /* still have ownership of dev */
-/// }
-/// ```
-pub trait ObjectCastMut: Sized + ObjectDeref + DerefMut
-where
- Self::Target: ObjectType,
-{
- /// Safely convert from a derived type to one of its parent types.
- ///
- /// This is always safe; the [`IsA`] trait provides static verification
- /// that `Self` dereferences to `U` or a child of `U`.
- fn upcast_mut<'a, U: ObjectType>(self) -> &'a mut U
- where
- Self::Target: IsA<U>,
- Self: 'a,
- {
- // SAFETY: soundness is declared via IsA<U>, which is an unsafe trait
- unsafe { self.unsafe_cast_mut::<U>() }
- }
-
- /// Attempt to convert to a derived type.
- ///
- /// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the
- /// object if the conversion failed. This is verified at runtime by
- /// checking the object's type information.
- fn downcast_mut<'a, U: IsA<Self::Target>>(self) -> Result<&'a mut U, Self>
- where
- Self: 'a,
- {
- self.dynamic_cast_mut::<U>()
- }
-
- /// Attempt to convert between any two types in the QOM hierarchy.
- ///
- /// Returns `Ok(..)` if the object is of type `U`, or `Err(self)` if the
- /// object if the conversion failed. This is verified at runtime by
- /// checking the object's type information.
- fn dynamic_cast_mut<'a, U: ObjectType>(self) -> Result<&'a mut U, Self>
- where
- Self: 'a,
- {
- unsafe {
- // SAFETY: upcasting to Object is always valid, and the
- // return type is either NULL or the argument itself
- let result: *mut U =
- object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast();
-
- result.as_mut().ok_or(self)
- }
- }
-
- /// Convert to any QOM type without verification.
- ///
- /// # Safety
- ///
- /// What safety? You need to know yourself that the cast is correct; only
- /// use when performance is paramount. It is still better than a raw
- /// pointer `cast()`, which does not even check that you remain in the
- /// realm of QOM `ObjectType`s.
- ///
- /// `unsafe_cast::<Object>()` is always safe.
- unsafe fn unsafe_cast_mut<'a, U: ObjectType>(self) -> &'a mut U
- where
- Self: 'a,
- {
- unsafe { &mut *self.as_mut_ptr::<Self::Target>().cast::<U>() }
- }
-}
-
impl<T: ObjectType> ObjectDeref for &mut T {}
-impl<T: ObjectType> ObjectCastMut for &mut T {}
/// Trait a type must implement to be registered with QEMU.
pub trait ObjectImpl: ObjectType + IsA<Object> {
@@ -621,7 +546,7 @@ pub trait ObjectImpl: ObjectType + IsA<Object> {
/// We expect the FFI user of this function to pass a valid pointer that
/// can be downcasted to type `T`. We also expect the device is
/// readable/writeable from one thread at any time.
-unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) {
+unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut bindings::Object) {
let state = NonNull::new(dev).unwrap().cast::<T>();
T::UNPARENT.unwrap()(unsafe { state.as_ref() });
}
@@ -796,8 +721,9 @@ pub trait ObjectClassMethods: IsA<Object> {
// SAFETY: the object created by object_new is allocated on
// the heap and has a reference count of 1
unsafe {
- let obj = &*object_new(Self::TYPE_NAME.as_ptr());
- Owned::from_raw(obj.unsafe_cast::<Self>())
+ let raw_obj = object_new(Self::TYPE_NAME.as_ptr());
+ let obj = Object::from_raw(raw_obj).unsafe_cast::<Self>();
+ Owned::from_raw(obj)
}
}
}
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
index 04821a2..e92502a 100644
--- a/rust/qemu-api/src/sysbus.rs
+++ b/rust/qemu-api/src/sysbus.rs
@@ -6,11 +6,11 @@
use std::{ffi::CStr, ptr::addr_of_mut};
-pub use bindings::{SysBusDevice, SysBusDeviceClass};
+pub use bindings::SysBusDeviceClass;
use crate::{
bindings,
- cell::bql_locked,
+ cell::{bql_locked, Opaque},
irq::{IRQState, InterruptSource},
memory::MemoryRegion,
prelude::*,
@@ -18,6 +18,14 @@ use crate::{
qom::Owned,
};
+/// A safe wrapper around [`bindings::SysBusDevice`].
+#[repr(transparent)]
+#[derive(Debug, qemu_api_macros::Wrapper)]
+pub struct SysBusDevice(Opaque<bindings::SysBusDevice>);
+
+unsafe impl Send for SysBusDevice {}
+unsafe impl Sync for SysBusDevice {}
+
unsafe impl ObjectType for SysBusDevice {
type Class = SysBusDeviceClass;
const TYPE_NAME: &'static CStr =
@@ -49,7 +57,7 @@ where
fn init_mmio(&self, iomem: &MemoryRegion) {
assert!(bql_locked());
unsafe {
- bindings::sysbus_init_mmio(self.as_mut_ptr(), iomem.as_mut_ptr());
+ bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr());
}
}
@@ -60,7 +68,21 @@ where
fn init_irq(&self, irq: &InterruptSource) {
assert!(bql_locked());
unsafe {
- bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
+ bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr());
+ }
+ }
+
+ // TODO: do we want a type like GuestAddress here?
+ fn mmio_addr(&self, id: u32) -> Option<u64> {
+ assert!(bql_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() };
+ let id: usize = id.try_into().unwrap();
+ if sbd.mmio[id].memory.is_null() {
+ None
+ } else {
+ Some(sbd.mmio[id].addr)
}
}
@@ -69,7 +91,7 @@ where
assert!(bql_locked());
let id: i32 = id.try_into().unwrap();
unsafe {
- bindings::sysbus_mmio_map(self.as_mut_ptr(), id, addr);
+ bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr);
}
}
@@ -79,8 +101,9 @@ where
fn connect_irq(&self, id: u32, irq: &Owned<IRQState>) {
assert!(bql_locked());
let id: i32 = id.try_into().unwrap();
+ let irq: &IRQState = irq;
unsafe {
- bindings::sysbus_connect_irq(self.as_mut_ptr(), id, irq.as_mut_ptr());
+ bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr());
}
}
@@ -88,7 +111,10 @@ where
// TODO: return an Error
assert!(bql_locked());
unsafe {
- bindings::sysbus_realize(self.as_mut_ptr(), addr_of_mut!(bindings::error_fatal));
+ bindings::sysbus_realize(
+ self.upcast().as_mut_ptr(),
+ addr_of_mut!(bindings::error_fatal),
+ );
}
}
}
diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs
index a593538..f0b04ef 100644
--- a/rust/qemu-api/src/timer.rs
+++ b/rust/qemu-api/src/timer.rs
@@ -2,31 +2,51 @@
// Author(s): Zhao Liu <zhai1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::os::raw::{c_int, c_void};
+use std::{
+ os::raw::{c_int, c_void},
+ pin::Pin,
+};
use crate::{
bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType},
callbacks::FnCall,
+ cell::Opaque,
};
-pub type Timer = bindings::QEMUTimer;
-pub type TimerListGroup = bindings::QEMUTimerListGroup;
+/// A safe wrapper around [`bindings::QEMUTimer`].
+#[repr(transparent)]
+#[derive(Debug, qemu_api_macros::Wrapper)]
+pub struct Timer(Opaque<bindings::QEMUTimer>);
+
+unsafe impl Send for Timer {}
+unsafe impl Sync for Timer {}
+
+#[repr(transparent)]
+#[derive(qemu_api_macros::Wrapper)]
+pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>);
+
+unsafe impl Send for TimerListGroup {}
+unsafe impl Sync for TimerListGroup {}
impl Timer {
pub const MS: u32 = bindings::SCALE_MS;
pub const US: u32 = bindings::SCALE_US;
pub const NS: u32 = bindings::SCALE_NS;
- pub fn new() -> Self {
- Default::default()
- }
-
- const fn as_mut_ptr(&self) -> *mut Self {
- self as *const Timer as *mut _
+ /// Create a `Timer` struct without initializing it.
+ ///
+ /// # Safety
+ ///
+ /// The timer must be initialized before it is armed with
+ /// [`modify`](Self::modify).
+ pub unsafe fn new() -> Self {
+ // SAFETY: requirements relayed to callers of Timer::new
+ Self(unsafe { Opaque::zeroed() })
}
+ /// Create a new timer with the given attributes.
pub fn init_full<'timer, 'opaque: 'timer, T, F>(
- &'timer mut self,
+ self: Pin<&'timer mut Self>,
timer_list_group: Option<&TimerListGroup>,
clk_type: ClockType,
scale: u32,
@@ -51,7 +71,7 @@ impl Timer {
// SAFETY: the opaque outlives the timer
unsafe {
timer_init_full(
- self,
+ self.as_mut_ptr(),
if let Some(g) = timer_list_group {
g as *const TimerListGroup as *mut _
} else {
@@ -67,14 +87,19 @@ impl Timer {
}
pub fn modify(&self, expire_time: u64) {
+ // SAFETY: the only way to obtain a Timer safely is via methods that
+ // take a Pin<&mut Self>, therefore the timer is pinned
unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) }
}
pub fn delete(&self) {
+ // SAFETY: the only way to obtain a Timer safely is via methods that
+ // take a Pin<&mut Self>, therefore the timer is pinned
unsafe { timer_del(self.as_mut_ptr()) }
}
}
+// FIXME: use something like PinnedDrop from the pinned_init crate
impl Drop for Timer {
fn drop(&mut self) {
self.delete()
diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs
index 24a4dc8..f0510ae 100644
--- a/rust/qemu-api/src/vmstate.rs
+++ b/rust/qemu-api/src/vmstate.rs
@@ -330,6 +330,7 @@ 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);
@@ -469,7 +470,7 @@ macro_rules! vmstate_clock {
$crate::assert_field_type!(
$struct_name,
$field_name,
- $crate::qom::Owned<$crate::bindings::Clock>
+ $crate::qom::Owned<$crate::qdev::Clock>
);
$crate::offset_of!($struct_name, $field_name)
},
diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs
index 47b6977..a3415a2 100644
--- a/rust/qemu-api/src/zeroable.rs
+++ b/rust/qemu-api/src/zeroable.rs
@@ -106,3 +106,4 @@ impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1);
impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2);
impl_zeroable!(crate::bindings::MemoryRegionOps);
impl_zeroable!(crate::bindings::MemTxAttrs);
+impl_zeroable!(crate::bindings::CharBackend);
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index e398578..269122e 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -2,13 +2,10 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::{
- ffi::{c_void, CStr},
- ptr::{addr_of, addr_of_mut},
-};
+use std::{ffi::CStr, ptr::addr_of};
use qemu_api::{
- bindings::{module_call_init, module_init_type, object_new, object_unref, qdev_prop_bool},
+ bindings::{module_call_init, module_init_type, qdev_prop_bool},
c_str,
cell::{self, BqlCell},
declare_properties, define_property,
@@ -182,30 +179,3 @@ fn test_cast() {
assert_eq!(addr_of!(*sbd_ref), p_ptr.cast());
}
}
-
-#[test]
-#[allow(clippy::shadow_unrelated)]
-/// Test casts on mutable references.
-fn test_cast_mut() {
- init_qom();
- let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
-
- let p_ref: &mut DummyState = unsafe { &mut *p };
- let obj_ref: &mut Object = p_ref.upcast_mut();
- assert_eq!(addr_of_mut!(*obj_ref), p.cast());
-
- let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut();
- let obj_ref = sbd_ref.unwrap_err();
-
- let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut();
- let dev_ref = dev_ref.unwrap();
- assert_eq!(addr_of_mut!(*dev_ref), p.cast());
-
- // SAFETY: the cast is wrong, but the value is only used for comparison
- unsafe {
- let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut();
- assert_eq!(addr_of_mut!(*sbd_ref), p.cast());
-
- object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
- }
-}
diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py
index 4814a8f..a3e2247 100644
--- a/scripts/meson-buildoptions.py
+++ b/scripts/meson-buildoptions.py
@@ -241,8 +241,14 @@ def print_parse(options):
print(" esac")
print("}")
-
-options = load_options(json.load(sys.stdin))
+json_data = sys.stdin.read()
+try:
+ options = load_options(json.loads(json_data))
+except:
+ print("Failure in scripts/meson-buildoptions.py parsing stdin as json",
+ file=sys.stderr)
+ print(json_data, file=sys.stderr)
+ sys.exit(1)
print("# This file is generated by meson-buildoptions.py, do not edit!")
print_help(options)
print_parse(options)
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 3e8e008..aca6e68 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -504,6 +504,8 @@ _meson_option_parse() {
--disable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=false ;;
--enable-strip) printf "%s" -Dstrip=true ;;
--disable-strip) printf "%s" -Dstrip=false ;;
+ --enable-split-debug) printf "%s" -Dsplit_debug=true ;;
+ --disable-split-debug) printf "%s" -Dsplit_debug=false ;;
--sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;;
--enable-tcg) printf "%s" -Dtcg=enabled ;;
--disable-tcg) printf "%s" -Dtcg=disabled ;;