Skip to main content

kernel/logging/x86_64/
serial.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! x86_64 UART serial port implementation of [`_Serial`].
3//!
4//! Authors: MarioS271
5
6use core::sync::atomic::{AtomicBool, Ordering};
7use x86_64::instructions::port::Port;
8use crate::logging::_serial::{SerialPort, _Serial};
9
10static COM1_BASE_ADDRESS: u16 = 0x03F8;
11static COM2_BASE_ADDRESS: u16 = 0x02F8;
12static COM3_BASE_ADDRESS: u16 = 0x03E8;
13static COM4_BASE_ADDRESS: u16 = 0x02E8;
14
15/// x86_64 UART serial port implementing [`_Serial`]; do not `write()` before `init()`.
16pub struct Serial {
17    initialized: AtomicBool,
18    /// I/O base address for this COM port (e.g., `0x03F8` for COM1).
19    base_addr: u16,
20}
21
22impl _Serial for Serial {
23    fn new(port: SerialPort) -> Self {
24        let base_address = match port {
25            SerialPort::Serial1 => { COM1_BASE_ADDRESS }
26            SerialPort::Serial2 => { COM2_BASE_ADDRESS }
27            SerialPort::Serial3 => { COM3_BASE_ADDRESS }
28            SerialPort::Serial4 => { COM4_BASE_ADDRESS }
29        };
30
31        Self {
32            initialized: AtomicBool::new(false),
33            base_addr: base_address,
34        }
35    }
36
37    fn init(&self) -> Result<(), &'static str> {
38        // Safe because:
39        // 1) static mut
40        //    No threading exists at the time of this being executed, as this function gets called
41        //    as one of the first things in kmain
42        // 2) at_offset().write()
43        //    Safe because only writing to common UART registers (follows the standard 16550 spec,
44        //    therefore will not corrupt unrelated things)
45        unsafe {
46            if self.initialized.load(Ordering::Acquire) {
47                return Err("Serial is already initialized");
48            }
49
50            self.initialized.store(true, Ordering::Relaxed);
51
52            at_offset(self.base_addr, 1).write(0x00);     // Disable Interrupts.
53            at_offset(self.base_addr, 3).write(0x80);     // Enable DLAB (divisor latch access bit)
54            at_offset(self.base_addr, 0).write(0x01);     // Baud Rate Divisor (low byte)
55            at_offset(self.base_addr, 1).write(0x00);     // Baud Rate Divisor (high byte)
56            at_offset(self.base_addr, 3).write(0x03);     // Disable DLAB and configure line
57            at_offset(self.base_addr, 2).write(0xC7);     // Enable and clear FIFO
58            at_offset(self.base_addr, 4).write(0x0B);     // Set DTR, RTS, OUT2
59
60            Ok(())
61        }
62    }
63
64    fn write(&self, string: &str) {
65        let mut port: Port<u8> = Port::new(self.base_addr);
66
67        for byte in string.bytes() {
68            // This calls write_byte which was already declared to be safe because
69            // it adheres to 16550 spec
70            unsafe {
71                // Wait for
72                while at_offset(self.base_addr, 5).read() & 0x20 == 0 {
73                    core::hint::spin_loop();
74                }
75                port.write(byte);
76            }
77        }
78    }
79}
80
81/// Return a `Port` for the UART register at `offset` from `base_addr`.
82fn at_offset(base_addr: u16, offset: u16) -> Port<u8> {
83    Port::new(base_addr + offset)
84}