1pub mod tracing;
2mod vga_graphics;
3mod vga_text;
4
5use core::{
6 cell::RefCell,
7 fmt::{self, Write},
8};
9
10use alloc::{boxed::Box, string::String, sync::Arc};
11
12use crate::{
13 devices::{
14 self,
15 keyboard_mouse::{self, KeyboardReader},
16 Device,
17 },
18 fs::FileSystemError,
19 multiboot2::{self, FramebufferColorInfo},
20 sync::{once::OnceLock, spin::remutex::ReMutex},
21};
22
23use self::{vga_graphics::VgaGraphics, vga_text::VgaText};
24
25use super::uart::{Uart, UartPort};
26
27static CONSOLE: OnceLock<ConsoleController> = OnceLock::new();
29
30pub(super) fn run_with_console<F, U>(mut f: F) -> U
34where
35 F: FnMut(&mut dyn core::fmt::Write) -> U,
36{
37 CONSOLE.get().run_with(|c| f(c))
38}
39
40pub fn early_init() {
42 CONSOLE
43 .set(ConsoleController::early())
44 .expect("Console should only be initialized once");
45}
46
47pub fn init_late_device(framebuffer: Option<multiboot2::Framebuffer>) {
50 let device = unsafe {
55 CONSOLE.get().init_late(framebuffer);
56 CONSOLE.get().late_device().unwrap()
58 };
59
60 devices::register_device(device);
61}
62
63#[allow(dead_code)]
64pub fn start_capture() -> Option<String> {
65 CONSOLE.get().run_with(|c| c.start_capture())
66}
67
68#[allow(dead_code)]
69pub fn stop_capture() -> Option<String> {
70 CONSOLE.get().run_with(|c| c.stop_capture())
71}
72
73fn create_video_console(framebuffer: Option<multiboot2::Framebuffer>) -> Box<dyn VideoConsole> {
74 match framebuffer {
75 Some(framebuffer) => match framebuffer.color_info {
76 FramebufferColorInfo::Indexed { .. } => todo!(),
77 FramebufferColorInfo::Rgb { .. } => {
78 Box::new(VgaGraphics::new())
80 }
81 FramebufferColorInfo::EgaText => Box::new(VgaText::new(framebuffer)),
82 },
83 None => panic!("No framebuffer provided"),
84 }
85}
86
87#[derive(Debug, Clone, Copy)]
88#[repr(u8)]
89enum AnsiColor {
90 Black = 0,
91 Red,
92 Green,
93 Yellow,
94 Blue,
95 Magenta,
96 Cyan,
97 White,
98 BrightBlack,
99 BrightRed,
100 BrightGreen,
101 BrightYellow,
102 BrightBlue,
103 BrightMagenta,
104 BrightCyan,
105 BrightWhite,
106}
107
108impl AnsiColor {
109 fn from_u8(color: u8) -> Self {
110 match color {
111 0 => Self::Black,
112 1 => Self::Red,
113 2 => Self::Green,
114 3 => Self::Yellow,
115 4 => Self::Blue,
116 5 => Self::Magenta,
117 6 => Self::Cyan,
118 7 => Self::White,
119 8 => Self::BrightBlack,
120 9 => Self::BrightRed,
121 10 => Self::BrightGreen,
122 11 => Self::BrightYellow,
123 12 => Self::BrightBlue,
124 13 => Self::BrightMagenta,
125 14 => Self::BrightCyan,
126 15 => Self::BrightWhite,
127 _ => panic!("Invalid color"),
128 }
129 }
130}
131
132#[derive(Debug, Clone, Copy)]
133struct VideoConsoleAttribute {
134 foreground: AnsiColor,
135 background: AnsiColor,
136 bold: bool,
137 faint: bool,
138}
139
140impl Default for VideoConsoleAttribute {
141 fn default() -> Self {
142 Self {
143 foreground: AnsiColor::White,
144 background: AnsiColor::Black,
145 bold: false,
146 faint: false,
147 }
148 }
149}
150
151trait VideoConsole: Send + Sync {
152 #[allow(dead_code)]
153 fn init(&mut self);
154 fn set_attrib(&mut self, attrib: VideoConsoleAttribute);
155 fn write_byte(&mut self, c: u8);
156 fn backspace(&mut self);
157}
158
159trait Console: Write {
160 fn write(&mut self, src: &[u8]) -> usize;
161 fn read(&mut self, dst: &mut [u8]) -> usize;
162 #[must_use]
163 fn start_capture(&mut self) -> Option<String>;
164 fn stop_capture(&mut self) -> Option<String>;
165}
166
167#[derive(Debug)]
168pub(super) struct ConsoleController {
169 early: ReMutex<RefCell<EarlyConsole>>,
170 late: OnceLock<Arc<ReMutex<RefCell<LateConsole>>>>,
171}
172
173impl ConsoleController {
174 fn early() -> Self {
175 let mut early = EarlyConsole::empty();
176 early.init();
177 Self {
178 early: ReMutex::new(RefCell::new(early)),
179 late: OnceLock::new(),
180 }
181 }
182
183 unsafe fn init_late(&self, framebuffer: Option<multiboot2::Framebuffer>) {
186 let video_console = create_video_console(framebuffer);
187 let late_console = LateConsole::new(self.early.lock().borrow().uart.clone(), video_console);
188 self.late
189 .set(Arc::new(ReMutex::new(RefCell::new(late_console))))
190 .expect("Late console should only be initialized once");
191 }
192
193 fn late_device(&self) -> Option<Arc<ReMutex<RefCell<LateConsole>>>> {
194 self.late.try_get().cloned()
195 }
196
197 fn run_with<F, U>(&self, mut f: F) -> U
198 where
199 F: FnMut(&mut dyn Console) -> U,
200 {
201 let ret = if let Some(console) = self.late.try_get() {
202 let console = console.lock();
203 let x = if let Ok(mut c) = console.try_borrow_mut() {
204 Some(f(&mut *c))
205 } else {
206 None
207 };
208 x
209 } else {
210 let console = self.early.lock();
211 let x = if let Ok(mut c) = console.try_borrow_mut() {
212 Some(f(&mut *c))
213 } else {
214 None
215 };
216 x
217 };
218
219 if let Some(ret) = ret {
220 ret
221 } else {
222 let mut console = EarlyConsole::empty();
225 console.init();
226 f(&mut console)
227 }
228 }
229}
230#[derive(Debug)]
231pub(super) struct EarlyConsole {
232 uart: Uart,
233 capture: Option<String>,
234}
235
236impl EarlyConsole {
237 pub const fn empty() -> Self {
238 Self {
239 uart: Uart::new(UartPort::COM1),
240 capture: None,
241 }
242 }
243
244 pub fn init(&mut self) {
245 self.uart.init();
246 }
247
248 fn write_byte(&mut self, byte: u8) {
249 unsafe { self.uart.write_byte(byte) };
251 }
252}
253
254impl Write for EarlyConsole {
255 fn write_str(&mut self, s: &str) -> core::fmt::Result {
256 self.write(s.as_bytes());
257 Ok(())
258 }
259}
260
261impl Console for EarlyConsole {
262 fn write(&mut self, src: &[u8]) -> usize {
263 if let Some(capture) = &mut self.capture {
264 capture.push_str(core::str::from_utf8(src).expect("Non-UTF8"));
265 } else {
266 for &c in src {
267 self.write_byte(c);
268 }
269 }
270 src.len()
271 }
272
273 fn read(&mut self, _dst: &mut [u8]) -> usize {
274 0
276 }
277
278 fn start_capture(&mut self) -> Option<String> {
279 self.capture.replace(String::new())
280 }
281
282 fn stop_capture(&mut self) -> Option<String> {
283 self.capture.take()
284 }
285}
286
287pub(super) struct LateConsole {
288 uart: Uart,
289 video_console: Box<dyn VideoConsole>,
290 keyboard: KeyboardReader,
291 console_cmd_buffer: Option<String>,
292 current_attrib: VideoConsoleAttribute,
293 capture: Option<String>,
294}
295
296impl LateConsole {
297 unsafe fn new(uart: Uart, video_console: Box<dyn VideoConsole>) -> Self {
299 Self {
300 uart,
301 video_console,
302 keyboard: keyboard_mouse::get_keyboard_reader(),
303 console_cmd_buffer: None,
304 current_attrib: Default::default(),
305 capture: None,
306 }
307 }
308
309 fn write_byte(&mut self, byte: u8) {
310 let mut write_byte_inner = |byte: u8| {
311 if byte == 8 {
313 self.video_console.backspace();
314 unsafe {
316 self.uart.write_byte(byte);
318 self.uart.write_byte(b' ');
320 self.uart.write_byte(byte);
322 };
323 } else {
324 self.video_console.write_byte(byte);
325 unsafe { self.uart.write_byte(byte) };
327 }
328 };
329
330 if let Some(buf) = &mut self.console_cmd_buffer {
331 match byte {
333 b'0'..=b'9' | b';' | b'[' => {
334 buf.push(byte as char);
336 }
337 b'm' => {
338 if let Some(inner_cmd) = buf.strip_prefix('[') {
340 inner_cmd.split(';').for_each(|cmd| {
341 if let Ok(cmd) = cmd.parse::<u8>() {
342 match cmd {
343 0 => {
344 self.current_attrib = Default::default();
345 }
346 1 => {
347 self.current_attrib.bold = true;
348 self.current_attrib.faint = false;
349 }
350 2 => {
351 self.current_attrib.bold = false;
352 self.current_attrib.faint = true;
353 }
354 30..=37 => {
355 let color = cmd - 30;
356 self.current_attrib.foreground = AnsiColor::from_u8(color);
357 }
358 90..=97 => {
359 let color = (cmd - 90) + 8;
360 self.current_attrib.foreground = AnsiColor::from_u8(color);
361 }
362 40..=47 => {
363 let color = cmd - 40;
364 self.current_attrib.background = AnsiColor::from_u8(color);
365 }
366 100..=107 => {
367 let color = (cmd - 100) + 8;
368 self.current_attrib.background = AnsiColor::from_u8(color);
369 }
370 _ => {}
371 }
372 self.video_console.set_attrib(self.current_attrib);
373 }
374 });
375
376 unsafe {
379 self.uart.write_byte(0x1b);
380 self.uart.write_byte(b'[');
381 for &c in inner_cmd.as_bytes() {
382 self.uart.write_byte(c);
383 }
384 self.uart.write_byte(b'm');
385 }
386 self.console_cmd_buffer = None;
387 } else {
388 self.console_cmd_buffer = None;
391 write_byte_inner(byte);
392 }
393 }
394 _ => {
395 self.console_cmd_buffer = None;
399 write_byte_inner(byte);
400 }
401 }
402 } else {
403 if byte == 0x1b {
406 self.console_cmd_buffer = Some(String::new());
407 return;
408 }
409 write_byte_inner(byte);
411 }
412 }
413}
414
415impl Write for LateConsole {
416 fn write_str(&mut self, s: &str) -> core::fmt::Result {
417 self.write(s.as_bytes());
418 Ok(())
419 }
420}
421
422impl Console for LateConsole {
423 fn write(&mut self, src: &[u8]) -> usize {
424 if let Some(capture) = &mut self.capture {
425 capture.push_str(core::str::from_utf8(src).expect("Non-UTF8"));
426 } else {
427 for &c in src {
428 self.write_byte(c);
429 }
430 }
431 src.len()
432 }
433
434 fn read(&mut self, dst: &mut [u8]) -> usize {
435 let mut i = 0;
436
437 let read_uart = || unsafe {
441 self.uart.try_read_byte().map(|c| match c {
442 b'\r' => b'\n',
443 b'\x7f' => b'\x08', _ => c,
445 })
446 };
447
448 while i < dst.len() {
449 if let Some(c) = self
452 .keyboard
453 .recv()
454 .and_then(|c| if c.pressed { c.virtual_char() } else { None })
455 .or_else(read_uart)
456 {
457 dst[i] = c;
458 i += 1;
459 } else {
461 break;
462 }
463 }
464 i
465 }
466
467 fn start_capture(&mut self) -> Option<String> {
468 self.capture.replace(String::new())
469 }
470
471 fn stop_capture(&mut self) -> Option<String> {
472 self.capture.take()
473 }
474}
475
476impl fmt::Debug for LateConsole {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 f.debug_struct("LateConsole").finish()
479 }
480}
481
482impl Device for ReMutex<RefCell<LateConsole>> {
483 fn name(&self) -> &str {
484 "console"
485 }
486
487 fn read(&self, _offset: u64, buf: &mut [u8]) -> Result<u64, FileSystemError> {
488 let console = self.lock();
489 let x = if let Ok(mut c) = console.try_borrow_mut() {
490 c.read(buf)
491 } else {
492 0
494 };
495 Ok(x as u64)
496 }
497
498 fn write(&self, _offset: u64, buf: &[u8]) -> Result<u64, FileSystemError> {
499 let console = self.lock();
500 let x = if let Ok(mut c) = console.try_borrow_mut() {
501 c.write(buf)
502 } else {
503 let mut console = EarlyConsole::empty();
508 console.init();
509 console.write(buf)
510 };
511
512 Ok(x as u64)
513 }
514}