367 lines
9.8 KiB
C
Raw Permalink Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 1989-2002 Microsoft Corporation
Module Name:
sysptes.c
Abstract:
This module implements the functions to manipulate the system PTE space.
These PTES are used to map kernel stacks, pool pages, and I/O devices.
--*/
#include "mi.h"
//
// Define a macro to access the size for free blocks larger than one entry.
//
#define MI_PTE_LIST_LARGE_BLOCK_SIZE(ppte) (((ppte)+1)->List.NextEntry)
//
// Define a macro to get the next entry from a PTE list.
//
#define MI_PTE_LIST_NEXT_ENTRY(ppte) ((PMMPTE)((ppte)->Long & MM_PTE_NEXT_ENTRY_MASK))
//
// Define a macro to encode the next entry value for the supplied PTE.
//
#define MI_PTE_LIST_ENCODE_NEXT_ENTRY(ppte) (((ULONG)(ppte)) >> 2)
//
// PTE range starting at MM_SYSTEM_PTE_BASE.
//
MMPTERANGE MmSystemPteRange = {
{ MM_EMPTY_PTE_LIST << 2 },
MiGetPteAddress(MM_SYSTEM_PTE_BASE),
MiGetPteAddress(MM_SYSTEM_PTE_BASE),
MiGetPteAddress(MM_SYSTEM_PTE_END + 1),
&MmAvailablePages,
MiRemoveZeroPage
};
#ifdef DEVKIT
//
// PTE range starting at MM_DEVKIT_PTE_BASE.
//
// To allow consoles with only 64 megabytes to still be able to load the debug
// monitor, fall back on using the retail allocators.
//
// Arcade retail and DEVKIT units have the same amount of memory, so always use
// the retail allocators.
//
MMPTERANGE MmDeveloperKitPteRange = {
{ MM_EMPTY_PTE_LIST << 2 },
MiGetPteAddress(MM_DEVKIT_PTE_BASE),
MiGetPteAddress(MM_DEVKIT_PTE_BASE),
MiGetPteAddress(MM_DEVKIT_PTE_END + 1),
#ifdef ARCADE
&MmAvailablePages,
MiRemoveZeroPage
#else
&MmDeveloperKitPfnRegion.AvailablePages,
MiRemoveDebuggerPage
#endif
};
#endif
PMMPTE
MiReserveSystemPtes(
IN PMMPTERANGE PteRange,
IN PFN_COUNT NumberOfPtes
)
/*++
Routine Description:
This routine allocates the specified number of contiguous PTE slots from the
system page tables.
Arguments:
PteRange - Supplies the PTE range to allocate the PTEs from.
NumberOfPtes - Supplies the number of contiguous PTE slots to allocate.
Return Value:
Returns a pointer to the first allocated PTE.
Environment:
Kernel mode.
--*/
{
KIRQL OldIrql;
PMMPTE LastPointerPte;
PMMPTE PointerPte;
PFN_COUNT ExtraPtes;
PFN_COUNT PtesToCommit;
PFN_COUNT NumberOfPtesInBlock;
MMPTE TempPte;
PFN_COUNT PtesCommitted;
PMMPTE PointerPde;
ASSERT(NumberOfPtes != 0);
MI_LOCK_MM(&OldIrql);
//
// Scan the free list for a block that can satisfy the requested size.
//
RetryOperation:
LastPointerPte = &PteRange->HeadPte;
while (LastPointerPte->List.NextEntry != MM_EMPTY_PTE_LIST) {
PointerPte = MI_PTE_LIST_NEXT_ENTRY(LastPointerPte);
//
// Verify that the list is ordered by free address.
//
if (LastPointerPte != &PteRange->HeadPte) {
ASSERT(PointerPte > LastPointerPte);
}
if (PointerPte->List.OneEntry) {
if (NumberOfPtes == 1) {
LastPointerPte->List.NextEntry = PointerPte->List.NextEntry;
goto ReturnPointerPte;
}
} else if (MI_PTE_LIST_LARGE_BLOCK_SIZE(PointerPte) == NumberOfPtes) {
//
// A single PTE can't be encoded in a format where Link.OneEntry is
// clear.
//
ASSERT(NumberOfPtes != 1);
LastPointerPte->List.NextEntry = PointerPte->List.NextEntry;
goto ReturnPointerPte;
} else if (MI_PTE_LIST_LARGE_BLOCK_SIZE(PointerPte) > NumberOfPtes) {
//
// The block has more PTEs that we need so shuffle the free count
// into the next PTE or convert the PTE to the single PTE format.
//
ExtraPtes = MI_PTE_LIST_LARGE_BLOCK_SIZE(PointerPte) - NumberOfPtes;
if (ExtraPtes > 1) {
PointerPte[NumberOfPtes].List.OneEntry = 0;
MI_PTE_LIST_LARGE_BLOCK_SIZE(&PointerPte[NumberOfPtes]) = ExtraPtes;
} else {
PointerPte[NumberOfPtes].List.OneEntry = 1;
}
PointerPte[NumberOfPtes].List.NextEntry = PointerPte->List.NextEntry;
LastPointerPte->List.NextEntry += NumberOfPtes;
goto ReturnPointerPte;
}
LastPointerPte = PointerPte;
}
//
// The PTE list doesn't have a block that can satisfy the request. See how
// many PTEs we'll need to commit to make enough space.
//
PtesToCommit = NumberOfPtes;
if (LastPointerPte != &PteRange->HeadPte) {
NumberOfPtesInBlock = LastPointerPte->List.OneEntry ? 1 :
MI_PTE_LIST_LARGE_BLOCK_SIZE(LastPointerPte);
if (LastPointerPte + NumberOfPtesInBlock == PteRange->LastCommittedPte) {
PtesToCommit -= NumberOfPtesInBlock;
}
}
PtesToCommit = MI_ROUND_TO_SIZE(PtesToCommit, PTE_PER_PAGE);
ASSERT(PtesToCommit > 0);
//
// Commit the additional page tables if there's enough virtual address space
// to hold them.
//
if ((ULONG)(PteRange->LastReservedPte - PteRange->LastCommittedPte) >
PtesToCommit) {
if (*PteRange->AvailablePages >= (PtesToCommit / PTE_PER_PAGE)) {
PointerPte = PteRange->LastCommittedPte;
for (PtesCommitted = 0; PtesCommitted < PtesToCommit;
PtesCommitted += PTE_PER_PAGE) {
PointerPde = MiGetPteAddress(PteRange->LastCommittedPte);
TempPte.Long = MiGetValidKernelPdeBits();
TempPte.Hard.PageFrameNumber =
PteRange->RemovePageRoutine(MmSystemPageTableUsage, PointerPde);
MI_WRITE_PTE(PointerPde, TempPte);
PteRange->LastCommittedPte += PTE_PER_PAGE;
}
MiReleaseSystemPtes(PteRange, PointerPte, PtesToCommit);
goto RetryOperation;
}
}
PointerPte = NULL;
ReturnPointerPte:
MI_UNLOCK_MM(OldIrql);
return PointerPte;
}
VOID
MiReleaseSystemPtes(
IN PMMPTERANGE PteRange,
IN PMMPTE StartingPte,
IN PFN_COUNT NumberOfPtes
)
/*++
Routine Description:
This routine frees the specified number of contiguous PTE slots from the
system page tables.
Arguments:
PteRange - Supplies the PTE range to free the PTEs to.
StartingPte - Supplies the pointer to the first PTE to free.
NumberOfPtes - Supplies the number of contiguous PTE slots to fre.
Return Value:
None.
Environment:
Kernel mode.
--*/
{
KIRQL OldIrql;
PMMPTE LastPointerPte;
PMMPTE PointerPte;
PFN_COUNT NumberOfPtesInBlock;
ASSERT(NumberOfPtes != 0);
ASSERT(StartingPte >= PteRange->FirstCommittedPte);
ASSERT(StartingPte + NumberOfPtes <= PteRange->LastCommittedPte);
MI_LOCK_MM(&OldIrql);
//
// Zero the PTEs. The caller is responsible for deleting any pages attached
// to these PTEs and flushing the TLB.
//
RtlFillMemoryUlong(StartingPte, NumberOfPtes * sizeof(MMPTE), 0);
//
// Scan the free list for the position to insert the PTE block.
//
LastPointerPte = &PteRange->HeadPte;
while (LastPointerPte->List.NextEntry != MM_EMPTY_PTE_LIST) {
PointerPte = MI_PTE_LIST_NEXT_ENTRY(LastPointerPte);
//
// Verify that the list is ordered by free address.
//
if (LastPointerPte != &PteRange->HeadPte) {
ASSERT(PointerPte > LastPointerPte);
}
if (PointerPte > StartingPte) {
//
// Verify that the end of the block we're freeing up doesn't overlap
// the start of the current block.
//
ASSERT(PointerPte >= StartingPte + NumberOfPtes);
//
// If the current block is contiguous with the block we're freeing
// up, then combine the two blocks.
//
if (StartingPte + NumberOfPtes == PointerPte) {
NumberOfPtesInBlock = PointerPte->List.OneEntry ? 1 :
MI_PTE_LIST_LARGE_BLOCK_SIZE(PointerPte);
NumberOfPtes += NumberOfPtesInBlock;
LastPointerPte->List.NextEntry = PointerPte->List.NextEntry;
}
break;
}
//
// Verify that the end of the current block doesn't overlap the start of
// the block we're freeing up.
//
ASSERT((PointerPte + (PointerPte->List.OneEntry ? 1 :
MI_PTE_LIST_LARGE_BLOCK_SIZE(PointerPte))) <= StartingPte);
LastPointerPte = PointerPte;
}
//
// Set the number of PTEs in the freed block and link it into the free list.
//
if (NumberOfPtes > 1) {
StartingPte->List.OneEntry = 0;
StartingPte[1].List.NextEntry = NumberOfPtes;
} else {
StartingPte->List.OneEntry = 1;
}
StartingPte->List.NextEntry = LastPointerPte->List.NextEntry;
LastPointerPte->List.NextEntry = MI_PTE_LIST_ENCODE_NEXT_ENTRY(StartingPte);
//
// If the previous block is contiguous with the block we're freeing up,
// then combine the two blocks.
//
if (LastPointerPte != &PteRange->HeadPte) {
NumberOfPtesInBlock = LastPointerPte->List.OneEntry ? 1 :
MI_PTE_LIST_LARGE_BLOCK_SIZE(LastPointerPte);
if (LastPointerPte + NumberOfPtesInBlock == StartingPte) {
LastPointerPte->List.OneEntry = 0;
LastPointerPte->List.NextEntry = StartingPte->List.NextEntry;
MI_PTE_LIST_LARGE_BLOCK_SIZE(LastPointerPte) = NumberOfPtes +
NumberOfPtesInBlock;
}
}
MI_UNLOCK_MM(OldIrql);
}