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