Skip to main content

kernel/logging/
kprint.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Kernel print macro ([`kprint!`]) and the logging state behind it.
3//!
4//! Authors: MarioS271
5
6use crate::types::irq_mutex::{IrqMutex, IrqMutexGuard};
7
8/// Capacity of the in-memory log ring buffer in bytes.
9const LOG_BUFFER_SIZE: usize = u16::MAX as usize;
10
11/// All mutable state for the `kprint` subsystem, protected by an [`IrqMutex`].
12struct KernelPrintState {
13    /// Circular byte buffer holding the most recent log output.
14    log_buf: [u8; LOG_BUFFER_SIZE],
15    /// Index of the next byte to write into `log_buf`.
16    log_head: usize,
17    /// Index of the oldest readable byte in `log_buf`.
18    log_tail: usize,
19    cursor_x: usize,
20    cursor_y: usize,
21}
22
23/// Global logging state. Wrapped in [`IrqMutex`] so interrupt handlers can log safely.
24static KPRINT_STATE: IrqMutex<KernelPrintState> = IrqMutex::new(KernelPrintState {
25    log_buf: [0u8; LOG_BUFFER_SIZE],
26    log_head: 0,
27    log_tail: 0,
28    cursor_x: 0,
29    cursor_y: 0,
30});
31
32/// Append `s` to the ring buffer, advancing the tail when the head laps it.
33fn write_log_buf(state: &mut KernelPrintState, s: &str) {
34    for &b in s.as_bytes() {
35        state.log_buf[state.log_head % LOG_BUFFER_SIZE] = b;
36        state.log_head = state.log_head.wrapping_add(1);
37        if state.log_head.wrapping_sub(state.log_tail) > LOG_BUFFER_SIZE {
38            state.log_tail = state.log_tail.wrapping_add(1);
39        }
40    }
41}
42
43/// Write `string` to the ring buffer, serial port, and framebuffer (if each is initialized).
44fn kprint(state: &mut IrqMutexGuard<'static, KernelPrintState>, string: &str, color: Option<u32>) {
45    use crate::SIMPLE_STATE;
46
47    write_log_buf(state, string);
48
49    if SIMPLE_STATE.serial.is_completed() {
50        use crate::logging::_serial::_Serial;
51        SIMPLE_STATE.serial.get().unwrap().write(string);
52    }
53
54    let basic_fb = &SIMPLE_STATE.basic_fb;
55    let basic_fb_psf2_font = &SIMPLE_STATE.basic_fb_psf2_font;
56
57    if basic_fb.is_completed() && basic_fb_psf2_font.is_completed() {
58        let fb = basic_fb.get().unwrap();
59        let font = basic_fb_psf2_font.get().unwrap();
60
61        let s = &mut **state;
62        font.draw_string(fb, string, &mut s.cursor_x, &mut s.cursor_y, color);
63
64        let last_row = fb.height as usize - font.glyph_height();
65        if s.cursor_y > last_row {
66            s.cursor_y = last_row;
67
68            // Safe: src and dst are within framebuffer bounds, counts derived from fb dimensions
69            unsafe {
70                let src = fb.fb_pointer.add(font.glyph_height() * fb.bytes_per_row as usize);
71                let dst = fb.fb_pointer;
72                let count = (fb.height as usize - font.glyph_height()) * fb.bytes_per_row as usize;
73                core::ptr::copy(src, dst, count);
74            }
75
76            // Safe: iteration range is within framebuffer bounds
77            unsafe {
78                let start = (fb.height as usize - font.glyph_height()) * fb.bytes_per_row as usize;
79                let end = fb.height as usize * fb.bytes_per_row as usize;
80                for i in start..end {
81                    fb.fb_pointer.add(i).write_volatile(0u32);
82                }
83            }
84        }
85    }
86}
87
88/// Release [`KPRINT_STATE`]'s lock without restoring the interrupt flag, for panic paths.
89///
90/// # Safety
91/// Same contract as [`IrqMutex::force_unlock`]: only call from a panic handler that
92/// halts immediately after and never accesses [`KPRINT_STATE`] again.
93pub unsafe fn force_unlock_kprint_state() {
94    KPRINT_STATE.force_unlock();
95}
96
97/// RAII handle that holds the [`KPRINT_STATE`] lock for one `kprint!` call, preventing interleaved output.
98pub struct KernelWriter {
99    lock: IrqMutexGuard<'static, KernelPrintState>
100}
101impl KernelWriter {
102    /// Acquire [`KPRINT_STATE`] and return a [`KernelWriter`] holding the guard.
103    pub fn lock() -> Self {
104        Self { lock: KPRINT_STATE.lock() }
105    }
106
107    /// Write `string` with an optional color to all active output sinks.
108    pub fn print_raw(&mut self, string: &str, color: Option<u32>) {
109        kprint(&mut self.lock, string, color);
110    }
111}
112impl core::fmt::Write for KernelWriter {
113    fn write_str(&mut self, string: &str) -> core::fmt::Result {
114        kprint(&mut self.lock, string, None);
115        Ok(())
116    }
117}
118
119/// Print a formatted message (like `print!`) to all active kernel output sinks.
120#[macro_export]
121macro_rules! kprint {
122    ($($arg:tt)*) => {
123        {
124            let _ = core::fmt::write(
125                &mut $crate::logging::kprint::KernelWriter::lock(),
126                format_args!($($arg)*)
127            );
128        }
129    };
130}
131
132pub enum LogLevelColor {
133    Emergency = 0x00ff00aa,
134    Alert = 0x00ffaa00,
135    Critical = 0x008a00ff,
136    Error = 0x00ff0000,
137    Warn = 0x00ffff00,
138    Info = 0x0000ff00,
139    Debug = 0x000000ff,
140}
141
142/// Log a line at emergency severity (system is unusable).
143#[macro_export]
144macro_rules! kemerg {
145    ($($arg:tt)*) => {
146        {
147            use $crate::logging::kprint::{KernelWriter, LogLevelColor};
148            let mut w = KernelWriter::lock();
149            w.print_raw("! ", Some(LogLevelColor::Emergency as u32));
150            let _ = core::fmt::write(&mut w, format_args!($($arg)*));
151            w.print_raw("\n", None);
152        }
153    };
154}
155/// Log a line at alert severity (action must be taken immediately).
156#[macro_export]
157macro_rules! kalert {
158    ($($arg:tt)*) => {
159        {
160            use $crate::logging::kprint::{KernelWriter, LogLevelColor};
161            let mut w = KernelWriter::lock();
162            w.print_raw("A ", Some(LogLevelColor::Alert as u32));
163            let _ = core::fmt::write(&mut w, format_args!($($arg)*));
164            w.print_raw("\n", None);
165        }
166    };
167}
168/// Log a line at critical severity.
169#[macro_export]
170macro_rules! kcrit {
171    ($($arg:tt)*) => {
172        {
173            use $crate::logging::kprint::{KernelWriter, LogLevelColor};
174            let mut w = KernelWriter::lock();
175            w.print_raw("C ", Some(LogLevelColor::Critical as u32));
176            let _ = core::fmt::write(&mut w, format_args!($($arg)*));
177            w.print_raw("\n", None);
178        }
179    };
180}
181/// Log a line at error severity.
182#[macro_export]
183macro_rules! kerror {
184    ($($arg:tt)*) => {
185        {
186            use $crate::logging::kprint::{KernelWriter, LogLevelColor};
187            let mut w = KernelWriter::lock();
188            w.print_raw("E ", Some(LogLevelColor::Error as u32));
189            let _ = core::fmt::write(&mut w, format_args!($($arg)*));
190            w.print_raw("\n", None);
191        }
192    };
193}
194/// Log a line at warning severity.
195#[macro_export]
196macro_rules! kwarn {
197    ($($arg:tt)*) => {
198        {
199            use $crate::logging::kprint::{KernelWriter, LogLevelColor};
200            let mut w = KernelWriter::lock();
201            w.print_raw("W ", Some(LogLevelColor::Warn as u32));
202            let _ = core::fmt::write(&mut w, format_args!($($arg)*));
203            w.print_raw("\n", None);
204        }
205    };
206}
207/// Log a line at info severity.
208#[macro_export]
209macro_rules! kinfo {
210    ($($arg:tt)*) => {
211        {
212            use $crate::logging::kprint::{KernelWriter, LogLevelColor};
213            let mut w = KernelWriter::lock();
214            w.print_raw("I ", Some(LogLevelColor::Info as u32));
215            let _ = core::fmt::write(&mut w, format_args!($($arg)*));
216            w.print_raw("\n", None);
217        }
218    };
219}
220/// Log a line at debug severity; compiled out unless the `debug-logging` feature is enabled.
221#[macro_export]
222macro_rules! kdebug {
223    ($($arg:tt)*) => {
224        #[cfg(feature = "debug-logging")]
225        {
226            use $crate::logging::kprint::{KernelWriter, LogLevelColor};
227            let mut w = KernelWriter::lock();
228            w.print_raw("D ", Some(LogLevelColor::Debug as u32));
229            let _ = core::fmt::write(&mut w, format_args!($($arg)*));
230            w.print_raw("\n", None);
231        }
232    };
233}