xref: /reactos/ntoskrnl/mm/ARM3/session.c (revision c2c66aff)
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 = KeAcquireQueuedSpinLock(LockQueuePfnLock);
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     KeReleaseQueuedSpinLock(LockQueuePfnLock, 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 = KeAcquireQueuedSpinLock(LockQueuePfnLock);
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             KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
524             MiZeroPhysicalPage(PageFrameIndex);
525             OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
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         KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
556         MiZeroPhysicalPage(PageFrameIndex);
557         OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
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     KeReleaseQueuedSpinLock(LockQueuePfnLock, 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, Size, i, Color;
607     KIRQL OldIrql;
608     PMMPTE PointerPte, SessionPte;
609     PMMPDE PointerPde, PageTables;
610     PMM_SESSION_SPACE SessionGlobal;
611     MMPTE TempPte;
612     MMPDE TempPde;
613     NTSTATUS Status;
614     BOOLEAN Result;
615     PFN_NUMBER SessionPageDirIndex;
616     PFN_NUMBER TagPage[MI_SESSION_TAG_PAGES_MAXIMUM];
617     PFN_NUMBER DataPage[MI_SESSION_DATA_PAGES_MAXIMUM];
618 
619     /* This should not exist yet */
620     ASSERT(MmIsAddressValid(MmSessionSpace) == FALSE);
621 
622     /* Loop so we can set the session-is-creating flag */
623     Flags = Process->Flags;
624     while (TRUE)
625     {
626         /* Check if it's already set */
627         if (Flags & PSF_SESSION_CREATION_UNDERWAY_BIT)
628         {
629             /* Bail out */
630             DPRINT1("Lost session race\n");
631             return STATUS_ALREADY_COMMITTED;
632         }
633 
634         /* Now try to set it */
635         NewFlags = InterlockedCompareExchange((PLONG)&Process->Flags,
636                                               Flags | PSF_SESSION_CREATION_UNDERWAY_BIT,
637                                               Flags);
638         if (NewFlags == Flags) break;
639 
640         /* It changed, try again */
641         Flags = NewFlags;
642     }
643 
644     /* Now we should own the flag */
645     ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
646 
647     /*
648      * Session space covers everything from 0xA0000000 to 0xC0000000.
649      * Allocate enough page tables to describe the entire region
650      */
651     Size = (0x20000000 / PDE_MAPPED_VA) * sizeof(MMPTE);
652     PageTables = ExAllocatePoolWithTag(NonPagedPool, Size, 'tHmM');
653     ASSERT(PageTables != NULL);
654     RtlZeroMemory(PageTables, Size);
655 
656     /* Lock the session ID creation mutex */
657     KeAcquireGuardedMutex(&MiSessionIdMutex);
658 
659     /* Allocate a new Session ID */
660     *SessionId = RtlFindClearBitsAndSet(MiSessionIdBitmap, 1, 0);
661     if (*SessionId == 0xFFFFFFFF)
662     {
663         /* We ran out of session IDs, we should expand */
664         DPRINT1("Too many sessions created. Expansion not yet supported\n");
665         ExFreePoolWithTag(PageTables, 'tHmM');
666         return STATUS_NO_MEMORY;
667     }
668 
669     /* Unlock the session ID creation mutex */
670     KeReleaseGuardedMutex(&MiSessionIdMutex);
671 
672     /* Reserve the global PTEs */
673     SessionPte = MiReserveSystemPtes(MiSessionDataPages, SystemPteSpace);
674     ASSERT(SessionPte != NULL);
675 
676     /* Acquire the PFN lock while we set everything up */
677     OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
678 
679     /* Loop the global PTEs */
680     TempPte = ValidKernelPte;
681     for (i = 0; i < MiSessionDataPages; i++)
682     {
683         /* Get a zeroed colored zero page */
684         MI_SET_USAGE(MI_USAGE_INIT_MEMORY);
685         Color = MI_GET_NEXT_COLOR();
686         DataPage[i] = MiRemoveZeroPageSafe(Color);
687         if (!DataPage[i])
688         {
689             /* No zero pages, grab a free one */
690             DataPage[i] = MiRemoveAnyPage(Color);
691 
692             /* Zero it outside the PFN lock */
693             KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
694             MiZeroPhysicalPage(DataPage[i]);
695             OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
696         }
697 
698         /* Fill the PTE out */
699         TempPte.u.Hard.PageFrameNumber = DataPage[i];
700         MI_WRITE_VALID_PTE(SessionPte + i, TempPte);
701     }
702 
703     /* Set the pointer to global space */
704     SessionGlobal = MiPteToAddress(SessionPte);
705 
706     /* Get a zeroed colored zero page */
707     MI_SET_USAGE(MI_USAGE_INIT_MEMORY);
708     Color = MI_GET_NEXT_COLOR();
709     SessionPageDirIndex = MiRemoveZeroPageSafe(Color);
710     if (!SessionPageDirIndex)
711     {
712         /* No zero pages, grab a free one */
713         SessionPageDirIndex = MiRemoveAnyPage(Color);
714 
715         /* Zero it outside the PFN lock */
716         KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
717         MiZeroPhysicalPage(SessionPageDirIndex);
718         OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
719     }
720 
721     /* Fill the PTE out */
722     TempPde = ValidKernelPdeLocal;
723     TempPde.u.Hard.PageFrameNumber = SessionPageDirIndex;
724 
725     /* Setup, allocate, fill out the MmSessionSpace PTE */
726     PointerPde = MiAddressToPde(MmSessionSpace);
727     ASSERT(PointerPde->u.Long == 0);
728     MI_WRITE_VALID_PDE(PointerPde, TempPde);
729     MiInitializePfnForOtherProcess(SessionPageDirIndex,
730                                    PointerPde,
731                                    SessionPageDirIndex);
732     ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex)->u1.WsIndex == 0);
733 
734      /* Loop all the local PTEs for it */
735     TempPte = ValidKernelPteLocal;
736     PointerPte = MiAddressToPte(MmSessionSpace);
737     for (i = 0; i < MiSessionDataPages; i++)
738     {
739         /* And fill them out */
740         TempPte.u.Hard.PageFrameNumber = DataPage[i];
741         MiInitializePfnAndMakePteValid(DataPage[i], PointerPte + i, TempPte);
742         ASSERT(MI_PFN_ELEMENT(DataPage[i])->u1.WsIndex == 0);
743     }
744 
745      /* Finally loop all of the session pool tag pages */
746     for (i = 0; i < MiSessionTagPages; i++)
747     {
748         /* Grab a zeroed colored page */
749         MI_SET_USAGE(MI_USAGE_INIT_MEMORY);
750         Color = MI_GET_NEXT_COLOR();
751         TagPage[i] = MiRemoveZeroPageSafe(Color);
752         if (!TagPage[i])
753         {
754             /* No zero pages, grab a free one */
755             TagPage[i] = MiRemoveAnyPage(Color);
756 
757             /* Zero it outside the PFN lock */
758             KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
759             MiZeroPhysicalPage(TagPage[i]);
760             OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
761         }
762 
763         /* Fill the PTE out */
764         TempPte.u.Hard.PageFrameNumber = TagPage[i];
765         MiInitializePfnAndMakePteValid(TagPage[i],
766                                        PointerPte + MiSessionDataPages + i,
767                                        TempPte);
768     }
769 
770     /* PTEs have been setup, release the PFN lock */
771     KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
772 
773     /* Fill out the session space structure now */
774     MmSessionSpace->GlobalVirtualAddress = SessionGlobal;
775     MmSessionSpace->ReferenceCount = 1;
776     MmSessionSpace->ResidentProcessCount = 1;
777     MmSessionSpace->u.LongFlags = 0;
778     MmSessionSpace->SessionId = *SessionId;
779     MmSessionSpace->LocaleId = PsDefaultSystemLocaleId;
780     MmSessionSpace->SessionPageDirectoryIndex = SessionPageDirIndex;
781     MmSessionSpace->Color = Color;
782     MmSessionSpace->NonPageablePages = MiSessionCreateCharge;
783     MmSessionSpace->CommittedPages = MiSessionCreateCharge;
784 #ifndef _M_AMD64
785     MmSessionSpace->PageTables = PageTables;
786     MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde;
787 #endif
788     InitializeListHead(&MmSessionSpace->ImageList);
789     DPRINT1("Session %lu is ready to go: 0x%p 0x%p, %lx 0x%p\n",
790             *SessionId, MmSessionSpace, SessionGlobal, SessionPageDirIndex, PageTables);
791 
792     /* Initialize session pool */
793     //Status = MiInitializeSessionPool();
794     Status = STATUS_SUCCESS;
795     ASSERT(NT_SUCCESS(Status) == TRUE);
796 
797     /* Initialize system space */
798     Result = MiInitializeSystemSpaceMap(&SessionGlobal->Session);
799     ASSERT(Result == TRUE);
800 
801     /* Initialize the process list, make sure the workign set list is empty */
802     ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
803     ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
804     InitializeListHead(&SessionGlobal->ProcessList);
805 
806     /* We're done, clear the flag */
807     ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
808     PspClearProcessFlag(Process, PSF_SESSION_CREATION_UNDERWAY_BIT);
809 
810     /* Insert the process into the session  */
811     ASSERT(Process->Session == NULL);
812     ASSERT(SessionGlobal->ProcessReferenceToSession == 0);
813     SessionGlobal->ProcessReferenceToSession = 1;
814 
815     /* We're done */
816     InterlockedIncrement(&MmSessionDataPages);
817     return STATUS_SUCCESS;
818 }
819 
820 NTSTATUS
821 NTAPI
822 MmSessionCreate(OUT PULONG SessionId)
823 {
824     PEPROCESS Process = PsGetCurrentProcess();
825     ULONG SessionLeaderExists;
826     NTSTATUS Status;
827 
828     /* Fail if the process is already in a session */
829     if (Process->Flags & PSF_PROCESS_IN_SESSION_BIT)
830     {
831         DPRINT1("Process already in session\n");
832         return STATUS_ALREADY_COMMITTED;
833     }
834 
835     /* Check if the process is already the session leader */
836     if (!Process->Vm.Flags.SessionLeader)
837     {
838         /* Atomically set it as the leader */
839         SessionLeaderExists = InterlockedCompareExchange(&MiSessionLeaderExists, 1, 0);
840         if (SessionLeaderExists)
841         {
842             DPRINT1("Session leader race\n");
843             return STATUS_INVALID_SYSTEM_SERVICE;
844         }
845 
846         /* Do the work required to upgrade him */
847         MiSessionLeader(Process);
848     }
849 
850     /* Create the session */
851     KeEnterCriticalRegion();
852     Status = MiSessionCreateInternal(SessionId);
853     if (!NT_SUCCESS(Status))
854     {
855         KeLeaveCriticalRegion();
856         return Status;
857     }
858 
859     /* Set up the session working set */
860     Status = MiSessionInitializeWorkingSetList();
861     if (!NT_SUCCESS(Status))
862     {
863         /* Fail */
864         //MiDereferenceSession();
865         ASSERT(FALSE);
866         KeLeaveCriticalRegion();
867         return Status;
868     }
869 
870     /* All done */
871     KeLeaveCriticalRegion();
872 
873     /* Set and assert the flags, and return */
874     MmSessionSpace->u.Flags.Initialized = 1;
875     PspSetProcessFlag(Process, PSF_PROCESS_IN_SESSION_BIT);
876     ASSERT(MiSessionLeaderExists == 1);
877     return Status;
878 }
879 
880 NTSTATUS
881 NTAPI
882 MmSessionDelete(IN ULONG SessionId)
883 {
884     PEPROCESS Process = PsGetCurrentProcess();
885 
886     /* Process must be in a session */
887     if (!(Process->Flags & PSF_PROCESS_IN_SESSION_BIT))
888     {
889         DPRINT1("Not in a session!\n");
890         return STATUS_UNABLE_TO_FREE_VM;
891     }
892 
893     /* It must be the session leader */
894     if (!Process->Vm.Flags.SessionLeader)
895     {
896         DPRINT1("Not a session leader!\n");
897         return STATUS_UNABLE_TO_FREE_VM;
898     }
899 
900     /* Remove one reference count */
901     KeEnterCriticalRegion();
902     /* FIXME: Do it */
903     KeLeaveCriticalRegion();
904 
905     /* All done */
906     return STATUS_SUCCESS;
907 }
908 
909 _IRQL_requires_max_(APC_LEVEL)
910 NTSTATUS
911 NTAPI
912 MmAttachSession(
913     _Inout_ PVOID SessionEntry,
914     _Out_ PKAPC_STATE ApcState)
915 {
916     PEPROCESS EntryProcess;
917     PMM_SESSION_SPACE EntrySession, CurrentSession;
918     PEPROCESS CurrentProcess;
919     KIRQL OldIrql;
920 
921     /* The parameter is the actual process! */
922     EntryProcess = SessionEntry;
923     ASSERT(EntryProcess != NULL);
924 
925     /* Sanity checks */
926     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
927     ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0);
928 
929     /* Get the session from the process that was passed in */
930     EntrySession = EntryProcess->Session;
931     ASSERT(EntrySession != NULL);
932 
933     /* Get the current process and it's session */
934     CurrentProcess = PsGetCurrentProcess();
935     CurrentSession = CurrentProcess->Session;
936 
937     /* Acquire the expansion lock while touching the session */
938     OldIrql = MiAcquireExpansionLock();
939 
940     /* Check if the session is about to be deleted */
941     if (EntrySession->u.Flags.DeletePending)
942     {
943         /* We cannot attach to it, so unlock and fail */
944         MiReleaseExpansionLock(OldIrql);
945         return STATUS_PROCESS_IS_TERMINATING;
946     }
947 
948     /* Count the number of attaches */
949     EntrySession->AttachCount++;
950 
951     /* we can release the lock again */
952     MiReleaseExpansionLock(OldIrql);
953 
954     /* Check if we are not the session leader and we are in a session */
955     if (!CurrentProcess->Vm.Flags.SessionLeader && (CurrentSession != NULL))
956     {
957         /* Are we already in the right session? */
958         if (CurrentSession == EntrySession)
959         {
960             /* We are, so "attach" to the current process */
961             EntryProcess = CurrentProcess;
962         }
963         else
964         {
965             /* We are not, the session id should better not match! */
966             ASSERT(CurrentSession->SessionId != EntrySession->SessionId);
967         }
968     }
969 
970     /* Now attach to the process that we have */
971     KeStackAttachProcess(&EntryProcess->Pcb, ApcState);
972 
973     /* Success! */
974     return STATUS_SUCCESS;
975 }
976 
977 _IRQL_requires_max_(APC_LEVEL)
978 VOID
979 NTAPI
980 MmDetachSession(
981     _Inout_ PVOID SessionEntry,
982     _In_ PKAPC_STATE ApcState)
983 {
984     PEPROCESS EntryProcess;
985     PMM_SESSION_SPACE EntrySession;
986     KIRQL OldIrql;
987     BOOLEAN DeletePending;
988 
989     /* The parameter is the actual process! */
990     EntryProcess = SessionEntry;
991     ASSERT(EntryProcess != NULL);
992 
993     /* Sanity checks */
994     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
995     ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0);
996 
997     /* Get the session from the process that was passed in */
998     EntrySession = EntryProcess->Session;
999     ASSERT(EntrySession != NULL);
1000 
1001     /* Acquire the expansion lock while touching the session */
1002     OldIrql = MiAcquireExpansionLock();
1003 
1004     /* Make sure we have at least one attach and decrement the count */
1005     ASSERT(EntrySession->AttachCount >= 1);
1006     EntrySession->AttachCount--;
1007 
1008     /* Remember if a delete is pending and we were the last one attached */
1009     DeletePending = EntrySession->u.Flags.DeletePending &&
1010                     (EntrySession->AttachCount == 0);
1011 
1012     /* Release the lock again */
1013     MiReleaseExpansionLock(OldIrql);
1014 
1015     /* Detach from the process */
1016     KeUnstackDetachProcess(ApcState);
1017 
1018     /* Check if we need to set the attach event */
1019     if (DeletePending)
1020         KeSetEvent(&EntrySession->AttachEvent, IO_NO_INCREMENT, FALSE);
1021 }
1022 
1023 VOID
1024 NTAPI
1025 MmQuitNextSession(
1026     _Inout_ PVOID SessionEntry)
1027 {
1028     PEPROCESS EntryProcess;
1029 
1030     /* The parameter is the actual process! */
1031     EntryProcess = SessionEntry;
1032     ASSERT(EntryProcess != NULL);
1033 
1034     /* Sanity checks */
1035     ASSERT(KeGetCurrentIrql () <= APC_LEVEL);
1036     ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0);
1037     ASSERT(EntryProcess->Session != NULL);
1038 
1039     /* Get rid of the reference we took */
1040     ObDereferenceObject(EntryProcess);
1041 }
1042 
1043 PVOID
1044 NTAPI
1045 MmGetSessionById(
1046     _In_ ULONG SessionId)
1047 {
1048     PLIST_ENTRY ListEntry;
1049     PMM_SESSION_SPACE Session;
1050     PEPROCESS Process = NULL;
1051     KIRQL OldIrql;
1052 
1053     /* Acquire the expansion lock while touching the session */
1054     OldIrql = MiAcquireExpansionLock();
1055 
1056     /* Loop all entries in the session ws list */
1057     ListEntry = MiSessionWsList.Flink;
1058     while (ListEntry != &MiSessionWsList)
1059     {
1060         Session = CONTAINING_RECORD(ListEntry, MM_SESSION_SPACE, WsListEntry);
1061         ListEntry = ListEntry->Flink;
1062 
1063         /* Check if this is the session we are looking for */
1064         if (Session->SessionId == SessionId)
1065         {
1066             /* Check if we also have a process in the process list */
1067             if (!IsListEmpty(&Session->ProcessList))
1068             {
1069                 Process = CONTAINING_RECORD(Session->ProcessList.Flink,
1070                                             EPROCESS,
1071                                             SessionProcessLinks);
1072 
1073                 /* Reference the process */
1074                 ObReferenceObject(Process);
1075                 break;
1076             }
1077         }
1078     }
1079 
1080     /* Release the lock again */
1081     MiReleaseExpansionLock(OldIrql);
1082 
1083     return Process;
1084 }
1085