// Copyright 2024, Linaro Limited // Author(s): Manos Pitsidianakis // 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, #[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 = Some(pl011_init); const INSTANCE_POST_INIT: Option = None; const INSTANCE_FINALIZE: Option = 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::(); // 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::(), &PL011_OPS, addr_of_mut!(*self).cast::(), Self::TYPE_INFO.name, 0x1000, ); let sbd = addr_of_mut!(*self).cast::(); 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::(), ClockEvent::ClockUpdate.0, )) .unwrap(); } } pub fn read( &mut self, offset: hwaddr, _size: core::ffi::c_uint, ) -> std::ops::ControlFlow { 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::(), ); } 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::(), 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::()); 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::()); 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::()); 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::(); 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::()); state.as_mut().init(); } }