|
miniOS
x86_64 hobby kernel with SMP, VFS, and POSIX process model
|
#include <miniOS/types.h>

Go to the source code of this file.
Macros | |
| #define | PAGE_PRESENT (1ULL << 0) |
| #define | PAGE_WRITE (1ULL << 1) |
| #define | PAGE_USER (1ULL << 2) |
| #define | PAGE_COW (1ULL << 9) /* software: copy-on-write pending */ |
| #define | KERNEL_VMA 0xFFFF800000000000ULL |
| #define | HEAP_START 0xFFFF820000000000ULL |
| #define | HEAP_MAX (HEAP_START + 4ULL * 1024 * 1024) |
| #define | DMA_BUFFER_VA 0xFFFF830000000000ULL |
| #define | GOP_FB_VA 0xFFFF840000000000ULL |
| #define | VGA_BUFFER_VA (KERNEL_VMA + 0xB8000ULL) |
| #define | PAGE_SIZE 4096ULL |
| #define | PML4_SHIFT 39 /* bits [47:39] index PML4 */ |
| #define | PDPT_SHIFT 30 /* bits [38:30] index PDPT */ |
| #define | PD_SHIFT 21 /* bits [29:21] index PD */ |
| #define | PT_SHIFT 12 /* bits [20:12] index PT */ |
| #define | PT_INDEX_MASK 0x1FFULL /* 9-bit mask for any level index */ |
| #define | PTE_ADDR_MASK 0x000FFFFFFFFFF000ULL |
Functions | |
| int | vmm_map_page (uint64_t virt, uint64_t phys, uint64_t flags) |
| void | vmm_unmap_page (uint64_t virt) |
| uint64_t | vmm_virt_to_phys (uint64_t virt) |
| uint64_t | vmm_virt_to_pte (uint64_t virt) |
| int | vmm_resolve_user_fault (uint64_t cr2, uint64_t error_code) |
| uint64_t | vmm_new_address_space (void) |
| void | vmm_free_address_space (uint64_t pml4_phys) |
| int | vmm_map_page_in (uint64_t pml4_phys, uint64_t virt, uint64_t phys, uint64_t flags) |
| void | vmm_unmap_page_in (uint64_t pml4_phys, uint64_t virt) |
| uint64_t | vmm_virt_to_phys_in (uint64_t pml4_phys, uint64_t virt) |
| uint64_t | vmm_virt_to_pte_in (uint64_t pml4_phys, uint64_t virt) |
| #define DMA_BUFFER_VA 0xFFFF830000000000ULL |
| #define GOP_FB_VA 0xFFFF840000000000ULL |
| #define HEAP_MAX (HEAP_START + 4ULL * 1024 * 1024) |
| #define HEAP_START 0xFFFF820000000000ULL |
| #define KERNEL_VMA 0xFFFF800000000000ULL |
| #define PAGE_COW (1ULL << 9) /* software: copy-on-write pending */ |
| #define PAGE_PRESENT (1ULL << 0) |
| #define PAGE_SIZE 4096ULL |
| #define PAGE_USER (1ULL << 2) |
| #define PAGE_WRITE (1ULL << 1) |
| #define PD_SHIFT 21 /* bits [29:21] index PD */ |
| #define PDPT_SHIFT 30 /* bits [38:30] index PDPT */ |
| #define PML4_SHIFT 39 /* bits [47:39] index PML4 */ |
| #define PT_INDEX_MASK 0x1FFULL /* 9-bit mask for any level index */ |
| #define PT_SHIFT 12 /* bits [20:12] index PT */ |
| #define PTE_ADDR_MASK 0x000FFFFFFFFFF000ULL |
| #define VGA_BUFFER_VA (KERNEL_VMA + 0xB8000ULL) |
| void vmm_free_address_space | ( | uint64_t | pml4_phys | ) |
vmm_free_address_space() - Free a process's private page tables. @pml4_phys: PML4 physical address from vmm_new_address_space().
Walks only the user low half, pmm_unref_frame()'ing every mapped leaf page (frees private pages, decrements still-shared CoW pages) and freeing every intermediate PDPT/PD/PT frame plus the PML4 itself. Never touches the shared kernel high half.
vmm_free_address_space() - Tear down a process's private page tables. @pml4_phys: PML4 physical address returned by vmm_new_address_space().
Walks only the user low half (PML4 indices 0..255 — the per-process range; indices 256..511 are the shared kernel half and are never touched or freed here). For every present leaf PTE, calls pmm_unref_frame() — private pages (refcount 1) are freed; CoW-shared pages still held by another process are just decremented. Also frees the intermediate PDPT/PD/PT frames (always private to this address space; never shared across processes), then the PML4 frame itself.

