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