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