diff --git a/src/arch/aarch64/kernel/mod.rs b/src/arch/aarch64/kernel/mod.rs index 5ca663be46..fb0f5685c3 100644 --- a/src/arch/aarch64/kernel/mod.rs +++ b/src/arch/aarch64/kernel/mod.rs @@ -16,7 +16,6 @@ use core::arch::global_asm; use core::str; use core::sync::atomic::{AtomicU32, AtomicU64, Ordering}; -use hermit_sync::SpinMutex; use memory_addresses::arch::aarch64::{PhysAddr, VirtAddr}; use crate::arch::aarch64::kernel::core_local::*; @@ -25,7 +24,41 @@ use crate::env; const SERIAL_PORT_BAUDRATE: u32 = 115_200; -static COM1: SpinMutex = SpinMutex::new(SerialPort::new(0x800)); +pub struct Console { + serial_port: SerialPort, +} + +impl Console { + pub fn new() -> Self { + CoreLocal::install(); + + let base = env::boot_info() + .hardware_info + .serial_port_base + .map(|uartport| uartport.get()) + .unwrap_or_default() + .try_into() + .unwrap(); + + let serial_port = SerialPort::new(base); + + serial_port.init(SERIAL_PORT_BAUDRATE); + + Self { serial_port } + } + + pub fn write(&mut self, buf: &[u8]) { + for byte in buf { + self.serial_port.write_byte(*byte); + } + } +} + +impl Default for Console { + fn default() -> Self { + Self::new() + } +} /// `CPU_ONLINE` is the count of CPUs that finished initialization. /// @@ -77,36 +110,6 @@ pub fn args() -> Option<&'static str> { None } -/// Earliest initialization function called by the Boot Processor. -pub fn message_output_init() { - CoreLocal::install(); - - let mut com1 = COM1.lock(); - - com1.port_address = env::boot_info() - .hardware_info - .serial_port_base - .map(|uartport| uartport.get()) - .unwrap_or_default() - .try_into() - .unwrap(); - - // We can only initialize the serial port here, because VGA requires processor - // configuration first. - com1.init(SERIAL_PORT_BAUDRATE); -} - -pub fn output_message_byte(byte: u8) { - // Output messages to the serial port. - COM1.lock().write_byte(byte); -} - -pub fn output_message_buf(buf: &[u8]) { - for byte in buf { - output_message_byte(*byte); - } -} - /// Real Boot Processor initialization as soon as we have put the first Welcome message on the screen. #[cfg(target_os = "none")] pub fn boot_processor_init() { diff --git a/src/arch/aarch64/kernel/serial.rs b/src/arch/aarch64/kernel/serial.rs index b061573564..15577c22d3 100644 --- a/src/arch/aarch64/kernel/serial.rs +++ b/src/arch/aarch64/kernel/serial.rs @@ -34,11 +34,6 @@ impl SerialPort { } } - #[allow(dead_code)] - pub fn read(&mut self) -> Option { - None - } - pub fn init(&self, _baudrate: u32) { // We don't do anything here (yet). } diff --git a/src/arch/mod.rs b/src/arch/mod.rs index cd3a87fbb6..d2106bd5e6 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -20,8 +20,6 @@ cfg_if::cfg_if! { pub(crate) use self::aarch64::kernel::application_processor_init; pub(crate) use self::aarch64::kernel::{ get_processor_count, - message_output_init, - output_message_buf, }; pub use self::aarch64::mm::paging::{BasePageSize, PageSize}; } else if #[cfg(target_arch = "x86_64")] { @@ -46,8 +44,6 @@ cfg_if::cfg_if! { pub(crate) use self::x86_64::kernel::boot_processor_init; pub(crate) use self::x86_64::kernel::{ get_processor_count, - message_output_init, - output_message_buf, }; pub use self::x86_64::mm::paging::{BasePageSize, PageSize}; #[cfg(feature = "common-os")] @@ -68,8 +64,6 @@ cfg_if::cfg_if! { core_local, get_processor_count, interrupts, - message_output_init, - output_message_buf, scheduler, switch, }; diff --git a/src/arch/riscv64/kernel/mod.rs b/src/arch/riscv64/kernel/mod.rs index f194034c25..c975de6adc 100644 --- a/src/arch/riscv64/kernel/mod.rs +++ b/src/arch/riscv64/kernel/mod.rs @@ -27,6 +27,28 @@ use crate::config::KERNEL_STACK_SIZE; use crate::env; use crate::init_cell::InitCell; +pub struct Console {} + +impl Console { + pub fn new() -> Self { + CoreLocal::install(); + + Self {} + } + + pub fn write(&mut self, buf: &[u8]) { + for byte in buf { + sbi_rt::console_write_byte(*byte); + } + } +} + +impl Default for Console { + fn default() -> Self { + Self::new() + } +} + // Used to store information about available harts. The index of the hart in the vector // represents its CpuId and does not need to match its hart_id pub(crate) static HARTS_AVAILABLE: InitCell> = InitCell::new(Vec::new()); @@ -109,17 +131,6 @@ pub fn get_current_boot_id() -> u32 { CURRENT_BOOT_ID.load(Ordering::Relaxed) } -/// Earliest initialization function called by the Boot Processor. -pub fn message_output_init() { - CoreLocal::install(); -} - -pub fn output_message_buf(buf: &[u8]) { - for byte in buf { - sbi_rt::console_write_byte(*byte); - } -} - /// Real Boot Processor initialization as soon as we have put the first Welcome message on the screen. pub fn boot_processor_init() { devicetree::init(); diff --git a/src/arch/x86_64/kernel/interrupts.rs b/src/arch/x86_64/kernel/interrupts.rs index af73c19b16..aa8047bf3a 100644 --- a/src/arch/x86_64/kernel/interrupts.rs +++ b/src/arch/x86_64/kernel/interrupts.rs @@ -373,17 +373,17 @@ impl IrqStatistics { } pub(crate) fn print_statistics() { - println!("Number of interrupts"); + panic_println!("Number of interrupts"); for (core_id, irg_statistics) in IRQ_COUNTERS.lock().iter() { for (i, counter) in irg_statistics.counters.iter().enumerate() { let counter = counter.load(Ordering::Relaxed); if counter > 0 { match get_irq_name(i.try_into().unwrap()) { Some(name) => { - println!("[{core_id}][{name}]: {counter}"); + panic_println!("[{core_id}][{name}]: {counter}"); } _ => { - println!("[{core_id}][{i}]: {counter}"); + panic_println!("[{core_id}][{i}]: {counter}"); } } } diff --git a/src/arch/x86_64/kernel/mod.rs b/src/arch/x86_64/kernel/mod.rs index b6904ae252..85219bfa1b 100644 --- a/src/arch/x86_64/kernel/mod.rs +++ b/src/arch/x86_64/kernel/mod.rs @@ -5,7 +5,6 @@ use core::ptr; use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering}; use hermit_entry::boot_info::{PlatformInfo, RawBootInfo}; -use hermit_sync::InterruptSpinMutex; use memory_addresses::{PhysAddr, VirtAddr}; use x86::controlregs::{cr0, cr0_write, cr4, Cr0}; @@ -37,8 +36,49 @@ pub(crate) mod systemtime; #[cfg(feature = "vga")] mod vga; -/// Serial port to print kernel messages -pub(crate) static COM1: InterruptSpinMutex> = InterruptSpinMutex::new(None); +pub struct Console { + serial_port: SerialPort, +} + +impl Console { + pub fn new() -> Self { + CoreLocal::install(); + + let base = env::boot_info() + .hardware_info + .serial_port_base + .unwrap() + .get(); + let serial_port = unsafe { SerialPort::new(base) }; + Self { serial_port } + } + + pub fn write(&mut self, buf: &[u8]) { + self.serial_port.send(buf); + + #[cfg(feature = "vga")] + for &byte in buf { + // vga::write_byte() checks if VGA support has been initialized, + // so we don't need any additional if clause around it. + vga::write_byte(byte); + } + } + + pub fn buffer_input(&mut self) { + self.serial_port.buffer_input(); + } + + #[cfg(feature = "shell")] + pub fn read(&mut self) -> Option { + self.serial_port.read() + } +} + +impl Default for Console { + fn default() -> Self { + Self::new() + } +} pub fn get_ram_address() -> PhysAddr { PhysAddr::new(env::boot_info().hardware_info.phys_addr_range.start) @@ -106,33 +146,6 @@ pub fn args() -> Option<&'static str> { } } -// We can only initialize the serial port here, because VGA requires processor -// configuration first. -/// Earliest initialization function called by the Boot Processor. -pub fn message_output_init() { - CoreLocal::install(); - - let base = env::boot_info() - .hardware_info - .serial_port_base - .unwrap() - .get(); - let serial_port = unsafe { SerialPort::new(base) }; - *COM1.lock() = Some(serial_port); -} - -pub fn output_message_buf(buf: &[u8]) { - // Output messages to the serial port and VGA screen in unikernel mode. - COM1.lock().as_mut().unwrap().send(buf); - - #[cfg(feature = "vga")] - for &byte in buf { - // vga::write_byte() checks if VGA support has been initialized, - // so we don't need any additional if clause around it. - vga::write_byte(byte); - } -} - /// Real Boot Processor initialization as soon as we have put the first Welcome message on the screen. #[cfg(target_os = "none")] pub fn boot_processor_init() { diff --git a/src/arch/x86_64/kernel/serial.rs b/src/arch/x86_64/kernel/serial.rs index 5db44653ca..aef8eb107c 100644 --- a/src/arch/x86_64/kernel/serial.rs +++ b/src/arch/x86_64/kernel/serial.rs @@ -3,9 +3,9 @@ use alloc::collections::VecDeque; use x86_64::instructions::port::Port; +use crate::arch::x86_64::kernel::apic; use crate::arch::x86_64::kernel::core_local::increment_irq_counter; use crate::arch::x86_64::kernel::interrupts::{self, IDT}; -use crate::arch::x86_64::kernel::{apic, COM1}; const SERIAL_IRQ: u8 = 36; @@ -52,18 +52,11 @@ impl SerialPort { } } - #[allow(dead_code)] #[cfg(feature = "shell")] pub fn read(&mut self) -> Option { self.buffer.pop_front() } - #[allow(dead_code)] - #[cfg(not(feature = "shell"))] - pub fn read(&mut self) -> Option { - None - } - pub fn send(&mut self, buf: &[u8]) { match &mut self.inner { SerialInner::Uhyve(s) => { @@ -83,7 +76,7 @@ impl SerialPort { } extern "x86-interrupt" fn serial_interrupt(_stack_frame: crate::interrupts::ExceptionStackFrame) { - COM1.lock().as_mut().unwrap().buffer_input(); + crate::console::CONSOLE.lock().0.buffer_input(); increment_irq_counter(SERIAL_IRQ); apic::eoi(); diff --git a/src/console.rs b/src/console.rs index cd84f2deb8..9818ebdaaa 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,10 +1,25 @@ -use core::fmt; +use core::{fmt, mem}; -use hermit_sync::InterruptTicketMutex; +use hermit_sync::{InterruptTicketMutex, Lazy}; use crate::arch; -pub(crate) struct Console(()); +pub struct Console(pub arch::kernel::Console); + +impl Console { + fn new() -> Self { + Self(arch::kernel::Console::new()) + } + + pub fn write(&mut self, buf: &[u8]) { + self.0.write(buf); + } + + #[cfg(feature = "shell")] + pub fn read(&mut self) -> Option { + self.0.read() + } +} /// A collection of methods that are required to format /// a message to Hermit's console. @@ -13,15 +28,15 @@ impl fmt::Write for Console { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { if !s.is_empty() { - let buf = s.as_bytes(); - arch::output_message_buf(buf); + self.write(s.as_bytes()); } Ok(()) } } -static CONSOLE: InterruptTicketMutex = InterruptTicketMutex::new(Console(())); +pub static CONSOLE: Lazy> = + Lazy::new(|| InterruptTicketMutex::new(Console::new())); #[doc(hidden)] pub fn _print(args: fmt::Arguments<'_>) { @@ -29,6 +44,14 @@ pub fn _print(args: fmt::Arguments<'_>) { CONSOLE.lock().write_fmt(args).unwrap(); } +#[doc(hidden)] +pub fn _panic_print(args: fmt::Arguments<'_>) { + use fmt::Write; + let mut console = unsafe { CONSOLE.make_guard_unchecked() }; + console.write_fmt(args).ok(); + mem::forget(console); +} + #[cfg(all(test, not(target_os = "none")))] mod tests { use super::*; diff --git a/src/fd/stdio.rs b/src/fd/stdio.rs index 92f9558c2d..203793e6bb 100644 --- a/src/fd/stdio.rs +++ b/src/fd/stdio.rs @@ -2,7 +2,6 @@ use alloc::boxed::Box; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use core::ptr; -use async_lock::Mutex; use async_trait::async_trait; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use memory_addresses::VirtAddr; @@ -11,13 +10,12 @@ use x86::io::*; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use crate::arch::mm::paging; +use crate::console::CONSOLE; use crate::fd::{ObjectInterface, PollEvent, STDERR_FILENO, STDOUT_FILENO}; -use crate::{arch, io}; +use crate::io; const UHYVE_PORT_WRITE: u16 = 0x400; -static IO_LOCK: Mutex<()> = Mutex::new(()); - #[repr(C, packed)] struct SysWrite { fd: i32, @@ -91,9 +89,7 @@ impl ObjectInterface for GenericStdout { } async fn write(&self, buf: &[u8]) -> io::Result { - let _guard = IO_LOCK.lock().await; - arch::output_message_buf(buf); - + CONSOLE.lock().write(buf); Ok(buf.len()) } } @@ -115,9 +111,7 @@ impl ObjectInterface for GenericStderr { } async fn write(&self, buf: &[u8]) -> io::Result { - let _guard = IO_LOCK.lock().await; - arch::output_message_buf(buf); - + CONSOLE.lock().write(buf); Ok(buf.len()) } } diff --git a/src/lib.rs b/src/lib.rs index 3fe573ac06..b6824cff2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,7 +182,7 @@ fn synch_all_cores() { #[cfg(target_os = "none")] fn boot_processor_main() -> ! { // Initialize the kernel and hardware. - arch::message_output_init(); + hermit_sync::Lazy::force(&console::CONSOLE); unsafe { logging::init(); } @@ -262,7 +262,7 @@ fn application_processor_main() -> ! { #[panic_handler] fn panic(info: &core::panic::PanicInfo<'_>) -> ! { let core_id = crate::arch::core_local::core_id(); - println!("[{core_id}][PANIC] {info}"); + panic_println!("[{core_id}][PANIC] {info}\n"); crate::scheduler::shutdown(1); } diff --git a/src/macros.rs b/src/macros.rs index b63426156d..871fc798f8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -27,6 +27,26 @@ macro_rules! println { }}; } +/// Emergency output. +#[cfg(target_os = "none")] +#[macro_export] +macro_rules! panic_println { + () => {{ + $crate::console::_panic_print(::core::format_args!("\n")); + }}; + ($($arg:tt)*) => {{ + $crate::console::_panic_print(::core::format_args!("{}\n", format_args!($($arg)*))); + }}; +} + +#[cfg(not(target_os = "none"))] +#[macro_export] +macro_rules! panic_println { + ($($arg:tt)*) => { + println!($($arg)*); + }; +} + /// Prints and returns the value of a given expression for quick and dirty /// debugging. // Copied from std/macros.rs diff --git a/src/shell.rs b/src/shell.rs index 4aa7d7b7aa..f9771ec625 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,10 +1,9 @@ use simple_shell::*; -use crate::arch::kernel::COM1; use crate::interrupts::print_statistics; fn read() -> Option { - COM1.lock().as_mut().map(|s| s.read())? + crate::console::CONSOLE.lock().read() } pub(crate) fn init() { diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index 3afa06dfdc..b1154a9aaa 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -50,7 +50,7 @@ pub trait SyscallInterface: Send + Sync { fn shutdown(&self, error_code: i32) -> ! { // This is a stable message used for detecting exit codes for different hypervisors. - println!("exit status {error_code}"); + panic_println!("exit status {error_code}"); arch::processor::shutdown(error_code) } diff --git a/xtask/src/clippy.rs b/xtask/src/clippy.rs index b13bb208c6..b5957a2720 100644 --- a/xtask/src/clippy.rs +++ b/xtask/src/clippy.rs @@ -18,7 +18,7 @@ impl Clippy { let triple = arch.triple(); cmd!(sh, "cargo clippy --target={triple}").run()?; cmd!(sh, "cargo clippy --target={triple}") - .arg("--features=acpi,dns,fsgsbase,pci,shell,smp,vga") + .arg("--features=acpi,dns,fsgsbase,pci,smp,vga") .run()?; cmd!(sh, "cargo clippy --target={triple}") .arg("--no-default-features") @@ -29,19 +29,27 @@ impl Clippy { .run()?; cmd!(sh, "cargo clippy --target={triple}") .arg("--no-default-features") - .arg("--features=acpi,fsgsbase,pci,shell,smp,vga") + .arg("--features=acpi,fsgsbase,pci,smp,vga") .run()?; - if *arch == Arch::Riscv64 { - cmd!(sh, "cargo clippy --target={triple}") - .arg("--no-default-features") - .arg("--features=gem-net,tcp") - .run()?; + match *arch { + Arch::X86_64 => { + cmd!(sh, "cargo clippy --target={triple}") + .arg("--features=shell") + .run()?; + } + Arch::Aarch64 => {} + Arch::Riscv64 => { + cmd!(sh, "cargo clippy --target={triple}") + .arg("--no-default-features") + .arg("--features=gem-net,tcp") + .run()?; + } } cmd!(sh, "cargo clippy --target={triple}") .arg("--no-default-features") - .arg("--features=acpi,fsgsbase,newlib,shell,smp,vga") + .arg("--features=acpi,fsgsbase,newlib,smp,vga") .run()?; }