1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * FILE: ntoskrnl/mm/ARM3/section.c 5 * PURPOSE: ARM Memory Manager Section Support 6 * PROGRAMMERS: ReactOS Portable Systems Group 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 #define MODULE_INVOLVED_IN_ARM3 16 #include <mm/ARM3/miarm.h> 17 18 /* GLOBALS ********************************************************************/ 19 20 ACCESS_MASK MmMakeSectionAccess[8] = 21 { 22 SECTION_MAP_READ, 23 SECTION_MAP_READ, 24 SECTION_MAP_EXECUTE, 25 SECTION_MAP_EXECUTE | SECTION_MAP_READ, 26 SECTION_MAP_WRITE, 27 SECTION_MAP_READ, 28 SECTION_MAP_EXECUTE | SECTION_MAP_WRITE, 29 SECTION_MAP_EXECUTE | SECTION_MAP_READ 30 }; 31 32 ACCESS_MASK MmMakeFileAccess[8] = 33 { 34 FILE_READ_DATA, 35 FILE_READ_DATA, 36 FILE_EXECUTE, 37 FILE_EXECUTE | FILE_READ_DATA, 38 FILE_WRITE_DATA | FILE_READ_DATA, 39 FILE_READ_DATA, 40 FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA, 41 FILE_EXECUTE | FILE_READ_DATA 42 }; 43 44 CHAR MmUserProtectionToMask1[16] = 45 { 46 0, 47 MM_NOACCESS, 48 MM_READONLY, 49 (CHAR)MM_INVALID_PROTECTION, 50 MM_READWRITE, 51 (CHAR)MM_INVALID_PROTECTION, 52 (CHAR)MM_INVALID_PROTECTION, 53 (CHAR)MM_INVALID_PROTECTION, 54 MM_WRITECOPY, 55 (CHAR)MM_INVALID_PROTECTION, 56 (CHAR)MM_INVALID_PROTECTION, 57 (CHAR)MM_INVALID_PROTECTION, 58 (CHAR)MM_INVALID_PROTECTION, 59 (CHAR)MM_INVALID_PROTECTION, 60 (CHAR)MM_INVALID_PROTECTION, 61 (CHAR)MM_INVALID_PROTECTION 62 }; 63 64 CHAR MmUserProtectionToMask2[16] = 65 { 66 0, 67 MM_EXECUTE, 68 MM_EXECUTE_READ, 69 (CHAR)MM_INVALID_PROTECTION, 70 MM_EXECUTE_READWRITE, 71 (CHAR)MM_INVALID_PROTECTION, 72 (CHAR)MM_INVALID_PROTECTION, 73 (CHAR)MM_INVALID_PROTECTION, 74 MM_EXECUTE_WRITECOPY, 75 (CHAR)MM_INVALID_PROTECTION, 76 (CHAR)MM_INVALID_PROTECTION, 77 (CHAR)MM_INVALID_PROTECTION, 78 (CHAR)MM_INVALID_PROTECTION, 79 (CHAR)MM_INVALID_PROTECTION, 80 (CHAR)MM_INVALID_PROTECTION, 81 (CHAR)MM_INVALID_PROTECTION 82 }; 83 84 ULONG MmCompatibleProtectionMask[8] = 85 { 86 PAGE_NOACCESS, 87 88 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY, 89 90 PAGE_NOACCESS | PAGE_EXECUTE, 91 92 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE | 93 PAGE_EXECUTE_READ, 94 95 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE, 96 97 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY, 98 99 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE | 100 PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | 101 PAGE_EXECUTE_WRITECOPY, 102 103 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE | 104 PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY 105 }; 106 107 MMSESSION MmSession; 108 KGUARDED_MUTEX MmSectionCommitMutex; 109 MM_AVL_TABLE MmSectionBasedRoot; 110 KGUARDED_MUTEX MmSectionBasedMutex; 111 PVOID MmHighSectionBase; 112 113 /* PRIVATE FUNCTIONS **********************************************************/ 114 115 BOOLEAN 116 NTAPI 117 MiIsProtectionCompatible(IN ULONG SectionPageProtection, 118 IN ULONG NewSectionPageProtection) 119 { 120 ULONG ProtectionMask, CompatibleMask; 121 122 /* Calculate the protection mask and make sure it's valid */ 123 ProtectionMask = MiMakeProtectionMask(SectionPageProtection); 124 if (ProtectionMask == MM_INVALID_PROTECTION) 125 { 126 DPRINT1("Invalid protection mask\n"); 127 return FALSE; 128 } 129 130 /* Calculate the compatible mask */ 131 CompatibleMask = MmCompatibleProtectionMask[ProtectionMask & 0x7] | 132 PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE; 133 134 /* See if the mapping protection is compatible with the create protection */ 135 return ((CompatibleMask | NewSectionPageProtection) == CompatibleMask); 136 } 137 138 ULONG 139 NTAPI 140 MiMakeProtectionMask(IN ULONG Protect) 141 { 142 ULONG Mask1, Mask2, ProtectMask; 143 144 /* PAGE_EXECUTE_WRITECOMBINE is theoretically the maximum */ 145 if (Protect >= (PAGE_WRITECOMBINE * 2)) return MM_INVALID_PROTECTION; 146 147 /* 148 * Windows API protection mask can be understood as two bitfields, differing 149 * by whether or not execute rights are being requested 150 */ 151 Mask1 = Protect & 0xF; 152 Mask2 = (Protect >> 4) & 0xF; 153 154 /* Check which field is there */ 155 if (!Mask1) 156 { 157 /* Mask2 must be there, use it to determine the PTE protection */ 158 if (!Mask2) return MM_INVALID_PROTECTION; 159 ProtectMask = MmUserProtectionToMask2[Mask2]; 160 } 161 else 162 { 163 /* Mask2 should not be there, use Mask1 to determine the PTE mask */ 164 if (Mask2) return MM_INVALID_PROTECTION; 165 ProtectMask = MmUserProtectionToMask1[Mask1]; 166 } 167 168 /* Make sure the final mask is a valid one */ 169 if (ProtectMask == MM_INVALID_PROTECTION) return MM_INVALID_PROTECTION; 170 171 /* Check for PAGE_GUARD option */ 172 if (Protect & PAGE_GUARD) 173 { 174 /* It's not valid on no-access, nocache, or writecombine pages */ 175 if ((ProtectMask == MM_NOACCESS) || 176 (Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE))) 177 { 178 /* Fail such requests */ 179 return MM_INVALID_PROTECTION; 180 } 181 182 /* This actually turns on guard page in this scenario! */ 183 ProtectMask |= MM_GUARDPAGE; 184 } 185 186 /* Check for nocache option */ 187 if (Protect & PAGE_NOCACHE) 188 { 189 /* The earlier check should've eliminated this possibility */ 190 ASSERT((Protect & PAGE_GUARD) == 0); 191 192 /* Check for no-access page or write combine page */ 193 if ((ProtectMask == MM_NOACCESS) || (Protect & PAGE_WRITECOMBINE)) 194 { 195 /* Such a request is invalid */ 196 return MM_INVALID_PROTECTION; 197 } 198 199 /* Add the PTE flag */ 200 ProtectMask |= MM_NOCACHE; 201 } 202 203 /* Check for write combine option */ 204 if (Protect & PAGE_WRITECOMBINE) 205 { 206 /* The two earlier scenarios should've caught this */ 207 ASSERT((Protect & (PAGE_GUARD | PAGE_NOACCESS)) == 0); 208 209 /* Don't allow on no-access pages */ 210 if (ProtectMask == MM_NOACCESS) return MM_INVALID_PROTECTION; 211 212 /* This actually turns on write-combine in this scenario! */ 213 ProtectMask |= MM_NOACCESS; 214 } 215 216 /* Return the final MM PTE protection mask */ 217 return ProtectMask; 218 } 219 220 BOOLEAN 221 NTAPI 222 MiInitializeSystemSpaceMap(IN PMMSESSION InputSession OPTIONAL) 223 { 224 SIZE_T AllocSize, BitmapSize, Size; 225 PVOID ViewStart; 226 PMMSESSION Session; 227 228 /* Check if this a session or system space */ 229 if (InputSession) 230 { 231 /* Use the input session */ 232 Session = InputSession; 233 ViewStart = MiSessionViewStart; 234 Size = MmSessionViewSize; 235 } 236 else 237 { 238 /* Use the system space "session" */ 239 Session = &MmSession; 240 ViewStart = MiSystemViewStart; 241 Size = MmSystemViewSize; 242 } 243 244 /* Initialize the system space lock */ 245 Session->SystemSpaceViewLockPointer = &Session->SystemSpaceViewLock; 246 KeInitializeGuardedMutex(Session->SystemSpaceViewLockPointer); 247 248 /* Set the start address */ 249 Session->SystemSpaceViewStart = ViewStart; 250 251 /* Create a bitmap to describe system space */ 252 BitmapSize = sizeof(RTL_BITMAP) + ((((Size / MI_SYSTEM_VIEW_BUCKET_SIZE) + 31) / 32) * sizeof(ULONG)); 253 Session->SystemSpaceBitMap = ExAllocatePoolWithTag(NonPagedPool, 254 BitmapSize, 255 TAG_MM); 256 ASSERT(Session->SystemSpaceBitMap); 257 RtlInitializeBitMap(Session->SystemSpaceBitMap, 258 (PULONG)(Session->SystemSpaceBitMap + 1), 259 (ULONG)(Size / MI_SYSTEM_VIEW_BUCKET_SIZE)); 260 261 /* Set system space fully empty to begin with */ 262 RtlClearAllBits(Session->SystemSpaceBitMap); 263 264 /* Set default hash flags */ 265 Session->SystemSpaceHashSize = 31; 266 Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1; 267 Session->SystemSpaceHashEntries = 0; 268 269 /* Calculate how much space for the hash views we'll need */ 270 AllocSize = sizeof(MMVIEW) * Session->SystemSpaceHashSize; 271 ASSERT(AllocSize < PAGE_SIZE); 272 273 /* Allocate and zero the view table */ 274 Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session == &MmSession ? 275 NonPagedPool : 276 PagedPool, 277 AllocSize, 278 TAG_MM); 279 ASSERT(Session->SystemSpaceViewTable != NULL); 280 RtlZeroMemory(Session->SystemSpaceViewTable, AllocSize); 281 282 /* Success */ 283 return TRUE; 284 } 285 286 PVOID 287 NTAPI 288 MiInsertInSystemSpace(IN PMMSESSION Session, 289 IN ULONG Buckets, 290 IN PCONTROL_AREA ControlArea) 291 { 292 PVOID Base; 293 ULONG Entry, Hash, i, HashSize; 294 PMMVIEW OldTable; 295 PAGED_CODE(); 296 297 /* Stay within 4GB */ 298 ASSERT(Buckets < MI_SYSTEM_VIEW_BUCKET_SIZE); 299 300 /* Lock system space */ 301 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer); 302 303 /* Check if we're going to exhaust hash entries */ 304 if ((Session->SystemSpaceHashEntries + 8) > Session->SystemSpaceHashSize) 305 { 306 /* Double the hash size */ 307 HashSize = Session->SystemSpaceHashSize * 2; 308 309 /* Save the old table and allocate a new one */ 310 OldTable = Session->SystemSpaceViewTable; 311 Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session == 312 &MmSession ? 313 NonPagedPool : 314 PagedPool, 315 HashSize * 316 sizeof(MMVIEW), 317 TAG_MM); 318 if (!Session->SystemSpaceViewTable) 319 { 320 /* Failed to allocate a new table, keep the old one for now */ 321 Session->SystemSpaceViewTable = OldTable; 322 } 323 else 324 { 325 /* Clear the new table and set the new ahsh and key */ 326 RtlZeroMemory(Session->SystemSpaceViewTable, HashSize * sizeof(MMVIEW)); 327 Session->SystemSpaceHashSize = HashSize; 328 Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1; 329 330 /* Loop the old table */ 331 for (i = 0; i < Session->SystemSpaceHashSize / 2; i++) 332 { 333 /* Check if the entry was valid */ 334 if (OldTable[i].Entry) 335 { 336 /* Re-hash the old entry and search for space in the new table */ 337 Hash = (OldTable[i].Entry >> 16) % Session->SystemSpaceHashKey; 338 while (Session->SystemSpaceViewTable[Hash].Entry) 339 { 340 /* Loop back at the beginning if we had an overflow */ 341 if (++Hash >= Session->SystemSpaceHashSize) Hash = 0; 342 } 343 344 /* Write the old entry in the new table */ 345 Session->SystemSpaceViewTable[Hash] = OldTable[i]; 346 } 347 } 348 349 /* Free the old table */ 350 ExFreePool(OldTable); 351 } 352 } 353 354 /* Check if we ran out */ 355 if (Session->SystemSpaceHashEntries == Session->SystemSpaceHashSize) 356 { 357 DPRINT1("Ran out of system view hash entries\n"); 358 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 359 return NULL; 360 } 361 362 /* Find space where to map this view */ 363 i = RtlFindClearBitsAndSet(Session->SystemSpaceBitMap, Buckets, 0); 364 if (i == 0xFFFFFFFF) 365 { 366 /* Out of space, fail */ 367 Session->BitmapFailures++; 368 DPRINT1("Out of system view space\n"); 369 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 370 return NULL; 371 } 372 373 /* Compute the base address */ 374 Base = (PVOID)((ULONG_PTR)Session->SystemSpaceViewStart + (i * MI_SYSTEM_VIEW_BUCKET_SIZE)); 375 376 /* Get the hash entry for this allocation */ 377 Entry = ((ULONG_PTR)Base & ~(MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) + Buckets; 378 Hash = (Entry >> 16) % Session->SystemSpaceHashKey; 379 380 /* Loop hash entries until a free one is found */ 381 while (Session->SystemSpaceViewTable[Hash].Entry) 382 { 383 /* Unless we overflow, in which case loop back at hash o */ 384 if (++Hash >= Session->SystemSpaceHashSize) Hash = 0; 385 } 386 387 /* Add this entry into the hash table */ 388 Session->SystemSpaceViewTable[Hash].Entry = Entry; 389 Session->SystemSpaceViewTable[Hash].ControlArea = ControlArea; 390 391 /* Hash entry found, increment total and return the base address */ 392 Session->SystemSpaceHashEntries++; 393 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 394 return Base; 395 } 396 397 static 398 NTSTATUS 399 MiAddMappedPtes(IN PMMPTE FirstPte, 400 IN PFN_NUMBER PteCount, 401 IN PCONTROL_AREA ControlArea, 402 IN LONGLONG SectionOffset) 403 { 404 MMPTE TempPte; 405 PMMPTE PointerPte, ProtoPte, LastProtoPte, LastPte; 406 PSUBSECTION Subsection; 407 408 /* Mapping at offset not supported yet */ 409 ASSERT(SectionOffset == 0); 410 411 /* ARM3 doesn't support this yet */ 412 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 413 ASSERT(ControlArea->u.Flags.Rom == 0); 414 ASSERT(ControlArea->FilePointer == NULL); 415 416 /* Sanity checks */ 417 ASSERT(PteCount != 0); 418 ASSERT(ControlArea->NumberOfMappedViews >= 1); 419 ASSERT(ControlArea->NumberOfUserReferences >= 1); 420 ASSERT(ControlArea->NumberOfSectionReferences != 0); 421 ASSERT(ControlArea->u.Flags.BeingCreated == 0); 422 ASSERT(ControlArea->u.Flags.BeingDeleted == 0); 423 ASSERT(ControlArea->u.Flags.BeingPurged == 0); 424 425 /* Get the PTEs for the actual mapping */ 426 PointerPte = FirstPte; 427 LastPte = FirstPte + PteCount; 428 429 /* Get the prototype PTEs that desribe the section mapping in the subsection */ 430 Subsection = (PSUBSECTION)(ControlArea + 1); 431 ProtoPte = Subsection->SubsectionBase; 432 LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; 433 434 /* Loop the PTEs for the mapping */ 435 while (PointerPte < LastPte) 436 { 437 /* We may have run out of prototype PTEs in this subsection */ 438 if (ProtoPte >= LastProtoPte) 439 { 440 /* But we don't handle this yet */ 441 ASSERT(FALSE); 442 } 443 444 /* The PTE should be completely clear */ 445 ASSERT(PointerPte->u.Long == 0); 446 447 /* Build the prototype PTE and write it */ 448 MI_MAKE_PROTOTYPE_PTE(&TempPte, ProtoPte); 449 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 450 451 /* Keep going */ 452 PointerPte++; 453 ProtoPte++; 454 } 455 456 /* No failure path */ 457 return STATUS_SUCCESS; 458 } 459 460 VOID 461 NTAPI 462 MiFillSystemPageDirectory(IN PVOID Base, 463 IN SIZE_T NumberOfBytes) 464 { 465 PMMPDE PointerPde, LastPde, SystemMapPde; 466 MMPDE TempPde; 467 PFN_NUMBER PageFrameIndex, ParentPage; 468 KIRQL OldIrql; 469 PAGED_CODE(); 470 471 /* Find the PDEs needed for this mapping */ 472 PointerPde = MiAddressToPde(Base); 473 LastPde = MiAddressToPde((PVOID)((ULONG_PTR)Base + NumberOfBytes - 1)); 474 475 #if (_MI_PAGING_LEVELS == 2) 476 /* Find the system double-mapped PDE that describes this mapping */ 477 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)]; 478 #else 479 /* We don't have a double mapping */ 480 SystemMapPde = PointerPde; 481 #endif 482 483 /* Use the PDE template and loop the PDEs */ 484 TempPde = ValidKernelPde; 485 while (PointerPde <= LastPde) 486 { 487 /* Lock the PFN database */ 488 OldIrql = MiAcquirePfnLock(); 489 490 /* Check if we don't already have this PDE mapped */ 491 if (SystemMapPde->u.Hard.Valid == 0) 492 { 493 /* Grab a page for it */ 494 MI_SET_USAGE(MI_USAGE_PAGE_TABLE); 495 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName); 496 PageFrameIndex = MiRemoveZeroPage(MI_GET_NEXT_COLOR()); 497 ASSERT(PageFrameIndex); 498 TempPde.u.Hard.PageFrameNumber = PageFrameIndex; 499 500 #if (_MI_PAGING_LEVELS == 2) 501 ParentPage = MmSystemPageDirectory[(PointerPde - MiAddressToPde(NULL)) / PDE_PER_PAGE]; 502 #else 503 ParentPage = MiPdeToPpe(PointerPde)->u.Hard.PageFrameNumber; 504 #endif 505 /* Initialize its PFN entry, with the parent system page directory page table */ 506 MiInitializePfnForOtherProcess(PageFrameIndex, 507 (PMMPTE)PointerPde, 508 ParentPage); 509 510 /* Make the system PDE entry valid */ 511 MI_WRITE_VALID_PDE(SystemMapPde, TempPde); 512 513 /* The system PDE entry might be the PDE itself, so check for this */ 514 if (PointerPde->u.Hard.Valid == 0) 515 { 516 /* It's different, so make the real PDE valid too */ 517 MI_WRITE_VALID_PDE(PointerPde, TempPde); 518 } 519 } 520 521 /* Release the lock and keep going with the next PDE */ 522 MiReleasePfnLock(OldIrql); 523 SystemMapPde++; 524 PointerPde++; 525 } 526 } 527 528 NTSTATUS 529 NTAPI 530 MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea, 531 IN BOOLEAN FailIfSystemViews) 532 { 533 KIRQL OldIrql; 534 535 /* Flag not yet supported */ 536 ASSERT(FailIfSystemViews == FALSE); 537 538 /* Lock the PFN database */ 539 OldIrql = MiAcquirePfnLock(); 540 541 /* State not yet supported */ 542 ASSERT(ControlArea->u.Flags.BeingPurged == 0); 543 544 /* Increase the reference counts */ 545 ControlArea->NumberOfMappedViews++; 546 ControlArea->NumberOfUserReferences++; 547 ASSERT(ControlArea->NumberOfSectionReferences != 0); 548 549 /* Release the PFN lock and return success */ 550 MiReleasePfnLock(OldIrql); 551 return STATUS_SUCCESS; 552 } 553 554 PSUBSECTION 555 NTAPI 556 MiLocateSubsection(IN PMMVAD Vad, 557 IN ULONG_PTR Vpn) 558 { 559 PSUBSECTION Subsection; 560 PCONTROL_AREA ControlArea; 561 ULONG_PTR PteOffset; 562 563 /* Get the control area */ 564 ControlArea = Vad->ControlArea; 565 ASSERT(ControlArea->u.Flags.Rom == 0); 566 ASSERT(ControlArea->u.Flags.Image == 0); 567 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 568 569 /* Get the subsection */ 570 Subsection = (PSUBSECTION)(ControlArea + 1); 571 572 /* We only support single-subsection segments */ 573 ASSERT(Subsection->SubsectionBase != NULL); 574 ASSERT(Vad->FirstPrototypePte >= Subsection->SubsectionBase); 575 ASSERT(Vad->FirstPrototypePte < &Subsection->SubsectionBase[Subsection->PtesInSubsection]); 576 577 /* Compute the PTE offset */ 578 PteOffset = Vpn - Vad->StartingVpn; 579 PteOffset += Vad->FirstPrototypePte - Subsection->SubsectionBase; 580 581 /* Again, we only support single-subsection segments */ 582 ASSERT(PteOffset < 0xF0000000); 583 ASSERT(PteOffset < Subsection->PtesInSubsection); 584 585 /* Return the subsection */ 586 return Subsection; 587 } 588 589 VOID 590 NTAPI 591 MiSegmentDelete(IN PSEGMENT Segment) 592 { 593 PCONTROL_AREA ControlArea; 594 SEGMENT_FLAGS SegmentFlags; 595 PSUBSECTION Subsection; 596 PMMPTE PointerPte, LastPte, PteForProto; 597 PMMPFN Pfn1; 598 PFN_NUMBER PageFrameIndex; 599 MMPTE TempPte; 600 KIRQL OldIrql; 601 602 /* Capture data */ 603 SegmentFlags = Segment->SegmentFlags; 604 ControlArea = Segment->ControlArea; 605 606 /* Make sure control area is on the right delete path */ 607 ASSERT(ControlArea->u.Flags.BeingDeleted == 1); 608 ASSERT(ControlArea->WritableUserReferences == 0); 609 610 /* These things are not supported yet */ 611 ASSERT(ControlArea->DereferenceList.Flink == NULL); 612 ASSERT(!(ControlArea->u.Flags.Image) && !(ControlArea->u.Flags.File)); 613 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 614 ASSERT(ControlArea->u.Flags.Rom == 0); 615 616 /* Get the subsection and PTEs for this segment */ 617 Subsection = (PSUBSECTION)(ControlArea + 1); 618 PointerPte = Subsection->SubsectionBase; 619 LastPte = PointerPte + Segment->NonExtendedPtes; 620 621 /* Lock the PFN database */ 622 OldIrql = MiAcquirePfnLock(); 623 624 /* Check if the master PTE is invalid */ 625 PteForProto = MiAddressToPte(PointerPte); 626 if (!PteForProto->u.Hard.Valid) 627 { 628 /* Fault it in */ 629 MiMakeSystemAddressValidPfn(PointerPte, OldIrql); 630 } 631 632 /* Loop all the segment PTEs */ 633 while (PointerPte < LastPte) 634 { 635 /* Check if it's time to switch master PTEs if we passed a PDE boundary */ 636 if (MiIsPteOnPdeBoundary(PointerPte) && 637 (PointerPte != Subsection->SubsectionBase)) 638 { 639 /* Check if the master PTE is invalid */ 640 PteForProto = MiAddressToPte(PointerPte); 641 if (!PteForProto->u.Hard.Valid) 642 { 643 /* Fault it in */ 644 MiMakeSystemAddressValidPfn(PointerPte, OldIrql); 645 } 646 } 647 648 /* This should be a prototype PTE */ 649 TempPte = *PointerPte; 650 ASSERT(SegmentFlags.LargePages == 0); 651 ASSERT(TempPte.u.Hard.Valid == 0); 652 653 /* See if we should clean things up */ 654 if (!(ControlArea->u.Flags.Image) && !(ControlArea->u.Flags.File)) 655 { 656 /* 657 * This is a section backed by the pagefile. Now that it doesn't exist anymore, 658 * we can give everything back to the system. 659 */ 660 ASSERT(TempPte.u.Soft.Prototype == 0); 661 662 if (TempPte.u.Soft.Transition == 1) 663 { 664 /* We can give the page back for other use */ 665 DPRINT("Releasing page for transition PTE %p\n", PointerPte); 666 PageFrameIndex = PFN_FROM_PTE(&TempPte); 667 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 668 669 /* As this is a paged-backed section, nobody should reference it anymore (no cache or whatever) */ 670 ASSERT(Pfn1->u3.ReferenceCount == 0); 671 672 /* And it should be in standby or modified list */ 673 ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList)); 674 675 /* Unlink it and put it back in free list */ 676 MiUnlinkPageFromList(Pfn1); 677 678 /* Temporarily mark this as active and make it free again */ 679 Pfn1->u3.e1.PageLocation = ActiveAndValid; 680 MI_SET_PFN_DELETED(Pfn1); 681 682 MiInsertPageInFreeList(PageFrameIndex); 683 } 684 else if (TempPte.u.Soft.PageFileHigh != 0) 685 { 686 /* Should not happen for now */ 687 ASSERT(FALSE); 688 } 689 } 690 else 691 { 692 /* unsupported for now */ 693 ASSERT(FALSE); 694 695 /* File-backed section must have prototype PTEs */ 696 ASSERT(TempPte.u.Soft.Prototype == 1); 697 } 698 699 /* Zero the PTE and keep going */ 700 PointerPte->u.Long = 0; 701 PointerPte++; 702 } 703 704 /* Release the PFN lock */ 705 MiReleasePfnLock(OldIrql); 706 707 /* Free the structures */ 708 ExFreePool(ControlArea); 709 ExFreePool(Segment); 710 } 711 712 VOID 713 NTAPI 714 MiCheckControlArea(IN PCONTROL_AREA ControlArea, 715 IN KIRQL OldIrql) 716 { 717 BOOLEAN DeleteSegment = FALSE; 718 MI_ASSERT_PFN_LOCK_HELD(); 719 720 /* Check if this is the last reference or view */ 721 if (!(ControlArea->NumberOfMappedViews) && 722 !(ControlArea->NumberOfSectionReferences)) 723 { 724 /* There should be no more user references either */ 725 ASSERT(ControlArea->NumberOfUserReferences == 0); 726 727 /* Not yet supported */ 728 ASSERT(ControlArea->FilePointer == NULL); 729 730 /* The control area is being destroyed */ 731 ControlArea->u.Flags.BeingDeleted = TRUE; 732 DeleteSegment = TRUE; 733 } 734 735 /* Release the PFN lock */ 736 MiReleasePfnLock(OldIrql); 737 738 /* Delete the segment if needed */ 739 if (DeleteSegment) 740 { 741 /* No more user write references at all */ 742 ASSERT(ControlArea->WritableUserReferences == 0); 743 MiSegmentDelete(ControlArea->Segment); 744 } 745 } 746 747 VOID 748 NTAPI 749 MiDereferenceControlArea(IN PCONTROL_AREA ControlArea) 750 { 751 KIRQL OldIrql; 752 753 /* Lock the PFN database */ 754 OldIrql = MiAcquirePfnLock(); 755 756 /* Drop reference counts */ 757 ControlArea->NumberOfMappedViews--; 758 ControlArea->NumberOfUserReferences--; 759 760 /* Check if it's time to delete the CA. This releases the lock */ 761 MiCheckControlArea(ControlArea, OldIrql); 762 } 763 764 VOID 765 NTAPI 766 MiRemoveMappedView(IN PEPROCESS CurrentProcess, 767 IN PMMVAD Vad) 768 { 769 KIRQL OldIrql; 770 PCONTROL_AREA ControlArea; 771 PETHREAD CurrentThread = PsGetCurrentThread(); 772 773 /* Get the control area */ 774 ControlArea = Vad->ControlArea; 775 776 /* We only support non-extendable, non-image, pagefile-backed regular sections */ 777 ASSERT(Vad->u.VadFlags.VadType == VadNone); 778 ASSERT(Vad->u2.VadFlags2.ExtendableFile == FALSE); 779 ASSERT(ControlArea); 780 ASSERT(ControlArea->FilePointer == NULL); 781 782 /* Delete the actual virtual memory pages */ 783 MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT, 784 (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1), 785 Vad); 786 787 /* Release the working set */ 788 MiUnlockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread); 789 790 /* Lock the PFN database */ 791 OldIrql = MiAcquirePfnLock(); 792 793 /* Remove references */ 794 ControlArea->NumberOfMappedViews--; 795 ControlArea->NumberOfUserReferences--; 796 797 /* Check if it should be destroyed */ 798 MiCheckControlArea(ControlArea, OldIrql); 799 } 800 801 NTSTATUS 802 NTAPI 803 MiUnmapViewOfSection(IN PEPROCESS Process, 804 IN PVOID BaseAddress, 805 IN ULONG Flags) 806 { 807 PMEMORY_AREA MemoryArea; 808 BOOLEAN Attached = FALSE; 809 KAPC_STATE ApcState; 810 PMMVAD Vad; 811 PVOID DbgBase = NULL; 812 SIZE_T RegionSize; 813 NTSTATUS Status; 814 PETHREAD CurrentThread = PsGetCurrentThread(); 815 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 816 PAGED_CODE(); 817 818 /* Check if we need to lock the address space */ 819 if (!Flags) MmLockAddressSpace(&Process->Vm); 820 821 /* Check for Mm Region */ 822 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, BaseAddress); 823 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) 824 { 825 /* Call Mm API */ 826 NTSTATUS Status = MiRosUnmapViewOfSection(Process, BaseAddress, Process->ProcessExiting); 827 if (!Flags) MmUnlockAddressSpace(&Process->Vm); 828 return Status; 829 } 830 831 /* Check if we should attach to the process */ 832 if (CurrentProcess != Process) 833 { 834 /* The process is different, do an attach */ 835 KeStackAttachProcess(&Process->Pcb, &ApcState); 836 Attached = TRUE; 837 } 838 839 /* Check if the process is already dead */ 840 if (Process->VmDeleted) 841 { 842 /* Fail the call */ 843 DPRINT1("Process died!\n"); 844 if (!Flags) MmUnlockAddressSpace(&Process->Vm); 845 Status = STATUS_PROCESS_IS_TERMINATING; 846 goto Quickie; 847 } 848 849 /* Find the VAD for the address and make sure it's a section VAD */ 850 Vad = MiLocateAddress(BaseAddress); 851 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory)) 852 { 853 /* Couldn't find it, or invalid VAD, fail */ 854 DPRINT1("No VAD or invalid VAD\n"); 855 if (!Flags) MmUnlockAddressSpace(&Process->Vm); 856 Status = STATUS_NOT_MAPPED_VIEW; 857 goto Quickie; 858 } 859 860 /* We should be attached */ 861 ASSERT(Process == PsGetCurrentProcess()); 862 863 /* We need the base address for the debugger message on image-backed VADs */ 864 if (Vad->u.VadFlags.VadType == VadImageMap) 865 { 866 DbgBase = (PVOID)(Vad->StartingVpn >> PAGE_SHIFT); 867 } 868 869 /* Compute the size of the VAD region */ 870 RegionSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT); 871 872 /* For SEC_NO_CHANGE sections, we need some extra checks */ 873 if (Vad->u.VadFlags.NoChange == 1) 874 { 875 /* Are we allowed to mess with this VAD? */ 876 Status = MiCheckSecuredVad(Vad, 877 (PVOID)(Vad->StartingVpn >> PAGE_SHIFT), 878 RegionSize, 879 MM_DELETE_CHECK); 880 if (!NT_SUCCESS(Status)) 881 { 882 /* We failed */ 883 DPRINT1("Trying to unmap protected VAD!\n"); 884 if (!Flags) MmUnlockAddressSpace(&Process->Vm); 885 goto Quickie; 886 } 887 } 888 889 /* Not currently supported */ 890 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical); 891 892 /* FIXME: Remove VAD charges */ 893 894 /* Lock the working set */ 895 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 896 897 /* Remove the VAD */ 898 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 899 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 900 PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 901 902 /* Remove the PTEs for this view, which also releases the working set lock */ 903 MiRemoveMappedView(Process, Vad); 904 905 /* FIXME: Remove commitment */ 906 907 /* Update performance counter and release the lock */ 908 Process->VirtualSize -= RegionSize; 909 if (!Flags) MmUnlockAddressSpace(&Process->Vm); 910 911 /* Destroy the VAD and return success */ 912 ExFreePool(Vad); 913 Status = STATUS_SUCCESS; 914 915 /* Failure and success case -- send debugger message, detach, and return */ 916 Quickie: 917 if (DbgBase) DbgkUnMapViewOfSection(DbgBase); 918 if (Attached) KeUnstackDetachProcess(&ApcState); 919 return Status; 920 } 921 922 NTSTATUS 923 NTAPI 924 MiSessionCommitPageTables(IN PVOID StartVa, 925 IN PVOID EndVa) 926 { 927 KIRQL OldIrql; 928 ULONG Color, Index; 929 PMMPDE StartPde, EndPde; 930 MMPDE TempPde = ValidKernelPdeLocal; 931 PMMPFN Pfn1; 932 PFN_NUMBER PageCount = 0, ActualPages = 0, PageFrameNumber; 933 934 /* Windows sanity checks */ 935 ASSERT(StartVa >= (PVOID)MmSessionBase); 936 ASSERT(EndVa < (PVOID)MiSessionSpaceEnd); 937 ASSERT(PAGE_ALIGN(EndVa) == EndVa); 938 939 /* Get the start and end PDE, then loop each one */ 940 StartPde = MiAddressToPde(StartVa); 941 EndPde = MiAddressToPde((PVOID)((ULONG_PTR)EndVa - 1)); 942 Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22; 943 while (StartPde <= EndPde) 944 { 945 #ifndef _M_AMD64 946 /* If we don't already have a page table for it, increment count */ 947 if (MmSessionSpace->PageTables[Index].u.Long == 0) PageCount++; 948 #endif 949 /* Move to the next one */ 950 StartPde++; 951 Index++; 952 } 953 954 /* If there's no page tables to create, bail out */ 955 if (PageCount == 0) return STATUS_SUCCESS; 956 957 /* Reset the start PDE and index */ 958 StartPde = MiAddressToPde(StartVa); 959 Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22; 960 961 /* Loop each PDE while holding the working set lock */ 962 // MiLockWorkingSet(PsGetCurrentThread(), 963 // &MmSessionSpace->GlobalVirtualAddress->Vm); 964 #ifdef _M_AMD64 965 _WARN("MiSessionCommitPageTables halfplemented for amd64") 966 DBG_UNREFERENCED_LOCAL_VARIABLE(OldIrql); 967 DBG_UNREFERENCED_LOCAL_VARIABLE(Color); 968 DBG_UNREFERENCED_LOCAL_VARIABLE(TempPde); 969 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn1); 970 DBG_UNREFERENCED_LOCAL_VARIABLE(PageFrameNumber); 971 ASSERT(FALSE); 972 #else 973 while (StartPde <= EndPde) 974 { 975 /* Check if we already have a page table */ 976 if (MmSessionSpace->PageTables[Index].u.Long == 0) 977 { 978 /* We don't, so the PDE shouldn't be ready yet */ 979 ASSERT(StartPde->u.Hard.Valid == 0); 980 981 /* ReactOS check to avoid MiEnsureAvailablePageOrWait */ 982 ASSERT(MmAvailablePages >= 32); 983 984 /* Acquire the PFN lock and grab a zero page */ 985 OldIrql = MiAcquirePfnLock(); 986 MI_SET_USAGE(MI_USAGE_PAGE_TABLE); 987 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName); 988 Color = (++MmSessionSpace->Color) & MmSecondaryColorMask; 989 PageFrameNumber = MiRemoveZeroPage(Color); 990 TempPde.u.Hard.PageFrameNumber = PageFrameNumber; 991 MI_WRITE_VALID_PDE(StartPde, TempPde); 992 993 /* Write the page table in session space structure */ 994 ASSERT(MmSessionSpace->PageTables[Index].u.Long == 0); 995 MmSessionSpace->PageTables[Index] = TempPde; 996 997 /* Initialize the PFN */ 998 MiInitializePfnForOtherProcess(PageFrameNumber, 999 StartPde, 1000 MmSessionSpace->SessionPageDirectoryIndex); 1001 1002 /* And now release the lock */ 1003 MiReleasePfnLock(OldIrql); 1004 1005 /* Get the PFN entry and make sure there's no event for it */ 1006 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber); 1007 ASSERT(Pfn1->u1.Event == NULL); 1008 1009 /* Increment the number of pages */ 1010 ActualPages++; 1011 } 1012 1013 /* Move to the next PDE */ 1014 StartPde++; 1015 Index++; 1016 } 1017 #endif 1018 1019 /* Make sure we didn't do more pages than expected */ 1020 ASSERT(ActualPages <= PageCount); 1021 1022 /* Release the working set lock */ 1023 // MiUnlockWorkingSet(PsGetCurrentThread(), 1024 // &MmSessionSpace->GlobalVirtualAddress->Vm); 1025 1026 1027 /* If we did at least one page... */ 1028 if (ActualPages) 1029 { 1030 /* Update the performance counters! */ 1031 InterlockedExchangeAddSizeT(&MmSessionSpace->NonPageablePages, ActualPages); 1032 InterlockedExchangeAddSizeT(&MmSessionSpace->CommittedPages, ActualPages); 1033 } 1034 1035 /* Return status */ 1036 return STATUS_SUCCESS; 1037 } 1038 1039 NTSTATUS 1040 MiMapViewInSystemSpace( 1041 _In_ PVOID Section, 1042 _In_ PMMSESSION Session, 1043 _Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase, 1044 _Inout_ PSIZE_T ViewSize, 1045 _Inout_ PLARGE_INTEGER SectionOffset) 1046 { 1047 PVOID Base; 1048 PCONTROL_AREA ControlArea; 1049 ULONG Buckets; 1050 LONGLONG SectionSize; 1051 NTSTATUS Status; 1052 PAGED_CODE(); 1053 1054 /* Get the control area, check for any flags ARM3 doesn't yet support */ 1055 ControlArea = ((PSECTION)Section)->Segment->ControlArea; 1056 ASSERT(ControlArea->u.Flags.Image == 0); 1057 ASSERT(ControlArea->FilePointer == NULL); 1058 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 1059 ASSERT(ControlArea->u.Flags.Rom == 0); 1060 ASSERT(ControlArea->u.Flags.WasPurged == 0); 1061 1062 /* Increase the reference and map count on the control area, no purges yet */ 1063 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE); 1064 ASSERT(NT_SUCCESS(Status)); 1065 1066 /* Get the section size at creation time */ 1067 SectionSize = ((PSECTION)Section)->SizeOfSection.QuadPart; 1068 1069 /* If the caller didn't specify a view size, assume until the end of the section */ 1070 if (!(*ViewSize)) 1071 { 1072 /* Check for overflow first */ 1073 if ((SectionSize - SectionOffset->QuadPart) > SIZE_T_MAX) 1074 { 1075 DPRINT1("Section end is too far away from the specified offset.\n"); 1076 MiDereferenceControlArea(ControlArea); 1077 return STATUS_INVALID_VIEW_SIZE; 1078 } 1079 *ViewSize = SectionSize - SectionOffset->QuadPart; 1080 } 1081 1082 /* Check overflow */ 1083 if ((SectionOffset->QuadPart + *ViewSize) < SectionOffset->QuadPart) 1084 { 1085 DPRINT1("Integer overflow between size & offset!\n"); 1086 MiDereferenceControlArea(ControlArea); 1087 return STATUS_INVALID_VIEW_SIZE; 1088 } 1089 1090 /* Check if the caller wanted a larger section than the view */ 1091 if (SectionOffset->QuadPart + *ViewSize > SectionSize) 1092 { 1093 /* Fail */ 1094 DPRINT1("View is too large\n"); 1095 MiDereferenceControlArea(ControlArea); 1096 return STATUS_INVALID_VIEW_SIZE; 1097 } 1098 1099 /* Get the number of 64K buckets required for this mapping */ 1100 Buckets = (ULONG)(*ViewSize / MI_SYSTEM_VIEW_BUCKET_SIZE); 1101 if (*ViewSize & (MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) Buckets++; 1102 1103 /* Check if the view is more than 4GB large */ 1104 if (Buckets >= MI_SYSTEM_VIEW_BUCKET_SIZE) 1105 { 1106 /* Fail */ 1107 DPRINT1("View is too large\n"); 1108 MiDereferenceControlArea(ControlArea); 1109 return STATUS_INVALID_VIEW_SIZE; 1110 } 1111 1112 /* Insert this view into system space and get a base address for it */ 1113 Base = MiInsertInSystemSpace(Session, Buckets, ControlArea); 1114 if (!Base) 1115 { 1116 /* Fail */ 1117 DPRINT1("Out of system space\n"); 1118 MiDereferenceControlArea(ControlArea); 1119 return STATUS_NO_MEMORY; 1120 } 1121 1122 /* What's the underlying session? */ 1123 if (Session == &MmSession) 1124 { 1125 /* Create the PDEs needed for this mapping, and double-map them if needed */ 1126 MiFillSystemPageDirectory(Base, Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE); 1127 Status = STATUS_SUCCESS; 1128 } 1129 else 1130 { 1131 /* Create the PDEs needed for this mapping */ 1132 Status = MiSessionCommitPageTables(Base, 1133 (PVOID)((ULONG_PTR)Base + 1134 Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE)); 1135 ASSERT(NT_SUCCESS(Status)); 1136 } 1137 1138 /* Create the actual prototype PTEs for this mapping */ 1139 Status = MiAddMappedPtes(MiAddressToPte(Base), 1140 BYTES_TO_PAGES(*ViewSize), 1141 ControlArea, 1142 SectionOffset->QuadPart); 1143 ASSERT(NT_SUCCESS(Status)); 1144 1145 /* Return the base adress of the mapping and success */ 1146 *MappedBase = Base; 1147 return STATUS_SUCCESS; 1148 } 1149 1150 VOID 1151 NTAPI 1152 MiSetControlAreaSymbolsLoaded(IN PCONTROL_AREA ControlArea) 1153 { 1154 KIRQL OldIrql; 1155 1156 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 1157 1158 OldIrql = MiAcquirePfnLock(); 1159 ControlArea->u.Flags.DebugSymbolsLoaded |= 1; 1160 1161 ASSERT(OldIrql <= APC_LEVEL); 1162 MiReleasePfnLock(OldIrql); 1163 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 1164 } 1165 1166 VOID 1167 NTAPI 1168 MiLoadUserSymbols(IN PCONTROL_AREA ControlArea, 1169 IN PVOID BaseAddress, 1170 IN PEPROCESS Process) 1171 { 1172 NTSTATUS Status; 1173 ANSI_STRING FileNameA; 1174 PLIST_ENTRY NextEntry; 1175 PUNICODE_STRING FileName; 1176 PIMAGE_NT_HEADERS NtHeaders; 1177 PLDR_DATA_TABLE_ENTRY LdrEntry; 1178 1179 FileName = &ControlArea->FilePointer->FileName; 1180 if (FileName->Length == 0) 1181 { 1182 return; 1183 } 1184 1185 /* Acquire module list lock */ 1186 KeEnterCriticalRegion(); 1187 ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE); 1188 1189 /* Browse list to try to find current module */ 1190 for (NextEntry = MmLoadedUserImageList.Flink; 1191 NextEntry != &MmLoadedUserImageList; 1192 NextEntry = NextEntry->Flink) 1193 { 1194 /* Get the entry */ 1195 LdrEntry = CONTAINING_RECORD(NextEntry, 1196 LDR_DATA_TABLE_ENTRY, 1197 InLoadOrderLinks); 1198 1199 /* If already in the list, increase load count */ 1200 if (LdrEntry->DllBase == BaseAddress) 1201 { 1202 ++LdrEntry->LoadCount; 1203 break; 1204 } 1205 } 1206 1207 /* Not in the list, we'll add it */ 1208 if (NextEntry == &MmLoadedUserImageList) 1209 { 1210 /* Allocate our element, taking to the name string and its null char */ 1211 LdrEntry = ExAllocatePoolWithTag(NonPagedPool, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry), 'bDmM'); 1212 if (LdrEntry) 1213 { 1214 memset(LdrEntry, 0, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry)); 1215 1216 _SEH2_TRY 1217 { 1218 /* Get image checksum and size */ 1219 NtHeaders = RtlImageNtHeader(BaseAddress); 1220 if (NtHeaders) 1221 { 1222 LdrEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage; 1223 LdrEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum; 1224 } 1225 } 1226 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1227 { 1228 ExFreePoolWithTag(LdrEntry, 'bDmM'); 1229 ExReleaseResourceLite(&PsLoadedModuleResource); 1230 KeLeaveCriticalRegion(); 1231 _SEH2_YIELD(return); 1232 } 1233 _SEH2_END; 1234 1235 /* Fill all the details */ 1236 LdrEntry->DllBase = BaseAddress; 1237 LdrEntry->FullDllName.Buffer = (PVOID)((ULONG_PTR)LdrEntry + sizeof(*LdrEntry)); 1238 LdrEntry->FullDllName.Length = FileName->Length; 1239 LdrEntry->FullDllName.MaximumLength = FileName->Length + sizeof(UNICODE_NULL); 1240 memcpy(LdrEntry->FullDllName.Buffer, FileName->Buffer, FileName->Length); 1241 LdrEntry->FullDllName.Buffer[LdrEntry->FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL; 1242 LdrEntry->LoadCount = 1; 1243 1244 /* Insert! */ 1245 InsertHeadList(&MmLoadedUserImageList, &LdrEntry->InLoadOrderLinks); 1246 } 1247 } 1248 1249 /* Release locks */ 1250 ExReleaseResourceLite(&PsLoadedModuleResource); 1251 KeLeaveCriticalRegion(); 1252 1253 /* Load symbols */ 1254 Status = RtlUnicodeStringToAnsiString(&FileNameA, FileName, TRUE); 1255 if (NT_SUCCESS(Status)) 1256 { 1257 DbgLoadImageSymbols(&FileNameA, BaseAddress, (ULONG_PTR)Process->UniqueProcessId); 1258 RtlFreeAnsiString(&FileNameA); 1259 } 1260 } 1261 1262 NTSTATUS 1263 NTAPI 1264 MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea, 1265 IN PEPROCESS Process, 1266 IN PVOID *BaseAddress, 1267 IN PLARGE_INTEGER SectionOffset, 1268 IN PSIZE_T ViewSize, 1269 IN PSECTION Section, 1270 IN SECTION_INHERIT InheritDisposition, 1271 IN ULONG ProtectionMask, 1272 IN SIZE_T CommitSize, 1273 IN ULONG_PTR ZeroBits, 1274 IN ULONG AllocationType) 1275 { 1276 PMMVAD_LONG Vad; 1277 ULONG_PTR StartAddress; 1278 ULONG_PTR ViewSizeInPages; 1279 PSUBSECTION Subsection; 1280 PSEGMENT Segment; 1281 PFN_NUMBER PteOffset; 1282 NTSTATUS Status; 1283 ULONG QuotaCharge = 0, QuotaExcess = 0; 1284 PMMPTE PointerPte, LastPte; 1285 MMPTE TempPte; 1286 ULONG Granularity = MM_VIRTMEM_GRANULARITY; 1287 1288 DPRINT("Mapping ARM3 data section\n"); 1289 1290 /* Get the segment for this section */ 1291 Segment = ControlArea->Segment; 1292 1293 #ifdef _M_IX86 1294 /* ALlow being less restrictive on x86. */ 1295 if (AllocationType & MEM_DOS_LIM) 1296 Granularity = PAGE_SIZE; 1297 #endif 1298 1299 /* One can only reserve a file-based mapping, not shared memory! */ 1300 if ((AllocationType & MEM_RESERVE) && !(ControlArea->FilePointer)) 1301 { 1302 return STATUS_INVALID_PARAMETER_9; 1303 } 1304 1305 /* First, increase the map count. No purging is supported yet */ 1306 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE); 1307 if (!NT_SUCCESS(Status)) return Status; 1308 1309 /* Check if the caller specified the view size */ 1310 if (!(*ViewSize)) 1311 { 1312 LONGLONG ViewSizeLL; 1313 1314 /* The caller did not, so pick a 64K aligned view size based on the offset */ 1315 SectionOffset->LowPart &= ~(_64K - 1); 1316 1317 /* Calculate size and make sure this fits */ 1318 if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &ViewSizeLL)) 1319 || !NT_SUCCESS(RtlLongLongToSIZET(ViewSizeLL, ViewSize)) 1320 || (*ViewSize > MAXLONG_PTR)) 1321 { 1322 MiDereferenceControlArea(ControlArea); 1323 return STATUS_INVALID_VIEW_SIZE; 1324 } 1325 } 1326 else 1327 { 1328 /* A size was specified, align it to a 64K boundary 1329 * and check for overflow or huge value. */ 1330 if (!NT_SUCCESS(RtlSIZETAdd(*ViewSize, SectionOffset->LowPart & (_64K - 1), ViewSize)) 1331 || (*ViewSize > MAXLONG_PTR)) 1332 { 1333 MiDereferenceControlArea(ControlArea); 1334 return STATUS_INVALID_VIEW_SIZE; 1335 } 1336 1337 /* Align the offset as well to make this an aligned map */ 1338 SectionOffset->LowPart &= ~((ULONG)_64K - 1); 1339 } 1340 1341 /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */ 1342 ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0); 1343 1344 /* Windows ASSERTs for this flag */ 1345 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 1346 1347 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */ 1348 ASSERT(ControlArea->u.Flags.Rom == 0); 1349 Subsection = (PSUBSECTION)(ControlArea + 1); 1350 1351 /* Sections with extended segments are not supported in ARM3 */ 1352 ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0); 1353 1354 /* Within this section, figure out which PTEs will describe the view */ 1355 PteOffset = (PFN_NUMBER)(SectionOffset->QuadPart >> PAGE_SHIFT); 1356 1357 /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */ 1358 ASSERT(PteOffset < Segment->TotalNumberOfPtes); 1359 ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset); 1360 1361 /* In ARM3, only one subsection is used for now. It must contain these PTEs */ 1362 ASSERT(PteOffset < Subsection->PtesInSubsection); 1363 1364 /* In ARM3, only page-file backed sections (shared memory) are supported now */ 1365 ASSERT(ControlArea->FilePointer == NULL); 1366 1367 /* Windows ASSERTs for this too -- there must be a subsection base address */ 1368 ASSERT(Subsection->SubsectionBase != NULL); 1369 1370 /* Compute how much commit space the segment will take */ 1371 if ((CommitSize) && (Segment->NumberOfCommittedPages < Segment->TotalNumberOfPtes)) 1372 { 1373 /* Charge for the maximum pages */ 1374 QuotaCharge = BYTES_TO_PAGES(CommitSize); 1375 } 1376 1377 /* ARM3 does not currently support large pages */ 1378 ASSERT(Segment->SegmentFlags.LargePages == 0); 1379 1380 /* Calculate how many pages the region spans */ 1381 ViewSizeInPages = BYTES_TO_PAGES(*ViewSize); 1382 1383 /* A VAD can now be allocated. Do so and zero it out */ 1384 /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */ 1385 ASSERT((AllocationType & MEM_RESERVE) == 0); /* ARM3 does not support this */ 1386 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV'); 1387 if (!Vad) 1388 { 1389 MiDereferenceControlArea(ControlArea); 1390 return STATUS_INSUFFICIENT_RESOURCES; 1391 } 1392 1393 RtlZeroMemory(Vad, sizeof(MMVAD_LONG)); 1394 Vad->u4.Banked = (PVOID)(ULONG_PTR)0xDEADBABEDEADBABEULL; 1395 1396 /* Write all the data required in the VAD for handling a fault */ 1397 Vad->ControlArea = ControlArea; 1398 Vad->u.VadFlags.CommitCharge = 0; 1399 Vad->u.VadFlags.Protection = ProtectionMask; 1400 Vad->u2.VadFlags2.FileOffset = (ULONG)(SectionOffset->QuadPart >> 16); 1401 Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare); 1402 if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange)) 1403 { 1404 /* This isn't really implemented yet, but handle setting the flag */ 1405 Vad->u.VadFlags.NoChange = 1; 1406 Vad->u2.VadFlags2.SecNoChange = 1; 1407 } 1408 1409 /* Finally, write down the first and last prototype PTE */ 1410 Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset]; 1411 PteOffset += ViewSizeInPages - 1; 1412 ASSERT(PteOffset < Subsection->PtesInSubsection); 1413 Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset]; 1414 1415 /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */ 1416 ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte); 1417 1418 /* FIXME: Should setup VAD bitmap */ 1419 Status = STATUS_SUCCESS; 1420 1421 /* Check if anything was committed */ 1422 if (QuotaCharge) 1423 { 1424 /* Set the start and end PTE addresses, and pick the template PTE */ 1425 PointerPte = Vad->FirstPrototypePte; 1426 LastPte = PointerPte + BYTES_TO_PAGES(CommitSize); 1427 TempPte = Segment->SegmentPteTemplate; 1428 1429 /* Acquire the commit lock and loop all prototype PTEs to be committed */ 1430 KeAcquireGuardedMutex(&MmSectionCommitMutex); 1431 while (PointerPte < LastPte) 1432 { 1433 /* Make sure the PTE is already invalid */ 1434 if (PointerPte->u.Long == 0) 1435 { 1436 /* And write the invalid PTE */ 1437 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 1438 } 1439 else 1440 { 1441 /* The PTE is valid, so skip it */ 1442 QuotaExcess++; 1443 } 1444 1445 /* Move to the next PTE */ 1446 PointerPte++; 1447 } 1448 1449 /* Now check how many pages exactly we committed, and update accounting */ 1450 ASSERT(QuotaCharge >= QuotaExcess); 1451 QuotaCharge -= QuotaExcess; 1452 Segment->NumberOfCommittedPages += QuotaCharge; 1453 ASSERT(Segment->NumberOfCommittedPages <= Segment->TotalNumberOfPtes); 1454 1455 /* Now that we're done, release the lock */ 1456 KeReleaseGuardedMutex(&MmSectionCommitMutex); 1457 } 1458 1459 /* Is it SEC_BASED, or did the caller manually specify an address? */ 1460 if (*BaseAddress != NULL) 1461 { 1462 /* Just align what the caller gave us */ 1463 StartAddress = ALIGN_DOWN_BY((ULONG_PTR)*BaseAddress, Granularity); 1464 } 1465 else if (Section->Address.StartingVpn != 0) 1466 { 1467 /* It is a SEC_BASED mapping, use the address that was generated */ 1468 StartAddress = Section->Address.StartingVpn + SectionOffset->LowPart; 1469 } 1470 else 1471 { 1472 StartAddress = 0; 1473 } 1474 1475 Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 1476 if (!NT_SUCCESS(Status)) 1477 { 1478 ExFreePoolWithTag(Vad, 'ldaV'); 1479 MiDereferenceControlArea(ControlArea); 1480 1481 KeAcquireGuardedMutex(&MmSectionCommitMutex); 1482 Segment->NumberOfCommittedPages -= QuotaCharge; 1483 KeReleaseGuardedMutex(&MmSectionCommitMutex); 1484 return Status; 1485 } 1486 1487 /* Insert the VAD */ 1488 Status = MiInsertVadEx((PMMVAD)Vad, 1489 &StartAddress, 1490 ViewSizeInPages * PAGE_SIZE, 1491 MAXULONG_PTR >> ZeroBits, 1492 Granularity, 1493 AllocationType); 1494 if (!NT_SUCCESS(Status)) 1495 { 1496 ExFreePoolWithTag(Vad, 'ldaV'); 1497 MiDereferenceControlArea(ControlArea); 1498 1499 KeAcquireGuardedMutex(&MmSectionCommitMutex); 1500 Segment->NumberOfCommittedPages -= QuotaCharge; 1501 KeReleaseGuardedMutex(&MmSectionCommitMutex); 1502 1503 PsReturnProcessNonPagedPoolQuota(PsGetCurrentProcess(), sizeof(MMVAD_LONG)); 1504 return Status; 1505 } 1506 1507 /* Windows stores this for accounting purposes, do so as well */ 1508 if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress; 1509 1510 /* Finally, let the caller know where, and for what size, the view was mapped */ 1511 *ViewSize = ViewSizeInPages * PAGE_SIZE; 1512 *BaseAddress = (PVOID)StartAddress; 1513 DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress, *ViewSize); 1514 return STATUS_SUCCESS; 1515 } 1516 1517 VOID 1518 NTAPI 1519 MiSubsectionConsistent(IN PSUBSECTION Subsection) 1520 { 1521 /* ReactOS only supports systems with 4K pages and 4K sectors */ 1522 ASSERT(Subsection->u.SubsectionFlags.SectorEndOffset == 0); 1523 1524 /* Therefore, then number of PTEs should be equal to the number of sectors */ 1525 if (Subsection->NumberOfFullSectors != Subsection->PtesInSubsection) 1526 { 1527 /* Break and warn if this is inconsistent */ 1528 DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n", 1529 Subsection->NumberOfFullSectors, Subsection->PtesInSubsection); 1530 DbgBreakPoint(); 1531 } 1532 } 1533 1534 NTSTATUS 1535 NTAPI 1536 MiCreateDataFileMap(IN PFILE_OBJECT File, 1537 OUT PSEGMENT *Segment, 1538 IN PSIZE_T MaximumSize, 1539 IN ULONG SectionPageProtection, 1540 IN ULONG AllocationAttributes, 1541 IN ULONG IgnoreFileSizing) 1542 { 1543 /* Not yet implemented */ 1544 ASSERT(FALSE); 1545 *Segment = NULL; 1546 return STATUS_NOT_IMPLEMENTED; 1547 } 1548 1549 static 1550 NTSTATUS 1551 NTAPI 1552 MiCreatePagingFileMap(OUT PSEGMENT *Segment, 1553 IN PLARGE_INTEGER MaximumSize, 1554 IN ULONG ProtectionMask, 1555 IN ULONG AllocationAttributes) 1556 { 1557 ULONGLONG SizeLimit; 1558 PFN_COUNT PteCount; 1559 PMMPTE PointerPte; 1560 MMPTE TempPte; 1561 PCONTROL_AREA ControlArea; 1562 PSEGMENT NewSegment; 1563 PSUBSECTION Subsection; 1564 PAGED_CODE(); 1565 1566 /* No large pages in ARM3 yet */ 1567 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0); 1568 1569 /* Pagefile-backed sections need a known size */ 1570 if (!MaximumSize || !MaximumSize->QuadPart || MaximumSize->QuadPart < 0) 1571 return STATUS_INVALID_PARAMETER_4; 1572 1573 /* Calculate the maximum size possible, given the Prototype PTEs we'll need */ 1574 SizeLimit = MmSizeOfPagedPoolInBytes - sizeof(SEGMENT); 1575 SizeLimit /= sizeof(MMPTE); 1576 SizeLimit <<= PAGE_SHIFT; 1577 1578 /* Fail if this size is too big */ 1579 if (MaximumSize->QuadPart > SizeLimit) 1580 { 1581 return STATUS_SECTION_TOO_BIG; 1582 } 1583 1584 /* Calculate how many Prototype PTEs will be needed */ 1585 PteCount = (PFN_COUNT)((MaximumSize->QuadPart + PAGE_SIZE - 1) >> PAGE_SHIFT); 1586 1587 /* For commited memory, we must have a valid protection mask */ 1588 if (AllocationAttributes & SEC_COMMIT) ASSERT(ProtectionMask != 0); 1589 1590 /* The segment contains all the Prototype PTEs, allocate it in paged pool */ 1591 NewSegment = ExAllocatePoolWithTag(PagedPool, 1592 sizeof(SEGMENT) + 1593 sizeof(MMPTE) * (PteCount - 1), 1594 'tSmM'); 1595 if (!NewSegment) 1596 { 1597 return STATUS_INSUFFICIENT_RESOURCES; 1598 } 1599 *Segment = NewSegment; 1600 1601 /* Now allocate the control area, which has the subsection structure */ 1602 ControlArea = ExAllocatePoolWithTag(NonPagedPool, 1603 sizeof(CONTROL_AREA) + sizeof(SUBSECTION), 1604 'tCmM'); 1605 if (!ControlArea) 1606 { 1607 ExFreePoolWithTag(Segment, 'tSmM'); 1608 return STATUS_INSUFFICIENT_RESOURCES; 1609 } 1610 1611 /* And zero it out, filling the basic segmnet pointer and reference fields */ 1612 RtlZeroMemory(ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION)); 1613 ControlArea->Segment = NewSegment; 1614 ControlArea->NumberOfSectionReferences = 1; 1615 ControlArea->NumberOfUserReferences = 1; 1616 1617 /* Convert allocation attributes to control area flags */ 1618 if (AllocationAttributes & SEC_BASED) ControlArea->u.Flags.Based = 1; 1619 if (AllocationAttributes & SEC_RESERVE) ControlArea->u.Flags.Reserve = 1; 1620 if (AllocationAttributes & SEC_COMMIT) ControlArea->u.Flags.Commit = 1; 1621 1622 /* We just allocated it */ 1623 ControlArea->u.Flags.BeingCreated = 1; 1624 1625 /* The subsection follows, write the mask, PTE count and point back to the CA */ 1626 Subsection = (PSUBSECTION)(ControlArea + 1); 1627 Subsection->ControlArea = ControlArea; 1628 Subsection->PtesInSubsection = PteCount; 1629 Subsection->u.SubsectionFlags.Protection = ProtectionMask; 1630 1631 /* Zero out the segment's prototype PTEs, and link it with the control area */ 1632 PointerPte = &NewSegment->ThePtes[0]; 1633 RtlZeroMemory(NewSegment, sizeof(SEGMENT)); 1634 NewSegment->PrototypePte = PointerPte; 1635 NewSegment->ControlArea = ControlArea; 1636 1637 /* Save some extra accounting data for the segment as well */ 1638 NewSegment->u1.CreatingProcess = PsGetCurrentProcess(); 1639 NewSegment->SizeOfSegment = ((ULONGLONG)PteCount) * PAGE_SIZE; 1640 NewSegment->TotalNumberOfPtes = PteCount; 1641 NewSegment->NonExtendedPtes = PteCount; 1642 1643 /* The subsection's base address is the first Prototype PTE in the segment */ 1644 Subsection->SubsectionBase = PointerPte; 1645 1646 /* Start with an empty PTE, unless this is a commit operation */ 1647 TempPte.u.Long = 0; 1648 if (AllocationAttributes & SEC_COMMIT) 1649 { 1650 /* In which case, write down the protection mask in the Prototype PTEs */ 1651 TempPte.u.Soft.Protection = ProtectionMask; 1652 1653 /* For accounting, also mark these pages as being committed */ 1654 NewSegment->NumberOfCommittedPages = PteCount; 1655 } 1656 1657 /* The template PTE itself for the segment should also have the mask set */ 1658 NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask; 1659 1660 /* Write out the prototype PTEs, for now they're simply demand zero */ 1661 #ifdef _WIN64 1662 RtlFillMemoryUlonglong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long); 1663 #else 1664 RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long); 1665 #endif 1666 return STATUS_SUCCESS; 1667 } 1668 1669 NTSTATUS 1670 NTAPI 1671 MiGetFileObjectForSectionAddress( 1672 IN PVOID Address, 1673 OUT PFILE_OBJECT *FileObject) 1674 { 1675 PMMVAD Vad; 1676 PCONTROL_AREA ControlArea; 1677 1678 /* Get the VAD */ 1679 Vad = MiLocateAddress(Address); 1680 if (Vad == NULL) 1681 { 1682 /* Fail, the address does not exist */ 1683 DPRINT1("Invalid address\n"); 1684 return STATUS_INVALID_ADDRESS; 1685 } 1686 1687 /* Check if this is a RosMm memory area */ 1688 if (Vad->u.VadFlags.Spare != 0) 1689 { 1690 PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad; 1691 1692 /* Check if it's a section view (RosMm section) */ 1693 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) 1694 { 1695 /* Get the section pointer to the SECTION_OBJECT */ 1696 *FileObject = MemoryArea->SectionData.Segment->FileObject; 1697 } 1698 else 1699 { 1700 #ifdef NEWCC 1701 ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE); 1702 DPRINT1("Address is a cache section!\n"); 1703 return STATUS_SECTION_NOT_IMAGE; 1704 #else 1705 ASSERT(FALSE); 1706 return STATUS_SECTION_NOT_IMAGE; 1707 #endif 1708 } 1709 } 1710 else 1711 { 1712 /* Make sure it's not a VM VAD */ 1713 if (Vad->u.VadFlags.PrivateMemory == 1) 1714 { 1715 DPRINT1("Address is not a section\n"); 1716 return STATUS_SECTION_NOT_IMAGE; 1717 } 1718 1719 /* Get the control area */ 1720 ControlArea = Vad->ControlArea; 1721 if (!(ControlArea) || !(ControlArea->u.Flags.Image)) 1722 { 1723 DPRINT1("Address is not a section\n"); 1724 return STATUS_SECTION_NOT_IMAGE; 1725 } 1726 1727 /* Get the file object */ 1728 *FileObject = ControlArea->FilePointer; 1729 } 1730 1731 /* Return success */ 1732 return STATUS_SUCCESS; 1733 } 1734 1735 PFILE_OBJECT 1736 NTAPI 1737 MmGetFileObjectForSection(IN PVOID SectionObject) 1738 { 1739 PSECTION Section = SectionObject; 1740 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 1741 ASSERT(SectionObject != NULL); 1742 1743 /* Check if it's an ARM3, or ReactOS section */ 1744 if (MiIsRosSectionObject(SectionObject) == FALSE) 1745 { 1746 /* Return the file pointer stored in the control area */ 1747 return Section->Segment->ControlArea->FilePointer; 1748 } 1749 1750 /* Return the file object */ 1751 return ((PMM_SECTION_SEGMENT)Section->Segment)->FileObject; 1752 } 1753 1754 static 1755 PFILE_OBJECT 1756 MiGetFileObjectForVad( 1757 _In_ PMMVAD Vad) 1758 { 1759 PCONTROL_AREA ControlArea; 1760 PFILE_OBJECT FileObject; 1761 1762 /* Check if this is a RosMm memory area */ 1763 if (Vad->u.VadFlags.Spare != 0) 1764 { 1765 PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad; 1766 1767 /* Check if it's a section view (RosMm section) */ 1768 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) 1769 { 1770 /* Get the section pointer to the SECTION_OBJECT */ 1771 FileObject = MemoryArea->SectionData.Segment->FileObject; 1772 } 1773 else 1774 { 1775 #ifdef NEWCC 1776 ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE); 1777 DPRINT1("VAD is a cache section!\n"); 1778 #else 1779 ASSERT(FALSE); 1780 #endif 1781 return NULL; 1782 } 1783 } 1784 else 1785 { 1786 /* Make sure it's not a VM VAD */ 1787 if (Vad->u.VadFlags.PrivateMemory == 1) 1788 { 1789 DPRINT1("VAD is not a section\n"); 1790 return NULL; 1791 } 1792 1793 /* Get the control area */ 1794 ControlArea = Vad->ControlArea; 1795 if ((ControlArea == NULL) || !ControlArea->u.Flags.Image) 1796 { 1797 DPRINT1("Address is not a section\n"); 1798 return NULL; 1799 } 1800 1801 /* Get the file object */ 1802 FileObject = ControlArea->FilePointer; 1803 } 1804 1805 /* Return the file object */ 1806 return FileObject; 1807 } 1808 1809 VOID 1810 NTAPI 1811 MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation) 1812 { 1813 PSECTION SectionObject; 1814 1815 /* Get the section object of this process*/ 1816 SectionObject = PsGetCurrentProcess()->SectionObject; 1817 ASSERT(SectionObject != NULL); 1818 ASSERT(MiIsRosSectionObject(SectionObject) == TRUE); 1819 1820 if (SectionObject->u.Flags.Image == 0) 1821 { 1822 RtlZeroMemory(ImageInformation, sizeof(*ImageInformation)); 1823 return; 1824 } 1825 1826 /* Return the image information */ 1827 *ImageInformation = ((PMM_IMAGE_SECTION_OBJECT)SectionObject->Segment)->ImageInformation; 1828 } 1829 1830 NTSTATUS 1831 NTAPI 1832 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject, 1833 OUT POBJECT_NAME_INFORMATION *ModuleName) 1834 { 1835 POBJECT_NAME_INFORMATION ObjectNameInfo; 1836 NTSTATUS Status; 1837 ULONG ReturnLength; 1838 1839 /* Allocate memory for our structure */ 1840 ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, TAG_MM); 1841 if (!ObjectNameInfo) return STATUS_NO_MEMORY; 1842 1843 /* Query the name */ 1844 Status = ObQueryNameString(FileObject, 1845 ObjectNameInfo, 1846 1024, 1847 &ReturnLength); 1848 if (!NT_SUCCESS(Status)) 1849 { 1850 /* Failed, free memory */ 1851 DPRINT1("Name query failed\n"); 1852 ExFreePoolWithTag(ObjectNameInfo, TAG_MM); 1853 *ModuleName = NULL; 1854 return Status; 1855 } 1856 1857 /* Success */ 1858 *ModuleName = ObjectNameInfo; 1859 return STATUS_SUCCESS; 1860 } 1861 1862 NTSTATUS 1863 NTAPI 1864 MmGetFileNameForSection(IN PVOID Section, 1865 OUT POBJECT_NAME_INFORMATION *ModuleName) 1866 { 1867 PFILE_OBJECT FileObject; 1868 PSECTION SectionObject = Section; 1869 1870 /* Make sure it's an image section */ 1871 if (SectionObject->u.Flags.Image == 0) 1872 { 1873 /* It's not, fail */ 1874 DPRINT1("Not an image section\n"); 1875 return STATUS_SECTION_NOT_IMAGE; 1876 } 1877 1878 /* Get the file object */ 1879 FileObject = MmGetFileObjectForSection(Section); 1880 return MmGetFileNameForFileObject(FileObject, ModuleName); 1881 } 1882 1883 NTSTATUS 1884 NTAPI 1885 MmGetFileNameForAddress(IN PVOID Address, 1886 OUT PUNICODE_STRING ModuleName) 1887 { 1888 POBJECT_NAME_INFORMATION ModuleNameInformation; 1889 PVOID AddressSpace; 1890 NTSTATUS Status; 1891 PMMVAD Vad; 1892 PFILE_OBJECT FileObject = NULL; 1893 1894 /* Lock address space */ 1895 AddressSpace = MmGetCurrentAddressSpace(); 1896 MmLockAddressSpace(AddressSpace); 1897 1898 /* Get the VAD */ 1899 Vad = MiLocateAddress(Address); 1900 if (Vad == NULL) 1901 { 1902 /* Fail, the address does not exist */ 1903 DPRINT1("No VAD at address %p\n", Address); 1904 MmUnlockAddressSpace(AddressSpace); 1905 return STATUS_INVALID_ADDRESS; 1906 } 1907 1908 /* Get the file object pointer for the VAD */ 1909 FileObject = MiGetFileObjectForVad(Vad); 1910 if (FileObject == NULL) 1911 { 1912 DPRINT1("Failed to get file object for Address %p\n", Address); 1913 MmUnlockAddressSpace(AddressSpace); 1914 return STATUS_SECTION_NOT_IMAGE; 1915 } 1916 1917 /* Reference the file object */ 1918 ObReferenceObject(FileObject); 1919 1920 /* Unlock address space */ 1921 MmUnlockAddressSpace(AddressSpace); 1922 1923 /* Get the filename of the file object */ 1924 Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation); 1925 1926 /* Dereference the file object */ 1927 ObDereferenceObject(FileObject); 1928 1929 /* Check if we were able to get the file object name */ 1930 if (NT_SUCCESS(Status)) 1931 { 1932 /* Init modulename */ 1933 if (!RtlCreateUnicodeString(ModuleName, ModuleNameInformation->Name.Buffer)) 1934 Status = STATUS_INSUFFICIENT_RESOURCES; 1935 1936 /* Free temp taged buffer from MmGetFileNameForFileObject() */ 1937 ExFreePoolWithTag(ModuleNameInformation, TAG_MM); 1938 1939 DPRINT("Found ModuleName %wZ by address %p\n", ModuleName, Address); 1940 } 1941 1942 /* Return status */ 1943 return Status; 1944 } 1945 1946 NTSTATUS 1947 NTAPI 1948 MiQueryMemorySectionName(IN HANDLE ProcessHandle, 1949 IN PVOID BaseAddress, 1950 OUT PVOID MemoryInformation, 1951 IN SIZE_T MemoryInformationLength, 1952 OUT PSIZE_T ReturnLength) 1953 { 1954 PEPROCESS Process; 1955 NTSTATUS Status; 1956 UNICODE_STRING ModuleFileName; 1957 PMEMORY_SECTION_NAME SectionName = NULL; 1958 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 1959 1960 Status = ObReferenceObjectByHandle(ProcessHandle, 1961 PROCESS_QUERY_INFORMATION, 1962 NULL, 1963 PreviousMode, 1964 (PVOID*)(&Process), 1965 NULL); 1966 1967 if (!NT_SUCCESS(Status)) 1968 { 1969 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status); 1970 return Status; 1971 } 1972 1973 Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName); 1974 1975 if (NT_SUCCESS(Status)) 1976 { 1977 SectionName = MemoryInformation; 1978 if (PreviousMode != KernelMode) 1979 { 1980 _SEH2_TRY 1981 { 1982 RtlInitEmptyUnicodeString(&SectionName->SectionFileName, 1983 (PWSTR)(SectionName + 1), 1984 MemoryInformationLength - sizeof(MEMORY_SECTION_NAME)); 1985 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName); 1986 1987 if (ReturnLength) *ReturnLength = ModuleFileName.Length + sizeof(MEMORY_SECTION_NAME); 1988 1989 } 1990 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1991 { 1992 Status = _SEH2_GetExceptionCode(); 1993 } 1994 _SEH2_END; 1995 } 1996 else 1997 { 1998 RtlInitEmptyUnicodeString(&SectionName->SectionFileName, 1999 (PWSTR)(SectionName + 1), 2000 MemoryInformationLength - sizeof(MEMORY_SECTION_NAME)); 2001 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName); 2002 2003 if (ReturnLength) *ReturnLength = ModuleFileName.Length + sizeof(MEMORY_SECTION_NAME); 2004 2005 } 2006 2007 RtlFreeUnicodeString(&ModuleFileName); 2008 } 2009 ObDereferenceObject(Process); 2010 return Status; 2011 } 2012 2013 VOID 2014 NTAPI 2015 MiFlushTbAndCapture(IN PMMVAD FoundVad, 2016 IN PMMPTE PointerPte, 2017 IN ULONG ProtectionMask, 2018 IN PMMPFN Pfn1, 2019 IN BOOLEAN UpdateDirty) 2020 { 2021 MMPTE TempPte, PreviousPte; 2022 KIRQL OldIrql; 2023 BOOLEAN RebuildPte = FALSE; 2024 2025 // 2026 // User for sanity checking later on 2027 // 2028 PreviousPte = *PointerPte; 2029 2030 // 2031 // Build the PTE and acquire the PFN lock 2032 // 2033 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 2034 PointerPte, 2035 ProtectionMask, 2036 PreviousPte.u.Hard.PageFrameNumber); 2037 OldIrql = MiAcquirePfnLock(); 2038 2039 // 2040 // We don't support I/O mappings in this path yet 2041 // 2042 ASSERT(Pfn1 != NULL); 2043 ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined); 2044 2045 // 2046 // Make sure new protection mask doesn't get in conflict and fix it if it does 2047 // 2048 if (Pfn1->u3.e1.CacheAttribute == MiCached) 2049 { 2050 // 2051 // This is a cached PFN 2052 // 2053 if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) 2054 { 2055 RebuildPte = TRUE; 2056 ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS); 2057 } 2058 } 2059 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached) 2060 { 2061 // 2062 // This is a non-cached PFN 2063 // 2064 if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE) 2065 { 2066 RebuildPte = TRUE; 2067 ProtectionMask &= ~MM_NOACCESS; 2068 ProtectionMask |= MM_NOCACHE; 2069 } 2070 } 2071 2072 if (RebuildPte) 2073 { 2074 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 2075 PointerPte, 2076 ProtectionMask, 2077 PreviousPte.u.Hard.PageFrameNumber); 2078 } 2079 2080 // 2081 // Write the new PTE, making sure we are only changing the bits 2082 // 2083 MI_UPDATE_VALID_PTE(PointerPte, TempPte); 2084 2085 // 2086 // Flush the TLB 2087 // 2088 ASSERT(PreviousPte.u.Hard.Valid == 1); 2089 KeFlushCurrentTb(); 2090 ASSERT(PreviousPte.u.Hard.Valid == 1); 2091 2092 // 2093 // Windows updates the relevant PFN1 information, we currently don't. 2094 // 2095 if (UpdateDirty && PreviousPte.u.Hard.Dirty) 2096 { 2097 if (!Pfn1->u3.e1.Modified) 2098 { 2099 DPRINT1("FIXME: Mark PFN as dirty\n"); 2100 } 2101 } 2102 2103 // 2104 // Not supported in ARM3 2105 // 2106 ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch); 2107 2108 // 2109 // Release the PFN lock, we are done 2110 // 2111 MiReleasePfnLock(OldIrql); 2112 } 2113 2114 // 2115 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support 2116 // 2117 NTSTATUS 2118 NTAPI 2119 MiSetProtectionOnSection(IN PEPROCESS Process, 2120 IN PMMVAD FoundVad, 2121 IN PVOID StartingAddress, 2122 IN PVOID EndingAddress, 2123 IN ULONG NewProtect, 2124 OUT PULONG CapturedOldProtect, 2125 IN ULONG DontCharge, 2126 OUT PULONG Locked) 2127 { 2128 PMMPTE PointerPte, LastPte; 2129 MMPTE TempPte, PteContents; 2130 PMMPDE PointerPde; 2131 PMMPFN Pfn1; 2132 ULONG ProtectionMask, QuotaCharge = 0; 2133 PETHREAD Thread = PsGetCurrentThread(); 2134 PAGED_CODE(); 2135 2136 // 2137 // Tell caller nothing is being locked 2138 // 2139 *Locked = FALSE; 2140 2141 // 2142 // This function should only be used for section VADs. Windows ASSERT */ 2143 // 2144 ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0); 2145 2146 // 2147 // We don't support these features in ARM3 2148 // 2149 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap); 2150 ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0); 2151 2152 // 2153 // Convert and validate the protection mask 2154 // 2155 ProtectionMask = MiMakeProtectionMask(NewProtect); 2156 if (ProtectionMask == MM_INVALID_PROTECTION) 2157 { 2158 DPRINT1("Invalid section protect\n"); 2159 return STATUS_INVALID_PAGE_PROTECTION; 2160 } 2161 2162 // 2163 // Get the PTE and PDE for the address, as well as the final PTE 2164 // 2165 MiLockProcessWorkingSetUnsafe(Process, Thread); 2166 PointerPde = MiAddressToPde(StartingAddress); 2167 PointerPte = MiAddressToPte(StartingAddress); 2168 LastPte = MiAddressToPte(EndingAddress); 2169 2170 // 2171 // Make the PDE valid, and check the status of the first PTE 2172 // 2173 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2174 if (PointerPte->u.Long) 2175 { 2176 // 2177 // Not supported in ARM3 2178 // 2179 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical); 2180 2181 // 2182 // Capture the page protection and make the PDE valid 2183 // 2184 *CapturedOldProtect = MiGetPageProtection(PointerPte); 2185 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2186 } 2187 else 2188 { 2189 // 2190 // Only pagefile-backed section VADs are supported for now 2191 // 2192 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap); 2193 2194 // 2195 // Grab the old protection from the VAD itself 2196 // 2197 *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection]; 2198 } 2199 2200 // 2201 // Loop all the PTEs now 2202 // 2203 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2204 while (PointerPte <= LastPte) 2205 { 2206 // 2207 // Check if we've crossed a PDE boundary and make the new PDE valid too 2208 // 2209 if (MiIsPteOnPdeBoundary(PointerPte)) 2210 { 2211 PointerPde = MiPteToPde(PointerPte); 2212 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2213 } 2214 2215 // 2216 // Capture the PTE and see what we're dealing with 2217 // 2218 PteContents = *PointerPte; 2219 if (PteContents.u.Long == 0) 2220 { 2221 // 2222 // This used to be a zero PTE and it no longer is, so we must add a 2223 // reference to the pagetable. 2224 // 2225 MiIncrementPageTableReferences(MiPteToAddress(PointerPte)); 2226 2227 // 2228 // Create the demand-zero prototype PTE 2229 // 2230 TempPte = PrototypePte; 2231 TempPte.u.Soft.Protection = ProtectionMask; 2232 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2233 } 2234 else if (PteContents.u.Hard.Valid == 1) 2235 { 2236 // 2237 // Get the PFN entry 2238 // 2239 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents)); 2240 2241 // 2242 // We don't support these yet 2243 // 2244 ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0); 2245 ASSERT(Pfn1->u3.e1.PrototypePte == 0); 2246 2247 // 2248 // Write the protection mask and write it with a TLB flush 2249 // 2250 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask; 2251 MiFlushTbAndCapture(FoundVad, 2252 PointerPte, 2253 ProtectionMask, 2254 Pfn1, 2255 TRUE); 2256 } 2257 else 2258 { 2259 // 2260 // We don't support these cases yet 2261 // 2262 ASSERT(PteContents.u.Soft.Prototype == 0); 2263 ASSERT(PteContents.u.Soft.Transition == 0); 2264 2265 // 2266 // The PTE is already demand-zero, just update the protection mask 2267 // 2268 PointerPte->u.Soft.Protection = ProtectionMask; 2269 } 2270 2271 PointerPte++; 2272 } 2273 2274 // 2275 // Unlock the working set and update quota charges if needed, then return 2276 // 2277 MiUnlockProcessWorkingSetUnsafe(Process, Thread); 2278 if ((QuotaCharge > 0) && (!DontCharge)) 2279 { 2280 FoundVad->u.VadFlags.CommitCharge -= QuotaCharge; 2281 Process->CommitCharge -= QuotaCharge; 2282 } 2283 return STATUS_SUCCESS; 2284 } 2285 2286 VOID 2287 NTAPI 2288 MiRemoveMappedPtes(IN PVOID BaseAddress, 2289 IN ULONG NumberOfPtes, 2290 IN PCONTROL_AREA ControlArea, 2291 IN PMMSUPPORT Ws) 2292 { 2293 PMMPTE PointerPte, ProtoPte;//, FirstPte; 2294 PMMPDE PointerPde, SystemMapPde; 2295 PMMPFN Pfn1, Pfn2; 2296 MMPTE PteContents; 2297 KIRQL OldIrql; 2298 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress); 2299 2300 ASSERT(Ws == NULL); 2301 2302 /* Get the PTE and loop each one */ 2303 PointerPte = MiAddressToPte(BaseAddress); 2304 //FirstPte = PointerPte; 2305 while (NumberOfPtes) 2306 { 2307 /* Check if the PTE is already valid */ 2308 PteContents = *PointerPte; 2309 if (PteContents.u.Hard.Valid == 1) 2310 { 2311 /* Get the PFN entry */ 2312 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents)); 2313 2314 /* Get the PTE */ 2315 PointerPde = MiPteToPde(PointerPte); 2316 2317 /* Lock the PFN database and make sure this isn't a mapped file */ 2318 OldIrql = MiAcquirePfnLock(); 2319 ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0); 2320 2321 /* Mark the page as modified accordingly */ 2322 if (MI_IS_PAGE_DIRTY(&PteContents)) 2323 Pfn1->u3.e1.Modified = 1; 2324 2325 /* Was the PDE invalid */ 2326 if (PointerPde->u.Long == 0) 2327 { 2328 #if (_MI_PAGING_LEVELS == 2) 2329 /* Find the system double-mapped PDE that describes this mapping */ 2330 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)]; 2331 2332 /* Make it valid */ 2333 ASSERT(SystemMapPde->u.Hard.Valid == 1); 2334 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde); 2335 #else 2336 DBG_UNREFERENCED_LOCAL_VARIABLE(SystemMapPde); 2337 ASSERT(FALSE); 2338 #endif 2339 } 2340 2341 /* Dereference the PDE and the PTE */ 2342 Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde)); 2343 MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde)); 2344 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2); 2345 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents)); 2346 2347 /* Release the PFN lock */ 2348 MiReleasePfnLock(OldIrql); 2349 } 2350 else 2351 { 2352 /* Windows ASSERT */ 2353 ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1)); 2354 2355 /* Check if this is a prototype pointer PTE */ 2356 if (PteContents.u.Soft.Prototype == 1) 2357 { 2358 /* Get the prototype PTE */ 2359 ProtoPte = MiProtoPteToPte(&PteContents); 2360 2361 /* We don't support anything else atm */ 2362 ASSERT(ProtoPte->u.Long == 0); 2363 } 2364 } 2365 2366 /* Make the PTE into a zero PTE */ 2367 PointerPte->u.Long = 0; 2368 2369 /* Move to the next PTE */ 2370 PointerPte++; 2371 NumberOfPtes--; 2372 } 2373 2374 /* Flush the TLB */ 2375 KeFlushCurrentTb(); 2376 2377 /* Acquire the PFN lock */ 2378 OldIrql = MiAcquirePfnLock(); 2379 2380 /* Decrement the accounting counters */ 2381 ControlArea->NumberOfUserReferences--; 2382 ControlArea->NumberOfMappedViews--; 2383 2384 /* Check if we should destroy the CA and release the lock */ 2385 MiCheckControlArea(ControlArea, OldIrql); 2386 } 2387 2388 ULONG 2389 NTAPI 2390 MiRemoveFromSystemSpace(IN PMMSESSION Session, 2391 IN PVOID Base, 2392 OUT PCONTROL_AREA *ControlArea) 2393 { 2394 ULONG Hash, Size, Count = 0; 2395 ULONG_PTR Entry; 2396 PAGED_CODE(); 2397 2398 /* Compute the hash for this entry and loop trying to find it */ 2399 Entry = (ULONG_PTR)Base >> 16; 2400 Hash = Entry % Session->SystemSpaceHashKey; 2401 while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry) 2402 { 2403 /* Check if we overflew past the end of the hash table */ 2404 if (++Hash >= Session->SystemSpaceHashSize) 2405 { 2406 /* Reset the hash to zero and keep searching from the bottom */ 2407 Hash = 0; 2408 if (++Count == 2) 2409 { 2410 /* But if we overflew twice, then this is not a real mapping */ 2411 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW, 2412 (ULONG_PTR)Base, 2413 1, 2414 0, 2415 0); 2416 } 2417 } 2418 } 2419 2420 /* One less entry */ 2421 Session->SystemSpaceHashEntries--; 2422 2423 /* Extract the size and clear the entry */ 2424 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF; 2425 Session->SystemSpaceViewTable[Hash].Entry = 0; 2426 2427 /* Return the control area and the size */ 2428 *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea; 2429 return Size; 2430 } 2431 2432 NTSTATUS 2433 NTAPI 2434 MiUnmapViewInSystemSpace(IN PMMSESSION Session, 2435 IN PVOID MappedBase) 2436 { 2437 ULONG Size; 2438 PCONTROL_AREA ControlArea; 2439 PAGED_CODE(); 2440 2441 /* Remove this mapping */ 2442 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer); 2443 Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea); 2444 2445 /* Clear the bits for this mapping */ 2446 RtlClearBits(Session->SystemSpaceBitMap, 2447 (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16), 2448 Size); 2449 2450 /* Convert the size from a bit size into the actual size */ 2451 Size = Size * (_64K >> PAGE_SHIFT); 2452 2453 /* Remove the PTEs now */ 2454 MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL); 2455 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 2456 2457 /* Return success */ 2458 return STATUS_SUCCESS; 2459 } 2460 2461 /* PUBLIC FUNCTIONS ***********************************************************/ 2462 2463 /* 2464 * @implemented 2465 */ 2466 NTSTATUS 2467 NTAPI 2468 MmCreateArm3Section(OUT PVOID *SectionObject, 2469 IN ACCESS_MASK DesiredAccess, 2470 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, 2471 IN PLARGE_INTEGER InputMaximumSize, 2472 IN ULONG SectionPageProtection, 2473 IN ULONG AllocationAttributes, 2474 IN HANDLE FileHandle OPTIONAL, 2475 IN PFILE_OBJECT FileObject OPTIONAL) 2476 { 2477 SECTION Section; 2478 PSECTION NewSection; 2479 PSUBSECTION Subsection; 2480 PSEGMENT NewSegment, Segment; 2481 NTSTATUS Status; 2482 PCONTROL_AREA ControlArea; 2483 ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge; 2484 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 2485 BOOLEAN FileLock = FALSE, KernelCall = FALSE; 2486 KIRQL OldIrql; 2487 PFILE_OBJECT File; 2488 BOOLEAN UserRefIncremented = FALSE; 2489 PVOID PreviousSectionPointer; 2490 2491 /* Make the same sanity checks that the Nt interface should've validated */ 2492 ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED | 2493 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE | 2494 SEC_NO_CHANGE)) == 0); 2495 ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0); 2496 ASSERT(!((AllocationAttributes & SEC_IMAGE) && 2497 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | 2498 SEC_NOCACHE | SEC_NO_CHANGE)))); 2499 ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))); 2500 ASSERT(!((SectionPageProtection & PAGE_NOCACHE) || 2501 (SectionPageProtection & PAGE_WRITECOMBINE) || 2502 (SectionPageProtection & PAGE_GUARD) || 2503 (SectionPageProtection & PAGE_NOACCESS))); 2504 2505 /* Convert section flag to page flag */ 2506 if (AllocationAttributes & SEC_NOCACHE) 2507 SectionPageProtection |= PAGE_NOCACHE; 2508 2509 /* Check to make sure the protection is correct. Nt* does this already */ 2510 ProtectionMask = MiMakeProtectionMask(SectionPageProtection); 2511 if (ProtectionMask == MM_INVALID_PROTECTION) 2512 { 2513 DPRINT1("Invalid protection mask\n"); 2514 return STATUS_INVALID_PAGE_PROTECTION; 2515 } 2516 2517 /* Check if this is going to be a data or image backed file section */ 2518 if ((FileHandle) || (FileObject)) 2519 { 2520 /* These cannot be mapped with large pages */ 2521 if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6; 2522 2523 /* For now, only support the mechanism through a file handle */ 2524 ASSERT(FileObject == NULL); 2525 2526 /* Reference the file handle to get the object */ 2527 Status = ObReferenceObjectByHandle(FileHandle, 2528 MmMakeFileAccess[ProtectionMask], 2529 IoFileObjectType, 2530 PreviousMode, 2531 (PVOID*)&File, 2532 NULL); 2533 if (!NT_SUCCESS(Status)) return Status; 2534 2535 /* Make sure Cc has been doing its job */ 2536 if (!File->SectionObjectPointer) 2537 { 2538 /* This is not a valid file system-based file, fail */ 2539 ObDereferenceObject(File); 2540 return STATUS_INVALID_FILE_FOR_SECTION; 2541 } 2542 2543 /* Image-file backed sections are not yet supported */ 2544 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2545 2546 /* Compute the size of the control area, and allocate it */ 2547 ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION); 2548 ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM'); 2549 if (!ControlArea) 2550 { 2551 ObDereferenceObject(File); 2552 return STATUS_INSUFFICIENT_RESOURCES; 2553 } 2554 2555 /* Zero it out */ 2556 RtlZeroMemory(ControlArea, ControlAreaSize); 2557 2558 /* Did we get a handle, or an object? */ 2559 if (FileHandle) 2560 { 2561 /* We got a file handle so we have to lock down the file */ 2562 #if 0 2563 Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection); 2564 if (!NT_SUCCESS(Status)) 2565 { 2566 ExFreePool(ControlArea); 2567 ObDereferenceObject(File); 2568 return Status; 2569 } 2570 #else 2571 /* ReactOS doesn't support this API yet, so do nothing */ 2572 Status = STATUS_SUCCESS; 2573 #endif 2574 /* Update the top-level IRP so that drivers know what's happening */ 2575 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP); 2576 FileLock = TRUE; 2577 } 2578 2579 /* Lock the PFN database while we play with the section pointers */ 2580 OldIrql = MiAcquirePfnLock(); 2581 2582 /* Image-file backed sections are not yet supported */ 2583 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2584 2585 /* There should not already be a control area for this file */ 2586 ASSERT(File->SectionObjectPointer->DataSectionObject == NULL); 2587 NewSegment = NULL; 2588 2589 /* Write down that this CA is being created, and set it */ 2590 ControlArea->u.Flags.BeingCreated = TRUE; 2591 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2592 PreviousSectionPointer = File->SectionObjectPointer; 2593 File->SectionObjectPointer->DataSectionObject = ControlArea; 2594 2595 /* We can release the PFN lock now */ 2596 MiReleasePfnLock(OldIrql); 2597 2598 /* We don't support previously-mapped file */ 2599 ASSERT(NewSegment == NULL); 2600 2601 /* Image-file backed sections are not yet supported */ 2602 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2603 2604 /* So we always create a data file map */ 2605 Status = MiCreateDataFileMap(File, 2606 &Segment, 2607 (PSIZE_T)InputMaximumSize, 2608 SectionPageProtection, 2609 AllocationAttributes, 2610 KernelCall); 2611 if (!NT_SUCCESS(Status)) 2612 { 2613 /* Lock the PFN database while we play with the section pointers */ 2614 OldIrql = MiAcquirePfnLock(); 2615 2616 /* Reset the waiting-for-deletion event */ 2617 ASSERT(ControlArea->WaitingForDeletion == NULL); 2618 ControlArea->WaitingForDeletion = NULL; 2619 2620 /* Set the file pointer NULL flag */ 2621 ASSERT(ControlArea->u.Flags.FilePointerNull == 0); 2622 ControlArea->u.Flags.FilePointerNull = TRUE; 2623 2624 /* Delete the data section object */ 2625 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2626 File->SectionObjectPointer->DataSectionObject = NULL; 2627 2628 /* No longer being created */ 2629 ControlArea->u.Flags.BeingCreated = FALSE; 2630 2631 /* We can release the PFN lock now */ 2632 MiReleasePfnLock(OldIrql); 2633 2634 /* Check if we locked and set the IRP */ 2635 if (FileLock) 2636 { 2637 /* Undo */ 2638 IoSetTopLevelIrp(NULL); 2639 //FsRtlReleaseFile(File); 2640 } 2641 2642 /* Free the control area and de-ref the file object */ 2643 ExFreePool(ControlArea); 2644 ObDereferenceObject(File); 2645 2646 /* All done */ 2647 return Status; 2648 } 2649 2650 /* On success, we expect this */ 2651 ASSERT(PreviousSectionPointer == File->SectionObjectPointer); 2652 2653 /* Check if a maximum size was specified */ 2654 if (!InputMaximumSize->QuadPart) 2655 { 2656 /* Nope, use the segment size */ 2657 Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment; 2658 } 2659 else 2660 { 2661 /* Yep, use the entered size */ 2662 Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart; 2663 } 2664 } 2665 else 2666 { 2667 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */ 2668 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION; 2669 2670 /* Not yet supported */ 2671 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0); 2672 2673 /* So this must be a pagefile-backed section, create the mappings needed */ 2674 Status = MiCreatePagingFileMap(&NewSegment, 2675 InputMaximumSize, 2676 ProtectionMask, 2677 AllocationAttributes); 2678 if (!NT_SUCCESS(Status)) return Status; 2679 2680 /* Set the size here, and read the control area */ 2681 Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment; 2682 ControlArea = NewSegment->ControlArea; 2683 2684 /* MiCreatePagingFileMap increments user references */ 2685 UserRefIncremented = TRUE; 2686 } 2687 2688 /* Did we already have a segment? */ 2689 if (!NewSegment) 2690 { 2691 /* This must be the file path and we created a segment */ 2692 NewSegment = Segment; 2693 ASSERT(File != NULL); 2694 2695 /* Acquire the PFN lock while we set control area flags */ 2696 OldIrql = MiAcquirePfnLock(); 2697 2698 /* We don't support this race condition yet, so assume no waiters */ 2699 ASSERT(ControlArea->WaitingForDeletion == NULL); 2700 ControlArea->WaitingForDeletion = NULL; 2701 2702 /* Image-file backed sections are not yet supported, nor ROM images */ 2703 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2704 ASSERT(Segment->ControlArea->u.Flags.Rom == 0); 2705 2706 /* Take off the being created flag, and then release the lock */ 2707 ControlArea->u.Flags.BeingCreated = FALSE; 2708 MiReleasePfnLock(OldIrql); 2709 } 2710 2711 /* Check if we locked the file earlier */ 2712 if (FileLock) 2713 { 2714 /* Reset the top-level IRP and release the lock */ 2715 IoSetTopLevelIrp(NULL); 2716 //FsRtlReleaseFile(File); 2717 FileLock = FALSE; 2718 } 2719 2720 /* Set the initial section object data */ 2721 Section.InitialPageProtection = SectionPageProtection; 2722 2723 /* The mapping created a control area and segment, save the flags */ 2724 Section.Segment = NewSegment; 2725 Section.u.LongFlags = ControlArea->u.LongFlags; 2726 2727 /* Check if this is a user-mode read-write non-image file mapping */ 2728 if (!(FileObject) && 2729 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) && 2730 !(ControlArea->u.Flags.Image) && 2731 (ControlArea->FilePointer)) 2732 { 2733 /* Add a reference and set the flag */ 2734 Section.u.Flags.UserWritable = TRUE; 2735 InterlockedIncrement((volatile LONG*)&ControlArea->WritableUserReferences); 2736 } 2737 2738 /* Check for image mappings or page file mappings */ 2739 if ((ControlArea->u.Flags.Image) || !(ControlArea->FilePointer)) 2740 { 2741 /* Charge the segment size, and allocate a subsection */ 2742 PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE); 2743 Size = sizeof(SUBSECTION); 2744 } 2745 else 2746 { 2747 /* Charge nothing, and allocate a mapped subsection */ 2748 PagedCharge = 0; 2749 Size = sizeof(MSUBSECTION); 2750 } 2751 2752 /* Check if this is a normal CA */ 2753 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 2754 ASSERT(ControlArea->u.Flags.Rom == 0); 2755 2756 /* Charge only a CA, and the subsection is right after */ 2757 NonPagedCharge = sizeof(CONTROL_AREA); 2758 Subsection = (PSUBSECTION)(ControlArea + 1); 2759 2760 /* We only support single-subsection mappings */ 2761 NonPagedCharge += Size; 2762 ASSERT(Subsection->NextSubsection == NULL); 2763 2764 /* Create the actual section object, with enough space for the prototype PTEs */ 2765 Status = ObCreateObject(PreviousMode, 2766 MmSectionObjectType, 2767 ObjectAttributes, 2768 PreviousMode, 2769 NULL, 2770 sizeof(SECTION), 2771 PagedCharge, 2772 NonPagedCharge, 2773 (PVOID*)&NewSection); 2774 if (!NT_SUCCESS(Status)) 2775 { 2776 /* Check if this is a user-mode read-write non-image file mapping */ 2777 if (!(FileObject) && 2778 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) && 2779 !(ControlArea->u.Flags.Image) && 2780 (ControlArea->FilePointer)) 2781 { 2782 /* Remove a reference and check the flag */ 2783 ASSERT(Section.u.Flags.UserWritable == 1); 2784 InterlockedDecrement((volatile LONG*)&ControlArea->WritableUserReferences); 2785 } 2786 2787 /* Check if a user reference was added */ 2788 if (UserRefIncremented) 2789 { 2790 /* Acquire the PFN lock while we change counters */ 2791 OldIrql = MiAcquirePfnLock(); 2792 2793 /* Decrement the accounting counters */ 2794 ControlArea->NumberOfSectionReferences--; 2795 ASSERT((LONG)ControlArea->NumberOfUserReferences > 0); 2796 ControlArea->NumberOfUserReferences--; 2797 2798 /* Check if we should destroy the CA and release the lock */ 2799 MiCheckControlArea(ControlArea, OldIrql); 2800 } 2801 2802 /* Return the failure code */ 2803 return Status; 2804 } 2805 2806 /* NOTE: Past this point, all failures will be handled by Ob upon ref->0 */ 2807 2808 /* Now copy the local section object from the stack into this new object */ 2809 RtlCopyMemory(NewSection, &Section, sizeof(SECTION)); 2810 NewSection->Address.StartingVpn = 0; 2811 2812 /* For now, only user calls are supported */ 2813 ASSERT(KernelCall == FALSE); 2814 NewSection->u.Flags.UserReference = TRUE; 2815 2816 /* Is this a "based" allocation, in which all mappings are identical? */ 2817 if (AllocationAttributes & SEC_BASED) 2818 { 2819 /* Lock the VAD tree during the search */ 2820 KeAcquireGuardedMutex(&MmSectionBasedMutex); 2821 2822 /* Is it a brand new ControArea ? */ 2823 if (ControlArea->u.Flags.BeingCreated == 1) 2824 { 2825 ASSERT(ControlArea->u.Flags.Based == 1); 2826 /* Then we must find a global address, top-down */ 2827 Status = MiFindEmptyAddressRangeDownBasedTree((SIZE_T)ControlArea->Segment->SizeOfSegment, 2828 (ULONG_PTR)MmHighSectionBase, 2829 _64K, 2830 &MmSectionBasedRoot, 2831 (ULONG_PTR*)&ControlArea->Segment->BasedAddress); 2832 2833 if (!NT_SUCCESS(Status)) 2834 { 2835 /* No way to find a valid range. */ 2836 KeReleaseGuardedMutex(&MmSectionBasedMutex); 2837 ControlArea->u.Flags.Based = 0; 2838 NewSection->u.Flags.Based = 0; 2839 ObDereferenceObject(NewSection); 2840 return Status; 2841 } 2842 2843 /* Compute the ending address and insert it into the VAD tree */ 2844 NewSection->Address.StartingVpn = (ULONG_PTR)ControlArea->Segment->BasedAddress; 2845 NewSection->Address.EndingVpn = NewSection->Address.StartingVpn + NewSection->SizeOfSection.LowPart - 1; 2846 MiInsertBasedSection(NewSection); 2847 } 2848 else 2849 { 2850 /* FIXME : Should we deny section creation if SEC_BASED is not set ? Can we have two different section objects on the same based address ? Investigate !*/ 2851 ASSERT(FALSE); 2852 } 2853 2854 KeReleaseGuardedMutex(&MmSectionBasedMutex); 2855 } 2856 2857 /* The control area is not being created anymore */ 2858 if (ControlArea->u.Flags.BeingCreated == 1) 2859 { 2860 /* Acquire the PFN lock while we set control area flags */ 2861 OldIrql = MiAcquirePfnLock(); 2862 2863 /* Take off the being created flag, and then release the lock */ 2864 ControlArea->u.Flags.BeingCreated = 0; 2865 NewSection->u.Flags.BeingCreated = 0; 2866 2867 MiReleasePfnLock(OldIrql); 2868 } 2869 2870 /* Migrate the attribute into a flag */ 2871 if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE; 2872 2873 /* If R/W access is not requested, this might eventually become a CoW mapping */ 2874 if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE))) 2875 { 2876 NewSection->u.Flags.CopyOnWrite = TRUE; 2877 } 2878 2879 /* Write down if this was a kernel call */ 2880 ControlArea->u.Flags.WasPurged |= KernelCall; 2881 ASSERT(ControlArea->u.Flags.WasPurged == FALSE); 2882 2883 /* Make sure the segment and the section are the same size, or the section is smaller */ 2884 ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment); 2885 2886 /* Return the object and the creation status */ 2887 *SectionObject = (PVOID)NewSection; 2888 return Status; 2889 } 2890 2891 /* 2892 * @implemented 2893 */ 2894 NTSTATUS 2895 NTAPI 2896 MmMapViewOfArm3Section(IN PVOID SectionObject, 2897 IN PEPROCESS Process, 2898 IN OUT PVOID *BaseAddress, 2899 IN ULONG_PTR ZeroBits, 2900 IN SIZE_T CommitSize, 2901 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, 2902 IN OUT PSIZE_T ViewSize, 2903 IN SECTION_INHERIT InheritDisposition, 2904 IN ULONG AllocationType, 2905 IN ULONG Protect) 2906 { 2907 KAPC_STATE ApcState; 2908 BOOLEAN Attached = FALSE; 2909 PSECTION Section; 2910 PCONTROL_AREA ControlArea; 2911 ULONG ProtectionMask; 2912 NTSTATUS Status; 2913 ULONG64 CalculatedViewSize; 2914 PAGED_CODE(); 2915 2916 /* Get the segment and control area */ 2917 Section = (PSECTION)SectionObject; 2918 ControlArea = Section->Segment->ControlArea; 2919 2920 /* These flags/states are not yet supported by ARM3 */ 2921 ASSERT(Section->u.Flags.Image == 0); 2922 ASSERT(Section->u.Flags.NoCache == 0); 2923 ASSERT(Section->u.Flags.WriteCombined == 0); 2924 ASSERT(ControlArea->u.Flags.PhysicalMemory == 0); 2925 2926 /* FIXME */ 2927 if ((AllocationType & MEM_RESERVE) != 0) 2928 { 2929 DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n"); 2930 return STATUS_NOT_IMPLEMENTED; 2931 } 2932 2933 /* Check if the mapping protection is compatible with the create */ 2934 if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect)) 2935 { 2936 DPRINT1("Mapping protection is incompatible\n"); 2937 return STATUS_SECTION_PROTECTION; 2938 } 2939 2940 /* Check if the offset and size would cause an overflow */ 2941 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) < 2942 (ULONG64)SectionOffset->QuadPart) 2943 { 2944 DPRINT1("Section offset overflows\n"); 2945 return STATUS_INVALID_VIEW_SIZE; 2946 } 2947 2948 /* Check if the offset and size are bigger than the section itself */ 2949 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) > 2950 (ULONG64)Section->SizeOfSection.QuadPart) 2951 { 2952 DPRINT1("Section offset is larger than section\n"); 2953 return STATUS_INVALID_VIEW_SIZE; 2954 } 2955 2956 /* Check if the caller did not specify a view size */ 2957 if (!(*ViewSize)) 2958 { 2959 /* Compute it for the caller */ 2960 CalculatedViewSize = Section->SizeOfSection.QuadPart - 2961 SectionOffset->QuadPart; 2962 2963 /* Check if it's larger than 4GB or overflows into kernel-mode */ 2964 if (!NT_SUCCESS(RtlULongLongToSIZET(CalculatedViewSize, ViewSize)) || 2965 (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < CalculatedViewSize)) 2966 { 2967 DPRINT1("Section view won't fit\n"); 2968 return STATUS_INVALID_VIEW_SIZE; 2969 } 2970 } 2971 2972 /* Check if the commit size is larger than the view size */ 2973 if (CommitSize > *ViewSize) 2974 { 2975 DPRINT1("Attempting to commit more than the view itself\n"); 2976 return STATUS_INVALID_PARAMETER_5; 2977 } 2978 2979 /* Check if the view size is larger than the section */ 2980 if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart) 2981 { 2982 DPRINT1("The view is larger than the section\n"); 2983 return STATUS_INVALID_VIEW_SIZE; 2984 } 2985 2986 /* Compute and validate the protection mask */ 2987 ProtectionMask = MiMakeProtectionMask(Protect); 2988 if (ProtectionMask == MM_INVALID_PROTECTION) 2989 { 2990 DPRINT1("The protection is invalid\n"); 2991 return STATUS_INVALID_PAGE_PROTECTION; 2992 } 2993 2994 /* We only handle pagefile-backed sections, which cannot be writecombined */ 2995 if (Protect & PAGE_WRITECOMBINE) 2996 { 2997 DPRINT1("Cannot write combine a pagefile-backed section\n"); 2998 return STATUS_INVALID_PARAMETER_10; 2999 } 3000 3001 /* Start by attaching to the current process if needed */ 3002 if (PsGetCurrentProcess() != Process) 3003 { 3004 KeStackAttachProcess(&Process->Pcb, &ApcState); 3005 Attached = TRUE; 3006 } 3007 3008 /* Do the actual mapping */ 3009 Status = MiMapViewOfDataSection(ControlArea, 3010 Process, 3011 BaseAddress, 3012 SectionOffset, 3013 ViewSize, 3014 Section, 3015 InheritDisposition, 3016 ProtectionMask, 3017 CommitSize, 3018 ZeroBits, 3019 AllocationType); 3020 3021 /* Detach if needed, then return status */ 3022 if (Attached) KeUnstackDetachProcess(&ApcState); 3023 return Status; 3024 } 3025 3026 /* 3027 * @unimplemented 3028 */ 3029 BOOLEAN 3030 NTAPI 3031 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer) 3032 { 3033 UNIMPLEMENTED; 3034 return FALSE; 3035 } 3036 3037 /* 3038 * @unimplemented 3039 */ 3040 BOOLEAN 3041 NTAPI 3042 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer, 3043 IN BOOLEAN DelayClose) 3044 { 3045 UNIMPLEMENTED; 3046 return FALSE; 3047 } 3048 3049 /* 3050 * @implemented 3051 */ 3052 NTSTATUS 3053 NTAPI 3054 MmMapViewInSessionSpace(IN PVOID Section, 3055 OUT PVOID *MappedBase, 3056 IN OUT PSIZE_T ViewSize) 3057 { 3058 PAGED_CODE(); 3059 LARGE_INTEGER SectionOffset; 3060 3061 // HACK 3062 if (MiIsRosSectionObject(Section)) 3063 { 3064 return MmMapViewInSystemSpace(Section, MappedBase, ViewSize); 3065 } 3066 3067 /* Process must be in a session */ 3068 if (PsGetCurrentProcess()->ProcessInSession == FALSE) 3069 { 3070 DPRINT1("Process is not in session\n"); 3071 return STATUS_NOT_MAPPED_VIEW; 3072 } 3073 3074 /* Use the system space API, but with the session view instead */ 3075 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); 3076 SectionOffset.QuadPart = 0; 3077 return MiMapViewInSystemSpace(Section, 3078 &MmSessionSpace->Session, 3079 MappedBase, 3080 ViewSize, 3081 &SectionOffset); 3082 } 3083 3084 /* 3085 * @implemented 3086 */ 3087 NTSTATUS 3088 NTAPI 3089 MmUnmapViewInSessionSpace(IN PVOID MappedBase) 3090 { 3091 PAGED_CODE(); 3092 3093 // HACK 3094 if (!MI_IS_SESSION_ADDRESS(MappedBase)) 3095 { 3096 return MmUnmapViewInSystemSpace(MappedBase); 3097 } 3098 3099 /* Process must be in a session */ 3100 if (PsGetCurrentProcess()->ProcessInSession == FALSE) 3101 { 3102 DPRINT1("Proess is not in session\n"); 3103 return STATUS_NOT_MAPPED_VIEW; 3104 } 3105 3106 /* Use the system space API, but with the session view instead */ 3107 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); 3108 return MiUnmapViewInSystemSpace(&MmSessionSpace->Session, 3109 MappedBase); 3110 } 3111 3112 /* 3113 * @implemented 3114 */ 3115 NTSTATUS 3116 NTAPI 3117 MmUnmapViewOfSection(IN PEPROCESS Process, 3118 IN PVOID BaseAddress) 3119 { 3120 return MiUnmapViewOfSection(Process, BaseAddress, 0); 3121 } 3122 3123 /* 3124 * @implemented 3125 */ 3126 NTSTATUS 3127 NTAPI 3128 MmUnmapViewInSystemSpace(IN PVOID MappedBase) 3129 { 3130 PMEMORY_AREA MemoryArea; 3131 PAGED_CODE(); 3132 3133 /* Was this mapped by RosMm? */ 3134 MmLockAddressSpace(MmGetKernelAddressSpace()); 3135 MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase); 3136 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) 3137 { 3138 NTSTATUS Status = MiRosUnmapViewInSystemSpace(MappedBase); 3139 MmUnlockAddressSpace(MmGetKernelAddressSpace()); 3140 return Status; 3141 } 3142 MmUnlockAddressSpace(MmGetKernelAddressSpace()); 3143 3144 /* It was not, call the ARM3 routine */ 3145 return MiUnmapViewInSystemSpace(&MmSession, MappedBase); 3146 } 3147 3148 /* 3149 * @implemented 3150 */ 3151 NTSTATUS 3152 NTAPI 3153 MmCommitSessionMappedView(IN PVOID MappedBase, 3154 IN SIZE_T ViewSize) 3155 { 3156 ULONG_PTR StartAddress, EndingAddress, Base; 3157 ULONG Hash, Count = 0, Size, QuotaCharge; 3158 PMMSESSION Session; 3159 PMMPTE LastProtoPte, PointerPte, ProtoPte; 3160 PCONTROL_AREA ControlArea; 3161 PSEGMENT Segment; 3162 PSUBSECTION Subsection; 3163 MMPTE TempPte; 3164 PAGED_CODE(); 3165 3166 /* Make sure the base isn't past the session view range */ 3167 if ((MappedBase < MiSessionViewStart) || 3168 (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize))) 3169 { 3170 DPRINT1("Base outside of valid range\n"); 3171 return STATUS_INVALID_PARAMETER_1; 3172 } 3173 3174 /* Make sure the size isn't past the session view range */ 3175 if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize - 3176 (ULONG_PTR)MappedBase) < ViewSize) 3177 { 3178 DPRINT1("Size outside of valid range\n"); 3179 return STATUS_INVALID_PARAMETER_2; 3180 } 3181 3182 /* Sanity check */ 3183 ASSERT(ViewSize != 0); 3184 3185 /* Process must be in a session */ 3186 if (PsGetCurrentProcess()->ProcessInSession == FALSE) 3187 { 3188 DPRINT1("Process is not in session\n"); 3189 return STATUS_NOT_MAPPED_VIEW; 3190 } 3191 3192 /* Compute the correctly aligned base and end addresses */ 3193 StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase); 3194 EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1); 3195 3196 /* Sanity check and grab the session */ 3197 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); 3198 Session = &MmSessionSpace->Session; 3199 3200 /* Get the hash entry for this allocation */ 3201 Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey; 3202 3203 /* Lock system space */ 3204 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer); 3205 3206 /* Loop twice so we can try rolling over if needed */ 3207 while (TRUE) 3208 { 3209 /* Extract the size and base addresses from the entry */ 3210 Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF; 3211 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF; 3212 3213 /* Convert the size to bucket chunks */ 3214 Size *= MI_SYSTEM_VIEW_BUCKET_SIZE; 3215 3216 /* Bail out if this entry fits in here */ 3217 if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break; 3218 3219 /* Check if we overflew past the end of the hash table */ 3220 if (++Hash >= Session->SystemSpaceHashSize) 3221 { 3222 /* Reset the hash to zero and keep searching from the bottom */ 3223 Hash = 0; 3224 if (++Count == 2) 3225 { 3226 /* But if we overflew twice, then this is not a real mapping */ 3227 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW, 3228 Base, 3229 2, 3230 0, 3231 0); 3232 } 3233 } 3234 } 3235 3236 /* Make sure the view being mapped is not file-based */ 3237 ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea; 3238 if (ControlArea->FilePointer != NULL) 3239 { 3240 /* It is, so we have to bail out */ 3241 DPRINT1("Only page-filed backed sections can be commited\n"); 3242 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 3243 return STATUS_ALREADY_COMMITTED; 3244 } 3245 3246 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */ 3247 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 3248 ASSERT(ControlArea->u.Flags.Rom == 0); 3249 Subsection = (PSUBSECTION)(ControlArea + 1); 3250 3251 /* Get the start and end PTEs -- make sure the end PTE isn't past the end */ 3252 ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT); 3253 QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1; 3254 LastProtoPte = ProtoPte + QuotaCharge; 3255 if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection) 3256 { 3257 DPRINT1("PTE is out of bounds\n"); 3258 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 3259 return STATUS_INVALID_PARAMETER_2; 3260 } 3261 3262 /* Acquire the commit lock and count all the non-committed PTEs */ 3263 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex); 3264 PointerPte = ProtoPte; 3265 while (PointerPte < LastProtoPte) 3266 { 3267 if (PointerPte->u.Long) QuotaCharge--; 3268 PointerPte++; 3269 } 3270 3271 /* Was everything committed already? */ 3272 if (!QuotaCharge) 3273 { 3274 /* Nothing to do! */ 3275 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); 3276 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 3277 return STATUS_SUCCESS; 3278 } 3279 3280 /* Pick the segment and template PTE */ 3281 Segment = ControlArea->Segment; 3282 TempPte = Segment->SegmentPteTemplate; 3283 ASSERT(TempPte.u.Long != 0); 3284 3285 /* Loop all prototype PTEs to be committed */ 3286 PointerPte = ProtoPte; 3287 while (PointerPte < LastProtoPte) 3288 { 3289 /* Make sure the PTE is already invalid */ 3290 if (PointerPte->u.Long == 0) 3291 { 3292 /* And write the invalid PTE */ 3293 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 3294 } 3295 3296 /* Move to the next PTE */ 3297 PointerPte++; 3298 } 3299 3300 /* Check if we had at least one page charged */ 3301 if (QuotaCharge) 3302 { 3303 /* Update the accounting data */ 3304 Segment->NumberOfCommittedPages += QuotaCharge; 3305 InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge); 3306 } 3307 3308 /* Release all */ 3309 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); 3310 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 3311 return STATUS_SUCCESS; 3312 } 3313 3314 VOID 3315 NTAPI 3316 MiDeleteARM3Section(PVOID ObjectBody) 3317 { 3318 PSECTION SectionObject; 3319 PCONTROL_AREA ControlArea; 3320 KIRQL OldIrql; 3321 3322 SectionObject = (PSECTION)ObjectBody; 3323 3324 if (SectionObject->u.Flags.Based == 1) 3325 { 3326 /* Remove the node from the global section address tree */ 3327 KeAcquireGuardedMutex(&MmSectionBasedMutex); 3328 MiRemoveNode(&SectionObject->Address, &MmSectionBasedRoot); 3329 KeReleaseGuardedMutex(&MmSectionBasedMutex); 3330 } 3331 3332 /* Lock the PFN database */ 3333 OldIrql = MiAcquirePfnLock(); 3334 3335 ASSERT(SectionObject->Segment); 3336 ASSERT(SectionObject->Segment->ControlArea); 3337 3338 ControlArea = SectionObject->Segment->ControlArea; 3339 3340 /* Dereference */ 3341 ControlArea->NumberOfSectionReferences--; 3342 ControlArea->NumberOfUserReferences--; 3343 3344 ASSERT(ControlArea->u.Flags.BeingDeleted == 0); 3345 3346 /* Check it. It will delete it if there is no more reference to it */ 3347 MiCheckControlArea(ControlArea, OldIrql); 3348 } 3349 3350 ULONG 3351 NTAPI 3352 MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer) 3353 { 3354 UNIMPLEMENTED_ONCE; 3355 return 0; 3356 } 3357 3358 /* SYSTEM CALLS ***************************************************************/ 3359 3360 NTSTATUS 3361 NTAPI 3362 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage, 3363 IN PVOID File2MappedAsFile) 3364 { 3365 PVOID AddressSpace; 3366 PMMVAD Vad1, Vad2; 3367 PFILE_OBJECT FileObject1, FileObject2; 3368 NTSTATUS Status; 3369 3370 /* Lock address space */ 3371 AddressSpace = MmGetCurrentAddressSpace(); 3372 MmLockAddressSpace(AddressSpace); 3373 3374 /* Get the VAD for Address 1 */ 3375 Vad1 = MiLocateAddress(File1MappedAsAnImage); 3376 if (Vad1 == NULL) 3377 { 3378 /* Fail, the address does not exist */ 3379 DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage); 3380 Status = STATUS_INVALID_ADDRESS; 3381 goto Exit; 3382 } 3383 3384 /* Get the VAD for Address 2 */ 3385 Vad2 = MiLocateAddress(File2MappedAsFile); 3386 if (Vad2 == NULL) 3387 { 3388 /* Fail, the address does not exist */ 3389 DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile); 3390 Status = STATUS_INVALID_ADDRESS; 3391 goto Exit; 3392 } 3393 3394 /* Get the file object pointer for VAD 1 */ 3395 FileObject1 = MiGetFileObjectForVad(Vad1); 3396 if (FileObject1 == NULL) 3397 { 3398 DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage); 3399 Status = STATUS_CONFLICTING_ADDRESSES; 3400 goto Exit; 3401 } 3402 3403 /* Get the file object pointer for VAD 2 */ 3404 FileObject2 = MiGetFileObjectForVad(Vad2); 3405 if (FileObject2 == NULL) 3406 { 3407 DPRINT1("Failed to get file object for Address 2 %p\n", File2MappedAsFile); 3408 Status = STATUS_CONFLICTING_ADDRESSES; 3409 goto Exit; 3410 } 3411 3412 /* Make sure Vad1 is an image mapping */ 3413 if (Vad1->u.VadFlags.VadType != VadImageMap) 3414 { 3415 DPRINT1("Address 1 (%p) is not an image mapping\n", File1MappedAsAnImage); 3416 Status = STATUS_NOT_SAME_DEVICE; 3417 goto Exit; 3418 } 3419 3420 /* SectionObjectPointer is equal if the files are equal */ 3421 if (FileObject1->SectionObjectPointer == FileObject2->SectionObjectPointer) 3422 { 3423 Status = STATUS_SUCCESS; 3424 } 3425 else 3426 { 3427 Status = STATUS_NOT_SAME_DEVICE; 3428 } 3429 3430 Exit: 3431 /* Unlock address space */ 3432 MmUnlockAddressSpace(AddressSpace); 3433 return Status; 3434 } 3435 3436 /* 3437 * @implemented 3438 */ 3439 NTSTATUS 3440 NTAPI 3441 NtCreateSection(OUT PHANDLE SectionHandle, 3442 IN ACCESS_MASK DesiredAccess, 3443 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, 3444 IN PLARGE_INTEGER MaximumSize OPTIONAL, 3445 IN ULONG SectionPageProtection OPTIONAL, 3446 IN ULONG AllocationAttributes, 3447 IN HANDLE FileHandle OPTIONAL) 3448 { 3449 LARGE_INTEGER SafeMaximumSize; 3450 PVOID SectionObject; 3451 HANDLE Handle; 3452 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3453 NTSTATUS Status; 3454 PAGED_CODE(); 3455 3456 /* Check for non-existing flags */ 3457 if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED | 3458 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE | 3459 SEC_NO_CHANGE))) 3460 { 3461 if (!(AllocationAttributes & 1)) 3462 { 3463 DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes); 3464 return STATUS_INVALID_PARAMETER_6; 3465 } 3466 } 3467 3468 /* Check for no allocation type */ 3469 if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE))) 3470 { 3471 DPRINT1("Missing allocation type in allocation attributes\n"); 3472 return STATUS_INVALID_PARAMETER_6; 3473 } 3474 3475 /* Check for image allocation with invalid attributes */ 3476 if ((AllocationAttributes & SEC_IMAGE) && 3477 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES | 3478 SEC_NOCACHE | SEC_NO_CHANGE))) 3479 { 3480 DPRINT1("Image allocation with invalid attributes\n"); 3481 return STATUS_INVALID_PARAMETER_6; 3482 } 3483 3484 /* Check for allocation type is both commit and reserve */ 3485 if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)) 3486 { 3487 DPRINT1("Commit and reserve in the same time\n"); 3488 return STATUS_INVALID_PARAMETER_6; 3489 } 3490 3491 /* Now check for valid protection */ 3492 if ((SectionPageProtection & PAGE_NOCACHE) || 3493 (SectionPageProtection & PAGE_WRITECOMBINE) || 3494 (SectionPageProtection & PAGE_GUARD) || 3495 (SectionPageProtection & PAGE_NOACCESS)) 3496 { 3497 DPRINT1("Sections don't support these protections\n"); 3498 return STATUS_INVALID_PAGE_PROTECTION; 3499 } 3500 3501 /* Use a maximum size of zero, if none was specified */ 3502 SafeMaximumSize.QuadPart = 0; 3503 3504 /* Check for user-mode caller */ 3505 if (PreviousMode != KernelMode) 3506 { 3507 /* Enter SEH */ 3508 _SEH2_TRY 3509 { 3510 /* Safely check user-mode parameters */ 3511 if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize); 3512 MaximumSize = &SafeMaximumSize; 3513 ProbeForWriteHandle(SectionHandle); 3514 } 3515 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3516 { 3517 /* Return the exception code */ 3518 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3519 } 3520 _SEH2_END; 3521 } 3522 else if (!MaximumSize) MaximumSize = &SafeMaximumSize; 3523 3524 /* Check that MaximumSize is valid if backed by paging file */ 3525 if ((!FileHandle) && (!MaximumSize->QuadPart)) 3526 return STATUS_INVALID_PARAMETER_4; 3527 3528 /* Create the section */ 3529 Status = MmCreateSection(&SectionObject, 3530 DesiredAccess, 3531 ObjectAttributes, 3532 MaximumSize, 3533 SectionPageProtection, 3534 AllocationAttributes, 3535 FileHandle, 3536 NULL); 3537 if (!NT_SUCCESS(Status)) return Status; 3538 3539 /* FIXME: Should zero last page for a file mapping */ 3540 3541 /* Now insert the object */ 3542 Status = ObInsertObject(SectionObject, 3543 NULL, 3544 DesiredAccess, 3545 0, 3546 NULL, 3547 &Handle); 3548 if (NT_SUCCESS(Status)) 3549 { 3550 /* Enter SEH */ 3551 _SEH2_TRY 3552 { 3553 /* Return the handle safely */ 3554 *SectionHandle = Handle; 3555 } 3556 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3557 { 3558 /* Nothing here */ 3559 } 3560 _SEH2_END; 3561 } 3562 3563 /* Return the status */ 3564 return Status; 3565 } 3566 3567 NTSTATUS 3568 NTAPI 3569 NtOpenSection(OUT PHANDLE SectionHandle, 3570 IN ACCESS_MASK DesiredAccess, 3571 IN POBJECT_ATTRIBUTES ObjectAttributes) 3572 { 3573 HANDLE Handle; 3574 NTSTATUS Status; 3575 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3576 PAGED_CODE(); 3577 3578 /* Check for user-mode caller */ 3579 if (PreviousMode != KernelMode) 3580 { 3581 /* Enter SEH */ 3582 _SEH2_TRY 3583 { 3584 /* Safely check user-mode parameters */ 3585 ProbeForWriteHandle(SectionHandle); 3586 } 3587 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3588 { 3589 /* Return the exception code */ 3590 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3591 } 3592 _SEH2_END; 3593 } 3594 3595 /* Try opening the object */ 3596 Status = ObOpenObjectByName(ObjectAttributes, 3597 MmSectionObjectType, 3598 PreviousMode, 3599 NULL, 3600 DesiredAccess, 3601 NULL, 3602 &Handle); 3603 3604 /* Enter SEH */ 3605 _SEH2_TRY 3606 { 3607 /* Return the handle safely */ 3608 *SectionHandle = Handle; 3609 } 3610 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3611 { 3612 /* Nothing here */ 3613 } 3614 _SEH2_END; 3615 3616 /* Return the status */ 3617 return Status; 3618 } 3619 3620 NTSTATUS 3621 NTAPI 3622 NtMapViewOfSection(IN HANDLE SectionHandle, 3623 IN HANDLE ProcessHandle, 3624 IN OUT PVOID* BaseAddress, 3625 IN ULONG_PTR ZeroBits, 3626 IN SIZE_T CommitSize, 3627 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, 3628 IN OUT PSIZE_T ViewSize, 3629 IN SECTION_INHERIT InheritDisposition, 3630 IN ULONG AllocationType, 3631 IN ULONG Protect) 3632 { 3633 PVOID SafeBaseAddress; 3634 LARGE_INTEGER SafeSectionOffset; 3635 SIZE_T SafeViewSize; 3636 PSECTION Section; 3637 PEPROCESS Process; 3638 NTSTATUS Status; 3639 ACCESS_MASK DesiredAccess; 3640 ULONG ProtectionMask; 3641 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3642 #if defined(_M_IX86) || defined(_M_AMD64) 3643 static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES | 3644 MEM_DOS_LIM | SEC_NO_CHANGE | MEM_RESERVE); 3645 #else 3646 static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES | 3647 SEC_NO_CHANGE | MEM_RESERVE); 3648 #endif 3649 3650 /* Check for invalid inherit disposition */ 3651 if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare)) 3652 { 3653 DPRINT1("Invalid inherit disposition\n"); 3654 return STATUS_INVALID_PARAMETER_8; 3655 } 3656 3657 /* Allow only valid allocation types */ 3658 if (AllocationType & ~ValidAllocationType) 3659 { 3660 DPRINT1("Invalid allocation type\n"); 3661 return STATUS_INVALID_PARAMETER_9; 3662 } 3663 3664 /* Convert the protection mask, and validate it */ 3665 ProtectionMask = MiMakeProtectionMask(Protect); 3666 if (ProtectionMask == MM_INVALID_PROTECTION) 3667 { 3668 DPRINT1("Invalid page protection\n"); 3669 return STATUS_INVALID_PAGE_PROTECTION; 3670 } 3671 3672 /* Now convert the protection mask into desired section access mask */ 3673 DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7]; 3674 3675 /* Assume no section offset */ 3676 SafeSectionOffset.QuadPart = 0; 3677 3678 /* Enter SEH */ 3679 _SEH2_TRY 3680 { 3681 /* Check for unsafe parameters */ 3682 if (PreviousMode != KernelMode) 3683 { 3684 /* Probe the parameters */ 3685 ProbeForWritePointer(BaseAddress); 3686 ProbeForWriteSize_t(ViewSize); 3687 } 3688 3689 /* Check if a section offset was given */ 3690 if (SectionOffset) 3691 { 3692 /* Check for unsafe parameters and capture section offset */ 3693 if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset); 3694 SafeSectionOffset = *SectionOffset; 3695 } 3696 3697 /* Capture the other parameters */ 3698 SafeBaseAddress = *BaseAddress; 3699 SafeViewSize = *ViewSize; 3700 } 3701 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3702 { 3703 /* Return the exception code */ 3704 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3705 } 3706 _SEH2_END; 3707 3708 /* Check for kernel-mode address */ 3709 if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS) 3710 { 3711 DPRINT1("Kernel base not allowed\n"); 3712 return STATUS_INVALID_PARAMETER_3; 3713 } 3714 3715 /* Check for range entering kernel-mode */ 3716 if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize) 3717 { 3718 DPRINT1("Overflowing into kernel base not allowed\n"); 3719 return STATUS_INVALID_PARAMETER_3; 3720 } 3721 3722 /* Check for invalid zero bits */ 3723 if (ZeroBits) 3724 { 3725 if (ZeroBits > MI_MAX_ZERO_BITS) 3726 { 3727 DPRINT1("Invalid zero bits\n"); 3728 return STATUS_INVALID_PARAMETER_4; 3729 } 3730 3731 if ((((ULONG_PTR)SafeBaseAddress << ZeroBits) >> ZeroBits) != (ULONG_PTR)SafeBaseAddress) 3732 { 3733 DPRINT1("Invalid zero bits\n"); 3734 return STATUS_INVALID_PARAMETER_4; 3735 } 3736 3737 if (((((ULONG_PTR)SafeBaseAddress + SafeViewSize) << ZeroBits) >> ZeroBits) != ((ULONG_PTR)SafeBaseAddress + SafeViewSize)) 3738 { 3739 DPRINT1("Invalid zero bits\n"); 3740 return STATUS_INVALID_PARAMETER_4; 3741 } 3742 } 3743 3744 /* Reference the process */ 3745 Status = ObReferenceObjectByHandle(ProcessHandle, 3746 PROCESS_VM_OPERATION, 3747 PsProcessType, 3748 PreviousMode, 3749 (PVOID*)&Process, 3750 NULL); 3751 if (!NT_SUCCESS(Status)) return Status; 3752 3753 /* Reference the section */ 3754 Status = ObReferenceObjectByHandle(SectionHandle, 3755 DesiredAccess, 3756 MmSectionObjectType, 3757 PreviousMode, 3758 (PVOID*)&Section, 3759 NULL); 3760 if (!NT_SUCCESS(Status)) 3761 { 3762 ObDereferenceObject(Process); 3763 return Status; 3764 } 3765 3766 if (Section->u.Flags.PhysicalMemory) 3767 { 3768 if (PreviousMode == UserMode && 3769 SafeSectionOffset.QuadPart + SafeViewSize > MmHighestPhysicalPage << PAGE_SHIFT) 3770 { 3771 DPRINT1("Denying map past highest physical page.\n"); 3772 ObDereferenceObject(Section); 3773 ObDereferenceObject(Process); 3774 return STATUS_INVALID_PARAMETER_6; 3775 } 3776 } 3777 else if (!(AllocationType & MEM_DOS_LIM)) 3778 { 3779 /* Check for non-allocation-granularity-aligned BaseAddress */ 3780 if (SafeBaseAddress != ALIGN_DOWN_POINTER_BY(SafeBaseAddress, MM_VIRTMEM_GRANULARITY)) 3781 { 3782 DPRINT("BaseAddress is not at 64-kilobyte address boundary.\n"); 3783 ObDereferenceObject(Section); 3784 ObDereferenceObject(Process); 3785 return STATUS_MAPPED_ALIGNMENT; 3786 } 3787 3788 /* Do the same for the section offset */ 3789 if (SafeSectionOffset.LowPart != ALIGN_DOWN_BY(SafeSectionOffset.LowPart, MM_VIRTMEM_GRANULARITY)) 3790 { 3791 DPRINT("SectionOffset is not at 64-kilobyte address boundary.\n"); 3792 ObDereferenceObject(Section); 3793 ObDereferenceObject(Process); 3794 return STATUS_MAPPED_ALIGNMENT; 3795 } 3796 } 3797 3798 /* Now do the actual mapping */ 3799 Status = MmMapViewOfSection(Section, 3800 Process, 3801 &SafeBaseAddress, 3802 ZeroBits, 3803 CommitSize, 3804 &SafeSectionOffset, 3805 &SafeViewSize, 3806 InheritDisposition, 3807 AllocationType, 3808 Protect); 3809 3810 /* Return data only on success */ 3811 if (NT_SUCCESS(Status)) 3812 { 3813 /* Check if this is an image for the current process */ 3814 if ((Section->u.Flags.Image) && 3815 (Process == PsGetCurrentProcess()) && 3816 (Status != STATUS_IMAGE_NOT_AT_BASE)) 3817 { 3818 /* Notify the debugger */ 3819 DbgkMapViewOfSection(Section, 3820 SafeBaseAddress, 3821 SafeSectionOffset.LowPart, 3822 SafeViewSize); 3823 } 3824 3825 /* Enter SEH */ 3826 _SEH2_TRY 3827 { 3828 /* Return parameters to user */ 3829 *BaseAddress = SafeBaseAddress; 3830 *ViewSize = SafeViewSize; 3831 if (SectionOffset) *SectionOffset = SafeSectionOffset; 3832 } 3833 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3834 { 3835 /* Nothing to do */ 3836 } 3837 _SEH2_END; 3838 } 3839 3840 /* Dereference all objects and return status */ 3841 ObDereferenceObject(Section); 3842 ObDereferenceObject(Process); 3843 return Status; 3844 } 3845 3846 NTSTATUS 3847 NTAPI 3848 NtUnmapViewOfSection(IN HANDLE ProcessHandle, 3849 IN PVOID BaseAddress) 3850 { 3851 PEPROCESS Process; 3852 NTSTATUS Status; 3853 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3854 3855 /* Don't allowing mapping kernel views */ 3856 if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS)) 3857 { 3858 DPRINT1("Trying to unmap a kernel view\n"); 3859 return STATUS_NOT_MAPPED_VIEW; 3860 } 3861 3862 /* Reference the process */ 3863 Status = ObReferenceObjectByHandle(ProcessHandle, 3864 PROCESS_VM_OPERATION, 3865 PsProcessType, 3866 PreviousMode, 3867 (PVOID*)&Process, 3868 NULL); 3869 if (!NT_SUCCESS(Status)) return Status; 3870 3871 /* Unmap the view */ 3872 Status = MiUnmapViewOfSection(Process, BaseAddress, 0); 3873 3874 /* Dereference the process and return status */ 3875 ObDereferenceObject(Process); 3876 return Status; 3877 } 3878 3879 NTSTATUS 3880 NTAPI 3881 NtExtendSection(IN HANDLE SectionHandle, 3882 IN OUT PLARGE_INTEGER NewMaximumSize) 3883 { 3884 LARGE_INTEGER SafeNewMaximumSize; 3885 PSECTION Section; 3886 NTSTATUS Status; 3887 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3888 3889 /* Check for user-mode parameters */ 3890 if (PreviousMode != KernelMode) 3891 { 3892 /* Enter SEH */ 3893 _SEH2_TRY 3894 { 3895 /* Probe and capture the maximum size, it's both read and write */ 3896 ProbeForWriteLargeInteger(NewMaximumSize); 3897 SafeNewMaximumSize = *NewMaximumSize; 3898 } 3899 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3900 { 3901 /* Return the exception code */ 3902 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3903 } 3904 _SEH2_END; 3905 } 3906 else 3907 { 3908 /* Just read the size directly */ 3909 SafeNewMaximumSize = *NewMaximumSize; 3910 } 3911 3912 /* Reference the section */ 3913 Status = ObReferenceObjectByHandle(SectionHandle, 3914 SECTION_EXTEND_SIZE, 3915 MmSectionObjectType, 3916 PreviousMode, 3917 (PVOID*)&Section, 3918 NULL); 3919 if (!NT_SUCCESS(Status)) return Status; 3920 3921 Status = MmExtendSection(Section, &SafeNewMaximumSize); 3922 3923 /* Dereference the section */ 3924 ObDereferenceObject(Section); 3925 3926 if (NT_SUCCESS(Status)) 3927 { 3928 _SEH2_TRY 3929 { 3930 /* Write back the new size */ 3931 *NewMaximumSize = SafeNewMaximumSize; 3932 } 3933 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3934 { 3935 Status = _SEH2_GetExceptionCode(); 3936 } 3937 _SEH2_END; 3938 } 3939 3940 /* Return the status */ 3941 return STATUS_NOT_IMPLEMENTED; 3942 } 3943 3944 /* EOF */ 3945