Przeglądaj źródła

Merge branch 'mm-refactor'

greatbridf 8 miesięcy temu
rodzic
commit
896553fd7b

+ 2 - 2
Cargo.lock

@@ -61,7 +61,6 @@ name = "buddy_allocator"
 version = "0.1.0"
 dependencies = [
  "eonix_mm",
- "eonix_sync",
  "intrusive_list",
 ]
 
@@ -108,7 +107,7 @@ dependencies = [
 name = "eonix_mm"
 version = "0.1.0"
 dependencies = [
- "intrusive_list",
+ "bitflags",
 ]
 
 [[package]]
@@ -155,6 +154,7 @@ dependencies = [
  "eonix_runtime",
  "eonix_sync",
  "intrusive-collections",
+ "intrusive_list",
  "itertools",
  "pointers",
  "posix_types",

+ 1 - 0
Cargo.toml

@@ -17,6 +17,7 @@ eonix_preempt = { path = "./crates/eonix_preempt" }
 eonix_runtime = { path = "./crates/eonix_runtime" }
 eonix_sync = { path = "./crates/eonix_sync" }
 eonix_log = { path = "./crates/eonix_log" }
+intrusive_list = { path = "./crates/intrusive_list" }
 pointers = { path = "./crates/pointers" }
 posix_types = { path = "./crates/posix_types" }
 

+ 85 - 90
arch/src/x86_64/mm.rs

@@ -1,7 +1,9 @@
 use core::{marker::PhantomData, ptr::NonNull};
 use eonix_mm::{
     address::{Addr as _, PAddr},
-    page_table::{PageAttribute, PageTableLevel, PagingMode, RawPageTable, PTE},
+    page_table::{
+        PageAttribute, PageTableLevel, PagingMode, RawAttribute, RawPageTable, TableAttribute, PTE,
+    },
     paging::{PageBlock, PFN},
 };
 
@@ -18,7 +20,6 @@ const PA_PWT: u64 = 0x008;
 const PA_PCD: u64 = 0x010;
 const PA_A: u64 = 0x020;
 const PA_D: u64 = 0x040;
-#[allow(dead_code)]
 const PA_PS: u64 = 0x080;
 const PA_G: u64 = 0x100;
 const PA_COW: u64 = 0x200;
@@ -52,12 +53,6 @@ impl PTE for PTE64 {
             PageAttribute64(self.0 & PA_MASK),
         )
     }
-
-    fn take(&mut self) -> (PFN, Self::Attr) {
-        let pfn_attr = self.get();
-        self.0 = 0;
-        pfn_attr
-    }
 }
 
 impl PagingMode for PagingMode4Levels {
@@ -90,117 +85,117 @@ impl<'a> RawPageTable<'a> for RawPageTable4Levels<'a> {
     }
 }
 
