#![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 interrupts; 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"); // Interrupts — IDT, PIC, PIT timer, keyboard IRQ handler interrupts::init(); serial_println!("intr: IDT loaded, PIC remapped, timer ~100 Hz"); // 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()); }