From d062323dce5a86e252d80c5426061bb9d91dfda3 Mon Sep 17 00:00:00 2001 From: Jinank Jain Date: Tue, 29 Aug 2023 16:15:03 +0530 Subject: [PATCH 1/2] serial: Add FCR register to FIFO state In order to add support for controlling FIFO using FIFO control register we need to persist the state of FCR. Thus, add FCR to state of serial console. Signed-off-by: Jinank Jain --- crates/vm-superio-ser/src/serial.rs | 4 ++++ crates/vm-superio/src/serial.rs | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/crates/vm-superio-ser/src/serial.rs b/crates/vm-superio-ser/src/serial.rs index 5358944..c1ea10d 100644 --- a/crates/vm-superio-ser/src/serial.rs +++ b/crates/vm-superio-ser/src/serial.rs @@ -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, } @@ -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(), } } @@ -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(), } } diff --git a/crates/vm-superio/src/serial.rs b/crates/vm-superio/src/serial.rs index 8c30c60..0793fd1 100644 --- a/crates/vm-superio/src/serial.rs +++ b/crates/vm-superio/src/serial.rs @@ -99,6 +99,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 @@ -174,6 +175,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, } @@ -190,6 +193,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(), } } @@ -267,6 +271,7 @@ pub struct Serial { 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. @@ -349,6 +354,7 @@ impl Serial { 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, @@ -397,6 +403,7 @@ impl Serial { 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()), } } From 073a99b49599368cdf5d93af961eaf15f9061ce4 Mon Sep 17 00:00:00 2001 From: Jinank Jain Date: Tue, 29 Aug 2023 17:52:40 +0530 Subject: [PATCH 2/2] serial: Reset RX/TX queue using FCR register Currently I am handling partial writes to FIFO control register which deals with clearing RX/TX queues. There are other bits which deals with disabling/enabling FIFO along with setting different ITL mode. I think those can be added a later point when the need arises for it. This should fix the behavior from FreeBSD's point of view. Signed-off-by: Jinank Jain --- crates/vm-superio/src/serial.rs | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/crates/vm-superio/src/serial.rs b/crates/vm-superio/src/serial.rs index 0793fd1..bc113a6 100644 --- a/crates/vm-superio/src/serial.rs +++ b/crates/vm-superio/src/serial.rs @@ -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; @@ -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; @@ -542,6 +546,13 @@ impl Serial { } // 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 { + 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, @@ -1117,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); + } }