[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / pfnlist.c
index 5765d92..29777f6 100644 (file)
 #define MODULE_INVOLVED_IN_ARM3
 #include "../ARM3/miarm.h"
 
+#define ASSERT_LIST_INVARIANT(x) \
+do { \
+       ASSERT(((x)->Total == 0 && \
+            (x)->Flink == LIST_HEAD && \
+                       (x)->Blink == LIST_HEAD) || \
+                  ((x)->Total != 0 && \
+                       (x)->Flink != LIST_HEAD && \
+                       (x)->Blink != LIST_HEAD)); \
+} while (0)
+
 /* GLOBALS ********************************************************************/
 
-MMPFNLIST MmZeroedPageListHead;
-MMPFNLIST MmFreePageListHead;
-MMPFNLIST MmStandbyPageListHead;
-MMPFNLIST MmModifiedPageListHead;
-MMPFNLIST MmModifiedNoWritePageListHead;
+BOOLEAN MmDynamicPfn;
+BOOLEAN MmMirroring;
+
+MMPFNLIST MmZeroedPageListHead = {0, ZeroedPageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmFreePageListHead = {0, FreePageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmStandbyPageListHead = {0, StandbyPageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmModifiedPageListHead = {0, ModifiedPageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmModifiedNoWritePageListHead = {0, ModifiedNoWritePageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmBadPageListHead = {0, BadPageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmRomPageListHead = {0, StandbyPageList, LIST_HEAD, LIST_HEAD};
 
+PMMPFNLIST MmPageLocationList[] =
+{
+    &MmZeroedPageListHead,
+    &MmFreePageListHead,
+    &MmStandbyPageListHead,
+    &MmModifiedPageListHead,
+    &MmModifiedNoWritePageListHead,
+    &MmBadPageListHead,
+    NULL,
+    NULL
+};
 /* FUNCTIONS ******************************************************************/
 
 VOID
@@ -33,6 +59,9 @@ MiInsertInListTail(IN PMMPFNLIST ListHead,
 {
     PFN_NUMBER OldBlink, EntryIndex = MiGetPfnEntryIndex(Entry);
 
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+    ASSERT_LIST_INVARIANT(ListHead);
+
     /* Get the back link */
     OldBlink = ListHead->Blink;
     if (OldBlink != LIST_HEAD)
@@ -53,30 +82,135 @@ MiInsertInListTail(IN PMMPFNLIST ListHead,
     /* And now the head points back to us, since we are last */
     ListHead->Blink = EntryIndex;
     ListHead->Total++;
+       ASSERT_LIST_INVARIANT(ListHead);
 }
 
 VOID
 NTAPI
-MiRemoveFromList(IN PMMPFN Entry)
+MiInsertZeroListAtBack(IN PFN_NUMBER EntryIndex)
 {
-    PFN_NUMBER OldFlink, OldBlink;
+    PFN_NUMBER OldBlink;
     PMMPFNLIST ListHead;
+    PMMPFN Pfn1;
+#if 0
+    PMMPFN Blink;
+    ULONG Color;
+    PMMCOLOR_TABLES ColorHead;
+#endif
+
+    /* Make sure the PFN lock is held */
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
     
-    /* Find the list for this */
-    if (Entry->u3.e1.PageLocation == ZeroedPageList)
+    /* Get the descriptor */
+    Pfn1 = MiGetPfnEntry(EntryIndex);
+    ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
+    ASSERT(Pfn1->u4.MustBeCached == 0);
+    ASSERT(Pfn1->u3.e1.Rom == 0);
+    ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
+    ASSERT(Pfn1->u4.InPageError == 0);
+    
+    /* Use the zero list */
+    ListHead = &MmZeroedPageListHead;
+    ASSERT_LIST_INVARIANT(ListHead);
+    ListHead->Total++;
+
+    /* Get the back link */
+    OldBlink = ListHead->Blink;
+    if (OldBlink != LIST_HEAD)
     {
-        ListHead = &MmZeroedPageListHead;
+        /* Set the back pointer to point to us now */
+        MiGetPfnEntry(OldBlink)->u1.Flink = EntryIndex;
     }
-    else if (Entry->u3.e1.PageLocation == FreePageList)
+    else
     {
-        ListHead = &MmFreePageListHead;
+        /* Set the list to point to us */
+        ListHead->Flink = EntryIndex;
+    }
+    
+    /* Set the entry to point to the list head forwards, and the old page backwards */
+    Pfn1->u1.Flink = LIST_HEAD;
+    Pfn1->u2.Blink = OldBlink;
+    
+    /* And now the head points back to us, since we are last */
+    ListHead->Blink = EntryIndex;
+    
+    /* Update the page location */
+    Pfn1->u3.e1.PageLocation = ZeroedPageList;
+
+    /* FIXME: NOT YET Due to caller semantics: Update the available page count */
+    //MmAvailablePages++;
+
+    /* Check if we've reached the configured low memory threshold */
+    if (MmAvailablePages == MmLowMemoryThreshold)
+    {
+        /* Clear the event, because now we're ABOVE the threshold */
+        KeClearEvent(MiLowMemoryEvent);
+    }
+    else if (MmAvailablePages == MmHighMemoryThreshold)
+    {
+        /* Otherwise check if we reached the high threshold and signal the event */
+        KeSetEvent(MiHighMemoryEvent, 0, FALSE);
+    }
+
+       ASSERT_LIST_INVARIANT(ListHead);
+
+#if 0
+    /* Get the page color */
+    Color = EntryIndex & MmSecondaryColorMask;
+
+    /* Get the first page on the color list */
+    ColorHead = &MmFreePagesByColor[ZeroedPageList][Color];
+    if (ColorHead->Flink == LIST_HEAD)
+    {
+        /* The list is empty, so we are the first page */
+        Pfn1->u4.PteFrame = -1;
+        ColorHead->Flink = EntryIndex;
     }
     else
     {
-        ListHead = NULL;
-        ASSERT(ListHead != NULL);
+        /* Get the previous page */
+        Blink = (PMMPFN)ColorHead->Blink;
+        
+        /* Make it link to us */
+        Pfn1->u4.PteFrame = MiGetPfnEntryIndex(Blink);
+        Blink->OriginalPte.u.Long = EntryIndex;
     }
     
+    /* Now initialize our own list pointers */
+    ColorHead->Blink = Pfn1;
+    Pfn1->OriginalPte.u.Long = LIST_HEAD;
+    
+    /* And increase the count in the colored list */
+    ColorHead->Count++;
+#endif
+}
+
+VOID
+NTAPI
+MiUnlinkFreeOrZeroedPage(IN PMMPFN Entry)
+{
+    PFN_NUMBER OldFlink, OldBlink;
+    PMMPFNLIST ListHead;
+    MMLISTS ListName;
+    
+    /* Make sure the PFN lock is held */
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+    /* Make sure the PFN entry isn't in-use */
+    ASSERT(Entry->u3.e1.WriteInProgress == 0);
+    ASSERT(Entry->u3.e1.ReadInProgress == 0);
+    
+    /* Find the list for this entry, make sure it's the free or zero list */
+    ListHead = MmPageLocationList[Entry->u3.e1.PageLocation];
+    ListName = ListHead->ListName;
+    ASSERT(ListHead != NULL);
+    ASSERT(ListName <= FreePageList);
+    ASSERT_LIST_INVARIANT(ListHead);
+
+    /* Remove one count */
+    ASSERT(ListHead->Total != 0);
+    ListHead->Total--;
+    
     /* Get the forward and back pointers */
     OldFlink = Entry->u1.Flink;
     OldBlink = Entry->u2.Blink;
@@ -90,7 +224,7 @@ MiRemoveFromList(IN PMMPFN Entry)
     else
     {
         /* Set the list head's backlink instead */
-        ListHead->Blink = OldFlink;
+        ListHead->Blink = OldBlink;
     }
     
     /* Check if the back entry is the list head */
@@ -106,8 +240,205 @@ MiRemoveFromList(IN PMMPFN Entry)
     }
     
     /* We are not on a list anymore */
-    ListHead->Total--;
     Entry->u1.Flink = Entry->u2.Blink = 0;
+    ASSERT_LIST_INVARIANT(ListHead);
+
+    /* FIXME: Deal with color list */
+    
+    /* See if we hit any thresholds */
+    if (MmAvailablePages == MmHighMemoryThreshold)
+    {
+        /* Clear the high memory event */
+        KeClearEvent(MiHighMemoryEvent);
+    }
+    else if (MmAvailablePages == MmLowMemoryThreshold)
+    {
+        /* Signal the low memory event */
+        KeSetEvent(MiLowMemoryEvent, 0, FALSE);
+    }
+    
+    /* One less page */
+    if (--MmAvailablePages < MmMinimumFreePages)
+    {
+        /* FIXME: Should wake up the MPW and working set manager, if we had one */
+    }
+}
+
+PFN_NUMBER
+NTAPI
+MiRemovePageByColor(IN PFN_NUMBER PageIndex,
+                    IN ULONG Color)
+{
+    PMMPFN Pfn1;
+    PMMPFNLIST ListHead;
+    MMLISTS ListName;
+    PFN_NUMBER OldFlink, OldBlink;
+    ULONG OldColor, OldCache;
+#if 0
+    PMMCOLOR_TABLES ColorTable;
+#endif   
+    /* Make sure PFN lock is held */
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+    ASSERT(Color < MmSecondaryColors);
+
+    /* Get the PFN entry */
+    Pfn1 = MiGetPfnEntry(PageIndex);
+    ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
+    ASSERT(Pfn1->u3.e1.Rom == 0);
+    
+    /* Capture data for later */
+    OldColor = Pfn1->u3.e1.PageColor;
+    OldCache = Pfn1->u3.e1.CacheAttribute;
+
+    /* Could be either on free or zero list */
+    ListHead = MmPageLocationList[Pfn1->u3.e1.PageLocation];
+    ASSERT_LIST_INVARIANT(ListHead);
+    ListName = ListHead->ListName;
+    ASSERT(ListName <= FreePageList);
+    
+    /* Remove a page */
+    ListHead->Total--;
+
+    /* Get the forward and back pointers */
+    OldFlink = Pfn1->u1.Flink;
+    OldBlink = Pfn1->u2.Blink;
+    
+    /* Check if the next entry is the list head */
+    if (OldFlink != LIST_HEAD)
+    {
+        /* It is not, so set the backlink of the actual entry, to our backlink */
+        MiGetPfnEntry(OldFlink)->u2.Blink = OldBlink;
+    }
+    else
+    {
+        /* Set the list head's backlink instead */
+        ListHead->Blink = OldFlink;
+    }
+    
+    /* Check if the back entry is the list head */
+    if (OldBlink != LIST_HEAD)
+    {
+        /* It is not, so set the backlink of the actual entry, to our backlink */
+        MiGetPfnEntry(OldBlink)->u1.Flink = OldFlink;
+    }
+    else
+    {
+        /* Set the list head's backlink instead */
+        ListHead->Flink = OldFlink;
+    }
+    
+    /* We are not on a list anymore */
+    Pfn1->u1.Flink = Pfn1->u2.Blink = 0;
+    
+    /* Zero flags but restore color and cache */
+    Pfn1->u3.e2.ShortFlags = 0;
+    Pfn1->u3.e1.PageColor = OldColor;
+    Pfn1->u3.e1.CacheAttribute = OldCache;
+
+       ASSERT_LIST_INVARIANT(ListHead);
+
+#if 0 // When switching to ARM3
+    /* Get the first page on the color list */
+    ColorTable = &MmFreePagesByColor[ListName][Color];
+    ASSERT(ColorTable->Count >= 1);
+    
+    /* Set the forward link to whoever we were pointing to */
+    ColorTable->Flink = Pfn1->OriginalPte.u.Long;
+    if (ColorTable->Flink == LIST_HEAD)
+    {
+        /* This is the beginning of the list, so set the sentinel value */
+        ColorTable->Blink = LIST_HEAD;    
+    }
+    else
+    {
+        /* The list is empty, so we are the first page */
+        MiGetPfnEntry(ColorTable->Flink)->u4.PteFrame = -1;
+    }
+    
+    /* One more page */
+    ColorTable->Total++;
+#endif
+    /* See if we hit any thresholds */
+    if (MmAvailablePages == MmHighMemoryThreshold)
+    {
+        /* Clear the high memory event */
+        KeClearEvent(MiHighMemoryEvent);
+    }
+    else if (MmAvailablePages == MmLowMemoryThreshold)
+    {
+        /* Signal the low memory event */
+        KeSetEvent(MiLowMemoryEvent, 0, FALSE);
+    }
+    
+    /* One less page */
+    if (--MmAvailablePages < MmMinimumFreePages)
+    {
+        /* FIXME: Should wake up the MPW and working set manager, if we had one */
+    }
+
+    /* Return the page */
+    return PageIndex;
+}
+
+PFN_NUMBER
+NTAPI
+MiRemoveAnyPage(IN ULONG Color)
+{
+    PFN_NUMBER PageIndex;
+    PMMPFN Pfn1;
+
+    /* Make sure PFN lock is held and we have pages */
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+    ASSERT(MmAvailablePages != 0);
+    ASSERT(Color < MmSecondaryColors);
+    
+    /* Check the colored free list */
+#if 0 // Enable when using ARM3 database */
+    PageIndex = MmFreePagesByColor[FreePageList][Color].Flink;
+    if (PageIndex == LIST_HEAD)
+    {
+        /* Check the colored zero list */
+        PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink;
+        if (PageIndex == LIST_HEAD)
+        {
+#endif
+            /* Check the free list */
+            ASSERT_LIST_INVARIANT(&MmFreePageListHead);
+            PageIndex = MmFreePageListHead.Flink;
+            Color = PageIndex & MmSecondaryColorMask;
+            if (PageIndex == LIST_HEAD)
+            {
+                /* Check the zero list */
+                ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
+                PageIndex = MmZeroedPageListHead.Flink;
+                Color = PageIndex & MmSecondaryColorMask;
+                ASSERT(PageIndex != LIST_HEAD);
+                if (PageIndex == LIST_HEAD)
+                {
+                    /* FIXME: Should check the standby list */
+                    ASSERT(MmZeroedPageListHead.Total == 0);
+                }
+            }
+#if 0 // Enable when using ARM3 database */
+        }
+    }
+#endif
+
+    /* Remove the page from its list */
+    PageIndex = MiRemovePageByColor(PageIndex, Color);
+
+    /* Sanity checks */
+    Pfn1 = MiGetPfnEntry(PageIndex);
+    ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) ||
+           (Pfn1->u3.e1.PageLocation == ZeroedPageList));
+    ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
+    ASSERT(Pfn1->u2.ShareCount == 0);
+        
+    /* Return the page */
+    ASSERT_LIST_INVARIANT(&MmFreePageListHead);
+    ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
+
+    return PageIndex;
 }
 
 PMMPFN
@@ -117,6 +448,9 @@ MiRemoveHeadList(IN PMMPFNLIST ListHead)
     PFN_NUMBER Entry, Flink;
     PMMPFN Pfn1;
     
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+    ASSERT_LIST_INVARIANT(ListHead);
+
     /* Get the entry that's currently first on the list */
     Entry = ListHead->Flink;
     Pfn1 = MiGetPfnEntry(Entry);
@@ -141,6 +475,8 @@ MiRemoveHeadList(IN PMMPFNLIST ListHead)
     Pfn1->u1.Flink = Pfn1->u2.Blink = 0;
     ListHead->Total--;
     
+       ASSERT_LIST_INVARIANT(ListHead);
+
     /* Return the head element */
     return Pfn1;
 }
@@ -149,20 +485,22 @@ VOID
 NTAPI
 MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex)
 {
-    MMLISTS ListName;
     PMMPFNLIST ListHead;
     PFN_NUMBER LastPage;
-    PMMPFN Pfn1, Blink;
+    PMMPFN Pfn1;
+#if 0
     ULONG Color;
-    PMMCOLOR_TABLES ColorHead;
-
+    PMMPFN Blink;
+    PMMCOLOR_TABLES ColorTable;
+#endif
     /* Make sure the page index is valid */
+    ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
     ASSERT((PageFrameIndex != 0) &&
            (PageFrameIndex <= MmHighestPhysicalPage) &&
            (PageFrameIndex >= MmLowestPhysicalPage));
 
     /* Get the PFN entry */
-    Pfn1 = MI_PFN_TO_PFNENTRY(PageFrameIndex);
+    Pfn1 = MiGetPfnEntry(PageFrameIndex);
 
     /* Sanity checks that a right kind of page is being inserted here */
     ASSERT(Pfn1->u4.MustBeCached == 0);
@@ -173,15 +511,15 @@ MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex)
 
     /* Get the free page list and increment its count */
     ListHead = &MmFreePageListHead;
-    ListName = FreePageList;
+    ASSERT_LIST_INVARIANT(ListHead);
     ListHead->Total++;
 
     /* Get the last page on the list */
     LastPage = ListHead->Blink;
-    if (LastPage != -1)
+    if (LastPage != LIST_HEAD)
     {
         /* Link us with the previous page, so we're at the end now */
-        MI_PFN_TO_PFNENTRY(LastPage)->u1.Flink = PageFrameIndex;
+        MiGetPfnEntry(LastPage)->u1.Flink = PageFrameIndex;
     }
     else
     {
@@ -191,36 +529,52 @@ MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex)
 
     /* Now make the list head point back to us (since we go at the end) */
     ListHead->Blink = PageFrameIndex;
-    
+
     /* And initialize our own list pointers */
-    Pfn1->u1.Flink = -1;
+    Pfn1->u1.Flink = LIST_HEAD;
     Pfn1->u2.Blink = LastPage;
 
     /* Set the list name and default priority */
-    Pfn1->u3.e1.PageLocation = ListName;
+    Pfn1->u3.e1.PageLocation = FreePageList;
     Pfn1->u4.Priority = 3;
-    
+
     /* Clear some status fields */
     Pfn1->u4.InPageError = 0;
     Pfn1->u4.AweAllocation = 0;
 
-    /* FIXME: More work to be done regarding page accounting */
+    /* Increase available pages */
+    MmAvailablePages++;
+
+    /* Check if we've reached the configured low memory threshold */
+    if (MmAvailablePages == MmLowMemoryThreshold)
+    {
+        /* Clear the event, because now we're ABOVE the threshold */
+        KeClearEvent(MiLowMemoryEvent);
+    }
+    else if (MmAvailablePages == MmHighMemoryThreshold)
+    {
+        /* Otherwise check if we reached the high threshold and signal the event */
+        KeSetEvent(MiHighMemoryEvent, 0, FALSE);
+    }
+
+    ASSERT_LIST_INVARIANT(ListHead);
 
+#if 0 // When using ARM3 PFN
     /* Get the page color */
     Color = PageFrameIndex & MmSecondaryColorMask;
 
     /* Get the first page on the color list */
-    ColorHead = &MmFreePagesByColor[ListName][Color];
-    if (ColorHead->Flink == -1)
+    ColorTable = &MmFreePagesByColor[FreePageList][Color];
+    if (ColorTable->Flink == LIST_HEAD)
     {
         /* The list is empty, so we are the first page */
         Pfn1->u4.PteFrame = -1;
-        ColorHead->Flink = PageFrameIndex;
+        ColorTable->Flink = PageFrameIndex;
     }
     else
     {
         /* Get the previous page */
-        Blink = (PMMPFN)ColorHead->Blink;
+        Blink = (PMMPFN)ColorTable->Blink;
         
         /* Make it link to us */
         Pfn1->u4.PteFrame = MI_PFNENTRY_TO_PFN(Blink);
@@ -228,13 +582,184 @@ MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex)
     }
     
     /* Now initialize our own list pointers */
-    ColorHead->Blink = Pfn1;
-    Pfn1->OriginalPte.u.Long = -1;
+    ColorTable->Blink = Pfn1;
+    Pfn1->OriginalPte.u.Long = LIST_HEAD;
     
     /* And increase the count in the colored list */
-    ColorHead->Count++;
+    ColorTable->Count++;
+#endif
+    
+    /* Notify zero page thread if enough pages are on the free list now */
+    extern KEVENT ZeroPageThreadEvent;
+    if ((MmFreePageListHead.Total > 8) && !(KeReadStateEvent(&ZeroPageThreadEvent)))
+    {
+        /* This is ReactOS-specific */
+        KeSetEvent(&ZeroPageThreadEvent, IO_NO_INCREMENT, FALSE);
+    }
+}
+
+VOID
+NTAPI
+MiInitializePfn(IN PFN_NUMBER PageFrameIndex,
+                IN PMMPTE PointerPte,
+                IN BOOLEAN Modified)
+{
+    PMMPFN Pfn1;
+    NTSTATUS Status;
+    PMMPTE PointerPtePte;
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+    /* Setup the PTE */
+    Pfn1 = MiGetPfnEntry(PageFrameIndex);
+    Pfn1->PteAddress = PointerPte;
+
+    /* Check if this PFN is part of a valid address space */
+    if (PointerPte->u.Hard.Valid == 1)
+    {
+        /* FIXME: TODO */
+        ASSERT(FALSE);
+    }
+
+    /* Otherwise this is a fresh page -- set it up */
+    ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
+    Pfn1->u3.e2.ReferenceCount = 1;
+    Pfn1->u2.ShareCount = 1;
+    Pfn1->u3.e1.PageLocation = ActiveAndValid;
+    ASSERT(Pfn1->u3.e1.Rom == 0);
+    Pfn1->u3.e1.Modified = Modified;
+
+    /* Get the page table for the PTE */
+    PointerPtePte = MiAddressToPte(PointerPte);
+    if (PointerPtePte->u.Hard.Valid == 0)
+    {
+        /* Make sure the PDE gets paged in properly */
+        Status = MiCheckPdeForPagedPool(PointerPte);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Crash */
+            KeBugCheckEx(MEMORY_MANAGEMENT,
+                         0x61940,
+                         (ULONG_PTR)PointerPte,
+                         (ULONG_PTR)PointerPtePte->u.Long,
+                         (ULONG_PTR)MiPteToAddress(PointerPte));
+        }
+    }
+
+    /* Get the PFN for the page table */
+    PageFrameIndex = PFN_FROM_PTE(PointerPtePte);
+    ASSERT(PageFrameIndex != 0);
+    Pfn1->u4.PteFrame = PageFrameIndex;
+
+    /* Increase its share count so we don't get rid of it */
+    Pfn1 = MiGetPfnEntry(PageFrameIndex);
+    Pfn1->u2.ShareCount++;
+}
+
+PFN_NUMBER
+NTAPI
+MiAllocatePfn(IN PMMPTE PointerPte,
+              IN ULONG Protection)
+{
+    KIRQL OldIrql;
+    PFN_NUMBER PageFrameIndex;
+    MMPTE TempPte;
+    
+    /* Make an empty software PTE */
+    MI_MAKE_SOFTWARE_PTE(&TempPte, MM_READWRITE);
+    
+    /* Lock the PFN database */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    
+    /* Check if we're running low on pages */
+    if (MmAvailablePages < 128)
+    {
+        DPRINT1("Warning, running low on memory: %d pages left\n", MmAvailablePages);
+        //MiEnsureAvailablePageOrWait(NULL, OldIrql);
+    }
+    
+    /* Grab a page */
+    ASSERT_LIST_INVARIANT(&MmFreePageListHead);
+    ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
+    PageFrameIndex = MiRemoveAnyPage(0);
+
+    /* Write the software PTE */
+    ASSERT(PointerPte->u.Hard.Valid == 0);
+    *PointerPte = TempPte;
+    PointerPte->u.Soft.Protection |= Protection;
+    
+    /* Initialize its PFN entry */
+    MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
+    
+    /* Release the PFN lock and return the page */
+    ASSERT_LIST_INVARIANT(&MmFreePageListHead);
+    ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
+    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+    return PageFrameIndex;
+}
+
+VOID
+NTAPI
+MiDecrementShareCount(IN PMMPFN Pfn1,
+                      IN PFN_NUMBER PageFrameIndex)
+{
+    ASSERT(PageFrameIndex > 0);
+    ASSERT(MiGetPfnEntry(PageFrameIndex) != NULL);
+    ASSERT(Pfn1 == MiGetPfnEntry(PageFrameIndex));
+
+    /* Page must be in-use */
+    if ((Pfn1->u3.e1.PageLocation != ActiveAndValid) &&
+        (Pfn1->u3.e1.PageLocation != StandbyPageList))
+    {
+        /* Otherwise we have PFN corruption */
+        KeBugCheckEx(PFN_LIST_CORRUPT,
+                     0x99,
+                     PageFrameIndex,
+                     Pfn1->u3.e1.PageLocation,
+                     0);
+    }
+
+    /* Check if the share count is now 0 */
+    ASSERT(Pfn1->u2.ShareCount < 0xF000000);
+    if (!--Pfn1->u2.ShareCount)
+    {
+        /* ReactOS does not handle these */
+        ASSERT(Pfn1->u3.e1.PrototypePte == 0);
+
+        /* Put the page in transition */
+        Pfn1->u3.e1.PageLocation = TransitionPage;
     
-    /* FIXME: Notify zero page thread if enough pages are on the free list now */
+        /* PFN lock must be held */
+        ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+        
+        /* Page should at least have one reference */
+        ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
+        if (Pfn1->u3.e2.ReferenceCount == 1)
+        {
+            /* In ReactOS, this path should always be hit with a deleted PFN */
+            ASSERT(MI_IS_PFN_DELETED(Pfn1) == TRUE);
+
+            /* Clear the last reference */
+            Pfn1->u3.e2.ReferenceCount = 0;
+
+            /*
+             * OriginalPte is used by AweReferenceCount in ReactOS, but either 
+             * ways we shouldn't be seeing RMAP entries at this point
+             */
+            ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0);
+            ASSERT(Pfn1->OriginalPte.u.Long == 0);
+
+            /* Mark the page temporarily as valid, we're going to make it free soon */
+            Pfn1->u3.e1.PageLocation = ActiveAndValid;
+
+            /* Bring it back into the free list */
+            MiInsertPageInFreeList(PageFrameIndex);
+        }
+        else
+        {
+            /* Otherwise, just drop the reference count */
+            InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
+        }
+    }
 }
 
 /* EOF */