-impl PageAttribute for PageAttribute64 {
-    fn new() -> Self {
-        Self(PA_NXE)
+impl RawAttribute for PageAttribute64 {
+    fn null() -> Self {
+        Self(0)
     }
 
-    fn present(self, present: bool) -> Self {
-        if present {
-            Self(self.0 | PA_P)
-        } else {
-            Self(self.0 & !PA_P)
-        }
-    }
+    fn as_table_attr(self) -> Option<TableAttribute> {
+        let mut table_attr = TableAttribute::empty();
 
-    fn write(self, write: bool) -> Self {
-        if write {
-            Self(self.0 | PA_RW)
-        } else {
-            Self(self.0 & !PA_RW)
+        if self.0 & PA_PS != 0 {
+            panic!("Encountered a huge page while parsing table attributes");
         }
-    }
 
-    fn execute(self, execute: bool) -> Self {
-        if execute {
-            Self(self.0 & !PA_NXE)
-        } else {
-            Self(self.0 | PA_NXE)
+        if self.0 & PA_P != 0 {
+            table_attr |= TableAttribute::PRESENT;
+        }
+        if self.0 & PA_G != 0 {
+            table_attr |= TableAttribute::GLOBAL;
+        }
+        if self.0 & PA_US != 0 {
+            table_attr |= TableAttribute::USER;
         }
+        if self.0 & PA_A != 0 {
+            table_attr |= TableAttribute::ACCESSED;
+        }
+
+        Some(table_attr)
     }
 
-    fn user(self, user: bool) -> Self {
-        if user {
-            Self(self.0 | PA_US)
-        } else {
-            Self(self.0 & !PA_US)
+    fn as_page_attr(self) -> Option<PageAttribute> {
+        let mut page_attr = PageAttribute::READ;
+
+        if self.0 & PA_P != 0 {
+            page_attr |= PageAttribute::PRESENT;
         }
-    }
 
-    fn accessed(self, accessed: bool) -> Self {
-        if accessed {
-            Self(self.0 | PA_A)
-        } else {
-            Self(self.0 & !PA_A)
+        if self.0 & PA_RW != 0 {
+            page_attr |= PageAttribute::WRITE;
         }
-    }
 
-    fn dirty(self, dirty: bool) -> Self {
-        if dirty {
-            Self(self.0 | PA_D)
-        } else {
-            Self(self.0 & !PA_D)
+        if self.0 & PA_NXE == 0 {
+            page_attr |= PageAttribute::EXECUTE;
         }
-    }
 
-    fn global(self, global: bool) -> Self {
-        if global {
-            Self(self.0 | PA_G)
-        } else {
-            Self(self.0 & !PA_G)
+        if self.0 & PA_US != 0 {
+            page_attr |= PageAttribute::USER;
         }
-    }
 
-    fn copy_on_write(self, cow: bool) -> Self {
-        if cow {
-            Self(self.0 | PA_COW)
-        } else {
-            Self(self.0 & !PA_COW)
+        if self.0 & PA_A != 0 {
+            page_attr |= PageAttribute::ACCESSED;
         }
-    }
 
-    fn mapped(self, mmap: bool) -> Self {
-        if mmap {
-            Self(self.0 | PA_MMAP)
-        } else {
-            Self(self.0 & !PA_MMAP)
+        if self.0 & PA_D != 0 {
+            page_attr |= PageAttribute::DIRTY;
         }
-    }
 
-    fn is_present(&self) -> bool {
-        self.0 & PA_P != 0
-    }
+        if self.0 & PA_G != 0 {
+            page_attr |= PageAttribute::GLOBAL;
+        }
 
-    fn is_write(&self) -> bool {
-        self.0 & PA_RW != 0
-    }
+        if self.0 & PA_COW != 0 {
+            page_attr |= PageAttribute::COPY_ON_WRITE;
+        }
 
-    fn is_execute(&self) -> bool {
-        self.0 & PA_NXE == 0
-    }
+        if self.0 & PA_MMAP != 0 {
+            page_attr |= PageAttribute::MAPPED;
+        }
 
-    fn is_user(&self) -> bool {
-        self.0 & PA_US != 0
-    }
+        if self.0 & PA_ANON != 0 {
+            page_attr |= PageAttribute::ANONYMOUS;
+        }
 
-    fn is_accessed(&self) -> bool {
-        self.0 & PA_A != 0
+        Some(page_attr)
     }
 
-    fn is_dirty(&self) -> bool {
-        self.0 & PA_D != 0
-    }
+    fn from_table_attr(table_attr: TableAttribute) -> Self {
+        let mut raw_attr = PA_RW;
 
-    fn is_global(&self) -> bool {
-        self.0 & PA_G != 0
-    }
+        for attr in table_attr.iter() {
+            match attr {
+                TableAttribute::PRESENT => raw_attr |= PA_P,
+                TableAttribute::GLOBAL => raw_attr |= PA_G,
+                TableAttribute::USER => raw_attr |= PA_US,
+                TableAttribute::ACCESSED => raw_attr |= PA_A,
+                _ => unreachable!("Invalid table attribute"),
+            }
+        }
 
-    fn is_copy_on_write(&self) -> bool {
-        self.0 & PA_COW != 0
-    }
+        Self(raw_attr)
+    }
+
+    fn from_page_attr(page_attr: PageAttribute) -> Self {
+        let mut raw_attr = PA_NXE;
+
+        for attr in page_attr.iter() {
+            match attr {
+                PageAttribute::PRESENT => raw_attr |= PA_P,
+                PageAttribute::READ => {}
+                PageAttribute::WRITE => raw_attr |= PA_RW,
+                PageAttribute::EXECUTE => raw_attr &= !PA_NXE,
+                PageAttribute::USER => raw_attr |= PA_US,
+                PageAttribute::ACCESSED => raw_attr |= PA_A,
+                PageAttribute::DIRTY => raw_attr |= PA_D,
+                PageAttribute::GLOBAL => raw_attr |= PA_G,
+                PageAttribute::COPY_ON_WRITE => raw_attr |= PA_COW,
+                PageAttribute::MAPPED => raw_attr |= PA_MMAP,
+                PageAttribute::ANONYMOUS => raw_attr |= PA_ANON,
+                _ => unreachable!("Invalid page attribute"),
+            }
+        }
 
-    fn is_mapped(&self) -> bool {
-        self.0 & PA_MMAP != 0
+        Self(raw_attr)
     }
 }
 

+ 0 - 1
crates/buddy_allocator/Cargo.toml

@@ -5,5 +5,4 @@ edition = "2024"
 
 [dependencies]
 eonix_mm = { path = "../eonix_mm" }
-eonix_sync = { path = "../eonix_sync" }
 intrusive_list = { path = "../intrusive_list" }

+ 29 - 17
crates/buddy_allocator/src/free_area.rs

@@ -1,29 +1,36 @@
-use core::marker::{Send, Sync};
-use eonix_mm::paging::{PageFlags, RawPage, RawPagePtr};
-use intrusive_list::{container_of, Link};
+use crate::BuddyRawPage;
+use core::marker::{PhantomData, Send, Sync};
+use intrusive_list::Link;
 
-pub struct FreeArea {
+pub struct FreeArea<T> {
     free_list: Link,
     count: usize,
+    _phantom: PhantomData<T>,
 }
 
-unsafe impl Send for FreeArea {}
-unsafe impl Sync for FreeArea {}
+unsafe impl<T> Send for FreeArea<T> {}
+unsafe impl<T> Sync for FreeArea<T> {}
 
-impl FreeArea {
+impl<Raw> FreeArea<Raw>
+where
+    Raw: BuddyRawPage,
+{
     pub const fn new() -> Self {
         Self {
             free_list: Link::new(),
             count: 0,
+            _phantom: PhantomData,
         }
     }
 
-    pub fn get_free_pages(&mut self) -> Option<RawPagePtr> {
+    pub fn get_free_pages(&mut self) -> Option<Raw> {
         self.free_list.next_mut().map(|pages_link| {
             assert_ne!(self.count, 0);
 
-            let pages_ptr = unsafe { container_of!(pages_link, RawPage, link) };
-            let pages_ptr = RawPagePtr::new(pages_ptr);
+            let pages_ptr = unsafe {
+                // SAFETY: Items in `self.free_list` are guaranteed to be of type `Raw`.
+                Raw::from_link(pages_link)
+            };
 
             self.count -= 1;
             pages_link.remove();
@@ -32,16 +39,21 @@ impl FreeArea {
         })
     }
 
-    pub fn add_pages(&mut self, pages_ptr: RawPagePtr) {
+    pub fn add_pages(&mut self, pages_ptr: Raw) {
         self.count += 1;
-        pages_ptr.as_mut().flags.set(PageFlags::FREE);
-        self.free_list.insert(&mut pages_ptr.as_mut().link)
+        pages_ptr.set_free();
+
+        unsafe {
+            self.free_list.insert(pages_ptr.get_link());
+        }
     }
 
-    pub fn del_pages(&mut self, pages_ptr: RawPagePtr) {
-        assert!(self.count >= 1 && pages_ptr.as_ref().flags.has(PageFlags::FREE));
+    pub fn del_pages(&mut self, pages_ptr: Raw) {
+        assert!(self.count >= 1 && pages_ptr.is_free());
         self.count -= 1;
-        pages_ptr.as_mut().flags.clear(PageFlags::FREE);
-        pages_ptr.as_mut().link.remove();
+        pages_ptr.clear_free();
+        unsafe {
+            pages_ptr.get_link().remove();
+        }
     }
 }

+ 47 - 23
crates/buddy_allocator/src/lib.rs

@@ -6,54 +6,78 @@ mod zone;
 use core::sync::atomic::Ordering;
 use eonix_mm::{
     address::PAddr,
-    paging::{PageAlloc, PageFlags, RawPagePtr, PFN},
+    paging::{RawPage, PFN},
 };
-use eonix_sync::Spin;
+use intrusive_list::Link;
 use zone::Zone;
 
-pub use free_area::FreeArea;
-
 const MAX_ORDER: u32 = 10;
 const ZONE_AREAS: usize = const { MAX_ORDER as usize + 1 };
 
-static BUDDY_ALLOCATOR: BuddyAllocator = BuddyAllocator::new();
+pub trait BuddyRawPage: RawPage {
+    /// Get the container raw page struct of the list link.
+    ///
+    /// # Safety
+    /// The caller MUST ensure that the link points to a `RawPage`.
+    unsafe fn from_link(link: &mut Link) -> Self;
+
+    /// Get the list link of the raw page.
+    ///
+    /// # Safety
+    /// The caller MUST ensure that at any time, only one mutable reference
+    /// to the link exists.
+    unsafe fn get_link(&self) -> &mut Link;
+
+    fn set_order(&self, order: u32);
+
+    fn is_buddy(&self) -> bool;
+    fn is_free(&self) -> bool;
+
+    fn set_buddy(&self);
+    fn set_free(&self);
 
-pub struct BuddyAllocator {
-    zone: Spin<Zone<ZONE_AREAS>>,
+    fn clear_buddy(&self);
+    fn clear_free(&self);
 }
 
-impl BuddyAllocator {
-    const fn new() -> Self {
-        Self {
-            zone: Spin::new(Zone::new()),
-        }
+pub struct BuddyAllocator<T>
+where
+    T: BuddyRawPage,
+{
+    zone: Zone<T, ZONE_AREAS>,
+}
+
+impl<T> BuddyAllocator<T>
+where
+    T: BuddyRawPage,
+{
+    pub const fn new() -> Self {
+        Self { zone: Zone::new() }
     }
 
-    pub fn create_pages(start: PAddr, end: PAddr) {
-        BUDDY_ALLOCATOR.zone.lock().create_pages(start, end);
+    pub fn create_pages(&mut self, start: PAddr, end: PAddr) {
+        self.zone.create_pages(start, end);
     }
-}
 
-impl PageAlloc for BuddyAllocator {
-    fn alloc_order(order: u32) -> Option<RawPagePtr> {
-        let pages_ptr = BUDDY_ALLOCATOR.zone.lock().get_free_pages(order);
+    pub fn alloc_order(&mut self, order: u32) -> Option<T> {
+        let pages_ptr = self.zone.get_free_pages(order);
 
         if let Some(pages_ptr) = pages_ptr {
             // SAFETY: Memory order here can be Relaxed is for the same reason as that
             // in the copy constructor of `std::shared_ptr`.
             pages_ptr.refcount().fetch_add(1, Ordering::Relaxed);
-            pages_ptr.flags().clear(PageFlags::FREE);
+            pages_ptr.clear_free();
         }
 
         pages_ptr
     }
 
-    unsafe fn dealloc(page_ptr: RawPagePtr) {
-        BUDDY_ALLOCATOR.zone.lock().free_pages(page_ptr);
+    pub unsafe fn dealloc(&mut self, page_ptr: T) {
+        self.zone.free_pages(page_ptr);
     }
 
-    unsafe fn has_management_over(page_ptr: RawPagePtr) -> bool {
-        !page_ptr.flags().has(PageFlags::FREE) && page_ptr.flags().has(PageFlags::BUDDY)
+    pub fn has_management_over(page_ptr: T) -> bool {
+        !page_ptr.is_free() && page_ptr.is_buddy()
     }
 }
 

+ 32 - 31
crates/buddy_allocator/src/zone.rs

@@ -1,78 +1,82 @@
-use crate::BuddyPFNOps as _;
-
 use super::free_area::FreeArea;
+use crate::{BuddyPFNOps as _, BuddyRawPage};
 use core::sync::atomic::Ordering;
 use eonix_mm::{
     address::{AddrOps as _, PAddr},
-    paging::{PageFlags, RawPagePtr, PFN},
+    paging::PFN,
 };
 
-pub(super) struct Zone<const AREAS: usize> {
-    free_areas: [FreeArea; AREAS],
+pub(super) struct Zone<T, const AREAS: usize> {
+    free_areas: [FreeArea<T>; AREAS],
 }
 
-impl<const AREAS: usize> Zone<AREAS> {
+impl<Raw, const AREAS: usize> Zone<Raw, AREAS>
+where
+    Raw: BuddyRawPage,
+{
     pub const fn new() -> Self {
         Self {
             free_areas: [const { FreeArea::new() }; AREAS],
         }
     }
 
-    pub fn get_free_pages(&mut self, order: u32) -> Option<RawPagePtr> {
+    pub fn get_free_pages(&mut self, order: u32) -> Option<Raw> {
         for current_order in order..AREAS as u32 {
             let pages_ptr = self.free_areas[current_order as usize].get_free_pages();
             let Some(pages_ptr) = pages_ptr else { continue };
 
-            pages_ptr.as_mut().order = order;
+            pages_ptr.set_order(order);
 
             if current_order > order {
                 self.expand(pages_ptr, current_order, order);
             }
-            assert!(pages_ptr.flags().has(PageFlags::PRESENT | PageFlags::FREE));
+            assert!(pages_ptr.is_free() && pages_ptr.is_present());
 
             return Some(pages_ptr);
         }
         None
     }
 
-    fn expand(&mut self, pages_ptr: RawPagePtr, order: u32, target_order: u32) {
+    fn expand(&mut self, pages_ptr: Raw, order: u32, target_order: u32) {
         let mut offset = 1 << order;
+        let pages_pfn = Into::<PFN>::into(pages_ptr);
 
         for order in (target_order..order).rev() {
             offset >>= 1;
-            let split_pages_ptr = pages_ptr.offset(offset);
-            split_pages_ptr.as_mut().order = order;
-            split_pages_ptr.flags().set(PageFlags::BUDDY);
+
+            let split_pages_ptr = Raw::from(pages_pfn + offset);
+            split_pages_ptr.set_order(order);
+            split_pages_ptr.set_buddy();
             self.free_areas[order as usize].add_pages(split_pages_ptr);
         }
     }
 
-    pub fn free_pages(&mut self, mut pages_ptr: RawPagePtr) {
+    pub fn free_pages(&mut self, mut pages_ptr: Raw) {
         assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
 
-        let mut pfn = PFN::from(pages_ptr);
+        let mut pfn = Into::<PFN>::into(pages_ptr);
         let mut current_order = pages_ptr.order();
 
         while current_order < (AREAS - 1) as u32 {
             let buddy_pfn = pfn.buddy_pfn(current_order);
-            let buddy_pages_ptr = RawPagePtr::from(buddy_pfn);
+            let buddy_pages_ptr = Raw::from(buddy_pfn);
 
             if !self.buddy_check(buddy_pages_ptr, current_order) {
                 break;
             }
 
-            pages_ptr.flags().clear(PageFlags::BUDDY);
-            buddy_pages_ptr.flags().clear(PageFlags::BUDDY);
+            pages_ptr.clear_buddy();
+            buddy_pages_ptr.clear_buddy();
             self.free_areas[current_order as usize].del_pages(buddy_pages_ptr);
 
-            pages_ptr = RawPagePtr::from(pfn.combined_pfn(buddy_pfn));
+            pages_ptr = Raw::from(pfn.combined_pfn(buddy_pfn));
             pfn = pfn.combined_pfn(buddy_pfn);
 
-            pages_ptr.flags().set(PageFlags::BUDDY);
+            pages_ptr.set_buddy();
             current_order += 1;
         }
 
-        pages_ptr.as_mut().order = current_order;
+        pages_ptr.set_order(current_order);
         self.free_areas[current_order as usize].add_pages(pages_ptr);
     }
 
@@ -81,18 +85,15 @@ impl<const AREAS: usize> Zone<AREAS> {
     /// - the buddy is valid(present) &&
     /// - the buddy is right now in free_areas &&
     /// - a page and its buddy have the same order &&
-    /// - a page and its buddy are in the same zone.    // check when smp
-    fn buddy_check(&self, pages_ptr: RawPagePtr, order: u32) -> bool {
-        if !pages_ptr.flags().has(PageFlags::PRESENT) {
-            return false;
-        }
-        if !pages_ptr.flags().has(PageFlags::FREE) {
+    /// - a page and its buddy are in the same zone (on smp systems).
+    fn buddy_check(&self, pages_ptr: Raw, order: u32) -> bool {
+        if !pages_ptr.is_present() {
             return false;
         }
-        if pages_ptr.flags().has(PageFlags::LOCAL) {
+        if !pages_ptr.is_free() {
             return false;
         }
-        if pages_ptr.as_ref().order != order {
+        if pages_ptr.order() != order {
             return false;
         }
 
@@ -113,8 +114,8 @@ impl<const AREAS: usize> Zone<AREAS> {
             while start_pfn + order as usize > end_pfn {
                 order -= 1;
             }
-            let page_ptr: RawPagePtr = start_pfn.into();
-            page_ptr.flags().set(PageFlags::BUDDY);
+            let page_ptr = Raw::from(start_pfn);
+            page_ptr.set_buddy();
             self.free_areas[order as usize].add_pages(page_ptr);
             start_pfn = start_pfn + (1 << order) as usize;
         }

+ 1 - 1
crates/eonix_mm/Cargo.toml

@@ -4,4 +4,4 @@ version = "0.1.0"
 edition = "2024"
 
 [dependencies]
-intrusive_list = { path = "../intrusive_list" }
+bitflags = "2.6.0"

+ 1 - 1
crates/eonix_mm/src/page_table.rs

@@ -5,5 +5,5 @@ mod pte_iterator;
 
 pub use page_table::{PageTable, RawPageTable};
 pub use paging_mode::{PageTableLevel, PagingMode};
-pub use pte::{PageAttribute, PTE};
+pub use pte::{PageAttribute, RawAttribute, TableAttribute, PTE};
 pub use pte_iterator::PageTableIterator;

+ 30 - 9
crates/eonix_mm/src/page_table/page_table.rs

@@ -1,12 +1,13 @@
 use super::{
     paging_mode::PageTableLevel,
+    pte::{RawAttribute, TableAttribute},
     pte_iterator::{KernelIterator, UserIterator},
-    PageAttribute, PagingMode, PTE,
+    PagingMode, PTE,
 };
 use crate::{
     address::{PAddr, VRange},
     page_table::PageTableIterator,
-    paging::{Page, PageAccess, PageAlloc, PageBlock},
+    paging::{GlobalPageAlloc, Page, PageAccess, PageAlloc, PageBlock},
 };
 use core::{marker::PhantomData, ptr::NonNull};
 
@@ -41,8 +42,8 @@ where
     A: PageAlloc,
     X: PageAccess,
 {
-    pub fn new<A1: PageAlloc>(kernel_root_table_page: &Page<A1>) -> Self {
-        let new_root_table_page = Page::<A>::alloc();
+    pub fn new_in<A1: PageAlloc>(kernel_root_table_page: &Page<A1>, alloc: A) -> Self {
+        let new_root_table_page = Page::alloc_in(alloc);
         let new_table_data = X::get_ptr_for_page(&new_root_table_page);
         let kernel_table_data = X::get_ptr_for_page(kernel_root_table_page);
 
@@ -58,7 +59,7 @@ where
         };
 
         let level0 = M::LEVELS[0];
-        for idx in 0..level0.max_index() / 2 {
+        for idx in 0..=level0.max_index() / 2 {
             // We consider the first half of the page table as user space.
             // Clear all (potential) user space mappings.
             root_page_table.index_mut(idx).take();
@@ -75,28 +76,32 @@ where
     }
 
     pub fn iter_user(&self, range: VRange) -> impl Iterator<Item = &mut M::Entry> {
+        let alloc = self.root_table_page.allocator();
         let page_table_ptr = X::get_ptr_for_page(&self.root_table_page);
         let root_page_table = unsafe {
             // SAFETY: `page_table_ptr` is a valid pointer to a page table.
             M::RawTable::from_ptr(page_table_ptr)
         };
 
-        PageTableIterator::<M, A, X, UserIterator>::new(root_page_table, range)
+        PageTableIterator::<M, A, X, UserIterator>::new(root_page_table, range, alloc.clone())
     }
 
     pub fn iter_kernel(&self, range: VRange) -> impl Iterator<Item = &mut M::Entry> {
+        let alloc = self.root_table_page.allocator();
         let page_table_ptr = X::get_ptr_for_page(&self.root_table_page);
         let root_page_table = unsafe {
             // SAFETY: `page_table_ptr` is a valid pointer to a page table.
             M::RawTable::from_ptr(page_table_ptr)
         };
 
-        PageTableIterator::<M, A, X, KernelIterator>::new(root_page_table, range)
+        PageTableIterator::<M, A, X, KernelIterator>::new(root_page_table, range, alloc.clone())
     }
 
     fn drop_page_table_recursive(page_table: &Page<A>, levels: &[PageTableLevel]) {
         let [level, remaining_levels @ ..] = levels else { return };
 
+        let alloc = page_table.allocator();
+
         let page_table_ptr = X::get_ptr_for_page(page_table);
         let mut page_table = unsafe {
             // SAFETY: `page_table_ptr` is a valid pointer to a page table.
@@ -105,13 +110,17 @@ where
 
         for pte in (0..=level.max_index()).map(|i| page_table.index_mut(i)) {
             let (pfn, attr) = pte.take();
-            if !attr.is_present() || !attr.is_user() {
+            let Some(attr) = attr.as_table_attr() else {
+                continue;
+            };
+
+            if !attr.contains(TableAttribute::PRESENT | TableAttribute::USER) {
                 continue;
             }
 
             let page_table = unsafe {
                 // SAFETY: We got the pfn from a valid page table entry, so it should be valid.
-                Page::<A>::from_raw(pfn)
+                Page::from_raw_in(pfn, alloc.clone())
             };
 
             Self::drop_page_table_recursive(&page_table, remaining_levels);
@@ -119,6 +128,18 @@ where
     }
 }
 
+impl<'a, M, A, X> PageTable<'a, M, A, X>
+where
+    M: PagingMode,
+    M::Entry: 'a,
+    A: GlobalPageAlloc,
+    X: PageAccess,
+{
+    pub fn new<A1: PageAlloc>(kernel_root_table_page: &Page<A1>) -> Self {
+        Self::new_in(kernel_root_table_page, A::global())
+    }
+}
+
 impl<'a, M, A, X> Drop for PageTable<'a, M, A, X>
 where
     M: PagingMode,

+ 62 - 25
crates/eonix_mm/src/page_table/pte.rs

@@ -1,36 +1,73 @@
 use crate::paging::PFN;
+use bitflags::bitflags;
 
-pub trait PageAttribute: Copy {
-    /// Create a new instance of the attribute with all attributes set to false.
-    fn new() -> Self;
-
-    fn present(self, present: bool) -> Self;
-    fn write(self, write: bool) -> Self;
-    fn execute(self, execute: bool) -> Self;
-    fn user(self, user: bool) -> Self;
-    fn accessed(self, accessed: bool) -> Self;
-    fn dirty(self, dirty: bool) -> Self;
-    fn global(self, global: bool) -> Self;
-    fn copy_on_write(self, cow: bool) -> Self;
-    fn mapped(self, mmap: bool) -> Self;
-
-    fn is_present(&self) -> bool;
-    fn is_write(&self) -> bool;
-    fn is_execute(&self) -> bool;
-    fn is_user(&self) -> bool;
-    fn is_accessed(&self) -> bool;
-    fn is_dirty(&self) -> bool;
-    fn is_global(&self) -> bool;
-    fn is_copy_on_write(&self) -> bool;
-    fn is_mapped(&self) -> bool;
+bitflags! {
+    #[derive(Clone, Copy, PartialEq)]
+    pub struct TableAttribute: usize {
+        const PRESENT = 1;
+        const USER = 2;
+        const ACCESSED = 4;
+        const GLOBAL = 8;
+    }
+
+    #[derive(Clone, Copy, PartialEq)]
+    pub struct PageAttribute: usize {
+        const PRESENT = 1;
+        const READ = 2;
+        const WRITE = 4;
+        const EXECUTE = 8;
+        const USER = 16;
+        const ACCESSED = 32;
+        const DIRTY = 64;
+        const GLOBAL = 128;
+        const COPY_ON_WRITE = 256;
+        const MAPPED = 512;
+        const ANONYMOUS = 1024;
+    }
+}
+
+pub trait RawAttribute: Copy {
+    /// Create a new attribute representing a non-present page.
+    fn null() -> Self;
+
+    /// Interpret the attribute as a page table attribute. Return `None` if it is
+    /// not an attribute for a page table.
+    ///
+    /// # Panic
+    /// The implementor should panic if invalid combinations of flags are present.
+    fn as_table_attr(self) -> Option<TableAttribute>;
+
+    /// Interpret the attribute as a page attribute. Return `None` if it is not
+    /// an attribute for a page.
+    ///
+    /// # Panic
+    /// The implementor should panic if invalid combinations of flags are present.
+    fn as_page_attr(self) -> Option<PageAttribute>;
+
+    /// Convert the attribute to a raw value.
+    ///
+    /// # Panic
+    /// The implementor should panic if invalid combinations of flags are present.
+    fn from_table_attr(table_attr: TableAttribute) -> Self;
+
+    /// Convert the attribute to a raw value.
+    ///
+    /// # Panic
+    /// The implementor should panic if invalid combinations of flags are present.
+    fn from_page_attr(page_attr: PageAttribute) -> Self;
 }
 
 pub trait PTE: Sized {
-    type Attr: PageAttribute;
+    type Attr: RawAttribute;
 
     fn set(&mut self, pfn: PFN, attr: Self::Attr);
     fn get(&self) -> (PFN, Self::Attr);
-    fn take(&mut self) -> (PFN, Self::Attr);
+
+    fn take(&mut self) -> (PFN, Self::Attr) {
+        let pfn_attr = self.get();
+        self.set(PFN::from_val(0), Self::Attr::null());
+        pfn_attr
+    }
 
     fn set_pfn(&mut self, pfn: PFN) {
         self.set(pfn, self.get_attr());

+ 22 - 22
crates/eonix_mm/src/page_table/pte_iterator.rs

@@ -1,4 +1,7 @@
-use super::{PageAttribute as _, PagingMode, RawPageTable as _, PTE};
+use super::{
+    pte::{RawAttribute, TableAttribute},
+    PagingMode, RawPageTable as _, PTE,
+};
 use crate::{
     address::{AddrOps as _, VRange},
     paging::{Page, PageAccess, PageAlloc, PAGE_SIZE},
@@ -9,16 +12,16 @@ pub struct KernelIterator;
 pub struct UserIterator;
 
 pub trait IteratorType<M: PagingMode> {
-    fn page_table_attributes() -> <M::Entry as PTE>::Attr;
+    fn page_table_attributes() -> TableAttribute;
 
-    fn get_page_table<'a, A, X>(pte: &mut M::Entry) -> M::RawTable<'a>
+    fn get_page_table<'a, A, X>(pte: &mut M::Entry, alloc: &A) -> M::RawTable<'a>
     where
         A: PageAlloc,
         X: PageAccess,
     {
-        let attr = pte.get_attr();
+        let attr = pte.get_attr().as_table_attr().expect("Not a page table");
 
-        if attr.is_present() {
+        if attr.contains(TableAttribute::PRESENT) {
             let pfn = pte.get_pfn();
             unsafe {
                 // SAFETY: We are creating a pointer to a page referenced to in
@@ -28,7 +31,7 @@ pub trait IteratorType<M: PagingMode> {
                 M::RawTable::from_ptr(page_table_ptr)
             }
         } else {
-            let page = Page::<A>::alloc();
+            let page = Page::alloc_in(alloc.clone());
             let page_table_ptr = X::get_ptr_for_page(&page);
 
             unsafe {
@@ -36,7 +39,10 @@ pub trait IteratorType<M: PagingMode> {
                 page_table_ptr.write_bytes(0, 1);
             }
 
-            pte.set(page.into_raw(), Self::page_table_attributes());
+            pte.set(
+                page.into_raw(),
+                <M::Entry as PTE>::Attr::from_table_attr(Self::page_table_attributes()),
+            );
 
             unsafe {
                 // SAFETY: `page_table_ptr` is a valid pointer to a page table.
@@ -59,7 +65,8 @@ where
     indicies: [u16; 8],
     tables: [Option<M::RawTable<'a>>; 8],
 
-    _phantom: PhantomData<&'a (A, X, K)>,
+    alloc: A,
+    _phantom: PhantomData<&'a (X, K)>,
 }
 
 impl<'a, M, A, X, K> PageTableIterator<'a, M, A, X, K>
@@ -88,11 +95,11 @@ where
             };
             let parent_table = parent_table.as_mut().expect("Parent table is None");
             let next_pte = parent_table.index_mut(pt_idx);
-            child_table.replace(K::get_page_table::<A, X>(next_pte));
+            child_table.replace(K::get_page_table::<A, X>(next_pte, &self.alloc));
         }
     }
 
-    pub fn new(page_table: M::RawTable<'a>, range: VRange) -> Self {
+    pub fn new(page_table: M::RawTable<'a>, range: VRange, alloc: A) -> Self {
         let start = range.start().floor();
         let end = range.end().ceil();
 
@@ -100,6 +107,7 @@ where
             remaining: (end - start) / PAGE_SIZE,
             indicies: [0; 8],
             tables: [const { None }; 8],
+            alloc,
             _phantom: PhantomData,
         };
 
@@ -157,21 +165,13 @@ where
 }
 
 impl<M: PagingMode> IteratorType<M> for KernelIterator {
-    fn page_table_attributes() -> <M::Entry as PTE>::Attr {
-        <M::Entry as PTE>::Attr::new()
-            .present(true)
-            .write(true)
-            .execute(true)
-            .global(true)
+    fn page_table_attributes() -> TableAttribute {
+        TableAttribute::PRESENT | TableAttribute::GLOBAL
     }
 }
 
 impl<M: PagingMode> IteratorType<M> for UserIterator {
-    fn page_table_attributes() -> <M::Entry as PTE>::Attr {
-        <M::Entry as PTE>::Attr::new()
-            .present(true)
-            .write(true)
-            .execute(true)
-            .user(true)
+    fn page_table_attributes() -> TableAttribute {
+        TableAttribute::PRESENT | TableAttribute::USER
     }
 }

+ 2 - 2
crates/eonix_mm/src/paging.rs

@@ -4,6 +4,6 @@ mod pfn;
 mod raw_page;
 
 pub use page::{Page, PageAccess, PageBlock, PAGE_SIZE, PAGE_SIZE_BITS};
-pub use page_alloc::PageAlloc;
+pub use page_alloc::{GlobalPageAlloc, PageAlloc};
 pub use pfn::PFN;
-pub use raw_page::{PageFlags, RawPage, RawPagePtr};
+pub use raw_page::RawPage;

+ 137 - 50
crates/eonix_mm/src/paging/page.rs

@@ -1,6 +1,6 @@
-use super::{raw_page::RawPagePtr, PageAlloc, PFN};
+use super::{GlobalPageAlloc, PageAlloc, RawPage as _, PFN};
 use crate::address::{AddrRange, PAddr};
-use core::{fmt, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, sync::atomic::Ordering};
+use core::{fmt, mem::ManuallyDrop, ptr::NonNull, sync::atomic::Ordering};
 
 pub const PAGE_SIZE: usize = 4096;
 pub const PAGE_SIZE_BITS: u32 = PAGE_SIZE.trailing_zeros();
@@ -35,66 +35,126 @@ pub trait PageAccess {
 /// A Page allocated in allocator `A`.
 #[derive(PartialEq, Eq, PartialOrd, Ord)]
 pub struct Page<A: PageAlloc> {
-    raw_page: RawPagePtr,
-    _phantom: PhantomData<A>,
+    raw_page: A::RawPage,
+    alloc: A,
 }
 
 unsafe impl<A: PageAlloc> Send for Page<A> {}
 unsafe impl<A: PageAlloc> Sync for Page<A> {}
 
-impl<A: PageAlloc> Page<A> {
+impl<A> Page<A>
+where
+    A: GlobalPageAlloc,
+{
     /// Allocate a page of the given *order*.
     pub fn alloc_order(order: u32) -> Self {
-        Self {
-            raw_page: A::alloc_order(order).expect("Out of memory"),
-            _phantom: PhantomData,
-        }
+        Self::alloc_order_in(order, A::global())
     }
 
     /// Allocate exactly one page.
     pub fn alloc() -> Self {
-        Self {
-            raw_page: A::alloc().expect("Out of memory"),
-            _phantom: PhantomData,
-        }
+        Self::alloc_in(A::global())
     }
 
     /// Allocate a contiguous block of pages that can contain at least `count` pages.
     pub fn alloc_at_least(count: usize) -> Self {
-        Self {
-            raw_page: A::alloc_at_least(count).expect("Out of memory"),
-            _phantom: PhantomData,
-        }
+        Self::alloc_at_least_in(count, A::global())
     }
 
-    /// Whether we are the only owner of the page.
-    pub fn is_exclusive(&self) -> bool {
-        self.raw_page.refcount().load(Ordering::Acquire) == 1
+    /// Acquire the ownership of the page pointed to by `pfn`, leaving `refcount` untouched.
+    ///
+    /// # Safety
+    /// This function is unsafe because it assumes that the caller has ensured that
+    /// `pfn` points to a valid page allocated through `alloc_order()` and that the
+    /// page have not been freed or deallocated yet.
+    ///
+    /// No checks are done. Any violation of this assumption may lead to undefined behavior.
+    pub unsafe fn from_raw_unchecked(pfn: PFN) -> Self {
+        unsafe { Self::from_raw_unchecked_in(pfn, A::global()) }
     }
 
-    /// Returns the *order* of the page, which is the log2 of the number of pages
-    /// contained in the page object.
-    pub fn order(&self) -> u32 {
-        self.raw_page.order()
+    /// Acquire the ownership of the page pointed to by `pfn`, leaving `refcount` untouched.
+    ///
+    /// This function is a safe wrapper around `from_paddr_unchecked()` that does **some sort
+    /// of** checks to ensure that the page is valid and managed by the allocator.
+    ///
+    /// # Panic
+    /// This function will panic if the page is not valid or if the page is not managed by
+    /// the allocator.
+    ///
+    /// # Safety
+    /// This function is unsafe because it assumes that the caller has ensured that
+    /// `pfn` points to an existing page (A.K.A. inside the global page array) and the
+    /// page will not be freed or deallocated during the call.
+    pub unsafe fn from_raw(pfn: PFN) -> Self {
+        unsafe { Self::from_raw_in(pfn, A::global()) }
     }
 
-    /// Returns the total size of the page in bytes.
-    pub fn len(&self) -> usize {
-        1 << (self.order() + PAGE_SIZE_BITS)
+    /// Do some work with the page without touching the reference count with the same
+    /// restrictions as `from_raw_in()`.
+    ///
+    /// # Safety
+    /// Check `from_raw()` for the safety requirements.
+    pub unsafe fn with_raw<F, O>(pfn: PFN, func: F) -> O
+    where
+        F: FnOnce(&Self) -> O,
+    {
+        unsafe { Self::with_raw_in(pfn, func, A::global()) }
+    }
+
+    /// Do some work with the page without touching the reference count with the same
+    /// restrictions as `from_raw_unchecked_in()`.
+    ///
+    /// # Safety
+    /// Check `from_raw_unchecked()` for the safety requirements.
+    pub unsafe fn with_raw_unchecked<F, O>(pfn: PFN, func: F, alloc: A) -> O
+    where
+        F: FnOnce(&Self) -> O,
+    {
+        unsafe { Self::with_raw_unchecked_in(pfn, func, alloc) }
+    }
+}
+
+impl<A> Page<A>
+where
+    A: PageAlloc,
+{
+    /// Allocate a page of the given *order*.
+    pub fn alloc_order_in(order: u32, alloc: A) -> Self {
+        Self {
+            raw_page: alloc.alloc_order(order).expect("Out of memory"),
+            alloc,
+        }
+    }
+
+    /// Allocate exactly one page.
+    pub fn alloc_in(alloc: A) -> Self {
+        Self {
+            raw_page: alloc.alloc().expect("Out of memory"),
+            alloc,
+        }
+    }
+
+    /// Allocate a contiguous block of pages that can contain at least `count` pages.
+    pub fn alloc_at_least_in(count: usize, alloc: A) -> Self {
+        Self {
+            raw_page: alloc.alloc_at_least(count).expect("Out of memory"),
+            alloc,
+        }
     }
 
     /// Acquire the ownership of the page pointed to by `pfn`, leaving `refcount` untouched.
     ///
     /// # Safety
     /// This function is unsafe because it assumes that the caller has ensured that
-    /// `pfn` points to a valid page allocated through `alloc_order()` and that the
-    /// page have not been freed or deallocated yet.
+    /// `pfn` points to a valid page managed by `alloc` and that the page have not
+    /// been freed or deallocated yet.
     ///
     /// No checks are done. Any violation of this assumption may lead to undefined behavior.
-    pub unsafe fn from_raw_unchecked(pfn: PFN) -> Self {
+    pub unsafe fn from_raw_unchecked_in(pfn: PFN, alloc: A) -> Self {
         Self {
-            raw_page: RawPagePtr::from(pfn),
-            _phantom: PhantomData,
+            raw_page: A::RawPage::from(pfn),
+            alloc,
         }
     }
 
@@ -111,47 +171,63 @@ impl<A: PageAlloc> Page<A> {
     /// This function is unsafe because it assumes that the caller has ensured that
     /// `pfn` points to an existing page (A.K.A. inside the global page array) and the
     /// page will not be freed or deallocated during the call.
-    pub unsafe fn from_raw(pfn: PFN) -> Self {
+    pub unsafe fn from_raw_in(pfn: PFN, alloc: A) -> Self {
         unsafe {
             // SAFETY: The caller guarantees that the page is inside the global page array.
-            assert!(A::has_management_over(RawPagePtr::from(pfn)));
+            assert!(alloc.has_management_over(A::RawPage::from(pfn)));
 
             // SAFETY: We've checked that the validity of the page. And the caller guarantees
             //         that the page will not be freed or deallocated during the call.
-            Self::from_raw_unchecked(pfn)
+            Self::from_raw_unchecked_in(pfn, alloc)
         }
     }
 
     /// Do some work with the page without touching the reference count with the same
-    /// restrictions as `from_raw()`.
+    /// restrictions as `from_raw_in()`.
     ///
     /// # Safety
-    /// Check `from_raw()` for the safety requirements.
-    pub unsafe fn with_raw<F, O>(pfn: PFN, func: F) -> O
+    /// Check `from_raw_in()` for the safety requirements.
+    pub unsafe fn with_raw_in<F, O>(pfn: PFN, func: F, alloc: A) -> O
     where
         F: FnOnce(&Self) -> O,
     {
         unsafe {
-            let me = ManuallyDrop::new(Self::from_raw(pfn));
+            let me = ManuallyDrop::new(Self::from_raw_in(pfn, alloc));
             func(&me)
         }
     }
 
     /// Do some work with the page without touching the reference count with the same
-    /// restrictions as `from_raw_unchecked()`.
+    /// restrictions as `from_raw_unchecked_in()`.
     ///
     /// # Safety
-    /// Check `from_raw_unchecked()` for the safety requirements.
-    pub unsafe fn with_raw_unchecked<F, O>(pfn: PFN, func: F) -> O
+    /// Check `from_raw_unchecked_in()` for the safety requirements.
+    pub unsafe fn with_raw_unchecked_in<F, O>(pfn: PFN, func: F, alloc: A) -> O
     where
         F: FnOnce(&Self) -> O,
     {
         unsafe {
-            let me = ManuallyDrop::new(Self::from_raw_unchecked(pfn));
+            let me = ManuallyDrop::new(Self::from_raw_unchecked_in(pfn, alloc));
             func(&me)
         }
     }
 
+    /// Whether we are the only owner of the page.
+    pub fn is_exclusive(&self) -> bool {
+        self.raw_page.refcount().load(Ordering::Acquire) == 1
+    }
+
+    /// Returns the *order* of the page, which is the log2 of the number of pages
+    /// contained in the page object.
+    pub fn order(&self) -> u32 {
+        self.raw_page.order()
+    }
+
+    /// Returns the total size of the page in bytes.
+    pub fn len(&self) -> usize {
+        1 << (self.order() + PAGE_SIZE_BITS)
+    }
+
     /// Consumes the `Page` and returns the physical frame number without dropping
     /// the reference count the page holds.
     pub fn into_raw(self) -> PFN {
@@ -162,7 +238,7 @@ impl<A: PageAlloc> Page<A> {
     /// Returns the physical frame number of the page, which is aligned with the
     /// page size and valid.
     pub fn pfn(&self) -> PFN {
-        PFN::from(self.raw_page)
+        Into::<PFN>::into(self.raw_page)
     }
 
     /// Returns the start physical address of the page, which is guaranteed to be
@@ -176,9 +252,17 @@ impl<A: PageAlloc> Page<A> {
     pub fn range(&self) -> AddrRange<PAddr> {
         AddrRange::from(self.start()).grow(self.len())
     }
+
+    /// Get the allocator that manages this page.
+    pub fn allocator(&self) -> &A {
+        &self.alloc
+    }
 }
 
-impl<A: PageAlloc> Clone for Page<A> {
+impl<A> Clone for Page<A>
+where
+    A: PageAlloc,
+{
     fn clone(&self) -> Self {
         // SAFETY: Memory order here can be Relaxed is for the same reason as that
         // in the copy constructor of `std::shared_ptr`.
@@ -186,21 +270,24 @@ impl<A: PageAlloc> Clone for Page<A> {
 
         Self {
             raw_page: self.raw_page,
-            _phantom: PhantomData,
+            alloc: self.alloc.clone(),
         }
     }
 }
 
-impl<A: PageAlloc> Drop for Page<A> {
+impl<A> Drop for Page<A>
+where
+    A: PageAlloc,
+{
     fn drop(&mut self) {
         match self.raw_page.refcount().fetch_sub(1, Ordering::AcqRel) {
             0 => panic!("Refcount for an in-use page is 0"),
             1 => unsafe {
                 // SAFETY: `self.raw_page` points to a valid page inside the global page array.
-                assert!(A::has_management_over(self.raw_page));
+                assert!(self.alloc.has_management_over(self.raw_page));
 
                 // SAFETY: `self.raw_page` is managed by the allocator and we're dropping the page.
-                A::dealloc(self.raw_page)
+                self.alloc.dealloc(self.raw_page)
             },
             _ => {}
         }
@@ -212,7 +299,7 @@ impl<A: PageAlloc> fmt::Debug for Page<A> {
         write!(
             f,
             "Page({:?}, order={})",
-            usize::from(PFN::from(self.raw_page)),
+            Into::<PFN>::into(self.raw_page),
             self.order()
         )
     }

+ 46 - 15
crates/eonix_mm/src/paging/page_alloc.rs

@@ -1,31 +1,62 @@
-use super::raw_page::RawPagePtr;
+use super::RawPage;
+
+/// A trait for allocating and deallocating pages of memory.
+///
+/// Note that the instances of this trait should provide pointer-like or reference-like
+/// behavior, meaning that the allocators are to be passed around by value and stored in
+/// managed data structures. This is because the allocator may be used to deallocate the
+/// pages it allocates.
+pub trait PageAlloc: Clone {
+    type RawPage: RawPage;
 
-pub trait PageAlloc: Sized {
     /// Allocate a page of the given *order*.
-    fn alloc_order(order: u32) -> Option<RawPagePtr>;
+    fn alloc_order(&self, order: u32) -> Option<Self::RawPage>;
 
     /// Allocate exactly one page.
-    fn alloc() -> Option<RawPagePtr> {
-        Self::alloc_order(0)
+    fn alloc(&self) -> Option<Self::RawPage> {
+        self.alloc_order(0)
     }
 
     /// Allocate a contiguous block of pages that can contain at least `count` pages.
-    fn alloc_at_least(count: usize) -> Option<RawPagePtr> {
+    fn alloc_at_least(&self, count: usize) -> Option<Self::RawPage> {
         let order = count.next_power_of_two().trailing_zeros();
-        Self::alloc_order(order)
+        self.alloc_order(order)
     }
 
     /// Deallocate a page.
     ///
     /// # Safety
-    /// This function is unsafe because it assumes that the caller has ensured that
-    /// `page` is allocated in this allocator and never used after this call.
-    unsafe fn dealloc(page_ptr: RawPagePtr);
+    /// This function is unsafe because it assumes that the caller MUST ensure that
+    /// `raw_page` is allocated in this allocator and never used after this call.
+    unsafe fn dealloc(&self, raw_page: Self::RawPage);
 
     /// Check whether the page is allocated and managed by the allocator.
-    ///
-    /// # Safety
-    /// This function is unsafe because it assumes that the caller has ensured that
-    /// `page_ptr` points to a raw page inside the global page array.
-    unsafe fn has_management_over(page_ptr: RawPagePtr) -> bool;
+    fn has_management_over(&self, page_ptr: Self::RawPage) -> bool;
+}
+
+/// A trait for global page allocators.
+///
+/// Global means that we can get an instance of the allocator from anywhere in the kernel.
+pub trait GlobalPageAlloc: PageAlloc + 'static {
+    /// Get the global page allocator.
+    fn global() -> Self;
+}
+
+impl<'a, A> PageAlloc for &'a A
+where
+    A: PageAlloc,
+{
+    type RawPage = A::RawPage;
+
+    fn alloc_order(&self, order: u32) -> Option<Self::RawPage> {
+        (*self).alloc_order(order)
+    }
+
+    unsafe fn dealloc(&self, raw_page: Self::RawPage) {
+        unsafe { (*self).dealloc(raw_page) }
+    }
+
+    fn has_management_over(&self, raw_page: Self::RawPage) -> bool {
+        (*self).has_management_over(raw_page)
+    }
 }

+ 7 - 93
crates/eonix_mm/src/paging/raw_page.rs

@@ -1,97 +1,11 @@
 use super::PFN;
-use core::{
-    ptr::NonNull,
-    sync::atomic::{AtomicU32, AtomicUsize, Ordering},
-};
-use intrusive_list::Link;
+use core::sync::atomic::AtomicUsize;
 
-const PAGE_ARRAY: NonNull<RawPage> =
-    unsafe { NonNull::new_unchecked(0xffffff8040000000 as *mut _) };
+/// A `RawPage` represents a page of memory in the kernel. It is a low-level
+/// representation of a page that is used by the kernel to manage memory.
+pub trait RawPage: Clone + Copy + From<PFN> + Into<PFN> {
+    fn order(&self) -> u32;
+    fn refcount(&self) -> &AtomicUsize;
 
-pub struct PageFlags(AtomicU32);
-
-pub struct RawPage {
-    /// This can be used for LRU page swap in the future.
-    ///
-    /// Now only used for free page links in the buddy system.
-    pub link: Link,
-    /// # Safety
-    /// This field is only used in buddy system and is protected by the global lock.
-    pub order: u32,
-    pub flags: PageFlags,
-    pub refcount: AtomicUsize,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub struct RawPagePtr(NonNull<RawPage>);
-
-impl PageFlags {
-    pub const PRESENT: u32 = 1 << 0;
-    // pub const LOCKED: u32 = 1 << 1;
-    pub const BUDDY: u32 = 1 << 2;
-    // pub const SLAB: u32 = 1 << 3;
-    // pub const DIRTY: u32 = 1 << 4;
-    pub const FREE: u32 = 1 << 5;
-    pub const LOCAL: u32 = 1 << 6;
-
-    pub fn has(&self, flag: u32) -> bool {
-        (self.0.load(Ordering::Relaxed) & flag) == flag
-    }
-
-    pub fn set(&self, flag: u32) {
-        self.0.fetch_or(flag, Ordering::Relaxed);
-    }
-
-    pub fn clear(&self, flag: u32) {
-        self.0.fetch_and(!flag, Ordering::Relaxed);
-    }
-}
-
-impl RawPagePtr {
-    pub const fn new(ptr: NonNull<RawPage>) -> Self {
-        Self(ptr)
-    }
-
-    pub const fn as_ptr(self) -> *mut RawPage {
-        self.0.as_ptr()
-    }
-
-    pub const fn as_ref<'a>(self) -> &'a RawPage {
-        unsafe { &*self.as_ptr() }
-    }
-
-    pub const fn as_mut<'a>(self) -> &'a mut RawPage {
-        unsafe { &mut *self.as_ptr() }
-    }
-
-    pub const fn order(&self) -> u32 {
-        self.as_ref().order
-    }
-
-    pub const fn flags(&self) -> &PageFlags {
-        &self.as_ref().flags
-    }
-
-    pub const fn refcount(&self) -> &AtomicUsize {
-        &self.as_ref().refcount
-    }
-
-    pub const fn offset(&self, count: usize) -> Self {
-        let new_raw_ptr = unsafe { self.0.add(count) };
-        Self::new(new_raw_ptr)
-    }
-}
-
-impl From<RawPagePtr> for PFN {
-    fn from(value: RawPagePtr) -> Self {
-        let idx = unsafe { value.as_ptr().offset_from(PAGE_ARRAY.as_ptr()) as usize };
-        Self::from(idx)
-    }
-}
-
-impl From<PFN> for RawPagePtr {
-    fn from(pfn: PFN) -> Self {
-        let raw_page_ptr = unsafe { PAGE_ARRAY.add(usize::from(pfn)) };
-        Self::new(raw_page_ptr)
-    }
+    fn is_present(&self) -> bool;
 }

+ 36 - 0
crates/intrusive_list/src/lib.rs

@@ -2,11 +2,47 @@
 
 use core::ptr::NonNull;
 
+pub struct List {
+    head: Link,
+    count: usize,
+}
+
 pub struct Link {
     prev: Option<NonNull<Link>>,
     next: Option<NonNull<Link>>,
 }
 
+impl List {
+    pub const fn new() -> Self {
+        Self {
+            head: Link::new(),
+            count: 0,
+        }
+    }
+
+    pub const fn count(&self) -> usize {
+        self.count
+    }
+
+    pub fn insert(&mut self, node: &mut Link) {
+        self.head.insert(node);
+        self.count += 1;
+    }
+
+    pub fn remove(&mut self, node: &mut Link) {
+        node.remove();
+        self.count -= 1;
+    }
+
+    pub fn pop(&mut self) -> Option<&mut Link> {
+        self.head.next_mut().map(|node| {
+            self.count -= 1;
+            node.remove();
+            node
+        })
+    }
+}
+
 impl Link {
     pub const fn new() -> Self {
         Self {

+ 0 - 6
src/fs/procfs.rs

@@ -20,12 +20,6 @@ use eonix_runtime::task::Task;
 use eonix_sync::{AsProof as _, AsProofMut as _, LazyLock, Locked};
 use itertools::Itertools;
 
-fn split_len_offset(data: &[u8], len: usize, offset: usize) -> Option<&[u8]> {
-    let real_data = data.split_at_checked(len).map(|(data, _)| data)?;
-
-    real_data.split_at_checked(offset).map(|(_, data)| data)
-}
-
 #[allow(dead_code)]
 pub trait ProcFsFile: Send + Sync {
     fn can_read(&self) -> bool {

+ 0 - 61
src/intrusive_list.rs

@@ -1,61 +0,0 @@
-use core::ptr::NonNull;
-
-pub struct Link {
-    prev: Option<NonNull<Link>>,
-    next: Option<NonNull<Link>>,
-}
-
-#[allow(dead_code)]
-impl Link {
-    pub const fn new() -> Self {
-        Self {
-            prev: None,
-            next: None,
-        }
-    }
-
-    pub fn insert(&mut self, node: &mut Self) {
-        unsafe {
-            let insert_node = NonNull::new(node as *mut Self);
-            if let Some(next) = self.next {
-                (*next.as_ptr()).prev = insert_node;
-            }
-            node.next = self.next;
-            node.prev = NonNull::new(self as *mut Self);
-            self.next = insert_node;
-        }
-    }
-
-    pub fn remove(&mut self) {
-        if let Some(next) = self.next {
-            unsafe { (*next.as_ptr()).prev = self.prev };
-        }
-
-        if let Some(prev) = self.prev {
-            unsafe { (*prev.as_ptr()).next = self.next };
-        }
-
-        self.prev = None;
-        self.next = None;
-    }
-
-    pub fn next(&self) -> Option<&Self> {
-        self.next.map(|node| unsafe { &*node.as_ptr() })
-    }
-
-    pub fn next_mut(&mut self) -> Option<&mut Self> {
-        self.next.map(|node| unsafe { &mut *node.as_ptr() })
-    }
-}
-
-#[macro_export]
-macro_rules! container_of {
-    ($ptr:expr, $type:ty, $($f:tt)*) => {{
-        let ptr = $ptr as *const _ as *const u8;
-        let offset: usize = ::core::mem::offset_of!($type, $($f)*);
-        ptr.sub(offset) as *mut $type
-    }}
-}
-
-#[allow(unused_imports)]
-pub use container_of;

+ 2 - 3
src/kernel/cpu.rs

@@ -1,6 +1,5 @@
-use super::mem::AsMemoryBlock;
+use super::mem::{AsMemoryBlock, GlobalPageAlloc};
 use arch::{PercpuArea, CPU};
-use buddy_allocator::BuddyAllocator;
 use core::{alloc::Layout, pin::Pin, ptr::NonNull};
 use eonix_mm::paging::Page;
 use eonix_sync::LazyLock;
@@ -18,7 +17,7 @@ pub unsafe fn local_cpu() -> Pin<&'static mut CPU> {
 pub fn percpu_allocate(layout: Layout) -> NonNull<u8> {
     // TODO: Use page size defined in `arch`.
     let page_count = layout.size().div_ceil(arch::PAGE_SIZE);
-    let page = Page::<BuddyAllocator>::alloc_at_least(page_count);
+    let page = Page::alloc_at_least_in(page_count, GlobalPageAlloc::buddy_alloc());
     let page_data = page.as_memblk().as_byte_ptr();
     core::mem::forget(page);
 

+ 1 - 0
src/kernel/mem.rs

@@ -9,4 +9,5 @@ mod page_alloc;
 pub use access::{AsMemoryBlock, MemoryBlock, PhysAccess};
 pub(self) use mm_area::MMArea;
 pub use mm_list::{handle_page_fault, FileMapping, MMList, Mapping, Permission};
+pub use page_alloc::GlobalPageAlloc;
 pub use paging::{Page, PageBuffer};

+ 26 - 18
src/kernel/mem/mm_area.rs

@@ -1,12 +1,12 @@
+use super::mm_list::EMPTY_PAGE;
 use super::paging::AllocZeroed as _;
 use super::{AsMemoryBlock, Mapping, Page, Permission};
 use crate::io::ByteBuffer;
 use crate::KResult;
 use core::{borrow::Borrow, cell::UnsafeCell, cmp::Ordering};
 use eonix_mm::address::{AddrOps as _, VAddr, VRange};
-use eonix_mm::page_table::{PageAttribute, PTE};
+use eonix_mm::page_table::{PageAttribute, RawAttribute, PTE};
 use eonix_mm::paging::PFN;
-use super::mm_list::EMPTY_PAGE;
 
 #[derive(Debug)]
 pub struct MMArea {
@@ -81,25 +81,28 @@ impl MMArea {
 
     /// # Return
     /// Whether the whole handling process is done.
-    pub fn handle_cow(&self, pte: &mut impl PTE) -> bool {
-        let mut page_attr = pte.get_attr();
+    pub fn handle_cow<E>(&self, pte: &mut E) -> bool
+    where
+        E: PTE,
+    {
+        let mut page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
         let pfn = pte.get_pfn();
 
-        page_attr = page_attr.copy_on_write(false);
-        page_attr = page_attr.write(self.permission.write);
+        page_attr.remove(PageAttribute::COPY_ON_WRITE);
+        page_attr.set(PageAttribute::WRITE, self.permission.write);
 
         let page = unsafe { Page::from_raw(pfn) };
         if page.is_exclusive() {
             // SAFETY: This is actually safe. If we read `1` here and we have `MMList` lock
             // held, there couldn't be neither other processes sharing the page, nor other
             // threads making the page COW at the same time.
-            pte.set_attr(page_attr);
+            pte.set_attr(E::Attr::from_page_attr(page_attr));
             core::mem::forget(page);
             return true;
         }
 
         let new_page;
-        if is_anonymous(pfn) {
+        if is_empty_page(pfn) {
             new_page = Page::zeroed();
         } else {
             new_page = Page::alloc();
@@ -115,18 +118,21 @@ impl MMArea {
             };
         }
 
-        page_attr = page_attr.accessed(false);
+        page_attr.remove(PageAttribute::ACCESSED);
 
-        pte.set(new_page.into_raw(), page_attr);
+        pte.set(new_page.into_raw(), E::Attr::from_page_attr(page_attr));
 
         false
     }
 
     /// # Arguments
     /// * `offset`: The offset from the start of the mapping, aligned to 4KB boundary.
-    pub fn handle_mmap(&self, pte: &mut impl PTE, offset: usize) -> KResult<()> {
+    pub fn handle_mmap<E>(&self, pte: &mut E, offset: usize) -> KResult<()>
+    where
+        E: PTE,
+    {
         // TODO: Implement shared mapping
-        let mut page_attr = pte.get_attr();
+        let mut page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
         let pfn = pte.get_pfn();
 
         match &self.mapping {
@@ -155,19 +161,21 @@ impl MMArea {
             _ => panic!("Anonymous mapping should not be PA_MMAP"),
         }
 
-        page_attr = page_attr.present(true).mapped(false);
-        pte.set_attr(page_attr);
+        page_attr.insert(PageAttribute::PRESENT);
+        page_attr.remove(PageAttribute::MAPPED);
+
+        pte.set_attr(E::Attr::from_page_attr(page_attr));
         Ok(())
     }
 
     pub fn handle(&self, pte: &mut impl PTE, offset: usize) -> KResult<()> {
-        let page_attr = pte.get_attr();
+        let page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
 
-        if page_attr.is_copy_on_write() {
+        if page_attr.contains(PageAttribute::COPY_ON_WRITE) {
             self.handle_cow(pte);
         }
 
-        if page_attr.is_mapped() {
+        if page_attr.contains(PageAttribute::MAPPED) {
             self.handle_mmap(pte, offset)?;
         }
 
@@ -176,7 +184,7 @@ impl MMArea {
 }
 
 /// check pfn with EMPTY_PAGE's pfn
-fn is_anonymous(pfn: PFN) -> bool {
+fn is_empty_page(pfn: PFN) -> bool {
     let empty_pfn = EMPTY_PAGE.pfn();
     pfn == empty_pfn
 }

+ 22 - 19
src/kernel/mem/mm_list.rs

@@ -13,11 +13,11 @@ use bindings::{EEXIST, EFAULT, EINVAL, ENOMEM};
 use core::fmt;
 use core::sync::atomic::{AtomicUsize, Ordering};
 use eonix_mm::address::{Addr as _, PAddr};
-use eonix_mm::page_table::PagingMode;
+use eonix_mm::page_table::{PageAttribute, PagingMode};
 use eonix_mm::paging::PFN;
 use eonix_mm::{
     address::{AddrOps as _, VAddr, VRange},
-    page_table::{PageAttribute, PageTable, PTE},
+    page_table::{PageTable, RawAttribute, PTE},
     paging::PAGE_SIZE,
 };
 use eonix_runtime::task::Task;
@@ -587,41 +587,44 @@ where
     fn set_anonymous(&mut self, execute: bool) {
         // Writable flag is set during page fault handling while executable flag is
         // preserved across page faults, so we set executable flag now.
-        let attr = <Self as PTE>::Attr::new()
-            .present(true)
-            .user(true)
-            .copy_on_write(true)
-            .execute(execute);
+        let mut attr = PageAttribute::PRESENT | PageAttribute::USER | PageAttribute::COPY_ON_WRITE;
+        attr.set(PageAttribute::EXECUTE, execute);
 
-        self.set(EMPTY_PAGE.clone().into_raw(), attr);
+        self.set(EMPTY_PAGE.clone().into_raw(), T::Attr::from_page_attr(attr));
     }
 
     fn set_mapped(&mut self, execute: bool) {
         // Writable flag is set during page fault handling while executable flag is
         // preserved across page faults, so we set executable flag now.
-        let attr = <Self as PTE>::Attr::new()
-            .user(true)
-            .copy_on_write(true)
-            .mapped(true)
-            .execute(execute);
+        let mut attr = PageAttribute::MAPPED | PageAttribute::USER | PageAttribute::COPY_ON_WRITE;
+        attr.set(PageAttribute::EXECUTE, execute);
 
-        self.set(EMPTY_PAGE.clone().into_raw(), attr);
+        self.set(EMPTY_PAGE.clone().into_raw(), T::Attr::from_page_attr(attr));
     }
 
     fn set_copy_on_write(&mut self, from: &mut Self) {
-        let mut from_attr = from.get_attr();
-        if !from_attr.is_present() {
+        let mut from_attr = from
+            .get_attr()
+            .as_page_attr()
+            .expect("Not a page attribute");
+
+        if !from_attr.contains(PageAttribute::PRESENT) {
             return;
         }
 
-        from_attr = from_attr.write(false).copy_on_write(true);
+        from_attr.remove(PageAttribute::WRITE);
+        from_attr.insert(PageAttribute::COPY_ON_WRITE);
 
         let pfn = unsafe {
             // SAFETY: We get the pfn from a valid page table entry, so it should be valid as well.
             Page::with_raw(from.get_pfn(), |page| page.clone().into_raw())
         };
 
-        self.set(pfn, from_attr.accessed(false));
-        from.set_attr(from_attr);
+        self.set(
+            pfn,
+            T::Attr::from_page_attr(from_attr & !PageAttribute::ACCESSED),
+        );
+
+        from.set_attr(T::Attr::from_page_attr(from_attr));
     }
 }

+ 100 - 61
src/kernel/mem/page_alloc.rs

@@ -1,26 +1,38 @@
+mod raw_page;
+
 use super::{paging::AllocZeroed as _, Page};
-use buddy_allocator::{BuddyAllocator, FreeArea as BuddyFreeArea};
+use buddy_allocator::{BuddyAllocator, BuddyRawPage as _};
 use core::{ptr::NonNull, sync::atomic::Ordering};
 use eonix_mm::{
     address::{AddrOps as _, PAddr},
-    paging::{PageAlloc, PageFlags, RawPagePtr, PFN},
+    paging::{GlobalPageAlloc as GlobalPageAllocTrait, PageAlloc, PFN},
 };
+use eonix_sync::Spin;
+use intrusive_list::List;
+use raw_page::{PageFlags, RawPage, RawPagePtr};
 
 const COSTLY_ORDER: u32 = 3;
 const BATCH_SIZE: u32 = 64;
 
+static BUDDY_ALLOC: Spin<BuddyAllocator<RawPagePtr>> = Spin::new(BuddyAllocator::new());
+
 #[arch::define_percpu]
 static PERCPU_PAGE_ALLOC: PerCpuPageAlloc = PerCpuPageAlloc::new();
 
+#[derive(Clone)]
 pub struct NoAlloc;
 
+#[derive(Clone)]
 pub struct GlobalPageAlloc;
 
+#[derive(Clone)]
+pub struct BuddyPageAlloc;
+
 struct PerCpuPageAlloc {
     batch: u32,
     // TODO: might be used in the future.
     // high: u32,
-    free_areas: [BuddyFreeArea; COSTLY_ORDER as usize + 1],
+    free_areas: [List; COSTLY_ORDER as usize + 1],
 }
 
 impl PerCpuPageAlloc {
@@ -28,107 +40,133 @@ impl PerCpuPageAlloc {
         Self {
             batch: BATCH_SIZE,
             // high: 0,
-            free_areas: [const { BuddyFreeArea::new() }; COSTLY_ORDER as usize + 1],
+            free_areas: [const { List::new() }; COSTLY_ORDER as usize + 1],
         }
     }
 
-    fn do_alloc_order(&mut self, order: u32) -> Option<RawPagePtr> {
-        assert!(order <= COSTLY_ORDER);
+    fn insert_free_pages(&mut self, pages_ptr: RawPagePtr, order: u32) {
         let free_area = &mut self.free_areas[order as usize];
+        free_area.insert(unsafe { pages_ptr.get_link() });
+    }
 
-        let mut page_ptr = free_area.get_free_pages();
-
-        if page_ptr.is_none() {
-            let batch = self.batch >> order;
-            for _ in 0..batch {
-                if let Some(pages_ptr) = BuddyAllocator::alloc_order(order) {
-                    pages_ptr.flags().set(PageFlags::LOCAL);
-                    free_area.add_pages(pages_ptr);
-                } else {
-                    break;
-                };
-            }
+    fn get_free_pages(&mut self, order: u32) -> Option<RawPagePtr> {
+        let free_area = &mut self.free_areas[order as usize];
+        free_area.pop().map(|node| unsafe {
+            // SAFETY: `node` is a valid pointer to a `Link` that is not used by anyone.
+            RawPagePtr::from_link(node)
+        })
+    }
+
+    fn alloc_order(&mut self, order: u32) -> Option<RawPagePtr> {
+        assert!(order <= COSTLY_ORDER);
+        if let Some(pages) = self.get_free_pages(order) {
+            return Some(pages);
+        }
 
-            page_ptr = free_area.get_free_pages();
+        let batch = self.batch >> order;
+        for _ in 0..batch {
+            if let Some(pages_ptr) = BUDDY_ALLOC.lock().alloc_order(order) {
+                pages_ptr.flags().set(PageFlags::LOCAL);
+                self.insert_free_pages(pages_ptr, order);
+            } else {
+                break;
+            };
         }
 
-        page_ptr.inspect(|page_ptr| page_ptr.flags().clear(PageFlags::FREE))
+        self.get_free_pages(order)
     }
 
     fn free_pages(&mut self, pages_ptr: RawPagePtr, order: u32) {
         assert_eq!(pages_ptr.order(), order);
         assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
 
-        // TODO: Temporary workaround here.
         pages_ptr.refcount().store(1, Ordering::Relaxed);
-        self.free_areas[order as usize].add_pages(pages_ptr);
+        self.insert_free_pages(pages_ptr, order);
+    }
+}
+
+impl GlobalPageAlloc {
+    pub const fn buddy_alloc() -> BuddyPageAlloc {
+        BuddyPageAlloc
     }
 }
 
 impl PageAlloc for GlobalPageAlloc {
-    fn alloc_order(order: u32) -> Option<RawPagePtr> {
+    type RawPage = RawPagePtr;
+
+    fn alloc_order(&self, order: u32) -> Option<RawPagePtr> {
         if order > COSTLY_ORDER {
-            BuddyAllocator::alloc_order(order)
+            BUDDY_ALLOC.lock().alloc_order(order)
         } else {
-            PerCpuPageAlloc::alloc_order(order)
+            unsafe {
+                eonix_preempt::disable();
+                let page_ptr = PERCPU_PAGE_ALLOC.as_mut().alloc_order(order);
+                eonix_preempt::enable();
+                page_ptr
+            }
         }
     }
 
-    unsafe fn dealloc(page_ptr: RawPagePtr) {
+    unsafe fn dealloc(&self, page_ptr: RawPagePtr) {
         if page_ptr.order() > COSTLY_ORDER {
-            BuddyAllocator::dealloc(page_ptr);
+            BUDDY_ALLOC.lock().dealloc(page_ptr);
         } else {
-            PerCpuPageAlloc::dealloc(page_ptr);
+            let order = page_ptr.order();
+            unsafe {
+                eonix_preempt::disable();
+                PERCPU_PAGE_ALLOC.as_mut().free_pages(page_ptr, order);
+                eonix_preempt::enable();
+            }
         }
     }
 
-    unsafe fn has_management_over(page_ptr: RawPagePtr) -> bool {
-        if page_ptr.order() > COSTLY_ORDER {
-            BuddyAllocator::has_management_over(page_ptr)
-        } else {
-            PerCpuPageAlloc::has_management_over(page_ptr)
-        }
+    fn has_management_over(&self, page_ptr: RawPagePtr) -> bool {
+        BuddyAllocator::has_management_over(page_ptr)
+            && (page_ptr.order() > COSTLY_ORDER || page_ptr.flags().has(PageFlags::LOCAL))
     }
 }
 
 impl PageAlloc for NoAlloc {
-    fn alloc_order(_order: u32) -> Option<RawPagePtr> {
+    type RawPage = RawPagePtr;
+
+    fn alloc_order(&self, _order: u32) -> Option<RawPagePtr> {
         panic!("NoAlloc cannot allocate pages");
     }
 
-    unsafe fn dealloc(_: RawPagePtr) {
+    unsafe fn dealloc(&self, _: RawPagePtr) {
         panic!("NoAlloc cannot deallocate pages");
     }
 
-    unsafe fn has_management_over(_: RawPagePtr) -> bool {
+    fn has_management_over(&self, _: RawPagePtr) -> bool {
         true
     }
 }
 
-impl PageAlloc for PerCpuPageAlloc {
-    fn alloc_order(order: u32) -> Option<RawPagePtr> {
-        let page_ptr;
-        unsafe {
-            eonix_preempt::disable();
-            page_ptr = PERCPU_PAGE_ALLOC.as_mut().do_alloc_order(order);
-            eonix_preempt::enable();
-        }
+impl GlobalPageAllocTrait for GlobalPageAlloc {
+    fn global() -> Self {
+        GlobalPageAlloc
+    }
+}
 
-        page_ptr
+impl GlobalPageAllocTrait for NoAlloc {
+    fn global() -> Self {
+        NoAlloc
     }
+}
 
-    unsafe fn dealloc(page_ptr: RawPagePtr) {
-        let order = page_ptr.order();
+impl PageAlloc for BuddyPageAlloc {
+    type RawPage = RawPagePtr;
 
-        unsafe {
-            eonix_preempt::disable();
-            PERCPU_PAGE_ALLOC.as_mut().free_pages(page_ptr, order);
-            eonix_preempt::enable();
-        }
+    fn alloc_order(&self, order: u32) -> Option<RawPagePtr> {
+        BUDDY_ALLOC.lock().alloc_order(order)
+    }
+
+    unsafe fn dealloc(&self, page_ptr: RawPagePtr) {
+        BUDDY_ALLOC.lock().dealloc(page_ptr);
     }
 
-    unsafe fn has_management_over(page_ptr: RawPagePtr) -> bool {
-        BuddyAllocator::has_management_over(page_ptr) && page_ptr.flags().has(PageFlags::LOCAL)
+    fn has_management_over(&self, page_ptr: RawPagePtr) -> bool {
+        BuddyAllocator::has_management_over(page_ptr)
     }
 }
 
@@ -145,7 +183,7 @@ pub extern "C" fn mark_present(start: usize, end: usize) {
 
 #[no_mangle]
 pub extern "C" fn create_pages(start: PAddr, end: PAddr) {
-    BuddyAllocator::create_pages(start, end);
+    BUDDY_ALLOC.lock().create_pages(start, end);
 }
 
 #[no_mangle]
@@ -155,15 +193,16 @@ pub extern "C" fn page_to_pfn(page: *const ()) -> PFN {
 }
 
 #[no_mangle]
-pub extern "C" fn c_alloc_page() -> *const () {
-    GlobalPageAlloc::alloc().expect("Out of memory").as_ptr() as *const _
+pub extern "C" fn c_alloc_page() -> *const RawPage {
+    GlobalPageAlloc.alloc().expect("Out of memory").as_ref()
 }
 
 #[no_mangle]
-pub extern "C" fn c_alloc_pages(order: u32) -> *const () {
-    GlobalPageAlloc::alloc_order(order)
+pub extern "C" fn c_alloc_pages(order: u32) -> *const RawPage {
+    GlobalPageAlloc
+        .alloc_order(order)
         .expect("Out of memory")
-        .as_ptr() as *const _
+        .as_ref()
 }
 
 #[no_mangle]

+ 150 - 0
src/kernel/mem/page_alloc/raw_page.rs

@@ -0,0 +1,150 @@
+use buddy_allocator::BuddyRawPage;
+use core::{
+    ptr::NonNull,
+    sync::atomic::{AtomicU32, AtomicUsize, Ordering},
+};
+use eonix_mm::paging::{RawPage as RawPageTrait, PFN};
+use intrusive_list::{container_of, Link};
+
+const PAGE_ARRAY: NonNull<RawPage> =
+    unsafe { NonNull::new_unchecked(0xffffff8040000000 as *mut _) };
+
+pub struct PageFlags(AtomicU32);
+
+pub struct RawPage {
+    /// This can be used for LRU page swap in the future.
+    ///
+    /// Now only used for free page links in the buddy system.
+    pub link: Link,
+    /// # Safety
+    /// This field is only used in buddy system and is protected by the global lock.
+    pub order: u32,
+    pub flags: PageFlags,
+    pub refcount: AtomicUsize,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct RawPagePtr(NonNull<RawPage>);
+
+impl PageFlags {
+    pub const PRESENT: u32 = 1 << 0;
+    // pub const LOCKED: u32 = 1 << 1;
+    pub const BUDDY: u32 = 1 << 2;
+    // pub const SLAB: u32 = 1 << 3;
+    // pub const DIRTY: u32 = 1 << 4;
+    pub const FREE: u32 = 1 << 5;
+    pub const LOCAL: u32 = 1 << 6;
+
+    pub fn has(&self, flag: u32) -> bool {
+        (self.0.load(Ordering::Relaxed) & flag) == flag
+    }
+
+    pub fn set(&self, flag: u32) {
+        self.0.fetch_or(flag, Ordering::Relaxed);
+    }
+
+    pub fn clear(&self, flag: u32) {
+        self.0.fetch_and(!flag, Ordering::Relaxed);
+    }
+}
+
+impl RawPagePtr {
+    pub const fn new(ptr: NonNull<RawPage>) -> Self {
+        Self(ptr)
+    }
+
+    /// Get a raw pointer to the underlying `RawPage` struct.
+    ///
+    /// # Safety
+    /// Doing arithmetic on the pointer returned will cause immediate undefined behavior.
+    pub const unsafe fn as_ptr(self) -> *mut RawPage {
+        self.0.as_ptr()
+    }
+
+    pub const fn as_ref<'a>(self) -> &'a RawPage {
+        unsafe { &*self.as_ptr() }
+    }
+
+    pub const fn as_mut<'a>(self) -> &'a mut RawPage {
+        unsafe { &mut *self.as_ptr() }
+    }
+
+    pub const fn order(&self) -> u32 {
+        self.as_ref().order
+    }
+
+    pub const fn flags(&self) -> &PageFlags {
+        &self.as_ref().flags
+    }
+
+    pub const fn refcount(&self) -> &AtomicUsize {
+        &self.as_ref().refcount
+    }
+}
+
+impl From<RawPagePtr> for PFN {
+    fn from(value: RawPagePtr) -> Self {
+        let idx = unsafe { value.as_ptr().offset_from(PAGE_ARRAY.as_ptr()) as usize };
+        Self::from(idx)
+    }
+}
+
+impl From<PFN> for RawPagePtr {
+    fn from(pfn: PFN) -> Self {
+        let raw_page_ptr = unsafe { PAGE_ARRAY.add(usize::from(pfn)) };
+        Self::new(raw_page_ptr)
+    }
+}
+
+impl RawPageTrait for RawPagePtr {
+    fn order(&self) -> u32 {
+        self.order()
+    }
+
+    fn refcount(&self) -> &AtomicUsize {
+        self.refcount()
+    }
+
+    fn is_present(&self) -> bool {
+        self.flags().has(PageFlags::PRESENT)
+    }
+}
+
+impl BuddyRawPage for RawPagePtr {
+    unsafe fn from_link(link: &mut Link) -> Self {
+        let raw_page_ptr = container_of!(link, RawPage, link);
+        Self(raw_page_ptr)
+    }
+
+    fn set_order(&self, order: u32) {
+        self.as_mut().order = order;
+    }
+
+    unsafe fn get_link(&self) -> &mut Link {
+        &mut self.as_mut().link
+    }
+
+    fn is_buddy(&self) -> bool {
+        self.flags().has(PageFlags::BUDDY)
+    }
+
+    fn is_free(&self) -> bool {
+        self.flags().has(PageFlags::FREE)
+    }
+
+    fn set_buddy(&self) {
+        self.flags().set(PageFlags::BUDDY);
+    }
+
+    fn set_free(&self) {
+        self.flags().set(PageFlags::FREE);
+    }
+
+    fn clear_buddy(&self) {
+        self.flags().clear(PageFlags::BUDDY);
+    }
+
+    fn clear_free(&self) {
+        self.flags().clear(PageFlags::FREE);
+    }
+}

+ 0 - 1
src/lib.rs

@@ -16,7 +16,6 @@ mod driver;
 mod elf;
 mod fs;
 mod hash;
-mod intrusive_list;
 mod io;
 mod kernel;
 mod net;