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(VOID
)
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
)
488 PFN_NUMBER PageFrameIndex
;
489 PMM_SESSION_SPACE SessionGlobal
;
490 BOOLEAN AllocatedPageTable
;
491 PMMWSL WorkingSetList
;
493 /* Get pointers to session global and the session working set list */
494 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
495 WorkingSetList
= (PMMWSL
)MiSessionSpaceWs
;
497 /* Fill out the two pointers */
498 MmSessionSpace
->Vm
.VmWorkingSetList
= WorkingSetList
;
499 MmSessionSpace
->Wsle
= (PMMWSLE
)WorkingSetList
->UsedPageTableEntries
;
501 /* Get the PDE for the working set, and check if it's already allocated */
502 PointerPde
= MiAddressToPde(WorkingSetList
);
503 if (PointerPde
->u
.Hard
.Valid
== 1)
505 /* Nope, we'll have to do it */
507 ASSERT(PointerPde
->u
.Hard
.Global
== 0);
509 AllocatedPageTable
= FALSE
;
513 /* Yep, that makes our job easier */
514 AllocatedPageTable
= TRUE
;
517 /* Get the PTE for the working set */
518 PointerPte
= MiAddressToPte(WorkingSetList
);
520 /* Initialize the working set lock, and lock the PFN database */
521 ExInitializePushLock(&SessionGlobal
->Vm
.WorkingSetMutex
);
522 //MmLockPageableSectionByHandle(ExPageLockHandle);
523 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
525 /* Check if we need a page table */
526 if (AllocatedPageTable
!= FALSE
)
528 /* Get a zeroed colored zero page */
529 Color
= MI_GET_NEXT_COLOR();
530 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
533 /* No zero pages, grab a free one */
534 PageFrameIndex
= MiRemoveAnyPage(Color
);
536 /* Zero it outside the PFN lock */
537 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
538 MiZeroPhysicalPage(PageFrameIndex
);
539 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
542 /* Write a valid PDE for it */
543 TempPde
.u
.Long
= ValidKernelPdeLocal
.u
.Long
;
544 TempPde
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
545 MI_WRITE_VALID_PDE(PointerPde
, TempPde
);
547 /* Add this into the list */
548 Index
= ((ULONG_PTR
)WorkingSetList
- (ULONG_PTR
)MmSessionBase
) >> 22;
550 MmSessionSpace
->PageTables
[Index
] = TempPde
;
552 /* Initialize the page directory page, and now zero the working set list itself */
553 MiInitializePfnForOtherProcess(PageFrameIndex
,
555 MmSessionSpace
->SessionPageDirectoryIndex
);
556 KeZeroPages(PointerPte
, PAGE_SIZE
);
559 /* Get a zeroed colored zero page */
560 Color
= MI_GET_NEXT_COLOR();
561 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
564 /* No zero pages, grab a free one */
565 PageFrameIndex
= MiRemoveAnyPage(Color
);
567 /* Zero it outside the PFN lock */
568 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
569 MiZeroPhysicalPage(PageFrameIndex
);
570 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
573 /* Write a valid PTE for it */
574 TempPte
.u
.Long
= ValidKernelPteLocal
.u
.Long
;
575 MI_MAKE_DIRTY_PAGE(&TempPte
);
576 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
578 /* Initialize the working set list page */
579 MiInitializePfnAndMakePteValid(PageFrameIndex
, PointerPte
, TempPte
);
581 /* Now we can release the PFN database lock */
582 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
584 /* Fill out the working set structure */
585 MmSessionSpace
->Vm
.Flags
.SessionSpace
= 1;
586 MmSessionSpace
->Vm
.MinimumWorkingSetSize
= 20;
587 MmSessionSpace
->Vm
.MaximumWorkingSetSize
= 384;
588 WorkingSetList
->LastEntry
= 20;
589 WorkingSetList
->HashTable
= NULL
;
590 WorkingSetList
->HashTableSize
= 0;
591 WorkingSetList
->Wsle
= MmSessionSpace
->Wsle
;
593 /* Acquire the expansion lock while touching the session */
594 OldIrql
= MiAcquireExpansionLock();
596 /* Handle list insertions */
597 ASSERT(SessionGlobal
->WsListEntry
.Flink
== NULL
);
598 ASSERT(SessionGlobal
->WsListEntry
.Blink
== NULL
);
599 InsertTailList(&MiSessionWsList
, &SessionGlobal
->WsListEntry
);
601 ASSERT(SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
== NULL
);
602 ASSERT(SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Blink
== NULL
);
603 InsertTailList(&MmWorkingSetExpansionHead
,
604 &SessionGlobal
->Vm
.WorkingSetExpansionLinks
);
606 /* Release the lock again */
607 MiReleaseExpansionLock(OldIrql
);
609 /* All done, return */
610 //MmUnlockPageableImageSection(ExPageLockHandle);
611 return STATUS_SUCCESS
;
616 MiSessionCreateInternal(OUT PULONG SessionId
)
618 PEPROCESS Process
= PsGetCurrentProcess();
619 ULONG NewFlags
, Flags
, Size
, i
, Color
;
621 PMMPTE PointerPte
, SessionPte
;
622 PMMPDE PointerPde
, PageTables
;
623 PMM_SESSION_SPACE SessionGlobal
;
628 PFN_NUMBER SessionPageDirIndex
;
629 PFN_NUMBER TagPage
[MI_SESSION_TAG_PAGES_MAXIMUM
];
630 PFN_NUMBER DataPage
[MI_SESSION_DATA_PAGES_MAXIMUM
];
632 /* This should not exist yet */
633 ASSERT(MmIsAddressValid(MmSessionSpace
) == FALSE
);
635 /* Loop so we can set the session-is-creating flag */
636 Flags
= Process
->Flags
;
639 /* Check if it's already set */
640 if (Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
)
643 DPRINT1("Lost session race\n");
644 return STATUS_ALREADY_COMMITTED
;
647 /* Now try to set it */
648 NewFlags
= InterlockedCompareExchange((PLONG
)&Process
->Flags
,
649 Flags
| PSF_SESSION_CREATION_UNDERWAY_BIT
,
651 if (NewFlags
== Flags
) break;
653 /* It changed, try again */
657 /* Now we should own the flag */
658 ASSERT(Process
->Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
);
661 * Session space covers everything from 0xA0000000 to 0xC0000000.
662 * Allocate enough page tables to describe the entire region
664 Size
= (0x20000000 / PDE_MAPPED_VA
) * sizeof(MMPTE
);
665 PageTables
= ExAllocatePoolWithTag(NonPagedPool
, Size
, 'tHmM');
666 ASSERT(PageTables
!= NULL
);
667 RtlZeroMemory(PageTables
, Size
);
669 /* Lock the session ID creation mutex */
670 KeAcquireGuardedMutex(&MiSessionIdMutex
);
672 /* Allocate a new Session ID */
673 *SessionId
= RtlFindClearBitsAndSet(MiSessionIdBitmap
, 1, 0);
674 if (*SessionId
== 0xFFFFFFFF)
676 /* We ran out of session IDs, we should expand */
677 DPRINT1("Too many sessions created. Expansion not yet supported\n");
678 ExFreePoolWithTag(PageTables
, 'tHmM');
679 return STATUS_NO_MEMORY
;
682 /* Unlock the session ID creation mutex */
683 KeReleaseGuardedMutex(&MiSessionIdMutex
);
685 /* Reserve the global PTEs */
686 SessionPte
= MiReserveSystemPtes(MiSessionDataPages
, SystemPteSpace
);
687 ASSERT(SessionPte
!= NULL
);
689 /* Acquire the PFN lock while we set everything up */
690 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
692 /* Loop the global PTEs */
693 TempPte
.u
.Long
= ValidKernelPte
.u
.Long
;
694 for (i
= 0; i
< MiSessionDataPages
; i
++)
696 /* Get a zeroed colored zero page */
697 Color
= MI_GET_NEXT_COLOR();
698 DataPage
[i
] = MiRemoveZeroPageSafe(Color
);
701 /* No zero pages, grab a free one */
702 DataPage
[i
] = MiRemoveAnyPage(Color
);
704 /* Zero it outside the PFN lock */
705 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
706 MiZeroPhysicalPage(DataPage
[i
]);
707 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
710 /* Fill the PTE out */
711 TempPte
.u
.Hard
.PageFrameNumber
= DataPage
[i
];
712 MI_WRITE_VALID_PTE(SessionPte
+ i
, TempPte
);
715 /* Set the pointer to global space */
716 SessionGlobal
= MiPteToAddress(SessionPte
);
718 /* Get a zeroed colored zero page */
719 Color
= MI_GET_NEXT_COLOR();
720 SessionPageDirIndex
= MiRemoveZeroPageSafe(Color
);
721 if (!SessionPageDirIndex
)
723 /* No zero pages, grab a free one */
724 SessionPageDirIndex
= MiRemoveAnyPage(Color
);
726 /* Zero it outside the PFN lock */
727 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
728 MiZeroPhysicalPage(SessionPageDirIndex
);
729 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
732 /* Fill the PTE out */
733 TempPde
.u
.Long
= ValidKernelPdeLocal
.u
.Long
;
734 TempPde
.u
.Hard
.PageFrameNumber
= SessionPageDirIndex
;
736 /* Setup, allocate, fill out the MmSessionSpace PTE */
737 PointerPde
= MiAddressToPde(MmSessionSpace
);
738 ASSERT(PointerPde
->u
.Long
== 0);
739 MI_WRITE_VALID_PDE(PointerPde
, TempPde
);
740 MiInitializePfnForOtherProcess(SessionPageDirIndex
,
742 SessionPageDirIndex
);
743 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex
)->u1
.WsIndex
== 0);
745 /* Loop all the local PTEs for it */
746 TempPte
.u
.Long
= ValidKernelPteLocal
.u
.Long
;
747 PointerPte
= MiAddressToPte(MmSessionSpace
);
748 for (i
= 0; i
< MiSessionDataPages
; i
++)
750 /* And fill them out */
751 TempPte
.u
.Hard
.PageFrameNumber
= DataPage
[i
];
752 MiInitializePfnAndMakePteValid(DataPage
[i
], PointerPte
+ i
, TempPte
);
753 ASSERT(MI_PFN_ELEMENT(DataPage
[i
])->u1
.WsIndex
== 0);
756 /* Finally loop all of the session pool tag pages */
757 for (i
= 0; i
< MiSessionTagPages
; i
++)
759 /* Grab a zeroed colored page */
760 Color
= MI_GET_NEXT_COLOR();
761 TagPage
[i
] = MiRemoveZeroPageSafe(Color
);
764 /* No zero pages, grab a free one */
765 TagPage
[i
] = MiRemoveAnyPage(Color
);
767 /* Zero it outside the PFN lock */
768 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
769 MiZeroPhysicalPage(TagPage
[i
]);
770 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
773 /* Fill the PTE out */
774 TempPte
.u
.Hard
.PageFrameNumber
= TagPage
[i
];
775 MiInitializePfnAndMakePteValid(TagPage
[i
],
776 PointerPte
+ MiSessionDataPages
+ i
,
780 /* PTEs have been setup, release the PFN lock */
781 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
783 /* Fill out the session space structure now */
784 MmSessionSpace
->GlobalVirtualAddress
= SessionGlobal
;
785 MmSessionSpace
->ReferenceCount
= 1;
786 MmSessionSpace
->ResidentProcessCount
= 1;
787 MmSessionSpace
->u
.LongFlags
= 0;
788 MmSessionSpace
->SessionId
= *SessionId
;
789 MmSessionSpace
->LocaleId
= PsDefaultSystemLocaleId
;
790 MmSessionSpace
->SessionPageDirectoryIndex
= SessionPageDirIndex
;
791 MmSessionSpace
->Color
= Color
;
792 MmSessionSpace
->NonPageablePages
= MiSessionCreateCharge
;
793 MmSessionSpace
->CommittedPages
= MiSessionCreateCharge
;
795 MmSessionSpace
->PageTables
= PageTables
;
796 MmSessionSpace
->PageTables
[PointerPde
- MiAddressToPde(MmSessionBase
)] = *PointerPde
;
798 InitializeListHead(&MmSessionSpace
->ImageList
);
799 DPRINT1("Session %lu is ready to go: 0x%p 0x%p, %lx 0x%p\n",
800 *SessionId
, MmSessionSpace
, SessionGlobal
, SessionPageDirIndex
, PageTables
);
802 /* Initialize session pool */
803 //Status = MiInitializeSessionPool();
804 Status
= STATUS_SUCCESS
;
805 ASSERT(NT_SUCCESS(Status
) == TRUE
);
807 /* Initialize system space */
808 Result
= MiInitializeSystemSpaceMap(&SessionGlobal
->Session
);
809 ASSERT(Result
== TRUE
);
811 /* Initialize the process list, make sure the workign set list is empty */
812 ASSERT(SessionGlobal
->WsListEntry
.Flink
== NULL
);
813 ASSERT(SessionGlobal
->WsListEntry
.Blink
== NULL
);
814 InitializeListHead(&SessionGlobal
->ProcessList
);
816 /* We're done, clear the flag */
817 ASSERT(Process
->Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
);
818 PspClearProcessFlag(Process
, PSF_SESSION_CREATION_UNDERWAY_BIT
);
820 /* Insert the process into the session */
821 ASSERT(Process
->Session
== NULL
);
822 ASSERT(SessionGlobal
->ProcessReferenceToSession
== 0);
823 SessionGlobal
->ProcessReferenceToSession
= 1;
826 InterlockedIncrement(&MmSessionDataPages
);
827 return STATUS_SUCCESS
;
832 MmSessionCreate(OUT PULONG SessionId
)
834 PEPROCESS Process
= PsGetCurrentProcess();
835 ULONG SessionLeaderExists
;
838 /* Fail if the process is already in a session */
839 if (Process
->Flags
& PSF_PROCESS_IN_SESSION_BIT
)
841 DPRINT1("Process already in session\n");
842 return STATUS_ALREADY_COMMITTED
;
845 /* Check if the process is already the session leader */
846 if (!Process
->Vm
.Flags
.SessionLeader
)
848 /* Atomically set it as the leader */
849 SessionLeaderExists
= InterlockedCompareExchange(&MiSessionLeaderExists
, 1, 0);
850 if (SessionLeaderExists
)
852 DPRINT1("Session leader race\n");
853 return STATUS_INVALID_SYSTEM_SERVICE
;
856 /* Do the work required to upgrade him */
857 MiSessionLeader(Process
);
860 /* Create the session */
861 KeEnterCriticalRegion();
862 Status
= MiSessionCreateInternal(SessionId
);
863 if (!NT_SUCCESS(Status
))
865 KeLeaveCriticalRegion();
869 /* Set up the session working set */
870 Status
= MiSessionInitializeWorkingSetList();
871 if (!NT_SUCCESS(Status
))
874 //MiDereferenceSession();
876 KeLeaveCriticalRegion();
881 KeLeaveCriticalRegion();
883 /* Set and assert the flags, and return */
884 MmSessionSpace
->u
.Flags
.Initialized
= 1;
885 PspSetProcessFlag(Process
, PSF_PROCESS_IN_SESSION_BIT
);
886 ASSERT(MiSessionLeaderExists
== 1);
892 MmSessionDelete(IN ULONG SessionId
)
894 PEPROCESS Process
= PsGetCurrentProcess();
896 /* Process must be in a session */
897 if (!(Process
->Flags
& PSF_PROCESS_IN_SESSION_BIT
))
899 DPRINT1("Not in a session!\n");
900 return STATUS_UNABLE_TO_FREE_VM
;
903 /* It must be the session leader */
904 if (!Process
->Vm
.Flags
.SessionLeader
)
906 DPRINT1("Not a session leader!\n");
907 return STATUS_UNABLE_TO_FREE_VM
;
910 /* Remove one reference count */
911 KeEnterCriticalRegion();
913 KeLeaveCriticalRegion();
916 return STATUS_SUCCESS
;
919 _IRQL_requires_max_(APC_LEVEL
)
923 _Inout_ PVOID SessionEntry
,
924 _Out_ PKAPC_STATE ApcState
)
926 PEPROCESS EntryProcess
;
927 PMM_SESSION_SPACE EntrySession
, CurrentSession
;
928 PEPROCESS CurrentProcess
;
931 /* The parameter is the actual process! */
932 EntryProcess
= SessionEntry
;
933 ASSERT(EntryProcess
!= NULL
);
936 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
937 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
939 /* Get the session from the process that was passed in */
940 EntrySession
= EntryProcess
->Session
;
941 ASSERT(EntrySession
!= NULL
);
943 /* Get the current process and it's session */
944 CurrentProcess
= PsGetCurrentProcess();
945 CurrentSession
= CurrentProcess
->Session
;
947 /* Acquire the expansion lock while touching the session */
948 OldIrql
= MiAcquireExpansionLock();
950 /* Check if the session is about to be deleted */
951 if (EntrySession
->u
.Flags
.DeletePending
)
953 /* We cannot attach to it, so unlock and fail */
954 MiReleaseExpansionLock(OldIrql
);
955 return STATUS_PROCESS_IS_TERMINATING
;
958 /* Count the number of attaches */
959 EntrySession
->AttachCount
++;
961 /* we can release the lock again */
962 MiReleaseExpansionLock(OldIrql
);
964 /* Check if we are not the session leader and we are in a session */
965 if (!CurrentProcess
->Vm
.Flags
.SessionLeader
&& (CurrentSession
!= NULL
))
967 /* Are we already in the right session? */
968 if (CurrentSession
== EntrySession
)
970 /* We are, so "attach" to the current process */
971 EntryProcess
= CurrentProcess
;
975 /* We are not, the session id should better not match! */
976 ASSERT(CurrentSession
->SessionId
!= EntrySession
->SessionId
);
980 /* Now attach to the process that we have */
981 KeStackAttachProcess(&EntryProcess
->Pcb
, ApcState
);
984 return STATUS_SUCCESS
;
987 _IRQL_requires_max_(APC_LEVEL
)
991 _Inout_ PVOID SessionEntry
,
992 _In_ PKAPC_STATE ApcState
)
994 PEPROCESS EntryProcess
;
995 PMM_SESSION_SPACE EntrySession
;
997 BOOLEAN DeletePending
;
999 /* The parameter is the actual process! */
1000 EntryProcess
= SessionEntry
;
1001 ASSERT(EntryProcess
!= NULL
);
1004 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1005 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
1007 /* Get the session from the process that was passed in */
1008 EntrySession
= EntryProcess
->Session
;
1009 ASSERT(EntrySession
!= NULL
);
1011 /* Acquire the expansion lock while touching the session */
1012 OldIrql
= MiAcquireExpansionLock();
1014 /* Make sure we have at least one attach and decrement the count */
1015 ASSERT(EntrySession
->AttachCount
>= 1);
1016 EntrySession
->AttachCount
--;
1018 /* Remember if a delete is pending and we were the last one attached */
1019 DeletePending
= EntrySession
->u
.Flags
.DeletePending
&&
1020 (EntrySession
->AttachCount
== 0);
1022 /* Release the lock again */
1023 MiReleaseExpansionLock(OldIrql
);
1025 /* Detach from the process */
1026 KeUnstackDetachProcess(ApcState
);
1028 /* Check if we need to set the attach event */
1030 KeSetEvent(&EntrySession
->AttachEvent
, IO_NO_INCREMENT
, FALSE
);
1036 _Inout_ PVOID SessionEntry
)
1038 PEPROCESS EntryProcess
;
1040 /* The parameter is the actual process! */
1041 EntryProcess
= SessionEntry
;
1042 ASSERT(EntryProcess
!= NULL
);
1045 ASSERT(KeGetCurrentIrql () <= APC_LEVEL
);
1046 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
1047 ASSERT(EntryProcess
->Session
!= NULL
);
1049 /* Get rid of the reference we took */
1050 ObDereferenceObject(EntryProcess
);
1056 _In_ ULONG SessionId
)
1058 PLIST_ENTRY ListEntry
;
1059 PMM_SESSION_SPACE Session
;
1060 PEPROCESS Process
= NULL
;
1063 /* Acquire the expansion lock while touching the session */
1064 OldIrql
= MiAcquireExpansionLock();
1066 /* Loop all entries in the session ws list */
1067 ListEntry
= MiSessionWsList
.Flink
;
1068 while (ListEntry
!= &MiSessionWsList
)
1070 Session
= CONTAINING_RECORD(ListEntry
, MM_SESSION_SPACE
, WsListEntry
);
1072 /* Check if this is the session we are looking for */
1073 if (Session
->SessionId
== SessionId
)
1075 /* Check if we also have a process in the process list */
1076 if (!IsListEmpty(&Session
->ProcessList
))
1078 Process
= CONTAINING_RECORD(Session
->ProcessList
.Flink
,
1080 SessionProcessLinks
);
1082 /* Reference the process */
1083 ObReferenceObject(Process
);
1089 /* Release the lock again */
1090 MiReleaseExpansionLock(OldIrql
);