Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6595 lines
185 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
procsup.c
Abstract:
This module contains routines which support the process structure.
Author:
Lou Perazzoli (loup) 25-Apr-1989
Landy Wang (landyw) 02-June-1997
Revision History:
--*/
#include "mi.h"
#if (_MI_PAGING_LEVELS >= 3)
#include "wow64t.h"
#define MI_LARGE_STACK_SIZE KERNEL_LARGE_STACK_SIZE
#if defined(_AMD64_)
#define MM_PROCESS_COMMIT_CHARGE 6
#define MM_PROCESS_CREATE_CHARGE 8
#elif defined(_IA64_)
#define MM_PROCESS_COMMIT_CHARGE 5
#define MM_PROCESS_CREATE_CHARGE 8
#endif
#else
//
// Registry settable but must always be a page multiple and less than
// or equal to KERNEL_LARGE_STACK_SIZE.
//
ULONG MmLargeStackSize = KERNEL_LARGE_STACK_SIZE;
#define MI_LARGE_STACK_SIZE MmLargeStackSize
#if !defined (_X86PAE_)
#define MM_PROCESS_COMMIT_CHARGE 4
#define MM_PROCESS_CREATE_CHARGE 6
#else
#define MM_PROCESS_COMMIT_CHARGE 8
#define MM_PROCESS_CREATE_CHARGE 10
#endif
#endif
#define DONTASSERT(x)
extern ULONG MmProductType;
extern MM_SYSTEMSIZE MmSystemSize;
extern PVOID BBTBuffer;
SIZE_T MmProcessCommit;
ULONG MmKernelStackPages;
PFN_NUMBER MmKernelStackResident;
ULONG MmLargeStacks;
ULONG MmSmallStacks;
MMPTE KernelDemandZeroPte = {MM_KERNEL_DEMAND_ZERO_PTE};
CCHAR MmRotatingUniprocessorNumber;
//
// Enforced minimal commit for user mode stacks
//
ULONG MmMinimumStackCommitInBytes;
PFN_NUMBER
MiMakeOutswappedPageResident (
IN PMMPTE ActualPteAddress,
IN PMMPTE PointerTempPte,
IN ULONG Global,
IN PFN_NUMBER ContainingPage
);
NTSTATUS
MiCreatePebOrTeb (
IN PEPROCESS TargetProcess,
IN ULONG Size,
OUT PVOID *Base
);
VOID
MiDeleteAddressesInWorkingSet (
IN PEPROCESS Process
);
VOID
MiDeleteValidAddress (
IN PVOID Va,
IN PEPROCESS CurrentProcess
);
VOID
MiDeleteFreeVm (
IN PVOID StartingAddress,
IN PVOID EndingAddress
);
VOID
VadTreeWalk (
VOID
);
PMMVAD
MiAllocateVad(
IN ULONG_PTR StartingVirtualAddress,
IN ULONG_PTR EndingVirtualAddress,
IN LOGICAL Deletable
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,MmCreateTeb)
#pragma alloc_text(PAGE,MmCreatePeb)
#pragma alloc_text(PAGE,MiCreatePebOrTeb)
#pragma alloc_text(PAGE,MmDeleteTeb)
#pragma alloc_text(PAGE,MiAllocateVad)
#pragma alloc_text(PAGE,MiDeleteAddressesInWorkingSet)
#pragma alloc_text(PAGE,MmSetMemoryPriorityProcess)
#pragma alloc_text(PAGE,MmInitializeHandBuiltProcess)
#pragma alloc_text(PAGE,MmInitializeHandBuiltProcess2)
#pragma alloc_text(PAGE,MmGetDirectoryFrameFromProcess)
#endif
BOOLEAN
MmCreateProcessAddressSpace (
IN ULONG MinimumWorkingSetSize,
IN PEPROCESS NewProcess,
OUT PULONG_PTR DirectoryTableBase
)
/*++
Routine Description:
This routine creates an address space which maps the system
portion and contains a hyper space entry.
Arguments:
MinimumWorkingSetSize - Supplies the minimum working set size for
this address space. This value is only used
to ensure that ample physical pages exist
to create this process.
NewProcess - Supplies a pointer to the process object being created.
DirectoryTableBase - Returns the value of the newly created
address space's Page Directory (PD) page and
hyper space page.
Return Value:
Returns TRUE if an address space was successfully created, FALSE
if ample physical pages do not exist.
Environment:
Kernel mode. APCs Disabled.
--*/
{
PFN_NUMBER PageDirectoryIndex;
PFN_NUMBER HyperSpaceIndex;
PFN_NUMBER PageContainingWorkingSet;
PFN_NUMBER VadBitMapPage;
MMPTE TempPte;
PEPROCESS CurrentProcess;
KIRQL OldIrql;
PMMPFN Pfn1;
ULONG Color;
PMMPTE PointerPte;
#if (_MI_PAGING_LEVELS >= 4)
PMMPTE PointerPxe;
PFN_NUMBER PageDirectoryParentIndex;
#endif
#if (_MI_PAGING_LEVELS >= 3)
PMMPTE PointerPpe;
PMMPTE PointerPde;
PFN_NUMBER HyperDirectoryIndex;
#endif
#if defined (_X86PAE_)
ULONG MaximumStart;
ULONG TopQuad;
MMPTE TopPte;
PPAE_ENTRY PaeVa;
ULONG i;
ULONG NumberOfPdes;
PFN_NUMBER HyperSpaceIndex2;
PFN_NUMBER PageDirectories[PD_PER_SYSTEM];
#endif
#if !defined (_IA64_)
PMMPTE PointerFillPte;
PMMPTE CurrentAddressSpacePde;
#endif
CurrentProcess = PsGetCurrentProcess ();
//
// Charge commitment for the page directory pages, working set page table
// page, and working set list. If Vad bitmap lookups are enabled, then
// charge for a page or two for that as well.
//
if (MiChargeCommitment (MM_PROCESS_COMMIT_CHARGE, NULL) == FALSE) {
return FALSE;
}
NewProcess->NextPageColor = (USHORT)(RtlRandom(&MmProcessColorSeed));
KeInitializeSpinLock (&NewProcess->HyperSpaceLock);
#if defined (_X86PAE_)
TopQuad = MiPaeAllocate (&PaeVa);
if (TopQuad == 0) {
MiReturnCommitment (MM_PROCESS_COMMIT_CHARGE);
return FALSE;
}
#endif
LOCK_WS (CurrentProcess);
//
// Get the PFN lock to prevent another thread in this
// process from using hyper space and to get physical pages.
//
LOCK_PFN (OldIrql);
//
// Check to make sure the physical pages are available.
//
if (MI_NONPAGABLE_MEMORY_AVAILABLE() <= (SPFN_NUMBER)MinimumWorkingSetSize){
UNLOCK_PFN (OldIrql);
UNLOCK_WS (CurrentProcess);
MiReturnCommitment (MM_PROCESS_COMMIT_CHARGE);
#if defined (_X86PAE_)
MiPaeFree (PaeVa);
#endif
//
// Indicate no directory base was allocated.
//
return FALSE;
}
MM_TRACK_COMMIT (MM_DBG_COMMIT_PROCESS_CREATE, MM_PROCESS_COMMIT_CHARGE);
MmResidentAvailablePages -= MinimumWorkingSetSize;
MM_BUMP_COUNTER(6, MinimumWorkingSetSize);
MmProcessCommit += MM_PROCESS_COMMIT_CHARGE;
ASSERT (NewProcess->AddressSpaceInitialized == 0);
PS_SET_BITS (&NewProcess->Flags, PS_PROCESS_FLAGS_ADDRESS_SPACE1);
ASSERT (NewProcess->AddressSpaceInitialized == 1);
NewProcess->Vm.MinimumWorkingSetSize = MinimumWorkingSetSize;
//
// Allocate a page directory (parent for 64-bit systems) page.
//
MiEnsureAvailablePageOrWait (CurrentProcess, NULL);
Color = MI_PAGE_COLOR_PTE_PROCESS (PDE_BASE,
&CurrentProcess->NextPageColor);
PageDirectoryIndex = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql);
#if defined (_X86PAE_)
//
// Allocate the additional page directory pages.
//
for (i = 0; i < PD_PER_SYSTEM - 1; i += 1) {
MiEnsureAvailablePageOrWait (CurrentProcess, NULL);
Color = MI_PAGE_COLOR_PTE_PROCESS (PDE_BASE,
&CurrentProcess->NextPageColor);
PageDirectories[i] = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql);
}
PageDirectories[i] = PageDirectoryIndex;
//
// Recursively map each page directory page so it points to itself.
//
TempPte = ValidPdePde;
MI_SET_GLOBAL_STATE (TempPte, 0);
PointerPte = (PMMPTE)MiMapPageInHyperSpaceAtDpc (CurrentProcess, PageDirectoryIndex);
for (i = 0; i < PD_PER_SYSTEM; i += 1) {
TempPte.u.Hard.PageFrameNumber = PageDirectories[i];
PointerPte[i] = TempPte;
}
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPte);
//
// Initialize the parent page directory entries.
//
TopPte.u.Long = TempPte.u.Long & ~MM_PAE_PDPTE_MASK;
for (i = 0; i < PD_PER_SYSTEM; i += 1) {
TopPte.u.Hard.PageFrameNumber = PageDirectories[i];
PaeVa->PteEntry[i].u.Long = TopPte.u.Long;
}
NewProcess->PaeTop = (PVOID)PaeVa;
DirectoryTableBase[0] = TopQuad;
#else
INITIALIZE_DIRECTORY_TABLE_BASE(&DirectoryTableBase[0], PageDirectoryIndex);
#endif
#if (_MI_PAGING_LEVELS >= 3)
PointerPpe = KSEG_ADDRESS (PageDirectoryIndex);
TempPte = ValidPdePde;
//
// Map the top level page directory parent page recursively onto itself.
//
TempPte.u.Hard.PageFrameNumber = PageDirectoryIndex;
//
// Set the PTE address in the PFN for the top level page directory page.
//
Pfn1 = MI_PFN_ELEMENT (PageDirectoryIndex);
#if (_MI_PAGING_LEVELS >= 4)
PageDirectoryParentIndex = PageDirectoryIndex;
PointerPxe = (PMMPTE)MiMapPageInHyperSpaceAtDpc (CurrentProcess,
PageDirectoryIndex);
Pfn1->PteAddress = MiGetPteAddress(PXE_BASE);
PointerPxe[MiGetPxeOffset(PXE_BASE)] = TempPte;
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPxe);
//
// Now that the top level extended page parent page is initialized,
// allocate a page parent page.
//
MiEnsureAvailablePageOrWait (CurrentProcess, NULL);
Color = MI_PAGE_COLOR_PTE_PROCESS (PDE_BASE,
&CurrentProcess->NextPageColor);
PageDirectoryIndex = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql);
//
//
// Map this directory parent page into the top level
// extended page directory parent page.
//
TempPte.u.Hard.PageFrameNumber = PageDirectoryIndex;
PointerPxe = (PMMPTE)MiMapPageInHyperSpaceAtDpc (CurrentProcess,
PageDirectoryParentIndex);
PointerPxe[MiGetPxeOffset(HYPER_SPACE)] = TempPte;
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPxe);
#else
Pfn1->PteAddress = MiGetPteAddress((PVOID)PDE_TBASE);
PointerPpe[MiGetPpeOffset(PDE_TBASE)] = TempPte;
#endif
//
// Allocate the page directory for hyper space and map this directory
// page into the page directory parent page.
//
MiEnsureAvailablePageOrWait (CurrentProcess, NULL);
Color = MI_PAGE_COLOR_PTE_PROCESS (MiGetPpeAddress(HYPER_SPACE),
&CurrentProcess->NextPageColor);
HyperDirectoryIndex = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql);
TempPte.u.Hard.PageFrameNumber = HyperDirectoryIndex;
#if (_MI_PAGING_LEVELS >= 4)
PointerPpe = (PMMPTE)MiMapPageInHyperSpaceAtDpc (CurrentProcess,
PageDirectoryIndex);
#endif
PointerPpe[MiGetPpeOffset(HYPER_SPACE)] = TempPte;
#if (_MI_PAGING_LEVELS >= 4)
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPpe);
#endif
#if defined (_IA64_)
//
// Initialize the page directory parent for the session (or win32k) space.
// Any new process shares the session (or win32k) address space (and TB)
// of its parent.
//
NewProcess->Pcb.SessionParentBase = CurrentProcess->Pcb.SessionParentBase;
NewProcess->Pcb.SessionMapInfo = CurrentProcess->Pcb.SessionMapInfo;
#endif
#endif
//
// Allocate the hyper space page table page.
//
MiEnsureAvailablePageOrWait (CurrentProcess, NULL);
Color = MI_PAGE_COLOR_PTE_PROCESS (MiGetPdeAddress(HYPER_SPACE),
&CurrentProcess->NextPageColor);
HyperSpaceIndex = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql);
#if (_MI_PAGING_LEVELS >= 3)
#if defined (_IA64_)
TempPte.u.Hard.PageFrameNumber = HyperSpaceIndex;
PointerPde = KSEG_ADDRESS (HyperDirectoryIndex);
PointerPde[MiGetPdeOffset(HYPER_SPACE)] = TempPte;
#endif
#if (_AMD64_)
TempPte.u.Hard.PageFrameNumber = HyperSpaceIndex;
PointerPde = (PMMPTE)MiMapPageInHyperSpaceAtDpc (CurrentProcess,
HyperDirectoryIndex);
PointerPde[MiGetPdeOffset(HYPER_SPACE)] = TempPte;
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPde);
#endif
#endif
#if defined (_X86PAE_)
//
// Allocate the second hyper space page table page.
// Save it in the first PTE used by the first hyperspace PDE.
//
MiEnsureAvailablePageOrWait (CurrentProcess, NULL);
Color = MI_PAGE_COLOR_PTE_PROCESS (MiGetPdeAddress(HYPER_SPACE2),
&CurrentProcess->NextPageColor);
HyperSpaceIndex2 = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql);
//
// Unlike DirectoryTableBase[0], the HyperSpaceIndex is stored as an
// absolute PFN and does not need to be below 4GB.
//
DirectoryTableBase[1] = HyperSpaceIndex;
#else
INITIALIZE_DIRECTORY_TABLE_BASE(&DirectoryTableBase[1], HyperSpaceIndex);
#endif
//
// Remove page(s) for the VAD bitmap.
//
MiEnsureAvailablePageOrWait (CurrentProcess, NULL);
Color = MI_PAGE_COLOR_VA_PROCESS (MmWorkingSetList,
&CurrentProcess->NextPageColor);
VadBitMapPage = MiRemoveZeroPageMayReleaseLocks (Color, OldIrql);
//
// Remove page for the working set list.
//
MiEnsureAvailablePageOrWait (CurrentProcess, NULL);
Color = MI_PAGE_COLOR_VA_PROCESS (MmWorkingSetList,
&CurrentProcess->NextPageColor);
PageContainingWorkingSet = MiRemoveZeroPageIfAny (Color);
if (PageContainingWorkingSet == 0) {
PageContainingWorkingSet = MiRemoveAnyPage (Color);
UNLOCK_PFN (OldIrql);
MiZeroPhysicalPage (PageContainingWorkingSet, Color);
}
else {
//
// Release the PFN lock as the needed pages have been allocated.
//
UNLOCK_PFN (OldIrql);
}
NewProcess->WorkingSetPage = PageContainingWorkingSet;
//
// Initialize the page reserved for hyper space.
//
MI_INITIALIZE_HYPERSPACE_MAP (HyperSpaceIndex);
#if (_MI_PAGING_LEVELS >= 3)
//
// Set the PTE address in the PFN for the hyper space page directory page.
//
Pfn1 = MI_PFN_ELEMENT (HyperDirectoryIndex);
Pfn1->PteAddress = MiGetPpeAddress(HYPER_SPACE);
#if defined (_AMD64_)
//
// Copy the system mappings including the shared user page & session space.
//
CurrentAddressSpacePde = MiGetPxeAddress(KI_USER_SHARED_DATA);
PointerPxe = (PMMPTE)MiMapPageInHyperSpace (CurrentProcess,
PageDirectoryParentIndex,
&OldIrql);
PointerFillPte = &PointerPxe[MiGetPxeOffset(KI_USER_SHARED_DATA)];
RtlCopyMemory (PointerFillPte,
CurrentAddressSpacePde,
((1 + (MiGetPxeAddress(MM_SYSTEM_SPACE_END) -
CurrentAddressSpacePde)) * sizeof(MMPTE)));
MiUnmapPageInHyperSpace (CurrentProcess, PointerPxe, OldIrql);
#endif
TempPte = ValidPdePde;
TempPte.u.Hard.PageFrameNumber = VadBitMapPage;
MI_SET_GLOBAL_STATE (TempPte, 0);
#if defined (_AMD64_)
PointerPte = (PMMPTE)MiMapPageInHyperSpace (CurrentProcess,
HyperSpaceIndex,
&OldIrql);
PointerPte[MiGetPteOffset(VAD_BITMAP_SPACE)] = TempPte;
TempPte.u.Hard.PageFrameNumber = PageContainingWorkingSet;
PointerPte[MiGetPteOffset(MmWorkingSetList)] = TempPte;
MiUnmapPageInHyperSpace (CurrentProcess, PointerPte, OldIrql);
#else
PointerPte = KSEG_ADDRESS (HyperSpaceIndex);
PointerPte[MiGetPteOffset(VAD_BITMAP_SPACE)] = TempPte;
TempPte.u.Hard.PageFrameNumber = PageContainingWorkingSet;
PointerPte[MiGetPteOffset(MmWorkingSetList)] = TempPte;
#endif
#else // the following is for (_MI_PAGING_LEVELS < 3) only
#if defined (_X86PAE_)
//
// Stash the second hyperspace PDE in the first PTE for the initial
// hyperspace entry.
//
TempPte = ValidPdePde;
TempPte.u.Hard.PageFrameNumber = HyperSpaceIndex2;
MI_SET_GLOBAL_STATE (TempPte, 0);
PointerPte = (PMMPTE)MiMapPageInHyperSpace (CurrentProcess, HyperSpaceIndex, &OldIrql);
PointerPte[0] = TempPte;
TempPte.u.Hard.PageFrameNumber = VadBitMapPage;
PointerPte[MiGetPteOffset(VAD_BITMAP_SPACE)] = TempPte;
TempPte.u.Hard.PageFrameNumber = PageContainingWorkingSet;
PointerPte[MiGetPteOffset(MmWorkingSetList)] = TempPte;
MiUnmapPageInHyperSpace (CurrentProcess, PointerPte, OldIrql);
#else
TempPte = ValidPdePde;
TempPte.u.Hard.PageFrameNumber = VadBitMapPage;
MI_SET_GLOBAL_STATE (TempPte, 0);
PointerPte = (PMMPTE)MiMapPageInHyperSpace (CurrentProcess, HyperSpaceIndex, &OldIrql);
PointerPte[MiGetPteOffset(VAD_BITMAP_SPACE)] = TempPte;
TempPte.u.Hard.PageFrameNumber = PageContainingWorkingSet;
PointerPte[MiGetPteOffset(MmWorkingSetList)] = TempPte;
MiUnmapPageInHyperSpace (CurrentProcess, PointerPte, OldIrql);
#endif
//
// Set the PTE address in the PFN for the page directory page.
//
Pfn1 = MI_PFN_ELEMENT (PageDirectoryIndex);
Pfn1->PteAddress = (PMMPTE)PDE_BASE;
TempPte = ValidPdePde;
TempPte.u.Hard.PageFrameNumber = HyperSpaceIndex;
MI_SET_GLOBAL_STATE (TempPte, 0);
//
// Map the page directory page in hyperspace.
// Note for PAE, this is the high 1GB virtual only.
//
PointerPte = (PMMPTE)MiMapPageInHyperSpace (CurrentProcess, PageDirectoryIndex, &OldIrql);
PointerPte[MiGetPdeOffset(HYPER_SPACE)] = TempPte;
#if defined (_X86PAE_)
//
// Map in the second hyperspace page directory.
// The page directory page is already recursively mapped.
//
TempPte.u.Hard.PageFrameNumber = HyperSpaceIndex2;
PointerPte[MiGetPdeOffset(HYPER_SPACE2)] = TempPte;
#else
//
// Recursively map the page directory page so it points to itself.
//
TempPte.u.Hard.PageFrameNumber = PageDirectoryIndex;
PointerPte[MiGetPdeOffset(PTE_BASE)] = TempPte;
#endif
//
// Map in the non paged portion of the system.
//
//
// For the PAE case, only the last page directory is currently mapped, so
// only copy the system PDEs for the last 1GB - any that need copying in
// the 2gb->3gb range will be done a little later.
//
if (MmVirtualBias != 0) {
PointerFillPte = &PointerPte[MiGetPdeOffset(CODE_START + MmVirtualBias)];
CurrentAddressSpacePde = MiGetPdeAddress(CODE_START + MmVirtualBias);
RtlCopyMemory (PointerFillPte,
CurrentAddressSpacePde,
(((1 + CODE_END) - CODE_START) / MM_VA_MAPPED_BY_PDE) * sizeof(MMPTE));
}
PointerFillPte = &PointerPte[MiGetPdeOffset(MmNonPagedSystemStart)];
CurrentAddressSpacePde = MiGetPdeAddress(MmNonPagedSystemStart);
RtlCopyMemory (PointerFillPte,
CurrentAddressSpacePde,
((1 + (MiGetPdeAddress(NON_PAGED_SYSTEM_END) -
CurrentAddressSpacePde))) * sizeof(MMPTE));
//
// Map in the system cache page table pages.
//
PointerFillPte = &PointerPte[MiGetPdeOffset(MM_SYSTEM_CACHE_WORKING_SET)];
CurrentAddressSpacePde = MiGetPdeAddress(MM_SYSTEM_CACHE_WORKING_SET);
RtlCopyMemory (PointerFillPte,
CurrentAddressSpacePde,
((1 + (MiGetPdeAddress(MmSystemCacheEnd) -
CurrentAddressSpacePde))) * sizeof(MMPTE));
#if !defined (_X86PAE_)
//
// Map all the virtual space in the 2GB->3GB range when it's not user space.
// This includes kernel/HAL code & data, the PFN database, initial nonpaged
// pool, any extra system PTE or system cache areas, system views and
// session space.
//
if (MmVirtualBias == 0) {
PointerFillPte = &PointerPte[MiGetPdeOffset(CODE_START)];
CurrentAddressSpacePde = MiGetPdeAddress(CODE_START);
RtlCopyMemory (PointerFillPte,
CurrentAddressSpacePde,
((MM_SESSION_SPACE_DEFAULT_END - CODE_START) / MM_VA_MAPPED_BY_PDE) * sizeof(MMPTE));
}
else {
//
// Booted /3GB, so copy the bootstrap entry for session space as it's
// not included in 2GB->3GB copy above.
//
PointerFillPte = &PointerPte[MiGetPdeOffset(MmSessionSpace)];
CurrentAddressSpacePde = MiGetPdeAddress(MmSessionSpace);
if (CurrentAddressSpacePde->u.Hard.Valid == 1) {
MI_WRITE_VALID_PTE (PointerFillPte, *CurrentAddressSpacePde);
}
else {
MI_WRITE_INVALID_PTE (PointerFillPte, *CurrentAddressSpacePde);
}
}
if (MiMaximumSystemExtraSystemPdes) {
PointerFillPte = &PointerPte[MiGetPdeOffset(MiUseMaximumSystemSpace)];
CurrentAddressSpacePde = MiGetPdeAddress(MiUseMaximumSystemSpace);
RtlCopyMemory (PointerFillPte,
CurrentAddressSpacePde,
MiMaximumSystemExtraSystemPdes * sizeof(MMPTE));
}
#endif
MiUnmapPageInHyperSpace (CurrentProcess, PointerPte, OldIrql);
#if defined (_X86PAE_)
//
// Map all the virtual space in the 2GB->3GB range when it's not user space.
// This includes kernel/HAL code & data, the PFN database, initial nonpaged
// pool, any extra system PTE or system cache areas, system views and
// session space.
//
if (MmVirtualBias == 0) {
PageDirectoryIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PaeVa->PteEntry[PD_PER_SYSTEM - 2]);
PointerPte = (PMMPTE)MiMapPageInHyperSpace (CurrentProcess, PageDirectoryIndex, &OldIrql);
PointerFillPte = &PointerPte[MiGetPdeOffset(CODE_START)];
CurrentAddressSpacePde = MiGetPdeAddress(CODE_START);
RtlCopyMemory (PointerFillPte,
CurrentAddressSpacePde,
((MM_SESSION_SPACE_DEFAULT_END - CODE_START) / MM_VA_MAPPED_BY_PDE) * sizeof(MMPTE));
MiUnmapPageInHyperSpace (CurrentProcess, PointerPte, OldIrql);
}
//
// If portions of the range between 1GB and 2GB are being used for
// additional system PTEs, then copy those too.
//
if (MiMaximumSystemExtraSystemPdes != 0) {
MaximumStart = MiUseMaximumSystemSpace;
while (MaximumStart < MiUseMaximumSystemSpaceEnd) {
i = MiGetPdPteOffset (MiUseMaximumSystemSpace);
PageDirectoryIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PaeVa->PteEntry[i]);
PointerPte = (PMMPTE)MiMapPageInHyperSpace (CurrentProcess,
PageDirectoryIndex,
&OldIrql);
PointerFillPte = &PointerPte[MiGetPdeOffset(MaximumStart)];
CurrentAddressSpacePde = MiGetPdeAddress(MaximumStart);
NumberOfPdes = PDE_PER_PAGE - MiGetPdeOffset(MaximumStart);
RtlCopyMemory (PointerFillPte,
CurrentAddressSpacePde,
NumberOfPdes * sizeof(MMPTE));
MiUnmapPageInHyperSpace (CurrentProcess, PointerPte, OldIrql);
MaximumStart = (ULONG) MiGetVirtualAddressMappedByPde (CurrentAddressSpacePde + NumberOfPdes);
}
}
#endif
#endif // end of (_MI_PAGING_LEVELS < 3) specific else
//
// Up the session space reference count.
//
MiSessionAddProcess (NewProcess);
//
// Release working set mutex and lower IRQL.
//
UNLOCK_WS (CurrentProcess);
return TRUE;
}
NTSTATUS
MmInitializeProcessAddressSpace (
IN PEPROCESS ProcessToInitialize,
IN PEPROCESS ProcessToClone OPTIONAL,
IN PVOID SectionToMap OPTIONAL,
OUT POBJECT_NAME_INFORMATION *AuditName OPTIONAL
)
/*++
Routine Description:
This routine initializes the working set and mutexes within a
newly created address space to support paging.
No page faults may occur in a new process until this routine has
completed.
Arguments:
ProcessToInitialize - Supplies a pointer to the process to initialize.
ProcessToClone - Optionally supplies a pointer to the process whose
address space should be copied into the
ProcessToInitialize address space.
SectionToMap - Optionally supplies a section to map into the newly
initialized address space.
Only one of ProcessToClone and SectionToMap may be specified.
AuditName - Supplies an opaque object name information pointer.
Return Value:
NTSTATUS.
Environment:
Kernel mode. APCs disabled.
--*/
{
KIRQL OldIrql;
MMPTE TempPte;
PMMPTE PointerPte;
PVOID BaseAddress;
SIZE_T ViewSize;
NTSTATUS Status;
PFILE_OBJECT FilePointer;
PFN_NUMBER PageContainingWorkingSet;
LARGE_INTEGER SectionOffset;
PSECTION_IMAGE_INFORMATION ImageInfo;
PMMVAD VadShare;
PMMVAD VadReserve;
PLOCK_HEADER LockedPagesHeader;
PFN_NUMBER PdePhysicalPage;
PFN_NUMBER VadBitMapPage;
ULONG i;
ULONG NumberOfPages;
#if defined (_X86PAE_)
PFN_NUMBER PdePhysicalPage2;
#endif
#if (_MI_PAGING_LEVELS >= 3)
PFN_NUMBER PpePhysicalPage;
#if DBG
ULONG j;
PUCHAR p;
#endif
#endif
#if (_MI_PAGING_LEVELS >= 4)
PFN_NUMBER PxePhysicalPage;
#endif
#if defined(_WIN64)
PWOW64_PROCESS Wow64Process;
#endif
//
// Initialize Working Set Mutex in process header.
//
KeAttachProcess (&ProcessToInitialize->Pcb);
ASSERT (ProcessToInitialize->AddressSpaceInitialized <= 1);
PS_CLEAR_BITS (&ProcessToInitialize->Flags, PS_PROCESS_FLAGS_ADDRESS_SPACE1);
ASSERT (ProcessToInitialize->AddressSpaceInitialized == 0);
PS_SET_BITS (&ProcessToInitialize->Flags, PS_PROCESS_FLAGS_ADDRESS_SPACE2);
ASSERT (ProcessToInitialize->AddressSpaceInitialized == 2);
ExInitializeFastMutex (&ProcessToInitialize->AddressCreationLock);
ExInitializeFastMutex (&ProcessToInitialize->WorkingSetLock);
//
// NOTE: The process block has been zeroed when allocated, so
// there is no need to zero fields and set pointers to NULL.
//
ASSERT (ProcessToInitialize->VadRoot == NULL);
KeQuerySystemTime(&ProcessToInitialize->Vm.LastTrimTime);
ProcessToInitialize->Vm.VmWorkingSetList = MmWorkingSetList;
//
// Obtain a page to map the working set and initialize the
// working set. Get the PFN lock to allocate physical pages.
//
LOCK_PFN (OldIrql);
//
// Initialize the PFN database for the Page Directory and the
// PDE which maps hyper space.
//
#if (_MI_PAGING_LEVELS >= 3)
#if (_MI_PAGING_LEVELS >= 4)
PointerPte = MiGetPteAddress (PXE_BASE);
PxePhysicalPage = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
MiInitializePfn (PxePhysicalPage, PointerPte, 1);
PointerPte = MiGetPxeAddress (HYPER_SPACE);
#else
PointerPte = MiGetPteAddress ((PVOID)PDE_TBASE);
#endif
PpePhysicalPage = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
MiInitializePfn (PpePhysicalPage, PointerPte, 1);
PointerPte = MiGetPpeAddress (HYPER_SPACE);
#elif defined (_X86PAE_)
PointerPte = MiGetPdeAddress (PDE_BASE);
#else
PointerPte = MiGetPteAddress (PDE_BASE);
#endif
PdePhysicalPage = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
MiInitializePfn (PdePhysicalPage, PointerPte, 1);
PointerPte = MiGetPdeAddress (HYPER_SPACE);
MiInitializePfn (MI_GET_PAGE_FRAME_FROM_PTE (PointerPte), PointerPte, 1);
#if defined (_X86PAE_)
for (i = 0; i < PD_PER_SYSTEM - 1; i += 1) {
PointerPte = MiGetPteAddress (PDE_BASE + (i << PAGE_SHIFT));
PdePhysicalPage2 = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
MiInitializePfn (PdePhysicalPage2, PointerPte, 1);
}
PointerPte = MiGetPdeAddress (HYPER_SPACE2);
MiInitializePfn (MI_GET_PAGE_FRAME_FROM_PTE (PointerPte), PointerPte, 1);
#endif
//
// The VAD bitmap spans one page when booted 2GB and the working set
// page follows it. If booted 3GB, the VAD bitmap spans 1.5 pages and
// the working set list uses the last half of the second page.
//
NumberOfPages = 2;
PointerPte = MiGetPteAddress (VAD_BITMAP_SPACE);
for (i = 0; i < NumberOfPages; i += 1) {
ASSERT (PointerPte->u.Long != 0);
VadBitMapPage = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
PointerPte->u.Long = MM_DEMAND_ZERO_WRITE_PTE;
MiInitializePfn (VadBitMapPage, PointerPte, 1);
MI_MAKE_VALID_PTE (TempPte,
VadBitMapPage,
MM_READWRITE,
PointerPte);
MI_SET_PTE_DIRTY (TempPte);
MI_WRITE_VALID_PTE (PointerPte, TempPte);
PointerPte += 1;
}
UNLOCK_PFN (OldIrql);
PageContainingWorkingSet = ProcessToInitialize->WorkingSetPage;
ASSERT (ProcessToInitialize->LockedPagesList == NULL);
if (MmTrackLockedPages == TRUE) {
LockedPagesHeader = ExAllocatePoolWithTag (NonPagedPool,
sizeof(LOCK_HEADER),
'xTmM');
if (LockedPagesHeader != NULL) {
LockedPagesHeader->Count = 0;
InitializeListHead (&LockedPagesHeader->ListHead);
ProcessToInitialize->LockedPagesList = (PVOID)LockedPagesHeader;
}
}
MiInitializeWorkingSetList (ProcessToInitialize);
InitializeListHead (&ProcessToInitialize->PhysicalVadList);
#if (_MI_PAGING_LEVELS >= 3)
//
// Allocate the commitment tracking bitmaps for page directory and page
// table pages. This must be done before any VAD creations occur.
//
ASSERT (MmWorkingSetList->CommittedPageTables == NULL);
ASSERT (MmWorkingSetList->NumberOfCommittedPageDirectories == 0);
ASSERT ((ULONG_PTR)MM_SYSTEM_RANGE_START % (PTE_PER_PAGE * PAGE_SIZE) == 0);
MmWorkingSetList->CommittedPageTables = (PULONG)
ExAllocatePoolWithTag (MmPagedPoolEnd != NULL ? PagedPool : NonPagedPool,
(MM_USER_PAGE_TABLE_PAGES + 7) / 8,
'dPmM');
if (MmWorkingSetList->CommittedPageTables == NULL) {
KeDetachProcess ();
return STATUS_NO_MEMORY;
}
#if (_MI_PAGING_LEVELS >= 4)
#if DBG
p = (PUCHAR) MmWorkingSetList->CommittedPageDirectoryParents;
for (j = 0; j < ((MM_USER_PAGE_DIRECTORY_PARENT_PAGES + 7) / 8); j += 1) {
ASSERT (*p == 0);
p += 1;
}
#endif
ASSERT (MmWorkingSetList->CommittedPageDirectories == NULL);
ASSERT (MmWorkingSetList->NumberOfCommittedPageDirectoryParents == 0);
MmWorkingSetList->CommittedPageDirectories = (PULONG)
ExAllocatePoolWithTag (MmPagedPoolEnd != NULL ? PagedPool : NonPagedPool,
(MM_USER_PAGE_DIRECTORY_PAGES + 7) / 8,
'dPmM');
if (MmWorkingSetList->CommittedPageDirectories == NULL) {
ExFreePool (MmWorkingSetList->CommittedPageTables);
MmWorkingSetList->CommittedPageTables = NULL;
KeDetachProcess ();
return STATUS_NO_MEMORY;
}
RtlZeroMemory (MmWorkingSetList->CommittedPageDirectories,
(MM_USER_PAGE_DIRECTORY_PAGES + 7) / 8);
#endif
RtlZeroMemory (MmWorkingSetList->CommittedPageTables,
(MM_USER_PAGE_TABLE_PAGES + 7) / 8);
#if DBG
p = (PUCHAR) MmWorkingSetList->CommittedPageDirectories;
for (j = 0; j < ((MM_USER_PAGE_DIRECTORY_PAGES + 7) / 8); j += 1) {
ASSERT (*p == 0);
p += 1;
}
#endif
#endif
//
// Page faults may be taken now.
//
// If the system has been biased to an alternate base address to allow
// 3gb of user address space and a process is not being cloned, then
// create a VAD for the shared memory page.
//
#if defined(_X86_) && defined(MM_SHARED_USER_DATA_VA)
if ((MmVirtualBias != 0) && (ProcessToClone == NULL)) {
VadShare = NULL;
//
// Allocate a VAD to map the shared memory page. If a VAD cannot be
// allocated, then detach from the target process and return a failure
// status. This VAD is marked as not deletable.
//
if (MmHighestUserAddress > (PVOID) MM_SHARED_USER_DATA_VA) {
VadShare = MiAllocateVad (MM_SHARED_USER_DATA_VA,
MM_SHARED_USER_DATA_VA,
FALSE);
if (VadShare == NULL) {
KeDetachProcess ();
return STATUS_NO_MEMORY;
}
}
//
// If a section is being mapped and the executable is not large
// address space aware, then create a VAD that reserves the address
// space between 2gb and the highest user address.
//
if (SectionToMap != NULL) {
if (!((PSECTION)SectionToMap)->u.Flags.Image) {
KeDetachProcess ();
if (VadShare != NULL) {
ExFreePool (VadShare);
}
return STATUS_SECTION_NOT_IMAGE;
}
ImageInfo = ((PSECTION)SectionToMap)->Segment->u2.ImageInformation;
if ((ImageInfo->ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) == 0) {
//
// Allocate a VAD to map the address space between 2gb and
// the highest user address. If a VAD cannot be allocated,
// then deallocate the shared address space VAD, detach from
// the target process, and return a failure status.
// This VAD is marked as not deletable.
//
VadReserve = MiAllocateVad (_2gb,
(ULONG_PTR)MM_HIGHEST_VAD_ADDRESS,
FALSE);
if (VadReserve == NULL) {
KeDetachProcess ();
if (VadShare != NULL) {
ExFreePool (VadShare);
}
return STATUS_NO_MEMORY;
}
//
// Insert the VAD.
//
// N.B. No failure can occur since there is no commit charge.
//
Status = MiInsertVad (VadReserve);
ASSERT (NT_SUCCESS(Status));
}
}
//
// Insert the VAD.
//
// N.B. No failure can occur since there is no commit charge.
//
if (VadShare != NULL) {
Status = MiInsertVad (VadShare);
ASSERT (NT_SUCCESS(Status));
}
}
#endif
#if defined(_WIN64)
if (ProcessToClone == NULL) {
//
// Reserve the address space just below KUSER_SHARED_DATA as the
// compatibility area. This range (and pieces of it) can be
// unreserved by user mode code such as WOW64 or csrss. Hence
// commit must be charged for the page directory and table pages.
//
ASSERT(MiCheckForConflictingVad(ProcessToInitialize, WOW64_COMPATIBILITY_AREA_ADDRESS, MM_SHARED_USER_DATA_VA) == NULL);
VadShare = MiAllocateVad (WOW64_COMPATIBILITY_AREA_ADDRESS,
MM_SHARED_USER_DATA_VA,
TRUE);
if (VadShare == NULL) {
KeDetachProcess ();
return STATUS_NO_MEMORY;
}
//
// Zero the commit charge so inserting the VAD will result in the
// proper charges being applied. This way when it is split later,
// the correct commitment will be returned.
//
// N.B. The system process is not allocated with commit because
// paged pool and quotas don't exist at the point in Phase0
// where this is called.
//
if (MmPagedPoolEnd != NULL) {
VadShare->u.VadFlags.CommitCharge = 0;
}
//
// Reserve the memory above 2GB to prevent 32 bit (WOW64) process
// access.
//
if (SectionToMap != NULL) {
if (!((PSECTION)SectionToMap)->u.Flags.Image) {
KeDetachProcess ();
ExFreePool (VadShare);
return STATUS_SECTION_NOT_IMAGE;
}
ImageInfo = ((PSECTION)SectionToMap)->Segment->u2.ImageInformation;
if (((ProcessToInitialize->Flags & PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE) == 0) &&
((ImageInfo->ImageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) == 0 ||
ImageInfo->Machine == IMAGE_FILE_MACHINE_I386)) {
//
// Allocate a VAD to reserve the address space between 2gb and
// the highest user address. If a VAD cannot be allocated,
// then deallocate the compatibility VAD, detach from the target
// process and return a failure status.
//
VadReserve = MiAllocateVad (_2gb,
(ULONG_PTR)MM_HIGHEST_USER_ADDRESS,
TRUE);
if (VadReserve == NULL) {
KeDetachProcess ();
ExFreePool (VadShare);
return STATUS_NO_MEMORY;
}
//
// Insert the VAD.
//
// N.B. No failure can occur since there is no commit charge.
//
Status = MiInsertVad (VadReserve);
ASSERT (NT_SUCCESS(Status));
if (ImageInfo->Machine == IMAGE_FILE_MACHINE_I386) {
//
// Initialize the Wow64 process structure.
//
Wow64Process = (PWOW64_PROCESS) ExAllocatePoolWithTag (
NonPagedPool,
sizeof(WOW64_PROCESS),
'WowM');
if (Wow64Process == NULL) {
KeDetachProcess ();
ExFreePool (VadShare);
return STATUS_NO_MEMORY;
}
RtlZeroMemory (Wow64Process, sizeof(WOW64_PROCESS));
ProcessToInitialize->Wow64Process = Wow64Process;
#if defined(_MIALT4K_)
//
// Initialize the alternate page table for the 4k
// page functionality.
//
Status = MiInitializeAlternateTable (ProcessToInitialize);
if (Status != STATUS_SUCCESS) {
KeDetachProcess ();
ExFreePool (VadShare);
return Status;
}
#endif
}
}
}
//
// Insert the VAD. Since this VAD has a commit charge, the working set
// mutex must be held (as calls inside MiInsertVad to support routines
// to charge commit require it), failures can occur and must be handled.
//
LOCK_WS (ProcessToInitialize);
Status = MiInsertVad (VadShare);
UNLOCK_WS (ProcessToInitialize);
if (!NT_SUCCESS(Status)) {
//
// Note that the VadReserve and Wow64 allocations are automatically
// released on process destruction so there is no need to tear
// them down here.
//
ExFreePool (VadShare);
KeDetachProcess ();
return Status;
}
}
#endif
if (SectionToMap != NULL) {
//
// Map the specified section into the address space of the
// process but only if it is an image section.
//
if (!((PSECTION)SectionToMap)->u.Flags.Image) {
Status = STATUS_SECTION_NOT_IMAGE;
}
else {
UNICODE_STRING UnicodeString;
ULONG n;
PWSTR Src;
PCHAR Dst;
PSECTION_IMAGE_INFORMATION ImageInformation;
FilePointer = ((PSECTION)SectionToMap)->Segment->ControlArea->FilePointer;
ImageInformation = ((PSECTION)SectionToMap)->Segment->u2.ImageInformation;
UnicodeString = FilePointer->FileName;
Src = (PWSTR)((PCHAR)UnicodeString.Buffer + UnicodeString.Length);
n = 0;
if (UnicodeString.Buffer != NULL) {
while (Src > UnicodeString.Buffer) {
if (*--Src == OBJ_NAME_PATH_SEPARATOR) {
Src += 1;
break;
}
else {
n += 1;
}
}
}
Dst = (PCHAR)ProcessToInitialize->ImageFileName;
if (n >= sizeof (ProcessToInitialize->ImageFileName)) {
n = sizeof (ProcessToInitialize->ImageFileName) - 1;
}
while (n--) {
*Dst++ = (UCHAR)*Src++;
}
*Dst = '\0';
if (AuditName != NULL) {
Status = SeInitializeProcessAuditName (FilePointer, FALSE, AuditName);
if (!NT_SUCCESS(Status)) {
KeDetachProcess ();
return Status;
}
}
ProcessToInitialize->SubSystemMajorVersion =
(UCHAR)ImageInformation->SubSystemMajorVersion;
ProcessToInitialize->SubSystemMinorVersion =
(UCHAR)ImageInformation->SubSystemMinorVersion;
BaseAddress = NULL;
ViewSize = 0;
ZERO_LARGE (SectionOffset);
Status = MmMapViewOfSection ((PSECTION)SectionToMap,
ProcessToInitialize,
&BaseAddress,
0,
0,
&SectionOffset,
&ViewSize,
ViewShare,
0,
PAGE_READWRITE);
ProcessToInitialize->SectionBaseAddress = BaseAddress;
}
KeDetachProcess ();
return Status;
}
if (ProcessToClone != NULL) {
strcpy ((PCHAR)ProcessToInitialize->ImageFileName,
(PCHAR)ProcessToClone->ImageFileName);
//
// Clone the address space of the specified process.
//
// As the page directory and page tables are private to each
// process, the physical pages which map the directory page
// and the page table usage must be mapped into system space
// so they can be updated while in the context of the process
// we are cloning.
//
#if defined(_WIN64)
if (ProcessToClone->Wow64Process != NULL) {
//
// Initialize the Wow64 process structure.
//
Wow64Process = (PWOW64_PROCESS) ExAllocatePoolWithTag (
NonPagedPool,
sizeof(WOW64_PROCESS),
'WowM');
if (Wow64Process == NULL) {
KeDetachProcess ();
return STATUS_NO_MEMORY;
}
RtlZeroMemory (Wow64Process, sizeof(WOW64_PROCESS));
ProcessToInitialize->Wow64Process = Wow64Process;
#if defined(_MIALT4K_)
//
// Initialize the alternate page table for the 4k
// page functionality.
//
Status = MiInitializeAlternateTable (ProcessToInitialize);
if (Status != STATUS_SUCCESS) {
KeDetachProcess ();
return Status;
}
#endif
}
#endif
KeDetachProcess ();
return MiCloneProcessAddressSpace (ProcessToClone,
ProcessToInitialize,
#if (_MI_PAGING_LEVELS >= 4)
PxePhysicalPage,
#elif (_MI_PAGING_LEVELS >= 3)
PpePhysicalPage,
#else
PdePhysicalPage,
#endif
PageContainingWorkingSet);
}
//
// System Process.
//
KeDetachProcess ();
return STATUS_SUCCESS;
}
VOID
MmInitializeHandBuiltProcess (
IN PEPROCESS ProcessToInitialize,
OUT PULONG_PTR DirectoryTableBase
)
/*++
Routine Description:
This routine initializes the working set mutex and
address creation mutex for this "hand built" process.
Normally the call to MmInitializeAddressSpace initializes the
working set mutex. However, in this case, we have already initialized
the address space and we are now creating a second process using
the address space of the idle thread.
Arguments:
ProcessToInitialize - Supplies a pointer to the process to initialize.
DirectoryTableBase - Receives the pair of directory table base pointers.
Return Value:
None.
Environment:
Kernel mode. APCs disabled, idle process context.
--*/
{
PEPROCESS CurrentProcess;
#if defined (_X86PAE_)
ULONG i;
PMMPTE PdeBase;
PFN_NUMBER PageFrameIndex;
#endif
CurrentProcess = PsGetCurrentProcess();
DirectoryTableBase[0] = CurrentProcess->Pcb.DirectoryTableBase[0];
DirectoryTableBase[1] = CurrentProcess->Pcb.DirectoryTableBase[1];
#if defined(_IA64_)
ProcessToInitialize->Pcb.SessionMapInfo = CurrentProcess->Pcb.SessionMapInfo;
ProcessToInitialize->Pcb.SessionParentBase = CurrentProcess->Pcb.SessionParentBase;
#endif
ExInitializeFastMutex(&ProcessToInitialize->WorkingSetLock);
ExInitializeFastMutex(&ProcessToInitialize->AddressCreationLock);
KeInitializeSpinLock (&ProcessToInitialize->HyperSpaceLock);
ASSERT (ProcessToInitialize->VadRoot == NULL);
ProcessToInitialize->Vm.WorkingSetSize = CurrentProcess->Vm.WorkingSetSize;
ProcessToInitialize->Vm.VmWorkingSetList = MmWorkingSetList;
KeQuerySystemTime(&ProcessToInitialize->Vm.LastTrimTime);
#if defined (_X86PAE_)
if (MiSystemPaeVa.PteEntry[0].u.Long == 0) {
PageFrameIndex = (((PKPROCESS)CurrentProcess)->DirectoryTableBase[0] >> PAGE_SHIFT);
PdeBase = (PMMPTE)MiMapSinglePage (NULL,
PageFrameIndex,
MmCached,
HighPagePriority);
if (PdeBase == NULL) {
KeBugCheckEx (MEMORY_MANAGEMENT,
0x3452,
(ULONG_PTR)CurrentProcess,
(ULONG_PTR)ProcessToInitialize,
1);
}
for (i = 0; i < PD_PER_SYSTEM; i += 1) {
MiSystemPaeVa.PteEntry[i] = *PdeBase;
PdeBase += 1;
}
PdeBase -= PD_PER_SYSTEM;
MiUnmapSinglePage (PdeBase);
}
#endif
}
NTSTATUS
MmInitializeHandBuiltProcess2 (
IN PEPROCESS ProcessToInitialize
)
/*++
Routine Description:
This routine initializes the shared user VAD. This only needs to be done
for x86 when booted /3GB because on all other systems, the shared user
address is located above the highest user address. For x86 /3GB, this
VAD must be allocated so that other random VAD allocations do not overlap
this area which would cause the mapping to receive the wrong data.
Arguments:
ProcessToInitialize - Supplies the process that needs initialization.
Return Value:
NTSTATUS.
Environment:
Kernel mode. APCs Disabled.
--*/
{
#if defined(_X86_) && defined(MM_SHARED_USER_DATA_VA)
PMMVAD VadShare;
NTSTATUS Status;
Status = STATUS_SUCCESS;
//
// Allocate a VAD to map the shared memory page. If a VAD cannot be
// allocated, then detach from the target process and return a failure
// status. This VAD is marked as not deletable.
//
if ((MmVirtualBias != 0) &&
(MmHighestUserAddress > (PVOID) MM_SHARED_USER_DATA_VA)) {
KeAttachProcess (&ProcessToInitialize->Pcb);
VadShare = MiAllocateVad (MM_SHARED_USER_DATA_VA,
MM_SHARED_USER_DATA_VA,
FALSE);
//
// Insert the VAD.
//
// N.B. No failure can occur since there is no commit charge.
//
if (VadShare != NULL) {
Status = MiInsertVad (VadShare);
ASSERT (NT_SUCCESS(Status));
}
else {
Status = STATUS_NO_MEMORY;
}
KeDetachProcess ();
}
return Status;
#else
UNREFERENCED_PARAMETER (ProcessToInitialize);
return STATUS_SUCCESS;
#endif
}
VOID
MmDeleteProcessAddressSpace (
IN PEPROCESS Process
)
/*++
Routine Description:
This routine deletes a process's Page Directory and working set page.
Arguments:
Process - Supplies a pointer to the deleted process.
Return Value:
None.
Environment:
Kernel mode. APCs Disabled.
--*/
{
PEPROCESS CurrentProcess;
PMMPFN Pfn1;
KIRQL OldIrql;
PMMPTE PointerPte;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER VadBitMapPage;
PFN_NUMBER PageFrameIndex2;
#if (_MI_PAGING_LEVELS >= 4)
PFN_NUMBER PageFrameIndex3;
PMMPTE ExtendedPageDirectoryParent;
PMMPTE PointerPxe;
#endif
#if (_MI_PAGING_LEVELS >= 3)
PMMPTE PageDirectoryParent;
PMMPTE PointerPpe;
#endif
#if defined (_X86PAE_)
ULONG i;
PFN_NUMBER HyperPage2;
PPAE_ENTRY PaeVa;
PaeVa = (PPAE_ENTRY) Process->PaeTop;
#endif
CurrentProcess = PsGetCurrentProcess ();
//
// Return commitment.
//
MiReturnCommitment (MM_PROCESS_COMMIT_CHARGE);
MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PROCESS_DELETE, MM_PROCESS_COMMIT_CHARGE);
ASSERT (Process->CommitCharge == 0);
//
// Remove the working set list page from the deleted process.
//
Pfn1 = MI_PFN_ELEMENT (Process->WorkingSetPage);
LOCK_PFN (OldIrql);
MmProcessCommit -= MM_PROCESS_COMMIT_CHARGE;
if (Process->AddressSpaceInitialized == 2) {
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
MiDecrementShareCountOnly (Process->WorkingSetPage);
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
//
// Map the hyper space page table page from the deleted process
// so the vad bit map (and second hyperspace page) can be captured.
//
PageFrameIndex = MI_GET_HYPER_PAGE_TABLE_FRAME_FROM_PROCESS (Process);
PointerPte = (PMMPTE)MiMapPageInHyperSpaceAtDpc (CurrentProcess,
PageFrameIndex);
#if defined (_X86PAE_)
PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
#endif
VadBitMapPage = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte + MiGetPteOffset(VAD_BITMAP_SPACE));
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPte);
//
// Remove the VAD bitmap page.
//
Pfn1 = MI_PFN_ELEMENT (VadBitMapPage);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
MiDecrementShareCountOnly (VadBitMapPage);
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
//
// Remove the first hyper space page table page.
//
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
MiDecrementShareCountOnly (PageFrameIndex);
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
#if defined (_X86PAE_)
//
// Remove the second hyper space page table page.
//
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex2);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
MiDecrementShareCountOnly (PageFrameIndex2);
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
//
// Remove the page directory pages.
//
PointerPte = (PMMPTE) PaeVa;
for (i = 0; i < PD_PER_SYSTEM - 1; i += 1) {
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareAndValidCount (PageFrameIndex);
MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
PointerPte += 1;
}
#endif
//
// Remove the top level page directory page.
//
PageFrameIndex = MI_GET_DIRECTORY_FRAME_FROM_PROCESS(Process);
#if (_MI_PAGING_LEVELS >= 3)
//
// Get a pointer to the top-level page directory parent page via
// its KSEG0 address.
//
#if (_MI_PAGING_LEVELS >= 4)
ExtendedPageDirectoryParent = (PMMPTE) MiMapPageInHyperSpaceAtDpc (
CurrentProcess,
PageFrameIndex);
//
// Remove the hyper space page directory parent page
// from the deleted process.
//
PointerPxe = &ExtendedPageDirectoryParent[MiGetPxeOffset(HYPER_SPACE)];
PageFrameIndex3 = MI_GET_PAGE_FRAME_FROM_PTE(PointerPxe);
ASSERT (MI_PFN_ELEMENT(PageFrameIndex3)->u4.PteFrame == PageFrameIndex);
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, ExtendedPageDirectoryParent);
PageDirectoryParent = (PMMPTE) MiMapPageInHyperSpaceAtDpc (
CurrentProcess,
PageFrameIndex3);
#else
PageDirectoryParent = KSEG_ADDRESS (PageFrameIndex);
#endif
//
// Remove the hyper space page directory page from the deleted process.
//
PointerPpe = &PageDirectoryParent[MiGetPpeOffset(HYPER_SPACE)];
PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE(PointerPpe);
#if (_MI_PAGING_LEVELS >= 4)
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryParent);
#endif
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex2);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
MiDecrementShareCountOnly (PageFrameIndex2);
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
#if (_MI_PAGING_LEVELS >= 4)
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex3);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
MiDecrementShareCountOnly (PageFrameIndex3);
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
#endif
#endif
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareAndValidCount (PageFrameIndex);
MiDecrementShareCountOnly (PageFrameIndex);
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
}
else {
//
// Process initialization never completed, just return the pages
// to the free list.
//
MiInsertPageInFreeList (Process->WorkingSetPage);
PageFrameIndex = MI_GET_DIRECTORY_FRAME_FROM_PROCESS (Process);
#if (_MI_PAGING_LEVELS >= 3)
//
// Get a pointer to the top-level page directory parent page via
// its KSEG0 address.
//
PageDirectoryParent = KSEG_ADDRESS (PageFrameIndex);
#if (_MI_PAGING_LEVELS >= 4)
PageDirectoryParent = (PMMPTE) MiMapPageInHyperSpaceAtDpc (
CurrentProcess,
PageFrameIndex);
PageFrameIndex3 = MI_GET_PAGE_FRAME_FROM_PTE (&PageDirectoryParent[MiGetPxeOffset(HYPER_SPACE)]);
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryParent);
PageDirectoryParent = (PMMPTE) MiMapPageInHyperSpaceAtDpc (
CurrentProcess,
PageFrameIndex3);
#endif
PointerPpe = &PageDirectoryParent[MiGetPpeOffset(HYPER_SPACE)];
PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE(PointerPpe);
#if (_MI_PAGING_LEVELS >= 4)
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryParent);
#endif
MiInsertPageInFreeList (PageFrameIndex2);
#if (_MI_PAGING_LEVELS >= 4)
MiInsertPageInFreeList (PageFrameIndex3);
#endif
#endif
PageFrameIndex2 = MI_GET_HYPER_PAGE_TABLE_FRAME_FROM_PROCESS (Process);
PointerPte = (PMMPTE)MiMapPageInHyperSpaceAtDpc (CurrentProcess,
PageFrameIndex2);
#if defined (_X86PAE_)
HyperPage2 = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
#endif
VadBitMapPage = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte + MiGetPteOffset(VAD_BITMAP_SPACE));
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PointerPte);
//
// Free the VAD bitmap page.
//
MiInsertPageInFreeList (VadBitMapPage);
//
// Free the first hyper space page table page.
//
MiInsertPageInFreeList (PageFrameIndex2);
#if defined (_X86PAE_)
MiInsertPageInFreeList (HyperPage2);
PointerPte = (PMMPTE) PaeVa;
for (i = 0; i < PD_PER_SYSTEM - 1; i += 1) {
PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
MiInsertPageInFreeList (PageFrameIndex2);
PointerPte += 1;
}
#endif
//
// Free the topmost page directory page.
//
MiInsertPageInFreeList (PageFrameIndex);
}
MmResidentAvailablePages += MM_PROCESS_CREATE_CHARGE;
MM_BUMP_COUNTER(7, MM_PROCESS_CREATE_CHARGE);
UNLOCK_PFN (OldIrql);
#if defined (_X86PAE_)
//
// Free the page directory page pointers.
//
MiPaeFree (PaeVa);
#endif
if (Process->Session != NULL) {
//
// The Terminal Server session space data page and mapping PTE can only
// be freed when the last process in the session is deleted. This is
// because IA64 maps session space into region 1 and exited processes
// maintain their session space mapping as attaches may occur even
// after process exit that reference win32k, etc. Since the region 1
// mapping is being inserted into region registers during swap context,
// these mappings cannot be torn down until the very last deletion
// occurs.
//
MiReleaseProcessReferenceToSessionDataPage (Process->Session);
}
//
// Check to see if the paging files should be contracted.
//
MiContractPagingFiles ();
return;
}
VOID
MiDeletePteRange (
IN PEPROCESS Process,
IN PMMPTE PointerPte,
IN PMMPTE LastPte,
IN LOGICAL AddressSpaceDeletion
)
/*++
Routine Description:
This routine deletes a range of PTEs and when possible, the PDEs, PPEs and
PXEs as well. Commit is returned here for the hierarchies here.
Arguments:
Process - Supplies the process whose PTEs are being deleted.
PointerPte - Supplies the PTE to begin deleting at.
LastPte - Supplies the PTE to stop deleting at (don't delete this one).
-1 signifies keep going until a nonvalid PTE is found.
AddressSpaceDeletion - Supplies TRUE if the address space is in the final
stages of deletion, FALSE otherwise.
Return Value:
None.
Environment:
Kernel mode, APCs disabled.
--*/
{
PVOID TempVa;
KIRQL OldIrql;
MMPTE_FLUSH_LIST PteFlushList;
PFN_NUMBER CommittedPages;
#if (_MI_PAGING_LEVELS >= 3)
PMMPTE PointerPpe;
PMMPTE PointerPde;
LOGICAL Boundary;
LOGICAL FinalPte;
PMMPFN Pfn1;
#endif
#if (_MI_PAGING_LEVELS >= 4)
PMMPTE PointerPxe;
#endif
if (PointerPte >= LastPte) {
return;
}
CommittedPages = 0;
PteFlushList.Count = 0;
//
// Initializing OldIrql is not needed for correctness
// but without it the compiler cannot compile this code
// W4 to check for use of uninitialized variables.
//
OldIrql = PASSIVE_LEVEL;
//
// Final address space deletion is done with the PFN lock already held.
//
if (AddressSpaceDeletion == FALSE) {
LOCK_PFN (OldIrql);
}
#if (_MI_PAGING_LEVELS >= 3)
PointerPpe = MiGetPdeAddress (PointerPte);
PointerPde = MiGetPteAddress (PointerPte);
#if (_MI_PAGING_LEVELS >= 4)
PointerPxe = MiGetPpeAddress (PointerPte);
if ((PointerPxe->u.Hard.Valid == 1) &&
(PointerPpe->u.Hard.Valid == 1) &&
(PointerPde->u.Hard.Valid == 1) &&
(PointerPte->u.Hard.Valid == 1))
#else
if ((PointerPpe->u.Hard.Valid == 1) &&
(PointerPde->u.Hard.Valid == 1) &&
(PointerPte->u.Hard.Valid == 1))
#endif
{
do {
ASSERT (PointerPte->u.Hard.Valid == 1);
TempVa = MiGetVirtualAddressMappedByPte(PointerPte);
MiDeletePte (PointerPte,
TempVa,
AddressSpaceDeletion,
Process,
NULL,
&PteFlushList);
CommittedPages += 1;
PointerPte += 1;
Process->NumberOfPrivatePages += 1;
//
// If all the entries have been removed from the previous page
// table page, delete the page table page itself. Likewise with
// the page directory page.
//
if (MiIsPteOnPdeBoundary(PointerPte)) {
Boundary = TRUE;
}
else {
Boundary = FALSE;
}
if ((PointerPte >= LastPte) ||
#if (_MI_PAGING_LEVELS >= 4)
((MiGetPpeAddress(PointerPte))->u.Hard.Valid == 0) ||
#endif
((MiGetPdeAddress(PointerPte))->u.Hard.Valid == 0) ||
((MiGetPteAddress(PointerPte))->u.Hard.Valid == 0) ||
(PointerPte->u.Hard.Valid == 0)) {
FinalPte = TRUE;
}
else {
FinalPte = FALSE;
}
if ((Boundary == TRUE) || (FinalPte == TRUE)) {
MiFlushPteList (&PteFlushList, FALSE, ZeroPte);
PointerPde = MiGetPteAddress (PointerPte - 1);
ASSERT (PointerPde->u.Hard.Valid == 1);
Pfn1 = MI_PFN_ELEMENT (MI_GET_PAGE_FRAME_FROM_PTE (PointerPde));
if (Pfn1->u2.ShareCount == 1 && Pfn1->u3.e2.ReferenceCount == 1)
{
MiDeletePte (PointerPde,
PointerPte - 1,
AddressSpaceDeletion,
Process,
NULL,
NULL);
CommittedPages += 1;
Process->NumberOfPrivatePages += 1;
if ((FinalPte == TRUE) || (MiIsPteOnPpeBoundary(PointerPte))) {
PointerPpe = MiGetPteAddress (PointerPde);
ASSERT (PointerPpe->u.Hard.Valid == 1);
Pfn1 = MI_PFN_ELEMENT (MI_GET_PAGE_FRAME_FROM_PTE (PointerPpe));
if (Pfn1->u2.ShareCount == 1 && Pfn1->u3.e2.ReferenceCount == 1)
{
MiDeletePte (PointerPpe,
PointerPde,
AddressSpaceDeletion,
Process,
NULL,
NULL);
CommittedPages += 1;
Process->NumberOfPrivatePages += 1;
#if (_MI_PAGING_LEVELS >= 4)
if ((FinalPte == TRUE) || (MiIsPteOnPxeBoundary(PointerPte))) {
PointerPxe = MiGetPdeAddress (PointerPde);
ASSERT (PointerPxe->u.Hard.Valid == 1);
Pfn1 = MI_PFN_ELEMENT (MI_GET_PAGE_FRAME_FROM_PTE (PointerPxe));
if (Pfn1->u2.ShareCount == 1 && Pfn1->u3.e2.ReferenceCount == 1)
{
MiDeletePte (PointerPxe,
PointerPpe,
AddressSpaceDeletion,
Process,
NULL,
NULL);
CommittedPages += 1;
Process->NumberOfPrivatePages += 1;
}
}
#endif
}
}
}
if (FinalPte == TRUE) {
break;
}
}
ASSERT (PointerPte->u.Hard.Valid == 1);
} while (TRUE);
}
#else
while (PointerPte->u.Hard.Valid) {
TempVa = MiGetVirtualAddressMappedByPte(PointerPte);
MiDeletePte (PointerPte,
TempVa,
AddressSpaceDeletion,
Process,
NULL,
&PteFlushList);
CommittedPages += 1;
Process->NumberOfPrivatePages += 1;
PointerPte += 1;
if (PointerPte >= LastPte) {
break;
}
}
#endif
if (PteFlushList.Count != 0) {
MiFlushPteList (&PteFlushList, FALSE, ZeroPte);
}
if (AddressSpaceDeletion == FALSE) {
UNLOCK_PFN (OldIrql);
}
if (CommittedPages != 0) {
MiReturnCommitment (CommittedPages);
MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PTE_RANGE, CommittedPages);
}
return;
}
VOID
MmCleanProcessAddressSpace (
IN PEPROCESS Process
)
/*++
Routine Description:
This routine cleans an address space by deleting all the user and
pagable portions of the address space. At the completion of this
routine, no page faults may occur within the process.
Arguments:
None.
Return Value:
None.
Environment:
Kernel mode, APCs disabled.
--*/
{
PKTHREAD CurrentThread;
PMMVAD Vad;
KEVENT Event;
KIRQL OldIrql;
PMMPTE LastPte;
PMMPTE PointerPte;
LONG AboveWsMin;
ULONG NumberOfCommittedPageTables;
#if defined (_WIN64)
PWOW64_PROCESS TempWow64;
#endif
if ((Process->Flags & PS_PROCESS_FLAGS_VM_DELETED) ||
(Process->AddressSpaceInitialized == 0)) {
//
// This process's address space has already been deleted. However,
// this process can still have a session space. Get rid of it now.
//
MiSessionRemoveProcess ();
return;
}
if (Process->AddressSpaceInitialized == 1) {
//
// The process has been created but not fully initialized.
// Return partial resources now.
//
LOCK_PFN (OldIrql);
MmResidentAvailablePages += (Process->Vm.MinimumWorkingSetSize -
MM_PROCESS_CREATE_CHARGE);
MM_BUMP_COUNTER(41, Process->Vm.MinimumWorkingSetSize -
MM_PROCESS_CREATE_CHARGE);
UNLOCK_PFN (OldIrql);
//
// Clear the AddressSpaceInitialized flag so we don't over-return
// resident available as this routine can be called more than once
// for the same process.
//
PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_ADDRESS_SPACE1);
ASSERT (Process->AddressSpaceInitialized == 0);
//
// This process's address space has already been deleted. However,
// this process can still have a session space. Get rid of it now.
//
MiSessionRemoveProcess ();
return;
}
//
// If working set expansion for this process is allowed, disable
// it and remove the process from expanded process list if it
// is on it.
//
LOCK_EXPANSION (OldIrql);
if (Process->Vm.Flags.BeingTrimmed) {
//
// Initialize an event and put the event address
// in the blink field. When the trimming is complete,
// this event will be set.
//
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Process->Vm.WorkingSetExpansionLinks.Blink = (PLIST_ENTRY)&Event;
//
// Release the mutex and wait for the event.
//
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread (CurrentThread);
UNLOCK_EXPANSION_AND_THEN_WAIT (OldIrql);
KeWaitForSingleObject(&Event,
WrVirtualMemory,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
KeLeaveCriticalRegionThread (CurrentThread);
}
else if (Process->Vm.WorkingSetExpansionLinks.Flink == MM_NO_WS_EXPANSION) {
//
// No trimming is in progress and no expansion allowed, so this cannot
// be on any lists.
//
ASSERT (Process->Vm.WorkingSetExpansionLinks.Blink != MM_WS_EXPANSION_IN_PROGRESS);
UNLOCK_EXPANSION (OldIrql);
}
else {
RemoveEntryList (&Process->Vm.WorkingSetExpansionLinks);
//
// Disable expansion.
//
Process->Vm.WorkingSetExpansionLinks.Flink = MM_NO_WS_EXPANSION;
UNLOCK_EXPANSION (OldIrql);
}
MiSessionRemoveProcess ();
PointerPte = MiGetPteAddress (&MmWsle[MM_MAXIMUM_WORKING_SET]) + 1;
//
// Delete all the user owned pagable virtual addresses in the process.
//
//
// Both mutexes must be owned to synchronize with the bit setting and
// clearing of VM_DELETED. This is because various callers acquire
// only one of them (either one) before checking.
//
LOCK_WS_AND_ADDRESS_SPACE (Process);
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_VM_DELETED);
//
// Delete all the valid user mode addresses from the working set
// list. At this point NO page faults are allowed on user space
// addresses. Faults are allowed on page tables for user space, which
// requires that we keep the working set structure consistent until we
// finally take it all down.
//
MiDeleteAddressesInWorkingSet (Process);
//
// Remove hash table pages, if any. This is the first time we do this
// during the deletion path, but we need to do it again before we finish
// because we may fault in some page tables during the VAD clearing. We
// could have maintained the hash table validity during the WorkingSet
// deletion above in order to avoid freeing the hash table twice, but since
// we're just deleting it all anyway, it's faster to do it this way. Note
// that if we don't do this or maintain the validity, we can trap later
// in MiGrowWsleHash.
//
LastPte = MiGetPteAddress (MmWorkingSetList->HighestPermittedHashAddress);
MiDeletePteRange (Process, PointerPte, LastPte, FALSE);
//
// Clear the hash fields as a fault may occur below on the page table
// pages during VAD clearing and resolution of the fault may result in
// adding a hash table. Thus these fields must be consistent with the
// clearing just done above.
//
MmWorkingSetList->HashTableSize = 0;
MmWorkingSetList->HashTable = NULL;
//
// Delete the virtual address descriptors and dereference any
// section objects.
//
Vad = Process->VadRoot;
while (Vad != (PMMVAD)NULL) {
MiRemoveVad (Vad);
//
// If the system has been biased to an alternate base address to
// allow 3gb of user address space, then check if the current VAD
// describes the shared memory page.
//
#if defined(_X86_) && defined(MM_SHARED_USER_DATA_VA)
if (MmVirtualBias != 0) {
//
// If the VAD describes the shared memory page, then free the
// VAD and continue with the next entry.
//
if (Vad->StartingVpn == MI_VA_TO_VPN (MM_SHARED_USER_DATA_VA)) {
ASSERT (MmHighestUserAddress > (PVOID) MM_SHARED_USER_DATA_VA);
goto LoopEnd;
}
}
#endif
if (((Vad->u.VadFlags.PrivateMemory == 0) &&
(Vad->ControlArea != NULL)) ||
(Vad->u.VadFlags.PhysicalMapping == 1)) {
//
// This VAD represents a mapped view or a driver-mapped physical
// view - delete the view and perform any section related cleanup
// operations.
//
MiRemoveMappedView (Process, Vad);
}
else {
if (Vad->u.VadFlags.UserPhysicalPages == 1) {
//
// Free all the physical pages that this VAD might be mapping.
// Since only the AWE lock synchronizes the remap API, carefully
// remove this VAD from the list first.
//
MiAweViewRemover (Process, Vad);
MiRemoveUserPhysicalPagesVad ((PMMVAD_SHORT)Vad);
MiDeletePageTablesForPhysicalRange (
MI_VPN_TO_VA (Vad->StartingVpn),
MI_VPN_TO_VA_ENDING (Vad->EndingVpn));
}
else {
if (Vad->u.VadFlags.WriteWatch == 1) {
MiPhysicalViewRemover (Process, Vad);
}
LOCK_PFN (OldIrql);
//
// Don't specify address space deletion as TRUE as
// the working set must be consistent as page faults may
// be taken during clone removal, protoPTE lookup, etc.
//
MiDeleteVirtualAddresses (MI_VPN_TO_VA (Vad->StartingVpn),
MI_VPN_TO_VA_ENDING (Vad->EndingVpn),
FALSE,
Vad);
UNLOCK_PFN (OldIrql);
}
}
#if defined(_X86_) && defined(MM_SHARED_USER_DATA_VA)
LoopEnd:
#endif
ExFreePool (Vad);
Vad = Process->VadRoot;
}
ASSERT (Process->NumberOfVads == 0);
ASSERT (IsListEmpty (&Process->PhysicalVadList) != 0);
MiCleanPhysicalProcessPages (Process);
//
// Delete the shared data page, if any.
//
LOCK_PFN (OldIrql);
#if defined(MM_SHARED_USER_DATA_VA)
#if defined (_X86_)
if (MmHighestUserAddress > (PVOID) MM_SHARED_USER_DATA_VA) {
#endif
MiDeleteVirtualAddresses ((PVOID) MM_SHARED_USER_DATA_VA,
(PVOID) MM_SHARED_USER_DATA_VA,
FALSE,
NULL);
#if defined (_X86_)
}
#endif
#endif
//
// Delete the system portion of the address space.
// Only now is it safe to specify TRUE to MiDelete because now that the
// VADs have been deleted we can no longer fault on user space pages.
//
Process->Vm.Flags.AddressSpaceBeingDeleted = 1;
//
// Adjust the count of pages above working set maximum. This
// must be done here because the working set list is not
// updated during this deletion.
//
AboveWsMin = (LONG)Process->Vm.WorkingSetSize - (LONG)Process->Vm.MinimumWorkingSetSize;
if (AboveWsMin > 0) {
MmPagesAboveWsMinimum -= AboveWsMin;
}
UNLOCK_PFN (OldIrql);
//
// Return commitment for page table pages.
//
NumberOfCommittedPageTables = MmWorkingSetList->NumberOfCommittedPageTables;
#if (_MI_PAGING_LEVELS >= 3)
NumberOfCommittedPageTables += MmWorkingSetList->NumberOfCommittedPageDirectories;
#endif
#if (_MI_PAGING_LEVELS >= 4)
NumberOfCommittedPageTables += MmWorkingSetList->NumberOfCommittedPageDirectoryParents;
#endif
MiReturnCommitment (NumberOfCommittedPageTables);
MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PROCESS_CLEAN_PAGETABLES,
NumberOfCommittedPageTables);
if (Process->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
PsChangeJobMemoryUsage(-(SSIZE_T)NumberOfCommittedPageTables);
}
Process->CommitCharge -= NumberOfCommittedPageTables;
PsReturnProcessPageFileQuota (Process, NumberOfCommittedPageTables);
MI_INCREMENT_TOTAL_PROCESS_COMMIT (0 - NumberOfCommittedPageTables);
#if (_MI_PAGING_LEVELS >= 3)
if (MmWorkingSetList->CommittedPageTables != NULL) {
ExFreePool (MmWorkingSetList->CommittedPageTables);
MmWorkingSetList->CommittedPageTables = NULL;
}
#endif
#if (_MI_PAGING_LEVELS >= 4)
if (MmWorkingSetList->CommittedPageDirectories != NULL) {
ExFreePool (MmWorkingSetList->CommittedPageDirectories);
MmWorkingSetList->CommittedPageDirectories = NULL;
}
#endif
//
// Check to make sure all the clone descriptors went away.
//
ASSERT (Process->CloneRoot == (PMMCLONE_DESCRIPTOR)NULL);
if (Process->NumberOfLockedPages != 0) {
if (Process->LockedPagesList) {
PLIST_ENTRY NextEntry;
PLOCK_TRACKER Tracker;
PLOCK_HEADER LockedPagesHeader;
LockedPagesHeader = (PLOCK_HEADER)Process->LockedPagesList;
if ((LockedPagesHeader->Count != 0) && (MiTrackingAborted == FALSE)) {
ASSERT (IsListEmpty (&LockedPagesHeader->ListHead) == 0);
NextEntry = LockedPagesHeader->ListHead.Flink;
Tracker = CONTAINING_RECORD (NextEntry,
LOCK_TRACKER,
ListEntry);
KeBugCheckEx (DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS,
(ULONG_PTR)Tracker->CallingAddress,
(ULONG_PTR)Tracker->CallersCaller,
(ULONG_PTR)Tracker->Mdl,
Process->NumberOfLockedPages);
}
}
KeBugCheckEx (PROCESS_HAS_LOCKED_PAGES,
0,
(ULONG_PTR)Process,
Process->NumberOfLockedPages,
(ULONG_PTR)Process->LockedPagesList);
return;
}
if (Process->LockedPagesList) {
ASSERT (MmTrackLockedPages == TRUE);
ExFreePool (Process->LockedPagesList);
Process->LockedPagesList = NULL;
}
#if DBG
if ((Process->NumberOfPrivatePages != 0) && (MmDebug & MM_DBG_PRIVATE_PAGES)) {
DbgPrint("MM: Process contains private pages %ld\n",
Process->NumberOfPrivatePages);
DbgBreakPoint();
}
#endif
#if defined(_WIN64)
//
// Delete the WowProcess structure.
//
if (Process->Wow64Process != NULL) {
#if defined(_MIALT4K_)
MiDeleteAlternateTable(Process);
#endif
TempWow64 = Process->Wow64Process;
Process->Wow64Process = NULL;
ExFreePool (TempWow64);
}
#endif
//
// Remove the working set list pages (except for the first one).
// These pages are not removed because DPCs could still occur within
// the address space. In a DPC, nonpagedpool could be allocated
// which could require removing a page from the standby list, requiring
// hyperspace to map the previous PTE.
//
PointerPte = MiGetPteAddress (MmWorkingSetList) + 1;
LOCK_PFN (OldIrql);
MiDeletePteRange (Process, PointerPte, (PMMPTE)-1, TRUE);
//
// Remove hash table pages, if any. Yes, we've already done this once
// during the deletion path, but we need to do it again because we may
// have faulted in some page tables during the VAD clearing.
//
PointerPte = MiGetPteAddress (&MmWsle[MM_MAXIMUM_WORKING_SET]) + 1;
ASSERT (PointerPte < LastPte);
MiDeletePteRange (Process, PointerPte, LastPte, TRUE);
//
// Update the count of available resident pages.
//
ASSERT (Process->Vm.MinimumWorkingSetSize >= MM_PROCESS_CREATE_CHARGE);
MmResidentAvailablePages += Process->Vm.MinimumWorkingSetSize -
MM_PROCESS_CREATE_CHARGE;
MM_BUMP_COUNTER(8, Process->Vm.MinimumWorkingSetSize -
MM_PROCESS_CREATE_CHARGE);
ASSERT (Process->Vm.WorkingSetExpansionLinks.Flink == MM_NO_WS_EXPANSION);
UNLOCK_PFN (OldIrql);
UNLOCK_WS_AND_ADDRESS_SPACE (Process);
return;
}
#if !defined(_IA64_)
#define KERNEL_BSTORE_SIZE 0
#define KERNEL_LARGE_BSTORE_SIZE 0
#define KERNEL_LARGE_BSTORE_COMMIT 0
#define KERNEL_STACK_GUARD_PAGES 1
#else
#define KERNEL_STACK_GUARD_PAGES 2 // One for stack, one for RSE.
#endif
PVOID
MmCreateKernelStack (
IN BOOLEAN LargeStack,
IN UCHAR PreferredNode
)
/*++
Routine Description:
This routine allocates a kernel stack and a no-access page within
the non-pagable portion of the system address space.
Arguments:
LargeStack - Supplies the value TRUE if a large stack should be
created. FALSE if a small stack is to be created.
PreferredNode - Supplies the preferred node to use for the physical
page allocations. MP/NUMA systems only.
Return Value:
Returns a pointer to the base of the kernel stack. Note, that the
base address points to the guard page, so space must be allocated
on the stack before accessing the stack.
If a kernel stack cannot be created, the value NULL is returned.
Environment:
Kernel mode. APCs Disabled.
--*/
{
PMMPFN Pfn1;
PMMPTE PointerPte;
PMMPTE BasePte;
MMPTE TempPte;
PFN_NUMBER NumberOfPages;
ULONG NumberOfPtes;
ULONG ChargedPtes;
ULONG RequestedPtes;
ULONG NumberOfBackingStorePtes;
PFN_NUMBER PageFrameIndex;
ULONG i;
PVOID StackVa;
KIRQL OldIrql;
PSLIST_HEADER DeadStackList;
if (!LargeStack) {
//
// Check to see if any unused stacks are available.
//
#if defined(MI_MULTINODE)
DeadStackList = &KeNodeBlock[PreferredNode]->DeadStackList;
#else
UNREFERENCED_PARAMETER (PreferredNode);
DeadStackList = &MmDeadStackSListHead;
#endif
if (ExQueryDepthSList (DeadStackList) != 0) {
Pfn1 = (PMMPFN) InterlockedPopEntrySList (DeadStackList);
if (Pfn1 != NULL) {
PointerPte = Pfn1->PteAddress;
PointerPte += 1;
StackVa = (PVOID)MiGetVirtualAddressMappedByPte (PointerPte);
return StackVa;
}
}
NumberOfPtes = BYTES_TO_PAGES (KERNEL_STACK_SIZE);
NumberOfBackingStorePtes = BYTES_TO_PAGES (KERNEL_BSTORE_SIZE);
NumberOfPages = NumberOfPtes + NumberOfBackingStorePtes;
}
else {
NumberOfPtes = BYTES_TO_PAGES (MI_LARGE_STACK_SIZE);
NumberOfBackingStorePtes = BYTES_TO_PAGES (KERNEL_LARGE_BSTORE_SIZE);
NumberOfPages = BYTES_TO_PAGES (KERNEL_LARGE_STACK_COMMIT
+ KERNEL_LARGE_BSTORE_COMMIT);
}
ChargedPtes = NumberOfPtes + NumberOfBackingStorePtes;
//
// Charge commitment for the page file space for the kernel stack.
//
if (MiChargeCommitment (ChargedPtes, NULL) == FALSE) {
//
// Commitment exceeded, return NULL, indicating no kernel
// stacks are available.
//
return NULL;
}
//
// Obtain enough pages to contain the stack plus a guard page from
// the system PTE pool. The system PTE pool contains nonpaged PTEs
// which are currently empty.
//
// Note for IA64, the PTE allocation is divided between kernel stack
// and RSE space. The stack grows downward and the RSE grows upward.
//
RequestedPtes = ChargedPtes + KERNEL_STACK_GUARD_PAGES;
BasePte = MiReserveSystemPtes (RequestedPtes, SystemPteSpace);
if (BasePte == NULL) {
MiReturnCommitment (ChargedPtes);
return NULL;
}
PointerPte = BasePte;
StackVa = (PVOID)MiGetVirtualAddressMappedByPte (PointerPte + NumberOfPtes + 1);
if (LargeStack) {
PointerPte += BYTES_TO_PAGES (MI_LARGE_STACK_SIZE - KERNEL_LARGE_STACK_COMMIT);
}
LOCK_PFN (OldIrql);
//
// Check to make sure the physical pages are available.
//
if (MI_NONPAGABLE_MEMORY_AVAILABLE() <= (SPFN_NUMBER)NumberOfPages) {
UNLOCK_PFN (OldIrql);
MiReleaseSystemPtes (BasePte, RequestedPtes, SystemPteSpace);
MiReturnCommitment (ChargedPtes);
return NULL;
}
MM_TRACK_COMMIT (MM_DBG_COMMIT_KERNEL_STACK_CREATE, ChargedPtes);
MmResidentAvailablePages -= NumberOfPages;
MM_BUMP_COUNTER(9, NumberOfPages);
for (i = 0; i < NumberOfPages; i += 1) {
PointerPte += 1;
ASSERT (PointerPte->u.Hard.Valid == 0);
MiEnsureAvailablePageOrWait (NULL, NULL);
PageFrameIndex = MiRemoveAnyPage (
MI_GET_PAGE_COLOR_NODE (PreferredNode));
PointerPte->u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
PointerPte->u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
MiInitializePfn (PageFrameIndex, PointerPte, 1);
MI_MAKE_VALID_PTE (TempPte,
PageFrameIndex,
MM_READWRITE,
PointerPte);
MI_SET_PTE_DIRTY (TempPte);
MI_WRITE_VALID_PTE (PointerPte, TempPte);
}
MmProcessCommit += ChargedPtes;
MmKernelStackResident += NumberOfPages;
MmLargeStacks += LargeStack;
MmSmallStacks += !LargeStack;
MmKernelStackPages += RequestedPtes;
UNLOCK_PFN (OldIrql);
return StackVa;
}
VOID
MmDeleteKernelStack (
IN PVOID PointerKernelStack,
IN BOOLEAN LargeStack
)
/*++
Routine Description:
This routine deletes a kernel stack and the no-access page within
the non-pagable portion of the system address space.
Arguments:
PointerKernelStack - Supplies a pointer to the base of the kernel stack.
LargeStack - Supplies the value TRUE if a large stack is being deleted.
FALSE if a small stack is to be deleted.
Return Value:
None.
Environment:
Kernel mode. APCs Disabled.
--*/
{
PMMPTE PointerPte;
PMMPFN Pfn1;
PMMPFN Pfn2;
PFN_NUMBER NumberOfPages;
ULONG NumberOfPtes;
ULONG NumberOfStackPtes;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER PageTableFrameIndex;
ULONG i;
KIRQL OldIrql;
MMPTE PteContents;
PSLIST_HEADER DeadStackList;
PointerPte = MiGetPteAddress (PointerKernelStack);
//
// PointerPte points to the guard page, point to the previous
// page before removing physical pages.
//
PointerPte -= 1;
//
// Check to see if the stack page should be placed on the dead
// kernel stack page list. The dead kernel stack list is a
// singly linked list of kernel stacks from terminated threads.
// The stacks are saved on a linked list up to a maximum number
// to avoid the overhead of flushing the entire TB on all processors
// everytime a thread terminates. The TB on all processors must
// be flushed as kernel stacks reside in the non paged system part
// of the address space.
//
if (!LargeStack) {
#if defined(MI_MULTINODE)
//
// Scan the physical page frames and only place this stack on the
// dead stack list if all the pages are on the same node. Realize
// if this push goes cross node it may make the interlocked instruction
// slightly more expensive, but worth it all things considered.
//
ULONG NodeNumber;
PteContents = *PointerPte;
ASSERT (PteContents.u.Hard.Valid == 1);
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
NodeNumber = Pfn1->u3.e1.PageColor;
DeadStackList = &KeNodeBlock[NodeNumber]->DeadStackList;
#else
DeadStackList = &MmDeadStackSListHead;
#endif
NumberOfPtes = BYTES_TO_PAGES (KERNEL_STACK_SIZE + KERNEL_BSTORE_SIZE);
if (ExQueryDepthSList (DeadStackList) < MmMaximumDeadKernelStacks) {
#if defined(MI_MULTINODE)
//
// The node could use some more dead stacks - but first make sure
// all the physical pages are from the same node in a multinode
// system.
//
if (KeNumberNodes > 1) {
ULONG CheckPtes;
//
// Note IA64 RSE space is not included for checking purposes
// since it's never trimmed for small stacks.
//
CheckPtes = BYTES_TO_PAGES (KERNEL_STACK_SIZE);
PointerPte -= 1;
for (i = 1; i < CheckPtes; i += 1) {
PteContents = *PointerPte;
if (PteContents.u.Hard.Valid == 0) {
break;
}
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
if (NodeNumber != Pfn1->u3.e1.PageColor) {
PointerPte += i;
goto FreeStack;
}
PointerPte -= 1;
}
PointerPte += CheckPtes;
}
#endif
Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
InterlockedPushEntrySList (DeadStackList, &Pfn1->u1.NextStackPfn);
PERFINFO_DELETE_STACK(PointerPte, NumberOfPtes);
return;
}
}
else {
NumberOfPtes = BYTES_TO_PAGES (MI_LARGE_STACK_SIZE + KERNEL_LARGE_BSTORE_SIZE);
}
#if defined(MI_MULTINODE)
FreeStack:
#endif
#if defined(_IA64_)
//
// Note on IA64, PointerKernelStack points to the center of the stack space,
// the size of kernel backing store needs to be added to get the
// top of the stack space.
//
if (LargeStack) {
PointerPte = MiGetPteAddress (
(PCHAR)PointerKernelStack + KERNEL_LARGE_BSTORE_SIZE);
}
else {
PointerPte = MiGetPteAddress (
(PCHAR)PointerKernelStack + KERNEL_BSTORE_SIZE);
}
//
// PointerPte points to the guard page, point to the previous
// page before removing physical pages.
//
PointerPte -= 1;
#endif
//
// We have exceeded the limit of dead kernel stacks or this is a large
// stack, delete this kernel stack.
//
NumberOfPages = 0;
NumberOfStackPtes = NumberOfPtes + KERNEL_STACK_GUARD_PAGES;
LOCK_PFN (OldIrql);
for (i = 0; i < NumberOfPtes; i += 1) {
PteContents = *PointerPte;
if (PteContents.u.Hard.Valid == 1) {
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
PageTableFrameIndex = Pfn1->u4.PteFrame;
Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
//
// Mark the page as deleted so it will be freed when the
// reference count goes to zero.
//
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareCountOnly (PageFrameIndex);
NumberOfPages += 1;
}
PointerPte -= 1;
}
//
// Now at the stack guard page, ensure it is still a guard page.
//
ASSERT (PointerPte->u.Hard.Valid == 0);
//
// Update the count of resident available pages.
//
MmResidentAvailablePages += NumberOfPages;
MM_BUMP_COUNTER(10, NumberOfPages);
MmKernelStackPages -= NumberOfStackPtes;
MmKernelStackResident -= NumberOfPages;
MmProcessCommit -= NumberOfPtes;
MmLargeStacks -= LargeStack;
MmSmallStacks -= !LargeStack;
UNLOCK_PFN (OldIrql);
//
// Return PTEs and commitment.
//
MiReleaseSystemPtes (PointerPte, NumberOfStackPtes, SystemPteSpace);
MiReturnCommitment (NumberOfPtes);
MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_KERNEL_STACK_DELETE, NumberOfPtes);
return;
}
#if defined(_IA64_)
ULONG MiStackGrowthFailures[2];
#else
ULONG MiStackGrowthFailures[1];
#endif
NTSTATUS
MmGrowKernelStack (
IN PVOID CurrentStack
)
/*++
Routine Description:
This function attempts to grows the current thread's kernel stack
such that there is always KERNEL_LARGE_STACK_COMMIT bytes below
the current stack pointer.
Arguments:
CurrentStack - Supplies a pointer to the current stack pointer.
Return Value:
STATUS_SUCCESS is returned if the stack was grown.
STATUS_STACK_OVERFLOW is returned if there was not enough space reserved
for the commitment.
STATUS_NO_MEMORY is returned if there was not enough physical memory
in the system.
--*/
{
PMMPTE NewLimit;
PMMPTE StackLimit;
PMMPTE EndStack;
PETHREAD Thread;
PFN_NUMBER NumberOfPages;
KIRQL OldIrql;
PFN_NUMBER PageFrameIndex;
MMPTE TempPte;
Thread = PsGetCurrentThread ();
ASSERT (((PCHAR)Thread->Tcb.StackBase - (PCHAR)Thread->Tcb.StackLimit) <=
((LONG)MI_LARGE_STACK_SIZE + PAGE_SIZE));
StackLimit = MiGetPteAddress (Thread->Tcb.StackLimit);
ASSERT (StackLimit->u.Hard.Valid == 1);
NewLimit = MiGetPteAddress ((PVOID)((PUCHAR)CurrentStack -
KERNEL_LARGE_STACK_COMMIT));
if (NewLimit == StackLimit) {
return STATUS_SUCCESS;
}
//
// If the new stack limit exceeds the reserved region for the kernel
// stack, then return an error.
//
EndStack = MiGetPteAddress ((PVOID)((PUCHAR)Thread->Tcb.StackBase -
MI_LARGE_STACK_SIZE));
if (NewLimit < EndStack) {
//
// Don't go into guard page.
//
MiStackGrowthFailures[0] += 1;
#if DBG
DbgPrint ("MmGrowKernelStack failed: Thread %p %p %p\n",
Thread, NewLimit, EndStack);
#endif
return STATUS_STACK_OVERFLOW;
}
//
// Lock the PFN database and attempt to expand the kernel stack.
//
StackLimit -= 1;
NumberOfPages = (PFN_NUMBER) (StackLimit - NewLimit + 1);
LOCK_PFN (OldIrql);
if (MI_NONPAGABLE_MEMORY_AVAILABLE() <= (SPFN_NUMBER)NumberOfPages) {
UNLOCK_PFN (OldIrql);
return STATUS_NO_MEMORY;
}
//
// Note MmResidentAvailablePages must be charged before calling
// MiEnsureAvailablePageOrWait as it may release the PFN lock.
//
MmResidentAvailablePages -= NumberOfPages;
MM_BUMP_COUNTER(11, NumberOfPages);
while (StackLimit >= NewLimit) {
ASSERT (StackLimit->u.Hard.Valid == 0);
MiEnsureAvailablePageOrWait (NULL, NULL);
PageFrameIndex = MiRemoveAnyPage (MI_GET_PAGE_COLOR_FROM_PTE (StackLimit));
StackLimit->u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
StackLimit->u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
MiInitializePfn (PageFrameIndex, StackLimit, 1);
MI_MAKE_VALID_PTE (TempPte,
PageFrameIndex,
MM_READWRITE,
StackLimit);
MI_SET_PTE_DIRTY (TempPte);
*StackLimit = TempPte;
StackLimit -= 1;
}
MmKernelStackResident += NumberOfPages;
UNLOCK_PFN (OldIrql);
#if DBG
ASSERT (NewLimit->u.Hard.Valid == 1);
if (NewLimit != EndStack) {
ASSERT ((NewLimit - 1)->u.Hard.Valid == 0);
}
#endif
Thread->Tcb.StackLimit = MiGetVirtualAddressMappedByPte (NewLimit);
PERFINFO_GROW_STACK(Thread);
return STATUS_SUCCESS;
}
#if defined(_IA64_)
NTSTATUS
MmGrowKernelBackingStore (
IN PVOID CurrentBackingStorePointer
)
/*++
Routine Description:
This function attempts to grows the backing store for the current thread's
kernel stack such that there is always KERNEL_LARGE_STACK_COMMIT bytes
above the current backing store pointer.
Arguments:
CurrentBackingStorePointer - Supplies a pointer to the current backing
store pointer for the active kernel stack.
Return Value:
NTSTATUS.
--*/
{
PMMPTE NewLimit;
PMMPTE BstoreLimit;
PMMPTE EndStack;
PETHREAD Thread;
PFN_NUMBER NumberOfPages;
KIRQL OldIrql;
PFN_NUMBER PageFrameIndex;
MMPTE TempPte;
Thread = PsGetCurrentThread ();
ASSERT (((PCHAR)Thread->Tcb.BStoreLimit - (PCHAR)Thread->Tcb.StackBase) <=
(KERNEL_LARGE_BSTORE_SIZE + PAGE_SIZE));
BstoreLimit = MiGetPteAddress ((PVOID)((PCHAR)Thread->Tcb.BStoreLimit - 1));
ASSERT (BstoreLimit->u.Hard.Valid == 1);
NewLimit = MiGetPteAddress ((PVOID)((PUCHAR)CurrentBackingStorePointer +
KERNEL_LARGE_BSTORE_COMMIT-1));
if (NewLimit == BstoreLimit) {
return STATUS_SUCCESS;
}
//
// If the new stack limit exceeds the reserved region for the kernel
// stack, then return an error.
//
EndStack = MiGetPteAddress ((PVOID)((PUCHAR)Thread->Tcb.StackBase +
KERNEL_LARGE_BSTORE_SIZE-1));
if (NewLimit > EndStack) {
//
// Don't go into guard page.
//
MiStackGrowthFailures[1] += 1;
#if DBG
DbgPrint ("MmGrowKernelBackingStore failed: Thread %p %p %p\n",
Thread, NewLimit, EndStack);
#endif
return STATUS_STACK_OVERFLOW;
}
//
// Lock the PFN database and attempt to expand the backing store.
//
BstoreLimit += 1;
NumberOfPages = (PFN_NUMBER)(NewLimit - BstoreLimit + 1);
LOCK_PFN (OldIrql);
if (MI_NONPAGABLE_MEMORY_AVAILABLE() <= (SPFN_NUMBER)NumberOfPages) {
UNLOCK_PFN (OldIrql);
return STATUS_NO_MEMORY;
}
//
// Note MmResidentAvailablePages must be charged before calling
// MiEnsureAvailablePageOrWait as it may release the PFN lock.
//
MmResidentAvailablePages -= NumberOfPages;
MM_BUMP_COUNTER(2, NumberOfPages);
while (BstoreLimit <= NewLimit) {
ASSERT (BstoreLimit->u.Hard.Valid == 0);
MiEnsureAvailablePageOrWait (NULL, NULL);
PageFrameIndex = MiRemoveAnyPage (MI_GET_PAGE_COLOR_FROM_PTE (BstoreLimit));
BstoreLimit->u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
BstoreLimit->u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
MiInitializePfn (PageFrameIndex, BstoreLimit, 1);
MI_MAKE_VALID_PTE (TempPte,
PageFrameIndex,
MM_READWRITE,
BstoreLimit);
MI_SET_PTE_DIRTY (TempPte);
*BstoreLimit = TempPte;
BstoreLimit += 1;
}
MmKernelStackResident += NumberOfPages;
UNLOCK_PFN (OldIrql);
#if DBG
ASSERT (NewLimit->u.Hard.Valid == 1);
if (NewLimit != EndStack) {
ASSERT ((NewLimit + 1)->u.Hard.Valid == 0);
}
#endif
Thread->Tcb.BStoreLimit = MiGetVirtualAddressMappedByPte (BstoreLimit);
return STATUS_SUCCESS;
}
#endif // defined(_IA64_)
VOID
MmOutPageKernelStack (
IN PKTHREAD Thread
)
/*++
Routine Description:
This routine makes the specified kernel stack non-resident and
puts the pages on the transition list. Note that pages below
the CurrentStackPointer are not useful and these pages are freed here.
Arguments:
Thread - Supplies a pointer to the thread whose stack should be removed.
Return Value:
None.
Environment:
Kernel mode.
--*/
#define MAX_STACK_PAGES ((KERNEL_LARGE_STACK_SIZE + KERNEL_LARGE_BSTORE_SIZE) / PAGE_SIZE)
{
PMMPTE PointerPte;
PMMPTE LastPte;
PMMPTE EndOfStackPte;
PMMPFN Pfn1;
PMMPFN Pfn2;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER PageTableFrameIndex;
KIRQL OldIrql;
MMPTE TempPte;
PVOID BaseOfKernelStack;
PVOID FlushVa[MAX_STACK_PAGES];
ULONG StackSize;
ULONG Count;
PMMPTE LimitPte;
PMMPTE LowestLivePte;
ASSERT (KERNEL_LARGE_STACK_SIZE >= MI_LARGE_STACK_SIZE);
ASSERT (((PCHAR)Thread->StackBase - (PCHAR)Thread->StackLimit) <=
((LONG)MI_LARGE_STACK_SIZE + PAGE_SIZE));
if (NtGlobalFlag & FLG_DISABLE_PAGE_KERNEL_STACKS) {
return;
}
//
// The first page of the stack is the page before the base
// of the stack.
//
BaseOfKernelStack = ((PCHAR)Thread->StackBase - PAGE_SIZE);
PointerPte = MiGetPteAddress (BaseOfKernelStack);
LastPte = MiGetPteAddress ((PULONG)Thread->KernelStack - 1);
if (Thread->LargeStack) {
StackSize = MI_LARGE_STACK_SIZE >> PAGE_SHIFT;
//
// The stack pagein won't necessarily bring back all the pages.
// Make sure that we account now for the ones that will disappear.
//
LimitPte = MiGetPteAddress (Thread->StackLimit);
LowestLivePte = MiGetPteAddress ((PVOID)((PUCHAR)Thread->InitialStack -
KERNEL_LARGE_STACK_COMMIT));
if (LowestLivePte < LimitPte) {
LowestLivePte = LimitPte;
}
}
else {
StackSize = KERNEL_STACK_SIZE >> PAGE_SHIFT;
LowestLivePte = MiGetPteAddress (Thread->StackLimit);
}
EndOfStackPte = PointerPte - StackSize;
ASSERT (LowestLivePte <= LastPte);
//
// Put a signature at the current stack location - sizeof(ULONG_PTR).
//
*((PULONG_PTR)Thread->KernelStack - 1) = (ULONG_PTR)Thread;
Count = 0;
LOCK_PFN (OldIrql);
do {
ASSERT (PointerPte->u.Hard.Valid == 1);
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
TempPte = *PointerPte;
MI_MAKE_VALID_PTE_TRANSITION (TempPte, 0);
TempPte.u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
Pfn2 = MI_PFN_ELEMENT (PageFrameIndex);
Pfn2->OriginalPte.u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
FlushVa[Count] = BaseOfKernelStack;
MiDecrementShareCount (PageFrameIndex);
PointerPte -= 1;
Count += 1;
BaseOfKernelStack = ((PCHAR)BaseOfKernelStack - PAGE_SIZE);
} while (PointerPte >= LastPte);
//
// Just toss the pages that won't ever come back in.
//
while (PointerPte != EndOfStackPte) {
if (PointerPte->u.Hard.Valid == 0) {
break;
}
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
PageTableFrameIndex = Pfn1->u4.PteFrame;
Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareCountOnly (MI_GET_PAGE_FRAME_FROM_PTE (PointerPte));
TempPte = KernelDemandZeroPte;
TempPte.u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
FlushVa[Count] = BaseOfKernelStack;
Count += 1;
//
// Return resident available for pages beyond the guaranteed portion
// as an explicit call to grow the kernel stack will be needed to get
// these pages back.
//
if (PointerPte < LowestLivePte) {
ASSERT (Thread->LargeStack);
MmResidentAvailablePages += 1;
MM_BUMP_COUNTER(12, 1);
}
PointerPte -= 1;
BaseOfKernelStack = ((PCHAR)BaseOfKernelStack - PAGE_SIZE);
}
#if defined(_IA64_)
//
// Transition or free RSE stack pages as appropriate.
//
BaseOfKernelStack = Thread->StackBase;
PointerPte = MiGetPteAddress (BaseOfKernelStack);
LastPte = MiGetPteAddress ((PULONG)Thread->KernelBStore);
if (Thread->LargeStack) {
StackSize = KERNEL_LARGE_BSTORE_SIZE >> PAGE_SHIFT;
LowestLivePte = MiGetPteAddress ((PVOID) ((PUCHAR) Thread->InitialBStore + KERNEL_LARGE_BSTORE_COMMIT - 1));
}
else {
StackSize = KERNEL_BSTORE_SIZE >> PAGE_SHIFT;
LowestLivePte = PointerPte + StackSize;
}
EndOfStackPte = PointerPte + StackSize;
do {
ASSERT (PointerPte->u.Hard.Valid == 1);
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
TempPte = *PointerPte;
MI_MAKE_VALID_PTE_TRANSITION (TempPte, 0);
TempPte.u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
Pfn2 = MI_PFN_ELEMENT(PageFrameIndex);
Pfn2->OriginalPte.u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
FlushVa[Count] = BaseOfKernelStack;
MiDecrementShareCount (PageFrameIndex);
PointerPte += 1;
Count += 1;
BaseOfKernelStack = ((PCHAR)BaseOfKernelStack + PAGE_SIZE);
} while (PointerPte <= LastPte);
while (PointerPte != EndOfStackPte) {
if (PointerPte->u.Hard.Valid == 0) {
break;
}
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
PageTableFrameIndex = Pfn1->u4.PteFrame;
Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
MI_SET_PFN_DELETED (Pfn1);
MiDecrementShareCountOnly (MI_GET_PAGE_FRAME_FROM_PTE (PointerPte));
TempPte = KernelDemandZeroPte;
TempPte.u.Soft.Protection = MM_KSTACK_OUTSWAPPED;
MI_WRITE_INVALID_PTE (PointerPte, TempPte);
FlushVa[Count] = BaseOfKernelStack;
Count += 1;
//
// Return resident available for pages beyond the guaranteed portion
// as an explicit call to grow the kernel stack will be needed to get
// these pages back.
//
if (PointerPte > LowestLivePte) {
ASSERT (Thread->LargeStack);
MmResidentAvailablePages += 1;
MM_BUMP_COUNTER(4, 1);
}
PointerPte += 1;
BaseOfKernelStack = ((PCHAR)BaseOfKernelStack + PAGE_SIZE);
}
#endif // _IA64_
//
// Increase the available pages by the number of pages that were
// deleted and turned into demand zero.
//
MmKernelStackResident -= Count;
UNLOCK_PFN (OldIrql);
ASSERT (Count <= MAX_STACK_PAGES);
if (Count < MM_MAXIMUM_FLUSH_COUNT) {
KeFlushMultipleTb (Count,
&FlushVa[0],
TRUE,
TRUE,
NULL,
*(PHARDWARE_PTE)&ZeroPte.u.Flush);
}
else {
KeFlushEntireTb (TRUE, TRUE);
}
return;
}
VOID
MmInPageKernelStack (
IN PKTHREAD Thread
)
/*++
Routine Description:
This routine makes the specified kernel stack resident.
Arguments:
Supplies a pointer to the base of the kernel stack.
Return Value:
Thread - Supplies a pointer to the thread whose stack should be
made resident.
Environment:
Kernel mode.
--*/
{
PVOID BaseOfKernelStack;
PMMPTE PointerPte;
PMMPTE EndOfStackPte;
PMMPTE SignaturePte;
ULONG DiskRead;
PFN_NUMBER ContainingPage;
KIRQL OldIrql;
ASSERT (((PCHAR)Thread->StackBase - (PCHAR)Thread->StackLimit) <=
((LONG)MI_LARGE_STACK_SIZE + PAGE_SIZE));
if (NtGlobalFlag & FLG_DISABLE_PAGE_KERNEL_STACKS) {
return;
}
//
// The first page of the stack is the page before the base
// of the stack.
//
if (Thread->LargeStack) {
PointerPte = MiGetPteAddress ((PVOID)((PUCHAR)Thread->StackLimit));
EndOfStackPte = MiGetPteAddress ((PVOID)((PUCHAR)Thread->InitialStack -
KERNEL_LARGE_STACK_COMMIT));
//
// Trim back the stack. Make sure that the stack does not grow, i.e.
// StackLimit remains the limit.
//
if (EndOfStackPte < PointerPte) {
EndOfStackPte = PointerPte;
}
Thread->StackLimit = MiGetVirtualAddressMappedByPte (EndOfStackPte);
}
else {
EndOfStackPte = MiGetPteAddress (Thread->StackLimit);
}
#if defined(_IA64_)
if (Thread->LargeStack) {
PVOID TempAddress = (PVOID)((PUCHAR)Thread->BStoreLimit);
BaseOfKernelStack = (PVOID)(((ULONG_PTR)Thread->InitialBStore +
KERNEL_LARGE_BSTORE_COMMIT) &
~(ULONG_PTR)(PAGE_SIZE - 1));
//
// Make sure the guard page is not set to valid.
//
if (BaseOfKernelStack > TempAddress) {
BaseOfKernelStack = TempAddress;
}
Thread->BStoreLimit = BaseOfKernelStack;
}
BaseOfKernelStack = ((PCHAR)Thread->BStoreLimit - PAGE_SIZE);
#else
BaseOfKernelStack = ((PCHAR)Thread->StackBase - PAGE_SIZE);
#endif // _IA64_
PointerPte = MiGetPteAddress (BaseOfKernelStack);
DiskRead = 0;
SignaturePte = MiGetPteAddress ((PULONG_PTR)Thread->KernelStack - 1);
ASSERT (SignaturePte->u.Hard.Valid == 0);
if ((SignaturePte->u.Long != MM_KERNEL_DEMAND_ZERO_PTE) &&
(SignaturePte->u.Soft.Transition == 0)) {
DiskRead = 1;
}
LOCK_PFN (OldIrql);
while (PointerPte >= EndOfStackPte) {
if (!((PointerPte->u.Long == KernelDemandZeroPte.u.Long) ||
(PointerPte->u.Soft.Protection == MM_KSTACK_OUTSWAPPED))) {
KeBugCheckEx (MEMORY_MANAGEMENT,
0x3451,
(ULONG_PTR)PointerPte,
(ULONG_PTR)Thread,
0);
}
ASSERT (PointerPte->u.Hard.Valid == 0);
if (PointerPte->u.Soft.Protection == MM_KSTACK_OUTSWAPPED) {
PointerPte->u.Soft.Protection = PAGE_READWRITE;
}
ContainingPage = MI_GET_PAGE_FRAME_FROM_PTE (MiGetPteAddress (PointerPte));
MiMakeOutswappedPageResident (PointerPte,
PointerPte,
1,
ContainingPage);
PointerPte -= 1;
MmKernelStackResident += 1;
}
//
// Check the signature at the current stack location - 4.
//
if (*((PULONG_PTR)Thread->KernelStack - 1) != (ULONG_PTR)Thread) {
KeBugCheckEx (KERNEL_STACK_INPAGE_ERROR,
DiskRead,
*((PULONG_PTR)Thread->KernelStack - 1),
0,
(ULONG_PTR)Thread->KernelStack);
}
UNLOCK_PFN (OldIrql);
return;
}
VOID
MmOutSwapProcess (
IN PKPROCESS Process
)
/*++
Routine Description:
This routine out swaps the specified process.
Arguments:
Process - Supplies a pointer to the process that is swapped out of memory.
Return Value:
None.
--*/
{
KIRQL OldIrql;
PEPROCESS OutProcess;
PMMPTE PointerPte;
PMMPFN Pfn1;
PFN_NUMBER HyperSpacePageTable;
PMMPTE HyperSpacePageTableMap;
PFN_NUMBER PdePage;
PFN_NUMBER ProcessPage;
MMPTE TempPte;
PMMPTE PageDirectoryMap;
PFN_NUMBER VadBitMapPage;
MMPTE TempPte2;
PEPROCESS CurrentProcess;
#if defined (_X86PAE_)
ULONG i;
PFN_NUMBER PdePage2;
PFN_NUMBER HyperPage2;
PPAE_ENTRY PaeVa;
#endif
#if (_MI_PAGING_LEVELS >= 3)
PFN_NUMBER PpePage;
#endif
#if (_MI_PAGING_LEVELS >= 4)
PFN_NUMBER PxePage;
#endif
OutProcess = CONTAINING_RECORD (Process, EPROCESS, Pcb);
PS_SET_BITS (&OutProcess->Flags, PS_PROCESS_FLAGS_OUTSWAP_ENABLED);
#if DBG
if ((MmDebug & MM_DBG_SWAP_PROCESS) != 0) {
return;
}
#endif
if (OutProcess->Flags & PS_PROCESS_FLAGS_IN_SESSION) {
MiSessionOutSwapProcess (OutProcess);
}
CurrentProcess = PsGetCurrentProcess ();
if ((OutProcess->Vm.WorkingSetSize == MM_PROCESS_COMMIT_CHARGE) &&
(OutProcess->Vm.Flags.AllowWorkingSetAdjustment)) {
LOCK_EXPANSION (OldIrql);
ASSERT (OutProcess->Outswapped == 0);
if (OutProcess->Vm.Flags.BeingTrimmed == TRUE) {
//
// An outswap is not allowed at this point because the process
// has been attached to and is being trimmed.
//
UNLOCK_EXPANSION (OldIrql);
return;
}
//
// Swap the process working set info and page parent/directory/table
// pages from memory.
//
PS_SET_BITS (&OutProcess->Flags, PS_PROCESS_FLAGS_OUTSWAPPED);
UNLOCK_EXPANSION (OldIrql);
LOCK_PFN (OldIrql);
//
// Remove the working set list page from the process.
//
HyperSpacePageTable = MI_GET_HYPER_PAGE_TABLE_FRAME_FROM_PROCESS (OutProcess);
HyperSpacePageTableMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess,
HyperSpacePageTable);
TempPte = HyperSpacePageTableMap[MiGetPteOffset(MmWorkingSetList)];
MI_MAKE_VALID_PTE_TRANSITION (TempPte, MM_READWRITE);
HyperSpacePageTableMap[MiGetPteOffset(MmWorkingSetList)] = TempPte;
PointerPte = &HyperSpacePageTableMap[MiGetPteOffset (VAD_BITMAP_SPACE)];
TempPte2 = *PointerPte;
VadBitMapPage = MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)&TempPte2);
MI_MAKE_VALID_PTE_TRANSITION (TempPte2, MM_READWRITE);
*PointerPte = TempPte2;
#if defined (_X86PAE_)
TempPte2 = HyperSpacePageTableMap[0];
HyperPage2 = MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)&TempPte2);
MI_MAKE_VALID_PTE_TRANSITION (TempPte2, MM_READWRITE);
HyperSpacePageTableMap[0] = TempPte2;
#endif
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, HyperSpacePageTableMap);
//
// Remove the VAD bitmap page from the process.
//
ASSERT ((MI_PFN_ELEMENT (VadBitMapPage))->u3.e1.Modified == 1);
MiDecrementShareCount (VadBitMapPage);
//
// Remove the hyper space page from the process.
//
ASSERT ((MI_PFN_ELEMENT (OutProcess->WorkingSetPage))->u3.e1.Modified == 1);
MiDecrementShareCount (OutProcess->WorkingSetPage);
//
// Remove the hyper space page table from the process.
//
Pfn1 = MI_PFN_ELEMENT (HyperSpacePageTable);
PdePage = Pfn1->u4.PteFrame;
ASSERT (PdePage);
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
TempPte = PageDirectoryMap[MiGetPdeOffset(MmWorkingSetList)];
ASSERT (TempPte.u.Hard.Valid == 1);
ASSERT (TempPte.u.Hard.PageFrameNumber == HyperSpacePageTable);
MI_MAKE_VALID_PTE_TRANSITION (TempPte, MM_READWRITE);
PageDirectoryMap[MiGetPdeOffset(MmWorkingSetList)] = TempPte;
ASSERT (Pfn1->u3.e1.Modified == 1);
MiDecrementShareCount (HyperSpacePageTable);
#if defined (_X86PAE_)
//
// Remove the second hyper space page from the process.
//
Pfn1 = MI_PFN_ELEMENT (HyperPage2);
ASSERT (Pfn1->u3.e1.Modified == 1);
PdePage = Pfn1->u4.PteFrame;
ASSERT (PdePage);
PageDirectoryMap[MiGetPdeOffset(HYPER_SPACE2)] = TempPte2;
MiDecrementShareCount (HyperPage2);
//
// Remove the additional page directory pages.
//
PaeVa = (PPAE_ENTRY)OutProcess->PaeTop;
for (i = 0; i < PD_PER_SYSTEM - 1; i += 1) {
TempPte = PageDirectoryMap[i];
PdePage2 = MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)&TempPte);
MI_MAKE_VALID_PTE_TRANSITION (TempPte, MM_READWRITE);
PageDirectoryMap[i] = TempPte;
Pfn1 = MI_PFN_ELEMENT (PdePage2);
ASSERT (Pfn1->u3.e1.Modified == 1);
MiDecrementShareCount (PdePage2);
PaeVa->PteEntry[i].u.Long = TempPte.u.Long;
}
#if DBG
TempPte = PageDirectoryMap[i];
PdePage2 = MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)&TempPte);
Pfn1 = MI_PFN_ELEMENT (PdePage2);
ASSERT (Pfn1->u3.e1.Modified == 1);
#endif
#endif
#if (_MI_PAGING_LEVELS >= 3)
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
//
// Remove the page directory page.
//
Pfn1 = MI_PFN_ELEMENT (PdePage);
PpePage = Pfn1->u4.PteFrame;
ASSERT (PpePage);
#if (_MI_PAGING_LEVELS==3)
ASSERT (PpePage == MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)(&(OutProcess->Pcb.DirectoryTableBase[0]))));
#endif
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PpePage);
TempPte = PageDirectoryMap[MiGetPpeOffset(MmWorkingSetList)];
ASSERT (TempPte.u.Hard.Valid == 1);
ASSERT (TempPte.u.Hard.PageFrameNumber == PdePage);
MI_MAKE_VALID_PTE_TRANSITION (TempPte, MM_READWRITE);
PageDirectoryMap[MiGetPpeOffset(MmWorkingSetList)] = TempPte;
ASSERT (Pfn1->u3.e1.Modified == 1);
MiDecrementShareCount (PdePage);
#if (_MI_PAGING_LEVELS >= 4)
//
// Remove the page directory parent page. Then remove
// the top level extended page directory parent page.
//
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
PxePage = Pfn1->u4.PteFrame;
ASSERT (PxePage);
ASSERT (PxePage == MI_GET_PAGE_FRAME_FROM_PTE((PMMPTE)(&(OutProcess->Pcb.DirectoryTableBase[0]))));
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PxePage);
TempPte = PageDirectoryMap[MiGetPxeOffset(MmWorkingSetList)];
ASSERT (TempPte.u.Hard.Valid == 1);
ASSERT (TempPte.u.Hard.PageFrameNumber == PpePage);
MI_MAKE_VALID_PTE_TRANSITION (TempPte, MM_READWRITE);
PageDirectoryMap[MiGetPxeOffset(MmWorkingSetList)] = TempPte;
ASSERT (MI_PFN_ELEMENT(PpePage)->u3.e1.Modified == 1);
MiDecrementShareCount (PpePage);
TempPte = PageDirectoryMap[MiGetPxeOffset(PXE_BASE)];
MI_MAKE_VALID_PTE_TRANSITION (TempPte, MM_READWRITE);
PageDirectoryMap[MiGetPxeOffset(PXE_BASE)] = TempPte;
Pfn1 = MI_PFN_ELEMENT (PxePage);
#else
//
// Remove the top level page directory parent page.
//
TempPte = PageDirectoryMap[MiGetPpeOffset(PDE_TBASE)];
MI_MAKE_VALID_PTE_TRANSITION (TempPte,
MM_READWRITE);
PageDirectoryMap[MiGetPpeOffset(PDE_TBASE)] = TempPte;
Pfn1 = MI_PFN_ELEMENT (PpePage);
#endif
#else
//
// Remove the top level page directory page.
//
TempPte = PageDirectoryMap[MiGetPdeOffset(PDE_BASE)];
MI_MAKE_VALID_PTE_TRANSITION (TempPte, MM_READWRITE);
PageDirectoryMap[MiGetPdeOffset(PDE_BASE)] = TempPte;
Pfn1 = MI_PFN_ELEMENT (PdePage);
#endif
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
//
// Decrement share count so the top level page directory page gets
// removed. This can cause the PteCount to equal the sharecount as the
// page directory page no longer contains itself, yet can have
// itself as a transition page.
//
Pfn1->u2.ShareCount -= 2;
Pfn1->PteAddress = (PMMPTE)&OutProcess->PageDirectoryPte;
OutProcess->PageDirectoryPte = TempPte.u.Flush;
#if defined (_X86PAE_)
PaeVa->PteEntry[i].u.Long = TempPte.u.Long;
#endif
if (MI_IS_PHYSICAL_ADDRESS(OutProcess)) {
ProcessPage = MI_CONVERT_PHYSICAL_TO_PFN (OutProcess);
}
else {
PointerPte = MiGetPteAddress (OutProcess);
ProcessPage = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
}
Pfn1->u4.PteFrame = ProcessPage;
Pfn1 = MI_PFN_ELEMENT (ProcessPage);
//
// Increment the share count for the process page.
//
Pfn1->u2.ShareCount += 1;
UNLOCK_PFN (OldIrql);
LOCK_EXPANSION (OldIrql);
if (OutProcess->Vm.WorkingSetExpansionLinks.Flink >
MM_IO_IN_PROGRESS) {
//
// The entry must be on the list.
//
RemoveEntryList (&OutProcess->Vm.WorkingSetExpansionLinks);
OutProcess->Vm.WorkingSetExpansionLinks.Flink = MM_WS_SWAPPED_OUT;
}
UNLOCK_EXPANSION (OldIrql);
OutProcess->WorkingSetPage = 0;
OutProcess->Vm.WorkingSetSize = 0;
#if defined(_IA64_)
//
// Force assignment of new PID as we have removed
// the page directory page.
// Note that a TB flush would not work here as we
// are in the wrong process context.
//
Process->ProcessRegion.SequenceNumber = 0;
#endif _IA64_
}
return;
}
VOID
MmInSwapProcess (
IN PKPROCESS Process
)
/*++
Routine Description:
This routine in swaps the specified process.
Arguments:
Process - Supplies a pointer to the process that is to be swapped
into memory.
Return Value:
None.
--*/
{
KIRQL OldIrql;
PEPROCESS OutProcess;
PEPROCESS CurrentProcess;
PFN_NUMBER PdePage;
PMMPTE PageDirectoryMap;
MMPTE VadBitMapPteContents;
PFN_NUMBER VadBitMapPage;
ULONG WorkingSetListPteOffset;
ULONG VadBitMapPteOffset;
PMMPTE WorkingSetListPte;
PMMPTE VadBitMapPte;
MMPTE TempPte;
PFN_NUMBER HyperSpacePageTable;
PMMPTE HyperSpacePageTableMap;
PFN_NUMBER WorkingSetPage;
PMMPFN Pfn1;
PMMPTE PointerPte;
PFN_NUMBER ProcessPage;
#if (_MI_PAGING_LEVELS >= 3)
PFN_NUMBER TopPage;
PFN_NUMBER PageDirectoryPage;
PMMPTE PageDirectoryParentMap;
#endif
#if defined (_X86PAE_)
ULONG i;
PPAE_ENTRY PaeVa;
MMPTE TempPte2;
MMPTE PageDirectoryPtes[PD_PER_SYSTEM];
#endif
CurrentProcess = PsGetCurrentProcess ();
OutProcess = CONTAINING_RECORD (Process, EPROCESS, Pcb);
if (OutProcess->Flags & PS_PROCESS_FLAGS_OUTSWAPPED) {
//
// The process is out of memory, rebuild the initialized page
// structure.
//
if (MI_IS_PHYSICAL_ADDRESS(OutProcess)) {
ProcessPage = MI_CONVERT_PHYSICAL_TO_PFN (OutProcess);
}
else {
PointerPte = MiGetPteAddress (OutProcess);
ProcessPage = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
}
WorkingSetListPteOffset = MiGetPteOffset (MmWorkingSetList);
VadBitMapPteOffset = MiGetPteOffset (VAD_BITMAP_SPACE);
WorkingSetListPte = MiGetPteAddress (MmWorkingSetList);
VadBitMapPte = MiGetPteAddress (VAD_BITMAP_SPACE);
LOCK_PFN (OldIrql);
PdePage = MiMakeOutswappedPageResident (
#if (_MI_PAGING_LEVELS >= 4)
MiGetPteAddress (PXE_BASE),
#elif (_MI_PAGING_LEVELS >= 3)
MiGetPteAddress ((PVOID)PDE_TBASE),
#else
MiGetPteAddress (PDE_BASE),
#endif
(PMMPTE)&OutProcess->PageDirectoryPte,
0,
ProcessPage);
//
// Adjust the counts for the process page.
//
Pfn1 = MI_PFN_ELEMENT (ProcessPage);
Pfn1->u2.ShareCount -= 1;
ASSERT ((LONG)Pfn1->u2.ShareCount >= 1);
#if (_MI_PAGING_LEVELS >= 3)
TopPage = PdePage;
#endif
//
// Adjust the counts properly for the page directory page.
//
Pfn1 = MI_PFN_ELEMENT (PdePage);
Pfn1->u2.ShareCount += 1;
Pfn1->u1.Event = (PVOID)OutProcess;
Pfn1->u4.PteFrame = PdePage;
#if (_MI_PAGING_LEVELS >= 4)
Pfn1->PteAddress = MiGetPteAddress (PXE_BASE);
#elif (_MI_PAGING_LEVELS >= 3)
Pfn1->PteAddress = MiGetPteAddress ((PVOID)PDE_TBASE);
#else
Pfn1->PteAddress = MiGetPteAddress (PDE_BASE);
#endif
#if (_MI_PAGING_LEVELS >= 4)
//
// Only the extended page directory parent page has really been
// read in above. Read in the page directory parent page now.
//
PageDirectoryParentMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
TempPte = PageDirectoryParentMap[MiGetPxeOffset(MmWorkingSetList)];
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryParentMap);
PageDirectoryPage = MiMakeOutswappedPageResident (
MiGetPxeAddress (MmWorkingSetList),
&TempPte,
0,
PdePage);
ASSERT (PageDirectoryPage == TempPte.u.Hard.PageFrameNumber);
ASSERT (Pfn1->u2.ShareCount >= 3);
PageDirectoryParentMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
PageDirectoryParentMap[MiGetPxeOffset(PXE_BASE)].u.Flush =
OutProcess->PageDirectoryPte;
PageDirectoryParentMap[MiGetPxeOffset(MmWorkingSetList)] = TempPte;
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryParentMap);
PdePage = PageDirectoryPage;
#endif
#if (_MI_PAGING_LEVELS >= 3)
//
// Only the page directory parent page has really been read in above
// (and the extended page directory parent for 4-level architectures).
// Read in the page directory page now.
//
PageDirectoryParentMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
TempPte = PageDirectoryParentMap[MiGetPpeOffset(MmWorkingSetList)];
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryParentMap);
PageDirectoryPage = MiMakeOutswappedPageResident (
MiGetPpeAddress (MmWorkingSetList),
&TempPte,
0,
PdePage);
ASSERT (PageDirectoryPage == TempPte.u.Hard.PageFrameNumber);
PageDirectoryParentMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
#if (_MI_PAGING_LEVELS==3)
ASSERT (Pfn1->u2.ShareCount >= 3);
PageDirectoryParentMap[MiGetPpeOffset(PDE_TBASE)].u.Flush =
OutProcess->PageDirectoryPte;
#endif
PageDirectoryParentMap[MiGetPpeOffset(MmWorkingSetList)] = TempPte;
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryParentMap);
PdePage = PageDirectoryPage;
#endif
#if defined (_X86PAE_)
//
// Locate the additional page directory pages and make them resident.
//
PaeVa = (PPAE_ENTRY)OutProcess->PaeTop;
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
for (i = 0; i < PD_PER_SYSTEM - 1; i += 1) {
PageDirectoryPtes[i] = PageDirectoryMap[i];
}
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
for (i = 0; i < PD_PER_SYSTEM - 1; i += 1) {
MiMakeOutswappedPageResident (
MiGetPteAddress (PDE_BASE + (i << PAGE_SHIFT)),
&PageDirectoryPtes[i],
0,
PdePage);
PaeVa->PteEntry[i].u.Long = (PageDirectoryPtes[i].u.Long & ~MM_PAE_PDPTE_MASK);
}
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
for (i = 0; i < PD_PER_SYSTEM - 1; i += 1) {
PageDirectoryMap[i] = PageDirectoryPtes[i];
}
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
TempPte.u.Flush = OutProcess->PageDirectoryPte;
TempPte.u.Long &= ~MM_PAE_PDPTE_MASK;
PaeVa->PteEntry[i].u.Flush = TempPte.u.Flush;
//
// Locate the second page table page for hyperspace & make it resident.
//
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
TempPte = PageDirectoryMap[MiGetPdeOffset(HYPER_SPACE2)];
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
HyperSpacePageTable = MiMakeOutswappedPageResident (
MiGetPdeAddress (HYPER_SPACE2),
&TempPte,
0,
PdePage);
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
PageDirectoryMap[MiGetPdeOffset(HYPER_SPACE2)] = TempPte;
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
TempPte2 = TempPte;
#endif
//
// Locate the page table page for hyperspace and make it resident.
//
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
TempPte = PageDirectoryMap[MiGetPdeOffset(MmWorkingSetList)];
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
HyperSpacePageTable = MiMakeOutswappedPageResident (
MiGetPdeAddress (HYPER_SPACE),
&TempPte,
0,
PdePage);
ASSERT (Pfn1->u2.ShareCount >= 3);
PageDirectoryMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, PdePage);
#if (_MI_PAGING_LEVELS==2)
PageDirectoryMap[MiGetPdeOffset(PDE_BASE)].u.Flush =
OutProcess->PageDirectoryPte;
#endif
PageDirectoryMap[MiGetPdeOffset(MmWorkingSetList)] = TempPte;
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, PageDirectoryMap);
//
// Map in the hyper space page table page and retrieve the
// PTEs that map the working set list and VAD bitmap. Note that
// although both PTEs lie in the same page table page, they must
// be retrieved separately because: the Vad PTE may indicate its page
// is in a paging file and the WSL PTE may indicate its PTE is in
// transition. The VAD page inswap may take the WSL page from
// the transition list - CHANGING the WSL PTE ! So the WSL PTE cannot
// be captured until after the VAD inswap completes.
//
HyperSpacePageTableMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, HyperSpacePageTable);
VadBitMapPteContents = HyperSpacePageTableMap[VadBitMapPteOffset];
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, HyperSpacePageTableMap);
Pfn1 = MI_PFN_ELEMENT (HyperSpacePageTable);
Pfn1->u1.WsIndex = 1;
//
// Read in the VAD bitmap page.
//
VadBitMapPage = MiMakeOutswappedPageResident (VadBitMapPte,
&VadBitMapPteContents,
0,
HyperSpacePageTable);
//
// Read in the working set list page.
//
HyperSpacePageTableMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, HyperSpacePageTable);
TempPte = HyperSpacePageTableMap[WorkingSetListPteOffset];
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, HyperSpacePageTableMap);
WorkingSetPage = MiMakeOutswappedPageResident (WorkingSetListPte,
&TempPte,
0,
HyperSpacePageTable);
//
// Update the PTEs, this can be done together for PTEs that lie within
// the same page table page.
//
HyperSpacePageTableMap = MiMapPageInHyperSpaceAtDpc (CurrentProcess, HyperSpacePageTable);
HyperSpacePageTableMap[WorkingSetListPteOffset] = TempPte;
#if defined (_X86PAE_)
HyperSpacePageTableMap[0] = TempPte2;
#endif
HyperSpacePageTableMap[VadBitMapPteOffset] = VadBitMapPteContents;
MiUnmapPageInHyperSpaceFromDpc (CurrentProcess, HyperSpacePageTableMap);
Pfn1 = MI_PFN_ELEMENT (WorkingSetPage);
Pfn1->u1.WsIndex = 3;
Pfn1 = MI_PFN_ELEMENT (VadBitMapPage);
Pfn1->u1.WsIndex = 2;
UNLOCK_PFN (OldIrql);
LOCK_EXPANSION (OldIrql);
//
// Allow working set trimming on this process.
//
OutProcess->Vm.Flags.AllowWorkingSetAdjustment = TRUE;
if (OutProcess->Vm.WorkingSetExpansionLinks.Flink == MM_WS_SWAPPED_OUT) {
InsertTailList (&MmWorkingSetExpansionHead.ListHead,
&OutProcess->Vm.WorkingSetExpansionLinks);
}
UNLOCK_EXPANSION (OldIrql);
//
// Set up process structures.
//
#if (_MI_PAGING_LEVELS >= 3)
PdePage = TopPage;
#endif
OutProcess->WorkingSetPage = WorkingSetPage;
OutProcess->Vm.WorkingSetSize = MM_PROCESS_COMMIT_CHARGE;
#if !defined (_X86PAE_)
INITIALIZE_DIRECTORY_TABLE_BASE (&Process->DirectoryTableBase[0],
PdePage);
INITIALIZE_DIRECTORY_TABLE_BASE (&Process->DirectoryTableBase[1],
HyperSpacePageTable);
#else
//
// The DirectoryTableBase[0] never changes for PAE processes.
//
Process->DirectoryTableBase[1] = HyperSpacePageTable;
#endif
PS_CLEAR_BITS (&OutProcess->Flags, PS_PROCESS_FLAGS_OUTSWAPPED);
}
if (OutProcess->Flags & PS_PROCESS_FLAGS_IN_SESSION) {
MiSessionInSwapProcess (OutProcess);
}
PS_CLEAR_BITS (&OutProcess->Flags, PS_PROCESS_FLAGS_OUTSWAP_ENABLED);
if (PERFINFO_IS_GROUP_ON(PERF_MEMORY)) {
PERFINFO_SWAPPROCESS_INFORMATION PerfInfoSwapProcess;
PerfInfoSwapProcess.ProcessId = HandleToUlong((OutProcess)->UniqueProcessId);
PerfInfoSwapProcess.PageDirectoryBase = MmGetDirectoryFrameFromProcess(OutProcess);
PerfInfoLogBytes (PERFINFO_LOG_TYPE_INSWAPPROCESS,
&PerfInfoSwapProcess,
sizeof(PerfInfoSwapProcess));
}
return;
}
NTSTATUS
MiCreatePebOrTeb (
IN PEPROCESS TargetProcess,
IN ULONG Size,
OUT PVOID *Base
)
/*++
Routine Description:
This routine creates a TEB or PEB page within the target process.
Arguments:
TargetProcess - Supplies a pointer to the process in which to create
the structure.
Size - Supplies the size of the structure to create a VAD for.
Base - Supplies a pointer to place the PEB/TEB virtual address on success.
This has no meaning if success is not returned.
Return Value:
NTSTATUS.
Environment:
Kernel mode, attached to the specified process.
--*/
{
PMMVAD_LONG Vad;
NTSTATUS Status;
//
// Allocate and initialize the Vad before acquiring the address space
// and working set mutexes so as to minimize mutex hold duration.
//
Vad = (PMMVAD_LONG) ExAllocatePoolWithTag (NonPagedPool,
sizeof(MMVAD_LONG),
'ldaV');
if (Vad == NULL) {
return STATUS_NO_MEMORY;
}
Vad->u.LongFlags = 0;
Vad->u.VadFlags.CommitCharge = BYTES_TO_PAGES (Size);
Vad->u.VadFlags.MemCommit = 1;
Vad->u.VadFlags.PrivateMemory = 1;
Vad->u.VadFlags.Protection = MM_EXECUTE_READWRITE;
//
// Mark VAD as not deletable, no protection change.
//
Vad->u.VadFlags.NoChange = 1;
Vad->u2.LongFlags2 = 0;
Vad->u2.VadFlags2.OneSecured = 1;
Vad->u2.VadFlags2.LongVad = 1;
Vad->u2.VadFlags2.ReadOnly = 0;
#if defined(_MIALT4K_)
Vad->AliasInformation = NULL;
#endif
//
// Get the address creation mutex to block multiple threads from
// creating or deleting address space at the same time and
// get the working set mutex so virtual address descriptors can
// be inserted and walked.
//
LOCK_ADDRESS_SPACE (TargetProcess);
//
// Find a VA for the PEB on a page-size boundary.
//
Status = MiFindEmptyAddressRangeDown (TargetProcess->VadRoot,
ROUND_TO_PAGES (Size),
((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1),
PAGE_SIZE,
Base);
if (!NT_SUCCESS(Status)) {
//
// No range was available, deallocate the Vad and return the status.
//
UNLOCK_ADDRESS_SPACE (TargetProcess);
ExFreePool (Vad);
return Status;
}
//
// An unoccupied address range has been found, finish initializing the
// virtual address descriptor to describe this range.
//
Vad->StartingVpn = MI_VA_TO_VPN (*Base);
Vad->EndingVpn = MI_VA_TO_VPN ((PCHAR)*Base + Size - 1);
Vad->u3.Secured.StartVpn = (ULONG_PTR)*Base;
Vad->u3.Secured.EndVpn = (ULONG_PTR)MI_VPN_TO_VA_ENDING (Vad->EndingVpn);
LOCK_WS_UNSAFE (TargetProcess);
Status = MiInsertVad ((PMMVAD) Vad);
UNLOCK_WS_UNSAFE (TargetProcess);
#if defined (_IA64_)
if ((NT_SUCCESS(Status)) && (TargetProcess->Wow64Process != NULL)) {
MiProtectFor4kPage (*Base,
ROUND_TO_PAGES (Size),
MM_READWRITE ,
ALT_COMMIT,
TargetProcess);
}
#endif
UNLOCK_ADDRESS_SPACE (TargetProcess);
if (!NT_SUCCESS(Status)) {
//
// A failure has occurred. Deallocate the Vad and return the status.
//
ExFreePool (Vad);
}
return Status;
}
NTSTATUS
MmCreateTeb (
IN PEPROCESS TargetProcess,
IN PINITIAL_TEB InitialTeb,
IN PCLIENT_ID ClientId,
OUT PTEB *Base
)
/*++
Routine Description:
This routine creates a TEB page within the target process
and copies the initial TEB values into it.
Arguments:
TargetProcess - Supplies a pointer to the process in which to create
and initialize the TEB.
InitialTeb - Supplies a pointer to the initial TEB to copy into the
newly created TEB.
ClientId - Supplies a client ID.
Base - Supplies a location to return the base of the newly created
TEB on success.
Return Value:
NTSTATUS.
Environment:
Kernel mode.
--*/
{
PTEB TebBase;
NTSTATUS Status;
ULONG TebSize;
#if defined(_WIN64)
PWOW64_PROCESS Wow64Process;
PTEB32 Teb32Base = NULL;
#endif
//
// Attach to the specified process.
//
KeAttachProcess (&TargetProcess->Pcb);
TebSize = sizeof (TEB);
#if defined(_WIN64)
Wow64Process = TargetProcess->Wow64Process;
if (Wow64Process != NULL) {
TebSize = ROUND_TO_PAGES (sizeof (TEB)) + sizeof (TEB32);
}
#endif
Status = MiCreatePebOrTeb (TargetProcess, TebSize, (PVOID) &TebBase);
if (!NT_SUCCESS(Status)) {
KeDetachProcess();
return Status;
}
//
// Initialize the TEB. Note accesses to the TEB can raise exceptions
// if no address space is available for the TEB or the user has exceeded
// quota (non-paged, pagefile, commit) or the TEB is paged out and an
// inpage error occurs when fetching it.
//
//
// Note that since the TEB is populated with demand zero pages, only
// nonzero fields need to be initialized here.
//
try {
#if !defined(_WIN64)
TebBase->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
#endif
//
// Although various fields must be zero for the process to launch
// properly, don't assert them as an ordinary user could provoke these
// by maliciously writing over random addresses in another thread,
// hoping to nail a just-being-created TEB.
//
DONTASSERT (TebBase->NtTib.SubSystemTib == NULL);
TebBase->NtTib.Version = OS2_VERSION;
DONTASSERT (TebBase->NtTib.ArbitraryUserPointer == NULL);
TebBase->NtTib.Self = (PNT_TIB)TebBase;
DONTASSERT (TebBase->EnvironmentPointer == NULL);
TebBase->ProcessEnvironmentBlock = TargetProcess->Peb;
TebBase->ClientId = *ClientId;
TebBase->RealClientId = *ClientId;
DONTASSERT (TebBase->ActivationContextStack.Flags == 0);
DONTASSERT (TebBase->ActivationContextStack.ActiveFrame == NULL);
InitializeListHead(&TebBase->ActivationContextStack.FrameListCache);
TebBase->ActivationContextStack.NextCookieSequenceNumber = 1;
if ((InitialTeb->OldInitialTeb.OldStackBase == NULL) &&
(InitialTeb->OldInitialTeb.OldStackLimit == NULL)) {
TebBase->NtTib.StackBase = InitialTeb->StackBase;
TebBase->NtTib.StackLimit = InitialTeb->StackLimit;
TebBase->DeallocationStack = InitialTeb->StackAllocationBase;
#if defined(_IA64_)
TebBase->BStoreLimit = InitialTeb->BStoreLimit;
TebBase->DeallocationBStore = (PCHAR)InitialTeb->StackBase
+ ((ULONG_PTR)InitialTeb->StackBase - (ULONG_PTR)InitialTeb->StackAllocationBase);
#endif
}
else {
TebBase->NtTib.StackBase = InitialTeb->OldInitialTeb.OldStackBase;
TebBase->NtTib.StackLimit = InitialTeb->OldInitialTeb.OldStackLimit;
}
TebBase->StaticUnicodeString.Buffer = TebBase->StaticUnicodeBuffer;
TebBase->StaticUnicodeString.MaximumLength = (USHORT) sizeof (TebBase->StaticUnicodeBuffer);
DONTASSERT (TebBase->StaticUnicodeString.Length == 0);
//
// Used for BBT of ntdll and kernel32.dll.
//
TebBase->ReservedForPerf = BBTBuffer;
#if defined(_WIN64)
if (Wow64Process != NULL) {
Teb32Base = (PTEB32)((PCHAR)TebBase + ROUND_TO_PAGES (sizeof(TEB)));
Teb32Base->NtTib.ExceptionList = PtrToUlong (EXCEPTION_CHAIN_END);
Teb32Base->NtTib.Version = TebBase->NtTib.Version;
Teb32Base->NtTib.Self = PtrToUlong (Teb32Base);
Teb32Base->ProcessEnvironmentBlock = PtrToUlong (Wow64Process->Wow64);
Teb32Base->ClientId.UniqueProcess = PtrToUlong (TebBase->ClientId.UniqueProcess);
Teb32Base->ClientId.UniqueThread = PtrToUlong (TebBase->ClientId.UniqueThread);
Teb32Base->RealClientId.UniqueProcess = PtrToUlong (TebBase->RealClientId.UniqueProcess);
Teb32Base->RealClientId.UniqueThread = PtrToUlong (TebBase->RealClientId.UniqueThread);
Teb32Base->StaticUnicodeString.Buffer = PtrToUlong (Teb32Base->StaticUnicodeBuffer);
Teb32Base->StaticUnicodeString.MaximumLength = (USHORT)sizeof (Teb32Base->StaticUnicodeBuffer);
ASSERT (Teb32Base->StaticUnicodeString.Length == 0);
Teb32Base->GdiBatchCount = PtrToUlong (TebBase);
Teb32Base->Vdm = PtrToUlong (TebBase->Vdm);
ASSERT (Teb32Base->ActivationContextStack.Flags == 0);
Teb32Base->ActivationContextStack.ActiveFrame = PtrToUlong(TebBase->ActivationContextStack.ActiveFrame);
InitializeListHead32 (&Teb32Base->ActivationContextStack.FrameListCache);
Teb32Base->ActivationContextStack.NextCookieSequenceNumber = TebBase->ActivationContextStack.NextCookieSequenceNumber;
}
TebBase->NtTib.ExceptionList = (PVOID)Teb32Base;
#endif
} except (EXCEPTION_EXECUTE_HANDLER) {
//
// An exception has occurred, inform our caller.
//
Status = GetExceptionCode ();
}
KeDetachProcess();
*Base = TebBase;
return Status;
}
//
// This code is built twice on the Win64 build - once for PE32+
// and once for PE32 images.
//
#define MI_INIT_PEB_FROM_IMAGE(Hdrs, ImgConfig) { \
PebBase->ImageSubsystem = (Hdrs)->OptionalHeader.Subsystem; \
PebBase->ImageSubsystemMajorVersion = \
(Hdrs)->OptionalHeader.MajorSubsystemVersion; \
PebBase->ImageSubsystemMinorVersion = \
(Hdrs)->OptionalHeader.MinorSubsystemVersion; \
\
/* */ \
/* See if this image wants GetVersion to lie about who the system is */ \
/* If so, capture the lie into the PEB for the process. */ \
/* */ \
\
if ((Hdrs)->OptionalHeader.Win32VersionValue != 0) { \
PebBase->OSMajorVersion = \
(Hdrs)->OptionalHeader.Win32VersionValue & 0xFF; \
PebBase->OSMinorVersion = \
((Hdrs)->OptionalHeader.Win32VersionValue >> 8) & 0xFF; \
PebBase->OSBuildNumber = \
(USHORT)(((Hdrs)->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF); \
if ((ImgConfig) != NULL && (ImgConfig)->CSDVersion != 0) { \
PebBase->OSCSDVersion = (ImgConfig)->CSDVersion; \
} \
\
/* Win32 API GetVersion returns the following bogus bit definitions */ \
/* in the high two bits: */ \
/* */ \
/* 00 - Windows NT */ \
/* 01 - reserved */ \
/* 10 - Win32s running on Windows 3.x */ \
/* 11 - Windows 95 */ \
/* */ \
/* */ \
/* Win32 API GetVersionEx returns a dwPlatformId with the following */ \
/* values defined in winbase.h */ \
/* */ \
/* 00 - VER_PLATFORM_WIN32s */ \
/* 01 - VER_PLATFORM_WIN32_WINDOWS */ \
/* 10 - VER_PLATFORM_WIN32_NT */ \
/* 11 - reserved */ \
/* */ \
/* */ \
/* So convert the former from the Win32VersionValue field into the */ \
/* OSPlatformId field. This is done by XORing with 0x2. The */ \
/* translation is symmetric so there is the same code to do the */ \
/* reverse in windows\base\client\module.c (GetVersion) */ \
/* */ \
PebBase->OSPlatformId = \
((Hdrs)->OptionalHeader.Win32VersionValue >> 30) ^ 0x2; \
} \
}
#if defined(_WIN64)
NTSTATUS
MiInitializeWowPeb (
IN PIMAGE_NT_HEADERS NtHeaders,
IN PPEB PebBase,
IN PEPROCESS TargetProcess
)
/*++
Routine Description:
This routine creates a PEB32 page within the target process
and copies the initial PEB32 values into it.
Arguments:
NtHeaders - Supplies a pointer to the NT headers for the image.
PebBase - Supplies a pointer to the initial PEB to derive the PEB32 values
from.
TargetProcess - Supplies a pointer to the process in which to create
and initialize the PEB32.
Return Value:
NTSTATUS.
Environment:
Kernel mode.
--*/
{
NTSTATUS Status;
ULONG ReturnedSize;
PPEB32 PebBase32;
ULONG ProcessAffinityMask;
PIMAGE_LOAD_CONFIG_DIRECTORY32 ImageConfigData32;
ProcessAffinityMask = 0;
ImageConfigData32 = NULL;
//
// All references to the Peb and NtHeaders must be wrapped in try-except
// in case the user has exceeded quota (non-paged, pagefile, commit)
// or any inpage errors happen for the user addresses, etc.
//
//
// Image is 32-bit.
//
try {
ImageConfigData32 = RtlImageDirectoryEntryToData (
PebBase->ImageBaseAddress,
TRUE,
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
&ReturnedSize);
ProbeForReadSmallStructure ((PVOID)ImageConfigData32,
sizeof (*ImageConfigData32),
sizeof (ULONG));
MI_INIT_PEB_FROM_IMAGE ((PIMAGE_NT_HEADERS32)NtHeaders,
ImageConfigData32);
if ((ImageConfigData32 != NULL) && (ImageConfigData32->ProcessAffinityMask != 0)) {
ProcessAffinityMask = ImageConfigData32->ProcessAffinityMask;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
return STATUS_INVALID_IMAGE_PROTECT;
}
//
// Create a PEB32 for the process.
//
Status = MiCreatePebOrTeb (TargetProcess,
(ULONG)sizeof (PEB32),
(PVOID)&PebBase32);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Mark the process as WOW64 by storing the 32-bit PEB pointer
// in the Wow64 field.
//
TargetProcess->Wow64Process->Wow64 = PebBase32;
//
// Clone the PEB into the PEB32.
//
try {
PebBase32->InheritedAddressSpace = PebBase->InheritedAddressSpace;
PebBase32->Mutant = PtrToUlong(PebBase->Mutant);
PebBase32->ImageBaseAddress = PtrToUlong(PebBase->ImageBaseAddress);
PebBase32->AnsiCodePageData = PtrToUlong(PebBase->AnsiCodePageData);
PebBase32->OemCodePageData = PtrToUlong(PebBase->OemCodePageData);
PebBase32->UnicodeCaseTableData = PtrToUlong(PebBase->UnicodeCaseTableData);
PebBase32->NumberOfProcessors = PebBase->NumberOfProcessors;
PebBase32->BeingDebugged = PebBase->BeingDebugged;
PebBase32->NtGlobalFlag = PebBase->NtGlobalFlag;
PebBase32->CriticalSectionTimeout = PebBase->CriticalSectionTimeout;
if (PebBase->HeapSegmentReserve > 1024*1024*1024) { // 1GB
PebBase32->HeapSegmentReserve = 1024*1024; // 1MB
}
else {
PebBase32->HeapSegmentReserve = (ULONG)PebBase->HeapSegmentReserve;
}
if (PebBase->HeapSegmentCommit > PebBase32->HeapSegmentReserve) {
PebBase32->HeapSegmentCommit = 2*PAGE_SIZE;
}
else {
PebBase32->HeapSegmentCommit = (ULONG)PebBase->HeapSegmentCommit;
}
PebBase32->HeapDeCommitTotalFreeThreshold = (ULONG)PebBase->HeapDeCommitTotalFreeThreshold;
PebBase32->HeapDeCommitFreeBlockThreshold = (ULONG)PebBase->HeapDeCommitFreeBlockThreshold;
PebBase32->NumberOfHeaps = PebBase->NumberOfHeaps;
PebBase32->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB32)) / sizeof(ULONG);
PebBase32->ProcessHeaps = PtrToUlong(PebBase32+1);
PebBase32->OSMajorVersion = PebBase->OSMajorVersion;
PebBase32->OSMinorVersion = PebBase->OSMinorVersion;
PebBase32->OSBuildNumber = PebBase->OSBuildNumber;
PebBase32->OSPlatformId = PebBase->OSPlatformId;
PebBase32->OSCSDVersion = PebBase->OSCSDVersion;
PebBase32->ImageSubsystem = PebBase->ImageSubsystem;
PebBase32->ImageSubsystemMajorVersion = PebBase->ImageSubsystemMajorVersion;
PebBase32->ImageSubsystemMinorVersion = PebBase->ImageSubsystemMinorVersion;
PebBase32->SessionId = MmGetSessionId (TargetProcess);
DONTASSERT (PebBase32->pShimData == 0);
DONTASSERT (PebBase32->AppCompatFlags.QuadPart == 0);
//
// Leave the AffinityMask in the 32bit PEB as zero and let the
// 64bit NTDLL set the initial mask. This is to allow the
// round robin scheduling of non MP safe imaging in the
// caller to work correctly.
//
// Later code will set the affinity mask in the PEB32 if the
// image actually specifies one.
//
// Note that the AffinityMask in the PEB is simply a mechanism
// to pass affinity information from the image to the loader.
//
// Pass the affinity mask up to the 32 bit NTDLL via
// the PEB32. The 32 bit NTDLL will determine that the
// affinity is not zero and try to set the affinity
// mask from user-mode. This call will be intercepted
// by the wow64 thunks which will convert it
// into a 64bit affinity mask and call the kernel.
//
PebBase32->ImageProcessAffinityMask = ProcessAffinityMask;
DONTASSERT (PebBase32->ActivationContextData == 0);
DONTASSERT (PebBase32->SystemDefaultActivationContextData == 0);
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode ();
}
return Status;
}
#endif
NTSTATUS
MmCreatePeb (
IN PEPROCESS TargetProcess,
IN PINITIAL_PEB InitialPeb,
OUT PPEB *Base
)
/*++
Routine Description:
This routine creates a PEB page within the target process
and copies the initial PEB values into it.
Arguments:
TargetProcess - Supplies a pointer to the process in which to create
and initialize the PEB.
InitialPeb - Supplies a pointer to the initial PEB to copy into the
newly created PEB.
Base - Supplies a location to return the base of the newly created
PEB on success.
Return Value:
NTSTATUS.
Environment:
Kernel mode.
--*/
{
PPEB PebBase;
USHORT Magic;
USHORT Characteristics;
NTSTATUS Status;
PVOID ViewBase;
LARGE_INTEGER SectionOffset;
PIMAGE_NT_HEADERS NtHeaders;
SIZE_T ViewSize;
ULONG ReturnedSize;
PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
ULONG_PTR ProcessAffinityMask;
ViewBase = NULL;
SectionOffset.LowPart = 0;
SectionOffset.HighPart = 0;
ViewSize = 0;
//
// Attach to the specified process.
//
KeAttachProcess (&TargetProcess->Pcb);
//
// Map the NLS tables into the application's address space.
//
Status = MmMapViewOfSection (InitNlsSectionPointer,
TargetProcess,
&ViewBase,
0L,
0L,
&SectionOffset,
&ViewSize,
ViewShare,
MEM_TOP_DOWN | SEC_NO_CHANGE,
PAGE_READONLY);
if (!NT_SUCCESS(Status)) {
KeDetachProcess ();
return Status;
}
Status = MiCreatePebOrTeb (TargetProcess, sizeof(PEB), (PVOID)&PebBase);
if (!NT_SUCCESS(Status)) {
KeDetachProcess ();
return Status;
}
//
// Initialize the Peb. Every reference to the Peb
// must be wrapped in try-except in case the inpage fails. The inpage
// can fail for any reason including network failures, disk errors,
// low resources, etc.
//
try {
PebBase->InheritedAddressSpace = InitialPeb->InheritedAddressSpace;
PebBase->Mutant = InitialPeb->Mutant;
PebBase->ImageBaseAddress = TargetProcess->SectionBaseAddress;
PebBase->AnsiCodePageData = (PVOID)((PUCHAR)ViewBase+InitAnsiCodePageDataOffset);
PebBase->OemCodePageData = (PVOID)((PUCHAR)ViewBase+InitOemCodePageDataOffset);
PebBase->UnicodeCaseTableData = (PVOID)((PUCHAR)ViewBase+InitUnicodeCaseTableDataOffset);
PebBase->NumberOfProcessors = KeNumberProcessors;
PebBase->BeingDebugged = (BOOLEAN)(TargetProcess->DebugPort != NULL ? TRUE : FALSE);
PebBase->NtGlobalFlag = NtGlobalFlag;
PebBase->CriticalSectionTimeout = MmCriticalSectionTimeout;
PebBase->HeapSegmentReserve = MmHeapSegmentReserve;
PebBase->HeapSegmentCommit = MmHeapSegmentCommit;
PebBase->HeapDeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
PebBase->HeapDeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
DONTASSERT (PebBase->NumberOfHeaps == 0);
PebBase->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof (PEB)) / sizeof( PVOID);
PebBase->ProcessHeaps = (PVOID *)(PebBase+1);
PebBase->OSMajorVersion = NtMajorVersion;
PebBase->OSMinorVersion = NtMinorVersion;
PebBase->OSBuildNumber = (USHORT)(NtBuildNumber & 0x3FFF);
PebBase->OSPlatformId = 2; // VER_PLATFORM_WIN32_NT from winbase.h
PebBase->OSCSDVersion = (USHORT)CmNtCSDVersion;
DONTASSERT (PebBase->pShimData == 0);
DONTASSERT (PebBase->AppCompatFlags.QuadPart == 0);
DONTASSERT (PebBase->ActivationContextData == NULL);
DONTASSERT (PebBase->SystemDefaultActivationContextData == NULL);
if (TargetProcess->Session != NULL) {
PebBase->SessionId = MmGetSessionId (TargetProcess);
}
PebBase->MinimumStackCommit = (SIZE_T)MmMinimumStackCommitInBytes;
} except (EXCEPTION_EXECUTE_HANDLER) {
KeDetachProcess();
return GetExceptionCode ();
}
//
// Every reference to NtHeaders (including the call to RtlImageNtHeader)
// must be wrapped in try-except in case the inpage fails. The inpage
// can fail for any reason including network failures, disk errors,
// low resources, etc.
//
try {
NtHeaders = RtlImageNtHeader (PebBase->ImageBaseAddress);
Magic = NtHeaders->OptionalHeader.Magic;
Characteristics = NtHeaders->FileHeader.Characteristics;
} except (EXCEPTION_EXECUTE_HANDLER) {
KeDetachProcess();
return STATUS_INVALID_IMAGE_PROTECT;
}
if (NtHeaders != NULL) {
ProcessAffinityMask = 0;
#if defined(_WIN64)
if (TargetProcess->Wow64Process) {
Status = MiInitializeWowPeb (NtHeaders, PebBase, TargetProcess);
if (!NT_SUCCESS(Status)) {
KeDetachProcess ();
return Status;
}
}
else // a PE32+ image
#endif
{
try {
ImageConfigData = RtlImageDirectoryEntryToData (
PebBase->ImageBaseAddress,
TRUE,
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
&ReturnedSize);
ProbeForReadSmallStructure ((PVOID)ImageConfigData,
sizeof (*ImageConfigData),
PROBE_ALIGNMENT (IMAGE_LOAD_CONFIG_DIRECTORY));
MI_INIT_PEB_FROM_IMAGE(NtHeaders, ImageConfigData);
if (ImageConfigData != NULL && ImageConfigData->ProcessAffinityMask != 0) {
ProcessAffinityMask = ImageConfigData->ProcessAffinityMask;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
KeDetachProcess();
return STATUS_INVALID_IMAGE_PROTECT;
}
}
//
// Note NT4 examined the NtHeaders->FileHeader.Characteristics
// for the IMAGE_FILE_AGGRESIVE_WS_TRIM bit, but this is not needed
// or used for NT5 and above.
//
//
// See if image wants to override the default processor affinity mask.
//
try {
if (Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY) {
//
// Image is NOT MP safe. Assign it a processor on a rotating
// basis to spread these processes around on MP systems.
//
do {
PebBase->ImageProcessAffinityMask = (KAFFINITY)(0x1 << MmRotatingUniprocessorNumber);
if (++MmRotatingUniprocessorNumber >= KeNumberProcessors) {
MmRotatingUniprocessorNumber = 0;
}
} while ((PebBase->ImageProcessAffinityMask & KeActiveProcessors) == 0);
}
else {
if (ProcessAffinityMask != 0) {
//
// Pass the affinity mask from the image header
// to LdrpInitializeProcess via the PEB.
//
PebBase->ImageProcessAffinityMask = ProcessAffinityMask;
}
}
} except (EXCEPTION_EXECUTE_HANDLER) {
KeDetachProcess();
return STATUS_INVALID_IMAGE_PROTECT;
}
}
KeDetachProcess();
*Base = PebBase;
return STATUS_SUCCESS;
}
VOID
MmDeleteTeb (
IN PEPROCESS TargetProcess,
IN PVOID TebBase
)
/*++
Routine Description:
This routine deletes a TEB page within the target process.
Arguments:
TargetProcess - Supplies a pointer to the process in which to delete
the TEB.
TebBase - Supplies the base address of the TEB to delete.
Return Value:
None.
Environment:
Kernel mode.
--*/
{
PVOID EndingAddress;
PMMVAD_LONG Vad;
NTSTATUS Status;
PMMSECURE_ENTRY Secure;
PMMVAD PreviousVad;
PMMVAD NextVad;
EndingAddress = ((PCHAR)TebBase +
ROUND_TO_PAGES (sizeof(TEB)) - 1);
#if defined(_WIN64)
if (TargetProcess->Wow64Process) {
EndingAddress = ((PCHAR)EndingAddress + ROUND_TO_PAGES (sizeof(TEB32)));
}
#endif
//
// Attach to the specified process.
//
KeAttachProcess (&TargetProcess->Pcb);
//
// Get the address creation mutex to block multiple threads from
// creating or deleting address space at the same time and
// get the working set mutex so virtual address descriptors can
// be inserted and walked.
//
LOCK_ADDRESS_SPACE (TargetProcess);
Vad = (PMMVAD_LONG) MiLocateAddress (TebBase);
ASSERT (Vad != NULL);
ASSERT ((Vad->StartingVpn == MI_VA_TO_VPN (TebBase)) &&
(Vad->EndingVpn == MI_VA_TO_VPN (EndingAddress)));
#if defined(_MIALT4K_)
ASSERT (Vad->AliasInformation == NULL);
#endif
//
// If someone has secured the TEB (in addition to the standard securing
// that was done by memory management on creation, then don't delete it
// now - just leave it around until the entire process is deleted.
//
ASSERT (Vad->u.VadFlags.NoChange == 1);
if (Vad->u2.VadFlags2.OneSecured) {
Status = STATUS_SUCCESS;
}
else {
ASSERT (Vad->u2.VadFlags2.MultipleSecured);
ASSERT (IsListEmpty (&Vad->u3.List) == 0);
//
// If there's only one entry, then that's the one we defined when we
// initially created the TEB. So TEB deletion can take place right
// now. If there's more than one entry, let the TEB sit around until
// the process goes away.
//
Secure = CONTAINING_RECORD (Vad->u3.List.Flink,
MMSECURE_ENTRY,
List);
if (Secure->List.Flink == &Vad->u3.List) {
Status = STATUS_SUCCESS;
}
else {
Status = STATUS_NOT_FOUND;
}
}
if (NT_SUCCESS(Status)) {
PreviousVad = MiGetPreviousVad (Vad);
NextVad = MiGetNextVad (Vad);
LOCK_WS_UNSAFE (TargetProcess);
MiRemoveVad ((PMMVAD)Vad);
//
// Return commitment for page table pages and clear VAD bitmaps
// if possible.
//
MiReturnPageTablePageCommitment (TebBase,
EndingAddress,
TargetProcess,
PreviousVad,
NextVad);
MiDeleteFreeVm (TebBase, EndingAddress);
UNLOCK_WS_AND_ADDRESS_SPACE (TargetProcess);
ExFreePool (Vad);
}
else {
UNLOCK_ADDRESS_SPACE (TargetProcess);
}
KeDetachProcess();
}
VOID
MmAllowWorkingSetExpansion (
VOID
)
/*++
Routine Description:
This routine updates the working set list head FLINK field to
indicate that working set adjustment is allowed.
NOTE: This routine may be called more than once per process.
Arguments:
None.
Return Value:
None.
Environment:
Kernel mode.
--*/
{
PEPROCESS CurrentProcess;
KIRQL OldIrql;
//
// Check the current state of the working set adjustment flag
// in the process header.
//
CurrentProcess = PsGetCurrentProcess();
LOCK_EXPANSION (OldIrql);
if (!CurrentProcess->Vm.Flags.AllowWorkingSetAdjustment) {
CurrentProcess->Vm.Flags.AllowWorkingSetAdjustment = TRUE;
InsertTailList (&MmWorkingSetExpansionHead.ListHead,
&CurrentProcess->Vm.WorkingSetExpansionLinks);
}
UNLOCK_EXPANSION (OldIrql);
return;
}
#if DBG
ULONG MiDeleteLocked;
#endif
VOID
MiDeleteAddressesInWorkingSet (
IN PEPROCESS Process
)
/*++
Routine Description:
This routine deletes all user mode addresses from the working set
list.
Arguments:
Process = Pointer to the current process.
Return Value:
None.
Environment:
Kernel mode, Working Set Lock held.
--*/
{
PMMWSLE Wsle;
WSLE_NUMBER index;
WSLE_NUMBER Entry;
PVOID Va;
#if DBG
PVOID SwapVa;
PMMPTE PointerPte;
PMMPFN Pfn1;
PMMWSLE LastWsle;
#endif
//
// Go through the working set and for any user-accessible page which is
// in it, rip it out of the working set and free the page.
//
index = 2;
Wsle = &MmWsle[index];
MmWorkingSetList->HashTable = NULL;
//
// Go through the working set list and remove all pages for user
// space addresses.
//
while (index <= MmWorkingSetList->LastEntry) {
if (Wsle->u1.e1.Valid == 1) {
#if (_MI_PAGING_LEVELS >= 4)
ASSERT(MiGetPxeAddress(Wsle->u1.VirtualAddress)->u.Hard.Valid == 1);
#endif
#if (_MI_PAGING_LEVELS >= 3)
ASSERT(MiGetPpeAddress(Wsle->u1.VirtualAddress)->u.Hard.Valid == 1);
#endif
ASSERT(MiGetPdeAddress(Wsle->u1.VirtualAddress)->u.Hard.Valid == 1);
ASSERT(MiGetPteAddress(Wsle->u1.VirtualAddress)->u.Hard.Valid == 1);
if (Wsle->u1.VirtualAddress < (PVOID)MM_HIGHEST_USER_ADDRESS) {
//
// This is a user mode address, for each one we remove we must
// maintain the NonDirectCount. This is because we may fault
// later for page tables and need to grow the hash table when
// updating the working set. NonDirectCount needs to be correct
// at that point.
//
if (Wsle->u1.e1.Direct == 0) {
Process->Vm.VmWorkingSetList->NonDirectCount -= 1;
}
//
// This entry is in the working set list.
//
Va = Wsle->u1.VirtualAddress;
MiReleaseWsle (index, &Process->Vm);
MiDeleteValidAddress (Va, Process);
if (index < MmWorkingSetList->FirstDynamic) {
//
// This entry is locked.
//
MmWorkingSetList->FirstDynamic -= 1;
if (index != MmWorkingSetList->FirstDynamic) {
Entry = MmWorkingSetList->FirstDynamic;
#if DBG
MiDeleteLocked += 1;
SwapVa = MmWsle[MmWorkingSetList->FirstDynamic].u1.VirtualAddress;
SwapVa = PAGE_ALIGN (SwapVa);
PointerPte = MiGetPteAddress (SwapVa);
Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
ASSERT (Entry == MiLocateWsle (SwapVa, MmWorkingSetList, Pfn1->u1.WsIndex));
#endif
MiSwapWslEntries (Entry, index, &Process->Vm);
}
}
}
}
index += 1;
Wsle += 1;
}
#if DBG
Wsle = &MmWsle[2];
LastWsle = &MmWsle[MmWorkingSetList->LastInitializedWsle];
while (Wsle <= LastWsle) {
if (Wsle->u1.e1.Valid == 1) {
#if (_MI_PAGING_LEVELS >= 4)
ASSERT(MiGetPxeAddress(Wsle->u1.VirtualAddress)->u.Hard.Valid == 1);
#endif
#if (_MI_PAGING_LEVELS >= 3)
ASSERT(MiGetPpeAddress(Wsle->u1.VirtualAddress)->u.Hard.Valid == 1);
#endif
ASSERT(MiGetPdeAddress(Wsle->u1.VirtualAddress)->u.Hard.Valid == 1);
ASSERT(MiGetPteAddress(Wsle->u1.VirtualAddress)->u.Hard.Valid == 1);
}
Wsle += 1;
}
#endif
}
VOID
MiDeleteValidAddress (
IN PVOID Va,
IN PEPROCESS CurrentProcess
)
/*++
Routine Description:
This routine deletes the specified virtual address.
Arguments:
Va - Supplies the virtual address to delete.
CurrentProcess - Supplies the current process.
Return Value:
None.
Environment:
Kernel mode. PFN LOCK HELD.
Note since this is only called during process teardown, the write watch
bits are not updated. If this ever called from other places, code
will need to be added here to update those bits.
--*/
{
PMMPTE PointerPde;
PMMPTE PointerPte;
PMMPFN Pfn1;
PMMPFN Pfn2;
KIRQL OldIrql;
PMMCLONE_BLOCK CloneBlock;
PMMCLONE_DESCRIPTOR CloneDescriptor;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER PageTableFrameIndex;
CloneDescriptor = NULL;
//
// Initializing CloneBlock is not needed for correctness
// but without it the compiler cannot compile this code
// W4 to check for use of uninitialized variables.
//
CloneBlock = NULL;
PointerPte = MiGetPteAddress (Va);
LOCK_PFN (OldIrql);
#if (_MI_PAGING_LEVELS >= 4)
ASSERT(MiGetPxeAddress(Va)->u.Hard.Valid == 1);
#endif
#if (_MI_PAGING_LEVELS >= 3)
ASSERT(MiGetPpeAddress(Va)->u.Hard.Valid == 1);
#endif
ASSERT(MiGetPdeAddress(Va)->u.Hard.Valid == 1);
ASSERT (PointerPte->u.Hard.Valid == 1);
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
if (Pfn1->u3.e1.PrototypePte == 1) {
CloneBlock = (PMMCLONE_BLOCK)Pfn1->PteAddress;
//
// Capture the state of the modified bit for this PTE.
//
MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1);
//
// Decrement the share and valid counts of the page table
// page which maps this PTE.
//
PointerPde = MiGetPteAddress (PointerPte);
PageTableFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde);
Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
//
// Decrement the share count for the physical page.
//
MiDecrementShareCount (PageFrameIndex);
//
// Check to see if this is a fork prototype PTE and if so
// update the clone descriptor address.
//
if (Va <= MM_HIGHEST_USER_ADDRESS) {
//
// Locate the clone descriptor within the clone tree.
//
CloneDescriptor = MiLocateCloneAddress (CurrentProcess, (PVOID)CloneBlock);
}
}
else {
//
// This PTE is a NOT a prototype PTE, delete the physical page.
//
//
// Decrement the share and valid counts of the page table
// page which maps this PTE.
//
PageTableFrameIndex = Pfn1->u4.PteFrame;
Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
MI_SET_PFN_DELETED (Pfn1);
//
// Decrement the share count for the physical page. As the page
// is private it will be put on the free list.
//
MiDecrementShareCountOnly (PageFrameIndex);
//
// Decrement the count for the number of private pages.
//
CurrentProcess->NumberOfPrivatePages -= 1;
}
//
// Set the pointer to PTE to be a demand zero PTE. This allows
// the page usage count to be kept properly and handles the case
// when a page table page has only valid PTEs and needs to be
// deleted later when the VADs are removed.
//
PointerPte->u.Long = MM_DEMAND_ZERO_WRITE_PTE;
if (CloneDescriptor != NULL) {
//
// Decrement the reference count for the clone block,
// note that this could release and reacquire
// the mutexes hence cannot be done until after the
// working set index has been removed.
//
if (MiDecrementCloneBlockReference (CloneDescriptor,
CloneBlock,
CurrentProcess)) {
}
}
UNLOCK_PFN (OldIrql);
}
PFN_NUMBER
MiMakeOutswappedPageResident (
IN PMMPTE ActualPteAddress,
IN OUT PMMPTE PointerTempPte,
IN ULONG Global,
IN PFN_NUMBER ContainingPage
)
/*++
Routine Description:
This routine makes the specified PTE valid.
Arguments:
ActualPteAddress - Supplies the actual address that the PTE will
reside at. This is used for page coloring.
PointerTempPte - Supplies the PTE to operate on, returns a valid
PTE.
Global - Supplies 1 if the resulting PTE is global.
ContainingPage - Supplies the physical page number of the page which
contains the resulting PTE. If this value is 0, no
operations on the containing page are performed.
Return Value:
Returns the physical page number that was allocated for the PTE.
Environment:
Kernel mode, PFN LOCK HELD!
--*/
{
MMPTE TempPte;
KIRQL OldIrql;
PFN_NUMBER PageFrameIndex;
PMMPFN Pfn1;
PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + 1];
PMDL Mdl;
LARGE_INTEGER StartingOffset;
KEVENT Event;
IO_STATUS_BLOCK IoStatus;
PFN_NUMBER PageFileNumber;
NTSTATUS Status;
PPFN_NUMBER Page;
ULONG RefaultCount;
#if DBG
PVOID HyperVa;
PEPROCESS CurrentProcess;
#endif
MM_PFN_LOCK_ASSERT();
#if defined (_IA64_)
UNREFERENCED_PARAMETER (Global);
#endif
restart:
OldIrql = APC_LEVEL;
ASSERT (PointerTempPte->u.Hard.Valid == 0);
if (PointerTempPte->u.Long == MM_KERNEL_DEMAND_ZERO_PTE) {
//
// Any page will do.
//
MiEnsureAvailablePageOrWait (NULL, NULL);
PageFrameIndex = MiRemoveAnyPage (
MI_GET_PAGE_COLOR_FROM_PTE (ActualPteAddress));
MI_MAKE_VALID_PTE (TempPte,
PageFrameIndex,
MM_READWRITE,
ActualPteAddress);
MI_SET_PTE_DIRTY (TempPte);
MI_SET_GLOBAL_STATE (TempPte, Global);
MI_WRITE_VALID_PTE (PointerTempPte, TempPte);
MiInitializePfnForOtherProcess (PageFrameIndex,
ActualPteAddress,
ContainingPage);
}
else if (PointerTempPte->u.Soft.Transition == 1) {
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerTempPte);
PointerTempPte->u.Trans.Protection = MM_READWRITE;
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
if ((MmAvailablePages == 0) ||
((Pfn1->u4.InPageError == 1) && (Pfn1->u3.e1.ReadInProgress == 1))) {
//
// This can only happen if the system is utilizing a hardware
// compression cache. This ensures that only a safe amount
// of the compressed virtual cache is directly mapped so that
// if the hardware gets into trouble, we can bail it out.
//
UNLOCK_PFN (OldIrql);
KeDelayExecutionThread (KernelMode,
FALSE,
(PLARGE_INTEGER)&MmHalfSecond);
LOCK_PFN (OldIrql);
goto restart;
}
//
// PTE refers to a transition PTE.
//
if (Pfn1->u3.e1.PageLocation != ActiveAndValid) {
MiUnlinkPageFromList (Pfn1);
//
// Even though this routine is only used to bring in special
// system pages that are separately charged, a modified write
// may be in progress and if so, will have applied a systemwide
// charge against the locked pages count. This all works out nicely
// (with no code needed here) as the write completion will see
// the nonzero ShareCount and remove the charge.
//
ASSERT ((Pfn1->u3.e2.ReferenceCount == 0) ||
(Pfn1->u3.e1.LockCharged == 1));
Pfn1->u3.e2.ReferenceCount += 1;
Pfn1->u3.e1.PageLocation = ActiveAndValid;
}
//
// Update the PFN database, the share count is now 1 and
// the reference count is incremented as the share count
// just went from zero to 1.
//
Pfn1->u2.ShareCount += 1;
MI_SET_MODIFIED (Pfn1, 1, 0x12);
if (Pfn1->u3.e1.WriteInProgress == 0) {
//
// Release the page file space for this page.
//
MiReleasePageFileSpace (Pfn1->OriginalPte);
Pfn1->OriginalPte.u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
}
MI_MAKE_TRANSITION_PTE_VALID (TempPte, PointerTempPte);
MI_SET_PTE_DIRTY (TempPte);
MI_SET_GLOBAL_STATE (TempPte, Global);
MI_WRITE_VALID_PTE (PointerTempPte, TempPte);
}
else {
//
// Page resides in a paging file.
// Any page will do.
//
MiEnsureAvailablePageOrWait (NULL, NULL);
PageFrameIndex = MiRemoveAnyPage (
MI_GET_PAGE_COLOR_FROM_PTE (ActualPteAddress));
//
// Initialize the PFN database element, but don't
// set read in progress as collided page faults cannot
// occur here.
//
MiInitializePfnForOtherProcess (PageFrameIndex,
ActualPteAddress,
ContainingPage);
UNLOCK_PFN (OldIrql);
PointerTempPte->u.Soft.Protection = MM_READWRITE;
KeInitializeEvent (&Event, NotificationEvent, FALSE);
//
// Calculate the VPN for the in-page operation.
//
TempPte = *PointerTempPte;
PageFileNumber = GET_PAGING_FILE_NUMBER (TempPte);
StartingOffset.QuadPart = (LONGLONG)(GET_PAGING_FILE_OFFSET (TempPte)) <<
PAGE_SHIFT;
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
//
// Build MDL for request.
//
Mdl = (PMDL)&MdlHack[0];
MmInitializeMdl (Mdl,
MiGetVirtualAddressMappedByPte (ActualPteAddress),
PAGE_SIZE);
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
Page = (PPFN_NUMBER)(Mdl + 1);
*Page = PageFrameIndex;
#if DBG
CurrentProcess = PsGetCurrentProcess ();
HyperVa = MiMapPageInHyperSpace (CurrentProcess, PageFrameIndex, &OldIrql);
RtlFillMemoryUlong (HyperVa,
PAGE_SIZE,
0x34785690);
MiUnmapPageInHyperSpace (CurrentProcess, HyperVa, OldIrql);
#endif
//
// Issue the read request.
//
RefaultCount = 0;
Refault:
Status = IoPageRead (MmPagingFile[PageFileNumber]->File,
Mdl,
&StartingOffset,
&Event,
&IoStatus);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject (&Event,
WrPageIn,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
Status = IoStatus.Status;
}
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
}
if (NT_SUCCESS(Status)) {
if (IoStatus.Information != PAGE_SIZE) {
KeBugCheckEx (KERNEL_STACK_INPAGE_ERROR,
2,
IoStatus.Status,
PageFileNumber,
StartingOffset.LowPart);
}
}
if ((!NT_SUCCESS(Status)) || (!NT_SUCCESS(IoStatus.Status))) {
if ((MmIsRetryIoStatus (Status)) ||
(MmIsRetryIoStatus (IoStatus.Status))) {
RefaultCount -= 1;
if (RefaultCount & MiFaultRetryMask) {
//
// Insufficient resources, delay and reissue
// the in page operation.
//
KeDelayExecutionThread (KernelMode,
FALSE,
(PLARGE_INTEGER)&MmHalfSecond);
KeClearEvent (&Event);
RefaultCount -= 1;
goto Refault;
}
}
KeBugCheckEx (KERNEL_STACK_INPAGE_ERROR,
Status,
IoStatus.Status,
PageFileNumber,
StartingOffset.LowPart);
}
LOCK_PFN (OldIrql);
//
// Release the page file space.
//
MiReleasePageFileSpace (TempPte);
Pfn1->OriginalPte.u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
MI_MAKE_VALID_PTE (TempPte,
PageFrameIndex,
MM_READWRITE,
ActualPteAddress);
MI_SET_PTE_DIRTY (TempPte);
MI_SET_MODIFIED (Pfn1, 1, 0x13);
MI_SET_GLOBAL_STATE (TempPte, Global);
MI_WRITE_VALID_PTE (PointerTempPte, TempPte);
}
return PageFrameIndex;
}
UCHAR
MiSetMemoryPriorityProcess (
IN PEPROCESS Process,
IN UCHAR MemoryPriority
)
/*++
Routine Description:
Nonpaged wrapper to set the memory priority of a process.
Arguments:
Process - Supplies the process to update.
MemoryPriority - Supplies the new memory priority of the process.
Return Value:
Old priority.
--*/
{
KIRQL OldIrql;
UCHAR OldPriority;
LOCK_EXPANSION (OldIrql);
OldPriority = (UCHAR) Process->Vm.Flags.MemoryPriority;
Process->Vm.Flags.MemoryPriority = MemoryPriority;
UNLOCK_EXPANSION (OldIrql);
return OldPriority;
}
VOID
MmSetMemoryPriorityProcess (
IN PEPROCESS Process,
IN UCHAR MemoryPriority
)
/*++
Routine Description:
Sets the memory priority of a process.
Arguments:
Process - Supplies the process to update
MemoryPriority - Supplies the new memory priority of the process
Return Value:
None.
--*/
{
if (MmSystemSize == MmSmallSystem && MmNumberOfPhysicalPages < ((15*1024*1024)/PAGE_SIZE)) {
//
// If this is a small system, make every process BACKGROUND.
//
MemoryPriority = MEMORY_PRIORITY_BACKGROUND;
}
MiSetMemoryPriorityProcess (Process, MemoryPriority);
return;
}
PMMVAD
MiAllocateVad (
IN ULONG_PTR StartingVirtualAddress,
IN ULONG_PTR EndingVirtualAddress,
IN LOGICAL Deletable
)
/*++
Routine Description:
Reserve the specified range of address space.
Arguments:
StartingVirtualAddress - Supplies the starting virtual address.
EndingVirtualAddress - Supplies the ending virtual address.
Deletable - Supplies TRUE if the VAD is to be marked as deletable, FALSE
if deletions of this VAD should be disallowed.
Return Value:
A VAD pointer on success, NULL on failure.
--*/
{
PMMVAD_LONG Vad;
ASSERT (StartingVirtualAddress <= EndingVirtualAddress);
if (Deletable == TRUE) {
Vad = (PMMVAD_LONG)ExAllocatePoolWithTag (NonPagedPool, sizeof(MMVAD_SHORT), 'SdaV');
}
else {
Vad = (PMMVAD_LONG)ExAllocatePoolWithTag (NonPagedPool, sizeof(MMVAD_LONG), 'ldaV');
}
if (Vad == NULL) {
return NULL;
}
//
// Set the starting and ending virtual page numbers of the VAD.
//
Vad->StartingVpn = MI_VA_TO_VPN (StartingVirtualAddress);
Vad->EndingVpn = MI_VA_TO_VPN (EndingVirtualAddress);
//
// Mark VAD as no commitment, private, and readonly.
//
Vad->u.LongFlags = 0;
Vad->u.VadFlags.CommitCharge = MM_MAX_COMMIT;
Vad->u.VadFlags.Protection = MM_READONLY;
Vad->u.VadFlags.PrivateMemory = 1;
if (Deletable == TRUE) {
ASSERT (Vad->u.VadFlags.NoChange == 0);
}
else {
Vad->u.VadFlags.NoChange = 1;
Vad->u2.LongFlags2 = 0;
Vad->u2.VadFlags2.OneSecured = 1;
Vad->u2.VadFlags2.LongVad = 1;
Vad->u2.VadFlags2.ReadOnly = 1;
Vad->u3.Secured.StartVpn = StartingVirtualAddress;
Vad->u3.Secured.EndVpn = EndingVirtualAddress;
#if defined(_MIALT4K_)
Vad->AliasInformation = NULL;
#endif
}
return (PMMVAD) Vad;
}
#if 0
VOID
MiVerifyReferenceCounts (
IN ULONG PdePage
)
//
// Verify the share and valid PTE counts for page directory page.
//
{
PMMPFN Pfn1;
PMMPFN Pfn3;
PMMPTE Pte1;
ULONG Share = 0;
ULONG Valid = 0;
ULONG i, ix, iy;
PMMPTE PageDirectoryMap;
KIRQL OldIrql;
PEPROCESS Process;
Process = PsGetCurrentProcess ();
PageDirectoryMap = (PMMPTE)MiMapPageInHyperSpace (Process, PdePage, &OldIrql);
Pfn1 = MI_PFN_ELEMENT (PdePage);
Pte1 = (PMMPTE)PageDirectoryMap;
//
// Map in the non paged portion of the system.
//
ix = MiGetPdeOffset(CODE_START);
for (i = 0;i < ix; i += 1) {
if (Pte1->u.Hard.Valid == 1) {
Valid += 1;
}
else if ((Pte1->u.Soft.Prototype == 0) &&
(Pte1->u.Soft.Transition == 1)) {
Pfn3 = MI_PFN_ELEMENT (Pte1->u.Trans.PageFrameNumber);
if (Pfn3->u3.e1.PageLocation == ActiveAndValid) {
ASSERT (Pfn1->u2.ShareCount > 1);
Valid += 1;
}
else {
Share += 1;
}
}
Pte1 += 1;
}
iy = MiGetPdeOffset(PTE_BASE);
Pte1 = &PageDirectoryMap[iy];
ix = MiGetPdeOffset(HYPER_SPACE_END) + 1;
for (i = iy; i < ix; i += 1) {
if (Pte1->u.Hard.Valid == 1) {
Valid += 1;
}
else if ((Pte1->u.Soft.Prototype == 0) &&
(Pte1->u.Soft.Transition == 1)) {
Pfn3 = MI_PFN_ELEMENT (Pte1->u.Trans.PageFrameNumber);
if (Pfn3->u3.e1.PageLocation == ActiveAndValid) {
ASSERT (Pfn1->u2.ShareCount > 1);
Valid += 1;
}
else {
Share += 1;
}
}
Pte1 += 1;
}
if (Pfn1->u2.ShareCount != (Share+Valid+1)) {
DbgPrint ("MMPROCSUP - PDE page %lx ShareCount %lx found %lx\n",
PdePage, Pfn1->u2.ShareCount, Valid+Share+1);
}
MiUnmapPageInHyperSpace (Process, PageDirectoryMap, OldIrql);
ASSERT (Pfn1->u2.ShareCount == (Share+Valid+1));
return;
}
#endif //0
PFN_NUMBER
MmGetDirectoryFrameFromProcess(
IN PEPROCESS Process
)
/*++
Routine Description:
This routine retrieves the PFN of the process's top pagetable page. It can
be used to map physical pages back to a process.
Arguments:
Process - Supplies the process to query.
Return Value:
Page frame number of the top level page table page.
Environment:
Kernel mode. No locks held.
--*/
{
ASSERT (KeGetCurrentIrql () == PASSIVE_LEVEL);
return MI_GET_DIRECTORY_FRAME_FROM_PROCESS(Process);
}