[CMAKE]
[reactos.git] / ntoskrnl / mm / ARM3 / mdlsup.c
index 7363158..b3449a3 100644 (file)
 #define MODULE_INVOLVED_IN_ARM3
 #include "../ARM3/miarm.h"
 
+/* GLOBALS ********************************************************************/
 BOOLEAN MmTrackPtes;
 BOOLEAN MmTrackLockedPages;
+SIZE_T MmSystemLockPagesCount;
 
 /* PUBLIC FUNCTIONS ***********************************************************/
 
@@ -248,34 +251,47 @@ MmFreePagesFromMdl(IN PMDL Mdl)
         //
         // Reached the last page
         //
-        if (*Pages == -1) break;
-        
-        //
-        // Sanity check
-        //
-        ASSERT(*Pages <= MmHighestPhysicalPage);
-        
+        if (*Pages == LIST_HEAD) break;
+
         //
         // Get the page entry
         //
         Pfn1 = MiGetPfnEntry(*Pages);
-        ASSERT(Pfn1->u3.ReferenceCount == 1);
+        ASSERT(Pfn1);
+        ASSERT(Pfn1->u2.ShareCount == 1);
+        ASSERT(MI_IS_PFN_DELETED(Pfn1) == TRUE);
+        if (Pfn1->u4.PteFrame != 0x1FFEDCB) 
+        {
+            /* Corrupted PFN entry or invalid free */
+            KeBugCheckEx(MEMORY_MANAGEMENT, 0x1236, (ULONG_PTR)Mdl, (ULONG_PTR)Pages, *Pages);
+        }
         
         //
         // Clear it
         //
         Pfn1->u3.e1.StartOfAllocation = 0;
         Pfn1->u3.e1.EndOfAllocation = 0;
+        Pfn1->u2.ShareCount == 0;
         
         //
         // Dereference it
         //
-        MmDereferencePage(*Pages);
+        ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
+        if (Pfn1->u3.e2.ReferenceCount != 1)
+        {
+            /* Just take off one reference */
+            InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
+        }
+        else
+        {
+            /* We'll be nuking the whole page */
+            MiDecrementReferenceCount(Pfn1, *Pages);
+        }
         
         //
         // Clear this page and move on
         //
-        *Pages++ = -1;
+        *Pages++ = LIST_HEAD;
     } while (--NumberOfPages != 0);
 
     //
@@ -411,7 +427,7 @@ MmMapLockedPagesSpecifyCache(IN PMDL Mdl,
             //
             // We're done here
             //
-            if (*MdlPages == -1) break;
+            if (*MdlPages == LIST_HEAD) break;
             
             //
             // Write the PTE
@@ -444,10 +460,8 @@ MmMapLockedPagesSpecifyCache(IN PMDL Mdl,
         return Base;
     }
     
-    //
-    // In user-mode, let ReactOS do it
-    //
-    return MiMapLockedPagesInUserSpace(Mdl, Base, CacheType, BaseAddress);
+    UNIMPLEMENTED;
+    return NULL;
 }
 
 /*
@@ -557,10 +571,7 @@ MmUnmapLockedPages(IN PVOID BaseAddress,
     }
     else
     {
-        //
-        // Let ReactOS handle it
-        //
-        MiUnmapLockedPagesInUserSpace(BaseAddress, Mdl);
+        UNIMPLEMENTED;
     }
 }
 
@@ -578,15 +589,14 @@ MmProbeAndLockPages(IN PMDL Mdl,
     ULONG LockPages, TotalPages;
     NTSTATUS Status = STATUS_SUCCESS;
     PEPROCESS CurrentProcess;
-    PETHREAD Thread;
-    PMMSUPPORT AddressSpace;
     NTSTATUS ProbeStatus;
     PMMPTE PointerPte, LastPte;
     PMMPDE PointerPde;
     PFN_NUMBER PageFrameIndex;
-    PMMPFN Pfn1;
     BOOLEAN UsePfnLock;
     KIRQL OldIrql;
+    USHORT OldRefCount, RefCount;
+    PMMPFN Pfn1;
     DPRINT("Probing MDL: %p\n", Mdl);
     
     //
@@ -615,10 +625,18 @@ MmProbeAndLockPages(IN PMDL Mdl,
     LockPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Address, Mdl->ByteCount);
     ASSERT(LockPages != 0);
     
+    /* Block invalid access */
+    if ((AccessMode != KernelMode) &&
+        ((LastAddress > (PVOID)MM_USER_PROBE_ADDRESS) || (Address >= LastAddress)))
+    {
+        /* Caller should be in SEH, raise the error */
+        *MdlPages = LIST_HEAD;
+        ExRaiseStatus(STATUS_ACCESS_VIOLATION);
+    }
+    
     //
