summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/font.rs5
-rw-r--r--src/gop.rs232
-rw-r--r--src/main.rs181
-rw-r--r--src/memory.rs196
-rw-r--r--src/ps2.rs329
-rw-r--r--src/serial.rs118
-rw-r--r--src/shell.rs274
-rw-r--r--src/uefi.rs289
8 files changed, 1624 insertions, 0 deletions
diff --git a/src/font.rs b/src/font.rs
new file mode 100644
index 0000000..e188513
--- /dev/null
+++ b/src/font.rs
@@ -0,0 +1,5 @@
1/// 8×16 bitmap font.
2///
3/// Glyphs are generated at build time by `build.rs` from the system PSF2 font
4/// (`/usr/share/kbd/consolefonts/lat1-16.psfu.gz`).
5include!(concat!(env!("OUT_DIR"), "/font_data.rs"));
diff --git a/src/gop.rs b/src/gop.rs
new file mode 100644
index 0000000..9645bb0
--- /dev/null
+++ b/src/gop.rs
@@ -0,0 +1,232 @@
1/// GOP framebuffer driver — renders text via a bitmap font.
2use crate::font;
3use crate::uefi::{GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB};
4use core::fmt;
5use core::ptr;
6
7/// Framebuffer state captured before ExitBootServices.
8pub struct Framebuffer {
9 base: *mut u8,
10 #[allow(dead_code)]
11 width: usize,
12 height: usize,
13 pitch: usize, // bytes per scan line
14 bytes_per_pixel: usize,
15 bg: u32, // packed BGRA or RGBA background colour
16 fg: u32, // packed foreground colour
17 cursor_x: usize,
18 cursor_y: usize,
19 rows: usize,
20 cols: usize,
21}
22
23static mut FB: Option<Framebuffer> = None;
24
25/// Initialise the framebuffer from GOP. Call *before* ExitBootServices.
26pub fn init(gop: &mut GraphicsOutputProtocol) {
27 let mode = unsafe { &*gop.mode };
28 let info = unsafe { &*mode.info };
29 let base = mode.framebuffer_base as *mut u8;
30 let width = info.horizontal_resolution as usize;
31 let height = info.vertical_resolution as usize;
32 let pitch = info.pixels_per_scan_line as usize * 4; // 32-bit pixel
33 let bpp = 4;
34
35 // Pick colours based on pixel format
36 let (fg, bg) = match info.pixel_format {
37 PIXEL_BGR => (0x00_FF_FF_FFu32, 0x00_00_00_00u32), // white fg, black bg
38 PIXEL_RGB => (0x00_FF_FF_FFu32, 0x00_00_00_00u32),
39 _ => (0x00_FF_FF_FFu32, 0x00_00_00_00u32),
40 };
41
42 let rows = height / 16;
43 let cols = width / 8;
44
45 let fb = Framebuffer {
46 base,
47 width,
48 height,
49 pitch,
50 bytes_per_pixel: bpp,
51 bg,
52 fg,
53 cursor_x: 0,
54 cursor_y: 0,
55 rows,
56 cols,
57 };
58
59 fb.clear();
60
61 unsafe {
62 FB = Some(fb);
63 }
64}
65
66/// Draw a character at the current cursor position and advance.
67pub fn put_char(c: char) {
68 let fb = unsafe {
69 match FB.as_mut() {
70 Some(f) => f,
71 None => return,
72 }
73 };
74
75 if c == '\x08' {
76 // Backspace - move cursor back and erase
77 if fb.cursor_x > 0 {
78 fb.cursor_x -= 1;
79 } else if fb.cursor_y > 0 {
80 fb.cursor_y -= 1;
81 fb.cursor_x = fb.cols - 1;
82 } else {
83 return;
84 }
85 // Draw glyph 32 (space) to erase — glyph 0 is NUL, not blank
86 fb.draw_glyph(32, fb.cursor_x, fb.cursor_y);
87 return;
88 }
89 if c == '\n' {
90 fb.cursor_x = 0;
91 fb.cursor_y += 1;
92 if fb.cursor_y >= fb.rows {
93 fb.scroll();
94 }
95 return;
96 }
97 if c == '\r' {
98 fb.cursor_x = 0;
99 return;
100 }
101
102 let idx = c as usize;
103 if idx >= 256 {
104 // Unmapped character: draw a space
105 fb.draw_glyph(0, fb.cursor_x, fb.cursor_y);
106 } else {
107 fb.draw_glyph(idx, fb.cursor_x, fb.cursor_y);
108 }
109
110 fb.cursor_x += 1;
111 if fb.cursor_x >= fb.cols {
112 fb.cursor_x = 0;
113 fb.cursor_y += 1;
114 if fb.cursor_y >= fb.rows {
115 fb.scroll();
116 }
117 }
118}
119
120/// Write a string to the framebuffer.
121pub fn write_str(s: &str) {
122 for c in s.chars() {
123 put_char(c);
124 }
125}
126
127/// Clear the entire screen to background colour and reset cursor to (0, 0).
128pub fn clear_screen() {
129 let fb = unsafe {
130 match FB.as_mut() {
131 Some(f) => f,
132 None => return,
133 }
134 };
135 fb.clear();
136 // Reset cursor back to top-left so the next prompt appears at (0, 0)
137 fb.cursor_x = 0;
138 fb.cursor_y = 0;
139}
140
141// ---------------------------------------------------------------------------
142// Framebuffer implementation
143// ---------------------------------------------------------------------------
144impl Framebuffer {
145 fn clear(&self) {
146 let size = self.pitch * self.height;
147 // Fill with background colour (32-bit writes)
148 let mut p = self.base;
149 let end = unsafe { p.add(size) };
150 unsafe {
151 while p < end {
152 ptr::write_volatile(p as *mut u32, self.bg);
153 p = p.add(4);
154 }
155 }
156 }
157
158 fn scroll(&mut self) {
159 // Move everything up one row (16 pixels)
160 let row_bytes = self.pitch * 16;
161 let total = self.pitch * self.height;
162 unsafe {
163 ptr::copy(self.base.add(row_bytes), self.base, total - row_bytes);
164 // Clear last row
165 let last_row = self.base.add(total - row_bytes);
166 for i in 0..(self.pitch * 16 / 4) {
167 ptr::write_volatile(last_row.add(i * 4) as *mut u32, self.bg);
168 }
169 }
170 self.cursor_y -= 1;
171 }
172
173 fn draw_glyph(&self, glyph_idx: usize, col: usize, row: usize) {
174 let glyph = font::GLYPH_8X16;
175 let data = match glyph.get(glyph_idx) {
176 Some(d) => *d,
177 None => return,
178 };
179
180 let x = col * 8;
181 let y = row * 16;
182
183 for cy in 0..16 {
184 let row_bits = data[cy];
185 for cx in 0..8 {
186 if row_bits & (0x80 >> cx) != 0 {
187 // Set foreground pixel
188 let offset = (y + cy) * self.pitch + (x + cx) * self.bytes_per_pixel;
189 unsafe {
190 ptr::write_volatile(self.base.add(offset) as *mut u32, self.fg);
191 }
192 } else {
193 // Set background pixel
194 let offset = (y + cy) * self.pitch + (x + cx) * self.bytes_per_pixel;
195 unsafe {
196 ptr::write_volatile(self.base.add(offset) as *mut u32, self.bg);
197 }
198 }
199 }
200 }
201 }
202}
203
204// ---------------------------------------------------------------------------
205// fmt::Write wrapper
206// ---------------------------------------------------------------------------
207pub struct GopWriter;
208
209impl fmt::Write for GopWriter {
210 fn write_str(&mut self, s: &str) -> fmt::Result {
211 write_str(s);
212 Ok(())
213 }
214}
215
216/// Write a formatted string to the framebuffer.
217#[macro_export]
218macro_rules! gop_print {
219 ($($arg:tt)*) => {{
220 use core::fmt::Write;
221 let _ = write!(&mut $crate::gop::GopWriter, $($arg)*);
222 }};
223}
224
225#[macro_export]
226macro_rules! gop_println {
227 () => { $crate::gop::write_str("\n"); };
228 ($($arg:tt)*) => {{
229 $crate::gop_print!($($arg)*);
230 $crate::gop::write_str("\n");
231 }};
232}
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
8extern crate alloc;
9
10mod font;
11mod gop;
12mod memory;
13mod ps2;
14mod serial;
15mod shell;
16mod uefi;
17
18use core::panic::PanicInfo;
19use uefi::*;
20
21// ---------------------------------------------------------------------------
22// Panic handler
23// ---------------------------------------------------------------------------
24#[panic_handler]
25fn 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]
48pub 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// ---------------------------------------------------------------------------
143fn 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)]
171unsafe 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}
diff --git a/src/memory.rs b/src/memory.rs
new file mode 100644
index 0000000..d223f69
--- /dev/null
+++ b/src/memory.rs
@@ -0,0 +1,196 @@
1use crate::uefi::*;
2use core::alloc::{GlobalAlloc, Layout};
3use core::mem;
4use core::ptr;
5
6const MAX_MAP_ENTRIES: usize = 512;
7
8static mut MEMORY_MAP: [MemoryDescriptor; MAX_MAP_ENTRIES] = unsafe { mem::zeroed() };
9static mut MAP_ENTRIES: usize = 0;
10
11/// The map key returned by GetMemoryMap, needed for ExitBootServices.
12static mut MAP_KEY: usize = 0;
13
14/// Static working buffer for GetMemoryMap (64 KB, way more than enough).
15/// Lives in .bss; no pool allocation, so no map-key staleness.
16static mut MAP_WORK_BUF: [u8; 65536] = [0u8; 65536];
17
18// ---------------------------------------------------------------------------
19// Bump allocator — no deallocation, just pointer advancement.
20// Used as Rust's #[global_allocator] once the heap region is set up.
21// ---------------------------------------------------------------------------
22struct BumpAllocator;
23
24unsafe impl Sync for BumpAllocator {}
25
26unsafe impl GlobalAlloc for BumpAllocator {
27 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
28 let align = layout.align().max(16);
29 let size = layout.size();
30 unsafe {
31 let a = (HEAP_CURSOR + align - 1) & !(align - 1);
32 let e = a + size;
33 if e > HEAP_END || e < a {
34 return ptr::null_mut();
35 }
36 HEAP_CURSOR = e;
37 a as *mut u8
38 }
39 }
40
41 unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
42 // Bump allocator — cannot free individual allocations.
43 // Memory is reclaimed on reboot. Good enough for now.
44 unsafe {
45 core::ptr::read_volatile(&_ptr);
46 }
47 let _ = _layout;
48 }
49}
50
51#[global_allocator]
52static ALLOCATOR: BumpAllocator = BumpAllocator;
53
54/// Heap bump allocator bounds (`static mut` accessed only inside
55/// `GlobalAlloc` impl, which is already `unsafe`).
56static mut HEAP_START: usize = 0;
57static mut HEAP_END: usize = 0;
58static mut HEAP_CURSOR: usize = 0;
59
60/// Capture the UEFI memory map into the static buffer. Uses a pre-allocated
61/// .bss working buffer so no pool allocation (and thus no memory-map change)
62/// occurs between GetMemoryMap and ExitBootServices.
63pub fn capture_memory_map(bs: &BootServices) -> Result<usize, Status> {
64 let mut map_key: usize = 0;
65 let mut desc_size: usize = 0;
66 let mut desc_ver: u32 = 0;
67
68 // Probe with null to get required size + descriptor size
69 let mut buf_size: usize = 0;
70 let status = unsafe {
71 (bs.get_memory_map)(
72 &mut buf_size,
73 ptr::null_mut(),
74 &mut map_key,
75 &mut desc_size,
76 &mut desc_ver,
77 )
78 };
79 if status != EFI_BUFFER_TOO_SMALL {
80 // Fallback: allocate a sensible size
81 buf_size = 64 * 1024;
82 }
83 if desc_size == 0 {
84 desc_size = mem::size_of::<MemoryDescriptor>();
85 }
86
87 // Round up to desc_size alignment
88 let buf_size = (buf_size + desc_size - 1) / desc_size * desc_size;
89
90 // If our static buffer is too small, fall back to pool (rare)
91 if buf_size > unsafe { MAP_WORK_BUF.len() } {
92 return Err(EFI_BUFFER_TOO_SMALL);
93 }
94
95 let mut actual_size = buf_size;
96 let status = unsafe {
97 (bs.get_memory_map)(
98 &mut actual_size,
99 &raw mut MAP_WORK_BUF[0] as *mut MemoryDescriptor,
100 &mut map_key,
101 &mut desc_size,
102 &mut desc_ver,
103 )
104 };
105
106 if status == EFI_BUFFER_TOO_SMALL {
107 // The map grew between the probe and the real call. Try a larger
108 // static buffer (can't grow here without pool, so return error to
109 // let the caller retry if they can allocate pool).
110 // For our 64 KB buffer this should never happen on OVMF.
111 return Err(EFI_BUFFER_TOO_SMALL);
112 }
113 if status != EFI_SUCCESS {
114 return Err(status);
115 }
116
117 // Copy descriptors into our indexed array
118 let count = actual_size / desc_size;
119 let count = count.min(MAX_MAP_ENTRIES);
120 unsafe {
121 for i in 0..count {
122 let p = (&raw const MAP_WORK_BUF[..]) as *const u8;
123 let off = i * desc_size;
124 let src = p.add(off) as *const MemoryDescriptor;
125 MEMORY_MAP[i] = ptr::read(src);
126 }
127 MAP_ENTRIES = count;
128 MAP_KEY = map_key;
129 }
130
131 // Do NOT free anything — the static buffer stays. Map key is still
132 // valid because no pool allocation/free happened after GetMemoryMap.
133
134 init_bump_allocator();
135 Ok(map_key)
136}
137
138/// Retry ExitBootServices with a fresh memory map.
139pub fn refresh_and_exit(bs: &BootServices, image_handle: Handle) -> Result<usize, Status> {
140 let key = capture_memory_map(bs)?;
141 let status = unsafe { (bs.exit_boot_services)(image_handle, key) };
142 if status == EFI_SUCCESS {
143 Ok(key)
144 } else {
145 Err(status)
146 }
147}
148
149fn init_bump_allocator() {
150 unsafe {
151 let mut best_start = 0usize;
152 let mut best_size = 0usize;
153 for i in 0..MAP_ENTRIES {
154 let d = &MEMORY_MAP[i];
155 if d.kind == MEMORY_TYPE_CONVENTIONAL {
156 let sz = d.number_of_pages as usize * 4096;
157 if sz > best_size {
158 best_start = d.physical_start as usize;
159 best_size = sz;
160 }
161 }
162 }
163 if best_size > 2 * 1024 * 1024 {
164 let s = best_start + 2 * 1024 * 1024;
165 let e = best_start + best_size;
166 if s < e {
167 HEAP_START = s;
168 HEAP_END = e;
169 HEAP_CURSOR = s;
170 }
171 }
172 }
173}
174
175#[allow(dead_code)]
176pub fn allocate(size: usize) -> *mut u8 {
177 unsafe {
178 let a = (HEAP_CURSOR + 15) & !15;
179 let e = a + size;
180 if e > HEAP_END || e < a {
181 return ptr::null_mut();
182 }
183 HEAP_CURSOR = e;
184 a as *mut u8
185 }
186}
187
188#[allow(dead_code)]
189pub fn get_map() -> (&'static [MemoryDescriptor], usize) {
190 unsafe {
191 (
192 core::slice::from_raw_parts(&MEMORY_MAP[0], MAP_ENTRIES),
193 MAP_KEY,
194 )
195 }
196}
diff --git a/src/ps2.rs b/src/ps2.rs
new file mode 100644
index 0000000..e972658
--- /dev/null
+++ b/src/ps2.rs
@@ -0,0 +1,329 @@
1/// PS/2 keyboard driver — polling, scancode set 1, US QWERTY layout.
2// Port addresses
3const DATA_PORT: u16 = 0x60;
4const STATUS_PORT: u16 = 0x64;
5
6// Status register bits
7const STAT_OUTPUT_FULL: u8 = 0x01;
8
9// Modifier bit constants
10const MOD_LSHIFT: u8 = 0x01;
11const MOD_RSHIFT: u8 = 0x02;
12const MOD_LCTRL: u8 = 0x04;
13const MOD_RCTRL: u8 = 0x08;
14const MOD_LALT: u8 = 0x10;
15const MOD_RALT: u8 = 0x20;
16const MOD_CAPS: u8 = 0x40;
17
18/// A decoded key event.
19pub enum KeyEvent {
20 Char(char),
21 Enter,
22 Backspace,
23 Tab,
24 Escape,
25 ArrowUp,
26 ArrowDown,
27 ArrowLeft,
28 ArrowRight,
29 Home,
30 End,
31 PageUp,
32 PageDown,
33 Delete,
34 F(u8),
35 Unknown(u8),
36}
37
38// ----- scancode-set-1 translation tables (US QWERTY) -----
39
40/// Normal (unshifted) scancode → ASCII mapping. 0 = unmapped.
41static SCAN_NORMAL: [u8; 128] = {
42 let mut t = [0u8; 128];
43 t[0x02] = b'1';
44 t[0x03] = b'2';
45 t[0x04] = b'3';
46 t[0x05] = b'4';
47 t[0x06] = b'5';
48 t[0x07] = b'6';
49 t[0x08] = b'7';
50 t[0x09] = b'8';
51 t[0x0A] = b'9';
52 t[0x0B] = b'0';
53 t[0x0C] = b'-';
54 t[0x0D] = b'=';
55 t[0x10] = b'q';
56 t[0x11] = b'w';
57 t[0x12] = b'e';
58 t[0x13] = b'r';
59 t[0x14] = b't';
60 t[0x15] = b'y';
61 t[0x16] = b'u';
62 t[0x17] = b'i';
63 t[0x18] = b'o';
64 t[0x19] = b'p';
65 t[0x1A] = b'[';
66 t[0x1B] = b']';
67 t[0x1E] = b'a';
68 t[0x1F] = b's';
69 t[0x20] = b'd';
70 t[0x21] = b'f';
71 t[0x22] = b'g';
72 t[0x23] = b'h';
73 t[0x24] = b'j';
74 t[0x25] = b'k';
75 t[0x26] = b'l';
76 t[0x27] = b';';
77 t[0x28] = b'\'';
78 t[0x29] = b'`';
79 t[0x2B] = b'\\';
80 t[0x2C] = b'z';
81 t[0x2D] = b'x';
82 t[0x2E] = b'c';
83 t[0x2F] = b'v';
84 t[0x30] = b'b';
85 t[0x31] = b'n';
86 t[0x32] = b'm';
87 t[0x33] = b',';
88 t[0x34] = b'.';
89 t[0x35] = b'/';
90 t[0x37] = b'*';
91 t[0x39] = b' ';
92 t
93};
94
95/// Shifted scancode → ASCII.
96static SCAN_SHIFT: [u8; 128] = {
97 let mut t = [0u8; 128];
98 t[0x02] = b'!';
99 t[0x03] = b'@';
100 t[0x04] = b'#';
101 t[0x05] = b'$';
102 t[0x06] = b'%';
103 t[0x07] = b'^';
104 t[0x08] = b'&';
105 t[0x09] = b'*';
106 t[0x0A] = b'(';
107 t[0x0B] = b')';
108 t[0x0C] = b'_';
109 t[0x0D] = b'+';
110 t[0x10] = b'Q';
111 t[0x11] = b'W';
112 t[0x12] = b'E';
113 t[0x13] = b'R';
114 t[0x14] = b'T';
115 t[0x15] = b'Y';
116 t[0x16] = b'U';
117 t[0x17] = b'I';
118 t[0x18] = b'O';
119 t[0x19] = b'P';
120 t[0x1A] = b'{';
121 t[0x1B] = b'}';
122 t[0x1E] = b'A';
123 t[0x1F] = b'S';
124 t[0x20] = b'D';
125 t[0x21] = b'F';
126 t[0x22] = b'G';
127 t[0x23] = b'H';
128 t[0x24] = b'J';
129 t[0x25] = b'K';
130 t[0x26] = b'L';
131 t[0x27] = b':';
132 t[0x28] = b'"';
133 t[0x29] = b'~';
134 t[0x2B] = b'|';
135 t[0x2C] = b'Z';
136 t[0x2D] = b'X';
137 t[0x2E] = b'C';
138 t[0x2F] = b'V';
139 t[0x30] = b'B';
140 t[0x31] = b'N';
141 t[0x32] = b'M';
142 t[0x33] = b'<';
143 t[0x34] = b'>';
144 t[0x35] = b'?';
145 t[0x37] = b'*';
146 t[0x39] = b' ';
147 t
148};
149
150/// Internal keyboard state
151struct KbdState {
152 modifiers: u8,
153 got_e0: bool,
154}
155
156static mut KBD: KbdState = KbdState {
157 modifiers: 0,
158 got_e0: false,
159};
160
161/// Initialise the PS/2 port for the keyboard — just drain stale bytes.
162pub fn init() {
163 drain_input();
164}
165
166fn drain_input() {
167 unsafe {
168 loop {
169 let status: u8;
170 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT,
171 options(nostack, preserves_flags));
172 if status & STAT_OUTPUT_FULL == 0 {
173 break;
174 }
175 let _data: u8;
176 core::arch::asm!("in al, dx", out("al") _data, in("dx") DATA_PORT,
177 options(nostack, preserves_flags));
178 }
179 }
180}
181
182/// Poll for a key event. Blocks until a key is pressed.
183pub fn poll_key() -> KeyEvent {
184 loop {
185 let byte = read_data();
186 if let Some(ev) = process_byte(byte) {
187 return ev;
188 }
189 }
190}
191
192/// Non-blocking poll.
193pub fn try_poll_key() -> Option<KeyEvent> {
194 unsafe {
195 let status: u8;
196 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT,
197 options(nostack, preserves_flags));
198 if status & STAT_OUTPUT_FULL == 0 {
199 return None;
200 }
201 let data: u8;
202 core::arch::asm!("in al, dx", out("al") data, in("dx") DATA_PORT,
203 options(nostack, preserves_flags));
204 process_byte(data)
205 }
206}
207
208/// Read a byte from the keyboard data port (blocking).
209fn read_data() -> u8 {
210 unsafe {
211 loop {
212 let status: u8;
213 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT,
214 options(nostack, preserves_flags));
215 if status & STAT_OUTPUT_FULL != 0 {
216 let data: u8;
217 core::arch::asm!("in al, dx", out("al") data, in("dx") DATA_PORT,
218 options(nostack, preserves_flags));
219 return data;
220 }
221 }
222 }
223}
224
225/// Process a single scancode byte. Returns Some(KeyEvent) when a visible
226/// event happens; returns None for modifier changes, releases, and prefixes.
227fn process_byte(byte: u8) -> Option<KeyEvent> {
228 let state = unsafe { &mut KBD };
229
230 if byte == 0xE0 {
231 state.got_e0 = true;
232 return None;
233 }
234
235 if state.got_e0 {
236 state.got_e0 = false;
237 return process_e0(byte);
238 }
239
240 // Break code (key release) — bit 7 set
241 if byte & 0x80 != 0 {
242 update_modifiers(byte & 0x7F, false, false);
243 return None;
244 }
245
246 // Make code (key press)
247 update_modifiers(byte, true, false);
248
249 match byte {
250 0x01 => Some(KeyEvent::Escape),
251 0x0E => Some(KeyEvent::Backspace),
252 0x0F => Some(KeyEvent::Tab),
253 0x1C => Some(KeyEvent::Enter),
254 0x37 => Some(KeyEvent::Char('*')),
255 0x3B..=0x44 => Some(KeyEvent::F(byte - 0x3B + 1)),
256 _ => {
257 let shifted = (state.modifiers & (MOD_LSHIFT | MOD_RSHIFT)) != 0;
258 // CapsLock inverts case for letters
259 let caps = state.modifiers & MOD_CAPS != 0;
260 let table = if shifted { SCAN_SHIFT } else { SCAN_NORMAL };
261 let mut ch = table[byte as usize];
262 // Handle CapsLock for letters
263 if caps && ch.is_ascii_alphabetic() {
264 // Swap case: if table gives lowercase, make uppercase; etc.
265 // Since normal table has lowercase, shift table has uppercase:
266 ch = if shifted {
267 ch.to_ascii_lowercase()
268 } else {
269 ch.to_ascii_uppercase()
270 };
271 }
272 if ch != 0 {
273 Some(KeyEvent::Char(ch as char))
274 } else {
275 Some(KeyEvent::Unknown(byte))
276 }
277 }
278 }
279}
280
281/// Process an E0-prefixed scancode.
282fn process_e0(byte: u8) -> Option<KeyEvent> {
283 if byte & 0x80 != 0 {
284 update_modifiers(byte & 0x7F, false, true);
285 return None;
286 }
287
288 update_modifiers(byte, true, true);
289
290 match byte {
291 0x48 => Some(KeyEvent::ArrowUp),
292 0x50 => Some(KeyEvent::ArrowDown),
293 0x4B => Some(KeyEvent::ArrowLeft),
294 0x4D => Some(KeyEvent::ArrowRight),
295 0x47 => Some(KeyEvent::Home),
296 0x4F => Some(KeyEvent::End),
297 0x49 => Some(KeyEvent::PageUp),
298 0x51 => Some(KeyEvent::PageDown),
299 0x53 => Some(KeyEvent::Delete),
300 0x35 => Some(KeyEvent::Char('/')),
301 _ => Some(KeyEvent::Unknown(byte)),
302 }
303}
304
305fn update_modifiers(scancode: u8, pressed: bool, e0: bool) {
306 let state = unsafe { &mut KBD };
307 match (scancode, e0) {
308 (0x2A, false) => set_mod(state, MOD_LSHIFT, pressed),
309 (0x36, false) => set_mod(state, MOD_RSHIFT, pressed),
310 (0x1D, false) => set_mod(state, MOD_LCTRL, pressed),
311 (0x1D, true) => set_mod(state, MOD_RCTRL, pressed),
312 (0x38, false) => set_mod(state, MOD_LALT, pressed),
313 (0x38, true) => set_mod(state, MOD_RALT, pressed),
314 (0x3A, false) => {
315 if pressed {
316 state.modifiers ^= MOD_CAPS;
317 }
318 }
319 _ => {}
320 }
321}
322
323fn set_mod(state: &mut KbdState, bit: u8, pressed: bool) {
324 if pressed {
325 state.modifiers |= bit;
326 } else {
327 state.modifiers &= !bit;
328 }
329}
diff --git a/src/serial.rs b/src/serial.rs
new file mode 100644
index 0000000..03fa570
--- /dev/null
+++ b/src/serial.rs
@@ -0,0 +1,118 @@
1/// UART 16550 serial driver — port-mapped I/O on COM1 (0x3F8)
2use core::fmt;
3
4const COM1: u16 = 0x3F8;
5
6const DATA_REG: u16 = COM1;
7const INTR_ENABLE: u16 = COM1 + 1;
8const FIFO_CONTROL: u16 = COM1 + 2;
9const LINE_CONTROL: u16 = COM1 + 3;
10const MODEM_CONTROL: u16 = COM1 + 4;
11const LINE_STATUS: u16 = COM1 + 5;
12
13const LSR_TX_EMPTY: u8 = 0x20;
14const LSR_DATA_READY: u8 = 0x01;
15
16/// Initialise the UART with 115200 baud, 8N1.
17pub fn init() {
18 unsafe {
19 // Disable interrupts
20 port_out(INTR_ENABLE, 0x00);
21 // Set DLAB=1 (baud rate divisor)
22 port_out(LINE_CONTROL, 0x80);
23 // Divisor = 1 → 115200 baud (with 1.8432 MHz clock)
24 port_out(DATA_REG, 0x01);
25 port_out(INTR_ENABLE, 0x00);
26 // 8-bit, 1 stop, no parity, DLAB=0
27 port_out(LINE_CONTROL, 0x03);
28 // Enable FIFO, clear, 14-byte threshold
29 port_out(FIFO_CONTROL, 0xC7);
30 // RTS/DSR set
31 port_out(MODEM_CONTROL, 0x0B);
32 }
33}
34
35/// Write a single byte to the serial port (blocking poll).
36pub fn put_byte(b: u8) {
37 unsafe {
38 // Wait for transmitter holding register empty
39 while port_in(LINE_STATUS) & LSR_TX_EMPTY == 0 {}
40 port_out(DATA_REG, b);
41 }
42}
43
44/// Read a single byte from the serial port (blocking poll).
45pub fn get_byte() -> u8 {
46 unsafe {
47 while port_in(LINE_STATUS) & LSR_DATA_READY == 0 {}
48 port_in(DATA_REG)
49 }
50}
51
52/// Non-blocking read: returns Some(byte) if data ready, None otherwise.
53pub fn try_get_byte() -> Option<u8> {
54 unsafe {
55 if port_in(LINE_STATUS) & LSR_DATA_READY != 0 {
56 Some(port_in(DATA_REG))
57 } else {
58 None
59 }
60 }
61}
62
63/// Write a string to serial.
64pub fn write_str(s: &str) {
65 for &b in s.as_bytes() {
66 if b == b'\n' {
67 put_byte(b'\r');
68 }
69 put_byte(b);
70 }
71}
72
73// ---------------------------------------------------------------------------
74// fmt::Write implementation
75// ---------------------------------------------------------------------------
76pub struct SerialWriter;
77
78impl fmt::Write for SerialWriter {
79 fn write_str(&mut self, s: &str) -> fmt::Result {
80 write_str(s);
81 Ok(())
82 }
83}
84
85/// Helper macro — prints to serial via format_args!.
86#[macro_export]
87macro_rules! serial_print {
88 ($($arg:tt)*) => {{
89 use core::fmt::Write;
90 let _ = write!(&mut $crate::serial::SerialWriter, $($arg)*);
91 }};
92}
93
94#[macro_export]
95macro_rules! serial_println {
96 () => { $crate::serial::write_str("\r\n"); };
97 ($($arg:tt)*) => {{
98 $crate::serial_print!($($arg)*);
99 $crate::serial::write_str("\r\n");
100 }};
101}
102
103// ---------------------------------------------------------------------------
104// Port I/O helpers
105// ---------------------------------------------------------------------------
106unsafe fn port_in(port: u16) -> u8 {
107 let val: u8;
108 unsafe {
109 core::arch::asm!("in al, dx", out("al") val, in("dx") port, options(nostack, preserves_flags));
110 }
111 val
112}
113
114unsafe fn port_out(port: u16, val: u8) {
115 unsafe {
116 core::arch::asm!("out dx, al", in("al") val, in("dx") port, options(nostack, preserves_flags));
117 }
118}
diff --git a/src/shell.rs b/src/shell.rs
new file mode 100644
index 0000000..f21525e
--- /dev/null
+++ b/src/shell.rs
@@ -0,0 +1,274 @@
1/// Minimal command shell — reads from PS/2 keyboard, writes to GOP + serial.
2use crate::ps2::{self, KeyEvent};
3use crate::{gop_print, gop_println, serial_print, serial_println};
4
5/// Maximum input line length.
6const LINE_BUF_SIZE: usize = 256;
7
8/// Run the shell forever.
9pub fn run() -> ! {
10 gop_println!("raisernics v0.1 — type 'help' for commands");
11 serial_println!("========== raisernics v0.1 ==========");
12
13 loop {
14 // Print prompt
15 gop_print!("> ");
16 serial_print!("> ");
17
18 // Read one line from keyboard
19 let line = read_line();
20
21 // Parse and execute
22 let trimmed = line.trim();
23 if trimmed.is_empty() {
24 continue;
25 }
26
27 let (cmd, args) = split_first_word(trimmed);
28
29 match cmd {
30 "help" => cmd_help(),
31 "echo" => cmd_echo(args),
32 "clear" => cmd_clear(),
33 "halt" | "poweroff" | "exit" => cmd_halt(),
34 "hello" => {
35 gop_println!("World!");
36 serial_println!("World!");
37 }
38 "info" => cmd_info(),
39 "mem" => cmd_mem(),
40 _ => {
41 gop_println!("unknown command: {}", cmd);
42 serial_println!("unknown command: {}", cmd);
43 }
44 }
45 }
46}
47
48// ---------------------------------------------------------------------------
49// Line input (PS/2 keyboard, no heap)
50// ---------------------------------------------------------------------------
51fn read_line() -> heapless_str::String<LINE_BUF_SIZE> {
52 let mut buf = heapless_str::String::<LINE_BUF_SIZE>::new();
53 loop {
54 match ps2::poll_key() {
55 KeyEvent::Enter => {
56 crate::gop::put_char('\n');
57 crate::serial::write_str("\r\n");
58 return buf;
59 }
60 KeyEvent::Char(c) if c.is_ascii_graphic() || c == ' ' => {
61 if buf.push(c).is_ok() {
62 crate::gop::put_char(c);
63 // Echo to serial too
64 let mut s = [0u8; 4];
65 let s = c.encode_utf8(&mut s);
66 crate::serial::write_str(s);
67 }
68 }
69 KeyEvent::Backspace => {
70 if buf.pop().is_some() {
71 // GOP backspace: move cursor back and erase
72 crate::gop::put_char('\x08');
73 // Serial backspace: send BS SP BS (standard terminal protocol)
74 crate::serial::write_str("\x08 \x08");
75 }
76 }
77 KeyEvent::Tab => {
78 for _ in 0..4 {
79 if buf.push(' ').is_ok() {
80 crate::gop::put_char(' ');
81 crate::serial::write_str(" ");
82 }
83 }
84 }
85 _ => {}
86 }
87 }
88}
89
90/// Split a command line into the first word and the rest.
91fn split_first_word(s: &str) -> (&str, &str) {
92 let s = s.trim_start();
93 if let Some(i) = s.find(char::is_whitespace) {
94 (&s[..i], s[i..].trim_start())
95 } else {
96 (s, "")
97 }
98}
99
100// ---------------------------------------------------------------------------
101// Built-in commands
102// ---------------------------------------------------------------------------
103
104fn cmd_help() {
105 gop_println!("available commands:");
106 gop_println!(" help — show this message");
107 gop_println!(" echo — echo arguments");
108 gop_println!(" clear — clear screen");
109 gop_println!(" hello — print 'World!'");
110 gop_println!(" info — show system info");
111 gop_println!(" mem — show memory info");
112 gop_println!(" halt — halt the system");
113 gop_println!(" exit — alias for halt");
114 serial_println!("available commands:");
115 serial_println!(" help, echo, clear, hello, info, mem, halt, exit");
116}
117
118fn cmd_echo(args: &str) {
119 gop_println!("{}", args);
120}
121
122fn cmd_clear() {
123 crate::gop::clear_screen();
124}
125
126fn cmd_info() {
127 // Test that the global allocator works: build a String on the heap
128 let mut s = alloc::string::String::from("raisernics v0.1");
129 s.push_str(" — RNG custom kernel");
130 gop_println!("{}", s);
131 gop_println!("arch: x86_64, freestanding, no userspace");
132 serial_println!("{}", s);
133 serial_println!("arch: x86_64, freestanding, no userspace");
134 // String is freed (dropped) when this scope ends — tests dealloc too
135}
136
137fn cmd_mem() {
138 let (map, key) = crate::memory::get_map();
139
140 let mut total_kb = 0u64;
141 let mut conventional_kb = 0u64;
142 let mut loader_kb = 0u64;
143 let mut boot_code_kb = 0u64;
144 let mut boot_data_kb = 0u64;
145 let mut reserved_kb = 0u64;
146 let mut acpi_kb = 0u64;
147 let mut mmio_kb = 0u64;
148
149 for desc in map {
150 let pages = desc.number_of_pages;
151 let bytes_4k = pages * 4;
152 total_kb += bytes_4k;
153 match desc.kind {
154 1 => reserved_kb += bytes_4k,
155 3 => boot_code_kb += bytes_4k,
156 4 => boot_data_kb += bytes_4k,
157 5 | 10 => acpi_kb += bytes_4k,
158 6 => loader_kb += bytes_4k,
159 7 => conventional_kb += bytes_4k,
160 11 | 12 => mmio_kb += bytes_4k,
161 _ => reserved_kb += bytes_4k,
162 }
163 }
164
165 // Build a big heap string with format! to test the allocator
166 let mut out = alloc::string::String::new();
167 use alloc::fmt::Write;
168 let _ = writeln!(out, "memory map (key=0x{:x}, {} entries)", key, map.len());
169 let _ = writeln!(
170 out,
171 " usable {:4} MB Conventional",
172 conventional_kb / 1024
173 );
174 let _ = writeln!(
175 out,
176 " boot code {:4} MB BootServicesCode",
177 boot_code_kb / 1024
178 );
179 let _ = writeln!(
180 out,
181 " boot data {:4} MB BootServicesData",
182 boot_data_kb / 1024
183 );
184 let _ = writeln!(out, " loader {:4} MB LoaderData", loader_kb / 1024);
185 let _ = writeln!(out, " ACPI {:4} MB", acpi_kb / 1024);
186 let _ = writeln!(out, " MMIO {:4} MB", mmio_kb / 1024);
187 let _ = writeln!(out, " reserved {:4} MB", reserved_kb / 1024);
188 let _ = writeln!(
189 out,
190 " raw total {:4} MB (incl. PCIe MMIO)",
191 total_kb / 1024
192 );
193
194 gop_print!("{}", out);
195 serial_print!("{}", out);
196}
197
198fn cmd_halt() -> ! {
199 gop_println!("halting...");
200 serial_println!("halting...");
201 loop {
202 unsafe {
203 core::arch::asm!("cli; hlt");
204 }
205 }
206}
207
208// ---------------------------------------------------------------------------
209// Minimal fixed-capacity string for no-stack use (avoids alloc dependency)
210// ---------------------------------------------------------------------------
211mod heapless_str {
212 use core::fmt;
213
214 /// A fixed-capacity string allocated entirely on the stack.
215 pub struct String<const N: usize> {
216 buf: [u8; N],
217 len: usize,
218 }
219
220 impl<const N: usize> String<N> {
221 pub const fn new() -> Self {
222 String {
223 buf: [0u8; N],
224 len: 0,
225 }
226 }
227
228 pub fn as_str(&self) -> &str {
229 // SAFETY: we only write valid ASCII bytes.
230 unsafe { core::str::from_utf8_unchecked(&self.buf[..self.len]) }
231 }
232
233 pub fn push(&mut self, c: char) -> Result<(), ()> {
234 if self.len >= N {
235 return Err(());
236 }
237 self.buf[self.len] = c as u8;
238 self.len += 1;
239 Ok(())
240 }
241
242 pub fn pop(&mut self) -> Option<char> {
243 if self.len == 0 {
244 return None;
245 }
246 self.len -= 1;
247 Some(self.buf[self.len] as char)
248 }
249
250 pub fn is_empty(&self) -> bool {
251 self.len == 0
252 }
253
254 pub fn clear(&mut self) {
255 self.len = 0;
256 }
257
258 pub fn trim(&self) -> &str {
259 self.as_str().trim()
260 }
261 }
262
263 impl<const N: usize> fmt::Display for String<N> {
264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265 write!(f, "{}", self.as_str())
266 }
267 }
268
269 impl<const N: usize> fmt::Debug for String<N> {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 write!(f, "{:?}", self.as_str())
272 }
273 }
274}
diff --git a/src/uefi.rs b/src/uefi.rs
new file mode 100644
index 0000000..2e3ed6b
--- /dev/null
+++ b/src/uefi.rs
@@ -0,0 +1,289 @@
1use core::ffi::c_void;
2
3/// Opaque EFI handle
4pub type Handle = *mut c_void;
5
6/// EFI status (0 = success, bit 63 = error code)
7pub type Status = u64;
8
9/// Helper: UEFI error codes always have bit 63 set
10const fn efi_err(n: u64) -> u64 {
11 n | (1u64 << 63)
12}
13
14pub const EFI_SUCCESS: Status = 0;
15pub const EFI_LOAD_ERROR: Status = efi_err(1);
16pub const EFI_INVALID_PARAMETER: Status = efi_err(2);
17#[allow(dead_code)]
18pub const EFI_UNSUPPORTED: Status = efi_err(3);
19pub const EFI_BUFFER_TOO_SMALL: Status = efi_err(5);
20#[allow(dead_code)]
21pub const EFI_NOT_FOUND: Status = efi_err(14);
22pub const EFI_OUT_OF_RESOURCES: Status = efi_err(9);
23
24// ---------------------------------------------------------------------------
25// GUID
26// ---------------------------------------------------------------------------
27#[repr(C)]
28#[derive(Debug, Clone, Copy)]
29pub struct Guid {
30 pub a: u32,
31 pub b: u16,
32 pub c: u16,
33 pub d: [u8; 8],
34}
35
36pub const EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID: Guid = Guid {
37 a: 0x9042a9de,
38 b: 0x23dc,
39 c: 0x4a38,
40 d: [0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a],
41};
42
43// ---------------------------------------------------------------------------
44// Table header
45// ---------------------------------------------------------------------------
46#[repr(C)]
47pub struct TableHeader {
48 pub signature: u64,
49 pub revision: u32,
50 pub header_size: u32,
51 pub crc32: u32,
52 pub reserved: u32,
53}
54
55// ---------------------------------------------------------------------------
56// Simple text output protocol (console output)
57// ---------------------------------------------------------------------------
58#[repr(C)]
59pub struct SimpleTextOutput {
60 _reset: *mut c_void,
61 pub output_string: unsafe extern "efiapi" fn(*mut SimpleTextOutput, *const u16) -> Status,
62 _test_string: *mut c_void,
63 _query_mode: *mut c_void,
64 _set_mode: *mut c_void,
65 _set_attribute: *mut c_void,
66 _clear_screen: *mut c_void,
67 _set_cursor_position: *mut c_void,
68 _enable_cursor: *mut c_void,
69 _mode: *mut c_void,
70}
71
72// ---------------------------------------------------------------------------
73// Simple text input protocol (keyboard input)
74// ---------------------------------------------------------------------------
75#[repr(C)]
76pub struct InputKey {
77 pub scan_code: u16,
78 pub unicode_char: u16,
79}
80
81#[repr(C)]
82pub struct SimpleTextInput {
83 _reset: *mut c_void,
84 pub read_key_stroke: unsafe extern "efiapi" fn(*mut SimpleTextInput, *mut InputKey) -> Status,
85 _wait_for_key: Handle,
86}
87
88// ---------------------------------------------------------------------------
89// Runtime services (stub — we don't use them)
90// ---------------------------------------------------------------------------
91#[repr(C)]
92pub struct RuntimeServices {
93 _hdr: TableHeader,
94 _reserved: [*mut c_void; 30],
95}
96
97// ---------------------------------------------------------------------------
98// Configuration table entry
99// ---------------------------------------------------------------------------
100#[repr(C)]
101pub struct ConfigurationTable {
102 pub vendor_guid: Guid,
103 pub vendor_table: *mut c_void,
104}
105
106// ---------------------------------------------------------------------------
107/// UEFI memory descriptor (from GetMemoryMap)
108// ---------------------------------------------------------------------------
109#[repr(C)]
110pub struct MemoryDescriptor {
111 pub kind: u32,
112 pub pad: u32,
113 pub physical_start: u64,
114 pub virtual_start: u64,
115 pub number_of_pages: u64,
116 pub attribute: u64,
117}
118
119pub const MEMORY_TYPE_CONVENTIONAL: u32 = 7;
120#[allow(dead_code)]
121pub const MEMORY_TYPE_LOADER_DATA: u32 = 6;
122#[allow(dead_code)]
123pub const MEMORY_TYPE_BOOT_SERVICES_CODE: u32 = 3;
124#[allow(dead_code)]
125pub const MEMORY_TYPE_BOOT_SERVICES_DATA: u32 = 4;
126
127// ---------------------------------------------------------------------------
128// Boot services table
129// ---------------------------------------------------------------------------
130pub type EfiGetMemoryMap = unsafe extern "efiapi" fn(
131 *mut usize,
132 *mut MemoryDescriptor,
133 *mut usize,
134 *mut usize,
135 *mut u32,
136) -> Status;
137
138pub type EfiExitBootServices = unsafe extern "efiapi" fn(Handle, usize) -> Status;
139
140pub type EfiHandleProtocol =
141 unsafe extern "efiapi" fn(Handle, *const Guid, *mut *mut c_void) -> Status;
142
143pub type EfiLocateProtocol =
144 unsafe extern "efiapi" fn(*const Guid, *mut c_void, *mut *mut c_void) -> Status;
145
146pub type EfiAllocatePool = unsafe extern "efiapi" fn(u8, usize, *mut *mut c_void) -> Status;
147
148pub type EfiFreePool = unsafe extern "efiapi" fn(*mut c_void) -> Status;
149
150pub type EfiStall = unsafe extern "efiapi" fn(usize) -> Status;
151
152#[repr(C)]
153pub struct BootServices {
154 pub hdr: TableHeader,
155
156 // 0-1: task priority
157 pub raise_tpl: *mut c_void,
158 pub restore_tpl: *mut c_void,
159
160 // 2-3: memory allocation
161 pub allocate_pages: *mut c_void,
162 pub free_pages: *mut c_void,
163
164 // 4: GetMemoryMap
165 pub get_memory_map: EfiGetMemoryMap,
166
167 // 5-6: pool
168 pub allocate_pool: EfiAllocatePool,
169 pub free_pool: EfiFreePool,
170
171 // 7-12: events
172 pub create_event: *mut c_void,
173 pub set_timer: *mut c_void,
174 pub wait_for_event: *mut c_void,
175 pub signal_event: *mut c_void,
176 pub close_event: *mut c_void,
177 pub check_event: *mut c_void,
178
179 // 13-14: protocol install
180 pub install_protocol_interface: *mut c_void,
181 pub reinstall_protocol_interface: *mut c_void,
182 pub uninstall_protocol_interface: *mut c_void,
183
184 // 16: HandleProtocol
185 pub handle_protocol: EfiHandleProtocol,
186
187 pub _reserved: *mut c_void,
188
189 // 18: register protocol notify
190 pub register_protocol_notify: *mut c_void,
191
192 // 19-21: locate
193 pub locate_handle: *mut c_void,
194 pub locate_device_path: *mut c_void,
195 pub install_configuration_table: *mut c_void,
196
197 // 22-23: image loading
198 pub load_image: *mut c_void,
199 pub start_image: *mut c_void,
200
201 // 24-25
202 pub exit: *mut c_void,
203 pub unload_image: *mut c_void,
204
205 // 26: ExitBootServices
206 pub exit_boot_services: EfiExitBootServices,
207
208 // 27-29
209 pub get_next_monotonic_count: *mut c_void,
210 pub stall: EfiStall,
211 pub set_watchdog_timer: *mut c_void,
212
213 // 30-36
214 pub connect_controller: *mut c_void,
215 pub disconnect_controller: *mut c_void,
216 pub open_protocol: *mut c_void,
217 pub close_protocol: *mut c_void,
218 pub open_protocol_information: *mut c_void,
219 pub protocols_per_handle: *mut c_void,
220 pub locate_handle_buffer: *mut c_void,
221
222 // 37: LocateProtocol
223 pub locate_protocol: EfiLocateProtocol,
224
225 // 38-43
226 pub install_multiple_protocol_interfaces: *mut c_void,
227 pub uninstall_multiple_protocol_interfaces: *mut c_void,
228 pub calculate_crc32: *mut c_void,
229 pub copy_mem: *mut c_void,
230 pub set_mem: *mut c_void,
231 pub create_event_ex: *mut c_void,
232}
233
234// ---------------------------------------------------------------------------
235// Graphics Output Protocol (GOP)
236// ---------------------------------------------------------------------------
237#[repr(C)]
238pub struct GopModeInfo {
239 pub version: u32,
240 pub horizontal_resolution: u32,
241 pub vertical_resolution: u32,
242 pub pixel_format: u32, // PixelFormat enum
243 pub red_mask: u32,
244 pub green_mask: u32,
245 pub blue_mask: u32,
246 pub reserved_mask: u32,
247 pub pixels_per_scan_line: u32,
248}
249
250pub const PIXEL_RGB: u32 = 0;
251pub const PIXEL_BGR: u32 = 1;
252
253#[repr(C)]
254pub struct GopMode {
255 pub max_mode: u32,
256 pub mode: u32,
257 pub info: *mut GopModeInfo,
258 pub info_size: usize,
259 pub framebuffer_base: *mut c_void,
260 pub framebuffer_size: usize,
261}
262
263#[repr(C)]
264pub struct GraphicsOutputProtocol {
265 pub query_mode: *mut c_void,
266 pub set_mode: *mut c_void,
267 pub get_current_mode: *mut c_void,
268 pub mode: *mut GopMode,
269}
270
271// ---------------------------------------------------------------------------
272// System table
273// ---------------------------------------------------------------------------
274#[repr(C)]
275pub struct SystemTable {
276 pub hdr: TableHeader,
277 pub firmware_vendor: *const u16,
278 pub firmware_revision: u32,
279 pub console_in_handle: Handle,
280 pub con_in: *mut SimpleTextInput,
281 pub console_out_handle: Handle,
282 pub con_out: *mut SimpleTextOutput,
283 pub stderr_handle: Handle,
284 pub std_err: *mut SimpleTextOutput,
285 pub runtime_services: *mut RuntimeServices,
286 pub boot_services: *mut BootServices,
287 pub number_of_table_entries: usize,
288 pub configuration_table: *const ConfigurationTable,
289}