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