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/memory.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 '')
| -rw-r--r-- | src/memory.rs | 196 |
1 files changed, 196 insertions, 0 deletions
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 @@ | |||
| 1 | use crate::uefi::*; | ||
| 2 | use core::alloc::{GlobalAlloc, Layout}; | ||
| 3 | use core::mem; | ||
| 4 | use core::ptr; | ||
| 5 | |||
| 6 | const MAX_MAP_ENTRIES: usize = 512; | ||
| 7 | |||
| 8 | static mut MEMORY_MAP: [MemoryDescriptor; MAX_MAP_ENTRIES] = unsafe { mem::zeroed() }; | ||
| 9 | static mut MAP_ENTRIES: usize = 0; | ||
| 10 | |||
| 11 | /// The map key returned by GetMemoryMap, needed for ExitBootServices. | ||
| 12 | static 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. | ||
| 16 | static 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 | // --------------------------------------------------------------------------- | ||
| 22 | struct BumpAllocator; | ||
| 23 | |||
| 24 | unsafe impl Sync for BumpAllocator {} | ||
| 25 | |||
| 26 | unsafe 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] | ||
| 52 | static ALLOCATOR: BumpAllocator = BumpAllocator; | ||
| 53 | |||
| 54 | /// Heap bump allocator bounds (`static mut` accessed only inside | ||
| 55 | /// `GlobalAlloc` impl, which is already `unsafe`). | ||
| 56 | static mut HEAP_START: usize = 0; | ||
| 57 | static mut HEAP_END: usize = 0; | ||
| 58 | static 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. | ||
| 63 | pub 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. | ||
| 139 | pub 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 | |||
| 149 | fn 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)] | ||
| 176 | pub 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)] | ||
| 189 | pub 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 | } | ||
