diff --git a/xtoskrnl/includes/mm/pfn.hh b/xtoskrnl/includes/mm/pfn.hh index a0b9c34..072184e 100644 --- a/xtoskrnl/includes/mm/pfn.hh +++ b/xtoskrnl/includes/mm/pfn.hh @@ -39,6 +39,11 @@ namespace MM STATIC XTAPI PFN_NUMBER AllocateBootstrapPages(IN PFN_NUMBER NumberOfPages); STATIC XTAPI PFN_NUMBER AllocatePhysicalPage(IN ULONG Color); STATIC XTAPI VOID ComputePfnDatabaseSize(VOID); + STATIC XTAPI VOID DecrementReferenceCount(IN PMMPFN Pfn1, + IN PFN_NUMBER PageFrameIndex); + STATIC XTAPI VOID DecrementShareCount(IN PMMPFN Pfn1, + IN PFN_NUMBER PageFrameIndex); + STATIC XTAPI VOID FreePhysicalPage(IN PMMPTE PointerPte); STATIC XTAPI ULONG_PTR GetHighestPhysicalPage(VOID); STATIC XTAPI ULONGLONG GetNumberOfPhysicalPages(VOID); STATIC XTAPI PFN_NUMBER GetPfnDatabaseSize(VOID); diff --git a/xtoskrnl/mm/kpool.cc b/xtoskrnl/mm/kpool.cc index e9d3afe..2d650e9 100644 --- a/xtoskrnl/mm/kpool.cc +++ b/xtoskrnl/mm/kpool.cc @@ -155,7 +155,35 @@ VOID MM::KernelPool::FreeKernelStack(IN PVOID Stack, IN ULONG StackSize) { - UNIMPLEMENTED; + PFN_COUNT StackPages; + PMMPTE PointerPte; + ULONG Index; + + /* Get the PTE for the top of the stack, including the guard page */ + MM::Paging::AdvancePte(MM::Paging::GetPteAddress(Stack), -1); + + /* Convert the stack size into a page count */ + StackPages = SIZE_TO_PAGES(StackSize); + + /* Acquire the PFN database lock */ + KE::QueuedSpinLockGuard SpinLock(SystemSpaceLock); + + /* Loop through each page of the stack that needs to be freed */ + for(Index = 0; Index < StackPages; Index++) + { + /* Ensure the PTE is valid */ + if(MM::Paging::PteValid(PointerPte)) + { + /* Free the physical page */ + MM::Pfn::FreePhysicalPage(PointerPte); + } + + /* Advance to the next PTE */ + PointerPte = MM::Paging::AdvancePte(PointerPte, -1); + } + + /* Release all system PTEs used by the stack, including the guard page */ + MM::Pte::ReleaseSystemPtes(PointerPte, StackPages + 1, SystemPteSpace); } /** diff --git a/xtoskrnl/mm/pfn.cc b/xtoskrnl/mm/pfn.cc index dfc77e7..9a7a652 100644 --- a/xtoskrnl/mm/pfn.cc +++ b/xtoskrnl/mm/pfn.cc @@ -129,6 +129,203 @@ MM::Pfn::DecrementAvailablePages(VOID) AvailablePages--; } +/** + * Decrements the reference count of a PFN entry. + * + * @param PageFrameNumber + * A pointer to the PFN database entry for the physical page. + * + * @param PageFrameIndex + * The page frame number of the physical page. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +MM::Pfn::DecrementReferenceCount(IN PMMPFN PageFrameNumber, + IN PFN_NUMBER PageFrameIndex) +{ + /* Decrement the PFN reference count */ + PageFrameNumber->u3.e2.ReferenceCount--; + + /* If other references exist, no further action is needed */ + if(PageFrameNumber->u3.e2.ReferenceCount) + { + /* No further action can be taken */ + return; + } + + /* If the reference count is zero, the share count should also be zero */ + if(PageFrameNumber->u2.ShareCount) + { + /* This indicates a bug; crash the system */ + KE::Crash::PanicEx(0x4E, + 0x07, + PageFrameIndex, + PageFrameNumber->u2.ShareCount, + 0); + } + + /* Check if the PTE is marked as being ready for removal */ + if(MM::Paging::GetPte(PageFrameNumber->PteAddress) & 0x1) + { + /* Check the page's cache attribute */ + if((PageFrameNumber->u3.e1.CacheAttribute != PfnCached) && + (PageFrameNumber->u3.e1.CacheAttribute != PfnNotMapped)) + { + UNIMPLEMENTED; + } + + /* The page is no longer needed, free it by linking it to the free list */ + LinkFreePage(PageFrameIndex); + + /* No further action is needed */ + return; + } + + /* The page is unreferenced and unmapped, so place it on the appropriate list */ + if(PageFrameNumber->u3.e1.Modified == 1) + { + /* Link dirty page to the modified list */ + LinkPage(&ModifiedPagesList, PageFrameIndex); + } + else + { + /* Check if the page has been marked for removal */ + if(PageFrameNumber->u3.e1.RemovalRequested == 1) + { + /* Update the page location and move it to the bad pages list */ + PageFrameNumber->u3.e1.PageLocation = StandbyPageList; + LinkPage(&BadPagesList, PageFrameIndex); + } + else + { + /* Link clean page to the standby list */ + LinkPage(&StandbyPagesList, PageFrameIndex) + } + } +} + +/** + * Decrements the share count of a PFN entry, which represents the number of PTEs mapping the page. + * + * @param PageFrameNumber + * A pointer to the PFN database entry for the physical page. + * + * @param PageFrameIndex + * The page frame number of the physical page. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +MM::Pfn::DecrementShareCount(IN PMMPFN PageFrameNumber, + IN PFN_NUMBER PageFrameIndex) +{ + /* Ensure the page is in a valid state */ + if((PageFrameNumber->u3.e1.PageLocation != ActiveAndValid) && + (PageFrameNumber->u3.e1.PageLocation != StandbyPageList)) + { + /* This indicates a bug; crash the system */ + KE::Crash::PanicEx(0x4E, + 0x99, + PageFrameIndex, + PageFrameNumber->u3.e1.PageLocation, + 0); + } + + /* Decrement the PFN share count */ + PageFrameNumber->u2.ShareCount--; + + /* Check if the share count has dropped to zero */ + if(!PageFrameNumber->u2.ShareCount) + { + /* Check if this is a prototype PTE */ + if(PageFrameNumber->u3.e1.PrototypePte) + { + /* Transition the PTE to invalid state */ + MM::Paging::TransitionPte(PageFrameNumber->PteAddress, + MM::Paging::GetPteSoftwareProtection(&PageFrameNumber->OriginalPte)); + } + + /* Mark the page as being in a transition state */ + PageFrameNumber->u3.e1.PageLocation = TransitionPage; + + /* Check if there are still outstanding references */ + if(PageFrameNumber->u3.e2.ReferenceCount == 1) + { + /* Check if the PTE is marked as being ready for removal */ + if(MM::Paging::GetPte(PageFrameNumber->PteAddress) & 0x1) + { + /* Reset the reference count */ + PageFrameNumber->u3.e2.ReferenceCount = 0; + + /* Check the page's cache attribute */ + if((PageFrameNumber->u3.e1.CacheAttribute != PfnCached) && + (PageFrameNumber->u3.e1.CacheAttribute != PfnNotMapped)) + { + UNIMPLEMENTED; + } + + /* Mark the page as active and valid again */ + PageFrameNumber->u3.e1.PageLocation = ActiveAndValid; + + /* The page is no longer needed, free it by linking it to the free list */ + MM::Pfn::LinkFreePage(PageFrameIndex); + } + else + { + /* The PTE can not be removed yet, decrement the reference count */ + DecrementReferenceCount(PageFrameNumber, PageFrameIndex); + } + } + else + { + /* There are still some outstanding references, decrement the reference count */ + PageFrameNumber->u3.e2.ReferenceCount--; + } + } +} + +/** + * Frees a physical page that is mapped by a given PTE. + * + * @param PointerPte + * A pointer to the Page Table Entry (PTE) that maps the physical page to be freed. + * + * @return This routine does not return a value. + * + * @since XT 1.0 + */ +XTAPI +VOID +MM::Pfn::FreePhysicalPage(IN PMMPTE PointerPte) +{ + PFN_COUNT PageFrameNumber, PageTableFrameNumber; + PMMPFN PageFrame, PageTableFrame; + + /* Get the page frame number from the PTE */ + PageFrameNumber = MM::Paging::GetPageFrameNumber(PointerPte); + PageFrame = MM::Pfn::GetPfnEntry(PageFrameNumber); + + /* Get the page table frame number corresponding to the PTE */ + PageTableFrameNumber = PageFrame->u4.PteFrame; + PageTableFrame = MM::Pfn::GetPfnEntry(PageTableFrameNumber); + + /* Decrement the share count of the page table */ + MM::Pfn::DecrementShareCount(PageTableFrame, PageTableFrameNumber); + + /* Mark the PTE as being ready for removal */ + MM::Paging::SetPte(PageFrame->PteAddress, MM::Paging::GetPte(PageFrame->PteAddress) | 1); + + /* Decrement the share count of the page */ + MM::Pfn::DecrementShareCount(PageFrame, PageFrameNumber); +} + /** * Retrieves the highest physical page number (PFN) detected in the system. *