diff --git a/xtoskrnl/includes/mm/amd64/pte.hh b/xtoskrnl/includes/mm/amd64/pte.hh index 891d29a0..ca2932d7 100644 --- a/xtoskrnl/includes/mm/amd64/pte.hh +++ b/xtoskrnl/includes/mm/amd64/pte.hh @@ -18,6 +18,11 @@ namespace MM class Pte { private: + STATIC MMPTE FirstSystemFreePte[MaximumPtePoolTypes]; + STATIC PMMPTE SystemPteBase; + STATIC PMMPTE SystemPtesEnd[MaximumPtePoolTypes]; + STATIC PMMPTE SystemPtesStart[MaximumPtePoolTypes]; + STATIC ULONG TotalSystemFreePtes[MaximumPtePoolTypes]; STATIC MMPTE ValidPte; public: @@ -25,6 +30,7 @@ namespace MM STATIC XTAPI ULONG GetPtesPerPage(VOID); STATIC XTAPI PMMPTE GetSystemPteBaseAddress(VOID); STATIC XTAPI VOID InitializePageTable(VOID); + STATIC XTAPI VOID InitializeSystemPteSpace(VOID); STATIC XTAPI VOID MapP5E(PVOID StartAddress, PVOID EndAddress, PMMP5E TemplateP5e); @@ -40,6 +46,19 @@ namespace MM STATIC XTAPI VOID MapPXE(PVOID StartAddress, PVOID EndAddress, PMMPXE TemplatePxe); + STATIC XTAPI PMMPTE ReserveSystemPtes(IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType, + IN ULONG Alignment); + + private: + STATIC XTAPI BOOLEAN FindFreeCluster(IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType, + OUT PMMPTE *FoundCluster, + OUT PMMPTE *PreviousClusterNode); + STATIC XTAPI ULONG GetClusterSize(IN PMMPTE Pte); + STATIC XTAPI VOID InitializeSystemPtePool(IN PMMPTE StartingPte, + IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE PoolType); }; } diff --git a/xtoskrnl/includes/mm/i686/pte.hh b/xtoskrnl/includes/mm/i686/pte.hh index de7cdf74..9bf46613 100644 --- a/xtoskrnl/includes/mm/i686/pte.hh +++ b/xtoskrnl/includes/mm/i686/pte.hh @@ -18,6 +18,11 @@ namespace MM class Pte { private: + STATIC MMPTE FirstSystemFreePte[MaximumPtePoolTypes]; + STATIC PMMPTE SystemPteBase; + STATIC PMMPTE SystemPtesEnd[MaximumPtePoolTypes]; + STATIC PMMPTE SystemPtesStart[MaximumPtePoolTypes]; + STATIC ULONG TotalSystemFreePtes[MaximumPtePoolTypes]; STATIC MMPTE ValidPte; public: @@ -25,6 +30,7 @@ namespace MM STATIC XTAPI ULONG GetPtesPerPage(VOID); STATIC XTAPI PMMPTE GetSystemPteBaseAddress(VOID); STATIC XTAPI VOID InitializePageTable(VOID); + STATIC XTAPI VOID InitializeSystemPteSpace(VOID); STATIC XTAPI VOID MapPDE(PVOID StartAddress, PVOID EndAddress, PMMPDE TemplatePde); @@ -34,6 +40,19 @@ namespace MM STATIC XTAPI VOID MapPTE(PVOID StartAddress, PVOID EndAddress, PMMPTE TemplatePte); + STATIC XTAPI PMMPTE ReserveSystemPtes(IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType, + IN ULONG Alignment); + + private: + STATIC XTAPI BOOLEAN FindFreeCluster(IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType, + OUT PMMPTE *FoundCluster, + OUT PMMPTE *PreviousClusterNode); + STATIC XTAPI ULONG GetClusterSize(IN PMMPTE Pte); + STATIC XTAPI VOID InitializeSystemPtePool(IN PMMPTE StartingPte, + IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE PoolType); }; } diff --git a/xtoskrnl/mm/data.cc b/xtoskrnl/mm/data.cc index 8124fe3b..be173b1a 100644 --- a/xtoskrnl/mm/data.cc +++ b/xtoskrnl/mm/data.cc @@ -57,5 +57,20 @@ LOADER_MEMORY_DESCRIPTOR MM::Pfn::OriginalFreeDescriptor; /* Size of the PFN database in pages */ PFN_NUMBER MM::Pfn::PfnDatabaseSize; +/* Array of lists for available System PTEs, separated by pool type */ +MMPTE MM::Pte::FirstSystemFreePte[MaximumPtePoolTypes]; + +/* Virtual base address of the System PTE space */ +PMMPTE MM::Pte::SystemPteBase; + +/* End addresses for the System PTE ranges */ +PMMPTE MM::Pte::SystemPtesEnd[MaximumPtePoolTypes]; + +/* Start addresses for the System PTE ranges */ +PMMPTE MM::Pte::SystemPtesStart[MaximumPtePoolTypes]; + +/* Total count of available System PTEs */ +ULONG MM::Pte::TotalSystemFreePtes[MaximumPtePoolTypes]; + /* Template PTE entry containing standard flags for a valid, present kernel page */ MMPTE MM::Pte::ValidPte = {MM_PTE_VALID|MM_PTE_EXECUTE_READWRITE|MM_PTE_DIRTY|MM_PTE_ACCESSED}; diff --git a/xtoskrnl/mm/mmgr.cc b/xtoskrnl/mm/mmgr.cc index c305c04e..a8a4dbf3 100644 --- a/xtoskrnl/mm/mmgr.cc +++ b/xtoskrnl/mm/mmgr.cc @@ -65,6 +65,9 @@ MM::Manager::InitializeMemoryManager(VOID) /* Initialize page table */ MM::Pte::InitializePageTable(); + + /* Initialize system PTE space */ + MM::Pte::InitializeSystemPteSpace(); } /** diff --git a/xtoskrnl/mm/pte.cc b/xtoskrnl/mm/pte.cc index 9874b2af..a2a3f6f5 100644 --- a/xtoskrnl/mm/pte.cc +++ b/xtoskrnl/mm/pte.cc @@ -9,6 +9,86 @@ #include +/** + * Finds a free cluster of system PTEs that can satisfy a given size. + * + * @param NumberOfPtes + * The number of contiguous PTEs required. + * + * @param SystemPtePoolType + * Specifies the system PTE pool to search within. + * + * @param FoundCluster + * On success, receives a pointer to the first PTE of the found cluster. + * + * @param PreviousClusterNode + * On success, receives a pointer to the list node that precedes the found cluster. + * + * @return This routine returns TRUE if a suitable cluster was found, FALSE otherwise. + * + * @since XT 1.0 + */ +XTAPI +BOOLEAN +MM::Pte::FindFreeCluster(IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType, + OUT PMMPTE *FoundCluster, + OUT PMMPTE *PreviousClusterNode) +{ + PMMPTE CurrentCluster; + PMMPTE PreviousNode = &FirstSystemFreePte[SystemPtePoolType]; + ULONG ClusterSize; + + /* Find a free PTE cluster large enough for the request */ + while(MM::Paging::GetNextEntry(PreviousNode) != MAXULONG) + { + /* Retrieve the cluster and its size */ + CurrentCluster = MM::Paging::AdvancePte(SystemPteBase, MM::Paging::GetNextEntry(PreviousNode)); + ClusterSize = GetClusterSize(CurrentCluster); + + /* Check if this cluster is large enough */ + if(NumberOfPtes <= ClusterSize) + { + /* Found a suitable cluster */ + *FoundCluster = CurrentCluster; + *PreviousClusterNode = PreviousNode; + return TRUE; + } + + /* This cluster is too small, check the next one */ + PreviousNode = CurrentCluster; + } + + /* No suitable cluster was found */ + return FALSE; +} + +/** + * Decodes and returns the size of a free PTE cluster. + * + * @param Pte + * Supplies a pointer to the first PTE of the free cluster to inspect. + * + * @return This routine returns the total number of contiguous PTEs in the free cluster. + * + * @since XT 1.0 + */ +XTAPI +ULONG +MM::Pte::GetClusterSize(IN PMMPTE Pte) +{ + /* A special flag in the first PTE indicates a free cluster of size one */ + if(MM::Paging::GetOneEntry(Pte)) + { + /* Flag is set, so the cluster size is 1 by definition */ + return 1; + } + + /* For larger clusters, the size is encoded in the second PTE of the block */ + Pte = MM::Paging::GetNextPte(Pte); + return MM::Paging::GetNextEntry(Pte); +} + /** * Calculates the number of Page Table Entries (PTEs) that fit within a single page. * @@ -24,6 +104,86 @@ MM::Pte::GetPtesPerPage(VOID) return MM_PAGE_SIZE / MM::Paging::GetPteSize(); } +/** + * Formats a range of PTEs into a freelist-based pool for system allocations. + * + * @param StartingPte + * Supplies a pointer to the start of the PTE range to be formatted. + * + * @param NumberOfPtes + * Supplies the total number of PTEs in the contiguous range. + * + * @param PoolType + * The system PTE pool type that this range will be used for. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +MM::Pte::InitializeSystemPtePool(IN PMMPTE StartingPte, + IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE PoolType) +{ + /* Set the system PTE base address */ + SystemPteBase = GetSystemPteBaseAddress(); + + /* Record the boundaries of this new PTE pool */ + SystemPtesStart[PoolType] = StartingPte; + SystemPtesEnd[PoolType] = MM::Paging::AdvancePte(StartingPte, NumberOfPtes - 1); + + /* Zero the memory for the new PTE pool before use */ + RTL::Memory::ZeroMemory(StartingPte, NumberOfPtes * MM::Paging::GetPteSize()); + + /* Build the free list head to point to the start of the pool */ + MM::Paging::SetNextEntry(StartingPte, MAXULONG); + MM::Paging::ClearPte(&FirstSystemFreePte[PoolType]); + MM::Paging::SetNextEntry(&FirstSystemFreePte[PoolType], MM::Paging::GetPteDistance(StartingPte, SystemPteBase)); + + /* Use the second PTE slot to store the total size of this pool */ + StartingPte = MM::Paging::GetNextPte(StartingPte); + MM::Paging::ClearPte(StartingPte); + MM::Paging::SetNextEntry(StartingPte, NumberOfPtes); + + /* Record the total number of free PTEs in this pool */ + TotalSystemFreePtes[PoolType] = NumberOfPtes; +} + +/** + * Sets up the entire system PTE address space. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +MM::Pte::InitializeSystemPteSpace(VOID) +{ + PMMPTE PointerPte; + PMMPTE FirstZeroingPte; + PMMMEMORY_LAYOUT MemoryLayout; + + /* Retrieve the system's memory layout */ + MemoryLayout = MM::Manager::GetMemoryLayout(); + + /* Map the page table hierarchy for the entire system PTE space */ + MM::Pte::MapPPE(MemoryLayout->SystemSpaceStart, MemoryLayout->SystemSpaceEnd, &ValidPte); + MM::Pte::MapPDE(MemoryLayout->SystemSpaceStart, MemoryLayout->SystemSpaceEnd, &ValidPte); + + /* Format the main block of system PTEs into a free list pool */ + PointerPte = MM::Paging::GetPteAddress(MemoryLayout->SystemSpaceStart); + InitializeSystemPtePool(PointerPte, MM::Manager::GetNumberOfSystemPtes(), SystemPteSpace); + + /* Reserve and zero a dedicated block of system PTEs */ + FirstZeroingPte = ReserveSystemPtes(MM_RESERVED_ZERO_PTES + 1, SystemPteSpace, 0); + RTL::Memory::ZeroMemory(FirstZeroingPte, (MM_RESERVED_ZERO_PTES + 1) * MM::Paging::GetPteSize()); + + /* Use the first PTE of this block as a counter for available zeroing PTEs */ + MM::Paging::SetPte(FirstZeroingPte, MM_RESERVED_ZERO_PTES, 0); +} + /** * Maps a range of virtual addresses at the PDE (Page Directory Entry) level. * @@ -117,3 +277,119 @@ MM::Pte::MapPTE(PVOID StartAddress, PointerPte = MM::Paging::GetNextPte(PointerPte); } } + +/** + * Reserves a contiguous block of system PTEs from a specified pool. + * + * @param NumberOfPtes + * The number of contiguous PTEs to reserve. + * + * @param SystemPtePoolType + * Specifies the system PTE pool from which to allocate. + * + * @param Alignment + * This parameter is currently unused. + * + * @return This routine returns a pointer to the beginning of the reserved block, + * or NULLPTR if not enough contiguous PTEs are available. + * + * @since XT 1.0 + */ +XTAPI +PMMPTE +MM::Pte::ReserveSystemPtes(IN ULONG NumberOfPtes, + IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType, + IN ULONG Alignment) +{ + PMMPTE PreviousPte, NextPte, ReservedPte; + ULONG ClusterSize; + + /* Raise runlevel and acquire lock to protect the PTE pool */ + KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL); + KE::QueuedSpinLockGuard SpinLock(SystemSpaceLock); + + /* Find a free PTE cluster large enough for the request */ + if(!FindFreeCluster(NumberOfPtes, SystemPtePoolType, &NextPte, &PreviousPte)) + { + /* Out of system PTEs for this pool, return NULLPTR */ + return NULLPTR; + } + + /* We have the cluster, now get its size for the allocation logic below */ + ClusterSize = GetClusterSize(NextPte); + + /* Unlink the found cluster from the free list for processing */ + MM::Paging::SetNextEntry(PreviousPte, MM::Paging::GetNextEntry(NextPte)); + + /* Handle the allocation based on whether the cluster size is an exact match */ + if(ClusterSize == NumberOfPtes) + { + /* Exact match, allocate the entire cluster */ + ReservedPte = NextPte; + + /* Handle metadata cleanup for a single-PTE cluster */ + if(MM::Paging::GetOneEntry(NextPte)) + { + /* Clear the PTE that held the list metadata */ + MM::Paging::ClearPte(NextPte); + NextPte = MM::Paging::GetNextPte(NextPte); + } + + /* Clear the PTE that held the cluster size */ + MM::Paging::ClearPte(NextPte); + } + else + { + /* Cluster is larger than needed, so it will be split */ + ClusterSize -= NumberOfPtes; + ReservedPte = MM::Paging::AdvancePte(NextPte, ClusterSize); + + /* Update metadata for the new, smaller leftover cluster */ + if(ClusterSize == 1) + { + /* The leftover fragment is a single PTE */ + MM::Paging::SetOneEntry(NextPte, 1); + MM::Paging::ClearPte(ReservedPte); + } + else + { + /* The leftover fragment is larger than one PTE */ + NextPte = MM::Paging::GetNextPte(NextPte); + MM::Paging::SetNextEntry(NextPte, ClusterSize); + } + + /* Find the correct sorted position to re-insert the leftover fragment */ + PreviousPte = &FirstSystemFreePte[SystemPtePoolType]; + while(MM::Paging::GetNextEntry(PreviousPte) != MAXULONG) + { + /* Get the next free cluster to check its size */ + NextPte = MM::Paging::AdvancePte(SystemPteBase, MM::Paging::GetNextEntry(PreviousPte)); + + /* Check if the leftover fragment should be inserted here */ + if(ClusterSize <= GetClusterSize(NextPte)) + { + /* Found the correct sorted position */ + break; + } + + /* Advance to the next entry */ + PreviousPte = NextPte; + } + + /* Get a pointer to the start of the leftover fragment */ + NextPte = MM::Paging::AdvancePte(ReservedPte, -ClusterSize); + + /* Insert the leftover fragment back into the free list at its sorted position */ + MM::Paging::SetNextEntry(NextPte, MM::Paging::GetNextEntry(PreviousPte)); + MM::Paging::SetNextEntry(PreviousPte, MM::Paging::GetPteDistance(NextPte, SystemPteBase)); + } + + /* Decrement the total number of available PTEs in this pool */ + TotalSystemFreePtes[SystemPtePoolType] -= NumberOfPtes; + + /* Flush the TLB to ensure address translation consistency */ + AR::CpuFunc::FlushTlb(); + + /* Return a pointer to the start of the reserved PTE block */ + return ReservedPte; +}