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)
10 /* INCLUDES *******************************************************************/
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
19 /* GLOBALS ********************************************************************/
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
;
29 LIST_ENTRY MiSessionWsList
;
30 LIST_ENTRY MmWorkingSetExpansionHead
;
32 KSPIN_LOCK MmExpansionLock
;
33 PETHREAD MiExpansionLockOwner
;
36 /* PRIVATE FUNCTIONS **********************************************************/
40 MiAcquireExpansionLock()
44 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
45 KeAcquireSpinLock(&MmExpansionLock
, &OldIrql
);
46 ASSERT(MiExpansionLockOwner
== NULL
);
47 MiExpansionLockOwner
= PsGetCurrentThread();
53 MiReleaseExpansionLock(KIRQL OldIrql
)
55 ASSERT(MiExpansionLockOwner
== PsGetCurrentThread());
56 MiExpansionLockOwner
= NULL
;
57 KeReleaseSpinLock(&MmExpansionLock
, OldIrql
);
58 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
63 MiInitializeSessionWsSupport(VOID
)
65 /* Initialize the list heads */
66 InitializeListHead(&MiSessionWsList
);
67 InitializeListHead(&MmWorkingSetExpansionHead
);
72 MmGetSessionLocaleId(VOID
)
78 // Get the current process
80 Process
= PsGetCurrentProcess();
83 // Check if it's NOT the Session Leader
85 if (!Process
->Vm
.Flags
.SessionLeader
)
88 // Make sure it has a valid Session
95 return ((PMM_SESSION_SPACE
)Process
->Session
)->LocaleId
;
100 // Not a session leader, return the default
102 return PsDefaultThreadLocaleId
;
105 _IRQL_requires_max_(APC_LEVEL
)
108 MmSetSessionLocaleId(
111 PEPROCESS CurrentProcess
;
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
))
119 /* Set the session locale Id */
120 ((PMM_SESSION_SPACE
)CurrentProcess
->Session
)->LocaleId
= LocaleId
;
124 /* Set the default locale */
125 PsDefaultThreadLocaleId
= LocaleId
;
132 MiInitializeSessionIds(VOID
)
134 ULONG Size
, BitmapSize
;
135 PFN_NUMBER TotalPages
;
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
;
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
);
150 /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */
151 MiSessionCreateCharge
= 1 + MiSessionDataPages
+ MiSessionTagPages
;
153 /* Initialize the lock */
154 KeInitializeGuardedMutex(&MiSessionIdMutex
);
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
,
162 if (MiSessionIdBitmap
)
164 /* Free all the bits */
165 RtlInitializeBitMap(MiSessionIdBitmap
,
166 (PVOID
)(MiSessionIdBitmap
+ 1),
168 RtlClearAllBits(MiSessionIdBitmap
);
172 /* Die if we couldn't allocate the bitmap */
173 KeBugCheckEx(INSTALL_MORE_MEMORY
,
174 MmNumberOfPhysicalPages
,
175 MmLowestPhysicalPage
,
176 MmHighestPhysicalPage
,
183 MiSessionLeader(IN PEPROCESS Process
)
187 /* Set the flag while under the expansion lock */
188 OldIrql
= KeAcquireQueuedSpinLock(LockQueueExpansionLock
);
189 Process
->Vm
.Flags
.SessionLeader
= TRUE
;
190 KeReleaseQueuedSpinLock(LockQueueExpansionLock
, OldIrql
);
195 MmGetSessionId(IN PEPROCESS Process
)
197 PMM_SESSION_SPACE SessionGlobal
;
199 /* The session leader is always session zero */
200 if (Process
->Vm
.Flags
.SessionLeader
== 1) return 0;
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
;
210 MmGetSessionIdEx(IN PEPROCESS Process
)
212 PMM_SESSION_SPACE SessionGlobal
;
214 /* The session leader is always session zero */
215 if (Process
->Vm
.Flags
.SessionLeader
== 1) return 0;
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
;
225 MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal
)
229 PFN_NUMBER PageFrameIndex
[MI_SESSION_DATA_PAGES_MAXIMUM
];
233 /* Is there more than just this reference? If so, bail out */
234 if (InterlockedDecrement(&SessionGlobal
->ProcessReferenceToSession
)) return;
236 /* Get the session ID */
237 SessionId
= SessionGlobal
->SessionId
;
238 DPRINT1("Last process in session %lu going down!!!\n", SessionId
);
240 /* Free the session page tables */
242 ExFreePoolWithTag(SessionGlobal
->PageTables
, 'tHmM');
244 ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal
));
246 /* Capture the data page PFNs */
247 PointerPte
= MiAddressToPte(SessionGlobal
);
248 for (i
= 0; i
< MiSessionDataPages
; i
++)
250 PageFrameIndex
[i
] = PFN_FROM_PTE(PointerPte
+ i
);
254 MiReleaseSystemPtes(PointerPte
, MiSessionDataPages
, SystemPteSpace
);
256 /* Mark them as deleted */
257 for (i
= 0; i
< MiSessionDataPages
; i
++)
259 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
[i
]);
260 MI_SET_PFN_DELETED(Pfn1
);
263 /* Loop every data page and drop a reference count */
264 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
265 for (i
= 0; i
< MiSessionDataPages
; i
++)
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
]);
274 /* Done playing with pages, release the lock */
275 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
277 /* Decrement the number of data pages */
278 InterlockedDecrement(&MmSessionDataPages
);
280 /* Free this session ID from the session bitmap */
281 KeAcquireGuardedMutex(&MiSessionIdMutex
);
282 ASSERT(RtlCheckBit(MiSessionIdBitmap
, SessionId
));
283 RtlClearBit(MiSessionIdBitmap
, SessionId
);
284 KeReleaseGuardedMutex(&MiSessionIdMutex
);
289 MiDereferenceSessionFinal(VOID
)
291 PMM_SESSION_SPACE SessionGlobal
;
294 /* Get the pointer to the global session address */
295 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
297 /* Acquire the expansion lock */
298 OldIrql
= MiAcquireExpansionLock();
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;
305 /* Check if we have any attached processes */
306 if (SessionGlobal
->AttachCount
)
308 /* Initialize the event (it's not in use yet!) */
309 KeInitializeEvent(&SessionGlobal
->AttachEvent
, NotificationEvent
, FALSE
);
311 /* Release the expansion lock for the wait */
312 MiReleaseExpansionLock(OldIrql
);
314 /* Wait for the event to be set due to the last process detach */
315 KeWaitForSingleObject(&SessionGlobal
->AttachEvent
, WrVirtualMemory
, 0, 0, 0);
317 /* Reacquire the expansion lock */
318 OldIrql
= MiAcquireExpansionLock();
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);
325 /* Check if the session is in the workingset expansion list */
326 if (SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
!= NULL
)
328 /* Remove the session from the list and zero the list entry */
329 RemoveEntryList(&SessionGlobal
->Vm
.WorkingSetExpansionLinks
);
330 SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
= 0;
333 /* Check if the session is in the workingset list */
334 if (SessionGlobal
->WsListEntry
.Flink
)
336 /* Remove the session from the list and zero the list entry */
337 RemoveEntryList(&SessionGlobal
->WsListEntry
);
338 SessionGlobal
->WsListEntry
.Flink
= NULL
;
341 /* Release the expansion lock */
342 MiReleaseExpansionLock(OldIrql
);
344 /* Check for a win32k unload routine */
345 if (SessionGlobal
->Win32KDriverUnload
)
348 SessionGlobal
->Win32KDriverUnload(NULL
);
355 MiDereferenceSession(VOID
)
357 PMM_SESSION_SPACE SessionGlobal
;
359 ULONG ReferenceCount
, SessionId
;
362 ASSERT(PsGetCurrentProcess()->ProcessInSession
||
363 ((MmSessionSpace
->u
.Flags
.Initialized
== 0) &&
364 (PsGetCurrentProcess()->Vm
.Flags
.SessionLeader
== 1) &&
365 (MmSessionSpace
->ReferenceCount
== 1)));
367 /* The session bit must be set */
368 SessionId
= MmSessionSpace
->SessionId
;
369 ASSERT(RtlCheckBit(MiSessionIdBitmap
, SessionId
));
371 /* Get the current process */
372 Process
= PsGetCurrentProcess();
374 /* Decrement the process count */
375 InterlockedDecrement(&MmSessionSpace
->ResidentProcessCount
);
377 /* Decrement the reference count and check if was the last reference */
378 ReferenceCount
= InterlockedDecrement(&MmSessionSpace
->ReferenceCount
);
379 if (ReferenceCount
== 0)
381 /* No more references left, kill the session completely */
382 MiDereferenceSessionFinal();
385 /* Check if tis is the session leader or the last process in the session */
386 if ((Process
->Vm
.Flags
.SessionLeader
) || (ReferenceCount
== 0))
388 /* Get the global session address before we kill the session mapping */
389 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
391 /* Delete all session PDEs and flush the TB */
392 RtlZeroMemory(MiAddressToPde(MmSessionBase
),
393 BYTES_TO_PAGES(MmSessionSize
) * sizeof(MMPDE
));
394 KeFlushEntireTb(FALSE
, FALSE
);
396 /* Is this the session leader? */
397 if (Process
->Vm
.Flags
.SessionLeader
)
399 /* Clean up the references here. */
400 ASSERT(Process
->Session
== NULL
);
401 MiReleaseProcessReferenceToSessionDataPage(SessionGlobal
);
405 /* Reset the current process' session flag */
406 RtlInterlockedClearBits(&Process
->Flags
, PSF_PROCESS_IN_SESSION_BIT
);
411 MiSessionRemoveProcess(VOID
)
413 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
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
))
420 /* Then there's nothing to do */
425 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
427 /* Acquire the expansion lock while touching the session */
428 OldIrql
= MiAcquireExpansionLock();
430 /* Remove the process from the list */
431 RemoveEntryList(&CurrentProcess
->SessionProcessLinks
);
433 /* Release the lock again */
434 MiReleaseExpansionLock(OldIrql
);
436 /* Dereference the session */
437 MiDereferenceSession();
442 MiSessionAddProcess(IN PEPROCESS NewProcess
)
444 PMM_SESSION_SPACE SessionGlobal
;
447 /* The current process must already be in a session */
448 if (!(PsGetCurrentProcess()->Flags
& PSF_PROCESS_IN_SESSION_BIT
)) return;
451 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
453 /* Get the global session */
454 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
456 /* Increment counters */
457 InterlockedIncrement((PLONG
)&SessionGlobal
->ReferenceCount
);
458 InterlockedIncrement(&SessionGlobal
->ResidentProcessCount
);
459 InterlockedIncrement(&SessionGlobal
->ProcessReferenceToSession
);
461 /* Set the session pointer */
462 ASSERT(NewProcess
->Session
== NULL
);
463 NewProcess
->Session
= SessionGlobal
;
465 /* Acquire the expansion lock while touching the session */
466 OldIrql
= MiAcquireExpansionLock();
468 /* Insert it into the process list */
469 InsertTailList(&SessionGlobal
->ProcessList
, &NewProcess
->SessionProcessLinks
);
471 /* Release the lock again */
472 MiReleaseExpansionLock(OldIrql
);
475 PspSetProcessFlag(NewProcess
, PSF_PROCESS_IN_SESSION_BIT
);
480 MiSessionInitializeWorkingSetList(VOID
)
483 PMMPTE PointerPte
, PointerPde
;
486 PFN_NUMBER PageFrameIndex
;
487 PMM_SESSION_SPACE SessionGlobal
;
488 BOOLEAN AllocatedPageTable
;
489 PMMWSL WorkingSetList
;
491 /* Get pointers to session global and the session working set list */
492 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
493 WorkingSetList
= (PMMWSL
)MiSessionSpaceWs
;
495 /* Fill out the two pointers */
496 MmSessionSpace
->Vm
.VmWorkingSetList
= WorkingSetList
;
497 MmSessionSpace
->Wsle
= (PMMWSLE
)WorkingSetList
->UsedPageTableEntries
;
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)
503 /* Nope, we'll have to do it */
504 ASSERT(PointerPde
->u
.Hard
.Global
== 0);
505 AllocatedPageTable
= FALSE
;
509 /* Yep, that makes our job easier */
510 AllocatedPageTable
= TRUE
;
513 /* Get the PTE for the working set */
514 PointerPte
= MiAddressToPte(WorkingSetList
);
516 /* Initialize the working set lock, and lock the PFN database */
517 ExInitializePushLock(&SessionGlobal
->Vm
.WorkingSetMutex
);
518 //MmLockPageableSectionByHandle(ExPageLockHandle);
519 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
521 /* Check if we need a page table */
522 if (AllocatedPageTable
!= FALSE
)
524 /* Get a zeroed colored zero page */
525 Color
= MI_GET_NEXT_COLOR();
526 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
529 /* No zero pages, grab a free one */
530 PageFrameIndex
= MiRemoveAnyPage(Color
);
532 /* Zero it outside the PFN lock */
533 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
534 MiZeroPhysicalPage(PageFrameIndex
);
535 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
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
);
543 /* Add this into the list */
544 Index
= ((ULONG_PTR
)WorkingSetList
- (ULONG_PTR
)MmSessionBase
) >> 22;
546 MmSessionSpace
->PageTables
[Index
] = TempPte
;
548 /* Initialize the page directory page, and now zero the working set list itself */
549 MiInitializePfnForOtherProcess(PageFrameIndex
,
551 MmSessionSpace
->SessionPageDirectoryIndex
);
552 KeZeroPages(PointerPte
, PAGE_SIZE
);
555 /* Get a zeroed colored zero page */
556 Color
= MI_GET_NEXT_COLOR();
557 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
560 /* No zero pages, grab a free one */
561 PageFrameIndex
= MiRemoveAnyPage(Color
);
563 /* Zero it outside the PFN lock */
564 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
565 MiZeroPhysicalPage(PageFrameIndex
);
566 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
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
;
574 /* Initialize the working set list page */
575 MiInitializePfnAndMakePteValid(PageFrameIndex
, PointerPte
, TempPte
);
577 /* Now we can release the PFN database lock */
578 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
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
;
589 /* Acquire the expansion lock while touching the session */
590 OldIrql
= MiAcquireExpansionLock();
592 /* Handle list insertions */
593 ASSERT(SessionGlobal
->WsListEntry
.Flink
== NULL
);
594 ASSERT(SessionGlobal
->WsListEntry
.Blink
== NULL
);
595 InsertTailList(&MiSessionWsList
, &SessionGlobal
->WsListEntry
);
597 ASSERT(SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
== NULL
);
598 ASSERT(SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Blink
== NULL
);
599 InsertTailList(&MmWorkingSetExpansionHead
,
600 &SessionGlobal
->Vm
.WorkingSetExpansionLinks
);
602 /* Release the lock again */
603 MiReleaseExpansionLock(OldIrql
);
605 /* All done, return */
606 //MmUnlockPageableImageSection(ExPageLockHandle);
607 return STATUS_SUCCESS
;
612 MiSessionCreateInternal(OUT PULONG SessionId
)
614 PEPROCESS Process
= PsGetCurrentProcess();
615 ULONG NewFlags
, Flags
, Size
, i
, Color
;
617 PMMPTE PointerPte
, PageTables
, SessionPte
;
619 PMM_SESSION_SPACE SessionGlobal
;
623 PFN_NUMBER SessionPageDirIndex
;
624 PFN_NUMBER TagPage
[MI_SESSION_TAG_PAGES_MAXIMUM
];
625 PFN_NUMBER DataPage
[MI_SESSION_DATA_PAGES_MAXIMUM
];
627 /* This should not exist yet */
628 ASSERT(MmIsAddressValid(MmSessionSpace
) == FALSE
);
630 /* Loop so we can set the session-is-creating flag */
631 Flags
= Process
->Flags
;
634 /* Check if it's already set */
635 if (Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
)
638 DPRINT1("Lost session race\n");
639 return STATUS_ALREADY_COMMITTED
;
642 /* Now try to set it */
643 NewFlags
= InterlockedCompareExchange((PLONG
)&Process
->Flags
,
644 Flags
| PSF_SESSION_CREATION_UNDERWAY_BIT
,
646 if (NewFlags
== Flags
) break;
648 /* It changed, try again */
652 /* Now we should own the flag */
653 ASSERT(Process
->Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
);
656 * Session space covers everything from 0xA0000000 to 0xC0000000.
657 * Allocate enough page tables to describe the entire region
659 Size
= (0x20000000 / PDE_MAPPED_VA
) * sizeof(MMPTE
);
660 PageTables
= ExAllocatePoolWithTag(NonPagedPool
, Size
, 'tHmM');
661 ASSERT(PageTables
!= NULL
);
662 RtlZeroMemory(PageTables
, Size
);
664 /* Lock the session ID creation mutex */
665 KeAcquireGuardedMutex(&MiSessionIdMutex
);
667 /* Allocate a new Session ID */
668 *SessionId
= RtlFindClearBitsAndSet(MiSessionIdBitmap
, 1, 0);
669 if (*SessionId
== 0xFFFFFFFF)
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
;
677 /* Unlock the session ID creation mutex */
678 KeReleaseGuardedMutex(&MiSessionIdMutex
);
680 /* Reserve the global PTEs */
681 SessionPte
= MiReserveSystemPtes(MiSessionDataPages
, SystemPteSpace
);
682 ASSERT(SessionPte
!= NULL
);
684 /* Acquire the PFN lock while we set everything up */
685 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
687 /* Loop the global PTEs */
688 TempPte
.u
.Long
= ValidKernelPte
.u
.Long
;
689 for (i
= 0; i
< MiSessionDataPages
; i
++)
691 /* Get a zeroed colored zero page */
692 Color
= MI_GET_NEXT_COLOR();
693 DataPage
[i
] = MiRemoveZeroPageSafe(Color
);
696 /* No zero pages, grab a free one */
697 DataPage
[i
] = MiRemoveAnyPage(Color
);
699 /* Zero it outside the PFN lock */
700 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
701 MiZeroPhysicalPage(DataPage
[i
]);
702 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
705 /* Fill the PTE out */
706 TempPte
.u
.Hard
.PageFrameNumber
= DataPage
[i
];
707 MI_WRITE_VALID_PTE(SessionPte
+ i
, TempPte
);
710 /* Set the pointer to global space */
711 SessionGlobal
= MiPteToAddress(SessionPte
);
713 /* Get a zeroed colored zero page */
714 Color
= MI_GET_NEXT_COLOR();
715 SessionPageDirIndex
= MiRemoveZeroPageSafe(Color
);
716 if (!SessionPageDirIndex
)
718 /* No zero pages, grab a free one */
719 SessionPageDirIndex
= MiRemoveAnyPage(Color
);
721 /* Zero it outside the PFN lock */
722 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
723 MiZeroPhysicalPage(SessionPageDirIndex
);
724 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
727 /* Fill the PTE out */
728 TempPte
.u
.Long
= ValidKernelPdeLocal
.u
.Long
;
729 TempPte
.u
.Hard
.PageFrameNumber
= SessionPageDirIndex
;
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
,
737 SessionPageDirIndex
);
738 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex
)->u1
.WsIndex
== 0);
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
++)
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);
751 /* Finally loop all of the session pool tag pages */
752 for (i
= 0; i
< MiSessionTagPages
; i
++)
754 /* Grab a zeroed colored page */
755 Color
= MI_GET_NEXT_COLOR();
756 TagPage
[i
] = MiRemoveZeroPageSafe(Color
);
759 /* No zero pages, grab a free one */
760 TagPage
[i
] = MiRemoveAnyPage(Color
);
762 /* Zero it outside the PFN lock */
763 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
764 MiZeroPhysicalPage(TagPage
[i
]);
765 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
768 /* Fill the PTE out */
769 TempPte
.u
.Hard
.PageFrameNumber
= TagPage
[i
];
770 MiInitializePfnAndMakePteValid(TagPage
[i
],
771 PointerPte
+ MiSessionDataPages
+ i
,
775 /* PTEs have been setup, release the PFN lock */
776 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
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
;
790 MmSessionSpace
->PageTables
= PageTables
;
791 MmSessionSpace
->PageTables
[PointerPde
- MiAddressToPde(MmSessionBase
)] = *PointerPde
;
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
);
797 /* Initialize session pool */
798 //Status = MiInitializeSessionPool();
799 Status
= STATUS_SUCCESS
;
800 ASSERT(NT_SUCCESS(Status
) == TRUE
);
802 /* Initialize system space */
803 Result
= MiInitializeSystemSpaceMap(&SessionGlobal
->Session
);
804 ASSERT(Result
== TRUE
);
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
);
811 /* We're done, clear the flag */
812 ASSERT(Process
->Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
);
813 PspClearProcessFlag(Process
, PSF_SESSION_CREATION_UNDERWAY_BIT
);
815 /* Insert the process into the session */
816 ASSERT(Process
->Session
== NULL
);
817 ASSERT(SessionGlobal
->ProcessReferenceToSession
== 0);
818 SessionGlobal
->ProcessReferenceToSession
= 1;
821 InterlockedIncrement(&MmSessionDataPages
);
822 return STATUS_SUCCESS
;
827 MmSessionCreate(OUT PULONG SessionId
)
829 PEPROCESS Process
= PsGetCurrentProcess();
830 ULONG SessionLeaderExists
;
833 /* Fail if the process is already in a session */
834 if (Process
->Flags
& PSF_PROCESS_IN_SESSION_BIT
)
836 DPRINT1("Process already in session\n");
837 return STATUS_ALREADY_COMMITTED
;
840 /* Check if the process is already the session leader */
841 if (!Process
->Vm
.Flags
.SessionLeader
)
843 /* Atomically set it as the leader */
844 SessionLeaderExists
= InterlockedCompareExchange(&MiSessionLeaderExists
, 1, 0);
845 if (SessionLeaderExists
)
847 DPRINT1("Session leader race\n");
848 return STATUS_INVALID_SYSTEM_SERVICE
;
851 /* Do the work required to upgrade him */
852 MiSessionLeader(Process
);
855 /* Create the session */
856 KeEnterCriticalRegion();
857 Status
= MiSessionCreateInternal(SessionId
);
858 if (!NT_SUCCESS(Status
))
860 KeLeaveCriticalRegion();
864 /* Set up the session working set */
865 Status
= MiSessionInitializeWorkingSetList();
866 if (!NT_SUCCESS(Status
))
869 //MiDereferenceSession();
871 KeLeaveCriticalRegion();
876 KeLeaveCriticalRegion();
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);
887 MmSessionDelete(IN ULONG SessionId
)
889 PEPROCESS Process
= PsGetCurrentProcess();
891 /* Process must be in a session */
892 if (!(Process
->Flags
& PSF_PROCESS_IN_SESSION_BIT
))
894 DPRINT1("Not in a session!\n");
895 return STATUS_UNABLE_TO_FREE_VM
;
898 /* It must be the session leader */
899 if (!Process
->Vm
.Flags
.SessionLeader
)
901 DPRINT1("Not a session leader!\n");
902 return STATUS_UNABLE_TO_FREE_VM
;
905 /* Remove one reference count */
906 KeEnterCriticalRegion();
908 KeLeaveCriticalRegion();
911 return STATUS_SUCCESS
;
914 _IRQL_requires_max_(APC_LEVEL
)
918 _Inout_ PVOID SessionEntry
,
919 _Out_ PKAPC_STATE ApcState
)
921 PEPROCESS EntryProcess
;
922 PMM_SESSION_SPACE EntrySession
, CurrentSession
;
923 PEPROCESS CurrentProcess
;
926 /* The parameter is the actual process! */
927 EntryProcess
= SessionEntry
;
928 NT_ASSERT(EntryProcess
!= NULL
);
931 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
932 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
934 /* Get the session from the process that was passed in */
935 EntrySession
= EntryProcess
->Session
;
936 ASSERT(EntrySession
!= NULL
);
938 /* Get the current process and it's session */
939 CurrentProcess
= PsGetCurrentProcess();
940 CurrentSession
= CurrentProcess
->Session
;
942 /* Acquire the expansion lock while touching the session */
943 OldIrql
= MiAcquireExpansionLock();
945 /* Check if the session is about to be deleted */
946 if (EntrySession
->u
.Flags
.DeletePending
)
948 /* We cannot attach to it, so unlock and fail */
949 MiReleaseExpansionLock(OldIrql
);
950 return STATUS_PROCESS_IS_TERMINATING
;
953 /* Count the number of attaches */
954 EntrySession
->AttachCount
++;
956 /* we can release the lock again */
957 MiReleaseExpansionLock(OldIrql
);
959 /* Check if we are not the session leader and we are in a session */
960 if (!CurrentProcess
->Vm
.Flags
.SessionLeader
&& (CurrentSession
!= NULL
))
962 /* Are we already in the right session? */
963 if (CurrentSession
== EntrySession
)
965 /* We are, so "attach" to the current process */
966 EntryProcess
= CurrentProcess
;
970 /* We are not, the session id should better not match! */
971 ASSERT(CurrentSession
->SessionId
!= EntrySession
->SessionId
);
975 /* Now attach to the process that we have */
976 KeStackAttachProcess(&EntryProcess
->Pcb
, ApcState
);
979 return STATUS_SUCCESS
;
982 _IRQL_requires_max_(APC_LEVEL
)
986 _Inout_ PVOID SessionEntry
,
987 _In_ PKAPC_STATE ApcState
)
989 PEPROCESS EntryProcess
;
990 PMM_SESSION_SPACE EntrySession
;
992 BOOLEAN DeletePending
;
994 /* The parameter is the actual process! */
995 EntryProcess
= SessionEntry
;
996 NT_ASSERT(EntryProcess
!= NULL
);
999 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1000 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
1002 /* Get the session from the process that was passed in */
1003 EntrySession
= EntryProcess
->Session
;
1004 ASSERT(EntrySession
!= NULL
);
1006 /* Acquire the expansion lock while touching the session */
1007 OldIrql
= MiAcquireExpansionLock();
1009 /* Make sure we have at least one attach and decrement the count */
1010 ASSERT(EntrySession
->AttachCount
>= 1);
1011 EntrySession
->AttachCount
--;
1013 /* Remember if a delete is pending and we were the last one attached */
1014 DeletePending
= EntrySession
->u
.Flags
.DeletePending
&&
1015 (EntrySession
->AttachCount
== 0);
1017 /* Release the lock again */
1018 MiReleaseExpansionLock(OldIrql
);
1020 /* Detach from the process */
1021 KeUnstackDetachProcess(ApcState
);
1023 /* Check if we need to set the attach event */
1025 KeSetEvent(&EntrySession
->AttachEvent
, IO_NO_INCREMENT
, FALSE
);
1031 _Inout_ PVOID SessionEntry
)
1033 PEPROCESS EntryProcess
;
1035 /* The parameter is the actual process! */
1036 EntryProcess
= SessionEntry
;
1037 NT_ASSERT(EntryProcess
!= NULL
);
1040 ASSERT(KeGetCurrentIrql () <= APC_LEVEL
);
1041 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
1042 ASSERT(EntryProcess
->Session
!= NULL
);
1044 /* Get rid of the reference we took */
1045 ObDereferenceObject(EntryProcess
);
1051 _In_ ULONG SessionId
)
1053 PLIST_ENTRY ListEntry
;
1054 PMM_SESSION_SPACE Session
;
1055 PEPROCESS Process
= NULL
;
1058 /* Acquire the expansion lock while touching the session */
1059 OldIrql
= MiAcquireExpansionLock();
1061 /* Loop all entries in the session ws list */
1062 ListEntry
= MiSessionWsList
.Flink
;
1063 while (ListEntry
!= &MiSessionWsList
)
1065 Session
= CONTAINING_RECORD(ListEntry
, MM_SESSION_SPACE
, WsListEntry
);
1067 /* Check if this is the session we are looking for */
1068 if (Session
->SessionId
== SessionId
)
1070 /* Check if we also have a process in the process list */
1071 if (!IsListEmpty(&Session
->ProcessList
))
1073 Process
= CONTAINING_RECORD(Session
->ProcessList
.Flink
,
1075 SessionProcessLinks
);
1077 /* Reference the process */
1078 ObReferenceObject(Process
);
1084 /* Release the lock again */
1085 MiReleaseExpansionLock(OldIrql
);