strat9_kernel/arch/x86_64/
serial.rs1use core::{
2 fmt,
3 sync::atomic::{AtomicBool, Ordering},
4};
5use spin::Mutex;
6use uart_16550::SerialPort;
7
8static SERIAL1: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3F8) });
10
11static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
14
15const ANSI_RESET: &str = "\x1b[0m";
16const ANSI_RED: &str = "\x1b[31m";
17const ANSI_GREEN: &str = "\x1b[32m";
18const ANSI_VIOLET: &str = "\x1b[35m";
19const TOKEN_BUF_CAP: usize = 64;
20
21pub fn enter_emergency_mode() {
23 PANIC_IN_PROGRESS.store(true, Ordering::SeqCst);
24}
25
26#[inline]
28fn is_token_char(b: u8) -> bool {
29 b.is_ascii_alphanumeric() || b == b'_'
30}
31
32#[inline]
34fn is_hex_word(s: &str) -> bool {
35 if s.len() <= 2 {
36 return false;
37 }
38 let bytes = s.as_bytes();
39 if bytes[0] != b'0' || (bytes[1] != b'x' && bytes[1] != b'X') {
40 return false;
41 }
42 bytes[2..].iter().all(|b| b.is_ascii_hexdigit())
43}
44
45struct AnsiStylingWriter<'a, W: fmt::Write> {
46 inner: &'a mut W,
47 in_escape: bool,
48 token_buf: [u8; TOKEN_BUF_CAP],
49 token_len: usize,
50 token_passthrough: bool,
51}
52
53impl<'a, W: fmt::Write> AnsiStylingWriter<'a, W> {
54 fn new(inner: &'a mut W) -> Self {
56 Self {
57 inner,
58 in_escape: false,
59 token_buf: [0u8; TOKEN_BUF_CAP],
60 token_len: 0,
61 token_passthrough: false,
62 }
63 }
64
65 fn flush_token(&mut self) -> fmt::Result {
67 if self.token_len == 0 {
68 return Ok(());
69 }
70 let token = unsafe { core::str::from_utf8_unchecked(&self.token_buf[..self.token_len]) };
71 if token == "PASS" {
72 self.inner.write_str(ANSI_GREEN)?;
73 self.inner.write_str(token)?;
74 self.inner.write_str(ANSI_RESET)?;
75 } else if token == "FAIL" {
76 self.inner.write_str(ANSI_RED)?;
77 self.inner.write_str(token)?;
78 self.inner.write_str(ANSI_RESET)?;
79 } else if is_hex_word(token) {
80 self.inner.write_str(ANSI_VIOLET)?;
81 self.inner.write_str(token)?;
82 self.inner.write_str(ANSI_RESET)?;
83 } else {
84 self.inner.write_str(token)?;
85 }
86 self.token_len = 0;
87 Ok(())
88 }
89
90 fn write_byte_raw(&mut self, b: u8) -> fmt::Result {
92 self.inner.write_char(b as char)
93 }
94
95 fn finish(&mut self) -> fmt::Result {
97 self.flush_token()
98 }
99}
100
101impl<W: fmt::Write> fmt::Write for AnsiStylingWriter<'_, W> {
102 fn write_str(&mut self, s: &str) -> fmt::Result {
104 for &b in s.as_bytes() {
105 if self.in_escape {
106 self.write_byte_raw(b)?;
107 if (b as char).is_ascii_alphabetic() {
108 self.in_escape = false;
109 }
110 continue;
111 }
112
113 if b == 0x1b {
114 self.flush_token()?;
115 self.token_passthrough = false;
116 self.in_escape = true;
117 self.write_byte_raw(b)?;
118 continue;
119 }
120
121 if is_token_char(b) {
122 if self.token_passthrough {
123 self.write_byte_raw(b)?;
124 continue;
125 }
126 if self.token_len < TOKEN_BUF_CAP {
127 self.token_buf[self.token_len] = b;
128 self.token_len += 1;
129 } else {
130 self.flush_token()?;
131 self.token_passthrough = true;
132 self.write_byte_raw(b)?;
133 }
134 } else {
135 self.flush_token()?;
136 self.token_passthrough = false;
137 self.write_byte_raw(b)?;
138 }
139 }
140 Ok(())
141 }
142}
143
144pub fn init() {
146 SERIAL1.lock().init();
147}
148
149#[doc(hidden)]
151pub fn _print(args: fmt::Arguments) {
152 use core::fmt::Write;
153
154 if PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
156 let mut port = unsafe { SerialPort::new(0x3F8) };
159 let _ = port.write_fmt(args);
161 return;
162 }
163
164 if let Some(mut port) = SERIAL1.try_lock() {
166 let mut writer = AnsiStylingWriter::new(&mut *port);
167 let _ = writer.write_fmt(args);
168 let _ = writer.finish();
169 }
170}
171
172#[doc(hidden)]
174pub fn _print_force(args: fmt::Arguments) {
175 use core::fmt::Write;
176
177 let mut port = unsafe { SerialPort::new(0x3F8) };
181 let _ = port.write_fmt(args);
182}
183
184#[macro_export]
186macro_rules! serial_print {
187 ($($arg:tt)*) => {
188 $crate::arch::x86_64::serial::_print(format_args!($($arg)*))
189 };
190}
191
192#[macro_export]
194macro_rules! serial_println {
195 () => ($crate::serial_print!("\n"));
196 ($($arg:tt)*) => ($crate::serial_print!("{}\n", format_args!($($arg)*)));
197}
198
199#[macro_export]
201macro_rules! serial_force_println {
202 () => ($crate::arch::x86_64::serial::_print_force(format_args!("\n")));
203 ($($arg:tt)*) => ($crate::arch::x86_64::serial::_print_force(format_args!("{}\n", format_args!($($arg)*))));
204}