diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/Cargo.toml | 24 | ||||
-rw-r--r-- | kernel/default.nix | 10 | ||||
-rw-r--r-- | kernel/src/console.rs | 211 |
3 files changed, 0 insertions, 245 deletions
diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml deleted file mode 100644 index f2bb9ff..0000000 --- a/kernel/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "kernel" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["staticlib"] - -[dependencies] -allocator-api2 = { version = "0.2.18", default-features = false } -bstr = { version = "1.10.0", default-features = false } -cfg-if = { version = "1.0.0", default-features = false } -contracts = { version = "0.6.3", default-features = false } -either = { version = "1.13.0", default-features = false } -log = { version = "0.4.20", default-features = false } -spin = { version = "0.9.8", default-features = false, features = ["mutex", "use_ticket_mutex"] } -static_assertions = { version = "1.1.0", default-features = false } -void = { version = "1.0.2", default-features = false } - -[profile.release] -codegen-units = 1 -debug = true -lto = true -overflow-checks = true diff --git a/kernel/default.nix b/kernel/default.nix deleted file mode 100644 index 0949b58..0000000 --- a/kernel/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ rust }: - -let toml = builtins.fromTOML (builtins.readFile ./Cargo.toml); -in rust.buildRustPackage { - pname = toml.package.name; - version = toml.package.version; - src = ./.; - cargoLock.lockFile = ./Cargo.lock; - dontFixup = true; -} diff --git a/kernel/src/console.rs b/kernel/src/console.rs deleted file mode 100644 index 1c0fe78..0000000 --- a/kernel/src/console.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! The console subsystem, used for the kernel to log things. - -use crate::util::likely; -use contracts::{ensures, invariant}; -use core::fmt::Write; -use log::Log; -use spin::Mutex; - -/// The console singleton. -static CONSOLE: Console = Console(Mutex::new(ConsoleInner::new())); - -/// A function pointer that may be set by the platform-specific initialization -/// code **before** hart0_boot is called. It must never be modified after that -/// point. -/// -/// If present, this function is called after every write to the console with -/// the buffer contents, after which the console buffer is cleared. -#[no_mangle] -static mut CONSOLE_STRICT_FLUSH: Option<unsafe extern "C" fn(*const u8, usize)> = None; - -/// Initializes the console subsystem. This must be done before any logging is -/// performed, and must -/// only be called once. -pub fn init() { - log::set_logger(&CONSOLE).expect("failed to set logger"); - log::set_max_level(log::LevelFilter::Trace); -} - -/// The point of interaction with the console. This is a singleton; see -/// `CONSOLE`. -struct Console(Mutex<ConsoleInner<4096>>); - -impl Log for Console { - fn enabled(&self, _metadata: &log::Metadata) -> bool { - true - } - - fn log(&self, record: &log::Record) { - if !self.enabled(record.metadata()) { - return; - } - - let mut inner = self.0.lock(); - let mut body = |line| { - let level = match record.level() { - log::Level::Error => "\x1b[1;31mERR\x1b[0m", - log::Level::Warn => "\x1b[1;33mWRN\x1b[0m", - log::Level::Info => "\x1b[1;36mINF\x1b[0m", - log::Level::Debug => "\x1b[1;35mDBG\x1b[0m", - log::Level::Trace => "TRC", - }; - let file = record.file().unwrap_or("???"); - let args = record.args(); - - let result = if args.as_str() == Some("") { - // A silly convenience, but don't write the extra space if we're - // not going to write anything anyways. - writeln!(inner, "[{level}][{file}:{line}]") - } else { - writeln!(inner, "[{level}][{file}:{line}] {args}") - }; - - // UNWRAP: Since the fmt::Write impl for ConsoleInner has a - // contract promising that it won't ever return an error, this - // should be unreachable. - result.unwrap(); - }; - - // Some contortions to avoid running afoul of the lifetime requirements - // of format_args... - match record.line() { - Some(line) => body(format_args!("{line}")), - None => body(format_args!("???")), - } - - // Check for a strict flush handler. - // - // SAFETY: We document the function that should be called, as well as - // the requirement that the function pointer cannot be concurrently - // modified. - unsafe { - if let Some(console_strict_flush) = CONSOLE_STRICT_FLUSH { - let buffer = inner.buffer(); - console_strict_flush(buffer.as_ptr(), buffer.len()); - inner.clear_buffer(); - } - } - } - - fn flush(&self) {} -} - -/// The internals of the console. This is put behind a lock. -struct ConsoleInner<const BUFFER_SIZE: usize> { - /// The buffer that gets written to. Wraps on overflow. - buffer: [u8; BUFFER_SIZE], - - /// The number of bytes that have actually been written to. - len: usize, - - /// A flag for whether the log buffer has overflowed since the last time it - /// was cleared. - has_overflowed: bool, -} - -#[invariant(self.len <= BUFFER_SIZE)] -impl<const BUFFER_SIZE: usize> ConsoleInner<BUFFER_SIZE> { - const fn new() -> ConsoleInner<BUFFER_SIZE> { - ConsoleInner { - buffer: [0; BUFFER_SIZE], - len: 0, - has_overflowed: false, - } - } - - /// Returns a reference to the filled portion of the buffer. - fn buffer<'a>(&'a self) -> &'a [u8] { - &self.buffer[..self.len] - } - - /// Marks the buffer as cleared. - fn clear_buffer(&mut self) { - self.len = 0; - if self.has_overflowed { - self.has_overflowed = false; - log::warn!("console buffer overflowed"); - } - } - - /// Writes bytes to the buffer, wrapping and setting the flag on overflow. - fn write(&mut self, bytes: &[u8]) { - // Check if there's enough room for the contents of the buffer. - let remaining = &mut self.buffer[self.len..]; - if likely(bytes.len() <= remaining.len()) { - // If there is, copy the whole buffer in. - remaining[..bytes.len()].copy_from_slice(bytes); - self.len += bytes.len(); - } else if bytes.len() >= BUFFER_SIZE { - // If not, and the bytes to write would fill up the entire buffer, - // just write over the whole thing. (This frees us from handling - // this case below.) - self.buffer - .copy_from_slice(&bytes[bytes.len() - BUFFER_SIZE..]); - self.len = BUFFER_SIZE; - self.has_overflowed = true; - } else { - // If there wasn't enough space, but there still will be enough - // space for some of the current contents, calculate how many bytes - // will be kept. - // - // This won't overflow (or even be zero), because we tested for - // that above. - let bytes_to_keep = BUFFER_SIZE - bytes.len(); - - // Copy those bytes down to the start of the buffer. - self.buffer - .copy_within(self.len - bytes_to_keep..self.len, 0); - - // Copy the new bytes to the end of the buffer. - self.buffer[bytes_to_keep..].copy_from_slice(bytes); - - // Update the length and mark the buffer as having overflowed. - self.len = BUFFER_SIZE; - self.has_overflowed = true; - } - } -} - -impl<const BUFFER_SIZE: usize> Write for ConsoleInner<BUFFER_SIZE> { - #[ensures(ret.is_ok())] - fn write_str(&mut self, s: &str) -> core::fmt::Result { - self.write(s.as_bytes()); - Ok(()) - } -} - -#[test] -fn console_handles_overflow() { - let mut console = ConsoleInner::<8>::new(); - assert_eq!(&console.buffer, b"\0\0\0\0\0\0\0\0"); - assert_eq!(console.len, 0); - assert_eq!(console.has_overflowed, false); - - console.write(b"Hello"); - assert_eq!(&console.buffer, b"Hello\0\0\0"); - assert_eq!(console.len, 5); - assert_eq!(console.has_overflowed, false); - - console.write(b", "); - assert_eq!(&console.buffer, b"Hello, \0"); - assert_eq!(console.len, 7); - assert_eq!(console.has_overflowed, false); - - console.write(b"world!"); - assert_eq!(&console.buffer, b", world!"); - assert_eq!(console.len, 8); - assert_eq!(console.has_overflowed, true); -} - -#[test] -fn console_handles_overflow_larger_than_buffer() { - let mut console = ConsoleInner::<8>::new(); - assert_eq!(&console.buffer, b"\0\0\0\0\0\0\0\0"); - assert_eq!(console.len, 0); - assert_eq!(console.has_overflowed, false); - - console.write("Hello, world!".as_bytes()); - assert_eq!(&console.buffer, b", world!"); - assert_eq!(console.len, 8); - assert_eq!(console.has_overflowed, true); -} |