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

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