mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-01 23:03:54 -06:00
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:
parent
a22bd55ffd
commit
7630ca2a70
2 changed files with 49 additions and 22 deletions
|
@ -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)]
|
||||||
|
|
|
@ -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> {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue