- Created two-level x86 paging (page directory + page tables) with 4 KiB pages.
- Identity maps all detected physical memory in two phases:
1) Static: first 16 MiB using 4 BSS-allocated page tables (avoids
chicken-and-egg with PMM bitmap in BSS).
2) Dynamic: memory above 16 MiB using PMM-allocated page tables,
created before paging is enabled so physical addresses still work.
- Provides kernel heap at 0xD0000000–0xF0000000 for virtual page allocation.
- API: paging_map_page, paging_unmap_page, paging_alloc_page, paging_free_page,
paging_get_physical.
- Added pmm_get_memory_size() to expose detected RAM for paging init.
- Kernel tests paging by allocating a virtual page, writing 0xDEADBEEF, and
reading it back, then freeing it.
- Added documentation in docs/paging.md.
Tested: boots and passes paging test with both 4 MiB and 128 MiB RAM in QEMU.
2.9 KiB
Paging Subsystem
Overview
The paging subsystem manages virtual memory using the x86 two-level paging scheme (no PAE). It provides identity mapping for all physical memory and a kernel heap region for dynamic virtual page allocation.
Architecture
Page Table Structure
x86 32-bit paging uses two levels:
| Level | Entries | Each Entry Maps | Total Coverage |
|---|---|---|---|
| Page Directory | 1024 | 4 MiB (one page table) | 4 GiB |
| Page Table | 1024 | 4 KiB (one page) | 4 MiB |
Each entry is a 32-bit value containing a 20-bit physical page frame number and 12 bits of flags.
Identity Mapping
During initialization, all detected physical memory is identity-mapped (virtual address = physical address). This is done in two phases:
-
Static mapping (first 16 MiB): Four page tables are statically allocated in BSS. This avoids a chicken-and-egg problem since the PMM bitmap itself resides in this region.
-
Dynamic mapping (above 16 MiB): Additional page tables are allocated from the PMM before paging is enabled (so physical addresses are still directly accessible). These cover all remaining detected physical memory.
Kernel Heap
The kernel heap region occupies virtual addresses 0xD0000000 through 0xF0000000 (768 MiB).
When paging_alloc_page() is called:
- A physical page is allocated from the PMM.
- A page table entry is created mapping the next free virtual address to the physical page.
- The virtual address is returned.
When paging_free_page() is called:
- The physical address is looked up via the page table entry.
- The virtual mapping is removed.
- The physical page is returned to the PMM.
TLB Management
- Single-page invalidations use
invlpg. - Full TLB flushes use CR3 reload.
API
void init_paging(void);
void paging_map_page(uint32_t vaddr, uint32_t paddr, uint32_t flags);
void paging_unmap_page(uint32_t vaddr);
void *paging_alloc_page(void);
void paging_free_page(void *vaddr);
uint32_t paging_get_physical(uint32_t vaddr);
Flags
| Flag | Value | Meaning |
|---|---|---|
PAGE_PRESENT |
0x001 | Page is present in memory |
PAGE_WRITE |
0x002 | Page is writable |
PAGE_USER |
0x004 | Page is user-accessible (ring 3) |
Key Files
src/paging.c/src/paging.h— Implementation and API.src/pmm.c/src/pmm.h— Physical page allocation backing.
Design Decisions
- No higher-half kernel yet: The kernel runs at its physical load address (1 MiB) with identity mapping. Higher-half mapping (0xC0000000) can be added later without changing the paging API.
- Static + dynamic page tables: The first 16 MiB uses BSS-allocated tables to bootstrap, while memory above 16 MiB uses PMM-allocated tables. This keeps BSS usage bounded at ~16 KiB regardless of total RAM.
- Sequential heap allocation: The heap grows upward linearly. No free-list reuse of freed virtual addresses is implemented yet.