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