xref: /reactos/ntoskrnl/mm/ARM3/session.c (revision 6d8aafb6)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/session.c
5  * PURPOSE:         Session support routines
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  *                  Timo Kreuzer (timo.kreuzer@reactos.org)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
18 
19 /* GLOBALS ********************************************************************/
20 
21 PMM_SESSION_SPACE MmSessionSpace;
22 PFN_NUMBER MiSessionDataPages, MiSessionTagPages, MiSessionTagSizePages;
23 PFN_NUMBER MiSessionBigPoolPages, MiSessionCreateCharge;
24 KGUARDED_MUTEX MiSessionIdMutex;
25 LONG MmSessionDataPages;
26 PRTL_BITMAP MiSessionIdBitmap;
27 volatile LONG MiSessionLeaderExists;
28 
29 LIST_ENTRY MiSessionWsList;
30 LIST_ENTRY MmWorkingSetExpansionHead;
31 
32 KSPIN_LOCK MmExpansionLock;
33 PETHREAD MiExpansionLockOwner;
34 
35 
36 /* PRIVATE FUNCTIONS **********************************************************/
37 
38 VOID
39 NTAPI
40 MiInitializeSessionWsSupport(VOID)
41 {
42     /* Initialize the list heads */
43     InitializeListHead(&MiSessionWsList);
44     InitializeListHead(&MmWorkingSetExpansionHead);
45 }
46 
47 BOOLEAN
48 NTAPI
49 MmIsSessionAddress(IN PVOID Address)
50 {
51     /* Check if it is in range */
52     return MI_IS_SESSION_ADDRESS(Address) ? TRUE : FALSE;
53 }
54 
55 LCID
56 NTAPI
57 MmGetSessionLocaleId(VOID)
58 {
59     PEPROCESS Process;
60     PAGED_CODE();
61 
62     //
63     // Get the current process
64     //
65     Process = PsGetCurrentProcess();
66 
67     //
68     // Check if it's NOT the Session Leader
69     //
70     if (!Process->Vm.Flags.SessionLeader)
71     {
72         //
73         // Make sure it has a valid Session
74         //
75         if (Process->Session)
76         {
77             //
78             // Get the Locale ID
79             //
80             return ((PMM_SESSION_SPACE)Process->Session)->LocaleId;
81         }
82     }
83 
84     //
85     // Not a session leader, return the default
86     //
87     return PsDefaultThreadLocaleId;
88 }
89 
90 _IRQL_requires_max_(APC_LEVEL)
91 VOID
92 NTAPI
93 MmSetSessionLocaleId(
94     _In_ LCID LocaleId)
95 {
96     PEPROCESS CurrentProcess;
97     PAGED_CODE();
98 
99     /* Get the current process and check if it is in a session */
100     CurrentProcess = PsGetCurrentProcess();
101     if ((CurrentProcess->Vm.Flags.SessionLeader == 0) &&
102         (CurrentProcess->Session != NULL))
103     {
104         /* Set the session locale Id */
105         ((PMM_SESSION_SPACE)CurrentProcess->Session)->LocaleId = LocaleId;
106     }
107     else
108     {
109         /* Set the default locale */
110         PsDefaultThreadLocaleId = LocaleId;
111     }
112 }
113 
114 
115 VOID
116 NTAPI
117 MiInitializeSessionIds(VOID)
118 {
119     ULONG Size, BitmapSize;
120     PFN_NUMBER TotalPages;
121 
122     /* Setup the total number of data pages needed for the structure */
123     TotalPages = MI_SESSION_DATA_PAGES_MAXIMUM;
124     MiSessionDataPages = ROUND_TO_PAGES(sizeof(MM_SESSION_SPACE)) >> PAGE_SHIFT;
125     ASSERT(MiSessionDataPages <= MI_SESSION_DATA_PAGES_MAXIMUM - 3);
126     TotalPages -= MiSessionDataPages;
127 
128     /* Setup the number of pages needed for session pool tags */
129     MiSessionTagSizePages = 2;
130     MiSessionBigPoolPages = 1;
131     MiSessionTagPages = MiSessionTagSizePages + MiSessionBigPoolPages;
132     ASSERT(MiSessionTagPages <= TotalPages);
133     ASSERT(MiSessionTagPages < MI_SESSION_TAG_PAGES_MAXIMUM);
134 
135     /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */
136     MiSessionCreateCharge = 1 + MiSessionDataPages + MiSessionTagPages;
137 
138     /* Initialize the lock */
139     KeInitializeGuardedMutex(&MiSessionIdMutex);
140 
141     /* Allocate the bitmap */
142     Size = MI_INITIAL_SESSION_IDS;
143     BitmapSize = ((Size + 31) / 32) * sizeof(ULONG);
144     MiSessionIdBitmap = ExAllocatePoolWithTag(PagedPool,
145                                               sizeof(RTL_BITMAP) + BitmapSize,
146                                               TAG_MM);
147     if (MiSessionIdBitmap)
148     {
149         /* Free all the bits */
150         RtlInitializeBitMap(MiSessionIdBitmap,
151                             (PVOID)(MiSessionIdBitmap + 1),
152                             Size);
153         RtlClearAllBits(MiSessionIdBitmap);
154     }
155     else
156     {
157         /* Die if we couldn't allocate the bitmap */
158         KeBugCheckEx(INSTALL_MORE_MEMORY,
159                      MmNumberOfPhysicalPages,
160                      MmLowestPhysicalPage,
161                      MmHighestPhysicalPage,
162                      0x200);
163     }
164 }
165 
166 VOID
167 NTAPI
168 MiSessionLeader(IN PEPROCESS Process)
169 {
170     KIRQL OldIrql;
171 
172     /* Set the flag while under the expansion lock */
173     OldIrql = MiAcquireExpansionLock();
174     Process->Vm.Flags.SessionLeader = TRUE;
175     MiReleaseExpansionLock(OldIrql);
176 }
177 
178 ULONG
179 NTAPI
180 MmGetSessionId(IN PEPROCESS Process)
181 {
182     PMM_SESSION_SPACE SessionGlobal;
183 
184     /* The session leader is always session zero */
185     if (Process->Vm.Flags.SessionLeader == 1) return 0;
186 
187     /* Otherwise, get the session global, and read the session ID from it */
188     SessionGlobal = (PMM_SESSION_SPACE)Process->Session;
189     if (!SessionGlobal) return 0;
190     return SessionGlobal->SessionId;
191 }
192 
193 ULONG
194 NTAPI
195 MmGetSessionIdEx(IN PEPROCESS Process)
196 {
197     PMM_SESSION_SPACE SessionGlobal;
198 
199     /* The session leader is always session zero */
200     if (Process->Vm.Flags.SessionLeader == 1) return 0;
201 
202     /* Otherwise, get the session global, and read the session ID from it */
203     SessionGlobal = (PMM_SESSION_SPACE)Process->Session;
204     if (!SessionGlobal) return -1;
205     return SessionGlobal->SessionId;
206 }
207 
208 VOID
209 NTAPI
210 MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal)
211 {
212     ULONG i, SessionId;
213     PMMPTE PointerPte;
214     PFN_NUMBER PageFrameIndex[MI_SESSION_DATA_PAGES_MAXIMUM];
215     PMMPFN Pfn1;
216     KIRQL OldIrql;
217 
218     /* Is there more than just this reference? If so, bail out */
219     if (InterlockedDecrement(&SessionGlobal->ProcessReferenceToSession)) return;
220 
221     /* Get the session ID */
222     SessionId = SessionGlobal->SessionId;
223     DPRINT1("Last process in session %lu going down!!!\n", SessionId);
224 
225     /* Free the session page tables */
226 #ifndef _M_AMD64
227     ExFreePoolWithTag(SessionGlobal->PageTables, 'tHmM');
228 #endif
229     ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal));
230 
231     /* Capture the data page PFNs */
232     PointerPte = MiAddressToPte(SessionGlobal);
233     for (i = 0; i < MiSessionDataPages; i++)
234     {
235         PageFrameIndex[i] = PFN_FROM_PTE(PointerPte + i);
236     }
237 
238     /* Release them */
239     MiReleaseSystemPtes(PointerPte, MiSessionDataPages, SystemPteSpace);
240 
241     /* Mark them as deleted */
242     for (i = 0; i < MiSessionDataPages; i++)
243     {
244         Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
245         MI_SET_PFN_DELETED(Pfn1);
246     }
247 
248     /* Loop every data page and drop a reference count */
249     OldIrql = MiAcquirePfnLock();
250     for (i = 0; i < MiSessionDataPages; i++)
251     {
252         /* Sanity check that the page is correct, then decrement it */
253         Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
254         ASSERT(Pfn1->u2.ShareCount == 1);
255         ASSERT(Pfn1->u3.e2.ReferenceCount == 1);
256         MiDecrementShareCount(Pfn1, PageFrameIndex[i]);
257     }
258 
259     /* Done playing with pages, release the lock */
260     MiReleasePfnLock(OldIrql);
261 
262     /* Decrement the number of data pages */
263     InterlockedDecrement(&MmSessionDataPages);
264 
265     /* Free this session ID from the session bitmap */
266     KeAcquireGuardedMutex(&MiSessionIdMutex);
267     ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId));
268     RtlClearBit(MiSessionIdBitmap, SessionId);
269     KeReleaseGuardedMutex(&MiSessionIdMutex);
270 }
271 
272 VOID
273 NTAPI
274 MiDereferenceSessionFinal(VOID)
275 {
276     PMM_SESSION_SPACE SessionGlobal;
277     KIRQL OldIrql;
278 
279     /* Get the pointer to the global session address */
280     SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
281 
282     /* Acquire the expansion lock */
283     OldIrql = MiAcquireExpansionLock();
284 
285     /* Set delete pending flag, so that processes can no longer attach to this
286        session and the last process that detaches sets the AttachEvent */
287     ASSERT(SessionGlobal->u.Flags.DeletePending == 0);
288     SessionGlobal->u.Flags.DeletePending = 1;
289 
290     /* Check if we have any attached processes */
291     if (SessionGlobal->AttachCount)
292     {
293         /* Initialize the event (it's not in use yet!) */
294         KeInitializeEvent(&SessionGlobal->AttachEvent, NotificationEvent, FALSE);
295 
296         /* Release the expansion lock for the wait */
297         MiReleaseExpansionLock(OldIrql);
298 
299         /* Wait for the event to be set due to the last process detach */
300         KeWaitForSingleObject(&SessionGlobal->AttachEvent, WrVirtualMemory, 0, 0, 0);
301 
302         /* Reacquire the expansion lock */
303         OldIrql = MiAcquireExpansionLock();
304 
305         /* Makes sure we still have the delete flag and no attached processes */
306         ASSERT(MmSessionSpace->u.Flags.DeletePending == 1);
307         ASSERT(MmSessionSpace->AttachCount == 0);
308     }
309 
310     /* Check if the session is in the workingset expansion list */
311     if (SessionGlobal->Vm.WorkingSetExpansionLinks.Flink != NULL)
312     {
313         /* Remove the session from the list and zero the list entry */
314         RemoveEntryList(&SessionGlobal->Vm.WorkingSetExpansionLinks);
315         SessionGlobal->Vm.WorkingSetExpansionLinks.Flink = 0;
316     }
317 
318     /* Check if the session is in the workingset list */
319     if (SessionGlobal->WsListEntry.Flink)
320     {
321         /* Remove the session from the list and zero the list entry */
322         RemoveEntryList(&SessionGlobal->WsListEntry);
323         SessionGlobal->WsListEntry.Flink = NULL;
324     }
325 
326     /* Release the expansion lock */
327     MiReleaseExpansionLock(OldIrql);
328 
329     /* Check for a win32k unload routine */
330     if (SessionGlobal->Win32KDriverUnload)
331     {
332         /* Call it */
333         SessionGlobal->Win32KDriverUnload(NULL);
334     }
335 }
336 
337 
338 VOID
339 NTAPI
340 MiDereferenceSession(VOID)
341 {
342     PMM_SESSION_SPACE SessionGlobal;
343     PEPROCESS Process;
344     ULONG ReferenceCount, SessionId;
345 
346     /* Sanity checks */
347     ASSERT(PsGetCurrentProcess()->ProcessInSession ||
348            ((MmSessionSpace->u.Flags.Initialized == 0) &&
349             (PsGetCurrentProcess()->Vm.Flags.SessionLeader == 1) &&
350             (MmSessionSpace->ReferenceCount == 1)));
351 
352     /* The session bit must be set */
353     SessionId = MmSessionSpace->SessionId;
354     ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId));
355 
356     /* Get the current process */
357     Process = PsGetCurrentProcess();
358 
359     /* Decrement the process count */
360     InterlockedDecrement(&MmSessionSpace->ResidentProcessCount);
361 
362     /* Decrement the reference count and check if was the last reference */
363     ReferenceCount = InterlockedDecrement(&MmSessionSpace->ReferenceCount);
364     if (ReferenceCount == 0)
365     {
366         /* No more references left, kill the session completely */
367         MiDereferenceSessionFinal();
368     }
369 
370     /* Check if tis is the session leader or the last process in the session */
371     if ((Process->Vm.Flags.SessionLeader) || (ReferenceCount == 0))
372     {
373         /* Get the global session address before we kill the session mapping */
374         SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
375 
376         /* Delete all session PDEs and flush the TB */
377         RtlZeroMemory(MiAddressToPde(MmSessionBase),
378                       BYTES_TO_PAGES(MmSessionSize) * sizeof(MMPDE));
379         KeFlushEntireTb(FALSE, FALSE);
380 
381         /* Is this the session leader? */
382         if (Process->Vm.Flags.SessionLeader)
383         {
384             /* Clean up the references here. */
385             ASSERT(Process->Session == NULL);
386             MiReleaseProcessReferenceToSessionDataPage(SessionGlobal);
387         }
388     }
389 
390     /* Reset the current process' session flag */
391     RtlInterlockedClearBits(&Process->Flags, PSF_PROCESS_IN_SESSION_BIT);
392 }
393 
394 VOID
395 NTAPI
396 MiSessionRemoveProcess(VOID)
397 {
398     PEPROCESS CurrentProcess = PsGetCurrentProcess();
399     KIRQL OldIrql;
400 
401     /* If the process isn't already in a session, or if it's the leader... */
402     if (!(CurrentProcess->Flags & PSF_PROCESS_IN_SESSION_BIT) ||
403         (CurrentProcess->Vm.Flags.SessionLeader))
404     {
405         /* Then there's nothing to do */
406         return;
407     }
408 
409     /* Sanity check */
410     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
411 
412     /* Acquire the expansion lock while touching the session */
413     OldIrql = MiAcquireExpansionLock();
414 
415     /* Remove the process from the list */
416     RemoveEntryList(&CurrentProcess->SessionProcessLinks);
417 
418     /* Release the lock again */
419     MiReleaseExpansionLock(OldIrql);
420 
421     /* Dereference the session */
422     MiDereferenceSession();
423 }
424 
425 VOID
426 NTAPI
427 MiSessionAddProcess(IN PEPROCESS NewProcess)
428 {
429     PMM_SESSION_SPACE SessionGlobal;
430     KIRQL OldIrql;
431 
432     /* The current process must already be in a session */
433     if (!(PsGetCurrentProcess()->Flags & PSF_PROCESS_IN_SESSION_BIT)) return;
434 
435     /* Sanity check */
436     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
437 
438     /* Get the global session */
439     SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
440 
441     /* Increment counters */
442     InterlockedIncrement((PLONG)&SessionGlobal->ReferenceCount);
443     InterlockedIncrement(&SessionGlobal->ResidentProcessCount);
444     InterlockedIncrement(&SessionGlobal->ProcessReferenceToSession);
445 
446     /* Set the session pointer */
447     ASSERT(NewProcess->Session == NULL);
448     NewProcess->Session = SessionGlobal;
449 
450     /* Acquire the expansion lock while touching the session */
451     OldIrql = MiAcquireExpansionLock();
452 
453     /* Insert it into the process list */
454     InsertTailList(&SessionGlobal->ProcessList, &NewProcess->SessionProcessLinks);
455 
456     /* Release the lock again */
457     MiReleaseExpansionLock(OldIrql);
458 
459     /* Set the flag */
460     PspSetProcessFlag(NewProcess, PSF_PROCESS_IN_SESSION_BIT);
461 }
462 
463 NTSTATUS
464 NTAPI
465 MiSessionInitializeWorkingSetList(VOID)
466 {
467     KIRQL OldIrql;
468     PMMPTE PointerPte;
469     PMMPDE PointerPde;
470     MMPTE TempPte;
471     MMPDE TempPde;
472     ULONG Color, Index;
473     PFN_NUMBER PageFrameIndex;
474     PMM_SESSION_SPACE SessionGlobal;
475     BOOLEAN AllocatedPageTable;
476     PMMWSL WorkingSetList;
477 
478     /* Get pointers to session global and the session working set list */
479     SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
480     WorkingSetList = (PMMWSL)MiSessionSpaceWs;
481 
482     /* Fill out the two pointers */
483     MmSessionSpace->Vm.VmWorkingSetList = WorkingSetList;
484     MmSessionSpace->Wsle = (PMMWSLE)WorkingSetList->UsedPageTableEntries;
485 
486     /* Get the PDE for the working set, and check if it's already allocated */
487     PointerPde = MiAddressToPde(WorkingSetList);
488     if (PointerPde->u.Hard.Valid == 1)
489     {
490         /* Nope, we'll have to do it */
491 #ifndef _M_ARM
492         ASSERT(PointerPde->u.Hard.Global == 0);
493 #endif
494         AllocatedPageTable = FALSE;
495     }
496     else
497     {
498         /* Yep, that makes our job easier */
499         AllocatedPageTable = TRUE;
500     }
501 
502     /* Get the PTE for the working set */
503     PointerPte = MiAddressToPte(WorkingSetList);
504 
505     /* Initialize the working set lock, and lock the PFN database */
506     ExInitializePushLock(&SessionGlobal->Vm.WorkingSetMutex);
507     //MmLockPageableSectionByHandle(ExPageLockHandle);
508     OldIrql = MiAcquirePfnLock();
509 
510     /* Check if we need a page table */
511     if (AllocatedPageTable != FALSE)
512     {
513         /* Get a zeroed colored zero page */
514         MI_SET_USAGE(MI_USAGE_INIT_MEMORY);
515         Color = MI_GET_NEXT_COLOR();
516         PageFrameIndex = MiRemoveZeroPageSafe(Color);
517         if (!PageFrameIndex)
518         {
519             /* No zero pages, grab a free one */
520             PageFrameIndex = MiRemoveAnyPage(Color);
521 
522             /* Zero it outside the PFN lock */
523             MiReleasePfnLock(OldIrql);
524             MiZeroPhysicalPage(PageFrameIndex);
525             OldIrql = MiAcquirePfnLock();
526         }
527 
528         /* Write a valid PDE for it */
529         TempPde = ValidKernelPdeLocal;
530         TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
531         MI_WRITE_VALID_PDE(PointerPde, TempPde);
532 
533         /* Add this into the list */
534         Index = ((ULONG_PTR)WorkingSetList - (ULONG_PTR)MmSessionBase) >> 22;
535 #ifndef _M_AMD64
536         MmSessionSpace->PageTables[Index] = TempPde;
537 #endif
538         /* Initialize the page directory page, and now zero the working set list itself */
539         MiInitializePfnForOtherProcess(PageFrameIndex,
540                                        PointerPde,
541                                        MmSessionSpace->SessionPageDirectoryIndex);
542         KeZeroPages(PointerPte, PAGE_SIZE);
543     }
544 
545     /* Get a zeroed colored zero page */
546     MI_SET_USAGE(MI_USAGE_INIT_MEMORY);
547     Color = MI_GET_NEXT_COLOR();
548     PageFrameIndex = MiRemoveZeroPageSafe(Color);
549     if (!PageFrameIndex)
550     {
551         /* No zero pages, grab a free one */
552         PageFrameIndex = MiRemoveAnyPage(Color);
553 
554         /* Zero it outside the PFN lock */
555         MiReleasePfnLock(OldIrql);
556         MiZeroPhysicalPage(PageFrameIndex);
557         OldIrql = MiAcquirePfnLock();
558     }
559 
560     /* Write a valid PTE for it */
561     TempPte = ValidKernelPteLocal;
562     MI_MAKE_DIRTY_PAGE(&TempPte);
563     TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
564 
565     /* Initialize the working set list page */
566     MiInitializePfnAndMakePteValid(PageFrameIndex, PointerPte, TempPte);
567 
568     /* Now we can release the PFN database lock */
569     MiReleasePfnLock(OldIrql);
570 
571     /* Fill out the working set structure */
572     MmSessionSpace->Vm.Flags.SessionSpace = 1;
573     MmSessionSpace->Vm.MinimumWorkingSetSize = 20;
574     MmSessionSpace->Vm.MaximumWorkingSetSize = 384;
575     WorkingSetList->LastEntry = 20;
576     WorkingSetList->HashTable = NULL;
577     WorkingSetList->HashTableSize = 0;
578     WorkingSetList->Wsle = MmSessionSpace->Wsle;
579 
580     /* Acquire the expansion lock while touching the session */
581     OldIrql = MiAcquireExpansionLock();
582 
583     /* Handle list insertions */
584     ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
585     ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
586     InsertTailList(&MiSessionWsList, &SessionGlobal->WsListEntry);
587 
588     ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Flink == NULL);
589     ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Blink == NULL);
590     InsertTailList(&MmWorkingSetExpansionHead,
591                    &SessionGlobal->Vm.WorkingSetExpansionLinks);
592 
593     /* Release the lock again */
594     MiReleaseExpansionLock(OldIrql);
595 
596     /* All done, return */
597     //MmUnlockPageableImageSection(ExPageLockHandle);
598     return STATUS_SUCCESS;
599 }
600 
601 NTSTATUS
602 NTAPI
603 MiSessionCreateInternal(OUT PULONG SessionId)
604 {
605     PEPROCESS Process = PsGetCurrentProcess();
606     ULONG NewFlags, Flags, i, Color;
607 #if (_MI_PAGING_LEVELS < 3)
608     ULONG Size;
609 #endif // (_MI_PAGING_LEVELS < 3)
610     PMMPDE PageTables = NULL;
611     KIRQL OldIrql;
612     PMMPTE PointerPte, SessionPte;
613     PMMPDE PointerPde;
614     PMM_SESSION_SPACE SessionGlobal;
615     MMPTE TempPte;
616     MMPDE TempPde;
617     NTSTATUS Status;
618     BOOLEAN Result;
619     PFN_NUMBER SessionPageDirIndex;
620     PFN_NUMBER TagPage[MI_SESSION_TAG_PAGES_MAXIMUM];
621     PFN_NUMBER DataPage[MI_SESSION_DATA_PAGES_MAXIMUM];
622 
623     /* This should not exist yet */
624     ASSERT(MmIsAddressValid(MmSessionSpace) == FALSE);
625 
626     /* Loop so we can set the session-is-creating flag */
627     Flags = Process->Flags;
628     while (TRUE)
629     {
630         /* Check if it's already set */
631         if (Flags & PSF_SESSION_CREATION_UNDERWAY_BIT)
632         {
633             /* Bail out */
634             DPRINT1("Lost session race\n");
635             return STATUS_ALREADY_COMMITTED;
636         }
637 
638         /* Now try to set it */
639         NewFlags = InterlockedCompareExchange((PLONG)&Process->Flags,
640                                               Flags | PSF_SESSION_CREATION_UNDERWAY_BIT,
641                                               Flags);
642         if (NewFlags == Flags) break;
643 
644         /* It changed, try again */
645         Flags = NewFlags;
646     }
647 
648     /* Now we should own the flag */
649     ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
650 
651 #if (_MI_PAGING_LEVELS < 3)
652     /*
653      * Session space covers everything from 0xA0000000 to 0xC0000000.
654      * Allocate enough page tables to describe the entire region
655      */
656     Size = (0x20000000 / PDE_MAPPED_VA) * sizeof(MMPTE);
657     PageTables = ExAllocatePoolWithTag(NonPagedPool, Size, 'tHmM');
658     ASSERT(PageTables != NULL);
659     RtlZeroMemory(PageTables, Size);
660 #endif // (_MI_PAGING_LEVELS < 3)
661 
662     /* Lock the session ID creation mutex */
663     KeAcquireGuardedMutex(&MiSessionIdMutex);
664 
665     /* Allocate a new Session ID */
666     *SessionId = RtlFindClearBitsAndSet(MiSessionIdBitmap, 1, 0);
667     if (*SessionId == 0xFFFFFFFF)
668     {
669         /* We ran out of session IDs, we should expand */
670         DPRINT1("Too many sessions created. Expansion not yet supported\n");
671 #if (_MI_PAGING_LEVELS < 3)
672         ExFreePoolWithTag(PageTables, 'tHmM');
673 #endif // (_MI_PAGING_LEVELS < 3)
674         return STATUS_NO_MEMORY;
675     }
676 
677     /* Unlock the session ID creation mutex */
678     KeReleaseGuardedMutex(&MiSessionIdMutex);
679 
680     /* Reserve the global PTEs */
681     SessionPte = MiReserveSystemPtes(MiSessionDataPages, SystemPteSpace);
682     ASSERT(SessionPte != NULL);
683 
684     /* Acquire the PFN lock while we set everything up */
685     OldIrql = MiAcquirePfnLock();
686 
687     /* Loop the global PTEs */
688     TempPte = ValidKernelPte;
689     for (i = 0; i < MiSessionDataPages; i++)
690     {
691         /* Get a zeroed colored zero page */
692         MI_SET_USAGE(MI_USAGE_INIT_MEMORY);
693         Color = MI_GET_NEXT_COLOR();
694         DataPage[i] = MiRemoveZeroPageSafe(Color);
695         if (!DataPage[i])
696         {
697             /* No zero pages, grab a free one */
698             DataPage[i] = MiRemoveAnyPage(Color);
699 
700             /* Zero it outside the PFN lock */
701             MiReleasePfnLock(OldIrql);
702             MiZeroPhysicalPage(DataPage[i]);
703             OldIrql = MiAcquirePfnLock();
704         }
705 
706         /* Fill the PTE out */
707         TempPte.u.Hard.PageFrameNumber = DataPage[i];
708         MI_WRITE_VALID_PTE(SessionPte + i, TempPte);
709     }
710 
711     /* Set the pointer to global space */
712     SessionGlobal = MiPteToAddress(SessionPte);
713 
714     /* Get a zeroed colored zero page */
715     MI_SET_USAGE(MI_USAGE_INIT_MEMORY);
716     Color = MI_GET_NEXT_COLOR();
717     SessionPageDirIndex = MiRemoveZeroPageSafe(Color);
718     if (!SessionPageDirIndex)
719     {
720         /* No zero pages, grab a free one */
721         SessionPageDirIndex = MiRemoveAnyPage(Color);
722 
723         /* Zero it outside the PFN lock */
724         MiReleasePfnLock(OldIrql);
725         MiZeroPhysicalPage(SessionPageDirIndex);
726         OldIrql = MiAcquirePfnLock();
727     }
728 
729     /* Fill the PTE out */
730     TempPde = ValidKernelPdeLocal;
731     TempPde.u.Hard.PageFrameNumber = SessionPageDirIndex;
732 
733     /* Setup, allocate, fill out the MmSessionSpace PTE */
734     PointerPde = MiAddressToPde(MmSessionSpace);
735     ASSERT(PointerPde->u.Long == 0);
736     MI_WRITE_VALID_PDE(PointerPde, TempPde);
737     MiInitializePfnForOtherProcess(SessionPageDirIndex,
738                                    PointerPde,
739                                    SessionPageDirIndex);
740     ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex)->u1.WsIndex == 0);
741 
742      /* Loop all the local PTEs for it */
743     TempPte = ValidKernelPteLocal;
744     PointerPte = MiAddressToPte(MmSessionSpace);
745     for (i = 0; i < MiSessionDataPages; i++)
746     {
747         /* And fill them out */
748         TempPte.u.Hard.PageFrameNumber = DataPage[i];
749         MiInitializePfnAndMakePteValid(DataPage[i], PointerPte + i, TempPte);
750         ASSERT(MI_PFN_ELEMENT(DataPage[i])->u1.WsIndex == 0);
751     }
752 
753      /* Finally loop all of the session pool tag pages */
754     for (i = 0; i < MiSessionTagPages; i++)
755     {
756         /* Grab a zeroed colored page */
757         MI_SET_USAGE(MI_USAGE_INIT_MEMORY);
758         Color = MI_GET_NEXT_COLOR();
759         TagPage[i] = MiRemoveZeroPageSafe(Color);
760         if (!TagPage[i])
761         {
762             /* No zero pages, grab a free one */
763             TagPage[i] = MiRemoveAnyPage(Color);
764 
765             /* Zero it outside the PFN lock */
766             MiReleasePfnLock(OldIrql);
767             MiZeroPhysicalPage(TagPage[i]);
768             OldIrql = MiAcquirePfnLock();
769         }
770 
771         /* Fill the PTE out */
772         TempPte.u.Hard.PageFrameNumber = TagPage[i];
773         MiInitializePfnAndMakePteValid(TagPage[i],
774                                        PointerPte + MiSessionDataPages + i,
775                                        TempPte);
776     }
777 
778     /* PTEs have been setup, release the PFN lock */
779     MiReleasePfnLock(OldIrql);
780 
781     /* Fill out the session space structure now */
782     MmSessionSpace->GlobalVirtualAddress = SessionGlobal;
783     MmSessionSpace->ReferenceCount = 1;
784     MmSessionSpace->ResidentProcessCount = 1;
785     MmSessionSpace->u.LongFlags = 0;
786     MmSessionSpace->SessionId = *SessionId;
787     MmSessionSpace->LocaleId = PsDefaultSystemLocaleId;
788     MmSessionSpace->SessionPageDirectoryIndex = SessionPageDirIndex;
789     MmSessionSpace->Color = Color;
790     MmSessionSpace->NonPageablePages = MiSessionCreateCharge;
791     MmSessionSpace->CommittedPages = MiSessionCreateCharge;
792 #ifndef _M_AMD64
793     MmSessionSpace->PageTables = PageTables;
794     MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde;
795 #endif
796     InitializeListHead(&MmSessionSpace->ImageList);
797 
798     DPRINT1("Session %lu is ready to go: 0x%p 0x%p, %lx 0x%p\n",
799             *SessionId, MmSessionSpace, SessionGlobal, SessionPageDirIndex, PageTables);
800 
801     /* Initialize session pool */
802     //Status = MiInitializeSessionPool();
803     Status = STATUS_SUCCESS;
804     ASSERT(NT_SUCCESS(Status) == TRUE);
805 
806     /* Initialize system space */
807     Result = MiInitializeSystemSpaceMap(&SessionGlobal->Session);
808     ASSERT(Result == TRUE);
809 
810     /* Initialize the process list, make sure the workign set list is empty */
811     ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
812     ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
813     InitializeListHead(&SessionGlobal->ProcessList);
814 
815     /* We're done, clear the flag */
816     ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
817     PspClearProcessFlag(Process, PSF_SESSION_CREATION_UNDERWAY_BIT);
818 
819     /* Insert the process into the session  */
820     ASSERT(Process->Session == NULL);
821     ASSERT(SessionGlobal->ProcessReferenceToSession == 0);
822     SessionGlobal->ProcessReferenceToSession = 1;
823 
824     /* We're done */
825     InterlockedIncrement(&MmSessionDataPages);
826     return STATUS_SUCCESS;
827 }
828 
829 NTSTATUS
830 NTAPI
831 MmSessionCreate(OUT PULONG SessionId)
832 {
833     PEPROCESS Process = PsGetCurrentProcess();
834     ULONG SessionLeaderExists;
835     NTSTATUS Status;
836 
837     /* Fail if the process is already in a session */
838     if (Process->Flags & PSF_PROCESS_IN_SESSION_BIT)
839     {
840         DPRINT1("Process already in session\n");
841         return STATUS_ALREADY_COMMITTED;
842     }
843 
844     /* Check if the process is already the session leader */
845     if (!Process->Vm.Flags.SessionLeader)
846     {
847         /* Atomically set it as the leader */
848         SessionLeaderExists = InterlockedCompareExchange(&MiSessionLeaderExists, 1, 0);
849         if (SessionLeaderExists)
850         {
851             DPRINT1("Session leader race\n");
852             return STATUS_INVALID_SYSTEM_SERVICE;
853         }
854 
855         /* Do the work required to upgrade him */
856         MiSessionLeader(Process);
857     }
858 
859     /* Create the session */
860     KeEnterCriticalRegion();
861     Status = MiSessionCreateInternal(SessionId);
862     if (!NT_SUCCESS(Status))
863     {
864         KeLeaveCriticalRegion();
865         return Status;
866     }
867 
868     /* Set up the session working set */
869     Status = MiSessionInitializeWorkingSetList();
870     if (!NT_SUCCESS(Status))
871     {
872         /* Fail */
873         //MiDereferenceSession();
874         ASSERT(FALSE);
875         KeLeaveCriticalRegion();
876         return Status;
877     }
878 
879     /* All done */
880     KeLeaveCriticalRegion();
881 
882     /* Set and assert the flags, and return */
883     MmSessionSpace->u.Flags.Initialized = 1;
884     PspSetProcessFlag(Process, PSF_PROCESS_IN_SESSION_BIT);
885     ASSERT(MiSessionLeaderExists == 1);
886     return Status;
887 }
888 
889 NTSTATUS
890 NTAPI
891 MmSessionDelete(IN ULONG SessionId)
892 {
893     PEPROCESS Process = PsGetCurrentProcess();
894 
895     /* Process must be in a session */
896     if (!(Process->Flags & PSF_PROCESS_IN_SESSION_BIT))
897     {
898         DPRINT1("Not in a session!\n");
899         return STATUS_UNABLE_TO_FREE_VM;
900     }
901 
902     /* It must be the session leader */
903     if (!Process->Vm.Flags.SessionLeader)
904     {
905         DPRINT1("Not a session leader!\n");
906         return STATUS_UNABLE_TO_FREE_VM;
907     }
908 
909     /* Remove one reference count */
910     KeEnterCriticalRegion();
911     /* FIXME: Do it */
912     KeLeaveCriticalRegion();
913 
914     /* All done */
915     return STATUS_SUCCESS;
916 }
917 
918 _IRQL_requires_max_(APC_LEVEL)
919 NTSTATUS
920 NTAPI
921 MmAttachSession(
922     _Inout_ PVOID SessionEntry,
923     _Out_ PKAPC_STATE ApcState)
924 {
925     PEPROCESS EntryProcess;
926     PMM_SESSION_SPACE EntrySession, CurrentSession;
927     PEPROCESS CurrentProcess;
928     KIRQL OldIrql;
929 
930     /* The parameter is the actual process! */
931     EntryProcess = SessionEntry;
932     ASSERT(EntryProcess != NULL);
933 
934     /* Sanity checks */
935     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
936     ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0);
937 
938     /* Get the session from the process that was passed in */
939     EntrySession = EntryProcess->Session;
940     ASSERT(EntrySession != NULL);
941 
942     /* Get the current process and it's session */
943     CurrentProcess = PsGetCurrentProcess();
944     CurrentSession = CurrentProcess->Session;
945 
946     /* Acquire the expansion lock while touching the session */
947     OldIrql = MiAcquireExpansionLock();
948 
949     /* Check if the session is about to be deleted */
950     if (EntrySession->u.Flags.DeletePending)
951     {
952         /* We cannot attach to it, so unlock and fail */
953         MiReleaseExpansionLock(OldIrql);
954         return STATUS_PROCESS_IS_TERMINATING;
955     }
956 
957     /* Count the number of attaches */
958     EntrySession->AttachCount++;
959 
960     /* we can release the lock again */
961     MiReleaseExpansionLock(OldIrql);
962 
963     /* Check if we are not the session leader and we are in a session */
964     if (!CurrentProcess->Vm.Flags.SessionLeader && (CurrentSession != NULL))
965     {
966         /* Are we already in the right session? */
967         if (CurrentSession == EntrySession)
968         {
969             /* We are, so "attach" to the current process */
970             EntryProcess = CurrentProcess;
971         }
972         else
973         {
974             /* We are not, the session id should better not match! */
975             ASSERT(CurrentSession->SessionId != EntrySession->SessionId);
976         }
977     }
978 
979     /* Now attach to the process that we have */
980     KeStackAttachProcess(&EntryProcess->Pcb, ApcState);
981 
982     /* Success! */
983     return STATUS_SUCCESS;
984 }
985 
986 _IRQL_requires_max_(APC_LEVEL)
987 VOID
988 NTAPI
989 MmDetachSession(
990     _Inout_ PVOID SessionEntry,
991     _In_ PKAPC_STATE ApcState)
992 {
993     PEPROCESS EntryProcess;
994     PMM_SESSION_SPACE EntrySession;
995     KIRQL OldIrql;
996     BOOLEAN DeletePending;
997 
998     /* The parameter is the actual process! */
999     EntryProcess = SessionEntry;
1000     ASSERT(EntryProcess != NULL);
1001 
1002     /* Sanity checks */
1003     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1004     ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0);
1005 
1006     /* Get the session from the process that was passed in */
1007     EntrySession = EntryProcess->Session;
1008     ASSERT(EntrySession != NULL);
1009 
1010     /* Acquire the expansion lock while touching the session */
1011     OldIrql = MiAcquireExpansionLock();
1012 
1013     /* Make sure we have at least one attach and decrement the count */
1014     ASSERT(EntrySession->AttachCount >= 1);
1015     EntrySession->AttachCount--;
1016 
1017     /* Remember if a delete is pending and we were the last one attached */
1018     DeletePending = EntrySession->u.Flags.DeletePending &&
1019                     (EntrySession->AttachCount == 0);
1020 
1021     /* Release the lock again */
1022     MiReleaseExpansionLock(OldIrql);
1023 
1024     /* Detach from the process */
1025     KeUnstackDetachProcess(ApcState);
1026 
1027     /* Check if we need to set the attach event */
1028     if (DeletePending)
1029         KeSetEvent(&EntrySession->AttachEvent, IO_NO_INCREMENT, FALSE);
1030 }
1031 
1032 VOID
1033 NTAPI
1034 MmQuitNextSession(
1035     _Inout_ PVOID SessionEntry)
1036 {
1037     PEPROCESS EntryProcess;
1038 
1039     /* The parameter is the actual process! */
1040     EntryProcess = SessionEntry;
1041     ASSERT(EntryProcess != NULL);
1042 
1043     /* Sanity checks */
1044     ASSERT(KeGetCurrentIrql () <= APC_LEVEL);
1045     ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0);
1046     ASSERT(EntryProcess->Session != NULL);
1047 
1048     /* Get rid of the reference we took */
1049     ObDereferenceObject(EntryProcess);
1050 }
1051 
1052 PVOID
1053 NTAPI
1054 MmGetSessionById(
1055     _In_ ULONG SessionId)
1056 {
1057     PLIST_ENTRY ListEntry;
1058     PMM_SESSION_SPACE Session;
1059     PEPROCESS Process = NULL;
1060     KIRQL OldIrql;
1061 
1062     /* Acquire the expansion lock while touching the session */
1063     OldIrql = MiAcquireExpansionLock();
1064 
1065     /* Loop all entries in the session ws list */
1066     ListEntry = MiSessionWsList.Flink;
1067     while (ListEntry != &MiSessionWsList)
1068     {
1069         Session = CONTAINING_RECORD(ListEntry, MM_SESSION_SPACE, WsListEntry);
1070         ListEntry = ListEntry->Flink;
1071 
1072         /* Check if this is the session we are looking for */
1073         if (Session->SessionId == SessionId)
1074         {
1075             /* Check if we also have a process in the process list */
1076             if (!IsListEmpty(&Session->ProcessList))
1077             {
1078                 Process = CONTAINING_RECORD(Session->ProcessList.Flink,
1079                                             EPROCESS,
1080                                             SessionProcessLinks);
1081 
1082                 /* Reference the process */
1083                 ObReferenceObject(Process);
1084                 break;
1085             }
1086         }
1087     }
1088 
1089     /* Release the lock again */
1090     MiReleaseExpansionLock(OldIrql);
1091 
1092     return Process;
1093 }
1094