From 7bdd0dfe2c5d132e4521b96cc88be7940a65bbbe Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Thu, 26 Feb 2026 20:10:03 +0100 Subject: [PATCH] Implement basic non-paged pool allocator --- xtoskrnl/includes/mm/alloc.hh | 3 + xtoskrnl/mm/alloc.cc | 180 +++++++++++++++++++++++++++++++++- xtoskrnl/mm/data.cc | 3 + 3 files changed, 182 insertions(+), 4 deletions(-) diff --git a/xtoskrnl/includes/mm/alloc.hh b/xtoskrnl/includes/mm/alloc.hh index a2f133a..2f3dc5b 100644 --- a/xtoskrnl/includes/mm/alloc.hh +++ b/xtoskrnl/includes/mm/alloc.hh @@ -17,6 +17,9 @@ namespace MM { class Allocator { + private: + STATIC LIST_ENTRY NonPagedPoolFreeList[MM_MAX_FREE_PAGE_LIST_HEADS]; + public: STATIC XTAPI XTSTATUS AllocatePages(IN MMPOOL_TYPE PoolType, IN SIZE_T Bytes, diff --git a/xtoskrnl/mm/alloc.cc b/xtoskrnl/mm/alloc.cc index 8bcd572..e60a18c 100644 --- a/xtoskrnl/mm/alloc.cc +++ b/xtoskrnl/mm/alloc.cc @@ -27,10 +27,155 @@ XTSTATUS MM::Allocator::AllocateNonPagedPoolPages(IN PFN_COUNT Pages, OUT PVOID *Memory) { - UNIMPLEMENTED; + PLIST_ENTRY Entry, LastHead, ListHead; + PMMPTE PointerPte, ValidPte; + PMMFREE_POOL_ENTRY FreePage; + PFN_NUMBER PageFrameNumber; + PVOID BaseAddress; + ULONG Index; + PMMPFN Pfn; - /* Return not implemented status code */ - return STATUS_NOT_IMPLEMENTED; + /* Calculate the free list index based on the requested page count, capped at the maximum list head index */ + Index = MIN(Pages, MM_MAX_FREE_PAGE_LIST_HEADS) - 1; + + /* Set the starting list head and the boundary for the search loop */ + ListHead = &NonPagedPoolFreeList[Index]; + LastHead = &NonPagedPoolFreeList[MM_MAX_FREE_PAGE_LIST_HEADS]; + + /* Start a guarded code block */ + { + /* Acquire the Non-Paged pool lock and raise runlevel to DISPATCH_LEVEL */ + KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL); + KE::QueuedSpinLockGuard NonPagedPoolSpinLock(NonPagedPoolLock); + + /* Iterate through the free lists */ + do + { + /* Iterate through the free entries in the current list */ + Entry = ListHead->Flink; + while(Entry != ListHead) + { + /* Get the free pool entry structure from the list entry */ + FreePage = CONTAIN_RECORD(Entry, MMFREE_POOL_ENTRY, List); + + /* Check if this block is large enough to satisfy the request */ + if(FreePage->Size >= Pages) + { + /* Adjust the size of the free block to account for the allocated pages */ + FreePage->Size -= Pages; + + /* Calculate the base address of the allocated block */ + BaseAddress = (PVOID)((ULONG_PTR)FreePage + (FreePage->Size << MM_PAGE_SHIFT)); + + /* Remove the entry from the free list */ + RTL::LinkedList::RemoveEntryList(&FreePage->List); + + /* Check if there is remaining space in the entry */ + if(FreePage->Size != 0) + { + /* Calculate the new list index for the remaining fragment */ + Index = MIN(FreePage->Size, MM_MAX_FREE_PAGE_LIST_HEADS) - 1; + + /* Insert the entry into the free list */ + RTL::LinkedList::InsertTailList(&NonPagedPoolFreeList[Index], &FreePage->List); + } + + /* Get the Page Table Entry (PTE) for the allocated address */ + PointerPte = MM::Paging::GetPteAddress(BaseAddress); + + /* Get the Page Frame Number (PFN) database entry for the corresponding physical page */ + Pfn = MM::Pfn::GetPfnEntry(MM::Paging::GetPageFrameNumber(PointerPte)); + + /* Denote allocation boundaries */ + Pfn->u3.e1.ReadInProgress = 1; + + /* Check if multiple pages were requested */ + if(Pages != 1) + { + /* Advance to the PTE of the last page in the allocation */ + PointerPte = MM::Paging::AdvancePte(PointerPte, Pages - 1); + + /* Get the PFN entry for the last page */ + Pfn = MM::Pfn::GetPfnEntry(MM::Paging::GetPageFrameNumber(PointerPte)); + } + + /* Denote allocation boundaries */ + Pfn->u3.e1.WriteInProgress = 1; + + /* Set the allocated memory address and return success */ + *Memory = BaseAddress; + return STATUS_SUCCESS; + } + + /* Move to the next entry in the free list */ + Entry = FreePage->List.Flink; + } + } + while(++ListHead < LastHead); + } + + /* No suitable free block found; try to expand the pool by reserving system PTEs */ + PointerPte = MM::Pte::ReserveSystemPtes(Pages, NonPagedPoolExpansion); + if(PointerPte == NULLPTR) + { + /* PTE reservation failed, return insufficient resources */ + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Acquire the Non-Paged pool lock and raise runlevel to DISPATCH_LEVEL */ + KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL); + KE::QueuedSpinLockGuard NonPagedPoolSpinLock(NonPagedPoolLock); + + /* Acquire the PFN database lock */ + KE::QueuedSpinLockGuard PfnSpinLock(PfnLock); + + /* Check if there are enough available physical pages to back the allocation */ + if(Pages >= MM::Pfn::GetAvailablePages()) + { + /* Not enough physical pages, release the reserved system PTEs */ + MM::Pte::ReleaseSystemPtes(PointerPte, Pages, NonPagedPoolExpansion); + + /* Return failure due to insufficient resources */ + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Get a template valid PTE and loop through the allocation to map physical pages */ + ValidPte = MM::Pte::GetValidPte(); + do + { + /* Allocate a physical page */ + PageFrameNumber = MM::Pfn::AllocatePhysicalPage(MM::Colors::GetNextColor()); + + /* Initialize the PFN entry for the allocated physical page */ + Pfn = MM::Pfn::GetPfnEntry(PageFrameNumber); + Pfn->PteAddress = PointerPte; + Pfn->u2.ShareCount = 1; + Pfn->u3.e1.PageLocation = ActiveAndValid; + Pfn->u3.e2.ReferenceCount = 1; + Pfn->u4.VerifierAllocation = 0; + + /* Build a valid PTE pointing to the allocated page frame */ + MM::Paging::SetPte(ValidPte, PageFrameNumber, 0); + + /* Write the valid PTE into the system PTE range */ + *(MM::Paging::GetNextPte(PointerPte)) = *ValidPte; + } + while(--Pages > 0); + + /* Dnote allocation boundaries */ + Pfn->u3.e1.WriteInProgress = 1; + + /* Get the PFN entry for the first page of the allocation */ + Pfn = MM::Pfn::GetPfnEntry(MM::Paging::GetPageFrameNumber(PointerPte)); + + /* Denote allocation boundaries */ + Pfn->u3.e1.ReadInProgress = 1; + + /* Convert the PTE address to the virtual address and store in the buffer */ + *Memory = MM::Paging::GetPteVirtualAddress(PointerPte); + + /* Return success */ + return STATUS_SUCCESS; } /** @@ -120,10 +265,37 @@ XTAPI VOID MM::Allocator::InitializeNonPagedPool(VOID) { - UNIMPLEMENTED; + PMMMEMORY_LAYOUT MemoryLayout; + PMMFREE_POOL_ENTRY FreeEntry; + ULONG Index; + + /* Retrieve memory layout */ + MemoryLayout = MM::Manager::GetMemoryLayout(); /* Map PTEs for the non-paged pool */ MapNonPagedPool(); + + /* Iterate over the free page list heads */ + for(Index = 0; Index < MM_MAX_FREE_PAGE_LIST_HEADS; Index++) + { + /* Initialize a free page list head */ + RTL::LinkedList::InitializeListHead(&NonPagedPoolFreeList[Index]); + } + + /* Take the first free page from the pool */ + FreeEntry = (PMMFREE_POOL_ENTRY)MemoryLayout->NonPagedPoolStart; + + /* Take number of pages in the pool */ + Index = (ULONG)(MemoryLayout->NonPagedPoolSize - 1); + if(Index >= MM_MAX_FREE_PAGE_LIST_HEADS) + { + /* Number of pages exceeds the number of free page list heads */ + Index = MM_MAX_FREE_PAGE_LIST_HEADS - 1; + } + + /* Insert the first free page into the free page list and set its size */ + RTL::LinkedList::InsertHeadList(&NonPagedPoolFreeList[Index], &FreeEntry->List); + FreeEntry->Size = MemoryLayout->NonPagedPoolSize; } /** diff --git a/xtoskrnl/mm/data.cc b/xtoskrnl/mm/data.cc index 6586526..91c32e8 100644 --- a/xtoskrnl/mm/data.cc +++ b/xtoskrnl/mm/data.cc @@ -9,6 +9,9 @@ #include +/* Array of non-paged pool free list heads */ +LIST_ENTRY MM::Allocator::NonPagedPoolFreeList[MM_MAX_FREE_PAGE_LIST_HEADS]; + /* Array of free page lists segregated by cache color */ PMMCOLOR_TABLES MM::Colors::FreePages[FreePageList + 1];