From 6a5c32fa6ae8ec34e3e5b44d7e5a78ded8fe729b Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 31 Jul 2026 02:58:31 +0200 Subject: restructure: rsnix kernel + shell crate + rsdos entry point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture: rs-dos/ (workspace root, was raisernics/) ├── rsnix/ — kernel lib crate (uefi, serial, gop, ps2, interrupts, memory) ├── shell/ — shell lib crate (prompt, commands, line input) ├── rsdos/ — binary crate (efi_main, init, panic handler) └── rsboot/ — future git submodule No functional changes — boots to shell prompt identically. Version auto-pulled from Cargo.toml via env!("CARGO_PKG_VERSION"). --- src/shell.rs | 275 ----------------------------------------------------------- 1 file changed, 275 deletions(-) delete mode 100644 src/shell.rs (limited to 'src/shell.rs') diff --git a/src/shell.rs b/src/shell.rs deleted file mode 100644 index 8e4cb62..0000000 --- a/src/shell.rs +++ /dev/null @@ -1,275 +0,0 @@ -/// Minimal command shell — reads from PS/2 keyboard, writes to GOP + serial. -use crate::ps2::{self, KeyEvent}; -use crate::{gop_print, gop_println, serial_print, serial_println}; - -/// Maximum input line length. -const LINE_BUF_SIZE: usize = 256; - -/// Run the shell forever. -pub fn run() -> ! { - gop_println!("raisernics v{} — type 'help' for commands", env!("CARGO_PKG_VERSION")); - serial_println!("========== raisernics v{} ==========", env!("CARGO_PKG_VERSION")); - - loop { - // Print prompt - gop_print!("> "); - serial_print!("> "); - - // Read one line from keyboard - let line = read_line(); - - // Parse and execute - let trimmed = line.trim(); - if trimmed.is_empty() { - continue; - } - - let (cmd, args) = split_first_word(trimmed); - - match cmd { - "help" => cmd_help(), - "echo" => cmd_echo(args), - "clear" => cmd_clear(), - "halt" | "poweroff" | "exit" => cmd_halt(), - "hello" => { - gop_println!("World!"); - serial_println!("World!"); - } - "info" => cmd_info(), - "mem" => cmd_mem(), - _ => { - gop_println!("unknown command: {}", cmd); - serial_println!("unknown command: {}", cmd); - } - } - } -} - -// --------------------------------------------------------------------------- -// Line input (PS/2 keyboard, no heap) -// --------------------------------------------------------------------------- -fn read_line() -> heapless_str::String { - let mut buf = heapless_str::String::::new(); - loop { - match ps2::poll_key() { - KeyEvent::Enter => { - crate::gop::put_char('\n'); - crate::serial::write_str("\r\n"); - return buf; - } - KeyEvent::Char(c) if c.is_ascii_graphic() || c == ' ' => { - if buf.push(c).is_ok() { - crate::gop::put_char(c); - // Echo to serial too - let mut s = [0u8; 4]; - let s = c.encode_utf8(&mut s); - crate::serial::write_str(s); - } - } - KeyEvent::Backspace => { - if buf.pop().is_some() { - // GOP backspace: move cursor back and erase - crate::gop::put_char('\x08'); - // Serial backspace: send BS SP BS (standard terminal protocol) - crate::serial::write_str("\x08 \x08"); - } - } - KeyEvent::Tab => { - for _ in 0..4 { - if buf.push(' ').is_ok() { - crate::gop::put_char(' '); - crate::serial::write_str(" "); - } - } - } - _ => {} - } - } -} - -/// Split a command line into the first word and the rest. -fn split_first_word(s: &str) -> (&str, &str) { - let s = s.trim_start(); - if let Some(i) = s.find(char::is_whitespace) { - (&s[..i], s[i..].trim_start()) - } else { - (s, "") - } -} - -// --------------------------------------------------------------------------- -// Built-in commands -// --------------------------------------------------------------------------- - -fn cmd_help() { - gop_println!("available commands:"); - gop_println!(" help — show this message"); - gop_println!(" echo — echo arguments"); - gop_println!(" clear — clear screen"); - gop_println!(" hello — print 'World!'"); - gop_println!(" info — show system info"); - gop_println!(" mem — show memory info"); - gop_println!(" halt — halt the system"); - gop_println!(" exit — alias for halt"); - serial_println!("available commands:"); - serial_println!(" help, echo, clear, hello, info, mem, halt, exit"); -} - -fn cmd_echo(args: &str) { - gop_println!("{}", args); -} - -fn cmd_clear() { - crate::gop::clear_screen(); -} - -fn cmd_info() { - // Test that the global allocator works: build a String on the heap - let mut s = alloc::string::String::from("raisernics v"); - s.push_str(env!("CARGO_PKG_VERSION")); - s.push_str(" — RNG custom kernel"); - gop_println!("{}", s); - gop_println!("arch: x86_64, freestanding, no userspace"); - serial_println!("{}", s); - serial_println!("arch: x86_64, freestanding, no userspace"); - // String is freed (dropped) when this scope ends — tests dealloc too -} - -fn cmd_mem() { - let (map, key) = crate::memory::get_map(); - - let mut total_kb = 0u64; - let mut conventional_kb = 0u64; - let mut loader_kb = 0u64; - let mut boot_code_kb = 0u64; - let mut boot_data_kb = 0u64; - let mut reserved_kb = 0u64; - let mut acpi_kb = 0u64; - let mut mmio_kb = 0u64; - - for desc in map { - let pages = desc.number_of_pages; - let bytes_4k = pages * 4; - total_kb += bytes_4k; - match desc.kind { - 1 => reserved_kb += bytes_4k, - 3 => boot_code_kb += bytes_4k, - 4 => boot_data_kb += bytes_4k, - 5 | 10 => acpi_kb += bytes_4k, - 6 => loader_kb += bytes_4k, - 7 => conventional_kb += bytes_4k, - 11 | 12 => mmio_kb += bytes_4k, - _ => reserved_kb += bytes_4k, - } - } - - // Build a big heap string with format! to test the allocator - let mut out = alloc::string::String::new(); - use alloc::fmt::Write; - let _ = writeln!(out, "memory map (key=0x{:x}, {} entries)", key, map.len()); - let _ = writeln!( - out, - " usable {:4} MB Conventional", - conventional_kb / 1024 - ); - let _ = writeln!( - out, - " boot code {:4} MB BootServicesCode", - boot_code_kb / 1024 - ); - let _ = writeln!( - out, - " boot data {:4} MB BootServicesData", - boot_data_kb / 1024 - ); - let _ = writeln!(out, " loader {:4} MB LoaderData", loader_kb / 1024); - let _ = writeln!(out, " ACPI {:4} MB", acpi_kb / 1024); - let _ = writeln!(out, " MMIO {:4} MB", mmio_kb / 1024); - let _ = writeln!(out, " reserved {:4} MB", reserved_kb / 1024); - let _ = writeln!( - out, - " raw total {:4} MB (incl. PCIe MMIO)", - total_kb / 1024 - ); - - gop_print!("{}", out); - serial_print!("{}", out); -} - -fn cmd_halt() -> ! { - gop_println!("halting..."); - serial_println!("halting..."); - loop { - unsafe { - core::arch::asm!("cli; hlt"); - } - } -} - -// --------------------------------------------------------------------------- -// Minimal fixed-capacity string for no-stack use (avoids alloc dependency) -// --------------------------------------------------------------------------- -mod heapless_str { - use core::fmt; - - /// A fixed-capacity string allocated entirely on the stack. - pub struct String { - buf: [u8; N], - len: usize, - } - - impl String { - pub const fn new() -> Self { - String { - buf: [0u8; N], - len: 0, - } - } - - pub fn as_str(&self) -> &str { - // SAFETY: we only write valid ASCII bytes. - unsafe { core::str::from_utf8_unchecked(&self.buf[..self.len]) } - } - - pub fn push(&mut self, c: char) -> Result<(), ()> { - if self.len >= N { - return Err(()); - } - self.buf[self.len] = c as u8; - self.len += 1; - Ok(()) - } - - pub fn pop(&mut self) -> Option { - if self.len == 0 { - return None; - } - self.len -= 1; - Some(self.buf[self.len] as char) - } - - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - pub fn clear(&mut self) { - self.len = 0; - } - - pub fn trim(&self) -> &str { - self.as_str().trim() - } - } - - impl fmt::Display for String { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) - } - } - - impl fmt::Debug for String { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.as_str()) - } - } -} -- cgit v1.2.3