diff --git a/reactos/ntoskrnl/include/internal/mm.h b/reactos/ntoskrnl/include/internal/mm.h index 8de04f4ef2c..c67ebd852b1 100644 --- a/reactos/ntoskrnl/include/internal/mm.h +++ b/reactos/ntoskrnl/include/internal/mm.h @@ -1045,9 +1045,13 @@ PMMPFN MiGetPfnEntry(IN PFN_TYPE Pfn) { PMMPFN Page; + extern RTL_BITMAP MiPfnBitMap; /* Make sure the PFN number is valid */ if (Pfn > MmHighestPhysicalPage) return NULL; + + /* Make sure this page actually has a PFN entry */ + if ((MiPfnBitMap.Buffer) && !(RtlTestBit(&MiPfnBitMap, Pfn))) return NULL; /* Get the entry */ Page = &MmPfnDatabase[Pfn]; diff --git a/reactos/ntoskrnl/mm/ARM3/i386/init.c b/reactos/ntoskrnl/mm/ARM3/i386/init.c index a3e886bae75..abf4aafe7a3 100644 --- a/reactos/ntoskrnl/mm/ARM3/i386/init.c +++ b/reactos/ntoskrnl/mm/ARM3/i386/init.c @@ -170,6 +170,20 @@ ULONG MmNumberOfSystemPtes; // ULONG MxPfnAllocation; +// +// Unlike the old ReactOS Memory Manager, ARM³ (and Windows) does not keep track +// of pages that are not actually valid physical memory, such as ACPI reserved +// regions, BIOS address ranges, or holes in physical memory address space which +// could indicate device-mapped I/O memory. +// +// In fact, the lack of a PFN entry for a page usually indicates that this is +// I/O space instead. +// +// A bitmap, called the PFN bitmap, keeps track of all page frames by assigning +// a bit to each. If the bit is set, then the page is valid physical RAM. +// +RTL_BITMAP MiPfnBitMap; + // // This structure describes the different pieces of RAM-backed address space // @@ -596,6 +610,9 @@ MmArmInitSystem(IN ULONG Phase, ULONG OldCount; BOOLEAN IncludeType[LoaderMaximum]; ULONG i; + PVOID Bitmap; + PPHYSICAL_MEMORY_RUN Run; + PFN_NUMBER FreePage, FreePageCount, PagesLeft, BasePage, PageCount; BoundaryAddressMultiple.QuadPart = 0; if (Phase == 0) @@ -1108,9 +1125,138 @@ MmArmInitSystem(IN ULONG Phase, // Now go ahead and initialize the ARM³ nonpaged pool // MiInitializeArmPool(); + + // + // Get current page data, since we won't be using MxGetNextPage as it + // would corrupt our state + // + FreePage = MxFreeDescriptor->BasePage; + FreePageCount = MxFreeDescriptor->PageCount; + PagesLeft = 0; + + // + // Loop the memory descriptors + // + NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink; + while (NextEntry != &KeLoaderBlock->MemoryDescriptorListHead) + { + // + // Get the descriptor + // + MdBlock = CONTAINING_RECORD(NextEntry, + MEMORY_ALLOCATION_DESCRIPTOR, + ListEntry); + if ((MdBlock->MemoryType == LoaderFirmwarePermanent) || + (MdBlock->MemoryType == LoaderBBTMemory) || + (MdBlock->MemoryType == LoaderSpecialMemory)) + { + // + // These pages are not part of the PFN database + // + NextEntry = MdBlock->ListEntry.Flink; + continue; + } + + // + // Next, check if this is our special free descriptor we've found + // + if (MdBlock == MxFreeDescriptor) + { + // + // Use the real numbers instead + // + BasePage = MxOldFreeDescriptor.BasePage; + PageCount = MxOldFreeDescriptor.PageCount; + } + else + { + // + // Use the descriptor's numbers + // + BasePage = MdBlock->BasePage; + PageCount = MdBlock->PageCount; + } + + // + // Get the PTEs for this range + // + PointerPte = MiAddressToPte(&MmPfnDatabase[BasePage]); + LastPte = MiAddressToPte(((ULONG_PTR)&MmPfnDatabase[BasePage + PageCount]) - 1); + DPRINT("MD Type: %lx Base: %lx Count: %lx\n", MdBlock->MemoryType, BasePage, PageCount); + + // + // Loop them + // + while (PointerPte <= LastPte) + { + // + // We'll only touch PTEs that aren't already valid + // + if (PointerPte->u.Hard.Valid == 0) + { + // + // Use the next free page + // + TempPte.u.Hard.PageFrameNumber = FreePage; + ASSERT(FreePageCount != 0); + + // + // Consume free pages + // + FreePage++; + FreePageCount--; + if (!FreePageCount) + { + // + // Out of memory + // + KeBugCheckEx(INSTALL_MORE_MEMORY, + MmNumberOfPhysicalPages, + FreePageCount, + MxOldFreeDescriptor.PageCount, + 1); + } + + // + // Write out this PTE + // + PagesLeft++; + ASSERT(PointerPte->u.Hard.Valid == 0); + ASSERT(TempPte.u.Hard.Valid == 1); + *PointerPte = TempPte; + + // + // Zero this page + // + RtlZeroMemory(MiPteToAddress(PointerPte), PAGE_SIZE); + } + + // + // Next! + // + PointerPte++; + } + + // + // Do the next address range + // + NextEntry = MdBlock->ListEntry.Flink; + } + + // + // Now update the free descriptors to consume the pages we used up during + // the PFN allocation loop + // + MxFreeDescriptor->BasePage = FreePage; + MxFreeDescriptor->PageCount = FreePageCount; } else if (Phase == 1) // IN BETWEEN, THE PFN DATABASE IS NOW CREATED - { + { + // + // Reset the descriptor back so we can create the correct memory blocks + // + *MxFreeDescriptor = MxOldFreeDescriptor; + // // Initialize the nonpaged pool // @@ -1197,16 +1343,57 @@ MmArmInitSystem(IN ULONG Phase, // MmPhysicalMemoryBlock = MmInitializeMemoryLimits(LoaderBlock, IncludeType); + + // + // Allocate enough buffer for the PFN bitmap + // Align it up to a 32-bit boundary + // + Bitmap = ExAllocatePoolWithTag(NonPagedPool, + (((MmHighestPhysicalPage + 1) + 31) / 32) * 4, + ' mM'); + if (!Bitmap) + { + // + // This is critical + // + KeBugCheckEx(INSTALL_MORE_MEMORY, + MmNumberOfPhysicalPages, + MmLowestPhysicalPage, + MmHighestPhysicalPage, + 0x101); + } + + // + // Initialize it and clear all the bits to begin with + // + RtlInitializeBitMap(&MiPfnBitMap, + Bitmap, + MmHighestPhysicalPage + 1); + RtlClearAllBits(&MiPfnBitMap); + + // + // Loop physical memory runs + // for (i = 0; i < MmPhysicalMemoryBlock->NumberOfRuns; i++) { // - // Dump it for debugging + // Get the run // - PPHYSICAL_MEMORY_RUN Run; Run = &MmPhysicalMemoryBlock->Run[i]; DPRINT("PHYSICAL RAM [0x%08p to 0x%08p]\n", Run->BasePage << PAGE_SHIFT, (Run->BasePage + Run->PageCount) << PAGE_SHIFT); + + // + // Make sure it has pages inside it + // + if (Run->PageCount) + { + // + // Set the bits in the PFN bitmap + // + RtlSetBits(&MiPfnBitMap, Run->BasePage, Run->PageCount); + } } // diff --git a/reactos/ntoskrnl/mm/ARM3/miarm.h b/reactos/ntoskrnl/mm/ARM3/miarm.h index 4bb93fdb2d1..a9506daa88a 100644 --- a/reactos/ntoskrnl/mm/ARM3/miarm.h +++ b/reactos/ntoskrnl/mm/ARM3/miarm.h @@ -133,6 +133,7 @@ extern PMEMORY_ALLOCATION_DESCRIPTOR MxFreeDescriptor; extern MEMORY_ALLOCATION_DESCRIPTOR MxOldFreeDescriptor; extern ULONG MxPfnAllocation; extern MM_PAGED_POOL_INFO MmPagedPoolInfo; +extern RTL_BITMAP MiPfnBitMap; VOID NTAPI diff --git a/reactos/ntoskrnl/mm/freelist.c b/reactos/ntoskrnl/mm/freelist.c index dc518b98aad..b3d4605886e 100644 --- a/reactos/ntoskrnl/mm/freelist.c +++ b/reactos/ntoskrnl/mm/freelist.c @@ -25,7 +25,6 @@ #define MM_PHYSICAL_PAGE_FREE (0x1) #define MM_PHYSICAL_PAGE_USED (0x2) -#define MM_PHYSICAL_PAGE_BIOS (0x3) /* GLOBALS ****************************************************************/ @@ -98,6 +97,7 @@ MmInsertLRULastUserPage(PFN_TYPE Pfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); + ASSERT(Page); ASSERT(Page->Flags.Type == MM_PHYSICAL_PAGE_USED); ASSERT(Page->Flags.Consumer == MC_USER); InsertTailList(&UserPageListHead, &Page->ListEntry); @@ -115,6 +115,7 @@ MmGetLRUNextUserPage(PFN_TYPE PreviousPfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(PreviousPfn); + ASSERT(Page); ASSERT(Page->Flags.Type == MM_PHYSICAL_PAGE_USED); ASSERT(Page->Flags.Consumer == MC_USER); NextListEntry = (PLIST_ENTRY)Page->ListEntry.Flink; @@ -495,6 +496,7 @@ MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress, // Get the PFN entry for this page // Pfn1 = MiGetPfnEntry(Page); + ASSERT(Pfn1); // // Make sure it's free and if this is our first pass, zeroed @@ -591,6 +593,7 @@ MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress, // Get the PFN entry for the page and check if we should zero it out // Pfn1 = MiGetPfnEntry(Page); + ASSERT(Pfn1); if (Pfn1->Flags.Zero == 0) MiZeroPage(Page); } @@ -610,7 +613,7 @@ MmDumpPfnDatabase(VOID) PPHYSICAL_PAGE Pfn1; PCHAR State = "????", Consumer = "Unknown"; KIRQL OldIrql; - ULONG Totals[5] = {0}, BiosPages = 0, FreePages = 0; + ULONG Totals[5] = {0}, FreePages = 0; KeRaiseIrql(HIGH_LEVEL, &OldIrql); @@ -620,6 +623,7 @@ MmDumpPfnDatabase(VOID) for (i = 0; i <= MmHighestPhysicalPage; i++) { Pfn1 = MiGetPfnEntry(i); + if (!Pfn1) continue; // // Get the consumer @@ -669,13 +673,6 @@ MmDumpPfnDatabase(VOID) Consumer = "Free"; FreePages++; break; - - case MM_PHYSICAL_PAGE_BIOS: - - State = "BIOS"; - Consumer = "System Reserved"; - BiosPages++; - break; } // @@ -696,64 +693,25 @@ MmDumpPfnDatabase(VOID) DbgPrint("File System Cache: %d pages\t[%d KB]\n", Totals[MC_CACHE], (Totals[MC_CACHE] << PAGE_SHIFT) / 1024); DbgPrint("Process Working Set: %d pages\t[%d KB]\n", Totals[MC_USER], (Totals[MC_USER] << PAGE_SHIFT) / 1024); DbgPrint("System: %d pages\t[%d KB]\n", Totals[MC_SYSTEM], (Totals[MC_SYSTEM] << PAGE_SHIFT) / 1024); - DbgPrint("BIOS: %d pages\t[%d KB]\n", BiosPages, (BiosPages << PAGE_SHIFT) / 1024); DbgPrint("Free: %d pages\t[%d KB]\n", FreePages, (FreePages << PAGE_SHIFT) / 1024); KeLowerIrql(OldIrql); } -PFN_NUMBER -NTAPI -MxGetNextPage(IN PFN_NUMBER PageCount); - VOID NTAPI MmInitializePageList(VOID) { ULONG i; - NTSTATUS Status; - PFN_TYPE Pfn = 0; PHYSICAL_PAGE UsedPage; - PLIST_ENTRY NextEntry; PMEMORY_ALLOCATION_DESCRIPTOR Md; + PLIST_ENTRY NextEntry; /* Initialize the page lists */ InitializeListHead(&UserPageListHead); InitializeListHead(&FreeUnzeroedPageListHead); InitializeListHead(&FreeZeroedPageListHead); - /* Loop every page required to hold the PFN database */ - for (i = 0; i < MxPfnAllocation; i++) - { - PVOID Address = (char*)MmPfnDatabase + (i * PAGE_SIZE); - - /* Check if FreeLDR has already allocated it for us */ - if (!MmIsPagePresent(NULL, Address)) - { - /* Use one of our highest usable pages */ - Pfn = MxGetNextPage(1); - - /* Set the PFN */ - Status = MmCreateVirtualMappingForKernel(Address, - PAGE_READWRITE, - &Pfn, - 1); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Unable to create virtual mapping\n"); - KeBugCheck(MEMORY_MANAGEMENT); - } - } - else - { - /* Setting the page protection is necessary to set the global bit */ - MmSetPageProtect(NULL, Address, PAGE_READWRITE); - } - } - - /* Clear the PFN database */ - RtlZeroMemory(MmPfnDatabase, (MmHighestPhysicalPage + 1) * sizeof(PHYSICAL_PAGE)); - /* This is what a used page looks like */ RtlZeroMemory(&UsedPage, sizeof(UsedPage)); UsedPage.Flags.Type = MM_PHYSICAL_PAGE_USED; @@ -779,17 +737,10 @@ MmInitializePageList(VOID) (Md->MemoryType == LoaderSpecialMemory) || (Md->MemoryType == LoaderBad)) { - /* Loop every page part of the block but valid in the database */ - for (i = 0; i < Md->PageCount; i++) - { - /* Skip memory we ignore completely */ - if ((Md->BasePage + i) > MmHighestPhysicalPage) break; - - /* These are pages reserved by the BIOS/ROMs */ - MmPfnDatabase[Md->BasePage + i].Flags.Type = MM_PHYSICAL_PAGE_BIOS; - MmPfnDatabase[Md->BasePage + i].Flags.Consumer = MC_NPPOOL; - MmStats.NrSystemPages++; - } + // + // We do not build PFN entries for this + // + continue; } else if ((Md->MemoryType == LoaderFree) || (Md->MemoryType == LoaderLoadedProgram) || @@ -872,13 +823,16 @@ MmMarkPageMapped(PFN_TYPE Pfn) { oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); - if (Page->Flags.Type == MM_PHYSICAL_PAGE_FREE) + if (Page) { - DPRINT1("Mapping non-used page\n"); - KeBugCheck(MEMORY_MANAGEMENT); + if (Page->Flags.Type == MM_PHYSICAL_PAGE_FREE) + { + DPRINT1("Mapping non-used page\n"); + KeBugCheck(MEMORY_MANAGEMENT); + } + Page->MapCount++; + Page->ReferenceCount++; } - Page->MapCount++; - Page->ReferenceCount++; KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); } } @@ -894,18 +848,21 @@ MmMarkPageUnmapped(PFN_TYPE Pfn) { oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); - if (Page->Flags.Type == MM_PHYSICAL_PAGE_FREE) + if (Page) { - DPRINT1("Unmapping non-used page\n"); - KeBugCheck(MEMORY_MANAGEMENT); + if (Page->Flags.Type == MM_PHYSICAL_PAGE_FREE) + { + DPRINT1("Unmapping non-used page\n"); + KeBugCheck(MEMORY_MANAGEMENT); + } + if (Page->MapCount == 0) + { + DPRINT1("Unmapping not mapped page\n"); + KeBugCheck(MEMORY_MANAGEMENT); + } + Page->MapCount--; + Page->ReferenceCount--; } - if (Page->MapCount == 0) - { - DPRINT1("Unmapping not mapped page\n"); - KeBugCheck(MEMORY_MANAGEMENT); - } - Page->MapCount--; - Page->ReferenceCount--; KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); } } @@ -952,6 +909,7 @@ MmReferencePageUnsafe(PFN_TYPE Pfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); + ASSERT(Page); if (Page->Flags.Type != MM_PHYSICAL_PAGE_USED) { DPRINT1("Referencing non-used page\n"); @@ -983,6 +941,7 @@ MmGetReferenceCountPage(PFN_TYPE Pfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); + ASSERT(Page); if (Page->Flags.Type != MM_PHYSICAL_PAGE_USED) { DPRINT1("Getting reference count for free page\n"); @@ -1017,6 +976,7 @@ MmDereferencePage(PFN_TYPE Pfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); + ASSERT(Page); if (Page->Flags.Type != MM_PHYSICAL_PAGE_USED) { @@ -1088,6 +1048,7 @@ MmGetLockCountPage(PFN_TYPE Pfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); + ASSERT(Page); if (Page->Flags.Type != MM_PHYSICAL_PAGE_USED) { DPRINT1("Getting lock count for free page\n"); @@ -1112,6 +1073,7 @@ MmLockPageUnsafe(PFN_TYPE Pfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); + ASSERT(Page); if (Page->Flags.Type != MM_PHYSICAL_PAGE_USED) { DPRINT1("Locking free page\n"); @@ -1143,6 +1105,7 @@ MmUnlockPage(PFN_TYPE Pfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); + ASSERT(Page); if (Page->Flags.Type != MM_PHYSICAL_PAGE_USED) { DPRINT1("Unlocking free page\n"); @@ -1173,7 +1136,7 @@ MmAllocPage(ULONG Consumer, SWAPENTRY SwapEntry) /* Check if this allocation is for the PFN DB itself */ if (MmStats.NrTotalPages == 0) { - MxGetNextPage(1); + ASSERT(FALSE); } DPRINT1("MmAllocPage(): Out of memory\n");