vmm_map_page() - Map a 4 KiB virtual page to a physical frame. @virt: Virtual address to map. Must be >= KERNEL_VMA (0xFFFF800000000000) for kernel mappings, or a valid user VA for user mappings. @phys: Physical frame address (must be 4 KiB-aligned). @flags: PTE flags: PAGE_PRESENT (bit 0), PAGE_WRITE (bit 1), PAGE_USER (bit 2). Combine as needed.
Walks the 4-level page table (PML4 -> PDPT -> PD -> PT), allocating missing intermediate levels via pmm_alloc_frame. All intermediate-level entries are created with PAGE_PRESENT|PAGE_WRITE|PAGE_USER to allow both kernel and user mappings to be installed. Flushes TLB via invlpg.
Context: ISR-safe — pmm_alloc_frame uses spinlock_irqsave.
vmm_map_page() - Install a PTE mapping virt -> phys in the live address space. @virt: Virtual address (>= KERNEL_VMA for kernel; user VA for ring-3 pages). @phys: Physical frame address (4 KiB-aligned). @flags: PTE attribute bits (PAGE_PRESENT, PAGE_WRITE, PAGE_USER).
Thin wrapper over vmm_map_page_in() using the live CR3.


vmm_map_page_in() - Install a PTE mapping virt -> phys in an explicit PML4. @pml4_phys: Physical address of the target PML4 (not necessarily live in CR3).
Same walk as vmm_map_page(), but against @pml4_phys via the KERNEL_VMA direct-map alias rather than the live CR3 — lets a process's page tables be built (fork) or inspected (ptrace PEEK/POKE, once implemented) without ever loading that process's CR3. Still issues invlpg/shootdown for @virt; those are only meaningful if @pml4_phys happens to be live somewhere, but are cheap no-ops otherwise.


| uint64_t vmm_new_address_space | ( | void | ) |
vmm_new_address_space() - Allocate a fresh per-process PML4.
Higher half (kernel/heap/DMA/GOP/MMIO, PML4[256..511]) is copied by value from the live PML4 so kernel mappings are identical in every process. Low half (PML4[0..255], user code/stack/mmap) starts zeroed.
vmm_new_address_space() - Allocate a fresh per-process PML4.
Copies PML4 entries 256..511 (the entire canonical higher half) by value from the currently-live PML4 into the new one, so every kernel mapping (kernel image, heap, DMA bounce buffer, GOP framebuffer, LAPIC/IOAPIC MMIO) is identically visible regardless of which process's PML4 is loaded in CR3 — all process PML4s share the same underlying PDPT/PD/PT chain for these entries, so kernel-side growth (e.g. heap.c mapping a new page) automatically stays visible to every process without re-syncing.
Invariant this relies on: every kernel top-level region (indices 256, 260, 262, 264 — KERNEL_VMA/VGA, HEAP_START, DMA_BUFFER_VA, GOP_FB_VA) must already have its first page mapped by the time the first process is created, so its PML4 entry exists to copy. True today — all of them are touched during early boot, before the first sched_create_user_task() call.
Entries 0..255 (user low half) are left zero — the caller builds those fresh via vmm_map_page_in().


vmm_resolve_user_fault() - Demand-page handler for user #PF. @cr2: Faulting virtual address (from CR2 register). @error_code: x86 page-fault error code pushed by CPU.
Called from the #PF exception handler when bit 2 (U/S) of error_code is set (user-mode fault). Checks whether the fault is a not-present fault (bit 0 = 0) in a lazy mmap region. If so, allocates a physical frame, zeros it, and installs the PTE with permissions derived from region.prot. Handles SMP double-fault races by checking vmm_virt_to_phys() first.


| void vmm_unmap_page | ( | uint64_t | virt | ) |
vmm_unmap_page() - Remove a page mapping. @virt: Virtual address of the page to unmap (4 KiB-aligned).
Clears the leaf PTE to 0 (not-present) and flushes the TLB via invlpg. Does NOT free the physical frame — that is the caller's responsibility. Does NOT reclaim depopulated intermediate page table levels.
vmm_unmap_page() - Clear a leaf PTE and flush TLB. @virt: Virtual address of the page to unmap.
Walks all four page table levels. Writes 0 to the leaf PT entry (clears present bit). Issues invlpg on @virt. Does not free intermediate levels or the physical frame.


vmm_unmap_page_in() - Clear a leaf PTE in an explicit PML4 and flush TLB.


vmm_virt_to_phys() - Translate a virtual address to its physical frame address. @virt: Virtual address to translate.
Walks the 4-level page table via cr3 + KERNEL_VMA. Stops and returns 0 if any level's entry is not-present (bit 0 = 0).
vmm_virt_to_phys() - Walk page tables to resolve a virtual address. @virt: Virtual address to translate.
Thin wrapper over vmm_virt_to_phys_in() using the live CR3.


vmm_virt_to_phys_in() - Walk an explicit PML4 to resolve a virtual address.


