mirror of
https://github.com/reactos/reactos.git
synced 2024-09-11 13:23:51 +00:00
425 lines
12 KiB
C
425 lines
12 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
* FILE: ntoskrnl/mm/ARM3/syspte.c
|
|
* PURPOSE: ARM Memory Manager System PTE Allocator
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#line 15 "ARM³::SYSPTE"
|
|
#define MODULE_INVOLVED_IN_ARM3
|
|
#include "../ARM3/miarm.h"
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
PMMPTE MmSystemPteBase;
|
|
PMMPTE MmSystemPtesStart[MaximumPtePoolTypes];
|
|
PMMPTE MmSystemPtesEnd[MaximumPtePoolTypes];
|
|
MMPTE MmFirstFreeSystemPte[MaximumPtePoolTypes];
|
|
ULONG MmTotalFreeSystemPtes[MaximumPtePoolTypes];
|
|
ULONG MmTotalSystemPtes;
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
PMMPTE
|
|
NTAPI
|
|
MiReserveAlignedSystemPtes(IN ULONG NumberOfPtes,
|
|
IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType,
|
|
IN ULONG Alignment)
|
|
{
|
|
KIRQL OldIrql;
|
|
PMMPTE PointerPte, NextPte, PreviousPte;
|
|
ULONG_PTR ClusterSize;
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
ASSERT(Alignment <= PAGE_SIZE);
|
|
|
|
//
|
|
// Lock the system PTE space
|
|
//
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock);
|
|
|
|
//
|
|
// Get the first free cluster and make sure we have PTEs available
|
|
//
|
|
PointerPte = &MmFirstFreeSystemPte[SystemPtePoolType];
|
|
if (PointerPte->u.List.NextEntry == ((ULONG)0xFFFFF))
|
|
{
|
|
//
|
|
// Fail
|
|
//
|
|
KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Now move to the first free system PTE cluster
|
|
//
|
|
PreviousPte = PointerPte;
|
|
PointerPte = MmSystemPteBase + PointerPte->u.List.NextEntry;
|
|
|
|
//
|
|
// Loop each cluster
|
|
//
|
|
while (TRUE)
|
|
{
|
|
//
|
|
// Check if we're done to only one PTE left
|
|
//
|
|
if (!PointerPte->u.List.OneEntry)
|
|
{
|
|
//
|
|
// Keep track of the next cluster in case we have to relink
|
|
//
|
|
NextPte = PointerPte + 1;
|
|
|
|
//
|
|
// Can this cluster satisfy the request?
|
|
//
|
|
ClusterSize = (ULONG_PTR)NextPte->u.List.NextEntry;
|
|
if (NumberOfPtes < ClusterSize)
|
|
{
|
|
//
|
|
// It can, and it will leave just one PTE left
|
|
//
|
|
if ((ClusterSize - NumberOfPtes) == 1)
|
|
{
|
|
//
|
|
// This cluster becomes a single system PTE entry
|
|
//
|
|
PointerPte->u.List.OneEntry = 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise, the next cluster aborbs what's left
|
|
//
|
|
NextPte->u.List.NextEntry = ClusterSize - NumberOfPtes;
|
|
}
|
|
|
|
//
|
|
// Decrement the free count and move to the next starting PTE
|
|
//
|
|
MmTotalFreeSystemPtes[SystemPtePoolType] -= NumberOfPtes;
|
|
PointerPte += (ClusterSize - NumberOfPtes);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Did we find exactly what you wanted?
|
|
//
|
|
if (NumberOfPtes == ClusterSize)
|
|
{
|
|
//
|
|
// Yes, fixup the cluster and decrease free system PTE count
|
|
//
|
|
PreviousPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
|
|
MmTotalFreeSystemPtes[SystemPtePoolType] -= NumberOfPtes;
|
|
break;
|
|
}
|
|
}
|
|
else if (NumberOfPtes == 1)
|
|
{
|
|
//
|
|
// We have one PTE in this cluster, and it's all you want
|
|
//
|
|
PreviousPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
|
|
MmTotalFreeSystemPtes[SystemPtePoolType]--;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We couldn't find what you wanted -- is this the last cluster?
|
|
//
|
|
if (PointerPte->u.List.NextEntry == ((ULONG)0xFFFFF))
|
|
{
|
|
//
|
|
// Fail
|
|
//
|
|
KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Go to the next cluster
|
|
//
|
|
PreviousPte = PointerPte;
|
|
PointerPte = MmSystemPteBase + PointerPte->u.List.NextEntry;
|
|
ASSERT(PointerPte > PreviousPte);
|
|
}
|
|
|
|
//
|
|
// Release the lock, flush the TLB and return the first PTE
|
|
//
|
|
KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
|
|
KeFlushProcessTb();
|
|
return PointerPte;
|
|
}
|
|
|
|
PMMPTE
|
|
NTAPI
|
|
MiReserveSystemPtes(IN ULONG NumberOfPtes,
|
|
IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType)
|
|
{
|
|
PMMPTE PointerPte;
|
|
|
|
//
|
|
// Use the extended function
|
|
//
|
|
PointerPte = MiReserveAlignedSystemPtes(NumberOfPtes, SystemPtePoolType, 0);
|
|
|
|
//
|
|
// Check if allocation failed
|
|
//
|
|
if (!PointerPte)
|
|
{
|
|
//
|
|
// Warn that we are out of memory
|
|
//
|
|
DPRINT1("MiReserveSystemPtes: Failed to reserve %lu PTE(s)!\n", NumberOfPtes);
|
|
}
|
|
|
|
//
|
|
// Return the PTE Pointer
|
|
//
|
|
return PointerPte;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiReleaseSystemPtes(IN PMMPTE StartingPte,
|
|
IN ULONG NumberOfPtes,
|
|
IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType)
|
|
{
|
|
KIRQL OldIrql;
|
|
ULONG_PTR ClusterSize, CurrentSize;
|
|
PMMPTE CurrentPte, NextPte, PointerPte;
|
|
|
|
//
|
|
// Check to make sure the PTE address is within bounds
|
|
//
|
|
ASSERT(NumberOfPtes != 0);
|
|
ASSERT(StartingPte >= MmSystemPtesStart[SystemPtePoolType]);
|
|
ASSERT(StartingPte <= MmSystemPtesEnd[SystemPtePoolType]);
|
|
|
|
//
|
|
// Zero PTEs
|
|
//
|
|
RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE));
|
|
CurrentSize = (ULONG_PTR)(StartingPte - MmSystemPteBase);
|
|
|
|
//
|
|
// Acquire the system PTE lock
|
|
//
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueSystemSpaceLock);
|
|
|
|
//
|
|
// Increase availability
|
|
//
|
|
MmTotalFreeSystemPtes[SystemPtePoolType] += NumberOfPtes;
|
|
|
|
//
|
|
// Get the free cluster and start going through them
|
|
//
|
|
CurrentPte = &MmFirstFreeSystemPte[SystemPtePoolType];
|
|
while (TRUE)
|
|
{
|
|
//
|
|
// Get the first real cluster of PTEs and check if it's ours
|
|
//
|
|
PointerPte = MmSystemPteBase + CurrentPte->u.List.NextEntry;
|
|
if (CurrentSize < CurrentPte->u.List.NextEntry)
|
|
{
|
|
//
|
|
// Sanity check
|
|
//
|
|
ASSERT(((StartingPte + NumberOfPtes) <= PointerPte) ||
|
|
(CurrentPte->u.List.NextEntry == ((ULONG)0xFFFFF)));
|
|
|
|
//
|
|
// Get the next cluster in case it's the one
|
|
//
|
|
NextPte = CurrentPte + 1;
|
|
|
|
//
|
|
// Check if this was actually a single-PTE entry
|
|
//
|
|
if (CurrentPte->u.List.OneEntry)
|
|
{
|
|
//
|
|
// We only have one page
|
|
//
|
|
ClusterSize = 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The next cluster will have the page count
|
|
//
|
|
ClusterSize = (ULONG_PTR)NextPte->u.List.NextEntry;
|
|
}
|
|
|
|
//
|
|
// So check if this cluster actually describes the entire mapping
|
|
//
|
|
if ((CurrentPte + ClusterSize) == StartingPte)
|
|
{
|
|
//
|
|
// It does -- collapse the free PTEs into the next cluster
|
|
//
|
|
NumberOfPtes += ClusterSize;
|
|
NextPte->u.List.NextEntry = NumberOfPtes;
|
|
CurrentPte->u.List.OneEntry = 0;
|
|
|
|
//
|
|
// Make another pass
|
|
//
|
|
StartingPte = CurrentPte;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There's still PTEs left -- make us into a cluster
|
|
//
|
|
StartingPte->u.List.NextEntry = CurrentPte->u.List.NextEntry;
|
|
CurrentPte->u.List.NextEntry = CurrentSize;
|
|
|
|
//
|
|
// Is there just one page left?
|
|
//
|
|
if (NumberOfPtes == 1)
|
|
{
|
|
//
|
|
// Then this actually becomes a single PTE entry
|
|
//
|
|
StartingPte->u.List.OneEntry = 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise, create a new cluster for the remaining pages
|
|
//
|
|
StartingPte->u.List.OneEntry = 0;
|
|
NextPte = StartingPte + 1;
|
|
NextPte->u.List.NextEntry = NumberOfPtes;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now check if we've arrived at yet another cluster
|
|
//
|
|
if ((StartingPte + NumberOfPtes) == PointerPte)
|
|
{
|
|
//
|
|
// We'll collapse the next cluster into us
|
|
//
|
|
StartingPte->u.List.NextEntry = PointerPte->u.List.NextEntry;
|
|
StartingPte->u.List.OneEntry = 0;
|
|
NextPte = StartingPte + 1;
|
|
|
|
//
|
|
// Check if the cluster only had one page
|
|
//
|
|
if (PointerPte->u.List.OneEntry)
|
|
{
|
|
//
|
|
// So will we...
|
|
//
|
|
ClusterSize = 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise, grab the page count from the next-next cluster
|
|
//
|
|
PointerPte++;
|
|
ClusterSize = (ULONG_PTR)PointerPte->u.List.NextEntry;
|
|
}
|
|
|
|
//
|
|
// And create the final combined cluster
|
|
//
|
|
NextPte->u.List.NextEntry = NumberOfPtes + ClusterSize;
|
|
}
|
|
|
|
//
|
|
// We released the PTEs into their cluster (and optimized the list)
|
|
//
|
|
KeReleaseQueuedSpinLock(LockQueueSystemSpaceLock, OldIrql);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Try the next cluster of PTEs...
|
|
//
|
|
CurrentPte = PointerPte;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiInitializeSystemPtes(IN PMMPTE StartingPte,
|
|
IN ULONG NumberOfPtes,
|
|
IN MMSYSTEM_PTE_POOL_TYPE PoolType)
|
|
{
|
|
//
|
|
// Sanity checks
|
|
//
|
|
ASSERT(NumberOfPtes >= 1);
|
|
|
|
//
|
|
// Set the starting and ending PTE addresses for this space
|
|
//
|
|
MmSystemPteBase = (PVOID)PTE_BASE;
|
|
MmSystemPtesStart[PoolType] = StartingPte;
|
|
MmSystemPtesEnd[PoolType] = StartingPte + NumberOfPtes - 1;
|
|
DPRINT("System PTE space for %d starting at: %p and ending at: %p\n",
|
|
PoolType, MmSystemPtesStart[PoolType], MmSystemPtesEnd[PoolType]);
|
|
|
|
//
|
|
// Clear all the PTEs to start with
|
|
//
|
|
RtlZeroMemory(StartingPte, NumberOfPtes * sizeof(MMPTE));
|
|
|
|
//
|
|
// Make the first entry free and link it
|
|
//
|
|
StartingPte->u.List.NextEntry = ((ULONG)0xFFFFF);
|
|
MmFirstFreeSystemPte[PoolType].u.Long = 0;
|
|
MmFirstFreeSystemPte[PoolType].u.List.NextEntry = StartingPte -
|
|
MmSystemPteBase;
|
|
|
|
//
|
|
// The second entry stores the size of this PTE space
|
|
//
|
|
StartingPte++;
|
|
StartingPte->u.Long = 0;
|
|
StartingPte->u.List.NextEntry = NumberOfPtes;
|
|
|
|
//
|
|
// We also keep a global for it
|
|
//
|
|
MmTotalFreeSystemPtes[PoolType] = NumberOfPtes;
|
|
|
|
//
|
|
// Check if this is the system PTE space
|
|
//
|
|
if (PoolType == SystemPteSpace)
|
|
{
|
|
//
|
|
// Remember how many PTEs we have
|
|
//
|
|
MmTotalSystemPtes = NumberOfPtes;
|
|
}
|
|
}
|
|
|
|
/* EOF */
|