* [PATCH v2 0/2] rust: safe wrappers for interrupt sources
@ 2024-12-02 11:06 Paolo Bonzini
2024-12-02 11:06 ` [PATCH v2 1/2] rust: add BQL-enforcing Cell variant Paolo Bonzini
2024-12-02 11:06 ` [PATCH v2 2/2] rust: add bindings for interrupt sources Paolo Bonzini
0 siblings, 2 replies; 5+ messages in thread
From: Paolo Bonzini @ 2024-12-02 11:06 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust
Interrupt sources, qemu_irq in C code, are pointers to IRQState objects.
They are QOM link properties and can be written to outside the control
of the device (i.e. from a shared reference); therefore their Rust
representation must be an interior-mutable field.
This make interrupt sources similar to a Cell<*mut IRQState>. However,
a Cell can only live within one thread, while here the semantics are
"accessible by multiple threads but only under the Big QEMU Lock".
Therefore, this series adds to QEMU a specialized cell type that checks
locking rules with respect to the "Big QEMU Lock". In particular,
qemu_api::cell::BqlCell only allows get()/set() under BQL protection and
therefore is Send/Sync. The code for BqlCell is a bit long but most of
it is lifted from the standard library and almost half is documentation,
including doctests.
Likewise, qemu_api::cell::RefCell would be a RefCell that is Send/Sync,
because it checks that borrow()/borrow_mut() is only done under BQL.
This is not added here because there is no use case (yet), but Zhao
is going to use it for his HPET implementation.
I am not fully satisfied with the solution I used for mocking the BQL;
I have a prototype that runs doctests from "meson test" but that may be
better left to Meson itself.
Paolo
v1->v2:
- change debug_assert to assert
- clarify meaning of active-high
- allow declaring vectored interrupt source
- fix documentation wrt threading rules
Paolo Bonzini (2):
rust: add BQL-enforcing Cell variant
rust: add bindings for interrupt sources
rust/hw/char/pl011/src/device.rs | 22 +--
rust/qemu-api/meson.build | 3 +
rust/qemu-api/src/cell.rs | 298 +++++++++++++++++++++++++++++++
rust/qemu-api/src/irq.rs | 91 ++++++++++
rust/qemu-api/src/lib.rs | 3 +
rust/qemu-api/src/sysbus.rs | 26 +++
6 files changed, 433 insertions(+), 10 deletions(-)
create mode 100644 rust/qemu-api/src/cell.rs
create mode 100644 rust/qemu-api/src/irq.rs
create mode 100644 rust/qemu-api/src/sysbus.rs
--
2.47.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] rust: add BQL-enforcing Cell variant
2024-12-02 11:06 [PATCH v2 0/2] rust: safe wrappers for interrupt sources Paolo Bonzini
@ 2024-12-02 11:06 ` Paolo Bonzini
2024-12-03 1:01 ` Junjie Mao
2024-12-02 11:06 ` [PATCH v2 2/2] rust: add bindings for interrupt sources Paolo Bonzini
1 sibling, 1 reply; 5+ messages in thread
From: Paolo Bonzini @ 2024-12-02 11:06 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust
QEMU objects usually have their pointer shared with the "outside
world" very early in their lifetime, for example when they create their
MemoryRegions. Because at this point it is not valid anymore to
create a &mut reference to the device, individual parts of the
device struct must be made mutable in a controlled manner.
QEMU's Big Lock (BQL) effectively turns multi-threaded code into
single-threaded code while device code runs, as long as the BQL is not
released while the device is borrowed (because C code could sneak in and
mutate the device). We can then introduce custom interior mutability primitives
that are semantically similar to the standard library's (single-threaded)
Cell and RefCell, but account for QEMU's threading model. Accessing
the "BqlCell" or borrowing the "BqlRefCell" requires proving that the
BQL is held, and attempting to access without the BQL is a runtime panic,
similar to RefCell's already-borrowed panic.
With respect to naming I also considered omitting the "Bql" prefix or
moving it to the module, e.g. qemu_api::bql::{Cell, RefCell}. However,
this could easily lead to mistakes and confusion; for example rustc could
suggest the wrong import, leading to subtle bugs.
As a start introduce the an equivalent of Cell. Almost all of the code
was taken from Rust's standard library, while removing unstable features
and probably-unnecessary functionality that constitute a large of the
original code. A lot of what's left is documentation, as well as unit
tests in the form of doctests. These are not yet integrated in "make
check" but can be run with "cargo test --doc".
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/qemu-api/meson.build | 1 +
rust/qemu-api/src/cell.rs | 298 ++++++++++++++++++++++++++++++++++++++
rust/qemu-api/src/lib.rs | 1 +
3 files changed, 300 insertions(+)
create mode 100644 rust/qemu-api/src/cell.rs
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index d719c13f46d..edc21e1a3f8 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -13,6 +13,7 @@ _qemu_api_rs = static_library(
[
'src/lib.rs',
'src/bindings.rs',
+ 'src/cell.rs',
'src/c_str.rs',
'src/definitions.rs',
'src/device_class.rs',
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
new file mode 100644
index 00000000000..373be23ba8b
--- /dev/null
+++ b/rust/qemu-api/src/cell.rs
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: MIT
+//
+// This file is based on library/core/src/cell.rs from
+// Rust 1.82.0.
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and associated
+// documentation files (the "Software"), to deal in the
+// Software without restriction, including without
+// limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+//! BQL-protected mutable containers.
+//!
+//! Rust memory safety is based on this rule: Given an object `T`, it is only
+//! possible to have one of the following:
+//!
+//! - Having several immutable references (`&T`) to the object (also known as
+//! **aliasing**).
+//! - Having one mutable reference (`&mut T`) to the object (also known as
+//! **mutability**).
+//!
+//! This is enforced by the Rust compiler. However, there are situations where
+//! this rule is not flexible enough. Sometimes it is required to have multiple
+//! references to an object and yet mutate it. In particular, QEMU objects
+//! 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.
+//!
+//! This module provides a way to do so via the Big QEMU Lock. While
+//! [`BqlCell<T>`] is essentially the same single-threaded primitive that is
+//! available in `std::cell`, the BQL allows it to be used from a multi-threaded
+//! context and to share references across threads, while maintaining Rust's
+//! safety guarantees. For this reason, unlike its `std::cell` counterpart,
+//! `BqlCell` implements the `Sync` trait.
+//!
+//! BQL checks are performed in debug builds but can be optimized away in
+//! release builds, providing runtime safety during development with no overhead
+//! in production.
+//!
+//! Warning: While `BqlCell` is similar to its `std::cell` counterpart, the two
+//! are not interchangeable. Using `std::cell` types in QEMU device
+//! implementations is usually incorrect and can lead to thread-safety issues.
+//!
+//! ## `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
+//! long as the cell is shared. The value itself cannot be directly obtained
+//! without copying it, cloning it, or replacing it with something else. This
+//! type provides the following methods, all of which can be called only while
+//! the BQL is held:
+//!
+//! - For types that implement [`Copy`], the [`get`](BqlCell::get) method
+//! retrieves the current interior value by duplicating it.
+//! - For types that implement [`Default`], the [`take`](BqlCell::take) method
+//! replaces the current interior value with [`Default::default()`] and
+//! returns the replaced value.
+//! - All types have:
+//! - [`replace`](BqlCell::replace): replaces the current interior value and
+//! returns the replaced value.
+//! - [`set`](BqlCell::set): this method replaces the interior value,
+//! dropping the replaced value.
+
+use std::{cell::UnsafeCell, cmp::Ordering, fmt, mem};
+
+use crate::bindings;
+
+// TODO: When building doctests do not include the actual BQL, because cargo
+// does not know how to link them to libqemuutil. This can be fixed by
+// running rustdoc from "meson test" instead of relying on cargo.
+fn bql_locked() -> bool {
+ // SAFETY: the function does nothing but return a thread-local bool
+ !cfg!(MESON) || unsafe { bindings::bql_locked() }
+}
+
+/// A mutable memory location that is protected by the Big QEMU Lock.
+///
+/// # Memory layout
+///
+/// `BqlCell<T>` has the same in-memory representation as its inner type `T`.
+#[repr(transparent)]
+pub struct BqlCell<T> {
+ value: UnsafeCell<T>,
+}
+
+// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex,
+// except it is stored out-of-line
+unsafe impl<T: Send> Send for BqlCell<T> {}
+unsafe impl<T: Send> Sync for BqlCell<T> {}
+
+impl<T: Copy> Clone for BqlCell<T> {
+ #[inline]
+ fn clone(&self) -> BqlCell<T> {
+ BqlCell::new(self.get())
+ }
+}
+
+impl<T: Default> Default for BqlCell<T> {
+ /// Creates a `BqlCell<T>`, with the `Default` value for T.
+ #[inline]
+ fn default() -> BqlCell<T> {
+ BqlCell::new(Default::default())
+ }
+}
+
+impl<T: PartialEq + Copy> PartialEq for BqlCell<T> {
+ #[inline]
+ fn eq(&self, other: &BqlCell<T>) -> bool {
+ self.get() == other.get()
+ }
+}
+
+impl<T: Eq + Copy> Eq for BqlCell<T> {}
+
+impl<T: PartialOrd + Copy> PartialOrd for BqlCell<T> {
+ #[inline]
+ fn partial_cmp(&self, other: &BqlCell<T>) -> Option<Ordering> {
+ self.get().partial_cmp(&other.get())
+ }
+}
+
+impl<T: Ord + Copy> Ord for BqlCell<T> {
+ #[inline]
+ fn cmp(&self, other: &BqlCell<T>) -> Ordering {
+ self.get().cmp(&other.get())
+ }
+}
+
+impl<T> From<T> for BqlCell<T> {
+ /// Creates a new `BqlCell<T>` containing the given value.
+ fn from(t: T) -> BqlCell<T> {
+ BqlCell::new(t)
+ }
+}
+
+impl<T: fmt::Debug + Copy> fmt::Debug for BqlCell<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.get().fmt(f)
+ }
+}
+
+impl<T: fmt::Display + Copy> fmt::Display for BqlCell<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.get().fmt(f)
+ }
+}
+
+impl<T> BqlCell<T> {
+ /// Creates a new `BqlCell` containing the given value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ /// ```
+ #[inline]
+ pub const fn new(value: T) -> BqlCell<T> {
+ BqlCell {
+ value: UnsafeCell::new(value),
+ }
+ }
+
+ /// Sets the contained value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ ///
+ /// c.set(10);
+ /// ```
+ #[inline]
+ pub fn set(&self, val: T) {
+ self.replace(val);
+ }
+
+ /// Replaces the contained value with `val`, and returns the old contained
+ /// value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let cell = BqlCell::new(5);
+ /// assert_eq!(cell.get(), 5);
+ /// assert_eq!(cell.replace(10), 5);
+ /// assert_eq!(cell.get(), 10);
+ /// ```
+ #[inline]
+ pub fn replace(&self, val: T) -> T {
+ assert!(bql_locked());
+ // SAFETY: This can cause data races if called from multiple threads,
+ // but it won't happen as long as C code accesses the value
+ // under BQL protection only.
+ mem::replace(unsafe { &mut *self.value.get() }, val)
+ }
+
+ /// Unwraps the value, consuming the cell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ /// let five = c.into_inner();
+ ///
+ /// assert_eq!(five, 5);
+ /// ```
+ pub fn into_inner(self) -> T {
+ assert!(bql_locked());
+ self.value.into_inner()
+ }
+}
+
+impl<T: Copy> BqlCell<T> {
+ /// Returns a copy of the contained value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ ///
+ /// let five = c.get();
+ /// ```
+ #[inline]
+ pub fn get(&self) -> T {
+ assert!(bql_locked());
+ // SAFETY: This can cause data races if called from multiple threads,
+ // but it won't happen as long as C code accesses the value
+ // under BQL protection only.
+ unsafe { *self.value.get() }
+ }
+}
+
+impl<T> BqlCell<T> {
+ /// Returns a raw pointer to the underlying data in this cell.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ ///
+ /// let ptr = c.as_ptr();
+ /// ```
+ #[inline]
+ pub const fn as_ptr(&self) -> *mut T {
+ self.value.get()
+ }
+}
+
+impl<T: Default> BqlCell<T> {
+ /// Takes the value of the cell, leaving `Default::default()` in its place.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use qemu_api::cell::BqlCell;
+ ///
+ /// let c = BqlCell::new(5);
+ /// let five = c.take();
+ ///
+ /// assert_eq!(five, 5);
+ /// assert_eq!(c.into_inner(), 0);
+ /// ```
+ pub fn take(&self) -> T {
+ self.replace(Default::default())
+ }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 440aff3817d..b04d110b3f5 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -8,6 +8,7 @@
pub mod bindings;
pub mod c_str;
+pub mod cell;
pub mod definitions;
pub mod device_class;
pub mod offset_of;
--
2.47.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] rust: add bindings for interrupt sources
2024-12-02 11:06 [PATCH v2 0/2] rust: safe wrappers for interrupt sources Paolo Bonzini
2024-12-02 11:06 ` [PATCH v2 1/2] rust: add BQL-enforcing Cell variant Paolo Bonzini
@ 2024-12-02 11:06 ` Paolo Bonzini
2024-12-03 15:47 ` Zhao Liu
1 sibling, 1 reply; 5+ messages in thread
From: Paolo Bonzini @ 2024-12-02 11:06 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-rust
The InterruptSource bindings let us call qemu_set_irq() and sysbus_init_irq()
as safe code.
Interrupt sources, qemu_irq in C code, are pointers to IRQState objects.
They are QOM link properties and can be written to outside the control
of the device (i.e. from a shared reference); therefore they must be
interior-mutable in Rust. Since thread-safety is provided by the BQL,
what we want here is the newly-introduced BqlCell. A pointer to the
contents of the BqlCell (an IRQState**, or equivalently qemu_irq*)
is then passed to the C sysbus_init_irq function.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/char/pl011/src/device.rs | 22 ++++----
rust/qemu-api/meson.build | 2 +
rust/qemu-api/src/irq.rs | 91 ++++++++++++++++++++++++++++++++
rust/qemu-api/src/lib.rs | 2 +
rust/qemu-api/src/sysbus.rs | 26 +++++++++
5 files changed, 133 insertions(+), 10 deletions(-)
create mode 100644 rust/qemu-api/src/irq.rs
create mode 100644 rust/qemu-api/src/sysbus.rs
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 317a9b3c5ad..c5c8c463d37 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -13,6 +13,7 @@
c_str,
definitions::ObjectImpl,
device_class::TYPE_SYS_BUS_DEVICE,
+ irq::InterruptSource,
};
use crate::{
@@ -94,7 +95,7 @@ pub struct PL011State {
/// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
/// ```
#[doc(alias = "irq")]
- pub interrupts: [qemu_irq; 6usize],
+ pub interrupts: [InterruptSource; IRQMASK.len()],
#[doc(alias = "clk")]
pub clock: NonNull<Clock>,
#[doc(alias = "migrate_clk")]
@@ -139,7 +140,8 @@ impl PL011State {
unsafe fn init(&mut self) {
const CLK_NAME: &CStr = c_str!("clk");
- let dev = addr_of_mut!(*self).cast::<DeviceState>();
+ let sbd = unsafe { &mut *(addr_of_mut!(*self).cast::<SysBusDevice>()) };
+
// SAFETY:
//
// self and self.iomem are guaranteed to be valid at this point since callers
@@ -153,12 +155,15 @@ unsafe fn init(&mut self) {
Self::TYPE_INFO.name,
0x1000,
);
- let sbd = addr_of_mut!(*self).cast::<SysBusDevice>();
sysbus_init_mmio(sbd, addr_of_mut!(self.iomem));
- for irq in self.interrupts.iter_mut() {
- sysbus_init_irq(sbd, irq);
- }
}
+
+ for irq in self.interrupts.iter() {
+ sbd.init_irq(irq);
+ }
+
+ let dev = addr_of_mut!(*self).cast::<DeviceState>();
+
// SAFETY:
//
// self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
@@ -498,10 +503,7 @@ pub fn put_fifo(&mut self, value: c_uint) {
pub fn update(&self) {
let flags = self.int_level & self.int_enabled;
for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
- // SAFETY: self.interrupts have been initialized in init().
- unsafe {
- qemu_set_irq(*irq, i32::from(flags & i != 0));
- }
+ irq.set(flags & i != 0);
}
}
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index edc21e1a3f8..973cfbcfb4a 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -17,7 +17,9 @@ _qemu_api_rs = static_library(
'src/c_str.rs',
'src/definitions.rs',
'src/device_class.rs',
+ 'src/irq.rs',
'src/offset_of.rs',
+ 'src/sysbus.rs',
'src/vmstate.rs',
'src/zeroable.rs',
],
diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs
new file mode 100644
index 00000000000..011dbcf3dba
--- /dev/null
+++ b/rust/qemu-api/src/irq.rs
@@ -0,0 +1,91 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Bindings for interrupt sources
+
+use core::ptr;
+use std::{marker::PhantomData, os::raw::c_int};
+
+use crate::{
+ bindings::{qemu_set_irq, IRQState},
+ cell::BqlCell,
+};
+
+/// 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.
+///
+/// As far as devices are concerned, interrupt sources are always active-high:
+/// for example, `InterruptSource<bool>`'s [`raise`](InterruptSource::raise)
+/// 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.
+///
+/// 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
+/// [`SysBusDevice::init_irq`](crate::sysbus::SysBusDevice::init_irq), and
+/// initially leaves the pointer to a NULL value, representing an unconnected
+/// interrupt. To connect it, whoever creates the device fills the pointer with
+/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because
+/// devices are generally shared objects, interrupt sources are an example of
+/// the interior mutability pattern.
+///
+/// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell`
+/// allows access from whatever thread has it.
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct InterruptSource<T = bool>
+where
+ c_int: From<T>,
+{
+ cell: BqlCell<*mut IRQState>,
+ _marker: PhantomData<T>,
+}
+
+impl InterruptSource<bool> {
+ /// Send a low (`false`) value to the interrupt sink.
+ pub fn lower(&self) {
+ self.set(false);
+ }
+
+ /// Send a high-low pulse to the interrupt sink.
+ pub fn pulse(&self) {
+ self.set(true);
+ self.set(false);
+ }
+
+ /// Send a high (`true`) value to the interrupt sink.
+ pub fn raise(&self) {
+ self.set(true);
+ }
+}
+
+impl<T> InterruptSource<T>
+where
+ c_int: From<T>,
+{
+ /// Send `level` to the interrupt sink.
+ pub fn set(&self, level: T) {
+ let ptr = self.cell.get();
+ // SAFETY: the pointer is retrieved under the BQL and remains valid
+ // until the BQL is released, which is after qemu_set_irq() is entered.
+ unsafe {
+ qemu_set_irq(ptr, level.into());
+ }
+ }
+
+ pub(crate) const fn as_ptr(&self) -> *mut *mut IRQState {
+ self.cell.as_ptr()
+ }
+}
+
+impl Default for InterruptSource {
+ fn default() -> Self {
+ InterruptSource {
+ cell: BqlCell::new(ptr::null_mut()),
+ _marker: PhantomData,
+ }
+ }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index b04d110b3f5..aa692939688 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -11,7 +11,9 @@
pub mod cell;
pub mod definitions;
pub mod device_class;
+pub mod irq;
pub mod offset_of;
+pub mod sysbus;
pub mod vmstate;
pub mod zeroable;
diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs
new file mode 100644
index 00000000000..1a9b8a1f971
--- /dev/null
+++ b/rust/qemu-api/src/sysbus.rs
@@ -0,0 +1,26 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+use std::ptr::addr_of;
+
+pub use bindings::{SysBusDevice, SysBusDeviceClass};
+
+use crate::{bindings, irq::InterruptSource};
+
+impl SysBusDevice {
+ /// Return `self` cast to a mutable pointer, for use in calls to C code.
+ const fn as_mut_ptr(&self) -> *mut SysBusDevice {
+ addr_of!(*self) as *mut _
+ }
+
+ /// Expose an interrupt source outside the device as a qdev GPIO output.
+ /// Note that the ordering of calls to `init_irq` is important, since
+ /// whoever creates the sysbus device will refer to the interrupts with
+ /// a number that corresponds to the order of calls to `init_irq`.
+ pub fn init_irq(&self, irq: &InterruptSource) {
+ unsafe {
+ bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
+ }
+ }
+}
--
2.47.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] rust: add BQL-enforcing Cell variant
2024-12-02 11:06 ` [PATCH v2 1/2] rust: add BQL-enforcing Cell variant Paolo Bonzini
@ 2024-12-03 1:01 ` Junjie Mao
0 siblings, 0 replies; 5+ messages in thread
From: Junjie Mao @ 2024-12-03 1:01 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust
Paolo Bonzini <pbonzini@redhat.com> writes:
> QEMU objects usually have their pointer shared with the "outside
> world" very early in their lifetime, for example when they create their
> MemoryRegions. Because at this point it is not valid anymore to
> create a &mut reference to the device, individual parts of the
> device struct must be made mutable in a controlled manner.
>
> QEMU's Big Lock (BQL) effectively turns multi-threaded code into
> single-threaded code while device code runs, as long as the BQL is not
> released while the device is borrowed (because C code could sneak in and
> mutate the device). We can then introduce custom interior mutability primitives
> that are semantically similar to the standard library's (single-threaded)
> Cell and RefCell, but account for QEMU's threading model. Accessing
> the "BqlCell" or borrowing the "BqlRefCell" requires proving that the
> BQL is held, and attempting to access without the BQL is a runtime panic,
> similar to RefCell's already-borrowed panic.
>
> With respect to naming I also considered omitting the "Bql" prefix or
> moving it to the module, e.g. qemu_api::bql::{Cell, RefCell}. However,
> this could easily lead to mistakes and confusion; for example rustc could
> suggest the wrong import, leading to subtle bugs.
>
> As a start introduce the an equivalent of Cell. Almost all of the code
> was taken from Rust's standard library, while removing unstable features
> and probably-unnecessary functionality that constitute a large of the
> original code. A lot of what's left is documentation, as well as unit
> tests in the form of doctests. These are not yet integrated in "make
> check" but can be run with "cargo test --doc".
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Junjie Mao <junjie.mao@hotmail.com>
--
Best Regards
Junjie Mao
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] rust: add bindings for interrupt sources
2024-12-02 11:06 ` [PATCH v2 2/2] rust: add bindings for interrupt sources Paolo Bonzini
@ 2024-12-03 15:47 ` Zhao Liu
0 siblings, 0 replies; 5+ messages in thread
From: Zhao Liu @ 2024-12-03 15:47 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel, qemu-rust
On Mon, Dec 02, 2024 at 12:06:09PM +0100, Paolo Bonzini wrote:
> Date: Mon, 2 Dec 2024 12:06:09 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH v2 2/2] rust: add bindings for interrupt sources
> X-Mailer: git-send-email 2.47.0
>
> The InterruptSource bindings let us call qemu_set_irq() and sysbus_init_irq()
> as safe code.
>
> Interrupt sources, qemu_irq in C code, are pointers to IRQState objects.
> They are QOM link properties and can be written to outside the control
> of the device (i.e. from a shared reference); therefore they must be
> interior-mutable in Rust. Since thread-safety is provided by the BQL,
> what we want here is the newly-introduced BqlCell. A pointer to the
> contents of the BqlCell (an IRQState**, or equivalently qemu_irq*)
> is then passed to the C sysbus_init_irq function.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/hw/char/pl011/src/device.rs | 22 ++++----
> rust/qemu-api/meson.build | 2 +
> rust/qemu-api/src/irq.rs | 91 ++++++++++++++++++++++++++++++++
> rust/qemu-api/src/lib.rs | 2 +
> rust/qemu-api/src/sysbus.rs | 26 +++++++++
> 5 files changed, 133 insertions(+), 10 deletions(-)
> create mode 100644 rust/qemu-api/src/irq.rs
> create mode 100644 rust/qemu-api/src/sysbus.rs
>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2024-12-03 15:30 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-02 11:06 [PATCH v2 0/2] rust: safe wrappers for interrupt sources Paolo Bonzini
2024-12-02 11:06 ` [PATCH v2 1/2] rust: add BQL-enforcing Cell variant Paolo Bonzini
2024-12-03 1:01 ` Junjie Mao
2024-12-02 11:06 ` [PATCH v2 2/2] rust: add bindings for interrupt sources Paolo Bonzini
2024-12-03 15:47 ` Zhao Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).