1138 lines
35 KiB
C++
1138 lines
35 KiB
C++
/**
|
||
* PROJECT: ExectOS
|
||
* COPYRIGHT: See COPYING.md in the top level directory
|
||
* FILE: xtoskrnl/mm/pfn.cc
|
||
* DESCRIPTION: Physical Frame Number (PFN) support
|
||
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
|
||
* Rafal Kupiec <belliash@codingworkshop.eu.org>
|
||
*/
|
||
|
||
#include <xtos.hh>
|
||
|
||
|
||
/**
|
||
* Allocates a block of physical pages for early kernel initialization.
|
||
*
|
||
* @param NumberOfPages
|
||
* The number of physical pages to allocate.
|
||
*
|
||
* @return This routine returns the base page frame number (PFN) of the allocated block.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
PFN_NUMBER
|
||
MM::Pfn::AllocateBootstrapPages(IN PFN_NUMBER NumberOfPages)
|
||
{
|
||
PFN_NUMBER Pfn;
|
||
|
||
/* Check if the largest free memory block has enough pages */
|
||
if(NumberOfPages > FreeDescriptor->PageCount)
|
||
{
|
||
/* Not enough physical memory available, kernel panic */
|
||
DebugPrint(L"Insufficient physical pages! Install additional memory\n");
|
||
KE::Crash::Panic(0);
|
||
}
|
||
|
||
/* Allocate pages from the beginning of the free descriptor */
|
||
Pfn = FreeDescriptor->BasePage;
|
||
FreeDescriptor->BasePage += NumberOfPages;
|
||
FreeDescriptor->PageCount -= NumberOfPages;
|
||
|
||
/* Return the base page frame number of the allocated block */
|
||
return Pfn;
|
||
}
|
||
|
||
/**
|
||
* Allocates a physical page frame (PFN) from one of the system's free page lists.
|
||
*
|
||
* @param Color
|
||
* The preferred page color, used to optimize CPU cache alignment and reduce cache contention.
|
||
*
|
||
* @return This routine returns the Page Frame Number (PFN) of the allocated page.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
PFN_NUMBER
|
||
MM::Pfn::AllocatePhysicalPage(IN ULONG Color)
|
||
{
|
||
PFN_NUMBER PageNumber;
|
||
ULONG PagingColorsMask;
|
||
|
||
/* Check if any physical pages are available in the system */
|
||
if(!AvailablePages)
|
||
{
|
||
/* No physical pages are available in the system, return 0 */
|
||
return 0;
|
||
}
|
||
|
||
/* Retrieve the bitmask used for calculating a page's color */
|
||
PagingColorsMask = MM::Colors::GetPagingColorsMask();
|
||
|
||
/* Attempt to retrieve a page from the colored free page list */
|
||
PageNumber = MM::Colors::GetFreePages(FreePageList, Color)->Flink;
|
||
if(PageNumber == MAXULONG_PTR)
|
||
{
|
||
/* No page was found in the colored free page list, check the colored zero page list */
|
||
PageNumber = MM::Colors::GetFreePages(ZeroedPageList, Color)->Flink;
|
||
}
|
||
|
||
/* Attempt to retrieve a page from the colored zero list */
|
||
if(PageNumber == MAXULONG_PTR)
|
||
{
|
||
/* No page was found in the colored zero page list, check the global free page list */
|
||
PageNumber = FreePagesList.Flink;
|
||
}
|
||
|
||
/* Attempt to retrieve a page from the global free page list */
|
||
if(PageNumber == MAXULONG_PTR)
|
||
{
|
||
/* No page was found in the global free page list, check the global zero page list */
|
||
PageNumber = ZeroedPagesList.Flink;
|
||
}
|
||
|
||
/* Remove the page from its list and return its PFN */
|
||
return UnlinkFreePage(PageNumber, PageNumber & PagingColorsMask);
|
||
}
|
||
|
||
/**
|
||
* Calculates the total number of pages required for the PFN database and its associated color tables.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::ComputePfnDatabaseSize(VOID)
|
||
{
|
||
/* Calculate the total number of pages required for the PFN database */
|
||
PfnDatabaseSize = (HighestPhysicalPage + 1) * sizeof(MMPFN);
|
||
PfnDatabaseSize += (MM::Colors::GetPagingColors() * sizeof(MMCOLOR_TABLES) * 2);
|
||
PfnDatabaseSize = ROUND_UP(PfnDatabaseSize, MM_PAGE_SIZE);
|
||
PfnDatabaseSize >>= MM_PAGE_SHIFT;
|
||
}
|
||
|
||
/**
|
||
* Decrements the global count of available pages.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::DecrementAvailablePages(VOID)
|
||
{
|
||
/* Decrement the global count of available pages */
|
||
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.
|
||
*
|
||
* @param BeginStandbyList
|
||
* Determines whether the page should be added to the beginning of the standby list.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::DecrementReferenceCount(IN PMMPFN PageFrameNumber,
|
||
IN PFN_NUMBER PageFrameIndex,
|
||
IN BOOLEAN BeginStandbyList)
|
||
{
|
||
/* 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 */
|
||
if(BeginStandbyList)
|
||
{
|
||
/* Link clean page to the beginning of the standby list */
|
||
LinkStandbyPage(PageFrameIndex);
|
||
}
|
||
else
|
||
{
|
||
/* Link clean page to the end of 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.
|
||
*
|
||
* @param BeginStandbyList
|
||
* Determines whether the page should be added to the beginning of the standby list.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::DecrementShareCount(IN PMMPFN PageFrameNumber,
|
||
IN PFN_NUMBER PageFrameIndex,
|
||
IN BOOLEAN BeginStandbyList)
|
||
{
|
||
/* 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, BeginStandbyList);
|
||
}
|
||
}
|
||
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, FALSE);
|
||
|
||
/* 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, FALSE);
|
||
}
|
||
|
||
/**
|
||
* Retrieves the highest physical page number (PFN) detected in the system.
|
||
*
|
||
* @return This routine returns the highest physical page number.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
ULONG_PTR
|
||
MM::Pfn::GetHighestPhysicalPage(VOID)
|
||
{
|
||
/* Return the highest physical page number */
|
||
return HighestPhysicalPage;
|
||
}
|
||
|
||
/**
|
||
* Retrieves the total number of physical pages managed by the system.
|
||
*
|
||
* @return Returns the total count of physical memory pages.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
ULONGLONG
|
||
MM::Pfn::GetNumberOfPhysicalPages(VOID)
|
||
{
|
||
/* Return the number of physical pages */
|
||
return NumberOfPhysicalPages;
|
||
}
|
||
|
||
/**
|
||
* Gets the size of the PFN database and its associated structures, in pages.
|
||
*
|
||
* @return This routine returns the total number of pages required for the PFN database and its associated structures.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
PFN_NUMBER
|
||
MM::Pfn::GetPfnDatabaseSize(VOID)
|
||
{
|
||
/* Return the pre-calculated size of the PFN database in pages */
|
||
return PfnDatabaseSize;
|
||
}
|
||
|
||
/**
|
||
* Retrieves a pointer to the PFN database entry for a given physical page.
|
||
*
|
||
* @param Pfn
|
||
* The Page Frame Number (PFN) to look up.
|
||
*
|
||
* @return This routine returns a pointer to the MMPFN structure for the given PFN, or NULLPTR if the PFN is invalid.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
PMMPFN
|
||
MM::Pfn::GetPfnEntry(IN PFN_NUMBER Pfn)
|
||
{
|
||
PMMMEMORY_LAYOUT MemoryLayout;
|
||
|
||
/* Validate that the PFN is within the range of managed physical memory */
|
||
if(Pfn > HighestPhysicalPage)
|
||
{
|
||
/* The requested page number is outside the bounds, return NULLPTR */
|
||
return NULLPTR;
|
||
}
|
||
|
||
/* Get the memory layout */
|
||
MemoryLayout = MM::Manager::GetMemoryLayout();
|
||
|
||
/* Calculate the address of the PFN entry by indexing into the PFN database array and return it */
|
||
return &((PMMPFN)MemoryLayout->PfnDatabaseAddress)[Pfn];
|
||
}
|
||
|
||
/**
|
||
* Increments the global count of available pages.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::IncrementAvailablePages(VOID)
|
||
{
|
||
/* Increment the global count of available pages */
|
||
AvailablePages++;
|
||
}
|
||
|
||
/**
|
||
* Links a physical page to the appropriate free lists.
|
||
*
|
||
* @param PageFrameIndex
|
||
* The Page Frame Number (PFN) of the page to link.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::LinkFreePage(IN PFN_NUMBER PageFrameIndex)
|
||
{
|
||
ULONG Color;
|
||
PMMPFNLIST ListHead;
|
||
PFN_NUMBER LastPage;
|
||
PMMPFN ColoredPfn, PfnEntry;
|
||
PMMCOLOR_TABLES ColorTable;
|
||
PMMMEMORY_LAYOUT MemoryLayout;
|
||
|
||
/* Get the PFN database entry for the page */
|
||
PfnEntry = GetPfnEntry(PageFrameIndex);
|
||
|
||
/* Check if the page is part of a ROM image */
|
||
if(PfnEntry->u3.e1.Rom == 1)
|
||
{
|
||
/* Mark the page as inactive and clear its links */
|
||
PfnEntry->u1.Flink = 0;
|
||
PfnEntry->u3.e1.PageLocation = 0;
|
||
|
||
/* Do not free ROM pages */
|
||
return;
|
||
}
|
||
|
||
/* Check if the page is marked for removal */
|
||
if(PfnEntry->u3.e1.RemovalRequested == 1)
|
||
{
|
||
/* Update cache attribute to not mapped */
|
||
PfnEntry->u3.e1.CacheAttribute = PfnNotMapped;
|
||
|
||
/* Insert the page into the bad page list instead */
|
||
LinkPage(&BadPagesList, PageFrameIndex);
|
||
|
||
/* Do not add it to the free list */
|
||
return;
|
||
}
|
||
|
||
/* Insert the page into the global free list */
|
||
ListHead = &FreePagesList;
|
||
ListHead->Total++;
|
||
|
||
/* Get the current last page on the list */
|
||
LastPage = ListHead->Blink;
|
||
|
||
/* Check if the list is not empty */
|
||
if(LastPage != MAXULONG_PTR)
|
||
{
|
||
/* Link with the previous last page */
|
||
GetPfnEntry(LastPage)->u1.Flink = PageFrameIndex;
|
||
}
|
||
else
|
||
{
|
||
/* Put the page as the first entry */
|
||
ListHead->Flink = PageFrameIndex;
|
||
}
|
||
|
||
/* Set the page as the new tail of the list */
|
||
ListHead->Blink = PageFrameIndex;
|
||
PfnEntry->u1.Flink = MAXULONG_PTR;
|
||
PfnEntry->u2.Blink = LastPage;
|
||
PfnEntry->u3.e1.CacheAttribute = PfnNotMapped;
|
||
PfnEntry->u3.e1.PageLocation = FreePageList;
|
||
PfnEntry->u4.AweAllocation = 0;
|
||
PfnEntry->u4.InPageError = 0;
|
||
PfnEntry->u4.Priority = 3;
|
||
|
||
/* Insert the page into the colored free list */
|
||
Color = PageFrameIndex & MM::Colors::GetPagingColorsMask();
|
||
ColorTable = MM::Colors::GetFreePages(FreePageList, Color);
|
||
|
||
/* Get the memory layout */
|
||
MemoryLayout = MM::Manager::GetMemoryLayout();
|
||
|
||
/* Check if the colored list is empty */
|
||
if(ColorTable->Flink == MAXULONG_PTR)
|
||
{
|
||
/* Put the page as the first entry */
|
||
ColorTable->Flink = PageFrameIndex;
|
||
PfnEntry->u4.PteFrame = MM_PFN_PTE_FRAME;
|
||
}
|
||
else
|
||
{
|
||
/* Get the current last page on the colored list */
|
||
ColoredPfn = (PMMPFN)ColorTable->Blink;
|
||
|
||
/* Link with the previous last page */
|
||
MM::Paging::SetPte(&ColoredPfn->OriginalPte, PageFrameIndex);
|
||
PfnEntry->u4.PteFrame = ColoredPfn - (PMMPFN)MemoryLayout->PfnDatabaseAddress;
|
||
}
|
||
|
||
/* Set the page as the new tail of the colored list */
|
||
ColorTable->Blink = PfnEntry;
|
||
ColorTable->Count++;
|
||
MM::Paging::SetPte(&PfnEntry->OriginalPte, MAXULONG_PTR);
|
||
|
||
/* Increment number of available pages */
|
||
IncrementAvailablePages();
|
||
}
|
||
|
||
|
||
/**
|
||
* Links a physical page to the appropriate list.
|
||
*
|
||
* @param ListHead
|
||
* Pointer to the list head.
|
||
*
|
||
* @param PageFrameIndex
|
||
* The Page Frame Number (PFN) of the page to link.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::LinkPage(IN PMMPFNLIST ListHead,
|
||
IN PFN_NUMBER PageFrameIndex)
|
||
{
|
||
PMMCOLOR_TABLES ColorHead;
|
||
MMPAGELISTS ListName;
|
||
PMMPFN PageFrame;
|
||
|
||
/* Get the memory layout */
|
||
PMMMEMORY_LAYOUT MemoryLayout = MM::Manager::GetMemoryLayout();
|
||
|
||
/* Get the PFN database entry for the target page */
|
||
PageFrame = &((PMMPFN)MemoryLayout->PfnDatabaseAddress)[PageFrameIndex];
|
||
|
||
/* Get the list name */
|
||
ListName = ListHead->ListName;
|
||
|
||
/* Handle pages being linked to the modified or standby lists */
|
||
if(ListName == ModifiedPageList || ListName == StandbyPageList)
|
||
{
|
||
/* Detect an invalid prototype/transition PTE state */
|
||
if(!MM::Paging::GetPteSoftwarePrototype(&PageFrame->OriginalPte) &&
|
||
MM::Paging::GetPteSoftwareTransition(&PageFrame->OriginalPte))
|
||
{
|
||
/* Crash system due to corrupted PFN/PTE state */
|
||
KE::Crash::PanicEx(0x71, 0x8888, 0, 0, 0);
|
||
}
|
||
}
|
||
|
||
/* Check if the page is ROM */
|
||
if(PageFrame->u3.e1.Rom == 1)
|
||
{
|
||
/* Link the page to the ROM list */
|
||
ListHead = &RomPagesList;
|
||
ListHead->Total++;
|
||
|
||
/* Append the page to the end of the ROM list */
|
||
if(ListHead->Blink != MAXULONG_PTR)
|
||
{
|
||
/* Update the previous tail to point to this page */
|
||
(&((PMMPFN)MemoryLayout->PfnDatabaseAddress)[ListHead->Blink])->u1.Flink = PageFrameIndex;
|
||
}
|
||
else
|
||
{
|
||
/* Initialize the list if it was empty */
|
||
ListHead->Flink = PageFrameIndex;
|
||
}
|
||
|
||
/* Update list tail and PFN linkage */
|
||
ListHead->Blink = PageFrameIndex;
|
||
PageFrame->u1.Flink = MAXULONG_PTR;
|
||
PageFrame->u2.Blink = ListHead->Blink;
|
||
PageFrame->u3.e1.PageLocation = ListName;
|
||
|
||
/* ROM pages require no further processing */
|
||
return;
|
||
}
|
||
|
||
/* Account for the page being inserted into the target list */
|
||
ListHead->Total++;
|
||
|
||
/* Redirect modified pages to the per-color modified list */
|
||
if(ListHead == &ModifiedPagesList)
|
||
{
|
||
/* Select the modified list matching the page color */
|
||
ListHead = MM::Colors::GetModifiedPages(PageFrame->u3.e1.PageColor);
|
||
ListHead->Total++;
|
||
}
|
||
else if((PageFrame->u3.e1.RemovalRequested == 1) && (ListName <= StandbyPageList))
|
||
{
|
||
/* Undo the insertion into the current list */
|
||
ListHead->Total--;
|
||
|
||
/* Preserve the standby location for removed pages */
|
||
if(ListName == StandbyPageList)
|
||
{
|
||
/* Keep the page marked as standby */
|
||
PageFrame->u3.e1.PageLocation = StandbyPageList;
|
||
}
|
||
|
||
/* Mark the page as no longer cache-mapped */
|
||
PageFrame->u3.e1.CacheAttribute = PfnNotMapped;
|
||
|
||
/* Move the page to the bad page list */
|
||
ListHead = &BadPagesList;
|
||
ListHead->Total++;
|
||
ListName = BadPageList;
|
||
}
|
||
|
||
/* Insert zeroed pages at the head of the list */
|
||
if(ListName == ZeroedPageList)
|
||
{
|
||
/* Link the page as the new list head */
|
||
ListHead->Flink = PageFrameIndex;
|
||
|
||
/* Initialize PFN forward and backward links */
|
||
PageFrame->u1.Flink = ListHead->Flink;
|
||
PageFrame->u2.Blink = MAXULONG_PTR;
|
||
|
||
/* Update the previous head if it exists */
|
||
if(ListHead->Flink != MAXULONG_PTR)
|
||
{
|
||
/* Fix up the backward link of the old head */
|
||
(&((PMMPFN)MemoryLayout->PfnDatabaseAddress)[ListHead->Flink])->u2.Blink = PageFrameIndex;
|
||
}
|
||
else
|
||
{
|
||
/* Set the tail if the list was empty */
|
||
ListHead->Blink = PageFrameIndex;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Append the page to the tail of the list */
|
||
if(ListHead->Blink != MAXULONG_PTR)
|
||
{
|
||
/* Link the current tail to the new page */
|
||
(&((PMMPFN)MemoryLayout->PfnDatabaseAddress)[ListHead->Blink])->u1.Flink = PageFrameIndex;
|
||
}
|
||
else
|
||
{
|
||
/* Initialize the list if it was empty */
|
||
ListHead->Flink = PageFrameIndex;
|
||
}
|
||
|
||
/* Update list tail */
|
||
ListHead->Blink = PageFrameIndex;
|
||
|
||
/* Terminate PFN forward link and set backward link */
|
||
PageFrame->u1.Flink = MAXULONG_PTR;
|
||
PageFrame->u2.Blink = ListHead->Blink;
|
||
}
|
||
|
||
/* Record the page’s current location */
|
||
PageFrame->u3.e1.PageLocation = ListName;
|
||
|
||
/* Handle pages that contribute to the available page count */
|
||
if(ListName <= StandbyPageList)
|
||
{
|
||
/* Increment the system-wide available page counter */
|
||
MM::Pfn::IncrementAvailablePages();
|
||
|
||
/* Select the free list matching the page color */
|
||
ColorHead = MM::Colors::GetFreePages(ZeroedPageList, PageFrameIndex & MM::Colors::GetPagingColorsMask());
|
||
|
||
/* Store the color list linkage in the original PTE */
|
||
MM::Paging::SetPte(&PageFrame->OriginalPte, ColorHead->Flink);
|
||
PageFrame->u4.PteFrame = MM_PFN_PTE_FRAME;
|
||
|
||
/* Insert the page into the color free list */
|
||
ColorHead->Flink = PageFrameIndex;
|
||
|
||
/* Update the previous head or initialize the tail */
|
||
if(ColorHead->Flink != MAXULONG_PTR)
|
||
{
|
||
/* Fix up the PTE frame of the previous entry */
|
||
(&((PMMPFN)MemoryLayout->PfnDatabaseAddress)[ColorHead->Flink])->u4.PteFrame = PageFrameIndex;
|
||
}
|
||
else
|
||
{
|
||
/* Set the tail for an empty color list */
|
||
ColorHead->Blink = (PVOID)PageFrame;
|
||
}
|
||
|
||
/* Increment the color list page count */
|
||
ColorHead->Count++;
|
||
}
|
||
else if(ListName == ModifiedPageList)
|
||
{
|
||
/* Modified page insertion logic not implemented yet */
|
||
UNIMPLEMENTED;
|
||
}
|
||
else if(ListName == ModifiedReadOnlyPageList)
|
||
{
|
||
/* Modified read-only page handling not implemented yet */
|
||
UNIMPLEMENTED;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Initializes the PFN database entry for a physical page that is used as a page table.
|
||
*
|
||
* @param PageFrameIndex
|
||
* The page frame number of the physical page being used as a page table.
|
||
*
|
||
* @param PointerPte
|
||
* A pointer to the higher-level PTE that maps this page table page.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::LinkPfnForPageTable(IN PFN_NUMBER PageFrameIndex,
|
||
IN PMMPTE PointerPte)
|
||
{
|
||
PMMPFN Pfn;
|
||
PMMPDE PointerPde;
|
||
PVOID EndAddress;
|
||
|
||
/* Retrieve the PFN database entry for the physical page of the page table */
|
||
Pfn = GetPfnEntry(PageFrameIndex);
|
||
|
||
/* Calculate the end address of the PFN entry to ensure it's mapped */
|
||
EndAddress = (PUCHAR)(Pfn + 1) - 1;
|
||
|
||
/* Validate that the PFN entry corresponds to a valid, active physical page */
|
||
if((PageFrameIndex <= HighestPhysicalPage) && (MM::Pte::AddressValid(Pfn)) &&
|
||
(MM::Pte::AddressValid(EndAddress)) && (Pfn->u3.e1.PageLocation == ActiveAndValid))
|
||
{
|
||
/* Initialize the PFN entry for this page table page */
|
||
Pfn->OriginalPte = *PointerPte;
|
||
Pfn->PteAddress = PointerPte;
|
||
Pfn->u1.WsIndex = 0;
|
||
Pfn->u2.ShareCount++;
|
||
Pfn->u3.e1.CacheAttribute = PfnNonCached;
|
||
Pfn->u3.e1.PageLocation = ActiveAndValid;
|
||
Pfn->u3.e2.ReferenceCount = 1;
|
||
Pfn->u4.PteFrame = MM::Paging::GetPageFrameNumber(MM::Paging::GetPteAddress(PointerPte));
|
||
}
|
||
|
||
/* Increment the share count of the parent page table that contains the mapping */
|
||
PointerPde = MM::Paging::GetPdeAddress(MM::Paging::GetPteVirtualAddress(PointerPte));
|
||
Pfn = GetPfnEntry(MM::Paging::GetPageFrameNumber(PointerPde));
|
||
Pfn->u2.ShareCount++;
|
||
}
|
||
|
||
/**
|
||
* Links a PFN entry to its corresponding PTE and ensures the page table that contains the PTE is resident in memory.
|
||
*
|
||
* @param PageFrameIndex
|
||
* Supplies the index into the PFN database for the page being initialized.
|
||
*
|
||
* @param PointerPte
|
||
* Supplies the pointer to the PTE which maps the physical page.
|
||
*
|
||
* @param Modified
|
||
* Supplies a flag indicating if the page's initial state is modified.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::LinkPfnToPte(IN PFN_NUMBER PageFrameIndex,
|
||
IN PMMPTE PointerPte,
|
||
IN BOOLEAN Modified)
|
||
{
|
||
PMMMEMORY_LAYOUT MemoryLayout;
|
||
XTSTATUS Status;
|
||
PMMPFN Pfn;
|
||
PMMPTE Pte;
|
||
|
||
/* Get the memory layout */
|
||
MemoryLayout = MM::Manager::GetMemoryLayout();
|
||
|
||
/* Point the PFN to its PTE */
|
||
Pfn = &((PMMPFN)MemoryLayout->PfnDatabaseAddress)[PageFrameIndex];
|
||
Pfn->PteAddress = PointerPte;
|
||
|
||
/* Check if the page is already mapped and in use */
|
||
if(MM::Paging::PteValid(PointerPte))
|
||
{
|
||
/* Clear the original PTE information */
|
||
MM::Paging::SetPte(&Pfn->OriginalPte, 0, MM_PTE_READWRITE | MM_PTE_CACHE_ENABLE);
|
||
}
|
||
else
|
||
{
|
||
/* Page is not resident, so save the PTE contents for later use */
|
||
Pfn->OriginalPte = *PointerPte;
|
||
}
|
||
|
||
/* Initialize the PFN database entry for this page */
|
||
Pfn->u2.ShareCount = 1;
|
||
Pfn->u3.e1.Modified = Modified;
|
||
Pfn->u3.e1.PageLocation = ActiveAndValid;
|
||
Pfn->u3.e2.ReferenceCount = 1;
|
||
|
||
/* Get the PDE that maps the page table containing this PTE */
|
||
Pte = MM::Paging::GetPteAddress(PointerPte);
|
||
if(!MM::Paging::PteValid(Pte))
|
||
{
|
||
/* Check if page table is resident */
|
||
Status = MM::PageFault::CheckPdeForPagedPool(PointerPte);
|
||
if(Status != STATUS_SUCCESS)
|
||
{
|
||
/* Could not make the page table resident, crash system */
|
||
KE::Crash::PanicEx(0x1,
|
||
(ULONG_PTR)0x61940,
|
||
(ULONG_PTR)PointerPte,
|
||
MM::Paging::GetPageFrameNumber(PointerPte),
|
||
(ULONG_PTR)MM::Paging::GetPteVirtualAddress(PointerPte));
|
||
}
|
||
}
|
||
|
||
/* Record the page frame of the page table itself */
|
||
PageFrameIndex = MM::Paging::GetPageFrameNumber(Pte);
|
||
Pfn->u4.PteFrame = PageFrameIndex;
|
||
|
||
/* Pin the page table in memory by incrementing its PFN share count */
|
||
Pfn = &((PMMPFN)MemoryLayout->PfnDatabaseAddress)[PageFrameIndex];
|
||
Pfn->u2.ShareCount++;
|
||
}
|
||
|
||
/**
|
||
* Links a page to the beginning of the appropriate standby or ROM list.
|
||
*
|
||
* @param PageFrameIndex
|
||
* The Page Frame Number (PFN) of the page to link.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
|
||
XTFASTCALL
|
||
VOID
|
||
MM::Pfn::LinkStandbyPage(IN PFN_NUMBER PageFrameIndex)
|
||
{
|
||
PMMPFN AdjacentPageFrame, CurrentPageFrame;
|
||
PMMMEMORY_LAYOUT MemoryLayout;
|
||
PFN_NUMBER Flink;
|
||
|
||
/* Get the memory layout */
|
||
MemoryLayout = MM::Manager::GetMemoryLayout();
|
||
|
||
/* Get the PFN database entry for the target page */
|
||
CurrentPageFrame = &((PMMPFN)MemoryLayout->PfnDatabaseAddress)[PageFrameIndex];
|
||
|
||
/* Check if the page is part of a ROM image */
|
||
if(CurrentPageFrame->u3.e1.Rom == 1)
|
||
{
|
||
/* Increment the total number of ROM pages */
|
||
RomPagesList.Total++;
|
||
|
||
/* If the ROM list is not empty, link the new page */
|
||
if(RomPagesList.Blink != (ULONG_PTR)-1)
|
||
{
|
||
/* Update the old tail to point to the new page */
|
||
AdjacentPageFrame = &((PMMPFN)MemoryLayout->PfnDatabaseAddress)[RomPagesList.Blink];
|
||
AdjacentPageFrame->u1.Flink = PageFrameIndex;
|
||
}
|
||
else
|
||
{
|
||
/* Otherwise, this page is now the head of the list */
|
||
RomPagesList.Flink = PageFrameIndex;
|
||
}
|
||
|
||
/* Set the new page as the tail and update its links */
|
||
RomPagesList.Blink = PageFrameIndex;
|
||
CurrentPageFrame->u1.Flink = (ULONG_PTR)-1;
|
||
CurrentPageFrame->u2.Blink = RomPagesList.Blink;
|
||
CurrentPageFrame->u3.e1.PageLocation = StandbyPageList;
|
||
|
||
/* ROM pages require no further processing */
|
||
return;
|
||
}
|
||
|
||
/* Increment the count of pages on the standby list */
|
||
StandbyPagesList.Total++;
|
||
|
||
/* Save the old head and set the current page as the new head */
|
||
Flink = StandbyPagesList.Flink;
|
||
StandbyPagesList.Flink = PageFrameIndex;
|
||
|
||
/* Point the new head to the old one, marking it as the list front */
|
||
CurrentPageFrame->u1.Flink = Flink;
|
||
CurrentPageFrame->u2.Blink = MAXULONG_PTR;
|
||
|
||
/* If the ROM list is not empty, link the new page */
|
||
if(Flink != MAXULONG_PTR)
|
||
{
|
||
/* Update the old head to point to the new page */
|
||
AdjacentPageFrame = &((PMMPFN)MemoryLayout->PfnDatabaseAddress)[Flink];
|
||
AdjacentPageFrame->u2.Blink = PageFrameIndex;
|
||
}
|
||
else
|
||
{
|
||
/* Otherwise, this page is now the tail of the list */
|
||
StandbyPagesList.Blink = PageFrameIndex;
|
||
}
|
||
|
||
/* Update the page's location to the standby list */
|
||
CurrentPageFrame->u3.e1.PageLocation = StandbyPageList;
|
||
|
||
/* Increment number of available pages */
|
||
IncrementAvailablePages();
|
||
}
|
||
|
||
/**
|
||
* Scans memory descriptors provided by the boot loader.
|
||
*
|
||
* @return This routine does not return any value.
|
||
*
|
||
* @since XT 1.0
|
||
*/
|
||
XTAPI
|
||
VOID
|
||
MM::Pfn::ScanMemoryDescriptors(VOID)
|
||
{
|
||
PLIST_ENTRY LoaderMemoryDescriptors, MemoryMappings;
|
||
PLOADER_MEMORY_DESCRIPTOR MemoryDescriptor;
|
||
PFN_NUMBER FreePages;
|
||
|
||
/* Initially, set number of free pages to 0 */
|
||
FreePages = 0;
|
||
|
||
/* Get the list head of memory descriptors */
|
||
LoaderMemoryDescriptors = KE::BootInformation::GetMemoryDescriptors();
|
||
|
||
/* Iterate through the memory descriptors */
|
||
MemoryMappings = LoaderMemoryDescriptors->Flink;
|
||
while(MemoryMappings != LoaderMemoryDescriptors)
|
||
{
|
||
/* Get the memory descriptor */
|
||
MemoryDescriptor = CONTAIN_RECORD(MemoryMappings, LOADER_MEMORY_DESCRIPTOR, ListEntry);
|
||
|
||
/* Skip invisible or hardware cached memory regions */
|
||
if(MM::Manager::VerifyMemoryTypeInvisible(MemoryDescriptor->MemoryType) ||
|
||
(MemoryDescriptor->MemoryType == LoaderHardwareCachedMemory))
|
||
{
|
||
/* Move to the next descriptor and skip further processing */
|
||
MemoryMappings = MemoryMappings->Flink;
|
||
continue;
|
||
}
|
||
|
||
/* Count the number of physical pages, excluding bad memory */
|
||
if(MemoryDescriptor->MemoryType != LoaderBad)
|
||
{
|
||
/* Add the pages from this descriptor to the total count */
|
||
NumberOfPhysicalPages += MemoryDescriptor->PageCount;
|
||
}
|
||
|
||
/* Check if this physical page is the lowest one yet */
|
||
if(MemoryDescriptor->BasePage < LowestPhysicalPage)
|
||
{
|
||
/* Update the lowest physical page number found so far */
|
||
LowestPhysicalPage = MemoryDescriptor->BasePage;
|
||
}
|
||
|
||
/* Check if this physical page is the highest one yet */
|
||
if((MemoryDescriptor->BasePage + MemoryDescriptor->PageCount) > HighestPhysicalPage)
|
||
{
|
||
/* Update the highest physical page number found so far */
|
||
HighestPhysicalPage = (MemoryDescriptor->BasePage + MemoryDescriptor->PageCount) - 1;
|
||
}
|
||
|
||
/* Identify the largest block of free memory */
|
||
if(MM::Manager::VerifyMemoryTypeFree(MemoryDescriptor->MemoryType))
|
||
{
|
||
/* Check if this free memory block is the largest one yet */
|
||
if(MemoryDescriptor->PageCount >= FreePages)
|
||
{
|
||
/* Update the largest free block size and save the descriptor */
|
||
FreePages = MemoryDescriptor->PageCount;
|
||
FreeDescriptor = MemoryDescriptor;
|
||
}
|
||
}
|
||
|
||
/* Get next memory descriptor */
|
||
MemoryMappings = MemoryMappings->Flink;
|
||
}
|
||
|
||
/* Ensure a free memory descriptor was found */
|
||
if(!FreeDescriptor)
|
||
{
|
||
/* No free memory available to bootstrap the system */
|
||
KE::Crash::Panic(0);
|
||
}
|
||
|
||
/* Save a copy of the original free descriptor before it gets modified */
|
||
RTL::Memory::CopyMemory(&OriginalFreeDescriptor, FreeDescriptor, sizeof(LOADER_MEMORY_DESCRIPTOR));
|
||
}
|
||
|
||
/**
|
||
* Unlinks a physical page from its corresponding list.
|
||
*
|
||
* @param PageIndex
|
||
* The Page Frame Number (PFN) of the page to unlink.
|
||
*
|
||
* @param Color
|
||
* The color of the page, used to find the correct colored list.
|
||
*
|
||
* @return This routine returns the PFN of the page that was unlinked.
|
||
*/
|
||
XTAPI
|
||
PFN_NUMBER
|
||
MM::Pfn::UnlinkFreePage(IN PFN_NUMBER PageFrameIndex,
|
||
IN ULONG Color)
|
||
{
|
||
PMMPFN Pfn;
|
||
PMMPFNLIST PfnList;
|
||
MMPAGELISTS PageList;
|
||
ULONG NodeColor;
|
||
PMMCOLOR_TABLES ColorTable;
|
||
PFN_NUMBER NextPage, PrevPage;
|
||
|
||
/* Get the PFN database entry for the target page */
|
||
Pfn = GetPfnEntry(PageFrameIndex);
|
||
|
||
/* Identify which list the page belongs to (FreePageList or ZeroedPageList) */
|
||
PfnList = PageLocationList[Pfn->u3.e1.PageLocation];
|
||
PageList = PfnList->ListName;
|
||
|
||
/* Update the forward link of the previous page */
|
||
if(Pfn->u2.Blink != MAXULONG_PTR)
|
||
{
|
||
/* The page is not the head of the list; update the previous page's Flink */
|
||
GetPfnEntry(Pfn->u2.Blink)->u1.Flink = Pfn->u1.Flink;
|
||
}
|
||
else
|
||
{
|
||
/* This is the first page in the list; update the list head's Flink */
|
||
PfnList->Flink = Pfn->u1.Flink;
|
||
}
|
||
|
||
/* Update the backward link of the next page */
|
||
if(Pfn->u1.Flink != MAXULONG_PTR)
|
||
{
|
||
/* The page is not the tail of the list; update the next page's Blink */
|
||
GetPfnEntry(Pfn->u1.Flink)->u2.Blink = Pfn->u2.Blink;
|
||
}
|
||
else
|
||
{
|
||
/* This is the last page in the list; update the list head's Blink */
|
||
PfnList->Blink = Pfn->u2.Blink;
|
||
}
|
||
|
||
/* Get the first page on the color list */
|
||
ColorTable = MM::Colors::GetFreePages(PageList, Color);
|
||
NodeColor = Pfn->u3.e1.PageColor;
|
||
PrevPage = Pfn->u4.PteFrame;
|
||
NextPage = MM::Paging::GetPte(&Pfn->OriginalPte);
|
||
|
||
/* Decrement the count of pages for this specific color and total page count for this list */
|
||
ColorTable->Count--;
|
||
PfnList->Total--;
|
||
|
||
/* Update the forward link of the previous colored page */
|
||
if(PrevPage != MM_PFN_PTE_FRAME)
|
||
{
|
||
/* This is not the first page; update the previous page's Flink */
|
||
MM::Paging::SetPte(&GetPfnEntry(PrevPage)->OriginalPte, NextPage);
|
||
}
|
||
else
|
||
{
|
||
/* This was the first page; update the color table's Flink */
|
||
ColorTable->Flink = NextPage;
|
||
}
|
||
|
||
/* Update the backward link of the next colored page */
|
||
if (NextPage != MAXULONG_PTR)
|
||
{
|
||
/* This is not the last page; update the next page's Blink */
|
||
GetPfnEntry(NextPage)->u4.PteFrame = PrevPage;
|
||
}
|
||
else
|
||
{
|
||
/* This was the last page; update the color table's Blink */
|
||
ColorTable->Blink = (PVOID)PrevPage;
|
||
}
|
||
|
||
/* Clear the list pointers and flags, but preserve the color and cache attributes */
|
||
Pfn->u1.Flink = 0;
|
||
Pfn->u2.Blink = 0;
|
||
Pfn->u3.e1.CacheAttribute = PfnNotMapped;
|
||
Pfn->u3.e1.PageColor = NodeColor;
|
||
Pfn->u3.e2.ShortFlags = 0;
|
||
|
||
/* Decrement the global count of available pages */
|
||
DecrementAvailablePages();
|
||
|
||
/* Return the page that was just unlinked */
|
||
return PageFrameIndex;
|
||
}
|