diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8a42939..b82aae5 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -19,6 +19,13 @@ futures-util = { version = "0.3.31", default-features = false, features = [ "alloc", ] } linked_list_allocator = { version = "0.10.5", features = ["use_spin"] } +# unwinding = { version = "0.2.5", default-features = false, features = [ +# "unwinder", +# "fde-static", +# "personality", +# "panic", +# "hide-trace", +# ] } [build-dependencies] cc = "1.2.14" diff --git a/kernel/linker.ld b/kernel/linker.ld index b327207..a193ba6 100644 --- a/kernel/linker.ld +++ b/kernel/linker.ld @@ -46,6 +46,12 @@ SECTIONS *(.rodata .rodata.*) } :rodata + /* Adds support for stack unwinding using the unwinding crate. */ + . = ALIGN(8); + PROVIDE(__eh_frame = .); + .eh_frame : { KEEP (*(.eh_frame)) *(.eh_frame.*) } + + /* Move to the next memory page for .data */ . = ALIGN(CONSTANT(MAXPAGESIZE)); @@ -62,9 +68,8 @@ SECTIONS *(COMMON) } :data - /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */ + /* Discard .note.* and ~~.eh_frame*~~ since they may cause issues on some hosts. */ /DISCARD/ : { - *(.eh_frame*) *(.note .note.*) } } diff --git a/kernel/src/arch/x86_64/drivers/ascii/mod.rs b/kernel/src/arch/x86_64/drivers/ascii/mod.rs index d380b76..e2a75fa 100644 --- a/kernel/src/arch/x86_64/drivers/ascii/mod.rs +++ b/kernel/src/arch/x86_64/drivers/ascii/mod.rs @@ -237,12 +237,12 @@ macro_rules! print { } #[macro_export] -macro_rules! printlnerr { +macro_rules! eprintln { () => ($crate::printerr!("\n")); - ($($arg:tt)*) => ($crate::printerr!("{}\n", format_args!($($arg)*))); + ($($arg:tt)*) => ($crate::eprint!("{}\n", format_args!($($arg)*))); } #[macro_export] -macro_rules! printerr { +macro_rules! eprint { ($($arg:tt)*) => ($crate::prelude::_print_err(format_args!($($arg)*))); } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index a74060b..59d616f 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -22,16 +22,17 @@ use limine::BaseRevision; use x86_64::VirtAddr; pub mod arch; +mod panic; pub mod resources; #[allow(unused)] // We aren't using much of this right now. pub mod std; pub mod util; pub mod prelude { - pub use crate::std::io::{_print, _print_log, _serial_write}; pub use crate::{ - print, print_log, printerr, println, println_log, printlnerr, - serial_print, serial_println, + eprint, eprintln, print, print_log, println, println_log, serial_print, + serial_println, + std::io::{_print, _print_err, _print_log, _serial_write}, }; } diff --git a/kernel/src/panic.rs b/kernel/src/panic.rs new file mode 100644 index 0000000..a10718d --- /dev/null +++ b/kernel/src/panic.rs @@ -0,0 +1,75 @@ +// //! Stack-unwinding code for the Kernel. This is a modified version of https://github.com/nbdd0121/unwinding/blob/trunk/src/panic_handler.rs +// //! which does not support environment variables for obvious reasons, however we +// //! may implement a cmdline similar to Linux in the near future. + +// #![expect( +// clippy::empty_loop, +// reason = "We don't yet have working power management. This is OK for now." +// )] +// use crate::prelude::*; +// use alloc::boxed::Box; +// use core::any::Any; +// use core::cell::Cell; +// use core::ffi::c_void; +// use core::panic::{Location, PanicInfo}; +// use unwinding::abi::*; +// use unwinding::panic::begin_panic; + +// #[thread_local] +// static PANIC_COUNT: Cell = Cell::new(0); + +// #[link(name = "c")] +// unsafe extern "C" {} + +// fn stack_trace() { +// struct CallbackData { +// counter: usize, +// } + +// extern "C" fn callback( +// unwind_ctx: &UnwindContext<'_>, +// arg: *mut c_void, +// ) -> UnwindReasonCode { +// let data = unsafe { &mut *(arg as *mut CallbackData) }; +// data.counter += 1; +// eprintln!( +// "{:4}:{:#19x} - ", +// data.counter, +// _Unwind_GetIP(unwind_ctx) +// ); +// UnwindReasonCode::NO_REASON +// } +// let mut data = CallbackData { counter: 0 }; +// _Unwind_Backtrace(callback, &mut data as *mut _ as _); +// } + +// fn do_panic(msg: Box) -> ! { +// if PANIC_COUNT.get() >= 1 { +// stack_trace(); +// eprintln!("Thread panicked while processing panic. aborting."); +// loop {} +// } + +// PANIC_COUNT.set(1); +// stack_trace(); + +// let code = begin_panic(Box::new(msg)); +// eprintln!("Failed to initiate panic: error {}", code.0); + +// loop {} +// } + +// #[panic_handler] +// fn panic(info: &PanicInfo<'_>) -> ! { +// eprintln!("{}", info); + +// struct NoPayload; +// do_panic(Box::new(NoPayload)) +// } + +// #[track_caller] +// #[expect(unused, reason = "May still be used in the future.")] +// pub fn panic_any(msg: M) -> ! { +// eprintln!("Panicked at {}", Location::caller()); +// do_panic(Box::new(msg)) +// } diff --git a/kernel/src/std/io.rs b/kernel/src/std/io.rs deleted file mode 100644 index c974bd7..0000000 --- a/kernel/src/std/io.rs +++ /dev/null @@ -1,84 +0,0 @@ -pub use crate::arch::x86_64::drivers::{ - ascii::{_print, _print_err, _print_log}, - serial::_serial_write, -}; - -pub mod stdin { - use crate::arch::x86_64::drivers::{ - ascii::WRITER, - keyboard::{KeyStroke, get_keystroke_async, get_keystroke_optional}, - }; - use alloc::string::String; - - /// Reads a line of input from standard input asynchronously, returning a - /// `String` containing the input line. Does not include the newline - /// character at the end of the line. - /// - /// If the user presses the abort key (usually Ctrl+C), the returned string - /// will be empty. - /// - /// This function is currently unimplemented. - pub async fn read_line() -> String { - let mut writer = WRITER.lock(); - - let mut buff = String::new(); - loop { - match get_keystroke_async().await { - KeyStroke::Char(c) => match c { - '\n' => { - writer.write_glyph(c as u8); - return buff; - } - '\r' => { - writer.write_glyph(c as u8); - return buff; - } - '\x08' => { - if !buff.is_empty() { - buff.pop(); - writer.backspace(); - } - } - - c => { - writer.write_glyph(c as u8); - buff.push(c) - } - }, - KeyStroke::Enter => { - writer.write_glyph(b'\n'); - return buff; - } - KeyStroke::Backspace => { - if !buff.is_empty() { - buff.pop(); - writer.backspace(); - } - } - _ => continue, - } - } - } - - /// Reads a character from standard input and blocks the current task until - /// a character is available. - /// - /// # Note - /// - /// This function is not yet implemented. - pub async fn async_keystroke() -> KeyStroke { - get_keystroke_async().await - } - - /// Attempt to read a character from standard input without blocking the - /// current task. - /// - /// If no character is available, returns `None`. - /// - /// # Note - /// - /// This function is not yet implemented. - pub fn keystroke() -> Option { - get_keystroke_optional() - } -} diff --git a/kernel/src/std/io/mod.rs b/kernel/src/std/io/mod.rs new file mode 100644 index 0000000..b43503a --- /dev/null +++ b/kernel/src/std/io/mod.rs @@ -0,0 +1,6 @@ +pub use crate::arch::x86_64::drivers::{ + ascii::{_print, _print_err, _print_log}, + serial::_serial_write, +}; + +pub mod stdin; diff --git a/kernel/src/std/io/stdin.rs b/kernel/src/std/io/stdin.rs new file mode 100644 index 0000000..d9934a8 --- /dev/null +++ b/kernel/src/std/io/stdin.rs @@ -0,0 +1,75 @@ +use crate::arch::x86_64::drivers::{ + ascii::WRITER, + keyboard::{KeyStroke, get_keystroke_async, get_keystroke_optional}, +}; +use alloc::string::String; + +/// Reads a line of input from standard input asynchronously, returning a +/// `String` containing the input line. Does not include the newline +/// character at the end of the line. +/// +/// If the user presses the abort key (usually Ctrl+C), the returned string +/// will be empty. +pub async fn read_line() -> String { + let mut writer = WRITER.lock(); + + let mut buff = String::new(); + loop { + match get_keystroke_async().await { + KeyStroke::Char(c) => match c { + '\n' => { + writer.write_glyph(c as u8); + return buff; + } + '\r' => { + writer.write_glyph(c as u8); + return buff; + } + '\x08' => { + if !buff.is_empty() { + buff.pop(); + writer.backspace(); + } + } + + c => { + writer.write_glyph(c as u8); + buff.push(c) + } + }, + KeyStroke::Enter => { + writer.write_glyph(b'\n'); + return buff; + } + KeyStroke::Backspace => { + if !buff.is_empty() { + buff.pop(); + writer.backspace(); + } + } + _ => continue, + } + } +} + +/// Reads a character from standard input and blocks the current task until +/// a character is available. +/// +/// # Note +/// +/// This function is not yet implemented. +pub async fn async_keystroke() -> KeyStroke { + get_keystroke_async().await +} + +/// Attempt to read a character from standard input without blocking the +/// current task. +/// +/// If no character is available, returns `None`. +/// +/// # Note +/// +/// This function is not yet implemented. +pub fn keystroke() -> Option { + get_keystroke_optional() +}