Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rework console and resolve deadlock when panicking while printing #1473

Merged
merged 6 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 35 additions & 32 deletions src/arch/aarch64/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -25,7 +24,41 @@ use crate::env;

const SERIAL_PORT_BAUDRATE: u32 = 115_200;

static COM1: SpinMutex<SerialPort> = 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.
///
Expand Down Expand Up @@ -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() {
Expand Down
5 changes: 0 additions & 5 deletions src/arch/aarch64/kernel/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@ impl SerialPort {
}
}

#[allow(dead_code)]
pub fn read(&mut self) -> Option<u8> {
None
}

pub fn init(&self, _baudrate: u32) {
// We don't do anything here (yet).
}
Expand Down
6 changes: 0 additions & 6 deletions src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")] {
Expand All @@ -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")]
Expand All @@ -68,8 +64,6 @@ cfg_if::cfg_if! {
core_local,
get_processor_count,
interrupts,
message_output_init,
output_message_buf,
scheduler,
switch,
};
Expand Down
33 changes: 22 additions & 11 deletions src/arch/riscv64/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<usize>> = InitCell::new(Vec::new());
Expand Down Expand Up @@ -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();
Expand Down
6 changes: 3 additions & 3 deletions src/arch/x86_64/kernel/interrupts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}");
}
}
}
Expand Down
73 changes: 43 additions & 30 deletions src/arch/x86_64/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -37,8 +36,49 @@ pub(crate) mod systemtime;
#[cfg(feature = "vga")]
mod vga;

/// Serial port to print kernel messages
pub(crate) static COM1: InterruptSpinMutex<Option<SerialPort>> = 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<u8> {
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)
Expand Down Expand Up @@ -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() {
Expand Down
11 changes: 2 additions & 9 deletions src/arch/x86_64/kernel/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -52,18 +52,11 @@ impl SerialPort {
}
}

#[allow(dead_code)]
#[cfg(feature = "shell")]
pub fn read(&mut self) -> Option<u8> {
self.buffer.pop_front()
}

#[allow(dead_code)]
#[cfg(not(feature = "shell"))]
pub fn read(&mut self) -> Option<u8> {
None
}

pub fn send(&mut self, buf: &[u8]) {
match &mut self.inner {
SerialInner::Uhyve(s) => {
Expand All @@ -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();
Expand Down
35 changes: 29 additions & 6 deletions src/console.rs
Original file line number Diff line number Diff line change
@@ -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<u8> {
self.0.read()
}
}

/// A collection of methods that are required to format
/// a message to Hermit's console.
Expand All @@ -13,22 +28,30 @@ 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<Console> = InterruptTicketMutex::new(Console(()));
pub static CONSOLE: Lazy<InterruptTicketMutex<Console>> =
Lazy::new(|| InterruptTicketMutex::new(Console::new()));

#[doc(hidden)]
pub fn _print(args: fmt::Arguments<'_>) {
use fmt::Write;
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::*;
Expand Down
Loading
Loading