/* PRIVATE FUNCTIONS **********************************************************/
+PFN_NUMBER
+NTAPI
+MiFindContiguousPages(IN PFN_NUMBER LowestPfn,
+ IN PFN_NUMBER HighestPfn,
+ IN PFN_NUMBER BoundaryPfn,
+ IN PFN_NUMBER SizeInPages,
+ IN MEMORY_CACHING_TYPE CacheType)
+{
+ PFN_NUMBER Page, PageCount, LastPage, Length, BoundaryMask;
+ ULONG i = 0;
+ PMMPFN Pfn1, EndPfn;
+ KIRQL OldIrql;
+ PAGED_CODE ();
+ ASSERT(SizeInPages != 0);
+
+ //
+ // Convert the boundary PFN into an alignment mask
+ //
+ BoundaryMask = ~(BoundaryPfn - 1);
+
+ //
+ // Loop all the physical memory blocks
+ //
+ do
+ {
+ //
+ // Capture the base page and length of this memory block
+ //
+ Page = MmPhysicalMemoryBlock->Run[i].BasePage;
+ PageCount = MmPhysicalMemoryBlock->Run[i].PageCount;
+
+ //
+ // Check how far this memory block will go
+ //
+ LastPage = Page + PageCount;
+
+ //
+ // Trim it down to only the PFNs we're actually interested in
+ //
+ if ((LastPage - 1) > HighestPfn) LastPage = HighestPfn + 1;
+ if (Page < LowestPfn) Page = LowestPfn;
+
+ //
+ // Skip this run if it's empty or fails to contain all the pages we need
+ //
+ if (!(PageCount) || ((Page + SizeInPages) > LastPage)) continue;
+
+ //
+ // Now scan all the relevant PFNs in this run
+ //
+ Length = 0;
+ for (Pfn1 = MiGetPfnEntry(Page); Page < LastPage; Page++, Pfn1++)
+ {
+ //
+ // If this PFN is in use, ignore it
+ //
+ if (MiIsPfnInUse(Pfn1)) continue;
+
+ //
+ // If we haven't chosen a start PFN yet and the caller specified an
+ // alignment, make sure the page matches the alignment restriction
+ //
+ if ((!(Length) && (BoundaryPfn)) &&
+ (((Page ^ (Page + SizeInPages - 1)) & BoundaryMask)))
+ {
+ //
+ // It does not, so bail out
+ //
+ continue;
+ }
+
+ //
+ // Increase the number of valid pages, and check if we have enough
+ //
+ if (++Length == SizeInPages)
+ {
+ //
+ // It appears we've amassed enough legitimate pages, rollback
+ //
+ Pfn1 -= (Length - 1);
+ Page -= (Length - 1);
+
+ //
+ // Acquire the PFN lock
+ //
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ do
+ {
+ //
+ // Things might've changed for us. Is the page still free?
+ //
+ if (MiIsPfnInUse(Pfn1)) break;
+
+ //
+ // So far so good. Is this the last confirmed valid page?
+ //
+ if (!--Length)
+ {
+ //
+ // Sanity check that we didn't go out of bounds
+ //
+ ASSERT(i != MmPhysicalMemoryBlock->NumberOfRuns);
+
+ //
+ // Loop until all PFN entries have been processed
+ //
+ EndPfn = Pfn1 - SizeInPages + 1;
+ do
+ {
+ //
+ // This PFN is now a used page, set it up
+ //
+ MiUnlinkFreeOrZeroedPage(Pfn1);
+ Pfn1->u3.e2.ReferenceCount = 1;
+ Pfn1->u2.ShareCount = 1;
+ Pfn1->u3.e1.PageLocation = ActiveAndValid;
+ Pfn1->u3.e1.StartOfAllocation = 0;
+ Pfn1->u3.e1.EndOfAllocation = 0;
+ Pfn1->u3.e1.PrototypePte = 0;
+ Pfn1->u4.VerifierAllocation = 0;
+ Pfn1->PteAddress = (PVOID)0xBAADF00D;
+
+ //
+ // Check if this is the last PFN, otherwise go on
+ //
+ if (Pfn1 == EndPfn) break;
+ Pfn1--;
+ } while (TRUE);
+
+ //
+ // Mark the first and last PFN so we can find them later
+ //
+ Pfn1->u3.e1.StartOfAllocation = 1;
+ (Pfn1 + SizeInPages - 1)->u3.e1.EndOfAllocation = 1;
+
+ //
+ // Now it's safe to let go of the PFN lock
+ //
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+ //
+ // Quick sanity check that the last PFN is consistent
+ //
+ EndPfn = Pfn1 + SizeInPages;
+ ASSERT(EndPfn == MiGetPfnEntry(Page + 1));
+
+ //
+ // Compute the first page, and make sure it's consistent
+ //
+ Page -= SizeInPages - 1;
+ ASSERT(Pfn1 == MiGetPfnEntry(Page));
+ ASSERT(Page != 0);
+ return Page;
+ }
+
+ //
+ // Keep going. The purpose of this loop is to reconfirm that
+ // after acquiring the PFN lock these pages are still usable
+ //
+ Pfn1++;
+ Page++;
+ } while (TRUE);
+
+ //
+ // If we got here, something changed while we hadn't acquired
+ // the PFN lock yet, so we'll have to restart
+ //
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ Length = 0;
+ }
+ }
+ } while (++i != MmPhysicalMemoryBlock->NumberOfRuns);
+
+ //
+ // And if we get here, it means no suitable physical memory runs were found
+ //
+ return 0;
+}
+
PVOID
NTAPI
MiCheckForContiguousMemory(IN PVOID BaseAddress,
{
PFN_NUMBER Page;
PHYSICAL_ADDRESS PhysicalAddress;
- PAGED_CODE ();
+ PMMPFN Pfn1, EndPfn;
+ PMMPTE PointerPte;
+ PVOID BaseAddress;
+ PAGED_CODE();
ASSERT(SizeInPages != 0);
//
// We'll just piggyback on the I/O memory mapper
//
PhysicalAddress.QuadPart = Page << PAGE_SHIFT;
- return MmMapIoSpace(PhysicalAddress, SizeInPages << PAGE_SHIFT, CacheType);
+ BaseAddress = MmMapIoSpace(PhysicalAddress, SizeInPages << PAGE_SHIFT, CacheType);
+ ASSERT(BaseAddress);
+
+ /* Loop the PFN entries */
+ Pfn1 = MiGetPfnEntry(Page);
+ EndPfn = Pfn1 + SizeInPages;
+ PointerPte = MiAddressToPte(BaseAddress);
+ do
+ {
+ /* Write the PTE address */
+ Pfn1->PteAddress = PointerPte++;
+ Pfn1->u4.PteFrame = PFN_FROM_PTE(MiAddressToPte(PointerPte));
+ } while (Pfn1++ < EndPfn);
+
+ /* Return the address */
+ return BaseAddress;
}
PVOID
KIRQL OldIrql;
PFN_NUMBER PageFrameIndex, LastPage, PageCount;
PMMPFN Pfn1, StartPfn;
+ PMMPTE PointerPte;
PAGED_CODE();
//
return;
}
- //
- // Otherwise, get the PTE and page number for the allocation
- //
- PageFrameIndex = PFN_FROM_PTE(MiAddressToPte(BaseAddress));
+ /* Get the PTE and frame number for the allocation*/
+ PointerPte = MiAddressToPte(BaseAddress);
+ PageFrameIndex = PFN_FROM_PTE(PointerPte);
//
// Now get the PFN entry for this, and make sure it's the correct one
//
// This probably means you did a free on an address that was in between
//
- KeBugCheckEx (BAD_POOL_CALLER,
- 0x60,
- (ULONG_PTR)BaseAddress,
- 0,
- 0);
+ KeBugCheckEx(BAD_POOL_CALLER,
+ 0x60,
+ (ULONG_PTR)BaseAddress,
+ 0,
+ 0);
}
//
StartPfn = Pfn1;
Pfn1->u3.e1.StartOfAllocation = 0;
- //
- // Look the PFNs
- //
+ /* Loop the PFNs until we find the one that marks the end of the allocation */
do
{
- //
- // Until we find the one that marks the end of the allocation
- //
+ /* Make sure these are the pages we setup in the allocation routine */
+ ASSERT(Pfn1->u3.e2.ReferenceCount == 1);
+ ASSERT(Pfn1->u2.ShareCount == 1);
+ ASSERT(Pfn1->PteAddress == PointerPte);
+ ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
+ ASSERT(Pfn1->u4.VerifierAllocation == 0);
+ ASSERT(Pfn1->u3.e1.PrototypePte == 0);
+
+ /* Keep going for assertions */
+ PointerPte++;
} while (Pfn1++->u3.e1.EndOfAllocation == 0);
-
+
//
// Found it, unmark it
//
//
// Loop all the pages
//
- LastPage = PageFrameIndex + PageCount;
+ LastPage = PageFrameIndex + PageCount;
do
{
//
// Free each one, and move on
//
- MmReleasePageMemoryConsumer(MC_NPPOOL, PageFrameIndex);
- } while (++PageFrameIndex < LastPage);
+ MmReleasePageMemoryConsumer(MC_NPPOOL, PageFrameIndex++);
+ } while (PageFrameIndex < LastPage);
//
// Release the PFN lock