diff options
| author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-01-29 09:51:03 -0500 |
|---|---|---|
| committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-01-29 09:51:03 -0500 |
| commit | 871af84dd599fab68c8ed414d9ecbdb2bcfc5801 (patch) | |
| tree | 508d69f0e934ceda69c18525c8871797036f2a05 /rust/hw/char/pl011/src | |
| parent | fb49b69bf9fd584546c7d946eaeec90941941d25 (diff) | |
| parent | 3b36ee720288ba17962a17b305243ea34100e1f3 (diff) | |
| download | focaccia-qemu-871af84dd599fab68c8ed414d9ecbdb2bcfc5801.tar.gz focaccia-qemu-871af84dd599fab68c8ed414d9ecbdb2bcfc5801.zip | |
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* target/i386: optimize string instructions * target/i386: new Sierra Forest and Clearwater Forest models * rust: type-safe vmstate implementation * rust: use interior mutability for PL011 * rust: clean ups * memtxattrs: remove usage of bitfields from MEMTXATTRS_UNSPECIFIED * gitlab-ci: enable Rust backtraces # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmeZ6VYUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroMjbQgApuooMOp0z/8Ky4/ux8M8/vrlcNCH # V1Pm6WzrjEzd9TIMLGr6npOyLOkWI31Aa4o/TuW09SeKE3dpCf/7LYA5VDEtkH79 # F57MgnSj56sMNgu+QZ/SiGvkKJXl+3091jIianrrI0dtX8hPonm6bt55woDvQt3z # p94+4zzv5G0nc+ncITCDho8sn5itdZWVOjf9n6VCOumMjF4nRSoMkJKYIvjNht6n # GtjMhYA70tzjkIi4bPyYkhFpMNlAqEDIp2TvPzp6klG5QoUErHIzdzoRTAtE4Dpb # 7240r6jarQX41TBXGOFq0NrxES1cm5zO/6159D24qZGHGm2hG4nDx+t2jw== # =ZKFy # -----END PGP SIGNATURE----- # gpg: Signature made Wed 29 Jan 2025 03:39:50 EST # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (49 commits) gitlab-ci: include full Rust backtraces in test runs rust: qemu-api: add sub-subclass to the integration tests rust/zeroable: Implement Zeroable with const_zero macro rust: qdev: make reset take a shared reference rust: pl011: drop use of ControlFlow rust: pl011: pull device-specific code out of MemoryRegionOps callbacks rust: pl011: remove duplicate definitions rust: pl011: wrap registers with BqlRefCell rust: pl011: extract PL011Registers rust: pl011: pull interrupt updates out of read/write ops rust: pl011: extract CharBackend receive logic into a separate function rust: pl011: extract conversion to RegisterOffset rust: pl011: hide unnecessarily "pub" items from outside pl011::device rust: pl011: remove unnecessary "extern crate" rust: prefer NonNull::new to assertions rust: vmstate: make order of parameters consistent in vmstate_clock rust: vmstate: remove translation of C vmstate macros rust: pl011: switch vmstate to new-style macros rust: qemu_api: add vmstate_struct rust: vmstate: add public utility macros to implement VMState ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'rust/hw/char/pl011/src')
| -rw-r--r-- | rust/hw/char/pl011/src/device.rs | 515 | ||||
| -rw-r--r-- | rust/hw/char/pl011/src/device_class.rs | 73 | ||||
| -rw-r--r-- | rust/hw/char/pl011/src/lib.rs | 69 | ||||
| -rw-r--r-- | rust/hw/char/pl011/src/memory_ops.rs | 25 |
4 files changed, 365 insertions, 317 deletions
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 994c2fc059..8050ede9c8 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,19 +2,27 @@ // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::{addr_of_mut, NonNull}; +use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, - os::raw::{c_int, c_uint, c_void}, + os::raw::{c_int, c_void}, }; use qemu_api::{ - bindings::{self, *}, - c_str, + bindings::{ + error_fatal, hwaddr, memory_region_init_io, qdev_init_clock_in, qdev_new, + qdev_prop_set_chr, qemu_chr_fe_accept_input, qemu_chr_fe_ioctl, qemu_chr_fe_set_handlers, + qemu_chr_fe_write_all, qemu_irq, sysbus_connect_irq, sysbus_mmio_map, + sysbus_realize_and_unref, CharBackend, Chardev, Clock, ClockEvent, MemoryRegion, + QEMUChrEvent, CHR_IOCTL_SERIAL_SET_BREAK, + }, + c_str, impl_vmstate_forward, irq::InterruptSource, prelude::*, - qdev::DeviceImpl, + qdev::{DeviceImpl, DeviceState, Property}, qom::{ClassInitImpl, ObjectImpl, ParentField}, + sysbus::{SysBusDevice, SysBusDeviceClass}, + vmstate::VMStateDescription, }; use crate::{ @@ -54,6 +62,7 @@ impl DeviceId { #[repr(transparent)] #[derive(Debug, Default)] pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]); +impl_vmstate_forward!(Fifo); impl Fifo { const fn len(&self) -> u32 { @@ -76,11 +85,8 @@ impl std::ops::Index<u32> for Fifo { } #[repr(C)] -#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] -/// PL011 Device Model in QEMU -pub struct PL011State { - pub parent_obj: ParentField<SysBusDevice>, - pub iomem: MemoryRegion, +#[derive(Debug, Default, qemu_api_macros::offsets)] +pub struct PL011Registers { #[doc(alias = "fr")] pub flags: registers::Flags, #[doc(alias = "lcr")] @@ -100,8 +106,17 @@ pub struct PL011State { pub read_pos: u32, pub read_count: u32, pub read_trigger: u32, +} + +#[repr(C)] +#[derive(qemu_api_macros::Object, qemu_api_macros::offsets)] +/// PL011 Device Model in QEMU +pub struct PL011State { + pub parent_obj: ParentField<SysBusDevice>, + pub iomem: MemoryRegion, #[doc(alias = "chr")] pub char_backend: CharBackend, + pub regs: BqlRefCell<PL011Registers>, /// QEMU interrupts /// /// ```text @@ -123,6 +138,7 @@ pub struct PL011State { qom_isa!(PL011State : SysBusDevice, DeviceState, Object); +#[repr(C)] pub struct PL011Class { parent_class: <SysBusDevice as ObjectType>::Class, /// The byte string that identifies the device. @@ -155,77 +171,17 @@ impl DeviceImpl for PL011State { fn vmsd() -> Option<&'static VMStateDescription> { Some(&device_class::VMSTATE_PL011) } - const REALIZE: Option<fn(&mut Self)> = Some(Self::realize); - const RESET: Option<fn(&mut Self)> = Some(Self::reset); + const REALIZE: Option<fn(&Self)> = Some(Self::realize); + const RESET: Option<fn(&Self)> = Some(Self::reset); } -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`. - unsafe fn init(&mut self) { - const CLK_NAME: &CStr = c_str!("clk"); - - // 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_NAME.as_ptr(), - 0x1000, - ); - } - - // 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 { - let dev: &mut DeviceState = self.upcast_mut(); - 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(); - } - } - - fn post_init(&self) { - self.init_mmio(&self.iomem); - for irq in self.interrupts.iter() { - self.init_irq(irq); - } - } - - pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> { +impl PL011Registers { + pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) { use RegisterOffset::*; - let value = match RegisterOffset::try_from(offset) { - Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { - let device_id = self.get_class().device_id; - u32::from(device_id[(offset - 0xfe0) >> 2]) - } - Err(_) => { - // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); - 0 - } - Ok(DR) => { + let mut update = false; + let result = match offset { + DR => { self.flags.set_receive_fifo_full(false); let c = self.read_fifo[self.read_pos]; if self.read_count > 0 { @@ -236,117 +192,109 @@ impl PL011State { self.flags.set_receive_fifo_empty(true); } if self.read_count + 1 == self.read_trigger { - self.int_level &= !registers::INT_RX; + self.int_level &= !Interrupt::RX.0; } // Update error bits. self.receive_status_error_clear.set_from_data(c); - self.update(); - // Must call qemu_chr_fe_accept_input, so return Continue: - let c = u32::from(c); - return std::ops::ControlFlow::Continue(u64::from(c)); + // Must call qemu_chr_fe_accept_input + update = true; + u32::from(c) } - 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) => { + RSR => u32::from(self.receive_status_error_clear), + FR => u32::from(self.flags), + FBRD => self.fbrd, + ILPR => self.ilpr, + IBRD => self.ibrd, + LCR_H => u32::from(self.line_control), + CR => u32::from(self.control), + FLS => self.ifl, + IMSC => self.int_enabled, + RIS => self.int_level, + MIS => self.int_level & self.int_enabled, + 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, + DMACR => self.dmacr, }; - std::ops::ControlFlow::Break(value.into()) + (update, result) } - pub fn write(&mut self, offset: hwaddr, value: u64) { + pub(self) fn write( + &mut self, + offset: RegisterOffset, + value: u32, + char_backend: *mut CharBackend, + ) -> bool { // 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}"); + match offset { + DR => { + // interrupts always checked + let _ = self.loopback_tx(value); + self.int_level |= Interrupt::TX.0; + return true; } - 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.reset(); + RSR => { + self.receive_status_error_clear = 0.into(); } - Ok(FR) => { + FR => { // flag writes are ignored } - Ok(ILPR) => { + ILPR => { self.ilpr = value; } - Ok(IBRD) => { + IBRD => { self.ibrd = value; } - Ok(FBRD) => { + FBRD => { self.fbrd = value; } - Ok(LCR_H) => { + LCR_H => { let new_val: registers::LineControl = value.into(); // Reset the FIFO state on FIFO enable or disable 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 update = (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), + char_backend, CHR_IOCTL_SERIAL_SET_BREAK as i32, addr_of_mut!(break_enable).cast::<c_void>(), ); } - self.loopback_break(break_enable > 0); - } + self.loopback_break(break_enable > 0) + }; self.line_control = new_val; self.set_read_trigger(); + return update; } - Ok(CR) => { + CR => { // ??? Need to implement the enable bit. self.control = value.into(); - self.loopback_mdmctrl(); + return self.loopback_mdmctrl(); } - Ok(FLS) => { + FLS => { self.ifl = value; self.set_read_trigger(); } - Ok(IMSC) => { + IMSC => { self.int_enabled = value; - self.update(); + return true; } - Ok(RIS) => {} - Ok(MIS) => {} - Ok(ICR) => { + RIS => {} + MIS => {} + ICR => { self.int_level &= !value; - self.update(); + return true; } - Ok(DMACR) => { + DMACR => { self.dmacr = value; if value & 3 > 0 { // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); @@ -354,14 +302,12 @@ impl PL011State { } } } + false } #[inline] - fn loopback_tx(&mut self, value: u32) { - if !self.loopback_enabled() { - return; - } - + #[must_use] + fn loopback_tx(&mut self, value: u32) -> bool { // Caveat: // // In real hardware, TX loopback happens at the serial-bit level @@ -379,12 +325,13 @@ impl PL011State { // hardware flow-control is enabled. // // For simplicity, the above described is not emulated. - self.put_fifo(value); + self.loopback_enabled() && self.put_fifo(value) } - fn loopback_mdmctrl(&mut self) { + #[must_use] + fn loopback_mdmctrl(&mut self) -> bool { if !self.loopback_enabled() { - return; + return false; } /* @@ -410,51 +357,32 @@ impl PL011State { // Change interrupts based on updated FR let mut il = self.int_level; - il &= !Interrupt::MS; + il &= !Interrupt::MS.0; if self.flags.data_set_ready() { - il |= Interrupt::DSR as u32; + il |= Interrupt::DSR.0; } if self.flags.data_carrier_detect() { - il |= Interrupt::DCD as u32; + il |= Interrupt::DCD.0; } if self.flags.clear_to_send() { - il |= Interrupt::CTS as u32; + il |= Interrupt::CTS.0; } if self.flags.ring_indicator() { - il |= Interrupt::RI as u32; + il |= Interrupt::RI.0; } self.int_level = il; - self.update(); + true } - fn loopback_break(&mut self, enable: bool) { - if enable { - self.loopback_tx(registers::Data::BREAK.into()); - } + fn loopback_break(&mut self, enable: bool) -> bool { + enable && self.loopback_tx(registers::Data::BREAK.into()) } 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(); @@ -487,17 +415,6 @@ impl PL011State { self.flags.set_transmit_fifo_empty(true); } - 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.loopback_enabled() { - self.put_fifo(registers::Data::BREAK.into()); - } - } - #[inline] pub fn fifo_enabled(&self) -> bool { self.line_control.fifos_enabled() == registers::Mode::FIFO @@ -517,7 +434,8 @@ impl PL011State { 1 } - pub fn put_fifo(&mut self, value: c_uint) { + #[must_use] + pub fn put_fifo(&mut self, value: u32) -> bool { let depth = self.fifo_depth(); assert!(depth > 0); let slot = (self.read_pos + self.read_count) & (depth - 1); @@ -529,19 +447,13 @@ impl PL011State { } if self.read_count == self.read_trigger { - self.int_level |= registers::INT_RX; - self.update(); + self.int_level |= Interrupt::RX.0; + return true; } + false } - pub fn update(&self) { - let flags = self.int_level & self.int_enabled; - for (irq, i) in self.interrupts.iter().zip(IRQMASK) { - irq.set(flags & i != 0); - } - } - - pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> { + pub fn post_load(&mut self) -> Result<(), ()> { /* Sanity-check input state */ if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { return Err(()); @@ -563,19 +475,188 @@ impl PL011State { } } +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`. + unsafe fn init(&mut self) { + const CLK_NAME: &CStr = c_str!("clk"); + + // 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_NAME.as_ptr(), + 0x1000, + ); + } + + self.regs = Default::default(); + + // 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 { + let dev: &mut DeviceState = self.upcast_mut(); + 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(); + } + } + + fn post_init(&self) { + self.init_mmio(&self.iomem); + for irq in self.interrupts.iter() { + self.init_irq(irq); + } + } + + pub fn read(&mut self, offset: hwaddr, _size: u32) -> u64 { + match RegisterOffset::try_from(offset) { + Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { + let device_id = self.get_class().device_id; + u64::from(device_id[(offset - 0xfe0) >> 2]) + } + Err(_) => { + // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); + 0 + } + Ok(field) => { + let (update_irq, result) = self.regs.borrow_mut().read(field); + if update_irq { + self.update(); + unsafe { + qemu_chr_fe_accept_input(&mut self.char_backend); + } + } + result.into() + } + } + } + + pub fn write(&mut self, offset: hwaddr, value: u64) { + let mut update_irq = false; + if let Ok(field) = RegisterOffset::try_from(offset) { + // qemu_chr_fe_write_all() calls into the can_receive + // callback, so handle writes before entering PL011Registers. + if field == RegisterOffset::DR { + // ??? Check if transmitter is enabled. + let ch: u8 = value as u8; + // SAFETY: char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + // XXX this blocks entire thread. Rewrite to use + // qemu_chr_fe_write and background I/O callbacks + unsafe { + qemu_chr_fe_write_all(&mut self.char_backend, &ch, 1); + } + } + + update_irq = self + .regs + .borrow_mut() + .write(field, value as u32, &mut self.char_backend); + } else { + eprintln!("write bad offset {offset} value {value}"); + } + if update_irq { + self.update(); + } + } + + pub fn can_receive(&self) -> bool { + // trace_pl011_can_receive(s->lcr, s->read_count, r); + let regs = self.regs.borrow(); + regs.read_count < regs.fifo_depth() + } + + pub fn receive(&self, ch: u32) { + let mut regs = self.regs.borrow_mut(); + let update_irq = !regs.loopback_enabled() && regs.put_fifo(ch); + // Release the BqlRefCell before calling self.update() + drop(regs); + + if update_irq { + self.update(); + } + } + + pub fn event(&self, event: QEMUChrEvent) { + let mut update_irq = false; + let mut regs = self.regs.borrow_mut(); + if event == QEMUChrEvent::CHR_EVENT_BREAK && !regs.loopback_enabled() { + update_irq = regs.put_fifo(registers::Data::BREAK.into()); + } + // Release the BqlRefCell before calling self.update() + drop(regs); + + if update_irq { + self.update() + } + } + + pub fn realize(&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!(self.char_backend) as *mut CharBackend, + Some(pl011_can_receive), + Some(pl011_receive), + Some(pl011_event), + None, + addr_of!(*self).cast::<c_void>() as *mut c_void, + core::ptr::null_mut(), + true, + ); + } + } + + pub fn reset(&self) { + self.regs.borrow_mut().reset(); + } + + pub fn update(&self) { + let regs = self.regs.borrow(); + let flags = regs.int_level & regs.int_enabled; + for (irq, i) in self.interrupts.iter().zip(IRQMASK) { + irq.set(flags & i != 0); + } + } + + pub fn post_load(&self, _version_id: u32) -> Result<(), ()> { + self.regs.borrow_mut().post_load() + } +} + /// Which bits in the interrupt status matter for each outbound IRQ line ? -pub const IRQMASK: [u32; 6] = [ +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, + Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0, + Interrupt::RX.0, + Interrupt::TX.0, + Interrupt::RT.0, + Interrupt::MS.0, + Interrupt::E.0, ]; /// # Safety @@ -584,11 +665,8 @@ pub const IRQMASK: [u32; 6] = [ /// the same size as [`PL011State`]. We also expect the device is /// readable/writeable from one thread at any time. 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() - } + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_ref().can_receive().into() } } /// # Safety @@ -599,15 +677,11 @@ pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { /// /// The buffer and size arguments must also be valid. pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) { + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); 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())) + state.as_ref().receive(u32::from(buf.read_volatile())); } } } @@ -618,11 +692,8 @@ pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size /// the same size as [`PL011State`]. We also expect the device is /// readable/writeable from one thread at any time. pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - state.as_mut().event(event) - } + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_ref().event(event) } } /// # Safety @@ -647,7 +718,7 @@ pub unsafe extern "C" fn pl011_create( } #[repr(C)] -#[derive(Debug, qemu_api_macros::Object)] +#[derive(qemu_api_macros::Object)] /// PL011 Luminary device model. pub struct PL011Luminary { parent_obj: ParentField<PL011State>, diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 7f3ca89507..8a157a663f 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -6,45 +6,64 @@ use core::ptr::NonNull; use std::os::raw::{c_int, c_void}; use qemu_api::{ - bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_subsections, vmstate_uint32, - vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, + bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, + vmstate_subsections, vmstate_unused, zeroable::Zeroable, }; -use crate::device::{PL011State, PL011_FIFO_DEPTH}; +use crate::device::{PL011Registers, PL011State}; +#[allow(clippy::missing_const_for_fn)] extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { - unsafe { - debug_assert!(!opaque.is_null()); - let state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - state.as_ref().migrate_clock - } + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_ref().migrate_clock } } /// Migration subsection for [`PL011State`] clock. -pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { +static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { name: c_str!("pl011/clock").as_ptr(), version_id: 1, minimum_version_id: 1, needed: Some(pl011_clock_needed), fields: vmstate_fields! { - vmstate_clock!(clock, PL011State), + vmstate_clock!(PL011State, clock), }, ..Zeroable::ZERO }; extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - unsafe { - debug_assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - let result = state.as_mut().post_load(version_id as u32); - if result.is_err() { - -1 - } else { - 0 - } + let state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + let result = unsafe { state.as_ref().post_load(version_id as u32) }; + if result.is_err() { + -1 + } else { + 0 } } +static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { + name: c_str!("pl011/regs").as_ptr(), + version_id: 2, + minimum_version_id: 2, + fields: vmstate_fields! { + vmstate_of!(PL011Registers, flags), + vmstate_of!(PL011Registers, line_control), + vmstate_of!(PL011Registers, receive_status_error_clear), + vmstate_of!(PL011Registers, control), + vmstate_of!(PL011Registers, dmacr), + vmstate_of!(PL011Registers, int_enabled), + vmstate_of!(PL011Registers, int_level), + vmstate_of!(PL011Registers, read_fifo), + vmstate_of!(PL011Registers, ilpr), + vmstate_of!(PL011Registers, ibrd), + vmstate_of!(PL011Registers, fbrd), + vmstate_of!(PL011Registers, ifl), + vmstate_of!(PL011Registers, read_pos), + vmstate_of!(PL011Registers, read_count), + vmstate_of!(PL011Registers, read_trigger), + }, + ..Zeroable::ZERO +}; + pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { name: c_str!("pl011").as_ptr(), version_id: 2, @@ -52,21 +71,7 @@ pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { post_load: Some(pl011_post_load), fields: vmstate_fields! { vmstate_unused!(core::mem::size_of::<u32>()), - vmstate_uint32!(flags, PL011State), - vmstate_uint32!(line_control, PL011State), - vmstate_uint32!(receive_status_error_clear, PL011State), - vmstate_uint32!(control, PL011State), - vmstate_uint32!(dmacr, PL011State), - vmstate_uint32!(int_enabled, PL011State), - vmstate_uint32!(int_level, PL011State), - vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH), - vmstate_uint32!(ilpr, PL011State), - vmstate_uint32!(ibrd, PL011State), - vmstate_uint32!(fbrd, PL011State), - vmstate_uint32!(ifl, PL011State), - vmstate_uint32!(read_pos, PL011State), - vmstate_uint32!(read_count, PL011State), - vmstate_uint32!(read_trigger, PL011State), + vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>), }, 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 0a89d393e0..e2df4586bc 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -25,15 +25,13 @@ #![allow(clippy::upper_case_acronyms)] #![allow(clippy::result_unit_err)] -extern crate bilge; -extern crate bilge_impl; -extern crate qemu_api; - use qemu_api::c_str; -pub mod device; -pub mod device_class; -pub mod memory_ops; +mod device; +mod device_class; +mod memory_ops; + +pub use device::pl011_create; pub const TYPE_PL011: &::std::ffi::CStr = c_str!("pl011"); pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); @@ -45,8 +43,8 @@ pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug, qemu_api_macros::TryInto)] -pub enum RegisterOffset { +#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] +enum RegisterOffset { /// Data Register /// /// A write to this register initiates the actual data transmission @@ -102,10 +100,11 @@ pub enum RegisterOffset { //Reserved = 0x04C, } -pub mod registers { +mod registers { //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. use bilge::prelude::*; + use qemu_api::impl_vmstate_bitsized; /// Receive Status Register / Data Register common error bits /// @@ -172,6 +171,7 @@ pub mod registers { pub errors: Errors, _reserved: u16, } + impl_vmstate_bitsized!(Data); impl Data { // bilge is not very const-friendly, unfortunately @@ -208,6 +208,7 @@ pub mod registers { pub errors: Errors, _reserved_unpredictable: u24, } + impl_vmstate_bitsized!(ReceiveStatusErrorClear); impl ReceiveStatusErrorClear { pub fn set_from_data(&mut self, data: Data) { @@ -280,6 +281,7 @@ pub mod registers { pub ring_indicator: bool, _reserved_zero_no_modify: u23, } + impl_vmstate_bitsized!(Flags); impl Flags { pub fn reset(&mut self) { @@ -354,6 +356,7 @@ pub mod registers { /// 31:8 - Reserved, do not modify, read as zero. _reserved_zero_no_modify: u24, } + impl_vmstate_bitsized!(LineControl); impl LineControl { pub fn reset(&mut self) { @@ -498,6 +501,7 @@ pub mod registers { /// 31:16 - Reserved, do not modify, read as zero. _reserved_zero_no_modify2: u16, } + impl_vmstate_bitsized!(Control); impl Control { pub fn reset(&mut self) { @@ -516,38 +520,23 @@ pub mod registers { } /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC - pub const INT_OE: u32 = 1 << 10; - pub const INT_BE: u32 = 1 << 9; - pub const INT_PE: u32 = 1 << 8; - pub const INT_FE: u32 = 1 << 7; - pub const INT_RT: u32 = 1 << 6; - pub const INT_TX: u32 = 1 << 5; - pub const INT_RX: u32 = 1 << 4; - pub const INT_DSR: u32 = 1 << 3; - pub const INT_DCD: u32 = 1 << 2; - pub const INT_CTS: u32 = 1 << 1; - pub const INT_RI: u32 = 1 << 0; - pub const INT_E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; - pub const INT_MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; - - #[repr(u32)] - pub enum Interrupt { - OE = 1 << 10, - BE = 1 << 9, - PE = 1 << 8, - FE = 1 << 7, - RT = 1 << 6, - TX = 1 << 5, - RX = 1 << 4, - DSR = 1 << 3, - DCD = 1 << 2, - CTS = 1 << 1, - RI = 1 << 0, - } + pub struct Interrupt(pub u32); impl Interrupt { - pub const E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; - pub const MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; + pub const OE: Self = Self(1 << 10); + pub const BE: Self = Self(1 << 9); + pub const PE: Self = Self(1 << 8); + pub const FE: Self = Self(1 << 7); + pub const RT: Self = Self(1 << 6); + pub const TX: Self = Self(1 << 5); + pub const RX: Self = Self(1 << 4); + pub const DSR: Self = Self(1 << 3); + pub const DCD: Self = Self(1 << 2); + pub const CTS: Self = Self(1 << 1); + pub const RI: Self = Self(1 << 0); + + pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0); + pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0); } } diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs index c4e8599ba4..432d326389 100644 --- a/rust/hw/char/pl011/src/memory_ops.rs +++ b/rust/hw/char/pl011/src/memory_ops.rs @@ -24,28 +24,11 @@ pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { }; unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 { - assert!(!opaque.is_null()); - let mut state = unsafe { NonNull::new_unchecked(opaque.cast::<PL011State>()) }; - let val = unsafe { state.as_mut().read(addr, size) }; - match val { - std::ops::ControlFlow::Break(val) => val, - std::ops::ControlFlow::Continue(val) => { - // SAFETY: self.char_backend is a valid CharBackend instance after it's been - // initialized in realize(). - let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) }; - unsafe { - qemu_chr_fe_accept_input(cb_ptr); - } - - val - } - } + let mut state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_mut() }.read(addr, size) } unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) { - unsafe { - assert!(!opaque.is_null()); - let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>()); - state.as_mut().write(addr, data) - } + let mut state = NonNull::new(opaque).unwrap().cast::<PL011State>(); + unsafe { state.as_mut() }.write(addr, data); } |