diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-05-13 15:54:57 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-05-13 15:54:57 +0800 |
| commit | 70fa5d207fecf4db0eef58f2b6120bdd0b0f3e96 (patch) | |
| tree | e2e7e431eeddced8c845986dd5469448b2ae5d5c | |
| parent | c9f158361cba8218a45fdc0ccb9a14ba05ddac3a (diff) | |
| download | lume-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.h | 44 | ||||
| -rw-r--r-- | na/mm/pmm.cc | 255 |
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; |
