strat9_kernel/arch/x86_64/
serial.rs1use core::{
2 fmt,
3 sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering},
4};
5use spin::Mutex;
6use uart_16550::SerialPort;
7
8static SERIAL1: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3F8) });
10
11static CMDLINE_BUF: [u8; 2048] = [0; 2048];
15static CMDLINE_LEN: AtomicUsize = AtomicUsize::new(0);
16static CMDLINE_READY: AtomicBool = AtomicBool::new(false);
17
18static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
21static BOOT_LOG_PREFIX_ENABLED: AtomicBool = AtomicBool::new(false);
22static SERIAL_AT_LINE_START: AtomicBool = AtomicBool::new(true);
23
24static FORCE_LOCK: AtomicU8 = AtomicU8::new(0);
32
33#[inline(always)]
34fn force_lock_acquire() -> u64 {
35 let saved = crate::arch::x86_64::save_flags_and_cli();
38 while FORCE_LOCK
39 .compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
40 .is_err()
41 {
42 core::hint::spin_loop();
43 }
44 saved
45}
46
47#[inline(always)]
48fn force_lock_release(saved_flags: u64) {
49 FORCE_LOCK.store(0, Ordering::Release);
50 crate::arch::x86_64::restore_flags(saved_flags);
52}
53
54const ANSI_RESET: &str = "\x1b[0m";
55const ANSI_RED: &str = "\x1b[31m";
56const ANSI_GREEN: &str = "\x1b[32m";
57const ANSI_VIOLET: &str = "\x1b[35m";
58const TOKEN_BUF_CAP: usize = 64;
59
60pub fn enter_emergency_mode() {
62 PANIC_IN_PROGRESS.store(true, Ordering::SeqCst);
63}
64
65pub fn set_boot_log_prefix_enabled(enabled: bool) {
67 BOOT_LOG_PREFIX_ENABLED.store(enabled, Ordering::SeqCst);
68 SERIAL_AT_LINE_START.store(true, Ordering::SeqCst);
69}
70
71#[inline]
73fn is_token_char(b: u8) -> bool {
74 b.is_ascii_alphanumeric() || b == b'_'
75}
76
77#[inline]
79fn is_hex_word(s: &str) -> bool {
80 if s.len() <= 2 {
81 return false;
82 }
83 let bytes = s.as_bytes();
84 if bytes[0] != b'0' || (bytes[1] != b'x' && bytes[1] != b'X') {
85 return false;
86 }
87 bytes[2..].iter().all(|b| b.is_ascii_hexdigit())
88}
89
90struct AnsiStylingWriter<'a, W: fmt::Write> {
91 inner: &'a mut W,
92 in_escape: bool,
93 token_buf: [u8; TOKEN_BUF_CAP],
94 token_len: usize,
95 token_passthrough: bool,
96}
97
98impl<'a, W: fmt::Write> AnsiStylingWriter<'a, W> {
99 fn new(inner: &'a mut W) -> Self {
101 Self {
102 inner,
103 in_escape: false,
104 token_buf: [0u8; TOKEN_BUF_CAP],
105 token_len: 0,
106 token_passthrough: false,
107 }
108 }
109
110 fn flush_token(&mut self) -> fmt::Result {
112 if self.token_len == 0 {
113 return Ok(());
114 }
115 let token = unsafe { core::str::from_utf8_unchecked(&self.token_buf[..self.token_len]) };
116 if token == "PASS" {
117 self.inner.write_str(ANSI_GREEN)?;
118 self.inner.write_str(token)?;
119 self.inner.write_str(ANSI_RESET)?;
120 } else if token == "FAIL" {
121 self.inner.write_str(ANSI_RED)?;
122 self.inner.write_str(token)?;
123 self.inner.write_str(ANSI_RESET)?;
124 } else if is_hex_word(token) {
125 self.inner.write_str(ANSI_VIOLET)?;
126 self.inner.write_str(token)?;
127 self.inner.write_str(ANSI_RESET)?;
128 } else {
129 self.inner.write_str(token)?;
130 }
131 self.token_len = 0;
132 Ok(())
133 }
134
135 fn write_byte_raw(&mut self, b: u8) -> fmt::Result {
137 self.inner.write_char(b as char)
138 }
139
140 fn finish(&mut self) -> fmt::Result {
142 self.flush_token()
143 }
144}
145
146impl<W: fmt::Write> fmt::Write for AnsiStylingWriter<'_, W> {
147 fn write_str(&mut self, s: &str) -> fmt::Result {
149 for &b in s.as_bytes() {
150 if self.in_escape {
151 self.write_byte_raw(b)?;
152 if (b as char).is_ascii_alphabetic() {
153 self.in_escape = false;
154 }
155 continue;
156 }
157
158 if b == 0x1b {
159 self.flush_token()?;
160 self.token_passthrough = false;
161 self.in_escape = true;
162 self.write_byte_raw(b)?;
163 continue;
164 }
165
166 if is_token_char(b) {
167 if self.token_passthrough {
168 self.write_byte_raw(b)?;
169 continue;
170 }
171 if self.token_len < TOKEN_BUF_CAP {
172 self.token_buf[self.token_len] = b;
173 self.token_len += 1;
174 } else {
175 self.flush_token()?;
176 self.token_passthrough = true;
177 self.write_byte_raw(b)?;
178 }
179 } else {
180 self.flush_token()?;
181 self.token_passthrough = false;
182 self.write_byte_raw(b)?;
183 }
184 }
185 Ok(())
186 }
187}
188
189struct BootPrefixWriter<'a, W: fmt::Write> {
190 inner: &'a mut W,
191 line_start: bool,
192 prefix_enabled: bool,
193}
194
195impl<'a, W: fmt::Write> BootPrefixWriter<'a, W> {
196 fn new(inner: &'a mut W) -> Self {
197 Self {
198 inner,
199 line_start: SERIAL_AT_LINE_START.load(Ordering::Relaxed),
200 prefix_enabled: BOOT_LOG_PREFIX_ENABLED.load(Ordering::Relaxed),
201 }
202 }
203
204 fn write_prefix(&mut self) -> fmt::Result {
205 if !self.prefix_enabled {
206 return Ok(());
207 }
208 let elapsed_us = crate::arch::x86_64::boot_timestamp::elapsed_us();
209 let secs = elapsed_us / 1_000_000;
210 let micros = elapsed_us % 1_000_000;
211 write!(self.inner, "[{:>5}.{:06}] ", secs, micros)
212 }
213
214 fn finish(&mut self) {
215 SERIAL_AT_LINE_START.store(self.line_start, Ordering::Relaxed);
216 }
217}
218
219impl<W: fmt::Write> fmt::Write for BootPrefixWriter<'_, W> {
220 fn write_str(&mut self, s: &str) -> fmt::Result {
221 for ch in s.chars() {
222 if self.line_start && ch != '\n' {
223 self.write_prefix()?;
224 self.line_start = false;
225 }
226 self.inner.write_char(ch)?;
227 if ch == '\n' {
228 self.line_start = true;
229 }
230 }
231 Ok(())
232 }
233}
234
235pub fn init() {
237 SERIAL1.lock().init();
238}
239
240pub unsafe fn parse_cmdline(ptr: u64, len: u64) {
249 if ptr == 0 || len == 0 {
250 return;
251 }
252
253 let cstr = core::ffi::CStr::from_ptr(ptr as *const core::ffi::c_char);
255 let cmdline = cstr.to_str().unwrap_or("");
256
257 let copy_len = cmdline.len().min(2047);
258 let buf_ptr = CMDLINE_BUF.as_ptr() as *mut u8;
260 core::ptr::copy_nonoverlapping(cmdline.as_ptr(), buf_ptr, copy_len);
261 CMDLINE_LEN.store(copy_len, Ordering::Release);
262 CMDLINE_READY.store(true, Ordering::Release);
263
264 let cmdline_str =
266 core::str::from_utf8_unchecked(core::slice::from_raw_parts(buf_ptr, copy_len));
267
268 let mut has_serial_console = false;
269 let mut baud: Option<u32> = None;
270
271 let mut pos = 0;
272 while pos < cmdline_str.len() {
273 while pos < cmdline_str.len() && cmdline_str.as_bytes()[pos].is_ascii_whitespace() {
274 pos += 1;
275 }
276 if pos >= cmdline_str.len() {
277 break;
278 }
279 let start = pos;
280 while pos < cmdline_str.len() && !cmdline_str.as_bytes()[pos].is_ascii_whitespace() {
281 pos += 1;
282 }
283 let token = &cmdline_str[start..pos];
284
285 if let Some(value) = token.strip_prefix("console=") {
286 if value.starts_with("ttyS0") {
287 has_serial_console = true;
288 if let Some((_, baud_str)) = value.split_once(',') {
289 if let Ok(b) = baud_str.parse::<u32>() {
290 baud = Some(b);
291 }
292 }
293 }
294 }
295 }
296
297 if has_serial_console {
298 if let Some(b) = baud {
299 crate::serial_force_println!("[cmdline] console=ttyS0,{}", b);
300 } else {
301 crate::serial_force_println!("[cmdline] console=ttyS0 (115200 baud)");
302 }
303 } else {
304 crate::serial_force_println!("[cmdline] no serial console detected");
305 }
306}
307
308pub fn get_cmdline() -> &'static str {
310 if !CMDLINE_READY.load(Ordering::Acquire) {
311 return "";
312 }
313 let len = CMDLINE_LEN.load(Ordering::Acquire);
314 unsafe { core::str::from_utf8_unchecked(&CMDLINE_BUF[..len]) }
316}
317
318#[doc(hidden)]
320pub fn _print(args: fmt::Arguments) {
321 use core::fmt::Write;
322
323 if PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
325 let mut port = unsafe { SerialPort::new(0x3F8) };
328 let _ = port.write_fmt(args);
330 return;
331 }
332
333 if let Some(mut port) = SERIAL1.try_lock() {
335 let mut prefix_writer = BootPrefixWriter::new(&mut *port);
336 let mut writer = AnsiStylingWriter::new(&mut prefix_writer);
337 let _ = writer.write_fmt(args);
338 let _ = writer.finish();
339 prefix_writer.finish();
340 }
341}
342
343#[doc(hidden)]
350pub fn _print_force(args: fmt::Arguments) {
351 use core::fmt::Write;
352
353 if PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
355 let mut port = unsafe { SerialPort::new(0x3F8) };
357 let _ = port.write_fmt(args);
358 return;
359 }
360
361 let saved_flags = force_lock_acquire();
363 let mut port = unsafe { SerialPort::new(0x3F8) };
365 let mut prefix_writer = BootPrefixWriter::new(&mut port);
366 let _ = prefix_writer.write_fmt(args);
367 prefix_writer.finish();
368 force_lock_release(saved_flags);
370}
371
372#[macro_export]
374macro_rules! serial_print {
375 ($($arg:tt)*) => {
376 $crate::arch::x86_64::serial::_print(format_args!($($arg)*))
377 };
378}
379
380#[macro_export]
382macro_rules! serial_println {
383 () => ($crate::serial_print!("\n"));
384 ($($arg:tt)*) => ($crate::serial_print!("{}\n", format_args!($($arg)*)));
385}
386
387#[macro_export]
389macro_rules! serial_force_println {
390 () => ($crate::arch::x86_64::serial::_print_force(format_args!("\n")));
391 ($($arg:tt)*) => ($crate::arch::x86_64::serial::_print_force(format_args!("{}\n", format_args!($($arg)*))));
392}