feat: add pmm, add vmm, add allocator, create memory management foundation
This commit is contained in:
36
kernel/src/mem/address.rs
Normal file
36
kernel/src/mem/address.rs
Normal 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 }
|
||||
}
|
||||
89
kernel/src/mem/allocator.rs
Normal file
89
kernel/src/mem/allocator.rs
Normal 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
19
kernel/src/mem/mod.rs
Normal 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
77
kernel/src/mem/paging.rs
Normal 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
128
kernel/src/mem/pmm.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user