952 lines
28 KiB
C
952 lines
28 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
shutdown.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the shutdown code for the memory management system.
|
||
|
||
Author:
|
||
|
||
Lou Perazzoli (loup) 21-Aug-1991
|
||
Landy Wang (landyw) 02-June-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "mi.h"
|
||
|
||
extern ULONG MmSystemShutdown;
|
||
|
||
VOID
|
||
MiReleaseAllMemory (
|
||
VOID
|
||
);
|
||
|
||
BOOLEAN
|
||
MiShutdownSystem (
|
||
VOID
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGELK,MiShutdownSystem)
|
||
#pragma alloc_text(PAGELK,MiReleaseAllMemory)
|
||
#pragma alloc_text(PAGELK,MmShutdownSystem)
|
||
#endif
|
||
|
||
ULONG MmZeroPageFile;
|
||
|
||
extern ULONG MmUnusedSegmentForceFree;
|
||
extern LIST_ENTRY MmSessionWideAddressList;
|
||
extern LIST_ENTRY MiVerifierDriverAddedThunkListHead;
|
||
extern LIST_ENTRY MmLoadedUserImageList;
|
||
extern PRTL_BITMAP MiSessionIdBitmap;
|
||
extern LOGICAL MiZeroingDisabled;
|
||
extern ULONG MmNumberOfMappedMdls;
|
||
extern ULONG MmNumberOfMappedMdlsInUse;
|
||
|
||
|
||
BOOLEAN
|
||
MiShutdownSystem (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function performs the shutdown of memory management. This
|
||
is accomplished by writing out all modified pages which are
|
||
destined for files other than the paging file.
|
||
|
||
All processes have already been killed, the registry shutdown and
|
||
shutdown IRPs already sent. On return from this phase all mapped
|
||
file data must be flushed and the unused segment list emptied.
|
||
This releases all the Mm references to file objects, allowing many
|
||
drivers (especially the network) to unload.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the pages were successfully written, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
LOGICAL PageLk;
|
||
SIZE_T ImportListSize;
|
||
PLOAD_IMPORTS ImportList;
|
||
PLOAD_IMPORTS ImportListNonPaged;
|
||
PLIST_ENTRY NextEntry;
|
||
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
PFN_NUMBER ModifiedPage;
|
||
PMMPFN Pfn1;
|
||
PSUBSECTION Subsection;
|
||
PCONTROL_AREA ControlArea;
|
||
PPFN_NUMBER Page;
|
||
PFILE_OBJECT FilePointer;
|
||
ULONG ConsecutiveFileLockFailures;
|
||
PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + MM_MAXIMUM_WRITE_CLUSTER];
|
||
PMDL Mdl;
|
||
NTSTATUS Status;
|
||
KEVENT IoEvent;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
KIRQL OldIrql;
|
||
LARGE_INTEGER StartingOffset;
|
||
ULONG count;
|
||
PFN_NUMBER j;
|
||
ULONG k;
|
||
PFN_NUMBER first;
|
||
ULONG write;
|
||
PMMPAGING_FILE PagingFile;
|
||
|
||
PageLk = FALSE;
|
||
|
||
//
|
||
// Don't do this more than once.
|
||
//
|
||
|
||
if (MmSystemShutdown == 0) {
|
||
|
||
PageLk = TRUE;
|
||
MmLockPagableSectionByHandle (ExPageLockHandle);
|
||
|
||
Mdl = (PMDL)&MdlHack;
|
||
Page = (PPFN_NUMBER)(Mdl + 1);
|
||
|
||
KeInitializeEvent (&IoEvent, NotificationEvent, FALSE);
|
||
|
||
MmInitializeMdl(Mdl,
|
||
NULL,
|
||
PAGE_SIZE);
|
||
|
||
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
ModifiedPage = MmModifiedPageListHead.Flink;
|
||
while (ModifiedPage != MM_EMPTY_LIST) {
|
||
|
||
//
|
||
// There are modified pages.
|
||
//
|
||
|
||
Pfn1 = MI_PFN_ELEMENT (ModifiedPage);
|
||
|
||
if (Pfn1->OriginalPte.u.Soft.Prototype == 1) {
|
||
|
||
//
|
||
// This page is destined for a file.
|
||
//
|
||
|
||
Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
|
||
ControlArea = Subsection->ControlArea;
|
||
if ((!ControlArea->u.Flags.Image) &&
|
||
(!ControlArea->u.Flags.NoModifiedWriting)) {
|
||
|
||
MiUnlinkPageFromList (Pfn1);
|
||
|
||
//
|
||
// Issue the write.
|
||
//
|
||
|
||
MI_SET_MODIFIED (Pfn1, 0, 0x28);
|
||
|
||
//
|
||
// Up the reference count for the physical page as there
|
||
// is I/O in progress.
|
||
//
|
||
|
||
MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1, 26);
|
||
Pfn1->u3.e2.ReferenceCount += 1;
|
||
|
||
*Page = ModifiedPage;
|
||
ControlArea->NumberOfMappedViews += 1;
|
||
ControlArea->NumberOfPfnReferences += 1;
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
StartingOffset.QuadPart = MiStartingOffset (Subsection,
|
||
Pfn1->PteAddress);
|
||
Mdl->StartVa = NULL;
|
||
|
||
ConsecutiveFileLockFailures = 0;
|
||
FilePointer = ControlArea->FilePointer;
|
||
|
||
retry:
|
||
KeClearEvent (&IoEvent);
|
||
|
||
Status = FsRtlAcquireFileForCcFlushEx (FilePointer);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = IoSynchronousPageWrite (FilePointer,
|
||
Mdl,
|
||
&StartingOffset,
|
||
&IoEvent,
|
||
&IoStatus);
|
||
|
||
//
|
||
// Release the file we acquired.
|
||
//
|
||
|
||
FsRtlReleaseFileForCcFlush (FilePointer);
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Only try the request more than once if the
|
||
// filesystem said it had a deadlock.
|
||
//
|
||
|
||
if (Status == STATUS_FILE_LOCK_CONFLICT) {
|
||
ConsecutiveFileLockFailures += 1;
|
||
if (ConsecutiveFileLockFailures < 5) {
|
||
KeDelayExecutionThread (KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)&MmShortTime);
|
||
goto retry;
|
||
}
|
||
goto wait_complete;
|
||
}
|
||
|
||
//
|
||
// Ignore all I/O failures - there is nothing that
|
||
// can be done at this point.
|
||
//
|
||
|
||
KeSetEvent (&IoEvent, 0, FALSE);
|
||
}
|
||
|
||
Status = KeWaitForSingleObject (&IoEvent,
|
||
WrPageOut,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)&MmTwentySeconds);
|
||
|
||
wait_complete:
|
||
|
||
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
|
||
MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
|
||
}
|
||
|
||
if (Status == STATUS_TIMEOUT) {
|
||
|
||
//
|
||
// The write did not complete in 20 seconds, assume
|
||
// that the file systems are hung and return an
|
||
// error.
|
||
//
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
MI_SET_MODIFIED (Pfn1, 1, 0xF);
|
||
|
||
MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (Pfn1, 27);
|
||
ControlArea->NumberOfMappedViews -= 1;
|
||
ControlArea->NumberOfPfnReferences -= 1;
|
||
|
||
//
|
||
// This routine returns with the PFN lock released!
|
||
//
|
||
|
||
MiCheckControlArea (ControlArea, NULL, OldIrql);
|
||
|
||
MmUnlockPagableImageSection (ExPageLockHandle);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
LOCK_PFN (OldIrql);
|
||
MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (Pfn1, 27);
|
||
ControlArea->NumberOfMappedViews -= 1;
|
||
ControlArea->NumberOfPfnReferences -= 1;
|
||
|
||
//
|
||
// This routine returns with the PFN lock released!
|
||
//
|
||
|
||
MiCheckControlArea (ControlArea, NULL, OldIrql);
|
||
LOCK_PFN (OldIrql);
|
||
|
||
//
|
||
// Restart scan at the front of the list.
|
||
//
|
||
|
||
ModifiedPage = MmModifiedPageListHead.Flink;
|
||
continue;
|
||
}
|
||
}
|
||
ModifiedPage = Pfn1->u1.Flink;
|
||
}
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
//
|
||
// If a high number of modified pages still exist, start the
|
||
// modified page writer and wait for 5 seconds.
|
||
//
|
||
|
||
if (MmAvailablePages < (MmFreeGoal * 2)) {
|
||
LARGE_INTEGER FiveSeconds = {(ULONG)(-5 * 1000 * 1000 * 10), -1};
|
||
|
||
KeSetEvent (&MmModifiedPageWriterEvent, 0, FALSE);
|
||
KeDelayExecutionThread (KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)&FiveSeconds);
|
||
}
|
||
|
||
//
|
||
// Indicate to the modified page writer that the system has
|
||
// shutdown.
|
||
//
|
||
|
||
MmSystemShutdown = 1;
|
||
|
||
//
|
||
// Check to see if the paging file should be overwritten.
|
||
// Only free blocks are written.
|
||
//
|
||
|
||
if (MmZeroPageFile) {
|
||
|
||
//
|
||
// Get pages to complete the write request.
|
||
//
|
||
|
||
Mdl->StartVa = NULL;
|
||
j = 0;
|
||
k = 0;
|
||
Page = (PPFN_NUMBER)(Mdl + 1);
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
if (MmAvailablePages < (MmModifiedWriteClusterSize + 20)) {
|
||
UNLOCK_PFN(OldIrql);
|
||
goto freecache;
|
||
}
|
||
|
||
do {
|
||
*Page = MiRemoveZeroPage ((ULONG)j & MmSecondaryColorMask);
|
||
Pfn1 = MI_PFN_ELEMENT (*Page);
|
||
Pfn1->u3.e2.ReferenceCount = 1;
|
||
ASSERT (Pfn1->u2.ShareCount == 0);
|
||
Pfn1->OriginalPte.u.Long = 0;
|
||
MI_SET_PFN_DELETED (Pfn1);
|
||
Page += 1;
|
||
j += 1;
|
||
} while (j < MmModifiedWriteClusterSize);
|
||
|
||
while (k < MmNumberOfPagingFiles) {
|
||
|
||
PagingFile = MmPagingFile[k];
|
||
|
||
count = 0;
|
||
write = FALSE;
|
||
|
||
//
|
||
// Initializing first is not needed for correctness, but
|
||
// without it the compiler cannot compile this code W4 to
|
||
// check for use of uninitialized variables.
|
||
//
|
||
|
||
first = 0;
|
||
|
||
for (j = 1; j < PagingFile->Size; j += 1) {
|
||
|
||
if (RtlCheckBit (PagingFile->Bitmap, j) == 0) {
|
||
|
||
if (count == 0) {
|
||
first = j;
|
||
}
|
||
count += 1;
|
||
if (count == MmModifiedWriteClusterSize) {
|
||
write = TRUE;
|
||
}
|
||
} else {
|
||
if (count != 0) {
|
||
|
||
//
|
||
// Issue a write.
|
||
//
|
||
|
||
write = TRUE;
|
||
}
|
||
}
|
||
|
||
if ((j == (PagingFile->Size - 1)) &&
|
||
(count != 0)) {
|
||
write = TRUE;
|
||
}
|
||
|
||
if (write) {
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
StartingOffset.QuadPart = (LONGLONG)first << PAGE_SHIFT;
|
||
Mdl->ByteCount = count << PAGE_SHIFT;
|
||
KeClearEvent (&IoEvent);
|
||
|
||
Status = IoSynchronousPageWrite (PagingFile->File,
|
||
Mdl,
|
||
&StartingOffset,
|
||
&IoEvent,
|
||
&IoStatus);
|
||
|
||
//
|
||
// Ignore all I/O failures - there is nothing that can
|
||
// be done at this point.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
KeSetEvent (&IoEvent, 0, FALSE);
|
||
}
|
||
|
||
Status = KeWaitForSingleObject (&IoEvent,
|
||
WrPageOut,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)&MmTwentySeconds);
|
||
|
||
if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
|
||
MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
|
||
}
|
||
|
||
if (Status == STATUS_TIMEOUT) {
|
||
|
||
//
|
||
// The write did not complete in 20 seconds, assume
|
||
// that the file systems are hung and return an
|
||
// error.
|
||
//
|
||
|
||
j = 0;
|
||
Page = (PPFN_NUMBER)(Mdl + 1);
|
||
LOCK_PFN (OldIrql);
|
||
do {
|
||
MiDecrementReferenceCount (*Page);
|
||
Page += 1;
|
||
j += 1;
|
||
} while (j < MmModifiedWriteClusterSize);
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
MmUnlockPagableImageSection (ExPageLockHandle);
|
||
return FALSE;
|
||
}
|
||
|
||
count = 0;
|
||
write = FALSE;
|
||
LOCK_PFN (OldIrql);
|
||
}
|
||
}
|
||
k += 1;
|
||
}
|
||
j = 0;
|
||
Page = (PPFN_NUMBER)(Mdl + 1);
|
||
do {
|
||
MiDecrementReferenceCount (*Page);
|
||
Page += 1;
|
||
j += 1;
|
||
} while (j < MmModifiedWriteClusterSize);
|
||
UNLOCK_PFN (OldIrql);
|
||
}
|
||
}
|
||
|
||
freecache:
|
||
|
||
if (PageLk == TRUE) {
|
||
MmUnlockPagableImageSection (ExPageLockHandle);
|
||
}
|
||
|
||
if (PoCleanShutdownEnabled ()) {
|
||
|
||
//
|
||
// Empty the unused segment list.
|
||
//
|
||
|
||
LOCK_PFN (OldIrql);
|
||
MmUnusedSegmentForceFree = (ULONG)-1;
|
||
KeSetEvent (&MmUnusedSegmentCleanup, 0, FALSE);
|
||
|
||
//
|
||
// Give it 5 seconds to empty otherwise assume the filesystems are
|
||
// hung and march on.
|
||
//
|
||
|
||
for (count = 0; count < 500; count += 1) {
|
||
|
||
if (IsListEmpty(&MmUnusedSegmentList)) {
|
||
break;
|
||
}
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
KeDelayExecutionThread (KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)&MmShortTime);
|
||
LOCK_PFN (OldIrql);
|
||
|
||
#if DBG
|
||
if (count == 400) {
|
||
|
||
//
|
||
// Everything should have been flushed by now. Give the
|
||
// filesystem team a chance to debug this on checked builds.
|
||
//
|
||
|
||
ASSERT (FALSE);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Resignal if needed in case more closed file objects triggered
|
||
// additional entries.
|
||
//
|
||
|
||
if (MmUnusedSegmentForceFree == 0) {
|
||
MmUnusedSegmentForceFree = (ULONG)-1;
|
||
KeSetEvent (&MmUnusedSegmentCleanup, 0, FALSE);
|
||
}
|
||
}
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
//
|
||
// Get rid of any paged pool references as they will be illegal
|
||
// by the time MmShutdownSystem is called again since the filesystems
|
||
// will have shutdown.
|
||
//
|
||
|
||
KeWaitForSingleObject (&MmSystemLoadLock,
|
||
WrVirtualMemory,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL);
|
||
|
||
NextEntry = PsLoadedModuleList.Flink;
|
||
while (NextEntry != &PsLoadedModuleList) {
|
||
|
||
DataTableEntry = CONTAINING_RECORD (NextEntry,
|
||
KLDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
|
||
ImportList = (PLOAD_IMPORTS)DataTableEntry->LoadedImports;
|
||
|
||
if ((ImportList != (PVOID)LOADED_AT_BOOT) &&
|
||
(ImportList != (PVOID)NO_IMPORTS_USED) &&
|
||
(!SINGLE_ENTRY(ImportList))) {
|
||
|
||
ImportListSize = ImportList->Count * sizeof(PVOID) + sizeof(SIZE_T);
|
||
ImportListNonPaged = (PLOAD_IMPORTS) ExAllocatePoolWithTag (NonPagedPool,
|
||
ImportListSize,
|
||
'TDmM');
|
||
|
||
if (ImportListNonPaged != NULL) {
|
||
RtlCopyMemory (ImportListNonPaged, ImportList, ImportListSize);
|
||
ExFreePool (ImportList);
|
||
DataTableEntry->LoadedImports = ImportListNonPaged;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Don't bother with the clean shutdown at this point.
|
||
//
|
||
|
||
PopShutdownCleanly = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free the full DLL name as it is pagable.
|
||
//
|
||
|
||
if (DataTableEntry->FullDllName.Buffer != NULL) {
|
||
ExFreePool (DataTableEntry->FullDllName.Buffer);
|
||
DataTableEntry->FullDllName.Buffer = NULL;
|
||
}
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
}
|
||
|
||
//
|
||
// Free any Hydra resources. Note that if any session is still alive
|
||
// (ie: all processes must be gone at this point) then the session
|
||
// drivers will not have unloaded. This is not supposed to happen
|
||
// so assert here to ensure this.
|
||
//
|
||
|
||
if (PoCleanShutdownEnabled()) {
|
||
ASSERT (IsListEmpty (&MmSessionWideAddressList) != 0);
|
||
ExFreePool (MiSessionIdBitmap);
|
||
}
|
||
|
||
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
|
||
|
||
//
|
||
// Close all the pagefile handles, note we still have an object
|
||
// reference to each keeping the underlying object resident.
|
||
// At the end of Phase1 shutdown we'll release those references
|
||
// to trigger the storage stack unload. The handle close must be
|
||
// done here however as it will reference pagable structures.
|
||
//
|
||
|
||
for (k = 0; k < MmNumberOfPagingFiles; k += 1) {
|
||
|
||
//
|
||
// Free each pagefile name now as it resides in paged pool and
|
||
// may need to be inpaged to be freed. Since the paging files
|
||
// are going to be shutdown shortly, now is the time to access
|
||
// pagable stuff and get rid of it. Zeroing the buffer pointer
|
||
// is sufficient as the only accesses to this are from the
|
||
// try-except-wrapped GetSystemInformation APIs and all the
|
||
// user processes are gone already.
|
||
//
|
||
|
||
ASSERT (MmPagingFile[k]->PageFileName.Buffer != NULL);
|
||
ExFreePool (MmPagingFile[k]->PageFileName.Buffer);
|
||
MmPagingFile[k]->PageFileName.Buffer = NULL;
|
||
|
||
ZwClose (MmPagingFile[k]->FileHandle);
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
MmShutdownSystem (
|
||
IN ULONG Phase
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function performs the shutdown of memory management. This
|
||
is accomplished by writing out all modified pages which are
|
||
destined for files other than the paging file.
|
||
|
||
Arguments:
|
||
|
||
Phase - Supplies 0 on the initiation of shutdown. All processes have
|
||
already been killed, the registry shutdown and shutdown IRPs already
|
||
sent. On return from this phase all mapped file data must be
|
||
flushed and the unused segment list emptied. This releases all
|
||
the Mm references to file objects, allowing many drivers (especially
|
||
the network) to unload.
|
||
|
||
Supplies 1 on the initiation of shutdown. The filesystem stack
|
||
has received its shutdown IRPs (the stack must free its paged pool
|
||
allocations here and lock down any pagable code it intends to call)
|
||
as no more references to pagable code or data are allowed on return.
|
||
ie: Any IoPageRead at this point is illegal.
|
||
Close the pagefile handles here so the filesystem stack will be
|
||
dereferenced causing those drivers to unload as well.
|
||
|
||
Supplies 2 on final shutdown of the system. Any resources not
|
||
freed by this point are treated as leaks and cause a bugcheck.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the pages were successfully written, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
|
||
if (Phase == 0) {
|
||
return MiShutdownSystem ();
|
||
}
|
||
|
||
if (Phase == 1) {
|
||
|
||
//
|
||
// The filesystem has shutdown. References to pagable code or data
|
||
// is no longer allowed at this point.
|
||
//
|
||
// Close the pagefile handles here so the filesystem stack will be
|
||
// dereferenced causing those drivers to unload as well.
|
||
//
|
||
|
||
if (MmSystemShutdown < 2) {
|
||
|
||
MmSystemShutdown = 2;
|
||
|
||
if (PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_PAGING) {
|
||
|
||
//
|
||
// Make any IoPageRead at this point illegal. Detect this by
|
||
// purging all system pagable memory.
|
||
//
|
||
|
||
MmTrimAllSystemPagableMemory (TRUE);
|
||
|
||
//
|
||
// There should be no dirty pages destined for the filesystem.
|
||
// Give the filesystem team a shot to debug this on checked
|
||
// builds.
|
||
//
|
||
|
||
ASSERT (MmModifiedPageListHead.Total == MmTotalPagesForPagingFile);
|
||
//
|
||
// Dereference all the pagefile objects to trigger a cascading
|
||
// unload of the storage stack as this should be the last
|
||
// reference to their driver objects.
|
||
//
|
||
|
||
for (i = 0; i < MmNumberOfPagingFiles; i += 1) {
|
||
ObDereferenceObject (MmPagingFile[i]->File);
|
||
}
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
ASSERT (Phase == 2);
|
||
|
||
//
|
||
// Check for resource leaks and bugcheck if any are found.
|
||
//
|
||
|
||
if (MmSystemShutdown < 3) {
|
||
MmSystemShutdown = 3;
|
||
if (PoCleanShutdownEnabled ()) {
|
||
MiReleaseAllMemory ();
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
MiReleaseAllMemory (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function performs the final release of memory management allocations.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
No references to paged pool or pagable code/data are allowed.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PEVENT_COUNTER EventSupport;
|
||
PUNLOADED_DRIVERS Entry;
|
||
PLIST_ENTRY NextEntry;
|
||
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
PLOAD_IMPORTS ImportList;
|
||
PMI_VERIFIER_DRIVER_ENTRY Verifier;
|
||
PMMINPAGE_SUPPORT Support;
|
||
PSINGLE_LIST_ENTRY SingleListEntry;
|
||
PDRIVER_SPECIFIED_VERIFIER_THUNKS ThunkTableBase;
|
||
PMMMOD_WRITER_MDL_ENTRY ModWriterEntry;
|
||
|
||
ASSERT (MmUnusedSegmentList.Flink == &MmUnusedSegmentList);
|
||
|
||
//
|
||
// Don't clear free pages so problems can be debugged.
|
||
//
|
||
|
||
MiZeroingDisabled = TRUE;
|
||
|
||
if (MiMirrorBitMap != NULL) {
|
||
ExFreePool (MiMirrorBitMap);
|
||
ASSERT (MiMirrorBitMap2);
|
||
ExFreePool (MiMirrorBitMap2);
|
||
}
|
||
|
||
//
|
||
// Free the unloaded driver list.
|
||
//
|
||
|
||
if (MmUnloadedDrivers != NULL) {
|
||
Entry = &MmUnloadedDrivers[0];
|
||
for (i = 0; i < MI_UNLOADED_DRIVERS; i += 1) {
|
||
if (Entry->Name.Buffer != NULL) {
|
||
RtlFreeUnicodeString (&Entry->Name);
|
||
}
|
||
Entry += 1;
|
||
}
|
||
ExFreePool (MmUnloadedDrivers);
|
||
}
|
||
|
||
NextEntry = MmLoadedUserImageList.Flink;
|
||
while (NextEntry != &MmLoadedUserImageList) {
|
||
|
||
DataTableEntry = CONTAINING_RECORD (NextEntry,
|
||
KLDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
|
||
ExFreePool ((PVOID)DataTableEntry);
|
||
}
|
||
|
||
//
|
||
// Release the loaded module list entries.
|
||
//
|
||
|
||
NextEntry = PsLoadedModuleList.Flink;
|
||
while (NextEntry != &PsLoadedModuleList) {
|
||
|
||
DataTableEntry = CONTAINING_RECORD (NextEntry,
|
||
KLDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
|
||
ImportList = (PLOAD_IMPORTS)DataTableEntry->LoadedImports;
|
||
|
||
if ((ImportList != (PVOID)LOADED_AT_BOOT) &&
|
||
(ImportList != (PVOID)NO_IMPORTS_USED) &&
|
||
(!SINGLE_ENTRY(ImportList))) {
|
||
|
||
ExFreePool (ImportList);
|
||
}
|
||
|
||
if (DataTableEntry->FullDllName.Buffer != NULL) {
|
||
ASSERT (DataTableEntry->FullDllName.Buffer == DataTableEntry->BaseDllName.Buffer);
|
||
}
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
|
||
ExFreePool ((PVOID)DataTableEntry);
|
||
}
|
||
|
||
//
|
||
// Free the physical memory descriptor block.
|
||
//
|
||
|
||
ExFreePool (MmPhysicalMemoryBlock);
|
||
|
||
//
|
||
// Free the system views structure.
|
||
//
|
||
|
||
if (MmSession.SystemSpaceViewTable != NULL) {
|
||
ExFreePool (MmSession.SystemSpaceViewTable);
|
||
}
|
||
|
||
if (MmSession.SystemSpaceBitMap != NULL) {
|
||
ExFreePool (MmSession.SystemSpaceBitMap);
|
||
}
|
||
|
||
//
|
||
// Free the pagefile structures - note the PageFileName buffer was freed
|
||
// earlier as it resided in paged pool and may have needed an inpage
|
||
// to be freed.
|
||
//
|
||
|
||
for (i = 0; i < MmNumberOfPagingFiles; i += 1) {
|
||
ASSERT (MmPagingFile[i]->PageFileName.Buffer == NULL);
|
||
ExFreePool (MmPagingFile[i]->Entry[0]);
|
||
ExFreePool (MmPagingFile[i]->Entry[1]);
|
||
ExFreePool (MmPagingFile[i]->Bitmap);
|
||
ExFreePool (MmPagingFile[i]);
|
||
}
|
||
|
||
ASSERT (MmNumberOfMappedMdlsInUse == 0);
|
||
|
||
i = 0;
|
||
while (IsListEmpty (&MmMappedFileHeader.ListHead) != 0) {
|
||
|
||
ModWriterEntry = (PMMMOD_WRITER_MDL_ENTRY)RemoveHeadList (
|
||
&MmMappedFileHeader.ListHead);
|
||
|
||
ExFreePool (ModWriterEntry);
|
||
i += 1;
|
||
}
|
||
ASSERT (i == MmNumberOfMappedMdls);
|
||
|
||
//
|
||
// Free the paged pool bitmaps.
|
||
//
|
||
|
||
ExFreePool (MmPagedPoolInfo.PagedPoolAllocationMap);
|
||
ExFreePool (MmPagedPoolInfo.EndOfPagedPoolBitmap);
|
||
|
||
if (VerifierLargePagedPoolMap != NULL) {
|
||
ExFreePool (VerifierLargePagedPoolMap);
|
||
}
|
||
|
||
//
|
||
// Free the inpage structures.
|
||
//
|
||
|
||
while (ExQueryDepthSList (&MmInPageSupportSListHead) != 0) {
|
||
|
||
SingleListEntry = InterlockedPopEntrySList (&MmInPageSupportSListHead);
|
||
|
||
if (SingleListEntry != NULL) {
|
||
Support = CONTAINING_RECORD (SingleListEntry,
|
||
MMINPAGE_SUPPORT,
|
||
ListEntry);
|
||
|
||
ASSERT (Support->u1.e1.PrefetchMdlHighBits == 0);
|
||
ExFreePool (Support);
|
||
}
|
||
}
|
||
|
||
while (ExQueryDepthSList (&MmEventCountSListHead) != 0) {
|
||
|
||
EventSupport = (PEVENT_COUNTER) InterlockedPopEntrySList (&MmEventCountSListHead);
|
||
|
||
if (EventSupport != NULL) {
|
||
ExFreePool (EventSupport);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free the verifier list last because it must be consulted to debug
|
||
// any bugchecks.
|
||
//
|
||
|
||
NextEntry = MiVerifierDriverAddedThunkListHead.Flink;
|
||
if (NextEntry != NULL) {
|
||
while (NextEntry != &MiVerifierDriverAddedThunkListHead) {
|
||
|
||
ThunkTableBase = CONTAINING_RECORD (NextEntry,
|
||
DRIVER_SPECIFIED_VERIFIER_THUNKS,
|
||
ListEntry );
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
ExFreePool (ThunkTableBase);
|
||
}
|
||
}
|
||
|
||
NextEntry = MiSuspectDriverList.Flink;
|
||
while (NextEntry != &MiSuspectDriverList) {
|
||
|
||
Verifier = CONTAINING_RECORD(NextEntry,
|
||
MI_VERIFIER_DRIVER_ENTRY,
|
||
Links);
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
ExFreePool (Verifier);
|
||
}
|
||
}
|