ostd/arch/x86/
serial.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! The console I/O.
4
5use spin::Once;
6use x86_64::instructions::port::ReadWriteAccess;
7
8use crate::{
9    console::uart_ns16650a::{Ns16550aAccess, Ns16550aRegister, Ns16550aUart},
10    io::{IoPort, reserve_io_port_range},
11    sync::{LocalIrqDisabled, SpinLock},
12};
13
14/// The primary serial port, which serves as an early console.
15pub static SERIAL_PORT: Once<SpinLock<Ns16550aUart<SerialAccess>, LocalIrqDisabled>> =
16    Once::initialized(SpinLock::new(Ns16550aUart::new(
17        // SAFETY:
18        // 1. It is assumed that the serial port exists and can be accessed via the I/O registers.
19        //    (FIXME: This needs to be confirmed by checking the ACPI table or using kernel
20        //    parameters to obtain early information for building the early console.)
21        // 2. `reserve_io_port_range` guarantees exclusive ownership of the I/O registers.
22        unsafe { SerialAccess::new(0x3F8) },
23    )));
24reserve_io_port_range!(0x3F8..0x400);
25
26/// Access to serial registers via I/O ports in x86.
27#[derive(Debug)]
28pub struct SerialAccess {
29    data: IoPort<u8, ReadWriteAccess>,
30    int_en: IoPort<u8, ReadWriteAccess>,
31    fifo_ctrl: IoPort<u8, ReadWriteAccess>,
32    line_ctrl: IoPort<u8, ReadWriteAccess>,
33    modem_ctrl: IoPort<u8, ReadWriteAccess>,
34    line_stat: IoPort<u8, ReadWriteAccess>,
35    modem_stat: IoPort<u8, ReadWriteAccess>,
36}
37
38impl SerialAccess {
39    /// # Safety
40    ///
41    /// The caller must ensure that the base port is a valid serial base port and that it has
42    /// exclusive ownership of the serial registers.
43    const unsafe fn new(port: u16) -> Self {
44        // SAFETY: The safety is upheld by the caller.
45        unsafe {
46            Self {
47                data: IoPort::new(port),
48                int_en: IoPort::new(port + 1),
49                fifo_ctrl: IoPort::new(port + 2),
50                line_ctrl: IoPort::new(port + 3),
51                modem_ctrl: IoPort::new(port + 4),
52                line_stat: IoPort::new(port + 5),
53                modem_stat: IoPort::new(port + 6),
54            }
55        }
56    }
57}
58
59impl Ns16550aAccess for SerialAccess {
60    fn read(&self, reg: Ns16550aRegister) -> u8 {
61        match reg {
62            Ns16550aRegister::DataOrDivisorLo => self.data.read(),
63            Ns16550aRegister::IntEnOrDivisorHi => self.int_en.read(),
64            Ns16550aRegister::FifoCtrl => self.fifo_ctrl.read(),
65            Ns16550aRegister::LineCtrl => self.line_ctrl.read(),
66            Ns16550aRegister::ModemCtrl => self.modem_ctrl.read(),
67            Ns16550aRegister::LineStat => self.line_stat.read(),
68            Ns16550aRegister::ModemStat => self.modem_stat.read(),
69        }
70    }
71
72    fn write(&mut self, reg: Ns16550aRegister, val: u8) {
73        match reg {
74            Ns16550aRegister::DataOrDivisorLo => self.data.write(val),
75            Ns16550aRegister::IntEnOrDivisorHi => self.int_en.write(val),
76            Ns16550aRegister::FifoCtrl => self.fifo_ctrl.write(val),
77            Ns16550aRegister::LineCtrl => self.line_ctrl.write(val),
78            Ns16550aRegister::ModemCtrl => self.modem_ctrl.write(val),
79            Ns16550aRegister::LineStat => self.line_stat.write(val),
80            Ns16550aRegister::ModemStat => self.modem_stat.write(val),
81        }
82    }
83}
84
85/// Initializes the serial port.
86pub(crate) fn init() {
87    SERIAL_PORT.get().unwrap().lock().init();
88}