mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 15:23:53 -06:00
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:
parent
137612772e
commit
ab6b6a8a55
1 changed files with 48 additions and 36 deletions
|
@ -242,7 +242,6 @@ impl PL011State {
|
|||
}
|
||||
// Update error bits.
|
||||
self.receive_status_error_clear.set_from_data(c);
|
||||
self.update();
|
||||
// Must call qemu_chr_fe_accept_input, so return Continue:
|
||||
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}");
|
||||
use RegisterOffset::*;
|
||||
match offset {
|
||||
DR => {
|
||||
self.loopback_tx(value);
|
||||
// interrupts always checked
|
||||
let _ = self.loopback_tx(value);
|
||||
self.int_level |= registers::INT_TX;
|
||||
self.update();
|
||||
return true;
|
||||
}
|
||||
RSR => {
|
||||
self.receive_status_error_clear = 0.into();
|
||||
|
@ -297,7 +297,7 @@ impl PL011State {
|
|||
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().
|
||||
|
@ -308,15 +308,16 @@ impl PL011State {
|
|||
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;
|
||||
}
|
||||
CR => {
|
||||
// ??? Need to implement the enable bit.
|
||||
self.control = value.into();
|
||||
self.loopback_mdmctrl();
|
||||
return self.loopback_mdmctrl();
|
||||
}
|
||||
FLS => {
|
||||
self.ifl = value;
|
||||
|
@ -324,13 +325,13 @@ impl PL011State {
|
|||
}
|
||||
IMSC => {
|
||||
self.int_enabled = value;
|
||||
self.update();
|
||||
return true;
|
||||
}
|
||||
RIS => {}
|
||||
MIS => {}
|
||||
ICR => {
|
||||
self.int_level &= !value;
|
||||
self.update();
|
||||
return true;
|
||||
}
|
||||
DMACR => {
|
||||
self.dmacr = value;
|
||||
|
@ -340,14 +341,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
|
||||
|
@ -365,12 +364,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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -411,13 +411,11 @@ impl PL011State {
|
|||
il |= Interrupt::RI as u32;
|
||||
}
|
||||
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) {
|
||||
|
@ -479,14 +477,17 @@ impl PL011State {
|
|||
}
|
||||
|
||||
pub fn receive(&mut self, ch: u32) {
|
||||
if !self.loopback_enabled() {
|
||||
self.put_fifo(ch)
|
||||
if !self.loopback_enabled() && self.put_fifo(ch) {
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(&mut self, event: QEMUChrEvent) {
|
||||
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
|
||||
}
|
||||
|
||||
pub fn put_fifo(&mut self, value: u32) {
|
||||
#[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);
|
||||
|
@ -522,8 +524,9 @@ impl PL011State {
|
|||
|
||||
if self.read_count == self.read_trigger {
|
||||
self.int_level |= registers::INT_RX;
|
||||
self.update();
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn update(&self) {
|
||||
|
@ -555,7 +558,8 @@ impl PL011State {
|
|||
}
|
||||
|
||||
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)) => {
|
||||
let device_id = self.get_class().device_id;
|
||||
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);
|
||||
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()),
|
||||
Ok(field) => match self.regs_read(field) {
|
||||
ControlFlow::Break(value) => ControlFlow::Break(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) {
|
||||
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.
|
||||
|
@ -590,10 +599,13 @@ impl PL011State {
|
|||
}
|
||||
}
|
||||
|
||||
self.regs_write(field, value as u32);
|
||||
update_irq = self.regs_write(field, value as u32);
|
||||
} else {
|
||||
eprintln!("write bad offset {offset} value {value}");
|
||||
}
|
||||
if update_irq {
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue