diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 9c8642f..68ed0e8 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -10,6 +10,7 @@ path = "src/main.rs" [dependencies] limine = "0.5" embedded-graphics = "0.8" +bitflags = "2.11.0" [profile.dev] panic = "abort" diff --git a/kernel/src/debug.rs b/kernel/src/debug.rs new file mode 100644 index 0000000..601c4ef --- /dev/null +++ b/kernel/src/debug.rs @@ -0,0 +1,57 @@ +pub mod serial; + +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::RgbColor; + +pub enum LogLevel { + Info, Warn, Error, +} + +impl LogLevel { + pub fn serial_color_code(&self) -> &str { + match self { + LogLevel::Info => "\x1b[32m", + LogLevel::Warn => "\x1b[33m", + LogLevel::Error => "\x1b[31m", + } + } + + pub fn console_color(&self) -> Rgb888 { + match self { + LogLevel::Info => Rgb888::GREEN, + LogLevel::Warn => Rgb888::YELLOW, + LogLevel::Error => Rgb888::RED, + } + } +} + +#[macro_export] +macro_rules! log { + ($console:expr, $level:expr, $module:expr, $($arg:tt)*) => {{ + use core::fmt::Write; + use embedded_graphics::pixelcolor::Rgb888; + + // Visual screen output + $console.set_color($level.console_color()); + let _ = write!($console, "[ LOG ] "); + + $console.set_color(Rgb888::WHITE); + let _ = write!($console, "{:<6} | ", $module); + let _ = writeln!($console, $($arg)*); + + // Serial debug output + let mut sp = unsafe { $crate::debug::serial::SerialPort::init() }; + let _ = writeln!( + sp, + "{}[{:>5}]\x1b[0m {:<8} | {}", + $level.serial_color_code(), + "LOG", + $module, + format_args!($($arg)*) + ); + }}; +} + +#[macro_export] macro_rules! info { ($c:expr, $m:expr, $($p:tt)*) => { $crate::log!($c, $crate::debug::LogLevel::Info, $m, $($p)*) }; } +#[macro_export] macro_rules! warn { ($c:expr, $m:expr, $($p:tt)*) => { $crate::log!($c, $crate::debug::LogLevel::Warn, $m, $($p)*) }; } +#[macro_export] macro_rules! error { ($c:expr, $m:expr, $($p:tt)*) => { $crate::log!($c, $crate::debug::LogLevel::Error, $m, $($p)*) }; } diff --git a/kernel/src/debug/serial.rs b/kernel/src/debug/serial.rs new file mode 100644 index 0000000..7a79a23 --- /dev/null +++ b/kernel/src/debug/serial.rs @@ -0,0 +1,53 @@ +use core::arch::asm; + +pub struct SerialPort(u16); + +impl SerialPort { + pub const COM1: u16 = 0x3F8; + + pub unsafe fn init() -> Self { + let port = Self::COM1; + // В новых версиях Rust даже внутри unsafe fn + // вызовы других unsafe функций требуют явного блока + unsafe { + outb(port + 1, 0x00); // Disable interrupts + outb(port + 3, 0x80); // Enable DLAB + outb(port + 0, 0x03); // Divisor 3 (38400 baud) + outb(port + 1, 0x00); + outb(port + 3, 0x03); // 8 bits, no parity, 1 stop bit + outb(port + 2, 0xC7); // Enable FIFO + outb(port + 4, 0x0B); // IRQs enabled, RTS/DSR set + } + SerialPort(port) + } + + fn is_transmit_empty(&self) -> bool { + unsafe { (inb(self.0 + 5) & 0x20) != 0 } + } + + pub fn send(&self, data: u8) { + while !self.is_transmit_empty() {} + unsafe { outb(self.0, data); } + } +} + +impl core::fmt::Write for SerialPort { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + for byte in s.bytes() { self.send(byte); } + Ok(()) + } +} + +unsafe fn outb(port: u16, val: u8) { + unsafe { + asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags)); + } +} + +unsafe fn inb(port: u16) -> u8 { + let res: u8; + unsafe { + asm!("in al, dx", out("al") res, in("dx") port, options(nomem, nostack, preserves_flags)); + } + res +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 26781aa..881a79c 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,10 +1,25 @@ #![no_std] #![no_main] +extern crate alloc; + use core::arch::asm; use core::fmt::{self, Write}; +use alloc::vec::Vec; + use limine::BaseRevision; -use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker}; +use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker, HhdmRequest, MemoryMapRequest, ExecutableAddressRequest}; + +use crate::mem::paging::{PageTable, PageTableFlags}; +use crate::mem::address::{PhysAddr, VirtAddr}; + +mod mem; +#[macro_use] +pub mod debug; + +pub mod allocator { + pub use crate::mem::allocator::ALLOCATOR; +} use embedded_graphics::{ mono_font::{ascii::FONT_6X10, MonoTextStyle}, @@ -14,7 +29,7 @@ use embedded_graphics::{ Drawable, }; -// Отрисовка пикселей +/// Low-level pixel rendering engine struct FramebufferDisplay<'a> { framebuffer: &'a limine::framebuffer::Framebuffer<'a>, } @@ -46,82 +61,70 @@ impl OriginDimensions for FramebufferDisplay<'_> { } } -// Терминальный вывод. Следит за положением курсора и умеет двигать текст +/// High-level console for terminal-like output struct Console<'a> { display: FramebufferDisplay<'a>, x: i32, y: i32, + current_color: Rgb888, } impl<'a> Console<'a> { pub fn new(display: FramebufferDisplay<'a>) -> Self { - Self { display, x: 10, y: 20 } // Начальный отступ + Self { display, x: 10, y: 20, current_color: Rgb888::WHITE } + } + + pub fn set_color(&mut self, color: Rgb888) { + self.current_color = color; } - // Метод для очистки экрана pub fn clear(&mut self) { - let size = self.display.size(); let fb = self.display.framebuffer; unsafe { - // Быстрая очистка через заполнение памяти нулями core::ptr::write_bytes(fb.addr(), 0, fb.pitch() as usize * fb.height() as usize); } self.x = 10; self.y = 20; } - // Логика скроллинга. копирует пиксели снизу вверх + /// Shifts pixels upward to make room for new lines fn scroll(&mut self) { let fb = self.display.framebuffer; let pitch = fb.pitch() as usize; let height = fb.height() as usize; - let font_height = 10; // Высота FONT_6X10 + let font_height = 10; let shift = font_height * pitch; unsafe { let addr = fb.addr(); - // Копируем всё содержимое экрана на одну строку вверх - core::ptr::copy( - addr.add(shift), - addr, - pitch * (height - font_height) - ); - // Очищаем последнюю строку - core::ptr::write_bytes( - addr.add(pitch * (height - font_height)), - 0, - shift - ); + core::ptr::copy(addr.add(shift), addr, pitch * (height - font_height)); + core::ptr::write_bytes(addr.add(pitch * (height - font_height)), 0, shift); } self.y -= font_height as i32; } } -// Вывод текста. write!(console, "Hello {}", name) impl fmt::Write for Console<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { - let style = MonoTextStyle::new(&FONT_6X10, Rgb888::WHITE); - + let style = MonoTextStyle::new(&FONT_6X10, self.current_color); + for c in s.chars() { if c == '\n' { self.x = 10; self.y += 10; } else { - // Если текст выходит за границы экрана — перенос строки if self.x + 6 > self.display.framebuffer.width() as i32 { self.x = 10; self.y += 10; } - // Рисуем символ (превращаем char в временную строку &str) let mut buf = [0u8; 4]; let s_char = c.encode_utf8(&mut buf); let _ = Text::new(s_char, Point::new(self.x, self.y), style).draw(&mut self.display); - self.x += 6; // Ширина FONT_6X10 + self.x += 6; } - // Если достигли низа экрана, то скроллим if self.y > self.display.framebuffer.height() as i32 - 10 { self.scroll(); } @@ -130,64 +133,113 @@ impl fmt::Write for Console<'_> { } } -// boot -#[used] -#[unsafe(link_section = ".requests")] +// --- Limine Requests --- + +#[used] #[unsafe(link_section = ".requests")] +static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new(); + +#[used] #[unsafe(link_section = ".requests")] +static HHDM_REQUEST: HhdmRequest = HhdmRequest::new(); + +#[used] #[unsafe(link_section = ".requests")] +static KERNEL_ADDR_REQUEST: ExecutableAddressRequest = ExecutableAddressRequest::new(); + +#[used] #[unsafe(link_section = ".requests")] static BASE_REVISION: BaseRevision = BaseRevision::new(); -#[used] -#[unsafe(link_section = ".requests")] +#[used] #[unsafe(link_section = ".requests")] static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); -#[used] -#[unsafe(link_section = ".requests_start_marker")] +#[used] #[unsafe(link_section = ".requests_start_marker")] static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new(); -#[used] -#[unsafe(link_section = ".requests_end_marker")] + +#[used] #[unsafe(link_section = ".requests_end_marker")] static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new(); #[unsafe(no_mangle)] unsafe extern "C" fn kmain() -> ! { assert!(BASE_REVISION.is_supported()); - if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() { - if let Some(fb) = framebuffer_response.framebuffers().next() { - - // Инициализируем консоль - let mut console = Console::new(FramebufferDisplay { framebuffer: &fb }); - console.clear(); + let fb_res = FRAMEBUFFER_REQUEST.get_response().expect("Limine: No Framebuffer"); + let mmap_res = MEMORY_MAP_REQUEST.get_response().expect("Limine: No Memory Map"); + let hhdm_res = HHDM_REQUEST.get_response().expect("Limine: No HHDM"); + let kaddr_res = KERNEL_ADDR_REQUEST.get_response().expect("Limine: No Kernel Address"); + let hhdm_offset = hhdm_res.offset(); - let _ = writeln!(console, "RINA Kernel"); - let _ = writeln!(console, "-----------------------"); - let _ = writeln!(console, "Framebuffer: {}x{}", fb.width(), fb.height()); - let _ = writeln!(console, "Memory mapping... OK"); - let _ = writeln!(console, "Hello, Arina! Terminal mode active :3"); - - // Тест скроллинга: выведем много строк - for i in 0..5 { - let _ = writeln!(console, "System log entry #{}", i); - } + let fb = fb_res.framebuffers().next().expect("Limine: No active framebuffer found"); + let mut console = Console::new(FramebufferDisplay { framebuffer: &fb }); + console.clear(); + + info!(console, "BOOT", "RINA Kernel Starting..."); + + // Initialize Physical Memory Manager + unsafe { mem::pmm::BitmapPMM::init(&mmap_res, hhdm_offset); } + info!(console, "MEM", "Physical Memory Manager initialized."); + + // Prepare Kernel Page Tables (P4) + let p4_phys = mem::pmm::alloc_page().expect("OOM: Failed to allocate P4 table"); + let p4 = unsafe { &mut *p4_phys.to_virt(hhdm_offset).as_mut_ptr::() }; + unsafe { core::ptr::write_bytes(p4 as *mut _ as *mut u8, 0, 4096); } + + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + + // Mapping Strategy: HHDM + Identity Mapping for active segments + for entry in mmap_res.entries() { + let phys = PhysAddr(entry.base); + let virt_hhdm = phys.to_virt(hhdm_offset); + + p4.map_region(virt_hhdm, phys, entry.length, flags, hhdm_offset); + + if entry.entry_type != limine::memory_map::EntryType::RESERVED { + p4.map_region(VirtAddr(entry.base), phys, entry.length, flags, hhdm_offset); } } - hcf(); -} + // Map the kernel itself based on provided executable addresses + p4.map_region( + VirtAddr(kaddr_res.virtual_base()), + PhysAddr(kaddr_res.physical_base()), + 0x1000 * 1024, // 4MB default + flags, + hhdm_offset + ); + + info!(console, "MMU", "Switching to Kernel Page Tables..."); + unsafe { p4.activate(p4_phys); } + + // Initialize Global Heap + let heap_start = 0xFFFF_9000_0000_0000; + let heap_size = 8 * 1024 * 1024; + + for i in (0..heap_size).step_by(4096) { + let frame = mem::pmm::alloc_page().expect("OOM: Heap allocation failed"); + p4.map_page(VirtAddr(heap_start + i as u64), frame, flags, hhdm_offset); + } + + { + let mut allocator = allocator::ALLOCATOR.lock(); + allocator.init(heap_start as usize, heap_size); + } + + info!(console, "HEAP", "Global Allocator is online."); + + // Integrity Test + let mut test_vec = Vec::new(); + test_vec.push(42); + info!(console, "CORE", "Vec test passed. Pointer: {:?}", test_vec.as_ptr()); -#[panic_handler] -fn rust_panic(_info: &core::panic::PanicInfo) -> ! { - // В будущем тут будет выводиться текст ошибки hcf(); } fn hcf() -> ! { loop { - unsafe { - #[cfg(target_arch = "x86_64")] - asm!("hlt"); - #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] - asm!("wfi"); - #[cfg(target_arch = "loongarch64")] - asm!("idle 0"); - } + unsafe { asm!("hlt", options(nomem, nostack, preserves_flags)); } } } + +#[panic_handler] +fn rust_panic(info: &core::panic::PanicInfo) -> ! { + let mut sp = unsafe { debug::serial::SerialPort::init() }; + let _ = writeln!(sp, "KERNEL PANIC: {:?}", info); + hcf(); +} diff --git a/kernel/src/mem/address.rs b/kernel/src/mem/address.rs new file mode 100644 index 0000000..8a7675f --- /dev/null +++ b/kernel/src/mem/address.rs @@ -0,0 +1,36 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct PhysAddr(pub u64); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct VirtAddr(pub u64); + +impl PhysAddr { + /// Convert physical address to virtual via HHDM offset + pub fn to_virt(self, hhdm_offset: u64) -> VirtAddr { + VirtAddr(self.0 + hhdm_offset) + } + + #[allow(dead_code)] + pub fn is_aligned(self) -> bool { self.0 % 4096 == 0 } + + #[allow(dead_code)] + pub fn align_down(self) -> Self { Self(self.0 & !0xFFF) } + + #[allow(dead_code)] + pub fn align_up(self) -> Self { Self((self.0 + 4095) & !0xFFF) } +} + +impl VirtAddr { + #[allow(dead_code)] + pub fn to_phys(self, hhdm_offset: u64) -> Option { + if self.0 < hhdm_offset { return None; } + Some(PhysAddr(self.0 - hhdm_offset)) + } + + #[allow(dead_code)] + pub fn as_ptr(self) -> *const T { self.0 as *const T } + + pub fn as_mut_ptr(self) -> *mut T { self.0 as *mut T } +} diff --git a/kernel/src/mem/allocator.rs b/kernel/src/mem/allocator.rs new file mode 100644 index 0000000..0823f29 --- /dev/null +++ b/kernel/src/mem/allocator.rs @@ -0,0 +1,89 @@ +use core::alloc::{GlobalAlloc, Layout}; +use core::ptr::null_mut; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::ops::{Deref, DerefMut}; + +pub struct Locked { + inner: core::cell::UnsafeCell, + lock: AtomicBool, +} + +pub struct LockedGuard<'a, A> { + lock: &'a AtomicBool, + data: &'a mut A, +} + +unsafe impl Sync for Locked {} + +impl Locked { + pub const fn new(inner: A) -> Self { + Self { + inner: core::cell::UnsafeCell::new(inner), + lock: AtomicBool::new(false), + } + } + + pub fn lock(&self) -> LockedGuard<'_, A> { + while self.lock.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() { + core::hint::spin_loop(); + } + LockedGuard { + lock: &self.lock, + data: unsafe { &mut *self.inner.get() }, + } + } +} + +impl Drop for LockedGuard<'_, A> { + fn drop(&mut self) { + self.lock.store(false, Ordering::Release); + } +} + +impl Deref for LockedGuard<'_, A> { + type Target = A; + fn deref(&self) -> &Self::Target { self.data } +} + +impl DerefMut for LockedGuard<'_, A> { + fn deref_mut(&mut self) -> &mut Self::Target { self.data } +} + +pub struct BumpAllocator { + start: usize, + end: usize, + next: usize, +} + +impl BumpAllocator { + pub const fn new() -> Self { + Self { start: 0, end: 0, next: 0 } + } + + pub fn init(&mut self, start: usize, size: usize) { + self.start = start; + self.next = start; + self.end = start + size; + } +} + +unsafe impl GlobalAlloc for Locked { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let mut bump = self.lock(); + + let alloc_start = (bump.next + layout.align() - 1) & !(layout.align() - 1); + let alloc_end = alloc_start + layout.size(); + + if alloc_end > bump.end { + null_mut() + } else { + bump.next = alloc_end; + alloc_start as *mut u8 + } + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} + +#[global_allocator] +pub static ALLOCATOR: Locked = Locked::new(BumpAllocator::new()); diff --git a/kernel/src/mem/mod.rs b/kernel/src/mem/mod.rs new file mode 100644 index 0000000..9492015 --- /dev/null +++ b/kernel/src/mem/mod.rs @@ -0,0 +1,19 @@ +pub mod pmm; +pub mod address; +pub mod paging; +pub mod allocator; + +#[allow(dead_code)] +pub fn init(memmap: &limine::response::MemoryMapResponse, hhdm_offset: u64) { + unsafe { + pmm::BitmapPMM::init(memmap, hhdm_offset); + } +} + +#[allow(dead_code)] +pub fn get_stats() -> (usize, usize) { + unsafe { + let pmm = pmm::get_pmm_unchecked(); + (pmm.used_pages(), pmm.total_pages()) + } +} diff --git a/kernel/src/mem/paging.rs b/kernel/src/mem/paging.rs new file mode 100644 index 0000000..61a5127 --- /dev/null +++ b/kernel/src/mem/paging.rs @@ -0,0 +1,77 @@ +use crate::mem::address::{PhysAddr, VirtAddr}; +use crate::mem::pmm; +use core::arch::asm; +use bitflags::bitflags; + +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct PageTableFlags: u64 { + const PRESENT = 1 << 0; + const WRITABLE = 1 << 1; + const USER = 1 << 2; + const WRITE_THROUGH = 1 << 3; + const NO_CACHE = 1 << 4; + const ACCESSED = 1 << 5; + const DIRTY = 1 << 6; + const HUGE_PAGE = 1 << 7; + const GLOBAL = 1 << 8; + const NO_EXECUTE = 1 << 63; + } +} + +#[repr(C, align(4096))] +pub struct PageTable { + entries: [u64; 512], +} + +impl PageTable { + pub fn map_region(&mut self, virt: VirtAddr, phys: PhysAddr, size: u64, flags: PageTableFlags, hhdm: u64) { + let pages = size.div_ceil(4096); + for i in 0..pages { + let offset = i * 4096; + self.map_page(VirtAddr(virt.0 + offset), PhysAddr(phys.0 + offset), flags, hhdm); + } + } + + /// Loads the page table into the CR3 register + pub unsafe fn activate(&self, phys_addr: PhysAddr) { + unsafe { + asm!( + "mov cr3, {0}", + "jmp 2f", + "2:", + in(reg) phys_addr.0, + options(nostack, preserves_flags) + ); + } + } + + pub fn map_page(&mut self, virt: VirtAddr, phys: PhysAddr, flags: PageTableFlags, hhdm_offset: u64) { + let p4_idx = (virt.0 >> 39) & 0x1FF; + let p3_idx = (virt.0 >> 30) & 0x1FF; + let p2_idx = (virt.0 >> 21) & 0x1FF; + let p1_idx = (virt.0 >> 12) & 0x1FF; + + let p3 = self.get_or_create_next_table(p4_idx as usize, hhdm_offset); + let p2 = p3.get_or_create_next_table(p3_idx as usize, hhdm_offset); + let p1 = p2.get_or_create_next_table(p2_idx as usize, hhdm_offset); + + p1.entries[p1_idx as usize] = phys.0 | flags.bits(); + + unsafe { + asm!("invlpg [{}]", in(reg) virt.0, options(nostack, preserves_flags)); + } + } + + fn get_or_create_next_table(&mut self, index: usize, hhdm: u64) -> &mut Self { + if self.entries[index] & PageTableFlags::PRESENT.bits() == 0 { + let pt_phys = pmm::alloc_page().expect("VMM: Table allocation failed"); + let pt_virt = pt_phys.to_virt(hhdm); + unsafe { core::ptr::write_bytes(pt_virt.as_mut_ptr::(), 0, 4096); } + + self.entries[index] = pt_phys.0 | (PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER).bits(); + } + let next_pt_phys = PhysAddr(self.entries[index] & 0x000F_FFFF_FFFF_F000); + unsafe { &mut *next_pt_phys.to_virt(hhdm).as_mut_ptr() } + } +} diff --git a/kernel/src/mem/pmm.rs b/kernel/src/mem/pmm.rs new file mode 100644 index 0000000..38e744f --- /dev/null +++ b/kernel/src/mem/pmm.rs @@ -0,0 +1,128 @@ +use crate::mem::address::{PhysAddr}; + +pub const PAGE_SIZE: u64 = 4096; + +#[allow(dead_code)] +pub struct BitmapPMM { + bitmap: &'static mut [u8], + total_pages: usize, + used_pages: usize, + last_idx: usize, +} + +static mut PMM: Option = None; + +impl BitmapPMM { + #[allow(dead_code)] + pub fn used_pages(&self) -> usize { self.used_pages } + #[allow(dead_code)] + pub fn total_pages(&self) -> usize { self.total_pages } + + pub unsafe fn init(mmap: &limine::response::MemoryMapResponse, hhdm_offset: u64) { + let max_addr = mmap.entries().iter() + .map(|e| e.base + e.length) + .max() + .unwrap_or(0); + + let total_pages = (max_addr / PAGE_SIZE) as usize; + let bitmap_size = total_pages.div_ceil(8); + + let bitmap_phys_addr = mmap.entries().iter() + .find(|e| e.entry_type == limine::memory_map::EntryType::USABLE && e.length >= bitmap_size as u64) + .map(|e| e.base) + .expect("PMM: Insufficient memory for bitmap"); + + let bitmap_virt_ptr = (bitmap_phys_addr + hhdm_offset) as *mut u8; + // Оборачиваем небезопасные операции + let bitmap_slice = unsafe { core::slice::from_raw_parts_mut(bitmap_virt_ptr, bitmap_size) }; + bitmap_slice.fill(0xFF); + + let mut pmm = Self { + bitmap: bitmap_slice, + total_pages, + used_pages: total_pages, + last_idx: 0, + }; + + for entry in mmap.entries() { + if entry.entry_type == limine::memory_map::EntryType::USABLE { + for addr in (entry.base..entry.base + entry.length).step_by(PAGE_SIZE as usize) { + pmm.free_frame(PhysAddr(addr)); + } + } + } + + for addr in (bitmap_phys_addr..bitmap_phys_addr + bitmap_size as u64).step_by(PAGE_SIZE as usize) { + pmm.lock_frame(PhysAddr(addr)); + } + + pmm.lock_frame(PhysAddr(0)); + + unsafe { + core::ptr::write(core::ptr::addr_of_mut!(PMM), Some(pmm)); + } + } + + pub fn free_frame(&mut self, phys_addr: PhysAddr) { + let idx = (phys_addr.0 / PAGE_SIZE) as usize; + if idx < self.total_pages { + let byte_idx = idx / 8; + let bit_idx = idx % 8; + if (self.bitmap[byte_idx] & (1 << bit_idx)) != 0 { + self.bitmap[byte_idx] &= !(1 << bit_idx); + self.used_pages -= 1; + } + } + } + + pub fn lock_frame(&mut self, phys_addr: PhysAddr) { + let idx = (phys_addr.0 / PAGE_SIZE) as usize; + if idx < self.total_pages { + let byte_idx = idx / 8; + let bit_idx = idx % 8; + if (self.bitmap[byte_idx] & (1 << bit_idx)) == 0 { + self.bitmap[byte_idx] |= 1 << bit_idx; + self.used_pages += 1; + } + } + } + + pub fn alloc_frame(&mut self) -> Option { + for i in self.last_idx..self.total_pages { + let byte_idx = i / 8; + if self.bitmap[byte_idx] == 0xFF { continue; } + + let bit_idx = i % 8; + if (self.bitmap[byte_idx] & (1 << bit_idx)) == 0 { + let addr = PhysAddr(i as u64 * PAGE_SIZE); + self.lock_frame(addr); + self.last_idx = i; + return Some(addr); + } + } + None + } +} + +#[allow(dead_code)] +pub unsafe fn get_pmm_unchecked() -> &'static BitmapPMM { + let pmm_ptr = core::ptr::addr_of!(PMM); + unsafe { (*pmm_ptr).as_ref().expect("PMM: Not initialized") } +} + +pub fn alloc_page() -> Option { + unsafe { + let pmm_ptr = core::ptr::addr_of_mut!(PMM); + (*pmm_ptr).as_mut()?.alloc_frame() + } +} + +#[allow(dead_code)] +pub fn free_page(addr: PhysAddr) { + unsafe { + let pmm_ptr = core::ptr::addr_of_mut!(PMM); + if let Some(pmm) = (*pmm_ptr).as_mut() { + pmm.free_frame(addr); + } + } +}