/* * Copyright (c) 2026, Chloe M. * Provided under the BSD-3 clause. * * Description: Virtual memory management * Author: Chloe M. */ #include #include #include #include #include #include #include #include #define DTRACE(Fmt, ...) \ TRACE("[ VM ]: " Fmt, ##__VA_ARGS__) /* * Number of pages we start with that can be valloc'd * total. */ #define INITIAL_VALLOC_PAGES 128 /* Use to safely convert page size */ #define PAGE_SIZE(PS) \ ((PS) >= NELEM(PageSizeTable)) \ ? PageSizeTable[PAGESIZE_4K] \ : PageSizeTable[(PS)] /* Page size table */ static USIZE PageSizeTable[] = { [PAGESIZE_4K] = 0x1000 }; /* Kernel VAS */ static MMU_VAS KVas; /* Bump pointer and vad tree */ static UPTR VallocBump = VALLOC_BASE; static MM_VAD_TREE VadTree; PT_STATUS MmAllocatePages(USIZE Count, MM_RANGE *Result) { MM_VAD *Vad; if (Count == 0 || Result == NULL) { return STATUS_INVALID_PARAM; } Vad = MmVadTreeAllocate(&VadTree, Count); if (Vad == NULL) { return STATUS_NO_MEMORY; } *Result = Vad->Range; return STATUS_SUCCESS; } PT_STATUS MmUnmapRange(MMU_VAS *Vas, const MM_RANGE *Range, MMU_PAGESIZE PageSize) { PT_STATUS Status; UPTR Off = 0; UPTR VirtualBase; USIZE Gran; if (Vas == NULL || Range == NULL) { return STATUS_INVALID_PARAM; } /* Align down the base addresses */ VirtualBase = ALIGN_DOWN(Range->VirtualBase, PAGESIZE); Gran = PAGE_SIZE(PageSize); for (Off = 0; Off < Range->Length; Off += Gran) { Status = HalMmuUnmapPage( Vas, VirtualBase + Off, PageSize ); if (PT_ERROR(Status)) { return Status; } } return STATUS_SUCCESS; } PT_STATUS MmMapRange(MMU_VAS *Vas, const MM_RANGE *Range, MM_PROT Prot, MMU_PAGESIZE PageSize) { PT_STATUS Status; UPTR Off = 0; UPTR VirtualBase; UPTR PhysicalBase; USIZE Gran; if (Vas == NULL || Range == NULL) { return STATUS_INVALID_PARAM; } /* Align down the base addresses */ VirtualBase = ALIGN_DOWN(Range->VirtualBase, PAGESIZE); PhysicalBase = ALIGN_DOWN(Range->PhysicalBase, PAGESIZE); Gran = PAGE_SIZE(PageSize); for (Off = 0; Off < Range->Length; Off += Gran) { Status = HalMmuMapPage( Vas, VirtualBase + Off, PhysicalBase + Off, Prot, PageSize ); if (PT_ERROR(Status)) { MmUnmapRange(Vas, Range, PageSize); return Status; } } return STATUS_SUCCESS; } VOID MmVmInit(VOID) { PT_STATUS Status; MMU_VAS VasCurrent; UPTR VirtualStart; UPTR VirtualEnd, Off; UPTR PhysicalBase; USIZE NumPages; MM_PFN Pfn; MM_RANGE *CurrentRange; MM_VAD *CurrentVad; HalMmuReadVas(&VasCurrent); MmVadTreeInit(&VadTree); Status = HalMmuForkVas(&VasCurrent, &KVas); if (PT_ERROR(Status)) { KeBugCheck( BUGCHECK_OOM, "failed to allocate kernel vas\n" ); } HalMmuWriteVas(&KVas); VirtualStart = VallocBump; VallocBump += INITIAL_VALLOC_PAGES * PAGESIZE; VirtualEnd = VallocBump; NumPages = (VirtualEnd - VirtualStart) / PAGESIZE; for (Off = 0; Off < NumPages * PAGESIZE; Off += PAGESIZE) { Pfn = MmRequestFrame(); if (Pfn == 0) { KeBugCheck(BUGCHECK_OOM, "out of memory\n"); } PhysicalBase = PFN_TO_ADDRESS(Pfn); Status = HalMmuMapPage( &KVas, VirtualStart + Off, PhysicalBase, PAGE_READ| PAGE_WRITE, PAGESIZE_4K ); if (PT_ERROR(Status)) { KeBugCheck(BUGCHECK_OOM, "failed to map valloc pages\n"); } CurrentVad = (MM_VAD *)(VirtualStart + Off); CurrentRange = &CurrentVad->Range; CurrentRange->VirtualBase = VirtualStart + Off; CurrentRange->PhysicalBase = PhysicalBase; CurrentRange->Length = PAGESIZE; Status = MmVadTreeInsert( &VadTree, CurrentVad ); if (PT_ERROR(Status)) { KeBugCheck(BUGCHECK_MISC, "failed to initialize vad tree\n"); } } DTRACE("initialized %d valloc pages\n", NumPages); }