[NTOS]
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / procsup.c
index d4eb646..9a0ae20 100644 (file)
@@ -19,6 +19,8 @@
 
 ULONG MmProcessColorSeed = 0x12345678;
 PMMWSL MmWorkingSetList;
+ULONG MmMaximumDeadKernelStacks = 5;
+SLIST_HEADER MmDeadStackSListHead;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
@@ -116,7 +118,11 @@ MiCreatePebOrTeb(IN PEPROCESS Process,
                                                  Base,
                                                  &Parent);
         /* Bail out, if still nothing free was found */
-        if (Result == TableFoundNode) return STATUS_NO_MEMORY;
+        if (Result == TableFoundNode)
+        {
+            ExFreePoolWithTag(Vad, 'ldaV');
+            return STATUS_NO_MEMORY;
+        }
     }
 
     /* Validate that it came from the VAD ranges */
@@ -133,7 +139,7 @@ MiCreatePebOrTeb(IN PEPROCESS Process,
     Status = STATUS_SUCCESS;
 
     /* Pretend as if we own the working set */
-    MiLockProcessWorkingSet(Process, Thread);
+    MiLockProcessWorkingSetUnsafe(Process, Thread);
 
     /* Insert the VAD */
     ASSERT(Vad->EndingVpn >= Vad->StartingVpn);
@@ -145,7 +151,7 @@ MiCreatePebOrTeb(IN PEPROCESS Process,
     MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
 
     /* Release the working set */
-    MiUnlockProcessWorkingSet(Process, Thread);
+    MiUnlockProcessWorkingSetUnsafe(Process, Thread);
 
     /* Release the address space lock */
     KeReleaseGuardedMutex(&Process->AddressCreationLock);
@@ -193,7 +199,7 @@ MmDeleteTeb(IN PEPROCESS Process,
         ASSERT(Vad->u2.VadFlags2.MultipleSecured == FALSE);
 
         /* Lock the working set */
-        MiLockProcessWorkingSet(Process, Thread);
+        MiLockProcessWorkingSetUnsafe(Process, Thread);
 
         /* Remove this VAD from the tree */
         ASSERT(VadTree->NumberGenericTableElements >= 1);
@@ -201,9 +207,9 @@ MmDeleteTeb(IN PEPROCESS Process,
 
         /* Delete the pages */
         MiDeleteVirtualAddresses((ULONG_PTR)Teb, TebEnd, NULL);
-    
+
         /* Release the working set */
-        MiUnlockProcessWorkingSet(Process, Thread);
+        MiUnlockProcessWorkingSetUnsafe(Process, Thread);
 
         /* Remove the VAD */
         ExFreePool(Vad);
@@ -224,7 +230,7 @@ MmDeleteKernelStack(IN PVOID StackBase,
     PMMPTE PointerPte;
     PFN_NUMBER PageFrameNumber, PageTableFrameNumber;
     PFN_COUNT StackPages;
-    PMMPFN Pfn1;//, Pfn2;
+    PMMPFN Pfn1, Pfn2;
     ULONG i;
     KIRQL OldIrql;
 
@@ -234,6 +240,20 @@ MmDeleteKernelStack(IN PVOID StackBase,
     PointerPte = MiAddressToPte(StackBase);
     PointerPte--;
 
+    //
+    // If this is a small stack, just push the stack onto the dead stack S-LIST
+    //
+    if (!GuiStack)
+    {
+        if (ExQueryDepthSList(&MmDeadStackSListHead) < MmMaximumDeadKernelStacks)
+        {
+            Pfn1 = MiGetPfnEntry(PointerPte->u.Hard.PageFrameNumber);
+            InterlockedPushEntrySList(&MmDeadStackSListHead,
+                                      (PSLIST_ENTRY)&Pfn1->u1.NextStackPfn);
+            return;
+        }
+    }
+
     //
     // Calculate pages used
     //
@@ -256,15 +276,14 @@ MmDeleteKernelStack(IN PVOID StackBase,
             /* Get the PTE's page */
             PageFrameNumber = PFN_FROM_PTE(PointerPte);
             Pfn1 = MiGetPfnEntry(PageFrameNumber);
-#if 1 // ARM3 might not own the page table, so don't take this risk. Leak it instead!
+
             /* Now get the page of the page table mapping it */
             PageTableFrameNumber = Pfn1->u4.PteFrame;
-            //Pfn2 = MiGetPfnEntry(PageTableFrameNumber);
+            Pfn2 = MiGetPfnEntry(PageTableFrameNumber);
 
             /* Remove a shared reference, since the page is going away */
-            DPRINT("SystemPTE PDE: %lx\n", PageTableFrameNumber);
-            //MiDecrementShareCount(Pfn2, PageTableFrameNumber);
-#endif
+            MiDecrementShareCount(Pfn2, PageTableFrameNumber);
+
             /* Set the special pending delete marker */
             MI_SET_PFN_DELETED(Pfn1);
 
@@ -304,6 +323,7 @@ MmCreateKernelStack(IN BOOLEAN GuiStack,
     KIRQL OldIrql;
     PFN_NUMBER PageFrameIndex;
     ULONG i;
+    PMMPFN Pfn1;
 
     //
     // Calculate pages needed
@@ -319,6 +339,21 @@ MmCreateKernelStack(IN BOOLEAN GuiStack,
     }
     else
     {
+        //
+        // If the dead stack S-LIST has a stack on it, use it instead of allocating
+        // new system PTEs for this stack
+        //
+        if (ExQueryDepthSList(&MmDeadStackSListHead))
+        {
+            Pfn1 = (PMMPFN)InterlockedPopEntrySList(&MmDeadStackSListHead);
+            if (Pfn1)
+            {
+                PointerPte = Pfn1->PteAddress;
+                BaseAddress = MiPteToAddress(++PointerPte);
+                return BaseAddress;
+            }
+        }
+
         //
         // We'll allocate 12K and that's it
         //
@@ -546,9 +581,7 @@ MmGetSessionLocaleId(VOID)
             //
             // Get the Locale ID
             //
-#if ROS_HAS_SESSIONS
             return ((PMM_SESSION_SPACE)Process->Session)->LocaleId;
-#endif
         }
     }
 
@@ -645,7 +678,7 @@ MmCreatePeb(IN PEPROCESS Process,
         Peb->OSMajorVersion = NtMajorVersion;
         Peb->OSMinorVersion = NtMinorVersion;
         Peb->OSBuildNumber = (USHORT)(NtBuildNumber & 0x3FFF);
-        Peb->OSPlatformId = 2; /* VER_PLATFORM_WIN32_NT */
+        Peb->OSPlatformId = VER_PLATFORM_WIN32_NT;
         Peb->OSCSDVersion = (USHORT)CmNtCSDVersion;
 
         //
@@ -654,20 +687,19 @@ MmCreatePeb(IN PEPROCESS Process,
         Peb->NumberOfProcessors = KeNumberProcessors;
         Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL);
         Peb->NtGlobalFlag = NtGlobalFlag;
-        /*Peb->HeapSegmentReserve = MmHeapSegmentReserve;
-         Peb->HeapSegmentCommit = MmHeapSegmentCommit;
-         Peb->HeapDeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
-         Peb->HeapDeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
-         Peb->CriticalSectionTimeout = MmCriticalSectionTimeout;
-         Peb->MinimumStackCommit = MmMinimumStackCommitInBytes;
-         */
+        Peb->HeapSegmentReserve = MmHeapSegmentReserve;
+        Peb->HeapSegmentCommit = MmHeapSegmentCommit;
+        Peb->HeapDeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
+        Peb->HeapDeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
+        Peb->CriticalSectionTimeout = MmCriticalSectionTimeout;
+        Peb->MinimumStackCommit = MmMinimumStackCommitInBytes;
         Peb->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB)) / sizeof(PVOID);
         Peb->ProcessHeaps = (PVOID*)(Peb + 1);
 
         //
         // Session ID
         //
-        if (Process->Session) Peb->SessionId = 0; // MmGetSessionId(Process);
+        if (Process->Session) Peb->SessionId = MmGetSessionId(Process);
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
@@ -904,6 +936,8 @@ NTAPI
 MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess)
 {
     PMMPFN Pfn1;
+    PMMPTE sysPte;
+    MMPTE tempPte;
 
     /* Setup some bogus list data */
     MmWorkingSetList->LastEntry = CurrentProcess->Vm.MinimumWorkingSetSize;
@@ -923,6 +957,12 @@ MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess)
     Pfn1 = MiGetPfnEntry(CurrentProcess->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT);
     ASSERT(Pfn1->u4.PteFrame == MiGetPfnEntryIndex(Pfn1));
     Pfn1->u1.Event = (PKEVENT)CurrentProcess;
+
+    /* Map the process working set in kernel space */
+    sysPte = MiReserveSystemPtes(1, SystemPteSpace);
+    MI_MAKE_HARDWARE_PTE_KERNEL(&tempPte, sysPte, MM_READWRITE, CurrentProcess->WorkingSetPage);
+    MI_WRITE_VALID_PTE(sysPte, tempPte);
+    CurrentProcess->Vm.VmWorkingSetList = MiPteToAddress(sysPte);
 }
 
 NTSTATUS
@@ -1098,6 +1138,9 @@ MmInitializeHandBuiltProcess(IN PEPROCESS Process,
     ASSERT(Process->VadRoot.NumberGenericTableElements == 0);
     Process->VadRoot.BalancedRoot.u1.Parent = &Process->VadRoot.BalancedRoot;
 
+    /* Use idle process Working set */
+    Process->Vm.VmWorkingSetList = PsGetCurrentProcess()->Vm.VmWorkingSetList;
+
     /* Done */
     Process->HasAddressSpace = TRUE;//??
     return STATUS_SUCCESS;
@@ -1220,6 +1263,8 @@ MmCreateProcessAddressSpace(IN ULONG MinWs,
     /* Now write the PTE/PDE entry for the working set list index itself */
     TempPte = ValidKernelPte;
     TempPte.u.Hard.PageFrameNumber = WsListIndex;
+    /* Hyperspace is local */
+    MI_MAKE_LOCAL_PAGE(&TempPte);
     PdeOffset = MiAddressToPteOffset(MmWorkingSetList);
     HyperTable[PdeOffset] = TempPte;
 
@@ -1273,6 +1318,9 @@ MmCreateProcessAddressSpace(IN ULONG MinWs,
 
     /* Let go of the system PTE */
     MiReleaseSystemPtes(PointerPte, 1, SystemPteSpace);
+
+    /* Add the process to the session */
+    MiSessionAddProcess(Process);
     return TRUE;
 }
 #endif
@@ -1288,11 +1336,16 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
     /* Only support this */
     ASSERT(Process->AddressSpaceInitialized == 2);
 
+    /* Remove from the session */
+    MiSessionRemoveProcess();
+
     /* Lock the process address space from changes */
     MmLockAddressSpace(&Process->Vm);
+    MiLockProcessWorkingSetUnsafe(Process, Thread);
 
     /* VM is deleted now */
     Process->VmDeleted = TRUE;
+    MiUnlockProcessWorkingSetUnsafe(Process, Thread);
 
     /* Enumerate the VADs */
     VadTree = &Process->VadRoot;
@@ -1302,7 +1355,7 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
         Vad = (PMMVAD)VadTree->BalancedRoot.RightChild;
 
         /* Lock the working set */
-        MiLockProcessWorkingSet(Process, Thread);
+        MiLockProcessWorkingSetUnsafe(Process, Thread);
 
         /* Remove this VAD from the tree */
         ASSERT(VadTree->NumberGenericTableElements >= 1);
@@ -1325,7 +1378,7 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
                                      Vad);
 
             /* Release the working set */
-            MiUnlockProcessWorkingSet(Process, Thread);
+            MiUnlockProcessWorkingSetUnsafe(Process, Thread);
         }
 
         /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */
@@ -1340,6 +1393,17 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
         ExFreePool(Vad);
     }
 
+    /* Lock the working set */
+    MiLockProcessWorkingSetUnsafe(Process, Thread);
+    ASSERT(Process->CloneRoot == NULL);
+    ASSERT(Process->PhysicalVadRoot == NULL);
+
+    /* Delete the shared user data section */
+    MiDeleteVirtualAddresses(USER_SHARED_DATA, USER_SHARED_DATA, NULL);
+
+    /* Release the working set */
+    MiUnlockProcessWorkingSetUnsafe(Process, Thread);
+
     /* Release the address space */
     MmUnlockAddressSpace(&Process->Vm);
 }
@@ -1354,14 +1418,6 @@ MmDeleteProcessAddressSpace2(IN PEPROCESS Process)
 
     //ASSERT(Process->CommitCharge == 0);
 
-    /* Delete the shared user data section (Should be done in clean, not delete) */
-    ASSERT(MmHighestUserAddress > (PVOID)USER_SHARED_DATA);
-    KeAttachProcess(&Process->Pcb);
-    //DPRINT1("Killing shared user data page no longer works -- has someone changed ARM3 in a way to make this fail now?\n");
-    //MiDeleteVirtualAddresses(USER_SHARED_DATA, USER_SHARED_DATA, NULL);
-    //DPRINT1("Done\n");
-    KeDetachProcess();
-    
     /* Acquire the PFN lock */
     OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
 
@@ -1377,7 +1433,8 @@ MmDeleteProcessAddressSpace2(IN PEPROCESS Process)
         MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
         MiDecrementShareCount(Pfn1, Process->WorkingSetPage);
         ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
-            
+        MiReleaseSystemPtes(MiAddressToPte(Process->Vm.VmWorkingSetList), 1, SystemPteSpace);
+
         /* Now map hyperspace and its page table */
         PageFrameIndex = Process->Pcb.DirectoryTableBase[1] >> PAGE_SHIFT;
         Pfn1 = MiGetPfnEntry(PageFrameIndex);
@@ -1395,10 +1452,9 @@ MmDeleteProcessAddressSpace2(IN PEPROCESS Process)
         MI_SET_PFN_DELETED(Pfn1);
         MiDecrementShareCount(Pfn1, PageFrameIndex);
         MiDecrementShareCount(Pfn1, PageFrameIndex);
-        
-        /* HACK: In Richard's original patch this ASSERT did work */
-        //DPRINT1("Ref count: %lx %lx\n", Pfn1->u3.e2.ReferenceCount, Pfn1->u2.ShareCount);
-        //ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
+
+        /* Page table is now dead. Bye bye... */
+        ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
     }
     else
     {
@@ -1409,9 +1465,9 @@ MmDeleteProcessAddressSpace2(IN PEPROCESS Process)
     /* Release the PFN lock */
     KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
 
-    /* No support for sessions yet */
-    ASSERT(Process->Session == 0);
-    
+    /* Drop a reference on the session */
+    if (Process->Session) MiReleaseProcessReferenceToSessionDataPage(Process->Session);
+
     /* Clear out the PDE pages */
     Process->Pcb.DirectoryTableBase[0] = 0;
     Process->Pcb.DirectoryTableBase[1] = 0;
@@ -1419,7 +1475,11 @@ MmDeleteProcessAddressSpace2(IN PEPROCESS Process)
 
 /* SESSION CODE TO MOVE TO SESSION.C ******************************************/
 
+PMM_SESSION_SPACE MmSessionSpace;
+PFN_NUMBER MiSessionDataPages, MiSessionTagPages, MiSessionTagSizePages;
+PFN_NUMBER MiSessionBigPoolPages, MiSessionCreateCharge;
 KGUARDED_MUTEX MiSessionIdMutex;
+LONG MmSessionDataPages;
 PRTL_BITMAP MiSessionIdBitmap;
 volatile LONG MiSessionLeaderExists;
 
@@ -1427,19 +1487,40 @@ VOID
 NTAPI
 MiInitializeSessionIds(VOID)
 {
-    /* FIXME: Other stuff should go here */
+    ULONG Size, BitmapSize;
+    PFN_NUMBER TotalPages;
+
+    /* Setup the total number of data pages needed for the structure */
+    TotalPages = MI_SESSION_DATA_PAGES_MAXIMUM;
+    MiSessionDataPages = ROUND_TO_PAGES(sizeof(MM_SESSION_SPACE)) >> PAGE_SHIFT;
+    ASSERT(MiSessionDataPages <= MI_SESSION_DATA_PAGES_MAXIMUM - 3);
+    TotalPages -= MiSessionDataPages;
+
+    /* Setup the number of pages needed for session pool tags */
+    MiSessionTagSizePages = 2;
+    MiSessionBigPoolPages = 1;
+    MiSessionTagPages = MiSessionTagSizePages + MiSessionBigPoolPages;
+    ASSERT(MiSessionTagPages <= TotalPages);
+    ASSERT(MiSessionTagPages < MI_SESSION_TAG_PAGES_MAXIMUM);
+
+    /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */
+    MiSessionCreateCharge = 1 + MiSessionDataPages + MiSessionTagPages;
 
     /* Initialize the lock */
     KeInitializeGuardedMutex(&MiSessionIdMutex);
 
     /* Allocate the bitmap */
+    Size = MI_INITIAL_SESSION_IDS;
+    BitmapSize = ((Size + 31) / 32) * sizeof(ULONG);
     MiSessionIdBitmap = ExAllocatePoolWithTag(PagedPool,
-                                              sizeof(RTL_BITMAP) + ((64 + 31) / 32) * 4,
+                                              sizeof(RTL_BITMAP) + BitmapSize,
                                               '  mM');
     if (MiSessionIdBitmap)
     {
         /* Free all the bits */
-        RtlInitializeBitMap(MiSessionIdBitmap, (PVOID)(MiSessionIdBitmap + 1), 64);
+        RtlInitializeBitMap(MiSessionIdBitmap,
+                            (PVOID)(MiSessionIdBitmap + 1),
+                            Size);
         RtlClearAllBits(MiSessionIdBitmap);
     }
     else
@@ -1465,12 +1546,281 @@ MiSessionLeader(IN PEPROCESS Process)
     KeReleaseQueuedSpinLock(LockQueueExpansionLock, OldIrql);
 }
 
+ULONG
+NTAPI
+MmGetSessionId(IN PEPROCESS Process)
+{
+    PMM_SESSION_SPACE SessionGlobal;
+
+    /* The session leader is always session zero */
+    if (Process->Vm.Flags.SessionLeader == 1) return 0;
+
+    /* Otherwise, get the session global, and read the session ID from it */
+    SessionGlobal = (PMM_SESSION_SPACE)Process->Session;
+    if (!SessionGlobal) return 0;
+    return SessionGlobal->SessionId;
+}
+
+VOID
+NTAPI
+MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal)
+{
+    ULONG i, SessionId;
+    PMMPTE PointerPte;
+    PFN_NUMBER PageFrameIndex[MI_SESSION_DATA_PAGES_MAXIMUM];
+    PMMPFN Pfn1;
+    KIRQL OldIrql;
+
+    /* Is there more than just this reference? If so, bail out */
+    if (InterlockedDecrement(&SessionGlobal->ProcessReferenceToSession)) return;
+
+    /* Get the session ID */
+    SessionId = SessionGlobal->SessionId;
+    DPRINT1("Last process in session %lu going down!!!\n", SessionId);
+
+    /* Free the session page tables */
+#ifndef _M_AMD64
+    ExFreePoolWithTag(SessionGlobal->PageTables, 'tHmM');
+#endif
+    ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal));
+
+    /* Capture the data page PFNs */
+    PointerPte = MiAddressToPte(SessionGlobal);
+    for (i = 0; i < MiSessionDataPages; i++)
+    {
+        PageFrameIndex[i] = PFN_FROM_PTE(PointerPte + i);
+    }
+
+    /* Release them */
+    MiReleaseSystemPtes(PointerPte, MiSessionDataPages, SystemPteSpace);
+
+    /* Mark them as deleted */
+    for (i = 0; i < MiSessionDataPages; i++)
+    {
+        Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
+        MI_SET_PFN_DELETED(Pfn1);
+    }
+
+    /* Loop every data page and drop a reference count */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    for (i = 0; i < MiSessionDataPages; i++)
+    {
+        /* Sanity check that the page is correct, then decrement it */
+        Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
+        ASSERT(Pfn1->u2.ShareCount == 1);
+        ASSERT(Pfn1->u3.e2.ReferenceCount == 1);
+        MiDecrementShareCount(Pfn1, PageFrameIndex[i]);
+    }
+
+    /* Done playing with pages, release the lock */
+    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+    /* Decrement the number of data pages */
+    InterlockedDecrement(&MmSessionDataPages);
+
+    /* Free this session ID from the session bitmap */
+    KeAcquireGuardedMutex(&MiSessionIdMutex);
+    ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId));
+    RtlClearBit(MiSessionIdBitmap, SessionId);
+    KeReleaseGuardedMutex(&MiSessionIdMutex);
+}
+
+VOID
+NTAPI
+MiSessionRemoveProcess(VOID)
+{
+    PEPROCESS CurrentProcess = PsGetCurrentProcess();
+
+    /* If the process isn't already in a session, or if it's the leader... */
+    if (!(CurrentProcess->Flags & PSF_PROCESS_IN_SESSION_BIT) ||
+        (CurrentProcess->Vm.Flags.SessionLeader))
+    {
+        /* Then there's nothing to do */
+        return;
+    }
+
+    /* Sanity check */
+    ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
+
+    /* Remove the process from the list ,and dereference the session */
+    // DO NOT ENABLE THIS UNLESS YOU FIXED THE NP POOL CORRUPTION THAT IT CAUSES!!!
+    //RemoveEntryList(&CurrentProcess->SessionProcessLinks);
+    //MiDereferenceSession();
+}
+
+VOID
+NTAPI
+MiSessionAddProcess(IN PEPROCESS NewProcess)
+{
+    PMM_SESSION_SPACE SessionGlobal;
+
+    /* The current process must already be in a session */
+    if (!(PsGetCurrentProcess()->Flags & PSF_PROCESS_IN_SESSION_BIT)) return;
+
+    /* Sanity check */
+    ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
+
+    /* Get the global session */
+    SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
+
+    /* Increment counters */
+    InterlockedIncrement((PLONG)&SessionGlobal->ReferenceCount);
+    InterlockedIncrement(&SessionGlobal->ResidentProcessCount);
+    InterlockedIncrement(&SessionGlobal->ProcessReferenceToSession);
+
+    /* Set the session pointer */
+    ASSERT(NewProcess->Session == NULL);
+    NewProcess->Session = SessionGlobal;
+
+    /* Insert it into the process list */
+    // DO NOT ENABLE THIS UNLESS YOU FIXED THE NP POOL CORRUPTION THAT IT CAUSES!!!
+    //InsertTailList(&SessionGlobal->ProcessList, &NewProcess->SessionProcessLinks);
+
+    /* Set the flag */
+    PspSetProcessFlag(NewProcess, PSF_PROCESS_IN_SESSION_BIT);
+}
+
+NTSTATUS
+NTAPI
+MiSessionInitializeWorkingSetList(VOID)
+{
+    KIRQL OldIrql;
+    PMMPTE PointerPte, PointerPde;
+    MMPTE TempPte;
+    ULONG Color, Index;
+    PFN_NUMBER PageFrameIndex;
+    PMM_SESSION_SPACE SessionGlobal;
+    BOOLEAN AllocatedPageTable;
+    PMMWSL WorkingSetList;
+
+    /* Get pointers to session global and the session working set list */
+    SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
+    WorkingSetList = (PMMWSL)MiSessionSpaceWs;
+
+    /* Fill out the two pointers */
+    MmSessionSpace->Vm.VmWorkingSetList = WorkingSetList;
+    MmSessionSpace->Wsle = (PMMWSLE)WorkingSetList->UsedPageTableEntries;
+
+    /* Get the PDE for the working set, and check if it's already allocated */
+    PointerPde = MiAddressToPde(WorkingSetList);
+    if (PointerPde->u.Hard.Valid == 1)
+    {
+        /* Nope, we'll have to do it */
+        ASSERT(PointerPde->u.Hard.Global == 0);
+        AllocatedPageTable = FALSE;
+    }
+    else
+    {
+        /* Yep, that makes our job easier */
+        AllocatedPageTable = TRUE;
+    }
+
+    /* Get the PTE for the working set */
+    PointerPte = MiAddressToPte(WorkingSetList);
+
+    /* Initialize the working set lock, and lock the PFN database */
+    ExInitializePushLock(&SessionGlobal->Vm.WorkingSetMutex);
+    //MmLockPageableSectionByHandle(ExPageLockHandle);
+    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+    /* Check if we need a page table */
+    if (AllocatedPageTable == TRUE)
+    {
+        /* Get a zeroed colored zero page */
+        Color = MI_GET_NEXT_COLOR();
+        PageFrameIndex = MiRemoveZeroPageSafe(Color);
+        if (!PageFrameIndex)
+        {
+            /* No zero pages, grab a free one */
+            PageFrameIndex = MiRemoveAnyPage(Color);
+
+            /* Zero it outside the PFN lock */
+            KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+            MiZeroPhysicalPage(PageFrameIndex);
+            OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+        }
+
+        /* Write a valid PDE for it */
+        TempPte.u.Long = ValidKernelPdeLocal.u.Long;
+        TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
+        MI_WRITE_VALID_PTE(PointerPde, TempPte);
+
+        /* Add this into the list */
+        Index = ((ULONG_PTR)WorkingSetList - (ULONG_PTR)MmSessionBase) >> 22;
+#ifndef _M_AMD64
+        MmSessionSpace->PageTables[Index] = TempPte;
+#endif
+        /* Initialize the page directory page, and now zero the working set list itself */
+        MiInitializePfnForOtherProcess(PageFrameIndex,
+                                       PointerPde,
+                                       MmSessionSpace->SessionPageDirectoryIndex);
+        KeZeroPages(PointerPte, PAGE_SIZE);
+    }
+
+    /* Get a zeroed colored zero page */
+    Color = MI_GET_NEXT_COLOR();
+    PageFrameIndex = MiRemoveZeroPageSafe(Color);
+    if (!PageFrameIndex)
+    {
+        /* No zero pages, grab a free one */
+        PageFrameIndex = MiRemoveAnyPage(Color);
+
+        /* Zero it outside the PFN lock */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+        MiZeroPhysicalPage(PageFrameIndex);
+        OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    }
+
+    /* Write a valid PTE for it */
+    TempPte.u.Long = ValidKernelPteLocal.u.Long;
+    TempPte.u.Hard.Dirty = TRUE;
+    TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
+
+    /* Initialize the working set list page */
+    MiInitializePfnAndMakePteValid(PageFrameIndex, PointerPte, TempPte);
+
+    /* Now we can release the PFN database lock */
+    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+    /* Fill out the working set structure */
+    MmSessionSpace->Vm.Flags.SessionSpace = 1;
+    MmSessionSpace->Vm.MinimumWorkingSetSize = 20;
+    MmSessionSpace->Vm.MaximumWorkingSetSize = 384;
+    WorkingSetList->LastEntry = 20;
+    WorkingSetList->HashTable = NULL;
+    WorkingSetList->HashTableSize = 0;
+    WorkingSetList->Wsle = MmSessionSpace->Wsle;
+
+    /* FIXME: Handle list insertions */
+    ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
+    ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
+    ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Flink == NULL);
+    ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Blink == NULL);
+
+    /* All done, return */
+    //MmUnlockPageableImageSection(ExPageLockHandle);
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS
 NTAPI
 MiSessionCreateInternal(OUT PULONG SessionId)
 {
     PEPROCESS Process = PsGetCurrentProcess();
-    ULONG NewFlags, Flags;
+    ULONG NewFlags, Flags, Size, i, Color;
+    KIRQL OldIrql;
+    PMMPTE PointerPte, PageTables, SessionPte;
+    PMMPDE PointerPde;
+    PMM_SESSION_SPACE SessionGlobal;
+    MMPTE TempPte;
+    NTSTATUS Status;
+    BOOLEAN Result;
+    PFN_NUMBER SessionPageDirIndex;
+    PFN_NUMBER TagPage[MI_SESSION_TAG_PAGES_MAXIMUM];
+    PFN_NUMBER DataPage[MI_SESSION_DATA_PAGES_MAXIMUM];
+
+    /* This should not exist yet */
+    ASSERT(MmIsAddressValid(MmSessionSpace) == FALSE);
 
     /* Loop so we can set the session-is-creating flag */
     Flags = Process->Flags;
@@ -1497,19 +1847,173 @@ MiSessionCreateInternal(OUT PULONG SessionId)
     /* Now we should own the flag */
     ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
 
-    /* Allocate a new Session ID */
+    /*
+     * Session space covers everything from 0xA0000000 to 0xC0000000.
+     * Allocate enough page tables to describe the entire region
+     */
+    Size = (0x20000000 / PDE_MAPPED_VA) * sizeof(MMPTE);
+    PageTables = ExAllocatePoolWithTag(NonPagedPool, Size, 'tHmM');
+    ASSERT(PageTables != NULL);
+    RtlZeroMemory(PageTables, Size);
+
+    /* Lock the session ID creation mutex */
     KeAcquireGuardedMutex(&MiSessionIdMutex);
+
+    /* Allocate a new Session ID */
     *SessionId = RtlFindClearBitsAndSet(MiSessionIdBitmap, 1, 0);
     if (*SessionId == 0xFFFFFFFF)
     {
+        /* We ran out of session IDs, we should expand */
         DPRINT1("Too many sessions created. Expansion not yet supported\n");
+        ExFreePoolWithTag(PageTables, 'tHmM');
         return STATUS_NO_MEMORY;
     }
+
+    /* Unlock the session ID creation mutex */
     KeReleaseGuardedMutex(&MiSessionIdMutex);
 
+    /* Reserve the global PTEs */
+    SessionPte = MiReserveSystemPtes(MiSessionDataPages, SystemPteSpace);
+    ASSERT(SessionPte != NULL);
+
+    /* Acquire the PFN lock while we set everything up */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+    /* Loop the global PTEs */
+    TempPte.u.Long = ValidKernelPte.u.Long;
+    for (i = 0; i < MiSessionDataPages; i++)
+    {
+        /* Get a zeroed colored zero page */
+        Color = MI_GET_NEXT_COLOR();
+        DataPage[i] = MiRemoveZeroPageSafe(Color);
+        if (!DataPage[i])
+        {
+            /* No zero pages, grab a free one */
+            DataPage[i] = MiRemoveAnyPage(Color);
+
+            /* Zero it outside the PFN lock */
+            KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+            MiZeroPhysicalPage(DataPage[i]);
+            OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+        }
+
+        /* Fill the PTE out */
+        TempPte.u.Hard.PageFrameNumber = DataPage[i];
+        MI_WRITE_VALID_PTE(SessionPte + i, TempPte);
+    }
+
+    /* Set the pointer to global space */
+    SessionGlobal = MiPteToAddress(SessionPte);
+
+    /* Get a zeroed colored zero page */
+    Color = MI_GET_NEXT_COLOR();
+    SessionPageDirIndex = MiRemoveZeroPageSafe(Color);
+    if (!SessionPageDirIndex)
+    {
+        /* No zero pages, grab a free one */
+        SessionPageDirIndex = MiRemoveAnyPage(Color);
+
+        /* Zero it outside the PFN lock */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+        MiZeroPhysicalPage(SessionPageDirIndex);
+        OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    }
+
+    /* Fill the PTE out */
+    TempPte.u.Long = ValidKernelPdeLocal.u.Long;
+    TempPte.u.Hard.PageFrameNumber = SessionPageDirIndex;
+
+    /* Setup, allocate, fill out the MmSessionSpace PTE */
+    PointerPde = MiAddressToPde(MmSessionSpace);
+    ASSERT(PointerPde->u.Long == 0);
+    MI_WRITE_VALID_PTE(PointerPde, TempPte);
+    MiInitializePfnForOtherProcess(SessionPageDirIndex,
+                                   PointerPde,
+                                   SessionPageDirIndex);
+    ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex)->u1.WsIndex == 0);
+
+     /* Loop all the local PTEs for it */
+    TempPte.u.Long = ValidKernelPteLocal.u.Long;
+    PointerPte = MiAddressToPte(MmSessionSpace);
+    for (i = 0; i < MiSessionDataPages; i++)
+    {
+        /* And fill them out */
+        TempPte.u.Hard.PageFrameNumber = DataPage[i];
+        MiInitializePfnAndMakePteValid(DataPage[i], PointerPte + i, TempPte);
+        ASSERT(MI_PFN_ELEMENT(DataPage[i])->u1.WsIndex == 0);
+    }
+
+     /* Finally loop all of the session pool tag pages */
+    for (i = 0; i < MiSessionTagPages; i++)
+    {
+        /* Grab a zeroed colored page */
+        Color = MI_GET_NEXT_COLOR();
+        TagPage[i] = MiRemoveZeroPageSafe(Color);
+        if (!TagPage[i])
+        {
+            /* No zero pages, grab a free one */
+            TagPage[i] = MiRemoveAnyPage(Color);
+
+            /* Zero it outside the PFN lock */
+            KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+            MiZeroPhysicalPage(TagPage[i]);
+            OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+        }
+
+        /* Fill the PTE out */
+        TempPte.u.Hard.PageFrameNumber = TagPage[i];
+        MiInitializePfnAndMakePteValid(TagPage[i],
+                                       PointerPte + MiSessionDataPages + i,
+                                       TempPte);
+    }
+
+    /* PTEs have been setup, release the PFN lock */
+    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+    /* Fill out the session space structure now */
+    MmSessionSpace->GlobalVirtualAddress = SessionGlobal;
+    MmSessionSpace->ReferenceCount = 1;
+    MmSessionSpace->ResidentProcessCount = 1;
+    MmSessionSpace->u.LongFlags = 0;
+    MmSessionSpace->SessionId = *SessionId;
+    MmSessionSpace->LocaleId = PsDefaultSystemLocaleId;
+    MmSessionSpace->SessionPageDirectoryIndex = SessionPageDirIndex;
+    MmSessionSpace->Color = Color;
+    MmSessionSpace->NonPageablePages = MiSessionCreateCharge;
+    MmSessionSpace->CommittedPages = MiSessionCreateCharge;
+#ifndef _M_AMD64
+    MmSessionSpace->PageTables = PageTables;
+    MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde;
+#endif
+    InitializeListHead(&MmSessionSpace->ImageList);
+    DPRINT1("Session %lu is ready to go: 0x%p 0x%p, %lx 0x%p\n",
+            *SessionId, MmSessionSpace, SessionGlobal, SessionPageDirIndex, PageTables);
+
+    /* Initialize session pool */
+    //Status = MiInitializeSessionPool();
+    Status = STATUS_SUCCESS;
+    ASSERT(NT_SUCCESS(Status) == TRUE);
+
+    /* Initialize system space */
+    Result = MiInitializeSystemSpaceMap(&SessionGlobal->Session);
+    ASSERT(Result == TRUE);
+
+    /* Initialize the process list, make sure the workign set list is empty */
+    ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
+    ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
+    InitializeListHead(&SessionGlobal->ProcessList);
+
     /* We're done, clear the flag */
     ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
     PspClearProcessFlag(Process, PSF_SESSION_CREATION_UNDERWAY_BIT);
