kernel/io/
console.rs

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
27// SAFETY: the console is only used inside a lock or mutex
28static CONSOLE: OnceLock<ConsoleController> = OnceLock::new();
29
30/// # SAFETY
31/// the caller must assure that this is not called while not being initialized
32/// at the same time
33pub(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
40/// Create an early console, this is used before the kernel heap is initialized
41pub fn early_init() {
42    CONSOLE
43        .set(ConsoleController::early())
44        .expect("Console should only be initialized once");
45}
46
47/// Create a late console, this is used after the kernel heap is initialized
48/// And also assign a console device
49pub fn init_late_device(framebuffer: Option<multiboot2::Framebuffer>) {
50    // SAFETY: we are running this initialization at `kernel_main` and its done alone
51    //  without printing anything at the same time since we are only
52    //  running 1 CPU at the  time
53    //  We are also sure that no one is printing at this time
54    let device = unsafe {
55        CONSOLE.get().init_late(framebuffer);
56        // Must have a device
57        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                // assumes we have already initialized the vga display
79                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    /// # SAFETY
184    /// Must ensure that there is no console is being printed to/running at the same time
185    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            // if we can't get the lock, we are inside `panic`
223            //  create a new early console and print to it
224            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        // Safety: we are sure that the uart is initialized
250        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        // we can't read from early console
275        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    /// SAFETY: must ensure that there is no console running at the same time
298    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            // backspace
312            if byte == 8 {
313                self.video_console.backspace();
314                // Safety: we are sure that the uart is initialized
315                unsafe {
316                    // write backspace
317                    self.uart.write_byte(byte);
318                    // write space to clear the character
319                    self.uart.write_byte(b' ');
320                    // write backspace again
321                    self.uart.write_byte(byte);
322                };
323            } else {
324                self.video_console.write_byte(byte);
325                // Safety: we are sure that the uart is initialized
326                unsafe { self.uart.write_byte(byte) };
327            }
328        };
329
330        if let Some(buf) = &mut self.console_cmd_buffer {
331            // is this the end of the command
332            match byte {
333                b'0'..=b'9' | b';' | b'[' => {
334                    // part of the command
335                    buf.push(byte as char);
336                }
337                b'm' => {
338                    // end of the color command
339                    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                        // output all saved into the uart as well
377                        // Safety: we are sure that the uart is initialized
378                        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                        // not a valid command
389                        // abort and write the char
390                        self.console_cmd_buffer = None;
391                        write_byte_inner(byte);
392                    }
393                }
394                _ => {
395                    // unsupported command or character of a command
396                    // abort and write char, probably we lost some characters
397                    // if this was not intended to be a command
398                    self.console_cmd_buffer = None;
399                    write_byte_inner(byte);
400                }
401            }
402        } else {
403            // start of a new command
404            // 0x1b = ESC
405            if byte == 0x1b {
406                self.console_cmd_buffer = Some(String::new());
407                return;
408            }
409            // otherwise, just write to the screen
410            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        // for some reason, uart returns \r instead of \n when pressing <enter>
438        // so we have to convert it to \n
439        // Safety: we are sure that the uart is initialized
440        let read_uart = || unsafe {
441            self.uart.try_read_byte().map(|c| match c {
442                b'\r' => b'\n',
443                b'\x7f' => b'\x08', // delete -> backspace
444                _ => c,
445            })
446        };
447
448        while i < dst.len() {
449            // try to read from keyboard
450            // if we can't read from keyboard, try to read from uart
451            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                // ignore if it's not a valid char
460            } 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            // cannot read from console if its taken
493            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            // this should not be reached at all, but just in case
504            //
505            // if we can't get the lock, we are inside `panic`
506            //  create a new early console and print to it
507            let mut console = EarlyConsole::empty();
508            console.init();
509            console.write(buf)
510        };
511
512        Ok(x as u64)
513    }
514}