As an added bonus, this also makes the new function return u32 instead of u64, thus factoring some casts into a single place.
Reviewed-by: Zhao Liu <zhao1....@intel.com> Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- rust/hw/char/pl011/src/device.rs | 137 ++++++++++++++++++------------- rust/hw/char/pl011/src/lib.rs | 2 +- 2 files changed, 79 insertions(+), 60 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index c8496eeb1b6..64e7234f627 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -5,6 +5,7 @@ use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, + ops::ControlFlow, os::raw::{c_int, c_uint, c_void}, }; @@ -222,19 +223,11 @@ fn post_init(&self) { } } - pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> { + fn regs_read(&mut self, offset: RegisterOffset) -> ControlFlow<u32, 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) => { + ControlFlow::Break(match offset { + DR => { self.flags.set_receive_fifo_full(false); let c = self.read_fifo[self.read_pos]; if self.read_count > 0 { @@ -251,69 +244,53 @@ pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u 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)); + return ControlFlow::Continue(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, - }; - std::ops::ControlFlow::Break(value.into()) + DMACR => self.dmacr, + }) } - pub fn write(&mut self, offset: hwaddr, value: u64) { + fn regs_write(&mut self, offset: RegisterOffset, value: u32) { // 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); - } + match offset { + DR => { 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() { @@ -336,26 +313,26 @@ pub fn write(&mut self, offset: hwaddr, value: u64) { self.line_control = new_val; self.set_read_trigger(); } - Ok(CR) => { + CR => { // ??? Need to implement the enable bit. self.control = value.into(); self.loopback_mdmctrl(); } - Ok(FLS) => { + FLS => { self.ifl = value; self.set_read_trigger(); } - Ok(IMSC) => { + IMSC => { self.int_enabled = value; self.update(); } - Ok(RIS) => {} - Ok(MIS) => {} - Ok(ICR) => { + RIS => {} + MIS => {} + ICR => { self.int_level &= !value; self.update(); } - Ok(DMACR) => { + DMACR => { self.dmacr = value; if value & 3 > 0 { // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); @@ -570,6 +547,48 @@ pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> { Ok(()) } + + pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow<u64, u64> { + match RegisterOffset::try_from(offset) { + Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { + let device_id = self.get_class().device_id; + ControlFlow::Break(u64::from(device_id[(offset - 0xfe0) >> 2])) + } + Err(_) => { + // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); + ControlFlow::Break(0) + } + Ok(field) => { + let result = self.regs_read(field); + match result { + ControlFlow::Break(value) => ControlFlow::Break(value.into()), + ControlFlow::Continue(value) => ControlFlow::Continue(value.into()), + } + } + } + } + + pub fn write(&mut self, offset: hwaddr, value: u64) { + 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); + } + } + + self.regs_write(field, value as u32); + } else { + eprintln!("write bad offset {offset} value {value}"); + } + } } /// Which bits in the interrupt status matter for each outbound IRQ line ? diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 2baacba2306..a35fff8d44d 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -43,7 +43,7 @@ #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] -#[derive(Debug, qemu_api_macros::TryInto)] +#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] enum RegisterOffset { /// Data Register /// -- 2.48.1