Skip to main content
The Virtual Memory Manager (VMM) provides dynamic 4 KiB page mapping on top of the x86_64 four-level paging hierarchy (PML4 → PDPT → PD → PT). You use the VMM to establish, modify, and tear down arbitrary virtual-to-physical address mappings at any time after vmm_init returns. The VMM allocates intermediate page-table structures (PDPT, PD, PT nodes) from the PMM on demand, so the PMM must be initialized before you call vmm_init.

Page Flag Constants

Each mapping carries a 64-bit flags word whose bits directly correspond to x86_64 page-table entry attributes. The following constants from mvh/memory.h cover every attribute the VMM exposes. Combine them with the bitwise-OR operator when calling vmm_map_page.
VMM_NO_EXECUTE requires the CPU’s NXE bit to be set in IA32_EFER. MVH Kernel sets this during early CPU initialization, so VMM_NO_EXECUTE is always safe to use on supported hardware.
VMM_USER is defined and accepted by vmm_map_page, but user/kernel privilege separation is not yet enforced in the current release — the kernel operates in a single privilege mode. The flag is reserved for forward compatibility with a future user-space implementation.

Security Conventions

MVH Kernel enforces the following mapping policies for its own address space:
  • Kernel text (.text) — mapped VMM_PRESENT | VMM_GLOBAL (read-only, executable). The VMM_WRITABLE flag is intentionally absent to prevent accidental or malicious code modification at runtime.
  • Kernel data/BSS (.data, .bss) — mapped VMM_PRESENT | VMM_WRITABLE | VMM_GLOBAL | VMM_NO_EXECUTE. Marking these pages non-executable closes the most common code-injection vector.
  • MMIO regions — mapped with VMM_CACHE_DISABLE (and optionally VMM_WRITE_THROUGH) to prevent the CPU from caching device-register reads and writes.
When you add new mappings, follow the same principle: grant only the permissions that the mapping actually requires.

Functions

vmm_init

Initializes the Virtual Memory Manager, sets up the top-level PML4 page table, and installs the identity map for the first 1024 MiB with the kernel’s own text/data permissions. You must call this once, after pmm_init, and before any call to vmm_map_page.
int
0 on success. A non-zero value indicates a fatal initialization failure (for example, the PMM could not allocate a root PML4 page); the kernel should treat this as unrecoverable.

vmm_map_page

Maps a single 4 KiB physical page to a virtual address with the specified flags. Both addresses are automatically aligned down to the nearest 4 KiB boundary. If any intermediate page-table node (PDPT, PD, or PT) does not yet exist, the VMM allocates it from the PMM. Mapping an address that is already mapped overwrites the existing entry and invalidates the corresponding TLB entry via invlpg.
uintptr_t
required
The virtual address to create the mapping at. Aligned down to the nearest page boundary (4096-byte multiple) before use.
uintptr_t
required
The physical address of the 4 KiB page to map. Must lie within the identity-mapped first 1024 MiB. Aligned down to the nearest page boundary before use.
uint64_t
required
A bitwise OR of one or more VMM_* flag constants. You must always include VMM_PRESENT; a mapping without it is immediately invalid. Example: VMM_PRESENT | VMM_WRITABLE | VMM_NO_EXECUTE for a writable, non-executable data page.
int
0 on success. Non-zero if the mapping could not be created — for example, because the PMM failed to allocate a page-table node.
Always include VMM_PRESENT in your flags. Omitting it creates a page-table entry that the CPU treats as not-present, which means the virtual address will fault on first access even though vmm_map_page returned 0.

vmm_unmap_page

Removes the mapping for the 4 KiB page containing virtual_address and issues an invlpg to flush the corresponding TLB entry on the current CPU. The underlying physical page is not freed — you are responsible for returning it to the PMM with pmm_free_pages if you no longer need it.
uintptr_t
required
Any virtual address within the page to unmap. Aligned down to the nearest page boundary before use. Unmapping an address that is not currently mapped is a no-op and returns 0.
int
0 on success. Non-zero if an internal error prevents the unmap (for example, a corrupt page-table walk).
vmm_unmap_page only operates on the current CPU’s TLB. In a future multiprocessor build, you will need to issue an inter-processor interrupt (IPI) to flush remote TLBs when unmapping shared mappings.

vmm_query_page

Walks the page-table hierarchy to look up the physical address and attribute flags for the virtual page containing virtual_address. Use this function to inspect an existing mapping without modifying it.
uintptr_t
required
Any virtual address within the page to query. Aligned down to the nearest page boundary before the walk.
uintptr_t *
required
Pointer to a uintptr_t that receives the physical base address of the mapped page on success. Must not be NULL. Unmodified if the function returns non-zero.
uint64_t *
required
Pointer to a uint64_t that receives the page-table entry’s flag bits on success. Must not be NULL. Unmodified if the function returns non-zero.
int
0 if the page is mapped and *physical_address and *flags have been written. Non-zero if the virtual address is not mapped (any level of the walk yields a not-present entry).

vmm_mapped_pages

Returns the total number of virtual pages that are currently mapped (i.e., have a present entry in the page table). This counter is maintained incrementally by vmm_map_page and vmm_unmap_page and does not require a page-table walk.
uint64_t
The number of currently mapped 4 KiB pages.

vmm_self_test

Runs the VMM’s built-in self-test suite. The test maps, queries, and unmaps pages in various patterns to verify that the page-table walk, invlpg issuance, and counter maintenance are all functioning correctly. All temporary mappings are removed before the function returns.
int
0 if all self-test cases pass. Non-zero indicates the index of the first failing sub-test; check the kernel serial log for details.
Call vmm_self_test immediately after vmm_init in debug builds. It is safe to call against a live VMM because the test fully cleans up after itself.

Usage Example

The following example maps a single physical page at a chosen virtual address with read/write, no-execute permissions, queries it to verify, then unmaps it.