Repository navigation
Memory
Two address-space domains: the kernel's, and each process's. They
share the top-level page table entry PML4[0] (kernel), while
PML4[1..255] are per-process.
USER_VA_BASE + 0x00000000 ELF image load_base .. +load_size
then heap / SYS_MAP allocations
USER_VA_BASE + 0x00100000 USER_STACK_TOP (ring-3 selftest)
USER_VA_BASE + 0x01000000 USER_STACK_TOP_INIT (init and children)
USER_VA_BASE + 0x02000000 INITRD_VA (read-only archive)
USER_VA_BASE = 0x0000008000000000 — 512 GiB, so user mappings
live in PML4[1]. USER_VA_TOP = 0x0000800000000000 — the
canonical lower half.
pmm_alloc_page() returns a 4 KiB physical page, or 0 on
exhaustion. pmm_free_page() returns it. The allocator does not
track page ownership; whoever allocated a page is responsible for
freeing it.
pmm_free_page_count() is used as a leak check in the boot tests:
the count is captured before and after the userspace phase and the
two must match.
vmm_create() -> fresh address space (refcount 1)
vmm_switch(space) -> load CR3
vmm_map(space, va, pa, flags)
vmm_unmap(space, va)
vmm_query(space, va, &pa, &flags)
vmm_space_ref(space)
vmm_space_unref(space)
Flags:
VM_PRESENT (1 << 0) must be set
VM_WRITE (1 << 1)
VM_USER (1 << 2) required for user mappings
VM_NOCACHE (1 << 3)
Address spaces are refcounted. A process owns one reference; each
thread given the space holds its own. When the refcount drops to
zero, the whole PML4[1..511] subtree is walked and every leaf page
and intermediate table is freed.
Kernel code never dereferences user VAs directly. Two options:
-
copy_to_user/copy_from_user/strncpy_from_user— with a pre-check (user_range_ok) plus extable-based fault recovery. A page fault at the copy returns-EFAULTinstead of panicking. - Bounce buffers — for short syscall arguments, copy into a kernel buffer first, then work on the kernel copy.
user_range_ok(space, va, n) verifies that [va, va+n) lies
entirely below USER_VA_TOP and that every page is mapped with
VM_USER. It does not touch memory.
KERNEL_HEAP_BASE = KERNEL_VA_BASE + 0x00100000
KERNEL_HEAP_MAX = KERNEL_VA_BASE + 0x10000000
Grows by mapping pages into the kernel space as needed. Pages are never returned to the PMM — the free list keeps the VA mapped and usable for later allocations.
heap_reserve(bytes) grows the heap until at least bytes of
payload is available in the free list, without allocating anything
yet. The kernel calls heap_reserve(HEAP_BOOT_RESERVE) at boot
(4 MiB + 256 KiB) so that the largest single allocation the kernel
can need in normal operation — the SPAWN_ELF bounce buffer — never
triggers a heap growth in the middle of userspace execution. That
would show up as pmm-free drift and fail the leak check.
The libc malloc maps one 4 MiB arena with SYS_MAP on first use
and manages it as a boundary-tag heap. free coalesces but does
not return pages to the kernel. realloc grows in place when the
next block is free, otherwise allocates and copies.