feat: add pmm, add vmm, add allocator, create memory management foundation

This commit is contained in:
Faynot
2026-03-29 19:07:41 +03:00
parent 83117c0a65
commit 7860db3814
9 changed files with 578 additions and 66 deletions

View File

@@ -10,6 +10,7 @@ path = "src/main.rs"
[dependencies]
limine = "0.5"
embedded-graphics = "0.8"
bitflags = "2.11.0"
[profile.dev]
panic = "abort"

57
kernel/src/debug.rs Normal file
View File

@@ -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)*) }; }

View File

@@ -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
}

View File

@@ -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::<PageTable>() };
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();
}

36
kernel/src/mem/address.rs Normal file
View File

@@ -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<PhysAddr> {
if self.0 < hhdm_offset { return None; }
Some(PhysAddr(self.0 - hhdm_offset))
}
#[allow(dead_code)]
pub fn as_ptr<T>(self) -> *const T { self.0 as *const T }
pub fn as_mut_ptr<T>(self) -> *mut T { self.0 as *mut T }
}

View File

@@ -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<A> {
inner: core::cell::UnsafeCell<A>,
lock: AtomicBool,
}
pub struct LockedGuard<'a, A> {
lock: &'a AtomicBool,
data: &'a mut A,
}
unsafe impl<A> Sync for Locked<A> {}
impl<A> Locked<A> {
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<A> Drop for LockedGuard<'_, A> {
fn drop(&mut self) {
self.lock.store(false, Ordering::Release);
}
}
impl<A> Deref for LockedGuard<'_, A> {
type Target = A;
fn deref(&self) -> &Self::Target { self.data }
}
impl<A> 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<BumpAllocator> {
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<BumpAllocator> = Locked::new(BumpAllocator::new());

19
kernel/src/mem/mod.rs Normal file
View File

@@ -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())
}
}

77
kernel/src/mem/paging.rs Normal file
View File

@@ -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::<u8>(), 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() }
}
}

128
kernel/src/mem/pmm.rs Normal file
View File

@@ -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<BitmapPMM> = 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<PhysAddr> {
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<PhysAddr> {
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);
}
}
}