summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-07-31 02:58:31 +0200
committersalmonik <salmonik@tuta.io>2026-07-31 02:58:31 +0200
commit6a5c32fa6ae8ec34e3e5b44d7e5a78ded8fe729b (patch)
treeed59a5bd59a94b3a731b28c68c507f18afc44b0b
parentauto-version all strings from Cargo.toml via env!("CARGO_PKG_VERSION") (diff)
downloadrs-dos-6a5c32fa6ae8ec34e3e5b44d7e5a78ded8fe729b.tar.gz
rs-dos-6a5c32fa6ae8ec34e3e5b44d7e5a78ded8fe729b.zip
restructure: rsnix kernel + shell crate + rsdos entry point
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").
-rw-r--r--Cargo.lock17
-rw-r--r--Cargo.toml13
-rw-r--r--Makefile22
-rw-r--r--rsdos/Cargo.toml8
-rw-r--r--rsdos/src/main.rs138
-rw-r--r--rsnix/Cargo.toml4
-rw-r--r--rsnix/build.rs (renamed from build.rs)0
-rw-r--r--rsnix/src/font.rs (renamed from src/font.rs)0
-rw-r--r--rsnix/src/gop.rs (renamed from src/gop.rs)0
-rw-r--r--rsnix/src/interrupts.rs (renamed from src/interrupts.rs)4
-rw-r--r--rsnix/src/lib.rs11
-rw-r--r--rsnix/src/memory.rs (renamed from src/memory.rs)2
-rw-r--r--rsnix/src/ps2.rs (renamed from src/ps2.rs)5
-rw-r--r--rsnix/src/serial.rs (renamed from src/serial.rs)0
-rw-r--r--rsnix/src/uefi.rs (renamed from src/uefi.rs)0
-rw-r--r--shell/Cargo.toml7
-rw-r--r--shell/src/lib.rs (renamed from src/shell.rs)42
-rw-r--r--src/main.rs186
18 files changed, 229 insertions, 230 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 611d52a..1d697fc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,5 +3,20 @@
3version = 4 3version = 4
4 4
5[[package]] 5[[package]]
6name = "raisernics" 6name = "rsdos"
7version = "0.2.0" 7version = "0.2.0"
8dependencies = [
9 "rsnix",
10 "shell",
11]
12
13[[package]]
14name = "rsnix"
15version = "0.2.0"
16
17[[package]]
18name = "shell"
19version = "0.2.0"
20dependencies = [
21 "rsnix",
22]
diff --git a/Cargo.toml b/Cargo.toml
index d88efe3..14eaeff 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,10 +1,7 @@
1[package] 1[workspace]
2name = "raisernics" 2resolver = "2"
3members = ["rsnix", "shell", "rsdos"]
4
5[workspace.package]
3version = "0.2.0" 6version = "0.2.0"
4edition = "2021" 7edition = "2021"
5
6[profile.release]
7lto = true
8codegen-units = 1
9strip = "symbols"
10opt-level = "z"
diff --git a/Makefile b/Makefile
index 9f03f93..1fc4194 100644
--- a/Makefile
+++ b/Makefile
@@ -4,10 +4,10 @@ export TARGET=x86_64-unknown-uefi
4export PROFILE=debug 4export PROFILE=debug
5export OUT_DIR=target/$(TARGET)/$(PROFILE) 5export OUT_DIR=target/$(TARGET)/$(PROFILE)
6export ESP_DIR=esp 6export ESP_DIR=esp
7export KERNEL=$(OUT_DIR)/raisernics.efi 7export KERNEL=$(OUT_DIR)/rsdos.efi
8export BZIMAGE=$(ESP_DIR)/bzImage 8export BZIMAGE=$(ESP_DIR)/bzImage
9export INITRD=$(ESP_DIR)/initramfs.cpio.gz 9export INITRD=$(ESP_DIR)/initramfs.cpio.gz
10export BOOTLOADER=../rng/rsbl/../build/bootloader/BOOTX64.EFI 10export BOOTLOADER=rsboot/rsboot.efi
11export OVMF=/usr/share/edk2/x64/OVMF.4m.fd 11export OVMF=/usr/share/edk2/x64/OVMF.4m.fd
12export BOOT_EFI=$(ESP_DIR)/EFI/BOOT/BOOTX64.EFI 12export BOOT_EFI=$(ESP_DIR)/EFI/BOOT/BOOTX64.EFI
13 13
@@ -15,7 +15,7 @@ export BOOT_EFI=$(ESP_DIR)/EFI/BOOT/BOOTX64.EFI
15 15
16# ---- Build --------------------------------------------------------------- 16# ---- Build ---------------------------------------------------------------
17 17
18all: $(BZIMAGE) $(INITRD) $(BOOT_EFI) 18all: $(BZIMAGE) $(INITRD)
19 19
20release: PROFILE=release 20release: PROFILE=release
21release: export PROFILE=release 21release: export PROFILE=release
@@ -26,16 +26,12 @@ release: all
26# shellcheck disable=SC2034 26# shellcheck disable=SC2034
27CARGO_FLAGS= 27CARGO_FLAGS=
28 28
29$(OUT_DIR)/raisernics.efi: $(shell find src -name '*.rs') Cargo.toml build.rs 29$(OUT_DIR)/rsdos.efi: $(shell find rsnix shell rsdos -name '*.rs') Cargo.toml rsnix/Cargo.toml shell/Cargo.toml rsdos/Cargo.toml
30 cargo build --target $(TARGET) $(CARGO_FLAGS) 30 cargo build --package rsdos --target $(TARGET) $(CARGO_FLAGS)
31 31
32$(BZIMAGE): $(KERNEL) | $(ESP_DIR) 32$(BZIMAGE): $(KERNEL) | $(ESP_DIR)
33 cp "$<" "$@" 33 cp "$<" "$@"
34 34
35$(BOOT_EFI): $(BOOTLOADER) | $(ESP_DIR)
36 mkdir -p $(ESP_DIR)/EFI/BOOT
37 cp "$<" "$@"
38
39$(ESP_DIR): 35$(ESP_DIR):
40 mkdir -p $(ESP_DIR) 36 mkdir -p $(ESP_DIR)
41 37
@@ -95,7 +91,7 @@ run-image: all
95# ---- Disk image ---------------------------------------------------------- 91# ---- Disk image ----------------------------------------------------------
96 92
97image: all 93image: all
98 ./tools/boot-image.sh raisernics.img 94 ./tools/boot-image.sh rs-dos.img
99 95
100# ---- Utilities ----------------------------------------------------------- 96# ---- Utilities -----------------------------------------------------------
101 97
@@ -104,14 +100,14 @@ fmt:
104 100
105clean: 101clean:
106 cargo clean 102 cargo clean
107 rm -rf $(ESP_DIR) raisernics.img 103 rm -rf $(ESP_DIR) rs-dos.img
108 104
109help: 105help:
110 @echo "raisernics — Make targets" 106 @echo "rs-dos — Make targets"
111 @echo "" 107 @echo ""
112 @echo " make build kernel + ESP (default)" 108 @echo " make build kernel + ESP (default)"
113 @echo " make release build with --release (smaller binary)" 109 @echo " make release build with --release (smaller binary)"
114 @echo " make image create bootable USB image (raisernics.img)" 110 @echo " make image create bootable USB image (rs-dos.img)"
115 @echo " make run QEMU serial console (no graphics)" 111 @echo " make run QEMU serial console (no graphics)"
116 @echo " make run-gui QEMU with graphical window (GOP/PS2)" 112 @echo " make run-gui QEMU with graphical window (GOP/PS2)"
117 @echo " make debug QEMU + GDB stub on :1234" 113 @echo " make debug QEMU + GDB stub on :1234"
diff --git a/rsdos/Cargo.toml b/rsdos/Cargo.toml
new file mode 100644
index 0000000..5bf83fe
--- /dev/null
+++ b/rsdos/Cargo.toml
@@ -0,0 +1,8 @@
1[package]
2name = "rsdos"
3version.workspace = true
4edition.workspace = true
5
6[dependencies]
7rsnix = { path = "../rsnix" }
8shell = { path = "../shell" }
diff --git a/rsdos/src/main.rs b/rsdos/src/main.rs
new file mode 100644
index 0000000..543556b
--- /dev/null
+++ b/rsdos/src/main.rs
@@ -0,0 +1,138 @@
1#![no_std]
2#![no_main]
3#![allow(static_mut_refs, unsafe_code)]
4
5use core::panic::PanicInfo;
6use rsnix::uefi::*;
7
8// ---------------------------------------------------------------------------
9// Panic handler
10// ---------------------------------------------------------------------------
11#[panic_handler]
12fn panic(info: &PanicInfo) -> ! {
13 use core::fmt::Write;
14 rsnix::serial::write_str("\r\n*** PANIC ***\r\n");
15 let _ = write!(&mut rsnix::serial::SerialWriter, "{}\r\n", info.message());
16 if let Some(loc) = info.location() {
17 let _ = write!(
18 &mut rsnix::serial::SerialWriter,
19 " at {}:{}\r\n",
20 loc.file(),
21 loc.line()
22 );
23 }
24 loop {
25 unsafe {
26 core::arch::asm!("cli; hlt");
27 }
28 }
29}
30
31// ---------------------------------------------------------------------------
32// EFI entry point
33// ---------------------------------------------------------------------------
34#[no_mangle]
35pub extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut SystemTable) -> ! {
36 let st = unsafe { &mut *system_table };
37 let bs = unsafe { &mut *st.boot_services };
38 let con_out = unsafe { &mut *st.con_out };
39
40 // ---- Phase 1: UEFI boot services alive ----
41
42 // Print banner via UEFI console
43 unsafe {
44 let s = concat!("rsnix v", env!("CARGO_PKG_VERSION"), " booting..\n");
45 let bytes = s.as_bytes();
46 let mut buf: [u16; 48] = [0; 48];
47 for (i, &b) in bytes.iter().enumerate().take(47) {
48 buf[i] = b as u16;
49 }
50 (con_out.output_string)(con_out, buf.as_ptr());
51 }
52
53 rsnix::serial::init();
54 rsnix::serial_println!("=== rsnix boot ===");
55
56 // Stash the memory map
57 let map_key = match rsnix::memory::capture_memory_map(bs) {
58 Ok(k) => k,
59 Err(e) => {
60 rsnix::serial_println!("mem: capture_memory_map failed 0x{:x}", e);
61 0
62 }
63 };
64
65 // Locate GOP (Graphics Output Protocol)
66 let mut gop_interface: *mut core::ffi::c_void = core::ptr::null_mut();
67 let gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
68 let gop_result = unsafe {
69 (bs.locate_protocol)(
70 &gop_guid as *const Guid,
71 core::ptr::null_mut(),
72 &mut gop_interface,
73 )
74 };
75 if gop_result == EFI_SUCCESS {
76 let gop = unsafe { &mut *(gop_interface as *mut GraphicsOutputProtocol) };
77 rsnix::serial_println!("gop: framebuffer found");
78 rsnix::gop::init(gop);
79 } else {
80 rsnix::serial_println!("gop: not found (0x{:x})", gop_result);
81 }
82
83 // ExitBootServices — we now own the machine
84 if unsafe { (bs.exit_boot_services)(image_handle, map_key) } != EFI_SUCCESS {
85 rsnix::serial_println!("WARN: ExitBootServices failed, retrying...");
86 match rsnix::memory::refresh_and_exit(bs, image_handle) {
87 Ok(_) => {}
88 Err(e) => {
89 rsnix::serial_println!("FATAL: cannot ExitBootServices (0x{:x})", e);
90 loop {
91 unsafe {
92 core::arch::asm!("cli; hlt");
93 }
94 }
95 }
96 }
97 }
98
99 // ---- Phase 2: Native mode ----
100 rsnix::serial::init();
101 rsnix::serial_println!("serial: COM1 115200 8N1");
102 rsnix::ps2::init();
103 rsnix::serial_println!("ps2: keyboard initialized");
104 rsnix::interrupts::init();
105 rsnix::serial_println!("intr: IDT loaded, PIC remapped, timer ~100 Hz");
106
107 dump_memory_summary();
108
109 rsnix::serial_println!("boot: entering shell");
110 shell::run();
111}
112
113// ---------------------------------------------------------------------------
114// Print a memory summary from the stashed memory map
115// ---------------------------------------------------------------------------
116fn dump_memory_summary() {
117 let (map, key) = rsnix::memory::get_map();
118 let mut conventional_4k = 0u64;
119 let mut addr_space_4k = 0u64;
120 for desc in map.iter() {
121 let pages = desc.number_of_pages;
122 addr_space_4k += pages;
123 if desc.kind == 7 {
124 conventional_4k += pages;
125 }
126 }
127 rsnix::serial_println!(
128 "mem: {} entries, key=0x{:x}, usable: {} KB ({} MB)",
129 map.len(),
130 key,
131 conventional_4k * 4,
132 (conventional_4k * 4) / 1024,
133 );
134 rsnix::serial_println!(
135 "mem: address space: {} MB (incl. PCIe MMIO)",
136 (addr_space_4k * 4) / 1024,
137 );
138}
diff --git a/rsnix/Cargo.toml b/rsnix/Cargo.toml
new file mode 100644
index 0000000..d8cd26b
--- /dev/null
+++ b/rsnix/Cargo.toml
@@ -0,0 +1,4 @@
1[package]
2name = "rsnix"
3version.workspace = true
4edition.workspace = true
diff --git a/build.rs b/rsnix/build.rs
index 35447e9..35447e9 100644
--- a/build.rs
+++ b/rsnix/build.rs
diff --git a/src/font.rs b/rsnix/src/font.rs
index e188513..e188513 100644
--- a/src/font.rs
+++ b/rsnix/src/font.rs
diff --git a/src/gop.rs b/rsnix/src/gop.rs
index 9645bb0..9645bb0 100644
--- a/src/gop.rs
+++ b/rsnix/src/gop.rs
diff --git a/src/interrupts.rs b/rsnix/src/interrupts.rs
index 90d5bbb..8f2acd7 100644
--- a/src/interrupts.rs
+++ b/rsnix/src/interrupts.rs
@@ -174,7 +174,7 @@ impl KeyBuf {
174 let h = self.head.load(Ordering::Acquire); 174 let h = self.head.load(Ordering::Acquire);
175 let next = t.wrapping_add(1); 175 let next = t.wrapping_add(1);
176 // Full if the distance between head and tail == capacity (256). 176 // Full if the distance between head and tail == capacity (256).
177 if next.wrapping_sub(h) as u16 > 255 { 177 if next.wrapping_sub(h) > 255 {
178 return; // drop — ring is full 178 return; // drop — ring is full
179 } 179 }
180 unsafe { 180 unsafe {
@@ -362,7 +362,7 @@ pub fn init() {
362 362
363 // Build the IDTR pseudo-descriptor. 363 // Build the IDTR pseudo-descriptor.
364 IDTR = [ 364 IDTR = [
365 (256u16 * 16 - 1) as u16, // limit = 256*16 - 1 365 ((256u16 * 16 - 1)), // limit = 256*16 - 1
366 idt_ptr as u16, // base lo 366 idt_ptr as u16, // base lo
367 (idt_ptr as u64 >> 16) as u16, // base mid 367 (idt_ptr as u64 >> 16) as u16, // base mid
368 (idt_ptr as u64 >> 32) as u16, // base hi 368 (idt_ptr as u64 >> 32) as u16, // base hi
diff --git a/rsnix/src/lib.rs b/rsnix/src/lib.rs
new file mode 100644
index 0000000..6c7fb3c
--- /dev/null
+++ b/rsnix/src/lib.rs
@@ -0,0 +1,11 @@
1#![no_std]
2
3extern crate alloc;
4
5pub mod uefi;
6pub mod serial;
7pub mod gop;
8pub mod font;
9pub mod ps2;
10pub mod interrupts;
11pub mod memory;
diff --git a/src/memory.rs b/rsnix/src/memory.rs
index d223f69..39fd88d 100644
--- a/src/memory.rs
+++ b/rsnix/src/memory.rs
@@ -85,7 +85,7 @@ pub fn capture_memory_map(bs: &BootServices) -> Result<usize, Status> {
85 } 85 }
86 86
87 // Round up to desc_size alignment 87 // Round up to desc_size alignment
88 let buf_size = (buf_size + desc_size - 1) / desc_size * desc_size; 88 let buf_size = buf_size.div_ceil(desc_size) * desc_size;
89 89
90 // If our static buffer is too small, fall back to pool (rare) 90 // If our static buffer is too small, fall back to pool (rare)
91 if buf_size > unsafe { MAP_WORK_BUF.len() } { 91 if buf_size > unsafe { MAP_WORK_BUF.len() } {
diff --git a/src/ps2.rs b/rsnix/src/ps2.rs
index 4ea8b7a..b83a4e0 100644
--- a/src/ps2.rs
+++ b/rsnix/src/ps2.rs
@@ -346,11 +346,10 @@ fn update_modifiers(scancode: u8, pressed: bool, e0: bool) {
346 (0x1D, true) => set_mod(state, MOD_RCTRL, pressed), 346 (0x1D, true) => set_mod(state, MOD_RCTRL, pressed),
347 (0x38, false) => set_mod(state, MOD_LALT, pressed), 347 (0x38, false) => set_mod(state, MOD_LALT, pressed),
348 (0x38, true) => set_mod(state, MOD_RALT, pressed), 348 (0x38, true) => set_mod(state, MOD_RALT, pressed),
349 (0x3A, false) => { 349 (0x3A, false)
350 if pressed { 350 if pressed => {
351 state.modifiers ^= MOD_CAPS; 351 state.modifiers ^= MOD_CAPS;
352 } 352 }
353 }
354 _ => {} 353 _ => {}
355 } 354 }
356} 355}
diff --git a/src/serial.rs b/rsnix/src/serial.rs
index 03fa570..03fa570 100644
--- a/src/serial.rs
+++ b/rsnix/src/serial.rs
diff --git a/src/uefi.rs b/rsnix/src/uefi.rs
index 2e3ed6b..2e3ed6b 100644
--- a/src/uefi.rs
+++ b/rsnix/src/uefi.rs
diff --git a/shell/Cargo.toml b/shell/Cargo.toml
new file mode 100644
index 0000000..9fd10bc
--- /dev/null
+++ b/shell/Cargo.toml
@@ -0,0 +1,7 @@
1[package]
2name = "shell"
3version.workspace = true
4edition.workspace = true
5
6[dependencies]
7rsnix = { path = "../rsnix" }
diff --git a/src/shell.rs b/shell/src/lib.rs
index 8e4cb62..bdd79c6 100644
--- a/src/shell.rs
+++ b/shell/src/lib.rs
@@ -1,14 +1,24 @@
1#![no_std]
2
3extern crate alloc;
4
1/// Minimal command shell — reads from PS/2 keyboard, writes to GOP + serial. 5/// Minimal command shell — reads from PS/2 keyboard, writes to GOP + serial.
2use crate::ps2::{self, KeyEvent}; 6use rsnix::ps2::{self, KeyEvent};
3use crate::{gop_print, gop_println, serial_print, serial_println}; 7use rsnix::{gop_print, gop_println, serial_print, serial_println};
4 8
5/// Maximum input line length. 9/// Maximum input line length.
6const LINE_BUF_SIZE: usize = 256; 10const LINE_BUF_SIZE: usize = 256;
7 11
8/// Run the shell forever. 12/// Run the shell forever.
9pub fn run() -> ! { 13pub fn run() -> ! {
10 gop_println!("raisernics v{} — type 'help' for commands", env!("CARGO_PKG_VERSION")); 14 gop_println!(
11 serial_println!("========== raisernics v{} ==========", env!("CARGO_PKG_VERSION")); 15 "rs-dos v{} — type 'help' for commands",
16 env!("CARGO_PKG_VERSION")
17 );
18 serial_println!(
19 "========== rs-dos v{} ==========",
20 env!("CARGO_PKG_VERSION")
21 );
12 22
13 loop { 23 loop {
14 // Print prompt 24 // Print prompt
@@ -53,32 +63,32 @@ fn read_line() -> heapless_str::String<LINE_BUF_SIZE> {
53 loop { 63 loop {
54 match ps2::poll_key() { 64 match ps2::poll_key() {
55 KeyEvent::Enter => { 65 KeyEvent::Enter => {
56 crate::gop::put_char('\n'); 66 rsnix::gop::put_char('\n');
57 crate::serial::write_str("\r\n"); 67 rsnix::serial::write_str("\r\n");
58 return buf; 68 return buf;
59 } 69 }
60 KeyEvent::Char(c) if c.is_ascii_graphic() || c == ' ' => { 70 KeyEvent::Char(c) if c.is_ascii_graphic() || c == ' ' => {
61 if buf.push(c).is_ok() { 71 if buf.push(c).is_ok() {
62 crate::gop::put_char(c); 72 rsnix::gop::put_char(c);
63 // Echo to serial too 73 // Echo to serial too
64 let mut s = [0u8; 4]; 74 let mut s = [0u8; 4];
65 let s = c.encode_utf8(&mut s); 75 let s = c.encode_utf8(&mut s);
66 crate::serial::write_str(s); 76 rsnix::serial::write_str(s);
67 } 77 }
68 } 78 }
69 KeyEvent::Backspace => { 79 KeyEvent::Backspace => {
70 if buf.pop().is_some() { 80 if buf.pop().is_some() {
71 // GOP backspace: move cursor back and erase 81 // GOP backspace: move cursor back and erase
72 crate::gop::put_char('\x08'); 82 rsnix::gop::put_char('\x08');
73 // Serial backspace: send BS SP BS (standard terminal protocol) 83 // Serial backspace: send BS SP BS (standard terminal protocol)
74 crate::serial::write_str("\x08 \x08"); 84 rsnix::serial::write_str("\x08 \x08");
75 } 85 }
76 } 86 }
77 KeyEvent::Tab => { 87 KeyEvent::Tab => {
78 for _ in 0..4 { 88 for _ in 0..4 {
79 if buf.push(' ').is_ok() { 89 if buf.push(' ').is_ok() {
80 crate::gop::put_char(' '); 90 rsnix::gop::put_char(' ');
81 crate::serial::write_str(" "); 91 rsnix::serial::write_str(" ");
82 } 92 }
83 } 93 }
84 } 94 }
@@ -120,14 +130,14 @@ fn cmd_echo(args: &str) {
120} 130}
121 131
122fn cmd_clear() { 132fn cmd_clear() {
123 crate::gop::clear_screen(); 133 rsnix::gop::clear_screen();
124} 134}
125 135
126fn cmd_info() { 136fn cmd_info() {
127 // Test that the global allocator works: build a String on the heap 137 // Test that the global allocator works: build a String on the heap
128 let mut s = alloc::string::String::from("raisernics v"); 138 let mut s = alloc::string::String::from("rs-dos v");
129 s.push_str(env!("CARGO_PKG_VERSION")); 139 s.push_str(env!("CARGO_PKG_VERSION"));
130 s.push_str(" — RNG custom kernel"); 140 s.push_str(" — rsnix kernel");
131 gop_println!("{}", s); 141 gop_println!("{}", s);
132 gop_println!("arch: x86_64, freestanding, no userspace"); 142 gop_println!("arch: x86_64, freestanding, no userspace");
133 serial_println!("{}", s); 143 serial_println!("{}", s);
@@ -136,7 +146,7 @@ fn cmd_info() {
136} 146}
137 147
138fn cmd_mem() { 148fn cmd_mem() {
139 let (map, key) = crate::memory::get_map(); 149 let (map, key) = rsnix::memory::get_map();
140 150
141 let mut total_kb = 0u64; 151 let mut total_kb = 0u64;
142 let mut conventional_kb = 0u64; 152 let mut conventional_kb = 0u64;
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 9a901f5..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,186 +0,0 @@
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
8extern crate alloc;
9
10mod font;
11mod gop;
12mod interrupts;
13mod memory;
14mod ps2;
15mod serial;
16mod shell;
17mod uefi;
18
19use core::panic::PanicInfo;
20use uefi::*;
21
22// ---------------------------------------------------------------------------
23// Panic handler
24// ---------------------------------------------------------------------------
25#[panic_handler]
26fn panic(info: &PanicInfo) -> ! {
27 use core::fmt::Write;
28 serial::write_str("\r\n*** PANIC ***\r\n");
29 let _ = write!(&mut serial::SerialWriter, "{}\r\n", info.message());
30 if let Some(loc) = info.location() {
31 let _ = write!(
32 &mut serial::SerialWriter,
33 " at {}:{}\r\n",
34 loc.file(),
35 loc.line()
36 );
37 }
38 loop {
39 unsafe {
40 core::arch::asm!("cli; hlt");
41 }
42 }
43}
44
45// ---------------------------------------------------------------------------
46// EFI entry point
47// ---------------------------------------------------------------------------
48#[no_mangle]
49pub extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut SystemTable) -> ! {
50 let st = unsafe { &mut *system_table };
51 let bs = unsafe { &mut *st.boot_services };
52 let con_out = unsafe { &mut *st.con_out };
53
54 // ---- Phase 1: UEFI boot services alive ----
55
56 // Print banner via UEFI console
57 unsafe {
58 let banner: [u16; 28] = [
59 'r' as u16, 'a' as u16, 'i' as u16, 's' as u16, 'e' as u16, 'r' as u16, 'n' as u16,
60 'i' as u16, 'c' as u16, 's' as u16, ' ' as u16, 'v' as u16, '0' as u16, '.' as u16,
61 '1' as u16, ' ' as u16, '-' as u16, ' ' as u16, 'b' as u16, 'o' as u16, 'o' as u16,
62 't' as u16, 'i' as u16, 'n' as u16, 'g' as u16, '.' as u16, '.' as u16, 0x0A,
63 ];
64 (con_out.output_string)(con_out, banner.as_ptr());
65 }
66
67 // Init serial early so we can debug-print before EBS
68 serial::init();
69
70 serial_println!("=== raisernics boot ===");
71
72 // Stash the memory map
73 let map_key = memory::capture_memory_map(bs);
74 let map_key = match map_key {
75 Ok(k) => k,
76 Err(e) => {
77 serial_println!("mem: capture_memory_map failed 0x{:x}", e);
78 0
79 }
80 };
81
82 // Locate GOP (Graphics Output Protocol)
83 let mut gop_interface: *mut core::ffi::c_void = core::ptr::null_mut();
84 let gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
85 let gop_result = unsafe {
86 (bs.locate_protocol)(
87 &gop_guid as *const Guid,
88 core::ptr::null_mut(),
89 &mut gop_interface,
90 )
91 };
92 if gop_result == EFI_SUCCESS {
93 let gop = unsafe { &mut *(gop_interface as *mut GraphicsOutputProtocol) };
94 serial_println!("gop: framebuffer found");
95 gop::init(gop);
96 } else {
97 serial_println!("gop: not found (0x{:x})", gop_result);
98 }
99
100 // ExitBootServices — we now own the machine
101 let ebs_result = unsafe { (bs.exit_boot_services)(image_handle, map_key) };
102 if ebs_result != EFI_SUCCESS {
103 // Memory map may have changed; try again with fresh map
104 // (This is the standard UEFI EBS retry dance.)
105 serial_println!(
106 "WARN: ExitBootServices failed (0x{:x}), retrying...",
107 ebs_result
108 );
109 let _map_key = match memory::refresh_and_exit(bs, image_handle) {
110 Ok(k) => k,
111 Err(e) => {
112 serial_println!("FATAL: cannot ExitBootServices (0x{:x})", e);
113 loop {
114 unsafe {
115 core::arch::asm!("cli; hlt");
116 }
117 }
118 }
119 };
120 }
121
122 // ---- Phase 2: Native mode ----
123
124 serial::init();
125 serial_println!("serial: COM1 115200 8N1");
126
127 // PS/2 keyboard
128 ps2::init();
129 serial_println!("ps2: keyboard initialized");
130
131 // Interrupts — IDT, PIC, PIT timer, keyboard IRQ handler
132 interrupts::init();
133 serial_println!("intr: IDT loaded, PIC remapped, timer ~100 Hz");
134
135 // Memory: the bump allocator was set up during capture_memory_map.
136 // `alloc::Vec`, `alloc::String`, `format!` etc. are now usable.
137
138 // Print memory summary
139 dump_memory_summary();
140
141 serial_println!("boot: entering shell");
142 shell::run();
143}
144
145// ---------------------------------------------------------------------------
146// Print a memory summary from the stashed memory map
147// ---------------------------------------------------------------------------
148fn dump_memory_summary() {
149 let (map, key) = memory::get_map();
150 let mut conventional_4k = 0u64;
151 let mut addr_space_4k = 0u64;
152 for desc in map.iter() {
153 let pages = desc.number_of_pages;
154 addr_space_4k += pages;
155 if desc.kind == 7 {
156 conventional_4k += pages;
157 }
158 }
159 serial_println!(
160 "mem: {} entries, key=0x{:x}, usable: {} KB ({} MB)",
161 map.len(),
162 key,
163 conventional_4k * 4,
164 (conventional_4k * 4) / 1024,
165 );
166 serial_println!(
167 "mem: address space: {} MB (incl. PCIe MMIO)",
168 (addr_space_4k * 4) / 1024,
169 );
170}
171
172// ---------------------------------------------------------------------------
173// Helper: write a panic-proof string via UEFI console (used before serial)
174// ---------------------------------------------------------------------------
175#[allow(dead_code)]
176unsafe fn uefi_puts(con_out: *mut SimpleTextOutput, s: &str) {
177 // Simple ASCII → UEFI-CHAR16 conversion (stack buffer, panic-safe)
178 let mut buf: [u16; 128] = [0; 128];
179 let len = s.len().min(127);
180 for (i, &b) in s.as_bytes()[..len].iter().enumerate() {
181 buf[i] = b as u16;
182 }
183 buf[len] = 0x0A; // LF
184 let out = unsafe { &mut *con_out };
185 (out.output_string)(out, buf.as_ptr());
186}