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.
3349 lines
108 KiB
3349 lines
108 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
init386.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the machine dependent initialization for the
|
|
memory management component. It is specifically tailored to the
|
|
INTEL x86 and PAE machines.
|
|
|
|
Author:
|
|
|
|
Lou Perazzoli (loup) 6-Jan-1990
|
|
Landy Wang (landyw) 2-Jun-1997
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "mi.h"
|
|
|
|
PFN_NUMBER
|
|
MxGetNextPage (
|
|
IN PFN_NUMBER PagesNeeded
|
|
);
|
|
|
|
PFN_NUMBER
|
|
MxPagesAvailable (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
MxConvertToLargePage (
|
|
IN PVOID VirtualAddress,
|
|
IN PVOID EndVirtualAddress
|
|
);
|
|
|
|
LOGICAL
|
|
MiIsRegularMemory (
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|
IN PFN_NUMBER PageFrameIndex
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,MiInitMachineDependent)
|
|
#pragma alloc_text(INIT,MxGetNextPage)
|
|
#pragma alloc_text(INIT,MxPagesAvailable)
|
|
#pragma alloc_text(INIT,MxConvertToLargePage)
|
|
#pragma alloc_text(INIT,MiReportPhysicalMemory)
|
|
#pragma alloc_text(INIT,MiIsRegularMemory)
|
|
#endif
|
|
|
|
#define MM_LARGE_PAGE_MINIMUM ((255*1024*1024) >> PAGE_SHIFT)
|
|
|
|
extern ULONG MmLargeSystemCache;
|
|
extern ULONG MmLargeStackSize;
|
|
extern LOGICAL MmMakeLowMemory;
|
|
extern LOGICAL MmPagedPoolMaximumDesired;
|
|
|
|
#if defined(_X86PAE_)
|
|
LOGICAL MiUseGlobalBitInLargePdes;
|
|
PVOID MmHyperSpaceEnd;
|
|
#endif
|
|
|
|
extern KEVENT MiImageMappingPteEvent;
|
|
|
|
//
|
|
// Local data.
|
|
//
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("INITDATA")
|
|
#endif
|
|
|
|
ULONG MxPfnAllocation;
|
|
|
|
PMEMORY_ALLOCATION_DESCRIPTOR MxFreeDescriptor;
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
MEMORY_ALLOCATION_DESCRIPTOR MxOldFreeDescriptor;
|
|
|
|
typedef struct _MI_LARGE_VA_RANGES {
|
|
PVOID VirtualAddress;
|
|
PVOID EndVirtualAddress;
|
|
} MI_LARGE_VA_RANGES, *PMI_LARGE_VA_RANGES;
|
|
|
|
//
|
|
// There are potentially 4 large page ranges:
|
|
//
|
|
// 1. PFN database
|
|
// 2. Initial nonpaged pool
|
|
// 3. Kernel code/data
|
|
// 4. HAL code/data
|
|
//
|
|
|
|
#define MI_LARGE_PFN_DATABASE 0x1
|
|
#define MI_LARGE_NONPAGED_POOL 0x2
|
|
#define MI_LARGE_KERNEL_HAL 0x4
|
|
|
|
#define MI_LARGE_ALL 0x7
|
|
|
|
ULONG MxMapLargePages = MI_LARGE_ALL;
|
|
|
|
#define MI_MAX_LARGE_VA_RANGES 4
|
|
|
|
ULONG MiLargeVaRangeIndex;
|
|
MI_LARGE_VA_RANGES MiLargeVaRanges[MI_MAX_LARGE_VA_RANGES];
|
|
|
|
#define MM_PFN_MAPPED_BY_PDE (MM_VA_MAPPED_BY_PDE >> PAGE_SHIFT)
|
|
|
|
|
|
PFN_NUMBER
|
|
MxGetNextPage (
|
|
IN PFN_NUMBER PagesNeeded
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the next physical page number from the largest
|
|
largest free descriptor. If there are not enough physical pages left
|
|
to satisfy the request then a bugcheck is executed since the system
|
|
cannot be initialized.
|
|
|
|
Arguments:
|
|
|
|
PagesNeeded - Supplies the number of pages needed.
|
|
|
|
Return Value:
|
|
|
|
The base of the range of physically contiguous pages.
|
|
|
|
Environment:
|
|
|
|
Kernel mode, Phase 0 only.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFN_NUMBER PageFrameIndex;
|
|
|
|
//
|
|
// Examine the free descriptor to see if enough usable memory is available.
|
|
//
|
|
|
|
if (PagesNeeded > MxFreeDescriptor->PageCount) {
|
|
|
|
KeBugCheckEx (INSTALL_MORE_MEMORY,
|
|
MmNumberOfPhysicalPages,
|
|
MxFreeDescriptor->PageCount,
|
|
MxOldFreeDescriptor.PageCount,
|
|
PagesNeeded);
|
|
}
|
|
|
|
PageFrameIndex = MxFreeDescriptor->BasePage;
|
|
|
|
MxFreeDescriptor->BasePage += PagesNeeded;
|
|
MxFreeDescriptor->PageCount -= PagesNeeded;
|
|
|
|
return PageFrameIndex;
|
|
}
|
|
|
|
PFN_NUMBER
|
|
MxPagesAvailable (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the number of pages available.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
The number of physically contiguous pages currently available.
|
|
|
|
Environment:
|
|
|
|
Kernel mode, Phase 0 only.
|
|
|
|
--*/
|
|
|
|
{
|
|
return MxFreeDescriptor->PageCount;
|
|
}
|
|
|
|
|
|
VOID
|
|
MxConvertToLargePage (
|
|
IN PVOID VirtualAddress,
|
|
IN PVOID EndVirtualAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function converts the backing for the supplied virtual address range
|
|
to a large page mapping.
|
|
|
|
Arguments:
|
|
|
|
VirtualAddress - Supplies the virtual address to convert to a large page.
|
|
|
|
EndVirtualAddress - Supplies the end virtual address to convert to a
|
|
large page.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel mode, Phase 1 only.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
MMPTE TempPde;
|
|
PMMPTE PointerPde;
|
|
PMMPTE LastPde;
|
|
PMMPTE PointerPte;
|
|
KIRQL OldIrql;
|
|
PMMPFN Pfn1;
|
|
PFN_NUMBER PageFrameIndex;
|
|
LOGICAL ValidPteFound;
|
|
PFN_NUMBER LargePageBaseFrame;
|
|
|
|
ASSERT (MxMapLargePages != 0);
|
|
|
|
PointerPde = MiGetPdeAddress (VirtualAddress);
|
|
LastPde = MiGetPdeAddress (EndVirtualAddress);
|
|
|
|
TempPde = ValidKernelPde;
|
|
TempPde.u.Hard.LargePage = 1;
|
|
|
|
#if defined(_X86PAE_)
|
|
if (MiUseGlobalBitInLargePdes == TRUE) {
|
|
TempPde.u.Hard.Global = 1;
|
|
}
|
|
#endif
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
do {
|
|
ASSERT (PointerPde->u.Hard.Valid == 1);
|
|
|
|
if (PointerPde->u.Hard.LargePage == 1) {
|
|
goto skip;
|
|
}
|
|
|
|
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
|
|
|
//
|
|
// Here's a nasty little hack - the page table page mapping the kernel
|
|
// and HAL (built by the loader) does not necessarily fill all the
|
|
// page table entries (ie: any number of leading entries may be zero).
|
|
//
|
|
// To deal with this, walk forward until a nonzero entry is found
|
|
// and re-index the large page based on this.
|
|
//
|
|
|
|
ValidPteFound = FALSE;
|
|
LargePageBaseFrame = (ULONG)-1;
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
|
|
ASSERT ((PageFrameIndex & (MM_PFN_MAPPED_BY_PDE - 1)) == 0);
|
|
|
|
for (i = 0; i < PTE_PER_PAGE; i += 1) {
|
|
|
|
ASSERT ((PointerPte->u.Long == ZeroKernelPte.u.Long) ||
|
|
(ValidPteFound == FALSE) ||
|
|
(PageFrameIndex == MI_GET_PAGE_FRAME_FROM_PTE (PointerPte)));
|
|
if (PointerPte->u.Hard.Valid == 1) {
|
|
if (ValidPteFound == FALSE) {
|
|
ValidPteFound = TRUE;
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
LargePageBaseFrame = PageFrameIndex - i;
|
|
}
|
|
}
|
|
PointerPte += 1;
|
|
PageFrameIndex += 1;
|
|
}
|
|
|
|
if (ValidPteFound == FALSE) {
|
|
goto skip;
|
|
}
|
|
|
|
TempPde.u.Hard.PageFrameNumber = LargePageBaseFrame;
|
|
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde);
|
|
|
|
MI_WRITE_VALID_PTE_NEW_PAGE (PointerPde, TempPde);
|
|
|
|
//
|
|
// Update the idle process to use the large page mapping also as
|
|
// the page table page is going to be freed.
|
|
//
|
|
|
|
MmSystemPagePtes [((ULONG_PTR)PointerPde &
|
|
(PD_PER_SYSTEM * (sizeof(MMPTE) * PDE_PER_PAGE) - 1)) / sizeof(MMPTE)] = TempPde;
|
|
|
|
KeFlushEntireTb (TRUE, TRUE);
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
|
Pfn1->u2.ShareCount = 0;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
Pfn1->u3.e1.PageLocation = StandbyPageList;
|
|
MI_SET_PFN_DELETED (Pfn1);
|
|
MiDecrementReferenceCount (Pfn1, PageFrameIndex);
|
|
|
|
skip:
|
|
PointerPde += 1;
|
|
} while (PointerPde <= LastPde);
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
}
|
|
|
|
VOID
|
|
MiReportPhysicalMemory (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during Phase 0 initialization once the
|
|
MmPhysicalMemoryBlock has been constructed. It's job is to decide
|
|
which large page ranges to enable later and also to construct a
|
|
large page comparison list so any requests which are not fully cached
|
|
can check this list in order to refuse conflicting requests.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel mode. Phase 0 only.
|
|
|
|
This is called before any non-MmCached allocations are made.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i, j;
|
|
PMMPTE PointerPte;
|
|
LOGICAL EntryFound;
|
|
PFN_NUMBER count;
|
|
PFN_NUMBER Page;
|
|
PFN_NUMBER LastPage;
|
|
PFN_NUMBER PageFrameIndex;
|
|
PFN_NUMBER LastPageFrameIndex;
|
|
PFN_NUMBER PageFrameIndex2;
|
|
|
|
//
|
|
// Examine the physical memory block to see whether large pages should
|
|
// be enabled. The key point is that all the physical pages within a
|
|
// given large page range must have the same cache attributes (MmCached)
|
|
// in order to maintain TB coherency. This can be done provided all
|
|
// the pages within the large page range represent real RAM (as described
|
|
// by the loader) so that memory management can control it. If any
|
|
// portion of the large page range is not RAM, it is possible that it
|
|
// may get used as noncached or writecombined device memory and
|
|
// therefore large pages cannot be used.
|
|
//
|
|
|
|
if (MxMapLargePages == 0) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < MiLargeVaRangeIndex; i += 1) {
|
|
PointerPte = MiGetPteAddress (MiLargeVaRanges[i].VirtualAddress);
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
|
|
PointerPte = MiGetPteAddress (MiLargeVaRanges[i].EndVirtualAddress);
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
LastPageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
|
|
//
|
|
// Round the start down to a page directory boundary and the end to
|
|
// the last page directory entry before the next boundary.
|
|
//
|
|
|
|
PageFrameIndex &= ~(MM_PFN_MAPPED_BY_PDE - 1);
|
|
LastPageFrameIndex |= (MM_PFN_MAPPED_BY_PDE - 1);
|
|
|
|
EntryFound = FALSE;
|
|
|
|
j = 0;
|
|
do {
|
|
|
|
count = MmPhysicalMemoryBlock->Run[j].PageCount;
|
|
Page = MmPhysicalMemoryBlock->Run[j].BasePage;
|
|
|
|
LastPage = Page + count;
|
|
|
|
if ((PageFrameIndex >= Page) && (LastPageFrameIndex < LastPage)) {
|
|
EntryFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
j += 1;
|
|
|
|
} while (j != MmPhysicalMemoryBlock->NumberOfRuns);
|
|
|
|
if (EntryFound == FALSE) {
|
|
|
|
//
|
|
// No entry was found that completely spans this large page range.
|
|
// Zero it so this range will not be converted into large pages
|
|
// later.
|
|
//
|
|
|
|
DbgPrint ("MM: Loader/HAL memory block indicates large pages cannot be used for %p->%p\n",
|
|
MiLargeVaRanges[i].VirtualAddress,
|
|
MiLargeVaRanges[i].EndVirtualAddress);
|
|
|
|
MiLargeVaRanges[i].VirtualAddress = NULL;
|
|
|
|
//
|
|
// Don't use large pages for anything if this chunk overlaps any
|
|
// others in the request list. This is because 2 separate ranges
|
|
// may share a straddling large page. If the first range was unable
|
|
// to use large pages, but the second one does ... then only part
|
|
// of the first range will get large pages if we enable large
|
|
// pages for the second range. This would be vey bad as we use
|
|
// the MI_IS_PHYSICAL macro everywhere and assume the entire
|
|
// range is in or out, so disable all large pages here instead.
|
|
//
|
|
|
|
for (j = 0; j < MiLargeVaRangeIndex; j += 1) {
|
|
|
|
//
|
|
// Skip the range that is already being rejected.
|
|
//
|
|
|
|
if (i == j) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Skip any range which has already been removed.
|
|
//
|
|
|
|
if (MiLargeVaRanges[j].VirtualAddress == NULL) {
|
|
continue;
|
|
}
|
|
|
|
PointerPte = MiGetPteAddress (MiLargeVaRanges[j].VirtualAddress);
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
|
|
if ((PageFrameIndex2 >= PageFrameIndex) &&
|
|
(PageFrameIndex2 <= LastPageFrameIndex)) {
|
|
|
|
DbgPrint ("MM: Disabling large pages for all ranges due to overlap\n");
|
|
|
|
goto RemoveAllRanges;
|
|
}
|
|
|
|
//
|
|
// Since it is not possible for any request chunk to completely
|
|
// encompass another one, checking only the start and end
|
|
// addresses is sufficient.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (MiLargeVaRanges[j].EndVirtualAddress);
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
PageFrameIndex2 = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
|
|
if ((PageFrameIndex2 >= PageFrameIndex) &&
|
|
(PageFrameIndex2 <= LastPageFrameIndex)) {
|
|
|
|
DbgPrint ("MM: Disabling large pages for all ranges due to overlap\n");
|
|
|
|
goto RemoveAllRanges;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No other ranges overlapped with this one, it is sufficient to
|
|
// just disable this range and continue to attempt to use large
|
|
// pages for any others.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
MiAddCachedRange (PageFrameIndex, LastPageFrameIndex);
|
|
}
|
|
|
|
return;
|
|
|
|
RemoveAllRanges:
|
|
|
|
while (i != 0) {
|
|
|
|
i -= 1;
|
|
|
|
if (MiLargeVaRanges[i].VirtualAddress != NULL) {
|
|
|
|
PointerPte = MiGetPteAddress (MiLargeVaRanges[i].VirtualAddress);
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
|
|
PointerPte = MiGetPteAddress (MiLargeVaRanges[i].EndVirtualAddress);
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
LastPageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
|
|
//
|
|
// Round the start down to a page directory boundary and the end to
|
|
// the last page directory entry before the next boundary.
|
|
//
|
|
|
|
PageFrameIndex &= ~(MM_PFN_MAPPED_BY_PDE - 1);
|
|
LastPageFrameIndex |= (MM_PFN_MAPPED_BY_PDE - 1);
|
|
|
|
MiRemoveCachedRange (PageFrameIndex, LastPageFrameIndex);
|
|
}
|
|
}
|
|
|
|
MiLargeVaRangeIndex = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
LONG MiAddPtesCount;
|
|
|
|
PMMPTE MiExtraPtes1Pointer;
|
|
ULONG MiExtraPtes1;
|
|
ULONG MiExtraPtes2;
|
|
|
|
|
|
LOGICAL
|
|
MiRecoverExtraPtes (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to recover extra PTEs for the system PTE pool.
|
|
These are not just added in earlier in Phase 0 because the system PTE
|
|
allocator uses the low addresses first which would fragment these
|
|
bigger ranges.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE if any PTEs were added, FALSE if not.
|
|
|
|
Environment:
|
|
|
|
Kernel mode.
|
|
|
|
--*/
|
|
|
|
{
|
|
LOGICAL PtesAdded;
|
|
PMMPTE PointerPte;
|
|
ULONG OriginalAddPtesCount;
|
|
|
|
//
|
|
// Make sure the add is only done once as this is called multiple times.
|
|
//
|
|
|
|
OriginalAddPtesCount = InterlockedCompareExchange (&MiAddPtesCount, 1, 0);
|
|
|
|
if (OriginalAddPtesCount != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
PtesAdded = FALSE;
|
|
|
|
if (MiExtraPtes1 != 0) {
|
|
|
|
//
|
|
// Add extra system PTEs to the pool.
|
|
//
|
|
|
|
MiAddSystemPtes (MiExtraPtes1Pointer, MiExtraPtes1, SystemPteSpace);
|
|
PtesAdded = TRUE;
|
|
}
|
|
|
|
if (MiExtraPtes2 != 0) {
|
|
|
|
//
|
|
// Add extra system PTEs to the pool.
|
|
//
|
|
|
|
if (MM_SHARED_USER_DATA_VA > MiUseMaximumSystemSpace) {
|
|
if (MiUseMaximumSystemSpaceEnd > MM_SHARED_USER_DATA_VA) {
|
|
MiExtraPtes2 = BYTES_TO_PAGES(MM_SHARED_USER_DATA_VA - MiUseMaximumSystemSpace);
|
|
}
|
|
}
|
|
|
|
if (MiExtraPtes2 != 0) {
|
|
PointerPte = MiGetPteAddress (MiUseMaximumSystemSpace);
|
|
MiAddSystemPtes (PointerPte, MiExtraPtes2, SystemPteSpace);
|
|
}
|
|
PtesAdded = TRUE;
|
|
}
|
|
|
|
return PtesAdded;
|
|
}
|
|
LOGICAL
|
|
MiIsRegularMemory (
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|
IN PFN_NUMBER PageFrameIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks whether the argument page frame index represents
|
|
regular memory in the loader descriptor block. It is only used very
|
|
early during Phase0 init because the MmPhysicalMemoryBlock is not yet
|
|
initialized.
|
|
|
|
Arguments:
|
|
|
|
LoaderBlock - Supplies a pointer to the firmware setup loader block.
|
|
|
|
PageFrameIndex - Supplies the page frame index to check.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the frame represents regular memory, FALSE if not.
|
|
|
|
Environment:
|
|
|
|
Kernel mode.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY NextMd;
|
|
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
|
|
|
|
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
|
|
|
|
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
|
|
|
|
MemoryDescriptor = CONTAINING_RECORD (NextMd,
|
|
MEMORY_ALLOCATION_DESCRIPTOR,
|
|
ListEntry);
|
|
|
|
if (PageFrameIndex >= MemoryDescriptor->BasePage) {
|
|
|
|
if (PageFrameIndex < MemoryDescriptor->BasePage + MemoryDescriptor->PageCount) {
|
|
|
|
if ((MemoryDescriptor->MemoryType == LoaderFirmwarePermanent) ||
|
|
(MemoryDescriptor->MemoryType == LoaderBBTMemory) ||
|
|
(MemoryDescriptor->MemoryType == LoaderSpecialMemory)) {
|
|
|
|
//
|
|
// This page lies in a memory descriptor for which we will
|
|
// never create PFN entries, hence return FALSE.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Since the loader memory list is sorted in ascending order,
|
|
// the requested page must not be in the loader list at all.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
NextMd = MemoryDescriptor->ListEntry.Flink;
|
|
}
|
|
|
|
//
|
|
// The final check before returning FALSE is to ensure that the requested
|
|
// page wasn't one of the ones we used to normal-map the loader mappings,
|
|
// etc.
|
|
//
|
|
|
|
if ((PageFrameIndex >= MxOldFreeDescriptor.BasePage) &&
|
|
(PageFrameIndex < MxOldFreeDescriptor.BasePage + MxOldFreeDescriptor.PageCount)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
MiInitMachineDependent (
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the necessary operations to enable virtual
|
|
memory. This includes building the page directory page, building
|
|
page table pages to map the code section, the data section, the
|
|
stack section and the trap handler.
|
|
|
|
It also initializes the PFN database and populates the free list.
|
|
|
|
Arguments:
|
|
|
|
LoaderBlock - Supplies a pointer to the firmware setup loader block.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
Kernel mode.
|
|
|
|
N.B. This routine uses memory from the loader block descriptors, but
|
|
the descriptors themselves must be restored prior to return as our caller
|
|
walks them to create the MmPhysicalMemoryBlock.
|
|
|
|
--*/
|
|
|
|
{
|
|
LOGICAL InitialNonPagedPoolSetViaRegistry;
|
|
PHYSICAL_ADDRESS MaxHotPlugMemoryAddress;
|
|
ULONG Bias;
|
|
PMMPTE BasePte;
|
|
PMMPFN BasePfn;
|
|
PMMPFN BottomPfn;
|
|
PMMPFN TopPfn;
|
|
PFN_NUMBER FirstNonPagedPoolPage;
|
|
PFN_NUMBER FirstPfnDatabasePage;
|
|
LOGICAL PfnInLargePages;
|
|
ULONG BasePage;
|
|
ULONG PagesLeft;
|
|
ULONG Range;
|
|
ULONG PageCount;
|
|
ULONG i, j;
|
|
ULONG PdePageNumber;
|
|
ULONG PdePage;
|
|
ULONG PageFrameIndex;
|
|
ULONG MaxPool;
|
|
PEPROCESS CurrentProcess;
|
|
ULONG DirBase;
|
|
ULONG MostFreePage;
|
|
ULONG MostFreeLowMem;
|
|
PFN_NUMBER PagesNeeded;
|
|
PLIST_ENTRY NextMd;
|
|
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
|
|
MMPTE TempPde;
|
|
MMPTE TempPte;
|
|
PMMPTE PointerPde;
|
|
PMMPTE PointerPte;
|
|
PMMPTE LastPte;
|
|
PMMPTE Pde;
|
|
PMMPTE StartPde;
|
|
PMMPTE EndPde;
|
|
PMMPFN Pfn1;
|
|
PMMPFN Pfn2;
|
|
ULONG PdeCount;
|
|
ULONG va;
|
|
KIRQL OldIrql;
|
|
PVOID VirtualAddress;
|
|
PVOID NonPagedPoolStartVirtual;
|
|
ULONG LargestFreePfnCount;
|
|
ULONG LargestFreePfnStart;
|
|
ULONG FreePfnCount;
|
|
PVOID NonPagedPoolStartLow;
|
|
LOGICAL ExtraSystemCacheViews;
|
|
SIZE_T MaximumNonPagedPoolInBytesLimit;
|
|
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
|
|
PLIST_ENTRY NextEntry;
|
|
ULONG ReturnedLength;
|
|
NTSTATUS status;
|
|
UCHAR Associativity;
|
|
ULONG NonPagedSystemStart;
|
|
LOGICAL PagedPoolMaximumDesired;
|
|
SIZE_T NumberOfBytes;
|
|
|
|
if (InitializationPhase == 1) {
|
|
|
|
//
|
|
// If the kernel image has not been biased to allow for 3gb of user
|
|
// space, *ALL* the booted processors support large pages, and the
|
|
// number of physical pages is greater than the threshold, then map
|
|
// the kernel image, HAL, PFN database and initial nonpaged pool
|
|
// with large pages.
|
|
//
|
|
|
|
if ((KeFeatureBits & KF_LARGE_PAGE) && (MxMapLargePages != 0)) {
|
|
for (i = 0; i < MiLargeVaRangeIndex; i += 1) {
|
|
if (MiLargeVaRanges[i].VirtualAddress != NULL) {
|
|
MxConvertToLargePage (MiLargeVaRanges[i].VirtualAddress,
|
|
MiLargeVaRanges[i].EndVirtualAddress);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
ASSERT (InitializationPhase == 0);
|
|
ASSERT (MxMapLargePages == MI_LARGE_ALL);
|
|
|
|
PfnInLargePages = FALSE;
|
|
ExtraSystemCacheViews = FALSE;
|
|
MostFreePage = 0;
|
|
MostFreeLowMem = 0;
|
|
LargestFreePfnCount = 0;
|
|
NonPagedPoolStartLow = NULL;
|
|
PagedPoolMaximumDesired = FALSE;
|
|
|
|
//
|
|
// Initializing these is not needed for correctness, but without it
|
|
// the compiler cannot compile this code W4 to check for use of
|
|
// uninitialized variables.
|
|
//
|
|
|
|
LargestFreePfnStart = 0;
|
|
FirstPfnDatabasePage = 0;
|
|
MaximumNonPagedPoolInBytesLimit = 0;
|
|
|
|
//
|
|
// If the chip doesn't support large pages or the system is booted /3GB,
|
|
// then disable large page support.
|
|
//
|
|
|
|
if (((KeFeatureBits & KF_LARGE_PAGE) == 0) || (MmVirtualBias != 0)) {
|
|
MxMapLargePages = 0;
|
|
}
|
|
|
|
//
|
|
// This flag is registry-settable so check before overriding.
|
|
//
|
|
|
|
if (MmProtectFreedNonPagedPool == TRUE) {
|
|
MxMapLargePages &= ~(MI_LARGE_PFN_DATABASE | MI_LARGE_NONPAGED_POOL);
|
|
}
|
|
|
|
//
|
|
// Sanitize this registry-specifiable large stack size. Note the registry
|
|
// size is in 1K chunks, ie: 32 means 32k. Note also that the registry
|
|
// setting does not include the guard page and we don't want to burden
|
|
// administrators with knowing about it so we automatically subtract one
|
|
// page from their request.
|
|
//
|
|
|
|
if (MmLargeStackSize > (KERNEL_LARGE_STACK_SIZE / 1024)) {
|
|
|
|
//
|
|
// No registry override or the override is too high.
|
|
// Set it to the default.
|
|
//
|
|
|
|
MmLargeStackSize = KERNEL_LARGE_STACK_SIZE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Convert registry override from 1K units to bytes. Note intelligent
|
|
// choices are 16k or 32k because we bin those sizes in sysptes.
|
|
//
|
|
|
|
MmLargeStackSize *= 1024;
|
|
MmLargeStackSize = MI_ROUND_TO_SIZE (MmLargeStackSize, PAGE_SIZE);
|
|
MmLargeStackSize -= PAGE_SIZE;
|
|
ASSERT (MmLargeStackSize <= KERNEL_LARGE_STACK_SIZE);
|
|
ASSERT ((MmLargeStackSize & (PAGE_SIZE-1)) == 0);
|
|
|
|
//
|
|
// Don't allow a value that is too low either.
|
|
//
|
|
|
|
if (MmLargeStackSize < KERNEL_STACK_SIZE) {
|
|
MmLargeStackSize = KERNEL_STACK_SIZE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the host processor supports global bits, then set the global
|
|
// bit in the template kernel PTE and PDE entries.
|
|
//
|
|
|
|
if (KeFeatureBits & KF_GLOBAL_PAGE) {
|
|
ValidKernelPte.u.Long |= MM_PTE_GLOBAL_MASK;
|
|
|
|
#if defined(_X86PAE_)
|
|
|
|
//
|
|
// Note that the PAE mode of the processor does not support the
|
|
// global bit in PDEs which map 4K page table pages.
|
|
//
|
|
|
|
MiUseGlobalBitInLargePdes = TRUE;
|
|
#else
|
|
ValidKernelPde.u.Long |= MM_PTE_GLOBAL_MASK;
|
|
#endif
|
|
MmPteGlobal.u.Long = MM_PTE_GLOBAL_MASK;
|
|
}
|
|
|
|
TempPte = ValidKernelPte;
|
|
TempPde = ValidKernelPde;
|
|
|
|
//
|
|
// Set the directory base for the system process.
|
|
//
|
|
|
|
PointerPte = MiGetPdeAddress (PDE_BASE);
|
|
PdePageNumber = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
|
|
|
|
CurrentProcess = PsGetCurrentProcess ();
|
|
|
|
#if defined(_X86PAE_)
|
|
|
|
PrototypePte.u.Soft.PageFileHigh = MI_PTE_LOOKUP_NEEDED;
|
|
|
|
_asm {
|
|
mov eax, cr3
|
|
mov DirBase, eax
|
|
}
|
|
|
|
//
|
|
// Note cr3 must be 32-byte aligned.
|
|
//
|
|
|
|
ASSERT ((DirBase & 0x1f) == 0);
|
|
|
|
//
|
|
// Initialize the PaeTop for this process right away.
|
|
//
|
|
|
|
RtlCopyMemory ((PVOID) &MiSystemPaeVa,
|
|
(PVOID) (KSEG0_BASE | DirBase),
|
|
sizeof (MiSystemPaeVa));
|
|
|
|
CurrentProcess->PaeTop = &MiSystemPaeVa;
|
|
|
|
#else
|
|
|
|
DirBase = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte) << PAGE_SHIFT;
|
|
|
|
#endif
|
|
|
|
CurrentProcess->Pcb.DirectoryTableBase[0] = DirBase;
|
|
KeSweepDcache (FALSE);
|
|
|
|
//
|
|
// Unmap the low 2Gb of memory.
|
|
//
|
|
|
|
PointerPde = MiGetPdeAddress (0);
|
|
LastPte = MiGetPdeAddress (KSEG0_BASE);
|
|
|
|
MiFillMemoryPte (PointerPde,
|
|
LastPte - PointerPde,
|
|
ZeroKernelPte.u.Long);
|
|
|
|
//
|
|
// Get the lower bound of the free physical memory and the
|
|
// number of physical pages by walking the memory descriptor lists.
|
|
//
|
|
|
|
MxFreeDescriptor = NULL;
|
|
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
|
|
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
|
|
MemoryDescriptor = CONTAINING_RECORD(NextMd,
|
|
MEMORY_ALLOCATION_DESCRIPTOR,
|
|
ListEntry);
|
|
|
|
if ((MemoryDescriptor->MemoryType != LoaderFirmwarePermanent) &&
|
|
(MemoryDescriptor->MemoryType != LoaderBBTMemory) &&
|
|
(MemoryDescriptor->MemoryType != LoaderHALCachedMemory) &&
|
|
(MemoryDescriptor->MemoryType != LoaderSpecialMemory)) {
|
|
|
|
//
|
|
// This check results in /BURNMEMORY chunks not being counted.
|
|
//
|
|
|
|
if (MemoryDescriptor->MemoryType != LoaderBad) {
|
|
MmNumberOfPhysicalPages += MemoryDescriptor->PageCount;
|
|
}
|
|
|
|
if (MemoryDescriptor->BasePage < MmLowestPhysicalPage) {
|
|
MmLowestPhysicalPage = MemoryDescriptor->BasePage;
|
|
}
|
|
|
|
if ((MemoryDescriptor->BasePage + MemoryDescriptor->PageCount) >
|
|
MmHighestPhysicalPage) {
|
|
MmHighestPhysicalPage =
|
|
MemoryDescriptor->BasePage + MemoryDescriptor->PageCount - 1;
|
|
}
|
|
|
|
//
|
|
// Locate the largest free descriptor.
|
|
//
|
|
|
|
if ((MemoryDescriptor->MemoryType == LoaderFree) ||
|
|
(MemoryDescriptor->MemoryType == LoaderLoadedProgram) ||
|
|
(MemoryDescriptor->MemoryType == LoaderFirmwareTemporary) ||
|
|
(MemoryDescriptor->MemoryType == LoaderOsloaderStack)) {
|
|
|
|
if (MemoryDescriptor->PageCount > MostFreePage) {
|
|
MostFreePage = MemoryDescriptor->PageCount;
|
|
MxFreeDescriptor = MemoryDescriptor;
|
|
}
|
|
}
|
|
}
|
|
|
|
NextMd = MemoryDescriptor->ListEntry.Flink;
|
|
}
|
|
|
|
if (MmLargeSystemCache != 0) {
|
|
ExtraSystemCacheViews = TRUE;
|
|
}
|
|
|
|
//
|
|
// This flag is registry-settable so check before overriding.
|
|
//
|
|
// Enabling special IRQL automatically disables mapping the kernel with
|
|
// large pages so we can catch kernel and HAL code.
|
|
//
|
|
|
|
if (MmVerifyDriverBufferLength != (ULONG)-1) {
|
|
MmLargePageMinimum = (ULONG)-2;
|
|
}
|
|
else if (MmLargePageMinimum == 0) {
|
|
MmLargePageMinimum = MM_LARGE_PAGE_MINIMUM;
|
|
}
|
|
|
|
//
|
|
// Capture the registry-specified initial nonpaged pool setting as we
|
|
// will modify the variable later.
|
|
//
|
|
|
|
if ((MmSizeOfNonPagedPoolInBytes != 0) ||
|
|
(MmMaximumNonPagedPoolPercent != 0)) {
|
|
|
|
InitialNonPagedPoolSetViaRegistry = TRUE;
|
|
}
|
|
else {
|
|
InitialNonPagedPoolSetViaRegistry = FALSE;
|
|
}
|
|
|
|
if (MmNumberOfPhysicalPages <= MmLargePageMinimum) {
|
|
|
|
MxMapLargePages = 0;
|
|
|
|
//
|
|
// Reduce the size of the initial nonpaged pool on small configurations
|
|
// as RAM is precious (unless the registry has overridden it).
|
|
//
|
|
|
|
if ((MmNumberOfPhysicalPages <= MM_LARGE_PAGE_MINIMUM) &&
|
|
(MmSizeOfNonPagedPoolInBytes == 0)) {
|
|
|
|
MmSizeOfNonPagedPoolInBytes = 2*1024*1024;
|
|
}
|
|
}
|
|
|
|
//
|
|
// MmDynamicPfn may have been initialized based on the registry to
|
|
// a value representing the highest physical address in gigabytes.
|
|
//
|
|
|
|
MmDynamicPfn *= ((1024 * 1024 * 1024) / PAGE_SIZE);
|
|
|
|
//
|
|
// Retrieve highest hot plug memory range from the HAL if
|
|
// available and not otherwise retrieved from the registry.
|
|
//
|
|
|
|
if (MmDynamicPfn == 0) {
|
|
|
|
status = HalQuerySystemInformation (HalQueryMaxHotPlugMemoryAddress,
|
|
sizeof(PHYSICAL_ADDRESS),
|
|
(PPHYSICAL_ADDRESS) &MaxHotPlugMemoryAddress,
|
|
&ReturnedLength);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
ASSERT (ReturnedLength == sizeof(PHYSICAL_ADDRESS));
|
|
|
|
MmDynamicPfn = (PFN_NUMBER) (MaxHotPlugMemoryAddress.QuadPart / PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
if (MmDynamicPfn != 0) {
|
|
if (MmVirtualBias != 0) {
|
|
MmDynamicPfn = 0;
|
|
}
|
|
}
|
|
|
|
if (MmDynamicPfn != 0) {
|
|
#if defined(_X86PAE_)
|
|
MmHighestPossiblePhysicalPage = MI_DTC_MAX_PAGES - 1;
|
|
#else
|
|
MmHighestPossiblePhysicalPage = MI_DEFAULT_MAX_PAGES - 1;
|
|
#endif
|
|
if (MmDynamicPfn - 1 < MmHighestPossiblePhysicalPage) {
|
|
if (MmDynamicPfn - 1 < MmHighestPhysicalPage) {
|
|
MmDynamicPfn = MmHighestPhysicalPage + 1;
|
|
}
|
|
MmHighestPossiblePhysicalPage = MmDynamicPfn - 1;
|
|
}
|
|
}
|
|
else {
|
|
MmHighestPossiblePhysicalPage = MmHighestPhysicalPage;
|
|
}
|
|
|
|
if (MmHighestPossiblePhysicalPage > 0x400000 - 1) {
|
|
|
|
//
|
|
// The PFN database is more than 112mb. Force it to come from the
|
|
// 2GB->3GB virtual address range. Note the administrator cannot be
|
|
// booting /3GB as when he does, the loader throws away memory
|
|
// above the physical 16GB line, so this must be a hot-add
|
|
// configuration. Since the loader has already put the system at
|
|
// 3GB, the highest possible hot add page must be reduced now.
|
|
//
|
|
|
|
if (MmVirtualBias != 0) {
|
|
MmHighestPossiblePhysicalPage = 0x400000 - 1;
|
|
|
|
if (MmHighestPhysicalPage > MmHighestPossiblePhysicalPage) {
|
|
MmHighestPhysicalPage = MmHighestPossiblePhysicalPage;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The virtual space between 2 and 3GB virtual is best used
|
|
// for system PTEs when this much physical memory is present.
|
|
//
|
|
|
|
ExtraSystemCacheViews = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Don't enable extra system cache views as virtual addresses are limited.
|
|
// Only a kernel-verifier special case can trigger this.
|
|
//
|
|
|
|
if ((KernelVerifier == TRUE) &&
|
|
(MmVirtualBias == 0) &&
|
|
(MmNumberOfPhysicalPages <= MmLargePageMinimum) &&
|
|
(MmHighestPossiblePhysicalPage > 0x100000)) {
|
|
|
|
ExtraSystemCacheViews = FALSE;
|
|
}
|
|
|
|
#if defined(_X86PAE_)
|
|
|
|
if (MmVirtualBias != 0) {
|
|
|
|
//
|
|
// User space is larger than 2GB, make extra room for the user space
|
|
// working set list & associated hash tables.
|
|
//
|
|
|
|
MmSystemCacheWorkingSetList = (PMMWSL) ((ULONG_PTR)
|
|
MmSystemCacheWorkingSetList + MM_SYSTEM_CACHE_WORKING_SET_3GB_DELTA);
|
|
}
|
|
|
|
MmHyperSpaceEnd = (PVOID)((ULONG_PTR)MmSystemCacheWorkingSetList - 1);
|
|
|
|
MmSystemCacheWorkingSetListPte = MiGetPteAddress (MmSystemCacheWorkingSetList);
|
|
|
|
//
|
|
// Only PAE machines with at least 5GB of physical memory get to use this
|
|
// and then only if they are NOT booted /3GB.
|
|
//
|
|
|
|
if (strstr(LoaderBlock->LoadOptions, "NOLOWMEM")) {
|
|
if ((MmVirtualBias == 0) &&
|
|
(MmNumberOfPhysicalPages >= 5 * 1024 * 1024 / 4)) {
|
|
MiNoLowMemory = (PFN_NUMBER)((ULONGLONG)_4gb / PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
if (MiNoLowMemory != 0) {
|
|
MmMakeLowMemory = TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Save the original descriptor value as everything must be restored
|
|
// prior to this function returning.
|
|
//
|
|
|
|
*(PMEMORY_ALLOCATION_DESCRIPTOR)&MxOldFreeDescriptor = *MxFreeDescriptor;
|
|
|
|
if (MmNumberOfPhysicalPages < 1100) {
|
|
KeBugCheckEx (INSTALL_MORE_MEMORY,
|
|
MmNumberOfPhysicalPages,
|
|
MmLowestPhysicalPage,
|
|
MmHighestPhysicalPage,
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Build non-paged pool using the physical pages following the
|
|
// data page in which to build the pool from. Non-paged pool grows
|
|
// from the high range of the virtual address space and expands
|
|
// downward.
|
|
//
|
|
// At this time non-paged pool is constructed so virtual addresses
|
|
// are also physically contiguous.
|
|
//
|
|
|
|
if ((MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT) >
|
|
(7 * (MmNumberOfPhysicalPages >> 3))) {
|
|
|
|
//
|
|
// More than 7/8 of memory is allocated to nonpagedpool, reset to 0.
|
|
//
|
|
|
|
MmSizeOfNonPagedPoolInBytes = 0;
|
|
if (MmMaximumNonPagedPoolPercent == 0) {
|
|
InitialNonPagedPoolSetViaRegistry = FALSE;
|
|
}
|
|
}
|
|
|
|
if (MmSizeOfNonPagedPoolInBytes < MmMinimumNonPagedPoolSize) {
|
|
|
|
//
|
|
// Calculate the size of nonpaged pool.
|
|
// Use the minimum size, then for every MB above 4mb add extra
|
|
// pages.
|
|
//
|
|
|
|
MmSizeOfNonPagedPoolInBytes = MmMinimumNonPagedPoolSize;
|
|
|
|
MmSizeOfNonPagedPoolInBytes +=
|
|
((MmNumberOfPhysicalPages - 1024)/256) *
|
|
MmMinAdditionNonPagedPoolPerMb;
|
|
}
|
|
|
|
if (MmSizeOfNonPagedPoolInBytes > MM_MAX_INITIAL_NONPAGED_POOL) {
|
|
MmSizeOfNonPagedPoolInBytes = MM_MAX_INITIAL_NONPAGED_POOL;
|
|
}
|
|
|
|
//
|
|
// If the registry specifies a total nonpaged pool percentage cap, enforce
|
|
// it here.
|
|
//
|
|
|
|
if (MmMaximumNonPagedPoolPercent != 0) {
|
|
|
|
if (MmMaximumNonPagedPoolPercent < 5) {
|
|
MmMaximumNonPagedPoolPercent = 5;
|
|
}
|
|
else if (MmMaximumNonPagedPoolPercent > 80) {
|
|
MmMaximumNonPagedPoolPercent = 80;
|
|
}
|
|
|
|
//
|
|
// Use the registry-expressed percentage value.
|
|
//
|
|
|
|
MaximumNonPagedPoolInBytesLimit =
|
|
((MmNumberOfPhysicalPages * MmMaximumNonPagedPoolPercent) / 100);
|
|
|
|
//
|
|
// Carefully set the maximum keeping in mind that maximum PAE
|
|
// machines can have 16*1024*1024 pages so care must be taken
|
|
// that multiplying by PAGE_SIZE doesn't overflow here.
|
|
//
|
|
|
|
if (MaximumNonPagedPoolInBytesLimit > ((MM_MAX_INITIAL_NONPAGED_POOL + MM_MAX_ADDITIONAL_NONPAGED_POOL) / PAGE_SIZE)) {
|
|
MaximumNonPagedPoolInBytesLimit = MM_MAX_INITIAL_NONPAGED_POOL + MM_MAX_ADDITIONAL_NONPAGED_POOL;
|
|
}
|
|
else {
|
|
MaximumNonPagedPoolInBytesLimit *= PAGE_SIZE;
|
|
}
|
|
|
|
if (MaximumNonPagedPoolInBytesLimit < 6 * 1024 * 1024) {
|
|
MaximumNonPagedPoolInBytesLimit = 6 * 1024 * 1024;
|
|
}
|
|
|
|
if (MmSizeOfNonPagedPoolInBytes > MaximumNonPagedPoolInBytesLimit) {
|
|
MmSizeOfNonPagedPoolInBytes = MaximumNonPagedPoolInBytesLimit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Align to page size boundary.
|
|
//
|
|
|
|
MmSizeOfNonPagedPoolInBytes &= ~(PAGE_SIZE - 1);
|
|
|
|
//
|
|
// Calculate the maximum size of pool.
|
|
//
|
|
|
|
if (MmMaximumNonPagedPoolInBytes == 0) {
|
|
|
|
//
|
|
// Calculate the size of nonpaged pool. If 4mb or less use
|
|
// the minimum size, then for every MB above 4mb add extra
|
|
// pages.
|
|
//
|
|
|
|
MmMaximumNonPagedPoolInBytes = MmDefaultMaximumNonPagedPool;
|
|
|
|
//
|
|
// Make sure enough expansion for the PFN database exists.
|
|
//
|
|
|
|
MmMaximumNonPagedPoolInBytes += (ULONG)PAGE_ALIGN (
|
|
(MmHighestPossiblePhysicalPage + 1) * sizeof(MMPFN));
|
|
|
|
//
|
|
// Only use the new formula for autosizing nonpaged pool on machines
|
|
// with at least 512MB. The new formula allocates 1/2 as much nonpaged
|
|
// pool per MB but scales much higher - machines with ~1.2GB or more
|
|
// get 256MB of nonpaged pool. Note that the old formula gave machines
|
|
// with 512MB of RAM 128MB of nonpaged pool so this behavior is
|
|
// preserved with the new formula as well.
|
|
//
|
|
|
|
if (MmNumberOfPhysicalPages >= 0x1f000) {
|
|
MmMaximumNonPagedPoolInBytes +=
|
|
((MmNumberOfPhysicalPages - 1024)/256) *
|
|
(MmMaxAdditionNonPagedPoolPerMb / 2);
|
|
|
|
if (MmMaximumNonPagedPoolInBytes < MM_MAX_ADDITIONAL_NONPAGED_POOL) {
|
|
MmMaximumNonPagedPoolInBytes = MM_MAX_ADDITIONAL_NONPAGED_POOL;
|
|
}
|
|
}
|
|
else {
|
|
MmMaximumNonPagedPoolInBytes +=
|
|
((MmNumberOfPhysicalPages - 1024)/256) *
|
|
MmMaxAdditionNonPagedPoolPerMb;
|
|
}
|
|
if ((MmMaximumNonPagedPoolPercent != 0) &&
|
|
(MmMaximumNonPagedPoolInBytes > MaximumNonPagedPoolInBytesLimit)) {
|
|
MmMaximumNonPagedPoolInBytes = MaximumNonPagedPoolInBytesLimit;
|
|
}
|
|
}
|
|
|
|
MaxPool = MmSizeOfNonPagedPoolInBytes + PAGE_SIZE * 16 +
|
|
(ULONG)PAGE_ALIGN (
|
|
(MmHighestPossiblePhysicalPage + 1) * sizeof(MMPFN));
|
|
|
|
if (MmMaximumNonPagedPoolInBytes < MaxPool) {
|
|
MmMaximumNonPagedPoolInBytes = MaxPool;
|
|
}
|
|
|
|
//
|
|
// Systems that are booted /3GB have a 128MB nonpaged pool maximum,
|
|
//
|
|
// Systems that have a full 2GB system virtual address space can enjoy an
|
|
// extra 128MB of nonpaged pool in the upper GB of the address space.
|
|
//
|
|
|
|
MaxPool = MM_MAX_INITIAL_NONPAGED_POOL;
|
|
|
|
if (MmVirtualBias == 0) {
|
|
MaxPool += MM_MAX_ADDITIONAL_NONPAGED_POOL;
|
|
}
|
|
|
|
if (InitialNonPagedPoolSetViaRegistry == TRUE) {
|
|
MaxPool = MmSizeOfNonPagedPoolInBytes + MM_MAX_ADDITIONAL_NONPAGED_POOL;
|
|
}
|
|
|
|
if (MmMaximumNonPagedPoolInBytes > MaxPool) {
|
|
MmMaximumNonPagedPoolInBytes = MaxPool;
|
|
}
|
|
|
|
//
|
|
// Grow the initial nonpaged pool if necessary so that the overall pool
|
|
// will aggregate to the right size.
|
|
//
|
|
|
|
if ((MmMaximumNonPagedPoolInBytes > MM_MAX_INITIAL_NONPAGED_POOL) &&
|
|
(InitialNonPagedPoolSetViaRegistry == FALSE)) {
|
|
|
|
if (MmSizeOfNonPagedPoolInBytes < MmMaximumNonPagedPoolInBytes - MM_MAX_ADDITIONAL_NONPAGED_POOL) {
|
|
|
|
//
|
|
// Note the initial nonpaged pool can only be grown if there
|
|
// is a sufficient contiguous physical memory chunk it can
|
|
// be carved from immediately.
|
|
//
|
|
|
|
PagesLeft = MxPagesAvailable ();
|
|
|
|
if (((MmMaximumNonPagedPoolInBytes - MM_MAX_ADDITIONAL_NONPAGED_POOL) >> PAGE_SHIFT) + ((32 * 1024 * 1024) >> PAGE_SHIFT) < PagesLeft) {
|
|
|
|
MmSizeOfNonPagedPoolInBytes = MmMaximumNonPagedPoolInBytes - MM_MAX_ADDITIONAL_NONPAGED_POOL;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Since the initial nonpaged pool could not be grown, don't
|
|
// leave any excess in the expansion nonpaged pool as we
|
|
// cannot encode it into subsection format on non-pae
|
|
// machines.
|
|
//
|
|
|
|
if (MmMaximumNonPagedPoolInBytes > MmSizeOfNonPagedPoolInBytes + MM_MAX_ADDITIONAL_NONPAGED_POOL) {
|
|
MmMaximumNonPagedPoolInBytes = MmSizeOfNonPagedPoolInBytes + MM_MAX_ADDITIONAL_NONPAGED_POOL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get secondary color value from:
|
|
//
|
|
// (a) from the registry (already filled in) or
|
|
// (b) from the PCR or
|
|
// (c) default value.
|
|
//
|
|
|
|
if (MmSecondaryColors == 0) {
|
|
|
|
Associativity = KeGetPcr()->SecondLevelCacheAssociativity;
|
|
|
|
MmSecondaryColors = KeGetPcr()->SecondLevelCacheSize;
|
|
|
|
if (Associativity != 0) {
|
|
MmSecondaryColors /= Associativity;
|
|
}
|
|
}
|
|
|
|
MmSecondaryColors = MmSecondaryColors >> PAGE_SHIFT;
|
|
|
|
if (MmSecondaryColors == 0) {
|
|
MmSecondaryColors = MM_SECONDARY_COLORS_DEFAULT;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Make sure the value is a power of two and within limits.
|
|
//
|
|
|
|
if (((MmSecondaryColors & (MmSecondaryColors - 1)) != 0) ||
|
|
(MmSecondaryColors < MM_SECONDARY_COLORS_MIN) ||
|
|
(MmSecondaryColors > MM_SECONDARY_COLORS_MAX)) {
|
|
MmSecondaryColors = MM_SECONDARY_COLORS_DEFAULT;
|
|
}
|
|
}
|
|
|
|
MmSecondaryColorMask = MmSecondaryColors - 1;
|
|
|
|
#if defined(MI_MULTINODE)
|
|
|
|
//
|
|
// Determine the number of bits in MmSecondaryColorMask. This
|
|
// is the number of bits the Node color must be shifted
|
|
// by before it is included in colors.
|
|
//
|
|
|
|
i = MmSecondaryColorMask;
|
|
MmSecondaryColorNodeShift = 0;
|
|
while (i != 0) {
|
|
i >>= 1;
|
|
MmSecondaryColorNodeShift += 1;
|
|
}
|
|
|
|
//
|
|
// Adjust the number of secondary colors by the number of nodes
|
|
// in the machine. The secondary color mask is NOT adjusted
|
|
// as it is used to control coloring within a node. The node
|
|
// color is added to the color AFTER normal color calculations
|
|
// are performed.
|
|
//
|
|
|
|
MmSecondaryColors *= KeNumberNodes;
|
|
|
|
for (i = 0; i < KeNumberNodes; i += 1) {
|
|
KeNodeBlock[i]->Color = i;
|
|
KeNodeBlock[i]->MmShiftedColor = i << MmSecondaryColorNodeShift;
|
|
InitializeSListHead(&KeNodeBlock[i]->DeadStackList);
|
|
}
|
|
|
|
#endif
|
|
|
|
MiMaximumSystemCacheSizeExtra = 0;
|
|
|
|
//
|
|
// Add in the PFN database size (based on the number of pages required
|
|
// from page zero to the highest page).
|
|
//
|
|
// Get the number of secondary colors and add the array for tracking
|
|
// secondary colors to the end of the PFN database.
|
|
//
|
|
|
|
MxPfnAllocation = 1 + ((((MmHighestPossiblePhysicalPage + 1) * sizeof(MMPFN)) +
|
|
(MmSecondaryColors * sizeof(MMCOLOR_TABLES)*2))
|
|
>> PAGE_SHIFT);
|
|
|
|
if (MmVirtualBias == 0) {
|
|
|
|
MmNonPagedPoolStart = (PVOID)((ULONG)MmNonPagedPoolEnd
|
|
- MmMaximumNonPagedPoolInBytes
|
|
+ MmSizeOfNonPagedPoolInBytes);
|
|
}
|
|
else {
|
|
|
|
MmNonPagedPoolStart = (PVOID)((ULONG) MmNonPagedPoolEnd -
|
|
(MmMaximumNonPagedPoolInBytes +
|
|
(MxPfnAllocation << PAGE_SHIFT)));
|
|
}
|
|
|
|
MmNonPagedPoolStart = (PVOID) PAGE_ALIGN (MmNonPagedPoolStart);
|
|
|
|
NonPagedPoolStartVirtual = MmNonPagedPoolStart;
|
|
|
|
//
|
|
// Allocate additional paged pool provided it can fit and either the
|
|
// user asked for it or we decide 460MB of PTE space is sufficient.
|
|
//
|
|
// Note at 64GB of RAM, the PFN database spans 464mb. Given that plus
|
|
// initial nonpaged pool at 128mb and space for the loader's highest
|
|
// page and session space, there may not be any room left to guarantee
|
|
// we will be able to allocate system PTEs out of the virtual address
|
|
// space below 3gb. So don't crimp for more than 64GB.
|
|
//
|
|
|
|
if ((MmVirtualBias == 0) &&
|
|
(MmHighestPossiblePhysicalPage <= 0x1000000)) {
|
|
|
|
if (((MmLargeStackSize <= (32 * 1024 - PAGE_SIZE)) && (MiUseMaximumSystemSpace != 0)) ||
|
|
((MmSizeOfPagedPoolInBytes == (SIZE_T)-1) ||
|
|
((MmSizeOfPagedPoolInBytes == 0) &&
|
|
(MmNumberOfPhysicalPages >= (1 * 1024 * 1024 * 1024 / PAGE_SIZE)) &&
|
|
(MiRequestedSystemPtes != (ULONG)-1)))) {
|
|
|
|
if ((ExpMultiUserTS == FALSE) || (MmSizeOfPagedPoolInBytes != 0)) {
|
|
|
|
PagedPoolMaximumDesired = TRUE;
|
|
MmPagedPoolMaximumDesired = TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// This is a multiuser TS machine defaulting to
|
|
// autoconfiguration. These machines use approximately
|
|
// 3.25x PTEs compared to paged pool per session.
|
|
// If the stack size is halved, then 1.6x becomes the ratio.
|
|
//
|
|
// Estimate how many PTEs and paged pool virtual space
|
|
// will be available and divide it up now.
|
|
//
|
|
|
|
ULONG LowVa;
|
|
ULONG TotalVirtualSpace;
|
|
ULONG PagedPoolPortion;
|
|
ULONG PtePortion;
|
|
|
|
TotalVirtualSpace = (ULONG) NonPagedPoolStartVirtual - (ULONG) MM_PAGED_POOL_START;
|
|
LowVa = (MM_KSEG0_BASE | MmBootImageSize) + MxPfnAllocation * PAGE_SIZE + MmSizeOfNonPagedPoolInBytes;
|
|
|
|
if (LowVa < MiSystemViewStart) {
|
|
TotalVirtualSpace += (MiSystemViewStart - LowVa);
|
|
}
|
|
|
|
PtePortion = 77;
|
|
PagedPoolPortion = 100 - PtePortion;
|
|
|
|
//
|
|
// If the large stack size has been reduced, then adjust the
|
|
// ratio automatically as well.
|
|
//
|
|
|
|
if (MmLargeStackSize != KERNEL_LARGE_STACK_SIZE) {
|
|
PtePortion = (PtePortion * MmLargeStackSize) / KERNEL_LARGE_STACK_SIZE;
|
|
}
|
|
|
|
MmSizeOfPagedPoolInBytes = (TotalVirtualSpace / (PagedPoolPortion + PtePortion)) * PagedPoolPortion;
|
|
}
|
|
|
|
//
|
|
// Make sure we always allocate extra PTEs later as we have crimped
|
|
// the initial allocation here.
|
|
//
|
|
|
|
ExtraSystemCacheViews = FALSE;
|
|
MmNumberOfSystemPtes = 3000;
|
|
MiRequestedSystemPtes = (ULONG)-1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Calculate the starting PDE for the system PTE pool which is
|
|
// right below the nonpaged pool.
|
|
//
|
|
|
|
MmNonPagedSystemStart = (PVOID)(((ULONG)NonPagedPoolStartVirtual -
|
|
((MmNumberOfSystemPtes + 1) * PAGE_SIZE)) &
|
|
(~PAGE_DIRECTORY_MASK));
|
|
|
|
if (MmNonPagedSystemStart < MM_LOWEST_NONPAGED_SYSTEM_START) {
|
|
|
|
MmNonPagedSystemStart = MM_LOWEST_NONPAGED_SYSTEM_START;
|
|
|
|
MmNumberOfSystemPtes = (((ULONG)NonPagedPoolStartVirtual -
|
|
(ULONG)MmNonPagedSystemStart) >> PAGE_SHIFT)-1;
|
|
|
|
ASSERT (MmNumberOfSystemPtes > 1000);
|
|
}
|
|
|
|
if (MmVirtualBias == 0) {
|
|
|
|
if ((MmSizeOfPagedPoolInBytes > ((ULONG) MmNonPagedSystemStart - (ULONG) MM_PAGED_POOL_START)) &&
|
|
(MmPagedPoolMaximumDesired == FALSE)) {
|
|
|
|
ULONG OldNonPagedSystemStart;
|
|
ULONG ExtraPtesNeeded;
|
|
ULONG InitialPagedPoolSize;
|
|
|
|
MmSizeOfPagedPoolInBytes = MI_ROUND_TO_SIZE (MmSizeOfPagedPoolInBytes, MM_VA_MAPPED_BY_PDE);
|
|
|
|
//
|
|
// Recalculate the starting PDE for the system PTE pool which is
|
|
// right below the nonpaged pool. Leave at least 3000 high
|
|
// system PTEs.
|
|
//
|
|
|
|
OldNonPagedSystemStart = (ULONG) MmNonPagedSystemStart;
|
|
|
|
NonPagedSystemStart = ((ULONG)NonPagedPoolStartVirtual -
|
|
((3000 + 1) * PAGE_SIZE)) &
|
|
~PAGE_DIRECTORY_MASK;
|
|
|
|
if (NonPagedSystemStart < (ULONG) MM_LOWEST_NONPAGED_SYSTEM_START) {
|
|
NonPagedSystemStart = (ULONG) MM_LOWEST_NONPAGED_SYSTEM_START;
|
|
}
|
|
|
|
InitialPagedPoolSize = NonPagedSystemStart - (ULONG) MM_PAGED_POOL_START;
|
|
|
|
if (MmSizeOfPagedPoolInBytes > InitialPagedPoolSize) {
|
|
MmSizeOfPagedPoolInBytes = InitialPagedPoolSize;
|
|
}
|
|
else {
|
|
NonPagedSystemStart = ((ULONG) MM_PAGED_POOL_START +
|
|
MmSizeOfPagedPoolInBytes);
|
|
|
|
ASSERT ((NonPagedSystemStart & PAGE_DIRECTORY_MASK) == 0);
|
|
|
|
ASSERT (NonPagedSystemStart >= (ULONG) MM_LOWEST_NONPAGED_SYSTEM_START);
|
|
}
|
|
|
|
ASSERT (NonPagedSystemStart >= OldNonPagedSystemStart);
|
|
ExtraPtesNeeded = (NonPagedSystemStart - OldNonPagedSystemStart) >> PAGE_SHIFT;
|
|
|
|
//
|
|
// Note the PagedPoolMaximumDesired local is deliberately not set
|
|
// because we don't want or need to delete PDEs later in this
|
|
// routine. The exact amount has been allocated here.
|
|
// The global MmPagedPoolMaximumDesired is set because other parts
|
|
// of memory management use it to finish sizing properly.
|
|
//
|
|
|
|
MmPagedPoolMaximumDesired = TRUE;
|
|
|
|
MmNonPagedSystemStart = (PVOID) NonPagedSystemStart;
|
|
MmNumberOfSystemPtes = (((ULONG)NonPagedPoolStartVirtual -
|
|
(ULONG)NonPagedSystemStart) >> PAGE_SHIFT)-1;
|
|
}
|
|
|
|
//
|
|
// If the kernel image has not been biased to allow for 3gb of user
|
|
// space, the host processor supports large pages, and the number of
|
|
// physical pages is greater than the threshold, then map the kernel
|
|
// image and HAL into a large page.
|
|
//
|
|
|
|
if (MxMapLargePages & MI_LARGE_KERNEL_HAL) {
|
|
|
|
//
|
|
// Add the kernel and HAL ranges to the large page ranges.
|
|
//
|
|
|
|
i = 0;
|
|
NextEntry = LoaderBlock->LoadOrderListHead.Flink;
|
|
|
|
for ( ; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) {
|
|
|
|
DataTableEntry = CONTAINING_RECORD (NextEntry,
|
|
KLDR_DATA_TABLE_ENTRY,
|
|
InLoadOrderLinks);
|
|
|
|
MiLargeVaRanges[MiLargeVaRangeIndex].VirtualAddress = DataTableEntry->DllBase;
|
|
MiLargeVaRanges[MiLargeVaRangeIndex].EndVirtualAddress =
|
|
(PVOID)((ULONG_PTR)DataTableEntry->DllBase + DataTableEntry->SizeOfImage - 1);
|
|
MiLargeVaRangeIndex += 1;
|
|
|
|
i += 1;
|
|
if (i == 2) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// If the processor supports large pages and the descriptor has
|
|
// enough contiguous pages for the entire PFN database then use
|
|
// large pages to map it. Regardless of large page support, put
|
|
// the PFN database in low virtual memory just above the loaded images.
|
|
//
|
|
|
|
PagesLeft = MxPagesAvailable ();
|
|
|
|
if ((MxMapLargePages & (MI_LARGE_PFN_DATABASE | MI_LARGE_NONPAGED_POOL))&&
|
|
(PagesLeft > MxPfnAllocation + (MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT) + ((32 * 1024 * 1024) >> PAGE_SHIFT))) {
|
|
|
|
//
|
|
// Allocate the PFN database using large pages as there is enough
|
|
// physically contiguous and decently aligned memory available.
|
|
//
|
|
|
|
PfnInLargePages = TRUE;
|
|
|
|
FirstPfnDatabasePage = MxGetNextPage (MxPfnAllocation);
|
|
|
|
MmPfnDatabase = (PMMPFN)(MM_KSEG0_BASE | MmBootImageSize);
|
|
|
|
ASSERT (((ULONG_PTR)MmPfnDatabase & (MM_VA_MAPPED_BY_PDE - 1)) == 0);
|
|
|
|
MmPfnDatabase = (PMMPFN) ((ULONG_PTR)MmPfnDatabase + (((FirstPfnDatabasePage & (MM_PFN_MAPPED_BY_PDE - 1))) << PAGE_SHIFT));
|
|
|
|
//
|
|
// Add the PFN database range to the large page ranges.
|
|
//
|
|
|
|
MiLargeVaRanges[MiLargeVaRangeIndex].VirtualAddress = MmPfnDatabase;
|
|
MiLargeVaRanges[MiLargeVaRangeIndex].EndVirtualAddress =
|
|
(PVOID) (((ULONG_PTR)MmPfnDatabase + (MxPfnAllocation << PAGE_SHIFT)) - 1);
|
|
MiLargeVaRangeIndex += 1;
|
|
}
|
|
else {
|
|
MxMapLargePages &= ~(MI_LARGE_PFN_DATABASE | MI_LARGE_NONPAGED_POOL);
|
|
MmPfnDatabase = (PMMPFN)(MM_KSEG0_BASE | MmBootImageSize);
|
|
}
|
|
|
|
//
|
|
// The system is booted 2GB, initial nonpaged pool immediately
|
|
// follows the PFN database.
|
|
//
|
|
// Since the PFN database and the initial nonpaged pool are physically
|
|
// adjacent, a single PDE is shared, thus reducing the number of pages
|
|
// that otherwise might need to be marked as must-be-cachable.
|
|
//
|
|
// Calculate the correct initial nonpaged pool virtual address and
|
|
// maximum size now. Don't allocate pages for any other use at this
|
|
// point to guarantee that the PFN database and nonpaged pool are
|
|
// physically contiguous so large pages can be enabled.
|
|
//
|
|
|
|
MmNonPagedPoolStart = (PVOID)((ULONG_PTR)MmPfnDatabase + (MxPfnAllocation << PAGE_SHIFT));
|
|
|
|
//
|
|
// Systems with extremely large PFN databases (ie: spanning 64GB of RAM)
|
|
// and bumped session space sizes may require a reduction in the initial
|
|
// nonpaged pool size in order to fit.
|
|
//
|
|
|
|
NumberOfBytes = MiSystemViewStart - (ULONG_PTR) MmNonPagedPoolStart;
|
|
|
|
if (MmSizeOfNonPagedPoolInBytes > NumberOfBytes) {
|
|
|
|
MmMaximumNonPagedPoolInBytes -= (MmSizeOfNonPagedPoolInBytes - NumberOfBytes);
|
|
|
|
MmSizeOfNonPagedPoolInBytes = NumberOfBytes;
|
|
}
|
|
|
|
if (PagedPoolMaximumDesired == TRUE) {
|
|
|
|
//
|
|
// Maximum paged pool was requested. This means slice away most of
|
|
// the system PTEs being used at the high end of the virtual address
|
|
// space and use that address range for more paged pool instead.
|
|
//
|
|
|
|
ASSERT (MiIsVirtualAddressOnPdeBoundary (MmNonPagedSystemStart));
|
|
|
|
PointerPde = MiGetPdeAddress (NonPagedPoolStartVirtual);
|
|
PointerPde -= 2;
|
|
|
|
MmNonPagedSystemStart = MiGetVirtualAddressMappedByPde (PointerPde);
|
|
MmNumberOfSystemPtes = (((ULONG)MmNonPagedPoolStart -
|
|
(ULONG)MmNonPagedSystemStart) >> PAGE_SHIFT)-1;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if ((MxPfnAllocation + 500) * PAGE_SIZE > MmMaximumNonPagedPoolInBytes - MmSizeOfNonPagedPoolInBytes) {
|
|
|
|
//
|
|
// Recarve portions of the initial and expansion nonpaged pools
|
|
// so enough expansion PTEs will be available to map the PFN
|
|
// database on large memory systems that are booted /3GB.
|
|
//
|
|
|
|
if ((MxPfnAllocation + 500) * PAGE_SIZE < MmSizeOfNonPagedPoolInBytes) {
|
|
MmSizeOfNonPagedPoolInBytes -= ((MxPfnAllocation + 500) * PAGE_SIZE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate pages and fill in the PTEs for the initial nonpaged pool.
|
|
//
|
|
|
|
PagesNeeded = MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT;
|
|
|
|
//
|
|
// Don't ask for more than is reasonable both in terms of physical pages
|
|
// left and virtual space available.
|
|
//
|
|
|
|
PagesLeft = MxPagesAvailable ();
|
|
|
|
if (PagesNeeded > PagesLeft) {
|
|
PagesNeeded = PagesLeft;
|
|
}
|
|
|
|
if (MxMapLargePages & MI_LARGE_NONPAGED_POOL) {
|
|
|
|
ASSERT (MmVirtualBias == 0);
|
|
|
|
//
|
|
// The PFN database has already been allocated (but not mapped).
|
|
// Shortly we will transition from the descriptors to the real PFN
|
|
// database so eat up the slush now.
|
|
//
|
|
|
|
VirtualAddress = (PVOID) ((ULONG_PTR)NonPagedPoolStartLow + (PagesNeeded << PAGE_SHIFT));
|
|
|
|
if (((ULONG_PTR)VirtualAddress & (MM_VA_MAPPED_BY_PDE - 1)) &&
|
|
(PagesLeft - PagesNeeded > MM_PFN_MAPPED_BY_PDE) &&
|
|
(MmSizeOfNonPagedPoolInBytes + MM_VA_MAPPED_BY_PDE < MM_MAX_INITIAL_NONPAGED_POOL)) {
|
|
|
|
//
|
|
// Expand the initial nonpaged pool to use the slush efficiently.
|
|
//
|
|
|
|
VirtualAddress = (PVOID) MI_ROUND_TO_SIZE ((ULONG_PTR)VirtualAddress, MM_VA_MAPPED_BY_PDE);
|
|
PagesNeeded = ((ULONG_PTR)VirtualAddress - (ULONG_PTR)NonPagedPoolStartLow) >> PAGE_SHIFT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update various globals since the size of initial pool may have
|
|
// changed.
|
|
//
|
|
|
|
if (MmSizeOfNonPagedPoolInBytes != (PagesNeeded << PAGE_SHIFT)) {
|
|
MmMaximumNonPagedPoolInBytes -= (MmSizeOfNonPagedPoolInBytes - (PagesNeeded << PAGE_SHIFT));
|
|
MmSizeOfNonPagedPoolInBytes = PagesNeeded << PAGE_SHIFT;
|
|
}
|
|
|
|
MmMaximumNonPagedPoolInPages = (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT);
|
|
|
|
//
|
|
// Allocate the actual pages for the initial nonpaged pool before
|
|
// any other Mx allocations as these will be sharing the large page
|
|
// with the PFN database when large pages are enabled.
|
|
//
|
|
|
|
PageFrameIndex = MxGetNextPage (PagesNeeded);
|
|
FirstNonPagedPoolPage = PageFrameIndex;
|
|
|
|
//
|
|
// Set up page table pages to map system PTEs and the expansion nonpaged
|
|
// pool. If the system was booted /3GB, then the initial nonpaged pool
|
|
// is mapped here as well.
|
|
//
|
|
|
|
StartPde = MiGetPdeAddress (MmNonPagedSystemStart);
|
|
EndPde = MiGetPdeAddress ((PVOID)((PCHAR)MmNonPagedPoolEnd - 1));
|
|
|
|
while (StartPde <= EndPde) {
|
|
|
|
ASSERT (StartPde->u.Hard.Valid == 0);
|
|
|
|
//
|
|
// Map in a page table page.
|
|
//
|
|
|
|
TempPde.u.Hard.PageFrameNumber = MxGetNextPage (1);
|
|
|
|
*StartPde = TempPde;
|
|
PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
|
|
RtlZeroMemory (PointerPte, PAGE_SIZE);
|
|
StartPde += 1;
|
|
}
|
|
|
|
if (MmVirtualBias == 0) {
|
|
|
|
if (MxMapLargePages & MI_LARGE_PFN_DATABASE) {
|
|
ASSERT (FirstNonPagedPoolPage == FirstPfnDatabasePage + MxPfnAllocation);
|
|
}
|
|
|
|
//
|
|
// Allocate the page table pages to map the PFN database and the
|
|
// initial nonpaged pool now. If the system switches to large
|
|
// pages in Phase 1, these pages will be discarded then.
|
|
//
|
|
|
|
StartPde = MiGetPdeAddress (MmPfnDatabase);
|
|
|
|
VirtualAddress = (PVOID) ((ULONG_PTR)MmNonPagedPoolStart + MmSizeOfNonPagedPoolInBytes - 1);
|
|
|
|
EndPde = MiGetPdeAddress (VirtualAddress);
|
|
|
|
//
|
|
// Use any extra virtual address space between the top of initial
|
|
// nonpaged pool and session space for additional system PTEs or
|
|
// caching.
|
|
//
|
|
|
|
PointerPde = EndPde + 1;
|
|
EndPde = MiGetPdeAddress (MiSystemViewStart - 1);
|
|
|
|
if (PointerPde <= EndPde) {
|
|
|
|
//
|
|
// There is available virtual space - consume everything up
|
|
// to the system view area (which is always rounded to a page
|
|
// directory boundary to avoid wasting valuable virtual
|
|
// address space.
|
|
//
|
|
|
|
MiExtraResourceStart = (ULONG) MiGetVirtualAddressMappedByPde (PointerPde);
|
|
MiExtraResourceEnd = MiSystemViewStart;
|
|
MiNumberOfExtraSystemPdes = EndPde - PointerPde + 1;
|
|
|
|
//
|
|
// Mark the new range as PTEs iff maximum PTEs are requested,
|
|
// TS in app server mode is selected or special pooling is
|
|
// enabled. Otherwise if large system caching was selected
|
|
// then use it for that. Finally default to PTEs if neither
|
|
// of the above.
|
|
//
|
|
|
|
if ((MiRequestedSystemPtes == (ULONG)-1) ||
|
|
(ExpMultiUserTS == TRUE) ||
|
|
(MmVerifyDriverBufferLength != (ULONG)-1) ||
|
|
((MmSpecialPoolTag != 0) && (MmSpecialPoolTag != (ULONG)-1))) {
|
|
|
|
ExtraSystemCacheViews = FALSE;
|
|
}
|
|
|
|
if (ExtraSystemCacheViews == TRUE) {
|
|
|
|
//
|
|
// The system is configured to favor large system caching,
|
|
// so share the remaining virtual address space between the
|
|
// system cache and system PTEs.
|
|
//
|
|
|
|
MiMaximumSystemCacheSizeExtra =
|
|
(MiNumberOfExtraSystemPdes * 5) / 6;
|
|
|
|
MiExtraPtes1 = MiNumberOfExtraSystemPdes -
|
|
MiMaximumSystemCacheSizeExtra;
|
|
|
|
MiExtraPtes1 *= (MM_VA_MAPPED_BY_PDE / PAGE_SIZE);
|
|
|
|
MiMaximumSystemCacheSizeExtra *= MM_VA_MAPPED_BY_PDE;
|
|
|
|
MiExtraPtes1Pointer = MiGetPteAddress (MiExtraResourceStart +
|
|
MiMaximumSystemCacheSizeExtra);
|
|
|
|
MiMaximumSystemCacheSizeExtra >>= PAGE_SHIFT;
|
|
}
|
|
else {
|
|
MiExtraPtes1 = BYTES_TO_PAGES(MiExtraResourceEnd - MiExtraResourceStart);
|
|
MiExtraPtes1Pointer = MiGetPteAddress (MiExtraResourceStart);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate and initialize the page table pages.
|
|
//
|
|
|
|
while (StartPde <= EndPde) {
|
|
|
|
ASSERT (StartPde->u.Hard.Valid == 0);
|
|
if (StartPde->u.Hard.Valid == 0) {
|
|
|
|
//
|
|
// Map in a page directory page.
|
|
//
|
|
|
|
TempPde.u.Hard.PageFrameNumber = MxGetNextPage (1);
|
|
|
|
*StartPde = TempPde;
|
|
PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
|
|
RtlZeroMemory (PointerPte, PAGE_SIZE);
|
|
}
|
|
StartPde += 1;
|
|
}
|
|
|
|
if (MiUseMaximumSystemSpace != 0) {
|
|
|
|
//
|
|
// Use the 1GB->2GB virtual range for even more system PTEs.
|
|
// Note the shared user data PTE (and PDE) must be left user
|
|
// accessible, but everything else is kernelmode only.
|
|
//
|
|
|
|
MiExtraPtes2 = BYTES_TO_PAGES(MiUseMaximumSystemSpaceEnd - MiUseMaximumSystemSpace);
|
|
|
|
StartPde = MiGetPdeAddress (MiUseMaximumSystemSpace);
|
|
EndPde = MiGetPdeAddress (MiUseMaximumSystemSpaceEnd);
|
|
|
|
while (StartPde < EndPde) {
|
|
|
|
ASSERT (StartPde->u.Hard.Valid == 0);
|
|
|
|
//
|
|
// Map in a page directory page.
|
|
//
|
|
|
|
TempPde.u.Hard.PageFrameNumber = MxGetNextPage (1);
|
|
|
|
*StartPde = TempPde;
|
|
PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
|
|
RtlZeroMemory (PointerPte, PAGE_SIZE);
|
|
StartPde += 1;
|
|
MiMaximumSystemExtraSystemPdes += 1;
|
|
}
|
|
|
|
ASSERT (MiExtraPtes2 == MiMaximumSystemExtraSystemPdes * PTE_PER_PAGE);
|
|
}
|
|
|
|
//
|
|
// The virtual address, length and page tables to map the initial
|
|
// nonpaged pool are already allocated - just fill in the mappings.
|
|
//
|
|
|
|
MmSubsectionBase = (ULONG)MmNonPagedPoolStart;
|
|
|
|
PointerPte = MiGetPteAddress (MmNonPagedPoolStart);
|
|
|
|
LastPte = MiGetPteAddress ((ULONG)MmNonPagedPoolStart +
|
|
MmSizeOfNonPagedPoolInBytes);
|
|
|
|
if (MxMapLargePages & (MI_LARGE_PFN_DATABASE | MI_LARGE_NONPAGED_POOL)) {
|
|
//
|
|
// Since every page table page needs to be filled, ensure PointerPte
|
|
// and LastPte span entire page table pages, and adjust
|
|
// PageFrameIndex to account for this.
|
|
//
|
|
|
|
if (!MiIsPteOnPdeBoundary(PointerPte)) {
|
|
PageFrameIndex -= (BYTE_OFFSET (PointerPte) / sizeof (MMPTE));
|
|
PointerPte = PAGE_ALIGN (PointerPte);
|
|
}
|
|
|
|
if (!MiIsPteOnPdeBoundary(LastPte)) {
|
|
LastPte = (PMMPTE) (PAGE_ALIGN (LastPte)) + PTE_PER_PAGE;
|
|
}
|
|
|
|
//
|
|
// Add the initial nonpaged pool range to the large page ranges.
|
|
//
|
|
|
|
MiLargeVaRanges[MiLargeVaRangeIndex].VirtualAddress = MmNonPagedPoolStart;
|
|
MiLargeVaRanges[MiLargeVaRangeIndex].EndVirtualAddress =
|
|
(PVOID) ((ULONG_PTR)MmNonPagedPoolStart + MmSizeOfNonPagedPoolInBytes - 1);
|
|
MiLargeVaRangeIndex += 1;
|
|
}
|
|
|
|
MI_ADD_EXECUTE_TO_VALID_PTE_IF_PAE (TempPte);
|
|
|
|
while (PointerPte < LastPte) {
|
|
ASSERT (PointerPte->u.Hard.Valid == 0);
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
MI_WRITE_VALID_PTE (PointerPte, TempPte);
|
|
PointerPte += 1;
|
|
PageFrameIndex += 1;
|
|
}
|
|
|
|
TempPte = ValidKernelPte;
|
|
|
|
MmNonPagedPoolExpansionStart = NonPagedPoolStartVirtual;
|
|
}
|
|
else {
|
|
|
|
PointerPte = MiGetPteAddress (MmNonPagedPoolStart);
|
|
|
|
LastPte = MiGetPteAddress((ULONG)MmNonPagedPoolStart +
|
|
MmSizeOfNonPagedPoolInBytes - 1);
|
|
|
|
ASSERT (PagesNeeded == (PFN_NUMBER)(LastPte - PointerPte + 1));
|
|
|
|
MI_ADD_EXECUTE_TO_VALID_PTE_IF_PAE (TempPte);
|
|
|
|
while (PointerPte <= LastPte) {
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
MI_WRITE_VALID_PTE (PointerPte, TempPte);
|
|
PointerPte += 1;
|
|
PageFrameIndex += 1;
|
|
}
|
|
|
|
TempPte = ValidKernelPte;
|
|
|
|
MmNonPagedPoolExpansionStart = (PVOID)((PCHAR)NonPagedPoolStartVirtual +
|
|
MmSizeOfNonPagedPoolInBytes);
|
|
|
|
//
|
|
// When booted /3GB, if /USERVA was specified then use any leftover
|
|
// virtual space between 2 and 3gb for extra system PTEs.
|
|
//
|
|
|
|
if (MiUseMaximumSystemSpace != 0) {
|
|
|
|
MiExtraPtes2 = BYTES_TO_PAGES(MiUseMaximumSystemSpaceEnd - MiUseMaximumSystemSpace);
|
|
|
|
StartPde = MiGetPdeAddress (MiUseMaximumSystemSpace);
|
|
EndPde = MiGetPdeAddress (MiUseMaximumSystemSpaceEnd);
|
|
|
|
while (StartPde < EndPde) {
|
|
|
|
ASSERT (StartPde->u.Hard.Valid == 0);
|
|
|
|
//
|
|
// Map in a page directory page.
|
|
//
|
|
|
|
TempPde.u.Hard.PageFrameNumber = MxGetNextPage (1);
|
|
|
|
*StartPde = TempPde;
|
|
PointerPte = MiGetVirtualAddressMappedByPte (StartPde);
|
|
RtlZeroMemory (PointerPte, PAGE_SIZE);
|
|
StartPde += 1;
|
|
MiMaximumSystemExtraSystemPdes += 1;
|
|
}
|
|
|
|
ASSERT (MiExtraPtes2 == MiMaximumSystemExtraSystemPdes * PTE_PER_PAGE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// There must be at least one page of system PTEs before the expanded
|
|
// nonpaged pool.
|
|
//
|
|
|
|
ASSERT (MiGetPteAddress(MmNonPagedSystemStart) < MiGetPteAddress(MmNonPagedPoolExpansionStart));
|
|
|
|
//
|
|
// Non-paged pages now exist, build the pool structures.
|
|
//
|
|
|
|
MmPageAlignedPoolBase[NonPagedPool] = MmNonPagedPoolStart;
|
|
|
|
if (MmVirtualBias != 0) {
|
|
|
|
ULONG NonPagedVa;
|
|
|
|
NonPagedVa = (ULONG) MmNonPagedPoolEnd - (ULONG) MmNonPagedPoolExpansionStart;
|
|
|
|
ASSERT (NonPagedVa >= (MxPfnAllocation << PAGE_SHIFT));
|
|
|
|
//
|
|
// Add one to account for the system PTE top guard page VA.
|
|
//
|
|
|
|
NonPagedVa -= ((MxPfnAllocation + 1) << PAGE_SHIFT);
|
|
|
|
if (NonPagedVa > MM_MAX_ADDITIONAL_NONPAGED_POOL) {
|
|
NonPagedVa = MM_MAX_ADDITIONAL_NONPAGED_POOL;
|
|
}
|
|
|
|
MmMaximumNonPagedPoolInBytes = NonPagedVa + (MxPfnAllocation << PAGE_SHIFT) + MmSizeOfNonPagedPoolInBytes;
|
|
MmMaximumNonPagedPoolInPages = (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT);
|
|
}
|
|
|
|
MiInitializeNonPagedPool ();
|
|
|
|
MiInitializeNonPagedPoolThresholds ();
|
|
|
|
//
|
|
// Before nonpaged pool can be used, the PFN database must
|
|
// be built. This is due to the fact that the start and end of
|
|
// allocation bits for nonpaged pool are maintained in the
|
|
// PFN elements for the corresponding pages.
|
|
//
|
|
|
|
if (MxMapLargePages & MI_LARGE_PFN_DATABASE) {
|
|
|
|
//
|
|
// The physical pages to be used for the PFN database have already
|
|
// been allocated. Initialize their mappings now.
|
|
//
|
|
|
|
//
|
|
// Initialize the page table mappings (the directory mappings are
|
|
// already initialized) for the PFN database until the switch to large
|
|
// pages occurs in Phase 1.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (MmPfnDatabase);
|
|
BasePte = MiGetVirtualAddressMappedByPte (MiGetPdeAddress (MmPfnDatabase));
|
|
|
|
LastPte = MiGetPteAddress ((ULONG_PTR)MmPfnDatabase + (MxPfnAllocation << PAGE_SHIFT));
|
|
if (!MiIsPteOnPdeBoundary(LastPte)) {
|
|
LastPte = MiGetVirtualAddressMappedByPte (MiGetPteAddress (LastPte) + 1);
|
|
}
|
|
|
|
PageFrameIndex = FirstPfnDatabasePage - (PointerPte - BasePte);
|
|
PointerPte = BasePte;
|
|
|
|
while (PointerPte < LastPte) {
|
|
ASSERT ((PointerPte->u.Hard.Valid == 0) ||
|
|
(PointerPte->u.Hard.PageFrameNumber == PageFrameIndex));
|
|
if (MiIsPteOnPdeBoundary(PointerPte)) {
|
|
ASSERT ((PageFrameIndex & (MM_PFN_MAPPED_BY_PDE - 1)) == 0);
|
|
}
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
if (PointerPte->u.Hard.Valid == 0) {
|
|
MI_WRITE_VALID_PTE (PointerPte, TempPte);
|
|
}
|
|
else {
|
|
MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte);
|
|
}
|
|
PointerPte += 1;
|
|
PageFrameIndex += 1;
|
|
}
|
|
|
|
RtlZeroMemory (MmPfnDatabase, MxPfnAllocation << PAGE_SHIFT);
|
|
|
|
//
|
|
// The PFN database was allocated in large pages. Since space was left
|
|
// for it virtually (in the nonpaged pool expansion PTEs), remove this
|
|
// now unused space if it can cause PTE encoding to exceed the 27 bits.
|
|
//
|
|
|
|
if (MmTotalFreeSystemPtes[NonPagedPoolExpansion] >
|
|
(MM_MAX_ADDITIONAL_NONPAGED_POOL >> PAGE_SHIFT)) {
|
|
//
|
|
// Reserve the expanded pool PTEs so they cannot be used.
|
|
//
|
|
|
|
ULONG PfnDatabaseSpace;
|
|
|
|
PfnDatabaseSpace = MmTotalFreeSystemPtes[NonPagedPoolExpansion] -
|
|
(MM_MAX_ADDITIONAL_NONPAGED_POOL >> PAGE_SHIFT);
|
|
|
|
if (MiReserveSystemPtes (PfnDatabaseSpace, NonPagedPoolExpansion) == NULL) {
|
|
MiIssueNoPtesBugcheck (PfnDatabaseSpace, NonPagedPoolExpansion);
|
|
}
|
|
|
|
//
|
|
// Adjust the end of nonpaged pool to reflect this reservation.
|
|
// This is so the entire nonpaged pool expansion space is available
|
|
// not just for general purpose consumption, but also for subsection
|
|
// encoding into protoptes when subsections are allocated from the
|
|
// very end of the expansion range.
|
|
//
|
|
|
|
MmNonPagedPoolEnd = (PVOID)((PCHAR)MmNonPagedPoolEnd - PfnDatabaseSpace * PAGE_SIZE);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Allocate one more PTE just below the PFN database. This provides
|
|
// protection against the caller of the first real nonpaged
|
|
// expansion allocation in case he accidentally overruns his pool
|
|
// block. (We'll trap instead of corrupting the PFN database).
|
|
// This also allows us to freely increment in MiFreePoolPages
|
|
// without having to worry about a valid PTE just after the end of
|
|
// the highest nonpaged pool allocation.
|
|
//
|
|
|
|
if (MiReserveSystemPtes (1, NonPagedPoolExpansion) == NULL) {
|
|
MiIssueNoPtesBugcheck (1, NonPagedPoolExpansion);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
ULONG FreeNextPhysicalPage;
|
|
ULONG FreeNumberOfPages;
|
|
|
|
//
|
|
// Calculate the start of the PFN database (it starts at physical
|
|
// page zero, even if the lowest physical page is not zero).
|
|
//
|
|
|
|
if (MmVirtualBias == 0) {
|
|
ASSERT (MmPfnDatabase != NULL);
|
|
PointerPte = MiGetPteAddress (MmPfnDatabase);
|
|
}
|
|
else {
|
|
ASSERT (MxPagesAvailable () >= MxPfnAllocation);
|
|
|
|
PointerPte = MiReserveSystemPtes (MxPfnAllocation,
|
|
NonPagedPoolExpansion);
|
|
|
|
if (PointerPte == NULL) {
|
|
MiIssueNoPtesBugcheck (MxPfnAllocation, NonPagedPoolExpansion);
|
|
}
|
|
|
|
MmPfnDatabase = (PMMPFN)(MiGetVirtualAddressMappedByPte (PointerPte));
|
|
|
|
//
|
|
// Adjust the end of nonpaged pool to reflect the PFN database
|
|
// allocation. This is so the entire nonpaged pool expansion space
|
|
// is available not just for general purpose consumption, but also
|
|
// for subsection encoding into protoptes when subsections are
|
|
// allocated from the very beginning of the initial nonpaged pool
|
|
// range.
|
|
//
|
|
|
|
MmMaximumNonPagedPoolInBytes -= (MxPfnAllocation << PAGE_SHIFT);
|
|
MmMaximumNonPagedPoolInPages = (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT);
|
|
|
|
MmNonPagedPoolEnd = (PVOID)MmPfnDatabase;
|
|
|
|
//
|
|
// Allocate one more PTE just below the PFN database. This provides
|
|
// protection against the caller of the first real nonpaged
|
|
// expansion allocation in case he accidentally overruns his pool
|
|
// block. (We'll trap instead of corrupting the PFN database).
|
|
// This also allows us to freely increment in MiFreePoolPages
|
|
// without having to worry about a valid PTE just after the end of
|
|
// the highest nonpaged pool allocation.
|
|
//
|
|
|
|
if (MiReserveSystemPtes (1, NonPagedPoolExpansion) == NULL) {
|
|
MiIssueNoPtesBugcheck (1, NonPagedPoolExpansion);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go through the memory descriptors and for each physical page make
|
|
// sure the PFN database has a valid PTE to map it. This allows
|
|
// machines with sparse physical memory to have a minimal PFN database.
|
|
//
|
|
|
|
FreeNextPhysicalPage = MxFreeDescriptor->BasePage;
|
|
FreeNumberOfPages = MxFreeDescriptor->PageCount;
|
|
|
|
PagesLeft = 0;
|
|
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
|
|
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
|
|
MemoryDescriptor = CONTAINING_RECORD(NextMd,
|
|
MEMORY_ALLOCATION_DESCRIPTOR,
|
|
ListEntry);
|
|
|
|
if ((MemoryDescriptor->MemoryType == LoaderFirmwarePermanent) ||
|
|
(MemoryDescriptor->MemoryType == LoaderBBTMemory) ||
|
|
(MemoryDescriptor->MemoryType == LoaderSpecialMemory)) {
|
|
|
|
//
|
|
// Skip these ranges.
|
|
//
|
|
|
|
NextMd = MemoryDescriptor->ListEntry.Flink;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Temporarily add back in the memory allocated since Phase 0
|
|
// began so PFN entries for it will be created and mapped.
|
|
//
|
|
// Note actual PFN entry allocations must be done carefully as
|
|
// memory from the descriptor itself could get used to map
|
|
// the PFNs for the descriptor !
|
|
//
|
|
|
|
if (MemoryDescriptor == MxFreeDescriptor) {
|
|
BasePage = MxOldFreeDescriptor.BasePage;
|
|
PageCount = MxOldFreeDescriptor.PageCount;
|
|
}
|
|
else {
|
|
BasePage = MemoryDescriptor->BasePage;
|
|
PageCount = MemoryDescriptor->PageCount;
|
|
}
|
|
|
|
PointerPte = MiGetPteAddress (MI_PFN_ELEMENT(BasePage));
|
|
|
|
LastPte = MiGetPteAddress (((PCHAR)(MI_PFN_ELEMENT(
|
|
BasePage + PageCount))) - 1);
|
|
|
|
while (PointerPte <= LastPte) {
|
|
if (PointerPte->u.Hard.Valid == 0) {
|
|
TempPte.u.Hard.PageFrameNumber = FreeNextPhysicalPage;
|
|
ASSERT (FreeNumberOfPages != 0);
|
|
FreeNextPhysicalPage += 1;
|
|
FreeNumberOfPages -= 1;
|
|
if (FreeNumberOfPages == 0) {
|
|
KeBugCheckEx (INSTALL_MORE_MEMORY,
|
|
MmNumberOfPhysicalPages,
|
|
FreeNumberOfPages,
|
|
MxOldFreeDescriptor.PageCount,
|
|
1);
|
|
}
|
|
PagesLeft += 1;
|
|
MI_WRITE_VALID_PTE (PointerPte, TempPte);
|
|
RtlZeroMemory (MiGetVirtualAddressMappedByPte (PointerPte),
|
|
PAGE_SIZE);
|
|
}
|
|
PointerPte += 1;
|
|
}
|
|
|
|
NextMd = MemoryDescriptor->ListEntry.Flink;
|
|
}
|
|
|
|
//
|
|
// Update the global counts - this would have been tricky to do while
|
|
// removing pages from them as we looped above.
|
|
//
|
|
// Later we will walk the memory descriptors and add pages to the free
|
|
// list in the PFN database.
|
|
//
|
|
// To do this correctly:
|
|
//
|
|
// The FreeDescriptor fields must be updated so the PFN database
|
|
// consumption isn't added to the freelist.
|
|
//
|
|
|
|
MxFreeDescriptor->BasePage = FreeNextPhysicalPage;
|
|
MxFreeDescriptor->PageCount = FreeNumberOfPages;
|
|
}
|
|
|
|
#if defined (_X86PAE_)
|
|
|
|
for (i = 0; i < 32; i += 1) {
|
|
j = i & 7;
|
|
switch (j) {
|
|
case MM_READONLY:
|
|
case MM_READWRITE:
|
|
case MM_WRITECOPY:
|
|
MmProtectToPteMask[i] |= MmPaeMask;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Initialize support for colored pages.
|
|
//
|
|
|
|
MmFreePagesByColor[0] = (PMMCOLOR_TABLES)
|
|
&MmPfnDatabase[MmHighestPossiblePhysicalPage + 1];
|
|
|
|
MmFreePagesByColor[1] = &MmFreePagesByColor[0][MmSecondaryColors];
|
|
|
|
//
|
|
// Make sure the PTEs are mapped.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (&MmFreePagesByColor[0][0]);
|
|
|
|
LastPte = MiGetPteAddress (
|
|
(PVOID)((PCHAR)&MmFreePagesByColor[1][MmSecondaryColors] - 1));
|
|
|
|
while (PointerPte <= LastPte) {
|
|
if (PointerPte->u.Hard.Valid == 0) {
|
|
TempPte.u.Hard.PageFrameNumber = MxGetNextPage (1);
|
|
MI_WRITE_VALID_PTE (PointerPte, TempPte);
|
|
RtlZeroMemory (MiGetVirtualAddressMappedByPte (PointerPte),
|
|
PAGE_SIZE);
|
|
}
|
|
|
|
PointerPte += 1;
|
|
}
|
|
|
|
for (i = 0; i < MmSecondaryColors; i += 1) {
|
|
MmFreePagesByColor[ZeroedPageList][i].Flink = MM_EMPTY_LIST;
|
|
MmFreePagesByColor[ZeroedPageList][i].Blink = (PVOID) MM_EMPTY_LIST;
|
|
MmFreePagesByColor[ZeroedPageList][i].Count = 0;
|
|
MmFreePagesByColor[FreePageList][i].Flink = MM_EMPTY_LIST;
|
|
MmFreePagesByColor[FreePageList][i].Blink = (PVOID) MM_EMPTY_LIST;
|
|
MmFreePagesByColor[FreePageList][i].Count = 0;
|
|
}
|
|
|
|
//
|
|
// Add nonpaged pool to PFN database if mapped via large pages.
|
|
//
|
|
|
|
PointerPde = MiGetPdeAddress (PTE_BASE);
|
|
|
|
if ((MmNonPagedPoolStart < (PVOID)MM_SYSTEM_CACHE_END_EXTRA) &&
|
|
(MxMapLargePages & MI_LARGE_NONPAGED_POOL)) {
|
|
|
|
j = FirstNonPagedPoolPage;
|
|
Pfn1 = MI_PFN_ELEMENT (j);
|
|
i = MmSizeOfNonPagedPoolInBytes >> PAGE_SHIFT;
|
|
|
|
do {
|
|
PointerPde = MiGetPdeAddress ((ULONG_PTR)MmNonPagedPoolStart + ((j - FirstNonPagedPoolPage) << PAGE_SHIFT));
|
|
Pfn1->u4.PteFrame = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
|
|
Pfn1->PteAddress = (PMMPTE)(j << PAGE_SHIFT);
|
|
Pfn1->u2.ShareCount += 1;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
Pfn1->u3.e1.PageLocation = ActiveAndValid;
|
|
Pfn1->u3.e1.CacheAttribute = MiCached;
|
|
MiDetermineNode (j, Pfn1);
|
|
j += 1;
|
|
Pfn1 += 1;
|
|
i -= 1;
|
|
} while (i != 0);
|
|
}
|
|
|
|
//
|
|
// Go through the page table entries and for any page which is valid,
|
|
// update the corresponding PFN database element.
|
|
//
|
|
|
|
Pde = MiGetPdeAddress (NULL);
|
|
va = 0;
|
|
PdeCount = PD_PER_SYSTEM * PDE_PER_PAGE;
|
|
|
|
for (i = 0; i < PdeCount; i += 1) {
|
|
|
|
//
|
|
// If the kernel image has been biased to allow for 3gb of user
|
|
// address space, then the first several mb of memory is
|
|
// double mapped to KSEG0_BASE and to ALTERNATE_BASE. Therefore,
|
|
// the KSEG0_BASE entries must be skipped.
|
|
//
|
|
|
|
if (MmVirtualBias != 0) {
|
|
if ((Pde >= MiGetPdeAddress(KSEG0_BASE)) &&
|
|
(Pde < MiGetPdeAddress(KSEG0_BASE + MmBootImageSize))) {
|
|
Pde += 1;
|
|
va += (ULONG)PDE_PER_PAGE * (ULONG)PAGE_SIZE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((Pde->u.Hard.Valid == 1) && (Pde->u.Hard.LargePage == 0)) {
|
|
|
|
PdePage = MI_GET_PAGE_FRAME_FROM_PTE(Pde);
|
|
|
|
if (MiIsRegularMemory (LoaderBlock, PdePage)) {
|
|
|
|
Pfn1 = MI_PFN_ELEMENT(PdePage);
|
|
Pfn1->u4.PteFrame = PdePageNumber;
|
|
Pfn1->PteAddress = Pde;
|
|
Pfn1->u2.ShareCount += 1;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
Pfn1->u3.e1.PageLocation = ActiveAndValid;
|
|
Pfn1->u3.e1.CacheAttribute = MiCached;
|
|
MiDetermineNode (PdePage, Pfn1);
|
|
}
|
|
else {
|
|
Pfn1 = NULL;
|
|
}
|
|
|
|
PointerPte = MiGetPteAddress (va);
|
|
|
|
//
|
|
// Set global bit.
|
|
//
|
|
|
|
TempPde.u.Long = MiDetermineUserGlobalPteMask (PointerPte) &
|
|
~MM_PTE_ACCESS_MASK;
|
|
|
|
#if defined(_X86PAE_)
|
|
|
|
//
|
|
// Note that the PAE mode of the processor does not support the
|
|
// global bit in PDEs which map 4K page table pages.
|
|
//
|
|
|
|
TempPde.u.Hard.Global = 0;
|
|
#endif
|
|
|
|
Pde->u.Long |= TempPde.u.Long;
|
|
|
|
for (j = 0 ; j < PTE_PER_PAGE; j += 1) {
|
|
if (PointerPte->u.Hard.Valid == 1) {
|
|
|
|
PointerPte->u.Long |= MiDetermineUserGlobalPteMask (PointerPte) &
|
|
~MM_PTE_ACCESS_MASK;
|
|
|
|
ASSERT (Pfn1 != NULL);
|
|
Pfn1->u2.ShareCount += 1;
|
|
|
|
if ((MiIsRegularMemory (LoaderBlock, (PFN_NUMBER) PointerPte->u.Hard.PageFrameNumber)) &&
|
|
|
|
((va >= MM_KSEG2_BASE) &&
|
|
((va < KSEG0_BASE + MmVirtualBias) ||
|
|
(va >= (KSEG0_BASE + MmVirtualBias + MmBootImageSize)))) ||
|
|
((MmVirtualBias == 0) &&
|
|
(va >= (ULONG)MmNonPagedPoolStart) &&
|
|
(va < (ULONG)MmNonPagedPoolStart + MmSizeOfNonPagedPoolInBytes))) {
|
|
|
|
Pfn2 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
|
|
|
|
if (MmIsAddressValid(Pfn2) &&
|
|
MmIsAddressValid((PUCHAR)(Pfn2+1)-1)) {
|
|
|
|
Pfn2->u4.PteFrame = PdePage;
|
|
Pfn2->PteAddress = PointerPte;
|
|
Pfn2->u2.ShareCount += 1;
|
|
Pfn2->u3.e2.ReferenceCount = 1;
|
|
Pfn2->u3.e1.PageLocation = ActiveAndValid;
|
|
Pfn2->u3.e1.CacheAttribute = MiCached;
|
|
MiDetermineNode(
|
|
(PFN_NUMBER)PointerPte->u.Hard.PageFrameNumber,
|
|
Pfn2);
|
|
}
|
|
}
|
|
}
|
|
|
|
va += PAGE_SIZE;
|
|
PointerPte += 1;
|
|
}
|
|
|
|
}
|
|
else {
|
|
va += (ULONG)PDE_PER_PAGE * (ULONG)PAGE_SIZE;
|
|
}
|
|
|
|
Pde += 1;
|
|
}
|
|
|
|
KeFlushCurrentTb ();
|
|
|
|
//
|
|
// If the lowest physical page is zero and the page is still unused, mark
|
|
// it as in use. This is because we want to find bugs where a physical
|
|
// page is specified as zero.
|
|
//
|
|
|
|
Pfn1 = &MmPfnDatabase[MmLowestPhysicalPage];
|
|
|
|
if ((MmLowestPhysicalPage == 0) && (Pfn1->u3.e2.ReferenceCount == 0)) {
|
|
|
|
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
|
|
|
|
//
|
|
// Make the reference count non-zero and point it into a
|
|
// page directory.
|
|
//
|
|
|
|
Pde = MiGetPdeAddress (0xffffffff);
|
|
PdePage = MI_GET_PAGE_FRAME_FROM_PTE(Pde);
|
|
Pfn1->u4.PteFrame = PdePageNumber;
|
|
Pfn1->PteAddress = Pde;
|
|
Pfn1->u2.ShareCount += 1;
|
|
Pfn1->u3.e2.ReferenceCount = 0xfff0;
|
|
Pfn1->u3.e1.PageLocation = ActiveAndValid;
|
|
Pfn1->u3.e1.CacheAttribute = MiCached;
|
|
MiDetermineNode (0, Pfn1);
|
|
}
|
|
|
|
//
|
|
// Walk through the memory descriptors and add pages to the
|
|
// free list in the PFN database. Before doing this, adjust the
|
|
// two descriptors we used so they only contain memory that can be
|
|
// freed now (ie: any memory we removed from them earlier in this routine
|
|
// without updating the descriptor for must be updated now).
|
|
//
|
|
|
|
//
|
|
// We may have taken memory out of the MxFreeDescriptor - but
|
|
// that's ok because we wouldn't want to free that memory right now
|
|
// (or ever) anyway.
|
|
//
|
|
|
|
//
|
|
// Since the LoaderBlock memory descriptors are ordered
|
|
// from low physical memory address to high, walk it backwards so the
|
|
// high physical pages go to the front of the freelists. The thinking
|
|
// is that pages initially allocated by the system are less likely to be
|
|
// freed so don't waste memory below 16mb (or 4gb) that may be needed
|
|
// by ISA drivers later.
|
|
//
|
|
|
|
NextMd = LoaderBlock->MemoryDescriptorListHead.Blink;
|
|
|
|
Bias = 0;
|
|
if (MmVirtualBias != 0) {
|
|
|
|
//
|
|
// This is nasty. You don't want to know. Cleanup needed.
|
|
//
|
|
|
|
Bias = ALTERNATE_BASE - KSEG0_BASE;
|
|
}
|
|
|
|
while (NextMd != &LoaderBlock->MemoryDescriptorListHead) {
|
|
|
|
MemoryDescriptor = CONTAINING_RECORD(NextMd,
|
|
MEMORY_ALLOCATION_DESCRIPTOR,
|
|
ListEntry);
|
|
|
|
i = MemoryDescriptor->PageCount;
|
|
PageFrameIndex = MemoryDescriptor->BasePage;
|
|
|
|
//
|
|
// Ensure no frames are inserted beyond the end of the PFN
|
|
// database. This can happen for example if the system
|
|
// has > 16GB of RAM and is booted /3GB - the top of this
|
|
// routine reduces the highest physical page and then
|
|
// creates the PFN database. But the loader block still
|
|
// contains descriptions of the pages above 16GB.
|
|
//
|
|
|
|
if (PageFrameIndex > MmHighestPhysicalPage) {
|
|
NextMd = MemoryDescriptor->ListEntry.Blink;
|
|
continue;
|
|
}
|
|
|
|
if (PageFrameIndex + i > MmHighestPhysicalPage + 1) {
|
|
i = MmHighestPhysicalPage + 1 - PageFrameIndex;
|
|
MemoryDescriptor->PageCount = i;
|
|
if (i == 0) {
|
|
NextMd = MemoryDescriptor->ListEntry.Blink;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (MemoryDescriptor->MemoryType) {
|
|
case LoaderBad:
|
|
while (i != 0) {
|
|
MiInsertPageInList (&MmBadPageListHead, PageFrameIndex);
|
|
i -= 1;
|
|
PageFrameIndex += 1;
|
|
}
|
|
break;
|
|
|
|
case LoaderFree:
|
|
case LoaderLoadedProgram:
|
|
case LoaderFirmwareTemporary:
|
|
case LoaderOsloaderStack:
|
|
|
|
FreePfnCount = 0;
|
|
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
|
LOCK_PFN (OldIrql);
|
|
while (i != 0) {
|
|
if (Pfn1->u3.e2.ReferenceCount == 0) {
|
|
|
|
//
|
|
// Set the PTE address to the physical page for
|
|
// virtual address alignment checking.
|
|
//
|
|
|
|
Pfn1->PteAddress =
|
|
(PMMPTE)(PageFrameIndex << PTE_SHIFT);
|
|
|
|
//
|
|
// No need to initialize Pfn1->u3.e1.CacheAttribute
|
|
// here as the freelist insertion will mark it as
|
|
// not-mapped.
|
|
//
|
|
|
|
MiDetermineNode (PageFrameIndex, Pfn1);
|
|
MiInsertPageInFreeList (PageFrameIndex);
|
|
FreePfnCount += 1;
|
|
}
|
|
else {
|
|
if (FreePfnCount > LargestFreePfnCount) {
|
|
LargestFreePfnCount = FreePfnCount;
|
|
LargestFreePfnStart = PageFrameIndex - FreePfnCount;
|
|
FreePfnCount = 0;
|
|
}
|
|
}
|
|
|
|
Pfn1 += 1;
|
|
i -= 1;
|
|
PageFrameIndex += 1;
|
|
}
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
if (FreePfnCount > LargestFreePfnCount) {
|
|
LargestFreePfnCount = FreePfnCount;
|
|
LargestFreePfnStart = PageFrameIndex - FreePfnCount;
|
|
}
|
|
|
|
break;
|
|
|
|
case LoaderFirmwarePermanent:
|
|
case LoaderSpecialMemory:
|
|
case LoaderBBTMemory:
|
|
|
|
//
|
|
// Skip these ranges.
|
|
//
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PointerPte = MiGetPteAddress (KSEG0_BASE + Bias +
|
|
(PageFrameIndex << PAGE_SHIFT));
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
|
while (i != 0) {
|
|
|
|
//
|
|
// Set page as in use.
|
|
//
|
|
|
|
PointerPde = MiGetPdeAddress (KSEG0_BASE + Bias +
|
|
(PageFrameIndex << PAGE_SHIFT));
|
|
|
|
if (Pfn1->u3.e2.ReferenceCount == 0) {
|
|
Pfn1->u4.PteFrame = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
|
|
Pfn1->PteAddress = PointerPte;
|
|
Pfn1->u2.ShareCount += 1;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
Pfn1->u3.e1.PageLocation = ActiveAndValid;
|
|
MiDetermineNode (PageFrameIndex, Pfn1);
|
|
|
|
if (MemoryDescriptor->MemoryType == LoaderXIPRom) {
|
|
Pfn1->u1.Flink = 0;
|
|
Pfn1->u2.ShareCount = 0;
|
|
Pfn1->u3.e2.ReferenceCount = 0;
|
|
Pfn1->u3.e1.PageLocation = 0;
|
|
Pfn1->u3.e1.Rom = 1;
|
|
Pfn1->u4.InPageError = 0;
|
|
Pfn1->u3.e1.PrototypePte = 1;
|
|
}
|
|
Pfn1->u3.e1.CacheAttribute = MiCached;
|
|
}
|
|
Pfn1 += 1;
|
|
i -= 1;
|
|
PageFrameIndex += 1;
|
|
PointerPte += 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
NextMd = MemoryDescriptor->ListEntry.Blink;
|
|
}
|
|
|
|
if (PfnInLargePages == FALSE) {
|
|
|
|
//
|
|
// Indicate that the PFN database is allocated in nonpaged pool.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (&MmPfnDatabase[MmLowestPhysicalPage]);
|
|
Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
|
|
Pfn1->u3.e1.StartOfAllocation = 1;
|
|
|
|
if (MmVirtualBias == 0) {
|
|
LastPte = MiGetPteAddress (&MmPfnDatabase[MmHighestPossiblePhysicalPage]);
|
|
while (PointerPte <= LastPte) {
|
|
Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
|
|
Pfn1->u2.ShareCount = 1;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
PointerPte += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the end of the allocation.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (&MmPfnDatabase[MmHighestPossiblePhysicalPage]);
|
|
Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
|
|
Pfn1->u3.e1.EndOfAllocation = 1;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The PFN database is allocated using large pages.
|
|
//
|
|
// Mark all PFN entries for the PFN pages in use.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (MmPfnDatabase);
|
|
PageFrameIndex = (PFN_NUMBER)PointerPte->u.Hard.PageFrameNumber;
|
|
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
|
|
i = MxPfnAllocation;
|
|
|
|
do {
|
|
Pfn1->PteAddress = (PMMPTE)(PageFrameIndex << PTE_SHIFT);
|
|
Pfn1->u3.e1.CacheAttribute = MiCached;
|
|
MiDetermineNode (PageFrameIndex, Pfn1);
|
|
Pfn1->u3.e2.ReferenceCount += 1;
|
|
PageFrameIndex += 1;
|
|
Pfn1 += 1;
|
|
i -= 1;
|
|
} while (i != 0);
|
|
|
|
if (MmDynamicPfn == 0) {
|
|
|
|
//
|
|
// Scan the PFN database backward for pages that are completely
|
|
// zero. These pages are unused and can be added to the free list.
|
|
//
|
|
|
|
BottomPfn = MI_PFN_ELEMENT(MmHighestPhysicalPage);
|
|
do {
|
|
|
|
//
|
|
// Compute the address of the start of the page that is next
|
|
// lower in memory and scan backwards until that page address
|
|
// is reached or just crossed.
|
|
//
|
|
|
|
if (((ULONG)BottomPfn & (PAGE_SIZE - 1)) != 0) {
|
|
BasePfn = (PMMPFN)((ULONG)BottomPfn & ~(PAGE_SIZE - 1));
|
|
TopPfn = BottomPfn + 1;
|
|
|
|
}
|
|
else {
|
|
BasePfn = (PMMPFN)((ULONG)BottomPfn - PAGE_SIZE);
|
|
TopPfn = BottomPfn;
|
|
}
|
|
|
|
while (BottomPfn > BasePfn) {
|
|
BottomPfn -= 1;
|
|
}
|
|
|
|
//
|
|
// If the entire range over which the PFN entries span is
|
|
// completely zero and the PFN entry that maps the page is
|
|
// not in the range, then add the page to the appropriate
|
|
// free list.
|
|
//
|
|
|
|
Range = (ULONG)TopPfn - (ULONG)BottomPfn;
|
|
if (RtlCompareMemoryUlong((PVOID)BottomPfn, Range, 0) == Range) {
|
|
|
|
//
|
|
// Set the PTE address to the physical page for virtual
|
|
// address alignment checking.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (BasePfn);
|
|
PageFrameIndex = (PFN_NUMBER)PointerPte->u.Hard.PageFrameNumber;
|
|
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
|
|
|
|
ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
|
|
ASSERT (Pfn1->PteAddress == (PMMPTE)(PageFrameIndex << PTE_SHIFT));
|
|
Pfn1->u3.e2.ReferenceCount = 0;
|
|
Pfn1->PteAddress = (PMMPTE)(PageFrameIndex << PTE_SHIFT);
|
|
|
|
//
|
|
// No need to initialize Pfn1->u3.e1.CacheAttribute
|
|
// here as the freelist insertion will mark it as
|
|
// not-mapped.
|
|
//
|
|
|
|
MiDetermineNode (PageFrameIndex, Pfn1);
|
|
LOCK_PFN (OldIrql);
|
|
MiInsertPageInFreeList (PageFrameIndex);
|
|
UNLOCK_PFN (OldIrql);
|
|
}
|
|
} while (BottomPfn > MmPfnDatabase);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adjust the memory descriptor to indicate that free pool has
|
|
// been used for nonpaged pool creation.
|
|
//
|
|
// N.B. This is required because the descriptors are walked upon
|
|
// return from this routine to create the MmPhysicalMemoryBlock.
|
|
//
|
|
|
|
*MxFreeDescriptor = *(PMEMORY_ALLOCATION_DESCRIPTOR)&MxOldFreeDescriptor;
|
|
|
|
//
|
|
// Initialize the nonpaged pool.
|
|
//
|
|
|
|
InitializePool (NonPagedPool, 0);
|
|
|
|
//
|
|
// Initialize the system PTE pool now that nonpaged pool exists.
|
|
// This is used for mapping I/O space, driver images and kernel stacks.
|
|
// Note this expands the initial PTE allocation to use all possible
|
|
// available virtual space by reclaiming the initial nonpaged
|
|
// pool range (in non /3GB systems) because that range has already been
|
|
// moved into the 2GB virtual range.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (MmNonPagedSystemStart);
|
|
ASSERT (((ULONG)PointerPte & (PAGE_SIZE - 1)) == 0);
|
|
|
|
MmNumberOfSystemPtes = MiGetPteAddress (NonPagedPoolStartVirtual) - PointerPte - 1;
|
|
|
|
MiInitializeSystemPtes (PointerPte, MmNumberOfSystemPtes, SystemPteSpace);
|
|
|
|
if (MiExtraPtes1 != 0) {
|
|
|
|
//
|
|
// Increment the system PTEs (for autoconfiguration purposes) but
|
|
// don't actually add the PTEs till later (to prevent fragmentation).
|
|
//
|
|
|
|
MiIncrementSystemPtes (MiExtraPtes1);
|
|
}
|
|
|
|
if (MiExtraPtes2 != 0) {
|
|
|
|
//
|
|
// Add extra system PTEs to the pool.
|
|
//
|
|
|
|
if (MM_SHARED_USER_DATA_VA > MiUseMaximumSystemSpace) {
|
|
if (MiUseMaximumSystemSpaceEnd > MM_SHARED_USER_DATA_VA) {
|
|
MiExtraPtes2 = BYTES_TO_PAGES(MM_SHARED_USER_DATA_VA - MiUseMaximumSystemSpace);
|
|
}
|
|
}
|
|
else {
|
|
ASSERT (MmVirtualBias != 0);
|
|
}
|
|
|
|
if (MiExtraPtes2 != 0) {
|
|
|
|
//
|
|
// Increment the system PTEs (for autoconfiguration purposes) but
|
|
// don't actually add the PTEs till later (to prevent
|
|
// fragmentation).
|
|
//
|
|
|
|
MiIncrementSystemPtes (MiExtraPtes2);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Recover the extra PTE ranges immediately if special pool is enabled
|
|
// so the special pool range can be made as large as possible by consuming
|
|
// these.
|
|
//
|
|
|
|
if ((MmVerifyDriverBufferLength != (ULONG)-1) ||
|
|
((MmSpecialPoolTag != 0) && (MmSpecialPoolTag != (ULONG)-1))) {
|
|
MiRecoverExtraPtes ();
|
|
}
|
|
|
|
//
|
|
// Initialize memory management structures for this process.
|
|
//
|
|
// Build the working set list. This requires the creation of a PDE
|
|
// to map hyperspace and the page table page pointed to
|
|
// by the PDE must be initialized.
|
|
//
|
|
// Note we can't remove a zeroed page as hyperspace does not
|
|
// exist and we map non-zeroed pages into hyperspace to zero them.
|
|
//
|
|
|
|
TempPde = ValidKernelPdeLocal;
|
|
|
|
PointerPde = MiGetPdeAddress (HYPER_SPACE);
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
PageFrameIndex = MiRemoveAnyPage (0);
|
|
TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
|
|
MI_WRITE_VALID_PTE (PointerPde, TempPde);
|
|
|
|
#if defined (_X86PAE_)
|
|
PointerPde = MiGetPdeAddress((PVOID)((PCHAR)HYPER_SPACE + MM_VA_MAPPED_BY_PDE));
|
|
|
|
PageFrameIndex = MiRemoveAnyPage (0);
|
|
TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
|
|
MI_WRITE_VALID_PTE (PointerPde, TempPde);
|
|
|
|
//
|
|
// Point to the page table page we just created and zero it.
|
|
//
|
|
|
|
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
|
RtlZeroMemory (PointerPte, PAGE_SIZE);
|
|
#endif
|
|
|
|
KeFlushCurrentTb();
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
//
|
|
// Point to the page table page we just created and zero it.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress(HYPER_SPACE);
|
|
RtlZeroMemory ((PVOID)PointerPte, PAGE_SIZE);
|
|
|
|
//
|
|
// Hyper space now exists, set the necessary variables.
|
|
//
|
|
|
|
MmFirstReservedMappingPte = MiGetPteAddress (FIRST_MAPPING_PTE);
|
|
MmLastReservedMappingPte = MiGetPteAddress (LAST_MAPPING_PTE);
|
|
|
|
MmFirstReservedMappingPte->u.Hard.PageFrameNumber = NUMBER_OF_MAPPING_PTES;
|
|
|
|
MmWorkingSetList = (PMMWSL) ((ULONG_PTR)VAD_BITMAP_SPACE + PAGE_SIZE);
|
|
|
|
//
|
|
// Create zeroing PTEs for the zero page thread.
|
|
//
|
|
|
|
MiFirstReservedZeroingPte = MiReserveSystemPtes (NUMBER_OF_ZEROING_PTES + 1,
|
|
SystemPteSpace);
|
|
|
|
RtlZeroMemory (MiFirstReservedZeroingPte,
|
|
(NUMBER_OF_ZEROING_PTES + 1) * sizeof(MMPTE));
|
|
|
|
//
|
|
// Use the page frame number field of the first PTE as an
|
|
// offset into the available zeroing PTEs.
|
|
//
|
|
|
|
MiFirstReservedZeroingPte->u.Hard.PageFrameNumber = NUMBER_OF_ZEROING_PTES;
|
|
|
|
//
|
|
// Create the VAD bitmap for this process.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (VAD_BITMAP_SPACE);
|
|
|
|
LOCK_PFN (OldIrql);
|
|
PageFrameIndex = MiRemoveAnyPage (0);
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
//
|
|
// Note the global bit must be off for the bitmap data.
|
|
//
|
|
|
|
TempPte = ValidKernelPteLocal;
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
MI_WRITE_VALID_PTE (PointerPte, TempPte);
|
|
|
|
//
|
|
// Point to the page we just created and zero it.
|
|
//
|
|
|
|
RtlZeroMemory (VAD_BITMAP_SPACE, PAGE_SIZE);
|
|
|
|
//
|
|
// If booted /3GB, then the bitmap needs to be 2K bigger, shift
|
|
// the working set accordingly as well.
|
|
//
|
|
// Note the 2K expansion portion of the bitmap is automatically
|
|
// carved out of the working set page allocated below.
|
|
//
|
|
|
|
if (MmVirtualBias != 0) {
|
|
MmWorkingSetList = (PMMWSL) ((ULONG_PTR)MmWorkingSetList + PAGE_SIZE / 2);
|
|
}
|
|
|
|
MiLastVadBit = (((ULONG_PTR) MI_64K_ALIGN (MM_HIGHEST_VAD_ADDRESS))) / X64K;
|
|
|
|
#if defined (_X86PAE_)
|
|
|
|
//
|
|
// Only bitmap the first 2GB of the PAE address space when booted /3GB.
|
|
// This is because PAE has twice as many pagetable pages as non-PAE which
|
|
// causes the MMWSL structure to be larger than 2K. If we bitmapped the
|
|
// entire user address space in this configuration then we'd need a 6K
|
|
// bitmap and this would cause the initial MMWSL structure to overflow
|
|
// into a second page. This would require a bunch of extra code throughout
|
|
// process support and other areas so just limit the bitmap for now.
|
|
//
|
|
|
|
if (MiLastVadBit > PAGE_SIZE * 8 - 1) {
|
|
ASSERT (MmVirtualBias != 0);
|
|
MiLastVadBit = PAGE_SIZE * 8 - 1;
|
|
MmWorkingSetList = (PMMWSL) ((ULONG_PTR)VAD_BITMAP_SPACE + PAGE_SIZE);
|
|
}
|
|
|
|
#endif
|
|
|
|
KeInitializeEvent (&MiImageMappingPteEvent,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
//
|
|
// Initialize this process's memory management structures including
|
|
// the working set list.
|
|
//
|
|
// The PFN element for the page directory has already been initialized,
|
|
// zero the reference count and the share count so they won't be
|
|
// wrong.
|
|
//
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (PdePageNumber);
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
Pfn1->u2.ShareCount = 0;
|
|
Pfn1->u3.e2.ReferenceCount = 0;
|
|
|
|
#if defined (_X86PAE_)
|
|
PointerPte = MiGetPteAddress (PDE_BASE);
|
|
for (i = 0; i < PD_PER_SYSTEM; i += 1) {
|
|
|
|
PdePageNumber = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (PdePageNumber);
|
|
Pfn1->u2.ShareCount = 0;
|
|
Pfn1->u3.e2.ReferenceCount = 0;
|
|
|
|
PointerPte += 1;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Get a page for the working set list and zero it.
|
|
//
|
|
|
|
TempPte = ValidKernelPteLocal;
|
|
PageFrameIndex = MiRemoveAnyPage (0);
|
|
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
|
|
|
PointerPte = MiGetPteAddress (MmWorkingSetList);
|
|
MI_WRITE_VALID_PTE (PointerPte, TempPte);
|
|
|
|
//
|
|
// Note that when booted /3GB, MmWorkingSetList is not page aligned, so
|
|
// always start zeroing from the start of the page regardless.
|
|
//
|
|
|
|
RtlZeroMemory (MiGetVirtualAddressMappedByPte (PointerPte), PAGE_SIZE);
|
|
|
|
CurrentProcess->WorkingSetPage = PageFrameIndex;
|
|
|
|
#if defined (_X86PAE_)
|
|
MiPaeInitialize ();
|
|
#endif
|
|
|
|
KeFlushCurrentTb();
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
CurrentProcess->Vm.MaximumWorkingSetSize = MmSystemProcessWorkingSetMax;
|
|
CurrentProcess->Vm.MinimumWorkingSetSize = MmSystemProcessWorkingSetMin;
|
|
|
|
MmInitializeProcessAddressSpace (CurrentProcess, NULL, NULL, NULL);
|
|
|
|
//
|
|
// Ensure the secondary page structures are marked as in use.
|
|
//
|
|
|
|
if (MmVirtualBias == 0) {
|
|
|
|
ASSERT (MmFreePagesByColor[0] < (PMMCOLOR_TABLES)MM_SYSTEM_CACHE_END_EXTRA);
|
|
|
|
PointerPde = MiGetPdeAddress(MmFreePagesByColor[0]);
|
|
ASSERT (PointerPde->u.Hard.Valid == 1);
|
|
|
|
PointerPte = MiGetPteAddress(MmFreePagesByColor[0]);
|
|
ASSERT (PointerPte->u.Hard.Valid == 1);
|
|
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
|
|
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
if (Pfn1->u3.e2.ReferenceCount == 0) {
|
|
Pfn1->u4.PteFrame = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
|
|
Pfn1->PteAddress = PointerPte;
|
|
Pfn1->u2.ShareCount += 1;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
Pfn1->u3.e1.PageLocation = ActiveAndValid;
|
|
Pfn1->u3.e1.CacheAttribute = MiCached;
|
|
MiDetermineNode (PageFrameIndex, Pfn1);
|
|
}
|
|
UNLOCK_PFN (OldIrql);
|
|
}
|
|
else if ((((ULONG)MmFreePagesByColor[0] & (PAGE_SIZE - 1)) == 0) &&
|
|
((MmSecondaryColors * 2 * sizeof(MMCOLOR_TABLES)) < PAGE_SIZE)) {
|
|
|
|
PMMCOLOR_TABLES c;
|
|
|
|
c = MmFreePagesByColor[0];
|
|
|
|
MmFreePagesByColor[0] = ExAllocatePoolWithTag (NonPagedPool,
|
|
MmSecondaryColors * 2 * sizeof(MMCOLOR_TABLES),
|
|
' mM');
|
|
|
|
if (MmFreePagesByColor[0] != NULL) {
|
|
|
|
MmFreePagesByColor[1] = &MmFreePagesByColor[0][MmSecondaryColors];
|
|
|
|
RtlCopyMemory (MmFreePagesByColor[0],
|
|
c,
|
|
MmSecondaryColors * 2 * sizeof(MMCOLOR_TABLES));
|
|
|
|
//
|
|
// Free the page.
|
|
//
|
|
|
|
PointerPte = MiGetPteAddress (c);
|
|
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
|
|
|
|
ASSERT (c > (PMMCOLOR_TABLES)MM_SYSTEM_CACHE_END_EXTRA);
|
|
MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
|
|
|
|
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
|
|
|
LOCK_PFN (OldIrql);
|
|
|
|
ASSERT ((Pfn1->u2.ShareCount <= 1) && (Pfn1->u3.e2.ReferenceCount <= 1));
|
|
Pfn1->u2.ShareCount = 0;
|
|
Pfn1->u3.e2.ReferenceCount = 1;
|
|
MI_SET_PFN_DELETED (Pfn1);
|
|
#if DBG
|
|
Pfn1->u3.e1.PageLocation = StandbyPageList;
|
|
#endif
|
|
MiDecrementReferenceCount (Pfn1, PageFrameIndex);
|
|
|
|
UNLOCK_PFN (OldIrql);
|
|
|
|
KeFlushSingleTb (c, FALSE);
|
|
}
|
|
else {
|
|
MmFreePagesByColor[0] = c;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|