diff options
Diffstat (limited to 'rust/hw/char')
| -rw-r--r-- | rust/hw/char/pl011/src/device.rs | 154 | ||||
| -rw-r--r-- | rust/hw/char/pl011/src/device_class.rs | 10 | ||||
| -rw-r--r-- | rust/hw/char/pl011/src/lib.rs | 161 |
3 files changed, 173 insertions, 152 deletions
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 3e29442a62..18cc122951 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -11,11 +11,10 @@ use std::{ use qemu_api::{ bindings::{self, *}, c_str, - definitions::ObjectImpl, - device_class::DeviceImpl, - impl_device_class, irq::InterruptSource, prelude::*, + qdev::DeviceImpl, + qom::ObjectImpl, }; use crate::{ @@ -31,10 +30,8 @@ const IBRD_MASK: u32 = 0xffff; /// Fractional Baud Rate Divider, `UARTFBRD` const FBRD_MASK: u32 = 0x3f; -const DATA_BREAK: u32 = 1 << 10; - /// QEMU sourced constant. -pub const PL011_FIFO_DEPTH: usize = 16_usize; +pub const PL011_FIFO_DEPTH: u32 = 16; #[derive(Clone, Copy, Debug)] enum DeviceId { @@ -59,6 +56,32 @@ impl DeviceId { const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]; } +// FIFOs use 32-bit indices instead of usize, for compatibility with +// the migration stream produced by the C version of this device. +#[repr(transparent)] +#[derive(Debug, Default)] +pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]); + +impl Fifo { + const fn len(&self) -> u32 { + self.0.len() as u32 + } +} + +impl std::ops::IndexMut<u32> for Fifo { + fn index_mut(&mut self, idx: u32) -> &mut Self::Output { + &mut self.0[idx as usize] + } +} + +impl std::ops::Index<u32> for Fifo { + type Output = registers::Data; + + fn index(&self, idx: u32) -> &Self::Output { + &self.0[idx as usize] + } +} + #[repr(C)] #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] /// PL011 Device Model in QEMU @@ -76,14 +99,14 @@ pub struct PL011State { pub dmacr: u32, pub int_enabled: u32, pub int_level: u32, - pub read_fifo: [u32; PL011_FIFO_DEPTH], + pub read_fifo: Fifo, pub ilpr: u32, pub ibrd: u32, pub fbrd: u32, pub ifl: u32, - pub read_pos: usize, - pub read_count: usize, - pub read_trigger: usize, + pub read_pos: u32, + pub read_count: u32, + pub read_trigger: u32, #[doc(alias = "chr")] pub char_backend: CharBackend, /// QEMU interrupts @@ -107,8 +130,10 @@ pub struct PL011State { device_id: DeviceId, } +qom_isa!(PL011State : SysBusDevice, DeviceState, Object); + unsafe impl ObjectType for PL011State { - type Class = PL011Class; + type Class = <SysBusDevice as ObjectType>::Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; } @@ -118,11 +143,6 @@ impl ObjectImpl for PL011State { const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init); } -#[repr(C)] -pub struct PL011Class { - _inner: [u8; 0], -} - impl DeviceImpl for PL011State { fn properties() -> &'static [Property] { &device_class::PL011_PROPERTIES @@ -134,8 +154,6 @@ impl DeviceImpl for PL011State { const RESET: Option<fn(&mut Self)> = Some(Self::reset); } -impl_device_class!(PL011State); - impl PL011State { /// Initializes a pre-allocated, unitialized instance of `PL011State`. /// @@ -148,8 +166,6 @@ impl PL011State { unsafe fn init(&mut self) { const CLK_NAME: &CStr = c_str!("clk"); - let sbd = unsafe { &mut *(addr_of_mut!(*self).cast::<SysBusDevice>()) }; - // SAFETY: // // self and self.iomem are guaranteed to be valid at this point since callers @@ -163,15 +179,16 @@ impl PL011State { Self::TYPE_NAME.as_ptr(), 0x1000, ); + + let sbd: &mut SysBusDevice = self.upcast_mut(); sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); } for irq in self.interrupts.iter() { + let sbd: &SysBusDevice = self.upcast(); sbd.init_irq(irq); } - let dev = addr_of_mut!(*self).cast::<DeviceState>(); - // SAFETY: // // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, @@ -180,6 +197,7 @@ impl PL011State { // calls this function to initialize the fields; therefore no code is // able to access an invalid self.clock value. unsafe { + let dev: &mut DeviceState = self.upcast_mut(); self.clock = NonNull::new(qdev_init_clock_in( dev, CLK_NAME.as_ptr(), @@ -194,9 +212,9 @@ impl PL011State { pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> { use RegisterOffset::*; - std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) { + let value = match RegisterOffset::try_from(offset) { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { - u64::from(self.device_id[(offset - 0xfe0) >> 2]) + u32::from(self.device_id[(offset - 0xfe0) >> 2]) } Err(_) => { // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); @@ -216,32 +234,31 @@ impl PL011State { self.int_level &= !registers::INT_RX; } // Update error bits. - self.receive_status_error_clear = c.to_be_bytes()[3].into(); + self.receive_status_error_clear.set_from_data(c); self.update(); // Must call qemu_chr_fe_accept_input, so return Continue: - return std::ops::ControlFlow::Continue(c.into()); + let c = u32::from(c); + return std::ops::ControlFlow::Continue(u64::from(c)); } - 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(RSR) => u32::from(self.receive_status_error_clear), + Ok(FR) => u32::from(self.flags), + Ok(FBRD) => self.fbrd, + Ok(ILPR) => self.ilpr, + Ok(IBRD) => self.ibrd, + Ok(LCR_H) => u32::from(self.line_control), + Ok(CR) => u32::from(self.control), + Ok(FLS) => self.ifl, + Ok(IMSC) => self.int_enabled, + Ok(RIS) => self.int_level, + Ok(MIS) => 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(), - }) + Ok(DMACR) => self.dmacr, + }; + std::ops::ControlFlow::Break(value.into()) } pub fn write(&mut self, offset: hwaddr, value: u64) { @@ -268,7 +285,7 @@ impl PL011State { self.update(); } Ok(RSR) => { - self.receive_status_error_clear = 0.into(); + self.receive_status_error_clear.reset(); } Ok(FR) => { // flag writes are ignored @@ -283,13 +300,11 @@ impl PL011State { 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.fifos_enabled() != new_val.fifos_enabled() { + self.reset_rx_fifo(); + self.reset_tx_fifo(); } if self.line_control.send_break() ^ new_val.send_break() { let mut break_enable: c_int = new_val.send_break().into(); @@ -309,7 +324,6 @@ impl PL011State { } Ok(CR) => { // ??? Need to implement the enable bit. - let value = value as u16; self.control = value.into(); self.loopback_mdmctrl(); } @@ -411,7 +425,7 @@ impl PL011State { fn loopback_break(&mut self, enable: bool) { if enable { - self.loopback_tx(DATA_BREAK); + self.loopback_tx(registers::Data::BREAK.into()); } } @@ -448,16 +462,24 @@ impl PL011State { self.read_trigger = 1; self.ifl = 0x12; self.control.reset(); - self.flags = 0.into(); - self.reset_fifo(); + self.flags.reset(); + self.reset_rx_fifo(); + self.reset_tx_fifo(); } - pub fn reset_fifo(&mut self) { + pub fn reset_rx_fifo(&mut self) { self.read_count = 0; self.read_pos = 0; - /* Reset FIFO flags */ - self.flags.reset(); + // Reset FIFO flags + self.flags.set_receive_fifo_full(false); + self.flags.set_receive_fifo_empty(true); + } + + pub fn reset_tx_fifo(&mut self) { + // Reset FIFO flags + self.flags.set_transmit_fifo_full(false); + self.flags.set_transmit_fifo_empty(true); } pub fn can_receive(&self) -> bool { @@ -466,15 +488,14 @@ impl PL011State { } 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); + if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { + self.put_fifo(registers::Data::BREAK.into()); } } #[inline] pub fn fifo_enabled(&self) -> bool { - matches!(self.line_control.fifos_enabled(), registers::Mode::FIFO) + self.line_control.fifos_enabled() == registers::Mode::FIFO } #[inline] @@ -483,7 +504,7 @@ impl PL011State { } #[inline] - pub fn fifo_depth(&self) -> usize { + pub fn fifo_depth(&self) -> u32 { // Note: FIFO depth is expected to be power-of-2 if self.fifo_enabled() { return PL011_FIFO_DEPTH; @@ -495,7 +516,7 @@ impl PL011State { 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_fifo[slot] = registers::Data::from(value); self.read_count += 1; self.flags.set_receive_fifo_empty(false); if self.read_count == depth { @@ -627,11 +648,6 @@ pub struct PL011Luminary { parent_obj: PL011State, } -#[repr(C)] -pub struct PL011LuminaryClass { - _inner: [u8; 0], -} - impl PL011Luminary { /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`. /// @@ -645,8 +661,10 @@ impl PL011Luminary { } } +qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object); + unsafe impl ObjectType for PL011Luminary { - type Class = PL011LuminaryClass; + type Class = <PL011State as ObjectType>::Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; } @@ -657,5 +675,3 @@ impl ObjectImpl for PL011Luminary { } impl DeviceImpl for PL011Luminary {} - -impl_device_class!(PL011Luminary); diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 975c3d42be..7f3ca89507 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -6,8 +6,8 @@ use core::ptr::NonNull; use std::os::raw::{c_int, c_void}; use qemu_api::{ - bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections, - vmstate_uint32, vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, + bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_subsections, vmstate_uint32, + vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, }; use crate::device::{PL011State, PL011_FIFO_DEPTH}; @@ -64,9 +64,9 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { vmstate_uint32!(ibrd, PL011State), vmstate_uint32!(fbrd, PL011State), vmstate_uint32!(ifl, PL011State), - vmstate_int32!(read_pos, PL011State), - vmstate_int32!(read_count, PL011State), - vmstate_int32!(read_trigger, PL011State), + vmstate_uint32!(read_pos, PL011State), + vmstate_uint32!(read_count, PL011State), + vmstate_uint32!(read_trigger, PL011State), }, subsections: vmstate_subsections! { VMSTATE_PL011_CLOCK diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 4dc0e8f345..69064d6929 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -131,14 +131,23 @@ impl core::convert::TryFrom<u64> for RegisterOffset { pub mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. - //! - //! All PL011 registers are essentially 32-bit wide, but are typed here as - //! bitmaps with only the necessary width. That is, if a struct bitmap - //! in this module is for example 16 bits long, it should be conceived - //! as a 32-bit register where the unmentioned higher bits are always - //! unused thus treated as zero when read or written. use bilge::prelude::*; + /// Receive Status Register / Data Register common error bits + /// + /// The `UARTRSR` register is updated only when a read occurs + /// from the `UARTDR` register with the same status information + /// that can also be obtained by reading the `UARTDR` register + #[bitsize(8)] + #[derive(Clone, Copy, Default, DebugBits, FromBits)] + pub struct Errors { + pub framing_error: bool, + pub parity_error: bool, + pub break_error: bool, + pub overrun_error: bool, + _reserved_unpredictable: u4, + } + // TODO: FIFO Mode has different semantics /// Data Register, `UARTDR` /// @@ -181,16 +190,18 @@ pub mod registers { /// /// # Source /// ARM DDI 0183G 3.3.1 Data Register, UARTDR - #[bitsize(16)] - #[derive(Clone, Copy, DebugBits, FromBits)] + #[bitsize(32)] + #[derive(Clone, Copy, Default, DebugBits, FromBits)] #[doc(alias = "UARTDR")] pub struct Data { - _reserved: u4, pub data: u8, - pub framing_error: bool, - pub parity_error: bool, - pub break_error: bool, - pub overrun_error: bool, + pub errors: Errors, + _reserved: u16, + } + + impl Data { + // bilge is not very const-friendly, unfortunately + pub const BREAK: Self = Self { value: 1 << 10 }; } // TODO: FIFO Mode has different semantics @@ -217,20 +228,21 @@ pub mod registers { /// # Source /// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register, /// UARTRSR/UARTECR - #[bitsize(8)] + #[bitsize(32)] #[derive(Clone, Copy, DebugBits, FromBits)] pub struct ReceiveStatusErrorClear { - pub framing_error: bool, - pub parity_error: bool, - pub break_error: bool, - pub overrun_error: bool, - _reserved_unpredictable: u4, + pub errors: Errors, + _reserved_unpredictable: u24, } impl ReceiveStatusErrorClear { + pub fn set_from_data(&mut self, data: Data) { + self.set_errors(data.errors()); + } + pub fn reset(&mut self) { // All the bits are cleared to 0 on reset. - *self = 0.into(); + *self = Self::default(); } } @@ -240,7 +252,7 @@ pub mod registers { } } - #[bitsize(16)] + #[bitsize(32)] #[derive(Clone, Copy, DebugBits, FromBits)] /// Flag Register, `UARTFR` #[doc(alias = "UARTFR")] @@ -292,59 +304,45 @@ pub mod registers { pub transmit_fifo_empty: bool, /// `RI`, is `true` when `nUARTRI` is `LOW`. pub ring_indicator: bool, - _reserved_zero_no_modify: u7, + _reserved_zero_no_modify: u23, } impl Flags { pub fn reset(&mut self) { - // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 - self.set_receive_fifo_full(false); - self.set_transmit_fifo_full(false); - self.set_busy(false); - self.set_receive_fifo_empty(true); - self.set_transmit_fifo_empty(true); + *self = Self::default(); } } impl Default for Flags { fn default() -> Self { let mut ret: Self = 0.into(); - ret.reset(); + // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 + ret.set_receive_fifo_empty(true); + ret.set_transmit_fifo_empty(true); ret } } - #[bitsize(16)] + #[bitsize(32)] #[derive(Clone, Copy, DebugBits, FromBits)] /// Line Control Register, `UARTLCR_H` #[doc(alias = "UARTLCR_H")] pub struct LineControl { - /// 15:8 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u8, - /// 7 SPS Stick parity select. - /// 0 = stick parity is disabled - /// 1 = either: - /// • if the EPS bit is 0 then the parity bit is transmitted and checked - /// as a 1 • if the EPS bit is 1 then the parity bit is - /// transmitted and checked as a 0. This bit has no effect when - /// the PEN bit disables parity checking and generation. See Table 3-11 - /// on page 3-14 for the parity truth table. - pub sticky_parity: bool, - /// WLEN Word length. These bits indicate the number of data bits - /// transmitted or received in a frame as follows: b11 = 8 bits - /// b10 = 7 bits - /// b01 = 6 bits - /// b00 = 5 bits. - pub word_length: WordLength, - /// FEN Enable FIFOs: - /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become - /// 1-byte-deep holding registers 1 = transmit and receive FIFO - /// buffers are enabled (FIFO mode). - pub fifos_enabled: Mode, - /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits - /// are transmitted at the end of the frame. The receive - /// logic does not check for two stop bits being received. - pub two_stops_bits: bool, + /// BRK Send break. + /// + /// If this bit is set to `1`, a low-level is continually output on the + /// `UARTTXD` output, after completing transmission of the + /// current character. For the proper execution of the break command, + /// the software must set this bit for at least two complete + /// frames. For normal use, this bit must be cleared to `0`. + pub send_break: bool, + /// 1 PEN Parity enable: + /// + /// - 0 = parity is disabled and no parity bit added to the data frame + /// - 1 = parity checking and generation is enabled. + /// + /// See Table 3-11 on page 3-14 for the parity truth table. + pub parity_enabled: bool, /// EPS Even parity select. Controls the type of parity the UART uses /// during transmission and reception: /// - 0 = odd parity. The UART generates or checks for an odd number of @@ -355,21 +353,32 @@ pub mod registers { /// and generation. See Table 3-11 on page 3-14 for the parity /// truth table. pub parity: Parity, - /// 1 PEN Parity enable: - /// - /// - 0 = parity is disabled and no parity bit added to the data frame - /// - 1 = parity checking and generation is enabled. - /// - /// See Table 3-11 on page 3-14 for the parity truth table. - pub parity_enabled: bool, - /// BRK Send break. - /// - /// If this bit is set to `1`, a low-level is continually output on the - /// `UARTTXD` output, after completing transmission of the - /// current character. For the proper execution of the break command, - /// the software must set this bit for at least two complete - /// frames. For normal use, this bit must be cleared to `0`. - pub send_break: bool, + /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits + /// are transmitted at the end of the frame. The receive + /// logic does not check for two stop bits being received. + pub two_stops_bits: bool, + /// FEN Enable FIFOs: + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become + /// 1-byte-deep holding registers 1 = transmit and receive FIFO + /// buffers are enabled (FIFO mode). + pub fifos_enabled: Mode, + /// WLEN Word length. These bits indicate the number of data bits + /// transmitted or received in a frame as follows: b11 = 8 bits + /// b10 = 7 bits + /// b01 = 6 bits + /// b00 = 5 bits. + pub word_length: WordLength, + /// 7 SPS Stick parity select. + /// 0 = stick parity is disabled + /// 1 = either: + /// • if the EPS bit is 0 then the parity bit is transmitted and checked + /// as a 1 • if the EPS bit is 1 then the parity bit is + /// transmitted and checked as a 0. This bit has no effect when + /// the PEN bit disables parity checking and generation. See Table 3-11 + /// on page 3-14 for the parity truth table. + pub sticky_parity: bool, + /// 31:8 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u24, } impl LineControl { @@ -410,12 +419,6 @@ pub mod registers { FIFO = 1, } - impl From<Mode> for bool { - fn from(val: Mode) -> Self { - matches!(val, Mode::FIFO) - } - } - #[bitsize(2)] #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] /// `WLEN` Word length, field of [Line Control register](LineControl). @@ -440,7 +443,7 @@ pub mod registers { /// /// # Source /// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12 - #[bitsize(16)] + #[bitsize(32)] #[doc(alias = "UARTCR")] #[derive(Clone, Copy, DebugBits, FromBits)] pub struct Control { @@ -518,6 +521,8 @@ pub mod registers { /// CTS hardware flow control is enabled. Data is only transmitted when /// the `nUARTCTS` signal is asserted. pub cts_hardware_flow_control_enable: bool, + /// 31:16 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify2: u16, } impl Control { |