From 76103215711d268b4b02f62e900f2be3032cf295 Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 31 Jul 2026 02:18:30 +0200 Subject: Initial commit: raisernics toy kernel - Bare-metal x86_64 UEFI kernel (PE32+, no libc) - GOP framebuffer + 8x16 bitmap font - PS/2 keyboard (polling, scancode set 1) - UART 16550 serial driver (COM1, 115200 8N1) - Bump allocator as #[global_allocator] (alloc::String, Vec, format!) - UEFI memory map capture (static buffer, ADR 0001) - Shell with 7 commands (help, echo, clear, hello, info, mem, halt) - Dual output: GOP framebuffer + serial console - Makefile: run, run-gui, debug, release, image targets - tools/: qemu.sh, boot-image.sh, debug-kernel.py --- src/main.rs | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/main.rs (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..2b1df77 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,181 @@ +#![no_std] +#![no_main] +#![allow(static_mut_refs, unsafe_code, dead_code, unused_doc_comments)] + +// SAFETY: single-core kernel, no preemption — static mut access is sound +// Toy kernel: dead code and unused items during active development + +extern crate alloc; + +mod font; +mod gop; +mod memory; +mod ps2; +mod serial; +mod shell; +mod uefi; + +use core::panic::PanicInfo; +use uefi::*; + +// --------------------------------------------------------------------------- +// Panic handler +// --------------------------------------------------------------------------- +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + use core::fmt::Write; + serial::write_str("\r\n*** PANIC ***\r\n"); + let _ = write!(&mut serial::SerialWriter, "{}\r\n", info.message()); + if let Some(loc) = info.location() { + let _ = write!( + &mut serial::SerialWriter, + " at {}:{}\r\n", + loc.file(), + loc.line() + ); + } + loop { + unsafe { + core::arch::asm!("cli; hlt"); + } + } +} + +// --------------------------------------------------------------------------- +// EFI entry point +// --------------------------------------------------------------------------- +#[no_mangle] +pub extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut SystemTable) -> ! { + let st = unsafe { &mut *system_table }; + let bs = unsafe { &mut *st.boot_services }; + let con_out = unsafe { &mut *st.con_out }; + + // ---- Phase 1: UEFI boot services alive ---- + + // Print banner via UEFI console + unsafe { + let banner: [u16; 28] = [ + 'r' as u16, 'a' as u16, 'i' as u16, 's' as u16, 'e' as u16, 'r' as u16, 'n' as u16, + 'i' as u16, 'c' as u16, 's' as u16, ' ' as u16, 'v' as u16, '0' as u16, '.' as u16, + '1' as u16, ' ' as u16, '-' as u16, ' ' as u16, 'b' as u16, 'o' as u16, 'o' as u16, + 't' as u16, 'i' as u16, 'n' as u16, 'g' as u16, '.' as u16, '.' as u16, 0x0A, + ]; + (con_out.output_string)(con_out, banner.as_ptr()); + } + + // Init serial early so we can debug-print before EBS + serial::init(); + + serial_println!("=== raisernics boot ==="); + + // Stash the memory map + let map_key = memory::capture_memory_map(bs); + let map_key = match map_key { + Ok(k) => k, + Err(e) => { + serial_println!("mem: capture_memory_map failed 0x{:x}", e); + 0 + } + }; + + // Locate GOP (Graphics Output Protocol) + let mut gop_interface: *mut core::ffi::c_void = core::ptr::null_mut(); + let gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + let gop_result = unsafe { + (bs.locate_protocol)( + &gop_guid as *const Guid, + core::ptr::null_mut(), + &mut gop_interface, + ) + }; + if gop_result == EFI_SUCCESS { + let gop = unsafe { &mut *(gop_interface as *mut GraphicsOutputProtocol) }; + serial_println!("gop: framebuffer found"); + gop::init(gop); + } else { + serial_println!("gop: not found (0x{:x})", gop_result); + } + + // ExitBootServices — we now own the machine + let ebs_result = unsafe { (bs.exit_boot_services)(image_handle, map_key) }; + if ebs_result != EFI_SUCCESS { + // Memory map may have changed; try again with fresh map + // (This is the standard UEFI EBS retry dance.) + serial_println!( + "WARN: ExitBootServices failed (0x{:x}), retrying...", + ebs_result + ); + let _map_key = match memory::refresh_and_exit(bs, image_handle) { + Ok(k) => k, + Err(e) => { + serial_println!("FATAL: cannot ExitBootServices (0x{:x})", e); + loop { + unsafe { + core::arch::asm!("cli; hlt"); + } + } + } + }; + } + + // ---- Phase 2: Native mode ---- + + serial::init(); + serial_println!("serial: COM1 115200 8N1"); + + // PS/2 keyboard + ps2::init(); + serial_println!("ps2: keyboard initialized"); + + // Memory: the bump allocator was set up during capture_memory_map. + // `alloc::Vec`, `alloc::String`, `format!` etc. are now usable. + + // Print memory summary + dump_memory_summary(); + + serial_println!("boot: entering shell"); + shell::run(); +} + +// --------------------------------------------------------------------------- +// Print a memory summary from the stashed memory map +// --------------------------------------------------------------------------- +fn dump_memory_summary() { + let (map, key) = memory::get_map(); + let mut conventional_4k = 0u64; + let mut addr_space_4k = 0u64; + for desc in map.iter() { + let pages = desc.number_of_pages; + addr_space_4k += pages; + if desc.kind == 7 { + conventional_4k += pages; + } + } + serial_println!( + "mem: {} entries, key=0x{:x}, usable: {} KB ({} MB)", + map.len(), + key, + conventional_4k * 4, + (conventional_4k * 4) / 1024, + ); + serial_println!( + "mem: address space: {} MB (incl. PCIe MMIO)", + (addr_space_4k * 4) / 1024, + ); +} + +// --------------------------------------------------------------------------- +// Helper: write a panic-proof string via UEFI console (used before serial) +// --------------------------------------------------------------------------- +#[allow(dead_code)] +unsafe fn uefi_puts(con_out: *mut SimpleTextOutput, s: &str) { + // Simple ASCII → UEFI-CHAR16 conversion (stack buffer, panic-safe) + let mut buf: [u16; 128] = [0; 128]; + let len = s.len().min(127); + for (i, &b) in s.as_bytes()[..len].iter().enumerate() { + buf[i] = b as u16; + } + buf[len] = 0x0A; // LF + let out = unsafe { &mut *con_out }; + (out.output_string)(out, buf.as_ptr()); +} -- cgit v1.2.3