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