X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=reactos%2Fntoskrnl%2Fmm%2FARM3%2Fprocsup.c;h=ae5d496e9f9cabaf38cbd57e09e2a7a1f8157832;hp=f40e51712568ab4fecc0012a604eab83324f67bb;hb=24a14abf27698d4d27005910ce3cc9286640a27c;hpb=c6b26fa4525432d8011285c435fab6ee32c0839a diff --git a/reactos/ntoskrnl/mm/ARM3/procsup.c b/reactos/ntoskrnl/mm/ARM3/procsup.c index f40e5171256..ae5d496e9f9 100644 --- a/reactos/ntoskrnl/mm/ARM3/procsup.c +++ b/reactos/ntoskrnl/mm/ARM3/procsup.c @@ -19,24 +19,25 @@ ULONG MmProcessColorSeed = 0x12345678; PMMWSL MmWorkingSetList; +ULONG MmMaximumDeadKernelStacks = 5; +SLIST_HEADER MmDeadStackSListHead; /* PRIVATE FUNCTIONS **********************************************************/ VOID NTAPI -MiRosTakeOverPebTebRanges(IN PEPROCESS Process) +MiRosTakeOverSharedUserPage(IN PEPROCESS Process) { NTSTATUS Status; PMEMORY_AREA MemoryArea; PHYSICAL_ADDRESS BoundaryAddressMultiple; - PVOID AllocatedBase = (PVOID)USER_SHARED_DATA; + PVOID AllocatedBase = (PVOID)MM_SHARED_USER_DATA_VA; BoundaryAddressMultiple.QuadPart = 0; Status = MmCreateMemoryArea(&Process->Vm, MEMORY_AREA_OWNED_BY_ARM3, &AllocatedBase, - ((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - 1) - - (ULONG_PTR)USER_SHARED_DATA, + PAGE_SIZE, PAGE_READWRITE, &MemoryArea, TRUE, @@ -117,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 */ @@ -134,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); @@ -146,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); @@ -194,14 +199,17 @@ 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); MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree); + /* Delete the pages */ + MiDeleteVirtualAddresses((ULONG_PTR)Teb, TebEnd, NULL); + /* Release the working set */ - MiUnlockProcessWorkingSet(Process, Thread); + MiUnlockProcessWorkingSetUnsafe(Process, Thread); /* Remove the VAD */ ExFreePool(Vad); @@ -220,8 +228,9 @@ MmDeleteKernelStack(IN PVOID StackBase, IN BOOLEAN GuiStack) { PMMPTE PointerPte; - PFN_NUMBER StackPages, PageFrameNumber;//, PageTableFrameNumber; - PMMPFN Pfn1;//, Pfn2; + PFN_NUMBER PageFrameNumber, PageTableFrameNumber; + PFN_COUNT StackPages; + PMMPFN Pfn1, Pfn2; ULONG i; KIRQL OldIrql; @@ -231,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 // @@ -253,14 +276,14 @@ MmDeleteKernelStack(IN PVOID StackBase, /* Get the PTE's page */ PageFrameNumber = PFN_FROM_PTE(PointerPte); Pfn1 = MiGetPfnEntry(PageFrameNumber); -#if 0 // 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); /* Remove a shared reference, since the page is going away */ MiDecrementShareCount(Pfn2, PageTableFrameNumber); -#endif + /* Set the special pending delete marker */ MI_SET_PFN_DELETED(Pfn1); @@ -293,13 +316,14 @@ NTAPI MmCreateKernelStack(IN BOOLEAN GuiStack, IN UCHAR Node) { - PFN_NUMBER StackPtes, StackPages; + PFN_COUNT StackPtes, StackPages; PMMPTE PointerPte, StackPte; PVOID BaseAddress; MMPTE TempPte, InvalidPte; KIRQL OldIrql; PFN_NUMBER PageFrameIndex; ULONG i; + PMMPFN Pfn1; // // Calculate pages needed @@ -315,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 // @@ -542,9 +581,7 @@ MmGetSessionLocaleId(VOID) // // Get the Locale ID // -#if ROS_HAS_SESSIONS return ((PMM_SESSION_SPACE)Process->Session)->LocaleId; -#endif } } @@ -577,12 +614,6 @@ MmCreatePeb(IN PEPROCESS Process, // KeAttachProcess(&Process->Pcb); - // - // Allocate the PEB - // - Status = MiCreatePebOrTeb(Process, sizeof(PEB), (PULONG_PTR)&Peb); - ASSERT(NT_SUCCESS(Status)); - // // Map NLS Tables // @@ -596,7 +627,25 @@ MmCreatePeb(IN PEPROCESS Process, ViewShare, MEM_TOP_DOWN, PAGE_READONLY); - if (!NT_SUCCESS(Status)) return Status; + DPRINT("NLS Tables at: %p\n", TableBase); + if (!NT_SUCCESS(Status)) + { + /* Cleanup and exit */ + KeDetachProcess(); + return Status; + } + + // + // Allocate the PEB + // + Status = MiCreatePebOrTeb(Process, sizeof(PEB), (PULONG_PTR)&Peb); + DPRINT("PEB at: %p\n", Peb); + if (!NT_SUCCESS(Status)) + { + /* Cleanup and exit */ + KeDetachProcess(); + return Status; + } // // Use SEH in case we can't load the PEB @@ -636,7 +685,7 @@ MmCreatePeb(IN PEPROCESS Process, // Heap and Debug Data // Peb->NumberOfProcessors = KeNumberProcessors; - Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL ? TRUE : FALSE); + Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL); Peb->NtGlobalFlag = NtGlobalFlag; /*Peb->HeapSegmentReserve = MmHeapSegmentReserve; Peb->HeapSegmentCommit = MmHeapSegmentCommit; @@ -651,7 +700,7 @@ MmCreatePeb(IN PEPROCESS Process, // // Session ID // - if (Process->Session) Peb->SessionId = 0; // MmGetSessionId(Process); + MmGetSessionId(Process); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { @@ -730,34 +779,20 @@ MmCreatePeb(IN PEPROCESS Process, Peb->OSMinorVersion = (NtHeaders->OptionalHeader.Win32VersionValue >> 8) & 0xFF; Peb->OSBuildNumber = (NtHeaders->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF; Peb->OSPlatformId = (NtHeaders->OptionalHeader.Win32VersionValue >> 30) ^ 2; - } - // - // Process the image config data overrides if specfied - // - if (ImageConfigData != NULL) - { - // - // Process CSD version override - // - if (ImageConfigData->CSDVersion) + /* Process CSD version override */ + if ((ImageConfigData) && (ImageConfigData->CSDVersion)) { - // - // Set new data - // + /* Take the value from the image configuration directory */ Peb->OSCSDVersion = ImageConfigData->CSDVersion; } + } - // - // Process affinity mask ovverride - // - if (ImageConfigData->ProcessAffinityMask) - { - // - // Set new data - // - ProcessAffinityMask = ImageConfigData->ProcessAffinityMask; - } + /* Process optional process affinity mask override */ + if ((ImageConfigData) && (ImageConfigData->ProcessAffinityMask)) + { + /* Take the value from the image configuration directory */ + ProcessAffinityMask = ImageConfigData->ProcessAffinityMask; } // @@ -767,6 +802,7 @@ MmCreatePeb(IN PEPROCESS Process, // // Force it to use CPU 0 // + /* FIXME: this should use the MmRotatingUniprocessorNumber */ Peb->ImageProcessAffinityMask = 0; } else @@ -831,7 +867,11 @@ MmCreateTeb(IN PEPROCESS Process, // // Set TIB Data // +#ifdef _M_AMD64 + Teb->NtTib.ExceptionList = NULL; +#else Teb->NtTib.ExceptionList = EXCEPTION_CHAIN_END; +#endif Teb->NtTib.Self = (PNT_TIB)Teb; // @@ -897,6 +937,8 @@ NTAPI MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess) { PMMPFN Pfn1; + PMMPTE sysPte; + MMPTE tempPte; /* Setup some bogus list data */ MmWorkingSetList->LastEntry = CurrentProcess->Vm.MinimumWorkingSetSize; @@ -913,9 +955,15 @@ MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess) 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); + 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 @@ -963,13 +1011,23 @@ MmInitializeProcessAddressSpace(IN PEPROCESS Process, OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); /* Setup the PFN for the PDE base of this process */ +#ifdef _M_AMD64 + PointerPte = MiAddressToPte(PXE_BASE); +#else PointerPte = MiAddressToPte(PDE_BASE); +#endif PageFrameNumber = PFN_FROM_PTE(PointerPte); + ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE); MiInitializePfn(PageFrameNumber, PointerPte, TRUE); /* Do the same for hyperspace */ +#ifdef _M_AMD64 + PointerPde = MiAddressToPxe((PVOID)HYPER_SPACE); +#else PointerPde = MiAddressToPde(HYPER_SPACE); +#endif PageFrameNumber = PFN_FROM_PTE(PointerPde); + //ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE); // we're not lucky MiInitializePfn(PageFrameNumber, (PMMPTE)PointerPde, TRUE); /* Setup the PFN for the PTE for the working set */ @@ -992,7 +1050,7 @@ MmInitializeProcessAddressSpace(IN PEPROCESS Process, KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); /* Lock the VAD, ARM3-owned ranges away */ - MiRosTakeOverPebTebRanges(Process); + MiRosTakeOverSharedUserPage(Process); /* Check if there's a Section Object */ if (SectionObject) @@ -1081,6 +1139,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; @@ -1092,7 +1153,7 @@ INIT_FUNCTION MmInitializeHandBuiltProcess2(IN PEPROCESS Process) { /* Lock the VAD, ARM3-owned ranges away */ - MiRosTakeOverPebTebRanges(Process); + MiRosTakeOverSharedUserPage(Process); return STATUS_SUCCESS; } @@ -1114,7 +1175,7 @@ MmCreateProcessAddressSpace(IN ULONG MinWs, PMMPFN Pfn1; /* Choose a process color */ - Process->NextPageColor = RtlRandom(&MmProcessColorSeed); + Process->NextPageColor = (USHORT)RtlRandom(&MmProcessColorSeed); /* Setup the hyperspace lock */ KeInitializeSpinLock(&Process->HyperSpaceLock); @@ -1203,6 +1264,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; @@ -1256,6 +1319,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 @@ -1271,11 +1337,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; @@ -1285,7 +1356,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); @@ -1308,7 +1379,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 */ @@ -1323,10 +1394,712 @@ 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); } +VOID +NTAPI +MmDeleteProcessAddressSpace2(IN PEPROCESS Process) +{ + PMMPFN Pfn1, Pfn2; + KIRQL OldIrql; + PFN_NUMBER PageFrameIndex; + + //ASSERT(Process->CommitCharge == 0); + + /* Acquire the PFN lock */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Check for fully initialized process */ + if (Process->AddressSpaceInitialized == 2) + { + /* Map the working set page and its page table */ + Pfn1 = MiGetPfnEntry(Process->WorkingSetPage); + Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame); + + /* Nuke it */ + MI_SET_PFN_DELETED(Pfn1); + 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); + Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame); + + /* Nuke it */ + MI_SET_PFN_DELETED(Pfn1); + MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame); + MiDecrementShareCount(Pfn1, PageFrameIndex); + ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress)); + + /* Finally, nuke the PDE itself */ + PageFrameIndex = Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT; + Pfn1 = MiGetPfnEntry(PageFrameIndex); + MI_SET_PFN_DELETED(Pfn1); + MiDecrementShareCount(Pfn1, PageFrameIndex); + MiDecrementShareCount(Pfn1, PageFrameIndex); + + /* Page table is now dead. Bye bye... */ + ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress)); + } + else + { + /* A partly-initialized process should never exit through here */ + ASSERT(FALSE); + } + + /* Release the PFN lock */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* 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; +} + +/* 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; + +VOID +NTAPI +MiInitializeSessionIds(VOID) +{ + 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) + BitmapSize, + ' mM'); + if (MiSessionIdBitmap) + { + /* Free all the bits */ + RtlInitializeBitMap(MiSessionIdBitmap, + (PVOID)(MiSessionIdBitmap + 1), + Size); + RtlClearAllBits(MiSessionIdBitmap); + } + else + { + /* Die if we couldn't allocate the bitmap */ + KeBugCheckEx(INSTALL_MORE_MEMORY, + MmNumberOfPhysicalPages, + MmLowestPhysicalPage, + MmHighestPhysicalPage, + 0x200); + } +} + +VOID +NTAPI +MiSessionLeader(IN PEPROCESS Process) +{ + KIRQL OldIrql; + + /* Set the flag while under the expansion lock */ + OldIrql = KeAcquireQueuedSpinLock(LockQueueExpansionLock); + Process->Vm.Flags.SessionLeader = TRUE; + 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 sessino %d going down!!!\n", SessionId); + + /* Free the session page tables */ + ExFreePool(SessionGlobal->PageTables); + 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 */ + 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 */ + 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; + MmSessionSpace->PageTables[Index] = TempPte; + + /* 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, 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; + while (TRUE) + { + /* Check if it's already set */ + if (Flags & PSF_SESSION_CREATION_UNDERWAY_BIT) + { + /* Bail out */ + DPRINT1("Lost session race\n"); + return STATUS_ALREADY_COMMITTED; + } + + /* Now try to set it */ + NewFlags = InterlockedCompareExchange((PLONG)&Process->Flags, + Flags | PSF_SESSION_CREATION_UNDERWAY_BIT, + Flags); + if (NewFlags == Flags) break; + + /* It changed, try again */ + Flags = NewFlags; + } + + /* Now we should own the flag */ + ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT); + + /* + * 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; + MmSessionSpace->PageTables = PageTables; + MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde; + InitializeListHead(&MmSessionSpace->ImageList); + DPRINT1("Session %d 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; +} + +NTSTATUS +NTAPI +MmSessionCreate(OUT PULONG SessionId) +{ + PEPROCESS Process = PsGetCurrentProcess(); + ULONG SessionLeaderExists; + NTSTATUS Status; + + /* Fail if the process is already in a session */ + if (Process->Flags & PSF_PROCESS_IN_SESSION_BIT) + { + DPRINT1("Process already in session\n"); + return STATUS_ALREADY_COMMITTED; + } + + /* Check if the process is already the session leader */ + if (!Process->Vm.Flags.SessionLeader) + { + /* Atomically set it as the leader */ + SessionLeaderExists = InterlockedCompareExchange(&MiSessionLeaderExists, 1, 0); + if (SessionLeaderExists) + { + DPRINT1("Session leader race\n"); + return STATUS_INVALID_SYSTEM_SERVICE; + } + + /* Do the work required to upgrade him */ + MiSessionLeader(Process); + } + + /* 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); + return Status; +} + +NTSTATUS +NTAPI +MmSessionDelete(IN ULONG SessionId) +{ + PEPROCESS Process = PsGetCurrentProcess(); + + /* Process must be in a session */ + if (!(Process->Flags & PSF_PROCESS_IN_SESSION_BIT)) + { + DPRINT1("Not in a session!\n"); + return STATUS_UNABLE_TO_FREE_VM; + } + + /* It must be the session leader */ + if (!Process->Vm.Flags.SessionLeader) + { + DPRINT1("Not a session leader!\n"); + return STATUS_UNABLE_TO_FREE_VM; + } + + /* Remove one reference count */ + KeEnterCriticalRegion(); + /* FIXME: Do it */ + KeLeaveCriticalRegion(); + + /* All done */ + return STATUS_SUCCESS; +} + /* SYSTEM CALLS ***************************************************************/ NTSTATUS