Skip to main content

kernel/kprint/
kernel_writer.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Kernel Writer, locks the logging state (ringbuffer, basic fb state, ...), collects text,
3//! builds log entry, writes it and dispatches the full log message to receivers
4//!
5//! Authors: MarioS271
6
7use crate::kprint::log_entry::{LogFlags, LogLevel};
8use crate::kprint::receivers::KPrintReceiver;
9use crate::kprint::state::{KPrintState, LogTargets};
10use crate::lib::sync::irq_mutex::IrqMutexGuard;
11use crate::state::kstate::KSTATE;
12use core::fmt::Write;
13
14/// Size of the intermediary log buffer (2 KiB)
15pub const WRITE_BUFFER_SIZE: usize = 1 << 11;
16
17/// Wrapper that holds the acquired [`Ringbuffer`] guard and a buffer in which [`core::fmt::write`]
18/// calls get written, which then gets written to the ring buffer
19///
20/// [`Ringbuffer`]: super::ringbuffer::Ringbuffer
21pub struct KernelWriter {
22    state: IrqMutexGuard<'static, KPrintState>,
23    buffer: [u8; WRITE_BUFFER_SIZE],
24    text_len: usize,
25    log_level: LogLevel,
26    log_flags: LogFlags
27}
28impl KernelWriter {
29    /// Acquire the ringbuffer and return a [`KernelWriter`] holding the guard
30    pub fn lock(log_level: LogLevel, log_flags: LogFlags) -> Self {
31        Self {
32            state: KSTATE.kprint.lock(),
33            buffer: [0u8; WRITE_BUFFER_SIZE],
34            text_len: 0,
35            log_level,
36            log_flags
37        }
38    }
39}
40impl Write for KernelWriter {
41    /// Write `string` into the intermediary write buffer
42    fn write_str(&mut self, string: &str) -> core::fmt::Result {
43        let bytes = string.as_bytes();
44        let remaining = WRITE_BUFFER_SIZE - self.text_len;
45        let to_copy = bytes.len().min(remaining);
46
47        self.buffer[self.text_len..self.text_len + to_copy]
48            .copy_from_slice(&bytes[..to_copy]);
49
50        self.text_len += to_copy;
51        Ok(())
52    }
53}
54impl Drop for KernelWriter {
55    /// Write intermediary write buffer into ringbuffer as an entry and output to all kprint receivers
56    fn drop(&mut self) {
57        if self.log_flags.contains(LogFlags::NEWLINE) {
58            if self.text_len < WRITE_BUFFER_SIZE {
59                self.buffer[self.text_len] = b'\n';
60                self.text_len += 1;
61            } else {
62                self.buffer[WRITE_BUFFER_SIZE - 1] = b'\n';
63            }
64        }
65
66        let header = self.state.ring_buffer.write_entry(
67            self.log_level,
68            self.log_flags,
69            &self.buffer[..self.text_len]
70        );
71
72        // Safety: from_utf8_unchecked is safe here because we built the intermediary buffer from
73        // valid string slices
74        let text= unsafe {
75            core::str::from_utf8_unchecked(&self.buffer[..self.text_len])
76        };
77
78        let log_targets = KSTATE.kprint.config().log_targets();
79
80        if log_targets.contains(LogTargets::UART) {
81            super::receivers::uart::UartReceiver::receive(
82                &mut *self.state,
83                &header,
84                text
85            );
86        }
87        if log_targets.contains(LogTargets::BASIC_FB) {
88            super::receivers::basic_fb::BasicFbReceiver::receive(
89                &mut *self.state,
90                &header,
91                text
92            );
93        }
94    }
95}