+
+    /* Insert the process into the session  */
+    ASSERT(Process->Session == NULL);
+    ASSERT(SessionGlobal->ProcessReferenceToSession == 0);
+    SessionGlobal->ProcessReferenceToSession = 1;
+
+    /* We're done */
+    InterlockedIncrement(&MmSessionDataPages);
     return STATUS_SUCCESS;
 }
 
@@ -1543,15 +2047,33 @@ MmSessionCreate(OUT PULONG SessionId)
         MiSessionLeader(Process);
     }
 
-    /* FIXME: Actually create a session */
+    /* Create the session */
     KeEnterCriticalRegion();
     Status = MiSessionCreateInternal(SessionId);
+    if (!NT_SUCCESS(Status))
+    {
+        KeLeaveCriticalRegion();
+        return Status;
+    }
+
+    /* Set up the session working set */
+    Status = MiSessionInitializeWorkingSetList();
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail */
+        //MiDereferenceSession();
+        ASSERT(FALSE);
+        KeLeaveCriticalRegion();
+        return Status;
+    }
+
+    /* All done */
     KeLeaveCriticalRegion();
 
     /* Set and assert the flags, and return */
+    MmSessionSpace->u.Flags.Initialized = 1;
     PspSetProcessFlag(Process, PSF_PROCESS_IN_SESSION_BIT);
     ASSERT(MiSessionLeaderExists == 1);
-    if (NT_SUCCESS(Status)) DPRINT1("New session created: %lx\n", *SessionId);
     return Status;
 }