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