x86_q35/
serial.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2024.
4
5//! Support for legacy 8250-compatible serial ports.
6//!
7//! This module implements support for 8250-compatible UART devices. These are somewhat common on
8//! x86 platforms and provide a simple interface for diagnostics and debugging.
9//!
10//! This implementation is based on guidance from the following sources:
11//!
12//! * <https://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming>
13//! * <https://wiki.osdev.org/Serial_Ports>
14//! * <https://docs.freebsd.org/en/articles/serial-uart/index.html>
15
16use core::cell::Cell;
17use core::fmt::{self, Write};
18use core::mem::MaybeUninit;
19
20use x86::registers::io;
21
22use kernel::component::Component;
23use kernel::debug::IoWrite;
24use kernel::deferred_call::{DeferredCall, DeferredCallClient};
25use kernel::hil::uart::{
26    Configure, Error, Parameters, Parity, Receive, ReceiveClient, StopBits, Transmit,
27    TransmitClient, Width,
28};
29use kernel::utilities::cells::OptionalCell;
30use kernel::ErrorCode;
31use tock_cells::take_cell::TakeCell;
32use tock_registers::{register_bitfields, LocalRegisterCopy};
33
34/// Base I/O port address of the standard COM1 serial device.
35pub const COM1_BASE: u16 = 0x03F8;
36
37/// Base I/O port address of the standard COM2 serial device.
38pub const COM2_BASE: u16 = 0x02F8;
39
40/// Base I/O port address of the standard COM3 serial device.
41pub const COM3_BASE: u16 = 0x03E8;
42
43/// Base I/O port address of the standard COM4 serial device.
44pub const COM4_BASE: u16 = 0x02E8;
45
46/// Fixed clock frequency used to generate baud rate on 8250-compatible UART devices.
47const BAUD_CLOCK: u32 = 115_200;
48
49/// The following offsets are relative to the base I/O port address of an 8250-compatible UART
50/// device. Reference: <https://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming>
51mod offsets {
52    /// Transmit Holding Register
53    pub(crate) const THR: u16 = 0;
54
55    /// Receive Buffer Register
56    pub(crate) const RBR: u16 = 0;
57
58    /// Divisor Latch Low Register
59    pub(crate) const DLL: u16 = 0;
60
61    /// Interrupt Enable Register
62    pub(crate) const IER: u16 = 1;
63
64    /// Divisor Latch High Register
65    pub(crate) const DLH: u16 = 1;
66
67    /// Interrupt Identification Register
68    pub(crate) const IIR: u16 = 2;
69
70    /// FIFO Control Register
71    pub(crate) const FCR: u16 = 2;
72
73    /// Line Control Register
74    pub(crate) const LCR: u16 = 3;
75
76    /// Line Status Register
77    pub(crate) const LSR: u16 = 5;
78}
79
80register_bitfields!(u8,
81    /// Interrupt Identification Register
82    IIR[
83        INTERRUPT_PENDING OFFSET(0) NUMBITS(1) [],
84        INTERRUPT_ID OFFSET(1) NUMBITS(3) [
85            THRE = 0b001, // Transmit Holding Register Empty
86            RDA = 0b010   // Received Data Available
87        ]
88    ],
89
90    /// Interrupt Enable Register
91    IER [
92        // IER: Interrupt Enable Register
93        RDA OFFSET(0) NUMBITS(1) [], // Received Data Available
94        THRE OFFSET(1) NUMBITS(1) [] // Transmit Holding Register Empty
95    ],
96
97    /// Line Control Register
98    LCR [
99        DLAB OFFSET(7) NUMBITS(1) [], // Divisor Latch Access Bit
100        PARITY OFFSET(3) NUMBITS(3) [
101            None = 0,
102            Odd = 1,
103            Even = 3,
104            Mark = 5,
105            Space = 7,
106        ],
107        STOP_BITS OFFSET(2) NUMBITS(1) [
108            One = 0,
109            Two = 1
110        ],
111        DATA_SIZE OFFSET(0) NUMBITS(2) [
112            Five = 0,
113            Six = 1,
114            Seven = 2,
115            Eight = 3
116        ]
117    ],
118
119    /// Line Status Register
120    LSR [
121        THRE OFFSET(5) NUMBITS(1) [] // Transmit Holding Register Empty
122    ],
123);
124
125pub struct SerialPort<'a> {
126    /// Base I/O port address
127    base: u16,
128
129    /// Client of transmit operations
130    tx_client: OptionalCell<&'a dyn TransmitClient>,
131
132    /// Buffer of data to transmit
133    tx_buffer: TakeCell<'static, [u8]>,
134
135    /// Number of bytes to transmit from tx_buffer
136    tx_len: Cell<usize>,
137
138    /// Index of next byte within tx_buffer to be transmitted
139    tx_index: Cell<usize>,
140
141    /// Whether the currently pending transmit has been aborted
142    tx_abort: Cell<bool>,
143
144    /// Client of receive operations
145    rx_client: OptionalCell<&'a dyn ReceiveClient>,
146
147    /// Buffer for received data
148    rx_buffer: TakeCell<'static, [u8]>,
149
150    /// Number of bytes to receive into rx_buffer
151    rx_len: Cell<usize>,
152
153    /// Index of next byte within rx_buffer to be received
154    rx_index: Cell<usize>,
155
156    /// Whether the currently pending receive has been aborted
157    rx_abort: Cell<bool>,
158
159    /// Deferred call instance
160    dc: DeferredCall,
161}
162
163impl SerialPort<'_> {
164    /// Finishes out a long-running TX operation.
165    fn finish_tx(&self, res: Result<(), ErrorCode>) {
166        if let Some(b) = self.tx_buffer.take() {
167            self.tx_client.map(|c| {
168                c.transmitted_buffer(b, self.tx_len.get(), res);
169            });
170        }
171    }
172
173    /// Finishes out a long-running RX operation.
174    fn finish_rx(&self, res: Result<(), ErrorCode>, error: Error) {
175        // Turn off RX interrupts
176        unsafe {
177            let ier_value = io::inb(self.base + offsets::IER);
178            let mut ier: LocalRegisterCopy<u8, IER::Register> = LocalRegisterCopy::new(ier_value);
179            ier.modify(IER::RDA::CLEAR);
180            io::outb(self.base + offsets::IER, ier.get());
181        }
182
183        if let Some(b) = self.rx_buffer.take() {
184            self.rx_client
185                .map(|c| c.received_buffer(b, self.rx_len.get(), res, error));
186        }
187    }
188
189    /// Handler to call when a TX interrupt occurs.
190    fn handle_tx_interrupt(&self) {
191        if self.tx_index.get() < self.tx_len.get() {
192            // Still have bytes to send
193            let tx_index = self.tx_index.get();
194            self.tx_buffer.map(|b| unsafe {
195                io::outb(self.base + offsets::THR, b[tx_index]);
196            });
197            self.tx_index.set(tx_index + 1);
198        } else {
199            self.finish_tx(Ok(()));
200        }
201    }
202
203    /// Handler to call when an RX interrupt occurs.
204    fn handle_rx_interrupt(&self) {
205        if self.rx_index.get() < self.rx_len.get() {
206            // Still have bytes to receive
207            let rx_index = self.rx_index.get();
208            self.rx_buffer.map(|b| unsafe {
209                b[rx_index] = io::inb(self.base + offsets::RBR);
210            });
211            self.rx_index.set(rx_index + 1);
212        }
213
214        if self.rx_index.get() == self.rx_len.get() {
215            self.finish_rx(Ok(()), Error::None);
216        }
217    }
218
219    /// Handler to call when a serial port interrupt is received.
220    pub fn handle_interrupt(&self) {
221        // There may be multiple interrupts pending for this device, but IIR will only show the
222        // highest-priority one. So we need to read IIR in a loop until the Interrupt Pending flag
223        // becomes set (indicating that there are no more pending interrupts).
224        loop {
225            let iir_val = unsafe { io::inb(self.base + offsets::IIR) };
226
227            let iir: LocalRegisterCopy<u8, IIR::Register> = LocalRegisterCopy::new(iir_val);
228
229            if iir.is_set(IIR::INTERRUPT_PENDING) {
230                // No interrupt pending for this port
231                return;
232            }
233
234            if iir.matches_all(IIR::INTERRUPT_ID::THRE) {
235                self.handle_tx_interrupt();
236            } else if iir.matches_all(IIR::INTERRUPT_ID::RDA) {
237                self.handle_rx_interrupt();
238            } else {
239                unimplemented!();
240            }
241        }
242    }
243}
244
245impl Configure for SerialPort<'_> {
246    fn configure(&self, params: Parameters) -> Result<(), ErrorCode> {
247        if params.baud_rate == 0 {
248            return Err(ErrorCode::INVAL);
249        }
250
251        if params.hw_flow_control {
252            return Err(ErrorCode::NOSUPPORT);
253        }
254
255        let divisor = BAUD_CLOCK / params.baud_rate;
256        if divisor == 0 || divisor > u16::MAX.into() {
257            return Err(ErrorCode::NOSUPPORT);
258        }
259
260        // Compute value for the line control register
261
262        let mut lcr: LocalRegisterCopy<u8, LCR::Register> = LocalRegisterCopy::new(0);
263
264        lcr.modify(match params.width {
265            Width::Six => LCR::DATA_SIZE::Six,
266            Width::Seven => LCR::DATA_SIZE::Seven,
267            Width::Eight => LCR::DATA_SIZE::Eight,
268        });
269
270        lcr.modify(match params.stop_bits {
271            StopBits::One => LCR::STOP_BITS::One,
272            StopBits::Two => LCR::STOP_BITS::Two,
273        });
274
275        lcr.modify(match params.parity {
276            Parity::None => LCR::PARITY::None,
277            Parity::Odd => LCR::PARITY::Odd,
278            Parity::Even => LCR::PARITY::Even,
279        });
280
281        lcr.modify(LCR::DLAB::SET);
282
283        unsafe {
284            // Program line control, and set DLAB so we can program baud divisor
285            io::outb(self.base + offsets::LCR, lcr.get());
286
287            // Program the divisor and clear DLAB
288            lcr.modify(LCR::DLAB::CLEAR);
289            let divisor_bytes = divisor.to_le_bytes();
290            io::outb(self.base + offsets::DLL, divisor_bytes[0]);
291            io::outb(self.base + offsets::DLH, divisor_bytes[1]);
292            io::outb(self.base + offsets::LCR, lcr.get());
293
294            // Disable FIFOs
295            io::outb(self.base + offsets::FCR, 0);
296
297            // Read IIR once to clear any pending interrupts
298            let _ = io::inb(self.base + offsets::IIR);
299
300            // Start with all interrupts disabled
301            io::outb(self.base + offsets::IER, 0);
302        }
303
304        Ok(())
305    }
306}
307
308impl<'a> Transmit<'a> for SerialPort<'a> {
309    fn set_transmit_client(&self, client: &'a dyn TransmitClient) {
310        self.tx_client.set(client);
311    }
312
313    fn transmit_buffer(
314        &self,
315        tx_buffer: &'static mut [u8],
316        tx_len: usize,
317    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
318        if self.tx_buffer.is_some() {
319            return Err((ErrorCode::BUSY, tx_buffer));
320        }
321
322        if tx_len == 0 || tx_len > tx_buffer.len() {
323            return Err((ErrorCode::SIZE, tx_buffer));
324        }
325
326        // Transmit the first byte
327        unsafe { io::outb(self.base + offsets::THR, tx_buffer[0]) };
328
329        self.tx_buffer.replace(tx_buffer);
330        self.tx_len.set(tx_len);
331        self.tx_index.set(1);
332
333        // Enable TX interrupts
334        unsafe {
335            let ier_value = io::inb(self.base + offsets::IER);
336            let mut ier: LocalRegisterCopy<u8, IER::Register> = LocalRegisterCopy::new(ier_value);
337            ier.modify(IER::THRE::SET);
338            io::outb(self.base + offsets::IER, ier.get());
339        }
340
341        Ok(())
342    }
343
344    fn transmit_word(&self, _word: u32) -> Result<(), ErrorCode> {
345        unimplemented!()
346    }
347
348    fn transmit_abort(&self) -> Result<(), ErrorCode> {
349        if self.tx_buffer.is_none() {
350            return Ok(());
351        }
352
353        self.tx_abort.set(true);
354        self.dc.set();
355
356        Err(ErrorCode::BUSY)
357    }
358}
359
360impl<'a> Receive<'a> for SerialPort<'a> {
361    fn set_receive_client(&self, client: &'a dyn ReceiveClient) {
362        self.rx_client.set(client);
363    }
364
365    fn receive_buffer(
366        &self,
367        rx_buffer: &'static mut [u8],
368        rx_len: usize,
369    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
370        if self.rx_buffer.is_some() {
371            return Err((ErrorCode::BUSY, rx_buffer));
372        }
373
374        if rx_len == 0 || rx_len > rx_buffer.len() {
375            return Err((ErrorCode::SIZE, rx_buffer));
376        }
377
378        self.rx_buffer.replace(rx_buffer);
379        self.rx_len.set(rx_len);
380        self.rx_index.set(0);
381
382        // Enable RX interrupts
383        unsafe {
384            let ier_value = io::inb(self.base + offsets::IER);
385            let mut ier: LocalRegisterCopy<u8, IER::Register> = LocalRegisterCopy::new(ier_value);
386            ier.modify(IER::RDA::SET);
387            io::outb(self.base + offsets::IER, ier.get());
388        }
389
390        Ok(())
391    }
392
393    fn receive_word(&self) -> Result<(), ErrorCode> {
394        unimplemented!()
395    }
396
397    fn receive_abort(&self) -> Result<(), ErrorCode> {
398        if self.rx_buffer.is_none() {
399            return Ok(());
400        }
401
402        self.rx_abort.set(true);
403        self.dc.set();
404
405        Err(ErrorCode::BUSY)
406    }
407}
408
409impl DeferredCallClient for SerialPort<'_> {
410    fn handle_deferred_call(&self) {
411        if self.tx_abort.get() {
412            self.finish_tx(Err(ErrorCode::CANCEL));
413            self.tx_abort.set(false);
414        }
415
416        if self.rx_abort.get() {
417            self.finish_rx(Err(ErrorCode::CANCEL), Error::None);
418            self.rx_abort.set(false);
419        }
420    }
421
422    fn register(&'static self) {
423        self.dc.register(self);
424    }
425}
426
427/// Component interface used to instantiate a [`SerialPort`]
428pub struct SerialPortComponent {
429    base: u16,
430}
431
432impl SerialPortComponent {
433    /// Constructs and returns a new instance of `SerialPortComponent`.
434    ///
435    /// ## Safety
436    ///
437    /// An 8250-compatible serial port must exist at the specified address. Otherwise we could end
438    /// up spamming some unknown device with I/O operations.
439    ///
440    /// The specified serial port must not be in use by any other instance of `SerialPort` or any
441    /// other code.
442    pub unsafe fn new(base: u16) -> Self {
443        Self { base }
444    }
445}
446
447impl Component for SerialPortComponent {
448    type StaticInput = (&'static mut MaybeUninit<SerialPort<'static>>,);
449    type Output = &'static SerialPort<'static>;
450
451    fn finalize(self, s: Self::StaticInput) -> Self::Output {
452        let serial = s.0.write(SerialPort {
453            base: self.base,
454            tx_client: OptionalCell::empty(),
455            tx_buffer: TakeCell::empty(),
456            tx_len: Cell::new(0),
457            tx_index: Cell::new(0),
458            tx_abort: Cell::new(false),
459            rx_client: OptionalCell::empty(),
460            rx_buffer: TakeCell::empty(),
461            rx_len: Cell::new(0),
462            rx_index: Cell::new(0),
463            rx_abort: Cell::new(false),
464            dc: DeferredCall::new(),
465        });
466
467        // Deferred call registration
468        serial.register();
469
470        serial
471    }
472}
473
474/// Statically allocates the storage needed to finalize a [`SerialPortComponent`].
475#[macro_export]
476macro_rules! serial_port_component_static {
477    () => {{
478        (kernel::static_buf!($crate::serial::SerialPort<'static>),)
479    }};
480}
481
482/// Serial port handle for blocking I/O
483///
484/// This struct is a lightweight version of [`SerialPort`] that can be used to perform blocking
485/// serial I/O (via [`Write`] or [`IoWrite`]). It is intended for use in places where
486/// interrupt-driven I/O is not possible, such as early bootstrapping or panic handling.
487pub struct BlockingSerialPort(u16);
488
489impl BlockingSerialPort {
490    /// Creates and returns a new `BlockingSerialPort` instance.
491    ///
492    /// ## Safety
493    ///
494    /// An 8250-compatible serial port must exist at the specified address. Otherwise we could end
495    /// up spamming some unknown device with I/O operations.
496    ///
497    /// For a given `base` address, there must be no other `SerialPort` or `BlockingSerialPort` in
498    /// active use.
499    pub unsafe fn new(base: u16) -> Self {
500        Self(base)
501    }
502}
503
504impl Write for BlockingSerialPort {
505    fn write_str(&mut self, s: &str) -> fmt::Result {
506        self.write(s.as_bytes());
507        Ok(())
508    }
509}
510
511impl IoWrite for BlockingSerialPort {
512    fn write(&mut self, buf: &[u8]) -> usize {
513        unsafe {
514            for b in buf {
515                // Wait for any pending transmission to complete
516                loop {
517                    let line_status_value = io::inb(self.0 + offsets::LSR);
518                    let lsr: LocalRegisterCopy<u8, LSR::Register> =
519                        LocalRegisterCopy::new(line_status_value);
520                    if lsr.is_set(LSR::THRE) {
521                        break;
522                    }
523                }
524
525                io::outb(self.0 + offsets::THR, *b);
526            }
527        }
528
529        buf.len()
530    }
531}