From cda4523639f1e75cd829af9a50445e323c6cef5a Mon Sep 17 00:00:00 2001 From: Vasant Karasulli Date: Wed, 19 Jun 2024 09:37:29 +0200 Subject: [PATCH 1/6] utils: Add a StringRingBuffer implementation The log buffer is implemented using a ring buffer that contains the characters within log entries. This patch introduces a generic StringRingBuffer that can be used for safely managing the contents of strings within a ring buffer that is suitable for use with the log buffer. This separates the ring buffer implementation from the log and allows implementation of ring buffer specific unit testing, which is included within this patch. Signed-off-by: Vasant Karasulli Signed-off-by: Roy Hopkins --- kernel/src/utils/mod.rs | 2 + kernel/src/utils/string_ring_buffer.rs | 155 +++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 kernel/src/utils/string_ring_buffer.rs diff --git a/kernel/src/utils/mod.rs b/kernel/src/utils/mod.rs index e2cca7290..424a4e7b0 100644 --- a/kernel/src/utils/mod.rs +++ b/kernel/src/utils/mod.rs @@ -7,9 +7,11 @@ pub mod bitmap_allocator; pub mod immut_after_init; pub mod memory_region; +pub mod string_ring_buffer; pub mod util; pub use memory_region::MemoryRegion; +pub use string_ring_buffer::StringRingBuffer; pub use util::{ align_down, align_up, halt, is_aligned, overlap, page_align_up, page_offset, zero_mem_region, }; diff --git a/kernel/src/utils/string_ring_buffer.rs b/kernel/src/utils/string_ring_buffer.rs new file mode 100644 index 000000000..4f3c8b6ac --- /dev/null +++ b/kernel/src/utils/string_ring_buffer.rs @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2024 SUSE LLC +// +// Author: Roy Hopkins + +extern crate alloc; + +use alloc::string::String; +use core::cmp::min; +use core::fmt::Debug; + +#[derive(Copy, Clone, Debug)] +pub struct StringRingBuffer { + data: [char; T], + tail: usize, + head: usize, + empty: bool, +} + +impl StringRingBuffer { + pub const fn new() -> Self { + Self { + data: ['\0'; T], + tail: 0, + head: 0, + empty: true, + } + } + + pub fn write(&mut self, s: impl Iterator) { + s.for_each(|c| self.write_char(c)); + } + + pub fn write_char(&mut self, c: char) { + let full = !self.empty && (self.head == self.tail); + self.data[self.head] = c; + self.head = (self.head + 1) % T; + if full { + self.tail = self.head; + } + self.empty = false; + } + + pub fn read_char(&mut self) -> Option { + if !self.empty { + let c = self.data[self.tail]; + self.tail = (self.tail + 1) % T; + self.empty = self.tail == self.head; + Some(c) + } else { + None + } + } + + pub fn read(&mut self) -> Option { + if !self.empty { + let len = if self.head == self.tail { + T + } else { + ((self.head + T) - self.tail) % T + }; + let end_len = min(T - self.tail, len); + let start_len = len - end_len; + + let a: String = self.data[self.tail..(self.tail + end_len)].iter().collect(); + let b: String = self.data[0..start_len].iter().collect(); + self.tail = self.head; + self.empty = true; + Some(a + &b) + } else { + None + } + } +} + +impl Default for StringRingBuffer { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ring_one_string() { + let mut ring = StringRingBuffer::<32>::new(); + ring.write("Hello".chars()); + + let s = ring.read(); + assert!(s.is_some()); + assert_eq!(s.unwrap(), "Hello"); + } + + #[test] + fn test_ring_two_strings() { + let mut ring = StringRingBuffer::<32>::new(); + ring.write("Hello".chars()); + ring.write("Hello".chars()); + + let s = ring.read(); + assert!(s.is_some()); + assert_eq!(s.unwrap(), "HelloHello"); + } + + #[test] + fn test_ring_wrap() { + let mut ring = StringRingBuffer::<32>::new(); + ring.write("0000000000000000".chars()); + ring.write("1111111111111111".chars()); + ring.write("2222222222222222".chars()); + + let s = ring.read(); + assert!(s.is_some()); + assert_eq!(s.unwrap(), "11111111111111112222222222222222"); + } + + #[test] + fn test_ring_overflow() { + let mut ring = StringRingBuffer::<32>::new(); + ring.write("000000000000000011111111111111112222222222222222".chars()); + + let s = ring.read(); + assert!(s.is_some()); + assert_eq!(s.unwrap(), "11111111111111112222222222222222"); + } + + #[test] + fn test_ring_second_read() { + let mut ring = StringRingBuffer::<32>::new(); + ring.write("Testing".chars()); + + let s = ring.read(); + assert!(s.is_some()); + assert_eq!(s.unwrap(), "Testing"); + let s = ring.read(); + assert!(s.is_none()); + } + + #[test] + fn test_ring_second_wrwr() { + let mut ring = StringRingBuffer::<32>::new(); + ring.write("Testing1".chars()); + let s = ring.read(); + assert!(s.is_some()); + assert_eq!(s.unwrap(), "Testing1"); + + ring.write("Testing2".chars()); + let s = ring.read(); + assert!(s.is_some()); + assert_eq!(s.unwrap(), "Testing2"); + } +} From 2eba737a4ba7ec38b0363acd1a4869f5720e1a33 Mon Sep 17 00:00:00 2001 From: Vasant Karasulli Date: Wed, 19 Jun 2024 09:40:46 +0200 Subject: [PATCH 2/6] Implement a log buffer which stores all log messages in memory This log buffer is based on the string ring buffer in utils/StringRingBuffer. Signed-off-by: Vasant Karasulli --- kernel/src/lib.rs | 1 + kernel/src/log_buffer/mod.rs | 53 ++++++++++++++++++++++++++++++++++++ kernel/src/string.rs | 8 ++++++ kernel/src/types.rs | 1 + 4 files changed, 63 insertions(+) create mode 100644 kernel/src/log_buffer/mod.rs diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 250b94e0b..0575fcdf1 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -27,6 +27,7 @@ pub mod insn_decode; pub mod io; pub mod kernel_region; pub mod locking; +pub mod log_buffer; pub mod mm; pub mod platform; pub mod protocols; diff --git a/kernel/src/log_buffer/mod.rs b/kernel/src/log_buffer/mod.rs new file mode 100644 index 000000000..ab2b4df37 --- /dev/null +++ b/kernel/src/log_buffer/mod.rs @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2023 SUSE LLC +// +// Author: Vasant Karasulli + +extern crate alloc; +use core::fmt::Debug; + +use crate::locking::{LockGuard, SpinLock}; +use crate::string::FixedString; +use crate::types::{LINE_BUFFER_SIZE, PAGE_SIZE}; +use crate::utils::StringRingBuffer; + +use alloc::vec; +use alloc::vec::Vec; + +const BUF_SIZE: usize = PAGE_SIZE / core::mem::size_of::(); + +#[derive(Copy, Clone, Debug)] +pub struct LogBuffer { + buf: StringRingBuffer, +} + +impl LogBuffer { + const fn new() -> Self { + Self { + buf: StringRingBuffer::::new(), + } + } + + pub fn migrate(&mut self, lb: &SpinLock) { + self.buf = lb.lock().buf; + } + + pub fn write_log(&mut self, s: &FixedString) { + self.buf.write(s.iter()); + } + + // A method used for testing + pub fn read_log(&mut self) -> Vec { + if let Some(str) = self.buf.read() { + str.as_bytes().to_vec() + } else { + vec![] + } + } +} + +static LB: SpinLock = SpinLock::new(LogBuffer::new()); +pub fn log_buffer() -> LockGuard<'static, LogBuffer> { + LB.lock() +} diff --git a/kernel/src/string.rs b/kernel/src/string.rs index a91b2150b..f6e0969b4 100644 --- a/kernel/src/string.rs +++ b/kernel/src/string.rs @@ -36,6 +36,14 @@ impl FixedString { pub fn length(&self) -> usize { self.len } + + pub fn iter(&self) -> impl Iterator + '_ { + self.data.iter().take(self.len).copied() + } + + pub fn clear(&mut self) { + self.len = 0; + } } impl Default for FixedString { diff --git a/kernel/src/types.rs b/kernel/src/types.rs index e77ba8b0b..f85ff425d 100644 --- a/kernel/src/types.rs +++ b/kernel/src/types.rs @@ -47,6 +47,7 @@ pub const GUEST_VMPL: usize = 2; const _: () = assert!(GUEST_VMPL > 0 && GUEST_VMPL < VMPL_MAX); pub const MAX_CPUS: usize = 512; +pub const LINE_BUFFER_SIZE: usize = 256; /// Length in byte which represents maximum 8 bytes(u64) #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] From a99fbe2784e13cfd350e023693c094992228329c Mon Sep 17 00:00:00 2001 From: Vasant Karasulli Date: Wed, 19 Jun 2024 09:43:08 +0200 Subject: [PATCH 3/6] Implement a percpu line buffer to store the individual log messages Each individual log message gets written into the global log buffer from the percpu line buffer. Signed-off-by: Vasant Karasulli --- kernel/src/cpu/line_buffer.rs | 118 ++++++++++++++++++++++++++++++++++ kernel/src/cpu/mod.rs | 1 + kernel/src/cpu/percpu.rs | 7 ++ 3 files changed, 126 insertions(+) create mode 100644 kernel/src/cpu/line_buffer.rs diff --git a/kernel/src/cpu/line_buffer.rs b/kernel/src/cpu/line_buffer.rs new file mode 100644 index 000000000..d858be2dd --- /dev/null +++ b/kernel/src/cpu/line_buffer.rs @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2023 SUSE LLC +// +// Author: Vasant Karasulli + +use crate::console::_print; +use crate::cpu::percpu::this_cpu; +use crate::log_buffer::log_buffer; +use crate::string::FixedString; +use crate::types::LINE_BUFFER_SIZE; +use crate::utils::immut_after_init::{ImmutAfterInitCell, ImmutAfterInitResult}; +use core::fmt; +use core::fmt::Write; + +#[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 + log_buffer().write_log(&self.buf); + self.buf.clear(); + } + } + } +} + +impl Write for LineBuffer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_buffer(s); + Ok(()) + } +} + +impl Default for LineBuffer { + fn default() -> Self { + Self::new() + } +} + +#[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 this_cpu().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) -> ImmutAfterInitResult<()> { + BUFFER_LOGGER.init(&BufferLogger::new(component))?; + + if let Err(e) = log::set_logger(&*BUFFER_LOGGER) { + // Failed to install the BufferLogger, presumably because something had + // installed another logger before. No logs will be stored in the buffer. + // Print an error string. + _print(format_args!( + "[{}]: ERROR: failed to install buffer logger: {:?}", + component, e, + )); + } + + // Log levels are to be configured via the log's library feature configuration. + log::set_max_level(log::LevelFilter::Trace); + Ok(()) +} diff --git a/kernel/src/cpu/mod.rs b/kernel/src/cpu/mod.rs index 5f57951b1..bcf3045a4 100644 --- a/kernel/src/cpu/mod.rs +++ b/kernel/src/cpu/mod.rs @@ -13,6 +13,7 @@ pub mod features; pub mod gdt; pub mod idt; pub mod irq_state; +pub mod line_buffer; pub mod mem; pub mod msr; pub mod percpu; diff --git a/kernel/src/cpu/percpu.rs b/kernel/src/cpu/percpu.rs index 6908c4b86..a7aa4e54e 100644 --- a/kernel/src/cpu/percpu.rs +++ b/kernel/src/cpu/percpu.rs @@ -10,6 +10,7 @@ use super::gdt_mut; use super::tss::{X86Tss, IST_DF}; use crate::address::{Address, PhysAddr, VirtAddr}; use crate::cpu::idt::common::INT_INJ_VECTOR; +use crate::cpu::line_buffer::LineBuffer; use crate::cpu::tss::TSS_LIMIT; use crate::cpu::vmsa::{init_guest_vmsa, init_svsm_vmsa}; use crate::cpu::{IrqState, LocalApic}; @@ -313,6 +314,7 @@ pub struct PerCpu { init_stack: Cell>, ist: IstStacks, + ln_buf: RefCell, /// Stack boundaries of the currently running task. current_stack: Cell>, @@ -344,6 +346,7 @@ impl PerCpu { hv_doorbell: Cell::new(None), init_stack: Cell::new(None), ist: IstStacks::new(), + ln_buf: RefCell::new(LineBuffer::new()), current_stack: Cell::new(MemoryRegion::new(VirtAddr::null(), 0)), } } @@ -852,6 +855,10 @@ impl PerCpu { tss.stacks[0] = addr; self.tss.set(tss); } + + pub fn get_line_buffer(&self) -> RefMut<'_, LineBuffer> { + self.ln_buf.borrow_mut() + } } pub fn this_cpu() -> &'static PerCpu { From 64e602387e9c02ebc259cc2076409ee0c8da6bad Mon Sep 17 00:00:00 2001 From: Vasant Karasulli Date: Wed, 16 Oct 2024 12:06:43 +0200 Subject: [PATCH 4/6] Initialize log buffer and migrate it from stage2 to svsm kernel Introduce a new struct MigrateInfo which contains the data to be migrated from stage2 to svsm kernel. Signed-off-by: Vasant Karasulli --- kernel/src/lib.rs | 1 + kernel/src/log_buffer/mod.rs | 8 ++++++++ kernel/src/migrate.rs | 25 +++++++++++++++++++++++++ kernel/src/stage2.rs | 9 ++++++--- kernel/src/svsm.rs | 13 ++++++++----- 5 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 kernel/src/migrate.rs diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 0575fcdf1..c783f93d8 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -28,6 +28,7 @@ pub mod io; pub mod kernel_region; pub mod locking; pub mod log_buffer; +pub mod migrate; pub mod mm; pub mod platform; pub mod protocols; diff --git a/kernel/src/log_buffer/mod.rs b/kernel/src/log_buffer/mod.rs index ab2b4df37..b649e6055 100644 --- a/kernel/src/log_buffer/mod.rs +++ b/kernel/src/log_buffer/mod.rs @@ -47,7 +47,15 @@ impl LogBuffer { } } +pub fn migrate_log_buffer(log_buf: &SpinLock) { + LB.lock().migrate(log_buf); +} + static LB: SpinLock = SpinLock::new(LogBuffer::new()); pub fn log_buffer() -> LockGuard<'static, LogBuffer> { LB.lock() } + +pub fn get_lb() -> &'static SpinLock { + &LB +} diff --git a/kernel/src/migrate.rs b/kernel/src/migrate.rs new file mode 100644 index 000000000..3e58f398a --- /dev/null +++ b/kernel/src/migrate.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2023 SUSE LLC +// +// Author: Vasant Karasulli +use crate::address::VirtAddr; +use crate::locking::SpinLock; +use crate::log_buffer::LogBuffer; + +// struct containing information that +// is migrated from stage2 to svsm kernel +#[derive(Copy, Clone, Debug)] +pub struct MigrateInfo { + pub bitmap_addr: VirtAddr, + pub lb: &'static SpinLock, +} + +impl MigrateInfo { + pub fn new(vb: VirtAddr, lb: &'static SpinLock) -> Self { + MigrateInfo { + bitmap_addr: vb, + lb, + } + } +} diff --git a/kernel/src/stage2.rs b/kernel/src/stage2.rs index 1bfd6650e..f9fa774e7 100755 --- a/kernel/src/stage2.rs +++ b/kernel/src/stage2.rs @@ -19,14 +19,16 @@ use cpuarch::snp_cpuid::SnpCpuidTable; use elf::ElfError; use svsm::address::{Address, PhysAddr, VirtAddr}; use svsm::config::SvsmConfig; -use svsm::console::install_console_logger; use svsm::cpu::cpuid::{dump_cpuid_table, register_cpuid_table}; use svsm::cpu::gdt; use svsm::cpu::idt::stage2::{early_idt_init, early_idt_init_no_ghcb}; +use svsm::cpu::line_buffer::install_buffer_logger; use svsm::cpu::percpu::{this_cpu, PerCpu}; use svsm::error::SvsmError; use svsm::fw_cfg::FwCfg; use svsm::igvm_params::IgvmParams; +use svsm::log_buffer::get_lb; +use svsm::migrate::MigrateInfo; use svsm::mm::alloc::{memory_info, print_memory_info, root_mem_init}; use svsm::mm::pagetable::{paging_init_early, PTEntryFlags, PageTable}; use svsm::mm::validate::{ @@ -83,7 +85,7 @@ fn setup_env( early_idt_init_no_ghcb(); let debug_serial_port = config.debug_serial_port(); - install_console_logger("Stage2").expect("Console logger already initialized"); + install_buffer_logger("Stage2").expect("Console logger already initialized"); platform .env_setup(debug_serial_port, launch_info.vtom.try_into().unwrap()) .expect("Early environment setup failed"); @@ -442,6 +444,7 @@ pub extern "C" fn stage2_main(launch_info: &Stage2LaunchInfo) { ); let valid_bitmap = valid_bitmap_addr(); + let migrate_info = MigrateInfo::new(VirtAddr::from(valid_bitmap.bits()), get_lb()); // Shut down the GHCB unsafe { @@ -452,7 +455,7 @@ pub extern "C" fn stage2_main(launch_info: &Stage2LaunchInfo) { asm!("jmp *%rax", in("rax") u64::from(kernel_entry), in("r8") &launch_info, - in("r9") valid_bitmap.bits(), + in("r9") &migrate_info, options(att_syntax)) }; diff --git a/kernel/src/svsm.rs b/kernel/src/svsm.rs index ee4418c42..40f5f3975 100755 --- a/kernel/src/svsm.rs +++ b/kernel/src/svsm.rs @@ -17,11 +17,11 @@ use core::slice; use cpuarch::snp_cpuid::SnpCpuidTable; use svsm::address::{PhysAddr, VirtAddr}; use svsm::config::SvsmConfig; -use svsm::console::install_console_logger; use svsm::cpu::control_regs::{cr0_init, cr4_init}; use svsm::cpu::cpuid::{dump_cpuid_table, register_cpuid_table}; use svsm::cpu::gdt; use svsm::cpu::idt::svsm::{early_idt_init, idt_init}; +use svsm::cpu::line_buffer::install_buffer_logger; use svsm::cpu::percpu::current_ghcb; use svsm::cpu::percpu::PerCpu; use svsm::cpu::percpu::{this_cpu, this_cpu_shared}; @@ -33,6 +33,8 @@ use svsm::fs::{initialize_fs, populate_ram_fs}; use svsm::fw_cfg::FwCfg; use svsm::igvm_params::IgvmParams; use svsm::kernel_region::new_kernel_region; +use svsm::log_buffer::migrate_log_buffer; +use svsm::migrate::MigrateInfo; use svsm::mm::alloc::{memory_info, print_memory_info, root_mem_init}; use svsm::mm::memory::{init_memory_map, write_guest_memory_map}; use svsm::mm::pagetable::paging_init; @@ -63,7 +65,7 @@ extern "C" { * startup_64. * * %r8 Pointer to the KernelLaunchInfo structure - * %r9 Pointer to the valid-bitmap from stage2 + * %r9 Pointer to the MigrateInfo from stage2 */ global_asm!( r#" @@ -273,9 +275,9 @@ fn init_cpuid_table(addr: VirtAddr) { } #[no_mangle] -pub extern "C" fn svsm_start(li: &KernelLaunchInfo, vb_addr: usize) { +pub extern "C" fn svsm_start(li: &KernelLaunchInfo, mi: &MigrateInfo) { let launch_info: KernelLaunchInfo = *li; - let vb_ptr = core::ptr::NonNull::new(VirtAddr::new(vb_addr).as_mut_ptr::()).unwrap(); + let vb_ptr = core::ptr::NonNull::new(mi.bitmap_addr.as_mut_ptr::()).unwrap(); mapping_info_init(&launch_info); @@ -310,13 +312,14 @@ pub extern "C" fn svsm_start(li: &KernelLaunchInfo, vb_addr: usize) { cr0_init(); cr4_init(platform); - install_console_logger("SVSM").expect("Console logger already initialized"); + install_buffer_logger("SVSM").expect("Console logger already initialized"); platform .env_setup(debug_serial_port, launch_info.vtom.try_into().unwrap()) .expect("Early environment setup failed"); memory_init(&launch_info); migrate_valid_bitmap().expect("Failed to migrate valid-bitmap"); + migrate_log_buffer(mi.lb); let kernel_elf_len = (launch_info.kernel_elf_stage2_virt_end - launch_info.kernel_elf_stage2_virt_start) as usize; From 971a443e15dab751796e08b537cbb7b46c173b80 Mon Sep 17 00:00:00 2001 From: Vasant Karasulli Date: Fri, 18 Oct 2024 15:25:17 +0200 Subject: [PATCH 5/6] Add a feature named enable-console-log This feature can be used to enable printing log messages to console in addition to storing them in the log buffer. Use ENABLE_CONSOLE_LOG=1 to enable this feature. By default, this feature is disabled for release builds and enabled for debug builds. Signed-off-by: Vasant Karasulli --- Makefile | 10 +++++++++- kernel/Cargo.toml | 1 + kernel/src/cpu/line_buffer.rs | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index eefbb4e03..2bf0e4946 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,16 @@ SVSM_ARGS_TEST += --no-default-features --features ${FEATURES_TEST} ifdef RELEASE TARGET_PATH=release CARGO_ARGS += --release +ENABLE_CONSOLE_LOG ?= 0 else TARGET_PATH=debug +ENABLE_CONSOLE_LOG ?= 1 +endif + +STAGE2_ARGS = +ifeq ($(ENABLE_CONSOLE_LOG), 1) +SVSM_ARGS += --features enable-console-log +STAGE2_ARGS += --features enable-console-log endif ifdef OFFLINE @@ -124,7 +132,7 @@ bin/meta.bin: utils/gen_meta utils/print-meta bin ./utils/gen_meta $@ bin/stage2.bin: bin - cargo build --manifest-path kernel/Cargo.toml ${CARGO_ARGS} --bin stage2 + cargo build --manifest-path kernel/Cargo.toml ${CARGO_ARGS} ${STAGE2_ARGS} --bin stage2 objcopy -O binary ${STAGE2_ELF} $@ bin/svsm-kernel.elf: bin diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index d2abf5a43..7080dd71f 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -43,6 +43,7 @@ test.workspace = true default = [] enable-gdb = ["dep:gdbstub", "dep:gdbstub_arch"] mstpm = ["dep:libmstpm"] +enable-console-log = [] [dev-dependencies] diff --git a/kernel/src/cpu/line_buffer.rs b/kernel/src/cpu/line_buffer.rs index d858be2dd..bff42ad6d 100644 --- a/kernel/src/cpu/line_buffer.rs +++ b/kernel/src/cpu/line_buffer.rs @@ -78,18 +78,30 @@ impl log::Log for BufferLogger { line_buf .write_fmt(format_args!("[{}] {}: {}\n", comp, lvl, rec_args)) .unwrap(); + #[cfg(feature = "enable-console-log")] + { + _print(format_args!("[{}] {}: {}\n", comp, lvl, rec_args)); + } } log::Level::Info => { line_buf .write_fmt(format_args!("[{}] {}\n", comp, rec_args)) .unwrap(); + #[cfg(feature = "enable-console-log")] + { + _print(format_args!("[{}] {}\n", comp, rec_args)); + } } log::Level::Debug | log::Level::Trace => { line_buf .write_fmt(format_args!("[{}/{}] {} {}\n", comp, target, lvl, rec_args)) .unwrap(); + #[cfg(feature = "enable-console-log")] + { + _print(format_args!("[{}/{}] {} {}\n", comp, target, lvl, rec_args)); + } } } } From 45c52f8721ed645d9c6042c1a54b4b0658bc3a29 Mon Sep 17 00:00:00 2001 From: Vasant Karasulli Date: Wed, 19 Jun 2024 10:34:34 +0200 Subject: [PATCH 6/6] log_buffer: add unit tests to test the functionality Test the log_buffer by calling methods like write_log() and read_log(). Signed-off-by: Vasant Karasulli --- kernel/src/log_buffer/mod.rs | 106 ++++++++++++++++++++++++++++++++++- kernel/src/types.rs | 5 ++ 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/kernel/src/log_buffer/mod.rs b/kernel/src/log_buffer/mod.rs index b649e6055..9758afbcf 100644 --- a/kernel/src/log_buffer/mod.rs +++ b/kernel/src/log_buffer/mod.rs @@ -4,18 +4,27 @@ // // Author: Vasant Karasulli +#[cfg(test)] extern crate alloc; use core::fmt::Debug; use crate::locking::{LockGuard, SpinLock}; use crate::string::FixedString; -use crate::types::{LINE_BUFFER_SIZE, PAGE_SIZE}; + +use crate::types::LINE_BUFFER_SIZE; +#[cfg(not(test))] +use crate::types::PAGE_SIZE; use crate::utils::StringRingBuffer; +#[cfg(test)] use alloc::vec; +#[cfg(test)] use alloc::vec::Vec; +#[cfg(not(test))] const BUF_SIZE: usize = PAGE_SIZE / core::mem::size_of::(); +#[cfg(test)] +const BUF_SIZE: usize = 64; #[derive(Copy, Clone, Debug)] pub struct LogBuffer { @@ -37,7 +46,7 @@ impl LogBuffer { self.buf.write(s.iter()); } - // A method used for testing + #[cfg(test)] pub fn read_log(&mut self) -> Vec { if let Some(str) = self.buf.read() { str.as_bytes().to_vec() @@ -59,3 +68,96 @@ pub fn log_buffer() -> LockGuard<'static, LogBuffer> { pub fn get_lb() -> &'static SpinLock { &LB } + +#[cfg(test)] +mod tests { + use super::*; + use crate::types::LINE_BUFFER_SIZE; + + #[test] + fn test_read_write_normal() { + let mut fs = FixedString::::new(); + for i in 1..=LINE_BUFFER_SIZE { + fs.push(char::from_u32(i as u32).unwrap()); + } + + let mut lb = LogBuffer::new(); + lb.write_log(&fs); + + let v = lb.read_log(); + assert_eq!(v.len(), LINE_BUFFER_SIZE); + for i in 1..=v.len() { + assert_eq!(i as u8, v[i - 1]); + } + } + + #[test] + fn test_read_write_interleaved() { + let mut fs = FixedString::::new(); + for i in 1..=LINE_BUFFER_SIZE / 2 { + fs.push(char::from_u32(i as u32).unwrap()); + } + + let mut lb = LogBuffer::new(); + lb.write_log(&fs); + + let v = lb.read_log(); + assert_eq!(v.len(), LINE_BUFFER_SIZE / 2); + for i in 1..=v.len() { + assert_eq!(i as u8, v[i - 1]); + } + + fs.clear(); + for i in LINE_BUFFER_SIZE / 2..LINE_BUFFER_SIZE { + fs.push(char::from_u32((i + 1) as u32).unwrap()); + } + + lb.write_log(&fs); + + let v = lb.read_log(); + assert_eq!(v.len(), LINE_BUFFER_SIZE / 2); + for i in 1..v.len() { + let val = (i + LINE_BUFFER_SIZE / 2) as u8; + assert_eq!(val, v[i - 1]); + } + } + + #[test] + fn test_write_wrap_around() { + let mut fs = FixedString::::new(); + for i in 1..=LINE_BUFFER_SIZE / 2 { + fs.push(char::from_u32(i as u32).unwrap()); + } + + let mut lb = LogBuffer::new(); + lb.write_log(&fs); + + let v = lb.read_log(); + assert_eq!(v.len(), LINE_BUFFER_SIZE / 2); + for i in 1..=v.len() { + assert_eq!(i as u8, v[i - 1]); + } + + fs.clear(); + for i in 1..=LINE_BUFFER_SIZE { + let val = (i + LINE_BUFFER_SIZE / 2) as u32; + fs.push(char::from_u32(val).unwrap()); + } + + lb.write_log(&fs); + + let v = lb.read_log(); + assert_eq!(v.len(), LINE_BUFFER_SIZE); + for i in 1..v.len() { + let val = (i + LINE_BUFFER_SIZE / 2) as u8; + assert_eq!(val, v[i - 1]); + } + } + + #[test] + fn test_read_empty_buffer() { + let mut lb = LogBuffer::new(); + let v = lb.read_log(); + assert_eq!(v.len(), 0); + } +} diff --git a/kernel/src/types.rs b/kernel/src/types.rs index f85ff425d..dc07b166d 100644 --- a/kernel/src/types.rs +++ b/kernel/src/types.rs @@ -47,8 +47,13 @@ pub const GUEST_VMPL: usize = 2; const _: () = assert!(GUEST_VMPL > 0 && GUEST_VMPL < VMPL_MAX); pub const MAX_CPUS: usize = 512; + +#[cfg(not(test))] pub const LINE_BUFFER_SIZE: usize = 256; +#[cfg(test)] +pub const LINE_BUFFER_SIZE: usize = 64; + /// Length in byte which represents maximum 8 bytes(u64) #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub enum Bytes {