Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/vm-superio-ser/src/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub struct SerialStateSer {
pub modem_status: u8,
/// Scratch Register
pub scratch: u8,
/// FIFO control register
pub fifo_control: u8,
/// Transmitter Holding Buffer/Receiver Buffer
pub in_buffer: Vec<u8>,
}
Expand All @@ -52,6 +54,7 @@ impl From<&SerialStateSer> for SerialState {
modem_control: state.modem_control,
modem_status: state.modem_status,
scratch: state.scratch,
fifo_control: state.fifo_control,
in_buffer: state.in_buffer.clone(),
}
}
Expand All @@ -69,6 +72,7 @@ impl From<&SerialState> for SerialStateSer {
modem_control: state.modem_control,
modem_status: state.modem_status,
scratch: state.scratch,
fifo_control: state.fifo_control,
in_buffer: state.in_buffer.clone(),
}
}
Expand Down
52 changes: 52 additions & 0 deletions crates/vm-superio/src/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::Trigger;
const DATA_OFFSET: u8 = 0;
const IER_OFFSET: u8 = 1;
const IIR_OFFSET: u8 = 2;
const FCR_OFFSET: u8 = 2;
const LCR_OFFSET: u8 = 3;
const MCR_OFFSET: u8 = 4;
const LSR_OFFSET: u8 = 5;
Expand Down Expand Up @@ -81,6 +82,9 @@ const MSR_RI_BIT: u8 = 0b0100_0000;
// Data Carrier Detect.
const MSR_DCD_BIT: u8 = 0b1000_0000;

const FCR_FIFO_RESET_RX: u8 = 0b0000_0010;
const FCR_FIFO_RESET_TX: u8 = 0b0000_0100;

// The following values can be used to set the baud rate to 9600 bps.
const DEFAULT_BAUD_DIVISOR_HIGH: u8 = 0x00;
const DEFAULT_BAUD_DIVISOR_LOW: u8 = 0x0C;
Expand All @@ -99,6 +103,7 @@ const DEFAULT_LINE_CONTROL: u8 = 0b0000_0011;
const DEFAULT_MODEM_CONTROL: u8 = MCR_OUT2_BIT;
const DEFAULT_MODEM_STATUS: u8 = MSR_DSR_BIT | MSR_CTS_BIT | MSR_DCD_BIT;
const DEFAULT_SCRATCH: u8 = 0x00;
const DEFAULT_FIFO_CONTROL: u8 = 0x00;

/// Defines a series of callbacks that are invoked in response to the occurrence of specific
/// events as part of the serial emulation logic (for example, when the driver reads data). The
Expand Down Expand Up @@ -174,6 +179,8 @@ pub struct SerialState {
pub modem_status: u8,
/// Scratch Register
pub scratch: u8,
/// FIFO control register
pub fifo_control: u8,
/// Transmitter Holding Buffer/Receiver Buffer
pub in_buffer: Vec<u8>,
}
Expand All @@ -190,6 +197,7 @@ impl Default for SerialState {
modem_control: DEFAULT_MODEM_CONTROL,
modem_status: DEFAULT_MODEM_STATUS,
scratch: DEFAULT_SCRATCH,
fifo_control: DEFAULT_FIFO_CONTROL,
in_buffer: Vec::new(),
}
}
Expand Down Expand Up @@ -267,6 +275,7 @@ pub struct Serial<T: Trigger, EV: SerialEvents, W: Write> {
modem_control: u8,
modem_status: u8,
scratch: u8,
fifo_control: u8,
// This is the buffer that is used for achieving the Receiver register
// functionality in FIFO mode. Reading from RBR will return the oldest
// unread byte from the RX FIFO.
Expand Down Expand Up @@ -349,6 +358,7 @@ impl<T: Trigger, EV: SerialEvents, W: Write> Serial<T, EV, W> {
modem_control: state.modem_control,
modem_status: state.modem_status,
scratch: state.scratch,
fifo_control: state.fifo_control,
in_buffer: VecDeque::from(state.in_buffer.clone()),
interrupt_evt: trigger,
events: serial_evts,
Expand Down Expand Up @@ -397,6 +407,7 @@ impl<T: Trigger, EV: SerialEvents, W: Write> Serial<T, EV, W> {
modem_control: self.modem_control,
modem_status: self.modem_status,
scratch: self.scratch,
fifo_control: self.fifo_control,
in_buffer: Vec::from(self.in_buffer.clone()),
}
}
Expand Down Expand Up @@ -535,6 +546,13 @@ impl<T: Trigger, EV: SerialEvents, W: Write> Serial<T, EV, W> {
}
// We want to enable only the interrupts that are available for 16550A (and below).
IER_OFFSET => self.interrupt_enable = value & IER_UART_VALID_BITS,
FCR_OFFSET => {
if value & FCR_FIFO_RESET_RX != 0 || value & FCR_FIFO_RESET_TX != 0 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by this. Resetting the RX buffer isn't the same thing as resetting the TX buffer.

self.in_buffer.clear();
self.clear_lsr_rda_bit();
self.events.in_buffer_empty();
}
}
LCR_OFFSET => self.line_control = value,
MCR_OFFSET => self.modem_control = value,
SCR_OFFSET => self.scratch = value,
Expand Down Expand Up @@ -1110,4 +1128,38 @@ mod tests {
// Verify the serial raised an interrupt again.
assert_eq!(intr_evt.read().unwrap(), 1);
}

fn fifo_rest_rx_tx_from_fifo_control_register(value: u8) {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink());

// Enqueue a non-empty slice.
serial.enqueue_raw_bytes(&RAW_INPUT_BUF).unwrap();

// Always verify that data ready bit in LSR is cleared off.
let lsr = serial.read(LSR_OFFSET);
assert_eq!(lsr & LSR_DATA_READY_BIT, 1);

// Verify that size of the current buffer is equal to RAW_INPUT_BUF.
assert_eq!(serial.in_buffer.len(), RAW_INPUT_BUF.len());

serial.write(FCR_OFFSET, value).unwrap();

// Verify that size of the current buffer is equal to 0 after setting 0x2 into FCR.
assert_eq!(serial.in_buffer.len(), 0);

// Always verify that data ready bit in LSR is cleared off.
let lsr = serial.read(LSR_OFFSET);
assert_eq!(lsr & LSR_DATA_READY_BIT, 0);
}

#[test]
fn test_fifo_reset_rx_from_fifo_control_register() {
fifo_rest_rx_tx_from_fifo_control_register(FCR_FIFO_RESET_RX);
}

#[test]
fn test_fifo_reset_tx_from_fifo_control_register() {
fifo_rest_rx_tx_from_fifo_control_register(FCR_FIFO_RESET_TX);
}
}