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 static 116 BOOLEAN 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 static 287 PVOID 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 static 529 NTSTATUS 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 static 590 VOID 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 static 713 VOID 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 static 748 VOID 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 static 802 NTSTATUS 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 static 923 NTSTATUS 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 address of the mapping and success */ 1146 *MappedBase = Base; 1147 return STATUS_SUCCESS; 1148 } 1149 1150 static 1151 NTSTATUS 1152 MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea, 1153 IN PEPROCESS Process, 1154 IN PVOID *BaseAddress, 1155 IN PLARGE_INTEGER SectionOffset, 1156 IN PSIZE_T ViewSize, 1157 IN PSECTION Section, 1158 IN SECTION_INHERIT InheritDisposition, 1159 IN ULONG ProtectionMask, 1160 IN SIZE_T CommitSize, 1161 IN ULONG_PTR ZeroBits, 1162 IN ULONG AllocationType) 1163 { 1164 PMMVAD_LONG Vad; 1165 ULONG_PTR StartAddress; 1166 ULONG_PTR ViewSizeInPages; 1167 PSUBSECTION Subsection; 1168 PSEGMENT Segment; 1169 PFN_NUMBER PteOffset; 1170 NTSTATUS Status; 1171 ULONG QuotaCharge = 0, QuotaExcess = 0; 1172 PMMPTE PointerPte, LastPte; 1173 MMPTE TempPte; 1174 ULONG Granularity = MM_VIRTMEM_GRANULARITY; 1175 1176 DPRINT("Mapping ARM3 data section\n"); 1177 1178 /* Get the segment for this section */ 1179 Segment = ControlArea->Segment; 1180 1181 #ifdef _M_IX86 1182 /* ALlow being less restrictive on x86. */ 1183 if (AllocationType & MEM_DOS_LIM) 1184 Granularity = PAGE_SIZE; 1185 #endif 1186 1187 /* One can only reserve a file-based mapping, not shared memory! */ 1188 if ((AllocationType & MEM_RESERVE) && !(ControlArea->FilePointer)) 1189 { 1190 return STATUS_INVALID_PARAMETER_9; 1191 } 1192 1193 /* First, increase the map count. No purging is supported yet */ 1194 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE); 1195 if (!NT_SUCCESS(Status)) return Status; 1196 1197 /* Check if the caller specified the view size */ 1198 if (!(*ViewSize)) 1199 { 1200 LONGLONG ViewSizeLL; 1201 1202 /* The caller did not, so pick a 64K aligned view size based on the offset */ 1203 SectionOffset->LowPart &= ~(_64K - 1); 1204 1205 /* Calculate size and make sure this fits */ 1206 if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &ViewSizeLL)) 1207 || !NT_SUCCESS(RtlLongLongToSIZET(ViewSizeLL, ViewSize)) 1208 || (*ViewSize > MAXLONG_PTR)) 1209 { 1210 MiDereferenceControlArea(ControlArea); 1211 return STATUS_INVALID_VIEW_SIZE; 1212 } 1213 } 1214 else 1215 { 1216 /* A size was specified, align it to a 64K boundary 1217 * and check for overflow or huge value. */ 1218 if (!NT_SUCCESS(RtlSIZETAdd(*ViewSize, SectionOffset->LowPart & (_64K - 1), ViewSize)) 1219 || (*ViewSize > MAXLONG_PTR)) 1220 { 1221 MiDereferenceControlArea(ControlArea); 1222 return STATUS_INVALID_VIEW_SIZE; 1223 } 1224 1225 /* Align the offset as well to make this an aligned map */ 1226 SectionOffset->LowPart &= ~((ULONG)_64K - 1); 1227 } 1228 1229 /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */ 1230 ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0); 1231 1232 /* Windows ASSERTs for this flag */ 1233 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 1234 1235 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */ 1236 ASSERT(ControlArea->u.Flags.Rom == 0); 1237 Subsection = (PSUBSECTION)(ControlArea + 1); 1238 1239 /* Sections with extended segments are not supported in ARM3 */ 1240 ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0); 1241 1242 /* Within this section, figure out which PTEs will describe the view */ 1243 PteOffset = (PFN_NUMBER)(SectionOffset->QuadPart >> PAGE_SHIFT); 1244 1245 /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */ 1246 ASSERT(PteOffset < Segment->TotalNumberOfPtes); 1247 ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset); 1248 1249 /* In ARM3, only one subsection is used for now. It must contain these PTEs */ 1250 ASSERT(PteOffset < Subsection->PtesInSubsection); 1251 1252 /* In ARM3, only page-file backed sections (shared memory) are supported now */ 1253 ASSERT(ControlArea->FilePointer == NULL); 1254 1255 /* Windows ASSERTs for this too -- there must be a subsection base address */ 1256 ASSERT(Subsection->SubsectionBase != NULL); 1257 1258 /* Compute how much commit space the segment will take */ 1259 if ((CommitSize) && (Segment->NumberOfCommittedPages < Segment->TotalNumberOfPtes)) 1260 { 1261 /* Charge for the maximum pages */ 1262 QuotaCharge = BYTES_TO_PAGES(CommitSize); 1263 } 1264 1265 /* ARM3 does not currently support large pages */ 1266 ASSERT(Segment->SegmentFlags.LargePages == 0); 1267 1268 /* Calculate how many pages the region spans */ 1269 ViewSizeInPages = BYTES_TO_PAGES(*ViewSize); 1270 1271 /* A VAD can now be allocated. Do so and zero it out */ 1272 /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */ 1273 ASSERT((AllocationType & MEM_RESERVE) == 0); /* ARM3 does not support this */ 1274 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV'); 1275 if (!Vad) 1276 { 1277 MiDereferenceControlArea(ControlArea); 1278 return STATUS_INSUFFICIENT_RESOURCES; 1279 } 1280 1281 RtlZeroMemory(Vad, sizeof(MMVAD_LONG)); 1282 Vad->u4.Banked = (PVOID)(ULONG_PTR)0xDEADBABEDEADBABEULL; 1283 1284 /* Write all the data required in the VAD for handling a fault */ 1285 Vad->ControlArea = ControlArea; 1286 Vad->u.VadFlags.CommitCharge = 0; 1287 Vad->u.VadFlags.Protection = ProtectionMask; 1288 Vad->u2.VadFlags2.FileOffset = (ULONG)(SectionOffset->QuadPart >> 16); 1289 Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare); 1290 if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange)) 1291 { 1292 /* This isn't really implemented yet, but handle setting the flag */ 1293 Vad->u.VadFlags.NoChange = 1; 1294 Vad->u2.VadFlags2.SecNoChange = 1; 1295 } 1296 1297 /* Finally, write down the first and last prototype PTE */ 1298 Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset]; 1299 PteOffset += ViewSizeInPages - 1; 1300 ASSERT(PteOffset < Subsection->PtesInSubsection); 1301 Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset]; 1302 1303 /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */ 1304 ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte); 1305 1306 /* FIXME: Should setup VAD bitmap */ 1307 Status = STATUS_SUCCESS; 1308 1309 /* Check if anything was committed */ 1310 if (QuotaCharge) 1311 { 1312 /* Set the start and end PTE addresses, and pick the template PTE */ 1313 PointerPte = Vad->FirstPrototypePte; 1314 LastPte = PointerPte + BYTES_TO_PAGES(CommitSize); 1315 TempPte = Segment->SegmentPteTemplate; 1316 1317 /* Acquire the commit lock and loop all prototype PTEs to be committed */ 1318 KeAcquireGuardedMutex(&MmSectionCommitMutex); 1319 while (PointerPte < LastPte) 1320 { 1321 /* Make sure the PTE is already invalid */ 1322 if (PointerPte->u.Long == 0) 1323 { 1324 /* And write the invalid PTE */ 1325 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 1326 } 1327 else 1328 { 1329 /* The PTE is valid, so skip it */ 1330 QuotaExcess++; 1331 } 1332 1333 /* Move to the next PTE */ 1334 PointerPte++; 1335 } 1336 1337 /* Now check how many pages exactly we committed, and update accounting */ 1338 ASSERT(QuotaCharge >= QuotaExcess); 1339 QuotaCharge -= QuotaExcess; 1340 Segment->NumberOfCommittedPages += QuotaCharge; 1341 ASSERT(Segment->NumberOfCommittedPages <= Segment->TotalNumberOfPtes); 1342 1343 /* Now that we're done, release the lock */ 1344 KeReleaseGuardedMutex(&MmSectionCommitMutex); 1345 } 1346 1347 /* Is it SEC_BASED, or did the caller manually specify an address? */ 1348 if (*BaseAddress != NULL) 1349 { 1350 /* Just align what the caller gave us */ 1351 StartAddress = ALIGN_DOWN_BY((ULONG_PTR)*BaseAddress, Granularity); 1352 } 1353 else if (Section->Address.StartingVpn != 0) 1354 { 1355 /* It is a SEC_BASED mapping, use the address that was generated */ 1356 StartAddress = Section->Address.StartingVpn + SectionOffset->LowPart; 1357 } 1358 else 1359 { 1360 StartAddress = 0; 1361 } 1362 1363 Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 1364 if (!NT_SUCCESS(Status)) 1365 { 1366 ExFreePoolWithTag(Vad, 'ldaV'); 1367 MiDereferenceControlArea(ControlArea); 1368 1369 KeAcquireGuardedMutex(&MmSectionCommitMutex); 1370 Segment->NumberOfCommittedPages -= QuotaCharge; 1371 KeReleaseGuardedMutex(&MmSectionCommitMutex); 1372 return Status; 1373 } 1374 1375 /* Insert the VAD */ 1376 Status = MiInsertVadEx((PMMVAD)Vad, 1377 &StartAddress, 1378 ViewSizeInPages * PAGE_SIZE, 1379 MAXULONG_PTR >> ZeroBits, 1380 Granularity, 1381 AllocationType); 1382 if (!NT_SUCCESS(Status)) 1383 { 1384 ExFreePoolWithTag(Vad, 'ldaV'); 1385 MiDereferenceControlArea(ControlArea); 1386 1387 KeAcquireGuardedMutex(&MmSectionCommitMutex); 1388 Segment->NumberOfCommittedPages -= QuotaCharge; 1389 KeReleaseGuardedMutex(&MmSectionCommitMutex); 1390 1391 PsReturnProcessNonPagedPoolQuota(PsGetCurrentProcess(), sizeof(MMVAD_LONG)); 1392 return Status; 1393 } 1394 1395 /* Windows stores this for accounting purposes, do so as well */ 1396 if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress; 1397 1398 /* Finally, let the caller know where, and for what size, the view was mapped */ 1399 *ViewSize = ViewSizeInPages * PAGE_SIZE; 1400 *BaseAddress = (PVOID)StartAddress; 1401 DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress, *ViewSize); 1402 return STATUS_SUCCESS; 1403 } 1404 1405 static 1406 NTSTATUS 1407 MiCreateDataFileMap(IN PFILE_OBJECT File, 1408 OUT PSEGMENT *Segment, 1409 IN PSIZE_T MaximumSize, 1410 IN ULONG SectionPageProtection, 1411 IN ULONG AllocationAttributes, 1412 IN ULONG IgnoreFileSizing) 1413 { 1414 /* Not yet implemented */ 1415 ASSERT(FALSE); 1416 *Segment = NULL; 1417 return STATUS_NOT_IMPLEMENTED; 1418 } 1419 1420 static 1421 NTSTATUS 1422 MiCreatePagingFileMap(OUT PSEGMENT *Segment, 1423 IN PLARGE_INTEGER MaximumSize, 1424 IN ULONG ProtectionMask, 1425 IN ULONG AllocationAttributes) 1426 { 1427 ULONGLONG SizeLimit; 1428 PFN_COUNT PteCount; 1429 PMMPTE PointerPte; 1430 MMPTE TempPte; 1431 PCONTROL_AREA ControlArea; 1432 PSEGMENT NewSegment; 1433 PSUBSECTION Subsection; 1434 PAGED_CODE(); 1435 1436 /* No large pages in ARM3 yet */ 1437 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0); 1438 1439 /* Pagefile-backed sections need a known size */ 1440 if (!MaximumSize || !MaximumSize->QuadPart || MaximumSize->QuadPart < 0) 1441 return STATUS_INVALID_PARAMETER_4; 1442 1443 /* Calculate the maximum size possible, given the Prototype PTEs we'll need */ 1444 SizeLimit = MmSizeOfPagedPoolInBytes - sizeof(SEGMENT); 1445 SizeLimit /= sizeof(MMPTE); 1446 SizeLimit <<= PAGE_SHIFT; 1447 1448 /* Fail if this size is too big */ 1449 if (MaximumSize->QuadPart > SizeLimit) 1450 { 1451 return STATUS_SECTION_TOO_BIG; 1452 } 1453 1454 /* Calculate how many Prototype PTEs will be needed */ 1455 PteCount = (PFN_COUNT)((MaximumSize->QuadPart + PAGE_SIZE - 1) >> PAGE_SHIFT); 1456 1457 /* For commited memory, we must have a valid protection mask */ 1458 if (AllocationAttributes & SEC_COMMIT) ASSERT(ProtectionMask != 0); 1459 1460 /* The segment contains all the Prototype PTEs, allocate it in paged pool */ 1461 NewSegment = ExAllocatePoolWithTag(PagedPool, 1462 sizeof(SEGMENT) + 1463 sizeof(MMPTE) * (PteCount - 1), 1464 'tSmM'); 1465 if (!NewSegment) 1466 { 1467 return STATUS_INSUFFICIENT_RESOURCES; 1468 } 1469 *Segment = NewSegment; 1470 1471 /* Now allocate the control area, which has the subsection structure */ 1472 ControlArea = ExAllocatePoolWithTag(NonPagedPool, 1473 sizeof(CONTROL_AREA) + sizeof(SUBSECTION), 1474 'tCmM'); 1475 if (!ControlArea) 1476 { 1477 ExFreePoolWithTag(Segment, 'tSmM'); 1478 return STATUS_INSUFFICIENT_RESOURCES; 1479 } 1480 1481 /* And zero it out, filling the basic segmnet pointer and reference fields */ 1482 RtlZeroMemory(ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION)); 1483 ControlArea->Segment = NewSegment; 1484 ControlArea->NumberOfSectionReferences = 1; 1485 ControlArea->NumberOfUserReferences = 1; 1486 1487 /* Convert allocation attributes to control area flags */ 1488 if (AllocationAttributes & SEC_BASED) ControlArea->u.Flags.Based = 1; 1489 if (AllocationAttributes & SEC_RESERVE) ControlArea->u.Flags.Reserve = 1; 1490 if (AllocationAttributes & SEC_COMMIT) ControlArea->u.Flags.Commit = 1; 1491 1492 /* We just allocated it */ 1493 ControlArea->u.Flags.BeingCreated = 1; 1494 1495 /* The subsection follows, write the mask, PTE count and point back to the CA */ 1496 Subsection = (PSUBSECTION)(ControlArea + 1); 1497 Subsection->ControlArea = ControlArea; 1498 Subsection->PtesInSubsection = PteCount; 1499 Subsection->u.SubsectionFlags.Protection = ProtectionMask; 1500 1501 /* Zero out the segment's prototype PTEs, and link it with the control area */ 1502 PointerPte = &NewSegment->ThePtes[0]; 1503 RtlZeroMemory(NewSegment, sizeof(SEGMENT)); 1504 NewSegment->PrototypePte = PointerPte; 1505 NewSegment->ControlArea = ControlArea; 1506 1507 /* Save some extra accounting data for the segment as well */ 1508 NewSegment->u1.CreatingProcess = PsGetCurrentProcess(); 1509 NewSegment->SizeOfSegment = ((ULONGLONG)PteCount) * PAGE_SIZE; 1510 NewSegment->TotalNumberOfPtes = PteCount; 1511 NewSegment->NonExtendedPtes = PteCount; 1512 1513 /* The subsection's base address is the first Prototype PTE in the segment */ 1514 Subsection->SubsectionBase = PointerPte; 1515 1516 /* Start with an empty PTE, unless this is a commit operation */ 1517 TempPte.u.Long = 0; 1518 if (AllocationAttributes & SEC_COMMIT) 1519 { 1520 /* In which case, write down the protection mask in the Prototype PTEs */ 1521 TempPte.u.Soft.Protection = ProtectionMask; 1522 1523 /* For accounting, also mark these pages as being committed */ 1524 NewSegment->NumberOfCommittedPages = PteCount; 1525 } 1526 1527 /* The template PTE itself for the segment should also have the mask set */ 1528 NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask; 1529 1530 /* Write out the prototype PTEs, for now they're simply demand zero */ 1531 #ifdef _WIN64 1532 RtlFillMemoryUlonglong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long); 1533 #else 1534 RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long); 1535 #endif 1536 return STATUS_SUCCESS; 1537 } 1538 1539 PFILE_OBJECT 1540 NTAPI 1541 MmGetFileObjectForSection(IN PVOID SectionObject) 1542 { 1543 PSECTION Section = SectionObject; 1544 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 1545 ASSERT(SectionObject != NULL); 1546 1547 /* Check if it's an ARM3, or ReactOS section */ 1548 if (MiIsRosSectionObject(SectionObject) == FALSE) 1549 { 1550 /* Return the file pointer stored in the control area */ 1551 return Section->Segment->ControlArea->FilePointer; 1552 } 1553 1554 /* Return the file object */ 1555 return ((PMM_SECTION_SEGMENT)Section->Segment)->FileObject; 1556 } 1557 1558 static 1559 PFILE_OBJECT 1560 MiGetFileObjectForVad( 1561 _In_ PMMVAD Vad) 1562 { 1563 PCONTROL_AREA ControlArea; 1564 PFILE_OBJECT FileObject; 1565 1566 /* Check if this is a RosMm memory area */ 1567 if (Vad->u.VadFlags.Spare != 0) 1568 { 1569 PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad; 1570 1571 /* Check if it's a section view (RosMm section) */ 1572 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) 1573 { 1574 /* Get the section pointer to the SECTION_OBJECT */ 1575 FileObject = MemoryArea->SectionData.Segment->FileObject; 1576 } 1577 else 1578 { 1579 #ifdef NEWCC 1580 ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE); 1581 DPRINT1("VAD is a cache section!\n"); 1582 #else 1583 ASSERT(FALSE); 1584 #endif 1585 return NULL; 1586 } 1587 } 1588 else 1589 { 1590 /* Make sure it's not a VM VAD */ 1591 if (Vad->u.VadFlags.PrivateMemory == 1) 1592 { 1593 DPRINT1("VAD is not a section\n"); 1594 return NULL; 1595 } 1596 1597 /* Get the control area */ 1598 ControlArea = Vad->ControlArea; 1599 if ((ControlArea == NULL) || !ControlArea->u.Flags.Image) 1600 { 1601 DPRINT1("Address is not a section\n"); 1602 return NULL; 1603 } 1604 1605 /* Get the file object */ 1606 FileObject = ControlArea->FilePointer; 1607 } 1608 1609 /* Return the file object */ 1610 return FileObject; 1611 } 1612 1613 VOID 1614 NTAPI 1615 MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation) 1616 { 1617 PSECTION SectionObject; 1618 1619 /* Get the section object of this process*/ 1620 SectionObject = PsGetCurrentProcess()->SectionObject; 1621 ASSERT(SectionObject != NULL); 1622 ASSERT(MiIsRosSectionObject(SectionObject) == TRUE); 1623 1624 if (SectionObject->u.Flags.Image == 0) 1625 { 1626 RtlZeroMemory(ImageInformation, sizeof(*ImageInformation)); 1627 return; 1628 } 1629 1630 /* Return the image information */ 1631 *ImageInformation = ((PMM_IMAGE_SECTION_OBJECT)SectionObject->Segment)->ImageInformation; 1632 } 1633 1634 static 1635 NTSTATUS 1636 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject, 1637 OUT POBJECT_NAME_INFORMATION *ModuleName) 1638 { 1639 POBJECT_NAME_INFORMATION ObjectNameInfo; 1640 NTSTATUS Status; 1641 ULONG ReturnLength; 1642 1643 /* Allocate memory for our structure */ 1644 ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, TAG_MM); 1645 if (!ObjectNameInfo) return STATUS_NO_MEMORY; 1646 1647 /* Query the name */ 1648 Status = ObQueryNameString(FileObject, 1649 ObjectNameInfo, 1650 1024, 1651 &ReturnLength); 1652 if (!NT_SUCCESS(Status)) 1653 { 1654 /* Failed, free memory */ 1655 DPRINT1("Name query failed\n"); 1656 ExFreePoolWithTag(ObjectNameInfo, TAG_MM); 1657 *ModuleName = NULL; 1658 return Status; 1659 } 1660 1661 /* Success */ 1662 *ModuleName = ObjectNameInfo; 1663 return STATUS_SUCCESS; 1664 } 1665 1666 NTSTATUS 1667 NTAPI 1668 MmGetFileNameForSection(IN PVOID Section, 1669 OUT POBJECT_NAME_INFORMATION *ModuleName) 1670 { 1671 PFILE_OBJECT FileObject; 1672 PSECTION SectionObject = Section; 1673 1674 /* Make sure it's an image section */ 1675 if (SectionObject->u.Flags.Image == 0) 1676 { 1677 /* It's not, fail */ 1678 DPRINT1("Not an image section\n"); 1679 return STATUS_SECTION_NOT_IMAGE; 1680 } 1681 1682 /* Get the file object */ 1683 FileObject = MmGetFileObjectForSection(Section); 1684 return MmGetFileNameForFileObject(FileObject, ModuleName); 1685 } 1686 1687 NTSTATUS 1688 NTAPI 1689 MmGetFileNameForAddress(IN PVOID Address, 1690 OUT PUNICODE_STRING ModuleName) 1691 { 1692 POBJECT_NAME_INFORMATION ModuleNameInformation; 1693 PVOID AddressSpace; 1694 NTSTATUS Status; 1695 PMMVAD Vad; 1696 PFILE_OBJECT FileObject = NULL; 1697 1698 /* Lock address space */ 1699 AddressSpace = MmGetCurrentAddressSpace(); 1700 MmLockAddressSpace(AddressSpace); 1701 1702 /* Get the VAD */ 1703 Vad = MiLocateAddress(Address); 1704 if (Vad == NULL) 1705 { 1706 /* Fail, the address does not exist */ 1707 DPRINT1("No VAD at address %p\n", Address); 1708 MmUnlockAddressSpace(AddressSpace); 1709 return STATUS_INVALID_ADDRESS; 1710 } 1711 1712 /* Get the file object pointer for the VAD */ 1713 FileObject = MiGetFileObjectForVad(Vad); 1714 if (FileObject == NULL) 1715 { 1716 DPRINT1("Failed to get file object for Address %p\n", Address); 1717 MmUnlockAddressSpace(AddressSpace); 1718 return STATUS_SECTION_NOT_IMAGE; 1719 } 1720 1721 /* Reference the file object */ 1722 ObReferenceObject(FileObject); 1723 1724 /* Unlock address space */ 1725 MmUnlockAddressSpace(AddressSpace); 1726 1727 /* Get the filename of the file object */ 1728 Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation); 1729 1730 /* Dereference the file object */ 1731 ObDereferenceObject(FileObject); 1732 1733 /* Check if we were able to get the file object name */ 1734 if (NT_SUCCESS(Status)) 1735 { 1736 /* Init modulename */ 1737 if (!RtlCreateUnicodeString(ModuleName, ModuleNameInformation->Name.Buffer)) 1738 Status = STATUS_INSUFFICIENT_RESOURCES; 1739 1740 /* Free temp taged buffer from MmGetFileNameForFileObject() */ 1741 ExFreePoolWithTag(ModuleNameInformation, TAG_MM); 1742 1743 DPRINT("Found ModuleName %wZ by address %p\n", ModuleName, Address); 1744 } 1745 1746 /* Return status */ 1747 return Status; 1748 } 1749 1750 NTSTATUS 1751 NTAPI 1752 MiQueryMemorySectionName(IN HANDLE ProcessHandle, 1753 IN PVOID BaseAddress, 1754 OUT PVOID MemoryInformation, 1755 IN SIZE_T MemoryInformationLength, 1756 OUT PSIZE_T ReturnLength) 1757 { 1758 PEPROCESS Process; 1759 NTSTATUS Status; 1760 UNICODE_STRING ModuleFileName; 1761 PMEMORY_SECTION_NAME SectionName = NULL; 1762 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 1763 1764 Status = ObReferenceObjectByHandle(ProcessHandle, 1765 PROCESS_QUERY_INFORMATION, 1766 NULL, 1767 PreviousMode, 1768 (PVOID*)(&Process), 1769 NULL); 1770 1771 if (!NT_SUCCESS(Status)) 1772 { 1773 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status); 1774 return Status; 1775 } 1776 1777 Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName); 1778 1779 if (NT_SUCCESS(Status)) 1780 { 1781 SectionName = MemoryInformation; 1782 if (PreviousMode != KernelMode) 1783 { 1784 _SEH2_TRY 1785 { 1786 RtlInitEmptyUnicodeString(&SectionName->SectionFileName, 1787 (PWSTR)(SectionName + 1), 1788 MemoryInformationLength - sizeof(MEMORY_SECTION_NAME)); 1789 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName); 1790 1791 if (ReturnLength) *ReturnLength = ModuleFileName.Length + sizeof(MEMORY_SECTION_NAME); 1792 1793 } 1794 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1795 { 1796 Status = _SEH2_GetExceptionCode(); 1797 } 1798 _SEH2_END; 1799 } 1800 else 1801 { 1802 RtlInitEmptyUnicodeString(&SectionName->SectionFileName, 1803 (PWSTR)(SectionName + 1), 1804 MemoryInformationLength - sizeof(MEMORY_SECTION_NAME)); 1805 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName); 1806 1807 if (ReturnLength) *ReturnLength = ModuleFileName.Length + sizeof(MEMORY_SECTION_NAME); 1808 1809 } 1810 1811 RtlFreeUnicodeString(&ModuleFileName); 1812 } 1813 ObDereferenceObject(Process); 1814 return Status; 1815 } 1816 1817 VOID 1818 NTAPI 1819 MiFlushTbAndCapture(IN PMMVAD FoundVad, 1820 IN PMMPTE PointerPte, 1821 IN ULONG ProtectionMask, 1822 IN PMMPFN Pfn1, 1823 IN BOOLEAN UpdateDirty) 1824 { 1825 MMPTE TempPte, PreviousPte; 1826 KIRQL OldIrql; 1827 BOOLEAN RebuildPte = FALSE; 1828 1829 // 1830 // User for sanity checking later on 1831 // 1832 PreviousPte = *PointerPte; 1833 1834 // 1835 // Build the PTE and acquire the PFN lock 1836 // 1837 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 1838 PointerPte, 1839 ProtectionMask, 1840 PreviousPte.u.Hard.PageFrameNumber); 1841 OldIrql = MiAcquirePfnLock(); 1842 1843 // 1844 // We don't support I/O mappings in this path yet 1845 // 1846 ASSERT(Pfn1 != NULL); 1847 ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined); 1848 1849 // 1850 // Make sure new protection mask doesn't get in conflict and fix it if it does 1851 // 1852 if (Pfn1->u3.e1.CacheAttribute == MiCached) 1853 { 1854 // 1855 // This is a cached PFN 1856 // 1857 if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) 1858 { 1859 RebuildPte = TRUE; 1860 ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS); 1861 } 1862 } 1863 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached) 1864 { 1865 // 1866 // This is a non-cached PFN 1867 // 1868 if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE) 1869 { 1870 RebuildPte = TRUE; 1871 ProtectionMask &= ~MM_NOACCESS; 1872 ProtectionMask |= MM_NOCACHE; 1873 } 1874 } 1875 1876 if (RebuildPte) 1877 { 1878 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 1879 PointerPte, 1880 ProtectionMask, 1881 PreviousPte.u.Hard.PageFrameNumber); 1882 } 1883 1884 // 1885 // Write the new PTE, making sure we are only changing the bits 1886 // 1887 MI_UPDATE_VALID_PTE(PointerPte, TempPte); 1888 1889 // 1890 // Flush the TLB 1891 // 1892 ASSERT(PreviousPte.u.Hard.Valid == 1); 1893 KeFlushCurrentTb(); 1894 ASSERT(PreviousPte.u.Hard.Valid == 1); 1895 1896 // 1897 // Windows updates the relevant PFN1 information, we currently don't. 1898 // 1899 if (UpdateDirty && PreviousPte.u.Hard.Dirty) 1900 { 1901 if (!Pfn1->u3.e1.Modified) 1902 { 1903 DPRINT1("FIXME: Mark PFN as dirty\n"); 1904 } 1905 } 1906 1907 // 1908 // Not supported in ARM3 1909 // 1910 ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch); 1911 1912 // 1913 // Release the PFN lock, we are done 1914 // 1915 MiReleasePfnLock(OldIrql); 1916 } 1917 1918 static 1919 VOID 1920 MiRemoveMappedPtes(IN PVOID BaseAddress, 1921 IN ULONG NumberOfPtes, 1922 IN PCONTROL_AREA ControlArea, 1923 IN PMMSUPPORT Ws) 1924 { 1925 PMMPTE PointerPte, ProtoPte;//, FirstPte; 1926 PMMPDE PointerPde, SystemMapPde; 1927 PMMPFN Pfn1, Pfn2; 1928 MMPTE PteContents; 1929 KIRQL OldIrql; 1930 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress); 1931 1932 ASSERT(Ws == NULL); 1933 1934 /* Get the PTE and loop each one */ 1935 PointerPte = MiAddressToPte(BaseAddress); 1936 //FirstPte = PointerPte; 1937 while (NumberOfPtes) 1938 { 1939 /* Check if the PTE is already valid */ 1940 PteContents = *PointerPte; 1941 if (PteContents.u.Hard.Valid == 1) 1942 { 1943 /* Get the PFN entry */ 1944 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents)); 1945 1946 /* Get the PTE */ 1947 PointerPde = MiPteToPde(PointerPte); 1948 1949 /* Lock the PFN database and make sure this isn't a mapped file */ 1950 OldIrql = MiAcquirePfnLock(); 1951 ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0); 1952 1953 /* Mark the page as modified accordingly */ 1954 if (MI_IS_PAGE_DIRTY(&PteContents)) 1955 Pfn1->u3.e1.Modified = 1; 1956 1957 /* Was the PDE invalid */ 1958 if (PointerPde->u.Long == 0) 1959 { 1960 #if (_MI_PAGING_LEVELS == 2) 1961 /* Find the system double-mapped PDE that describes this mapping */ 1962 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)]; 1963 1964 /* Make it valid */ 1965 ASSERT(SystemMapPde->u.Hard.Valid == 1); 1966 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde); 1967 #else 1968 DBG_UNREFERENCED_LOCAL_VARIABLE(SystemMapPde); 1969 ASSERT(FALSE); 1970 #endif 1971 } 1972 1973 /* Dereference the PDE and the PTE */ 1974 Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde)); 1975 MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde)); 1976 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2); 1977 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents)); 1978 1979 /* Release the PFN lock */ 1980 MiReleasePfnLock(OldIrql); 1981 } 1982 else 1983 { 1984 /* Windows ASSERT */ 1985 ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1)); 1986 1987 /* Check if this is a prototype pointer PTE */ 1988 if (PteContents.u.Soft.Prototype == 1) 1989 { 1990 /* Get the prototype PTE */ 1991 ProtoPte = MiProtoPteToPte(&PteContents); 1992 1993 /* We don't support anything else atm */ 1994 ASSERT(ProtoPte->u.Long == 0); 1995 } 1996 } 1997 1998 /* Make the PTE into a zero PTE */ 1999 PointerPte->u.Long = 0; 2000 2001 /* Move to the next PTE */ 2002 PointerPte++; 2003 NumberOfPtes--; 2004 } 2005 2006 /* Flush the TLB */ 2007 KeFlushCurrentTb(); 2008 2009 /* Acquire the PFN lock */ 2010 OldIrql = MiAcquirePfnLock(); 2011 2012 /* Decrement the accounting counters */ 2013 ControlArea->NumberOfUserReferences--; 2014 ControlArea->NumberOfMappedViews--; 2015 2016 /* Check if we should destroy the CA and release the lock */ 2017 MiCheckControlArea(ControlArea, OldIrql); 2018 } 2019 2020 static 2021 ULONG 2022 MiRemoveFromSystemSpace(IN PMMSESSION Session, 2023 IN PVOID Base, 2024 OUT PCONTROL_AREA *ControlArea) 2025 { 2026 ULONG Hash, Size, Count = 0; 2027 ULONG_PTR Entry; 2028 PAGED_CODE(); 2029 2030 /* Compute the hash for this entry and loop trying to find it */ 2031 Entry = (ULONG_PTR)Base >> 16; 2032 Hash = Entry % Session->SystemSpaceHashKey; 2033 while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry) 2034 { 2035 /* Check if we overflew past the end of the hash table */ 2036 if (++Hash >= Session->SystemSpaceHashSize) 2037 { 2038 /* Reset the hash to zero and keep searching from the bottom */ 2039 Hash = 0; 2040 if (++Count == 2) 2041 { 2042 /* But if we overflew twice, then this is not a real mapping */ 2043 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW, 2044 (ULONG_PTR)Base, 2045 1, 2046 0, 2047 0); 2048 } 2049 } 2050 } 2051 2052 /* One less entry */ 2053 Session->SystemSpaceHashEntries--; 2054 2055 /* Extract the size and clear the entry */ 2056 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF; 2057 Session->SystemSpaceViewTable[Hash].Entry = 0; 2058 2059 /* Return the control area and the size */ 2060 *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea; 2061 return Size; 2062 } 2063 2064 static 2065 NTSTATUS 2066 MiUnmapViewInSystemSpace(IN PMMSESSION Session, 2067 IN PVOID MappedBase) 2068 { 2069 ULONG Size; 2070 PCONTROL_AREA ControlArea; 2071 PAGED_CODE(); 2072 2073 /* Remove this mapping */ 2074 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer); 2075 Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea); 2076 2077 /* Clear the bits for this mapping */ 2078 RtlClearBits(Session->SystemSpaceBitMap, 2079 (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16), 2080 Size); 2081 2082 /* Convert the size from a bit size into the actual size */ 2083 Size = Size * (_64K >> PAGE_SHIFT); 2084 2085 /* Remove the PTEs now */ 2086 MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL); 2087 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 2088 2089 /* Return success */ 2090 return STATUS_SUCCESS; 2091 } 2092 2093 /* PUBLIC FUNCTIONS ***********************************************************/ 2094 2095 /* 2096 * @implemented 2097 */ 2098 NTSTATUS 2099 NTAPI 2100 MmCreateArm3Section(OUT PVOID *SectionObject, 2101 IN ACCESS_MASK DesiredAccess, 2102 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, 2103 IN PLARGE_INTEGER InputMaximumSize, 2104 IN ULONG SectionPageProtection, 2105 IN ULONG AllocationAttributes, 2106 IN HANDLE FileHandle OPTIONAL, 2107 IN PFILE_OBJECT FileObject OPTIONAL) 2108 { 2109 SECTION Section; 2110 PSECTION NewSection; 2111 PSUBSECTION Subsection; 2112 PSEGMENT NewSegment, Segment; 2113 NTSTATUS Status; 2114 PCONTROL_AREA ControlArea; 2115 ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge; 2116 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 2117 BOOLEAN FileLock = FALSE, KernelCall = FALSE; 2118 KIRQL OldIrql; 2119 PFILE_OBJECT File; 2120 BOOLEAN UserRefIncremented = FALSE; 2121 PVOID PreviousSectionPointer; 2122 2123 /* Make the same sanity checks that the Nt interface should've validated */ 2124 ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED | 2125 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE | 2126 SEC_NO_CHANGE)) == 0); 2127 ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0); 2128 ASSERT(!((AllocationAttributes & SEC_IMAGE) && 2129 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | 2130 SEC_NOCACHE | SEC_NO_CHANGE)))); 2131 ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))); 2132 ASSERT(!((SectionPageProtection & PAGE_NOCACHE) || 2133 (SectionPageProtection & PAGE_WRITECOMBINE) || 2134 (SectionPageProtection & PAGE_GUARD) || 2135 (SectionPageProtection & PAGE_NOACCESS))); 2136 2137 /* Convert section flag to page flag */ 2138 if (AllocationAttributes & SEC_NOCACHE) 2139 SectionPageProtection |= PAGE_NOCACHE; 2140 2141 /* Check to make sure the protection is correct. Nt* does this already */ 2142 ProtectionMask = MiMakeProtectionMask(SectionPageProtection); 2143 if (ProtectionMask == MM_INVALID_PROTECTION) 2144 { 2145 DPRINT1("Invalid protection mask\n"); 2146 return STATUS_INVALID_PAGE_PROTECTION; 2147 } 2148 2149 /* Check if this is going to be a data or image backed file section */ 2150 if ((FileHandle) || (FileObject)) 2151 { 2152 /* These cannot be mapped with large pages */ 2153 if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6; 2154 2155 /* For now, only support the mechanism through a file handle */ 2156 ASSERT(FileObject == NULL); 2157 2158 /* Reference the file handle to get the object */ 2159 Status = ObReferenceObjectByHandle(FileHandle, 2160 MmMakeFileAccess[ProtectionMask], 2161 IoFileObjectType, 2162 PreviousMode, 2163 (PVOID*)&File, 2164 NULL); 2165 if (!NT_SUCCESS(Status)) return Status; 2166 2167 /* Make sure Cc has been doing its job */ 2168 if (!File->SectionObjectPointer) 2169 { 2170 /* This is not a valid file system-based file, fail */ 2171 ObDereferenceObject(File); 2172 return STATUS_INVALID_FILE_FOR_SECTION; 2173 } 2174 2175 /* Image-file backed sections are not yet supported */ 2176 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2177 2178 /* Compute the size of the control area, and allocate it */ 2179 ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION); 2180 ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM'); 2181 if (!ControlArea) 2182 { 2183 ObDereferenceObject(File); 2184 return STATUS_INSUFFICIENT_RESOURCES; 2185 } 2186 2187 /* Zero it out */ 2188 RtlZeroMemory(ControlArea, ControlAreaSize); 2189 2190 /* Did we get a handle, or an object? */ 2191 if (FileHandle) 2192 { 2193 /* We got a file handle so we have to lock down the file */ 2194 #if 0 2195 Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection); 2196 if (!NT_SUCCESS(Status)) 2197 { 2198 ExFreePool(ControlArea); 2199 ObDereferenceObject(File); 2200 return Status; 2201 } 2202 #else 2203 /* ReactOS doesn't support this API yet, so do nothing */ 2204 UNIMPLEMENTED; 2205 Status = STATUS_SUCCESS; 2206 #endif 2207 /* Update the top-level IRP so that drivers know what's happening */ 2208 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP); 2209 FileLock = TRUE; 2210 } 2211 2212 /* Lock the PFN database while we play with the section pointers */ 2213 OldIrql = MiAcquirePfnLock(); 2214 2215 /* Image-file backed sections are not yet supported */ 2216 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2217 2218 /* There should not already be a control area for this file */ 2219 ASSERT(File->SectionObjectPointer->DataSectionObject == NULL); 2220 NewSegment = NULL; 2221 2222 /* Write down that this CA is being created, and set it */ 2223 ControlArea->u.Flags.BeingCreated = TRUE; 2224 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2225 PreviousSectionPointer = File->SectionObjectPointer; 2226 File->SectionObjectPointer->DataSectionObject = ControlArea; 2227 2228 /* We can release the PFN lock now */ 2229 MiReleasePfnLock(OldIrql); 2230 2231 /* We don't support previously-mapped file */ 2232 ASSERT(NewSegment == NULL); 2233 2234 /* Image-file backed sections are not yet supported */ 2235 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2236 2237 /* So we always create a data file map */ 2238 Status = MiCreateDataFileMap(File, 2239 &Segment, 2240 (PSIZE_T)InputMaximumSize, 2241 SectionPageProtection, 2242 AllocationAttributes, 2243 KernelCall); 2244 if (!NT_SUCCESS(Status)) 2245 { 2246 /* Lock the PFN database while we play with the section pointers */ 2247 OldIrql = MiAcquirePfnLock(); 2248 2249 /* Reset the waiting-for-deletion event */ 2250 ASSERT(ControlArea->WaitingForDeletion == NULL); 2251 ControlArea->WaitingForDeletion = NULL; 2252 2253 /* Set the file pointer NULL flag */ 2254 ASSERT(ControlArea->u.Flags.FilePointerNull == 0); 2255 ControlArea->u.Flags.FilePointerNull = TRUE; 2256 2257 /* Delete the data section object */ 2258 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2259 File->SectionObjectPointer->DataSectionObject = NULL; 2260 2261 /* No longer being created */ 2262 ControlArea->u.Flags.BeingCreated = FALSE; 2263 2264 /* We can release the PFN lock now */ 2265 MiReleasePfnLock(OldIrql); 2266 2267 /* Check if we locked and set the IRP */ 2268 if (FileLock) 2269 { 2270 /* Undo */ 2271 IoSetTopLevelIrp(NULL); 2272 //FsRtlReleaseFile(File); 2273 } 2274 2275 /* Free the control area and de-ref the file object */ 2276 ExFreePool(ControlArea); 2277 ObDereferenceObject(File); 2278 2279 /* All done */ 2280 return Status; 2281 } 2282 2283 /* On success, we expect this */ 2284 ASSERT(PreviousSectionPointer == File->SectionObjectPointer); 2285 2286 /* Check if a maximum size was specified */ 2287 if (!InputMaximumSize->QuadPart) 2288 { 2289 /* Nope, use the segment size */ 2290 Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment; 2291 } 2292 else 2293 { 2294 /* Yep, use the entered size */ 2295 Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart; 2296 } 2297 } 2298 else 2299 { 2300 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */ 2301 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION; 2302 2303 /* Not yet supported */ 2304 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0); 2305 2306 /* So this must be a pagefile-backed section, create the mappings needed */ 2307 Status = MiCreatePagingFileMap(&NewSegment, 2308 InputMaximumSize, 2309 ProtectionMask, 2310 AllocationAttributes); 2311 if (!NT_SUCCESS(Status)) return Status; 2312 2313 /* Set the size here, and read the control area */ 2314 Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment; 2315 ControlArea = NewSegment->ControlArea; 2316 2317 /* MiCreatePagingFileMap increments user references */ 2318 UserRefIncremented = TRUE; 2319 } 2320 2321 /* Did we already have a segment? */ 2322 if (!NewSegment) 2323 { 2324 /* This must be the file path and we created a segment */ 2325 NewSegment = Segment; 2326 ASSERT(File != NULL); 2327 2328 /* Acquire the PFN lock while we set control area flags */ 2329 OldIrql = MiAcquirePfnLock(); 2330 2331 /* We don't support this race condition yet, so assume no waiters */ 2332 ASSERT(ControlArea->WaitingForDeletion == NULL); 2333 ControlArea->WaitingForDeletion = NULL; 2334 2335 /* Image-file backed sections are not yet supported, nor ROM images */ 2336 ASSERT((AllocationAttributes & SEC_IMAGE) == 0); 2337 ASSERT(Segment->ControlArea->u.Flags.Rom == 0); 2338 2339 /* Take off the being created flag, and then release the lock */ 2340 ControlArea->u.Flags.BeingCreated = FALSE; 2341 MiReleasePfnLock(OldIrql); 2342 } 2343 2344 /* Check if we locked the file earlier */ 2345 if (FileLock) 2346 { 2347 /* Reset the top-level IRP and release the lock */ 2348 IoSetTopLevelIrp(NULL); 2349 //FsRtlReleaseFile(File); 2350 FileLock = FALSE; 2351 } 2352 2353 /* Set the initial section object data */ 2354 Section.InitialPageProtection = SectionPageProtection; 2355 2356 /* The mapping created a control area and segment, save the flags */ 2357 Section.Segment = NewSegment; 2358 Section.u.LongFlags = ControlArea->u.LongFlags; 2359 2360 /* Check if this is a user-mode read-write non-image file mapping */ 2361 if (!(FileObject) && 2362 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) && 2363 !(ControlArea->u.Flags.Image) && 2364 (ControlArea->FilePointer)) 2365 { 2366 /* Add a reference and set the flag */ 2367 Section.u.Flags.UserWritable = TRUE; 2368 InterlockedIncrement((volatile LONG*)&ControlArea->WritableUserReferences); 2369 } 2370 2371 /* Check for image mappings or page file mappings */ 2372 if ((ControlArea->u.Flags.Image) || !(ControlArea->FilePointer)) 2373 { 2374 /* Charge the segment size, and allocate a subsection */ 2375 PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE); 2376 Size = sizeof(SUBSECTION); 2377 } 2378 else 2379 { 2380 /* Charge nothing, and allocate a mapped subsection */ 2381 PagedCharge = 0; 2382 Size = sizeof(MSUBSECTION); 2383 } 2384 2385 /* Check if this is a normal CA */ 2386 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 2387 ASSERT(ControlArea->u.Flags.Rom == 0); 2388 2389 /* Charge only a CA, and the subsection is right after */ 2390 NonPagedCharge = sizeof(CONTROL_AREA); 2391 Subsection = (PSUBSECTION)(ControlArea + 1); 2392 2393 /* We only support single-subsection mappings */ 2394 NonPagedCharge += Size; 2395 ASSERT(Subsection->NextSubsection == NULL); 2396 2397 /* Create the actual section object, with enough space for the prototype PTEs */ 2398 Status = ObCreateObject(PreviousMode, 2399 MmSectionObjectType, 2400 ObjectAttributes, 2401 PreviousMode, 2402 NULL, 2403 sizeof(SECTION), 2404 PagedCharge, 2405 NonPagedCharge, 2406 (PVOID*)&NewSection); 2407 if (!NT_SUCCESS(Status)) 2408 { 2409 /* Check if this is a user-mode read-write non-image file mapping */ 2410 if (!(FileObject) && 2411 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) && 2412 !(ControlArea->u.Flags.Image) && 2413 (ControlArea->FilePointer)) 2414 { 2415 /* Remove a reference and check the flag */ 2416 ASSERT(Section.u.Flags.UserWritable == 1); 2417 InterlockedDecrement((volatile LONG*)&ControlArea->WritableUserReferences); 2418 } 2419 2420 /* Check if a user reference was added */ 2421 if (UserRefIncremented) 2422 { 2423 /* Acquire the PFN lock while we change counters */ 2424 OldIrql = MiAcquirePfnLock(); 2425 2426 /* Decrement the accounting counters */ 2427 ControlArea->NumberOfSectionReferences--; 2428 ASSERT((LONG)ControlArea->NumberOfUserReferences > 0); 2429 ControlArea->NumberOfUserReferences--; 2430 2431 /* Check if we should destroy the CA and release the lock */ 2432 MiCheckControlArea(ControlArea, OldIrql); 2433 } 2434 2435 /* Return the failure code */ 2436 return Status; 2437 } 2438 2439 /* NOTE: Past this point, all failures will be handled by Ob upon ref->0 */ 2440 2441 /* Now copy the local section object from the stack into this new object */ 2442 RtlCopyMemory(NewSection, &Section, sizeof(SECTION)); 2443 NewSection->Address.StartingVpn = 0; 2444 2445 /* For now, only user calls are supported */ 2446 ASSERT(KernelCall == FALSE); 2447 NewSection->u.Flags.UserReference = TRUE; 2448 2449 /* Is this a "based" allocation, in which all mappings are identical? */ 2450 if (AllocationAttributes & SEC_BASED) 2451 { 2452 /* Lock the VAD tree during the search */ 2453 KeAcquireGuardedMutex(&MmSectionBasedMutex); 2454 2455 /* Is it a brand new ControArea ? */ 2456 if (ControlArea->u.Flags.BeingCreated == 1) 2457 { 2458 ASSERT(ControlArea->u.Flags.Based == 1); 2459 /* Then we must find a global address, top-down */ 2460 Status = MiFindEmptyAddressRangeDownBasedTree((SIZE_T)ControlArea->Segment->SizeOfSegment, 2461 (ULONG_PTR)MmHighSectionBase, 2462 _64K, 2463 &MmSectionBasedRoot, 2464 (ULONG_PTR*)&ControlArea->Segment->BasedAddress); 2465 2466 if (!NT_SUCCESS(Status)) 2467 { 2468 /* No way to find a valid range. */ 2469 KeReleaseGuardedMutex(&MmSectionBasedMutex); 2470 ControlArea->u.Flags.Based = 0; 2471 NewSection->u.Flags.Based = 0; 2472 ObDereferenceObject(NewSection); 2473 return Status; 2474 } 2475 2476 /* Compute the ending address and insert it into the VAD tree */ 2477 NewSection->Address.StartingVpn = (ULONG_PTR)ControlArea->Segment->BasedAddress; 2478 NewSection->Address.EndingVpn = NewSection->Address.StartingVpn + NewSection->SizeOfSection.LowPart - 1; 2479 MiInsertBasedSection(NewSection); 2480 } 2481 else 2482 { 2483 /* 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 !*/ 2484 ASSERT(FALSE); 2485 } 2486 2487 KeReleaseGuardedMutex(&MmSectionBasedMutex); 2488 } 2489 2490 /* The control area is not being created anymore */ 2491 if (ControlArea->u.Flags.BeingCreated == 1) 2492 { 2493 /* Acquire the PFN lock while we set control area flags */ 2494 OldIrql = MiAcquirePfnLock(); 2495 2496 /* Take off the being created flag, and then release the lock */ 2497 ControlArea->u.Flags.BeingCreated = 0; 2498 NewSection->u.Flags.BeingCreated = 0; 2499 2500 MiReleasePfnLock(OldIrql); 2501 } 2502 2503 /* Migrate the attribute into a flag */ 2504 if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE; 2505 2506 /* If R/W access is not requested, this might eventually become a CoW mapping */ 2507 if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE))) 2508 { 2509 NewSection->u.Flags.CopyOnWrite = TRUE; 2510 } 2511 2512 /* Write down if this was a kernel call */ 2513 ControlArea->u.Flags.WasPurged |= KernelCall; 2514 ASSERT(ControlArea->u.Flags.WasPurged == FALSE); 2515 2516 /* Make sure the segment and the section are the same size, or the section is smaller */ 2517 ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment); 2518 2519 /* Return the object and the creation status */ 2520 *SectionObject = (PVOID)NewSection; 2521 return Status; 2522 } 2523 2524 /* 2525 * @implemented 2526 */ 2527 NTSTATUS 2528 NTAPI 2529 MmMapViewOfArm3Section(IN PVOID SectionObject, 2530 IN PEPROCESS Process, 2531 IN OUT PVOID *BaseAddress, 2532 IN ULONG_PTR ZeroBits, 2533 IN SIZE_T CommitSize, 2534 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, 2535 IN OUT PSIZE_T ViewSize, 2536 IN SECTION_INHERIT InheritDisposition, 2537 IN ULONG AllocationType, 2538 IN ULONG Protect) 2539 { 2540 KAPC_STATE ApcState; 2541 BOOLEAN Attached = FALSE; 2542 PSECTION Section; 2543 PCONTROL_AREA ControlArea; 2544 ULONG ProtectionMask; 2545 NTSTATUS Status; 2546 ULONG64 CalculatedViewSize; 2547 PAGED_CODE(); 2548 2549 /* Get the segment and control area */ 2550 Section = (PSECTION)SectionObject; 2551 ControlArea = Section->Segment->ControlArea; 2552 2553 /* These flags/states are not yet supported by ARM3 */ 2554 ASSERT(Section->u.Flags.Image == 0); 2555 ASSERT(Section->u.Flags.NoCache == 0); 2556 ASSERT(Section->u.Flags.WriteCombined == 0); 2557 ASSERT(ControlArea->u.Flags.PhysicalMemory == 0); 2558 2559 /* FIXME */ 2560 if ((AllocationType & MEM_RESERVE) != 0) 2561 { 2562 DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n"); 2563 return STATUS_NOT_IMPLEMENTED; 2564 } 2565 2566 /* Check if the mapping protection is compatible with the create */ 2567 if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect)) 2568 { 2569 DPRINT1("Mapping protection is incompatible\n"); 2570 return STATUS_SECTION_PROTECTION; 2571 } 2572 2573 /* Check if the offset and size would cause an overflow */ 2574 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) < 2575 (ULONG64)SectionOffset->QuadPart) 2576 { 2577 DPRINT1("Section offset overflows\n"); 2578 return STATUS_INVALID_VIEW_SIZE; 2579 } 2580 2581 /* Check if the offset and size are bigger than the section itself */ 2582 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) > 2583 (ULONG64)Section->SizeOfSection.QuadPart) 2584 { 2585 DPRINT1("Section offset is larger than section\n"); 2586 return STATUS_INVALID_VIEW_SIZE; 2587 } 2588 2589 /* Check if the caller did not specify a view size */ 2590 if (!(*ViewSize)) 2591 { 2592 /* Compute it for the caller */ 2593 CalculatedViewSize = Section->SizeOfSection.QuadPart - 2594 SectionOffset->QuadPart; 2595 2596 /* Check if it's larger than 4GB or overflows into kernel-mode */ 2597 if (!NT_SUCCESS(RtlULongLongToSIZET(CalculatedViewSize, ViewSize)) || 2598 (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < CalculatedViewSize)) 2599 { 2600 DPRINT1("Section view won't fit\n"); 2601 return STATUS_INVALID_VIEW_SIZE; 2602 } 2603 } 2604 2605 /* Check if the commit size is larger than the view size */ 2606 if (CommitSize > *ViewSize) 2607 { 2608 DPRINT1("Attempting to commit more than the view itself\n"); 2609 return STATUS_INVALID_PARAMETER_5; 2610 } 2611 2612 /* Check if the view size is larger than the section */ 2613 if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart) 2614 { 2615 DPRINT1("The view is larger than the section\n"); 2616 return STATUS_INVALID_VIEW_SIZE; 2617 } 2618 2619 /* Compute and validate the protection mask */ 2620 ProtectionMask = MiMakeProtectionMask(Protect); 2621 if (ProtectionMask == MM_INVALID_PROTECTION) 2622 { 2623 DPRINT1("The protection is invalid\n"); 2624 return STATUS_INVALID_PAGE_PROTECTION; 2625 } 2626 2627 /* We only handle pagefile-backed sections, which cannot be writecombined */ 2628 if (Protect & PAGE_WRITECOMBINE) 2629 { 2630 DPRINT1("Cannot write combine a pagefile-backed section\n"); 2631 return STATUS_INVALID_PARAMETER_10; 2632 } 2633 2634 /* Start by attaching to the current process if needed */ 2635 if (PsGetCurrentProcess() != Process) 2636 { 2637 KeStackAttachProcess(&Process->Pcb, &ApcState); 2638 Attached = TRUE; 2639 } 2640 2641 /* Do the actual mapping */ 2642 Status = MiMapViewOfDataSection(ControlArea, 2643 Process, 2644 BaseAddress, 2645 SectionOffset, 2646 ViewSize, 2647 Section, 2648 InheritDisposition, 2649 ProtectionMask, 2650 CommitSize, 2651 ZeroBits, 2652 AllocationType); 2653 2654 /* Detach if needed, then return status */ 2655 if (Attached) KeUnstackDetachProcess(&ApcState); 2656 return Status; 2657 } 2658 2659 /* 2660 * @unimplemented 2661 */ 2662 BOOLEAN 2663 NTAPI 2664 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer) 2665 { 2666 UNIMPLEMENTED; 2667 return FALSE; 2668 } 2669 2670 /* 2671 * @unimplemented 2672 */ 2673 BOOLEAN 2674 NTAPI 2675 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer, 2676 IN BOOLEAN DelayClose) 2677 { 2678 UNIMPLEMENTED; 2679 return FALSE; 2680 } 2681 2682 /* 2683 * @implemented 2684 */ 2685 NTSTATUS 2686 NTAPI 2687 MmMapViewInSessionSpace(IN PVOID Section, 2688 OUT PVOID *MappedBase, 2689 IN OUT PSIZE_T ViewSize) 2690 { 2691 PAGED_CODE(); 2692 LARGE_INTEGER SectionOffset; 2693 2694 // HACK 2695 if (MiIsRosSectionObject(Section)) 2696 { 2697 return MmMapViewInSystemSpace(Section, MappedBase, ViewSize); 2698 } 2699 2700 /* Process must be in a session */ 2701 if (PsGetCurrentProcess()->ProcessInSession == FALSE) 2702 { 2703 DPRINT1("Process is not in session\n"); 2704 return STATUS_NOT_MAPPED_VIEW; 2705 } 2706 2707 /* Use the system space API, but with the session view instead */ 2708 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); 2709 SectionOffset.QuadPart = 0; 2710 return MiMapViewInSystemSpace(Section, 2711 &MmSessionSpace->Session, 2712 MappedBase, 2713 ViewSize, 2714 &SectionOffset); 2715 } 2716 2717 /* 2718 * @implemented 2719 */ 2720 NTSTATUS 2721 NTAPI 2722 MmUnmapViewInSessionSpace(IN PVOID MappedBase) 2723 { 2724 PAGED_CODE(); 2725 2726 // HACK 2727 if (!MI_IS_SESSION_ADDRESS(MappedBase)) 2728 { 2729 return MmUnmapViewInSystemSpace(MappedBase); 2730 } 2731 2732 /* Process must be in a session */ 2733 if (PsGetCurrentProcess()->ProcessInSession == FALSE) 2734 { 2735 DPRINT1("Proess is not in session\n"); 2736 return STATUS_NOT_MAPPED_VIEW; 2737 } 2738 2739 /* Use the system space API, but with the session view instead */ 2740 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); 2741 return MiUnmapViewInSystemSpace(&MmSessionSpace->Session, 2742 MappedBase); 2743 } 2744 2745 /* 2746 * @implemented 2747 */ 2748 NTSTATUS 2749 NTAPI 2750 MmUnmapViewOfSection(IN PEPROCESS Process, 2751 IN PVOID BaseAddress) 2752 { 2753 return MiUnmapViewOfSection(Process, BaseAddress, 0); 2754 } 2755 2756 /* 2757 * @implemented 2758 */ 2759 NTSTATUS 2760 NTAPI 2761 MmUnmapViewInSystemSpace(IN PVOID MappedBase) 2762 { 2763 PMEMORY_AREA MemoryArea; 2764 PAGED_CODE(); 2765 2766 /* Was this mapped by RosMm? */ 2767 MmLockAddressSpace(MmGetKernelAddressSpace()); 2768 MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase); 2769 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) 2770 { 2771 NTSTATUS Status = MiRosUnmapViewInSystemSpace(MappedBase); 2772 MmUnlockAddressSpace(MmGetKernelAddressSpace()); 2773 return Status; 2774 } 2775 MmUnlockAddressSpace(MmGetKernelAddressSpace()); 2776 2777 /* It was not, call the ARM3 routine */ 2778 return MiUnmapViewInSystemSpace(&MmSession, MappedBase); 2779 } 2780 2781 /* 2782 * @implemented 2783 */ 2784 NTSTATUS 2785 NTAPI 2786 MmCommitSessionMappedView(IN PVOID MappedBase, 2787 IN SIZE_T ViewSize) 2788 { 2789 ULONG_PTR StartAddress, EndingAddress, Base; 2790 ULONG Hash, Count = 0, Size, QuotaCharge; 2791 PMMSESSION Session; 2792 PMMPTE LastProtoPte, PointerPte, ProtoPte; 2793 PCONTROL_AREA ControlArea; 2794 PSEGMENT Segment; 2795 PSUBSECTION Subsection; 2796 MMPTE TempPte; 2797 PAGED_CODE(); 2798 2799 /* Make sure the base isn't past the session view range */ 2800 if ((MappedBase < MiSessionViewStart) || 2801 (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize))) 2802 { 2803 DPRINT1("Base outside of valid range\n"); 2804 return STATUS_INVALID_PARAMETER_1; 2805 } 2806 2807 /* Make sure the size isn't past the session view range */ 2808 if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize - 2809 (ULONG_PTR)MappedBase) < ViewSize) 2810 { 2811 DPRINT1("Size outside of valid range\n"); 2812 return STATUS_INVALID_PARAMETER_2; 2813 } 2814 2815 /* Sanity check */ 2816 ASSERT(ViewSize != 0); 2817 2818 /* Process must be in a session */ 2819 if (PsGetCurrentProcess()->ProcessInSession == FALSE) 2820 { 2821 DPRINT1("Process is not in session\n"); 2822 return STATUS_NOT_MAPPED_VIEW; 2823 } 2824 2825 /* Compute the correctly aligned base and end addresses */ 2826 StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase); 2827 EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1); 2828 2829 /* Sanity check and grab the session */ 2830 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE); 2831 Session = &MmSessionSpace->Session; 2832 2833 /* Get the hash entry for this allocation */ 2834 Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey; 2835 2836 /* Lock system space */ 2837 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer); 2838 2839 /* Loop twice so we can try rolling over if needed */ 2840 while (TRUE) 2841 { 2842 /* Extract the size and base addresses from the entry */ 2843 Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF; 2844 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF; 2845 2846 /* Convert the size to bucket chunks */ 2847 Size *= MI_SYSTEM_VIEW_BUCKET_SIZE; 2848 2849 /* Bail out if this entry fits in here */ 2850 if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break; 2851 2852 /* Check if we overflew past the end of the hash table */ 2853 if (++Hash >= Session->SystemSpaceHashSize) 2854 { 2855 /* Reset the hash to zero and keep searching from the bottom */ 2856 Hash = 0; 2857 if (++Count == 2) 2858 { 2859 /* But if we overflew twice, then this is not a real mapping */ 2860 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW, 2861 Base, 2862 2, 2863 0, 2864 0); 2865 } 2866 } 2867 } 2868 2869 /* Make sure the view being mapped is not file-based */ 2870 ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea; 2871 if (ControlArea->FilePointer != NULL) 2872 { 2873 /* It is, so we have to bail out */ 2874 DPRINT1("Only page-filed backed sections can be commited\n"); 2875 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 2876 return STATUS_ALREADY_COMMITTED; 2877 } 2878 2879 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */ 2880 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); 2881 ASSERT(ControlArea->u.Flags.Rom == 0); 2882 Subsection = (PSUBSECTION)(ControlArea + 1); 2883 2884 /* Get the start and end PTEs -- make sure the end PTE isn't past the end */ 2885 ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT); 2886 QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1; 2887 LastProtoPte = ProtoPte + QuotaCharge; 2888 if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection) 2889 { 2890 DPRINT1("PTE is out of bounds\n"); 2891 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 2892 return STATUS_INVALID_PARAMETER_2; 2893 } 2894 2895 /* Acquire the commit lock and count all the non-committed PTEs */ 2896 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex); 2897 PointerPte = ProtoPte; 2898 while (PointerPte < LastProtoPte) 2899 { 2900 if (PointerPte->u.Long) QuotaCharge--; 2901 PointerPte++; 2902 } 2903 2904 /* Was everything committed already? */ 2905 if (!QuotaCharge) 2906 { 2907 /* Nothing to do! */ 2908 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); 2909 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 2910 return STATUS_SUCCESS; 2911 } 2912 2913 /* Pick the segment and template PTE */ 2914 Segment = ControlArea->Segment; 2915 TempPte = Segment->SegmentPteTemplate; 2916 ASSERT(TempPte.u.Long != 0); 2917 2918 /* Loop all prototype PTEs to be committed */ 2919 PointerPte = ProtoPte; 2920 while (PointerPte < LastProtoPte) 2921 { 2922 /* Make sure the PTE is already invalid */ 2923 if (PointerPte->u.Long == 0) 2924 { 2925 /* And write the invalid PTE */ 2926 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2927 } 2928 2929 /* Move to the next PTE */ 2930 PointerPte++; 2931 } 2932 2933 /* Check if we had at least one page charged */ 2934 if (QuotaCharge) 2935 { 2936 /* Update the accounting data */ 2937 Segment->NumberOfCommittedPages += QuotaCharge; 2938 InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge); 2939 } 2940 2941 /* Release all */ 2942 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); 2943 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer); 2944 return STATUS_SUCCESS; 2945 } 2946 2947 VOID 2948 NTAPI 2949 MiDeleteARM3Section(PVOID ObjectBody) 2950 { 2951 PSECTION SectionObject; 2952 PCONTROL_AREA ControlArea; 2953 KIRQL OldIrql; 2954 2955 SectionObject = (PSECTION)ObjectBody; 2956 2957 if (SectionObject->u.Flags.Based == 1) 2958 { 2959 /* Remove the node from the global section address tree */ 2960 KeAcquireGuardedMutex(&MmSectionBasedMutex); 2961 MiRemoveNode(&SectionObject->Address, &MmSectionBasedRoot); 2962 KeReleaseGuardedMutex(&MmSectionBasedMutex); 2963 } 2964 2965 /* Lock the PFN database */ 2966 OldIrql = MiAcquirePfnLock(); 2967 2968 ASSERT(SectionObject->Segment); 2969 ASSERT(SectionObject->Segment->ControlArea); 2970 2971 ControlArea = SectionObject->Segment->ControlArea; 2972 2973 /* Dereference */ 2974 ControlArea->NumberOfSectionReferences--; 2975 ControlArea->NumberOfUserReferences--; 2976 2977 ASSERT(ControlArea->u.Flags.BeingDeleted == 0); 2978 2979 /* Check it. It will delete it if there is no more reference to it */ 2980 MiCheckControlArea(ControlArea, OldIrql); 2981 } 2982 2983 ULONG 2984 NTAPI 2985 MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer) 2986 { 2987 UNIMPLEMENTED_ONCE; 2988 return 0; 2989 } 2990 2991 /* SYSTEM CALLS ***************************************************************/ 2992 2993 NTSTATUS 2994 NTAPI 2995 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage, 2996 IN PVOID File2MappedAsFile) 2997 { 2998 PVOID AddressSpace; 2999 PMMVAD Vad1, Vad2; 3000 PFILE_OBJECT FileObject1, FileObject2; 3001 NTSTATUS Status; 3002 3003 /* Lock address space */ 3004 AddressSpace = MmGetCurrentAddressSpace(); 3005 MmLockAddressSpace(AddressSpace); 3006 3007 /* Get the VAD for Address 1 */ 3008 Vad1 = MiLocateAddress(File1MappedAsAnImage); 3009 if (Vad1 == NULL) 3010 { 3011 /* Fail, the address does not exist */ 3012 DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage); 3013 Status = STATUS_INVALID_ADDRESS; 3014 goto Exit; 3015 } 3016 3017 /* Get the VAD for Address 2 */ 3018 Vad2 = MiLocateAddress(File2MappedAsFile); 3019 if (Vad2 == NULL) 3020 { 3021 /* Fail, the address does not exist */ 3022 DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile); 3023 Status = STATUS_INVALID_ADDRESS; 3024 goto Exit; 3025 } 3026 3027 /* Get the file object pointer for VAD 1 */ 3028 FileObject1 = MiGetFileObjectForVad(Vad1); 3029 if (FileObject1 == NULL) 3030 { 3031 DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage); 3032 Status = STATUS_CONFLICTING_ADDRESSES; 3033 goto Exit; 3034 } 3035 3036 /* Get the file object pointer for VAD 2 */ 3037 FileObject2 = MiGetFileObjectForVad(Vad2); 3038 if (FileObject2 == NULL) 3039 { 3040 DPRINT1("Failed to get file object for Address 2 %p\n", File2MappedAsFile); 3041 Status = STATUS_CONFLICTING_ADDRESSES; 3042 goto Exit; 3043 } 3044 3045 /* Make sure Vad1 is an image mapping */ 3046 if (Vad1->u.VadFlags.VadType != VadImageMap) 3047 { 3048 DPRINT1("Address 1 (%p) is not an image mapping\n", File1MappedAsAnImage); 3049 Status = STATUS_NOT_SAME_DEVICE; 3050 goto Exit; 3051 } 3052 3053 /* SectionObjectPointer is equal if the files are equal */ 3054 if (FileObject1->SectionObjectPointer == FileObject2->SectionObjectPointer) 3055 { 3056 Status = STATUS_SUCCESS; 3057 } 3058 else 3059 { 3060 Status = STATUS_NOT_SAME_DEVICE; 3061 } 3062 3063 Exit: 3064 /* Unlock address space */ 3065 MmUnlockAddressSpace(AddressSpace); 3066 return Status; 3067 } 3068 3069 /* 3070 * @implemented 3071 */ 3072 NTSTATUS 3073 NTAPI 3074 NtCreateSection(OUT PHANDLE SectionHandle, 3075 IN ACCESS_MASK DesiredAccess, 3076 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, 3077 IN PLARGE_INTEGER MaximumSize OPTIONAL, 3078 IN ULONG SectionPageProtection OPTIONAL, 3079 IN ULONG AllocationAttributes, 3080 IN HANDLE FileHandle OPTIONAL) 3081 { 3082 LARGE_INTEGER SafeMaximumSize; 3083 PVOID SectionObject; 3084 HANDLE Handle; 3085 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3086 NTSTATUS Status; 3087 PAGED_CODE(); 3088 3089 /* Check for non-existing flags */ 3090 if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED | 3091 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE | 3092 SEC_NO_CHANGE))) 3093 { 3094 if (!(AllocationAttributes & 1)) 3095 { 3096 DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes); 3097 return STATUS_INVALID_PARAMETER_6; 3098 } 3099 } 3100 3101 /* Check for no allocation type */ 3102 if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE))) 3103 { 3104 DPRINT1("Missing allocation type in allocation attributes\n"); 3105 return STATUS_INVALID_PARAMETER_6; 3106 } 3107 3108 /* Check for image allocation with invalid attributes */ 3109 if ((AllocationAttributes & SEC_IMAGE) && 3110 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES | 3111 SEC_NOCACHE | SEC_NO_CHANGE))) 3112 { 3113 DPRINT1("Image allocation with invalid attributes\n"); 3114 return STATUS_INVALID_PARAMETER_6; 3115 } 3116 3117 /* Check for allocation type is both commit and reserve */ 3118 if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)) 3119 { 3120 DPRINT1("Commit and reserve in the same time\n"); 3121 return STATUS_INVALID_PARAMETER_6; 3122 } 3123 3124 /* Now check for valid protection */ 3125 if ((SectionPageProtection & PAGE_NOCACHE) || 3126 (SectionPageProtection & PAGE_WRITECOMBINE) || 3127 (SectionPageProtection & PAGE_GUARD) || 3128 (SectionPageProtection & PAGE_NOACCESS)) 3129 { 3130 DPRINT1("Sections don't support these protections\n"); 3131 return STATUS_INVALID_PAGE_PROTECTION; 3132 } 3133 3134 /* Use a maximum size of zero, if none was specified */ 3135 SafeMaximumSize.QuadPart = 0; 3136 3137 /* Check for user-mode caller */ 3138 if (PreviousMode != KernelMode) 3139 { 3140 /* Enter SEH */ 3141 _SEH2_TRY 3142 { 3143 /* Safely check user-mode parameters */ 3144 if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize); 3145 MaximumSize = &SafeMaximumSize; 3146 ProbeForWriteHandle(SectionHandle); 3147 } 3148 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3149 { 3150 /* Return the exception code */ 3151 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3152 } 3153 _SEH2_END; 3154 } 3155 else if (!MaximumSize) MaximumSize = &SafeMaximumSize; 3156 3157 /* Check that MaximumSize is valid if backed by paging file */ 3158 if ((!FileHandle) && (!MaximumSize->QuadPart)) 3159 return STATUS_INVALID_PARAMETER_4; 3160 3161 /* Create the section */ 3162 Status = MmCreateSection(&SectionObject, 3163 DesiredAccess, 3164 ObjectAttributes, 3165 MaximumSize, 3166 SectionPageProtection, 3167 AllocationAttributes, 3168 FileHandle, 3169 NULL); 3170 if (!NT_SUCCESS(Status)) return Status; 3171 3172 /* FIXME: Should zero last page for a file mapping */ 3173 3174 /* Now insert the object */ 3175 Status = ObInsertObject(SectionObject, 3176 NULL, 3177 DesiredAccess, 3178 0, 3179 NULL, 3180 &Handle); 3181 if (NT_SUCCESS(Status)) 3182 { 3183 /* Enter SEH */ 3184 _SEH2_TRY 3185 { 3186 /* Return the handle safely */ 3187 *SectionHandle = Handle; 3188 } 3189 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3190 { 3191 /* Nothing here */ 3192 } 3193 _SEH2_END; 3194 } 3195 3196 /* Return the status */ 3197 return Status; 3198 } 3199 3200 NTSTATUS 3201 NTAPI 3202 NtOpenSection(OUT PHANDLE SectionHandle, 3203 IN ACCESS_MASK DesiredAccess, 3204 IN POBJECT_ATTRIBUTES ObjectAttributes) 3205 { 3206 HANDLE Handle; 3207 NTSTATUS Status; 3208 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3209 PAGED_CODE(); 3210 3211 /* Check for user-mode caller */ 3212 if (PreviousMode != KernelMode) 3213 { 3214 /* Enter SEH */ 3215 _SEH2_TRY 3216 { 3217 /* Safely check user-mode parameters */ 3218 ProbeForWriteHandle(SectionHandle); 3219 } 3220 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3221 { 3222 /* Return the exception code */ 3223 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3224 } 3225 _SEH2_END; 3226 } 3227 3228 /* Try opening the object */ 3229 Status = ObOpenObjectByName(ObjectAttributes, 3230 MmSectionObjectType, 3231 PreviousMode, 3232 NULL, 3233 DesiredAccess, 3234 NULL, 3235 &Handle); 3236 3237 /* Enter SEH */ 3238 _SEH2_TRY 3239 { 3240 /* Return the handle safely */ 3241 *SectionHandle = Handle; 3242 } 3243 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3244 { 3245 /* Nothing here */ 3246 } 3247 _SEH2_END; 3248 3249 /* Return the status */ 3250 return Status; 3251 } 3252 3253 NTSTATUS 3254 NTAPI 3255 NtMapViewOfSection(IN HANDLE SectionHandle, 3256 IN HANDLE ProcessHandle, 3257 IN OUT PVOID* BaseAddress, 3258 IN ULONG_PTR ZeroBits, 3259 IN SIZE_T CommitSize, 3260 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, 3261 IN OUT PSIZE_T ViewSize, 3262 IN SECTION_INHERIT InheritDisposition, 3263 IN ULONG AllocationType, 3264 IN ULONG Protect) 3265 { 3266 PVOID SafeBaseAddress; 3267 LARGE_INTEGER SafeSectionOffset; 3268 SIZE_T SafeViewSize; 3269 PSECTION Section; 3270 PEPROCESS Process; 3271 NTSTATUS Status; 3272 ACCESS_MASK DesiredAccess; 3273 ULONG ProtectionMask; 3274 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3275 #if defined(_M_IX86) || defined(_M_AMD64) 3276 static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES | 3277 MEM_DOS_LIM | SEC_NO_CHANGE | MEM_RESERVE); 3278 #else 3279 static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES | 3280 SEC_NO_CHANGE | MEM_RESERVE); 3281 #endif 3282 3283 /* Check for invalid inherit disposition */ 3284 if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare)) 3285 { 3286 DPRINT1("Invalid inherit disposition\n"); 3287 return STATUS_INVALID_PARAMETER_8; 3288 } 3289 3290 /* Allow only valid allocation types */ 3291 if (AllocationType & ~ValidAllocationType) 3292 { 3293 DPRINT1("Invalid allocation type\n"); 3294 return STATUS_INVALID_PARAMETER_9; 3295 } 3296 3297 /* Convert the protection mask, and validate it */ 3298 ProtectionMask = MiMakeProtectionMask(Protect); 3299 if (ProtectionMask == MM_INVALID_PROTECTION) 3300 { 3301 DPRINT1("Invalid page protection\n"); 3302 return STATUS_INVALID_PAGE_PROTECTION; 3303 } 3304 3305 /* Now convert the protection mask into desired section access mask */ 3306 DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7]; 3307 3308 /* Assume no section offset */ 3309 SafeSectionOffset.QuadPart = 0; 3310 3311 /* Enter SEH */ 3312 _SEH2_TRY 3313 { 3314 /* Check for unsafe parameters */ 3315 if (PreviousMode != KernelMode) 3316 { 3317 /* Probe the parameters */ 3318 ProbeForWritePointer(BaseAddress); 3319 ProbeForWriteSize_t(ViewSize); 3320 } 3321 3322 /* Check if a section offset was given */ 3323 if (SectionOffset) 3324 { 3325 /* Check for unsafe parameters and capture section offset */ 3326 if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset); 3327 SafeSectionOffset = *SectionOffset; 3328 } 3329 3330 /* Capture the other parameters */ 3331 SafeBaseAddress = *BaseAddress; 3332 SafeViewSize = *ViewSize; 3333 } 3334 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3335 { 3336 /* Return the exception code */ 3337 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3338 } 3339 _SEH2_END; 3340 3341 /* Check for kernel-mode address */ 3342 if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS) 3343 { 3344 DPRINT1("Kernel base not allowed\n"); 3345 return STATUS_INVALID_PARAMETER_3; 3346 } 3347 3348 /* Check for range entering kernel-mode */ 3349 if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize) 3350 { 3351 DPRINT1("Overflowing into kernel base not allowed\n"); 3352 return STATUS_INVALID_PARAMETER_3; 3353 } 3354 3355 /* Check for invalid zero bits */ 3356 if (ZeroBits) 3357 { 3358 if (ZeroBits > MI_MAX_ZERO_BITS) 3359 { 3360 DPRINT1("Invalid zero bits\n"); 3361 return STATUS_INVALID_PARAMETER_4; 3362 } 3363 3364 if ((((ULONG_PTR)SafeBaseAddress << ZeroBits) >> ZeroBits) != (ULONG_PTR)SafeBaseAddress) 3365 { 3366 DPRINT1("Invalid zero bits\n"); 3367 return STATUS_INVALID_PARAMETER_4; 3368 } 3369 3370 if (((((ULONG_PTR)SafeBaseAddress + SafeViewSize) << ZeroBits) >> ZeroBits) != ((ULONG_PTR)SafeBaseAddress + SafeViewSize)) 3371 { 3372 DPRINT1("Invalid zero bits\n"); 3373 return STATUS_INVALID_PARAMETER_4; 3374 } 3375 } 3376 3377 /* Reference the process */ 3378 Status = ObReferenceObjectByHandle(ProcessHandle, 3379 PROCESS_VM_OPERATION, 3380 PsProcessType, 3381 PreviousMode, 3382 (PVOID*)&Process, 3383 NULL); 3384 if (!NT_SUCCESS(Status)) return Status; 3385 3386 /* Reference the section */ 3387 Status = ObReferenceObjectByHandle(SectionHandle, 3388 DesiredAccess, 3389 MmSectionObjectType, 3390 PreviousMode, 3391 (PVOID*)&Section, 3392 NULL); 3393 if (!NT_SUCCESS(Status)) 3394 { 3395 ObDereferenceObject(Process); 3396 return Status; 3397 } 3398 3399 if (Section->u.Flags.PhysicalMemory) 3400 { 3401 if (PreviousMode == UserMode && 3402 SafeSectionOffset.QuadPart + SafeViewSize > MmHighestPhysicalPage << PAGE_SHIFT) 3403 { 3404 DPRINT1("Denying map past highest physical page.\n"); 3405 ObDereferenceObject(Section); 3406 ObDereferenceObject(Process); 3407 return STATUS_INVALID_PARAMETER_6; 3408 } 3409 } 3410 else if (!(AllocationType & MEM_DOS_LIM)) 3411 { 3412 /* Check for non-allocation-granularity-aligned BaseAddress */ 3413 if (SafeBaseAddress != ALIGN_DOWN_POINTER_BY(SafeBaseAddress, MM_VIRTMEM_GRANULARITY)) 3414 { 3415 DPRINT("BaseAddress is not at 64-kilobyte address boundary.\n"); 3416 ObDereferenceObject(Section); 3417 ObDereferenceObject(Process); 3418 return STATUS_MAPPED_ALIGNMENT; 3419 } 3420 3421 /* Do the same for the section offset */ 3422 if (SafeSectionOffset.LowPart != ALIGN_DOWN_BY(SafeSectionOffset.LowPart, MM_VIRTMEM_GRANULARITY)) 3423 { 3424 DPRINT("SectionOffset is not at 64-kilobyte address boundary.\n"); 3425 ObDereferenceObject(Section); 3426 ObDereferenceObject(Process); 3427 return STATUS_MAPPED_ALIGNMENT; 3428 } 3429 } 3430 3431 /* Now do the actual mapping */ 3432 Status = MmMapViewOfSection(Section, 3433 Process, 3434 &SafeBaseAddress, 3435 ZeroBits, 3436 CommitSize, 3437 &SafeSectionOffset, 3438 &SafeViewSize, 3439 InheritDisposition, 3440 AllocationType, 3441 Protect); 3442 3443 /* Return data only on success */ 3444 if (NT_SUCCESS(Status)) 3445 { 3446 /* Check if this is an image for the current process */ 3447 if ((Section->u.Flags.Image) && 3448 (Process == PsGetCurrentProcess()) && 3449 (Status != STATUS_IMAGE_NOT_AT_BASE)) 3450 { 3451 /* Notify the debugger */ 3452 DbgkMapViewOfSection(Section, 3453 SafeBaseAddress, 3454 SafeSectionOffset.LowPart, 3455 SafeViewSize); 3456 } 3457 3458 /* Enter SEH */ 3459 _SEH2_TRY 3460 { 3461 /* Return parameters to user */ 3462 *BaseAddress = SafeBaseAddress; 3463 *ViewSize = SafeViewSize; 3464 if (SectionOffset) *SectionOffset = SafeSectionOffset; 3465 } 3466 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3467 { 3468 /* Nothing to do */ 3469 } 3470 _SEH2_END; 3471 } 3472 3473 /* Dereference all objects and return status */ 3474 ObDereferenceObject(Section); 3475 ObDereferenceObject(Process); 3476 return Status; 3477 } 3478 3479 NTSTATUS 3480 NTAPI 3481 NtUnmapViewOfSection(IN HANDLE ProcessHandle, 3482 IN PVOID BaseAddress) 3483 { 3484 PEPROCESS Process; 3485 NTSTATUS Status; 3486 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3487 3488 /* Don't allowing mapping kernel views */ 3489 if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS)) 3490 { 3491 DPRINT1("Trying to unmap a kernel view\n"); 3492 return STATUS_NOT_MAPPED_VIEW; 3493 } 3494 3495 /* Reference the process */ 3496 Status = ObReferenceObjectByHandle(ProcessHandle, 3497 PROCESS_VM_OPERATION, 3498 PsProcessType, 3499 PreviousMode, 3500 (PVOID*)&Process, 3501 NULL); 3502 if (!NT_SUCCESS(Status)) return Status; 3503 3504 /* Unmap the view */ 3505 Status = MiUnmapViewOfSection(Process, BaseAddress, 0); 3506 3507 /* Dereference the process and return status */ 3508 ObDereferenceObject(Process); 3509 return Status; 3510 } 3511 3512 NTSTATUS 3513 NTAPI 3514 NtExtendSection(IN HANDLE SectionHandle, 3515 IN OUT PLARGE_INTEGER NewMaximumSize) 3516 { 3517 LARGE_INTEGER SafeNewMaximumSize; 3518 PSECTION Section; 3519 NTSTATUS Status; 3520 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3521 3522 /* Check for user-mode parameters */ 3523 if (PreviousMode != KernelMode) 3524 { 3525 /* Enter SEH */ 3526 _SEH2_TRY 3527 { 3528 /* Probe and capture the maximum size, it's both read and write */ 3529 ProbeForWriteLargeInteger(NewMaximumSize); 3530 SafeNewMaximumSize = *NewMaximumSize; 3531 } 3532 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3533 { 3534 /* Return the exception code */ 3535 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3536 } 3537 _SEH2_END; 3538 } 3539 else 3540 { 3541 /* Just read the size directly */ 3542 SafeNewMaximumSize = *NewMaximumSize; 3543 } 3544 3545 /* Reference the section */ 3546 Status = ObReferenceObjectByHandle(SectionHandle, 3547 SECTION_EXTEND_SIZE, 3548 MmSectionObjectType, 3549 PreviousMode, 3550 (PVOID*)&Section, 3551 NULL); 3552 if (!NT_SUCCESS(Status)) return Status; 3553 3554 Status = MmExtendSection(Section, &SafeNewMaximumSize); 3555 3556 /* Dereference the section */ 3557 ObDereferenceObject(Section); 3558 3559 if (NT_SUCCESS(Status)) 3560 { 3561 _SEH2_TRY 3562 { 3563 /* Write back the new size */ 3564 *NewMaximumSize = SafeNewMaximumSize; 3565 } 3566 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3567 { 3568 Status = _SEH2_GetExceptionCode(); 3569 } 3570 _SEH2_END; 3571 } 3572 3573 /* Return the status */ 3574 return STATUS_NOT_IMPLEMENTED; 3575 } 3576 3577 /* EOF */ 3578