rust: pl011: convert pl011_create to safe Rust

Not a major change but, as a small but significant step in creating
qdev bindings, show how pl011_create can be written without "unsafe"
calls (apart from converting pointers to references).

This also provides a starting point for creating Error** bindings.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2025-02-10 16:11:58 +01:00
parent a22bd55ffd
commit 7630ca2a70
2 changed files with 49 additions and 22 deletions

View file

@ -10,14 +10,12 @@ use std::{
use qemu_api::{ use qemu_api::{
bindings::{ bindings::{
error_fatal, qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers,
qemu_chr_fe_set_handlers, qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, qemu_chr_fe_write_all, CharBackend, QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK,
sysbus_mmio_map, sysbus_realize, CharBackend, QEMUChrEvent,
CHR_IOCTL_SERIAL_SET_BREAK,
}, },
chardev::Chardev, chardev::Chardev,
c_str, impl_vmstate_forward, impl_vmstate_forward,
irq::InterruptSource, irq::{IRQState, InterruptSource},
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
prelude::*, prelude::*,
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
@ -698,26 +696,27 @@ pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) {
/// # Safety /// # Safety
/// ///
/// We expect the FFI user of this function to pass a valid pointer for `chr`. /// We expect the FFI user of this function to pass a valid pointer for `chr`
/// and `irq`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn pl011_create( pub unsafe extern "C" fn pl011_create(
addr: u64, addr: u64,
irq: qemu_irq, irq: *mut IRQState,
chr: *mut Chardev, chr: *mut Chardev,
) -> *mut DeviceState { ) -> *mut DeviceState {
let pl011 = PL011State::new(); // SAFETY: The callers promise that they have owned references.
unsafe { // They do not gift them to pl011_create, so use `Owned::from`.
let dev = pl011.as_mut_ptr::<DeviceState>(); let irq = unsafe { Owned::<IRQState>::from(&*irq) };
qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr); let chr = unsafe { Owned::<Chardev>::from(&*chr) };
let sysbus = pl011.as_mut_ptr::<SysBusDevice>(); let dev = PL011State::new();
sysbus_realize(sysbus, addr_of_mut!(error_fatal)); dev.prop_set_chr("chardev", &chr);
sysbus_mmio_map(sysbus, 0, addr); dev.sysbus_realize();
sysbus_connect_irq(sysbus, 0, irq); dev.mmio_map(0, addr);
dev.connect_irq(0, &irq);
// return the pointer, which is kept alive by the QOM tree; drop owned ref // The pointer is kept alive by the QOM tree; drop the owned ref
pl011.as_mut_ptr() dev.as_mut_ptr()
}
} }
#[repr(C)] #[repr(C)]

View file

@ -2,18 +2,18 @@
// Author(s): Paolo Bonzini <pbonzini@redhat.com> // Author(s): Paolo Bonzini <pbonzini@redhat.com>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
use std::ffi::CStr; use std::{ffi::CStr, ptr::addr_of_mut};
pub use bindings::{SysBusDevice, SysBusDeviceClass}; pub use bindings::{SysBusDevice, SysBusDeviceClass};
use crate::{ use crate::{
bindings, bindings,
cell::bql_locked, cell::bql_locked,
irq::InterruptSource, irq::{IRQState, InterruptSource},
memory::MemoryRegion, memory::MemoryRegion,
prelude::*, prelude::*,
qdev::{DeviceClass, DeviceState}, qdev::{DeviceClass, DeviceState},
qom::ClassInitImpl, qom::{ClassInitImpl, Owned},
}; };
unsafe impl ObjectType for SysBusDevice { unsafe impl ObjectType for SysBusDevice {
@ -60,6 +60,34 @@ where
bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr()); bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
} }
} }
// TODO: do we want a type like GuestAddress here?
fn mmio_map(&self, id: u32, addr: u64) {
assert!(bql_locked());
let id: i32 = id.try_into().unwrap();
unsafe {
bindings::sysbus_mmio_map(self.as_mut_ptr(), id, addr);
}
}
// Owned<> is used here because sysbus_connect_irq (via
// object_property_set_link) adds a reference to the IRQState,
// which can prolong its life
fn connect_irq(&self, id: u32, irq: &Owned<IRQState>) {
assert!(bql_locked());
let id: i32 = id.try_into().unwrap();
unsafe {
bindings::sysbus_connect_irq(self.as_mut_ptr(), id, irq.as_mut_ptr());
}
}
fn sysbus_realize(&self) {
// TODO: return an Error
assert!(bql_locked());
unsafe {
bindings::sysbus_realize(self.as_mut_ptr(), addr_of_mut!(bindings::error_fatal));
}
}
} }
impl<R: ObjectDeref> SysBusDeviceMethods for R where R::Target: IsA<SysBusDevice> {} impl<R: ObjectDeref> SysBusDeviceMethods for R where R::Target: IsA<SysBusDevice> {}