[CMAKE]
[reactos.git] / ntoskrnl / mm / ARM3 / procsup.c
index 816c7d1..aec5af6 100644 (file)
 #define MODULE_INVOLVED_IN_ARM3
 #include "../ARM3/miarm.h"
 
-extern MM_SYSTEMSIZE MmSystemSize;
+/* GLOBALS ********************************************************************/
+
+ULONG MmProcessColorSeed = 0x12345678;
+PMMWSL MmWorkingSetList;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
@@ -27,14 +30,14 @@ MiRosTakeOverPebTebRanges(IN PEPROCESS Process)
     NTSTATUS Status;
     PMEMORY_AREA MemoryArea;
     PHYSICAL_ADDRESS BoundaryAddressMultiple;
-    PVOID AllocatedBase = (PVOID)MI_LOWEST_VAD_ADDRESS;
+    PVOID AllocatedBase = (PVOID)USER_SHARED_DATA;
     BoundaryAddressMultiple.QuadPart = 0;
 
     Status = MmCreateMemoryArea(&Process->Vm,
                                 MEMORY_AREA_OWNED_BY_ARM3,
                                 &AllocatedBase,
                                 ((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - 1) -
-                                (ULONG_PTR)MI_LOWEST_VAD_ADDRESS,
+                                (ULONG_PTR)USER_SHARED_DATA,
                                 PAGE_READWRITE,
                                 &MemoryArea,
                                 TRUE,
@@ -55,6 +58,8 @@ MiCreatePebOrTeb(IN PEPROCESS Process,
     ULONG RandomCoeff;
     ULONG_PTR StartAddress, EndAddress;
     LARGE_INTEGER CurrentTime;
+    TABLE_SEARCH_RESULT Result = TableFoundNode;
+    PMMADDRESS_NODE Parent;
     
     /* Allocate a VAD */
     Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV');
@@ -93,34 +98,38 @@ MiCreatePebOrTeb(IN PEPROCESS Process,
         StartAddress -= RandomCoeff;
         EndAddress = StartAddress + ROUND_TO_PAGES(Size) - 1;
 
-        /* See if this VA range can be obtained */
-        if (!MiCheckForConflictingNode(StartAddress >> PAGE_SHIFT,
-                                       EndAddress >> PAGE_SHIFT,
-                                       &Process->VadRoot))
-        {
-            /* No conflict, use this address */
-            *Base = StartAddress;
-            goto AfterFound;
-        }
+        /* Try to find something below the random upper margin */
+        Result = MiFindEmptyAddressRangeDownTree(ROUND_TO_PAGES(Size),
+                                                 EndAddress,
+                                                 PAGE_SIZE,
+                                                 &Process->VadRoot,
+                                                 Base,
+                                                 &Parent);
+    }
+
+    /* Check for success. TableFoundNode means nothing free. */
+    if (Result == TableFoundNode)
+    {
+        /* For TEBs, or if a PEB location couldn't be found, scan the VAD root */
+        Result = MiFindEmptyAddressRangeDownTree(ROUND_TO_PAGES(Size),
+                                                 (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1,
+                                                 PAGE_SIZE,
+                                                 &Process->VadRoot,
+                                                 Base,
+                                                 &Parent);
+        /* Bail out, if still nothing free was found */
+        if (Result == TableFoundNode) return STATUS_NO_MEMORY;
     }
     
-    /* For TEBs, or if a PEB location couldn't be found, scan the VAD root */
-    Status = MiFindEmptyAddressRangeDownTree(ROUND_TO_PAGES(Size),
-                                             (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1,
-                                             PAGE_SIZE,
-                                             &Process->VadRoot,
-                                             Base);
-    ASSERT(NT_SUCCESS(Status));
-    
-AfterFound:
     /* Validate that it came from the VAD ranges */
     ASSERT(*Base >= (ULONG_PTR)MI_LOWEST_VAD_ADDRESS);
     
     /* Build the rest of the VAD now */
     Vad->StartingVpn = (*Base) >> PAGE_SHIFT;
-    Vad->EndingVpn =  ((*Base) + Size - 1) >> PAGE_SHIFT;
+    Vad->EndingVpn = ((*Base) + Size - 1) >> PAGE_SHIFT;
     Vad->u3.Secured.StartVpn = *Base;
     Vad->u3.Secured.EndVpn = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
+    Vad->u1.Parent = NULL;
     
     /* FIXME: Should setup VAD bitmap */
     Status = STATUS_SUCCESS;
@@ -131,8 +140,12 @@ AfterFound:
     /* Insert the VAD */
     ASSERT(Vad->EndingVpn >= Vad->StartingVpn);
     Process->VadRoot.NodeHint = Vad;
-    MiInsertNode((PVOID)Vad, &Process->VadRoot);
-    
+    Vad->ControlArea = NULL; // For Memory-Area hack
+    Vad->FirstPrototypePte = NULL;
+    DPRINT("VAD: %p\n", Vad);
+    DPRINT("Allocated PEB/TEB at: 0x%p for %16s\n", *Base, Process->ImageFileName);
+    MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
+
     /* Release the working set */
     MiUnlockProcessWorkingSet(Process, Thread);
 
@@ -140,7 +153,6 @@ AfterFound:
     KeReleaseGuardedMutex(&Process->AddressCreationLock);
 
     /* Return the status */
-    DPRINT("Allocated PEB/TEB at: 0x%p for %16s\n", *Base, Process->ImageFileName);
     return Status;
 }
 
@@ -179,6 +191,7 @@ MmDeleteTeb(IN PEPROCESS Process,
         ASSERT((Vad->StartingVpn == ((ULONG_PTR)Teb >> PAGE_SHIFT) &&
                (Vad->EndingVpn == (TebEnd >> PAGE_SHIFT))));
         ASSERT(Vad->u.VadFlags.NoChange == TRUE);
+        ASSERT(Vad->u2.VadFlags2.OneSecured == TRUE);
         ASSERT(Vad->u2.VadFlags2.MultipleSecured == FALSE);
 
         /* Lock the working set */
@@ -250,7 +263,7 @@ MmDeleteKernelStack(IN PVOID StackBase,
             MiDecrementShareCount(Pfn2, PageTableFrameNumber);
 #endif
             /* Set the special pending delete marker */
-            Pfn1->PteAddress = (PMMPTE)((ULONG_PTR)Pfn1->PteAddress | 1);
+            MI_SET_PFN_DELETED(Pfn1);
             
             /* And now delete the actual stack page */
             MiDecrementShareCount(Pfn1, PageFrameNumber);
@@ -351,7 +364,9 @@ MmCreateKernelStack(IN BOOLEAN GuiStack,
         PointerPte++;
         
         /* Get a page and write the current invalid PTE */
-        PageFrameIndex = MiRemoveAnyPage(0);
+        MI_SET_USAGE(MI_USAGE_KERNEL_STACK);
+        MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
+        PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
         MI_WRITE_INVALID_PTE(PointerPte, InvalidPte);
 
         /* Initialize the PFN entry for this page */
@@ -362,9 +377,6 @@ MmCreateKernelStack(IN BOOLEAN GuiStack,
         MI_WRITE_VALID_PTE(PointerPte, TempPte);
     }
 
-    // Bug #4835
-    (VOID)InterlockedExchangeAddUL(&MiMemoryConsumers[MC_NPPOOL].PagesUsed, StackPages);
-
     //
     // Release the PFN lock
     //
@@ -383,7 +395,6 @@ MmGrowKernelStackEx(IN PVOID StackPointer,
 {
     PKTHREAD Thread = KeGetCurrentThread();
     PMMPTE LimitPte, NewLimitPte, LastPte;
-    PFN_NUMBER StackPages;
     KIRQL OldIrql;
     MMPTE TempPte, InvalidPte;
     PFN_NUMBER PageFrameIndex;
@@ -424,7 +435,6 @@ MmGrowKernelStackEx(IN PVOID StackPointer,
     // Calculate the number of new pages
     //
     LimitPte--;
-    StackPages = (LimitPte - NewLimitPte + 1);
     
     /* Setup the temporary invalid PTE */
     MI_MAKE_SOFTWARE_PTE(&InvalidPte, MM_NOACCESS);
@@ -440,7 +450,9 @@ MmGrowKernelStackEx(IN PVOID StackPointer,
     while (LimitPte >= NewLimitPte)
     {
         /* Get a page and write the current invalid PTE */
-        PageFrameIndex = MiRemoveAnyPage(0);
+        MI_SET_USAGE(MI_USAGE_KERNEL_STACK_EXPANSION);
+        MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
+        PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
         MI_WRITE_INVALID_PTE(LimitPte, InvalidPte);
 
         /* Initialize the PFN entry for this page */
@@ -881,6 +893,32 @@ MmCreateTeb(IN PEPROCESS Process,
     return Status;
 }
 
+VOID
+NTAPI
+MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess)
+{
+    PMMPFN Pfn1;
+
+    /* Setup some bogus list data */
+    MmWorkingSetList->LastEntry = CurrentProcess->Vm.MinimumWorkingSetSize;
+    MmWorkingSetList->HashTable = NULL;
+    MmWorkingSetList->HashTableSize = 0;
+    MmWorkingSetList->NumberOfImageWaiters = 0;
+    MmWorkingSetList->Wsle = (PVOID)0xDEADBABE;
+    MmWorkingSetList->VadBitMapHint = 1;
+    MmWorkingSetList->HashTableStart = (PVOID)0xBADAB00B;
+    MmWorkingSetList->HighestPermittedHashAddress = (PVOID)0xCAFEBABE;
+    MmWorkingSetList->FirstFree = 1;
+    MmWorkingSetList->FirstDynamic = 2;
+    MmWorkingSetList->NextSlot = 3;
+    MmWorkingSetList->LastInitializedWsle = 4;
+    
+    /* The rule is that the owner process is always in the FLINK of the PDE's PFN entry */
+    Pfn1 = MiGetPfnEntry(MiAddressToPte(PDE_BASE)->u.Hard.PageFrameNumber);
+    ASSERT(Pfn1->u4.PteFrame == MiGetPfnEntryIndex(Pfn1));
+    Pfn1->u1.Event = (PKEVENT)CurrentProcess;
+}
+
 NTSTATUS
 NTAPI
 MmInitializeProcessAddressSpace(IN PEPROCESS Process,
@@ -901,6 +939,7 @@ MmInitializeProcessAddressSpace(IN PEPROCESS Process,
     PWCHAR Source;
     PCHAR Destination;
     USHORT Length = 0;
+    MMPTE TempPte;
     
     /* We should have a PDE */
     ASSERT(Process->Pcb.DirectoryTableBase[0] != 0);
@@ -933,6 +972,22 @@ MmInitializeProcessAddressSpace(IN PEPROCESS Process,
     PointerPde = MiAddressToPde(HYPER_SPACE);
     PageFrameNumber = PFN_FROM_PTE(PointerPde);
     MiInitializePfn(PageFrameNumber, PointerPde, TRUE);
+    
+    /* Setup the PFN for the PTE for the working set */
+    PointerPte = MiAddressToPte(MI_WORKING_SET_LIST);
+    MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, MM_READWRITE, 0);
+    ASSERT(PointerPte->u.Long != 0);
+    PageFrameNumber = PFN_FROM_PTE(PointerPte);
+    MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPte);
+    MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
+    TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
+    MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+    /* Now initialize the working set list */
+    MiInitializeWorkingSetList(Process);
+
+    /* Sanity check */
+    ASSERT(Process->PhysicalVadRoot == NULL);
 
     /* Release PFN lock */
     KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
@@ -1012,6 +1067,7 @@ MmInitializeProcessAddressSpace(IN PEPROCESS Process,
 
 NTSTATUS
 NTAPI
+INIT_FUNCTION
 MmInitializeHandBuiltProcess(IN PEPROCESS Process,
                              IN PULONG_PTR DirectoryTableBase)
 {
@@ -1033,6 +1089,7 @@ MmInitializeHandBuiltProcess(IN PEPROCESS Process,
 
 NTSTATUS
 NTAPI
+INIT_FUNCTION
 MmInitializeHandBuiltProcess2(IN PEPROCESS Process)
 {
     /* Lock the VAD, ARM3-owned ranges away */                            
@@ -1049,14 +1106,16 @@ MmCreateProcessAddressSpace(IN ULONG MinWs,
                             OUT PULONG_PTR DirectoryTableBase)
 {
     KIRQL OldIrql;
-    PFN_NUMBER PdeIndex, HyperIndex;
+    PFN_NUMBER PdeIndex, HyperIndex, WsListIndex;
     PMMPTE PointerPte;
     MMPTE TempPte, PdePte;
     ULONG PdeOffset;
-    PMMPTE SystemTable;
+    PMMPTE SystemTable, HyperTable;
+    ULONG Color;
+    PMMPFN Pfn1;
 
-    /* No page colors yet */
-    Process->NextPageColor = 0;
+    /* Choose a process color */
+    Process->NextPageColor = RtlRandom(&MmProcessColorSeed);
     
     /* Setup the hyperspace lock */
     KeInitializeSpinLock(&Process->HyperSpaceLock);
@@ -1064,27 +1123,96 @@ MmCreateProcessAddressSpace(IN ULONG MinWs,
     /* Lock PFN database */
     OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
     
-    /* Get a page for the PDE */
-    PdeIndex = MiRemoveAnyPage(0);
-    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
-    MiZeroPhysicalPage(PdeIndex);
-    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    /* Get a zero page for the PDE, if possible */
+    Color = MI_GET_NEXT_PROCESS_COLOR(Process);
+    MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY);
+    PdeIndex = MiRemoveZeroPageSafe(Color);
+    if (!PdeIndex)
+    {
+        /* No zero pages, grab a free one */
+        PdeIndex = MiRemoveAnyPage(Color);
+        
+        /* Zero it outside the PFN lock */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+        MiZeroPhysicalPage(PdeIndex);
+        OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    }
+    
+    /* Get a zero page for hyperspace, if possible */
+    MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY);
+    Color = MI_GET_NEXT_PROCESS_COLOR(Process);
+    HyperIndex = MiRemoveZeroPageSafe(Color);
+    if (!HyperIndex)
+    {
+        /* No zero pages, grab a free one */
+        HyperIndex = MiRemoveAnyPage(Color);
+        
+        /* Zero it outside the PFN lock */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+        MiZeroPhysicalPage(HyperIndex);
+        OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    }
 
-    /* Get a page for hyperspace */
-    HyperIndex = MiRemoveAnyPage(0);
-    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
-    MiZeroPhysicalPage(HyperIndex);
+    /* Get a zero page for the woring set list, if possible */
+    MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
+    Color = MI_GET_NEXT_PROCESS_COLOR(Process);
+    WsListIndex = MiRemoveZeroPageSafe(Color);
+    if (!WsListIndex)
+    {
+        /* No zero pages, grab a free one */
+        WsListIndex = MiRemoveAnyPage(Color);
+        
+        /* Zero it outside the PFN lock */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+        MiZeroPhysicalPage(WsListIndex);
+    }
+    else
+    {
+        /* Release the PFN lock */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+    }
 
     /* Switch to phase 1 initialization */
     ASSERT(Process->AddressSpaceInitialized == 0);
     Process->AddressSpaceInitialized = 1;
 
     /* Set the base directory pointers */
+    Process->WorkingSetPage = WsListIndex;
     DirectoryTableBase[0] = PdeIndex << PAGE_SHIFT;
     DirectoryTableBase[1] = HyperIndex << PAGE_SHIFT;
 
     /* Make sure we don't already have a page directory setup */
     ASSERT(Process->Pcb.DirectoryTableBase[0] == 0);
+    
+    /* Get a PTE to map hyperspace */
+    PointerPte = MiReserveSystemPtes(1, SystemPteSpace);
+    ASSERT(PointerPte != NULL);
+
+    /* Build it */
+    MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte,
+                                PointerPte,
+                                MM_READWRITE,
+                                HyperIndex);
+
+    /* Set it dirty and map it */
+    PdePte.u.Hard.Dirty = TRUE;
+    MI_WRITE_VALID_PTE(PointerPte, PdePte);
+
+    /* Now get hyperspace's page table */
+    HyperTable = MiPteToAddress(PointerPte);
+
+    /* Now write the PTE/PDE entry for the working set list index itself */
+    TempPte = ValidKernelPte;
+    TempPte.u.Hard.PageFrameNumber = WsListIndex;
+    PdeOffset = MiAddressToPteOffset(MmWorkingSetList);
+    HyperTable[PdeOffset] = TempPte;
+
+    /* Let go of the system PTE */
+    MiReleaseSystemPtes(PointerPte, 1, SystemPteSpace);
+
+    /* Save the PTE address of the page directory itself */
+    Pfn1 = MiGetPfnEntry(PdeIndex);
+    Pfn1->PteAddress = (PMMPTE)PDE_BASE;
 
     /* Insert us into the Mm process list */
     InsertTailList(&MmProcessList, &Process->MmProcessLinks);
@@ -1108,7 +1236,6 @@ MmCreateProcessAddressSpace(IN ULONG MinWs,
 
     /* Copy all the kernel mappings */
     PdeOffset = MiGetPdeOffset(MmSystemRangeStart);
-
     RtlCopyMemory(&SystemTable[PdeOffset],
                   MiAddressToPde(MmSystemRangeStart),
                   PAGE_SIZE - PdeOffset * sizeof(MMPTE));
@@ -1142,12 +1269,17 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
     PMM_AVL_TABLE VadTree;
     PETHREAD Thread = PsGetCurrentThread();
     
+    /* Only support this */
+    ASSERT(Process->AddressSpaceInitialized == 2);
+    
     /* Lock the process address space from changes */
     MmLockAddressSpace(&Process->Vm);
     
+    /* VM is deleted now */
+    Process->VmDeleted = TRUE;
+    
     /* Enumerate the VADs */
     VadTree = &Process->VadRoot;
-    DPRINT("Cleaning up VADs: %d\n", VadTree->NumberGenericTableElements);
     while (VadTree->NumberGenericTableElements)
     {
         /* Grab the current VAD */
@@ -1158,24 +1290,35 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
 
         /* Remove this VAD from the tree */
         ASSERT(VadTree->NumberGenericTableElements >= 1);
-        DPRINT("Removing node for VAD: %lx %lx\n", Vad->StartingVpn, Vad->EndingVpn);
         MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree);
-        DPRINT("Moving on: %d\n", VadTree->NumberGenericTableElements);
 
-        /* Check if this VAD was the hint */
-        if (VadTree->NodeHint == Vad)
+        /* Only regular VADs supported for now */
+        ASSERT(Vad->u.VadFlags.VadType == VadNone);
+        
+        /* Check if this is a section VAD */
+        if (!(Vad->u.VadFlags.PrivateMemory) && (Vad->ControlArea))
         {
-            /* Get a new hint, unless we're empty now, in which case nothing */
-            VadTree->NodeHint = VadTree->BalancedRoot.RightChild;
-            if (!VadTree->NumberGenericTableElements) VadTree->NodeHint = NULL;
+            /* Remove the view */
+            MiRemoveMappedView(Process, Vad);
         }
+        else
+        {
+            /* Delete the addresses */
+            MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
+                                     (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1),
+                                     Vad);
         
-        /* Only PEB/TEB VADs supported for now */
-        ASSERT(Vad->u.VadFlags.PrivateMemory == 1);
-        ASSERT(Vad->u.VadFlags.VadType == VadNone);
+            /* Release the working set */
+            MiUnlockProcessWorkingSet(Process, Thread);
+        }
         
-        /* Release the working set */
-        MiUnlockProcessWorkingSet(Process, Thread);
+        /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */
+        if (Vad->u.VadFlags.Spare == 1)
+        {
+            /* Set a flag so MmDeleteMemoryArea knows to free, but not to remove */
+            Vad->u.VadFlags.Spare = 2;
+            continue;
+        }
         
         /* Free the VAD memory */
         ExFreePool(Vad);