-    // Get the thread and process
+    // Get the process
     //
-    Thread = PsGetCurrentThread();
     if (Address <= MM_HIGHEST_USER_ADDRESS)
     {
         //
@@ -640,6 +658,9 @@ MmProbeAndLockPages(IN PMDL Mdl,
     TotalPages = LockPages;
     StartAddress = Address;
     
+    /* Large pages not supported */
+    ASSERT(!MI_IS_PHYSICAL_ADDRESS(Address));
+    
     //
     // Now probe them
     //
@@ -654,7 +675,7 @@ MmProbeAndLockPages(IN PMDL Mdl,
             //
             // Assume failure
             //
-            *MdlPages = -1;
+            *MdlPages = LIST_HEAD;
             
             //
             // Read
@@ -676,8 +697,7 @@ MmProbeAndLockPages(IN PMDL Mdl,
             //
             // Next address...
             //
-            Address = (PVOID)((ULONG_PTR)Address + PAGE_SIZE);
-            Address = PAGE_ALIGN(Address);
+            Address = PAGE_ALIGN((ULONG_PTR)Address + PAGE_SIZE);
             
             //
             // Next page...
@@ -719,6 +739,10 @@ MmProbeAndLockPages(IN PMDL Mdl,
     //
     PointerPte = MiAddressToPte(StartAddress);
     PointerPde = MiAddressToPde(StartAddress);
+#if (_MI_PAGING_LEVELS >= 3)
+    DPRINT1("PAE/x64 Not Implemented\n");
+    ASSERT(FALSE);
+#endif
     
     //
     // Sanity check
@@ -769,7 +793,6 @@ MmProbeAndLockPages(IN PMDL Mdl,
         //
         UsePfnLock = TRUE;
         OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
-        AddressSpace = NULL; // Keep compiler happy
     }
     else
     {
@@ -790,13 +813,10 @@ MmProbeAndLockPages(IN PMDL Mdl,
         //
         Mdl->Process = CurrentProcess;
         
-        //
-        // Use the process lock
-        //
+        /* Lock the process working set */
+        MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
         UsePfnLock = FALSE;
-        AddressSpace = &CurrentProcess->Vm;
-        MmLockAddressSpace(AddressSpace);
-        OldIrql = DISPATCH_LEVEL; // Keep compiler happy
+        OldIrql = MM_NOIRQL;
     }
     
     //
@@ -812,7 +832,11 @@ MmProbeAndLockPages(IN PMDL Mdl,
         //
         // Assume failure and check for non-mapped pages
         //
-        *MdlPages = -1;
+        *MdlPages = LIST_HEAD;
+#if (_MI_PAGING_LEVELS >= 3)
+        /* Should be checking the PPE and PXE */
+        ASSERT(FALSE);
+#endif
         while ((PointerPde->u.Hard.Valid == 0) ||
                (PointerPte->u.Hard.Valid == 0))
         {
@@ -828,10 +852,8 @@ MmProbeAndLockPages(IN PMDL Mdl,
             }
             else
             {
-                //
-                // Release process address space lock
-                //
-                MmUnlockAddressSpace(AddressSpace);
+                /* Release process working set */
+                MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
             }
             
             //
@@ -860,10 +882,8 @@ MmProbeAndLockPages(IN PMDL Mdl,
             }
             else
             {
-                //
-                // Use the address space lock
-                //
-                MmLockAddressSpace(AddressSpace);
+                /* Lock the process working set */
+                MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
             }
         }
         
@@ -900,10 +920,8 @@ MmProbeAndLockPages(IN PMDL Mdl,
                         }
                         else
                         {
-                            //
-                            // Release process address space lock
-                            //
-                            MmUnlockAddressSpace(AddressSpace);
+                            /* Release process working set */
+                            MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
                         }
                         
                         //
@@ -931,10 +949,8 @@ MmProbeAndLockPages(IN PMDL Mdl,
                         }
                         else
                         {
-                            //
-                            // Use the address space lock
-                            //
-                            MmLockAddressSpace(AddressSpace);
+                            /* Lock the process working set */
+                            MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
                         }
                         
                         //
@@ -956,18 +972,55 @@ MmProbeAndLockPages(IN PMDL Mdl,
         // Grab the PFN
         //
         PageFrameIndex = PFN_FROM_PTE(PointerPte);
-        if (PageFrameIndex <= MmHighestPhysicalPage)
+        Pfn1 = MiGetPfnEntry(PageFrameIndex);
+        if (Pfn1)
         {
-            //
-            // Get the PFN entry
-            //
-            Pfn1 = MiGetPfnEntry(PageFrameIndex);
+            /* Either this is for kernel-mode, or the working set is held */
             ASSERT((CurrentProcess == NULL) || (UsePfnLock == FALSE));
             
-            //
-            // Now lock the page
-            //
-            MmReferencePage(PageFrameIndex);
+            /* No Physical VADs supported yet */
+            if (CurrentProcess) ASSERT(CurrentProcess->PhysicalVadRoot == NULL);
+            
+            /* This address should already exist and be fully valid */
+            ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
+            if (MI_IS_ROS_PFN(Pfn1))
+            {
+                /* ReactOS Mm doesn't track share count */
+                ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
+            }
+            else
+            {
+                /* On ARM3 pages, we should see a valid share count */
+                ASSERT((Pfn1->u2.ShareCount != 0) && (Pfn1->u3.e1.PageLocation == ActiveAndValid));
+                
+                /* We don't support mapping a prototype page yet */
+                ASSERT((Pfn1->u3.e1.PrototypePte == 0) && (Pfn1->OriginalPte.u.Soft.Prototype == 0));
+            }
+
+            /* More locked pages! */
+            InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, 1);
+
+            /* Loop trying to update the reference count */
+            do
+            {
+                /* Get the current reference count, make sure it's valid */
+                OldRefCount = Pfn1->u3.e2.ReferenceCount;
+                ASSERT(OldRefCount != 0);
+                ASSERT(OldRefCount < 2500);
+
+                /* Bump it up by one */
+                RefCount = InterlockedCompareExchange16((PSHORT)&Pfn1->u3.e2.ReferenceCount,
+                                                        OldRefCount + 1,
+                                                        OldRefCount);
+                ASSERT(RefCount != 0);
+            } while (OldRefCount != RefCount);
+            
+            /* Was this the first lock attempt? */
+            if (OldRefCount != 1)
+            {
+                /* Someone else came through */
+                InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1);
+            }
         }
         else
         {
@@ -981,7 +1034,10 @@ MmProbeAndLockPages(IN PMDL Mdl,
         // Write the page and move on
         //
         *MdlPages++ = PageFrameIndex;
-        if (!((ULONG_PTR)(++PointerPte) & (PAGE_SIZE - 1))) PointerPde++;
+        PointerPte++;
+        
+        /* Check if we're on a PDE boundary */
+        if (!((ULONG_PTR)PointerPte & (PD_SIZE - 1))) PointerPde++;
     } while (PointerPte <= LastPte);
     
     //
@@ -996,10 +1052,8 @@ MmProbeAndLockPages(IN PMDL Mdl,
     }
     else
     {
-        //
-        // Release process address space lock
-        //
-        MmUnlockAddressSpace(AddressSpace);
+        /* Release process working set */
+        MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
     }
     
     //
@@ -1026,10 +1080,8 @@ CleanupWithLock:
     }
     else
     {
-        //
-        // Release process address space lock
-        //
-        MmUnlockAddressSpace(AddressSpace);
+        /* Release process working set */
+        MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
     }
 Cleanup:
     //
@@ -1056,6 +1108,8 @@ MmUnlockPages(IN PMDL Mdl)
     PVOID Base;
     ULONG Flags, PageCount;
     KIRQL OldIrql;
+    USHORT RefCount, OldRefCount;
+    PMMPFN Pfn1;
     DPRINT("Unlocking MDL: %p\n", Mdl);
     
     //
@@ -1115,17 +1169,71 @@ MmUnlockPages(IN PMDL Mdl)
             //
             // Last page, break out
             //
-            if (*MdlPages == -1) break;
+            if (*MdlPages == LIST_HEAD) break;
             
             //
             // Check if this page is in the PFN database
             //
-            if (*MdlPages <= MmHighestPhysicalPage)
+            Pfn1 = MiGetPfnEntry(*MdlPages);
+            if (Pfn1);
             {
-                //
-                // Unlock and dereference
-                //
-                MmDereferencePage(*MdlPages);
+                /* Get the current entry and reference count */
+                OldRefCount = Pfn1->u3.e2.ReferenceCount;
+                ASSERT(OldRefCount != 0);
+
+                /* Is this already the last dereference */
+                if (OldRefCount == 1)
+                {
+                    /* It should be on a free list waiting for us */
+                    ASSERT(Pfn1->u3.e2.ReferenceCount == 1);
+                    ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
+                    ASSERT(Pfn1->u2.ShareCount == 0);
+                    
+                    /* Not supported yet */
+                    ASSERT(((Pfn1->u3.e1.PrototypePte == 0) &&
+                            (Pfn1->OriginalPte.u.Soft.Prototype == 0)));
+
+                    /* One less page */
+                    InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1);
+                    
+                    /* Do the last dereference, we're done here */
+                    MiDecrementReferenceCount(Pfn1, *MdlPages);
+                }
+                else
+                {
+                    /* Loop decrementing one reference */
+                    do
+                    {
+                        /* Make sure it's still valid */
+                        OldRefCount = Pfn1->u3.e2.ReferenceCount;
+                        ASSERT(OldRefCount != 0);
+
+                        /* Take off one reference */
+                        RefCount = InterlockedCompareExchange16((PSHORT)&Pfn1->u3.e2.ReferenceCount,
+                                                                OldRefCount - 1,
+                                                                OldRefCount);
+                        ASSERT(RefCount != 0);
+                     } while (OldRefCount != RefCount);
+                     ASSERT(RefCount > 1);
+
+                     /* Are there only lock references left? */
+                     if (RefCount == 2)
+                     {
+                         /* And does the page still have users? */
+                         if (Pfn1->u2.ShareCount >= 1)
+                         {
+                            /* Then it should still be valid */
+                            ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
+
+                            /* Not supported yet */
+                            ASSERT(((Pfn1->u3.e1.PrototypePte == 0) &&
+                                    (Pfn1->OriginalPte.u.Soft.Prototype == 0)));
+
+                            /* But there is one less "locked" page though */
+                            InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1);
+                         }
+                     }
+                 }
             }
         } while (++MdlPages < LastPage);
         
@@ -1177,7 +1285,7 @@ MmUnlockPages(IN PMDL Mdl)
         //
         // Last page reached
         //
-        if (*MdlPages == -1)
+        if (*MdlPages == LIST_HEAD)
         {
             //
             // Were there no pages at all?
@@ -1198,10 +1306,9 @@ MmUnlockPages(IN PMDL Mdl)
             break;
         }
         
-        //
-        // Sanity check
-        //
-        ASSERT(*MdlPages <= MmHighestPhysicalPage);
+        /* Save the PFN entry instead for the secondary loop */
+        *MdlPages = (PFN_NUMBER)MiGetPfnEntry(*MdlPages);
+        ASSERT((*MdlPages) != 0);
     } while (++MdlPages < LastPage);
     
     //
@@ -1215,10 +1322,64 @@ MmUnlockPages(IN PMDL Mdl)
     OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
     do
     {
-        //
-        // Unlock and dereference
-        //
-        MmDereferencePage(*MdlPages);
+        /* Get the current entry and reference count */
+        Pfn1 = (PMMPFN)(*MdlPages);
+        OldRefCount = Pfn1->u3.e2.ReferenceCount;
+        ASSERT(OldRefCount != 0);
+
+        /* Is this already the last dereference */
+        if (OldRefCount == 1)
+        {
+            /* It should be on a free list waiting for us */
+            ASSERT(Pfn1->u3.e2.ReferenceCount == 1);
+            ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
+            ASSERT(Pfn1->u2.ShareCount == 0);
+            
+            /* Not supported yet */
+            ASSERT(((Pfn1->u3.e1.PrototypePte == 0) &&
+                    (Pfn1->OriginalPte.u.Soft.Prototype == 0)));
+
+            /* One less page */
+            InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1);
+            
+            /* Do the last dereference, we're done here */
+            MiDecrementReferenceCount(Pfn1, MiGetPfnEntryIndex(Pfn1));
+        }
+        else
+        {
+            /* Loop decrementing one reference */
+            do
+            {
+                /* Make sure it's still valid */
+                OldRefCount = Pfn1->u3.e2.ReferenceCount;
+                ASSERT(OldRefCount != 0);
+
+                /* Take off one reference */
+                RefCount = InterlockedCompareExchange16((PSHORT)&Pfn1->u3.e2.ReferenceCount,
+                                                        OldRefCount - 1,
+                                                        OldRefCount);
+                ASSERT(RefCount != 0);
+             } while (OldRefCount != RefCount);
+             ASSERT(RefCount > 1);
+
+             /* Are there only lock references left? */
+             if (RefCount == 2)
+             {
+                 /* And does the page still have users? */
+                 if (Pfn1->u2.ShareCount >= 1)
+                 {
+                    /* Then it should still be valid */
+                    ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
+
+                    /* Not supported yet */
+                    ASSERT(((Pfn1->u3.e1.PrototypePte == 0) &&
+                            (Pfn1->OriginalPte.u.Soft.Prototype == 0)));
+
+                    /* But there is one less "locked" page though */
+                    InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1);
+                 }
+             }
+         }
     } while (++MdlPages < LastPage);
     
     //