250 lines
8.3 KiB
C++
250 lines
8.3 KiB
C++
/**
|
|
* PROJECT: ExectOS
|
|
* COPYRIGHT: See COPYING.md in the top level directory
|
|
* FILE: xtoskrnl/mm/amd64/pfn.cc
|
|
* DESCRIPTION: Physical Frame Number for AMD64 support
|
|
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
|
|
* Rafal Kupiec <belliash@codingworkshop.eu.org>
|
|
*/
|
|
|
|
#include <xtos.hh>
|
|
|
|
|
|
/**
|
|
* Initializes the PFN database by mapping virtual memory and populating entries.
|
|
*
|
|
* @return This routine does not return any value.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTAPI
|
|
VOID
|
|
MM::Pfn::InitializePfnDatabase(VOID)
|
|
{
|
|
PKERNEL_INITIALIZATION_BLOCK InitializationBlock;
|
|
PLIST_ENTRY ListEntry;
|
|
PLOADER_MEMORY_DESCRIPTOR Descriptor;
|
|
PFN_NUMBER BasePage, PageCount;
|
|
PUCHAR PfnDatabaseEnd;
|
|
PMMMEMORY_LAYOUT MemoryLayout;
|
|
PMMPTE ValidPte;
|
|
|
|
/* Raise runlevel and acquire the PFN lock */
|
|
KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL);
|
|
KE::QueuedSpinLockGuard SpinLock(SystemSpaceLock);
|
|
|
|
/* Get the kernel initialization block */
|
|
InitializationBlock = KE::BootInformation::GetInitializationBlock();
|
|
|
|
/* Get the memory layout */
|
|
MemoryLayout = MM::Manager::GetMemoryLayout();
|
|
|
|
/* Get the PFN database size and calculate the end of the PFN database virtual address space */
|
|
PfnDatabaseEnd = (PUCHAR)MemoryLayout->PfnDatabaseAddress + (PfnDatabaseSize * MM_PAGE_SIZE) - 1;
|
|
|
|
/* Get a template PTE for mapping the PFN database pages */
|
|
ValidPte = MM::Pte::GetValidPte();
|
|
|
|
/* Map the Page Directory and Page Directory Pointer tables for the PFN database */
|
|
MM::Pte::MapPPE(MemoryLayout->PfnDatabaseAddress, PfnDatabaseEnd, ValidPte);
|
|
MM::Pte::MapPDE(MemoryLayout->PfnDatabaseAddress, PfnDatabaseEnd, ValidPte);
|
|
|
|
/* Initialize the color tables */
|
|
MM::Colors::InitializeColorTables();
|
|
|
|
/* Iterate over memory descriptors to map the PFN database and initialize entries */
|
|
ListEntry = InitializationBlock->MemoryDescriptorListHead.Flink;
|
|
while(ListEntry != &InitializationBlock->MemoryDescriptorListHead)
|
|
{
|
|
/* Get the descriptor */
|
|
Descriptor = CONTAIN_RECORD(ListEntry, LOADER_MEMORY_DESCRIPTOR, ListEntry);
|
|
|
|
/* Skip invisible memory regions */
|
|
if(MM::Manager::VerifyMemoryTypeInvisible(Descriptor->MemoryType))
|
|
{
|
|
/* Move to the next descriptor and continue */
|
|
ListEntry = ListEntry->Flink;
|
|
continue;
|
|
}
|
|
|
|
/* Determine the physical page range to process */
|
|
if(Descriptor == FreeDescriptor)
|
|
{
|
|
BasePage = OriginalFreeDescriptor.BasePage;
|
|
PageCount = OriginalFreeDescriptor.PageCount;
|
|
}
|
|
else
|
|
{
|
|
BasePage = Descriptor->BasePage;
|
|
PageCount = Descriptor->PageCount;
|
|
}
|
|
|
|
/* Map PFN database entries for this physical range */
|
|
MM::Pte::MapPTE(&((PMMPFN)MemoryLayout->PfnDatabaseAddress)[BasePage],
|
|
(PUCHAR)&((PMMPFN)MemoryLayout->PfnDatabaseAddress)[BasePage + PageCount] - 1,
|
|
ValidPte);
|
|
|
|
/* Split PFN database allocation out of the free descriptor */
|
|
if(Descriptor == FreeDescriptor)
|
|
{
|
|
/* Initialize PFNs for the remaining free memory */
|
|
ProcessMemoryDescriptor(BasePage + PfnDatabaseSize, PageCount - PfnDatabaseSize, LoaderFree);
|
|
|
|
/* Initialize PFNs for the physical pages backing the PFN database */
|
|
ProcessMemoryDescriptor(BasePage, PfnDatabaseSize, LoaderMemoryData);
|
|
}
|
|
else
|
|
{
|
|
/* Initialize PFNs for this memory range */
|
|
ProcessMemoryDescriptor(BasePage, PageCount, Descriptor->MemoryType);
|
|
}
|
|
|
|
/* Move to the next descriptor */
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
/* Restore original free descriptor */
|
|
*FreeDescriptor = OriginalFreeDescriptor;
|
|
|
|
/* Initialize PFNs backing page tables */
|
|
InitializePageTablePfns();
|
|
}
|
|
|
|
/**
|
|
* Initializes PFN database entries for the system page tables.
|
|
*
|
|
* @return This routine does not return any value.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTAPI
|
|
VOID
|
|
MM::Pfn::InitializePageTablePfns(VOID)
|
|
{
|
|
PFN_NUMBER PageFrameIndex;
|
|
PMMPFN Pfn;
|
|
ULONG RootLevel;
|
|
PMMPTE RootPte;
|
|
|
|
/* Determine root structure based on paging mode */
|
|
if(MM::Paging::GetXpaStatus())
|
|
{
|
|
/* XPA enabled, 5-level paging (LA57) */
|
|
RootLevel = 5;
|
|
|
|
/* Retrieve the PFN of the PML5 table and its virtual base address */
|
|
PageFrameIndex = MM::Paging::GetPageFrameNumber(MM::Paging::GetPteAddress((PVOID)MM_P5E_LA57_BASE));
|
|
RootPte = (PMMPTE)MM::Paging::GetP5eAddress(NULLPTR);
|
|
}
|
|
else
|
|
{
|
|
/* XPA disabled, 4-level paging */
|
|
RootLevel = 4;
|
|
|
|
/* Retrieve the PFN of the PML4 table and its virtual base address */
|
|
PageFrameIndex = MM::Paging::GetPageFrameNumber(MM::Paging::GetPteAddress((PVOID)MM_PXE_BASE));
|
|
RootPte = (PMMPTE)MM::Paging::GetPxeAddress(NULLPTR);
|
|
}
|
|
|
|
/* Initialize the PFN entry for the root page table itself */
|
|
Pfn = GetPfnEntry(PageFrameIndex);
|
|
if(Pfn)
|
|
{
|
|
/* Initialize the PFN entry */
|
|
Pfn->PteAddress = NULLPTR;
|
|
Pfn->u1.WsIndex = 0;
|
|
Pfn->u2.ShareCount = 1;
|
|
Pfn->u3.e1.CacheAttribute = PfnNonCached;
|
|
Pfn->u3.e2.ReferenceCount = 1;
|
|
Pfn->u4.PteFrame = 0;
|
|
}
|
|
|
|
/* Start recursive scan from the top level */
|
|
if(RootPte)
|
|
{
|
|
/* Scan the root page table */
|
|
ScanPageTable(RootPte, RootLevel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively scans a page table to initialize PFN database entries for active pages.
|
|
*
|
|
* @param PointerPte
|
|
* Pointer to the base of the page table to scan.
|
|
*
|
|
* @param Level
|
|
* The paging level of the table being scanned.
|
|
*
|
|
* @return This routine does not return any value.
|
|
*
|
|
* @since XT 1.0
|
|
*/
|
|
XTAPI
|
|
VOID
|
|
MM::Pfn::ScanPageTable(IN PMMPTE PointerPte,
|
|
IN ULONG Level)
|
|
{
|
|
PVOID Address;
|
|
ULONG Index;
|
|
PMMPTE NextLevelPte;
|
|
ULONG PtesPerPage;
|
|
|
|
/* Get the number of PTEs per page */
|
|
PtesPerPage = MM::Pte::GetPtesPerPage();
|
|
|
|
/* Iterate through all entries in the current page table */
|
|
for(Index = 0; Index < PtesPerPage; Index++)
|
|
{
|
|
/* Check if the page table entry is present */
|
|
if(MM::Paging::PteValid(PointerPte))
|
|
{
|
|
/* Mark the PFN pointed to by this entry as active */
|
|
LinkPfnForPageTable(MM::Paging::GetPageFrameNumber(PointerPte), PointerPte);
|
|
|
|
/* Recurse to the next level, if this is not a leaf node (PTE) */
|
|
if(Level > 1)
|
|
{
|
|
/* Calculate the virtual address mapped by this entry to find the next table */
|
|
switch(Level)
|
|
{
|
|
case 5:
|
|
/* Calculate PXE */
|
|
Address = MM::Paging::GetP5eVirtualAddress((PMMP5E)PointerPte);
|
|
NextLevelPte = (PMMPTE)MM::Paging::GetPxeAddress(Address);
|
|
break;
|
|
case 4:
|
|
/* Calculate PPE */
|
|
Address = MM::Paging::GetPxeVirtualAddress((PMMPXE)PointerPte);
|
|
NextLevelPte = (PMMPTE)MM::Paging::GetPpeAddress(Address);
|
|
break;
|
|
case 3:
|
|
/* Calculate PDE */
|
|
Address = MM::Paging::GetPpeVirtualAddress((PMMPPE)PointerPte);
|
|
NextLevelPte = (PMMPTE)MM::Paging::GetPdeAddress(Address);
|
|
break;
|
|
case 2:
|
|
/* Calculate PTE */
|
|
Address = MM::Paging::GetPdeVirtualAddress((PMMPDE)PointerPte);
|
|
NextLevelPte = MM::Paging::GetPteAddress(Address);
|
|
break;
|
|
default:
|
|
/* Nothing to calculate, return NULLPTR */
|
|
NextLevelPte = NULLPTR;
|
|
break;
|
|
}
|
|
|
|
/* Recurse deeper if not at the bottom level (PTE) already */
|
|
if(NextLevelPte)
|
|
{
|
|
/* Recursively scan the next level page table */
|
|
ScanPageTable(NextLevelPte, Level - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Move to the next entry in the current table */
|
|
PointerPte = MM::Paging::GetNextPte(PointerPte);
|
|
}
|
|
}
|