minimal_rt/arch/x86_64/
serial.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Serial output for debugging.
5
6use core::arch::asm;
7use core::fmt;
8
9const COM3: u16 = 0x3e8;
10
11/// Write a byte to a port.
12///
13/// # Safety
14///
15/// The caller must be sure that the given port is safe to write to, and that the
16/// given value is safe for it.
17unsafe fn outb(port: u16, data: u8) {
18    // SAFETY: The caller has assured us this is safe.
19    unsafe {
20        asm! {
21            "out dx, al",
22            in("dx") port,
23            in("al") data,
24        }
25    }
26}
27
28/// Read a byte from a port.
29///
30/// # Safety
31///
32/// The caller must be sure that the given port is safe to read from.
33unsafe fn inb(port: u16) -> u8 {
34    let mut data;
35    // SAFETY: The caller has assured us this is safe.
36    unsafe {
37        asm! {
38            "in al, dx",
39            in("dx") port,
40            out("al") data,
41        }
42    }
43    data
44}
45
46/// A trait to access io ports used by the serial device.
47pub trait IoAccess {
48    /// Issue an in byte instruction.
49    ///
50    /// # Safety
51    ///
52    /// The caller must be sure that the given port is safe to read from.
53    unsafe fn inb(&self, port: u16) -> u8;
54    /// Issue an out byte instruction.
55    ///
56    /// # Safety
57    ///
58    /// The caller must be sure that the given port is safe to write to, and that the
59    /// given value is safe for it.
60    unsafe fn outb(&self, port: u16, data: u8);
61}
62
63/// A struct to access io ports using in/out instructions.
64pub struct InstrIoAccess;
65
66impl IoAccess for InstrIoAccess {
67    unsafe fn inb(&self, port: u16) -> u8 {
68        // SAFETY: The serial port caller has specified a valid port.
69        unsafe { inb(port) }
70    }
71
72    unsafe fn outb(&self, port: u16, data: u8) {
73        // SAFETY: The serial port caller has specified a valid port and data.
74        unsafe { outb(port, data) }
75    }
76}
77
78/// A writer for the COM3 UART.
79pub struct Serial<T: IoAccess> {
80    io: T,
81}
82
83impl<T: IoAccess> Serial<T> {
84    /// Initialize the serial port.
85    pub fn init(io: T) -> Self {
86        // SAFETY: Writing these values to the serial device is safe.
87        unsafe {
88            io.outb(COM3 + 1, 0x00); // Disable all interrupts
89            io.outb(COM3 + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
90            io.outb(COM3 + 4, 0x0F);
91        }
92
93        Self { io }
94    }
95
96    /// Create an instance without calling init.
97    pub fn new(io: T) -> Self {
98        Self { io }
99    }
100
101    fn write_byte(&self, b: u8) {
102        // SAFETY: Reading and writing text to the serial device is safe.
103        unsafe {
104            while self.io.inb(COM3 + 5) & 0x20 == 0 {}
105            self.io.outb(COM3, b);
106        }
107    }
108}
109
110impl<T: IoAccess> fmt::Write for Serial<T> {
111    fn write_str(&mut self, s: &str) -> fmt::Result {
112        for &b in s.as_bytes() {
113            if b == b'\n' {
114                self.write_byte(b'\r');
115            }
116            self.write_byte(b);
117        }
118        Ok(())
119    }
120}