rust: pl011: pull interrupt updates out of read/write ops

qemu_irqs are not part of the vmstate, therefore they will remain in
PL011State.  Update them if needed after regs_read()/regs_write().

Apply #[must_use] to functions that return whether the interrupt state
could have changed, so that it's harder to forget the call to update().

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2025-01-25 00:26:04 +01:00
parent 137612772e
commit ab6b6a8a55

View file

@ -242,7 +242,6 @@ impl PL011State {
} }
// Update error bits. // Update error bits.
self.receive_status_error_clear.set_from_data(c); self.receive_status_error_clear.set_from_data(c);
self.update();
// Must call qemu_chr_fe_accept_input, so return Continue: // Must call qemu_chr_fe_accept_input, so return Continue:
return ControlFlow::Continue(u32::from(c)); return ControlFlow::Continue(u32::from(c));
} }
@ -266,14 +265,15 @@ impl PL011State {
}) })
} }
fn regs_write(&mut self, offset: RegisterOffset, value: u32) { fn regs_write(&mut self, offset: RegisterOffset, value: u32) -> bool {
// eprintln!("write offset {offset} value {value}"); // eprintln!("write offset {offset} value {value}");
use RegisterOffset::*; use RegisterOffset::*;
match offset { match offset {
DR => { DR => {
self.loopback_tx(value); // interrupts always checked
let _ = self.loopback_tx(value);
self.int_level |= registers::INT_TX; self.int_level |= registers::INT_TX;
self.update(); return true;
} }
RSR => { RSR => {
self.receive_status_error_clear = 0.into(); self.receive_status_error_clear = 0.into();
@ -297,7 +297,7 @@ impl PL011State {
self.reset_rx_fifo(); self.reset_rx_fifo();
self.reset_tx_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(); let mut break_enable: c_int = new_val.send_break().into();
// SAFETY: self.char_backend is a valid CharBackend instance after it's been // SAFETY: self.char_backend is a valid CharBackend instance after it's been
// initialized in realize(). // initialized in realize().
@ -308,15 +308,16 @@ impl PL011State {
addr_of_mut!(break_enable).cast::<c_void>(), 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.line_control = new_val;
self.set_read_trigger(); self.set_read_trigger();
return update;
} }
CR => { CR => {
// ??? Need to implement the enable bit. // ??? Need to implement the enable bit.
self.control = value.into(); self.control = value.into();
self.loopback_mdmctrl(); return self.loopback_mdmctrl();
} }
FLS => { FLS => {
self.ifl = value; self.ifl = value;
@ -324,13 +325,13 @@ impl PL011State {
} }
IMSC => { IMSC => {
self.int_enabled = value; self.int_enabled = value;
self.update(); return true;
} }
RIS => {} RIS => {}
MIS => {} MIS => {}
ICR => { ICR => {
self.int_level &= !value; self.int_level &= !value;
self.update(); return true;
} }
DMACR => { DMACR => {
self.dmacr = value; self.dmacr = value;
@ -340,14 +341,12 @@ impl PL011State {
} }
} }
} }
false
} }
#[inline] #[inline]
fn loopback_tx(&mut self, value: u32) { #[must_use]
if !self.loopback_enabled() { fn loopback_tx(&mut self, value: u32) -> bool {
return;
}
// Caveat: // Caveat:
// //
// In real hardware, TX loopback happens at the serial-bit level // In real hardware, TX loopback happens at the serial-bit level
@ -365,12 +364,13 @@ impl PL011State {
// hardware flow-control is enabled. // hardware flow-control is enabled.
// //
// For simplicity, the above described is not emulated. // 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() { if !self.loopback_enabled() {
return; return false;
} }
/* /*
@ -411,13 +411,11 @@ impl PL011State {
il |= Interrupt::RI as u32; il |= Interrupt::RI as u32;
} }
self.int_level = il; self.int_level = il;
self.update(); true
} }
fn loopback_break(&mut self, enable: bool) { fn loopback_break(&mut self, enable: bool) -> bool {
if enable { enable && self.loopback_tx(registers::Data::BREAK.into())
self.loopback_tx(registers::Data::BREAK.into());
}
} }
fn set_read_trigger(&mut self) { fn set_read_trigger(&mut self) {
@ -479,14 +477,17 @@ impl PL011State {
} }
pub fn receive(&mut self, ch: u32) { pub fn receive(&mut self, ch: u32) {
if !self.loopback_enabled() { if !self.loopback_enabled() && self.put_fifo(ch) {
self.put_fifo(ch) self.update();
} }
} }
pub fn event(&mut self, event: QEMUChrEvent) { pub fn event(&mut self, event: QEMUChrEvent) {
if event == QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() { if event == QEMUChrEvent::CHR_EVENT_BREAK && !self.loopback_enabled() {
self.put_fifo(registers::Data::BREAK.into()); let update = self.put_fifo(registers::Data::BREAK.into());
if update {
self.update();
}
} }
} }
@ -509,7 +510,8 @@ impl PL011State {
1 1
} }
pub fn put_fifo(&mut self, value: u32) { #[must_use]
pub fn put_fifo(&mut self, value: u32) -> bool {
let depth = self.fifo_depth(); let depth = self.fifo_depth();
assert!(depth > 0); assert!(depth > 0);
let slot = (self.read_pos + self.read_count) & (depth - 1); let slot = (self.read_pos + self.read_count) & (depth - 1);
@ -522,8 +524,9 @@ impl PL011State {
if self.read_count == self.read_trigger { if self.read_count == self.read_trigger {
self.int_level |= registers::INT_RX; self.int_level |= registers::INT_RX;
self.update(); return true;
} }
false
} }
pub fn update(&self) { pub fn update(&self) {
@ -555,7 +558,8 @@ impl PL011State {
} }
pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow<u64, u64> { pub fn read(&mut self, offset: hwaddr, _size: u32) -> ControlFlow<u64, u64> {
match RegisterOffset::try_from(offset) { let mut update_irq = false;
let result = match RegisterOffset::try_from(offset) {
Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
let device_id = self.get_class().device_id; let device_id = self.get_class().device_id;
ControlFlow::Break(u64::from(device_id[(offset - 0xfe0) >> 2])) ControlFlow::Break(u64::from(device_id[(offset - 0xfe0) >> 2]))
@ -564,17 +568,22 @@ impl PL011State {
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
ControlFlow::Break(0) ControlFlow::Break(0)
} }
Ok(field) => { Ok(field) => match self.regs_read(field) {
let result = self.regs_read(field);
match result {
ControlFlow::Break(value) => ControlFlow::Break(value.into()), ControlFlow::Break(value) => ControlFlow::Break(value.into()),
ControlFlow::Continue(value) => ControlFlow::Continue(value.into()), ControlFlow::Continue(value) => {
} update_irq = true;
ControlFlow::Continue(value.into())
} }
},
};
if update_irq {
self.update();
} }
result
} }
pub fn write(&mut self, offset: hwaddr, value: u64) { pub fn write(&mut self, offset: hwaddr, value: u64) {
let mut update_irq = false;
if let Ok(field) = RegisterOffset::try_from(offset) { if let Ok(field) = RegisterOffset::try_from(offset) {
// qemu_chr_fe_write_all() calls into the can_receive // qemu_chr_fe_write_all() calls into the can_receive
// callback, so handle writes before entering PL011Registers. // callback, so handle writes before entering PL011Registers.
@ -590,10 +599,13 @@ impl PL011State {
} }
} }
self.regs_write(field, value as u32); update_irq = self.regs_write(field, value as u32);
} else { } else {
eprintln!("write bad offset {offset} value {value}"); eprintln!("write bad offset {offset} value {value}");
} }
if update_irq {
self.update();
}
} }
} }