ソースを参照

mem: introduce new `Folio` abstraction

- Remove struct `Page` and add `Folio`s to represent adjacent pages.
- Introduce `Zone`s similar to that in Linux. Looking forward to
  removing all occurrence of `ArchPhysAccess` and so on.
- Adapt existing code to new `Folio` interface in a dirty and rough way.

Signed-off-by: greatbridf <greatbridf@icloud.com>
greatbridf 2 週間 前
コミット
913f71f819
42 ファイル変更1116 行追加1244 行削除
  1. 1 1
      .vscode/tasks.json
  2. 56 56
      crates/buddy_allocator/src/lib.rs
  3. 34 28
      crates/eonix_hal/src/arch/riscv64/bootstrap.rs
  4. 27 24
      crates/eonix_hal/src/arch/riscv64/mm.rs
  5. 20 18
      crates/eonix_hal/src/mm.rs
  6. 5 5
      crates/eonix_mm/src/address/paddr.rs
  7. 1 1
      crates/eonix_mm/src/page_table.rs
  8. 60 89
      crates/eonix_mm/src/page_table/page_table.rs
  9. 2 1
      crates/eonix_mm/src/page_table/pte.rs
  10. 59 79
      crates/eonix_mm/src/page_table/pte_iterator.rs
  11. 210 0
      crates/eonix_mm/src/page_table/walk.rs
  12. 3 5
      crates/eonix_mm/src/paging.rs
  13. 7 7
      crates/eonix_mm/src/paging/list.rs
  14. 45 268
      crates/eonix_mm/src/paging/page.rs
  15. 17 62
      crates/eonix_mm/src/paging/page_alloc.rs
  16. 0 47
      crates/eonix_mm/src/paging/raw_page.rs
  17. 3 4
      crates/eonix_mm/src/paging/zone.rs
  18. 21 23
      crates/slab_allocator/src/lib.rs
  19. 15 13
      src/driver/ahci/command.rs
  20. 4 3
      src/driver/ahci/command_table.rs
  21. 4 2
      src/driver/ahci/defs.rs
  22. 6 4
      src/driver/ahci/slot.rs
  23. 12 7
      src/driver/e1000e.rs
  24. 4 4
      src/driver/virtio/virtio_blk.rs
  25. 5 7
      src/fs/fat32.rs
  26. 0 24
      src/fs/fat32/file.rs
  27. 1 1
      src/fs/tmpfs/file.rs
  28. 8 9
      src/kernel/block.rs
  29. 3 1
      src/kernel/mem.rs
  30. 12 14
      src/kernel/mem/allocator.rs
  31. 210 0
      src/kernel/mem/folio.rs
  32. 23 16
      src/kernel/mem/mm_area.rs
  33. 32 32
      src/kernel/mem/mm_list.rs
  34. 40 0
      src/kernel/mem/mm_list/page_table.rs
  35. 36 55
      src/kernel/mem/page_alloc.rs
  36. 24 112
      src/kernel/mem/page_alloc/raw_page.rs
  37. 14 8
      src/kernel/mem/page_alloc/zones.rs
  38. 41 56
      src/kernel/mem/page_cache.rs
  39. 4 118
      src/kernel/mem/paging.rs
  40. 11 14
      src/kernel/task/kernel_stack.rs
  41. 2 2
      src/kernel/vfs/file/mod.rs
  42. 34 24
      src/kernel_init.rs

+ 1 - 1
.vscode/tasks.json

@@ -6,7 +6,7 @@
         {
             "label": "debug run riscv64",
             "type": "shell",
-            "command": "make srun ARCH=riscv64 IMG=/Volumes/oscomp/sdcard-rv.img",
+            "command": "make srun ARCH=riscv64",
             "isBackground": true,
             "problemMatcher": [
                 {

+ 56 - 56
crates/buddy_allocator/src/lib.rs

@@ -3,12 +3,12 @@
 use core::hint::unreachable_unchecked;
 
 use eonix_mm::address::{AddrOps as _, PAddr, PRange};
-use eonix_mm::paging::{PageList, PageListSized, Zone, PFN};
+use eonix_mm::paging::{FolioList, FolioListSized, Zone, PFN};
 
 const MAX_ORDER: u32 = 10;
 const AREAS: usize = const { MAX_ORDER as usize + 1 };
 
-pub trait BuddyPage: Sized + 'static {
+pub trait BuddyFolio: Sized + 'static {
     fn pfn(&self) -> PFN;
 
     fn get_order(&self) -> u32;
@@ -20,19 +20,19 @@ pub trait BuddyPage: Sized + 'static {
 
 struct FreeArea<L>
 where
-    L: PageList,
+    L: FolioList,
 {
     free_list: L,
     count: usize,
 }
 
-unsafe impl<L> Send for FreeArea<L> where L: PageList {}
-unsafe impl<L> Sync for FreeArea<L> where L: PageList {}
+unsafe impl<L> Send for FreeArea<L> where L: FolioList {}
+unsafe impl<L> Sync for FreeArea<L> where L: FolioList {}
 
 pub struct BuddyAllocator<Z, L>
 where
     Z: Zone + 'static,
-    L: PageList,
+    L: FolioList,
 {
     zone: &'static Z,
     free_areas: [FreeArea<L>; AREAS],
@@ -41,8 +41,8 @@ where
 impl<Z, L> BuddyAllocator<Z, L>
 where
     Z: Zone + 'static,
-    Z::Page: BuddyPage,
-    L: PageListSized,
+    Z::Page: BuddyFolio,
+    L: FolioListSized,
 {
     pub const fn new(zone: &'static Z) -> Self {
         Self {
@@ -52,13 +52,13 @@ where
     }
 }
 
-impl<Z, L, P> BuddyAllocator<Z, L>
+impl<Z, L, F> BuddyAllocator<Z, L>
 where
-    Z: Zone<Page = P>,
-    L: PageList<Page = P>,
-    P: BuddyPage + 'static,
+    Z: Zone<Page = F>,
+    L: FolioList<Folio = F>,
+    F: BuddyFolio + 'static,
 {
-    pub fn create_pages(&mut self, start: PAddr, end: PAddr) {
+    pub fn create_folios(&mut self, start: PAddr, end: PAddr) {
         assert!(
             self.zone
                 .contains_prange(PRange::new(start.ceil(), end.floor())),
@@ -82,40 +82,40 @@ where
 
             unsafe {
                 // SAFETY: We've checked that the range is within the zone above.
-                self.add_page_unchecked(pfn, order)
+                self.add_folio_unchecked(pfn, order)
             };
 
             pfn = new_end_pfn;
         }
     }
 
-    fn add_page(&mut self, pfn: PFN, order: u32) {
+    fn add_folio(&mut self, pfn: PFN, order: u32) {
         let prange = PRange::from(PAddr::from(pfn)).grow(1 << (order + 12));
         assert!(
             self.zone.contains_prange(prange),
-            "The given page is not within the zone."
+            "The given folio is not within the zone."
         );
 
         unsafe {
             // SAFETY: Checks above.
-            self.add_page_unchecked(pfn, order);
+            self.add_folio_unchecked(pfn, order);
         }
     }
 
-    unsafe fn add_page_unchecked(&mut self, pfn: PFN, order: u32) {
-        let Some(page) = self.zone.get_page(pfn) else {
+    unsafe fn add_folio_unchecked(&mut self, pfn: PFN, order: u32) {
+        let Some(mut folio) = self.zone.get_page(pfn) else {
             unsafe { unreachable_unchecked() }
         };
 
         unsafe {
             // SAFETY: The caller ensures that the page is unused.
-            let page_mut = &mut *page.get();
-            self.free_areas[order as usize].add_page(page_mut, order);
+            let folio_mut = folio.as_mut();
+            self.free_areas[order as usize].add_folio(folio_mut, order);
         }
     }
 
-    fn break_page(&mut self, page: &mut P, order: u32, target_order: u32) {
-        let pfn = page.pfn();
+    fn break_folio(&mut self, folio: &mut F, order: u32, target_order: u32) {
+        let pfn = folio.pfn();
 
         for order in (target_order..order).rev() {
             let buddy_pfn = pfn + (1 << order);
@@ -123,50 +123,50 @@ where
             unsafe {
                 // SAFETY: We got the page from `self.free_areas`. Checks are
                 //         done when we've put the page into the buddy system.
-                self.add_page_unchecked(buddy_pfn, order);
+                self.add_folio_unchecked(buddy_pfn, order);
             }
         }
 
-        page.set_order(target_order);
+        folio.set_order(target_order);
     }
 
     pub fn alloc_order(&mut self, order: u32) -> Option<&'static mut Z::Page> {
         for current_order in order..AREAS as u32 {
-            let Some(page) = self.free_areas[current_order as usize].get_free_page() else {
+            let Some(folio) = self.free_areas[current_order as usize].get_free_folio() else {
                 continue;
             };
 
             if current_order > order {
-                self.break_page(page, current_order, order);
+                self.break_folio(folio, current_order, order);
             }
 
-            return Some(page);
+            return Some(folio);
         }
 
         None
     }
 
-    pub unsafe fn dealloc(&mut self, page: &'static mut Z::Page) {
-        let mut pfn = page.pfn();
-        let mut order = page.get_order();
+    pub unsafe fn dealloc(&mut self, folio: &'static mut Z::Page) {
+        let mut pfn = folio.pfn();
+        let mut order = folio.get_order();
 
         assert!(
-            !page.is_buddy(),
-            "Trying to free a page that is already in the buddy system: {pfn:?}",
+            !folio.is_buddy(),
+            "Trying to free a folio that is already in the buddy system: {pfn:?}",
         );
 
         while order < MAX_ORDER {
             let buddy_pfn = pfn.buddy_pfn(order);
-            let Some(buddy_page) = self.try_get_buddy(buddy_pfn, order) else {
+            let Some(buddy) = self.try_get_buddy(buddy_pfn, order) else {
                 break;
             };
 
-            self.free_areas[order as usize].remove_page(buddy_page);
+            self.free_areas[order as usize].remove_folio(buddy);
             pfn = pfn.combined_pfn(buddy_pfn);
             order += 1;
         }
 
-        self.add_page(pfn, order);
+        self.add_folio(pfn, order);
     }
 
     /// This function checks whether the given page is within our [`Zone`] and
@@ -176,32 +176,32 @@ where
     /// - the buddy is within the same [`Zone`] as us.
     /// - the buddy is a free buddy (in some [`FreeArea`])
     /// - the buddy has order [`order`]
-    fn try_get_buddy<'a>(&mut self, buddy_pfn: PFN, order: u32) -> Option<&'a mut P> {
-        let buddy_page = self.zone.get_page(buddy_pfn)?;
+    fn try_get_buddy<'a>(&mut self, buddy_pfn: PFN, order: u32) -> Option<&'a mut F> {
+        let mut buddy = self.zone.get_page(buddy_pfn)?;
 
         unsafe {
             // SAFETY: We just test whether the page is a buddy.
-            let buddy_page_ref = &*buddy_page.get();
+            let buddy_ref = buddy.as_ref();
 
-            if !buddy_page_ref.is_buddy() {
+            if !buddy_ref.is_buddy() {
                 return None;
             }
 
             // Sad...
-            if buddy_page_ref.get_order() != order {
+            if buddy_ref.get_order() != order {
                 return None;
             }
 
             // SAFETY: We have the mutable reference to the buddy allocator.
             //         So all the pages within are exclusively accessible to us.
-            Some(&mut *buddy_page.get())
+            Some(buddy.as_mut())
         }
     }
 }
 
 impl<L> FreeArea<L>
 where
-    L: PageListSized,
+    L: FolioListSized,
 {
     const fn new() -> Self {
         Self {
@@ -213,34 +213,34 @@ where
 
 impl<L> FreeArea<L>
 where
-    L: PageList,
-    L::Page: BuddyPage + 'static,
+    L: FolioList,
+    L::Folio: BuddyFolio + 'static,
 {
-    pub fn get_free_page(&mut self) -> Option<&'static mut L::Page> {
-        self.free_list.pop_head().map(|page| {
+    pub fn get_free_folio(&mut self) -> Option<&'static mut L::Folio> {
+        self.free_list.pop_head().map(|folio| {
             assert_ne!(self.count, 0, "Oops");
 
-            page.set_buddy(false);
+            folio.set_buddy(false);
             self.count -= 1;
 
-            page
+            folio
         })
     }
 
-    pub fn add_page(&mut self, page: &'static mut L::Page, order: u32) {
-        page.set_order(order);
-        page.set_buddy(true);
+    pub fn add_folio(&mut self, folio: &'static mut L::Folio, order: u32) {
+        folio.set_order(order);
+        folio.set_buddy(true);
 
         self.count += 1;
-        self.free_list.push_tail(page);
+        self.free_list.push_tail(folio);
     }
 
-    pub fn remove_page(&mut self, page: &mut L::Page) {
+    pub fn remove_folio(&mut self, folio: &mut L::Folio) {
         assert_ne!(self.count, 0, "Oops");
-        page.set_buddy(false);
+        folio.set_buddy(false);
 
         self.count -= 1;
-        self.free_list.remove(page);
+        self.free_list.remove(folio);
     }
 }
 

+ 34 - 28
crates/eonix_hal/src/arch/riscv64/bootstrap.rs

@@ -2,12 +2,13 @@ use core::alloc::Allocator;
 use core::arch::{asm, global_asm, naked_asm};
 use core::cell::RefCell;
 use core::hint::spin_loop;
+use core::ptr::NonNull;
 use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
 
 use eonix_hal_traits::mm::Memory;
 use eonix_mm::address::{Addr as _, PAddr, PRange, PhysAccess, VAddr, VRange};
-use eonix_mm::page_table::{PageAttribute, PagingMode, PTE as _};
-use eonix_mm::paging::{Page, PageAccess, PageAlloc, PAGE_SIZE, PFN};
+use eonix_mm::page_table::{PageAttribute, PageTable, PagingMode, TableAttribute, PTE as _};
+use eonix_mm::paging::{Folio, FrameAlloc, PageAccess, PageBlock, PAGE_SIZE, PFN};
 use eonix_percpu::PercpuArea;
 use fdt::Fdt;
 use riscv::asm::sfence_vma_all;
@@ -23,9 +24,12 @@ use super::cpu::{CPUID, CPU_COUNT};
 use super::time::set_next_timer;
 use crate::arch::cpu::CPU;
 use crate::arch::fdt::{init_dtb_and_fdt, FdtExt, FDT};
-use crate::arch::mm::{ArchPhysAccess, FreeRam, PageAttribute64, GLOBAL_PAGE_TABLE};
+use crate::arch::mm::{
+    ArchPagingMode, ArchPhysAccess, FreeRam, PageAccessImpl, PageAttribute64, RawPageTableSv48,
+    GLOBAL_PAGE_TABLE,
+};
 use crate::bootstrap::BootStrapData;
-use crate::mm::{ArchMemory, ArchPagingMode, BasicPageAlloc, BasicPageAllocRef, ScopedAllocator};
+use crate::mm::{ArchMemory, BasicPageAlloc, BasicPageAllocRef, ScopedAllocator};
 
 #[unsafe(link_section = ".bootstrap.stack")]
 static BOOT_STACK: [u8; 4096 * 16] = [0; 4096 * 16];
@@ -38,26 +42,26 @@ static TEMP_AP_STACK: [u8; 256] = [0; 256];
 static TEMP_AP_STACK_START: &'static [u8; 256] = &TEMP_AP_STACK;
 
 #[repr(C, align(4096))]
-struct PageTable([u64; PTES_PER_PAGE]);
+struct BootPageTable([u64; PTES_PER_PAGE]);
 
 /// map 0x8000 0000 to itself and 0xffff ffff 8000 0000
 #[unsafe(link_section = ".bootstrap.page_table.1")]
-static BOOT_PAGE_TABLE: PageTable = {
+static BOOT_PAGE_TABLE: BootPageTable = {
     let mut arr: [u64; PTES_PER_PAGE] = [0; PTES_PER_PAGE];
     arr[0] = 0 | 0x2f;
     arr[510] = 0 | 0x2f;
     arr[511] = (0x80202 << 10) | 0x21;
 
-    PageTable(arr)
+    BootPageTable(arr)
 };
 
 #[unsafe(link_section = ".bootstrap.page_table.2")]
 #[used]
-static PT1: PageTable = {
+static PT1: BootPageTable = {
     let mut arr: [u64; PTES_PER_PAGE] = [0; PTES_PER_PAGE];
     arr[510] = (0x80000 << 10) | 0x2f;
 
-    PageTable(arr)
+    BootPageTable(arr)
 };
 
 static BSP_PAGE_ALLOC: AtomicPtr<RefCell<BasicPageAlloc>> = AtomicPtr::new(core::ptr::null_mut());
@@ -111,7 +115,7 @@ pub unsafe extern "C" fn riscv64_start(hart_id: usize, dtb_addr: PAddr) -> ! {
         real_allocator.borrow_mut().add_range(range);
     }
 
-    setup_kernel_page_table(&alloc);
+    setup_kernel_page_table(alloc.clone());
     unsafe {
         init_dtb_and_fdt(dtb_addr);
     }
@@ -148,8 +152,12 @@ unsafe extern "C" {
 
 /// TODO:
 /// 对kernel image添加更细的控制,或者不加也行
-fn setup_kernel_page_table(alloc: impl PageAlloc) {
-    let global_page_table = &GLOBAL_PAGE_TABLE;
+fn setup_kernel_page_table(alloc: BasicPageAllocRef) {
+    let global_page_table = PageTable::<ArchPagingMode, _, _>::new(
+        GLOBAL_PAGE_TABLE.clone(),
+        alloc.clone(),
+        PageAccessImpl,
+    );
 
     let attr = PageAttribute::WRITE
         | PageAttribute::READ
@@ -160,18 +168,11 @@ fn setup_kernel_page_table(alloc: impl PageAlloc) {
     const KERNEL_BSS_START: VAddr = VAddr::from(0xffffffff40000000);
 
     // Map kernel BSS
-    for pte in global_page_table.iter_kernel_in(
-        VRange::from(KERNEL_BSS_START).grow(BSS_LENGTH as usize),
-        ArchPagingMode::LEVELS,
-        &alloc,
-    ) {
-        let page = Page::alloc_in(&alloc);
-
-        let attr = {
-            let mut attr = attr.clone();
-            attr.remove(PageAttribute::EXECUTE);
-            attr
-        };
+    let bss_range = VRange::from(KERNEL_BSS_START).grow(BSS_LENGTH as usize);
+    for pte in global_page_table.iter_kernel(bss_range) {
+        let page = alloc.alloc().unwrap();
+        let attr = attr.difference(PageAttribute::EXECUTE);
+
         pte.set(page.into_raw(), attr.into());
     }
 
@@ -189,17 +190,22 @@ fn setup_kernel_page_table(alloc: impl PageAlloc) {
         );
     }
     sfence_vma_all();
+
+    core::mem::forget(global_page_table);
 }
 
 /// set up tp register to percpu
-fn setup_cpu(alloc: impl PageAlloc, hart_id: usize) {
+fn setup_cpu(alloc: impl FrameAlloc, hart_id: usize) {
     CPU_COUNT.fetch_add(1, Ordering::Relaxed);
 
     let mut percpu_area = PercpuArea::new(|layout| {
         let page_count = layout.size().div_ceil(PAGE_SIZE);
-        let page = Page::alloc_at_least_in(page_count, alloc);
+        let page = alloc.alloc_at_least(page_count).unwrap();
 
-        let ptr = ArchPhysAccess::get_ptr_for_page(&page).cast();
+        let ptr = unsafe {
+            // TODO: safety
+            ArchPhysAccess::as_ptr(page.start())
+        };
         page.into_raw();
 
         ptr
@@ -243,7 +249,7 @@ fn bootstrap_smp(alloc: impl Allocator, page_alloc: &RefCell<BasicPageAlloc>) {
     for hart_id in FDT.harts().filter(|&id| id != local_hart_id) {
         let stack_range = {
             let page_alloc = BasicPageAllocRef::new(&page_alloc);
-            let ap_stack = Page::alloc_order_in(4, page_alloc);
+            let ap_stack = page_alloc.alloc_order(4).unwrap();
             let stack_range = ap_stack.range();
             ap_stack.into_raw();
             stack_range

+ 27 - 24
crates/eonix_hal/src/arch/riscv64/mm.rs

@@ -1,31 +1,25 @@
-use super::{
-    config::mm::{PHYS_MAP_VIRT, ROOT_PAGE_TABLE_PFN},
-    fdt::{FdtExt, FDT},
-};
-use crate::{arch::riscv64::config::mm::KIMAGE_OFFSET, traits::mm::Memory};
-use core::{marker::PhantomData, ptr::NonNull};
-use eonix_mm::{
-    address::{Addr as _, AddrOps, PAddr, PRange, PhysAccess, VAddr},
-    page_table::{
-        PageAttribute, PageTable, PageTableLevel, PagingMode, RawAttribute, RawPageTable,
-        TableAttribute, PTE,
-    },
-    paging::{NoAlloc, Page, PageBlock, PFN},
+use core::marker::PhantomData;
+use core::ptr::NonNull;
+
+use eonix_hal_traits::mm::Memory;
+use eonix_mm::address::{Addr as _, AddrOps, PAddr, PRange, PhysAccess, VAddr};
+use eonix_mm::page_table::{
+    PageAttribute, PageTable, PageTableLevel, PagingMode, RawAttribute, RawPageTable,
+    TableAttribute, PTE,
 };
+use eonix_mm::paging::{BasicFolio, Folio, PageAccess, PageBlock, PFN};
 use eonix_sync_base::LazyLock;
 use fdt::Fdt;
-use riscv::{
-    asm::{sfence_vma, sfence_vma_all},
-    register::satp,
-};
+use riscv::asm::{sfence_vma, sfence_vma_all};
+use riscv::register::satp;
 
-pub const PAGE_TABLE_BASE: PFN = PFN::from_val(ROOT_PAGE_TABLE_PFN);
-pub static GLOBAL_PAGE_TABLE: LazyLock<PageTable<ArchPagingMode, NoAlloc, ArchPhysAccess>> =
-    LazyLock::new(|| unsafe {
-        Page::with_raw(PAGE_TABLE_BASE, |root_table_page| {
-            PageTable::with_root_table(root_table_page.clone())
-        })
-    });
+use super::config::mm::{PHYS_MAP_VIRT, ROOT_PAGE_TABLE_PFN};
+use super::fdt::{FdtExt, FDT};
+use crate::arch::riscv64::config::mm::KIMAGE_OFFSET;
+use crate::mm::BasicPageAlloc;
+
+const PAGE_TABLE_BASE: PFN = PFN::from_val(ROOT_PAGE_TABLE_PFN);
+pub const GLOBAL_PAGE_TABLE: BasicFolio = BasicFolio::new(PAGE_TABLE_BASE, 0);
 
 pub const PA_V: u64 = 0b1 << 0;
 pub const PA_R: u64 = 0b1 << 1;
@@ -61,6 +55,9 @@ pub struct ArchPhysAccess;
 
 pub struct ArchMemory;
 
+#[derive(Clone)]
+pub struct PageAccessImpl;
+
 impl PTE for PTE64 {
     type Attr = PageAttribute64;
 
@@ -261,6 +258,12 @@ impl PhysAccess for ArchPhysAccess {
     }
 }
 
+impl PageAccess for PageAccessImpl {
+    unsafe fn get_ptr_for_pfn(&self, pfn: PFN) -> NonNull<PageBlock> {
+        unsafe { ArchPhysAccess::as_ptr(PAddr::from(pfn)) }
+    }
+}
+
 impl Memory for ArchMemory {
     fn present_ram() -> impl Iterator<Item = PRange> {
         FDT.present_ram()

+ 20 - 18
crates/eonix_hal/src/mm.rs

@@ -1,16 +1,14 @@
-use core::{
-    alloc::{AllocError, Allocator, Layout},
-    cell::RefCell,
-    ptr::NonNull,
-};
-use eonix_mm::{
-    address::{AddrOps as _, PRange},
-    paging::{PageAlloc, UnmanagedRawPage, PAGE_SIZE, PFN},
-};
+use core::alloc::{AllocError, Allocator, Layout};
+use core::cell::RefCell;
+use core::ptr::NonNull;
+
+use eonix_mm::address::{AddrOps as _, PRange};
+use eonix_mm::page_table::PageTableAlloc;
+use eonix_mm::paging::{BasicFolio, FrameAlloc, PAGE_SIZE, PFN};
 
 pub use crate::arch::mm::{
     flush_tlb, flush_tlb_all, get_root_page_table_pfn, set_root_page_table_pfn, ArchMemory,
-    ArchPagingMode, ArchPhysAccess, GLOBAL_PAGE_TABLE,
+    ArchPhysAccess, GLOBAL_PAGE_TABLE,
 };
 
 pub struct BasicPageAlloc {
@@ -118,19 +116,23 @@ impl<'a> BasicPageAllocRef<'a> {
     }
 }
 
-impl PageAlloc for BasicPageAllocRef<'_> {
-    type RawPage = UnmanagedRawPage;
+impl FrameAlloc for BasicPageAllocRef<'_> {
+    type Folio = BasicFolio;
 
-    fn alloc_order(&self, order: u32) -> Option<Self::RawPage> {
-        Some(Self::RawPage::new(self.0.borrow_mut().alloc(order), order))
+    fn alloc_order(&self, order: u32) -> Option<Self::Folio> {
+        Some(BasicFolio::new(self.0.borrow_mut().alloc(order), order))
     }
+}
+
+impl PageTableAlloc for BasicPageAllocRef<'_> {
+    type Folio = BasicFolio;
 
-    unsafe fn dealloc(&self, _: Self::RawPage) {
-        panic!("Dealloc is not supported in BasicPageAlloc");
+    fn alloc(&self) -> Self::Folio {
+        FrameAlloc::alloc(self).unwrap()
     }
 
-    fn has_management_over(&self, _: Self::RawPage) -> bool {
-        true
+    unsafe fn from_raw(&self, pfn: PFN) -> Self::Folio {
+        BasicFolio::new(pfn, 0)
     }
 }
 

+ 5 - 5
crates/eonix_mm/src/address/paddr.rs

@@ -1,11 +1,11 @@
+use core::fmt;
+use core::ops::{Add, Sub};
+use core::ptr::NonNull;
+
 use super::addr::Addr;
 use crate::paging::{PAGE_SIZE_BITS, PFN};
-use core::{
-    fmt,
-    ops::{Add, Sub},
-    ptr::NonNull,
-};
 
+/// Convert PAddr to VAddr.
 pub trait PhysAccess {
     /// Translate the data that this address is pointing to into kernel
     /// accessible pointer. Use it with care.

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

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

+ 60 - 89
crates/eonix_mm/src/page_table/page_table.rs

@@ -1,15 +1,12 @@
-use super::{
-    paging_mode::PageTableLevel,
-    pte::{RawAttribute, TableAttribute},
-    pte_iterator::{KernelIterator, UserIterator},
-    PagingMode, PTE,
-};
-use crate::{
-    address::{PAddr, VRange},
-    page_table::PageTableIterator,
-    paging::{GlobalPageAlloc, Page, PageAccess, PageAlloc, PageBlock},
-};
-use core::{marker::PhantomData, ptr::NonNull};
+use core::marker::PhantomData;
+use core::ptr::NonNull;
+
+use super::paging_mode::PageTableLevel;
+use super::pte::{RawAttribute, TableAttribute};
+use super::{PagingMode, PTE};
+use crate::address::{PAddr, VRange};
+use crate::page_table::PageTableIterator;
+use crate::paging::{Folio, PageAccess, PageBlock, PFN};
 
 pub trait RawPageTable<'a>: Send + 'a {
     type Entry: PTE + 'a;
@@ -24,45 +21,60 @@ pub trait RawPageTable<'a>: Send + 'a {
     unsafe fn from_ptr(ptr: NonNull<PageBlock>) -> Self;
 }
 
+pub trait PageTableAlloc: Clone {
+    type Folio: Folio;
+
+    fn alloc(&self) -> Self::Folio;
+    unsafe fn from_raw(&self, pfn: PFN) -> Self::Folio;
+}
+
+pub trait GlobalPageTableAlloc: PageTableAlloc {
+    const GLOBAL: Self;
+}
+
 pub struct PageTable<'a, M, A, X>
 where
     M: PagingMode,
     M::Entry: 'a,
-    A: PageAlloc,
+    A: PageTableAlloc,
     X: PageAccess,
 {
-    root_table_page: Page<A>,
-    phantom: PhantomData<&'a (M, X)>,
+    root_table_page: A::Folio,
+    alloc: A,
+    access: X,
+    phantom: PhantomData<&'a M>,
 }
 
 impl<'a, M, A, X> PageTable<'a, M, A, X>
 where
     M: PagingMode,
     M::Entry: 'a,
-    A: PageAlloc,
+    A: PageTableAlloc,
     X: PageAccess,
 {
-    pub fn with_root_table(root_table_page: Page<A>) -> Self {
+    pub fn new(root_table_page: A::Folio, alloc: A, access: X) -> Self {
         Self {
             root_table_page,
+            alloc,
+            access,
             phantom: PhantomData,
         }
     }
 
     pub fn clone_global<'b, B>(&self) -> PageTable<'b, M, B, X>
     where
-        B: GlobalPageAlloc,
+        B: GlobalPageTableAlloc,
     {
-        self.clone_in(B::global())
+        self.clone_in(B::GLOBAL)
     }
 
     pub fn clone_in<'b, B>(&self, alloc: B) -> PageTable<'b, M, B, X>
     where
-        B: PageAlloc,
+        B: PageTableAlloc,
     {
-        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(&self.root_table_page);
+        let new_root_table_page = alloc.alloc();
+        let new_table_data = self.access.get_ptr_for_page(&new_root_table_page);
+        let kernel_table_data = self.access.get_ptr_for_page(&self.root_table_page);
 
         unsafe {
             // SAFETY: `new_table_data` and `kernel_table_data` are both valid pointers
@@ -82,7 +94,7 @@ where
             root_page_table.index_mut(idx).take();
         }
 
-        PageTable::with_root_table(new_root_table_page)
+        PageTable::new(new_root_table_page, alloc, self.access.clone())
     }
 
     pub fn addr(&self) -> PAddr {
@@ -90,100 +102,59 @@ 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 page_table_ptr = self.access.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, alloc.clone())
-    }
-
-    pub fn iter_kernel(&self, range: VRange) -> impl Iterator<Item = &mut M::Entry> {
-        self.iter_kernel_levels(range, M::LEVELS)
+        PageTableIterator::<M, _, _>::new(
+            root_page_table,
+            range,
+            TableAttribute::USER,
+            self.alloc.clone(),
+            self.access.clone(),
+        )
     }
 
-    /// Iterates over the kernel space entries in the page table for the specified levels.
-    ///
-    /// # Parameters
-    /// - `range`: The virtual address range to iterate over.
-    /// - `levels`: A slice of `PageTableLevel` that specifies which levels of the page table
-    ///   should be included in the iteration. Each level corresponds to a level in the page
-    ///   table hierarchy, and the iterator will traverse entries at these levels.
+    /// Iterates over the kernel space entries in the page table.
     ///
     /// # Returns
     /// An iterator over mutable references to the page table entries (`M::Entry`) within the
-    /// specified range and levels.
+    /// specified range.
     ///
     /// # Example
     /// ```
     /// let range = VRange::new(0x1234000, 0x1300000);
-    /// let levels = &M::LEVELS[..2];
-    /// for pte in page_table.iter_kernel_levels(range, levels) {
+    /// for pte in page_table.iter_kernel(range) {
     ///     // Process each entry
     /// }
     /// ```
-    pub fn iter_kernel_levels(
-        &self,
-        range: VRange,
-        levels: &'static [PageTableLevel],
-    ) -> impl Iterator<Item = &mut M::Entry> {
-        self.iter_kernel_in(range, levels, self.root_table_page.allocator())
-    }
-
-    /// Iterates over the kernel space entries in the page table for the specified levels
-    /// with a given page allocator.
-    ///
-    /// # Parameters
-    /// - `range`: The virtual address range to iterate over.
-    /// - `levels`: A slice of `PageTableLevel` that specifies which levels of the page table
-    ///   should be included in the iteration. Each level corresponds to a level in the page
-    ///   table hierarchy, and the iterator will traverse entries at these levels.
-    /// - `alloc`: A page allocator that provides memory for the page table entries.
-    ///
-    /// # Returns
-    /// An iterator over mutable references to the page table entries (`M::Entry`) within the
-    /// specified range and levels.
-    ///
-    /// # Example
-    /// ```no_run
-    /// let range = VRange::new(0x1234000, 0x1300000);
-    /// let levels = &M::LEVELS[..2];
-    /// for pte in page_table.iter_kernel_in(range, levels, NoAlloc) {
-    ///     // Process each entry
-    /// }
-    /// ```
-    pub fn iter_kernel_in<A1: PageAlloc>(
-        &self,
-        range: VRange,
-        levels: &'static [PageTableLevel],
-        alloc: A1,
-    ) -> impl Iterator<Item = &mut M::Entry> {
-        let page_table_ptr = X::get_ptr_for_page(&self.root_table_page);
+    pub fn iter_kernel(&self, range: VRange) -> impl Iterator<Item = &mut M::Entry> {
+        let page_table_ptr = self.access.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, A1, X, KernelIterator>::with_levels(
+        PageTableIterator::<M, _, _>::with_levels(
             root_page_table,
             range,
-            alloc,
-            levels,
+            TableAttribute::GLOBAL,
+            self.alloc.clone(),
+            self.access.clone(),
+            M::LEVELS,
         )
     }
 
-    fn drop_page_table_recursive(page_table: &Page<A>, levels: &[PageTableLevel]) {
+    fn drop_page_table_recursive(&self, page_table: &A::Folio, levels: &[PageTableLevel]) {
         let [level, remaining_levels @ ..] = levels else { return };
         if remaining_levels.is_empty() {
             // We reached the last level, no need to go deeper.
             return;
         }
 
-        let alloc = page_table.allocator();
-
-        let page_table_ptr = X::get_ptr_for_page(page_table);
+        let page_table_ptr = self.access.get_ptr_for_page(page_table);
         let mut page_table = unsafe {
             // SAFETY: `page_table_ptr` is a valid pointer to a page table.
             M::RawTable::from_ptr(page_table_ptr)
@@ -201,10 +172,10 @@ where
 
             let page_table = unsafe {
                 // SAFETY: We got the pfn from a valid page table entry, so it should be valid.
-                Page::from_raw_in(pfn, alloc.clone())
+                self.alloc.from_raw(pfn)
             };
 
-            Self::drop_page_table_recursive(&page_table, remaining_levels);
+            self.drop_page_table_recursive(&page_table, remaining_levels);
         }
     }
 }
@@ -213,10 +184,10 @@ impl<'a, M, A, X> Drop for PageTable<'a, M, A, X>
 where
     M: PagingMode,
     M::Entry: 'a,
-    A: PageAlloc,
+    A: PageTableAlloc,
     X: PageAccess,
 {
     fn drop(&mut self) {
-        Self::drop_page_table_recursive(&self.root_table_page, M::LEVELS);
+        self.drop_page_table_recursive(&self.root_table_page, M::LEVELS);
     }
 }

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

@@ -1,6 +1,7 @@
-use crate::paging::PFN;
 use bitflags::bitflags;
 
+use crate::paging::PFN;
+
 bitflags! {
     #[derive(Clone, Copy, PartialEq)]
     pub struct TableAttribute: usize {

+ 59 - 79
crates/eonix_mm/src/page_table/pte_iterator.rs

@@ -1,62 +1,14 @@
-use super::{
-    pte::{RawAttribute, TableAttribute},
-    PageTableLevel, PagingMode, RawPageTable as _, PTE,
-};
-use crate::{
-    address::{AddrOps as _, VRange},
-    paging::{Page, PageAccess, PageAlloc},
-};
-use core::{marker::PhantomData};
-
-pub struct KernelIterator;
-pub struct UserIterator;
-
-pub trait IteratorType<M: PagingMode> {
-    fn page_table_attributes() -> TableAttribute;
-
-    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().as_table_attr().expect("Not a page table");
-
-        if attr.contains(TableAttribute::PRESENT) {
-            let pfn = pte.get_pfn();
-            unsafe {
-                // SAFETY: We are creating a pointer to a page referenced to in
-                //         some page table, which should be valid.
-                let page_table_ptr = X::get_ptr_for_pfn(pfn);
-                // SAFETY: `page_table_ptr` is a valid pointer to a page table.
-                M::RawTable::from_ptr(page_table_ptr)
-            }
-        } else {
-            let page = Page::alloc_in(alloc.clone());
-            let page_table_ptr = X::get_ptr_for_page(&page);
-
-            unsafe {
-                // SAFETY: `page_table_ptr` is good for writing and properly aligned.
-                page_table_ptr.write_bytes(0, 1);
-            }
-
-            pte.set(
-                page.into_raw(),
-                <M::Entry as PTE>::Attr::from(Self::page_table_attributes()),
-            );
-
-            unsafe {
-                // SAFETY: `page_table_ptr` is a valid pointer to a page table.
-                M::RawTable::from_ptr(page_table_ptr)
-            }
-        }
-    }
-}
+use super::page_table::PageTableAlloc;
+use super::pte::{RawAttribute, TableAttribute};
+use super::{PageTableLevel, PagingMode, RawPageTable as _, PTE};
+use crate::address::{AddrOps as _, VRange};
+use crate::paging::{Folio, PageAccess};
 
-pub struct PageTableIterator<'a, M, A, X, K>
+pub struct PageTableIterator<'a, M, A, X>
 where
     M: PagingMode,
     M::Entry: 'a,
-    A: PageAlloc,
+    A: PageTableAlloc,
     X: PageAccess,
 {
     /// Specifies the hierarchy of page table levels to iterate over.
@@ -69,19 +21,19 @@ where
     indicies: [u16; 8],
     tables: [Option<M::RawTable<'a>>; 8],
 
+    fill_entry_attr: TableAttribute,
+
     alloc: A,
-    _phantom: PhantomData<&'a (X, K)>,
+    access: X,
 }
 
-impl<'a, M, A, X, K> PageTableIterator<'a, M, A, X, K>
+impl<'a, M, A, X> PageTableIterator<'a, M, A, X>
 where
     M: PagingMode,
     M::Entry: 'a,
-    A: PageAlloc,
+    A: PageTableAlloc,
     X: PageAccess,
-    K: IteratorType<M>,
 {
-
     fn parse_tables_starting_from(&mut self, idx_level: usize) {
         for (idx, &pt_idx) in self
             .indicies
@@ -98,18 +50,58 @@ 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, &self.alloc));
+
+            child_table.replace({
+                let attr = next_pte
+                    .get_attr()
+                    .as_table_attr()
+                    .expect("Not a page table");
+
+                if attr.contains(TableAttribute::PRESENT) {
+                    let pfn = next_pte.get_pfn();
+                    unsafe {
+                        // SAFETY: We are creating a pointer to a page referenced to in
+                        //         some page table, which should be valid.
+                        let page_table_ptr = self.access.get_ptr_for_pfn(pfn);
+                        // SAFETY: `page_table_ptr` is a valid pointer to a page table.
+                        M::RawTable::from_ptr(page_table_ptr)
+                    }
+                } else {
+                    let page = self.alloc.alloc();
+                    let page_table_ptr = self.access.get_ptr_for_page(&page);
+
+                    unsafe {
+                        // SAFETY: `page_table_ptr` is good for writing and properly aligned.
+                        page_table_ptr.write_bytes(0, 1);
+                    }
+
+                    next_pte.set(page.into_raw(), self.fill_entry_attr.into());
+
+                    unsafe {
+                        // SAFETY: `page_table_ptr` is a valid pointer to a page table.
+                        M::RawTable::from_ptr(page_table_ptr)
+                    }
+                }
+            });
         }
     }
 
-    pub fn new(page_table: M::RawTable<'a>, range: VRange, alloc: A) -> Self {
-        Self::with_levels(page_table, range, alloc, M::LEVELS)
+    pub fn new(
+        page_table: M::RawTable<'a>,
+        range: VRange,
+        fill_entry_attr: TableAttribute,
+        alloc: A,
+        access: X,
+    ) -> Self {
+        Self::with_levels(page_table, range, fill_entry_attr, alloc, access, M::LEVELS)
     }
 
     pub fn with_levels(
         page_table: M::RawTable<'a>,
         range: VRange,
+        fill_entry_attr: TableAttribute,
         alloc: A,
+        access: X,
         levels: &'static [PageTableLevel],
     ) -> Self {
         let start = range.start().floor();
@@ -122,8 +114,9 @@ where
             remaining: (end - start) / last_level.page_size(),
             indicies: [0; 8],
             tables: [const { None }; 8],
+            fill_entry_attr: fill_entry_attr.union(TableAttribute::PRESENT),
             alloc,
-            _phantom: PhantomData,
+            access,
         };
 
         for (i, level) in levels.iter().enumerate() {
@@ -137,13 +130,12 @@ where
     }
 }
 
-impl<'a, M, A, X, K> Iterator for PageTableIterator<'a, M, A, X, K>
+impl<'a, M, A, X> Iterator for PageTableIterator<'a, M, A, X>
 where
     M: PagingMode,
     M::Entry: 'a,
-    A: PageAlloc,
+    A: PageTableAlloc,
     X: PageAccess,
-    K: IteratorType<M>,
 {
     type Item = &'a mut M::Entry;
 
@@ -178,15 +170,3 @@ where
         Some(retval)
     }
 }
-
-impl<M: PagingMode> IteratorType<M> for KernelIterator {
-    fn page_table_attributes() -> TableAttribute {
-        TableAttribute::PRESENT | TableAttribute::GLOBAL
-    }
-}
-
-impl<M: PagingMode> IteratorType<M> for UserIterator {
-    fn page_table_attributes() -> TableAttribute {
-        TableAttribute::PRESENT | TableAttribute::USER
-    }
-}

+ 210 - 0
crates/eonix_mm/src/page_table/walk.rs

@@ -0,0 +1,210 @@
+use super::pte::{RawAttribute, TableAttribute};
+use super::{PageTableLevel, PTE};
+use crate::address::{AddrOps, VAddr, VRange};
+use crate::paging::PFN;
+
+pub enum WalkState {
+    Next,
+    Skip,
+    Break,
+}
+
+pub trait PageTable: Sized {
+    type Entry: PTE;
+    const LEVELS: &'static [PageTableLevel];
+
+    fn index(&self, index: usize) -> &Self::Entry;
+    fn index_mut(&mut self, index: usize) -> &mut Self::Entry;
+
+    fn from_pfn(pfn: PFN) -> Self;
+    unsafe fn take_pfn(pfn: PFN) -> Self;
+}
+
+pub struct PageTableWalk<'a, T, D>
+where
+    T: PageTable,
+{
+    levels: &'a [PageTableLevel],
+    fill_entry: &'a [fn(&mut D, &mut T::Entry) -> Option<PFN>],
+    walk_entry: &'a [fn(&mut D, &mut T::Entry) -> WalkState],
+    data: D,
+}
+
+fn try_get_table<T, D>(
+    entry: &mut T::Entry,
+    data: &mut D,
+    fill_entry: fn(&mut D, &mut T::Entry) -> Option<PFN>,
+) -> Option<T>
+where
+    T: PageTable,
+{
+    let (mut pfn, attr) = entry.get();
+
+    // Always skip huge page entries
+    let attr = attr.as_table_attr()?;
+
+    // For normal entries, check present flags
+    if !attr.contains(TableAttribute::PRESENT) {
+        // Skip entries filled with nothing
+        pfn = fill_entry(data, entry)?;
+    }
+
+    Some(T::from_pfn(pfn))
+}
+
+fn _walk_page_table<T, D>(
+    walk: &mut PageTableWalk<T, D>,
+    cur_level: usize,
+    table: &mut T,
+    range: VRange,
+) where
+    T: PageTable,
+{
+    let level = walk.levels[cur_level];
+
+    let page_size = level.page_size();
+    let mut addr = range.start();
+
+    while addr < range.end() {
+        let idx = level.index_of(addr);
+        let entry = table.index_mut(idx);
+
+        let mut next_table = None;
+        if cur_level < walk.levels.len() - 1 {
+            next_table = try_get_table(entry, &mut walk.data, walk.fill_entry[cur_level]);
+        }
+
+        match (
+            walk.walk_entry[cur_level](&mut walk.data, entry),
+            &mut next_table,
+        ) {
+            (WalkState::Break, _) => break,
+            (WalkState::Next, Some(next_table)) => _walk_page_table(
+                walk,
+                cur_level + 1,
+                next_table,
+                VRange::new(addr, range.end()),
+            ),
+            // `fill_entry` says that we shouldn't continue.
+            (WalkState::Next, None) => {}
+            _ => {}
+        }
+
+        addr = addr.floor_to(page_size) + page_size;
+    }
+}
+
+pub fn walk_page_table<T, D>(walk: &mut PageTableWalk<T, D>, table: &mut T, range: VRange)
+where
+    T: PageTable,
+{
+    _walk_page_table(walk, 0, table, range);
+}
+
+pub fn drop_user_page_table<T>(mut root_page_table: T)
+where
+    T: PageTable,
+{
+    fn walk<T: PageTable, const LEVEL: usize>(_: &mut (), entry: &mut T::Entry) -> WalkState {
+        let (pfn, attr) = entry.get();
+        let Some(attr) = attr.as_table_attr() else {
+            return WalkState::Skip;
+        };
+
+        if !attr.contains(TableAttribute::USER) {
+            return WalkState::Skip;
+        }
+
+        unsafe {
+            // Check `_walk_page_table`: We will and only will touch the next level of table with
+            // `next_table` holding a refcount. We take the table away from the parent table now.
+            T::take_pfn(pfn);
+        }
+
+        entry.set(PFN::from_val(0), TableAttribute::empty().into());
+
+        if LEVEL == 2 {
+            WalkState::Skip
+        } else {
+            WalkState::Next
+        }
+    }
+
+    let mut walk = PageTableWalk {
+        levels: T::LEVELS,
+        fill_entry: &[no_fill::<T, ()>, no_fill::<T, ()>, no_fill::<T, ()>],
+        walk_entry: &[walk::<T, 0>, walk::<T, 1>, walk::<T, 2>, skip_walk::<T, ()>],
+        data: (),
+    };
+
+    walk_page_table(
+        &mut walk,
+        &mut root_page_table,
+        VRange::new(VAddr::from(0), VAddr::from(0x0000_8000_0000_0000)),
+    );
+}
+
+pub fn iter_pte<T: PageTable>(
+    page_table: &mut T,
+    range: VRange,
+    fill_func: impl FnMut(&mut T::Entry) -> Option<PFN>,
+    for_each: impl FnMut(&mut T::Entry),
+) {
+    let walker = (fill_func, for_each);
+
+    fn fill_entry<T: PageTable>(
+        (fill, _): &mut (
+            impl FnMut(&mut T::Entry) -> Option<PFN>,
+            impl FnMut(&mut T::Entry),
+        ),
+        entry: &mut T::Entry,
+    ) -> Option<PFN> {
+        fill(entry)
+    }
+
+    fn walk_entry<T: PageTable>(
+        (_, for_each): &mut (
+            impl FnMut(&mut T::Entry) -> Option<PFN>,
+            impl FnMut(&mut T::Entry),
+        ),
+        entry: &mut T::Entry,
+    ) -> WalkState {
+        for_each(entry);
+        WalkState::Next
+    }
+
+    let mut walk = PageTableWalk {
+        levels: T::LEVELS,
+        fill_entry: &[fill_entry::<T>, fill_entry::<T>, fill_entry::<T>],
+        walk_entry: &[
+            cont_walk::<T, _>,
+            cont_walk::<T, _>,
+            cont_walk::<T, _>,
+            walk_entry::<T>,
+        ],
+        data: walker,
+    };
+
+    walk_page_table(&mut walk, page_table, range);
+}
+
+pub fn no_fill<T, D>(_: &mut D, _: &mut T::Entry) -> Option<PFN>
+where
+    T: PageTable,
+{
+    None
+}
+
+pub fn skip_walk<T, D>(_: &mut D, _: &mut T::Entry) -> WalkState
+where
+    T: PageTable,
+{
+    WalkState::Skip
+}
+
+pub fn cont_walk<T, D>(_: &mut D, _: &mut T::Entry) -> WalkState
+where
+    T: PageTable,
+{
+    WalkState::Next
+}

+ 3 - 5
crates/eonix_mm/src/paging.rs

@@ -2,12 +2,10 @@ mod list;
 mod page;
 mod page_alloc;
 mod pfn;
-mod raw_page;
 mod zone;
 
-pub use list::{PageList, PageListSized};
-pub use page::{Page, PageAccess, PageBlock, PAGE_SIZE, PAGE_SIZE_BITS};
-pub use page_alloc::{GlobalPageAlloc, NoAlloc, PageAlloc};
+pub use list::{FolioList, FolioListSized};
+pub use page::{BasicFolio, Folio, PageAccess, PageBlock, PAGE_SIZE, PAGE_SIZE_BITS};
+pub use page_alloc::{FrameAlloc, GlobalFrameAlloc};
 pub use pfn::PFN;
-pub use raw_page::{RawPage, UnmanagedRawPage};
 pub use zone::Zone;

+ 7 - 7
crates/eonix_mm/src/paging/list.rs

@@ -1,16 +1,16 @@
-pub trait PageList {
-    type Page;
+pub trait FolioList {
+    type Folio;
 
     fn is_empty(&self) -> bool;
 
-    fn peek_head(&mut self) -> Option<&mut Self::Page>;
+    fn peek_head(&mut self) -> Option<&mut Self::Folio>;
 
-    fn pop_head(&mut self) -> Option<&'static mut Self::Page>;
-    fn push_tail(&mut self, page: &'static mut Self::Page);
-    fn remove(&mut self, page: &mut Self::Page);
+    fn pop_head(&mut self) -> Option<&'static mut Self::Folio>;
+    fn push_tail(&mut self, page: &'static mut Self::Folio);
+    fn remove(&mut self, page: &mut Self::Folio);
 }
 
-pub trait PageListSized: PageList + Sized {
+pub trait FolioListSized: FolioList + Sized {
     const NEW: Self;
 
     fn new() -> Self {

+ 45 - 268
crates/eonix_mm/src/paging/page.rs

@@ -1,6 +1,8 @@
-use super::{GlobalPageAlloc, PageAlloc, RawPage as _, PFN};
-use crate::address::{AddrRange, PAddr, PhysAccess};
-use core::{fmt, mem::ManuallyDrop, ptr::NonNull, sync::atomic::Ordering};
+use core::mem::ManuallyDrop;
+use core::ptr::NonNull;
+
+use super::PFN;
+use crate::address::{PAddr, PRange};
 
 pub const PAGE_SIZE: usize = 4096;
 pub const PAGE_SIZE_BITS: u32 = PAGE_SIZE.trailing_zeros();
@@ -15,306 +17,81 @@ pub struct PageBlock([u8; PAGE_SIZE]);
 
 /// A trait that provides the kernel access to the page.
 #[doc(notable_trait)]
-pub trait PageAccess {
+pub trait PageAccess: Clone {
     /// Returns a kernel-accessible pointer to the page referenced by the given
     /// physical frame number.
     ///
     /// # Safety
     /// This function is unsafe because calling this function on some non-existing
     /// pfn will cause undefined behavior.
-    unsafe fn get_ptr_for_pfn(pfn: PFN) -> NonNull<PageBlock>;
+    unsafe fn get_ptr_for_pfn(&self, pfn: PFN) -> NonNull<PageBlock>;
 
     /// Returns a kernel-accessible pointer to the given page.
-    fn get_ptr_for_page<A: PageAlloc>(page: &Page<A>) -> NonNull<PageBlock> {
+    fn get_ptr_for_page<F: Folio>(&self, page: &F) -> NonNull<PageBlock> {
         unsafe {
             // SAFETY: `page.pfn()` is guaranteed to be valid.
-            Self::get_ptr_for_pfn(page.pfn())
+            self.get_ptr_for_pfn(page.pfn())
         }
     }
 }
 
-/// A Page allocated in allocator `A`.
-#[derive(PartialEq, Eq, PartialOrd, Ord)]
-pub struct Page<A: PageAlloc> {
-    raw_page: A::RawPage,
-    alloc: A,
-}
-
-unsafe impl<A: PageAlloc> Send for Page<A> {}
-unsafe impl<A: PageAlloc> Sync for Page<A> {}
-
-impl<A> Page<A>
-where
-    A: GlobalPageAlloc,
-{
-    /// Allocate a page of the given *order*.
-    pub fn alloc_order(order: u32) -> Self {
-        Self::alloc_order_in(order, A::global())
-    }
-
-    /// Allocate exactly one page.
-    pub fn alloc() -> Self {
-        Self::alloc_in(A::global())
-    }
+/// A [`Folio`] represents one page or a bunch of adjacent pages.
+pub trait Folio {
+    /// Returns the physical frame number of the folio, which is aligned with
+    /// the folio's size and valid.
+    fn pfn(&self) -> PFN;
 
-    /// Allocate a contiguous block of pages that can contain at least `count` pages.
-    pub fn alloc_at_least(count: usize) -> Self {
-        Self::alloc_at_least_in(count, A::global())
-    }
+    /// Returns the folio's *order* (log2 of the number of pages contained in
+    /// the folio).
+    fn order(&self) -> u32;
 
-    /// 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()) }
-    }
-
-    /// 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()) }
-    }
-
-    /// 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, A::global(), func) }
-    }
-
-    /// 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 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_in(pfn: PFN, alloc: A) -> Self {
-        Self {
-            raw_page: A::RawPage::from(pfn),
-            alloc,
-        }
+    /// Returns the total size of the folio in bytes.
+    fn len(&self) -> usize {
+        1 << (self.order() + PAGE_SIZE_BITS)
     }
 
-    /// 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_in(pfn: PFN, alloc: A) -> Self {
-        unsafe {
-            // SAFETY: The caller guarantees that the page is inside the global page array.
-            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_in(pfn, alloc)
-        }
+    /// Returns the start physical address of the folio, which is guaranteed to
+    /// be aligned to the folio's size and valid.
+    fn start(&self) -> PAddr {
+        PAddr::from(self.pfn())
     }
 
-    /// Do some work with the page without touching the reference count with the same
-    /// restrictions as `from_raw_in()`.
-    ///
-    /// # Safety
-    /// Check `from_raw_in()` for the safety requirements.
-    pub unsafe fn with_raw_in<F, O>(pfn: PFN, alloc: A, func: F) -> O
-    where
-        F: FnOnce(&Self) -> O,
-    {
-        unsafe {
-            let me = ManuallyDrop::new(Self::from_raw_in(pfn, alloc));
-            func(&me)
-        }
+    /// Returns the physical address range of the ifolio, which is guaranteed to
+    /// be aligned to the folio's size and valid.
+    fn range(&self) -> PRange {
+        PRange::from(self.start()).grow(self.len())
     }
 
-    /// 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_in()` for the safety requirements.
-    pub unsafe fn with_raw_unchecked_in<F, O>(pfn: PFN, func: F, alloc: A) -> O
+    /// Consumes the folio and returns the PFN without dropping the reference
+    /// count the folio holds.
+    fn into_raw(self) -> PFN
     where
-        F: FnOnce(&Self) -> O,
+        Self: Sized,
     {
-        unsafe {
-            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 {
         let me = ManuallyDrop::new(self);
         me.pfn()
     }
-
-    /// Returns the physical frame number of the page, which is aligned with the
-    /// page size and valid.
-    pub fn pfn(&self) -> PFN {
-        Into::<PFN>::into(self.raw_page)
-    }
-
-    /// Returns the start physical address of the page, which is guaranteed to be
-    /// aligned to the page size and valid.
-    pub fn start(&self) -> PAddr {
-        PAddr::from(self.pfn())
-    }
-
-    /// Returns the physical address range of the page, which is guaranteed to be
-    /// aligned to the page size and valid.
-    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> 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`.
-        self.raw_page.refcount().fetch_add(1, Ordering::Relaxed);
-
-        Self {
-            raw_page: self.raw_page,
-            alloc: self.alloc.clone(),
-        }
-    }
+/// A simple [`Folio`] with no reference counting or other ownership mechanism.
+#[derive(Clone)]
+pub struct BasicFolio {
+    pfn: PFN,
+    order: u32,
 }
 
-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!(self.alloc.has_management_over(self.raw_page));
-
-                // SAFETY: `self.raw_page` is managed by the allocator and we're dropping the page.
-                self.alloc.dealloc(self.raw_page)
-            },
-            _ => {}
-        }
+impl BasicFolio {
+    pub const fn new(pfn: PFN, order: u32) -> Self {
+        Self { pfn, order }
     }
 }
 
-impl<A: PageAlloc> fmt::Debug for Page<A> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Page({:?}, order={})",
-            Into::<PFN>::into(self.raw_page),
-            self.order()
-        )
+impl Folio for BasicFolio {
+    fn pfn(&self) -> PFN {
+        self.pfn
     }
-}
 
-impl<T> PageAccess for T
-where
-    T: PhysAccess,
-{
-    unsafe fn get_ptr_for_pfn(pfn: PFN) -> NonNull<PageBlock> {
-        unsafe {
-            // SAFETY: The physical address of a existing page must be
-            //         aligned to the page size.
-            T::as_ptr(PAddr::from(pfn))
-        }
+    fn order(&self) -> u32 {
+        self.order
     }
 }

+ 17 - 62
crates/eonix_mm/src/paging/page_alloc.rs

@@ -1,89 +1,44 @@
-use super::{raw_page::UnmanagedRawPage, RawPage};
+use super::Folio;
 
-/// A trait for allocating and deallocating pages of memory.
+/// A trait for allocating and deallocating folios.
 ///
 /// 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.
-#[doc(notable_trait)]
-pub trait PageAlloc: Clone {
-    type RawPage: RawPage;
+pub trait FrameAlloc: Clone {
+    type Folio: Folio;
 
-    /// Allocate a page of the given *order*.
-    fn alloc_order(&self, order: u32) -> Option<Self::RawPage>;
+    /// Allocate a folio of the given *order*.
+    fn alloc_order(&self, order: u32) -> Option<Self::Folio>;
 
-    /// Allocate exactly one page.
-    fn alloc(&self) -> Option<Self::RawPage> {
+    /// Allocate exactly one folio.
+    fn alloc(&self) -> Option<Self::Folio> {
         self.alloc_order(0)
     }
 
-    /// Allocate a contiguous block of pages that can contain at least `count` pages.
-    fn alloc_at_least(&self, count: usize) -> Option<Self::RawPage> {
+    /// Allocate a folio that can contain at least [`count`] contiguous pages.
+    fn alloc_at_least(&self, count: usize) -> Option<Self::Folio> {
         let order = count.next_power_of_two().trailing_zeros();
         self.alloc_order(order)
     }
-
-    /// Deallocate a page.
-    ///
-    /// # Safety
-    /// 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.
-    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.
-#[doc(notable_trait)]
-pub trait GlobalPageAlloc: PageAlloc + 'static {
-    /// Get the global page allocator.
-    fn global() -> Self;
+pub trait GlobalFrameAlloc: FrameAlloc + 'static {
+    /// The global page allocator.
+    const GLOBAL: Self;
 }
 
-#[derive(Clone)]
-pub struct NoAlloc;
-
-impl<'a, A> PageAlloc for &'a A
+impl<'a, A> FrameAlloc for &'a A
 where
-    A: PageAlloc,
+    A: FrameAlloc,
 {
-    type RawPage = A::RawPage;
+    type Folio = A::Folio;
 
-    fn alloc_order(&self, order: u32) -> Option<Self::RawPage> {
+    fn alloc_order(&self, order: u32) -> Option<Self::Folio> {
         (*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)
-    }
-}
-
-impl PageAlloc for NoAlloc {
-    type RawPage = UnmanagedRawPage;
-
-    fn alloc_order(&self, _: u32) -> Option<Self::RawPage> {
-        panic!("`NoAlloc` cannot allocate pages");
-    }
-
-    unsafe fn dealloc(&self, _: Self::RawPage) {
-        panic!("`NoAlloc` cannot free pages");
-    }
-
-    fn has_management_over(&self, _: Self::RawPage) -> bool {
-        true
-    }
-}
-
-impl GlobalPageAlloc for NoAlloc {
-    fn global() -> Self {
-        Self
-    }
 }

+ 0 - 47
crates/eonix_mm/src/paging/raw_page.rs

@@ -1,47 +0,0 @@
-use core::sync::atomic::AtomicUsize;
-
-use super::PFN;
-
-/// 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.
-#[doc(notable_trait)]
-pub trait RawPage: Clone + Copy + From<PFN> + Into<PFN> {
-    fn order(&self) -> u32;
-    fn refcount(&self) -> &AtomicUsize;
-}
-
-#[derive(Clone, Copy)]
-pub struct UnmanagedRawPage(PFN, u32);
-
-/// Unmanaged raw pages should always have a non-zero refcount to
-/// avoid `free()` from being called.
-static UNMANAGED_RAW_PAGE_CLONE_COUNT: AtomicUsize = AtomicUsize::new(1);
-
-impl UnmanagedRawPage {
-    pub const fn new(pfn: PFN, order: u32) -> Self {
-        Self(pfn, order)
-    }
-}
-
-impl From<PFN> for UnmanagedRawPage {
-    fn from(value: PFN) -> Self {
-        Self::new(value, 0)
-    }
-}
-
-impl Into<PFN> for UnmanagedRawPage {
-    fn into(self) -> PFN {
-        let Self(pfn, _) = self;
-        pfn
-    }
-}
-
-impl RawPage for UnmanagedRawPage {
-    fn order(&self) -> u32 {
-        self.1
-    }
-
-    fn refcount(&self) -> &AtomicUsize {
-        &UNMANAGED_RAW_PAGE_CLONE_COUNT
-    }
-}

+ 3 - 4
crates/eonix_mm/src/paging/zone.rs

@@ -1,7 +1,6 @@
-use core::cell::UnsafeCell;
+use core::ptr::NonNull;
 
-#[allow(unused_imports)]
-use super::{Page, PageAlloc, RawPage, PFN};
+use super::PFN;
 use crate::address::PRange;
 
 /// A [`Zone`] holds a lot of [`Page`]s that share the same NUMA node or
@@ -16,5 +15,5 @@ pub trait Zone: Send + Sync {
     ///
     /// # Return
     /// [`None`] if [`pfn`] is not in this [`Zone`].
-    fn get_page(&self, pfn: PFN) -> Option<&UnsafeCell<Self::Page>>;
+    fn get_page(&self, pfn: PFN) -> Option<NonNull<Self::Page>>;
 }

+ 21 - 23
crates/slab_allocator/src/lib.rs

@@ -2,7 +2,7 @@
 
 use core::ptr::NonNull;
 
-use eonix_mm::paging::{PageList, PageListSized};
+use eonix_mm::paging::{FolioList, FolioListSized};
 use eonix_sync::Spin;
 
 #[repr(C)]
@@ -84,21 +84,21 @@ where
     }
 }
 
-pub trait SlabPageAlloc {
+/// Allocate a page suitable for slab system use. The page MUST come with
+/// its allocation count 0 and next free slot None.
+///
+/// # Safety
+/// The page returned MUST have been properly initialized after allocation.
+pub unsafe trait SlabPageAlloc {
     type Page: SlabPage;
-    type PageList: PageList<Page = Self::Page>;
+    type PageList: FolioList<Folio = Self::Page>;
 
-    /// Allocate a page suitable for slab system use. The page MUST come with
-    /// its allocation count 0 and next free slot None.
-    ///
-    /// # Safety
-    /// The page returned MUST be properly initialized before its usage.
-    unsafe fn alloc_uninit(&self) -> &'static mut Self::Page;
+    fn alloc_slab_page(&self) -> &'static mut Self::Page;
 }
 
 pub(crate) struct SlabList<T>
 where
-    T: PageList,
+    T: FolioList,
 {
     empty_list: T,
     partial_list: T,
@@ -120,7 +120,7 @@ unsafe impl<P, const COUNT: usize> Sync for SlabAlloc<P, COUNT> where P: SlabPag
 impl<L, const COUNT: usize> SlabAlloc<L, COUNT>
 where
     L: SlabPageAlloc,
-    L::PageList: PageListSized,
+    L::PageList: FolioListSized,
 {
     pub fn new_in(alloc: L) -> Self {
         Self {
@@ -148,7 +148,7 @@ where
 
 impl<T> SlabList<T>
 where
-    T: PageListSized,
+    T: FolioListSized,
 {
     const fn new(object_size: usize) -> Self {
         Self {
@@ -162,8 +162,8 @@ where
 
 impl<T> SlabList<T>
 where
-    T: PageList,
-    T::Page: SlabPage,
+    T: FolioList,
+    T::Folio: SlabPage,
 {
     fn alloc_from_partial(&mut self) -> NonNull<u8> {
         let head = self.partial_list.peek_head().unwrap();
@@ -190,18 +190,16 @@ where
         slot
     }
 
-    fn charge(&mut self, alloc: &impl SlabPageAlloc<Page = T::Page>) {
-        unsafe {
-            let slab = alloc.alloc_uninit();
-            let free_slot = make_slab_page(slab.get_data_ptr(), self.object_size);
+    fn charge(&mut self, alloc: &impl SlabPageAlloc<Page = T::Folio>) {
+        let slab = alloc.alloc_slab_page();
+        let free_slot = make_slab_page(slab.get_data_ptr(), self.object_size);
 
-            slab.set_free_slot(Some(free_slot));
+        slab.set_free_slot(Some(free_slot));
 
-            self.empty_list.push_tail(slab);
-        }
+        self.empty_list.push_tail(slab);
     }
 
-    fn alloc(&mut self, alloc: &impl SlabPageAlloc<Page = T::Page>) -> NonNull<u8> {
+    fn alloc(&mut self, alloc: &impl SlabPageAlloc<Page = T::Folio>) -> NonNull<u8> {
         if !self.partial_list.is_empty() {
             return self.alloc_from_partial();
         }
@@ -216,7 +214,7 @@ where
     unsafe fn dealloc(&mut self, ptr: NonNull<u8>, _alloc: &impl SlabPageAlloc) {
         let slab_page = unsafe {
             // SAFETY:
-            <T::Page>::from_allocated(ptr)
+            <T::Folio>::from_allocated(ptr)
         };
 
         let (was_full, is_empty);

+ 15 - 13
src/driver/ahci/command.rs

@@ -1,9 +1,11 @@
+use eonix_mm::paging::Folio as _;
+
 use crate::kernel::constants::EINVAL;
-use crate::kernel::mem::paging::Page;
+use crate::kernel::mem::Folio;
 use crate::prelude::*;
 
 pub trait Command {
-    fn pages(&self) -> &[Page];
+    fn pages(&self) -> &[Folio];
     fn lba(&self) -> u64;
 
     // in sectors
@@ -14,19 +16,19 @@ pub trait Command {
 }
 
 pub struct IdentifyCommand {
-    page: Page,
+    page: Folio,
 }
 
 impl IdentifyCommand {
     pub fn new() -> Self {
         Self {
-            page: Page::alloc(),
+            page: Folio::alloc(),
         }
     }
 }
 
 impl Command for IdentifyCommand {
-    fn pages(&self) -> &[Page] {
+    fn pages(&self) -> &[Folio] {
         core::slice::from_ref(&self.page)
     }
 
@@ -47,14 +49,14 @@ impl Command for IdentifyCommand {
     }
 }
 
-pub struct ReadLBACommand<'lt> {
-    pages: &'lt [Page],
+pub struct ReadLBACommand<'a> {
+    pages: &'a [Folio],
     lba: u64,
     count: u16,
 }
 
-impl<'lt> ReadLBACommand<'lt> {
-    pub fn new(pages: &'lt [Page], lba: u64, count: u16) -> KResult<Self> {
+impl<'a> ReadLBACommand<'a> {
+    pub fn new(pages: &'a [Folio], lba: u64, count: u16) -> KResult<Self> {
         if pages.len() > 248 {
             return Err(EINVAL);
         }
@@ -69,7 +71,7 @@ impl<'lt> ReadLBACommand<'lt> {
 }
 
 impl Command for ReadLBACommand<'_> {
-    fn pages(&self) -> &[Page] {
+    fn pages(&self) -> &[Folio] {
         self.pages
     }
 
@@ -91,13 +93,13 @@ impl Command for ReadLBACommand<'_> {
 }
 
 pub struct WriteLBACommand<'a> {
-    pages: &'a [Page],
+    pages: &'a [Folio],
     lba: u64,
     count: u16,
 }
 
 impl<'a> WriteLBACommand<'a> {
-    pub fn new(pages: &'a [Page], lba: u64, count: u16) -> KResult<Self> {
+    pub fn new(pages: &'a [Folio], lba: u64, count: u16) -> KResult<Self> {
         if pages.len() > 248 {
             return Err(EINVAL);
         }
@@ -112,7 +114,7 @@ impl<'a> WriteLBACommand<'a> {
 }
 
 impl Command for WriteLBACommand<'_> {
-    fn pages(&self) -> &[Page] {
+    fn pages(&self) -> &[Folio] {
         self.pages
     }
 

+ 4 - 3
src/driver/ahci/command_table.rs

@@ -1,13 +1,14 @@
 use core::ptr::NonNull;
 
 use eonix_mm::address::PAddr;
+use eonix_mm::paging::Folio as _;
 
 use super::command::Command;
 use super::{PRDTEntry, FISH2D};
-use crate::kernel::mem::{Page, PageExt};
+use crate::kernel::mem::FolioOwned;
 
 pub struct CommandTable {
-    page: Page,
+    page: FolioOwned,
     cmd_fis: NonNull<FISH2D>,
     prdt: NonNull<[PRDTEntry; 248]>,
     prdt_entries: usize,
@@ -18,7 +19,7 @@ unsafe impl Sync for CommandTable {}
 
 impl CommandTable {
     pub fn new() -> Self {
-        let page = Page::alloc();
+        let page = FolioOwned::alloc();
         let base = page.get_ptr();
 
         unsafe {

+ 4 - 2
src/driver/ahci/defs.rs

@@ -1,7 +1,9 @@
 #![allow(dead_code)]
 
-use crate::kernel::mem::paging::Page;
 use eonix_mm::address::Addr as _;
+use eonix_mm::paging::Folio as _;
+
+use crate::kernel::mem::Folio;
 
 pub const VENDOR_INTEL: u16 = 0x8086;
 pub const DEVICE_AHCI: u16 = 0x2922;
@@ -239,7 +241,7 @@ pub struct PRDTEntry {
 }
 
 impl PRDTEntry {
-    pub fn setup(&mut self, page: &Page) {
+    pub fn setup(&mut self, page: &Folio) {
         self.base = page.start().addr() as u64;
         self._reserved1 = 0;
 

+ 6 - 4
src/driver/ahci/slot.rs

@@ -3,18 +3,18 @@ use core::ptr::NonNull;
 use core::task::{Poll, Waker};
 
 use eonix_mm::address::{Addr as _, PAddr};
+use eonix_mm::paging::Folio as _;
 use eonix_sync::{Spin, SpinIrq as _};
 
 use super::command_table::CommandTable;
 use super::CommandHeader;
 use crate::kernel::constants::EIO;
-use crate::kernel::mem::paging::AllocZeroed;
-use crate::kernel::mem::{Page, PageExt};
+use crate::kernel::mem::FolioOwned;
 use crate::KResult;
 
 pub struct CommandList {
     base: NonNull<u8>,
-    _page: Page,
+    _page: FolioOwned,
 }
 
 unsafe impl Send for CommandList {}
@@ -75,7 +75,9 @@ impl CommandList {
     }
 
     pub fn new() -> Self {
-        let page = Page::zeroed();
+        let mut page = FolioOwned::alloc();
+        page.as_bytes_mut().fill(0);
+
         let base = page.get_ptr();
 
         let controls_ptr = Self::controls_ptr(base);

+ 12 - 7
src/driver/e1000e.rs

@@ -5,11 +5,12 @@ use core::ptr::NonNull;
 use async_trait::async_trait;
 use eonix_hal::fence::memory_barrier;
 use eonix_mm::address::{Addr, PAddr};
+use eonix_mm::paging::Folio as _;
 use eonix_sync::SpinIrq;
 
 use crate::kernel::constants::{EAGAIN, EFAULT, EINVAL, EIO};
 use crate::kernel::interrupt::register_irq_handler;
-use crate::kernel::mem::{PageExcl, PageExt, PhysAccess};
+use crate::kernel::mem::{FolioOwned, PhysAccess};
 use crate::kernel::pcie::{self, Header, PCIDevice, PCIDriver, PciError};
 use crate::net::netdev;
 use crate::prelude::*;
@@ -54,13 +55,13 @@ struct E1000eDev {
     id: u32,
 
     regs: Registers,
-    rt_desc_page: PageExcl,
+    rt_desc_page: FolioOwned,
     rx_head: Option<u32>,
     rx_tail: Option<u32>,
     tx_tail: Option<u32>,
 
-    rx_buffers: Box<[PageExcl; RX_DESC_SIZE]>,
-    tx_buffers: Box<[Option<PageExcl>; TX_DESC_SIZE]>,
+    rx_buffers: Box<[FolioOwned; RX_DESC_SIZE]>,
+    tx_buffers: Box<[Option<FolioOwned>; TX_DESC_SIZE]>,
 }
 
 fn test(val: u32, bit: u32) -> bool {
@@ -227,7 +228,7 @@ impl netdev::Netdev for E1000eDev {
             return Err(EIO);
         }
 
-        let mut buffer_page = PageExcl::alloc();
+        let mut buffer_page = FolioOwned::alloc();
         if buf.len() > buffer_page.len() {
             return Err(EFAULT);
         }
@@ -363,11 +364,15 @@ impl E1000eDev {
             speed: netdev::LinkSpeed::SpeedUnknown,
             id: netdev::alloc_id(),
             regs,
-            rt_desc_page: PageExcl::zeroed(),
+            rt_desc_page: {
+                let mut folio = FolioOwned::alloc();
+                folio.as_bytes_mut().fill(0);
+                folio
+            },
             rx_head: None,
             rx_tail: None,
             tx_tail: None,
-            rx_buffers: Box::new(core::array::from_fn(|_| PageExcl::alloc_order(2))),
+            rx_buffers: Box::new(core::array::from_fn(|_| FolioOwned::alloc_order(2))),
             tx_buffers: Box::new([const { None }; 32]),
         };
 

+ 4 - 4
src/driver/virtio/virtio_blk.rs

@@ -3,7 +3,7 @@ use alloc::boxed::Box;
 use async_trait::async_trait;
 use eonix_hal::mm::ArchPhysAccess;
 use eonix_mm::address::{Addr, PAddr, PhysAccess};
-use eonix_mm::paging::PFN;
+use eonix_mm::paging::{Folio as _, PFN};
 use eonix_sync::Spin;
 use virtio_drivers::device::blk::VirtIOBlk;
 use virtio_drivers::transport::Transport;
@@ -12,7 +12,7 @@ use virtio_drivers::Hal;
 use crate::io::Chunks;
 use crate::kernel::block::{BlockDeviceRequest, BlockRequestQueue};
 use crate::kernel::constants::EIO;
-use crate::kernel::mem::{Page, PageExt};
+use crate::kernel::mem::Folio;
 use crate::prelude::KResult;
 
 pub struct HAL;
@@ -22,7 +22,7 @@ unsafe impl Hal for HAL {
         pages: usize,
         _direction: virtio_drivers::BufferDirection,
     ) -> (virtio_drivers::PhysAddr, core::ptr::NonNull<u8>) {
-        let page = Page::alloc_at_least(pages);
+        let page = Folio::alloc_at_least(pages);
 
         let ptr = page.get_ptr();
         let pfn = page.into_raw();
@@ -40,7 +40,7 @@ unsafe impl Hal for HAL {
         unsafe {
             // SAFETY: The caller ensures that the pfn corresponds to a valid
             //         page allocated by `dma_alloc`.
-            Page::from_raw(pfn);
+            Folio::from_raw(pfn);
         }
 
         0

+ 5 - 7
src/fs/fat32.rs

@@ -1,5 +1,4 @@
 mod dir;
-mod file;
 
 use alloc::sync::Arc;
 use core::ops::Deref;
@@ -13,7 +12,7 @@ use itertools::Itertools;
 use crate::io::{Buffer, ByteBuffer, UninitBuffer};
 use crate::kernel::block::{BlockDevice, BlockDeviceRequest};
 use crate::kernel::constants::{EINVAL, EIO};
-use crate::kernel::mem::{CachePage, Page, PageExcl, PageExt, PageOffset};
+use crate::kernel::mem::{CachePage, Folio, FolioOwned, PageOffset};
 use crate::kernel::timer::Instant;
 use crate::kernel::vfs::dentry::Dentry;
 use crate::kernel::vfs::inode::{Ino, InodeInfo, InodeOps, InodeUse};
@@ -114,7 +113,7 @@ struct FatFs {
 impl SuperBlock for FatFs {}
 
 impl FatFs {
-    async fn read_cluster(&self, mut cluster: Cluster, buf: &Page) -> KResult<()> {
+    async fn read_cluster(&self, mut cluster: Cluster, buf: &Folio) -> KResult<()> {
         cluster = cluster.normalized();
 
         let rq = BlockDeviceRequest::Read {
@@ -278,7 +277,6 @@ impl InodeOps for FileInode {
             .next()
             .ok_or(EIO)?;
 
-        let page = page.get_page();
         fs.read_cluster(cluster, &page).await?;
 
         let real_len = (inode.info.lock().size as usize) - offset.byte_count();
@@ -293,7 +291,7 @@ impl InodeOps for FileInode {
 
 struct DirInode {
     // TODO: Use the new PageCache...
-    dir_pages: RwLock<Vec<PageExcl>>,
+    dir_pages: RwLock<Vec<FolioOwned>>,
 }
 
 impl DirInode {
@@ -330,7 +328,7 @@ impl DirInode {
         let clusters = ClusterIterator::new(fat.as_ref(), Cluster::from_ino(inode.ino));
 
         for cluster in clusters {
-            let page = PageExcl::alloc();
+            let page = FolioOwned::alloc();
             fs.read_cluster(cluster, &page).await?;
 
             dir_pages.push(page);
@@ -343,7 +341,7 @@ impl DirInode {
         &self,
         sb: &SbUse<FatFs>,
         inode: &InodeUse,
-    ) -> KResult<impl Deref<Target = Vec<PageExcl>> + use<'_>> {
+    ) -> KResult<impl Deref<Target = Vec<FolioOwned>> + use<'_>> {
         {
             let dir_pages = self.dir_pages.read().await;
             if !dir_pages.is_empty() {

+ 0 - 24
src/fs/fat32/file.rs

@@ -1,24 +0,0 @@
-use futures::Stream;
-
-use crate::{kernel::mem::Page, prelude::KResult};
-
-use super::{ClusterIterator, FatFs};
-
-pub trait ReadClusters {
-    fn read_clusters(self, fs: &FatFs) -> impl Stream<Item = KResult<Page>> + Send;
-}
-
-impl ReadClusters for ClusterIterator<'_> {
-    fn read_clusters(self, fs: &FatFs) -> impl Stream<Item = KResult<Page>> + Send {
-        futures::stream::unfold(self, move |mut me| async {
-            let cluster = me.next()?;
-            let page = Page::alloc();
-
-            if let Err(err) = fs.read_cluster(cluster, &page).await {
-                return Some((Err(err), me));
-            }
-
-            Some((Ok(page), me))
-        })
-    }
-}

+ 1 - 1
src/fs/tmpfs/file.rs

@@ -125,7 +125,7 @@ impl InodeOps for FileInode {
         page: &mut CachePage,
         _: PageOffset,
     ) -> KResult<()> {
-        page.as_bytes_mut().fill(0);
+        page.lock().as_bytes_mut().fill(0);
         Ok(())
     }
 

+ 8 - 9
src/kernel/block.rs

@@ -8,8 +8,7 @@ use async_trait::async_trait;
 use mbr::MBRPartTable;
 
 use super::constants::ENOENT;
-use super::mem::paging::Page;
-use super::mem::PageExt;
+use super::mem::Folio;
 use super::vfs::types::DeviceId;
 use crate::io::{Buffer, Chunks, FillResult};
 use crate::kernel::constants::{EEXIST, EINVAL};
@@ -202,15 +201,15 @@ impl BlockDevice {
             let (page_slice, page, mut page_vec);
             match nr_batch {
                 ..=8 => {
-                    page = Page::alloc();
+                    page = Folio::alloc();
                     page_slice = core::slice::from_ref(&page);
                 }
                 ..=16 => {
-                    page = Page::alloc_order(1);
+                    page = Folio::alloc_order(1);
                     page_slice = core::slice::from_ref(&page);
                 }
                 ..=32 => {
-                    page = Page::alloc_order(2);
+                    page = Folio::alloc_order(2);
                     page_slice = core::slice::from_ref(&page);
                 }
                 count => {
@@ -220,8 +219,8 @@ impl BlockDevice {
                     let nr_pages = nr_huge_pages + nr_small_pages;
                     page_vec = Vec::with_capacity(nr_pages);
 
-                    page_vec.resize_with(nr_huge_pages, || Page::alloc_order(2));
-                    page_vec.resize_with(nr_pages, || Page::alloc());
+                    page_vec.resize_with(nr_huge_pages, || Folio::alloc_order(2));
+                    page_vec.resize_with(nr_pages, || Folio::alloc());
                     page_slice = &page_vec;
                 }
             }
@@ -266,7 +265,7 @@ pub enum BlockDeviceRequest<'lt> {
         /// Number of sectors to read
         count: u64,
         /// Buffer pages to read into
-        buffer: &'lt [Page],
+        buffer: &'lt [Folio],
     },
     Write {
         /// Sector to write to, in 512-byte blocks
@@ -274,6 +273,6 @@ pub enum BlockDeviceRequest<'lt> {
         /// Number of sectors to write
         count: u64,
         /// Buffer pages to write from
-        buffer: &'lt [Page],
+        buffer: &'lt [Folio],
     },
 }

+ 3 - 1
src/kernel/mem.rs

@@ -3,14 +3,16 @@ pub mod paging;
 mod access;
 mod address;
 mod allocator;
+mod folio;
 mod mm_area;
 mod mm_list;
 mod page_alloc;
 mod page_cache;
 
 pub use access::PhysAccess;
+pub use folio::{Folio, FolioOwned, LockedFolio};
 pub(self) use mm_area::MMArea;
 pub use mm_list::{handle_kernel_page_fault, FileMapping, MMList, Mapping, Permission};
 pub use page_alloc::{GlobalPageAlloc, RawPage};
 pub use page_cache::{CachePage, PageCache, PageOffset};
-pub use paging::{Page, PageBuffer, PageExcl, PageExt};
+pub use paging::PageBuffer;

+ 12 - 14
src/kernel/mem/allocator.rs

@@ -3,11 +3,12 @@ use core::ptr::NonNull;
 
 use eonix_hal::mm::ArchPhysAccess;
 use eonix_mm::address::PhysAccess;
-use eonix_mm::paging::{PAGE_SIZE_BITS, PFN};
+use eonix_mm::paging::{Folio as _, PAGE_SIZE_BITS, PFN};
 use eonix_sync::LazyLock;
 use slab_allocator::SlabAlloc;
 
-use super::{GlobalPageAlloc, Page, PageExt};
+use super::folio::Folio;
+use super::GlobalPageAlloc;
 
 static SLAB_ALLOCATOR: LazyLock<SlabAlloc<GlobalPageAlloc, 9>> =
     LazyLock::new(|| SlabAlloc::new_in(GlobalPageAlloc));
@@ -18,19 +19,15 @@ unsafe impl GlobalAlloc for Allocator {
     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
         let size = layout.size().next_power_of_two();
 
-        let result = if size <= 2048 {
-            SLAB_ALLOCATOR.alloc(size)
+        if size <= 2048 {
+            SLAB_ALLOCATOR.alloc(size).as_ptr()
         } else {
-            let page_count = size >> PAGE_SIZE_BITS;
-            let page = Page::alloc_at_least(page_count);
-
-            let ptr = page.get_ptr();
-            page.into_raw();
+            let folio = Folio::alloc_at_least(size >> PAGE_SIZE_BITS);
+            let ptr = folio.get_ptr();
+            folio.into_raw();
 
-            ptr
-        };
-
-        result.as_ptr()
+            ptr.as_ptr()
+        }
     }
 
     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
@@ -45,7 +42,8 @@ unsafe impl GlobalAlloc for Allocator {
         } else {
             let paddr = ArchPhysAccess::from_ptr(ptr);
             let pfn = PFN::from(paddr);
-            Page::from_raw(pfn);
+
+            Folio::from_raw(pfn);
         };
     }
 }

+ 210 - 0
src/kernel/mem/folio.rs

@@ -0,0 +1,210 @@
+use core::fmt;
+use core::mem::ManuallyDrop;
+use core::ops::Deref;
+use core::ptr::NonNull;
+use core::sync::atomic::Ordering;
+
+use eonix_mm::paging::{Folio as FolioTrait, FrameAlloc, GlobalFrameAlloc, Zone, PFN};
+
+use super::page_alloc::ZONE;
+use super::{GlobalPageAlloc, PhysAccess as _, RawPage};
+
+#[repr(transparent)]
+pub struct Folio(NonNull<RawPage>);
+
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct FolioOwned(Folio);
+
+#[repr(transparent)]
+pub struct LockedFolio<'a>(&'a Folio);
+
+unsafe impl Send for Folio {}
+unsafe impl Sync for Folio {}
+
+impl Folio {
+    pub(super) const fn from_mut_page(raw_page: &'static mut RawPage) -> Self {
+        Self(NonNull::new(raw_page).unwrap())
+    }
+
+    /// Allocate a folio of the given *order*.
+    pub fn alloc_order(order: u32) -> Self {
+        GlobalPageAlloc::GLOBAL
+            .alloc_order(order)
+            .expect("Out of memory")
+    }
+
+    /// Allocate a folio of order 0
+    pub fn alloc() -> Self {
+        Self::alloc_order(0)
+    }
+
+    /// Allocate a folio consisting of at least [`count`] pages.
+    pub fn alloc_at_least(count: usize) -> Self {
+        GlobalPageAlloc::GLOBAL
+            .alloc_at_least(count)
+            .expect("Out of memory")
+    }
+
+    /// Acquire the ownership of the folio pointed to by [`pfn`], leaving
+    /// [`refcount`] untouched.
+    ///
+    /// # Panic
+    /// This function will panic if the folio is not within the global zone.
+    ///
+    /// # Safety
+    /// This function is unsafe because it assumes that the caller has to ensure
+    /// that [`pfn`] points to a valid folio allocated through [`Self::alloc()`]
+    /// and that the folio have not been freed or deallocated yet.
+    pub unsafe fn from_raw(pfn: PFN) -> Self {
+        unsafe {
+            // SAFETY: The caller ensures that [`pfn`] points to a folio within
+            //         the global zone.
+            Self(ZONE.get_page(pfn).unwrap_unchecked())
+        }
+    }
+
+    /// Do some work with the folio without touching the reference count with
+    /// the same restrictions as [`Self::from_raw()`].
+    ///
+    /// # Safety
+    /// Check [`Self::from_raw()`] for safety requirements.
+    pub unsafe fn with_raw<F, O>(pfn: PFN, func: F) -> O
+    where
+        F: FnOnce(&Self) -> O,
+    {
+        unsafe {
+            let me = ManuallyDrop::new(Self::from_raw(pfn));
+            func(&me)
+        }
+    }
+
+    pub fn lock(&self) -> LockedFolio {
+        // TODO: actually perform the lock...
+        LockedFolio(self)
+    }
+
+    /// Get a vmem pointer to the folio data as a byte slice.
+    pub fn get_bytes_ptr(&self) -> NonNull<[u8]> {
+        unsafe {
+            // SAFETY: `self.start()` can't be null.
+            NonNull::slice_from_raw_parts(self.start().as_ptr(), self.len())
+        }
+    }
+
+    /// Get a vmem pointer to the start of the folio.
+    pub fn get_ptr(&self) -> NonNull<u8> {
+        self.get_bytes_ptr().cast()
+    }
+}
+
+impl Deref for Folio {
+    type Target = RawPage;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            // SAFETY: We don't expose mutable references to the folio.
+            self.0.as_ref()
+        }
+    }
+}
+
+impl Clone for Folio {
+    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`.
+        self.refcount.fetch_add(1, Ordering::Relaxed);
+
+        Self(self.0)
+    }
+}
+
+impl Drop for Folio {
+    fn drop(&mut self) {
+        match self.refcount.fetch_sub(1, Ordering::AcqRel) {
+            0 => unreachable!("Refcount for an in-use page is 0"),
+            1 => unsafe { GlobalPageAlloc::GLOBAL.dealloc_raw(self.0.as_mut()) },
+            _ => {}
+        }
+    }
+}
+
+impl fmt::Debug for Folio {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Page({:?}, order={})", self.pfn(), self.order)
+    }
+}
+
+impl FolioTrait for Folio {
+    fn pfn(&self) -> PFN {
+        ZONE.get_pfn(self.0.as_ptr())
+    }
+
+    fn order(&self) -> u32 {
+        self.order
+    }
+}
+
+impl LockedFolio<'_> {
+    pub fn as_bytes(&self) -> &[u8] {
+        unsafe {
+            // SAFETY: `self.start()` points to valid memory of length `self.len()`.
+            core::slice::from_raw_parts(self.start().as_ptr().as_ptr(), self.len())
+        }
+    }
+
+    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
+        unsafe {
+            // SAFETY: `self.start()` points to valid memory of length `self.len()`.
+            core::slice::from_raw_parts_mut(self.start().as_ptr().as_ptr(), self.len())
+        }
+    }
+}
+
+impl Deref for LockedFolio<'_> {
+    type Target = Folio;
+
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+
+impl FolioOwned {
+    pub fn alloc() -> Self {
+        Self(Folio::alloc())
+    }
+
+    pub fn alloc_order(order: u32) -> Self {
+        Self(Folio::alloc_order(order))
+    }
+
+    pub fn alloc_at_least(count: usize) -> Self {
+        Self(Folio::alloc_at_least(count))
+    }
+
+    pub fn as_bytes(&self) -> &[u8] {
+        unsafe {
+            // SAFETY: The page is exclusively owned by us.
+            self.get_bytes_ptr().as_ref()
+        }
+    }
+
+    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
+        unsafe {
+            // SAFETY: The page is exclusively owned by us.
+            self.get_bytes_ptr().as_mut()
+        }
+    }
+
+    pub fn share(self) -> Folio {
+        self.0
+    }
+}
+
+impl Deref for FolioOwned {
+    type Target = Folio;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}

+ 23 - 16
src/kernel/mem/mm_area.rs

@@ -1,15 +1,16 @@
 use core::borrow::Borrow;
 use core::cell::UnsafeCell;
 use core::cmp;
+use core::sync::atomic::Ordering;
 
 use eonix_mm::address::{AddrOps as _, VAddr, VRange};
 use eonix_mm::page_table::{PageAttribute, RawAttribute, PTE};
-use eonix_mm::paging::PFN;
+use eonix_mm::paging::{Folio as _, PFN};
 
 use super::mm_list::EMPTY_PAGE;
-use super::{Mapping, Page, Permission};
-use crate::kernel::mem::page_cache::PageOffset;
-use crate::kernel::mem::{CachePage, PageExcl, PageExt};
+use super::{Mapping, Permission};
+use crate::kernel::mem::folio::Folio;
+use crate::kernel::mem::{CachePage, FolioOwned, PageOffset};
 use crate::prelude::KResult;
 
 #[derive(Debug)]
@@ -98,8 +99,10 @@ impl MMArea {
         attr.remove(PageAttribute::COPY_ON_WRITE);
         attr.set(PageAttribute::WRITE, self.permission.write);
 
-        let page = unsafe { Page::from_raw(*pfn) };
-        if page.is_exclusive() {
+        let page = unsafe { Folio::from_raw(*pfn) };
+
+        // XXX: Change me!!!
+        if page.refcount.load(Ordering::Relaxed) == 1 {
             // 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.
@@ -109,9 +112,13 @@ impl MMArea {
 
         let mut new_page;
         if *pfn == EMPTY_PAGE.pfn() {
-            new_page = PageExcl::zeroed();
+            new_page = {
+                let mut folio = FolioOwned::alloc();
+                folio.as_bytes_mut().fill(0);
+                folio
+            };
         } else {
-            new_page = PageExcl::alloc();
+            new_page = FolioOwned::alloc();
 
             unsafe {
                 // SAFETY: `page` is CoW, which means that others won't write to it.
@@ -123,7 +130,7 @@ impl MMArea {
         }
 
         attr.remove(PageAttribute::ACCESSED);
-        *pfn = new_page.into_page().into_raw();
+        *pfn = new_page.share().into_raw();
     }
 
     /// # Arguments
@@ -143,11 +150,11 @@ impl MMArea {
 
         let file_offset = file_mapping.offset + offset;
 
-        let map_page = |page: &Page, cache_page: &CachePage| {
+        let map_page = |cache_page: &CachePage| {
             if !self.permission.write {
                 assert!(!write, "Write fault on read-only mapping");
 
-                *pfn = page.clone().into_raw();
+                *pfn = cache_page.add_mapping();
                 return;
             }
 
@@ -157,26 +164,26 @@ impl MMArea {
                 // So here we can set the dirty flag now.
                 cache_page.set_dirty(true);
                 attr.insert(PageAttribute::WRITE);
-                *pfn = page.clone().into_raw();
+                *pfn = cache_page.add_mapping();
                 return;
             }
 
             if !write {
                 // Delay the copy-on-write until write fault happens.
                 attr.insert(PageAttribute::COPY_ON_WRITE);
-                *pfn = page.clone().into_raw();
+                *pfn = cache_page.add_mapping();
                 return;
             }
 
             // XXX: Change this. Let's handle mapped pages before CoW pages.
             // Nah, we are writing to a mapped private mapping...
-            let mut new_page = PageExcl::zeroed();
+            let mut new_page = FolioOwned::alloc();
             new_page
                 .as_bytes_mut()
-                .copy_from_slice(page.lock().as_bytes());
+                .copy_from_slice(cache_page.lock().as_bytes());
 
             attr.insert(PageAttribute::WRITE);
-            *pfn = new_page.into_page().into_raw();
+            *pfn = new_page.share().into_raw();
         };
 
         file_mapping

+ 32 - 32
src/kernel/mem/mm_list.rs

@@ -1,31 +1,33 @@
 mod mapping;
 mod page_fault;
+mod page_table;
 
 use alloc::collections::btree_set::BTreeSet;
 use core::fmt;
 use core::sync::atomic::{AtomicUsize, Ordering};
 
 use eonix_hal::mm::{
-    flush_tlb_all, get_root_page_table_pfn, set_root_page_table_pfn, ArchPagingMode,
-    ArchPhysAccess, GLOBAL_PAGE_TABLE,
+    flush_tlb_all, get_root_page_table_pfn, set_root_page_table_pfn, GLOBAL_PAGE_TABLE,
 };
 use eonix_mm::address::{Addr as _, AddrOps as _, PAddr, VAddr, VRange};
-use eonix_mm::page_table::{PageAttribute, PageTable, RawAttribute, PTE};
-use eonix_mm::paging::{PAGE_SIZE, PFN};
+use eonix_mm::page_table::{PageAttribute, RawAttribute, PTE};
+use eonix_mm::paging::{Folio as _, PAGE_SIZE, PFN};
 use eonix_sync::{LazyLock, Mutex};
 pub use mapping::{FileMapping, Mapping};
 pub use page_fault::handle_kernel_page_fault;
+use page_table::KernelPageTable;
 
 use super::address::{VAddrExt as _, VRangeExt as _};
-use super::page_alloc::GlobalPageAlloc;
-use super::paging::AllocZeroed as _;
-use super::{MMArea, Page, PageExt};
+use super::{Folio, FolioOwned, MMArea};
 use crate::kernel::constants::{EEXIST, EFAULT, EINVAL, ENOMEM};
-use crate::kernel::mem::page_alloc::RawPagePtr;
 use crate::prelude::*;
 use crate::sync::ArcSwap;
 
-pub static EMPTY_PAGE: LazyLock<Page> = LazyLock::new(|| Page::zeroed());
+pub static EMPTY_PAGE: LazyLock<Folio> = LazyLock::new(|| {
+    let mut folio = FolioOwned::alloc();
+    folio.as_bytes_mut().fill(0);
+    folio.share()
+});
 
 #[derive(Debug, Clone, Copy)]
 pub struct Permission {
@@ -34,23 +36,21 @@ pub struct Permission {
     pub execute: bool,
 }
 
-pub type KernelPageTable<'a> = PageTable<'a, ArchPagingMode, GlobalPageAlloc, ArchPhysAccess>;
-
-struct MMListInner<'a> {
+struct MMListInner {
     areas: BTreeSet<MMArea>,
-    page_table: KernelPageTable<'a>,
+    page_table: KernelPageTable,
     break_start: Option<VRange>,
     break_pos: Option<VAddr>,
 }
 
 pub struct MMList {
-    inner: ArcSwap<Mutex<MMListInner<'static>>>,
+    inner: ArcSwap<Mutex<MMListInner>>,
     user_count: AtomicUsize,
     /// Only used in kernel space to switch page tables on context switch.
     root_page_table: AtomicUsize,
 }
 
-impl MMListInner<'_> {
+impl MMListInner {
     fn overlapping_addr(&self, addr: VAddr) -> Option<&MMArea> {
         self.areas.get(&VRange::from(addr))
     }
@@ -96,7 +96,7 @@ impl MMListInner<'_> {
         }
     }
 
-    fn unmap(&mut self, start: VAddr, len: usize) -> KResult<Vec<Page>> {
+    fn unmap(&mut self, start: VAddr, len: usize) -> KResult<Vec<Folio>> {
         assert_eq!(start.floor(), start);
         let end = (start + len).ceil();
         let range_to_unmap = VRange::new(start, end);
@@ -120,7 +120,7 @@ impl MMListInner<'_> {
                 let (pfn, _) = pte.take();
                 pages_to_free.push(unsafe {
                     // SAFETY: We got the pfn from a valid page table entry, so it should be valid.
-                    Page::from_raw(pfn)
+                    Folio::from_raw(pfn)
                 });
             }
 
@@ -275,23 +275,23 @@ impl MMListInner<'_> {
     }
 }
 
-impl Drop for MMListInner<'_> {
+impl Drop for MMListInner {
     fn drop(&mut self) {
         // May buggy
         for area in &self.areas {
             if area.is_shared {
                 for pte in self.page_table.iter_user(area.range()) {
-                    let (pfn, _) = pte.take();
-                    let raw_page = RawPagePtr::from(pfn);
-                    if raw_page.refcount().fetch_sub(1, Ordering::Relaxed) == 1 {
-                        // Wrong here
-                        // unsafe { Page::from_raw(pfn) };
-                    }
+                    // XXX: Fix me
+                    let _ = pte.take();
+                    // let raw_page = RawPagePtr::from(pfn);
+                    // if raw_page.refcount().fetch_sub(1, Ordering::Relaxed) == 1 {
+                    //     unsafe { Page::from_raw(pfn) };
+                    // }
                 }
             } else {
                 for pte in self.page_table.iter_user(area.range()) {
                     let (pfn, _) = pte.take();
-                    unsafe { Page::from_raw(pfn) };
+                    unsafe { Folio::from_raw(pfn) };
                 }
             }
         }
@@ -327,7 +327,7 @@ impl MMList {
     }
 
     pub fn new() -> Self {
-        let page_table = GLOBAL_PAGE_TABLE.clone_global();
+        let page_table = KernelPageTable::new();
         Self {
             root_page_table: AtomicUsize::from(page_table.addr().addr()),
             user_count: AtomicUsize::new(0),
@@ -344,7 +344,7 @@ impl MMList {
         let inner = self.inner.borrow();
         let mut inner = inner.lock().await;
 
-        let page_table = GLOBAL_PAGE_TABLE.clone_global();
+        let page_table = KernelPageTable::new();
         let list = Self {
             root_page_table: AtomicUsize::from(page_table.addr().addr()),
             user_count: AtomicUsize::new(0),
@@ -392,7 +392,7 @@ impl MMList {
     }
 
     pub fn deactivate(&self) {
-        set_root_page_table_pfn(PFN::from(GLOBAL_PAGE_TABLE.addr()));
+        set_root_page_table_pfn(PFN::from(GLOBAL_PAGE_TABLE.start()));
 
         let old_user_count = self.user_count.fetch_sub(1, Ordering::Release);
         assert_ne!(old_user_count, 0);
@@ -444,7 +444,7 @@ impl MMList {
 
         let new_root_page_table = match &new {
             Some(new_mm) => new_mm.root_page_table.load(Ordering::Relaxed),
-            None => GLOBAL_PAGE_TABLE.addr().addr(),
+            None => GLOBAL_PAGE_TABLE.start().addr(),
         };
 
         set_root_page_table_pfn(PFN::from(PAddr::from(new_root_page_table)));
@@ -693,7 +693,7 @@ impl MMList {
 
                 unsafe {
                     // SAFETY: We are sure that the page is valid and we have the right to access it.
-                    Page::with_raw(pte.get_pfn(), |page| {
+                    Folio::with_raw(pte.get_pfn(), |page| {
                         let mut pg = page.lock();
                         let page_data = &mut pg.as_bytes_mut()[start_offset..end_offset];
 
@@ -724,7 +724,7 @@ trait PageTableExt {
     fn set_copied(&self, from: &Self, range: VRange);
 }
 
-impl PageTableExt for KernelPageTable<'_> {
+impl PageTableExt for KernelPageTable {
     fn set_anonymous(&self, range: VRange, permission: Permission) {
         for pte in self.iter_user(range) {
             pte.set_anonymous(permission.execute);
@@ -805,7 +805,7 @@ where
 
         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())
+            Folio::with_raw(from.get_pfn(), |page| page.clone().into_raw())
         };
 
         self.set(pfn, T::Attr::from(from_attr & !PageAttribute::ACCESSED));

+ 40 - 0
src/kernel/mem/mm_list/page_table.rs

@@ -0,0 +1,40 @@
+use core::ops::Deref;
+
+use eonix_hal::arch_exported::mm::{ArchPagingMode, PageAccessImpl};
+use eonix_hal::mm::GLOBAL_PAGE_TABLE;
+use eonix_mm::page_table::PageTable;
+use eonix_mm::paging::{Folio, GlobalFrameAlloc};
+
+use crate::kernel::mem::{FolioOwned, GlobalPageAlloc, PhysAccess};
+
+#[repr(transparent)]
+pub struct KernelPageTable(PageTable<'static, ArchPagingMode, GlobalPageAlloc, PageAccessImpl>);
+
+impl KernelPageTable {
+    pub fn new() -> Self {
+        let global_page_table = unsafe {
+            // SAFETY: The region is valid and read only after initialization.
+            GLOBAL_PAGE_TABLE.start().as_ptr::<[u8; 4096]>().as_ref()
+        };
+
+        let mut table_page = FolioOwned::alloc();
+        let entries = table_page.as_bytes_mut().len();
+        table_page.as_bytes_mut()[..(entries / 2)].fill(0);
+        table_page.as_bytes_mut()[(entries / 2)..]
+            .copy_from_slice(&global_page_table[(entries / 2)..]);
+
+        Self(PageTable::new(
+            table_page.share(),
+            GlobalPageAlloc::GLOBAL,
+            PageAccessImpl,
+        ))
+    }
+}
+
+impl Deref for KernelPageTable {
+    type Target = PageTable<'static, ArchPagingMode, GlobalPageAlloc, PageAccessImpl>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}

+ 36 - 55
src/kernel/mem/page_alloc.rs

@@ -5,14 +5,14 @@ use core::sync::atomic::Ordering;
 
 use buddy_allocator::BuddyAllocator;
 use eonix_mm::address::PRange;
-use eonix_mm::paging::{
-    GlobalPageAlloc as GlobalPageAllocTrait, PageAlloc, PageList, PageListSized as _,
-};
+use eonix_mm::page_table::PageTableAlloc;
+use eonix_mm::paging::{FolioList, FolioListSized as _, FrameAlloc, GlobalFrameAlloc, PFN};
 use eonix_preempt::PreemptGuard;
 use eonix_sync::{NoContext, Spin};
-use raw_page::{PageFlags, RawPageList};
-pub use raw_page::{RawPage, RawPagePtr};
-pub use zones::GlobalZone;
+pub use raw_page::{PageFlags, RawPage, RawPageList};
+pub use zones::{GlobalZone, ZONE};
+
+use super::folio::Folio;
 
 const COSTLY_ORDER: u32 = 3;
 const AREAS: usize = COSTLY_ORDER as usize + 1;
@@ -27,9 +27,6 @@ static PERCPU_PAGE_ALLOC: PerCpuPageAlloc = PerCpuPageAlloc::new();
 #[derive(Clone)]
 pub struct GlobalPageAlloc;
 
-#[derive(Clone)]
-pub struct BuddyPageAlloc();
-
 struct PerCpuPageAlloc {
     batch: u32,
     free_areas: [RawPageList; AREAS],
@@ -72,11 +69,6 @@ impl PerCpuPageAlloc {
 }
 
 impl GlobalPageAlloc {
-    #[allow(dead_code)]
-    pub const fn buddy_alloc() -> BuddyPageAlloc {
-        BuddyPageAlloc()
-    }
-
     /// Add the pages in the PAddr range `range` to the global allocator.
     ///
     /// This function is only to be called on system initialization when `eonix_preempt`
@@ -88,15 +80,11 @@ impl GlobalPageAlloc {
     pub unsafe fn add_pages(range: PRange) {
         BUDDY_ALLOC
             .lock_with_context(NoContext)
-            .create_pages(range.start(), range.end())
+            .create_folios(range.start(), range.end())
     }
-}
-
-impl PageAlloc for GlobalPageAlloc {
-    type RawPage = RawPagePtr;
 
-    fn alloc_order(&self, order: u32) -> Option<RawPagePtr> {
-        let raw_page = if order > COSTLY_ORDER {
+    pub fn alloc_raw_order(&self, order: u32) -> Option<&'static mut RawPage> {
+        if order > COSTLY_ORDER {
             BUDDY_ALLOC.lock().alloc_order(order)
         } else {
             unsafe {
@@ -106,61 +94,54 @@ impl PageAlloc for GlobalPageAlloc {
 
                 page
             }
-        };
-
-        raw_page.map(|raw_page| {
-            // SAFETY: Memory order here can be Relaxed is for the same reason
-            //         as that in the copy constructor of `std::shared_ptr`.
-            raw_page.refcount.fetch_add(1, Ordering::Relaxed);
-
-            RawPagePtr::from_ref(raw_page)
-        })
+        }
     }
 
-    unsafe fn dealloc(&self, page_ptr: RawPagePtr) {
+    pub unsafe fn dealloc_raw(&self, raw_page: &'static mut RawPage) {
         assert_eq!(
-            page_ptr.refcount().load(Ordering::Relaxed),
+            raw_page.refcount.load(Ordering::Relaxed),
             0,
             "Trying to free a page with refcount > 0"
         );
 
-        if page_ptr.order() > COSTLY_ORDER {
-            BUDDY_ALLOC.lock().dealloc(page_ptr.as_mut());
+        if raw_page.order > COSTLY_ORDER {
+            BUDDY_ALLOC.lock().dealloc(raw_page);
         } else {
-            let order = page_ptr.order();
+            let order = raw_page.order;
 
             unsafe {
-                PreemptGuard::new(PERCPU_PAGE_ALLOC.as_mut()).free_pages(page_ptr.as_mut(), order);
+                PreemptGuard::new(PERCPU_PAGE_ALLOC.as_mut()).free_pages(raw_page, order);
             }
         }
     }
-
-    fn has_management_over(&self, page_ptr: RawPagePtr) -> bool {
-        page_ptr.order() > COSTLY_ORDER || page_ptr.flags().has(PageFlags::LOCAL)
-    }
 }
 
-impl GlobalPageAllocTrait for GlobalPageAlloc {
-    fn global() -> Self {
-        GlobalPageAlloc
+impl FrameAlloc for GlobalPageAlloc {
+    type Folio = Folio;
+
+    fn alloc_order(&self, order: u32) -> Option<Self::Folio> {
+        self.alloc_raw_order(order).map(|raw_page| {
+            // SAFETY: Memory order here can be Relaxed is for the same reason
+            //         as that in the copy constructor of `std::shared_ptr`.
+
+            raw_page.refcount.fetch_add(1, Ordering::Relaxed);
+            Folio::from_mut_page(raw_page)
+        })
     }
 }
 
-impl PageAlloc for BuddyPageAlloc {
-    type RawPage = RawPagePtr;
+impl GlobalFrameAlloc for GlobalPageAlloc {
+    const GLOBAL: Self = GlobalPageAlloc;
+}
 
-    fn alloc_order(&self, order: u32) -> Option<RawPagePtr> {
-        BUDDY_ALLOC
-            .lock()
-            .alloc_order(order)
-            .map(|raw_page| RawPagePtr::from_ref(raw_page))
-    }
+impl PageTableAlloc for GlobalPageAlloc {
+    type Folio = Folio;
 
-    unsafe fn dealloc(&self, page_ptr: RawPagePtr) {
-        BUDDY_ALLOC.lock().dealloc(page_ptr.as_mut());
+    fn alloc(&self) -> Self::Folio {
+        FrameAlloc::alloc(self).unwrap()
     }
 
-    fn has_management_over(&self, _: RawPagePtr) -> bool {
-        true
+    unsafe fn from_raw(&self, pfn: PFN) -> Self::Folio {
+        unsafe { Folio::from_raw(pfn) }
     }
 }

+ 24 - 112
src/kernel/mem/page_alloc/raw_page.rs

@@ -1,20 +1,17 @@
 use core::ptr::NonNull;
 use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
 
-use buddy_allocator::BuddyPage;
+use buddy_allocator::BuddyFolio;
 use eonix_hal::mm::ArchPhysAccess;
 use eonix_mm::address::{PAddr, PhysAccess as _};
-use eonix_mm::paging::{PageAlloc, PageList, PageListSized, RawPage as RawPageTrait, PFN};
+use eonix_mm::paging::{FolioList, FolioListSized, Zone, PFN};
 use intrusive_list::{container_of, Link, List};
 use slab_allocator::{SlabPage, SlabPageAlloc, SlabSlot};
 
+use super::zones::ZONE;
 use super::{GlobalPageAlloc, PerCpuPage};
-use crate::kernel::mem::page_cache::PageCacheRawPage;
 use crate::kernel::mem::PhysAccess;
 
-pub const PAGE_ARRAY: NonNull<RawPage> =
-    unsafe { NonNull::new_unchecked(0xffffff8040000000 as *mut _) };
-
 pub struct PageFlags(AtomicU32);
 
 #[derive(Clone, Copy)]
@@ -41,11 +38,11 @@ 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.
-    link: Link,
+    pub link: Link,
     /// # Safety
     /// This field is only used in buddy system and is protected by the global lock.
-    order: u32,
-    flags: PageFlags,
+    pub order: u32,
+    pub flags: PageFlags,
     pub refcount: AtomicUsize,
 
     shared_data: PageData,
@@ -55,9 +52,6 @@ pub struct RawPage {
 unsafe impl Send for RawPage {}
 unsafe impl Sync for RawPage {}
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub struct RawPagePtr(NonNull<RawPage>);
-
 impl PageFlags {
     pub const LOCKED: u32 = 1 << 1;
     pub const BUDDY: u32 = 1 << 2;
@@ -85,80 +79,9 @@ impl PageFlags {
     }
 }
 
-impl RawPagePtr {
-    pub const fn from_ref(raw_page_ref: &RawPage) -> Self {
-        Self::new(unsafe {
-            // SAFETY: Rust references always points to non-null addresses.
-            NonNull::new_unchecked(&raw const *raw_page_ref as *mut _)
-        })
-    }
-
-    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
-    }
-
-    // return the ptr point to the actually raw page
-    pub fn real_ptr<T>(&self) -> NonNull<T> {
-        let pfn = unsafe { PFN::from(RawPagePtr(NonNull::new_unchecked(self.as_ptr()))) };
-        unsafe { PAddr::from(pfn).as_ptr::<T>() }
-    }
-}
-
-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()
-    }
-}
-
-impl BuddyPage for RawPage {
+impl BuddyFolio for RawPage {
     fn pfn(&self) -> PFN {
-        PFN::from(RawPagePtr::from_ref(self))
+        ZONE.get_pfn(self)
     }
 
     fn get_order(&self) -> u32 {
@@ -184,8 +107,7 @@ impl BuddyPage for RawPage {
 
 impl SlabPage for RawPage {
     fn get_data_ptr(&self) -> NonNull<[u8]> {
-        let raw_page_ptr = RawPagePtr::from_ref(self);
-        let paddr_start = PAddr::from(PFN::from(raw_page_ptr));
+        let paddr_start = PAddr::from(ZONE.get_pfn(self));
         let page_data_ptr = unsafe { paddr_start.as_ptr() };
 
         NonNull::slice_from_raw_parts(page_data_ptr, 1 << (self.order + 12))
@@ -233,21 +155,9 @@ impl SlabPage for RawPage {
             let paddr = ArchPhysAccess::from_ptr(ptr);
             let pfn = PFN::from(paddr);
 
-            RawPagePtr::from(pfn).as_mut()
-        }
-    }
-}
-
-impl PageCacheRawPage for RawPagePtr {
-    fn is_dirty(&self) -> bool {
-        self.flags().has(PageFlags::DIRTY)
-    }
-
-    fn set_dirty(&self, dirty: bool) {
-        if dirty {
-            self.flags().set(PageFlags::DIRTY);
-        } else {
-            self.flags().clear(PageFlags::DIRTY);
+            ZONE.get_page(pfn)
+                .expect("Page outside of the global zone")
+                .as_mut()
         }
     }
 }
@@ -264,14 +174,16 @@ impl PerCpuPage for RawPage {
 
 pub struct RawPageList(List);
 
-impl PageList for RawPageList {
-    type Page = RawPage;
+unsafe impl Send for RawPageList {}
+
+impl FolioList for RawPageList {
+    type Folio = RawPage;
 
     fn is_empty(&self) -> bool {
         self.0.is_empty()
     }
 
-    fn peek_head(&mut self) -> Option<&mut Self::Page> {
+    fn peek_head(&mut self) -> Option<&mut Self::Folio> {
         unsafe {
             let link = self.0.head()?;
             let mut raw_page_ptr = container_of!(link, RawPage, link);
@@ -280,7 +192,7 @@ impl PageList for RawPageList {
         }
     }
 
-    fn pop_head(&mut self) -> Option<&'static mut Self::Page> {
+    fn pop_head(&mut self) -> Option<&'static mut Self::Folio> {
         unsafe {
             let link = self.0.pop()?;
             let mut raw_page_ptr = container_of!(link, RawPage, link);
@@ -289,25 +201,25 @@ impl PageList for RawPageList {
         }
     }
 
-    fn push_tail(&mut self, page: &'static mut Self::Page) {
+    fn push_tail(&mut self, page: &'static mut Self::Folio) {
         self.0.insert(&mut page.link);
     }
 
-    fn remove(&mut self, page: &mut Self::Page) {
+    fn remove(&mut self, page: &mut Self::Folio) {
         self.0.remove(&mut page.link)
     }
 }
 
-impl PageListSized for RawPageList {
+impl FolioListSized for RawPageList {
     const NEW: Self = RawPageList(List::new());
 }
 
-impl SlabPageAlloc for GlobalPageAlloc {
+unsafe impl SlabPageAlloc for GlobalPageAlloc {
     type Page = RawPage;
     type PageList = RawPageList;
 
-    unsafe fn alloc_uninit(&self) -> &'static mut RawPage {
-        let raw_page = self.alloc().expect("Out of memory").as_mut();
+    fn alloc_slab_page(&self) -> &'static mut RawPage {
+        let raw_page = self.alloc_raw_order(0).expect("Out of memory");
         raw_page.flags.set(PageFlags::SLAB);
         raw_page.shared_data.slab = SlabPageData::new();
 

+ 14 - 8
src/kernel/mem/page_alloc/zones.rs

@@ -1,13 +1,23 @@
-use core::cell::UnsafeCell;
+use core::ptr::NonNull;
 
 use eonix_mm::address::PRange;
 use eonix_mm::paging::{Zone, PFN};
 
 use super::RawPage;
-use crate::kernel::mem::page_alloc::RawPagePtr;
+
+pub static ZONE: GlobalZone = GlobalZone();
+
+const PAGE_ARRAY: NonNull<RawPage> =
+    unsafe { NonNull::new_unchecked(0xffffff8040000000 as *mut _) };
 
 pub struct GlobalZone();
 
+impl GlobalZone {
+    pub fn get_pfn(&self, page_ptr: *const RawPage) -> PFN {
+        PFN::from(unsafe { page_ptr.offset_from(PAGE_ARRAY.as_ptr()) as usize })
+    }
+}
+
 impl Zone for GlobalZone {
     type Page = RawPage;
 
@@ -15,11 +25,7 @@ impl Zone for GlobalZone {
         true
     }
 
-    fn get_page(&self, pfn: PFN) -> Option<&UnsafeCell<Self::Page>> {
-        unsafe {
-            // SAFETY: The pointer returned by [`RawPagePtr::as_ptr()`] is valid.
-            //         And so is it wrapped with [`UnsafeCell`]
-            Some(&*(RawPagePtr::from(pfn).as_ptr() as *const UnsafeCell<Self::Page>))
-        }
+    fn get_page(&self, pfn: PFN) -> Option<NonNull<RawPage>> {
+        Some(unsafe { PAGE_ARRAY.add(usize::from(pfn)) })
     }
 }

+ 41 - 56
src/kernel/mem/page_cache.rs

@@ -1,19 +1,16 @@
 use alloc::collections::btree_map::{BTreeMap, Entry};
 use core::future::Future;
-use core::mem::ManuallyDrop;
+use core::ops::{Deref, DerefMut};
 
-use eonix_hal::mm::ArchPhysAccess;
-use eonix_mm::address::{PAddr, PhysAccess};
-use eonix_mm::paging::{PageAlloc, RawPage, PAGE_SIZE, PAGE_SIZE_BITS, PFN};
+use eonix_mm::paging::{Folio as _, PAGE_SIZE, PAGE_SIZE_BITS, PFN};
 use eonix_sync::Mutex;
 
-use super::Page;
+use super::page_alloc::PageFlags;
+use super::{Folio, FolioOwned};
 use crate::io::{Buffer, Stream};
 use crate::kernel::constants::EINVAL;
-use crate::kernel::mem::page_alloc::RawPagePtr;
 use crate::kernel::vfs::inode::InodeUse;
 use crate::prelude::KResult;
-use crate::GlobalPageAlloc;
 
 #[repr(transparent)]
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -24,14 +21,7 @@ pub struct PageCache {
     inode: InodeUse,
 }
 
-unsafe impl Send for PageCache {}
-unsafe impl Sync for PageCache {}
-
-#[derive(Clone, Copy)]
-pub struct CachePage(RawPagePtr);
-
-unsafe impl Send for CachePage {}
-unsafe impl Sync for CachePage {}
+pub struct CachePage(Folio);
 
 impl PageOffset {
     pub const fn from_byte_floor(offset: usize) -> Self {
@@ -57,39 +47,47 @@ impl PageOffset {
 
 impl CachePage {
     pub fn new() -> Self {
-        Self(GlobalPageAlloc.alloc().unwrap())
+        CachePage(Folio::alloc())
     }
 
-    pub fn as_bytes(&self) -> &[u8] {
-        unsafe {
-            core::slice::from_raw_parts(
-                // SAFETY: The page is owned by us, so we can safely access its data.
-                ArchPhysAccess::as_ptr(PAddr::from(PFN::from(self.0))).as_ptr(),
-                PAGE_SIZE,
-            )
-        }
-    }
+    pub fn new_zeroed() -> Self {
+        CachePage({
+            let mut folio = FolioOwned::alloc();
+            folio.as_bytes_mut().fill(0);
 
-    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
-        unsafe {
-            core::slice::from_raw_parts_mut(
-                // SAFETY: The page is exclusively owned by us, so we can safely access its data.
-                ArchPhysAccess::as_ptr(PAddr::from(PFN::from(self.0))).as_ptr(),
-                PAGE_SIZE,
-            )
-        }
+            folio.share()
+        })
     }
 
     pub fn is_dirty(&self) -> bool {
-        self.0.is_dirty()
+        self.flags.has(PageFlags::DIRTY)
     }
 
     pub fn set_dirty(&self, dirty: bool) {
-        self.0.set_dirty(dirty);
+        if dirty {
+            self.flags.set(PageFlags::DIRTY);
+        } else {
+            self.flags.clear(PageFlags::DIRTY);
+        }
+    }
+
+    pub fn add_mapping(&self) -> PFN {
+        // TODO: Increase map_count
+        self.0.clone().into_raw()
     }
+}
+
+impl Deref for CachePage {
+    type Target = Folio;
 
-    pub fn get_page(&self) -> Page {
-        unsafe { Page::with_raw(PFN::from(self.0), |page| page.clone()) }
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl DerefMut for CachePage {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
     }
 }
 
@@ -124,11 +122,7 @@ impl PageCache {
     }
 
     // TODO: Remove this.
-    pub async fn with_page(
-        &self,
-        pgoff: PageOffset,
-        func: impl FnOnce(&Page, &CachePage),
-    ) -> KResult<()> {
+    pub async fn with_page(&self, pgoff: PageOffset, func: impl FnOnce(&CachePage)) -> KResult<()> {
         let mut pages = self.pages.lock().await;
         if pgoff > PageOffset::from_byte_ceil(self.len()) {
             return Err(EINVAL);
@@ -136,11 +130,7 @@ impl PageCache {
 
         let cache_page = self.get_page_locked(&mut pages, pgoff).await?;
 
-        unsafe {
-            let page = ManuallyDrop::new(Page::from_raw_unchecked(PFN::from(cache_page.0)));
-
-            func(&page, cache_page);
-        }
+        func(cache_page);
 
         Ok(())
     }
@@ -166,7 +156,7 @@ impl PageCache {
             let data_len = real_end - offset;
 
             if buffer
-                .fill(&page.as_bytes()[inner_offset..inner_offset + data_len])?
+                .fill(&page.lock().as_bytes()[inner_offset..inner_offset + data_len])?
                 .should_stop()
                 || buffer.available() == 0
             {
@@ -195,7 +185,7 @@ impl PageCache {
 
             let inner_offset = offset % PAGE_SIZE;
             let written = stream
-                .poll_data(&mut page.as_bytes_mut()[inner_offset..])?
+                .poll_data(&mut page.lock().as_bytes_mut()[inner_offset..])?
                 .map(|b| b.len())
                 .unwrap_or(0);
 
@@ -237,14 +227,9 @@ impl core::fmt::Debug for PageCache {
     }
 }
 
-pub trait PageCacheRawPage: RawPage {
-    fn is_dirty(&self) -> bool;
-    fn set_dirty(&self, dirty: bool);
-}
-
 impl Drop for PageCache {
     fn drop(&mut self) {
-        // TODO: Write back dirty pages...
-        // let _ = self.fsync();
+        // XXX: Send the PageCache to some flusher worker.
+        let _ = self.fsync();
     }
 }

+ 4 - 118
src/kernel/mem/paging.rs

@@ -1,48 +1,22 @@
-use core::ops::Deref;
-use core::ptr::NonNull;
+use eonix_mm::paging::Folio as _;
 
-use eonix_mm::paging::Page as GenericPage;
-
-use super::page_alloc::GlobalPageAlloc;
-use super::PhysAccess;
+use super::folio::FolioOwned;
 use crate::io::{Buffer, FillResult};
 
-pub type Page = GenericPage<GlobalPageAlloc>;
-
 /// A buffer that wraps a page and provides a `Buffer` interface.
 pub struct PageBuffer {
-    page: PageExcl,
+    page: FolioOwned,
     offset: usize,
 }
 
-pub struct PageLocked<'a> {
-    page: &'a Page,
-}
-
-/// A page that is exclusively owned.
-#[repr(transparent)]
-pub struct PageExcl(Page);
-
 pub trait AllocZeroed {
     fn zeroed() -> Self;
 }
 
-pub trait PageExt {
-    fn lock(&self) -> PageLocked;
-
-    /// Get a vmem pointer to the page data as a byte slice.
-    fn get_bytes_ptr(&self) -> NonNull<[u8]>;
-
-    /// Get a vmem pointer to the start of the page.
-    fn get_ptr(&self) -> NonNull<u8> {
-        self.get_bytes_ptr().cast()
-    }
-}
-
 impl PageBuffer {
     pub fn new() -> Self {
         Self {
-            page: PageExcl::alloc(),
+            page: FolioOwned::alloc(),
             offset: 0,
         }
     }
@@ -86,91 +60,3 @@ impl Buffer for PageBuffer {
         }
     }
 }
-
-impl AllocZeroed for Page {
-    fn zeroed() -> Self {
-        let page = Self::alloc();
-
-        page.lock().as_bytes_mut().fill(0);
-
-        page
-    }
-}
-
-impl PageExt for Page {
-    fn lock(&self) -> PageLocked {
-        // TODO: Actually perform the lock.
-        PageLocked { page: self }
-    }
-
-    fn get_bytes_ptr(&self) -> NonNull<[u8]> {
-        unsafe {
-            // SAFETY: `self.start()` can't be null.
-            NonNull::slice_from_raw_parts(self.start().as_ptr(), self.len())
-        }
-    }
-}
-
-impl PageLocked<'_> {
-    pub fn as_bytes(&self) -> &[u8] {
-        unsafe {
-            // SAFETY: `self.start()` points to valid memory of length `self.len()`.
-            core::slice::from_raw_parts(self.start().as_ptr().as_ptr(), self.len())
-        }
-    }
-
-    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
-        unsafe {
-            // SAFETY: `self.start()` points to valid memory of length `self.len()`.
-            core::slice::from_raw_parts_mut(self.start().as_ptr().as_ptr(), self.len())
-        }
-    }
-}
-
-impl Deref for PageLocked<'_> {
-    type Target = Page;
-
-    fn deref(&self) -> &Self::Target {
-        self.page
-    }
-}
-
-impl PageExcl {
-    pub fn alloc() -> Self {
-        Self(Page::alloc())
-    }
-
-    pub fn alloc_order(order: u32) -> Self {
-        Self(Page::alloc_order(order))
-    }
-
-    pub fn zeroed() -> Self {
-        Self(Page::zeroed())
-    }
-
-    pub fn as_bytes(&self) -> &[u8] {
-        unsafe {
-            // SAFETY: The page is exclusively owned by us.
-            self.get_bytes_ptr().as_ref()
-        }
-    }
-
-    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
-        unsafe {
-            // SAFETY: The page is exclusively owned by us.
-            self.get_bytes_ptr().as_mut()
-        }
-    }
-
-    pub fn into_page(self) -> Page {
-        self.0
-    }
-}
-
-impl Deref for PageExcl {
-    type Target = Page;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}

+ 11 - 14
src/kernel/task/kernel_stack.rs

@@ -1,11 +1,12 @@
-use crate::kernel::mem::{paging::Page, PhysAccess as _};
-use core::{num::NonZero, ptr::NonNull};
+use core::ptr::NonNull;
+
 use eonix_runtime::executor::Stack;
 
+use crate::kernel::mem::FolioOwned;
+
 #[derive(Debug)]
 pub struct KernelStack {
-    _pages: Page,
-    bottom: NonZero<usize>,
+    folio: FolioOwned,
 }
 
 impl KernelStack {
@@ -14,15 +15,8 @@ impl KernelStack {
     const KERNEL_STACK_ORDER: u32 = 7;
 
     pub fn new() -> Self {
-        let pages = Page::alloc_order(Self::KERNEL_STACK_ORDER);
-        let bottom = unsafe {
-            // SAFETY: The paddr is from a page, which should be valid.
-            pages.range().end().as_ptr::<u8>().addr()
-        };
-
         Self {
-            _pages: pages,
-            bottom,
+            folio: FolioOwned::alloc_order(Self::KERNEL_STACK_ORDER),
         }
     }
 }
@@ -33,7 +27,10 @@ impl Stack for KernelStack {
     }
 
     fn get_bottom(&self) -> NonNull<()> {
-        // SAFETY: The stack is allocated and `bottom` is non-zero.
-        unsafe { NonNull::new_unchecked(self.bottom.get() as *mut _) }
+        let ptr = self.folio.get_bytes_ptr();
+        let len = ptr.len();
+
+        // SAFETY: The vaddr of the folio is guaranteed to be non-zero.
+        unsafe { ptr.cast().byte_add(len) }
     }
 }

+ 2 - 2
src/kernel/vfs/file/mod.rs

@@ -15,7 +15,7 @@ pub use terminal_file::TerminalFile;
 
 use crate::io::{Buffer, ByteBuffer, Chunks, IntoStream, Stream};
 use crate::kernel::constants::{EBADF, EINTR, EINVAL, ENOTTY};
-use crate::kernel::mem::PageExcl;
+use crate::kernel::mem::FolioOwned;
 use crate::kernel::task::Thread;
 use crate::kernel::CharDevice;
 use crate::prelude::KResult;
@@ -94,7 +94,7 @@ impl FileType {
     }
 
     pub async fn sendfile(&self, dest_file: &Self, count: usize) -> KResult<usize> {
-        let mut buffer_page = PageExcl::alloc();
+        let mut buffer_page = FolioOwned::alloc();
         let buffer = buffer_page.as_bytes_mut();
 
         self.sendfile_check()?;

+ 34 - 24
src/kernel_init.rs

@@ -1,32 +1,26 @@
+use eonix_hal::arch_exported::mm::{ArchPagingMode, PageAccessImpl};
 use eonix_hal::bootstrap::BootStrapData;
-use eonix_hal::mm::{ArchMemory, ArchPagingMode, GLOBAL_PAGE_TABLE};
+use eonix_hal::mm::{ArchMemory, BasicPageAllocRef, GLOBAL_PAGE_TABLE};
 use eonix_hal::traits::mm::Memory;
 use eonix_mm::address::{Addr as _, AddrOps as _, VAddr, VRange};
-use eonix_mm::page_table::{PageAttribute, PagingMode as _, PTE};
-use eonix_mm::paging::{Page as GenericPage, PAGE_SIZE, PFN};
+use eonix_mm::page_table::{PageAttribute, PageTable, PTE};
+use eonix_mm::paging::{Folio as _, FrameAlloc, PAGE_SIZE, PFN};
 
 use crate::kernel::mem::{GlobalPageAlloc, RawPage};
 
-pub fn setup_memory(data: &mut BootStrapData) {
-    let addr_max = ArchMemory::present_ram()
-        .map(|range| range.end())
-        .max()
-        .expect("No free memory");
-
-    let pfn_max = PFN::from(addr_max.ceil());
-    let len_bytes_page_array = usize::from(pfn_max) * size_of::<RawPage>();
-    let count_pages = len_bytes_page_array.div_ceil(PAGE_SIZE);
-
-    let alloc = data.get_alloc().unwrap();
+fn setup_kernel_page_array(alloc: BasicPageAllocRef, count_pages: usize) {
+    // TODO: This should be done by the global Zone
+    let global_page_table = PageTable::<ArchPagingMode, _, _>::new(
+        GLOBAL_PAGE_TABLE.clone(),
+        alloc.clone(),
+        PageAccessImpl,
+    );
 
     // Map kernel page array.
     const V_KERNEL_PAGE_ARRAY_START: VAddr = VAddr::from(0xffffff8040000000);
 
-    for pte in GLOBAL_PAGE_TABLE.iter_kernel_in(
-        VRange::from(V_KERNEL_PAGE_ARRAY_START).grow(PAGE_SIZE * count_pages),
-        ArchPagingMode::LEVELS,
-        &alloc,
-    ) {
+    let range = VRange::from(V_KERNEL_PAGE_ARRAY_START).grow(PAGE_SIZE * count_pages);
+    for pte in global_page_table.iter_kernel(range) {
         let attr = PageAttribute::PRESENT
             | PageAttribute::WRITE
             | PageAttribute::READ
@@ -34,10 +28,15 @@ pub fn setup_memory(data: &mut BootStrapData) {
             | PageAttribute::ACCESSED
             | PageAttribute::DIRTY;
 
-        let page = GenericPage::alloc_in(&alloc);
+        let page = alloc.alloc().unwrap();
         pte.set(page.into_raw(), attr.into());
     }
 
+    // TODO!!!: Construct the global zone with all present ram.
+    // for range in ArchMemory::present_ram() {
+    //     GlobalPageAlloc::mark_present(range);
+    // }
+
     unsafe {
         // SAFETY: We've just mapped the area with sufficient length.
         core::ptr::write_bytes(
@@ -47,10 +46,21 @@ pub fn setup_memory(data: &mut BootStrapData) {
         );
     }
 
-    // TODO!!!: Construct the global zone with all present ram.
-    // for range in ArchMemory::present_ram() {
-    //     GlobalPageAlloc::mark_present(range);
-    // }
+    core::mem::forget(global_page_table);
+}
+
+pub fn setup_memory(data: &mut BootStrapData) {
+    let addr_max = ArchMemory::present_ram()
+        .map(|range| range.end())
+        .max()
+        .expect("No free memory");
+
+    let pfn_max = PFN::from(addr_max.ceil());
+    let len_bytes_page_array = usize::from(pfn_max) * size_of::<RawPage>();
+    let count_pages = len_bytes_page_array.div_ceil(PAGE_SIZE);
+
+    let alloc = data.get_alloc().unwrap();
+    setup_kernel_page_array(alloc, count_pages);
 
     if let Some(early_alloc) = data.take_alloc() {
         for range in early_alloc.into_iter() {