test: session.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# session.c

Go to the documentation of this file.

    00001 /*++
    00002
    00003 Copyright (c) 1997  Microsoft Corporation
    00004
    00005 Module Name:
    00006
    00007    session.c
    00008
    00009 Abstract:
    00010
    00011     This module contains the routines which implement the creation and
    00012     deletion of session spaces along with associated support routines.
    00013
    00014 Author:
    00015
    00016     Landy Wang (landyw) 05-Dec-1997
    00017
    00018 Revision History:
    00019
    00020 --*/
    00021
    00022 #include "mi.h"
    00023
    00024 ULONG MiSessionCount;
    00025
    00026 #define MM_MAXIMUM_CONCURRENT_SESSIONS  16384
    00027
    00028 FAST_MUTEX  MiSessionIdMutex;
    00029
    00030 PRTL_BITMAP MiSessionIdBitmap;
    00031
    00032 #if defined (_WIN64)
    00033 #define MI_SESSION_COMMIT_CHARGE 3
    00034 #else
    00035 #define MI_SESSION_COMMIT_CHARGE 2
    00036 #endif
    00037
    00038 VOID
    00039 MiSessionAddProcess(
    00040     PEPROCESS NewProcess
    00041     );
    00042
    00043 VOID
    00044 MiSessionRemoveProcess (
    00045     VOID
    00046     );
    00047
    00048 VOID
    00049 MiInitializeSessionIds (
    00050     VOID
    00051     );
    00052
    00053 NTSTATUS
    00054 MiSessionCreateInternal(
    00055     OUT PULONG SessionId
    00056     );
    00057
    00058 NTSTATUS
    00059 MiSessionCommitPageTables(
    00060     IN PVOID StartVa,
    00061     IN PVOID EndVa
    00062     );
    00063
    00064 BOOLEAN
    00065 MiDereferenceSession(
    00066     VOID
    00067     );
    00068
    00069 VOID
    00070 MiSessionDeletePde(
    00071     IN PMMPTE Pde,
    00072     IN BOOLEAN WorkingSetInitialized,
    00073     IN PMMPTE SelfMapPde
    00074     );
    00075
    00076 #if DBG
    00077 VOID
    00078 MiCheckSessionVirtualSpace(
    00079     IN PVOID VirtualAddress,
    00080     IN ULONG NumberOfBytes
    00081     );
    00082 #endif
    00083
    00084 #ifdef ALLOC_PRAGMA
    00085 #pragma alloc_text(INIT,MiInitializeSessionIds)
    00086
    00087 #pragma alloc_text(PAGE, MmSessionLeader)
    00088 #pragma alloc_text(PAGE, MmSessionSetUnloadAddress)
    00089
    00090 #pragma alloc_text(PAGEHYDRA, MiSessionAddProcess)
    00091 #pragma alloc_text(PAGEHYDRA, MiSessionRemoveProcess)
    00092 #pragma alloc_text(PAGEHYDRA, MiSessionCreateInternal)
    00093 #pragma alloc_text(PAGEHYDRA, MmSessionCreate)
    00094 #pragma alloc_text(PAGEHYDRA, MmSessionDelete)
    00095 #pragma alloc_text(PAGEHYDRA, MiAttachSession)
    00096 #pragma alloc_text(PAGEHYDRA, MiDetachSession)
    00097 #pragma alloc_text(PAGEHYDRA, MiSessionCommitImagePages)
    00098 #pragma alloc_text(PAGEHYDRA, MiSessionCommitPageTables)
    00099 #pragma alloc_text(PAGEHYDRA, MiSessionOutSwapProcess)
    00100 #pragma alloc_text(PAGEHYDRA, MiSessionInSwapProcess)
    00101 #pragma alloc_text(PAGEHYDRA, MiSessionDeletePde)
    00102
    00103 #if DBG
    00104 #pragma alloc_text(PAGEHYDRA, MiCheckSessionVirtualSpace)
    00105 #endif
    00106 #endif
    00107
    00108 VOID
    00109 MmSessionLeader(
    00110     IN PEPROCESS Process
    00111     )
    00112
    00113 /*++
    00114
    00115 Routine Description:
    00116
    00117     Mark the argument process as having the ability to create or delete session
    00118     spaces.  This is only granted to the session manager process.
    00119
    00120 Arguments:
    00121
    00122     Process - Supplies a pointer to the privileged process.
    00123
    00124 Return Value:
    00125
    00126     None.
    00127
    00128 Environment:
    00129
    00130     Kernel mode.
    00131
    00132 --*/
    00133
    00134 {
    00135     Process->Vm.u.Flags.SessionLeader = 1;
    00136 }
    00137
    00138
    00139 VOID
    00140 MmSessionSetUnloadAddress (
    00141     IN PDRIVER_OBJECT pWin32KDevice
    00142     )
    00143
    00144 /*++
    00145
    00146 Routine Description:
    00147
    00148     Copy the win32k.sys driver object to the session structure for use during
    00149     unload.
    00150
    00151 Arguments:
    00152
    00153     NewProcess - Supplies a pointer to the win32k driver object.
    00154
    00155 Return Value:
    00156
    00157     None.
    00158
    00159 Environment:
    00160
    00161     Kernel mode.
    00162
    00163 --*/
    00164
    00165 {
    00166     if (MiHydra == TRUE && PsGetCurrentProcess()->Vm.u.Flags.ProcessInSession == 1) {
    00167
    00168         ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
    00169
    00170         RtlMoveMemory (&MmSessionSpace->Win32KDriverObject,
    00171                        pWin32KDevice,
    00172                        sizeof(DRIVER_OBJECT));
    00173         }
    00174 }
    00175
    00176
    00177 VOID
    00178 MiSessionAddProcess(
    00179     PEPROCESS NewProcess
    00180     )
    00181
    00182 /*++
    00183
    00184 Routine Description:
    00185
    00186     Add the new process to the current session space.
    00187
    00188 Arguments:
    00189
    00190     NewProcess - Supplies a pointer to the process being created.
    00191
    00192 Return Value:
    00193
    00194     None.
    00195
    00196 Environment:
    00197
    00198     Kernel mode, APCs disabled.
    00199
    00200 --*/
    00201
    00202 {
    00203     KIRQL OldIrql;
    00204     PMM_SESSION_SPACE SessionGlobal;
    00205
    00206     //
    00207     // If the calling process has no session, then the new process won't get
    00208     // one either.
    00209     //
    00210
    00211     if (PsGetCurrentProcess()->Vm.u.Flags.ProcessInSession == 0) {
    00212         return;
    00213     }
    00214
    00215     ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
    00216
    00217     SessionGlobal = SESSION_GLOBAL(MmSessionSpace);
    00218
    00219     LOCK_SESSION (OldIrql);
    00220
    00221     MmSessionSpace->ReferenceCount += 1;
    00222
    00223     UNLOCK_SESSION (OldIrql);
    00224
    00225 #if defined(_IA64_)
    00226     KeAddSessionSpace(&NewProcess->Pcb, &SessionGlobal->SessionMapInfo);
    00227 #endif
    00228
    00229     //
    00230     // Link the process entry into the session space and WSL structures.
    00231     //
    00232
    00233     LOCK_EXPANSION (OldIrql);
    00234
    00235     if (IsListEmpty(&SessionGlobal->ProcessList)) {
    00236
    00237         if (MmSessionSpace->Vm.AllowWorkingSetAdjustment == FALSE) {
    00238
    00239             ASSERT (MmSessionSpace->u.Flags.WorkingSetInserted == 0);
    00240
    00241             MmSessionSpace->Vm.AllowWorkingSetAdjustment = TRUE;
    00242
    00243             InsertTailList (&MmWorkingSetExpansionHead.ListHead,
    00244                             &SessionGlobal->Vm.WorkingSetExpansionLinks);
    00245
    00246             MmSessionSpace->u.Flags.WorkingSetInserted = 1;
    00247         }
    00248     }
    00249
    00250     InsertTailList (&SessionGlobal->ProcessList, &NewProcess->SessionProcessLinks);
    00251
    00252     NewProcess->Vm.u.Flags.ProcessInSession = 1;
    00253
    00254     UNLOCK_EXPANSION (OldIrql);
    00255 }
    00256
    00257
    00258 VOID
    00259 MiSessionRemoveProcess (
    00260     VOID
    00261     )
    00262
    00263 /*++
    00264
    00265 Routine Description:
    00266
    00267     This routine removes the current process from the current session space.
    00268     This may trigger a substantial round of dereferencing and resource freeing
    00269     if it is also the last process in the session, (holding the last image
    00270     in the group, etc).
    00271
    00272 Arguments:
    00273
    00274     None.
    00275
    00276 Return Value:
    00277
    00278     None.
    00279
    00280 Environment:
    00281
    00282     Kernel mode, APC_LEVEL and below, but queueing of APCs to this thread has
    00283     been permanently disabled.  This is the last thread in the process
    00284     being deleted.  The caller has ensured that this process is not
    00285     on the expansion list and therefore there can be no races in regards to
    00286     trimming.
    00287
    00288 --*/
    00289
    00290 {
    00291     KIRQL OldIrql;
    00292     PEPROCESS CurrentProcess;
    00293 #if DBG
    00294     ULONG Found;
    00295     PEPROCESS Process;
    00296     PLIST_ENTRY NextEntry;
    00297     PMM_SESSION_SPACE SessionGlobal;
    00298 #endif
    00299
    00300     CurrentProcess = PsGetCurrentProcess();
    00301
    00302     if (CurrentProcess->Vm.u.Flags.ProcessInSession == 0) {
    00303         return;
    00304     }
    00305
    00306     ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
    00307
    00308     //
    00309     // Remove this process from the list of processes in the current session.
    00310     //
    00311
    00312     LOCK_EXPANSION (OldIrql);
    00313
    00314 #if DBG
    00315
    00316     SessionGlobal = SESSION_GLOBAL(MmSessionSpace);
    00317
    00318     Found = 0;
    00319     NextEntry = SessionGlobal->ProcessList.Flink;
    00320
    00321     while (NextEntry != &SessionGlobal->ProcessList) {
    00322         Process = CONTAINING_RECORD (NextEntry, EPROCESS, SessionProcessLinks);
    00323
    00324         if (Process == CurrentProcess) {
    00325             Found = 1;
    00326         }
    00327
    00328         NextEntry = NextEntry->Flink;
    00329     }
    00330
    00331     ASSERT (Found == 1);
    00332
    00333 #endif
    00334
    00335     RemoveEntryList (&CurrentProcess->SessionProcessLinks);
    00336
    00337     UNLOCK_EXPANSION (OldIrql);
    00338
    00339     //
    00340     // Decrement this process' reference count to the session.  If this
    00341     // is the last reference, then the entire session will be destroyed
    00342     // upon return.  This includes unloading drivers, unmapping pools,
    00343     // freeing page tables, etc.
    00344     //
    00345
    00346     MiDereferenceSession ();
    00347 }
    00348
    00349 VOID
    00350 MiInitializeSessionIds (
    00351     VOID
    00352     )
    00353
    00354 /*++
    00355
    00356 Routine Description:
    00357
    00358     This routine creates and initializes session ID allocation/deallocation.
    00359
    00360 Arguments:
    00361
    00362     None.
    00363
    00364 Return Value:
    00365
    00366     None.
    00367
    00368 --*/
    00369
    00370 {
    00371     //
    00372     // If this ever grows beyond the size of a page, both the allocation and
    00373     // deletion code will need to be updated.
    00374     //
    00375
    00376     ASSERT (sizeof(MM_SESSION_SPACE) <= PAGE_SIZE);
    00377
    00378     ExInitializeFastMutex (&MiSessionIdMutex);
    00379
    00380     MiCreateBitMap (&MiSessionIdBitmap,
    00381                     MM_MAXIMUM_CONCURRENT_SESSIONS,
    00382                     PagedPool);
    00383
    00384     if (MiSessionIdBitmap == NULL) {
    00385         MiCreateBitMap (&MiSessionIdBitmap,
    00386                         MM_MAXIMUM_CONCURRENT_SESSIONS,
    00387                         NonPagedPoolMustSucceed);
    00388     }
    00389
    00390     RtlClearAllBits (MiSessionIdBitmap);
    00391 }
    00392
    00393 NTSTATUS
    00394 MiSessionCreateInternal(
    00395     OUT PULONG SessionId
    00396     )
    00397
    00398 /*++
    00399
    00400 Routine Description:
    00401
    00402     This routine creates the data structure that describes and maintains
    00403     the session space.  It resides at the beginning of the session space.
    00404     Carefully construct the first page mapping to bootstrap the fault
    00405     handler which relies on the session space data structure being
    00406     present and valid.
    00407
    00408     In 32-bit NT, this initial mapping for the portion of session space
    00409     mapped by the first PDE will automatically be inherited by all child
    00410     processes when the system copies the system portion of the page
    00411     directory for new address spaces.  Additional entries are faulted
    00412     in by the session space fault handler, which references this structure.
    00413     For 64-bit NT, everything is automatically inherited.
    00414
    00415     This routine commits virtual memory within the current session space with
    00416     backing pages.  The virtual addresses within session space are
    00417     allocated with a separate facility in the image management facility.
    00418     This is because images must be at a unique system wide virtual address.
    00419
    00420 Arguments:
    00421
    00422     SessionId - Supplies a pointer to place the new session ID into.
    00423
    00424 Return Value:
    00425
    00426     STATUS_SUCCESS if all went well, various failure status codes
    00427     if the session was not created.
    00428
    00429 Environment:
    00430
    00431     Kernel mode, no mutexes held.
    00432
    00433 --*/
    00434
    00435 {
    00436     KIRQL  OldIrql;
    00437     PMMPTE PointerPpe;
    00438     PMMPTE PointerPde;
    00439     PMMPTE PointerPte;
    00440     NTSTATUS Status;
    00441     PMM_SESSION_SPACE SessionSpace;
    00442     PMM_SESSION_SPACE SessionGlobal;
    00443     PFN_NUMBER ResidentPages;
    00444     BOOLEAN GotCommit;
    00445     BOOLEAN GotPages;
    00446     PMMPFN Pfn1;
    00447     MMPTE TempPte;
    00448     MMPTE AliasPte;
    00449     MMPTE PreviousPte;
    00450     ULONG_PTR Va;
    00451     PFN_NUMBER DataPage;
    00452     PFN_NUMBER PageTablePage;
    00453     PFN_NUMBER PageDirectoryPage;
    00454     ULONG PageColor;
    00455
    00456     SessionSpace = NULL;
    00457     GotCommit = FALSE;
    00458     GotPages = FALSE;
    00459
    00460     ASSERT (MmIsAddressValid(SessionSpace) == FALSE);
    00461
    00462     ExAcquireFastMutex (&MiSessionIdMutex);
    00463
    00464     *SessionId = RtlFindClearBitsAndSet (MiSessionIdBitmap, 1, 0);
    00465
    00466     ExReleaseFastMutex (&MiSessionIdMutex);
    00467
    00468     if (*SessionId == 0xFFFFFFFF) {
    00469 #if DBG
    00470         DbgPrint("MiSessionCreateInternal: No session IDs available\n");
    00471 #endif
    00472         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_IDS);
    00473         return STATUS_NO_MEMORY;
    00474     }
    00475
    00476     ResidentPages = MI_SESSION_COMMIT_CHARGE;
    00477
    00478     if (MiChargeCommitment (ResidentPages, NULL) == FALSE) {
    00479 #if DBG
    00480         if (MmDebug & MM_DBG_SESSIONS) {
    00481             DbgPrint("MiSessionCreateInternal: No commit for %d pages\n",
    00482                 ResidentPages);
    00483         }
    00484 #endif
    00485         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
    00486         goto Failure;
    00487     }
    00488
    00489     GotCommit = TRUE;
    00490
    00491     MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_CREATE, ResidentPages);
    00492
    00493     LOCK_PFN (OldIrql);
    00494
    00495     //
    00496     // Check to make sure the physical pages are available.
    00497     //
    00498
    00499     if ((SPFN_NUMBER)(ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM) > MI_NONPAGABLE_MEMORY_AVAILABLE()) {
    00500 #if DBG
    00501         if (MmDebug & MM_DBG_SESSIONS) {
    00502             DbgPrint("MiSessionCreateInternal: No Resident Pages %d, Need %d\n",
    00503                 MmResidentAvailablePages,
    00504                 ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00505         }
    00506 #endif
    00507         UNLOCK_PFN (OldIrql);
    00508
    00509         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_RESIDENT);
    00510         goto Failure;
    00511     }
    00512
    00513     GotPages = TRUE;
    00514
    00515     MmResidentAvailablePages -= (ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00516
    00517     MM_BUMP_COUNTER(40, ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00518
    00519 #if defined (_WIN64)
    00520
    00521     //
    00522     // Initialize the page directory page.
    00523     //
    00524
    00525     MiEnsureAvailablePageOrWait (NULL, NULL);
    00526
    00527     PageColor = MI_GET_PAGE_COLOR_FROM_VA (NULL);
    00528
    00529     PageDirectoryPage = MiRemoveZeroPageIfAny (PageColor);
    00530     if (PageDirectoryPage == 0) {
    00531         PageDirectoryPage = MiRemoveAnyPage (PageColor);
    00532         UNLOCK_PFN (OldIrql);
    00533         MiZeroPhysicalPage (PageDirectoryPage, PageColor);
    00534         LOCK_PFN (OldIrql);
    00535     }
    00536
    00537     //
    00538     // The global bit is masked off since we need to make sure the TB entry
    00539     // is flushed when we switch to a process in a different session space.
    00540     //
    00541
    00542     TempPte.u.Long = ValidKernelPdeLocal.u.Long;
    00543     TempPte.u.Hard.PageFrameNumber = PageDirectoryPage;
    00544
    00545     PointerPpe = MiGetPpeAddress ((PVOID)MmSessionSpace);
    00546
    00547     //
    00548     // Another thread may have gotten here before us, check for that now.
    00549     //
    00550
    00551     if (PointerPpe->u.Long != 0) {
    00552
    00553 #if DBG
    00554         ASSERT (PointerPpe->u.Hard.Valid == 1);
    00555         DbgPrint("MiSessionCreateInternal: Detected PPE %p race\n",
    00556             PointerPpe->u.Long);
    00557         DbgBreakPoint ();
    00558 #endif //DBG
    00559
    00560         Pfn1 = MI_PFN_ELEMENT (PageDirectoryPage);
    00561         ASSERT (Pfn1->u2.ShareCount == 0);
    00562         ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
    00563         Pfn1->u3.e2.ReferenceCount = 1;
    00564         Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
    00565         MI_SET_PFN_DELETED (Pfn1);
    00566 #if DBG
    00567         Pfn1->u3.e1.PageLocation = StandbyPageList;
    00568 #endif
    00569
    00570         MiDecrementReferenceCount (PageDirectoryPage);
    00571
    00572         MmResidentAvailablePages += (ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00573         MM_BUMP_COUNTER(46, ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00574
    00575         MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGETABLE_FREE_RACE, 1);
    00576
    00577         GotPages = FALSE;
    00578         UNLOCK_PFN (OldIrql);
    00579
    00580         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_RACE_DETECTED);
    00581
    00582         goto Failure;
    00583     }
    00584
    00585     *PointerPpe = TempPte;
    00586
    00587     MiInitializePfnForOtherProcess (PageDirectoryPage, PointerPpe, 0);
    00588     Pfn1 = MI_PFN_ELEMENT (PageDirectoryPage);
    00589     Pfn1->PteFrame = 0;
    00590
    00591     ASSERT (MI_PFN_ELEMENT(PageDirectoryPage)->u1.WsIndex == 0);
    00592
    00593     KeFillEntryTb ((PHARDWARE_PTE) PointerPpe,
    00594                    MiGetVirtualAddressMappedByPte (PointerPpe),
    00595                    FALSE);
    00596 #endif
    00597
    00598     //
    00599     // Initialize the page table page.
    00600     //
    00601
    00602     MiEnsureAvailablePageOrWait (NULL, NULL);
    00603
    00604     PageColor = MI_GET_PAGE_COLOR_FROM_VA (NULL);
    00605
    00606     PageTablePage = MiRemoveZeroPageIfAny (PageColor);
    00607     if (PageTablePage == 0) {
    00608         PageTablePage = MiRemoveAnyPage (PageColor);
    00609         UNLOCK_PFN (OldIrql);
    00610         MiZeroPhysicalPage (PageTablePage, PageColor);
    00611         LOCK_PFN (OldIrql);
    00612     }
    00613
    00614     //
    00615     // The global bit is masked off since we need to make sure the TB entry
    00616     // is flushed when we switch to a process in a different session space.
    00617     //
    00618
    00619     TempPte.u.Long = ValidKernelPdeLocal.u.Long;
    00620     TempPte.u.Hard.PageFrameNumber = PageTablePage;
    00621
    00622     PointerPde = MiGetPdeAddress ((PVOID)MmSessionSpace);
    00623
    00624 #if !defined (_WIN64)
    00625
    00626     //
    00627     // Another thread may have gotten here before us, check for that now.
    00628     //
    00629
    00630     if (PointerPde->u.Long != 0) {
    00631 #if DBG
    00632         ASSERT (PointerPde->u.Hard.Valid == 1);
    00633         DbgPrint("MiSessionCreateInternal: Detected PDE %p race\n",
    00634             PointerPde->u.Long);
    00635         DbgBreakPoint ();
    00636 #endif //DBG
    00637
    00638         Pfn1 = MI_PFN_ELEMENT (PageTablePage);
    00639         ASSERT (Pfn1->u2.ShareCount == 0);
    00640         ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
    00641         Pfn1->u3.e2.ReferenceCount = 1;
    00642         Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
    00643         MI_SET_PFN_DELETED (Pfn1);
    00644 #if DBG
    00645         Pfn1->u3.e1.PageLocation = StandbyPageList;
    00646 #endif
    00647
    00648         MiDecrementReferenceCount (PageTablePage);
    00649
    00650         MmResidentAvailablePages += (ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00651         MM_BUMP_COUNTER(46, ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00652
    00653         MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGETABLE_FREE_RACE, 1);
    00654
    00655         GotPages = FALSE;
    00656         UNLOCK_PFN (OldIrql);
    00657
    00658         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_RACE_DETECTED);
    00659
    00660         goto Failure;
    00661     }
    00662
    00663 #endif
    00664
    00665     MI_WRITE_VALID_PTE (PointerPde, TempPte);
    00666
    00667     //
    00668     // This page frame references itself instead of the current (SMSS.EXE)
    00669     // page directory as its PteFrame.  This allows the current process to
    00670     // appear more normal (at least on 32-bit NT).  It just means we have
    00671     // to treat this page specially during teardown.
    00672     //
    00673
    00674     MiInitializePfnForOtherProcess (PageTablePage, PointerPde, PageTablePage);
    00675
    00676     //
    00677     // This page is never paged, ensure that its WsIndex stays clear so the
    00678     // release of the page is handled correctly.
    00679     //
    00680
    00681     ASSERT (MI_PFN_ELEMENT(PageTablePage)->u1.WsIndex == 0);
    00682
    00683     Va = (ULONG_PTR)MiGetPteAddress (MmSessionSpace);
    00684
    00685     KeFillEntryTb ((PHARDWARE_PTE) PointerPde, (PMMPTE)Va, FALSE);
    00686
    00687     MiEnsureAvailablePageOrWait (NULL, NULL);
    00688
    00689     PageColor = MI_GET_PAGE_COLOR_FROM_VA (NULL);
    00690
    00691     DataPage = MiRemoveZeroPageIfAny (PageColor);
    00692     if (DataPage == 0) {
    00693         DataPage = MiRemoveAnyPage (PageColor);
    00694         UNLOCK_PFN (OldIrql);
    00695         MiZeroPhysicalPage (DataPage, PageColor);
    00696         LOCK_PFN (OldIrql);
    00697     }
    00698
    00699     //
    00700     // The global bit is masked off since we need to make sure the TB entry
    00701     // is flushed when we switch to a process in a different session space.
    00702     //
    00703
    00704     TempPte.u.Long = ValidKernelPteLocal.u.Long;
    00705     TempPte.u.Hard.PageFrameNumber = DataPage;
    00706
    00707     PointerPte = MiGetPteAddress (MmSessionSpace);
    00708
    00709     MI_WRITE_VALID_PTE (PointerPte, TempPte);
    00710
    00711     MiInitializePfn (DataPage, PointerPte, 1);
    00712
    00713     ASSERT (MI_PFN_ELEMENT(DataPage)->u1.WsIndex == 0);
    00714
    00715     UNLOCK_PFN (OldIrql);
    00716
    00717     KeFillEntryTb ((PHARDWARE_PTE) PointerPte, (PMMPTE)MmSessionSpace, FALSE);
    00718
    00719     AliasPte = *PointerPte;
    00720
    00721     //
    00722     // Initialize the new session space data structure.
    00723     //
    00724
    00725     SessionSpace = MmSessionSpace;
    00726
    00727     MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGETABLE_ALLOC, 1);
    00728     MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGE_ALLOC, 1);
    00729
    00730     SessionSpace->ReferenceCount = 1;
    00731     SessionSpace->u.LongFlags = 0;
    00732     SessionSpace->SessionId = *SessionId;
    00733     SessionSpace->SessionPageDirectoryIndex = PageTablePage;
    00734
    00735     SessionSpace->Color = PageColor;
    00736
    00737     //
    00738     // Track the page table page and the data page.
    00739     //
    00740
    00741     SessionSpace->NonPagablePages = ResidentPages;
    00742     SessionSpace->CommittedPages = ResidentPages;
    00743
    00744 #if defined (_WIN64)
    00745
    00746     //
    00747     // Initialize the session data page directory entry so trimmers can attach.
    00748     //
    00749
    00750     PointerPpe = MiGetPpeAddress ((PVOID)MmSessionSpace);
    00751     SessionSpace->PageDirectory = *PointerPpe;
    00752
    00753 #else
    00754
    00755     //
    00756     // Load the session data page table entry so that other processes
    00757     // can fault in the mapping.
    00758     //
    00759
    00760     SessionSpace->PageTables[PointerPde - MiGetPdeAddress (MmSessionBase)] = *PointerPde;
    00761
    00762 #endif
    00763
    00764     //
    00765     // Reserve a global system PTE and map the data page into it.
    00766     //
    00767
    00768     SessionSpace->GlobalPteEntry = MiReserveSystemPtes (1,
    00769                                                         SystemPteSpace,
    00770                                                         0,
    00771                                                         0,
    00772                                                         FALSE
    00773                                                         );
    00774
    00775     if (SessionSpace->GlobalPteEntry == NULL) {
    00776 #if DBG
    00777         if (MmDebug & MM_DBG_SESSIONS) {
    00778             DbgPrint("MiSessionCreateInternal: No memory for session self-map\n");
    00779         }
    00780 #endif
    00781         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_SYSPTES);
    00782         goto Failure;
    00783     }
    00784
    00785     *(SessionSpace->GlobalPteEntry) = AliasPte;
    00786
    00787     SessionSpace->GlobalVirtualAddress = MiGetVirtualAddressMappedByPte (SessionSpace->GlobalPteEntry);
    00788
    00789     SessionGlobal = SESSION_GLOBAL(SessionSpace);
    00790
    00791     //
    00792     // This list entry is only referenced while within the
    00793     // session space and has session space (not global) addresses.
    00794     //
    00795
    00796     InitializeListHead (&SessionSpace->ImageList);
    00797
    00798     //
    00799     // Initialize the session space pool.
    00800     //
    00801
    00802     Status = MiInitializeSessionPool ();
    00803
    00804     if (!NT_SUCCESS(Status)) {
    00805 #if DBG
    00806         if (MmDebug & MM_DBG_SESSIONS) {
    00807             DbgPrint("MiSessionCreateInternal: No memory for session pool\n");
    00808         }
    00809 #endif
    00810         goto Failure;
    00811     }
    00812
    00813     //
    00814     // Initialize the view mapping support - note this must happen after
    00815     // initializing session pool.
    00816     //
    00817
    00818     if (MiInitializeSystemSpaceMap (&SessionGlobal->Session) == FALSE) {
    00819 #if DBG
    00820         if (MmDebug & MM_DBG_SESSIONS) {
    00821             DbgPrint("MiSessionCreateInternal: No memory for view mapping\n");
    00822         }
    00823 #endif
    00824         goto Failure;
    00825     }
    00826
    00827     //
    00828     // Use the global virtual address rather than the session space virtual
    00829     // address to set up fields that need to be globally accessible.
    00830     //
    00831
    00832     InitializeListHead (&SessionGlobal->WsListEntry);
    00833
    00834     InitializeListHead (&SessionGlobal->ProcessList);
    00835
    00836     KeInitializeSpinLock (&SessionGlobal->SpinLock);
    00837
    00838 #if defined(_IA64_)
    00839     KeEnableSessionSharing(&SessionGlobal->SessionMapInfo);
    00840 #endif
    00841
    00842     return STATUS_SUCCESS;
    00843
    00844 Failure:
    00845
    00846     if (GotCommit == TRUE) {
    00847         MiReturnCommitment (ResidentPages);
    00848         MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SESSION_CREATE_FAILURE1,
    00849                          ResidentPages);
    00850     }
    00851
    00852     if (GotPages == TRUE) {
    00853
    00854 #if defined (_WIN64)
    00855
    00856         PointerPpe = MiGetPpeAddress (MI_SESSION_POOL);
    00857
    00858         ASSERT (PointerPpe->u.Hard.Valid != 0);
    00859
    00860 #endif
    00861
    00862         PointerPde = MiGetPdeAddress (MI_SESSION_POOL);
    00863
    00864         ASSERT (PointerPde->u.Hard.Valid != 0);
    00865
    00866         //
    00867         // Note that this call will acquire the session space lock, so the
    00868         // data page better be mapped.
    00869         //
    00870
    00871         MiFreeSessionSpaceMap ();
    00872
    00873         //
    00874         // Free the initial page table page that was allocated for the
    00875         // paged pool range (if it has been allocated at this point).
    00876         //
    00877
    00878         MiFreeSessionPoolBitMaps ();
    00879
    00880         if (SessionSpace->GlobalPteEntry) {
    00881             MiReleaseSystemPtes (SessionSpace->GlobalPteEntry,
    00882                                  1,
    00883                                  SystemPteSpace);
    00884             SessionSpace->GlobalPteEntry = (PMMPTE)0;
    00885         }
    00886
    00887         LOCK_PFN (OldIrql);
    00888
    00889         //
    00890         // Free the session data structure page.
    00891         //
    00892
    00893         Pfn1 = MI_PFN_ELEMENT (DataPage);
    00894         MiDecrementShareAndValidCount (Pfn1->PteFrame);
    00895         MI_SET_PFN_DELETED (Pfn1);
    00896         MiDecrementShareCountOnly (DataPage);
    00897
    00898         MI_FLUSH_SINGLE_SESSION_TB (MmSessionSpace,
    00899                                     TRUE,
    00900                                     TRUE,
    00901                                     (PHARDWARE_PTE)PointerPte,
    00902                                     ZeroKernelPte.u.Flush,
    00903                                     PreviousPte);
    00904
    00905         MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGE_FREE_FAIL1, 1);
    00906
    00907         //
    00908         // Free the page table page.
    00909         //
    00910
    00911         Pfn1 = MI_PFN_ELEMENT (PageTablePage);
    00912         ASSERT (PageTablePage == Pfn1->PteFrame);
    00913         ASSERT (Pfn1->u2.ShareCount == 2);
    00914         Pfn1->u2.ShareCount -= 1;
    00915         MI_SET_PFN_DELETED (Pfn1);
    00916         MiDecrementShareCountOnly (PageTablePage);
    00917
    00918         MI_FLUSH_SINGLE_SESSION_TB (MiGetVirtualAddressMappedByPte(PointerPde),
    00919                                     TRUE,
    00920                                     TRUE,
    00921                                     (PHARDWARE_PTE)PointerPde,
    00922                                     ZeroKernelPte.u.Flush,
    00923                                     PreviousPte);
    00924
    00925 #if defined (_WIN64)
    00926
    00927         //
    00928         // Free the page directory page.
    00929         //
    00930
    00931         Pfn1 = MI_PFN_ELEMENT (PageDirectoryPage);
    00932         ASSERT (PageDirectoryPage == Pfn1->PteFrame);
    00933         ASSERT (Pfn1->u2.ShareCount == 1);
    00934         ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
    00935         MI_SET_PFN_DELETED (Pfn1);
    00936         MiDecrementShareCountOnly (PageDirectoryPage);
    00937
    00938         MI_FLUSH_SINGLE_SESSION_TB (MiGetVirtualAddressMappedByPte(PointerPpe),
    00939                                     TRUE,
    00940                                     TRUE,
    00941                                     (PHARDWARE_PTE)PointerPpe,
    00942                                     ZeroKernelPte.u.Flush,
    00943                                     PreviousPte);
    00944
    00945 #endif
    00946
    00947         //
    00948         // Now that the PDE has been zeroed, another thread in this process
    00949         // could attempt a session create so be careful not to count on any
    00950         // anything in this particular session.
    00951         //
    00952
    00953         MmResidentAvailablePages += (ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00954
    00955         MM_BUMP_COUNTER (49, ResidentPages + MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    00956
    00957         MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGETABLE_FREE_FAIL1, 1);
    00958
    00959         UNLOCK_PFN (OldIrql);
    00960     }
    00961
    00962     ExAcquireFastMutex (&MiSessionIdMutex);
    00963
    00964     ASSERT (RtlCheckBit (MiSessionIdBitmap, *SessionId));
    00965     RtlClearBits (MiSessionIdBitmap, *SessionId, 1);
    00966
    00967     ExReleaseFastMutex (&MiSessionIdMutex);
    00968
    00969     return STATUS_NO_MEMORY;
    00970 }
    00971
    00972
    00973 NTSTATUS
    00974 MmSessionCreate(
    00975     OUT PULONG SessionId
    00976     )
    00977
    00978 /*++
    00979
    00980 Routine Description:
    00981
    00982     Called from NtSetSystemInformation() to create a session space
    00983     in the calling process with the specified SessionId.  An error is returned
    00984     if the calling process already has a session Space.
    00985
    00986 Arguments:
    00987
    00988     SessionId - Supplies a pointer to place the resulting session id in.
    00989
    00990 Return Value:
    00991
    00992     Various NTSTATUS error codes.
    00993
    00994 Environment:
    00995
    00996     Kernel mode, no mutexes held.
    00997
    00998 --*/
    00999
    01000 {
    01001     KIRQL OldIrql;
    01002     NTSTATUS Status;
    01003     PEPROCESS CurrentProcess;
    01004 #if DBG
    01005     PMMPTE StartPde;
    01006     PMMPTE EndPde;
    01007 #endif
    01008
    01009     if (MiHydra == FALSE) {
    01010         return STATUS_INVALID_SYSTEM_SERVICE;
    01011     }
    01012
    01013     CurrentProcess = PsGetCurrentProcess();
    01014
    01015     //
    01016     // A simple check to see if the calling process already has a session space.
    01017     // No need to go through all this if it does.  Creation races are caught
    01018     // below and recovered from regardless.
    01019     //
    01020
    01021     if (CurrentProcess->Vm.u.Flags.ProcessInSession == 1) {
    01022         return STATUS_ALREADY_COMMITTED;
    01023     }
    01024
    01025     if (CurrentProcess->Vm.u.Flags.SessionLeader == 0) {
    01026
    01027         //
    01028         // Only the session manager can create a session.
    01029         //
    01030
    01031         return STATUS_INVALID_SYSTEM_SERVICE;
    01032     }
    01033
    01034 #if DBG
    01035     ASSERT (MmIsAddressValid(MmSessionSpace) == FALSE);
    01036
    01037 #if defined (_WIN64)
    01038     ASSERT ((MiGetPpeAddress(MmSessionBase))->u.Long == ZeroKernelPte.u.Long);
    01039 #else
    01040     StartPde = MiGetPdeAddress (MmSessionBase);
    01041     EndPde = MiGetPdeAddress (MI_SESSION_SPACE_END);
    01042
    01043     while (StartPde < EndPde) {
    01044         ASSERT (StartPde->u.Long == ZeroKernelPte.u.Long);
    01045         StartPde += 1;
    01046     }
    01047 #endif
    01048
    01049 #endif
    01050
    01051     KeEnterCriticalRegion();
    01052
    01053     Status = MiSessionCreateInternal (SessionId);
    01054
    01055     if (!NT_SUCCESS(Status)) {
    01056         KeLeaveCriticalRegion();
    01057         return Status;
    01058     }
    01059
    01060     LOCK_EXPANSION (OldIrql);
    01061
    01062     MiSessionCount += 1;
    01063
    01064     UNLOCK_EXPANSION (OldIrql);
    01065
    01066     //
    01067     // Add the session space to the working set list.
    01068     //
    01069
    01070     Status = MiSessionInitializeWorkingSetList ();
    01071
    01072     if (!NT_SUCCESS(Status)) {
    01073         MiDereferenceSession ();
    01074         KeLeaveCriticalRegion();
    01075         return Status;
    01076     }
    01077
    01078     KeLeaveCriticalRegion();
    01079
    01080     MmSessionSpace->u.Flags.Initialized = 1;
    01081
    01082     LOCK_EXPANSION (OldIrql);
    01083
    01084     CurrentProcess->Vm.u.Flags.ProcessInSession = 1;
    01085
    01086     UNLOCK_EXPANSION (OldIrql);
    01087
    01088     return Status;
    01089 }
    01090
    01091
    01092 NTSTATUS
    01093 MmSessionDelete(
    01094     ULONG SessionId
    01095     )
    01096
    01097 /*++
    01098
    01099 Routine Description:
    01100
    01101     Called from NtSetSystemInformation() to detach from an existing
    01102     session space in the calling process.  An error is returned
    01103     if the calling process has no session Space.
    01104
    01105 Arguments:
    01106
    01107     SessionId - Supplies the session id to delete.
    01108
    01109 Return Value:
    01110
    01111     STATUS_SUCCESS on success, STATUS_UNABLE_TO_FREE_VM on failure.
    01112
    01113     This process will not be able to access session space anymore upon
    01114     a successful return.  If this is the last process in the session then
    01115     the entire session is torn down.
    01116
    01117 Environment:
    01118
    01119     Kernel mode, no mutexes held.
    01120
    01121 --*/
    01122
    01123 {
    01124     PEPROCESS CurrentProcess;
    01125
    01126     if (MiHydra == FALSE) {
    01127         return STATUS_INVALID_SYSTEM_SERVICE;
    01128     }
    01129
    01130     CurrentProcess = PsGetCurrentProcess();
    01131
    01132     //
    01133     // See if the calling process has a session space - this must be
    01134     // checked since we can be called via a system service.
    01135     //
    01136
    01137     if (CurrentProcess->Vm.u.Flags.ProcessInSession == 0) {
    01138 #if DBG
    01139         DbgPrint ("MmSessionDelete: Process %p not in a session\n",
    01140             PsGetCurrentProcess());
    01141         DbgBreakPoint();
    01142 #endif
    01143         return STATUS_UNABLE_TO_FREE_VM;
    01144     }
    01145
    01146     if (CurrentProcess->Vm.u.Flags.SessionLeader == 0) {
    01147
    01148         //
    01149         // Only the session manager can delete a session.  This is because
    01150         // it affects the virtual mappings for all threads within the process
    01151         // when this address space is deleted.  This is different from normal
    01152         // VAD clearing because win32k and other drivers rely on this space.
    01153         //
    01154
    01155         return STATUS_UNABLE_TO_FREE_VM;
    01156     }
    01157
    01158     ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
    01159
    01160     if (MmSessionSpace->SessionId != SessionId) {
    01161 #if DBG
    01162         DbgPrint("MmSessionDelete: Wrong SessionId! Own %d, Ask %d\n",
    01163             MmSessionSpace->SessionId,
    01164             SessionId);
    01165         DbgBreakPoint();
    01166 #endif
    01167         return STATUS_UNABLE_TO_FREE_VM;
    01168     }
    01169
    01170     KeEnterCriticalRegion ();
    01171
    01172     MiDereferenceSession ();
    01173
    01174     KeLeaveCriticalRegion ();
    01175
    01176     return STATUS_SUCCESS;
    01177 }
    01178
    01179
    01180 VOID
    01181 MiAttachSession(
    01182     IN PMM_SESSION_SPACE SessionGlobal
    01183     )
    01184
    01185 /*++
    01186
    01187 Routine Description:
    01188
    01189     Attaches to the specified session space.
    01190
    01191 Arguments:
    01192
    01193     SessionGlobal - Supplies a pointer to the session to attach to.
    01194
    01195 Return Value:
    01196
    01197     None.
    01198
    01199 Environment:
    01200
    01201     Kernel mode.  No locks held.  Current process must not have a session
    01202     space - ie: the caller should be the system process or smss.exe.
    01203
    01204 --*/
    01205
    01206 {
    01207     PMMPTE PointerPde;
    01208 #if DBG
    01209     PMMPTE EndPde;
    01210
    01211     ASSERT (MiHydra == TRUE);
    01212
    01213     ASSERT (PsGetCurrentProcess()->Vm.u.Flags.ProcessInSession == 0);
    01214
    01215 #if defined (_WIN64)
    01216
    01217     PointerPde = MiGetPpeAddress (MmSessionBase);
    01218     ASSERT (PointerPde->u.Long == ZeroKernelPte.u.Long);
    01219
    01220 #else
    01221     PointerPde = MiGetPdeAddress (MmSessionBase);
    01222     EndPde = MiGetPdeAddress (MI_SESSION_SPACE_END);
    01223
    01224     while (PointerPde < EndPde) {
    01225         ASSERT (PointerPde->u.Long == ZeroKernelPte.u.Long);
    01226         PointerPde += 1;
    01227     }
    01228 #endif
    01229
    01230 #endif
    01231
    01232 #if defined (_WIN64)
    01233
    01234     PointerPde = MiGetPpeAddress (MmSessionBase);
    01235     MI_WRITE_VALID_PTE (PointerPde, SessionGlobal->PageDirectory);
    01236
    01237 #else
    01238
    01239     PointerPde = MiGetPdeAddress (MmSessionBase);
    01240
    01241     RtlCopyMemory (PointerPde,
    01242                    &SessionGlobal->PageTables[0],
    01243                    MI_SESSION_SPACE_PAGE_TABLES * sizeof (MMPTE));
    01244 #endif
    01245
    01246 #if defined(_IA64_)
    01247     KeAttachSessionSpace(&SessionGlobal->SessionMapInfo);
    01248 #endif
    01249 }
    01250
    01251
    01252 VOID
    01253 MiDetachSession(
    01254     VOID
    01255     )
    01256
    01257 /*++
    01258
    01259 Routine Description:
    01260
    01261     Detaches from the specified session space.
    01262
    01263 Arguments:
    01264
    01265     None.
    01266
    01267 Return Value:
    01268
    01269     None.
    01270
    01271 Environment:
    01272
    01273     Kernel mode.  No locks held.  Current process must not have a session
    01274     space to return to - ie: this should be the system process.
    01275
    01276 --*/
    01277
    01278 {
    01279     PMMPTE PointerPde;
    01280     PMMPTE EndPde;
    01281     KIRQL OldIrql;
    01282
    01283     ASSERT (MiHydra == TRUE);
    01284
    01285     ASSERT (PsGetCurrentProcess()->Vm.u.Flags.ProcessInSession == 0);
    01286     ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
    01287
    01288 #if defined (_WIN64)
    01289     PointerPde = MiGetPpeAddress (MmSessionBase);
    01290     PointerPde->u.Long = ZeroKernelPte.u.Long;
    01291 #else
    01292     PointerPde = MiGetPdeAddress (MmSessionBase);
    01293
    01294     EndPde = MiGetPdeAddress (MI_SESSION_SPACE_END);
    01295
    01296     RtlZeroMemory (PointerPde, MI_SESSION_SPACE_PAGE_TABLES * sizeof (MMPTE));
    01297 #endif
    01298
    01299     MI_FLUSH_SESSION_TB (OldIrql);
    01300
    01301 #if defined(_IA64_)
    01302     KeDetachSessionSpace();
    01303 #endif
    01304 }
    01305
    01306 #if DBG
    01307
    01308 VOID
    01309 MiCheckSessionVirtualSpace(
    01310     IN PVOID VirtualAddress,
    01311     IN ULONG NumberOfBytes
    01312     )
    01313
    01314 /*++
    01315
    01316 Routine Description:
    01317
    01318     Used to verify that no drivers fail to clean up their session allocations.
    01319
    01320 Arguments:
    01321
    01322     VirtualAddress - Supplies the starting virtual address to check.
    01323
    01324     NumberOfBytes - Supplies the number of bytes to check.
    01325
    01326 Return Value:
    01327
    01328     TRUE if all the PTEs have been freed, FALSE if not.
    01329
    01330 Environment:
    01331
    01332     Kernel mode.  APCs disabled.
    01333
    01334 --*/
    01335
    01336 {
    01337     PMMPTE StartPde;
    01338     PMMPTE EndPde;
    01339     PMMPTE StartPte;
    01340     PMMPTE EndPte;
    01341     ULONG Index;
    01342
    01343     //
    01344     // Check the specified region.  Everything should have been cleaned up
    01345     // already.
    01346     //
    01347
    01348 #if defined (_WIN64)
    01349     ASSERT (MiGetPpeAddress (VirtualAddress)->u.Hard.Valid == 1);
    01350 #endif
    01351
    01352     StartPde = MiGetPdeAddress (VirtualAddress);
    01353     EndPde = MiGetPdeAddress ((PVOID)((PCHAR)VirtualAddress + NumberOfBytes - 1));
    01354
    01355     StartPte = MiGetPteAddress (VirtualAddress);
    01356     EndPte = MiGetPteAddress ((PVOID)((PCHAR)VirtualAddress + NumberOfBytes - 1));
    01357
    01358     Index = (ULONG)(StartPde - MiGetPdeAddress (MmSessionBase));
    01359
    01360 #if defined (_WIN64)
    01361     while (StartPde <= EndPde && StartPde->u.Long == 0)
    01362 #else
    01363     while (StartPde <= EndPde && MmSessionSpace->PageTables[Index].u.Long == 0)
    01364 #endif
    01365     {
    01366         StartPde += 1;
    01367         Index += 1;
    01368         StartPte = MiGetVirtualAddressMappedByPte (StartPde);
    01369     }
    01370
    01371     while (StartPte <= EndPte) {
    01372
    01373         if (MiIsPteOnPdeBoundary(StartPte)) {
    01374
    01375             StartPde = MiGetPteAddress (StartPte);
    01376             Index = (ULONG)(StartPde - MiGetPdeAddress (MmSessionBase));
    01377
    01378 #if defined (_WIN64)
    01379             while (StartPde <= EndPde && StartPde->u.Long == 0)
    01380 #else
    01381             while (StartPde <= EndPde && MmSessionSpace->PageTables[Index].u.Long == 0)
    01382 #endif
    01383             {
    01384                 Index += 1;
    01385                 StartPde += 1;
    01386                 StartPte = MiGetVirtualAddressMappedByPte (StartPde);
    01387             }
    01388             if (StartPde > EndPde) {
    01389                 break;
    01390             }
    01391         }
    01392
    01393         if (StartPte->u.Long != 0 && StartPte->u.Long != MM_KERNEL_NOACCESS_PTE) {
    01394             DbgPrint("MiCheckSessionVirtualSpace: StartPte 0x%p is still valid! 0x%p, VA 0x%p\n",
    01395                 StartPte,
    01396                 StartPte->u.Long,
    01397                 MiGetVirtualAddressMappedByPte(StartPte));
    01398
    01399             DbgBreakPoint();
    01400         }
    01401         StartPte += 1;
    01402     }
    01403 }
    01404 #endif
    01405
    01406
    01407 VOID
    01408 MiSessionDeletePde(
    01409     IN PMMPTE Pde,
    01410     IN BOOLEAN WorkingSetInitialized,
    01411     IN PMMPTE SelfMapPde
    01412     )
    01413
    01414 /*++
    01415
    01416 Routine Description:
    01417
    01418     Used to delete a page directory entry from a session space.
    01419
    01420 Arguments:
    01421
    01422     Pde - Supplies the page directory entry to delete.
    01423
    01424     WorkingSetInitialized - Supplies TRUE if the working set has been
    01425                             initialized.
    01426
    01427     SelfMapPde - Supplies the page directory entry that contains the self map
    01428                  session page.
    01429
    01430
    01431 Return Value:
    01432
    01433     None.
    01434
    01435 Environment:
    01436
    01437     Kernel mode.  PFN lock held.
    01438
    01439 --*/
    01440
    01441 {
    01442     PMMPFN Pfn1;
    01443     PMMPFN Pfn2;
    01444     KIRQL OldIrql2;
    01445     PFN_NUMBER PageFrameIndex;
    01446     BOOLEAN SelfMapPage;
    01447 #if DBG
    01448     ULONG i;
    01449     PMMPTE PointerPte;
    01450 #endif
    01451
    01452     if (Pde->u.Long == ZeroKernelPte.u.Long) {
    01453         return;
    01454     }
    01455
    01456     SelfMapPage = (Pde == SelfMapPde ? TRUE : FALSE);
    01457
    01458     ASSERT (Pde->u.Hard.Valid == 1);
    01459
    01460     PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (Pde);
    01461     Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
    01462
    01463 #if DBG
    01464
    01465     ASSERT (PageFrameIndex <= MmHighestPhysicalPage);
    01466
    01467     if (WorkingSetInitialized == TRUE) {
    01468         ASSERT (Pfn1->u1.WsIndex);
    01469     }
    01470
    01471     ASSERT (Pfn1->u3.e1.PrototypePte == 0);
    01472     ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
    01473     ASSERT (Pfn1->PteFrame <= MmHighestPhysicalPage);
    01474
    01475     Pfn2 = MI_PFN_ELEMENT (Pfn1->PteFrame);
    01476
    01477     //
    01478     // Verify the containing page table is still the page
    01479     // table page mapping the session data structure.
    01480     //
    01481
    01482     if (SelfMapPage == FALSE) {
    01483
    01484         //
    01485         // Note these ASSERTs will fail if win32k leaks pool.
    01486         //
    01487
    01488         ASSERT (Pfn1->u2.ShareCount == 1);
    01489 #if !defined (_WIN64)
    01490
    01491         //
    01492         // 32-bit NT points the additional page tables at the master.
    01493         // 64-bit NT doesn't need to use this trick as there is always
    01494         // an additional hierarchy level.
    01495         //
    01496
    01497         ASSERT (Pfn1->PteFrame == MI_GET_PAGE_FRAME_FROM_PTE (SelfMapPde));
    01498 #endif
    01499         ASSERT (Pfn2->u2.ShareCount > 2);
    01500     }
    01501     else {
    01502         ASSERT (Pfn1 == Pfn2);
    01503         ASSERT (Pfn1->u2.ShareCount == 2);
    01504     }
    01505
    01506     //
    01507     // Make sure the page table page is zeroed.  It should always be zero
    01508     // unless win32k leaks pool or random bits get set.
    01509     //
    01510
    01511     PointerPte = (PMMPTE)MiMapPageInHyperSpace (PageFrameIndex, &OldIrql2);
    01512
    01513     i = 0;
    01514     while (i < PTE_PER_PAGE) {
    01515         if (PointerPte->u.Long != 0 && PointerPte->u.Long != MM_KERNEL_NOACCESS_PTE) {
    01516             DbgPrint("MM: Deleting session page table: Index %d PTE %p is not zero! %p\n",
    01517                 i,
    01518                 PointerPte,
    01519                 PointerPte->u.Long);
    01520             DbgBreakPoint();
    01521         }
    01522         PointerPte += 1;
    01523         i += 1;
    01524     }
    01525
    01526     MiUnmapPageInHyperSpace (OldIrql2);
    01527
    01528 #endif // DBG
    01529
    01530     if (SelfMapPage == FALSE) {
    01531         MiDecrementShareAndValidCount (Pfn1->PteFrame);
    01532     }
    01533     else {
    01534         ASSERT (Pfn1 == Pfn2);
    01535         ASSERT (Pfn1->u2.ShareCount == 2);
    01536         Pfn1->u2.ShareCount -= 1;
    01537     }
    01538
    01539     MI_SET_PFN_DELETED (Pfn1);
    01540     MiDecrementShareCountOnly (PageFrameIndex);
    01541 }
    01542
    01543 BOOLEAN
    01544 MiDereferenceSession(
    01545     VOID
    01546     )
    01547
    01548 /*++
    01549
    01550 Routine Description:
    01551
    01552     Decrement this process' reference count to the session, unmapping access
    01553     to the session for the current process.  If this is the last process
    01554     reference to this session, then the entire session will be destroyed upon
    01555     return.  This includes unloading drivers, unmapping pools, freeing
    01556     page tables, etc.
    01557
    01558 Arguments:
    01559
    01560     None.
    01561
    01562 Return Value:
    01563
    01564     TRUE if the session was deleted, FALSE if only this process' access to
    01565     the session was deleted.
    01566
    01567 Environment:
    01568
    01569     Kernel mode, no mutexes held, APCs disabled.
    01570
    01571 --*/
    01572
    01573 {
    01574     KIRQL OldIrql;
    01575     ULONG Index;
    01576     ULONG CountReleased;
    01577     ULONG SessionId;
    01578     PFN_NUMBER PageFrameIndex;
    01579     ULONG SessionDataPdeIndex;
    01580     KEVENT Event;
    01581     MMPTE PreviousPte;
    01582     PMMPFN Pfn1;
    01583     PMMPTE PointerPpe;
    01584     PMMPTE PointerPte;
    01585     PMMPTE EndPte;
    01586     PMMPTE GlobalPteEntrySave;
    01587     PMMPTE StartPde;
    01588     PMMPTE EndPde;
    01589     PMM_SESSION_SPACE SessionGlobal;
    01590     MMPTE SavePageTables[MI_SESSION_SPACE_PAGE_TABLES];
    01591     BOOLEAN WorkingSetWasInitialized;
    01592     ULONG AttachCount;
    01593     PEPROCESS Process;
    01594
    01595     ASSERT ((PsGetCurrentProcess()->Vm.u.Flags.ProcessInSession == 1) ||
    01596             ((MmSessionSpace->u.Flags.Initialized == 0) && (PsGetCurrentProcess()->Vm.u.Flags.SessionLeader == 1) && (MmSessionSpace->ReferenceCount == 1)));
    01597
    01598     SessionId = MmSessionSpace->SessionId;
    01599
    01600     ASSERT (RtlCheckBit (MiSessionIdBitmap, SessionId));
    01601
    01602     LOCK_SESSION (OldIrql);
    01603
    01604     if (MmSessionSpace->ReferenceCount > 1) {
    01605
    01606         MmSessionSpace->ReferenceCount -= 1;
    01607
    01608         UNLOCK_SESSION (OldIrql);
    01609
    01610         Process = PsGetCurrentProcess();
    01611
    01612         LOCK_EXPANSION (OldIrql);
    01613
    01614         Process->Vm.u.Flags.ProcessInSession = 0;
    01615
    01616         UNLOCK_EXPANSION (OldIrql);
    01617
    01618         //
    01619         // Don't delete any non-smss session space mappings here.  Let them
    01620         // live on through process death.  This handles the case where
    01621         // MmDispatchWin32Callout picks csrss - csrss has exited as it's not
    01622         // the last process (smss is).  smss is simultaneously detaching from
    01623         // the session and since it is the last process, it's waiting on
    01624         // the AttachCount below.  The dispatch callout ends up in csrss but
    01625         // has no way to synchronize against csrss exiting through this path
    01626         // as the object reference count doesn't stop it.  So leave the
    01627         // session space mappings alive so the callout can execute through
    01628         // the remains of csrss.
    01629         //
    01630         // Note that when smss detaches, the address space must get cleared
    01631         // here so that subsequent session creations by smss will succeed.
    01632         //
    01633
    01634         if (Process->Vm.u.Flags.SessionLeader == 1) {
    01635
    01636 #if defined (_WIN64)
    01637             StartPde = MiGetPpeAddress (MmSessionBase);
    01638             StartPde->u.Long = ZeroKernelPte.u.Long;
    01639 #else
    01640             StartPde = MiGetPdeAddress (MmSessionBase);
    01641             EndPde = MiGetPdeAddress (MI_SESSION_SPACE_END);
    01642             RtlZeroMemory (StartPde, (EndPde - StartPde) * sizeof(MMPTE));
    01643 #endif
    01644
    01645             //
    01646             // Flush the session space TB entries.
    01647             //
    01648
    01649             MI_FLUSH_SESSION_TB (OldIrql);
    01650         }
    01651
    01652 #if defined(_IA64_)
    01653         KeDetachSessionSpace();
    01654 #endif
    01655
    01656         return FALSE;
    01657     }
    01658
    01659     //
    01660     // Mark it as being deleted.
    01661     //
    01662
    01663     MmSessionSpace->u.Flags.BeingDeleted = 1;
    01664
    01665     //
    01666     // This is the final dereference.  We could be any process
    01667     // including SMSS when a session space load fails.  Note also that
    01668     // processes can terminate in any order as well.
    01669     //
    01670
    01671     UNLOCK_SESSION (OldIrql);
    01672
    01673     SessionGlobal = SESSION_GLOBAL (MmSessionSpace);
    01674
    01675     LOCK_EXPANSION (OldIrql);
    01676
    01677     //
    01678     // Wait for any cross-session process attaches to detach.  Refuse
    01679     // subsequent attempts to cross-session attach so the address invalidation
    01680     // code doesn't surprise an ongoing or subsequent attachee.
    01681     //
    01682
    01683     ASSERT (MmSessionSpace->u.Flags.DeletePending == 0);
    01684
    01685     MmSessionSpace->u.Flags.DeletePending = 1;
    01686
    01687     AttachCount = MmSessionSpace->AttachCount;
    01688
    01689     if (AttachCount) {
    01690
    01691         KeInitializeEvent (&MmSessionSpace->AttachEvent,
    01692                            NotificationEvent,
    01693                            FALSE);
    01694
    01695         UNLOCK_EXPANSION (OldIrql);
    01696
    01697         KeWaitForSingleObject( &MmSessionSpace->AttachEvent,
    01698                                WrVirtualMemory,
    01699                                KernelMode,
    01700                                FALSE,
    01701                                (PLARGE_INTEGER)NULL);
    01702
    01703         LOCK_EXPANSION (OldIrql);
    01704
    01705         ASSERT (MmSessionSpace->u.Flags.DeletePending == 1);
    01706         ASSERT (MmSessionSpace->AttachCount == 0);
    01707     }
    01708
    01709     if (MmSessionSpace->Vm.u.Flags.BeingTrimmed) {
    01710
    01711         //
    01712         // Initialize an event and put the event address
    01713         // in the VmSupport.  When the trimming is complete,
    01714         // this event will be set.
    01715         //
    01716
    01717         KeInitializeEvent(&Event, NotificationEvent, FALSE);
    01718
    01719         MmSessionSpace->Vm.WorkingSetExpansionLinks.Blink = (PLIST_ENTRY)&Event;
    01720
    01721         //
    01722         // Release the mutex and wait for the event.
    01723         //
    01724
    01725         KeEnterCriticalRegion();
    01726         UNLOCK_EXPANSION_AND_THEN_WAIT (OldIrql);
    01727
    01728         KeWaitForSingleObject(&Event,
    01729                               WrVirtualMemory,
    01730                               KernelMode,
    01731                               FALSE,
    01732                               (PLARGE_INTEGER)NULL);
    01733         KeLeaveCriticalRegion();
    01734
    01735         LOCK_EXPANSION (OldIrql);
    01736     }
    01737     else if (MmSessionSpace->u.Flags.WorkingSetInserted == 1) {
    01738
    01739         //
    01740         // Remove this session from the session list and the working
    01741         // set list.
    01742         //
    01743
    01744         RemoveEntryList (&SessionGlobal->Vm.WorkingSetExpansionLinks);
    01745
    01746         MmSessionSpace->u.Flags.WorkingSetInserted = 0;
    01747     }
    01748
    01749     if (MmSessionSpace->u.Flags.SessionListInserted == 1) {
    01750
    01751         RemoveEntryList (&SessionGlobal->WsListEntry);
    01752
    01753         MmSessionSpace->u.Flags.SessionListInserted = 0;
    01754     }
    01755
    01756     MiSessionCount -= 1;
    01757
    01758     UNLOCK_EXPANSION (OldIrql);
    01759
    01760 #if DBG
    01761     if (PsGetCurrentProcess()->Vm.u.Flags.SessionLeader == 0) {
    01762         ASSERT (MmSessionSpace->ProcessOutSwapCount == 0);
    01763         ASSERT (MmSessionSpace->ReferenceCount == 1);
    01764     }
    01765 #endif
    01766
    01767     MM_SNAP_SESS_MEMORY_COUNTERS(0);
    01768
    01769     //
    01770     // If an unload function has been registered for WIN32K.SYS,
    01771     // call it now before we force an unload on any modules.  WIN32K.SYS
    01772     // is responsible for calling any other loaded modules that have
    01773     // unload routines to be run.  Another option is to have the other
    01774     // session drivers register a DLL initialize/uninitialize pair on load.
    01775     //
    01776
    01777     if (MmSessionSpace->Win32KDriverObject.DriverUnload ) {
    01778         MmSessionSpace->Win32KDriverObject.DriverUnload (&MmSessionSpace->Win32KDriverObject);
    01779     }
    01780
    01781     //
    01782     // Now that all modules have had their unload routine(s)
    01783     // called, check for pool leaks before unloading the images.
    01784     //
    01785
    01786     MiCheckSessionPoolAllocations ();
    01787
    01788     ASSERT (MmSessionSpace->ReferenceCount == 1);
    01789
    01790     MM_SNAP_SESS_MEMORY_COUNTERS(1);
    01791
    01792     //
    01793     // Destroy the view mapping structures.
    01794     //
    01795
    01796     MiFreeSessionSpaceMap ();
    01797
    01798     MM_SNAP_SESS_MEMORY_COUNTERS(2);
    01799
    01800     //
    01801     // Walk down the list of modules we have loaded dereferencing them.
    01802     //
    01803     // This allows us to force an unload of any kernel images loaded by
    01804     // the session so we do not have any virtual space and paging
    01805     // file leaks.
    01806     //
    01807
    01808     MiSessionUnloadAllImages ();
    01809
    01810     MM_SNAP_SESS_MEMORY_COUNTERS(3);
    01811
    01812     //
    01813     // Destroy the session space bitmap structure
    01814     //
    01815
    01816     MiFreeSessionPoolBitMaps ();
    01817
    01818     MM_SNAP_SESS_MEMORY_COUNTERS(4);
    01819
    01820     //
    01821     // Reference the session space structure using its global
    01822     // kernel PTE based address. This is to avoid deleting it out
    01823     // from underneath ourselves.
    01824     //
    01825
    01826     GlobalPteEntrySave = MmSessionSpace->GlobalPteEntry;
    01827
    01828     //
    01829     // Sweep the individual regions in their proper order.
    01830     //
    01831
    01832 #if DBG
    01833
    01834     //
    01835     // Check the executable image region. All images
    01836     // should have been unloaded by the image handler.
    01837     //
    01838
    01839     MiCheckSessionVirtualSpace ((PVOID)MI_SESSION_IMAGE_START,
    01840                                 MI_SESSION_IMAGE_SIZE);
    01841 #endif
    01842
    01843     MM_SNAP_SESS_MEMORY_COUNTERS(5);
    01844
    01845 #if DBG
    01846
    01847     //
    01848     // Check the view region. All views should have been cleaned up already.
    01849     //
    01850
    01851     MiCheckSessionVirtualSpace ((PVOID)MI_SESSION_VIEW_START,
    01852                                 MI_SESSION_VIEW_SIZE);
    01853 #endif
    01854
    01855     //
    01856     // Save the page tables in the session space structure since
    01857     // we are going to tear it down.
    01858     //
    01859
    01860 #if defined (_WIN64)
    01861     RtlCopyMemory (SavePageTables,
    01862                    MiGetPdeAddress (MmSessionBase),
    01863                    MI_SESSION_SPACE_PAGE_TABLES * sizeof (MMPTE));
    01864 #else
    01865     RtlCopyMemory (SavePageTables,
    01866                    MmSessionSpace->PageTables,
    01867                    MI_SESSION_SPACE_PAGE_TABLES * sizeof (MMPTE));
    01868 #endif
    01869
    01870     MM_SNAP_SESS_MEMORY_COUNTERS(6);
    01871
    01872 #if DBG
    01873     //
    01874     // Check everything possible before the remaining virtual address space
    01875     // is torn down.  In this way if anything is amiss, the data can be
    01876     // more easily examined.
    01877     //
    01878
    01879     Pfn1 = MI_PFN_ELEMENT (MmSessionSpace->SessionPageDirectoryIndex);
    01880
    01881     //
    01882     // This should be greater than 1 because working set page tables are
    01883     // using this as their parent as well.
    01884     //
    01885
    01886     ASSERT (Pfn1->u2.ShareCount > 1);
    01887 #endif
    01888
    01889     CountReleased = 0;
    01890
    01891     if (MmSessionSpace->u.Flags.HasWsLock == 1) {
    01892
    01893         PointerPte = MiGetPteAddress (MI_SESSION_SPACE_WS);
    01894         EndPte = MiGetPteAddress (MmSessionSpace->Vm.VmWorkingSetList->HighestPermittedHashAddress);
    01895
    01896         for ( ; PointerPte < EndPte; PointerPte += 1) {
    01897
    01898             if (PointerPte->u.Long) {
    01899
    01900                 ASSERT (PointerPte->u.Hard.Valid == 1);
    01901                 MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_WS_PAGE_FREE, 1);
    01902
    01903                 MmSessionSpace->CommittedPages -= 1;
    01904                 MmSessionSpace->NonPagablePages -= 1;
    01905                 CountReleased += 1;
    01906             }
    01907         }
    01908         WorkingSetWasInitialized = TRUE;
    01909         MmSessionSpace->u.Flags.HasWsLock = 0;
    01910     }
    01911     else {
    01912         WorkingSetWasInitialized = FALSE;
    01913     }
    01914
    01915     //
    01916     // Account for the session data structure data page.  For NT64, the page
    01917     // directory page is also accounted for here.
    01918     //
    01919
    01920 #if defined (_WIN64)
    01921     MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGE_FREE, 2);
    01922     MmSessionSpace->CommittedPages -= 2;
    01923     MmSessionSpace->NonPagablePages -= 2;
    01924     CountReleased += 2;
    01925 #else
    01926     MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_INITIAL_PAGE_FREE, 1);
    01927     MmSessionSpace->CommittedPages -= 1;
    01928     MmSessionSpace->NonPagablePages -= 1;
    01929     CountReleased += 1;
    01930 #endif
    01931
    01932     //
    01933     // Account for any needed session space page tables.
    01934     //
    01935
    01936     for (Index = 0; Index < MI_SESSION_SPACE_PAGE_TABLES; Index += 1) {
    01937
    01938         StartPde = &SavePageTables[Index];
    01939
    01940         if (StartPde->u.Long != ZeroKernelPte.u.Long) {
    01941             MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_PAGETABLE_FREE, 1);
    01942             MmSessionSpace->CommittedPages -= 1;
    01943             MmSessionSpace->NonPagablePages -= 1;
    01944             CountReleased += 1;
    01945         }
    01946     }
    01947
    01948     ASSERT (MmSessionSpace->NonPagablePages == 0);
    01949
    01950     //
    01951     // Note that whenever win32k or drivers loaded by it leak pool, the
    01952     // ASSERT below will be triggered.
    01953     //
    01954
    01955     ASSERT (MmSessionSpace->CommittedPages == 0);
    01956
    01957     MiReturnCommitment (CountReleased);
    01958
    01959     MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SESSION_DEREFERENCE, CountReleased);
    01960
    01961     //
    01962     // Sweep the working set entries.
    01963     // No more accesses to the working set or its lock are allowed.
    01964     //
    01965
    01966     if (WorkingSetWasInitialized == TRUE) {
    01967         ExDeleteResource (&SessionGlobal->WsLock);
    01968
    01969         PointerPte = MiGetPteAddress (MI_SESSION_SPACE_WS);
    01970         EndPte = MiGetPteAddress (MmSessionSpace->Vm.VmWorkingSetList->HighestPermittedHashAddress);
    01971
    01972         for ( ; PointerPte < EndPte; PointerPte += 1) {
    01973
    01974             if (PointerPte->u.Long) {
    01975
    01976                 ASSERT (PointerPte->u.Hard.Valid == 1);
    01977
    01978                 //
    01979                 // Delete the page.
    01980                 //
    01981
    01982                 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
    01983
    01984                 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
    01985
    01986                 //
    01987                 // Each page should still be locked in the session working set.
    01988                 //
    01989
    01990                 LOCK_PFN (OldIrql);
    01991
    01992                 ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
    01993
    01994                 MiDecrementShareAndValidCount (Pfn1->PteFrame);
    01995                 MI_SET_PFN_DELETED (Pfn1);
    01996                 MiDecrementShareCountOnly (PageFrameIndex);
    01997                 MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
    01998
    01999                 MmResidentAvailablePages += 1;
    02000                 MM_BUMP_COUNTER(52, 1);
    02001
    02002                 UNLOCK_PFN (OldIrql);
    02003             }
    02004         }
    02005     }
    02006
    02007 #if defined(_IA64_)
    02008     KeDisableSessionSharing(&SessionGlobal->SessionMapInfo);
    02009 #endif
    02010
    02011     //
    02012     // Now delete the session space structure itself.
    02013     // No more accesses to MmSessionSpace after this.
    02014     //
    02015
    02016     PointerPte = MiGetPteAddress (MmSessionSpace);
    02017
    02018     //
    02019     // Delete the page.
    02020     //
    02021
    02022     PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
    02023
    02024     Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
    02025
    02026     //
    02027     // Make sure this page is still locked.
    02028     //
    02029
    02030     ASSERT (Pfn1->u1.WsIndex == 0);
    02031
    02032     LOCK_PFN (OldIrql);
    02033
    02034     ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
    02035
    02036     ASSERT (Pfn1->PteFrame == MmSessionSpace->SessionPageDirectoryIndex);
    02037
    02038     MiDecrementShareAndValidCount (Pfn1->PteFrame);
    02039     MI_SET_PFN_DELETED (Pfn1);
    02040     MiDecrementShareCountOnly (PageFrameIndex);
    02041     MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
    02042
    02043     MmResidentAvailablePages += CountReleased;
    02044
    02045     MM_BUMP_COUNTER(53, CountReleased);
    02046
    02047     MmResidentAvailablePages += MI_SESSION_SPACE_WORKING_SET_MINIMUM;
    02048
    02049     MM_BUMP_COUNTER(56, MI_SESSION_SPACE_WORKING_SET_MINIMUM);
    02050
    02051     UNLOCK_PFN (OldIrql);
    02052
    02053     StartPde = MiGetPdeAddress (MmSessionBase);
    02054
    02055     EndPde = MiGetPdeAddress (MI_SESSION_SPACE_END);
    02056
    02057     RtlZeroMemory (StartPde, (EndPde - StartPde) * sizeof(MMPTE));
    02058
    02059     //
    02060     // Flush the session space TB entries.
    02061     //
    02062
    02063     MI_FLUSH_SESSION_TB (OldIrql);
    02064
    02065     //
    02066     // Free the self-map PTE.
    02067     //
    02068
    02069     MiReleaseSystemPtes (GlobalPteEntrySave, 1, SystemPteSpace);
    02070
    02071     //
    02072     // Delete page table pages.  Note that the page table page mapping the
    02073     // session space data structure is done last so that we can apply
    02074     // various ASSERTs in the DeletePde routine.
    02075     //
    02076
    02077     SessionDataPdeIndex = MiGetPdeSessionIndex (MmSessionSpace);
    02078
    02079     LOCK_PFN (OldIrql);
    02080
    02081     for (Index = 0; Index < MI_SESSION_SPACE_PAGE_TABLES; Index += 1) {
    02082
    02083         if (Index == SessionDataPdeIndex) {
    02084
    02085             //
    02086             // The self map entry must be done last.
    02087             //
    02088
    02089             continue;
    02090         }
    02091
    02092         MiSessionDeletePde (&SavePageTables[Index],
    02093                             WorkingSetWasInitialized,
    02094                             &SavePageTables[SessionDataPdeIndex]);
    02095     }
    02096
    02097     MiSessionDeletePde (&SavePageTables[SessionDataPdeIndex],
    02098                         WorkingSetWasInitialized,
    02099                         &SavePageTables[SessionDataPdeIndex]);
    02100
    02101 #if defined (_WIN64)
    02102
    02103     //
    02104     // Delete the session page directory page.
    02105     //
    02106
    02107     PointerPpe = MiGetPpeAddress (MmSessionSpace);
    02108     PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPpe);
    02109     Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
    02110     MI_SET_PFN_DELETED (Pfn1);
    02111     MiDecrementShareCountOnly (PageFrameIndex);
    02112
    02113     MI_FLUSH_SINGLE_SESSION_TB (MiGetPdeAddress (MmSessionSpace),
    02114                                 TRUE,
    02115                                 TRUE,
    02116                                 (PHARDWARE_PTE)PointerPpe,
    02117                                 ZeroKernelPte.u.Flush,
    02118                                 PreviousPte);
    02119 #endif
    02120
    02121     UNLOCK_PFN (OldIrql);
    02122
    02123     ExAcquireFastMutex (&MiSessionIdMutex);
    02124
    02125     ASSERT (RtlCheckBit (MiSessionIdBitmap, SessionId));
    02126     RtlClearBits (MiSessionIdBitmap, SessionId, 1);
    02127
    02128     ExReleaseFastMutex (&MiSessionIdMutex);
    02129
    02130     LOCK_EXPANSION (OldIrql);
    02131
    02132     PsGetCurrentProcess()->Vm.u.Flags.ProcessInSession = 0;
    02133
    02134     UNLOCK_EXPANSION (OldIrql);
    02135
    02136     //
    02137     // The session space has been deleted and all TB flushing is complete.
    02138     //
    02139
    02140     return TRUE;
    02141 }
    02142
    02143 NTSTATUS
    02144 MiSessionCommitImagePages(
    02145     IN PVOID VirtualAddress,
    02146     IN SIZE_T NumberOfBytes
    02147     )
    02148
    02149 /*++
    02150
    02151 Routine Description:
    02152
    02153     This routine commits virtual memory within the current session space with
    02154     backing pages.  The virtual addresses within session space are
    02155     allocated with a separate facility in the image management facility.
    02156     This is because images must be at a unique system wide virtual address.
    02157
    02158 Arguments:
    02159
    02160     VirtualAddress - Supplies the first virtual address to commit.
    02161
    02162     NumberOfBytes - Supplies the number of bytes to commit.
    02163
    02164 Return Value:
    02165
    02166     STATUS_SUCCESS if all went well, STATUS_NO_MEMORY if the current process
    02167     has no session.
    02168
    02169 Environment:
    02170
    02171     Kernel mode, MmSystemLoadLock held.
    02172
    02173 --*/
    02174
    02175 {
    02176     KIRQL WsIrql;
    02177     KIRQL OldIrql;
    02178     ULONG Color;
    02179     PFN_NUMBER SizeInPages;
    02180     PMMPFN Pfn1;
    02181     ULONG_PTR AllocationStart;
    02182     PFN_NUMBER PageFrameIndex;
    02183     NTSTATUS Status;
    02184     PMMPTE StartPte, EndPte;
    02185     MMPTE TempPte;
    02186
    02187     SYSLOAD_LOCK_OWNED_BY_ME ();
    02188
    02189     if (NumberOfBytes == 0) {
    02190         return STATUS_SUCCESS;
    02191     }
    02192
    02193     if (MmIsAddressValid(MmSessionSpace) == FALSE) {
    02194 #if DBG
    02195         DbgPrint ("MiSessionCommitImagePages: No session space!\n");
    02196 #endif
    02197         return STATUS_NO_MEMORY;
    02198     }
    02199
    02200     ASSERT (((ULONG_PTR)VirtualAddress % PAGE_SIZE) == 0);
    02201     ASSERT ((NumberOfBytes % PAGE_SIZE) == 0);
    02202
    02203     SizeInPages = (PFN_NUMBER)(NumberOfBytes >> PAGE_SHIFT);
    02204
    02205     //
    02206     // Calculate pages needed.
    02207     //
    02208
    02209     AllocationStart = (ULONG_PTR)VirtualAddress;
    02210
    02211     //
    02212     // Lock the session space working set.
    02213     //
    02214
    02215     LOCK_SESSION_SPACE_WS(WsIrql);
    02216
    02217     //
    02218     // Make sure we have page tables for the PTE
    02219     // entries we must fill in the session space structure.
    02220     //
    02221
    02222     Status = MiSessionCommitPageTables ((PVOID)AllocationStart,
    02223                                         (PVOID)(AllocationStart + NumberOfBytes));
    02224
    02225     if (!NT_SUCCESS(Status)) {
    02226 #if DBG
    02227         if (MmDebug & MM_DBG_SESSIONS) {
    02228             DbgPrint("MiSessionCommitImagePages: Could not commit pagetables, Not enough memory! MmResidentAvailablePages %d, SizeInPages %d\n",
    02229                 MmResidentAvailablePages,
    02230                 SizeInPages);
    02231         }
    02232 #endif
    02233         UNLOCK_SESSION_SPACE_WS(WsIrql);
    02234         return STATUS_NO_MEMORY;
    02235     }
    02236
    02237     //
    02238     // go into loop allocating them and placing them into the page
    02239     // tables.
    02240     //
    02241
    02242     StartPte = MiGetPteAddress (AllocationStart);
    02243     EndPte = MiGetPteAddress (AllocationStart + NumberOfBytes);
    02244
    02245     if (MiChargeCommitment (SizeInPages, NULL) == FALSE) {
    02246         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
    02247         UNLOCK_SESSION_SPACE_WS(WsIrql);
    02248         return STATUS_NO_MEMORY;
    02249     }
    02250
    02251     MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_IMAGE_PAGES, SizeInPages);
    02252
    02253     TempPte = ValidKernelPteLocal;
    02254
    02255     LOCK_PFN (OldIrql);
    02256
    02257     //
    02258     // Check to make sure the physical pages are available.
    02259     //
    02260
    02261     if ((SPFN_NUMBER)SizeInPages > MI_NONPAGABLE_MEMORY_AVAILABLE() - 20) {
    02262         UNLOCK_PFN (OldIrql);
    02263         MiReturnCommitment (SizeInPages);
    02264         MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SESSION_IMAGE_FAILURE1, SizeInPages);
    02265         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_RESIDENT);
    02266         UNLOCK_SESSION_SPACE_WS(WsIrql);
    02267         return STATUS_NO_MEMORY;
    02268     }
    02269
    02270     MmResidentAvailablePages -= SizeInPages;
    02271
    02272     MM_BUMP_COUNTER(45, SizeInPages);
    02273
    02274     while (StartPte < EndPte) {
    02275
    02276         ASSERT (StartPte->u.Long == ZeroKernelPte.u.Long);
    02277
    02278         MiEnsureAvailablePageOrWait (NULL, NULL);
    02279
    02280         Color = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
    02281
    02282         PageFrameIndex = MiRemoveZeroPageIfAny (Color);
    02283         if (PageFrameIndex == 0) {
    02284             PageFrameIndex = MiRemoveAnyPage (Color);
    02285             UNLOCK_PFN (OldIrql);
    02286             MiZeroPhysicalPage (PageFrameIndex, Color);
    02287             LOCK_PFN (OldIrql);
    02288         }
    02289
    02290         TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
    02291         MI_WRITE_VALID_PTE (StartPte, TempPte);
    02292
    02293         Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
    02294
    02295         ASSERT (Pfn1->u1.WsIndex == 0);
    02296
    02297         MiInitializePfn (PageFrameIndex, StartPte, 1);
    02298
    02299         KeFillEntryTb ((PHARDWARE_PTE) StartPte, (PMMPTE)AllocationStart, FALSE);
    02300
    02301         StartPte += 1;
    02302         AllocationStart += PAGE_SIZE;
    02303     }
    02304
    02305     UNLOCK_PFN (OldIrql);
    02306
    02307     MmSessionSpace->CommittedPages += SizeInPages;
    02308     MmSessionSpace->NonPagablePages += SizeInPages;
    02309
    02310     MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_DRIVER_PAGES_LOCKED, SizeInPages);
    02311
    02312     UNLOCK_SESSION_SPACE_WS(WsIrql);
    02313
    02314     return STATUS_SUCCESS;
    02315 }
    02316
    02317 NTSTATUS
    02318 MiSessionCommitPageTables(
    02319     IN PVOID StartVa,
    02320     IN PVOID EndVa
    02321     )
    02322
    02323 /*++
    02324
    02325 Routine Description:
    02326
    02327     Fill in page tables covering the specified virtual address range.
    02328
    02329 Arguments:
    02330
    02331     StartVa - Supplies a starting virtual address.
    02332
    02333     EndVa - Supplies an ending virtual address.
    02334
    02335 Return Value:
    02336
    02337     STATUS_SUCCESS on success, STATUS_NO_MEMORY on failure.
    02338
    02339 Environment:
    02340
    02341     Kernel mode.  Session space working set mutex held.
    02342
    02343 --*/
    02344
    02345 {
    02346     KIRQL OldIrql;
    02347     ULONG Color;
    02348     ULONG Index;
    02349     PMMPTE StartPde, EndPde;
    02350     MMPTE TempPte;
    02351     PMMPFN Pfn1;
    02352     ULONG Entry;
    02353     ULONG SwapEntry;
    02354     PFN_NUMBER SizeInPages;
    02355     PFN_NUMBER PageTablePage;
    02356     PVOID SessionPte;
    02357     PMMWSL WorkingSetList;
    02358     CHAR SavePageTables[MI_SESSION_SPACE_PAGE_TABLES];
    02359
    02360     ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
    02361
    02362     MM_SESSION_SPACE_WS_LOCK_ASSERT();
    02363
    02364     ASSERT (StartVa >= (PVOID)MmSessionBase);
    02365     ASSERT (EndVa < (PVOID)MI_SESSION_SPACE_END);
    02366
    02367     //
    02368     // Allocate the page table pages, loading them
    02369     // into the current process's page directory.
    02370     //
    02371
    02372     StartPde = MiGetPdeAddress (StartVa);
    02373     EndPde = MiGetPdeAddress (EndVa);
    02374     Index = MiGetPdeSessionIndex (StartVa);
    02375
    02376     SizeInPages = 0;
    02377
    02378     while (StartPde <= EndPde) {
    02379 #if defined (_WIN64)
    02380         if (StartPde->u.Long == ZeroKernelPte.u.Long)
    02381 #else
    02382         if (MmSessionSpace->PageTables[Index].u.Long == ZeroKernelPte.u.Long)
    02383 #endif
    02384         {
    02385             SizeInPages += 1;
    02386         }
    02387         StartPde += 1;
    02388         Index += 1;
    02389     }
    02390
    02391     if (SizeInPages == 0) {
    02392         return STATUS_SUCCESS;
    02393     }
    02394
    02395     if (MiChargeCommitment (SizeInPages, NULL) == FALSE) {
    02396         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
    02397         return STATUS_NO_MEMORY;
    02398     }
    02399
    02400     MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_PAGETABLE_PAGES, SizeInPages);
    02401
    02402     StartPde = MiGetPdeAddress (StartVa);
    02403     Index = MiGetPdeSessionIndex (StartVa);
    02404
    02405     TempPte = ValidKernelPdeLocal;
    02406
    02407     LOCK_PFN (OldIrql);
    02408
    02409     //
    02410     // Check to make sure the physical pages are available.
    02411     //
    02412
    02413     if ((SPFN_NUMBER)SizeInPages > MI_NONPAGABLE_MEMORY_AVAILABLE() - 20) {
    02414         UNLOCK_PFN (OldIrql);
    02415         MiReturnCommitment (SizeInPages);
    02416         MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SESSION_PAGETABLE_PAGES, SizeInPages);
    02417         MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_RESIDENT);
    02418         return STATUS_NO_MEMORY;
    02419     }
    02420
    02421     MmResidentAvailablePages -= SizeInPages;
    02422
    02423     MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_PAGETABLE_ALLOC, SizeInPages);
    02424
    02425     MM_BUMP_COUNTER(44, SizeInPages);
    02426
    02427     WorkingSetList = MmSessionSpace->Vm.VmWorkingSetList;
    02428
    02429     RtlZeroMemory (SavePageTables, sizeof (SavePageTables));
    02430
    02431     while (StartPde <= EndPde) {
    02432
    02433 #if defined (_WIN64)
    02434         if (StartPde->u.Long == ZeroKernelPte.u.Long)
    02435 #else
    02436         if (MmSessionSpace->PageTables[Index].u.Long == ZeroKernelPte.u.Long)
    02437 #endif
    02438         {
    02439
    02440             ASSERT (StartPde->u.Hard.Valid == 0);
    02441
    02442             SavePageTables[Index] = 1;
    02443
    02444             MiEnsureAvailablePageOrWait (NULL, NULL);
    02445
    02446             Color = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
    02447
    02448             PageTablePage = MiRemoveZeroPageIfAny (Color);
    02449             if (PageTablePage == 0) {
    02450                 PageTablePage = MiRemoveAnyPage (Color);
    02451                 UNLOCK_PFN (OldIrql);
    02452                 MiZeroPhysicalPage (PageTablePage, Color);
    02453                 LOCK_PFN (OldIrql);
    02454             }
    02455
    02456             TempPte.u.Hard.PageFrameNumber = PageTablePage;
    02457             MI_WRITE_VALID_PTE (StartPde, TempPte);
    02458
    02459 #if !defined (_WIN64)
    02460             MmSessionSpace->PageTables[Index] = TempPte;
    02461 #endif
    02462             MmSessionSpace->NonPagablePages += 1;
    02463             MmSessionSpace->CommittedPages += 1;
    02464
    02465             MiInitializePfnForOtherProcess (PageTablePage,
    02466                                             StartPde,
    02467                                             MmSessionSpace->SessionPageDirectoryIndex);
    02468         }
    02469
    02470         StartPde += 1;
    02471         Index += 1;
    02472     }
    02473
    02474     UNLOCK_PFN (OldIrql);
    02475
    02476     StartPde = MiGetPdeAddress (StartVa);
    02477     Index = MiGetPdeSessionIndex (StartVa);
    02478
    02479     while (StartPde <= EndPde) {
    02480
    02481         if (SavePageTables[Index] == 1) {
    02482
    02483             ASSERT (StartPde->u.Hard.Valid == 1);
    02484
    02485             PageTablePage = MI_GET_PAGE_FRAME_FROM_PTE (StartPde);
    02486
    02487             Pfn1 = MI_PFN_ELEMENT (PageTablePage);
    02488
    02489             ASSERT (Pfn1->u1.Event == NULL);
    02490             Pfn1->u1.Event = (PVOID)PsGetCurrentThread ();
    02491
    02492             SessionPte = MiGetVirtualAddressMappedByPte (StartPde);
    02493
    02494             MiAddValidPageToWorkingSet (SessionPte,
    02495                                         StartPde,
    02496                                         Pfn1,
    02497                                         0);
    02498
    02499             Entry = MiLocateWsle (SessionPte,
    02500                                   MmSessionSpace->Vm.VmWorkingSetList,
    02501                                   Pfn1->u1.WsIndex);
    02502
    02503             if (Entry >= WorkingSetList->FirstDynamic) {
    02504
    02505                 SwapEntry = WorkingSetList->FirstDynamic;
    02506
    02507                 if (Entry != WorkingSetList->FirstDynamic) {
    02508
    02509                     //
    02510                     // Swap this entry with the one at first dynamic.
    02511                     //
    02512
    02513                     MiSwapWslEntries (Entry, SwapEntry, &MmSessionSpace->Vm);
    02514                 }
    02515
    02516                 WorkingSetList->FirstDynamic += 1;
    02517             }
    02518             else {
    02519                 SwapEntry = Entry;
    02520             }
    02521
    02522             //
    02523             // Indicate that the page is locked.
    02524             //
    02525
    02526             MmSessionSpace->Wsle[SwapEntry].u1.e1.LockedInWs = 1;
    02527         }
    02528
    02529         StartPde += 1;
    02530         Index += 1;
    02531     }
    02532
    02533     return STATUS_SUCCESS;
    02534 }
    02535
    02536 #if DBG
    02537 typedef struct _MISWAP {
    02538     ULONG Flag;
    02539     PEPROCESS Process;
    02540     PMM_SESSION_SPACE Session;
    02541     ULONG OutSwapCount;
    02542 } MISWAP, *PMISWAP;
    02543
    02544 ULONG MiSessionInfo[4];
    02545 MISWAP MiSessionSwap[0x100];
    02546 ULONG  MiSwapIndex;
    02547 #endif
    02548
    02549
    02550 VOID
    02551 MiSessionOutSwapProcess (
    02552     IN PEPROCESS Process
    02553     )
    02554
    02555 /*++
    02556
    02557 Routine Description:
    02558
    02559     This routine notifies the containing session that the specified process is
    02560     being outswapped.  When all the processes within a session have been
    02561     outswapped, the containing session undergoes a heavy trim.
    02562
    02563 Arguments:
    02564
    02565     Process - Supplies a pointer to the process that is swapped out of memory.
    02566
    02567 Return Value:
    02568
    02569     None.
    02570
    02571 Environment:
    02572
    02573     Kernel mode.  This routine must not enter a wait state for memory resources
    02574     or the system will deadlock.
    02575
    02576 --*/
    02577
    02578 {
    02579     MMPTE TempPte;
    02580     PFN_NUMBER PdePage;
    02581     PMMPTE PageDirectoryMap;
    02582     KIRQL OldIrql;
    02583     PFN_NUMBER SessionPage;
    02584     PMM_SESSION_SPACE SessionGlobal;
    02585 #if DBG
    02586     ULONG InCount;
    02587     ULONG OutCount;
    02588     PLIST_ENTRY NextEntry;
    02589 #endif
    02590 #if defined (_X86PAE_)
    02591     ULONG i;
    02592     PPAE_ENTRY PaeVa;
    02593 #endif
    02594
    02595     ASSERT (MiHydra == TRUE && Process->Vm.u.Flags.ProcessInSession == 1);
    02596
    02597     //
    02598     // smss doesn't count when we swap it before it has detached from the
    02599     // session it is currently creating.
    02600     //
    02601
    02602     if (Process->Vm.u.Flags.SessionLeader == 1) {
    02603         return;
    02604     }
    02605
    02606 #if defined (_X86PAE_)
    02607     PaeVa = Process->PaeTop;
    02608
    02609 #if DBG
    02610     for (i = 0; i < PD_PER_SYSTEM; i += 1) {
    02611         ASSERT (PaeVa->PteEntry[i].u.Hard.Valid == 1);
    02612     }
    02613 #endif
    02614
    02615     PdePage = MI_GET_PAGE_FRAME_FROM_PTE (&PaeVa->PteEntry[MiGetPdPteOffset(MmSessionSpace)]);
    02616
    02617 #else
    02618
    02619     PdePage = MI_GET_DIRECTORY_FRAME_FROM_PROCESS(Process);
    02620
    02621 #endif
    02622
    02623     PageDirectoryMap = MiMapPageInHyperSpace (PdePage, &OldIrql);
    02624
    02625 #if defined (_WIN64)
    02626     TempPte = PageDirectoryMap[MiGetPpeOffset(MmSessionSpace)];
    02627
    02628     MiUnmapPageInHyperSpace (OldIrql);
    02629
    02630     PdePage = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
    02631
    02632     PageDirectoryMap = MiMapPageInHyperSpace (PdePage, &OldIrql);
    02633 #endif
    02634
    02635     TempPte = PageDirectoryMap[MiGetPdeOffset(MmSessionSpace)];
    02636
    02637     MiUnmapPageInHyperSpace (OldIrql);
    02638
    02639     PdePage = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
    02640
    02641     PageDirectoryMap = MiMapPageInHyperSpace (PdePage, &OldIrql);
    02642
    02643     TempPte = PageDirectoryMap[MiGetPteOffset(MmSessionSpace)];
    02644
    02645     MiUnmapPageInHyperSpace (OldIrql);
    02646
    02647     SessionPage = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
    02648
    02649     SessionGlobal = (PMM_SESSION_SPACE) MiMapPageInHyperSpace ( SessionPage,
    02650                                                                 &OldIrql);
    02651
    02652     ASSERT (MI_GET_PAGE_FRAME_FROM_PTE (MiGetPteAddress(SessionGlobal->GlobalVirtualAddress)) == SessionPage);
    02653
    02654     SessionGlobal = SessionGlobal->GlobalVirtualAddress;
    02655
    02656     MiUnmapPageInHyperSpace (OldIrql);
    02657
    02658     LOCK_EXPANSION (OldIrql);
    02659
    02660     SessionGlobal->ProcessOutSwapCount += 1;
    02661
    02662 #if DBG
    02663     ASSERT ((LONG)SessionGlobal->ProcessOutSwapCount > 0);
    02664
    02665     InCount = 0;
    02666     OutCount = 0;
    02667     NextEntry = SessionGlobal->ProcessList.Flink;
    02668
    02669     while (NextEntry != &SessionGlobal->ProcessList) {
    02670         Process = CONTAINING_RECORD (NextEntry, EPROCESS, SessionProcessLinks);
    02671
    02672         if (Process->ProcessOutswapEnabled == TRUE) {
    02673             OutCount += 1;
    02674         }
    02675         else {
    02676             InCount += 1;
    02677         }
    02678
    02679         NextEntry = NextEntry->Flink;
    02680     }
    02681
    02682     if (InCount + OutCount > SessionGlobal->ReferenceCount) {
    02683         DbgPrint ("MiSessionOutSwapProcess : process count mismatch %p %x %x %x\n",
    02684             SessionGlobal,
    02685             SessionGlobal->ReferenceCount,
    02686             InCount,
    02687             OutCount);
    02688         DbgBreakPoint ();
    02689     }
    02690
    02691     if (SessionGlobal->ProcessOutSwapCount != OutCount) {
    02692         DbgPrint ("MiSessionOutSwapProcess : out count mismatch %p %x %x %x %x\n",
    02693             SessionGlobal,
    02694             SessionGlobal->ReferenceCount,
    02695             SessionGlobal->ProcessOutSwapCount,
    02696             InCount,
    02697             OutCount);
    02698         DbgBreakPoint ();
    02699     }
    02700
    02701     ASSERT (SessionGlobal->ProcessOutSwapCount <= SessionGlobal->ReferenceCount);
    02702
    02703     MiSessionSwap[MiSwapIndex].Flag = 1;
    02704     MiSessionSwap[MiSwapIndex].Process = Process;
    02705     MiSessionSwap[MiSwapIndex].Session = SessionGlobal;
    02706     MiSessionSwap[MiSwapIndex].OutSwapCount = SessionGlobal->ProcessOutSwapCount;
    02707     MiSwapIndex += 1;
    02708     if (MiSwapIndex == 0x100) {
    02709         MiSwapIndex = 0;
    02710     }
    02711 #endif
    02712
    02713     if (SessionGlobal->ProcessOutSwapCount == SessionGlobal->ReferenceCount) {
    02714         SessionGlobal->Vm.u.Flags.TrimHard = 1;
    02715 #if DBG
    02716         if (MmDebug & MM_DBG_SESSIONS) {
    02717             DbgPrint ("Mm: Last process (%d total) just swapped out for session %d, %d pages\n",
    02718                 SessionGlobal->ProcessOutSwapCount,
    02719                 SessionGlobal->SessionId,
    02720                 SessionGlobal->Vm.WorkingSetSize);
    02721         }
    02722         MiSessionInfo[0] += 1;
    02723 #endif
    02724         KeQuerySystemTime (&SessionGlobal->LastProcessSwappedOutTime);
    02725     }
    02726 #if DBG
    02727     else {
    02728         MiSessionInfo[1] += 1;
    02729     }
    02730 #endif
    02731
    02732     UNLOCK_EXPANSION (OldIrql);
    02733 }
    02734
    02735
    02736 VOID
    02737 MiSessionInSwapProcess (
    02738     IN PEPROCESS Process
    02739     )
    02740
    02741 /*++
    02742
    02743 Routine Description:
    02744
    02745     This routine in swaps the specified process.
    02746
    02747 Arguments:
    02748
    02749     Process - Supplies a pointer to the process that is to be swapped
    02750         into memory.
    02751
    02752 Return Value:
    02753
    02754     None.
    02755
    02756 Environment:
    02757
    02758     Kernel mode.  This routine must not enter a wait state for memory resources
    02759     or the system will deadlock.
    02760
    02761 --*/
    02762
    02763 {
    02764     MMPTE TempPte;
    02765     PFN_NUMBER PdePage;
    02766     PMMPTE PageDirectoryMap;
    02767     KIRQL OldIrql;
    02768     PFN_NUMBER SessionPage;
    02769     PMM_SESSION_SPACE SessionGlobal;
    02770 #if DBG
    02771     ULONG InCount;
    02772     ULONG OutCount;
    02773     PLIST_ENTRY NextEntry;
    02774 #endif
    02775 #if defined (_X86PAE_)
    02776     ULONG i;
    02777     PPAE_ENTRY PaeVa;
    02778 #endif
    02779
    02780     ASSERT (MiHydra == TRUE && Process->Vm.u.Flags.ProcessInSession == 1);
    02781
    02782     //
    02783     // smss doesn't count when we catch it before it has detached from the
    02784     // session it is currently creating.
    02785     //
    02786
    02787     if (Process->Vm.u.Flags.SessionLeader == 1) {
    02788         return;
    02789     }
    02790
    02791     ASSERT (MiHydra == TRUE && Process->Vm.u.Flags.ProcessInSession == 1);
    02792
    02793     //
    02794     // smss doesn't count when we swap it before it has detached from the
    02795     // session it is currently creating.
    02796     //
    02797
    02798     if (Process->Vm.u.Flags.SessionLeader == 1) {
    02799         return;
    02800     }
    02801
    02802 #if defined (_X86PAE_)
    02803     PaeVa = Process->PaeTop;
    02804
    02805 #if DBG
    02806     for (i = 0; i < PD_PER_SYSTEM; i += 1) {
    02807         ASSERT (PaeVa->PteEntry[i].u.Hard.Valid == 1);
    02808     }
    02809 #endif
    02810
    02811     PdePage = MI_GET_PAGE_FRAME_FROM_PTE (&PaeVa->PteEntry[MiGetPdPteOffset(MmSessionSpace)]);
    02812
    02813 #else
    02814     PdePage = MI_GET_DIRECTORY_FRAME_FROM_PROCESS(Process);
    02815 #endif
    02816
    02817     PageDirectoryMap = MiMapPageInHyperSpace (PdePage, &OldIrql);
    02818
    02819 #if defined (_WIN64)
    02820     TempPte = PageDirectoryMap[MiGetPpeOffset(MmSessionSpace)];
    02821
    02822     MiUnmapPageInHyperSpace (OldIrql);
    02823
    02824     PdePage = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
    02825
    02826     PageDirectoryMap = MiMapPageInHyperSpace (PdePage, &OldIrql);
    02827 #endif
    02828
    02829     TempPte = PageDirectoryMap[MiGetPdeOffset(MmSessionSpace)];
    02830
    02831     MiUnmapPageInHyperSpace (OldIrql);
    02832
    02833     PdePage = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
    02834
    02835     PageDirectoryMap = MiMapPageInHyperSpace (PdePage, &OldIrql);
    02836
    02837     TempPte = PageDirectoryMap[MiGetPteOffset(MmSessionSpace)];
    02838
    02839     MiUnmapPageInHyperSpace (OldIrql);
    02840
    02841     SessionPage = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
    02842
    02843     SessionGlobal = (PMM_SESSION_SPACE) MiMapPageInHyperSpace (SessionPage,
    02844                                                                &OldIrql);
    02845
    02846     ASSERT (MI_GET_PAGE_FRAME_FROM_PTE (MiGetPteAddress(SessionGlobal->GlobalVirtualAddress)) == SessionPage);
    02847
    02848     SessionGlobal = SessionGlobal->GlobalVirtualAddress;
    02849
    02850     MiUnmapPageInHyperSpace (OldIrql);
    02851
    02852     LOCK_EXPANSION (OldIrql);
    02853
    02854 #if DBG
    02855     ASSERT ((LONG)SessionGlobal->ProcessOutSwapCount > 0);
    02856
    02857     InCount = 0;
    02858     OutCount = 0;
    02859     NextEntry = SessionGlobal->ProcessList.Flink;
    02860
    02861     while (NextEntry != &SessionGlobal->ProcessList) {
    02862         Process = CONTAINING_RECORD (NextEntry, EPROCESS, SessionProcessLinks);
    02863
    02864         if (Process->ProcessOutswapEnabled == TRUE) {
    02865             OutCount += 1;
    02866         }
    02867         else {
    02868             InCount += 1;
    02869         }
    02870
    02871         NextEntry = NextEntry->Flink;
    02872     }
    02873
    02874     if (InCount + OutCount > SessionGlobal->ReferenceCount) {
    02875         DbgPrint ("MiSessionInSwapProcess : count mismatch %p %x %x %x\n",
    02876             SessionGlobal,
    02877             SessionGlobal->ReferenceCount,
    02878             InCount,
    02879             OutCount);
    02880         DbgBreakPoint ();
    02881     }
    02882
    02883     if (SessionGlobal->ProcessOutSwapCount != OutCount) {
    02884         DbgPrint ("MiSessionInSwapProcess : out count mismatch %p %x %x %x %x\n",
    02885             SessionGlobal,
    02886             SessionGlobal->ReferenceCount,
    02887             SessionGlobal->ProcessOutSwapCount,
    02888             InCount,
    02889             OutCount);
    02890         DbgBreakPoint ();
    02891     }
    02892
    02893     ASSERT (SessionGlobal->ProcessOutSwapCount <= SessionGlobal->ReferenceCount);
    02894
    02895     MiSessionSwap[MiSwapIndex].Flag = 2;
    02896     MiSessionSwap[MiSwapIndex].Process = Process;
    02897     MiSessionSwap[MiSwapIndex].Session = SessionGlobal;
    02898     MiSessionSwap[MiSwapIndex].OutSwapCount = SessionGlobal->ProcessOutSwapCount;
    02899     MiSwapIndex += 1;
    02900     if (MiSwapIndex == 0x100) {
    02901         MiSwapIndex = 0;
    02902     }
    02903 #endif
    02904
    02905     if (SessionGlobal->ProcessOutSwapCount == SessionGlobal->ReferenceCount) {
    02906 #if DBG
    02907         MiSessionInfo[2] += 1;
    02908         if (MmDebug & MM_DBG_SESSIONS) {
    02909             DbgPrint ("Mm: First process (%d total) just swapped back in for session %d, %d pages\n",
    02910                 SessionGlobal->ProcessOutSwapCount,
    02911                 SessionGlobal->SessionId,
    02912                 SessionGlobal->Vm.WorkingSetSize);
    02913         }
    02914 #endif
    02915         SessionGlobal->Vm.u.Flags.TrimHard = 0;
    02916     }
    02917 #if DBG
    02918     else {
    02919         MiSessionInfo[3] += 1;
    02920     }
    02921 #endif
    02922
    02923     SessionGlobal->ProcessOutSwapCount -= 1;
    02924
    02925     ASSERT ((LONG)SessionGlobal->ProcessOutSwapCount >= 0);
    02926
    02927     UNLOCK_EXPANSION (OldIrql);
    02928 }