diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-05-13 16:29:29 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-05-13 16:29:29 +0800 |
| commit | fb4512afbec76e91cfaf8725d2d39a11c3969943 (patch) | |
| tree | 1b9a72a864ea3c3b3544026eb86f2b8e66973ed5 | |
| parent | 25fa7f331d6772f5b2cfee50c581db1c5aa71a1e (diff) | |
| download | lume-fb4512afbec76e91cfaf8725d2d39a11c3969943.tar.gz lume-fb4512afbec76e91cfaf8725d2d39a11c3969943.tar.xz | |
feat: update PMM self-test to use page terminology and improve clarity
| -rw-r--r-- | na/test/test_pmm.cc | 248 | ||||
| -rw-r--r-- | na/test/test_slab.cc | 12 | ||||
| -rw-r--r-- | na/test/test_vma.cc | 150 |
3 files changed, 267 insertions, 143 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(); diff --git a/na/test/test_slab.cc b/na/test/test_slab.cc index e3ac72d..2ba1a5d 100644 --- a/na/test/test_slab.cc +++ b/na/test/test_slab.cc @@ -174,7 +174,7 @@ void selftest_slab() } st_pass(); - st_begin("slab: empty page reclamation (verifiable by frame availability)"); + st_begin("slab: empty page reclamation (verifiable by page availability)"); { // Allocate 128-byte objects until we hit the start of a completely new page. void* base_ptr = nullptr; @@ -201,18 +201,18 @@ void selftest_slab() // At this point, we completely own all 32 objects of this slab page. uint64 page_pa = va_to_pa(virt_addr(reinterpret_cast<uint64>(base_ptr))).raw; - Frame* f = pa_to_frame(phys_addr(page_pa)); + Page* page = pa_to_page(phys_addr(page_pa)); - ST_ASSERT(f->state == FrameState::Slab); - ST_ASSERT(f->slab.obj_count == 32); + ST_ASSERT(page->state == PageState::Slab); + ST_ASSERT(page->slab.obj_count == 32); // Free all items in this isolated slab page for (int i = 0; i < 32; i++) { kfree(ptrs[i]); } - // The frame MUST have been returned to the PMM (either global Free or Per-CPU Cached). - ST_ASSERT(f->state == FrameState::Free || f->state == FrameState::PcpCached); + // The page MUST have been returned to the PMM (either global Free or Per-CPU Cached). + ST_ASSERT(page->state == PageState::Free || page->state == PageState::PcpCached); // Clean up the drain array for (int i = 0; i < drain_count; i++) { diff --git a/na/test/test_vma.cc b/na/test/test_vma.cc index d47fbd0..5278914 100644 --- a/na/test/test_vma.cc +++ b/na/test/test_vma.cc @@ -31,7 +31,7 @@ void selftest_vma() { ST_ASSERT_EQ(ret, 0); // Fault in the first page - ret = vmm_handle_page_fault(space, 0x400500, 0); + ret = vmm_handle_page_fault(space, 0x400500, VmFaultCause::Read); ST_ASSERT_EQ(ret, 0); // Verify mapping in hardware page table @@ -41,11 +41,11 @@ void selftest_vma() { ST_ASSERT_NE(pa, 0ULL); // Fault in the second page - ret = vmm_handle_page_fault(space, 0x401FFF, 0); + ret = vmm_handle_page_fault(space, 0x401FFF, VmFaultCause::Read); ST_ASSERT_EQ(ret, 0); // Should reject fault outside VMA with -EFAULT - ret = vmm_handle_page_fault(space, 0x402000, 0); + ret = vmm_handle_page_fault(space, 0x402000, VmFaultCause::Read); ST_ASSERT_EQ(ret, -EFAULT); space->destroy(); @@ -58,13 +58,13 @@ void selftest_vma() { vmm_map_user(space, 0x10000, 0x2000, VM_READ); // Inside the region — should succeed - ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x10500, 0), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x10500, VmFaultCause::Read), 0); // Unmap exactly vmm_unmap_user(space, 0x10000, 0x2000); // Should fail now with -EFAULT - ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x10500, 0), -EFAULT); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x10500, VmFaultCause::Read), -EFAULT); space->destroy(); } @@ -75,15 +75,15 @@ void selftest_vma() { VmSpace* space = VmSpace::create(); vmm_map_user(space, 0x200000, 0x1000, VM_READ | VM_WRITE); - // First fault allocates the physical frame - int ret = vmm_handle_page_fault(space, 0x200000, 0); + // First fault allocates the physical page + int ret = vmm_handle_page_fault(space, 0x200000, VmFaultCause::Read); ST_ASSERT_EQ(ret, 0); // Second fault on the same page should hit "already mapped" path - ret = vmm_handle_page_fault(space, 0x200100, 0); + ret = vmm_handle_page_fault(space, 0x200100, VmFaultCause::Read); ST_ASSERT_EQ(ret, 0); - // Verify both land on the same physical frame + // Verify both land on the same physical page uint64 pa1 = 0, pa2 = 0; pmap::lookup(space->root_pa, 0x200000, &pa1); pmap::lookup(space->root_pa, 0x200100, &pa2); @@ -138,11 +138,11 @@ void selftest_vma() { vmm_unmap_user(space, 0x60000, 0x1000); // First and third should still be reachable - ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x50000, 0), 0); - ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x70000, 0), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x50000, VmFaultCause::Read), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x70000, VmFaultCause::Read), 0); // Middle should fail - ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x60000, 0), -EFAULT); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x60000, VmFaultCause::Read), -EFAULT); space->destroy(); } @@ -152,7 +152,131 @@ void selftest_vma() { { ST_ASSERT_EQ(vmm_map_user(nullptr, 0x1000, 0x1000, VM_READ), -EINVAL); ST_ASSERT_EQ(vmm_unmap_user(nullptr, 0x1000, 0x1000), -EINVAL); - ST_ASSERT_EQ(vmm_handle_page_fault(nullptr, 0x1000, 0), -EINVAL); + ST_ASSERT_EQ(vmm_handle_page_fault(nullptr, 0x1000, VmFaultCause::Read), -EINVAL); + } + st_pass(); + + st_begin("vma: clone and copy-on-write"); + { + VmSpace* parent = VmSpace::create(); + vmm_map_user(parent, 0x80000, 0x1000, VM_READ | VM_WRITE); + + /* Trigger read fault in parent to allocate physical page */ + ST_ASSERT_EQ(vmm_handle_page_fault(parent, 0x80000, VmFaultCause::Read), 0); + uint64 parent_pa_initial; + ST_ASSERT(pmap::lookup(parent->root_pa, 0x80000, &parent_pa_initial)); + + /* Clone the address space */ + VmSpace* child = vmm_clone(parent); + ST_ASSERT(child != nullptr); + + /* Verify child has the same physical page mapped but COW */ + uint64 child_pa; + ST_ASSERT(pmap::lookup(child->root_pa, 0x80000, &child_pa)); + ST_ASSERT_EQ(child_pa, parent_pa_initial); + ST_ASSERT(pmap::is_cow(child->root_pa, 0x80000)); + ST_ASSERT(pmap::is_cow(parent->root_pa, 0x80000)); /* Parent also marked COW */ + + /* Trigger Store Page Fault (cause=15) in child */ + ST_ASSERT_EQ(vmm_handle_page_fault(child, 0x80000, VmFaultCause::Write), 0); + + /* Child should now have a new physical page, no longer COW */ + uint64 child_pa_new; + ST_ASSERT(pmap::lookup(child->root_pa, 0x80000, &child_pa_new)); + ST_ASSERT(child_pa_new != parent_pa_initial); + ST_ASSERT(!pmap::is_cow(child->root_pa, 0x80000)); + + /* Parent is still mapped to the old page, still COW because it hasn't written */ + uint64 parent_pa_current; + ST_ASSERT(pmap::lookup(parent->root_pa, 0x80000, &parent_pa_current)); + ST_ASSERT_EQ(parent_pa_current, parent_pa_initial); + ST_ASSERT(pmap::is_cow(parent->root_pa, 0x80000)); + + /* Trigger Store Page Fault in parent */ + ST_ASSERT_EQ(vmm_handle_page_fault(parent, 0x80000, VmFaultCause::Write), 0); + ST_ASSERT(!pmap::is_cow(parent->root_pa, 0x80000)); + + child->destroy(); + parent->destroy(); + } + st_pass(); + + st_begin("vma: gap search finds next free range"); + { + VmSpace* space = VmSpace::create(); + vmm_map_user(space, 0x1000, 0x3000, VM_READ); + + uint64 va = mm_find_free_va(space, 0x1000, kPageSize); + ST_ASSERT_EQ(va, 0x4000ULL); + + space->destroy(); + } + st_pass(); + + st_begin("vma: reject overlapping mapping"); + { + VmSpace* space = VmSpace::create(); + ST_ASSERT_EQ(vmm_map_user(space, 0x20000, 0x2000, VM_READ), 0); + ST_ASSERT_EQ(vmm_map_user(space, 0x21000, 0x1000, VM_READ), -EINVAL); + space->destroy(); + } + st_pass(); + + st_begin("vma: partial unmap splits a single VMA"); + { + VmSpace* space = VmSpace::create(); + ST_ASSERT_EQ(vmm_map_user(space, 0x80000, 0x3000, VM_READ | VM_WRITE), 0); + + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x80000, VmFaultCause::Read), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x81000, VmFaultCause::Read), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x82000, VmFaultCause::Read), 0); + + ST_ASSERT_EQ(vmm_unmap_user(space, 0x81000, 0x1000), 0); + + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x80000, VmFaultCause::Read), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x82000, VmFaultCause::Read), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x81000, VmFaultCause::Read), -EFAULT); + + space->destroy(); + } + st_pass(); + + st_begin("vma: mprotect read-only blocks write faults"); + { + VmSpace* space = VmSpace::create(); + ST_ASSERT_EQ(vmm_map_user(space, 0x90000, 0x2000, VM_READ | VM_WRITE), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x90000, VmFaultCause::Read), 0); + + ST_ASSERT_EQ(vmm_protect_user(space, 0x90000, 0x1000, VM_READ), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x90000, VmFaultCause::Write), -EFAULT); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x91000, VmFaultCause::Write), 0); + + space->destroy(); + } + st_pass(); + + st_begin("vma: PROT_NONE unmaps existing pages"); + { + VmSpace* space = VmSpace::create(); + ST_ASSERT_EQ(vmm_map_user(space, 0xA0000, 0x1000, VM_READ | VM_WRITE), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0xA0000, VmFaultCause::Read), 0); + + ST_ASSERT_EQ(vmm_protect_user(space, 0xA0000, 0x1000, 0), 0); + ST_ASSERT_EQ(vmm_handle_page_fault(space, 0xA0000, VmFaultCause::Read), -EFAULT); + + space->destroy(); + } + st_pass(); + + st_begin("vma: range covered check"); + { + VmSpace* space = VmSpace::create(); + ST_ASSERT_EQ(vmm_map_user(space, 0xB0000, 0x2000, VM_READ), 0); + + ST_ASSERT(vma_range_covered(space, 0xB0000, 0xB1000)); + ST_ASSERT(!vma_range_covered(space, 0xB0000, 0xB3000)); + + space->destroy(); } st_pass(); } |
