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