mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-27 03:51:57 -06:00
rust: add PL011 device model
This commit adds a re-implementation of hw/char/pl011.c in Rust. How to build: 1. Configure a QEMU build with: --enable-system --target-list=aarch64-softmmu --enable-rust 2. Launching a VM with qemu-system-aarch64 should use the Rust version of the pl011 device Co-authored-by: Junjie Mao <junjie.mao@intel.com> Co-authored-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Junjie Mao <junjie.mao@intel.com> Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Link: https://lore.kernel.org/r/20241024-rust-round-2-v1-2-051e7a25b978@linaro.org
This commit is contained in:
parent
ca5aa28e24
commit
37fdb2f56a
37 changed files with 1906 additions and 12 deletions
599
rust/hw/char/pl011/src/device.rs
Normal file
599
rust/hw/char/pl011/src/device.rs
Normal file
|
@ -0,0 +1,599 @@
|
|||
// Copyright 2024, Linaro Limited
|
||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
use core::{
|
||||
ffi::{c_int, c_uchar, c_uint, c_void, CStr},
|
||||
ptr::{addr_of, addr_of_mut, NonNull},
|
||||
};
|
||||
|
||||
use qemu_api::{
|
||||
bindings::{self, *},
|
||||
definitions::ObjectImpl,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
memory_ops::PL011_OPS,
|
||||
registers::{self, Interrupt},
|
||||
RegisterOffset,
|
||||
};
|
||||
|
||||
static PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
|
||||
|
||||
const DATA_BREAK: u32 = 1 << 10;
|
||||
|
||||
/// QEMU sourced constant.
|
||||
pub const PL011_FIFO_DEPTH: usize = 16_usize;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, qemu_api_macros::Object)]
|
||||
/// PL011 Device Model in QEMU
|
||||
pub struct PL011State {
|
||||
pub parent_obj: SysBusDevice,
|
||||
pub iomem: MemoryRegion,
|
||||
#[doc(alias = "fr")]
|
||||
pub flags: registers::Flags,
|
||||
#[doc(alias = "lcr")]
|
||||
pub line_control: registers::LineControl,
|
||||
#[doc(alias = "rsr")]
|
||||
pub receive_status_error_clear: registers::ReceiveStatusErrorClear,
|
||||
#[doc(alias = "cr")]
|
||||
pub control: registers::Control,
|
||||
pub dmacr: u32,
|
||||
pub int_enabled: u32,
|
||||
pub int_level: u32,
|
||||
pub read_fifo: [u32; PL011_FIFO_DEPTH],
|
||||
pub ilpr: u32,
|
||||
pub ibrd: u32,
|
||||
pub fbrd: u32,
|
||||
pub ifl: u32,
|
||||
pub read_pos: usize,
|
||||
pub read_count: usize,
|
||||
pub read_trigger: usize,
|
||||
#[doc(alias = "chr")]
|
||||
pub char_backend: CharBackend,
|
||||
/// QEMU interrupts
|
||||
///
|
||||
/// ```text
|
||||
/// * sysbus MMIO region 0: device registers
|
||||
/// * sysbus IRQ 0: `UARTINTR` (combined interrupt line)
|
||||
/// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line)
|
||||
/// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line)
|
||||
/// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line)
|
||||
/// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line)
|
||||
/// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
|
||||
/// ```
|
||||
#[doc(alias = "irq")]
|
||||
pub interrupts: [qemu_irq; 6usize],
|
||||
#[doc(alias = "clk")]
|
||||
pub clock: NonNull<Clock>,
|
||||
#[doc(alias = "migrate_clk")]
|
||||
pub migrate_clock: bool,
|
||||
}
|
||||
|
||||
impl ObjectImpl for PL011State {
|
||||
type Class = PL011Class;
|
||||
const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
|
||||
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
|
||||
const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
|
||||
const ABSTRACT: bool = false;
|
||||
const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
|
||||
const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
||||
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PL011Class {
|
||||
_inner: [u8; 0],
|
||||
}
|
||||
|
||||
impl qemu_api::definitions::Class for PL011Class {
|
||||
const CLASS_INIT: Option<
|
||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void),
|
||||
> = Some(crate::device_class::pl011_class_init);
|
||||
const CLASS_BASE_INIT: Option<
|
||||
unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut core::ffi::c_void),
|
||||
> = None;
|
||||
}
|
||||
|
||||
#[used]
|
||||
pub static CLK_NAME: &CStr = c"clk";
|
||||
|
||||
impl PL011State {
|
||||
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `self` must point to a correctly sized and aligned location for the
|
||||
/// `PL011State` type. It must not be called more than once on the same
|
||||
/// location/instance. All its fields are expected to hold unitialized
|
||||
/// values with the sole exception of `parent_obj`.
|
||||
pub unsafe fn init(&mut self) {
|
||||
let dev = addr_of_mut!(*self).cast::<DeviceState>();
|
||||
// SAFETY:
|
||||
//
|
||||
// self and self.iomem are guaranteed to be valid at this point since callers
|
||||
// must make sure the `self` reference is valid.
|
||||
unsafe {
|
||||
memory_region_init_io(
|
||||
addr_of_mut!(self.iomem),
|
||||
addr_of_mut!(*self).cast::<Object>(),
|
||||
&PL011_OPS,
|
||||
addr_of_mut!(*self).cast::<c_void>(),
|
||||
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);
|
||||
}
|
||||
}
|
||||
// SAFETY:
|
||||
//
|
||||
// self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
|
||||
// we can overwrite the undefined value without side effects. This is
|
||||
// safe since all PL011State instances are created by QOM code which
|
||||
// calls this function to initialize the fields; therefore no code is
|
||||
// able to access an invalid self.clock value.
|
||||
unsafe {
|
||||
self.clock = NonNull::new(qdev_init_clock_in(
|
||||
dev,
|
||||
CLK_NAME.as_ptr(),
|
||||
None, /* pl011_clock_update */
|
||||
addr_of_mut!(*self).cast::<c_void>(),
|
||||
ClockEvent::ClockUpdate.0,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(
|
||||
&mut self,
|
||||
offset: hwaddr,
|
||||
_size: core::ffi::c_uint,
|
||||
) -> std::ops::ControlFlow<u64, u64> {
|
||||
use RegisterOffset::*;
|
||||
|
||||
std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) {
|
||||
Err(v) if (0x3f8..0x400).contains(&v) => {
|
||||
u64::from(PL011_ID_ARM[((offset - 0xfe0) >> 2) as usize])
|
||||
}
|
||||
Err(_) => {
|
||||
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
|
||||
0
|
||||
}
|
||||
Ok(DR) => {
|
||||
// s->flags &= ~PL011_FLAG_RXFF;
|
||||
self.flags.set_receive_fifo_full(false);
|
||||
let c = self.read_fifo[self.read_pos];
|
||||
if self.read_count > 0 {
|
||||
self.read_count -= 1;
|
||||
self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
|
||||
}
|
||||
if self.read_count == 0 {
|
||||
// self.flags |= PL011_FLAG_RXFE;
|
||||
self.flags.set_receive_fifo_empty(true);
|
||||
}
|
||||
if self.read_count + 1 == self.read_trigger {
|
||||
//self.int_level &= ~ INT_RX;
|
||||
self.int_level &= !registers::INT_RX;
|
||||
}
|
||||
// Update error bits.
|
||||
self.receive_status_error_clear = c.to_be_bytes()[3].into();
|
||||
self.update();
|
||||
// Must call qemu_chr_fe_accept_input, so return Continue:
|
||||
return std::ops::ControlFlow::Continue(c.into());
|
||||
}
|
||||
Ok(RSR) => u8::from(self.receive_status_error_clear).into(),
|
||||
Ok(FR) => u16::from(self.flags).into(),
|
||||
Ok(FBRD) => self.fbrd.into(),
|
||||
Ok(ILPR) => self.ilpr.into(),
|
||||
Ok(IBRD) => self.ibrd.into(),
|
||||
Ok(LCR_H) => u16::from(self.line_control).into(),
|
||||
Ok(CR) => {
|
||||
// We exercise our self-control.
|
||||
u16::from(self.control).into()
|
||||
}
|
||||
Ok(FLS) => self.ifl.into(),
|
||||
Ok(IMSC) => self.int_enabled.into(),
|
||||
Ok(RIS) => self.int_level.into(),
|
||||
Ok(MIS) => u64::from(self.int_level & self.int_enabled),
|
||||
Ok(ICR) => {
|
||||
// "The UARTICR Register is the interrupt clear register and is write-only"
|
||||
// Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR
|
||||
0
|
||||
}
|
||||
Ok(DMACR) => self.dmacr.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write(&mut self, offset: hwaddr, value: u64) {
|
||||
// eprintln!("write offset {offset} value {value}");
|
||||
use RegisterOffset::*;
|
||||
let value: u32 = value as u32;
|
||||
match RegisterOffset::try_from(offset) {
|
||||
Err(_bad_offset) => {
|
||||
eprintln!("write bad offset {offset} value {value}");
|
||||
}
|
||||
Ok(DR) => {
|
||||
// ??? Check if transmitter is enabled.
|
||||
let ch: u8 = value as u8;
|
||||
// XXX this blocks entire thread. Rewrite to use
|
||||
// qemu_chr_fe_write and background I/O callbacks
|
||||
|
||||
// SAFETY: self.char_backend is a valid CharBackend instance after it's been
|
||||
// initialized in realize().
|
||||
unsafe {
|
||||
qemu_chr_fe_write_all(addr_of_mut!(self.char_backend), &ch, 1);
|
||||
}
|
||||
self.loopback_tx(value);
|
||||
self.int_level |= registers::INT_TX;
|
||||
self.update();
|
||||
}
|
||||
Ok(RSR) => {
|
||||
self.receive_status_error_clear = 0.into();
|
||||
}
|
||||
Ok(FR) => {
|
||||
// flag writes are ignored
|
||||
}
|
||||
Ok(ILPR) => {
|
||||
self.ilpr = value;
|
||||
}
|
||||
Ok(IBRD) => {
|
||||
self.ibrd = value;
|
||||
}
|
||||
Ok(FBRD) => {
|
||||
self.fbrd = value;
|
||||
}
|
||||
Ok(LCR_H) => {
|
||||
let value = value as u16;
|
||||
let new_val: registers::LineControl = value.into();
|
||||
// Reset the FIFO state on FIFO enable or disable
|
||||
if bool::from(self.line_control.fifos_enabled())
|
||||
^ bool::from(new_val.fifos_enabled())
|
||||
{
|
||||
self.reset_fifo();
|
||||
}
|
||||
if 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(
|
||||
addr_of_mut!(self.char_backend),
|
||||
CHR_IOCTL_SERIAL_SET_BREAK as i32,
|
||||
addr_of_mut!(break_enable).cast::<c_void>(),
|
||||
);
|
||||
}
|
||||
self.loopback_break(break_enable > 0);
|
||||
}
|
||||
self.line_control = new_val;
|
||||
self.set_read_trigger();
|
||||
}
|
||||
Ok(CR) => {
|
||||
// ??? Need to implement the enable bit.
|
||||
let value = value as u16;
|
||||
self.control = value.into();
|
||||
self.loopback_mdmctrl();
|
||||
}
|
||||
Ok(FLS) => {
|
||||
self.ifl = value;
|
||||
self.set_read_trigger();
|
||||
}
|
||||
Ok(IMSC) => {
|
||||
self.int_enabled = value;
|
||||
self.update();
|
||||
}
|
||||
Ok(RIS) => {}
|
||||
Ok(MIS) => {}
|
||||
Ok(ICR) => {
|
||||
self.int_level &= !value;
|
||||
self.update();
|
||||
}
|
||||
Ok(DMACR) => {
|
||||
self.dmacr = value;
|
||||
if value & 3 > 0 {
|
||||
// qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
|
||||
eprintln!("pl011: DMA not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn loopback_tx(&mut self, value: u32) {
|
||||
if !self.loopback_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Caveat:
|
||||
//
|
||||
// In real hardware, TX loopback happens at the serial-bit level
|
||||
// and then reassembled by the RX logics back into bytes and placed
|
||||
// into the RX fifo. That is, loopback happens after TX fifo.
|
||||
//
|
||||
// Because the real hardware TX fifo is time-drained at the frame
|
||||
// rate governed by the configured serial format, some loopback
|
||||
// bytes in TX fifo may still be able to get into the RX fifo
|
||||
// that could be full at times while being drained at software
|
||||
// pace.
|
||||
//
|
||||
// In such scenario, the RX draining pace is the major factor
|
||||
// deciding which loopback bytes get into the RX fifo, unless
|
||||
// hardware flow-control is enabled.
|
||||
//
|
||||
// For simplicity, the above described is not emulated.
|
||||
self.put_fifo(value);
|
||||
}
|
||||
|
||||
fn loopback_mdmctrl(&mut self) {
|
||||
if !self.loopback_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loopback software-driven modem control outputs to modem status inputs:
|
||||
* FR.RI <= CR.Out2
|
||||
* FR.DCD <= CR.Out1
|
||||
* FR.CTS <= CR.RTS
|
||||
* FR.DSR <= CR.DTR
|
||||
*
|
||||
* The loopback happens immediately even if this call is triggered
|
||||
* by setting only CR.LBE.
|
||||
*
|
||||
* CTS/RTS updates due to enabled hardware flow controls are not
|
||||
* dealt with here.
|
||||
*/
|
||||
|
||||
//fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD |
|
||||
// PL011_FLAG_DSR | PL011_FLAG_CTS);
|
||||
//fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0;
|
||||
//fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0;
|
||||
//fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0;
|
||||
//fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0;
|
||||
//
|
||||
self.flags.set_ring_indicator(self.control.out_2());
|
||||
self.flags.set_data_carrier_detect(self.control.out_1());
|
||||
self.flags.set_clear_to_send(self.control.request_to_send());
|
||||
self.flags
|
||||
.set_data_set_ready(self.control.data_transmit_ready());
|
||||
|
||||
// Change interrupts based on updated FR
|
||||
let mut il = self.int_level;
|
||||
|
||||
il &= !Interrupt::MS;
|
||||
//il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0;
|
||||
//il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0;
|
||||
//il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0;
|
||||
//il |= (fr & PL011_FLAG_RI) ? INT_RI : 0;
|
||||
|
||||
if self.flags.data_set_ready() {
|
||||
il |= Interrupt::DSR as u32;
|
||||
}
|
||||
if self.flags.data_carrier_detect() {
|
||||
il |= Interrupt::DCD as u32;
|
||||
}
|
||||
if self.flags.clear_to_send() {
|
||||
il |= Interrupt::CTS as u32;
|
||||
}
|
||||
if self.flags.ring_indicator() {
|
||||
il |= Interrupt::RI as u32;
|
||||
}
|
||||
self.int_level = il;
|
||||
self.update();
|
||||
}
|
||||
|
||||
fn loopback_break(&mut self, enable: bool) {
|
||||
if enable {
|
||||
self.loopback_tx(DATA_BREAK);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_read_trigger(&mut self) {
|
||||
self.read_trigger = 1;
|
||||
}
|
||||
|
||||
pub fn realize(&mut 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_mut!(self.char_backend),
|
||||
Some(pl011_can_receive),
|
||||
Some(pl011_receive),
|
||||
Some(pl011_event),
|
||||
None,
|
||||
addr_of_mut!(*self).cast::<c_void>(),
|
||||
core::ptr::null_mut(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.line_control.reset();
|
||||
self.receive_status_error_clear.reset();
|
||||
self.dmacr = 0;
|
||||
self.int_enabled = 0;
|
||||
self.int_level = 0;
|
||||
self.ilpr = 0;
|
||||
self.ibrd = 0;
|
||||
self.fbrd = 0;
|
||||
self.read_trigger = 1;
|
||||
self.ifl = 0x12;
|
||||
self.control.reset();
|
||||
self.flags = 0.into();
|
||||
self.reset_fifo();
|
||||
}
|
||||
|
||||
pub fn reset_fifo(&mut self) {
|
||||
self.read_count = 0;
|
||||
self.read_pos = 0;
|
||||
|
||||
/* Reset FIFO flags */
|
||||
self.flags.reset();
|
||||
}
|
||||
|
||||
pub fn can_receive(&self) -> bool {
|
||||
// trace_pl011_can_receive(s->lcr, s->read_count, r);
|
||||
self.read_count < self.fifo_depth()
|
||||
}
|
||||
|
||||
pub fn event(&mut self, event: QEMUChrEvent) {
|
||||
if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.fifo_enabled() {
|
||||
self.put_fifo(DATA_BREAK);
|
||||
self.receive_status_error_clear.set_break_error(true);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fifo_enabled(&self) -> bool {
|
||||
matches!(self.line_control.fifos_enabled(), registers::Mode::FIFO)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn loopback_enabled(&self) -> bool {
|
||||
self.control.enable_loopback()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fifo_depth(&self) -> usize {
|
||||
// Note: FIFO depth is expected to be power-of-2
|
||||
if self.fifo_enabled() {
|
||||
return PL011_FIFO_DEPTH;
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
pub fn put_fifo(&mut self, value: c_uint) {
|
||||
let depth = self.fifo_depth();
|
||||
assert!(depth > 0);
|
||||
let slot = (self.read_pos + self.read_count) & (depth - 1);
|
||||
self.read_fifo[slot] = value;
|
||||
self.read_count += 1;
|
||||
// s->flags &= ~PL011_FLAG_RXFE;
|
||||
self.flags.set_receive_fifo_empty(false);
|
||||
if self.read_count == depth {
|
||||
//s->flags |= PL011_FLAG_RXFF;
|
||||
self.flags.set_receive_fifo_full(true);
|
||||
}
|
||||
|
||||
if self.read_count == self.read_trigger {
|
||||
self.int_level |= registers::INT_RX;
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
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)) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Which bits in the interrupt status matter for each outbound IRQ line ?
|
||||
pub const IRQMASK: [u32; 6] = [
|
||||
/* combined IRQ */
|
||||
Interrupt::E
|
||||
| Interrupt::MS
|
||||
| Interrupt::RT as u32
|
||||
| Interrupt::TX as u32
|
||||
| Interrupt::RX as u32,
|
||||
Interrupt::RX as u32,
|
||||
Interrupt::TX as u32,
|
||||
Interrupt::RT as u32,
|
||||
Interrupt::MS,
|
||||
Interrupt::E,
|
||||
];
|
||||
|
||||
/// # 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.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int {
|
||||
unsafe {
|
||||
debug_assert!(!opaque.is_null());
|
||||
let state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||
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.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_receive(
|
||||
opaque: *mut core::ffi::c_void,
|
||||
buf: *const u8,
|
||||
size: core::ffi::c_int,
|
||||
) {
|
||||
unsafe {
|
||||
debug_assert!(!opaque.is_null());
|
||||
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||
if state.as_ref().loopback_enabled() {
|
||||
return;
|
||||
}
|
||||
if size > 0 {
|
||||
debug_assert!(!buf.is_null());
|
||||
state.as_mut().put_fifo(c_uint::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.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_event(opaque: *mut core::ffi::c_void, event: QEMUChrEvent) {
|
||||
unsafe {
|
||||
debug_assert!(!opaque.is_null());
|
||||
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
|
||||
state.as_mut().event(event)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// We expect the FFI user of this function to pass a valid pointer for `chr`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_create(
|
||||
addr: u64,
|
||||
irq: qemu_irq,
|
||||
chr: *mut Chardev,
|
||||
) -> *mut DeviceState {
|
||||
unsafe {
|
||||
let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name);
|
||||
let sysbus: *mut SysBusDevice = dev.cast::<SysBusDevice>();
|
||||
|
||||
qdev_prop_set_chr(dev, bindings::TYPE_CHARDEV.as_ptr(), chr);
|
||||
sysbus_realize_and_unref(sysbus, addr_of!(error_fatal) as *mut *mut Error);
|
||||
sysbus_mmio_map(sysbus, 0, addr);
|
||||
sysbus_connect_irq(sysbus, 0, irq);
|
||||
dev
|
||||
}
|
||||
}
|
||||
|
||||
/// # 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.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl011_init(obj: *mut Object) {
|
||||
unsafe {
|
||||
debug_assert!(!obj.is_null());
|
||||
let mut state = NonNull::new_unchecked(obj.cast::<PL011State>());
|
||||
state.as_mut().init();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue