summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-05-13 15:54:57 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-05-13 15:54:57 +0800
commit70fa5d207fecf4db0eef58f2b6120bdd0b0f3e96 (patch)
treee2e7e431eeddced8c845986dd5469448b2ae5d5c
parentc9f158361cba8218a45fdc0ccb9a14ba05ddac3a (diff)
downloadlume-70fa5d207fecf4db0eef58f2b6120bdd0b0f3e96.tar.gz
lume-70fa5d207fecf4db0eef58f2b6120bdd0b0f3e96.tar.xz
feat: refactor Physical Memory Manager to use Page instead of Frame
-rw-r--r--na/include/lume/pmm.h44
-rw-r--r--na/mm/pmm.cc255
2 files changed, 149 insertions, 150 deletions
diff --git a/na/include/lume/pmm.h b/na/include/lume/pmm.h
index 266a83c..f02a146 100644
--- a/na/include/lume/pmm.h
+++ b/na/include/lume/pmm.h
@@ -6,10 +6,10 @@
/*
* Physical Memory Manager (PMM) — Buddy System + Per-CPU Cache
*
- * Manages all physical page frames discovered via FDT. Built on a
+ * Manages all physical pages discovered via FDT. Built on a
* power-of-two buddy allocator for multi-page requests, with a
* Per-CPU single-page cache (PCP) to reduce lock contention on the
- * hot single-frame alloc/free path.
+ * hot single-page alloc/free path.
*
* Concurrency model:
* - PCP operations run with interrupts disabled (same-CPU safety).
@@ -20,7 +20,7 @@
*/
#include <lume/types.h>
-#include <lume/frame.h>
+#include <lume/page.h>
#include <lume/addr_types.h>
#include <lume/config.h>
@@ -37,43 +37,43 @@ extern uint32 g_pcp_batch_size;
/*
* Initialize the PMM. Called by BSP after vmm_init() + memblock.
- * frame_map is already mapped at VMEMMAP VA by vmm_init.
+ * page_map is already mapped at VMEMMAP VA by vmm_init.
* Populates buddy lists from memblock's free regions, then retires memblock.
* Panics on failure.
*/
void pmm_init();
-/* --- Single-frame fast path (uses PCP) --- */
+/* --- Single-page fast path (uses PCP) --- */
-// Allocate one 4KB frame. Returns nullptr on OOM.
-Frame* pmm_alloc_frame();
+// Allocate one 4KB page. Returns nullptr on OOM.
+Page* pmm_alloc_page();
-// Free one 4KB frame.
-void pmm_free_frame(Frame* frame);
+// Free one 4KB page.
+void pmm_free_page(Page* page);
-/* --- Multi-frame slow path (direct buddy) --- */
+/* --- Multi-page slow path (direct buddy) --- */
-// Allocate 2^order contiguous frames. Returns nullptr on OOM.
-Frame* pmm_alloc_frames(uint8 order);
+// Allocate 2^order contiguous pages. Returns nullptr on OOM.
+Page* pmm_alloc_pages(uint8 order);
-// Free 2^order contiguous frames.
-void pmm_free_frames(Frame* frame, uint8 order);
+// Free 2^order contiguous pages.
+void pmm_free_pages(Page* page, uint8 order);
/* --- Reference counting (atomic, lock-free) --- */
-void frame_incref(Frame* frame);
-void frame_decref(Frame* frame);
+void page_incref(Page* page);
+void page_decref(Page* page);
/* --- Address conversion --- */
-// Convert a Frame* to its physical address.
-PhysAddr frame_to_pa(const Frame* frame);
+// Convert a Page* to its physical address.
+PhysAddr page_to_pa(const Page* page);
-// Convert a physical address to its Frame*.
-Frame* pa_to_frame(PhysAddr pa);
+// Convert a physical address to its Page*.
+Page* pa_to_page(PhysAddr pa);
-// Convert a Frame* to a kernel virtual address.
-VirtAddr frame_to_va(const Frame* frame);
+// Convert a Page* to a kernel virtual address.
+VirtAddr page_to_va(const Page* page);
// Query PMM memory range (for VMM kernel mapping)
PhysAddr pmm_get_mem_base();
diff --git a/na/mm/pmm.cc b/na/mm/pmm.cc
index 633bd2f..a5a0b0e 100644
--- a/na/mm/pmm.cc
+++ b/na/mm/pmm.cc
@@ -7,11 +7,11 @@
* Buddy allocator with Per-CPU single-page caches (PCP).
*
* Initialization sequence (called by BSP in single-core period):
- * 1. memblock + vmm_init allocate and map the frame_map at VMEMMAP.
- * 2. Zero and mark all Frame descriptors reserved by default.
+ * 1. memblock + vmm_init allocate and map the page_map at VMEMMAP.
+ * 2. Zero and mark all Page descriptors reserved by default.
* 3. Carve free regions from memblock into the buddy system.
*
- * The frame_map is a flat array indexed by physical page frame number (PFN).
+ * The page_map is a flat array indexed by physical page frame number (PFN).
* PFN = (pa - mem_base) / PAGE_SIZE. This keeps address conversion O(1).
*
* Reference: docs/specs/pmm.md
@@ -35,8 +35,8 @@
/* ------------------------------------------------------------------ */
struct PerCpuCache {
- list_node free_list; // Single-frame free list
- uint32 count; // Number of cached frames
+ list_node free_list; // Single-page free list
+ uint32 count; // Number of cached pages
};
/* ------------------------------------------------------------------ */
@@ -47,12 +47,12 @@ static Spinlock g_pmm_lock;
static list_node g_free_areas[kMaxOrder]; // Buddy free lists [0..10]
static PerCpuCache g_pcp[kMaxCpus];
-static Frame *g_frame_map = nullptr; // Flat array of Frame descriptors
-static uint64 g_frame_map_pa = 0; // Physical base of frame_map storage
-static uint64 g_frame_map_bytes = 0; // Byte size of frame_map storage
+static Page *g_page_map = nullptr; // Flat array of Page descriptors
+static uint64 g_page_map_pa = 0; // Physical base of page_map storage
+static uint64 g_page_map_bytes = 0; // Byte size of page_map storage
static uint64 g_mem_base = 0; // Physical memory base (from FDT)
static uint64 g_mem_size = 0; // Total physical memory size
-static uint64 g_num_frames = 0; // Total managed frames
+static uint64 g_num_pages = 0; // Total managed pages
uint32 g_pcp_high_watermark = 0;
uint32 g_pcp_batch_size = 0;
@@ -62,40 +62,40 @@ uint32 g_pcp_batch_size = 0;
/* ------------------------------------------------------------------ */
/*
- * Physical Frame Number: index into the frame_map array.
+ * Physical Page Number: index into the page_map array.
* PFN = (pa - g_mem_base) / kPageSize
*/
-static inline uint64 pa_to_frame_index(uint64 pa) {
- if (pa < g_mem_base || pa >= g_mem_base + g_num_frames * kPageSize)
- kernel_panic("pa_to_frame: physical address outside managed range");
+static inline uint64 pa_to_page_index(uint64 pa) {
+ if (pa < g_mem_base || pa >= g_mem_base + g_num_pages * kPageSize)
+ kernel_panic("pa_to_page: physical address outside managed range");
return (pa - g_mem_base) / kPageSize;
}
-static inline uint64 frame_index_to_pa(uint64 idx) {
- if (idx >= g_num_frames)
- kernel_panic("frame_to_pa: frame index outside managed range");
+static inline uint64 page_index_to_pa(uint64 idx) {
+ if (idx >= g_num_pages)
+ kernel_panic("page_to_pa: page index outside managed range");
return g_mem_base + idx * kPageSize;
}
-PhysAddr frame_to_pa(const Frame *frame) {
- uintptr base = reinterpret_cast<uintptr>(g_frame_map);
- uintptr addr = reinterpret_cast<uintptr>(frame);
- uintptr end = base + g_num_frames * sizeof(Frame);
+PhysAddr page_to_pa(const Page *page) {
+ uintptr base = reinterpret_cast<uintptr>(g_page_map);
+ uintptr addr = reinterpret_cast<uintptr>(page);
+ uintptr end = base + g_num_pages * sizeof(Page);
if (addr < base || addr >= end)
- kernel_panic("frame_to_pa: frame outside frame_map bounds");
+ kernel_panic("page_to_pa: page outside page_map bounds");
- uint64 idx = (addr - base) / sizeof(Frame);
- return phys_addr(frame_index_to_pa(idx));
+ uint64 idx = (addr - base) / sizeof(Page);
+ return phys_addr(page_index_to_pa(idx));
}
-Frame *pa_to_frame(PhysAddr pa) {
- uint64 idx = pa_to_frame_index(pa.raw);
- return &g_frame_map[idx];
+Page *pa_to_page(PhysAddr pa) {
+ uint64 idx = pa_to_page_index(pa.raw);
+ return &g_page_map[idx];
}
-VirtAddr frame_to_va(const Frame *frame) {
- return pa_to_va(frame_to_pa(frame));
+VirtAddr page_to_va(const Page *page) {
+ return pa_to_va(page_to_pa(page));
}
/* ------------------------------------------------------------------ */
@@ -103,25 +103,25 @@ VirtAddr frame_to_va(const Frame *frame) {
/* ------------------------------------------------------------------ */
/*
- * Find a frame's buddy at a given order.
+ * Find a page's buddy at a given order.
* Buddy PFN = PFN ^ (1 << order).
*/
-static inline Frame *buddy_of(Frame *frame, uint8 order) {
- uint64 pfn = static_cast<uint64>(frame - g_frame_map);
+static inline Page *buddy_of(Page *page, uint8 order) {
+ uint64 pfn = static_cast<uint64>(page - g_page_map);
uint64 buddy_pfn = pfn ^ (1ULL << order);
- if (buddy_pfn >= g_num_frames)
+ if (buddy_pfn >= g_num_pages)
return nullptr;
- return &g_frame_map[buddy_pfn];
+ return &g_page_map[buddy_pfn];
}
/*
* Insert a free block into the buddy free list at the given order.
*/
-static void buddy_list_add(Frame *frame, uint8 order) {
- frame->state = FrameState::Free;
- frame->order = order;
+static void buddy_list_add(Page *page, uint8 order) {
+ page->state = PageState::Free;
+ page->order = order;
list_node *head = &g_free_areas[order];
- list_node *node = &frame->free.free_link;
+ list_node *node = &page->free.free_link;
node->next = head->next;
node->prev = head;
head->next->prev = node;
@@ -131,8 +131,8 @@ static void buddy_list_add(Frame *frame, uint8 order) {
/*
* Remove a block from whatever buddy free list it is on.
*/
-static void buddy_list_del(Frame *frame) {
- list_node *node = &frame->free.free_link;
+static void buddy_list_del(Page *page) {
+ list_node *node = &page->free.free_link;
node->prev->next = node->next;
node->next->prev = node->prev;
node->next = node;
@@ -140,11 +140,11 @@ static void buddy_list_del(Frame *frame) {
}
/*
- * Allocate a block of 2^order contiguous frames from the buddy system.
+ * Allocate a block of 2^order contiguous pages from the buddy system.
* Splits higher-order blocks if no exact match is available.
* Returns nullptr if no memory available.
*/
-static Frame *buddy_alloc(uint8 order) {
+static Page *buddy_alloc(uint8 order) {
for (int cur = order; cur < kMaxOrder; cur++) {
list_node *head = &g_free_areas[cur];
if (head->next == head)
@@ -152,56 +152,56 @@ static Frame *buddy_alloc(uint8 order) {
// Pop the first block from this order's free list
list_node *node = head->next;
- // Compute Frame* from the free_link member offset
- Frame *frame =
- reinterpret_cast<Frame *>(reinterpret_cast<uintptr>(node) -
- __builtin_offsetof(Frame, free.free_link));
- buddy_list_del(frame);
+ // Compute Page* from the free_link member offset
+ Page *page =
+ reinterpret_cast<Page *>(reinterpret_cast<uintptr>(node) -
+ __builtin_offsetof(Page, free.free_link));
+ buddy_list_del(page);
// Split down to the requested order
while (cur > order) {
cur--;
// The upper half becomes a free buddy
- Frame *split = &g_frame_map[(frame - g_frame_map) + (1ULL << cur)];
+ Page *split = &g_page_map[(page - g_page_map) + (1ULL << cur)];
buddy_list_add(split, static_cast<uint8>(cur));
}
- frame->state = FrameState::Allocated;
- frame->order = order;
- frame->refcount.store(1, __ATOMIC_RELAXED);
- return frame;
+ page->state = PageState::Allocated;
+ page->order = order;
+ page->refcount.store(1, __ATOMIC_RELAXED);
+ return page;
}
return nullptr;
}
/*
- * Return a block of 2^order frames to the buddy system.
+ * Return a block of 2^order pages to the buddy system.
* Coalesces with its buddy recursively up to kMaxOrder-1.
*/
-static void buddy_free(Frame *frame, uint8 order) {
- uint64 pfn = static_cast<uint64>(frame - g_frame_map);
+static void buddy_free(Page *page, uint8 order) {
+ uint64 pfn = static_cast<uint64>(page - g_page_map);
while (order < kMaxOrder - 1) {
- Frame *buddy = buddy_of(frame, order);
+ Page *buddy = buddy_of(page, order);
if (!buddy)
break;
// Buddy must be free AND at the same order to coalesce
- if (buddy->state != FrameState::Free || buddy->order != order)
+ if (buddy->state != PageState::Free || buddy->order != order)
break;
// Remove buddy from its free list and merge
buddy_list_del(buddy);
// The merged block starts at the lower PFN
- uint64 buddy_pfn = static_cast<uint64>(buddy - g_frame_map);
+ uint64 buddy_pfn = static_cast<uint64>(buddy - g_page_map);
if (buddy_pfn < pfn) {
- frame = buddy;
+ page = buddy;
pfn = buddy_pfn;
}
order++;
}
- buddy_list_add(frame, order);
+ buddy_list_add(page, order);
}
/* ------------------------------------------------------------------ */
@@ -215,12 +215,12 @@ static void buddy_free(Frame *frame, uint8 order) {
static void refill_pcp(PerCpuCache *pcp) {
LockGuard guard(g_pmm_lock);
for (uint32 i = 0; i < g_pcp_batch_size; i++) {
- Frame *f = buddy_alloc(0);
- if (!f)
+ Page *page = buddy_alloc(0);
+ if (!page)
break;
// Put onto PCP list (reuse the free_link for PCP chain)
- f->state = FrameState::PcpCached;
- list_node *node = &f->free.free_link;
+ page->state = PageState::PcpCached;
+ list_node *node = &page->free.free_link;
node->next = pcp->free_list.next;
node->prev = &pcp->free_list;
pcp->free_list.next->prev = node;
@@ -246,21 +246,21 @@ static void drain_pcp(PerCpuCache *pcp) {
node->next->prev = node->prev;
pcp->count--;
- Frame *f =
- reinterpret_cast<Frame *>(reinterpret_cast<uintptr>(node) -
- __builtin_offsetof(Frame, free.free_link));
- buddy_free(f, 0);
+ Page *page =
+ reinterpret_cast<Page *>(reinterpret_cast<uintptr>(node) -
+ __builtin_offsetof(Page, free.free_link));
+ buddy_free(page, 0);
}
}
/* ------------------------------------------------------------------ */
-/* Public API: single-frame (fast path via PCP) */
+/* Public API: single-page (fast path via PCP) */
/* ------------------------------------------------------------------ */
-Frame *pmm_alloc_frame() {
+Page *pmm_alloc_page() {
/*
* Disable interrupts to prevent reentry on the same CPU
- * (e.g. timer IRQ handler calling pmm_alloc_frame while we
+ * (e.g. timer IRQ handler calling pmm_alloc_page while we
* are mid-way through manipulating the PCP list).
*/
bool was_on = arch::cpu::intr_enabled();
@@ -272,7 +272,7 @@ Frame *pmm_alloc_frame() {
if (pcp->count == 0)
refill_pcp(pcp);
- Frame *frame = nullptr;
+ Page *page = nullptr;
if (pcp->count > 0) {
list_node *node = pcp->free_list.next;
// Unlink
@@ -280,28 +280,28 @@ Frame *pmm_alloc_frame() {
node->next->prev = node->prev;
pcp->count--;
- frame =
- reinterpret_cast<Frame *>(reinterpret_cast<uintptr>(node) -
- __builtin_offsetof(Frame, free.free_link));
+ page =
+ reinterpret_cast<Page *>(reinterpret_cast<uintptr>(node) -
+ __builtin_offsetof(Page, free.free_link));
- // Validate the frame is within the frame_map bounds
- if (frame < g_frame_map || frame >= g_frame_map + g_num_frames) {
- kernel_panic("pmm_alloc_frame: PCP returned out-of-bounds frame");
+ // Validate the page is within the page_map bounds
+ if (page < g_page_map || page >= g_page_map + g_num_pages) {
+ kernel_panic("pmm_alloc_page: PCP returned out-of-bounds page");
}
- frame->state = FrameState::Allocated;
- frame->order = 0;
- frame->refcount.store(1, __ATOMIC_RELAXED);
+ page->state = PageState::Allocated;
+ page->order = 0;
+ page->refcount.store(1, __ATOMIC_RELAXED);
}
if (was_on)
arch::cpu::intr_on();
- return frame;
+ return page;
}
-void pmm_free_frame(Frame *frame) {
- if (!frame)
- kernel_panic("pmm_free_frame: null frame");
+void pmm_free_page(Page *page) {
+ if (!page)
+ kernel_panic("pmm_free_page: null page");
bool was_on = arch::cpu::intr_enabled();
arch::cpu::intr_off();
@@ -310,9 +310,9 @@ void pmm_free_frame(Frame *frame) {
PerCpuCache *pcp = &g_pcp[cpu];
// Push onto PCP
- frame->state = FrameState::PcpCached;
- frame->order = 0;
- list_node *node = &frame->free.free_link;
+ page->state = PageState::PcpCached;
+ page->order = 0;
+ list_node *node = &page->free.free_link;
node->next = pcp->free_list.next;
node->prev = &pcp->free_list;
pcp->free_list.next->prev = node;
@@ -328,40 +328,40 @@ void pmm_free_frame(Frame *frame) {
}
/* ------------------------------------------------------------------ */
-/* Public API: multi-frame (slow path, direct buddy) */
+/* Public API: multi-page (slow path, direct buddy) */
/* ------------------------------------------------------------------ */
-Frame *pmm_alloc_frames(uint8 order) {
+Page *pmm_alloc_pages(uint8 order) {
if (order >= kMaxOrder)
return nullptr;
LockGuard guard(g_pmm_lock);
return buddy_alloc(order);
}
-void pmm_free_frames(Frame *frame, uint8 order) {
- if (!frame)
- kernel_panic("pmm_free_frames: null frame");
+void pmm_free_pages(Page *page, uint8 order) {
+ if (!page)
+ kernel_panic("pmm_free_pages: null page");
if (order >= kMaxOrder)
- kernel_panic("pmm_free_frames: order out of range");
+ kernel_panic("pmm_free_pages: order out of range");
LockGuard guard(g_pmm_lock);
- buddy_free(frame, order);
+ buddy_free(page, order);
}
/* ------------------------------------------------------------------ */
/* Public API: reference counting */
/* ------------------------------------------------------------------ */
-void frame_incref(Frame *frame) { frame->refcount.fetch_add(1); }
+void page_incref(Page *page) { page->refcount.fetch_add(1); }
-void frame_decref(Frame *frame) {
- uint32 old = frame->refcount.fetch_sub(1);
+void page_decref(Page *page) {
+ uint32 old = page->refcount.fetch_sub(1);
if (old == 1) {
// Refcount dropped to 0 — return to pool based on originally allocated
// order
- if (frame->order == 0)
- pmm_free_frame(frame);
+ if (page->order == 0)
+ pmm_free_page(page);
else
- pmm_free_frames(frame, frame->order);
+ pmm_free_pages(page, page->order);
}
}
@@ -383,33 +383,33 @@ void pmm_init() {
/* 1. Get memory layout from memblock (already scanned FDT) */
g_mem_base = memblock_get_mem_base();
g_mem_size = memblock_get_mem_size();
- g_num_frames = vmm_get_num_frames();
+ g_num_pages = vmm_get_num_pages();
if (g_mem_size == 0) {
kernel_panic("pmm_init: memblock reports zero memory");
}
/* 2. PCP parameters */
- uint32 calc_batch = static_cast<uint32>(g_num_frames / (kMaxCpus * 1024));
+ uint32 calc_batch = static_cast<uint32>(g_num_pages / (kMaxCpus * 1024));
g_pcp_batch_size = (calc_batch > 16) ? calc_batch : 16;
g_pcp_high_watermark = g_pcp_batch_size * 2;
- /* 3. frame_map was allocated by vmm_init at vmemmap VA.
- * No relocation needed — frame_map starts life at the right VA. */
- g_frame_map_pa = vmm_get_frame_map_pa();
- g_frame_map_bytes = vmm_get_frame_map_size();
- g_frame_map = reinterpret_cast<Frame *>(arch::g_vmemmap_base);
-
- /* 4. Zero out the entire frame_map */
- zero_range(g_frame_map, g_frame_map_bytes);
-
- /* 4b. Default every frame descriptor to reserved/non-free. */
- for (uint64 i = 0; i < g_num_frames; i++) {
- Frame *f = &g_frame_map[i];
- f->refcount.store(0, __ATOMIC_RELAXED);
- f->state = FrameState::Allocated;
- f->order = 0;
- f->free.free_link.init();
+ /* 3. page_map was allocated by vmm_init at vmemmap VA.
+ * No relocation needed — page_map starts life at the right VA. */
+ g_page_map_pa = vmm_get_page_map_pa();
+ g_page_map_bytes = vmm_get_page_map_size();
+ g_page_map = reinterpret_cast<Page *>(arch::g_vmemmap_base);
+
+ /* 4. Zero out the entire page_map */
+ zero_range(g_page_map, g_page_map_bytes);
+
+ /* 4b. Default every page descriptor to reserved/non-free. */
+ for (uint64 i = 0; i < g_num_pages; i++) {
+ Page *page = &g_page_map[i];
+ page->refcount.store(0, __ATOMIC_RELAXED);
+ page->state = PageState::Allocated;
+ page->order = 0;
+ page->free.free_link.init();
}
/* 5. Initialize buddy free list heads */
@@ -426,7 +426,7 @@ void pmm_init() {
/* 7. Feed free regions from memblock into the buddy system.
* memblock knows about all reserved regions (firmware, kernel,
- * page table pages, frame_map). Everything else is free. */
+ * page table pages, page_map). Everything else is free. */
struct FeedCtx {
uint64 total_pages;
};
@@ -438,9 +438,8 @@ void pmm_init() {
uint64 start_pfn = (base - memblock_get_mem_base()) / kPageSize;
uint64 end_pfn = start_pfn + size / kPageSize;
- /* Access frame_map via the extern g_frame_map (already at vmemmap VA)
- */
- extern Frame *g_frame_map;
+ /* Access page_map via the extern g_page_map (already at vmemmap VA) */
+ extern Page *g_page_map;
extern list_node g_free_areas[];
uint64 pfn = start_pfn;
@@ -454,15 +453,15 @@ void pmm_init() {
}
}
- Frame *f = &g_frame_map[pfn];
- f->state = FrameState::Free;
- f->order = order;
- f->refcount.store(0, __ATOMIC_RELAXED);
- f->free.free_link.init();
+ Page *page = &g_page_map[pfn];
+ page->state = PageState::Free;
+ page->order = order;
+ page->refcount.store(0, __ATOMIC_RELAXED);
+ page->free.free_link.init();
/* Inline buddy_list_add since we're inside a lambda */
list_node *head = &g_free_areas[order];
- list_node *node = &f->free.free_link;
+ list_node *node = &page->free.free_link;
node->next = head->next;
node->prev = head;
head->next->prev = node;