diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-31 02:18:30 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-31 02:18:30 +0200 |
| commit | 76103215711d268b4b02f62e900f2be3032cf295 (patch) | |
| tree | dcd86b5b02ae99c99ca833668055f4f53211be6b /src/main.rs | |
| download | rs-dos-76103215711d268b4b02f62e900f2be3032cf295.tar.gz rs-dos-76103215711d268b4b02f62e900f2be3032cf295.zip | |
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
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 181 |
1 files changed, 181 insertions, 0 deletions
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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![allow(static_mut_refs, unsafe_code, dead_code, unused_doc_comments)] | ||
| 4 | |||
| 5 | // SAFETY: single-core kernel, no preemption — static mut access is sound | ||
| 6 | // Toy kernel: dead code and unused items during active development | ||
| 7 | |||
| 8 | extern crate alloc; | ||
| 9 | |||
| 10 | mod font; | ||
| 11 | mod gop; | ||
| 12 | mod memory; | ||
| 13 | mod ps2; | ||
| 14 | mod serial; | ||
| 15 | mod shell; | ||
| 16 | mod uefi; | ||
| 17 | |||
| 18 | use core::panic::PanicInfo; | ||
| 19 | use uefi::*; | ||
| 20 | |||
| 21 | // --------------------------------------------------------------------------- | ||
| 22 | // Panic handler | ||
| 23 | // --------------------------------------------------------------------------- | ||
| 24 | #[panic_handler] | ||
| 25 | fn panic(info: &PanicInfo) -> ! { | ||
| 26 | use core::fmt::Write; | ||
| 27 | serial::write_str("\r\n*** PANIC ***\r\n"); | ||
| 28 | let _ = write!(&mut serial::SerialWriter, "{}\r\n", info.message()); | ||
| 29 | if let Some(loc) = info.location() { | ||
| 30 | let _ = write!( | ||
| 31 | &mut serial::SerialWriter, | ||
| 32 | " at {}:{}\r\n", | ||
| 33 | loc.file(), | ||
| 34 | loc.line() | ||
| 35 | ); | ||
| 36 | } | ||
| 37 | loop { | ||
| 38 | unsafe { | ||
| 39 | core::arch::asm!("cli; hlt"); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | // --------------------------------------------------------------------------- | ||
| 45 | // EFI entry point | ||
| 46 | // --------------------------------------------------------------------------- | ||
| 47 | #[no_mangle] | ||
| 48 | pub extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut SystemTable) -> ! { | ||
| 49 | let st = unsafe { &mut *system_table }; | ||
| 50 | let bs = unsafe { &mut *st.boot_services }; | ||
| 51 | let con_out = unsafe { &mut *st.con_out }; | ||
| 52 | |||
| 53 | // ---- Phase 1: UEFI boot services alive ---- | ||
| 54 | |||
| 55 | // Print banner via UEFI console | ||
| 56 | unsafe { | ||
| 57 | let banner: [u16; 28] = [ | ||
| 58 | 'r' as u16, 'a' as u16, 'i' as u16, 's' as u16, 'e' as u16, 'r' as u16, 'n' as u16, | ||
| 59 | 'i' as u16, 'c' as u16, 's' as u16, ' ' as u16, 'v' as u16, '0' as u16, '.' as u16, | ||
| 60 | '1' as u16, ' ' as u16, '-' as u16, ' ' as u16, 'b' as u16, 'o' as u16, 'o' as u16, | ||
| 61 | 't' as u16, 'i' as u16, 'n' as u16, 'g' as u16, '.' as u16, '.' as u16, 0x0A, | ||
| 62 | ]; | ||
| 63 | (con_out.output_string)(con_out, banner.as_ptr()); | ||
| 64 | } | ||
| 65 | |||
| 66 | // Init serial early so we can debug-print before EBS | ||
| 67 | serial::init(); | ||
| 68 | |||
| 69 | serial_println!("=== raisernics boot ==="); | ||
| 70 | |||
| 71 | // Stash the memory map | ||
| 72 | let map_key = memory::capture_memory_map(bs); | ||
| 73 | let map_key = match map_key { | ||
| 74 | Ok(k) => k, | ||
| 75 | Err(e) => { | ||
| 76 | serial_println!("mem: capture_memory_map failed 0x{:x}", e); | ||
| 77 | 0 | ||
| 78 | } | ||
| 79 | }; | ||
| 80 | |||
| 81 | // Locate GOP (Graphics Output Protocol) | ||
| 82 | let mut gop_interface: *mut core::ffi::c_void = core::ptr::null_mut(); | ||
| 83 | let gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; | ||
| 84 | let gop_result = unsafe { | ||
| 85 | (bs.locate_protocol)( | ||
| 86 | &gop_guid as *const Guid, | ||
| 87 | core::ptr::null_mut(), | ||
| 88 | &mut gop_interface, | ||
| 89 | ) | ||
| 90 | }; | ||
| 91 | if gop_result == EFI_SUCCESS { | ||
| 92 | let gop = unsafe { &mut *(gop_interface as *mut GraphicsOutputProtocol) }; | ||
| 93 | serial_println!("gop: framebuffer found"); | ||
| 94 | gop::init(gop); | ||
| 95 | } else { | ||
| 96 | serial_println!("gop: not found (0x{:x})", gop_result); | ||
| 97 | } | ||
| 98 | |||
| 99 | // ExitBootServices — we now own the machine | ||
| 100 | let ebs_result = unsafe { (bs.exit_boot_services)(image_handle, map_key) }; | ||
| 101 | if ebs_result != EFI_SUCCESS { | ||
| 102 | // Memory map may have changed; try again with fresh map | ||
| 103 | // (This is the standard UEFI EBS retry dance.) | ||
| 104 | serial_println!( | ||
| 105 | "WARN: ExitBootServices failed (0x{:x}), retrying...", | ||
| 106 | ebs_result | ||
| 107 | ); | ||
| 108 | let _map_key = match memory::refresh_and_exit(bs, image_handle) { | ||
| 109 | Ok(k) => k, | ||
| 110 | Err(e) => { | ||
| 111 | serial_println!("FATAL: cannot ExitBootServices (0x{:x})", e); | ||
| 112 | loop { | ||
| 113 | unsafe { | ||
| 114 | core::arch::asm!("cli; hlt"); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | } | ||
| 120 | |||
| 121 | // ---- Phase 2: Native mode ---- | ||
| 122 | |||
| 123 | serial::init(); | ||
| 124 | serial_println!("serial: COM1 115200 8N1"); | ||
| 125 | |||
| 126 | // PS/2 keyboard | ||
| 127 | ps2::init(); | ||
| 128 | serial_println!("ps2: keyboard initialized"); | ||
| 129 | |||
| 130 | // Memory: the bump allocator was set up during capture_memory_map. | ||
| 131 | // `alloc::Vec`, `alloc::String`, `format!` etc. are now usable. | ||
| 132 | |||
| 133 | // Print memory summary | ||
| 134 | dump_memory_summary(); | ||
| 135 | |||
| 136 | serial_println!("boot: entering shell"); | ||
| 137 | shell::run(); | ||
| 138 | } | ||
| 139 | |||
| 140 | // --------------------------------------------------------------------------- | ||
| 141 | // Print a memory summary from the stashed memory map | ||
| 142 | // --------------------------------------------------------------------------- | ||
| 143 | fn dump_memory_summary() { | ||
| 144 | let (map, key) = memory::get_map(); | ||
| 145 | let mut conventional_4k = 0u64; | ||
| 146 | let mut addr_space_4k = 0u64; | ||
| 147 | for desc in map.iter() { | ||
| 148 | let pages = desc.number_of_pages; | ||
| 149 | addr_space_4k += pages; | ||
| 150 | if desc.kind == 7 { | ||
| 151 | conventional_4k += pages; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | serial_println!( | ||
| 155 | "mem: {} entries, key=0x{:x}, usable: {} KB ({} MB)", | ||
| 156 | map.len(), | ||
| 157 | key, | ||
| 158 | conventional_4k * 4, | ||
| 159 | (conventional_4k * 4) / 1024, | ||
| 160 | ); | ||
| 161 | serial_println!( | ||
| 162 | "mem: address space: {} MB (incl. PCIe MMIO)", | ||
| 163 | (addr_space_4k * 4) / 1024, | ||
| 164 | ); | ||
| 165 | } | ||
| 166 | |||
| 167 | // --------------------------------------------------------------------------- | ||
| 168 | // Helper: write a panic-proof string via UEFI console (used before serial) | ||
| 169 | // --------------------------------------------------------------------------- | ||
| 170 | #[allow(dead_code)] | ||
| 171 | unsafe fn uefi_puts(con_out: *mut SimpleTextOutput, s: &str) { | ||
| 172 | // Simple ASCII → UEFI-CHAR16 conversion (stack buffer, panic-safe) | ||
| 173 | let mut buf: [u16; 128] = [0; 128]; | ||
| 174 | let len = s.len().min(127); | ||
| 175 | for (i, &b) in s.as_bytes()[..len].iter().enumerate() { | ||
| 176 | buf[i] = b as u16; | ||
| 177 | } | ||
| 178 | buf[len] = 0x0A; // LF | ||
| 179 | let out = unsafe { &mut *con_out }; | ||
| 180 | (out.output_string)(out, buf.as_ptr()); | ||
| 181 | } | ||
