diff --git a/xtoskrnl/CMakeLists.txt b/xtoskrnl/CMakeLists.txt index 8c881cb..e879b97 100644 --- a/xtoskrnl/CMakeLists.txt +++ b/xtoskrnl/CMakeLists.txt @@ -54,6 +54,7 @@ list(APPEND XTOSKRNL_SOURCE ${XTOSKRNL_SOURCE_DIR}/mm/${ARCH}/mmgr.cc ${XTOSKRNL_SOURCE_DIR}/mm/${ARCH}/pagemap.cc ${XTOSKRNL_SOURCE_DIR}/mm/${ARCH}/paging.cc + ${XTOSKRNL_SOURCE_DIR}/mm/${ARCH}/pfn.cc ${XTOSKRNL_SOURCE_DIR}/mm/${ARCH}/pte.cc ${XTOSKRNL_SOURCE_DIR}/mm/colors.cc ${XTOSKRNL_SOURCE_DIR}/mm/data.cc diff --git a/xtoskrnl/includes/mm/amd64/pte.hh b/xtoskrnl/includes/mm/amd64/pte.hh index ca2932d..9c43665 100644 --- a/xtoskrnl/includes/mm/amd64/pte.hh +++ b/xtoskrnl/includes/mm/amd64/pte.hh @@ -29,6 +29,7 @@ namespace MM STATIC XTAPI BOOLEAN AddressValid(IN PVOID VirtualAddress); STATIC XTAPI ULONG GetPtesPerPage(VOID); STATIC XTAPI PMMPTE GetSystemPteBaseAddress(VOID); + STATIC XTAPI PMMPTE GetValidPte(VOID); STATIC XTAPI VOID InitializePageTable(VOID); STATIC XTAPI VOID InitializeSystemPteSpace(VOID); STATIC XTAPI VOID MapP5E(PVOID StartAddress, diff --git a/xtoskrnl/includes/mm/colors.hh b/xtoskrnl/includes/mm/colors.hh index 229b649..d48c3d9 100644 --- a/xtoskrnl/includes/mm/colors.hh +++ b/xtoskrnl/includes/mm/colors.hh @@ -29,6 +29,7 @@ namespace MM STATIC XTAPI ULONG GetNextColor(); STATIC XTAPI ULONG GetPagingColors(); STATIC XTAPI ULONG GetPagingColorsMask(); + STATIC XTAPI VOID InitializeColorTables(VOID); }; } diff --git a/xtoskrnl/includes/mm/i686/pte.hh b/xtoskrnl/includes/mm/i686/pte.hh index 9bf4661..70393f9 100644 --- a/xtoskrnl/includes/mm/i686/pte.hh +++ b/xtoskrnl/includes/mm/i686/pte.hh @@ -29,6 +29,7 @@ namespace MM STATIC XTAPI BOOLEAN AddressValid(IN PVOID VirtualAddress); STATIC XTAPI ULONG GetPtesPerPage(VOID); STATIC XTAPI PMMPTE GetSystemPteBaseAddress(VOID); + STATIC XTAPI PMMPTE GetValidPte(VOID); STATIC XTAPI VOID InitializePageTable(VOID); STATIC XTAPI VOID InitializeSystemPteSpace(VOID); STATIC XTAPI VOID MapPDE(PVOID StartAddress, diff --git a/xtoskrnl/includes/mm/pfn.hh b/xtoskrnl/includes/mm/pfn.hh index 9f98251..1832a78 100644 --- a/xtoskrnl/includes/mm/pfn.hh +++ b/xtoskrnl/includes/mm/pfn.hh @@ -20,6 +20,7 @@ namespace MM private: STATIC PFN_NUMBER AvailablePages; STATIC PLOADER_MEMORY_DESCRIPTOR FreeDescriptor; + STATIC MMPFNLIST FreePagesList; STATIC ULONG_PTR HighestPhysicalPage; STATIC ULONG_PTR LowestPhysicalPage; STATIC ULONGLONG NumberOfPhysicalPages; @@ -29,14 +30,25 @@ namespace MM public: STATIC XTAPI PFN_NUMBER AllocateBootstrapPages(IN PFN_NUMBER NumberOfPages); STATIC XTAPI VOID ComputePfnDatabaseSize(VOID); + STATIC XTAPI ULONG_PTR GetHighestPhysicalPage(VOID); STATIC XTAPI ULONGLONG GetNumberOfPhysicalPages(VOID); STATIC XTAPI PFN_NUMBER GetPfnDatabaseSize(VOID); STATIC XTAPI PMMPFN GetPfnEntry(IN PFN_NUMBER Pfn); + STATIC XTAPI VOID InitializePfnDatabase(VOID); STATIC XTAPI VOID ScanMemoryDescriptors(VOID); private: STATIC XTAPI VOID DecrementAvailablePages(VOID); STATIC XTAPI VOID IncrementAvailablePages(VOID); + STATIC XTAPI VOID InitializePageTablePfns(VOID); + STATIC XTAPI VOID LinkFreePage(IN PFN_NUMBER PageFrameIndex); + STATIC XTAPI VOID LinkPfnForPageTable(PFN_NUMBER PageFrameIndex, + PMMPTE PointerPte); + STATIC XTAPI VOID ProcessMemoryDescriptor(PFN_NUMBER BasePage, + PFN_NUMBER PageCount, + LOADER_MEMORY_TYPE MemoryType); + STATIC XTAPI VOID ScanPageTable(IN PMMPTE PointerPte, + ULONG Level); }; } diff --git a/xtoskrnl/mm/amd64/pfn.cc b/xtoskrnl/mm/amd64/pfn.cc new file mode 100644 index 0000000..caa864f --- /dev/null +++ b/xtoskrnl/mm/amd64/pfn.cc @@ -0,0 +1,249 @@ +/** + * 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 + * Rafal Kupiec + */ + +#include + + +/** + * 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); + } +} diff --git a/xtoskrnl/mm/colors.cc b/xtoskrnl/mm/colors.cc index e225846..5774785 100644 --- a/xtoskrnl/mm/colors.cc +++ b/xtoskrnl/mm/colors.cc @@ -93,3 +93,69 @@ MM::Colors::GetPagingColorsMask() /* Return the mask used for page coloring calculations */ return PagingColorsMask; } + +/** + * Initializes the data structures for page coloring. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +MM::Colors::InitializeColorTables(VOID) +{ + PMMMEMORY_LAYOUT MemoryLayout; + PMMPTE PointerPte, LastPte; + ULONG Color; + PMMPTE ValidPte; + + /* Get the memory layout */ + MemoryLayout = MM::Manager::GetMemoryLayout(); + + /* Set the base address of the color tables to start right after the PFN database */ + FreePages[0] = (PMMCOLOR_TABLES)&((PMMPFN)MemoryLayout->PfnDatabaseAddress)[MM::Pfn::GetHighestPhysicalPage() + 1]; + + /* Calculate the virtual address range for both color tables */ + PointerPte = MM::Paging::GetPteAddress(&FreePages[0][0]); + LastPte = MM::Paging::GetPteAddress((PVOID)((ULONG_PTR)FreePages[0] + + (2 * PagingColors * sizeof(MMCOLOR_TABLES)) - 1)); + + /* Get a pointer to a PTE template */ + ValidPte = MM::Pte::GetValidPte(); + + /* Ensure the entire virtual address range for the color tables is mapped */ + while(PointerPte <= LastPte) + { + /* Check if a page in the range is not mapped */ + if(!MM::Paging::PteValid(PointerPte)) + { + /* Use the bootstrap allocator to get a physical page */ + MM::Paging::SetPte(ValidPte, MM::Pfn::AllocateBootstrapPages(1), 0); + *PointerPte = *ValidPte; + + /* Zero out the newly mapped page */ + RTL::Memory::ZeroMemory(MM::Paging::GetPteVirtualAddress(PointerPte), MM_PAGE_SIZE); + } + + /* Move to the next PTE in the range */ + PointerPte = MM::Paging::GetNextPte(PointerPte); + } + + /* Set the pointer for the second list */ + FreePages[1] = &FreePages[0][PagingColors]; + + /* Initialize all entries in both color tables */ + for(Color = 0; Color < PagingColors; Color++) + { + /* Initialize the FreePageList entry for the current color */ + FreePages[FreePageList][Color].Flink = MAXULONG_PTR; + FreePages[FreePageList][Color].Blink = (PVOID)MAXULONG_PTR; + FreePages[FreePageList][Color].Count = 0; + + /* Initialize the ZeroedPageList entry for the current color */ + FreePages[ZeroedPageList][Color].Flink = MAXULONG_PTR; + FreePages[ZeroedPageList][Color].Blink = (PVOID)MAXULONG_PTR; + FreePages[ZeroedPageList][Color].Count = 0; + } +} diff --git a/xtoskrnl/mm/data.cc b/xtoskrnl/mm/data.cc index 3c26171..886a78a 100644 --- a/xtoskrnl/mm/data.cc +++ b/xtoskrnl/mm/data.cc @@ -45,6 +45,9 @@ PFN_NUMBER MM::Pfn::AvailablePages; /* Biggest free memory descriptor */ PLOADER_MEMORY_DESCRIPTOR MM::Pfn::FreeDescriptor; +/* List containing free physical pages */ +MMPFNLIST MM::Pfn::FreePagesList = {0, FreePageList, MAXULONG_PTR, MAXULONG_PTR}; + /* Highest physical page number */ ULONG_PTR MM::Pfn::HighestPhysicalPage; diff --git a/xtoskrnl/mm/i686/pfn.cc b/xtoskrnl/mm/i686/pfn.cc new file mode 100644 index 0000000..030f97c --- /dev/null +++ b/xtoskrnl/mm/i686/pfn.cc @@ -0,0 +1,234 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/mm/i686/pfn.cc + * DESCRIPTION: Physical Frame Number for i686 support + * DEVELOPERS: Aiken Harris + * Rafal Kupiec + */ + +#include + + +/** + * 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) +{ + PLOADER_MEMORY_DESCRIPTOR Descriptor; + PKERNEL_INITIALIZATION_BLOCK InitializationBlock; + PLIST_ENTRY ListEntry; + PMMMEMORY_LAYOUT MemoryLayout; + PUCHAR PfnDatabaseEnd; + PMMPTE ValidPte; + + /* Raise runlevel and acquire PFN lock */ + KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL); + KE::QueuedSpinLockGuard SpinLock(SystemSpaceLock); + + /* Get the kernel initialization block */ + InitializationBlock = KE::BootInformation::GetInitializationBlock(); + + /* Get 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::MapPDE(MemoryLayout->PfnDatabaseAddress, PfnDatabaseEnd, ValidPte); + MM::Pte::MapPTE(MemoryLayout->PfnDatabaseAddress, PfnDatabaseEnd, ValidPte); + + /* Zero PFN database virtual space */ + RTL::Memory::ZeroMemory(MemoryLayout->PfnDatabaseAddress, PfnDatabaseSize * MM_PAGE_SIZE); + + /* 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; + } + + /* Split PFN DB allocation out of free descriptor */ + if(Descriptor == FreeDescriptor) + { + /* Initialize PFNs for the remaining free memory after the PFN database */ + ProcessMemoryDescriptor(OriginalFreeDescriptor.BasePage + PfnDatabaseSize, + OriginalFreeDescriptor.PageCount - PfnDatabaseSize, + LoaderFree); + + /* Initialize PFNs for the physical pages backing the PFN database itself */ + ProcessMemoryDescriptor(OriginalFreeDescriptor.BasePage, PfnDatabaseSize, LoaderMemoryData); + } + else + { + /* Initialize PFNs for the physical pages described by this descriptor */ + ProcessMemoryDescriptor(Descriptor->BasePage, Descriptor->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, 3-level paging (PAE) */ + RootLevel = 3; + + /* Retrieve the PFN of the PML3 table and its virtual base address */ + PageFrameIndex = AR::CpuFunc::ReadControlRegister(3) >> MM_PAGE_SHIFT; + RootPte = (PMMPTE)MM::Paging::GetPpeAddress(NULLPTR); + } + else + { + /* XPA disabled, 2-level paging */ + RootLevel = 2; + + /* Retrieve the PFN of the PML2 table and its virtual base address */ + PageFrameIndex = AR::CpuFunc::ReadControlRegister(3) >> MM_PAGE_SHIFT; + RootPte = (PMMPTE)MM::Paging::GetPdeAddress(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.e1.PageLocation = ActiveAndValid; + 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(); + + /* Check if PML3 is enabled and current level is PDPT */ + if(Level == 3) + { + /* PAE PDPT has only 4 entries */ + PtesPerPage = 4; + } + + /* 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 3: + /* Calculate PDE */ + Address = MM::Paging::GetPpeVirtualAddress(PointerPte); + NextLevelPte = (PMMPTE)MM::Paging::GetPdeAddress(Address); + break; + case 2: + /* Calculate PTE */ + Address = MM::Paging::GetPdeVirtualAddress(PointerPte); + NextLevelPte = (PMMPTE)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); + } +} diff --git a/xtoskrnl/mm/mmgr.cc b/xtoskrnl/mm/mmgr.cc index a8a4dbf..bf00683 100644 --- a/xtoskrnl/mm/mmgr.cc +++ b/xtoskrnl/mm/mmgr.cc @@ -68,6 +68,9 @@ MM::Manager::InitializeMemoryManager(VOID) /* Initialize system PTE space */ MM::Pte::InitializeSystemPteSpace(); + + /* Initialize PFN database */ + MM::Pfn::InitializePfnDatabase(); } /** diff --git a/xtoskrnl/mm/pfn.cc b/xtoskrnl/mm/pfn.cc index 966572e..6bd0648 100644 --- a/xtoskrnl/mm/pfn.cc +++ b/xtoskrnl/mm/pfn.cc @@ -75,6 +75,21 @@ MM::Pfn::DecrementAvailablePages(VOID) AvailablePages--; } +/** + * 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() +{ + /* Return the highest physical page number */ + return HighestPhysicalPage; +} + /** * Retrieves the total number of physical pages managed by the system. * @@ -150,6 +165,246 @@ MM::Pfn::IncrementAvailablePages(VOID) 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; + + /* 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(); +} + +/** + * 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(PFN_NUMBER PageFrameIndex, + 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->u1.WsIndex = 0; + Pfn->u2.ShareCount++; + Pfn->PteAddress = PointerPte; + Pfn->OriginalPte = *PointerPte; + Pfn->u3.e1.PageLocation = ActiveAndValid; + Pfn->u3.e1.CacheAttribute = PfnNonCached; + 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++; +} + +/** + * 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 */ + UNIMPLEMENTED; + 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.e2.ReferenceCount = 0; + Pfn->u3.e1.Rom = 1; + 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. * diff --git a/xtoskrnl/mm/pte.cc b/xtoskrnl/mm/pte.cc index a2a3f6f..c6a181d 100644 --- a/xtoskrnl/mm/pte.cc +++ b/xtoskrnl/mm/pte.cc @@ -104,6 +104,21 @@ MM::Pte::GetPtesPerPage(VOID) return MM_PAGE_SIZE / MM::Paging::GetPteSize(); } +/** + * Returns a pointer to the valid PTE. + * + * @return This routine returns a pointer to the valid PTE. + * + * @since XT 1.0 + */ +XTAPI +PMMPTE +MM::Pte::GetValidPte() +{ + /* Return a pointer to the valid PTE */ + return &ValidPte; +} + /** * Formats a range of PTEs into a freelist-based pool for system allocations. *