1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * FILE: ntoskrnl/mm/ARM3/session.c 5 * PURPOSE: Session support routines 6 * PROGRAMMERS: ReactOS Portable Systems Group 7 * Timo Kreuzer (timo.kreuzer@reactos.org) 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 #define MODULE_INVOLVED_IN_ARM3 17 #include <mm/ARM3/miarm.h> 18 19 /* GLOBALS ********************************************************************/ 20 21 PMM_SESSION_SPACE MmSessionSpace; 22 PFN_NUMBER MiSessionDataPages, MiSessionTagPages, MiSessionTagSizePages; 23 PFN_NUMBER MiSessionBigPoolPages, MiSessionCreateCharge; 24 KGUARDED_MUTEX MiSessionIdMutex; 25 LONG MmSessionDataPages; 26 PRTL_BITMAP MiSessionIdBitmap; 27 volatile LONG MiSessionLeaderExists; 28 29 LIST_ENTRY MiSessionWsList; 30 LIST_ENTRY MmWorkingSetExpansionHead; 31 32 KSPIN_LOCK MmExpansionLock; 33 PETHREAD MiExpansionLockOwner; 34 35 36 /* PRIVATE FUNCTIONS **********************************************************/ 37 38 VOID 39 NTAPI 40 MiInitializeSessionWsSupport(VOID) 41 { 42 /* Initialize the list heads */ 43 InitializeListHead(&MiSessionWsList); 44 InitializeListHead(&MmWorkingSetExpansionHead); 45 } 46 47 BOOLEAN 48 NTAPI 49 MmIsSessionAddress(IN PVOID Address) 50 { 51 /* Check if it is in range */ 52 return MI_IS_SESSION_ADDRESS(Address) ? TRUE : FALSE; 53 } 54 55 LCID 56 NTAPI 57 MmGetSessionLocaleId(VOID) 58 { 59 PEPROCESS Process; 60 PAGED_CODE(); 61 62 // 63 // Get the current process 64 // 65 Process = PsGetCurrentProcess(); 66 67 // 68 // Check if it's NOT the Session Leader 69 // 70 if (!Process->Vm.Flags.SessionLeader) 71 { 72 // 73 // Make sure it has a valid Session 74 // 75 if (Process->Session) 76 { 77 // 78 // Get the Locale ID 79 // 80 return ((PMM_SESSION_SPACE)Process->Session)->LocaleId; 81 } 82 } 83 84 // 85 // Not a session leader, return the default 86 // 87 return PsDefaultThreadLocaleId; 88 } 89 90 _IRQL_requires_max_(APC_LEVEL) 91 VOID 92 NTAPI 93 MmSetSessionLocaleId( 94 _In_ LCID LocaleId) 95 { 96 PEPROCESS CurrentProcess; 97 PAGED_CODE(); 98 99 /* Get the current process and check if it is in a session */ 100 CurrentProcess = PsGetCurrentProcess(); 101 if ((CurrentProcess->Vm.Flags.SessionLeader == 0) && 102 (CurrentProcess->Session != NULL)) 103 { 104 /* Set the session locale Id */ 105 ((PMM_SESSION_SPACE)CurrentProcess->Session)->LocaleId = LocaleId; 106 } 107 else 108 { 109 /* Set the default locale */ 110 PsDefaultThreadLocaleId = LocaleId; 111 } 112 } 113 114 115 VOID 116 NTAPI 117 MiInitializeSessionIds(VOID) 118 { 119 ULONG Size, BitmapSize; 120 PFN_NUMBER TotalPages; 121 122 /* Setup the total number of data pages needed for the structure */ 123 TotalPages = MI_SESSION_DATA_PAGES_MAXIMUM; 124 MiSessionDataPages = ROUND_TO_PAGES(sizeof(MM_SESSION_SPACE)) >> PAGE_SHIFT; 125 ASSERT(MiSessionDataPages <= MI_SESSION_DATA_PAGES_MAXIMUM - 3); 126 TotalPages -= MiSessionDataPages; 127 128 /* Setup the number of pages needed for session pool tags */ 129 MiSessionTagSizePages = 2; 130 MiSessionBigPoolPages = 1; 131 MiSessionTagPages = MiSessionTagSizePages + MiSessionBigPoolPages; 132 ASSERT(MiSessionTagPages <= TotalPages); 133 ASSERT(MiSessionTagPages < MI_SESSION_TAG_PAGES_MAXIMUM); 134 135 /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */ 136 MiSessionCreateCharge = 1 + MiSessionDataPages + MiSessionTagPages; 137 138 /* Initialize the lock */ 139 KeInitializeGuardedMutex(&MiSessionIdMutex); 140 141 /* Allocate the bitmap */ 142 Size = MI_INITIAL_SESSION_IDS; 143 BitmapSize = ((Size + 31) / 32) * sizeof(ULONG); 144 MiSessionIdBitmap = ExAllocatePoolWithTag(PagedPool, 145 sizeof(RTL_BITMAP) + BitmapSize, 146 TAG_MM); 147 if (MiSessionIdBitmap) 148 { 149 /* Free all the bits */ 150 RtlInitializeBitMap(MiSessionIdBitmap, 151 (PVOID)(MiSessionIdBitmap + 1), 152 Size); 153 RtlClearAllBits(MiSessionIdBitmap); 154 } 155 else 156 { 157 /* Die if we couldn't allocate the bitmap */ 158 KeBugCheckEx(INSTALL_MORE_MEMORY, 159 MmNumberOfPhysicalPages, 160 MmLowestPhysicalPage, 161 MmHighestPhysicalPage, 162 0x200); 163 } 164 } 165 166 VOID 167 NTAPI 168 MiSessionLeader(IN PEPROCESS Process) 169 { 170 KIRQL OldIrql; 171 172 /* Set the flag while under the expansion lock */ 173 OldIrql = MiAcquireExpansionLock(); 174 Process->Vm.Flags.SessionLeader = TRUE; 175 MiReleaseExpansionLock(OldIrql); 176 } 177 178 ULONG 179 NTAPI 180 MmGetSessionId(IN PEPROCESS Process) 181 { 182 PMM_SESSION_SPACE SessionGlobal; 183 184 /* The session leader is always session zero */ 185 if (Process->Vm.Flags.SessionLeader == 1) return 0; 186 187 /* Otherwise, get the session global, and read the session ID from it */ 188 SessionGlobal = (PMM_SESSION_SPACE)Process->Session; 189 if (!SessionGlobal) return 0; 190 return SessionGlobal->SessionId; 191 } 192 193 ULONG 194 NTAPI 195 MmGetSessionIdEx(IN PEPROCESS Process) 196 { 197 PMM_SESSION_SPACE SessionGlobal; 198 199 /* The session leader is always session zero */ 200 if (Process->Vm.Flags.SessionLeader == 1) return 0; 201 202 /* Otherwise, get the session global, and read the session ID from it */ 203 SessionGlobal = (PMM_SESSION_SPACE)Process->Session; 204 if (!SessionGlobal) return -1; 205 return SessionGlobal->SessionId; 206 } 207 208 VOID 209 NTAPI 210 MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal) 211 { 212 ULONG i, SessionId; 213 PMMPTE PointerPte; 214 PFN_NUMBER PageFrameIndex[MI_SESSION_DATA_PAGES_MAXIMUM]; 215 PMMPFN Pfn1; 216 KIRQL OldIrql; 217 218 /* Is there more than just this reference? If so, bail out */ 219 if (InterlockedDecrement(&SessionGlobal->ProcessReferenceToSession)) return; 220 221 /* Get the session ID */ 222 SessionId = SessionGlobal->SessionId; 223 DPRINT1("Last process in session %lu going down!!!\n", SessionId); 224 225 /* Free the session page tables */ 226 #ifndef _M_AMD64 227 ExFreePoolWithTag(SessionGlobal->PageTables, 'tHmM'); 228 #endif 229 ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal)); 230 231 /* Capture the data page PFNs */ 232 PointerPte = MiAddressToPte(SessionGlobal); 233 for (i = 0; i < MiSessionDataPages; i++) 234 { 235 PageFrameIndex[i] = PFN_FROM_PTE(PointerPte + i); 236 } 237 238 /* Release them */ 239 MiReleaseSystemPtes(PointerPte, MiSessionDataPages, SystemPteSpace); 240 241 /* Mark them as deleted */ 242 for (i = 0; i < MiSessionDataPages; i++) 243 { 244 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]); 245 MI_SET_PFN_DELETED(Pfn1); 246 } 247 248 /* Loop every data page and drop a reference count */ 249 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 250 for (i = 0; i < MiSessionDataPages; i++) 251 { 252 /* Sanity check that the page is correct, then decrement it */ 253 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]); 254 ASSERT(Pfn1->u2.ShareCount == 1); 255 ASSERT(Pfn1->u3.e2.ReferenceCount == 1); 256 MiDecrementShareCount(Pfn1, PageFrameIndex[i]); 257 } 258 259 /* Done playing with pages, release the lock */ 260 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 261 262 /* Decrement the number of data pages */ 263 InterlockedDecrement(&MmSessionDataPages); 264 265 /* Free this session ID from the session bitmap */ 266 KeAcquireGuardedMutex(&MiSessionIdMutex); 267 ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId)); 268 RtlClearBit(MiSessionIdBitmap, SessionId); 269 KeReleaseGuardedMutex(&MiSessionIdMutex); 270 } 271 272 VOID 273 NTAPI 274 MiDereferenceSessionFinal(VOID) 275 { 276 PMM_SESSION_SPACE SessionGlobal; 277 KIRQL OldIrql; 278 279 /* Get the pointer to the global session address */ 280 SessionGlobal = MmSessionSpace->GlobalVirtualAddress; 281 282 /* Acquire the expansion lock */ 283 OldIrql = MiAcquireExpansionLock(); 284 285 /* Set delete pending flag, so that processes can no longer attach to this 286 session and the last process that detaches sets the AttachEvent */ 287 ASSERT(SessionGlobal->u.Flags.DeletePending == 0); 288 SessionGlobal->u.Flags.DeletePending = 1; 289 290 /* Check if we have any attached processes */ 291 if (SessionGlobal->AttachCount) 292 { 293 /* Initialize the event (it's not in use yet!) */ 294 KeInitializeEvent(&SessionGlobal->AttachEvent, NotificationEvent, FALSE); 295 296 /* Release the expansion lock for the wait */ 297 MiReleaseExpansionLock(OldIrql); 298 299 /* Wait for the event to be set due to the last process detach */ 300 KeWaitForSingleObject(&SessionGlobal->AttachEvent, WrVirtualMemory, 0, 0, 0); 301 302 /* Reacquire the expansion lock */ 303 OldIrql = MiAcquireExpansionLock(); 304 305 /* Makes sure we still have the delete flag and no attached processes */ 306 ASSERT(MmSessionSpace->u.Flags.DeletePending == 1); 307 ASSERT(MmSessionSpace->AttachCount == 0); 308 } 309 310 /* Check if the session is in the workingset expansion list */ 311 if (SessionGlobal->Vm.WorkingSetExpansionLinks.Flink != NULL) 312 { 313 /* Remove the session from the list and zero the list entry */ 314 RemoveEntryList(&SessionGlobal->Vm.WorkingSetExpansionLinks); 315 SessionGlobal->Vm.WorkingSetExpansionLinks.Flink = 0; 316 } 317 318 /* Check if the session is in the workingset list */ 319 if (SessionGlobal->WsListEntry.Flink) 320 { 321 /* Remove the session from the list and zero the list entry */ 322 RemoveEntryList(&SessionGlobal->WsListEntry); 323 SessionGlobal->WsListEntry.Flink = NULL; 324 } 325 326 /* Release the expansion lock */ 327 MiReleaseExpansionLock(OldIrql); 328 329 /* Check for a win32k unload routine */ 330 if (SessionGlobal->Win32KDriverUnload) 331 { 332 /* Call it */ 333 SessionGlobal->Win32KDriverUnload(NULL); 334 } 335 } 336 337 338 VOID 339 NTAPI 340 MiDereferenceSession(VOID) 341 { 342 PMM_SESSION_SPACE SessionGlobal; 343 PEPROCESS Process; 344 ULONG ReferenceCount, SessionId; 345 346 /* Sanity checks */ 347 ASSERT(PsGetCurrentProcess()->ProcessInSession || 348 ((MmSessionSpace->u.Flags.Initialized == 0) && 349 (PsGetCurrentProcess()->Vm.Flags.SessionLeader == 1) && 350 (MmSessionSpace->ReferenceCount == 1))); 351 352 /* The session bit must be set */ 353 SessionId = MmSessionSpace->SessionId; 354 ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId)); 355 356 /* Get the current process */ 357 Process = PsGetCurrentProcess(); 358 359 /* Decrement the process count */ 360 InterlockedDecrement(&MmSessionSpace->ResidentProcessCount); 361 362 /* Decrement the reference count and check if was the last reference */ 363 ReferenceCount = InterlockedDecrement(&MmSessionSpace->ReferenceCount); 364 if (ReferenceCount == 0) 365 { 366 /* No more references left, kill the session completely */ 367 MiDereferenceSessionFinal(); 368 } 369 370 /* Check if tis is the session leader or the last process in the session */ 371 if ((Process->Vm.Flags.SessionLeader) || (ReferenceCount == 0)) 372 { 373 /* Get the global session address before we kill the session mapping */ 374 SessionGlobal = MmSessionSpace->GlobalVirtualAddress; 375 376 /* Delete all session PDEs and flush the TB */ 377 RtlZeroMemory(MiAddressToPde(MmSessionBase), 378 BYTES_TO_PAGES(MmSessionSize) * sizeof(MMPDE)); 379 KeFlushEntireTb(FALSE, FALSE); 380 381 /* Is this the session leader? */ 382 if (Process->Vm.Flags.SessionLeader) 383 { 384 /* Clean up the references here. */ 385 ASSERT(Process->Session == NULL); 386 MiReleaseProcessReferenceToSessionDataPage(SessionGlobal); 387 } 388 } 389 390 /* Reset the current process' session flag */ 391 RtlInterlockedClearBits(&Process->Flags, PSF_PROCESS_IN_SESSION_BIT); 392 } 393 394 VOID 395 NTAPI 396 MiSessionRemoveProcess(VOID) 397 { 398 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 399 KIRQL OldIrql; 400 401 /* If the process isn't already in a session, or if it's the leader... */ 402 if (!(CurrentProcess->Flags & PSF_PROCESS_IN_SESSION_BIT) || 403 (CurrentProcess->Vm.Flags.SessionLeader)) 404 { 405 /* Then there's nothing to do */ 406 return; 407 } 408 409 /* Sanity check */ 410 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); 411 412 /* Acquire the expansion lock while touching the session */ 413 OldIrql = MiAcquireExpansionLock(); 414 415 /* Remove the process from the list */ 416 RemoveEntryList(&CurrentProcess->SessionProcessLinks); 417 418 /* Release the lock again */ 419 MiReleaseExpansionLock(OldIrql); 420 421 /* Dereference the session */ 422 MiDereferenceSession(); 423 } 424 425 VOID 426 NTAPI 427 MiSessionAddProcess(IN PEPROCESS NewProcess) 428 { 429 PMM_SESSION_SPACE SessionGlobal; 430 KIRQL OldIrql; 431 432 /* The current process must already be in a session */ 433 if (!(PsGetCurrentProcess()->Flags & PSF_PROCESS_IN_SESSION_BIT)) return; 434 435 /* Sanity check */ 436 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); 437 438 /* Get the global session */ 439 SessionGlobal = MmSessionSpace->GlobalVirtualAddress; 440 441 /* Increment counters */ 442 InterlockedIncrement((PLONG)&SessionGlobal->ReferenceCount); 443 InterlockedIncrement(&SessionGlobal->ResidentProcessCount); 444 InterlockedIncrement(&SessionGlobal->ProcessReferenceToSession); 445 446 /* Set the session pointer */ 447 ASSERT(NewProcess->Session == NULL); 448 NewProcess->Session = SessionGlobal; 449 450 /* Acquire the expansion lock while touching the session */ 451 OldIrql = MiAcquireExpansionLock(); 452 453 /* Insert it into the process list */ 454 InsertTailList(&SessionGlobal->ProcessList, &NewProcess->SessionProcessLinks); 455 456 /* Release the lock again */ 457 MiReleaseExpansionLock(OldIrql); 458 459 /* Set the flag */ 460 PspSetProcessFlag(NewProcess, PSF_PROCESS_IN_SESSION_BIT); 461 } 462 463 NTSTATUS 464 NTAPI 465 MiSessionInitializeWorkingSetList(VOID) 466 { 467 KIRQL OldIrql; 468 PMMPTE PointerPte; 469 PMMPDE PointerPde; 470 MMPTE TempPte; 471 MMPDE TempPde; 472 ULONG Color, Index; 473 PFN_NUMBER PageFrameIndex; 474 PMM_SESSION_SPACE SessionGlobal; 475 BOOLEAN AllocatedPageTable; 476 PMMWSL WorkingSetList; 477 478 /* Get pointers to session global and the session working set list */ 479 SessionGlobal = MmSessionSpace->GlobalVirtualAddress; 480 WorkingSetList = (PMMWSL)MiSessionSpaceWs; 481 482 /* Fill out the two pointers */ 483 MmSessionSpace->Vm.VmWorkingSetList = WorkingSetList; 484 MmSessionSpace->Wsle = (PMMWSLE)WorkingSetList->UsedPageTableEntries; 485 486 /* Get the PDE for the working set, and check if it's already allocated */ 487 PointerPde = MiAddressToPde(WorkingSetList); 488 if (PointerPde->u.Hard.Valid == 1) 489 { 490 /* Nope, we'll have to do it */ 491 #ifndef _M_ARM 492 ASSERT(PointerPde->u.Hard.Global == 0); 493 #endif 494 AllocatedPageTable = FALSE; 495 } 496 else 497 { 498 /* Yep, that makes our job easier */ 499 AllocatedPageTable = TRUE; 500 } 501 502 /* Get the PTE for the working set */ 503 PointerPte = MiAddressToPte(WorkingSetList); 504 505 /* Initialize the working set lock, and lock the PFN database */ 506 ExInitializePushLock(&SessionGlobal->Vm.WorkingSetMutex); 507 //MmLockPageableSectionByHandle(ExPageLockHandle); 508 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 509 510 /* Check if we need a page table */ 511 if (AllocatedPageTable != FALSE) 512 { 513 /* Get a zeroed colored zero page */ 514 MI_SET_USAGE(MI_USAGE_INIT_MEMORY); 515 Color = MI_GET_NEXT_COLOR(); 516 PageFrameIndex = MiRemoveZeroPageSafe(Color); 517 if (!PageFrameIndex) 518 { 519 /* No zero pages, grab a free one */ 520 PageFrameIndex = MiRemoveAnyPage(Color); 521 522 /* Zero it outside the PFN lock */ 523 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 524 MiZeroPhysicalPage(PageFrameIndex); 525 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 526 } 527 528 /* Write a valid PDE for it */ 529 TempPde = ValidKernelPdeLocal; 530 TempPde.u.Hard.PageFrameNumber = PageFrameIndex; 531 MI_WRITE_VALID_PDE(PointerPde, TempPde); 532 533 /* Add this into the list */ 534 Index = ((ULONG_PTR)WorkingSetList - (ULONG_PTR)MmSessionBase) >> 22; 535 #ifndef _M_AMD64 536 MmSessionSpace->PageTables[Index] = TempPde; 537 #endif 538 /* Initialize the page directory page, and now zero the working set list itself */ 539 MiInitializePfnForOtherProcess(PageFrameIndex, 540 PointerPde, 541 MmSessionSpace->SessionPageDirectoryIndex); 542 KeZeroPages(PointerPte, PAGE_SIZE); 543 } 544 545 /* Get a zeroed colored zero page */ 546 MI_SET_USAGE(MI_USAGE_INIT_MEMORY); 547 Color = MI_GET_NEXT_COLOR(); 548 PageFrameIndex = MiRemoveZeroPageSafe(Color); 549 if (!PageFrameIndex) 550 { 551 /* No zero pages, grab a free one */ 552 PageFrameIndex = MiRemoveAnyPage(Color); 553 554 /* Zero it outside the PFN lock */ 555 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 556 MiZeroPhysicalPage(PageFrameIndex); 557 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 558 } 559 560 /* Write a valid PTE for it */ 561 TempPte = ValidKernelPteLocal; 562 MI_MAKE_DIRTY_PAGE(&TempPte); 563 TempPte.u.Hard.PageFrameNumber = PageFrameIndex; 564 565 /* Initialize the working set list page */ 566 MiInitializePfnAndMakePteValid(PageFrameIndex, PointerPte, TempPte); 567 568 /* Now we can release the PFN database lock */ 569 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 570 571 /* Fill out the working set structure */ 572 MmSessionSpace->Vm.Flags.SessionSpace = 1; 573 MmSessionSpace->Vm.MinimumWorkingSetSize = 20; 574 MmSessionSpace->Vm.MaximumWorkingSetSize = 384; 575 WorkingSetList->LastEntry = 20; 576 WorkingSetList->HashTable = NULL; 577 WorkingSetList->HashTableSize = 0; 578 WorkingSetList->Wsle = MmSessionSpace->Wsle; 579 580 /* Acquire the expansion lock while touching the session */ 581 OldIrql = MiAcquireExpansionLock(); 582 583 /* Handle list insertions */ 584 ASSERT(SessionGlobal->WsListEntry.Flink == NULL); 585 ASSERT(SessionGlobal->WsListEntry.Blink == NULL); 586 InsertTailList(&MiSessionWsList, &SessionGlobal->WsListEntry); 587 588 ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Flink == NULL); 589 ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Blink == NULL); 590 InsertTailList(&MmWorkingSetExpansionHead, 591 &SessionGlobal->Vm.WorkingSetExpansionLinks); 592 593 /* Release the lock again */ 594 MiReleaseExpansionLock(OldIrql); 595 596 /* All done, return */ 597 //MmUnlockPageableImageSection(ExPageLockHandle); 598 return STATUS_SUCCESS; 599 } 600 601 NTSTATUS 602 NTAPI 603 MiSessionCreateInternal(OUT PULONG SessionId) 604 { 605 PEPROCESS Process = PsGetCurrentProcess(); 606 ULONG NewFlags, Flags, Size, i, Color; 607 KIRQL OldIrql; 608 PMMPTE PointerPte, SessionPte; 609 PMMPDE PointerPde, PageTables; 610 PMM_SESSION_SPACE SessionGlobal; 611 MMPTE TempPte; 612 MMPDE TempPde; 613 NTSTATUS Status; 614 BOOLEAN Result; 615 PFN_NUMBER SessionPageDirIndex; 616 PFN_NUMBER TagPage[MI_SESSION_TAG_PAGES_MAXIMUM]; 617 PFN_NUMBER DataPage[MI_SESSION_DATA_PAGES_MAXIMUM]; 618 619 /* This should not exist yet */ 620 ASSERT(MmIsAddressValid(MmSessionSpace) == FALSE); 621 622 /* Loop so we can set the session-is-creating flag */ 623 Flags = Process->Flags; 624 while (TRUE) 625 { 626 /* Check if it's already set */ 627 if (Flags & PSF_SESSION_CREATION_UNDERWAY_BIT) 628 { 629 /* Bail out */ 630 DPRINT1("Lost session race\n"); 631 return STATUS_ALREADY_COMMITTED; 632 } 633 634 /* Now try to set it */ 635 NewFlags = InterlockedCompareExchange((PLONG)&Process->Flags, 636 Flags | PSF_SESSION_CREATION_UNDERWAY_BIT, 637 Flags); 638 if (NewFlags == Flags) break; 639 640 /* It changed, try again */ 641 Flags = NewFlags; 642 } 643 644 /* Now we should own the flag */ 645 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT); 646 647 /* 648 * Session space covers everything from 0xA0000000 to 0xC0000000. 649 * Allocate enough page tables to describe the entire region 650 */ 651 Size = (0x20000000 / PDE_MAPPED_VA) * sizeof(MMPTE); 652 PageTables = ExAllocatePoolWithTag(NonPagedPool, Size, 'tHmM'); 653 ASSERT(PageTables != NULL); 654 RtlZeroMemory(PageTables, Size); 655 656 /* Lock the session ID creation mutex */ 657 KeAcquireGuardedMutex(&MiSessionIdMutex); 658 659 /* Allocate a new Session ID */ 660 *SessionId = RtlFindClearBitsAndSet(MiSessionIdBitmap, 1, 0); 661 if (*SessionId == 0xFFFFFFFF) 662 { 663 /* We ran out of session IDs, we should expand */ 664 DPRINT1("Too many sessions created. Expansion not yet supported\n"); 665 ExFreePoolWithTag(PageTables, 'tHmM'); 666 return STATUS_NO_MEMORY; 667 } 668 669 /* Unlock the session ID creation mutex */ 670 KeReleaseGuardedMutex(&MiSessionIdMutex); 671 672 /* Reserve the global PTEs */ 673 SessionPte = MiReserveSystemPtes(MiSessionDataPages, SystemPteSpace); 674 ASSERT(SessionPte != NULL); 675 676 /* Acquire the PFN lock while we set everything up */ 677 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 678 679 /* Loop the global PTEs */ 680 TempPte = ValidKernelPte; 681 for (i = 0; i < MiSessionDataPages; i++) 682 { 683 /* Get a zeroed colored zero page */ 684 MI_SET_USAGE(MI_USAGE_INIT_MEMORY); 685 Color = MI_GET_NEXT_COLOR(); 686 DataPage[i] = MiRemoveZeroPageSafe(Color); 687 if (!DataPage[i]) 688 { 689 /* No zero pages, grab a free one */ 690 DataPage[i] = MiRemoveAnyPage(Color); 691 692 /* Zero it outside the PFN lock */ 693 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 694 MiZeroPhysicalPage(DataPage[i]); 695 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 696 } 697 698 /* Fill the PTE out */ 699 TempPte.u.Hard.PageFrameNumber = DataPage[i]; 700 MI_WRITE_VALID_PTE(SessionPte + i, TempPte); 701 } 702 703 /* Set the pointer to global space */ 704 SessionGlobal = MiPteToAddress(SessionPte); 705 706 /* Get a zeroed colored zero page */ 707 MI_SET_USAGE(MI_USAGE_INIT_MEMORY); 708 Color = MI_GET_NEXT_COLOR(); 709 SessionPageDirIndex = MiRemoveZeroPageSafe(Color); 710 if (!SessionPageDirIndex) 711 { 712 /* No zero pages, grab a free one */ 713 SessionPageDirIndex = MiRemoveAnyPage(Color); 714 715 /* Zero it outside the PFN lock */ 716 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 717 MiZeroPhysicalPage(SessionPageDirIndex); 718 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 719 } 720 721 /* Fill the PTE out */ 722 TempPde = ValidKernelPdeLocal; 723 TempPde.u.Hard.PageFrameNumber = SessionPageDirIndex; 724 725 /* Setup, allocate, fill out the MmSessionSpace PTE */ 726 PointerPde = MiAddressToPde(MmSessionSpace); 727 ASSERT(PointerPde->u.Long == 0); 728 MI_WRITE_VALID_PDE(PointerPde, TempPde); 729 MiInitializePfnForOtherProcess(SessionPageDirIndex, 730 PointerPde, 731 SessionPageDirIndex); 732 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex)->u1.WsIndex == 0); 733 734 /* Loop all the local PTEs for it */ 735 TempPte = ValidKernelPteLocal; 736 PointerPte = MiAddressToPte(MmSessionSpace); 737 for (i = 0; i < MiSessionDataPages; i++) 738 { 739 /* And fill them out */ 740 TempPte.u.Hard.PageFrameNumber = DataPage[i]; 741 MiInitializePfnAndMakePteValid(DataPage[i], PointerPte + i, TempPte); 742 ASSERT(MI_PFN_ELEMENT(DataPage[i])->u1.WsIndex == 0); 743 } 744 745 /* Finally loop all of the session pool tag pages */ 746 for (i = 0; i < MiSessionTagPages; i++) 747 { 748 /* Grab a zeroed colored page */ 749 MI_SET_USAGE(MI_USAGE_INIT_MEMORY); 750 Color = MI_GET_NEXT_COLOR(); 751 TagPage[i] = MiRemoveZeroPageSafe(Color); 752 if (!TagPage[i]) 753 { 754 /* No zero pages, grab a free one */ 755 TagPage[i] = MiRemoveAnyPage(Color); 756 757 /* Zero it outside the PFN lock */ 758 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 759 MiZeroPhysicalPage(TagPage[i]); 760 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 761 } 762 763 /* Fill the PTE out */ 764 TempPte.u.Hard.PageFrameNumber = TagPage[i]; 765 MiInitializePfnAndMakePteValid(TagPage[i], 766 PointerPte + MiSessionDataPages + i, 767 TempPte); 768 } 769 770 /* PTEs have been setup, release the PFN lock */ 771 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 772 773 /* Fill out the session space structure now */ 774 MmSessionSpace->GlobalVirtualAddress = SessionGlobal; 775 MmSessionSpace->ReferenceCount = 1; 776 MmSessionSpace->ResidentProcessCount = 1; 777 MmSessionSpace->u.LongFlags = 0; 778 MmSessionSpace->SessionId = *SessionId; 779 MmSessionSpace->LocaleId = PsDefaultSystemLocaleId; 780 MmSessionSpace->SessionPageDirectoryIndex = SessionPageDirIndex; 781 MmSessionSpace->Color = Color; 782 MmSessionSpace->NonPageablePages = MiSessionCreateCharge; 783 MmSessionSpace->CommittedPages = MiSessionCreateCharge; 784 #ifndef _M_AMD64 785 MmSessionSpace->PageTables = PageTables; 786 MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde; 787 #endif 788 InitializeListHead(&MmSessionSpace->ImageList); 789 DPRINT1("Session %lu is ready to go: 0x%p 0x%p, %lx 0x%p\n", 790 *SessionId, MmSessionSpace, SessionGlobal, SessionPageDirIndex, PageTables); 791 792 /* Initialize session pool */ 793 //Status = MiInitializeSessionPool(); 794 Status = STATUS_SUCCESS; 795 ASSERT(NT_SUCCESS(Status) == TRUE); 796 797 /* Initialize system space */ 798 Result = MiInitializeSystemSpaceMap(&SessionGlobal->Session); 799 ASSERT(Result == TRUE); 800 801 /* Initialize the process list, make sure the workign set list is empty */ 802 ASSERT(SessionGlobal->WsListEntry.Flink == NULL); 803 ASSERT(SessionGlobal->WsListEntry.Blink == NULL); 804 InitializeListHead(&SessionGlobal->ProcessList); 805 806 /* We're done, clear the flag */ 807 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT); 808 PspClearProcessFlag(Process, PSF_SESSION_CREATION_UNDERWAY_BIT); 809 810 /* Insert the process into the session */ 811 ASSERT(Process->Session == NULL); 812 ASSERT(SessionGlobal->ProcessReferenceToSession == 0); 813 SessionGlobal->ProcessReferenceToSession = 1; 814 815 /* We're done */ 816 InterlockedIncrement(&MmSessionDataPages); 817 return STATUS_SUCCESS; 818 } 819 820 NTSTATUS 821 NTAPI 822 MmSessionCreate(OUT PULONG SessionId) 823 { 824 PEPROCESS Process = PsGetCurrentProcess(); 825 ULONG SessionLeaderExists; 826 NTSTATUS Status; 827 828 /* Fail if the process is already in a session */ 829 if (Process->Flags & PSF_PROCESS_IN_SESSION_BIT) 830 { 831 DPRINT1("Process already in session\n"); 832 return STATUS_ALREADY_COMMITTED; 833 } 834 835 /* Check if the process is already the session leader */ 836 if (!Process->Vm.Flags.SessionLeader) 837 { 838 /* Atomically set it as the leader */ 839 SessionLeaderExists = InterlockedCompareExchange(&MiSessionLeaderExists, 1, 0); 840 if (SessionLeaderExists) 841 { 842 DPRINT1("Session leader race\n"); 843 return STATUS_INVALID_SYSTEM_SERVICE; 844 } 845 846 /* Do the work required to upgrade him */ 847 MiSessionLeader(Process); 848 } 849 850 /* Create the session */ 851 KeEnterCriticalRegion(); 852 Status = MiSessionCreateInternal(SessionId); 853 if (!NT_SUCCESS(Status)) 854 { 855 KeLeaveCriticalRegion(); 856 return Status; 857 } 858 859 /* Set up the session working set */ 860 Status = MiSessionInitializeWorkingSetList(); 861 if (!NT_SUCCESS(Status)) 862 { 863 /* Fail */ 864 //MiDereferenceSession(); 865 ASSERT(FALSE); 866 KeLeaveCriticalRegion(); 867 return Status; 868 } 869 870 /* All done */ 871 KeLeaveCriticalRegion(); 872 873 /* Set and assert the flags, and return */ 874 MmSessionSpace->u.Flags.Initialized = 1; 875 PspSetProcessFlag(Process, PSF_PROCESS_IN_SESSION_BIT); 876 ASSERT(MiSessionLeaderExists == 1); 877 return Status; 878 } 879 880 NTSTATUS 881 NTAPI 882 MmSessionDelete(IN ULONG SessionId) 883 { 884 PEPROCESS Process = PsGetCurrentProcess(); 885 886 /* Process must be in a session */ 887 if (!(Process->Flags & PSF_PROCESS_IN_SESSION_BIT)) 888 { 889 DPRINT1("Not in a session!\n"); 890 return STATUS_UNABLE_TO_FREE_VM; 891 } 892 893 /* It must be the session leader */ 894 if (!Process->Vm.Flags.SessionLeader) 895 { 896 DPRINT1("Not a session leader!\n"); 897 return STATUS_UNABLE_TO_FREE_VM; 898 } 899 900 /* Remove one reference count */ 901 KeEnterCriticalRegion(); 902 /* FIXME: Do it */ 903 KeLeaveCriticalRegion(); 904 905 /* All done */ 906 return STATUS_SUCCESS; 907 } 908 909 _IRQL_requires_max_(APC_LEVEL) 910 NTSTATUS 911 NTAPI 912 MmAttachSession( 913 _Inout_ PVOID SessionEntry, 914 _Out_ PKAPC_STATE ApcState) 915 { 916 PEPROCESS EntryProcess; 917 PMM_SESSION_SPACE EntrySession, CurrentSession; 918 PEPROCESS CurrentProcess; 919 KIRQL OldIrql; 920 921 /* The parameter is the actual process! */ 922 EntryProcess = SessionEntry; 923 ASSERT(EntryProcess != NULL); 924 925 /* Sanity checks */ 926 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 927 ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0); 928 929 /* Get the session from the process that was passed in */ 930 EntrySession = EntryProcess->Session; 931 ASSERT(EntrySession != NULL); 932 933 /* Get the current process and it's session */ 934 CurrentProcess = PsGetCurrentProcess(); 935 CurrentSession = CurrentProcess->Session; 936 937 /* Acquire the expansion lock while touching the session */ 938 OldIrql = MiAcquireExpansionLock(); 939 940 /* Check if the session is about to be deleted */ 941 if (EntrySession->u.Flags.DeletePending) 942 { 943 /* We cannot attach to it, so unlock and fail */ 944 MiReleaseExpansionLock(OldIrql); 945 return STATUS_PROCESS_IS_TERMINATING; 946 } 947 948 /* Count the number of attaches */ 949 EntrySession->AttachCount++; 950 951 /* we can release the lock again */ 952 MiReleaseExpansionLock(OldIrql); 953 954 /* Check if we are not the session leader and we are in a session */ 955 if (!CurrentProcess->Vm.Flags.SessionLeader && (CurrentSession != NULL)) 956 { 957 /* Are we already in the right session? */ 958 if (CurrentSession == EntrySession) 959 { 960 /* We are, so "attach" to the current process */ 961 EntryProcess = CurrentProcess; 962 } 963 else 964 { 965 /* We are not, the session id should better not match! */ 966 ASSERT(CurrentSession->SessionId != EntrySession->SessionId); 967 } 968 } 969 970 /* Now attach to the process that we have */ 971 KeStackAttachProcess(&EntryProcess->Pcb, ApcState); 972 973 /* Success! */ 974 return STATUS_SUCCESS; 975 } 976 977 _IRQL_requires_max_(APC_LEVEL) 978 VOID 979 NTAPI 980 MmDetachSession( 981 _Inout_ PVOID SessionEntry, 982 _In_ PKAPC_STATE ApcState) 983 { 984 PEPROCESS EntryProcess; 985 PMM_SESSION_SPACE EntrySession; 986 KIRQL OldIrql; 987 BOOLEAN DeletePending; 988 989 /* The parameter is the actual process! */ 990 EntryProcess = SessionEntry; 991 ASSERT(EntryProcess != NULL); 992 993 /* Sanity checks */ 994 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 995 ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0); 996 997 /* Get the session from the process that was passed in */ 998 EntrySession = EntryProcess->Session; 999 ASSERT(EntrySession != NULL); 1000 1001 /* Acquire the expansion lock while touching the session */ 1002 OldIrql = MiAcquireExpansionLock(); 1003 1004 /* Make sure we have at least one attach and decrement the count */ 1005 ASSERT(EntrySession->AttachCount >= 1); 1006 EntrySession->AttachCount--; 1007 1008 /* Remember if a delete is pending and we were the last one attached */ 1009 DeletePending = EntrySession->u.Flags.DeletePending && 1010 (EntrySession->AttachCount == 0); 1011 1012 /* Release the lock again */ 1013 MiReleaseExpansionLock(OldIrql); 1014 1015 /* Detach from the process */ 1016 KeUnstackDetachProcess(ApcState); 1017 1018 /* Check if we need to set the attach event */ 1019 if (DeletePending) 1020 KeSetEvent(&EntrySession->AttachEvent, IO_NO_INCREMENT, FALSE); 1021 } 1022 1023 VOID 1024 NTAPI 1025 MmQuitNextSession( 1026 _Inout_ PVOID SessionEntry) 1027 { 1028 PEPROCESS EntryProcess; 1029 1030 /* The parameter is the actual process! */ 1031 EntryProcess = SessionEntry; 1032 ASSERT(EntryProcess != NULL); 1033 1034 /* Sanity checks */ 1035 ASSERT(KeGetCurrentIrql () <= APC_LEVEL); 1036 ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0); 1037 ASSERT(EntryProcess->Session != NULL); 1038 1039 /* Get rid of the reference we took */ 1040 ObDereferenceObject(EntryProcess); 1041 } 1042 1043 PVOID 1044 NTAPI 1045 MmGetSessionById( 1046 _In_ ULONG SessionId) 1047 { 1048 PLIST_ENTRY ListEntry; 1049 PMM_SESSION_SPACE Session; 1050 PEPROCESS Process = NULL; 1051 KIRQL OldIrql; 1052 1053 /* Acquire the expansion lock while touching the session */ 1054 OldIrql = MiAcquireExpansionLock(); 1055 1056 /* Loop all entries in the session ws list */ 1057 ListEntry = MiSessionWsList.Flink; 1058 while (ListEntry != &MiSessionWsList) 1059 { 1060 Session = CONTAINING_RECORD(ListEntry, MM_SESSION_SPACE, WsListEntry); 1061 ListEntry = ListEntry->Flink; 1062 1063 /* Check if this is the session we are looking for */ 1064 if (Session->SessionId == SessionId) 1065 { 1066 /* Check if we also have a process in the process list */ 1067 if (!IsListEmpty(&Session->ProcessList)) 1068 { 1069 Process = CONTAINING_RECORD(Session->ProcessList.Flink, 1070 EPROCESS, 1071 SessionProcessLinks); 1072 1073 /* Reference the process */ 1074 ObReferenceObject(Process); 1075 break; 1076 } 1077 } 1078 } 1079 1080 /* Release the lock again */ 1081 MiReleaseExpansionLock(OldIrql); 1082 1083 return Process; 1084 } 1085