diff --git a/src/cpu/line_buffer.rs b/src/cpu/line_buffer.rs new file mode 100644 index 000000000..21bc5b0a6 --- /dev/null +++ b/src/cpu/line_buffer.rs @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2023 SUSE LLC +// +// Author: Vasant Karasulli + +use crate::cpu::percpu::this_cpu_mut; +use crate::log_buffer::LB; +use crate::string::FixedString; +use crate::utils::immut_after_init::ImmutAfterInitCell; +use core::fmt; +use core::fmt::Write; + +const LINE_BUFFER_SIZE: usize = 256; + +#[derive(Debug)] +pub struct LineBuffer { + buf: FixedString, +} + +impl LineBuffer { + pub const fn new() -> Self { + LineBuffer { + buf: FixedString::new(), + } + } + + pub fn write_buffer(&mut self, s: &str) { + for c in s.chars() { + self.buf.push(c); + if c == '\n' || self.buf.length() == LINE_BUFFER_SIZE { + // when buffer is full or '\n' character is encountered + let mut buf_str: [u8; LINE_BUFFER_SIZE * 4] = [0; LINE_BUFFER_SIZE * 4]; + let mut count: usize = 0; + let mut tmp: [u8; 4] = [0; 4]; + for ch in self.buf { + ch.encode_utf8(&mut tmp); + buf_str[count] = tmp[0]; + count += 1; + for item in tmp.iter().skip(1) { + /* ignore the trailing zeros in the utf8 array */ + if *item != 0 { + buf_str[count] = *item; + count += 1; + } + } + } + unsafe { LB.lock().write_log(&buf_str[..count]) }; + self.buf.reset(); + } + } + } +} + +impl fmt::Write for LineBuffer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_buffer(s); + Ok(()) + } +} + +#[derive(Clone, Copy)] +struct BufferLogger { + component: &'static str, +} + +impl BufferLogger { + fn new(component: &'static str) -> BufferLogger { + BufferLogger { component } + } +} + +impl log::Log for BufferLogger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + let comp: &'static str = self.component; + let line_buf: &mut LineBuffer = this_cpu_mut().get_line_buffer(); + // Log format/detail depends on the level. + let rec_args = record.args(); + let lvl = record.metadata().level().as_str(); + let target = record.metadata().target(); + match record.metadata().level() { + log::Level::Error | log::Level::Warn => { + line_buf + .write_fmt(format_args!("[{}] {}: {}\n", comp, lvl, rec_args)) + .unwrap(); + } + + log::Level::Info => { + line_buf + .write_fmt(format_args!("[{}] {}\n", comp, rec_args)) + .unwrap(); + } + + log::Level::Debug | log::Level::Trace => { + line_buf + .write_fmt(format_args!("[{}/{}] {} {}\n", comp, target, lvl, rec_args)) + .unwrap(); + } + } + } + + fn flush(&self) {} +} + +static BUFFER_LOGGER: ImmutAfterInitCell = ImmutAfterInitCell::uninit(); + +pub fn install_buffer_logger(component: &'static str) { + BUFFER_LOGGER + .init(&BufferLogger::new(component)) + .expect("already initialized the logger"); + let _ = log::set_logger(&*BUFFER_LOGGER); + + // Log levels are to be configured via the log's library feature configuration. + log::set_max_level(log::LevelFilter::Trace); +} diff --git a/src/cpu/mod.rs b/src/cpu/mod.rs index c339e4a56..d15de7297 100644 --- a/src/cpu/mod.rs +++ b/src/cpu/mod.rs @@ -11,6 +11,7 @@ pub mod extable; pub mod features; pub mod gdt; pub mod idt; +pub mod line_buffer; pub mod msr; pub mod percpu; pub mod registers; diff --git a/src/cpu/percpu.rs b/src/cpu/percpu.rs index 48c9d26f7..51f0592d8 100644 --- a/src/cpu/percpu.rs +++ b/src/cpu/percpu.rs @@ -9,6 +9,7 @@ extern crate alloc; use super::gdt::load_tss; use super::tss::{X86Tss, IST_DF}; use crate::address::{Address, PhysAddr, VirtAddr}; +use crate::cpu::line_buffer::LineBuffer; use crate::cpu::tss::TSS_LIMIT; use crate::cpu::vmsa::init_guest_vmsa; use crate::error::SvsmError; @@ -182,6 +183,7 @@ pub struct PerCpu { svsm_vmsa: Option, guest_vmsa: SpinLock, reset_ip: u64, + ln_buf: LineBuffer, /// Address allocator for per-cpu 4k temporary mappings pub vrange_4k: VirtualRange, @@ -202,6 +204,7 @@ impl PerCpu { svsm_vmsa: None, guest_vmsa: SpinLock::new(GuestVmsaRef::new()), reset_ip: 0xffff_fff0u64, + ln_buf: LineBuffer::new(), vrange_4k: VirtualRange::new(), vrange_2m: VirtualRange::new(), } @@ -502,6 +505,10 @@ impl PerCpu { PAGE_SHIFT_2M, ); } + + pub fn get_line_buffer(&mut self) -> &mut LineBuffer { + &mut self.ln_buf + } } unsafe impl Sync for PerCpu {} diff --git a/src/string.rs b/src/string.rs index 945df07eb..c178c8eea 100644 --- a/src/string.rs +++ b/src/string.rs @@ -12,6 +12,7 @@ use core::mem::MaybeUninit; pub struct FixedString { len: usize, data: [char; T], + current: usize, } impl FixedString { @@ -19,6 +20,7 @@ impl FixedString { FixedString { len: 0, data: ['\0'; T], + current: 0, } } @@ -36,12 +38,18 @@ impl FixedString { pub fn length(&self) -> usize { self.len } + + pub fn reset(&mut self) { + self.len = 0; + self.current = 0; + } } impl From<[u8; N]> for FixedString { fn from(arr: [u8; N]) -> FixedString { let mut data: [MaybeUninit; N] = array::from_fn(|_| MaybeUninit::uninit()); let mut len = N; + let current = 0; for (i, (d, val)) in data.iter_mut().zip(&arr).enumerate() { let val = *val; @@ -52,7 +60,7 @@ impl From<[u8; N]> for FixedString { } let data = unsafe { *(data.as_ptr().cast::<[char; N]>()) }; - FixedString { data, len } + FixedString { data, len, current } } } @@ -104,6 +112,20 @@ impl fmt::Display for FixedString { } } +impl Iterator for FixedString { + type Item = char; + + fn next(&mut self) -> Option { + let ret = if self.current == self.len { + None + } else { + Some(self.data[self.current]) + }; + self.current += 1; + ret + } +} + #[cfg(test)] mod tests { extern crate alloc;