Sync up to trunk head.
[reactos.git] / ntoskrnl / mm / ARM3 / contmem.c
index 890bd74..0d7628b 100644 (file)
 
 /* 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,
@@ -143,7 +322,10 @@ MiFindContiguousMemory(IN PFN_NUMBER LowestPfn,
 {
     PFN_NUMBER Page;
     PHYSICAL_ADDRESS PhysicalAddress;
-    PAGED_CODE ();
+    PMMPFN Pfn1, EndPfn;
+    PMMPTE PointerPte;
+    PVOID BaseAddress;
+    PAGED_CODE();
     ASSERT(SizeInPages != 0);
 
     //
@@ -160,7 +342,22 @@ MiFindContiguousMemory(IN PFN_NUMBER LowestPfn,
     // 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
@@ -249,6 +446,7 @@ MiFreeContiguousMemory(IN PVOID BaseAddress)
     KIRQL OldIrql;
     PFN_NUMBER PageFrameIndex, LastPage, PageCount;
     PMMPFN Pfn1, StartPfn;
+    PMMPTE PointerPte;
     PAGED_CODE();
     
     //
@@ -267,10 +465,9 @@ MiFreeContiguousMemory(IN PVOID BaseAddress)
         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
@@ -281,11 +478,11 @@ MiFreeContiguousMemory(IN PVOID BaseAddress)
         //
         // 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);
     }
     
     //
@@ -294,16 +491,21 @@ MiFreeContiguousMemory(IN PVOID BaseAddress)
     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
     //
@@ -328,14 +530,14 @@ MiFreeContiguousMemory(IN PVOID BaseAddress)
     //
     // 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