use crate::{PageFlags, PageFrame};
use faces::{AbsPageFrameManager, Convertable as _, PhysicalAddress, to};
use log::{debug, info};
#[derive(Debug, Copy, Clone, Default)]
pub struct PageFrameManager;
pub static PFM: PageFrameManager = PageFrameManager::new();
type Fa = &'static mut [spin::Mutex<PageFrame>];
static mut FRAME_ARRAY: &mut [spin::Mutex<PageFrame>] = Fa::default();
macro_rules! frame { ($pfn:expr) => {unsafe{FRAME_ARRAY[to($pfn)].lock()}} }
#[used]
#[unsafe(link_section = ".requests")]
static MMAP: limine::request::MemmapRequest = limine::request::MemmapRequest::new();
impl PageFrameManager {
pub const fn new() -> Self { Self {} }
fn detect_total_physical_memory() -> usize {
let memmap = MMAP.response().expect("Failed to obtain memory map").entries();
let mut total = 0;
for entry in memmap {
total += entry.length as usize;
}
total
}
fn find_better_place(num_frames: usize) -> (PhysicalAddress, usize) {
let memmap = MMAP.response().expect("Failed to obtain memory map").entries();
let required_size = (num_frames * core::mem::size_of::<PageFrame>()).next_multiple_of(4096);
let mut best_start = 0;
let mut best_len = 0;
for entry in memmap {
if entry.type_ != limine::memmap::MEMMAP_USABLE {
continue;
}
let len = entry.length as usize;
if len >= required_size && len > best_len {
best_len = len;
best_start = entry.base;
}
}
if best_len == 0 {
panic!("No suitable memory region for PageFrameInfo array (need {} bytes)", required_size);
}
(to(best_start as usize), required_size)
}
pub fn init(&self) {
let total_memory = Self::detect_total_physical_memory();
let num_frames = total_memory >> 12;
let (phys_addr, size) = Self::find_better_place(num_frames);
let virt_addr = phys_addr.to() << 12;
let addr: &mut spin::Mutex<PageFrame>;
unsafe {
addr = faces::unsafe_to(to::<faces::VirtualAddress, _>(virt_addr));
}
debug!("Total mem: {:#x}", total_memory);
debug!("Total frames: {}", num_frames);
debug!("PFI array address: {:#x}", virt_addr);
info!("pfm: Initializing page frame array");
unsafe {
let slice = core::slice::from_raw_parts_mut::<'static, spin::Mutex<PageFrame>>(addr, num_frames);
for frame in slice.iter_mut() {
*frame = spin::Mutex::<PageFrame>::new(PageFrame::new());
}
FRAME_ARRAY = slice;
}
info!("pfm: Obtaining memory map from bootloader");
let memmap = MMAP.response().expect("Failed to obtain memory map").entries();
info!("pfm: Reserving whole array");
for idx in 0..num_frames {
self.set_flags(to(idx), PageFlags::RESERVED);
}
info!("pfm: Searching usable memory");
for entry in memmap {
if entry.type_ == limine::memmap::MEMMAP_USABLE {
let start = (entry.base / 4096) as usize;
let end = ((entry.base + entry.length + 4095) / 4096) as usize;
for idx in start..end.min(num_frames) {
self.clear_flags(to(idx), PageFlags::RESERVED);
}
}
}
info!("pfm: Reserving system memory areas");
let array_start = to(phys_addr) >> 12;
let array_end = (array_start + size + 4095) >> 12;
for idx in array_start..array_end.min(num_frames) {
self.set_flags(to(idx), PageFlags::RESERVED);
}
}
}
type PFN = faces::PageFrameNumber;
impl AbsPageFrameManager for PageFrameManager {
type Flags = PageFlags;
type Access = spin::MutexGuard<'static, PageFrame, spin::Spin>;
fn check_flags(&self, pfn: PFN, flag: PageFlags) -> bool {
frame![pfn].flags & flag != PageFlags::empty()
}
fn clear_flags(&self, pfn: PFN, flag: PageFlags) {
frame![pfn].flags &= !flag
}
fn max(&self) -> PFN {
to(usize::MAX)
}
fn min(&self) -> PFN {
to(0)
}
fn present(&self, _pfn: PFN) -> bool {
true
}
fn set_flags(&self, pfn: PFN, flag: PageFlags) {
frame!(pfn).flags |= flag
}
fn get(&self, pfn: PFN) -> Self::Access {
frame!(pfn)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::PageFrame;
use spin::Mutex;
unsafe fn setup_mock_frames(num_frames: usize) {
let mut vec = Vec::with_capacity(num_frames);
for _ in 0..num_frames {
vec.push(Mutex::new(PageFrame::default()));
}
let slice: &'static mut [Mutex<PageFrame>] = Box::leak(vec.into_boxed_slice());
unsafe {
FRAME_ARRAY = slice;
}
}
unsafe fn teardown_mock_frames() {
unsafe {
FRAME_ARRAY = Fa::default();
}
}
#[test]
fn test_flag_operations() {
unsafe {
setup_mock_frames(10);
}
let pfm = PageFrameManager::new();
let pfn = faces::to(5);
assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
pfm.set_flags(pfn, PageFlags::LOCKED);
assert!(pfm.check_flags(pfn, PageFlags::LOCKED));
assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
pfm.set_flags(pfn, PageFlags::DIRTY);
assert!(pfm.check_flags(pfn, PageFlags::LOCKED));
assert!(pfm.check_flags(pfn, PageFlags::DIRTY));
pfm.clear_flags(pfn, PageFlags::LOCKED);
assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
assert!(pfm.check_flags(pfn, PageFlags::DIRTY));
pfm.clear_flags(pfn, PageFlags::DIRTY);
assert!(!pfm.check_flags(pfn, PageFlags::LOCKED));
assert!(!pfm.check_flags(pfn, PageFlags::DIRTY));
unsafe {
teardown_mock_frames();
}
}
#[test]
fn test_get_frame_mut() {
unsafe {
setup_mock_frames(3);
}
let pfm = PageFrameManager::new();
let pfn = faces::to(1);
{
let mut guard = pfm.get(pfn);
guard.flags = PageFlags::SWAPCACHE;
guard.order = 42;
guard.rc = 100;
}
assert!(pfm.check_flags(pfn, PageFlags::SWAPCACHE));
let guard = pfm.get(pfn);
assert_eq!(guard.order, 42);
assert_eq!(guard.rc, 100);
unsafe {
teardown_mock_frames();
}
}
#[test]
fn test_min_max_present() {
let pfm = PageFrameManager::new();
assert!(pfm.present(faces::to(0)));
assert!(pfm.present(faces::to(12345)));
}
#[test]
fn test_frame_macro() {
unsafe {
setup_mock_frames(5);
}
let pfn: faces::PageFrameNumber = faces::to(2);
{
let mut guard = frame!(pfn);
guard.flags = PageFlags::COMPOUND;
}
assert!(PFM.check_flags(pfn, PageFlags::COMPOUND));
unsafe {
teardown_mock_frames();
}
}
}