Master the fundamental concepts of build a mini kernel through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksPaging uses CR3 as root physical address of page tables. Set CR0.PG after building valid PDE/PTE chains mapping kernel text and an identity map for bring-up.
Bring-up pattern:
4 MB identity for transitionFor example, PTE for virtual 0xC0000000 might point to physical 0x00100000 with present+writable bits.
The two-level page directory and page table you set up here are the literal structures the x86 MMU walks via CR3 on every memory access, and identity-mapping the first 4MB is the standard trick every OSDev tutorial uses so the kernel doesn't crash the instant paging is enabled. Forget the present bit on a single entry and the very next instruction fetch after enabling paging triggers a page fault with no handler yet installed.
Before you call the implementation done, walk failure modes on purpose. Test empty structures, single-element edge cases, maximum concurrency, and errno paths that must not crash the program. OS code usually fails in production when happy-path tests pass but invariants break under contention or memory pressure.
Keep structures small and name fields after kernel counterparts when possible. That lets you read man pages and kernel source side by side while you work. Print observable events during development; remove noisy logs once tests pass reliably.
You will allocate page tables from the E820-backed allocator and enable paging. The task asks you to handle a deliberate page fault by fixing a missing PTE.
Simulate 32-bit x86 paging, which uses two levels of tables. A virtual address splits into a 10-bit directory index, a 10-bit table index, and a 12-bit offset. Build the page directory and page tables, turn paging on, and walk addresses the way the MMU does. Report the entries read, the physical result, or the page fault the CPU would raise.
cLoading…
FLAGS is one of kernel-ro, kernel-rw, user-ro and user-rw. Entry bits: P 0x1, RW 0x2, US 0x4, A (accessed) 0x20, D (dirty) 0x40.
0x00001000 (CR3). When a mapping needs a directory slot that is not present, allocate a new page table at the next free frame, starting at 0x00002000 and adding 0x1000 each time. The PDE starts as table | P. Each map ORs the page's RW and US bits into its PDE, so the directory stays at least as permissive as the pages below it.phys | flags. Mapping over a present PTE replaces it.enable, translate prints paging off -> physical VIRT.CR3 + dir*4. If the PDE is present, also print the PTE read from table + index*4. The access is allowed only if both levels allow it: a user access needs US at both levels, and a user write needs RW at both levels. Kernel accesses are never blocked by RW (CR0.WP is off).not present, user access to kernel page or write to read-only page.cLoading…
identity 0xSTART-0xLAST: N pages, K new page table(s).unmap 0xV: PTE[t] cleared, invlpg 0xPAGE or unmap 0xV: not mapped.mov cr3, 0x00001000 and mov cr0, cr0 | 0x80000000 (PG).kernel/user, ro/rw, plus accessed/dirty) and page count. It lists that PDE's PTEs only if the table has 8 or fewer pages.map 0xV: addresses must be 4 KiB aligned, map 0xV: bad flags F, or identity: range must be 4 KiB aligned and non-empty.Input:
cLoading…
Output:
cLoading…
dir = v >> 22, table = (v >> 12) & 0x3ff and offset = v & 0xfff, and build the physical address as (pte & ~0xfff) | offset.Hidden tests cover not-present faults at both levels, faults after unmap, user access to kernel pages, user writes to read-only pages, kernel writes to read-only pages, higher-half addresses, an identity map that crosses a 4 MiB boundary, replaced mappings, dirty and accessed bits in dump, and rejected input.