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 MmGetSessionLocaleId(VOID
)
55 // Get the current process
57 Process
= PsGetCurrentProcess();
60 // Check if it's NOT the Session Leader
62 if (!Process
->Vm
.Flags
.SessionLeader
)
65 // Make sure it has a valid Session
72 return ((PMM_SESSION_SPACE
)Process
->Session
)->LocaleId
;
77 // Not a session leader, return the default
79 return PsDefaultThreadLocaleId
;
82 _IRQL_requires_max_(APC_LEVEL
)
88 PEPROCESS CurrentProcess
;
91 /* Get the current process and check if it is in a session */
92 CurrentProcess
= PsGetCurrentProcess();
93 if ((CurrentProcess
->Vm
.Flags
.SessionLeader
== 0) &&
94 (CurrentProcess
->Session
!= NULL
))
96 /* Set the session locale Id */
97 ((PMM_SESSION_SPACE
)CurrentProcess
->Session
)->LocaleId
= LocaleId
;
101 /* Set the default locale */
102 PsDefaultThreadLocaleId
= LocaleId
;
109 MiInitializeSessionIds(VOID
)
111 ULONG Size
, BitmapSize
;
112 PFN_NUMBER TotalPages
;
114 /* Setup the total number of data pages needed for the structure */
115 TotalPages
= MI_SESSION_DATA_PAGES_MAXIMUM
;
116 MiSessionDataPages
= ROUND_TO_PAGES(sizeof(MM_SESSION_SPACE
)) >> PAGE_SHIFT
;
117 ASSERT(MiSessionDataPages
<= MI_SESSION_DATA_PAGES_MAXIMUM
- 3);
118 TotalPages
-= MiSessionDataPages
;
120 /* Setup the number of pages needed for session pool tags */
121 MiSessionTagSizePages
= 2;
122 MiSessionBigPoolPages
= 1;
123 MiSessionTagPages
= MiSessionTagSizePages
+ MiSessionBigPoolPages
;
124 ASSERT(MiSessionTagPages
<= TotalPages
);
125 ASSERT(MiSessionTagPages
< MI_SESSION_TAG_PAGES_MAXIMUM
);
127 /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */
128 MiSessionCreateCharge
= 1 + MiSessionDataPages
+ MiSessionTagPages
;
130 /* Initialize the lock */
131 KeInitializeGuardedMutex(&MiSessionIdMutex
);
133 /* Allocate the bitmap */
134 Size
= MI_INITIAL_SESSION_IDS
;
135 BitmapSize
= ((Size
+ 31) / 32) * sizeof(ULONG
);
136 MiSessionIdBitmap
= ExAllocatePoolWithTag(PagedPool
,
137 sizeof(RTL_BITMAP
) + BitmapSize
,
139 if (MiSessionIdBitmap
)
141 /* Free all the bits */
142 RtlInitializeBitMap(MiSessionIdBitmap
,
143 (PVOID
)(MiSessionIdBitmap
+ 1),
145 RtlClearAllBits(MiSessionIdBitmap
);
149 /* Die if we couldn't allocate the bitmap */
150 KeBugCheckEx(INSTALL_MORE_MEMORY
,
151 MmNumberOfPhysicalPages
,
152 MmLowestPhysicalPage
,
153 MmHighestPhysicalPage
,
160 MiSessionLeader(IN PEPROCESS Process
)
164 /* Set the flag while under the expansion lock */
165 OldIrql
= MiAcquireExpansionLock();
166 Process
->Vm
.Flags
.SessionLeader
= TRUE
;
167 MiReleaseExpansionLock(OldIrql
);
172 MmGetSessionId(IN PEPROCESS Process
)
174 PMM_SESSION_SPACE SessionGlobal
;
176 /* The session leader is always session zero */
177 if (Process
->Vm
.Flags
.SessionLeader
== 1) return 0;
179 /* Otherwise, get the session global, and read the session ID from it */
180 SessionGlobal
= (PMM_SESSION_SPACE
)Process
->Session
;
181 if (!SessionGlobal
) return 0;
182 return SessionGlobal
->SessionId
;
187 MmGetSessionIdEx(IN PEPROCESS Process
)
189 PMM_SESSION_SPACE SessionGlobal
;
191 /* The session leader is always session zero */
192 if (Process
->Vm
.Flags
.SessionLeader
== 1) return 0;
194 /* Otherwise, get the session global, and read the session ID from it */
195 SessionGlobal
= (PMM_SESSION_SPACE
)Process
->Session
;
196 if (!SessionGlobal
) return -1;
197 return SessionGlobal
->SessionId
;
202 MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal
)
206 PFN_NUMBER PageFrameIndex
[MI_SESSION_DATA_PAGES_MAXIMUM
];
210 /* Is there more than just this reference? If so, bail out */
211 if (InterlockedDecrement(&SessionGlobal
->ProcessReferenceToSession
)) return;
213 /* Get the session ID */
214 SessionId
= SessionGlobal
->SessionId
;
215 DPRINT1("Last process in session %lu going down!!!\n", SessionId
);
217 /* Free the session page tables */
219 ExFreePoolWithTag(SessionGlobal
->PageTables
, 'tHmM');
221 ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal
));
223 /* Capture the data page PFNs */
224 PointerPte
= MiAddressToPte(SessionGlobal
);
225 for (i
= 0; i
< MiSessionDataPages
; i
++)
227 PageFrameIndex
[i
] = PFN_FROM_PTE(PointerPte
+ i
);
231 MiReleaseSystemPtes(PointerPte
, MiSessionDataPages
, SystemPteSpace
);
233 /* Mark them as deleted */
234 for (i
= 0; i
< MiSessionDataPages
; i
++)
236 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
[i
]);
237 MI_SET_PFN_DELETED(Pfn1
);
240 /* Loop every data page and drop a reference count */
241 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
242 for (i
= 0; i
< MiSessionDataPages
; i
++)
244 /* Sanity check that the page is correct, then decrement it */
245 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
[i
]);
246 ASSERT(Pfn1
->u2
.ShareCount
== 1);
247 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 1);
248 MiDecrementShareCount(Pfn1
, PageFrameIndex
[i
]);
251 /* Done playing with pages, release the lock */
252 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
254 /* Decrement the number of data pages */
255 InterlockedDecrement(&MmSessionDataPages
);
257 /* Free this session ID from the session bitmap */
258 KeAcquireGuardedMutex(&MiSessionIdMutex
);
259 ASSERT(RtlCheckBit(MiSessionIdBitmap
, SessionId
));
260 RtlClearBit(MiSessionIdBitmap
, SessionId
);
261 KeReleaseGuardedMutex(&MiSessionIdMutex
);
266 MiDereferenceSessionFinal(VOID
)
268 PMM_SESSION_SPACE SessionGlobal
;
271 /* Get the pointer to the global session address */
272 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
274 /* Acquire the expansion lock */
275 OldIrql
= MiAcquireExpansionLock();
277 /* Set delete pending flag, so that processes can no longer attach to this
278 session and the last process that detaches sets the AttachEvent */
279 ASSERT(SessionGlobal
->u
.Flags
.DeletePending
== 0);
280 SessionGlobal
->u
.Flags
.DeletePending
= 1;
282 /* Check if we have any attached processes */
283 if (SessionGlobal
->AttachCount
)
285 /* Initialize the event (it's not in use yet!) */
286 KeInitializeEvent(&SessionGlobal
->AttachEvent
, NotificationEvent
, FALSE
);
288 /* Release the expansion lock for the wait */
289 MiReleaseExpansionLock(OldIrql
);
291 /* Wait for the event to be set due to the last process detach */
292 KeWaitForSingleObject(&SessionGlobal
->AttachEvent
, WrVirtualMemory
, 0, 0, 0);
294 /* Reacquire the expansion lock */
295 OldIrql
= MiAcquireExpansionLock();
297 /* Makes sure we still have the delete flag and no attached processes */
298 ASSERT(MmSessionSpace
->u
.Flags
.DeletePending
== 1);
299 ASSERT(MmSessionSpace
->AttachCount
== 0);
302 /* Check if the session is in the workingset expansion list */
303 if (SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
!= NULL
)
305 /* Remove the session from the list and zero the list entry */
306 RemoveEntryList(&SessionGlobal
->Vm
.WorkingSetExpansionLinks
);
307 SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
= 0;
310 /* Check if the session is in the workingset list */
311 if (SessionGlobal
->WsListEntry
.Flink
)
313 /* Remove the session from the list and zero the list entry */
314 RemoveEntryList(&SessionGlobal
->WsListEntry
);
315 SessionGlobal
->WsListEntry
.Flink
= NULL
;
318 /* Release the expansion lock */
319 MiReleaseExpansionLock(OldIrql
);
321 /* Check for a win32k unload routine */
322 if (SessionGlobal
->Win32KDriverUnload
)
325 SessionGlobal
->Win32KDriverUnload(NULL
);
332 MiDereferenceSession(VOID
)
334 PMM_SESSION_SPACE SessionGlobal
;
336 ULONG ReferenceCount
, SessionId
;
339 ASSERT(PsGetCurrentProcess()->ProcessInSession
||
340 ((MmSessionSpace
->u
.Flags
.Initialized
== 0) &&
341 (PsGetCurrentProcess()->Vm
.Flags
.SessionLeader
== 1) &&
342 (MmSessionSpace
->ReferenceCount
== 1)));
344 /* The session bit must be set */
345 SessionId
= MmSessionSpace
->SessionId
;
346 ASSERT(RtlCheckBit(MiSessionIdBitmap
, SessionId
));
348 /* Get the current process */
349 Process
= PsGetCurrentProcess();
351 /* Decrement the process count */
352 InterlockedDecrement(&MmSessionSpace
->ResidentProcessCount
);
354 /* Decrement the reference count and check if was the last reference */
355 ReferenceCount
= InterlockedDecrement(&MmSessionSpace
->ReferenceCount
);
356 if (ReferenceCount
== 0)
358 /* No more references left, kill the session completely */
359 MiDereferenceSessionFinal();
362 /* Check if tis is the session leader or the last process in the session */
363 if ((Process
->Vm
.Flags
.SessionLeader
) || (ReferenceCount
== 0))
365 /* Get the global session address before we kill the session mapping */
366 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
368 /* Delete all session PDEs and flush the TB */
369 RtlZeroMemory(MiAddressToPde(MmSessionBase
),
370 BYTES_TO_PAGES(MmSessionSize
) * sizeof(MMPDE
));
371 KeFlushEntireTb(FALSE
, FALSE
);
373 /* Is this the session leader? */
374 if (Process
->Vm
.Flags
.SessionLeader
)
376 /* Clean up the references here. */
377 ASSERT(Process
->Session
== NULL
);
378 MiReleaseProcessReferenceToSessionDataPage(SessionGlobal
);
382 /* Reset the current process' session flag */
383 RtlInterlockedClearBits(&Process
->Flags
, PSF_PROCESS_IN_SESSION_BIT
);
388 MiSessionRemoveProcess(VOID
)
390 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
393 /* If the process isn't already in a session, or if it's the leader... */
394 if (!(CurrentProcess
->Flags
& PSF_PROCESS_IN_SESSION_BIT
) ||
395 (CurrentProcess
->Vm
.Flags
.SessionLeader
))
397 /* Then there's nothing to do */
402 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
404 /* Acquire the expansion lock while touching the session */
405 OldIrql
= MiAcquireExpansionLock();
407 /* Remove the process from the list */
408 RemoveEntryList(&CurrentProcess
->SessionProcessLinks
);
410 /* Release the lock again */
411 MiReleaseExpansionLock(OldIrql
);
413 /* Dereference the session */
414 MiDereferenceSession();
419 MiSessionAddProcess(IN PEPROCESS NewProcess
)
421 PMM_SESSION_SPACE SessionGlobal
;
424 /* The current process must already be in a session */
425 if (!(PsGetCurrentProcess()->Flags
& PSF_PROCESS_IN_SESSION_BIT
)) return;
428 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
430 /* Get the global session */
431 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
433 /* Increment counters */
434 InterlockedIncrement((PLONG
)&SessionGlobal
->ReferenceCount
);
435 InterlockedIncrement(&SessionGlobal
->ResidentProcessCount
);
436 InterlockedIncrement(&SessionGlobal
->ProcessReferenceToSession
);
438 /* Set the session pointer */
439 ASSERT(NewProcess
->Session
== NULL
);
440 NewProcess
->Session
= SessionGlobal
;
442 /* Acquire the expansion lock while touching the session */
443 OldIrql
= MiAcquireExpansionLock();
445 /* Insert it into the process list */
446 InsertTailList(&SessionGlobal
->ProcessList
, &NewProcess
->SessionProcessLinks
);
448 /* Release the lock again */
449 MiReleaseExpansionLock(OldIrql
);
452 PspSetProcessFlag(NewProcess
, PSF_PROCESS_IN_SESSION_BIT
);
457 MiSessionInitializeWorkingSetList(VOID
)
465 PFN_NUMBER PageFrameIndex
;
466 PMM_SESSION_SPACE SessionGlobal
;
467 BOOLEAN AllocatedPageTable
;
468 PMMWSL WorkingSetList
;
470 /* Get pointers to session global and the session working set list */
471 SessionGlobal
= MmSessionSpace
->GlobalVirtualAddress
;
472 WorkingSetList
= (PMMWSL
)MiSessionSpaceWs
;
474 /* Fill out the two pointers */
475 MmSessionSpace
->Vm
.VmWorkingSetList
= WorkingSetList
;
476 MmSessionSpace
->Wsle
= (PMMWSLE
)WorkingSetList
->UsedPageTableEntries
;
478 /* Get the PDE for the working set, and check if it's already allocated */
479 PointerPde
= MiAddressToPde(WorkingSetList
);
480 if (PointerPde
->u
.Hard
.Valid
== 1)
482 /* Nope, we'll have to do it */
484 ASSERT(PointerPde
->u
.Hard
.Global
== 0);
486 AllocatedPageTable
= FALSE
;
490 /* Yep, that makes our job easier */
491 AllocatedPageTable
= TRUE
;
494 /* Get the PTE for the working set */
495 PointerPte
= MiAddressToPte(WorkingSetList
);
497 /* Initialize the working set lock, and lock the PFN database */
498 ExInitializePushLock(&SessionGlobal
->Vm
.WorkingSetMutex
);
499 //MmLockPageableSectionByHandle(ExPageLockHandle);
500 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
502 /* Check if we need a page table */
503 if (AllocatedPageTable
!= FALSE
)
505 /* Get a zeroed colored zero page */
506 Color
= MI_GET_NEXT_COLOR();
507 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
510 /* No zero pages, grab a free one */
511 PageFrameIndex
= MiRemoveAnyPage(Color
);
513 /* Zero it outside the PFN lock */
514 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
515 MiZeroPhysicalPage(PageFrameIndex
);
516 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
519 /* Write a valid PDE for it */
520 TempPde
= ValidKernelPdeLocal
;
521 TempPde
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
522 MI_WRITE_VALID_PDE(PointerPde
, TempPde
);
524 /* Add this into the list */
525 Index
= ((ULONG_PTR
)WorkingSetList
- (ULONG_PTR
)MmSessionBase
) >> 22;
527 MmSessionSpace
->PageTables
[Index
] = TempPde
;
529 /* Initialize the page directory page, and now zero the working set list itself */
530 MiInitializePfnForOtherProcess(PageFrameIndex
,
532 MmSessionSpace
->SessionPageDirectoryIndex
);
533 KeZeroPages(PointerPte
, PAGE_SIZE
);
536 /* Get a zeroed colored zero page */
537 Color
= MI_GET_NEXT_COLOR();
538 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
541 /* No zero pages, grab a free one */
542 PageFrameIndex
= MiRemoveAnyPage(Color
);
544 /* Zero it outside the PFN lock */
545 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
546 MiZeroPhysicalPage(PageFrameIndex
);
547 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
550 /* Write a valid PTE for it */
551 TempPte
= ValidKernelPteLocal
;
552 MI_MAKE_DIRTY_PAGE(&TempPte
);
553 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
555 /* Initialize the working set list page */
556 MiInitializePfnAndMakePteValid(PageFrameIndex
, PointerPte
, TempPte
);
558 /* Now we can release the PFN database lock */
559 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
561 /* Fill out the working set structure */
562 MmSessionSpace
->Vm
.Flags
.SessionSpace
= 1;
563 MmSessionSpace
->Vm
.MinimumWorkingSetSize
= 20;
564 MmSessionSpace
->Vm
.MaximumWorkingSetSize
= 384;
565 WorkingSetList
->LastEntry
= 20;
566 WorkingSetList
->HashTable
= NULL
;
567 WorkingSetList
->HashTableSize
= 0;
568 WorkingSetList
->Wsle
= MmSessionSpace
->Wsle
;
570 /* Acquire the expansion lock while touching the session */
571 OldIrql
= MiAcquireExpansionLock();
573 /* Handle list insertions */
574 ASSERT(SessionGlobal
->WsListEntry
.Flink
== NULL
);
575 ASSERT(SessionGlobal
->WsListEntry
.Blink
== NULL
);
576 InsertTailList(&MiSessionWsList
, &SessionGlobal
->WsListEntry
);
578 ASSERT(SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Flink
== NULL
);
579 ASSERT(SessionGlobal
->Vm
.WorkingSetExpansionLinks
.Blink
== NULL
);
580 InsertTailList(&MmWorkingSetExpansionHead
,
581 &SessionGlobal
->Vm
.WorkingSetExpansionLinks
);
583 /* Release the lock again */
584 MiReleaseExpansionLock(OldIrql
);
586 /* All done, return */
587 //MmUnlockPageableImageSection(ExPageLockHandle);
588 return STATUS_SUCCESS
;
593 MiSessionCreateInternal(OUT PULONG SessionId
)
595 PEPROCESS Process
= PsGetCurrentProcess();
596 ULONG NewFlags
, Flags
, Size
, i
, Color
;
598 PMMPTE PointerPte
, SessionPte
;
599 PMMPDE PointerPde
, PageTables
;
600 PMM_SESSION_SPACE SessionGlobal
;
605 PFN_NUMBER SessionPageDirIndex
;
606 PFN_NUMBER TagPage
[MI_SESSION_TAG_PAGES_MAXIMUM
];
607 PFN_NUMBER DataPage
[MI_SESSION_DATA_PAGES_MAXIMUM
];
609 /* This should not exist yet */
610 ASSERT(MmIsAddressValid(MmSessionSpace
) == FALSE
);
612 /* Loop so we can set the session-is-creating flag */
613 Flags
= Process
->Flags
;
616 /* Check if it's already set */
617 if (Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
)
620 DPRINT1("Lost session race\n");
621 return STATUS_ALREADY_COMMITTED
;
624 /* Now try to set it */
625 NewFlags
= InterlockedCompareExchange((PLONG
)&Process
->Flags
,
626 Flags
| PSF_SESSION_CREATION_UNDERWAY_BIT
,
628 if (NewFlags
== Flags
) break;
630 /* It changed, try again */
634 /* Now we should own the flag */
635 ASSERT(Process
->Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
);
638 * Session space covers everything from 0xA0000000 to 0xC0000000.
639 * Allocate enough page tables to describe the entire region
641 Size
= (0x20000000 / PDE_MAPPED_VA
) * sizeof(MMPTE
);
642 PageTables
= ExAllocatePoolWithTag(NonPagedPool
, Size
, 'tHmM');
643 ASSERT(PageTables
!= NULL
);
644 RtlZeroMemory(PageTables
, Size
);
646 /* Lock the session ID creation mutex */
647 KeAcquireGuardedMutex(&MiSessionIdMutex
);
649 /* Allocate a new Session ID */
650 *SessionId
= RtlFindClearBitsAndSet(MiSessionIdBitmap
, 1, 0);
651 if (*SessionId
== 0xFFFFFFFF)
653 /* We ran out of session IDs, we should expand */
654 DPRINT1("Too many sessions created. Expansion not yet supported\n");
655 ExFreePoolWithTag(PageTables
, 'tHmM');
656 return STATUS_NO_MEMORY
;
659 /* Unlock the session ID creation mutex */
660 KeReleaseGuardedMutex(&MiSessionIdMutex
);
662 /* Reserve the global PTEs */
663 SessionPte
= MiReserveSystemPtes(MiSessionDataPages
, SystemPteSpace
);
664 ASSERT(SessionPte
!= NULL
);
666 /* Acquire the PFN lock while we set everything up */
667 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
669 /* Loop the global PTEs */
670 TempPte
= ValidKernelPte
;
671 for (i
= 0; i
< MiSessionDataPages
; i
++)
673 /* Get a zeroed colored zero page */
674 Color
= MI_GET_NEXT_COLOR();
675 DataPage
[i
] = MiRemoveZeroPageSafe(Color
);
678 /* No zero pages, grab a free one */
679 DataPage
[i
] = MiRemoveAnyPage(Color
);
681 /* Zero it outside the PFN lock */
682 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
683 MiZeroPhysicalPage(DataPage
[i
]);
684 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
687 /* Fill the PTE out */
688 TempPte
.u
.Hard
.PageFrameNumber
= DataPage
[i
];
689 MI_WRITE_VALID_PTE(SessionPte
+ i
, TempPte
);
692 /* Set the pointer to global space */
693 SessionGlobal
= MiPteToAddress(SessionPte
);
695 /* Get a zeroed colored zero page */
696 Color
= MI_GET_NEXT_COLOR();
697 SessionPageDirIndex
= MiRemoveZeroPageSafe(Color
);
698 if (!SessionPageDirIndex
)
700 /* No zero pages, grab a free one */
701 SessionPageDirIndex
= MiRemoveAnyPage(Color
);
703 /* Zero it outside the PFN lock */
704 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
705 MiZeroPhysicalPage(SessionPageDirIndex
);
706 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
709 /* Fill the PTE out */
710 TempPde
= ValidKernelPdeLocal
;
711 TempPde
.u
.Hard
.PageFrameNumber
= SessionPageDirIndex
;
713 /* Setup, allocate, fill out the MmSessionSpace PTE */
714 PointerPde
= MiAddressToPde(MmSessionSpace
);
715 ASSERT(PointerPde
->u
.Long
== 0);
716 MI_WRITE_VALID_PDE(PointerPde
, TempPde
);
717 MiInitializePfnForOtherProcess(SessionPageDirIndex
,
719 SessionPageDirIndex
);
720 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex
)->u1
.WsIndex
== 0);
722 /* Loop all the local PTEs for it */
723 TempPte
= ValidKernelPteLocal
;
724 PointerPte
= MiAddressToPte(MmSessionSpace
);
725 for (i
= 0; i
< MiSessionDataPages
; i
++)
727 /* And fill them out */
728 TempPte
.u
.Hard
.PageFrameNumber
= DataPage
[i
];
729 MiInitializePfnAndMakePteValid(DataPage
[i
], PointerPte
+ i
, TempPte
);
730 ASSERT(MI_PFN_ELEMENT(DataPage
[i
])->u1
.WsIndex
== 0);
733 /* Finally loop all of the session pool tag pages */
734 for (i
= 0; i
< MiSessionTagPages
; i
++)
736 /* Grab a zeroed colored page */
737 Color
= MI_GET_NEXT_COLOR();
738 TagPage
[i
] = MiRemoveZeroPageSafe(Color
);
741 /* No zero pages, grab a free one */
742 TagPage
[i
] = MiRemoveAnyPage(Color
);
744 /* Zero it outside the PFN lock */
745 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
746 MiZeroPhysicalPage(TagPage
[i
]);
747 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
750 /* Fill the PTE out */
751 TempPte
.u
.Hard
.PageFrameNumber
= TagPage
[i
];
752 MiInitializePfnAndMakePteValid(TagPage
[i
],
753 PointerPte
+ MiSessionDataPages
+ i
,
757 /* PTEs have been setup, release the PFN lock */
758 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
760 /* Fill out the session space structure now */
761 MmSessionSpace
->GlobalVirtualAddress
= SessionGlobal
;
762 MmSessionSpace
->ReferenceCount
= 1;
763 MmSessionSpace
->ResidentProcessCount
= 1;
764 MmSessionSpace
->u
.LongFlags
= 0;
765 MmSessionSpace
->SessionId
= *SessionId
;
766 MmSessionSpace
->LocaleId
= PsDefaultSystemLocaleId
;
767 MmSessionSpace
->SessionPageDirectoryIndex
= SessionPageDirIndex
;
768 MmSessionSpace
->Color
= Color
;
769 MmSessionSpace
->NonPageablePages
= MiSessionCreateCharge
;
770 MmSessionSpace
->CommittedPages
= MiSessionCreateCharge
;
772 MmSessionSpace
->PageTables
= PageTables
;
773 MmSessionSpace
->PageTables
[PointerPde
- MiAddressToPde(MmSessionBase
)] = *PointerPde
;
775 InitializeListHead(&MmSessionSpace
->ImageList
);
776 DPRINT1("Session %lu is ready to go: 0x%p 0x%p, %lx 0x%p\n",
777 *SessionId
, MmSessionSpace
, SessionGlobal
, SessionPageDirIndex
, PageTables
);
779 /* Initialize session pool */
780 //Status = MiInitializeSessionPool();
781 Status
= STATUS_SUCCESS
;
782 ASSERT(NT_SUCCESS(Status
) == TRUE
);
784 /* Initialize system space */
785 Result
= MiInitializeSystemSpaceMap(&SessionGlobal
->Session
);
786 ASSERT(Result
== TRUE
);
788 /* Initialize the process list, make sure the workign set list is empty */
789 ASSERT(SessionGlobal
->WsListEntry
.Flink
== NULL
);
790 ASSERT(SessionGlobal
->WsListEntry
.Blink
== NULL
);
791 InitializeListHead(&SessionGlobal
->ProcessList
);
793 /* We're done, clear the flag */
794 ASSERT(Process
->Flags
& PSF_SESSION_CREATION_UNDERWAY_BIT
);
795 PspClearProcessFlag(Process
, PSF_SESSION_CREATION_UNDERWAY_BIT
);
797 /* Insert the process into the session */
798 ASSERT(Process
->Session
== NULL
);
799 ASSERT(SessionGlobal
->ProcessReferenceToSession
== 0);
800 SessionGlobal
->ProcessReferenceToSession
= 1;
803 InterlockedIncrement(&MmSessionDataPages
);
804 return STATUS_SUCCESS
;
809 MmSessionCreate(OUT PULONG SessionId
)
811 PEPROCESS Process
= PsGetCurrentProcess();
812 ULONG SessionLeaderExists
;
815 /* Fail if the process is already in a session */
816 if (Process
->Flags
& PSF_PROCESS_IN_SESSION_BIT
)
818 DPRINT1("Process already in session\n");
819 return STATUS_ALREADY_COMMITTED
;
822 /* Check if the process is already the session leader */
823 if (!Process
->Vm
.Flags
.SessionLeader
)
825 /* Atomically set it as the leader */
826 SessionLeaderExists
= InterlockedCompareExchange(&MiSessionLeaderExists
, 1, 0);
827 if (SessionLeaderExists
)
829 DPRINT1("Session leader race\n");
830 return STATUS_INVALID_SYSTEM_SERVICE
;
833 /* Do the work required to upgrade him */
834 MiSessionLeader(Process
);
837 /* Create the session */
838 KeEnterCriticalRegion();
839 Status
= MiSessionCreateInternal(SessionId
);
840 if (!NT_SUCCESS(Status
))
842 KeLeaveCriticalRegion();
846 /* Set up the session working set */
847 Status
= MiSessionInitializeWorkingSetList();
848 if (!NT_SUCCESS(Status
))
851 //MiDereferenceSession();
853 KeLeaveCriticalRegion();
858 KeLeaveCriticalRegion();
860 /* Set and assert the flags, and return */
861 MmSessionSpace
->u
.Flags
.Initialized
= 1;
862 PspSetProcessFlag(Process
, PSF_PROCESS_IN_SESSION_BIT
);
863 ASSERT(MiSessionLeaderExists
== 1);
869 MmSessionDelete(IN ULONG SessionId
)
871 PEPROCESS Process
= PsGetCurrentProcess();
873 /* Process must be in a session */
874 if (!(Process
->Flags
& PSF_PROCESS_IN_SESSION_BIT
))
876 DPRINT1("Not in a session!\n");
877 return STATUS_UNABLE_TO_FREE_VM
;
880 /* It must be the session leader */
881 if (!Process
->Vm
.Flags
.SessionLeader
)
883 DPRINT1("Not a session leader!\n");
884 return STATUS_UNABLE_TO_FREE_VM
;
887 /* Remove one reference count */
888 KeEnterCriticalRegion();
890 KeLeaveCriticalRegion();
893 return STATUS_SUCCESS
;
896 _IRQL_requires_max_(APC_LEVEL
)
900 _Inout_ PVOID SessionEntry
,
901 _Out_ PKAPC_STATE ApcState
)
903 PEPROCESS EntryProcess
;
904 PMM_SESSION_SPACE EntrySession
, CurrentSession
;
905 PEPROCESS CurrentProcess
;
908 /* The parameter is the actual process! */
909 EntryProcess
= SessionEntry
;
910 ASSERT(EntryProcess
!= NULL
);
913 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
914 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
916 /* Get the session from the process that was passed in */
917 EntrySession
= EntryProcess
->Session
;
918 ASSERT(EntrySession
!= NULL
);
920 /* Get the current process and it's session */
921 CurrentProcess
= PsGetCurrentProcess();
922 CurrentSession
= CurrentProcess
->Session
;
924 /* Acquire the expansion lock while touching the session */
925 OldIrql
= MiAcquireExpansionLock();
927 /* Check if the session is about to be deleted */
928 if (EntrySession
->u
.Flags
.DeletePending
)
930 /* We cannot attach to it, so unlock and fail */
931 MiReleaseExpansionLock(OldIrql
);
932 return STATUS_PROCESS_IS_TERMINATING
;
935 /* Count the number of attaches */
936 EntrySession
->AttachCount
++;
938 /* we can release the lock again */
939 MiReleaseExpansionLock(OldIrql
);
941 /* Check if we are not the session leader and we are in a session */
942 if (!CurrentProcess
->Vm
.Flags
.SessionLeader
&& (CurrentSession
!= NULL
))
944 /* Are we already in the right session? */
945 if (CurrentSession
== EntrySession
)
947 /* We are, so "attach" to the current process */
948 EntryProcess
= CurrentProcess
;
952 /* We are not, the session id should better not match! */
953 ASSERT(CurrentSession
->SessionId
!= EntrySession
->SessionId
);
957 /* Now attach to the process that we have */
958 KeStackAttachProcess(&EntryProcess
->Pcb
, ApcState
);
961 return STATUS_SUCCESS
;
964 _IRQL_requires_max_(APC_LEVEL
)
968 _Inout_ PVOID SessionEntry
,
969 _In_ PKAPC_STATE ApcState
)
971 PEPROCESS EntryProcess
;
972 PMM_SESSION_SPACE EntrySession
;
974 BOOLEAN DeletePending
;
976 /* The parameter is the actual process! */
977 EntryProcess
= SessionEntry
;
978 ASSERT(EntryProcess
!= NULL
);
981 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
982 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
984 /* Get the session from the process that was passed in */
985 EntrySession
= EntryProcess
->Session
;
986 ASSERT(EntrySession
!= NULL
);
988 /* Acquire the expansion lock while touching the session */
989 OldIrql
= MiAcquireExpansionLock();
991 /* Make sure we have at least one attach and decrement the count */
992 ASSERT(EntrySession
->AttachCount
>= 1);
993 EntrySession
->AttachCount
--;
995 /* Remember if a delete is pending and we were the last one attached */
996 DeletePending
= EntrySession
->u
.Flags
.DeletePending
&&
997 (EntrySession
->AttachCount
== 0);
999 /* Release the lock again */
1000 MiReleaseExpansionLock(OldIrql
);
1002 /* Detach from the process */
1003 KeUnstackDetachProcess(ApcState
);
1005 /* Check if we need to set the attach event */
1007 KeSetEvent(&EntrySession
->AttachEvent
, IO_NO_INCREMENT
, FALSE
);
1013 _Inout_ PVOID SessionEntry
)
1015 PEPROCESS EntryProcess
;
1017 /* The parameter is the actual process! */
1018 EntryProcess
= SessionEntry
;
1019 ASSERT(EntryProcess
!= NULL
);
1022 ASSERT(KeGetCurrentIrql () <= APC_LEVEL
);
1023 ASSERT(EntryProcess
->Vm
.Flags
.SessionLeader
== 0);
1024 ASSERT(EntryProcess
->Session
!= NULL
);
1026 /* Get rid of the reference we took */
1027 ObDereferenceObject(EntryProcess
);
1033 _In_ ULONG SessionId
)
1035 PLIST_ENTRY ListEntry
;
1036 PMM_SESSION_SPACE Session
;
1037 PEPROCESS Process
= NULL
;
1040 /* Acquire the expansion lock while touching the session */
1041 OldIrql
= MiAcquireExpansionLock();
1043 /* Loop all entries in the session ws list */
1044 ListEntry
= MiSessionWsList
.Flink
;
1045 while (ListEntry
!= &MiSessionWsList
)
1047 Session
= CONTAINING_RECORD(ListEntry
, MM_SESSION_SPACE
, WsListEntry
);
1049 /* Check if this is the session we are looking for */
1050 if (Session
->SessionId
== SessionId
)
1052 /* Check if we also have a process in the process list */
1053 if (!IsListEmpty(&Session
->ProcessList
))
1055 Process
= CONTAINING_RECORD(Session
->ProcessList
.Flink
,
1057 SessionProcessLinks
);
1059 /* Reference the process */
1060 ObReferenceObject(Process
);
1066 /* Release the lock again */
1067 MiReleaseExpansionLock(OldIrql
);