[REACTOS]
[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 "../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 FORCEINLINE
39 KIRQL
40 MiAcquireExpansionLock()
41 {
42 KIRQL OldIrql;
43
44 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
45 KeAcquireSpinLock(&MmExpansionLock, &OldIrql);
46 ASSERT(MiExpansionLockOwner == NULL);
47 MiExpansionLockOwner = PsGetCurrentThread();
48 return OldIrql;
49 }
50
51 FORCEINLINE
52 VOID
53 MiReleaseExpansionLock(KIRQL OldIrql)
54 {
55 ASSERT(MiExpansionLockOwner == PsGetCurrentThread());
56 MiExpansionLockOwner = NULL;
57 KeReleaseSpinLock(&MmExpansionLock, OldIrql);
58 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
59 }
60
61 VOID
62 NTAPI
63 MiInitializeSessionWsSupport(VOID)
64 {
65 /* Initialize the list heads */
66 InitializeListHead(&MiSessionWsList);
67 InitializeListHead(&MmWorkingSetExpansionHead);
68 }
69
70 LCID
71 NTAPI
72 MmGetSessionLocaleId(VOID)
73 {
74 PEPROCESS Process;
75 PAGED_CODE();
76
77 //
78 // Get the current process
79 //
80 Process = PsGetCurrentProcess();
81
82 //
83 // Check if it's NOT the Session Leader
84 //
85 if (!Process->Vm.Flags.SessionLeader)
86 {
87 //
88 // Make sure it has a valid Session
89 //
90 if (Process->Session)
91 {
92 //
93 // Get the Locale ID
94 //
95 return ((PMM_SESSION_SPACE)Process->Session)->LocaleId;
96 }
97 }
98
99 //
100 // Not a session leader, return the default
101 //
102 return PsDefaultThreadLocaleId;
103 }
104
105 _IRQL_requires_max_(APC_LEVEL)
106 VOID
107 NTAPI
108 MmSetSessionLocaleId(
109 _In_ LCID LocaleId)
110 {
111 PEPROCESS CurrentProcess;
112 PAGED_CODE();
113
114 /* Get the current process and check if it is in a session */
115 CurrentProcess = PsGetCurrentProcess();
116 if ((CurrentProcess->Vm.Flags.SessionLeader == 0) &&
117 (CurrentProcess->Session != NULL))
118 {
119 /* Set the session locale Id */
120 ((PMM_SESSION_SPACE)CurrentProcess->Session)->LocaleId = LocaleId;
121 }
122 else
123 {
124 /* Set the default locale */
125 PsDefaultThreadLocaleId = LocaleId;
126 }
127 }
128
129
130 VOID
131 NTAPI
132 MiInitializeSessionIds(VOID)
133 {
134 ULONG Size, BitmapSize;
135 PFN_NUMBER TotalPages;
136
137 /* Setup the total number of data pages needed for the structure */
138 TotalPages = MI_SESSION_DATA_PAGES_MAXIMUM;
139 MiSessionDataPages = ROUND_TO_PAGES(sizeof(MM_SESSION_SPACE)) >> PAGE_SHIFT;
140 ASSERT(MiSessionDataPages <= MI_SESSION_DATA_PAGES_MAXIMUM - 3);
141 TotalPages -= MiSessionDataPages;
142
143 /* Setup the number of pages needed for session pool tags */
144 MiSessionTagSizePages = 2;
145 MiSessionBigPoolPages = 1;
146 MiSessionTagPages = MiSessionTagSizePages + MiSessionBigPoolPages;
147 ASSERT(MiSessionTagPages <= TotalPages);
148 ASSERT(MiSessionTagPages < MI_SESSION_TAG_PAGES_MAXIMUM);
149
150 /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */
151 MiSessionCreateCharge = 1 + MiSessionDataPages + MiSessionTagPages;
152
153 /* Initialize the lock */
154 KeInitializeGuardedMutex(&MiSessionIdMutex);
155
156 /* Allocate the bitmap */
157 Size = MI_INITIAL_SESSION_IDS;
158 BitmapSize = ((Size + 31) / 32) * sizeof(ULONG);
159 MiSessionIdBitmap = ExAllocatePoolWithTag(PagedPool,
160 sizeof(RTL_BITMAP) + BitmapSize,
161 ' mM');
162 if (MiSessionIdBitmap)
163 {
164 /* Free all the bits */
165 RtlInitializeBitMap(MiSessionIdBitmap,
166 (PVOID)(MiSessionIdBitmap + 1),
167 Size);
168 RtlClearAllBits(MiSessionIdBitmap);
169 }
170 else
171 {
172 /* Die if we couldn't allocate the bitmap */
173 KeBugCheckEx(INSTALL_MORE_MEMORY,
174 MmNumberOfPhysicalPages,
175 MmLowestPhysicalPage,
176 MmHighestPhysicalPage,
177 0x200);
178 }
179 }
180
181 VOID
182 NTAPI
183 MiSessionLeader(IN PEPROCESS Process)
184 {
185 KIRQL OldIrql;
186
187 /* Set the flag while under the expansion lock */
188 OldIrql = KeAcquireQueuedSpinLock(LockQueueExpansionLock);
189 Process->Vm.Flags.SessionLeader = TRUE;
190 KeReleaseQueuedSpinLock(LockQueueExpansionLock, OldIrql);
191 }
192
193 ULONG
194 NTAPI
195 MmGetSessionId(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 0;
205 return SessionGlobal->SessionId;
206 }
207
208 ULONG
209 NTAPI
210 MmGetSessionIdEx(IN PEPROCESS Process)
211 {
212 PMM_SESSION_SPACE SessionGlobal;
213
214 /* The session leader is always session zero */
215 if (Process->Vm.Flags.SessionLeader == 1) return 0;
216
217 /* Otherwise, get the session global, and read the session ID from it */
218 SessionGlobal = (PMM_SESSION_SPACE)Process->Session;
219 if (!SessionGlobal) return -1;
220 return SessionGlobal->SessionId;
221 }
222
223 VOID
224 NTAPI
225 MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal)
226 {
227 ULONG i, SessionId;
228 PMMPTE PointerPte;
229 PFN_NUMBER PageFrameIndex[MI_SESSION_DATA_PAGES_MAXIMUM];
230 PMMPFN Pfn1;
231 KIRQL OldIrql;
232
233 /* Is there more than just this reference? If so, bail out */
234 if (InterlockedDecrement(&SessionGlobal->ProcessReferenceToSession)) return;
235
236 /* Get the session ID */
237 SessionId = SessionGlobal->SessionId;
238 DPRINT1("Last process in session %lu going down!!!\n", SessionId);
239
240 /* Free the session page tables */
241 #ifndef _M_AMD64
242 ExFreePoolWithTag(SessionGlobal->PageTables, 'tHmM');
243 #endif
244 ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal));
245
246 /* Capture the data page PFNs */
247 PointerPte = MiAddressToPte(SessionGlobal);
248 for (i = 0; i < MiSessionDataPages; i++)
249 {
250 PageFrameIndex[i] = PFN_FROM_PTE(PointerPte + i);
251 }
252
253 /* Release them */
254 MiReleaseSystemPtes(PointerPte, MiSessionDataPages, SystemPteSpace);
255
256 /* Mark them as deleted */
257 for (i = 0; i < MiSessionDataPages; i++)
258 {
259 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
260 MI_SET_PFN_DELETED(Pfn1);
261 }
262
263 /* Loop every data page and drop a reference count */
264 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
265 for (i = 0; i < MiSessionDataPages; i++)
266 {
267 /* Sanity check that the page is correct, then decrement it */
268 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
269 ASSERT(Pfn1->u2.ShareCount == 1);
270 ASSERT(Pfn1->u3.e2.ReferenceCount == 1);
271 MiDecrementShareCount(Pfn1, PageFrameIndex[i]);
272 }
273
274 /* Done playing with pages, release the lock */
275 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
276
277 /* Decrement the number of data pages */
278 InterlockedDecrement(&MmSessionDataPages);
279
280 /* Free this session ID from the session bitmap */
281 KeAcquireGuardedMutex(&MiSessionIdMutex);
282 ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId));
283 RtlClearBit(MiSessionIdBitmap, SessionId);
284 KeReleaseGuardedMutex(&MiSessionIdMutex);
285 }
286
287 VOID
288 NTAPI
289 MiDereferenceSessionFinal(VOID)
290 {
291 PMM_SESSION_SPACE SessionGlobal;
292 KIRQL OldIrql;
293
294 /* Get the pointer to the global session address */
295 SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
296
297 /* Acquire the expansion lock */
298 OldIrql = MiAcquireExpansionLock();
299
300 /* Set delete pending flag, so that processes can no longer attach to this
301 session and the last process that detaches sets the AttachEvent */
302 ASSERT(SessionGlobal->u.Flags.DeletePending == 0);
303 SessionGlobal->u.Flags.DeletePending = 1;
304
305 /* Check if we have any attached processes */
306 if (SessionGlobal->AttachCount)
307 {
308 /* Initialize the event (it's not in use yet!) */
309 KeInitializeEvent(&SessionGlobal->AttachEvent, NotificationEvent, FALSE);
310
311 /* Release the expansion lock for the wait */
312 MiReleaseExpansionLock(OldIrql);
313
314 /* Wait for the event to be set due to the last process detach */
315 KeWaitForSingleObject(&SessionGlobal->AttachEvent, WrVirtualMemory, 0, 0, 0);
316
317 /* Reacquire the expansion lock */
318 OldIrql = MiAcquireExpansionLock();
319
320 /* Makes sure we still have the delete flag and no attached processes */
321 ASSERT(MmSessionSpace->u.Flags.DeletePending == 1);
322 ASSERT(MmSessionSpace->AttachCount == 0);
323 }
324
325 /* Check if the session is in the workingset expansion list */
326 if (SessionGlobal->Vm.WorkingSetExpansionLinks.Flink != NULL)
327 {
328 /* Remove the session from the list and zero the list entry */
329 RemoveEntryList(&SessionGlobal->Vm.WorkingSetExpansionLinks);
330 SessionGlobal->Vm.WorkingSetExpansionLinks.Flink = 0;
331 }
332
333 /* Check if the session is in the workingset list */
334 if (SessionGlobal->WsListEntry.Flink)
335 {
336 /* Remove the session from the list and zero the list entry */
337 RemoveEntryList(&SessionGlobal->WsListEntry);
338 SessionGlobal->WsListEntry.Flink = NULL;
339 }
340
341 /* Release the expansion lock */
342 MiReleaseExpansionLock(OldIrql);
343
344 /* Check for a win32k unload routine */
345 if (SessionGlobal->Win32KDriverUnload)
346 {
347 /* Call it */
348 SessionGlobal->Win32KDriverUnload(NULL);
349 }
350 }
351
352
353 VOID
354 NTAPI
355 MiDereferenceSession(VOID)
356 {
357 PMM_SESSION_SPACE SessionGlobal;
358 PEPROCESS Process;
359 ULONG ReferenceCount, SessionId;
360
361 /* Sanity checks */
362 ASSERT(PsGetCurrentProcess()->ProcessInSession ||
363 ((MmSessionSpace->u.Flags.Initialized == 0) &&
364 (PsGetCurrentProcess()->Vm.Flags.SessionLeader == 1) &&
365 (MmSessionSpace->ReferenceCount == 1)));
366
367 /* The session bit must be set */
368 SessionId = MmSessionSpace->SessionId;
369 ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId));
370
371 /* Get the current process */
372 Process = PsGetCurrentProcess();
373
374 /* Decrement the process count */
375 InterlockedDecrement(&MmSessionSpace->ResidentProcessCount);
376
377 /* Decrement the reference count and check if was the last reference */
378 ReferenceCount = InterlockedDecrement(&MmSessionSpace->ReferenceCount);
379 if (ReferenceCount == 0)
380 {
381 /* No more references left, kill the session completely */
382 MiDereferenceSessionFinal();
383 }
384
385 /* Check if tis is the session leader or the last process in the session */
386 if ((Process->Vm.Flags.SessionLeader) || (ReferenceCount == 0))
387 {
388 /* Get the global session address before we kill the session mapping */
389 SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
390
391 /* Delete all session PDEs and flush the TB */
392 RtlZeroMemory(MiAddressToPde(MmSessionBase),
393 BYTES_TO_PAGES(MmSessionSize) * sizeof(MMPDE));
394 KeFlushEntireTb(FALSE, FALSE);
395
396 /* Is this the session leader? */
397 if (Process->Vm.Flags.SessionLeader)
398 {
399 /* Clean up the references here. */
400 ASSERT(Process->Session == NULL);
401 MiReleaseProcessReferenceToSessionDataPage(SessionGlobal);
402 }
403 }
404
405 /* Reset the current process' session flag */
406 RtlInterlockedClearBits(&Process->Flags, PSF_PROCESS_IN_SESSION_BIT);
407 }
408
409 VOID
410 NTAPI
411 MiSessionRemoveProcess(VOID)
412 {
413 PEPROCESS CurrentProcess = PsGetCurrentProcess();
414 KIRQL OldIrql;
415
416 /* If the process isn't already in a session, or if it's the leader... */
417 if (!(CurrentProcess->Flags & PSF_PROCESS_IN_SESSION_BIT) ||
418 (CurrentProcess->Vm.Flags.SessionLeader))
419 {
420 /* Then there's nothing to do */
421 return;
422 }
423
424 /* Sanity check */
425 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
426
427 /* Acquire the expansion lock while touching the session */
428 OldIrql = MiAcquireExpansionLock();
429
430 /* Remove the process from the list */
431 RemoveEntryList(&CurrentProcess->SessionProcessLinks);
432
433 /* Release the lock again */
434 MiReleaseExpansionLock(OldIrql);
435
436 /* Dereference the session */
437 MiDereferenceSession();
438 }
439
440 VOID
441 NTAPI
442 MiSessionAddProcess(IN PEPROCESS NewProcess)
443 {
444 PMM_SESSION_SPACE SessionGlobal;
445 KIRQL OldIrql;
446
447 /* The current process must already be in a session */
448 if (!(PsGetCurrentProcess()->Flags & PSF_PROCESS_IN_SESSION_BIT)) return;
449
450 /* Sanity check */
451 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
452
453 /* Get the global session */
454 SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
455
456 /* Increment counters */
457 InterlockedIncrement((PLONG)&SessionGlobal->ReferenceCount);
458 InterlockedIncrement(&SessionGlobal->ResidentProcessCount);
459 InterlockedIncrement(&SessionGlobal->ProcessReferenceToSession);
460
461 /* Set the session pointer */
462 ASSERT(NewProcess->Session == NULL);
463 NewProcess->Session = SessionGlobal;
464
465 /* Acquire the expansion lock while touching the session */
466 OldIrql = MiAcquireExpansionLock();
467
468 /* Insert it into the process list */
469 InsertTailList(&SessionGlobal->ProcessList, &NewProcess->SessionProcessLinks);
470
471 /* Release the lock again */
472 MiReleaseExpansionLock(OldIrql);
473
474 /* Set the flag */
475 PspSetProcessFlag(NewProcess, PSF_PROCESS_IN_SESSION_BIT);
476 }
477
478 NTSTATUS
479 NTAPI
480 MiSessionInitializeWorkingSetList(VOID)
481 {
482 KIRQL OldIrql;
483 PMMPTE PointerPte, PointerPde;
484 MMPTE TempPte;
485 ULONG Color, Index;
486 PFN_NUMBER PageFrameIndex;
487 PMM_SESSION_SPACE SessionGlobal;
488 BOOLEAN AllocatedPageTable;
489 PMMWSL WorkingSetList;
490
491 /* Get pointers to session global and the session working set list */
492 SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
493 WorkingSetList = (PMMWSL)MiSessionSpaceWs;
494
495 /* Fill out the two pointers */
496 MmSessionSpace->Vm.VmWorkingSetList = WorkingSetList;
497 MmSessionSpace->Wsle = (PMMWSLE)WorkingSetList->UsedPageTableEntries;
498
499 /* Get the PDE for the working set, and check if it's already allocated */
500 PointerPde = MiAddressToPde(WorkingSetList);
501 if (PointerPde->u.Hard.Valid == 1)
502 {
503 /* Nope, we'll have to do it */
504 ASSERT(PointerPde->u.Hard.Global == 0);
505 AllocatedPageTable = FALSE;
506 }
507 else
508 {
509 /* Yep, that makes our job easier */
510 AllocatedPageTable = TRUE;
511 }
512
513 /* Get the PTE for the working set */
514 PointerPte = MiAddressToPte(WorkingSetList);
515
516 /* Initialize the working set lock, and lock the PFN database */
517 ExInitializePushLock(&SessionGlobal->Vm.WorkingSetMutex);
518 //MmLockPageableSectionByHandle(ExPageLockHandle);
519 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
520
521 /* Check if we need a page table */
522 if (AllocatedPageTable == TRUE)
523 {
524 /* Get a zeroed colored zero page */
525 Color = MI_GET_NEXT_COLOR();
526 PageFrameIndex = MiRemoveZeroPageSafe(Color);
527 if (!PageFrameIndex)
528 {
529 /* No zero pages, grab a free one */
530 PageFrameIndex = MiRemoveAnyPage(Color);
531
532 /* Zero it outside the PFN lock */
533 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
534 MiZeroPhysicalPage(PageFrameIndex);
535 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
536 }
537
538 /* Write a valid PDE for it */
539 TempPte.u.Long = ValidKernelPdeLocal.u.Long;
540 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
541 MI_WRITE_VALID_PTE(PointerPde, TempPte);
542
543 /* Add this into the list */
544 Index = ((ULONG_PTR)WorkingSetList - (ULONG_PTR)MmSessionBase) >> 22;
545 #ifndef _M_AMD64
546 MmSessionSpace->PageTables[Index] = TempPte;
547 #endif
548 /* Initialize the page directory page, and now zero the working set list itself */
549 MiInitializePfnForOtherProcess(PageFrameIndex,
550 PointerPde,
551 MmSessionSpace->SessionPageDirectoryIndex);
552 KeZeroPages(PointerPte, PAGE_SIZE);
553 }
554
555 /* Get a zeroed colored zero page */
556 Color = MI_GET_NEXT_COLOR();
557 PageFrameIndex = MiRemoveZeroPageSafe(Color);
558 if (!PageFrameIndex)
559 {
560 /* No zero pages, grab a free one */
561 PageFrameIndex = MiRemoveAnyPage(Color);
562
563 /* Zero it outside the PFN lock */
564 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
565 MiZeroPhysicalPage(PageFrameIndex);
566 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
567 }
568
569 /* Write a valid PTE for it */
570 TempPte.u.Long = ValidKernelPteLocal.u.Long;
571 TempPte.u.Hard.Dirty = TRUE;
572 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
573
574 /* Initialize the working set list page */
575 MiInitializePfnAndMakePteValid(PageFrameIndex, PointerPte, TempPte);
576
577 /* Now we can release the PFN database lock */
578 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
579
580 /* Fill out the working set structure */
581 MmSessionSpace->Vm.Flags.SessionSpace = 1;
582 MmSessionSpace->Vm.MinimumWorkingSetSize = 20;
583 MmSessionSpace->Vm.MaximumWorkingSetSize = 384;
584 WorkingSetList->LastEntry = 20;
585 WorkingSetList->HashTable = NULL;
586 WorkingSetList->HashTableSize = 0;
587 WorkingSetList->Wsle = MmSessionSpace->Wsle;
588
589 /* Acquire the expansion lock while touching the session */
590 OldIrql = MiAcquireExpansionLock();
591
592 /* Handle list insertions */
593 ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
594 ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
595 InsertTailList(&MiSessionWsList, &SessionGlobal->WsListEntry);
596
597 ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Flink == NULL);
598 ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Blink == NULL);
599 InsertTailList(&MmWorkingSetExpansionHead,
600 &SessionGlobal->Vm.WorkingSetExpansionLinks);
601
602 /* Release the lock again */
603 MiReleaseExpansionLock(OldIrql);
604
605 /* All done, return */
606 //MmUnlockPageableImageSection(ExPageLockHandle);
607 return STATUS_SUCCESS;
608 }
609
610 NTSTATUS
611 NTAPI
612 MiSessionCreateInternal(OUT PULONG SessionId)
613 {
614 PEPROCESS Process = PsGetCurrentProcess();
615 ULONG NewFlags, Flags, Size, i, Color;
616 KIRQL OldIrql;
617 PMMPTE PointerPte, PageTables, SessionPte;
618 PMMPDE PointerPde;
619 PMM_SESSION_SPACE SessionGlobal;
620 MMPTE TempPte;
621 NTSTATUS Status;
622 BOOLEAN Result;
623 PFN_NUMBER SessionPageDirIndex;
624 PFN_NUMBER TagPage[MI_SESSION_TAG_PAGES_MAXIMUM];
625 PFN_NUMBER DataPage[MI_SESSION_DATA_PAGES_MAXIMUM];
626
627 /* This should not exist yet */
628 ASSERT(MmIsAddressValid(MmSessionSpace) == FALSE);
629
630 /* Loop so we can set the session-is-creating flag */
631 Flags = Process->Flags;
632 while (TRUE)
633 {
634 /* Check if it's already set */
635 if (Flags & PSF_SESSION_CREATION_UNDERWAY_BIT)
636 {
637 /* Bail out */
638 DPRINT1("Lost session race\n");
639 return STATUS_ALREADY_COMMITTED;
640 }
641
642 /* Now try to set it */
643 NewFlags = InterlockedCompareExchange((PLONG)&Process->Flags,
644 Flags | PSF_SESSION_CREATION_UNDERWAY_BIT,
645 Flags);
646 if (NewFlags == Flags) break;
647
648 /* It changed, try again */
649 Flags = NewFlags;
650 }
651
652 /* Now we should own the flag */
653 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
654
655 /*
656 * Session space covers everything from 0xA0000000 to 0xC0000000.
657 * Allocate enough page tables to describe the entire region
658 */
659 Size = (0x20000000 / PDE_MAPPED_VA) * sizeof(MMPTE);
660 PageTables = ExAllocatePoolWithTag(NonPagedPool, Size, 'tHmM');
661 ASSERT(PageTables != NULL);
662 RtlZeroMemory(PageTables, Size);
663
664 /* Lock the session ID creation mutex */
665 KeAcquireGuardedMutex(&MiSessionIdMutex);
666
667 /* Allocate a new Session ID */
668 *SessionId = RtlFindClearBitsAndSet(MiSessionIdBitmap, 1, 0);
669 if (*SessionId == 0xFFFFFFFF)
670 {
671 /* We ran out of session IDs, we should expand */
672 DPRINT1("Too many sessions created. Expansion not yet supported\n");
673 ExFreePoolWithTag(PageTables, 'tHmM');
674 return STATUS_NO_MEMORY;
675 }
676
677 /* Unlock the session ID creation mutex */
678 KeReleaseGuardedMutex(&MiSessionIdMutex);
679
680 /* Reserve the global PTEs */
681 SessionPte = MiReserveSystemPtes(MiSessionDataPages, SystemPteSpace);
682 ASSERT(SessionPte != NULL);
683
684 /* Acquire the PFN lock while we set everything up */
685 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
686
687 /* Loop the global PTEs */
688 TempPte.u.Long = ValidKernelPte.u.Long;
689 for (i = 0; i < MiSessionDataPages; i++)
690 {
691 /* Get a zeroed colored zero page */
692 Color = MI_GET_NEXT_COLOR();
693 DataPage[i] = MiRemoveZeroPageSafe(Color);
694 if (!DataPage[i])
695 {
696 /* No zero pages, grab a free one */
697 DataPage[i] = MiRemoveAnyPage(Color);
698
699 /* Zero it outside the PFN lock */
700 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
701 MiZeroPhysicalPage(DataPage[i]);
702 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
703 }
704
705 /* Fill the PTE out */
706 TempPte.u.Hard.PageFrameNumber = DataPage[i];
707 MI_WRITE_VALID_PTE(SessionPte + i, TempPte);
708 }
709
710 /* Set the pointer to global space */
711 SessionGlobal = MiPteToAddress(SessionPte);
712
713 /* Get a zeroed colored zero page */
714 Color = MI_GET_NEXT_COLOR();
715 SessionPageDirIndex = MiRemoveZeroPageSafe(Color);
716 if (!SessionPageDirIndex)
717 {
718 /* No zero pages, grab a free one */
719 SessionPageDirIndex = MiRemoveAnyPage(Color);
720
721 /* Zero it outside the PFN lock */
722 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
723 MiZeroPhysicalPage(SessionPageDirIndex);
724 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
725 }
726
727 /* Fill the PTE out */
728 TempPte.u.Long = ValidKernelPdeLocal.u.Long;
729 TempPte.u.Hard.PageFrameNumber = SessionPageDirIndex;
730
731 /* Setup, allocate, fill out the MmSessionSpace PTE */
732 PointerPde = MiAddressToPde(MmSessionSpace);
733 ASSERT(PointerPde->u.Long == 0);
734 MI_WRITE_VALID_PTE(PointerPde, TempPte);
735 MiInitializePfnForOtherProcess(SessionPageDirIndex,
736 PointerPde,
737 SessionPageDirIndex);
738 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex)->u1.WsIndex == 0);
739
740 /* Loop all the local PTEs for it */
741 TempPte.u.Long = ValidKernelPteLocal.u.Long;
742 PointerPte = MiAddressToPte(MmSessionSpace);
743 for (i = 0; i < MiSessionDataPages; i++)
744 {
745 /* And fill them out */
746 TempPte.u.Hard.PageFrameNumber = DataPage[i];
747 MiInitializePfnAndMakePteValid(DataPage[i], PointerPte + i, TempPte);
748 ASSERT(MI_PFN_ELEMENT(DataPage[i])->u1.WsIndex == 0);
749 }
750
751 /* Finally loop all of the session pool tag pages */
752 for (i = 0; i < MiSessionTagPages; i++)
753 {
754 /* Grab a zeroed colored page */
755 Color = MI_GET_NEXT_COLOR();
756 TagPage[i] = MiRemoveZeroPageSafe(Color);
757 if (!TagPage[i])
758 {
759 /* No zero pages, grab a free one */
760 TagPage[i] = MiRemoveAnyPage(Color);
761
762 /* Zero it outside the PFN lock */
763 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
764 MiZeroPhysicalPage(TagPage[i]);
765 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
766 }
767
768 /* Fill the PTE out */
769 TempPte.u.Hard.PageFrameNumber = TagPage[i];
770 MiInitializePfnAndMakePteValid(TagPage[i],
771 PointerPte + MiSessionDataPages + i,
772 TempPte);
773 }
774
775 /* PTEs have been setup, release the PFN lock */
776 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
777
778 /* Fill out the session space structure now */
779 MmSessionSpace->GlobalVirtualAddress = SessionGlobal;
780 MmSessionSpace->ReferenceCount = 1;
781 MmSessionSpace->ResidentProcessCount = 1;
782 MmSessionSpace->u.LongFlags = 0;
783 MmSessionSpace->SessionId = *SessionId;
784 MmSessionSpace->LocaleId = PsDefaultSystemLocaleId;
785 MmSessionSpace->SessionPageDirectoryIndex = SessionPageDirIndex;
786 MmSessionSpace->Color = Color;
787 MmSessionSpace->NonPageablePages = MiSessionCreateCharge;
788 MmSessionSpace->CommittedPages = MiSessionCreateCharge;
789 #ifndef _M_AMD64
790 MmSessionSpace->PageTables = PageTables;
791 MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde;
792 #endif
793 InitializeListHead(&MmSessionSpace->ImageList);
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
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
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 /* FIXME: Do it */
908 KeLeaveCriticalRegion();
909
910 /* All done */
911 return STATUS_SUCCESS;
912 }
913
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 NT_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
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 NT_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
1030 MmQuitNextSession(
1031 _Inout_ PVOID SessionEntry)
1032 {
1033 PEPROCESS EntryProcess;
1034
1035 /* The parameter is the actual process! */
1036 EntryProcess = SessionEntry;
1037 NT_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
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
1067 /* Check if this is the session we are looking for */
1068 if (Session->SessionId == SessionId)
1069 {
1070 /* Check if we also have a process in the process list */
1071 if (!IsListEmpty(&Session->ProcessList))
1072 {
1073 Process = CONTAINING_RECORD(Session->ProcessList.Flink,
1074 EPROCESS,
1075 SessionProcessLinks);
1076
1077 /* Reference the process */
1078 ObReferenceObject(Process);
1079 break;
1080 }
1081 }
1082 }
1083
1084 /* Release the lock again */
1085 MiReleaseExpansionLock(OldIrql);
1086
1087 return Process;
1088 }