diff options
Diffstat (limited to 'rust/hw/char/pl011/src/device.rs')
| -rw-r--r-- | rust/hw/char/pl011/src/device.rs | 599 |
1 files changed, 0 insertions, 599 deletions
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs deleted file mode 100644 index c7193b41be..0000000000 --- a/rust/hw/char/pl011/src/device.rs +++ /dev/null @@ -1,599 +0,0 @@ -// 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(); - } -} |