Files
exectos/xtoskrnl/mm/pfn.cc
Aiken Harris bd1a3605d2
All checks were successful
Builds / ExectOS (amd64, release) (push) Successful in 27s
Builds / ExectOS (amd64, debug) (push) Successful in 29s
Builds / ExectOS (i686, release) (push) Successful in 39s
Builds / ExectOS (i686, debug) (push) Successful in 38s
Add logic to insert pages at the head of standby list
2026-01-05 23:12:58 +01:00

1222 lines
38 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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 pages 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();
}
/**
* Processes a memory descriptor and initializes the corresponding PFN database entries
*
* @param BasePage
* The starting physical page number of the memory run
*
* @param PageCount
* The number of pages in the memory run
*
* @param MemoryType
* The type of memory as reported by the bootloader (e.g., free, ROM, in-use)
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
MM::Pfn::ProcessMemoryDescriptor(IN PFN_NUMBER BasePage,
IN PFN_NUMBER PageCount,
IN LOADER_MEMORY_TYPE MemoryType)
{
PFN_NUMBER CurrentPage, PageNumber;
PMMPFN Pfn;
/* Check if the memory descriptor describes a free memory region */
if(MM::Manager::VerifyMemoryTypeFree(MemoryType))
{
/* Iterate over each page in this free memory run */
for(PageNumber = 0; PageNumber < PageCount; PageNumber++)
{
/* Get the PFN entry for the current page and set its initial cache attribute */
CurrentPage = BasePage + PageNumber;
Pfn = GetPfnEntry(CurrentPage);
Pfn->u3.e1.CacheAttribute = PfnNonCached;
/* Add the page to the free list to make it available for allocation */
LinkFreePage(CurrentPage);
}
}
else
{
/* Handle all other (non-free) memory types */
switch(MemoryType)
{
case LoaderBad:
/* This memory is marked as bad and should not be used, add it to the bad pages list */
LinkPage(&BadPagesList, BasePage);
break;
case LoaderXIPRom:
/* This memory range contains Read-Only Memory (ROM) */
for(PageNumber = 0; PageNumber < PageCount; PageNumber++)
{
/* Get the PFN entry for the current ROM page */
Pfn = GetPfnEntry(BasePage + PageNumber);
/* Initialize the PFN entry to represent a ROM page */
Pfn->PteAddress = 0;
Pfn->u1.Flink = 0;
Pfn->u2.ShareCount = 0;
Pfn->u3.e1.CacheAttribute = PfnNonCached;
Pfn->u3.e1.PageLocation = 0;
Pfn->u3.e1.PrototypePte = 1;
Pfn->u3.e1.Rom = 1;
Pfn->u3.e2.ReferenceCount = 0;
Pfn->u4.InPageError = 0;
Pfn->u4.PteFrame = 0;
}
break;
default:
/* All other types are considered in-use (ie, by the kernel, ACPI, etc) */
for(PageNumber = 0; PageNumber < PageCount; PageNumber++)
{
/* Get the PFN entry for the current in-use page */
Pfn = GetPfnEntry(BasePage + PageNumber);
/* Mark the PFN as active and valid to prevent it from being allocated */
Pfn->u3.e1.PageLocation = ActiveAndValid;
}
break;
}
}
}
/**
* 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;
}