summaryrefslogtreecommitdiff
path: root/na/test/test_pmm.cc
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-05-13 16:29:29 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-05-13 16:29:29 +0800
commitfb4512afbec76e91cfaf8725d2d39a11c3969943 (patch)
tree1b9a72a864ea3c3b3544026eb86f2b8e66973ed5 /na/test/test_pmm.cc
parent25fa7f331d6772f5b2cfee50c581db1c5aa71a1e (diff)
downloadlume-fb4512afbec76e91cfaf8725d2d39a11c3969943.tar.gz
lume-fb4512afbec76e91cfaf8725d2d39a11c3969943.tar.xz
feat: update PMM self-test to use page terminology and improve clarity
Diffstat (limited to 'na/test/test_pmm.cc')
-rw-r--r--na/test/test_pmm.cc248
1 files changed, 124 insertions, 124 deletions
diff --git a/na/test/test_pmm.cc b/na/test/test_pmm.cc
index 948e444..53b2b2a 100644
--- a/na/test/test_pmm.cc
+++ b/na/test/test_pmm.cc
@@ -5,14 +5,14 @@
* Boot Self-Test: Physical Memory Manager (PMM)
*
* Tests:
- * 1. Single frame alloc/free round-trip
- * 2. Frame address alignment (4KB)
- * 3. No duplicate frames from consecutive allocs
- * 4. Free then re-alloc returns the same frame (PCP cache)
- * 5. Multi-frame (order > 0) alloc/free
+ * 1. Single page alloc/free round-trip
+ * 2. Page address alignment (4KB)
+ * 3. No duplicate pages from consecutive allocs
+ * 4. Free then re-alloc returns the same page (PCP cache)
+ * 5. Multi-page (order > 0) alloc/free
* 6. Bulk alloc stress test (alloc many, free all)
* 7. Reference counting (incref/decref)
- * 8. frame_to_pa / pa_to_frame round-trip
+ * 8. page_to_pa / pa_to_page round-trip
*/
#include <lume/selftest.h>
@@ -21,236 +21,236 @@
void selftest_pmm()
{
- st_begin("pmm: single frame alloc/free");
+ st_begin("pmm: single page alloc/free");
{
- Frame* f = pmm_alloc_frame();
- ST_ASSERT(f != nullptr);
- ST_ASSERT(f->state == FrameState::Allocated);
- ST_ASSERT(f->order == 0);
+ Page* page = pmm_alloc_page();
+ ST_ASSERT(page != nullptr);
+ ST_ASSERT(page->state == PageState::Allocated);
+ ST_ASSERT(page->order == 0);
- uint64 pa = frame_to_pa(f).raw;
+ uint64 pa = page_to_pa(page).raw;
ST_ASSERT_EQ(pa % kPageSize, 0ULL); // 4KB aligned
- uint64 va = frame_to_va(f).raw;
+ uint64 va = page_to_va(page).raw;
ST_ASSERT_EQ(va, pa_to_va(phys_addr(pa)).raw);
- pmm_free_frame(f);
+ pmm_free_page(page);
}
st_pass();
- st_begin("pmm: no duplicate frames");
+ st_begin("pmm: no duplicate pages");
{
- Frame* f1 = pmm_alloc_frame();
- Frame* f2 = pmm_alloc_frame();
- ST_ASSERT(f1 != nullptr);
- ST_ASSERT(f2 != nullptr);
- ST_ASSERT_NE(f1, f2);
- ST_ASSERT_NE(frame_to_pa(f1).raw, frame_to_pa(f2).raw);
- pmm_free_frame(f2);
- pmm_free_frame(f1);
+ Page* p1 = pmm_alloc_page();
+ Page* p2 = pmm_alloc_page();
+ ST_ASSERT(p1 != nullptr);
+ ST_ASSERT(p2 != nullptr);
+ ST_ASSERT_NE(p1, p2);
+ ST_ASSERT_NE(page_to_pa(p1).raw, page_to_pa(p2).raw);
+ pmm_free_page(p2);
+ pmm_free_page(p1);
}
st_pass();
st_begin("pmm: free then re-alloc (PCP)");
{
- Frame* f1 = pmm_alloc_frame();
- uint64 pa1 = frame_to_pa(f1).raw;
- pmm_free_frame(f1);
+ Page* p1 = pmm_alloc_page();
+ uint64 pa1 = page_to_pa(p1).raw;
+ pmm_free_page(p1);
- // PCP should return the most recently freed frame
- Frame* f2 = pmm_alloc_frame();
- uint64 pa2 = frame_to_pa(f2).raw;
+ // PCP should return the most recently freed page
+ Page* p2 = pmm_alloc_page();
+ uint64 pa2 = page_to_pa(p2).raw;
ST_ASSERT_EQ(pa1, pa2);
- pmm_free_frame(f2);
+ pmm_free_page(p2);
}
st_pass();
- st_begin("pmm: multi-frame alloc (order 2 = 4 pages)");
+ st_begin("pmm: multi-page alloc (order 2 = 4 pages)");
{
- Frame* f = pmm_alloc_frames(2);
- ST_ASSERT(f != nullptr);
- ST_ASSERT(f->order == 2);
+ Page* page = pmm_alloc_pages(2);
+ ST_ASSERT(page != nullptr);
+ ST_ASSERT(page->order == 2);
- uint64 pa = frame_to_pa(f).raw;
+ uint64 pa = page_to_pa(page).raw;
ST_ASSERT_EQ(pa % (kPageSize * 4), 0ULL); // Naturally aligned
- pmm_free_frames(f, 2);
+ pmm_free_pages(page, 2);
}
st_pass();
- st_begin("pmm: bulk alloc/free stress (256 frames)");
+ st_begin("pmm: bulk alloc/free stress (256 pages)");
{
constexpr int N = 256;
- Frame* frames[N];
+ Page* pages[N];
// Allocate all
for (int i = 0; i < N; i++) {
- frames[i] = pmm_alloc_frame();
- ST_ASSERT(frames[i] != nullptr);
+ pages[i] = pmm_alloc_page();
+ ST_ASSERT(pages[i] != nullptr);
}
// Verify no duplicates (spot check: consecutive pairs)
for (int i = 1; i < N; i++) {
- ST_ASSERT_NE(frames[i], frames[i - 1]);
+ ST_ASSERT_NE(pages[i], pages[i - 1]);
}
// Free all
for (int i = 0; i < N; i++) {
- pmm_free_frame(frames[i]);
+ pmm_free_page(pages[i]);
}
}
st_pass();
- st_begin("pmm: frame_to_pa / pa_to_frame round-trip");
+ st_begin("pmm: page_to_pa / pa_to_page round-trip");
{
- Frame* f = pmm_alloc_frame();
- ST_ASSERT(f != nullptr);
- uint64 pa = frame_to_pa(f).raw;
- Frame* f2 = pa_to_frame(phys_addr(pa));
- ST_ASSERT_EQ(f, f2);
- pmm_free_frame(f);
+ Page* page = pmm_alloc_page();
+ ST_ASSERT(page != nullptr);
+ uint64 pa = page_to_pa(page).raw;
+ Page* page2 = pa_to_page(phys_addr(pa));
+ ST_ASSERT_EQ(page, page2);
+ pmm_free_page(page);
}
st_pass();
st_begin("pmm: refcount incref/decref");
{
- Frame* f = pmm_alloc_frame();
- ST_ASSERT(f != nullptr);
- ST_ASSERT(f->refcount.load() == 1);
+ Page* page = pmm_alloc_page();
+ ST_ASSERT(page != nullptr);
+ ST_ASSERT(page->refcount.load() == 1);
- frame_incref(f);
- ST_ASSERT(f->refcount.load() == 2);
+ page_incref(page);
+ ST_ASSERT(page->refcount.load() == 2);
- frame_decref(f); // 2 -> 1, should NOT free
- ST_ASSERT(f->refcount.load() == 1);
- ST_ASSERT(f->state == FrameState::Allocated);
+ page_decref(page); // 2 -> 1, should NOT free
+ ST_ASSERT(page->refcount.load() == 1);
+ ST_ASSERT(page->state == PageState::Allocated);
- frame_decref(f); // 1 -> 0, should free to pool
- // After decref to 0, frame is returned to pool.
- // We can't safely check f->state because it might be reused.
+ page_decref(page); // 1 -> 0, should free to pool
+ // After decref to 0, page is returned to pool.
+ // We can't safely check page->state because it might be reused.
}
st_pass();
- st_begin("pmm: pmm_alloc_frames natural alignment (order-3)");
+ st_begin("pmm: pmm_alloc_pages natural alignment (order-3)");
{
- Frame* f = pmm_alloc_frames(3);
- ST_ASSERT(f != nullptr);
- uint64 pa = frame_to_pa(f).raw;
+ Page* page = pmm_alloc_pages(3);
+ ST_ASSERT(page != nullptr);
+ uint64 pa = page_to_pa(page).raw;
ST_ASSERT_EQ(pa % (kPageSize * 8), 0ULL);
- pmm_free_frames(f, 3);
+ pmm_free_pages(page, 3);
}
st_pass();
- st_begin("pmm: frame_decref to 0 and reuse (order-0)");
+ st_begin("pmm: page_decref to 0 and reuse (order-0)");
{
- Frame* f = pmm_alloc_frame();
- uint64 pa = frame_to_pa(f).raw;
- frame_decref(f); // drops 1 -> 0, freeing it
+ Page* page = pmm_alloc_page();
+ uint64 pa = page_to_pa(page).raw;
+ page_decref(page); // drops 1 -> 0, freeing it
- // Next alloc from PCP should give back the same frame
- Frame* f2 = pmm_alloc_frame();
- ST_ASSERT_EQ(frame_to_pa(f2).raw, pa);
- pmm_free_frame(f2);
+ // Next alloc from PCP should give back the same page
+ Page* page2 = pmm_alloc_page();
+ ST_ASSERT_EQ(page_to_pa(page2).raw, pa);
+ pmm_free_page(page2);
}
st_pass();
- st_begin("pmm: frame_decref to 0 on multi-page blocks (order-3)");
+ st_begin("pmm: page_decref to 0 on multi-page blocks (order-3)");
{
- Frame* f = pmm_alloc_frames(3);
- uint64 pa = frame_to_pa(f).raw;
- frame_decref(f); // Should internally route to pmm_free_frames(f, 3)
+ Page* page = pmm_alloc_pages(3);
+ uint64 pa = page_to_pa(page).raw;
+ page_decref(page); // Should internally route to pmm_free_pages(page, 3)
// Re-allocate order-3 and see if we get it back (buddy coalescing was respected)
- Frame* f2 = pmm_alloc_frames(3);
- ST_ASSERT_EQ(frame_to_pa(f2).raw, pa);
- pmm_free_frames(f2, 3);
+ Page* page2 = pmm_alloc_pages(3);
+ ST_ASSERT_EQ(page_to_pa(page2).raw, pa);
+ pmm_free_pages(page2, 3);
}
st_pass();
st_begin("pmm: buddy split and block coalescing correctness");
{
// Force split: allocate order-1, free it, allocate two order-0
- Frame* f_ord1 = pmm_alloc_frames(1);
- uint64 pa1 = frame_to_pa(f_ord1).raw;
- pmm_free_frames(f_ord1, 1);
+ Page* page_ord1 = pmm_alloc_pages(1);
+ uint64 pa1 = page_to_pa(page_ord1).raw;
+ pmm_free_pages(page_ord1, 1);
- Frame* f0_a = pmm_alloc_frame();
- Frame* f0_b = pmm_alloc_frame();
+ Page* p0_a = pmm_alloc_page();
+ Page* p0_b = pmm_alloc_page();
// Free them to trigger coalesce
- pmm_free_frame(f0_a);
- pmm_free_frame(f0_b);
+ pmm_free_page(p0_a);
+ pmm_free_page(p0_b);
// Allocate order-1 again to verify successful coalescing
- Frame* f_ord1_again = pmm_alloc_frames(1);
- ST_ASSERT_EQ(frame_to_pa(f_ord1_again).raw, pa1);
- pmm_free_frames(f_ord1_again, 1);
+ Page* page_ord1_again = pmm_alloc_pages(1);
+ ST_ASSERT_EQ(page_to_pa(page_ord1_again).raw, pa1);
+ pmm_free_pages(page_ord1_again, 1);
}
st_pass();
st_begin("pmm: mixed order cross allocation");
{
- Frame* f0 = pmm_alloc_frame();
- Frame* f2 = pmm_alloc_frames(2);
- Frame* f0b = pmm_alloc_frame();
+ Page* p0 = pmm_alloc_page();
+ Page* p2 = pmm_alloc_pages(2);
+ Page* p0b = pmm_alloc_page();
- ST_ASSERT(f0 != nullptr);
- ST_ASSERT(f2 != nullptr);
- ST_ASSERT(f0b != nullptr);
- ST_ASSERT_NE(frame_to_pa(f0).raw, frame_to_pa(f2).raw);
+ ST_ASSERT(p0 != nullptr);
+ ST_ASSERT(p2 != nullptr);
+ ST_ASSERT(p0b != nullptr);
+ ST_ASSERT_NE(page_to_pa(p0).raw, page_to_pa(p2).raw);
- pmm_free_frame(f0);
- pmm_free_frames(f2, 2);
- pmm_free_frame(f0b);
+ pmm_free_page(p0);
+ pmm_free_pages(p2, 2);
+ pmm_free_page(p0b);
}
st_pass();
st_begin("pmm: PCP drain/refill boundaries");
{
// kPcpHighWatermark = 64. Allocating and freeing 65 blocks triggers drain.
- Frame* frames[65];
- for (int i=0; i<65; i++) frames[i] = pmm_alloc_frame();
- for (int i=0; i<65; i++) pmm_free_frame(frames[i]);
+ Page* pages[65];
+ for (int i=0; i<65; i++) pages[i] = pmm_alloc_page();
+ for (int i=0; i<65; i++) pmm_free_page(pages[i]);
// The system shouldn't crash and PCP logic should handle this.
- Frame* verify_f = pmm_alloc_frame();
- ST_ASSERT(verify_f != nullptr);
- pmm_free_frame(verify_f);
+ Page* verify_page = pmm_alloc_page();
+ ST_ASSERT(verify_page != nullptr);
+ pmm_free_page(verify_page);
}
st_pass();
st_begin("pmm: OOM exhaustion stability");
{
- Frame* head = nullptr;
+ Page* head = nullptr;
// First allocate large chunks to be fast
while (true) {
- Frame* f = pmm_alloc_frames(5);
- if (!f) break;
- uint64* ptr = reinterpret_cast<uint64*>(frame_to_va(f).raw);
+ Page* page = pmm_alloc_pages(5);
+ if (!page) break;
+ uint64* ptr = reinterpret_cast<uint64*>(page_to_va(page).raw);
*ptr = reinterpret_cast<uint64>(head);
// encode order in the second word
*(ptr + 1) = 5;
- head = f;
+ head = page;
}
// Then allocate single frames until absolute zero
while (true) {
- Frame* f = pmm_alloc_frame();
- if (!f) break;
- uint64* ptr = reinterpret_cast<uint64*>(frame_to_va(f).raw);
+ Page* page = pmm_alloc_page();
+ if (!page) break;
+ uint64* ptr = reinterpret_cast<uint64*>(page_to_va(page).raw);
*ptr = reinterpret_cast<uint64>(head);
*(ptr + 1) = 0;
- head = f;
+ head = page;
}
// We hit nullptr. System is completely exhausted.
- ST_ASSERT(pmm_alloc_frame() == nullptr);
+ ST_ASSERT(pmm_alloc_page() == nullptr);
// Free everything
while (head) {
- uint64* ptr = reinterpret_cast<uint64*>(frame_to_va(head).raw);
- Frame* nxt = reinterpret_cast<Frame*>(*ptr);
+ uint64* ptr = reinterpret_cast<uint64*>(page_to_va(head).raw);
+ Page* nxt = reinterpret_cast<Page*>(*ptr);
uint64 order = *(ptr + 1);
- if (order == 5) pmm_free_frames(head, 5);
- else pmm_free_frame(head);
+ if (order == 5) pmm_free_pages(head, 5);
+ else pmm_free_page(head);
head = nxt;
}
}
@@ -259,10 +259,10 @@ void selftest_pmm()
st_begin("pmm: out of bounds order allocation gracefully fails");
{
// kMaxOrder is 11, so passing 11 or higher should safely return nullptr.
- Frame* f = pmm_alloc_frames(11);
- ST_ASSERT(f == nullptr);
- f = pmm_alloc_frames(255);
- ST_ASSERT(f == nullptr);
+ Page* page = pmm_alloc_pages(11);
+ ST_ASSERT(page == nullptr);
+ page = pmm_alloc_pages(255);
+ ST_ASSERT(page == nullptr);
}
st_pass();