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 = MiAcquirePfnLock(); 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 MiReleasePfnLock(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 = MiAcquirePfnLock(); 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 MiReleasePfnLock(OldIrql); 524 MiZeroPhysicalPage(PageFrameIndex); 525 OldIrql = MiAcquirePfnLock(); 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 MiReleasePfnLock(OldIrql); 556 MiZeroPhysicalPage(PageFrameIndex); 557 OldIrql = MiAcquirePfnLock(); 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 MiReleasePfnLock(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, i, Color; 607 #if (_MI_PAGING_LEVELS < 3) 608 ULONG Size; 609 #endif // (_MI_PAGING_LEVELS < 3) 610 PMMPDE PageTables = NULL; 611 KIRQL OldIrql; 612 PMMPTE PointerPte, SessionPte; 613 PMMPDE PointerPde; 614 PMM_SESSION_SPACE SessionGlobal; 615 MMPTE TempPte; 616 MMPDE TempPde; 617 NTSTATUS Status; 618 BOOLEAN Result; 619 PFN_NUMBER SessionPageDirIndex; 620 PFN_NUMBER TagPage[MI_SESSION_TAG_PAGES_MAXIMUM]; 621 PFN_NUMBER DataPage[MI_SESSION_DATA_PAGES_MAXIMUM]; 622 623 /* This should not exist yet */ 624 ASSERT(MmIsAddressValid(MmSessionSpace) == FALSE); 625 626 /* Loop so we can set the session-is-creating flag */ 627 Flags = Process->Flags; 628 while (TRUE) 629 { 630 /* Check if it's already set */ 631 if (Flags & PSF_SESSION_CREATION_UNDERWAY_BIT) 632 { 633 /* Bail out */ 634 DPRINT1("Lost session race\n"); 635 return STATUS_ALREADY_COMMITTED; 636 } 637 638 /* Now try to set it */ 639 NewFlags = InterlockedCompareExchange((PLONG)&Process->Flags, 640 Flags | PSF_SESSION_CREATION_UNDERWAY_BIT, 641 Flags); 642 if (NewFlags == Flags) break; 643 644 /* It changed, try again */ 645 Flags = NewFlags; 646 } 647 648 /* Now we should own the flag */ 649 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT); 650 651 #if (_MI_PAGING_LEVELS < 3) 652 /* 653 * Session space covers everything from 0xA0000000 to 0xC0000000. 654 * Allocate enough page tables to describe the entire region 655 */ 656 Size = (0x20000000 / PDE_MAPPED_VA) * sizeof(MMPTE); 657 PageTables = ExAllocatePoolWithTag(NonPagedPool, Size, 'tHmM'); 658 ASSERT(PageTables != NULL); 659 RtlZeroMemory(PageTables, Size); 660 #endif // (_MI_PAGING_LEVELS < 3) 661 662 /* Lock the session ID creation mutex */ 663 KeAcquireGuardedMutex(&MiSessionIdMutex); 664 665 /* Allocate a new Session ID */ 666 *SessionId = RtlFindClearBitsAndSet(MiSessionIdBitmap, 1, 0); 667 if (*SessionId == 0xFFFFFFFF) 668 { 669 /* We ran out of session IDs, we should expand */ 670 DPRINT1("Too many sessions created. Expansion not yet supported\n"); 671 #if (_MI_PAGING_LEVELS < 3) 672 ExFreePoolWithTag(PageTables, 'tHmM'); 673 #endif // (_MI_PAGING_LEVELS < 3) 674 return STATUS_NO_MEMORY; 675 } 676 677 /* Unlock the session ID creation mutex */ 678 KeReleaseGuardedMutex(&MiSessionIdMutex); 679 680 /* Reserve the global PTEs */ 681 SessionPte = MiReserveSystemPtes(MiSessionDataPages, SystemPteSpace); 682 ASSERT(SessionPte != NULL); 683 684 /* Acquire the PFN lock while we set everything up */ 685 OldIrql = MiAcquirePfnLock(); 686 687 /* Loop the global PTEs */ 688 TempPte = ValidKernelPte; 689 for (i = 0; i < MiSessionDataPages; i++) 690 { 691 /* Get a zeroed colored zero page */ 692 MI_SET_USAGE(MI_USAGE_INIT_MEMORY); 693 Color = MI_GET_NEXT_COLOR(); 694 DataPage[i] = MiRemoveZeroPageSafe(Color); 695 if (!DataPage[i]) 696 { 697 /* No zero pages, grab a free one */ 698 DataPage[i] = MiRemoveAnyPage(Color); 699 700 /* Zero it outside the PFN lock */ 701 MiReleasePfnLock(OldIrql); 702 MiZeroPhysicalPage(DataPage[i]); 703 OldIrql = MiAcquirePfnLock(); 704 } 705 706 /* Fill the PTE out */ 707 TempPte.u.Hard.PageFrameNumber = DataPage[i]; 708 MI_WRITE_VALID_PTE(SessionPte + i, TempPte); 709 } 710 711 /* Set the pointer to global space */ 712 SessionGlobal = MiPteToAddress(SessionPte); 713 714 /* Get a zeroed colored zero page */ 715 MI_SET_USAGE(MI_USAGE_INIT_MEMORY); 716 Color = MI_GET_NEXT_COLOR(); 717 SessionPageDirIndex = MiRemoveZeroPageSafe(Color); 718 if (!SessionPageDirIndex) 719 { 720 /* No zero pages, grab a free one */ 721 SessionPageDirIndex = MiRemoveAnyPage(Color); 722 723 /* Zero it outside the PFN lock */ 724 MiReleasePfnLock(OldIrql); 725 MiZeroPhysicalPage(SessionPageDirIndex); 726 OldIrql = MiAcquirePfnLock(); 727 } 728 729 /* Fill the PTE out */ 730 TempPde = ValidKernelPdeLocal; 731 TempPde.u.Hard.PageFrameNumber = SessionPageDirIndex; 732 733 /* Setup, allocate, fill out the MmSessionSpace PTE */ 734 PointerPde = MiAddressToPde(MmSessionSpace); 735 ASSERT(PointerPde->u.Long == 0); 736 MI_WRITE_VALID_PDE(PointerPde, TempPde); 737 MiInitializePfnForOtherProcess(SessionPageDirIndex, 738 PointerPde, 739 SessionPageDirIndex); 740 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex)->u1.WsIndex == 0); 741 742 /* Loop all the local PTEs for it */ 743 TempPte = ValidKernelPteLocal; 744 PointerPte = MiAddressToPte(MmSessionSpace); 745 for (i = 0; i < MiSessionDataPages; i++) 746 { 747 /* And fill them out */ 748 TempPte.u.Hard.PageFrameNumber = DataPage[i]; 749 MiInitializePfnAndMakePteValid(DataPage[i], PointerPte + i, TempPte); 750 ASSERT(MI_PFN_ELEMENT(DataPage[i])->u1.WsIndex == 0); 751 } 752 753 /* Finally loop all of the session pool tag pages */ 754 for (i = 0; i < MiSessionTagPages; i++) 755 { 756 /* Grab a zeroed colored page */ 757 MI_SET_USAGE(MI_USAGE_INIT_MEMORY); 758 Color = MI_GET_NEXT_COLOR(); 759 TagPage[i] = MiRemoveZeroPageSafe(Color); 760 if (!TagPage[i]) 761 { 762 /* No zero pages, grab a free one */ 763 TagPage[i] = MiRemoveAnyPage(Color); 764 765 /* Zero it outside the PFN lock */ 766 MiReleasePfnLock(OldIrql); 767 MiZeroPhysicalPage(TagPage[i]); 768 OldIrql = MiAcquirePfnLock(); 769 } 770 771 /* Fill the PTE out */ 772 TempPte.u.Hard.PageFrameNumber = TagPage[i]; 773 MiInitializePfnAndMakePteValid(TagPage[i], 774 PointerPte + MiSessionDataPages + i, 775 TempPte); 776 } 777 778 /* PTEs have been setup, release the PFN lock */ 779 MiReleasePfnLock(OldIrql); 780 781 /* Fill out the session space structure now */ 782 MmSessionSpace->GlobalVirtualAddress = SessionGlobal; 783 MmSessionSpace->ReferenceCount = 1; 784 MmSessionSpace->ResidentProcessCount = 1; 785 MmSessionSpace->u.LongFlags = 0; 786 MmSessionSpace->SessionId = *SessionId; 787 MmSessionSpace->LocaleId = PsDefaultSystemLocaleId; 788 MmSessionSpace->SessionPageDirectoryIndex = SessionPageDirIndex; 789 MmSessionSpace->Color = Color; 790 MmSessionSpace->NonPageablePages = MiSessionCreateCharge; 791 MmSessionSpace->CommittedPages = MiSessionCreateCharge; 792 #ifndef _M_AMD64 793 MmSessionSpace->PageTables = PageTables; 794 MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde; 795 #endif 796 InitializeListHead(&MmSessionSpace->ImageList); 797 798 DPRINT1("Session %lu is ready to go: 0x%p 0x%p, %lx 0x%p\n", 799 *SessionId, MmSessionSpace, SessionGlobal, SessionPageDirIndex, PageTables); 800 801 /* Initialize session pool */ 802 //Status = MiInitializeSessionPool(); 803 Status = STATUS_SUCCESS; 804 ASSERT(NT_SUCCESS(Status) == TRUE); 805 806 /* Initialize system space */ 807 Result = MiInitializeSystemSpaceMap(&SessionGlobal->Session); 808 ASSERT(Result == TRUE); 809 810 /* Initialize the process list, make sure the workign set list is empty */ 811 ASSERT(SessionGlobal->WsListEntry.Flink == NULL); 812 ASSERT(SessionGlobal->WsListEntry.Blink == NULL); 813 InitializeListHead(&SessionGlobal->ProcessList); 814 815 /* We're done, clear the flag */ 816 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT); 817 PspClearProcessFlag(Process, PSF_SESSION_CREATION_UNDERWAY_BIT); 818 819 /* Insert the process into the session */ 820 ASSERT(Process->Session == NULL); 821 ASSERT(SessionGlobal->ProcessReferenceToSession == 0); 822 SessionGlobal->ProcessReferenceToSession = 1; 823 824 /* We're done */ 825 InterlockedIncrement(&MmSessionDataPages); 826 return STATUS_SUCCESS; 827 } 828 829 NTSTATUS 830 NTAPI 831 MmSessionCreate(OUT PULONG SessionId) 832 { 833 PEPROCESS Process = PsGetCurrentProcess(); 834 ULONG SessionLeaderExists; 835 NTSTATUS Status; 836 837 /* Fail if the process is already in a session */ 838 if (Process->Flags & PSF_PROCESS_IN_SESSION_BIT) 839 { 840 DPRINT1("Process already in session\n"); 841 return STATUS_ALREADY_COMMITTED; 842 } 843 844 /* Check if the process is already the session leader */ 845 if (!Process->Vm.Flags.SessionLeader) 846 { 847 /* Atomically set it as the leader */ 848 SessionLeaderExists = InterlockedCompareExchange(&MiSessionLeaderExists, 1, 0); 849 if (SessionLeaderExists) 850 { 851 DPRINT1("Session leader race\n"); 852 return STATUS_INVALID_SYSTEM_SERVICE; 853 } 854 855 /* Do the work required to upgrade him */ 856 MiSessionLeader(Process); 857 } 858 859 /* Create the session */ 860 KeEnterCriticalRegion(); 861 Status = MiSessionCreateInternal(SessionId); 862 if (!NT_SUCCESS(Status)) 863 { 864 KeLeaveCriticalRegion(); 865 return Status; 866 } 867 868 /* Set up the session working set */ 869 Status = MiSessionInitializeWorkingSetList(); 870 if (!NT_SUCCESS(Status)) 871 { 872 /* Fail */ 873 //MiDereferenceSession(); 874 ASSERT(FALSE); 875 KeLeaveCriticalRegion(); 876 return Status; 877 } 878 879 /* All done */ 880 KeLeaveCriticalRegion(); 881 882 /* Set and assert the flags, and return */ 883 MmSessionSpace->u.Flags.Initialized = 1; 884 PspSetProcessFlag(Process, PSF_PROCESS_IN_SESSION_BIT); 885 ASSERT(MiSessionLeaderExists == 1); 886 return Status; 887 } 888 889 NTSTATUS 890 NTAPI 891 MmSessionDelete(IN ULONG SessionId) 892 { 893 PEPROCESS Process = PsGetCurrentProcess(); 894 895 /* Process must be in a session */ 896 if (!(Process->Flags & PSF_PROCESS_IN_SESSION_BIT)) 897 { 898 DPRINT1("Not in a session!\n"); 899 return STATUS_UNABLE_TO_FREE_VM; 900 } 901 902 /* It must be the session leader */ 903 if (!Process->Vm.Flags.SessionLeader) 904 { 905 DPRINT1("Not a session leader!\n"); 906 return STATUS_UNABLE_TO_FREE_VM; 907 } 908 909 /* Remove one reference count */ 910 KeEnterCriticalRegion(); 911 /* FIXME: Do it */ 912 KeLeaveCriticalRegion(); 913 914 /* All done */ 915 return STATUS_SUCCESS; 916 } 917 918 _IRQL_requires_max_(APC_LEVEL) 919 NTSTATUS 920 NTAPI 921 MmAttachSession( 922 _Inout_ PVOID SessionEntry, 923 _Out_ PKAPC_STATE ApcState) 924 { 925 PEPROCESS EntryProcess; 926 PMM_SESSION_SPACE EntrySession, CurrentSession; 927 PEPROCESS CurrentProcess; 928 KIRQL OldIrql; 929 930 /* The parameter is the actual process! */ 931 EntryProcess = SessionEntry; 932 ASSERT(EntryProcess != NULL); 933 934 /* Sanity checks */ 935 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 936 ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0); 937 938 /* Get the session from the process that was passed in */ 939 EntrySession = EntryProcess->Session; 940 ASSERT(EntrySession != NULL); 941 942 /* Get the current process and it's session */ 943 CurrentProcess = PsGetCurrentProcess(); 944 CurrentSession = CurrentProcess->Session; 945 946 /* Acquire the expansion lock while touching the session */ 947 OldIrql = MiAcquireExpansionLock(); 948 949 /* Check if the session is about to be deleted */ 950 if (EntrySession->u.Flags.DeletePending) 951 { 952 /* We cannot attach to it, so unlock and fail */ 953 MiReleaseExpansionLock(OldIrql); 954 return STATUS_PROCESS_IS_TERMINATING; 955 } 956 957 /* Count the number of attaches */ 958 EntrySession->AttachCount++; 959 960 /* we can release the lock again */ 961 MiReleaseExpansionLock(OldIrql); 962 963 /* Check if we are not the session leader and we are in a session */ 964 if (!CurrentProcess->Vm.Flags.SessionLeader && (CurrentSession != NULL)) 965 { 966 /* Are we already in the right session? */ 967 if (CurrentSession == EntrySession) 968 { 969 /* We are, so "attach" to the current process */ 970 EntryProcess = CurrentProcess; 971 } 972 else 973 { 974 /* We are not, the session id should better not match! */ 975 ASSERT(CurrentSession->SessionId != EntrySession->SessionId); 976 } 977 } 978 979 /* Now attach to the process that we have */ 980 KeStackAttachProcess(&EntryProcess->Pcb, ApcState); 981 982 /* Success! */ 983 return STATUS_SUCCESS; 984 } 985 986 _IRQL_requires_max_(APC_LEVEL) 987 VOID 988 NTAPI 989 MmDetachSession( 990 _Inout_ PVOID SessionEntry, 991 _In_ PKAPC_STATE ApcState) 992 { 993 PEPROCESS EntryProcess; 994 PMM_SESSION_SPACE EntrySession; 995 KIRQL OldIrql; 996 BOOLEAN DeletePending; 997 998 /* The parameter is the actual process! */ 999 EntryProcess = SessionEntry; 1000 ASSERT(EntryProcess != NULL); 1001 1002 /* Sanity checks */ 1003 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 1004 ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0); 1005 1006 /* Get the session from the process that was passed in */ 1007 EntrySession = EntryProcess->Session; 1008 ASSERT(EntrySession != NULL); 1009 1010 /* Acquire the expansion lock while touching the session */ 1011 OldIrql = MiAcquireExpansionLock(); 1012 1013 /* Make sure we have at least one attach and decrement the count */ 1014 ASSERT(EntrySession->AttachCount >= 1); 1015 EntrySession->AttachCount--; 1016 1017 /* Remember if a delete is pending and we were the last one attached */ 1018 DeletePending = EntrySession->u.Flags.DeletePending && 1019 (EntrySession->AttachCount == 0); 1020 1021 /* Release the lock again */ 1022 MiReleaseExpansionLock(OldIrql); 1023 1024 /* Detach from the process */ 1025 KeUnstackDetachProcess(ApcState); 1026 1027 /* Check if we need to set the attach event */ 1028 if (DeletePending) 1029 KeSetEvent(&EntrySession->AttachEvent, IO_NO_INCREMENT, FALSE); 1030 } 1031 1032 VOID 1033 NTAPI 1034 MmQuitNextSession( 1035 _Inout_ PVOID SessionEntry) 1036 { 1037 PEPROCESS EntryProcess; 1038 1039 /* The parameter is the actual process! */ 1040 EntryProcess = SessionEntry; 1041 ASSERT(EntryProcess != NULL); 1042 1043 /* Sanity checks */ 1044 ASSERT(KeGetCurrentIrql () <= APC_LEVEL); 1045 ASSERT(EntryProcess->Vm.Flags.SessionLeader == 0); 1046 ASSERT(EntryProcess->Session != NULL); 1047 1048 /* Get rid of the reference we took */ 1049 ObDereferenceObject(EntryProcess); 1050 } 1051 1052 PVOID 1053 NTAPI 1054 MmGetSessionById( 1055 _In_ ULONG SessionId) 1056 { 1057 PLIST_ENTRY ListEntry; 1058 PMM_SESSION_SPACE Session; 1059 PEPROCESS Process = NULL; 1060 KIRQL OldIrql; 1061 1062 /* Acquire the expansion lock while touching the session */ 1063 OldIrql = MiAcquireExpansionLock(); 1064 1065 /* Loop all entries in the session ws list */ 1066 ListEntry = MiSessionWsList.Flink; 1067 while (ListEntry != &MiSessionWsList) 1068 { 1069 Session = CONTAINING_RECORD(ListEntry, MM_SESSION_SPACE, WsListEntry); 1070 ListEntry = ListEntry->Flink; 1071 1072 /* Check if this is the session we are looking for */ 1073 if (Session->SessionId == SessionId) 1074 { 1075 /* Check if we also have a process in the process list */ 1076 if (!IsListEmpty(&Session->ProcessList)) 1077 { 1078 Process = CONTAINING_RECORD(Session->ProcessList.Flink, 1079 EPROCESS, 1080 SessionProcessLinks); 1081 1082 /* Reference the process */ 1083 ObReferenceObject(Process); 1084 break; 1085 } 1086 } 1087 } 1088 1089 /* Release the lock again */ 1090 MiReleaseExpansionLock(OldIrql); 1091 1092 return Process; 1093 } 1094