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}