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 MiInitializeSessionWsSupport(VOID
)
42 /* Initialize the list heads */
43 InitializeListHead(&MiSessionWsList
);
44 InitializeListHead(&MmWorkingSetExpansionHead
);
49 MmIsSessionAddress(IN PVOID Address
)
51 /* Check if it is in range */
52 return MI_IS_SESSION_ADDRESS(Address
) ? TRUE
: FALSE
;
57 MmGetSessionLocaleId(VOID
)
63 // Get the current process
65 Process
= PsGetCurrentProcess();
68 // Check if it's NOT the Session Leader
70 if (!Process
->Vm
.Flags
.SessionLeader
)
73 // Make sure it has a valid Session
80 return ((PMM_SESSION_SPACE
)Process
->Session
)->LocaleId
;
85 // Not a session leader, return the default
87 return PsDefaultThreadLocaleId
;
90 _IRQL_requires_max_(APC_LEVEL
)
96 PEPROCESS CurrentProcess
;
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
))
104 /* Set the session locale Id */
105 ((PMM_SESSION_SPACE
)CurrentProcess
->Session
)->LocaleId
= LocaleId
;
109 /* Set the default locale */
110 PsDefaultThreadLocaleId
= LocaleId
;
117 MiInitializeSessionIds(VOID
)
119 ULONG Size
, BitmapSize
;
120 PFN_NUMBER TotalPages
;
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
;
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
);
135 /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */
136 MiSessionCreateCharge
= 1 + MiSessionDataPages
+ MiSessionTagPages
;
138 /* Initialize the lock */
139 KeInitializeGuardedMutex(&MiSessionIdMutex
);
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
,
147 if (MiSessionIdBitmap
)
149 /* Free all the bits */
150 RtlInitializeBitMap(MiSessionIdBitmap
,
151 (PVOID
)(MiSessionIdBitmap
+ 1),
153 RtlClearAllBits(MiSessionIdBitmap
);
157 /* Die if we couldn't allocate the bitmap */
158 KeBugCheckEx(INSTALL_MORE_MEMORY
,
159 MmNumberOfPhysicalPages
,
160 MmLowestPhysicalPage
,
161 MmHighestPhysicalPage
,
168 MiSessionLeader(IN PEPROCESS Process
)
172 /* Set the flag while under the expansion lock */
173 OldIrql
= MiAcquireExpansionLock();
174 Process
->Vm
.Flags
.SessionLeader
= TRUE
;
175 MiReleaseExpansionLock(OldIrql
);
180 MmGetSessionId(IN PEPROCESS Process
)
182 PMM_SESSION_SPACE SessionGlobal
;
184 /* The session leader is always session zero */
185 if (Process
->Vm
.Flags
.SessionLeader
== 1) return 0;
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
;
195 MmGetSessionIdEx(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 -1;
205 return SessionGlobal
->SessionId
;
210 MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal
)
214 PFN_NUMBER PageFrameIndex
[MI_SESSION_DATA_PAGES_MAXIMUM
];
218 /* Is there more than just this reference? If so, bail out */
219 if (InterlockedDecrement(&SessionGlobal
->ProcessReferenceToSession
)) return;
221 /* Get the session ID */
222 SessionId
= SessionGlobal
->SessionId
;
223 DPRINT1("Last process in session %lu going down!!!\n", SessionId
);
225 /* Free the session page tables */
227 ExFreePoolWithTag(SessionGlobal
->PageTables
, 'tHmM');
229 ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal
));
231 /* Capture the data page PFNs */
232 PointerPte
= MiAddressToPte(SessionGlobal
);
233 for (i
= 0; i
< MiSessionDataPages
; i
++)
235 PageFrameIndex
[i
] = PFN_FROM_PTE(PointerPte
+ i
);
239 MiReleaseSystemPtes(PointerPte
, MiSessionDataPages
, SystemPteSpace
);
241 /* Mark them as deleted */
242 for (i
= 0; i
< MiSessionDataPages
; i
++)
244 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
[i
]);
245 MI_SET_PFN_DELETED(Pfn1
);
248 /* Loop every data page and drop a reference count */
249 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
250 for (i
= 0; i
< MiSessionDataPages
; i
++)
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
]);
259 /* Done playing with pages, release the lock */
260 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
262 /* Decrement the number of data pages */
263 InterlockedDecrement(&MmSessionDataPages
);
265 /* Free this session ID from the session bitmap */
266 KeAcquireGuardedMutex(&MiSessionIdMutex
);
267 ASSERT(RtlCheckBit(MiSessionIdBitmap
, SessionId
));
268 RtlClearBit(MiSessionIdBitmap
, SessionId
);
269 KeReleaseGuardedMutex(&MiSessionIdMutex
);
274 MiDereferenceSessionFinal(VOID
)
276 PMM_SESSION_SPACE SessionGlobal
;
279 /* Get the pointer to the global session address */
280 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
282 /* Acquire the expansion lock */
283 OldIrql
= MiAcquireExpansionLock();
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;
290 /* Check if we have any attached processes */
291 if (SessionGlobal
->AttachCount
)
293 /* Initialize the event (it's not in use yet!) */
294 KeInitializeEvent(&SessionGlobal
->AttachEvent
, NotificationEvent
, FALSE
);
296 /* Release the expansion lock for the wait */
297 MiReleaseExpansionLock(OldIrql
);
299 /* Wait for the event to be set due to the last process detach */
300 KeWaitForSingleObject(&SessionGlobal
->AttachEvent
, WrVirtualMemory
, 0, 0, 0);
302 /* Reacquire the expansion lock */
303 OldIrql
= MiAcquireExpansionLock();
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);
310 /* Check if the session is in the workingset expansion list */
311 if (SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
!= NULL
)
313 /* Remove the session from the list and zero the list entry */
314 RemoveEntryList(&SessionGlobal
->Vm
.WorkingSetExpansionLinks
);
315 SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
= 0;
318 /* Check if the session is in the workingset list */
319 if (SessionGlobal
->WsListEntry
.Flink
)
321 /* Remove the session from the list and zero the list entry */
322 RemoveEntryList(&SessionGlobal
->WsListEntry
);
323 SessionGlobal
->WsListEntry
.Flink
= NULL
;
326 /* Release the expansion lock */
327 MiReleaseExpansionLock(OldIrql
);
329 /* Check for a win32k unload routine */
330 if (SessionGlobal
->Win32KDriverUnload
)
333 SessionGlobal
->Win32KDriverUnload(NULL
);
340 MiDereferenceSession(VOID
)
342 PMM_SESSION_SPACE SessionGlobal
;
344 ULONG ReferenceCount
, SessionId
;
347 ASSERT(PsGetCurrentProcess()->ProcessInSession
||
348 ((MmSessionSpace
->u
.Flags
.Initialized
== 0) &&
349 (PsGetCurrentProcess()->Vm
.Flags
.SessionLeader
== 1) &&
350 (MmSessionSpace
->ReferenceCount
== 1)));
352 /* The session bit must be set */
353 SessionId
= MmSessionSpace
->SessionId
;
354 ASSERT(RtlCheckBit(MiSessionIdBitmap
, SessionId
));
356 /* Get the current process */
357 Process
= PsGetCurrentProcess();
359 /* Decrement the process count */
360 InterlockedDecrement(&MmSessionSpace
->ResidentProcessCount
);
362 /* Decrement the reference count and check if was the last reference */
363 ReferenceCount
= InterlockedDecrement(&MmSessionSpace
->ReferenceCount
);
364 if (ReferenceCount
== 0)
366 /* No more references left, kill the session completely */
367 MiDereferenceSessionFinal();
370 /* Check if tis is the session leader or the last process in the session */
371 if ((Process
->Vm
.Flags
.SessionLeader
) || (ReferenceCount
== 0))
373 /* Get the global session address before we kill the session mapping */
374 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
376 /* Delete all session PDEs and flush the TB */
377 RtlZeroMemory(MiAddressToPde(MmSessionBase
),
378 BYTES_TO_PAGES(MmSessionSize
) * sizeof(MMPDE
));
379 KeFlushEntireTb(FALSE
, FALSE
);
381 /* Is this the session leader? */
382 if (Process
->Vm
.Flags
.SessionLeader
)
384 /* Clean up the references here. */
385 ASSERT(Process
->Session
== NULL
);
386 MiReleaseProcessReferenceToSessionDataPage(SessionGlobal
);
390 /* Reset the current process' session flag */
391 RtlInterlockedClearBits(&Process
->Flags
, PSF_PROCESS_IN_SESSION_BIT
);
396 MiSessionRemoveProcess(VOID
)
398 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
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
))
405 /* Then there's nothing to do */
410 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
412 /* Acquire the expansion lock while touching the session */
413 OldIrql
= MiAcquireExpansionLock();
415 /* Remove the process from the list */
416 RemoveEntryList(&CurrentProcess
->SessionProcessLinks
);
418 /* Release the lock again */
419 MiReleaseExpansionLock(OldIrql
);
421 /* Dereference the session */
422 MiDereferenceSession();
427 MiSessionAddProcess(IN PEPROCESS NewProcess
)
429 PMM_SESSION_SPACE SessionGlobal
;
432 /* The current process must already be in a session */
433 if (!(PsGetCurrentProcess()->Flags
& PSF_PROCESS_IN_SESSION_BIT
)) return;
436 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
438 /* Get the global session */
439 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
441 /* Increment counters */
442 InterlockedIncrement((PLONG
)&SessionGlobal
->ReferenceCount
);
443 InterlockedIncrement(&SessionGlobal
->ResidentProcessCount
);
444 InterlockedIncrement(&SessionGlobal
->ProcessReferenceToSession
);
446 /* Set the session pointer */
447 ASSERT(NewProcess
->Session
== NULL
);
448 NewProcess
->Session
= SessionGlobal
;
450 /* Acquire the expansion lock while touching the session */
451 OldIrql
= MiAcquireExpansionLock();
453 /* Insert it into the process list */
454 InsertTailList(&SessionGlobal
->ProcessList
, &NewProcess
->SessionProcessLinks
);
456 /* Release the lock again */
457 MiReleaseExpansionLock(OldIrql
);
460 PspSetProcessFlag(NewProcess
, PSF_PROCESS_IN_SESSION_BIT
);
465 MiSessionInitializeWorkingSetList(VOID
)
473 PFN_NUMBER PageFrameIndex
;
474 PMM_SESSION_SPACE SessionGlobal
;
475 BOOLEAN AllocatedPageTable
;
476 PMMWSL WorkingSetList
;
478 /* Get pointers to session global and the session working set list */
479 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
480 WorkingSetList
= (PMMWSL
)MiSessionSpaceWs
;
482 /* Fill out the two pointers */
483 MmSessionSpace
->Vm
.VmWorkingSetList
= WorkingSetList
;
484 MmSessionSpace
->Wsle
= (PMMWSLE
)WorkingSetList
->UsedPageTableEntries
;
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)
490 /* Nope, we'll have to do it */
492 ASSERT(PointerPde
->u
.Hard
.Global
== 0);
494 AllocatedPageTable
= FALSE
;
498 /* Yep, that makes our job easier */
499 AllocatedPageTable
= TRUE
;
502 /* Get the PTE for the working set */
503 PointerPte
= MiAddressToPte(WorkingSetList
);
505 /* Initialize the working set lock, and lock the PFN database */
506 ExInitializePushLock(&SessionGlobal
->Vm
.WorkingSetMutex
);
507 //MmLockPageableSectionByHandle(ExPageLockHandle);
508 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
510 /* Check if we need a page table */
511 if (AllocatedPageTable
!= FALSE
)
513 /* Get a zeroed colored zero page */
514 Color
= MI_GET_NEXT_COLOR();
515 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
518 /* No zero pages, grab a free one */
519 PageFrameIndex
= MiRemoveAnyPage(Color
);
521 /* Zero it outside the PFN lock */
522 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
523 MiZeroPhysicalPage(PageFrameIndex
);
524 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
527 /* Write a valid PDE for it */
528 TempPde
= ValidKernelPdeLocal
;
529 TempPde
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
530 MI_WRITE_VALID_PDE(PointerPde
, TempPde
);
532 /* Add this into the list */
533 Index
= ((ULONG_PTR
)WorkingSetList
- (ULONG_PTR
)MmSessionBase
) >> 22;
535 MmSessionSpace
->PageTables
[Index
] = TempPde
;
537 /* Initialize the page directory page, and now zero the working set list itself */
538 MiInitializePfnForOtherProcess(PageFrameIndex
,
540 MmSessionSpace
->SessionPageDirectoryIndex
);
541 KeZeroPages(PointerPte
, PAGE_SIZE
);
544 /* Get a zeroed colored zero page */
545 Color
= MI_GET_NEXT_COLOR();
546 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
549 /* No zero pages, grab a free one */
550 PageFrameIndex
= MiRemoveAnyPage(Color
);
552 /* Zero it outside the PFN lock */
553 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
554 MiZeroPhysicalPage(PageFrameIndex
);
555 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
558 /* Write a valid PTE for it */
559 TempPte
= ValidKernelPteLocal
;
560 MI_MAKE_DIRTY_PAGE(&TempPte
);
561 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
563 /* Initialize the working set list page */
564 MiInitializePfnAndMakePteValid(PageFrameIndex
, PointerPte
, TempPte
);
566 /* Now we can release the PFN database lock */
567 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
569 /* Fill out the working set structure */
570 MmSessionSpace
->Vm
.Flags
.SessionSpace
= 1;
571 MmSessionSpace
->Vm
.MinimumWorkingSetSize
= 20;
572 MmSessionSpace
->Vm
.MaximumWorkingSetSize
= 384;
573 WorkingSetList
->LastEntry
= 20;
574 WorkingSetList
->HashTable
= NULL
;
575 WorkingSetList
->HashTableSize
= 0;
576 WorkingSetList
->Wsle
= MmSessionSpace
->Wsle
;
578 /* Acquire the expansion lock while touching the session */
579 OldIrql
= MiAcquireExpansionLock();
581 /* Handle list insertions */
582 ASSERT(SessionGlobal
->WsListEntry
.Flink
== NULL
);
583 ASSERT(SessionGlobal
->WsListEntry
.Blink
== NULL
);
584 InsertTailList(&MiSessionWsList
, &SessionGlobal
->WsListEntry
);
586 ASSERT(SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
== NULL
);
587 ASSERT(SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Blink
== NULL
);
588 InsertTailList(&MmWorkingSetExpansionHead
,
589 &SessionGlobal
->Vm
.WorkingSetExpansionLinks
);
591 /* Release the lock again */
592 MiReleaseExpansionLock(OldIrql
);
594 /* All done, return */
595 //MmUnlockPageableImageSection(ExPageLockHandle);
596 return STATUS_SUCCESS
;
601 MiSessionCreateInternal(OUT PULONG SessionId
)
603 PEPROCESS Process
= PsGetCurrentProcess();
604 ULONG NewFlags
, Flags
, Size
, i
, Color
;
606 PMMPTE PointerPte
, SessionPte
;
607 PMMPDE PointerPde
, PageTables
;
608 PMM_SESSION_SPACE SessionGlobal
;
613 PFN_NUMBER SessionPageDirIndex
;
614 PFN_NUMBER TagPage
[MI_SESSION_TAG_PAGES_MAXIMUM
];
615 PFN_NUMBER DataPage
[MI_SESSION_DATA_PAGES_MAXIMUM
];
617 /* This should not exist yet */
618 ASSERT(MmIsAddressValid(MmSessionSpace
) == FALSE
);
620 /* Loop so we can set the session-is-creating flag */
621 Flags
= Process
->Flags
;
624 /* Check if it's already set */
625 if (Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
)
628 DPRINT1("Lost session race\n");
629 return STATUS_ALREADY_COMMITTED
;
632 /* Now try to set it */
633 NewFlags
= InterlockedCompareExchange((PLONG
)&Process
->Flags
,
634 Flags
| PSF_SESSION_CREATION_UNDERWAY_BIT
,
636 if (NewFlags
== Flags
) break;
638 /* It changed, try again */
642 /* Now we should own the flag */
643 ASSERT(Process
->Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
);
646 * Session space covers everything from 0xA0000000 to 0xC0000000.
647 * Allocate enough page tables to describe the entire region
649 Size
= (0x20000000 / PDE_MAPPED_VA
) * sizeof(MMPTE
);
650 PageTables
= ExAllocatePoolWithTag(NonPagedPool
, Size
, 'tHmM');
651 ASSERT(PageTables
!= NULL
);
652 RtlZeroMemory(PageTables
, Size
);
654 /* Lock the session ID creation mutex */
655 KeAcquireGuardedMutex(&MiSessionIdMutex
);
657 /* Allocate a new Session ID */
658 *SessionId
= RtlFindClearBitsAndSet(MiSessionIdBitmap
, 1, 0);
659 if (*SessionId
== 0xFFFFFFFF)
661 /* We ran out of session IDs, we should expand */
662 DPRINT1("Too many sessions created. Expansion not yet supported\n");
663 ExFreePoolWithTag(PageTables
, 'tHmM');
664 return STATUS_NO_MEMORY
;
667 /* Unlock the session ID creation mutex */
668 KeReleaseGuardedMutex(&MiSessionIdMutex
);
670 /* Reserve the global PTEs */
671 SessionPte
= MiReserveSystemPtes(MiSessionDataPages
, SystemPteSpace
);
672 ASSERT(SessionPte
!= NULL
);
674 /* Acquire the PFN lock while we set everything up */
675 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
677 /* Loop the global PTEs */
678 TempPte
= ValidKernelPte
;
679 for (i
= 0; i
< MiSessionDataPages
; i
++)
681 /* Get a zeroed colored zero page */
682 Color
= MI_GET_NEXT_COLOR();
683 DataPage
[i
] = MiRemoveZeroPageSafe(Color
);
686 /* No zero pages, grab a free one */
687 DataPage
[i
] = MiRemoveAnyPage(Color
);
689 /* Zero it outside the PFN lock */
690 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
691 MiZeroPhysicalPage(DataPage
[i
]);
692 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
695 /* Fill the PTE out */
696 TempPte
.u
.Hard
.PageFrameNumber
= DataPage
[i
];
697 MI_WRITE_VALID_PTE(SessionPte
+ i
, TempPte
);
700 /* Set the pointer to global space */
701 SessionGlobal
= MiPteToAddress(SessionPte
);
703 /* Get a zeroed colored zero page */
704 Color
= MI_GET_NEXT_COLOR();
705 SessionPageDirIndex
= MiRemoveZeroPageSafe(Color
);
706 if (!SessionPageDirIndex
)
708 /* No zero pages, grab a free one */
709 SessionPageDirIndex
= MiRemoveAnyPage(Color
);
711 /* Zero it outside the PFN lock */
712 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
713 MiZeroPhysicalPage(SessionPageDirIndex
);
714 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
717 /* Fill the PTE out */
718 TempPde
= ValidKernelPdeLocal
;
719 TempPde
.u
.Hard
.PageFrameNumber
= SessionPageDirIndex
;
721 /* Setup, allocate, fill out the MmSessionSpace PTE */
722 PointerPde
= MiAddressToPde(MmSessionSpace
);
723 ASSERT(PointerPde
->u
.Long
== 0);
724 MI_WRITE_VALID_PDE(PointerPde
, TempPde
);
725 MiInitializePfnForOtherProcess(SessionPageDirIndex
,
727 SessionPageDirIndex
);
728 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex
)->u1
.WsIndex
== 0);
730 /* Loop all the local PTEs for it */
731 TempPte
= ValidKernelPteLocal
;
732 PointerPte
= MiAddressToPte(MmSessionSpace
);
733 for (i
= 0; i
< MiSessionDataPages
; i
++)
735 /* And fill them out */
736 TempPte
.u
.Hard
.PageFrameNumber
= DataPage
[i
];
737 MiInitializePfnAndMakePteValid(DataPage
[i
], PointerPte
+ i
, TempPte
);
738 ASSERT(MI_PFN_ELEMENT(DataPage
[i
])->u1
.WsIndex
== 0);
741 /* Finally loop all of the session pool tag pages */
742 for (i
= 0; i
< MiSessionTagPages
; i
++)
744 /* Grab a zeroed colored page */
745 Color
= MI_GET_NEXT_COLOR();
746 TagPage
[i
] = MiRemoveZeroPageSafe(Color
);
749 /* No zero pages, grab a free one */
750 TagPage
[i
] = MiRemoveAnyPage(Color
);
752 /* Zero it outside the PFN lock */
753 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
754 MiZeroPhysicalPage(TagPage
[i
]);
755 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
758 /* Fill the PTE out */
759 TempPte
.u
.Hard
.PageFrameNumber
= TagPage
[i
];
760 MiInitializePfnAndMakePteValid(TagPage
[i
],
761 PointerPte
+ MiSessionDataPages
+ i
,
765 /* PTEs have been setup, release the PFN lock */
766 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
768 /* Fill out the session space structure now */
769 MmSessionSpace
->GlobalVirtualAddress
= SessionGlobal
;
770 MmSessionSpace
->ReferenceCount
= 1;
771 MmSessionSpace
->ResidentProcessCount
= 1;
772 MmSessionSpace
->u
.LongFlags
= 0;
773 MmSessionSpace
->SessionId
= *SessionId
;
774 MmSessionSpace
->LocaleId
= PsDefaultSystemLocaleId
;
775 MmSessionSpace
->SessionPageDirectoryIndex
= SessionPageDirIndex
;
776 MmSessionSpace
->Color
= Color
;
777 MmSessionSpace
->NonPageablePages
= MiSessionCreateCharge
;
778 MmSessionSpace
->CommittedPages
= MiSessionCreateCharge
;
780 MmSessionSpace
->PageTables
= PageTables
;
781 MmSessionSpace
->PageTables
[PointerPde
- MiAddressToPde(MmSessionBase
)] = *PointerPde
;
783 InitializeListHead(&MmSessionSpace
->ImageList
);
784 DPRINT1("Session %lu is ready to go: 0x%p 0x%p, %lx 0x%p\n",
785 *SessionId
, MmSessionSpace
, SessionGlobal
, SessionPageDirIndex
, PageTables
);
787 /* Initialize session pool */
788 //Status = MiInitializeSessionPool();
789 Status
= STATUS_SUCCESS
;
790 ASSERT(NT_SUCCESS(Status
) == TRUE
);
792 /* Initialize system space */
793 Result
= MiInitializeSystemSpaceMap(&SessionGlobal
->Session
);
794 ASSERT(Result
== TRUE
);
796 /* Initialize the process list, make sure the workign set list is empty */
797 ASSERT(SessionGlobal
->WsListEntry
.Flink
== NULL
);
798 ASSERT(SessionGlobal
->WsListEntry
.Blink
== NULL
);
799 InitializeListHead(&SessionGlobal
->ProcessList
);
801 /* We're done, clear the flag */
802 ASSERT(Process
->Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
);
803 PspClearProcessFlag(Process
, PSF_SESSION_CREATION_UNDERWAY_BIT
);
805 /* Insert the process into the session */
806 ASSERT(Process
->Session
== NULL
);
807 ASSERT(SessionGlobal
->ProcessReferenceToSession
== 0);
808 SessionGlobal
->ProcessReferenceToSession
= 1;
811 InterlockedIncrement(&MmSessionDataPages
);
812 return STATUS_SUCCESS
;
817 MmSessionCreate(OUT PULONG SessionId
)
819 PEPROCESS Process
= PsGetCurrentProcess();
820 ULONG SessionLeaderExists
;
823 /* Fail if the process is already in a session */
824 if (Process
->Flags
& PSF_PROCESS_IN_SESSION_BIT
)
826 DPRINT1("Process already in session\n");
827 return STATUS_ALREADY_COMMITTED
;
830 /* Check if the process is already the session leader */
831 if (!Process
->Vm
.Flags
.SessionLeader
)
833 /* Atomically set it as the leader */
834 SessionLeaderExists
= InterlockedCompareExchange(&MiSessionLeaderExists
, 1, 0);
835 if (SessionLeaderExists
)
837 DPRINT1("Session leader race\n");
838 return STATUS_INVALID_SYSTEM_SERVICE
;
841 /* Do the work required to upgrade him */
842 MiSessionLeader(Process
);
845 /* Create the session */
846 KeEnterCriticalRegion();
847 Status
= MiSessionCreateInternal(SessionId
);
848 if (!NT_SUCCESS(Status
))
850 KeLeaveCriticalRegion();
854 /* Set up the session working set */
855 Status
= MiSessionInitializeWorkingSetList();
856 if (!NT_SUCCESS(Status
))
859 //MiDereferenceSession();
861 KeLeaveCriticalRegion();
866 KeLeaveCriticalRegion();
868 /* Set and assert the flags, and return */
869 MmSessionSpace
->u
.Flags
.Initialized
= 1;
870 PspSetProcessFlag(Process
, PSF_PROCESS_IN_SESSION_BIT
);
871 ASSERT(MiSessionLeaderExists
== 1);
877 MmSessionDelete(IN ULONG SessionId
)
879 PEPROCESS Process
= PsGetCurrentProcess();
881 /* Process must be in a session */
882 if (!(Process
->Flags
& PSF_PROCESS_IN_SESSION_BIT
))
884 DPRINT1("Not in a session!\n");
885 return STATUS_UNABLE_TO_FREE_VM
;
888 /* It must be the session leader */
889 if (!Process
->Vm
.Flags
.SessionLeader
)
891 DPRINT1("Not a session leader!\n");
892 return STATUS_UNABLE_TO_FREE_VM
;
895 /* Remove one reference count */
896 KeEnterCriticalRegion();
898 KeLeaveCriticalRegion();
901 return STATUS_SUCCESS
;
904 _IRQL_requires_max_(APC_LEVEL
)
908 _Inout_ PVOID SessionEntry
,
909 _Out_ PKAPC_STATE ApcState
)
911 PEPROCESS EntryProcess
;
912 PMM_SESSION_SPACE EntrySession
, CurrentSession
;
913 PEPROCESS CurrentProcess
;
916 /* The parameter is the actual process! */
917 EntryProcess
= SessionEntry
;
918 ASSERT(EntryProcess
!= NULL
);
921 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
922 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
924 /* Get the session from the process that was passed in */
925 EntrySession
= EntryProcess
->Session
;
926 ASSERT(EntrySession
!= NULL
);
928 /* Get the current process and it's session */
929 CurrentProcess
= PsGetCurrentProcess();
930 CurrentSession
= CurrentProcess
->Session
;
932 /* Acquire the expansion lock while touching the session */
933 OldIrql
= MiAcquireExpansionLock();
935 /* Check if the session is about to be deleted */
936 if (EntrySession
->u
.Flags
.DeletePending
)
938 /* We cannot attach to it, so unlock and fail */
939 MiReleaseExpansionLock(OldIrql
);
940 return STATUS_PROCESS_IS_TERMINATING
;
943 /* Count the number of attaches */
944 EntrySession
->AttachCount
++;
946 /* we can release the lock again */
947 MiReleaseExpansionLock(OldIrql
);
949 /* Check if we are not the session leader and we are in a session */
950 if (!CurrentProcess
->Vm
.Flags
.SessionLeader
&& (CurrentSession
!= NULL
))
952 /* Are we already in the right session? */
953 if (CurrentSession
== EntrySession
)
955 /* We are, so "attach" to the current process */
956 EntryProcess
= CurrentProcess
;
960 /* We are not, the session id should better not match! */
961 ASSERT(CurrentSession
->SessionId
!= EntrySession
->SessionId
);
965 /* Now attach to the process that we have */
966 KeStackAttachProcess(&EntryProcess
->Pcb
, ApcState
);
969 return STATUS_SUCCESS
;
972 _IRQL_requires_max_(APC_LEVEL
)
976 _Inout_ PVOID SessionEntry
,
977 _In_ PKAPC_STATE ApcState
)
979 PEPROCESS EntryProcess
;
980 PMM_SESSION_SPACE EntrySession
;
982 BOOLEAN DeletePending
;
984 /* The parameter is the actual process! */
985 EntryProcess
= SessionEntry
;
986 ASSERT(EntryProcess
!= NULL
);
989 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
990 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
992 /* Get the session from the process that was passed in */
993 EntrySession
= EntryProcess
->Session
;
994 ASSERT(EntrySession
!= NULL
);
996 /* Acquire the expansion lock while touching the session */
997 OldIrql
= MiAcquireExpansionLock();
999 /* Make sure we have at least one attach and decrement the count */
1000 ASSERT(EntrySession
->AttachCount
>= 1);
1001 EntrySession
->AttachCount
--;
1003 /* Remember if a delete is pending and we were the last one attached */
1004 DeletePending
= EntrySession
->u
.Flags
.DeletePending
&&
1005 (EntrySession
->AttachCount
== 0);
1007 /* Release the lock again */
1008 MiReleaseExpansionLock(OldIrql
);
1010 /* Detach from the process */
1011 KeUnstackDetachProcess(ApcState
);
1013 /* Check if we need to set the attach event */
1015 KeSetEvent(&EntrySession
->AttachEvent
, IO_NO_INCREMENT
, FALSE
);
1021 _Inout_ PVOID SessionEntry
)
1023 PEPROCESS EntryProcess
;
1025 /* The parameter is the actual process! */
1026 EntryProcess
= SessionEntry
;
1027 ASSERT(EntryProcess
!= NULL
);
1030 ASSERT(KeGetCurrentIrql () <= APC_LEVEL
);
1031 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
1032 ASSERT(EntryProcess
->Session
!= NULL
);
1034 /* Get rid of the reference we took */
1035 ObDereferenceObject(EntryProcess
);
1041 _In_ ULONG SessionId
)
1043 PLIST_ENTRY ListEntry
;
1044 PMM_SESSION_SPACE Session
;
1045 PEPROCESS Process
= NULL
;
1048 /* Acquire the expansion lock while touching the session */
1049 OldIrql
= MiAcquireExpansionLock();
1051 /* Loop all entries in the session ws list */
1052 ListEntry
= MiSessionWsList
.Flink
;
1053 while (ListEntry
!= &MiSessionWsList
)
1055 Session
= CONTAINING_RECORD(ListEntry
, MM_SESSION_SPACE
, WsListEntry
);
1057 /* Check if this is the session we are looking for */
1058 if (Session
->SessionId
== SessionId
)
1060 /* Check if we also have a process in the process list */
1061 if (!IsListEmpty(&Session
->ProcessList
))
1063 Process
= CONTAINING_RECORD(Session
->ProcessList
.Flink
,
1065 SessionProcessLinks
);
1067 /* Reference the process */
1068 ObReferenceObject(Process
);
1074 /* Release the lock again */
1075 MiReleaseExpansionLock(OldIrql
);