1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * FILE: ntoskrnl/mm/ARM3/pagfault.c 5 * PURPOSE: ARM Memory Manager Page Fault Handling 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 #define HYDRA_PROCESS (PEPROCESS)1 21 #if MI_TRACE_PFNS 22 BOOLEAN UserPdeFault = FALSE; 23 #endif 24 25 /* PRIVATE FUNCTIONS **********************************************************/ 26 27 static 28 NTSTATUS 29 NTAPI 30 MiCheckForUserStackOverflow(IN PVOID Address, 31 IN PVOID TrapInformation) 32 { 33 PETHREAD CurrentThread = PsGetCurrentThread(); 34 PTEB Teb = CurrentThread->Tcb.Teb; 35 PVOID StackBase, DeallocationStack, NextStackAddress; 36 SIZE_T GuaranteedSize; 37 NTSTATUS Status; 38 39 /* Do we own the address space lock? */ 40 if (CurrentThread->AddressSpaceOwner == 1) 41 { 42 /* This isn't valid */ 43 DPRINT1("Process owns address space lock\n"); 44 ASSERT(KeAreAllApcsDisabled() == TRUE); 45 return STATUS_GUARD_PAGE_VIOLATION; 46 } 47 48 /* Are we attached? */ 49 if (KeIsAttachedProcess()) 50 { 51 /* This isn't valid */ 52 DPRINT1("Process is attached\n"); 53 return STATUS_GUARD_PAGE_VIOLATION; 54 } 55 56 /* Read the current settings */ 57 StackBase = Teb->NtTib.StackBase; 58 DeallocationStack = Teb->DeallocationStack; 59 GuaranteedSize = Teb->GuaranteedStackBytes; 60 DPRINT("Handling guard page fault with Stacks Addresses 0x%p and 0x%p, guarantee: %lx\n", 61 StackBase, DeallocationStack, GuaranteedSize); 62 63 /* Guarantees make this code harder, for now, assume there aren't any */ 64 ASSERT(GuaranteedSize == 0); 65 66 /* So allocate only the minimum guard page size */ 67 GuaranteedSize = PAGE_SIZE; 68 69 /* Does this faulting stack address actually exist in the stack? */ 70 if ((Address >= StackBase) || (Address < DeallocationStack)) 71 { 72 /* That's odd... */ 73 DPRINT1("Faulting address outside of stack bounds. Address=%p, StackBase=%p, DeallocationStack=%p\n", 74 Address, StackBase, DeallocationStack); 75 return STATUS_GUARD_PAGE_VIOLATION; 76 } 77 78 /* This is where the stack will start now */ 79 NextStackAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(Address) - GuaranteedSize); 80 81 /* Do we have at least one page between here and the end of the stack? */ 82 if (((ULONG_PTR)NextStackAddress - PAGE_SIZE) <= (ULONG_PTR)DeallocationStack) 83 { 84 /* We don't -- Trying to make this guard page valid now */ 85 DPRINT1("Close to our death...\n"); 86 87 /* Calculate the next memory address */ 88 NextStackAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(DeallocationStack) + GuaranteedSize); 89 90 /* Allocate the memory */ 91 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 92 &NextStackAddress, 93 0, 94 &GuaranteedSize, 95 MEM_COMMIT, 96 PAGE_READWRITE); 97 if (NT_SUCCESS(Status)) 98 { 99 /* Success! */ 100 Teb->NtTib.StackLimit = NextStackAddress; 101 } 102 else 103 { 104 DPRINT1("Failed to allocate memory\n"); 105 } 106 107 return STATUS_STACK_OVERFLOW; 108 } 109 110 /* Don't handle this flag yet */ 111 ASSERT((PsGetCurrentProcess()->Peb->NtGlobalFlag & FLG_DISABLE_STACK_EXTENSION) == 0); 112 113 /* Update the stack limit */ 114 Teb->NtTib.StackLimit = (PVOID)((ULONG_PTR)NextStackAddress + GuaranteedSize); 115 116 /* Now move the guard page to the next page */ 117 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 118 &NextStackAddress, 119 0, 120 &GuaranteedSize, 121 MEM_COMMIT, 122 PAGE_READWRITE | PAGE_GUARD); 123 if ((NT_SUCCESS(Status) || (Status == STATUS_ALREADY_COMMITTED))) 124 { 125 /* We did it! */ 126 DPRINT("Guard page handled successfully for %p\n", Address); 127 return STATUS_PAGE_FAULT_GUARD_PAGE; 128 } 129 130 /* Fail, we couldn't move the guard page */ 131 DPRINT1("Guard page failure: %lx\n", Status); 132 ASSERT(FALSE); 133 return STATUS_STACK_OVERFLOW; 134 } 135 136 FORCEINLINE 137 BOOLEAN 138 MiIsAccessAllowed( 139 _In_ ULONG ProtectionMask, 140 _In_ BOOLEAN Write, 141 _In_ BOOLEAN Execute) 142 { 143 #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \ 144 (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \ 145 ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7) 146 static const UCHAR AccessAllowedMask[2][2] = 147 { 148 { // Protect 0 1 2 3 4 5 6 7 149 _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ 150 _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ 151 }, 152 { 153 _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE 154 _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE 155 } 156 }; 157 158 /* We want only the lower access bits */ 159 ProtectionMask &= MM_PROTECT_ACCESS; 160 161 /* Look it up in the table */ 162 return (AccessAllowedMask[Write != 0][Execute != 0] >> ProtectionMask) & 1; 163 } 164 165 static 166 NTSTATUS 167 NTAPI 168 MiAccessCheck(IN PMMPTE PointerPte, 169 IN BOOLEAN StoreInstruction, 170 IN KPROCESSOR_MODE PreviousMode, 171 IN ULONG_PTR ProtectionMask, 172 IN PVOID TrapFrame, 173 IN BOOLEAN LockHeld) 174 { 175 MMPTE TempPte; 176 177 /* Check for invalid user-mode access */ 178 if ((PreviousMode == UserMode) && (PointerPte > MiHighestUserPte)) 179 { 180 return STATUS_ACCESS_VIOLATION; 181 } 182 183 /* Capture the PTE -- is it valid? */ 184 TempPte = *PointerPte; 185 if (TempPte.u.Hard.Valid) 186 { 187 /* Was someone trying to write to it? */ 188 if (StoreInstruction) 189 { 190 /* Is it writable?*/ 191 if (MI_IS_PAGE_WRITEABLE(&TempPte) || 192 MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 193 { 194 /* Then there's nothing to worry about */ 195 return STATUS_SUCCESS; 196 } 197 198 /* Oops! This isn't allowed */ 199 return STATUS_ACCESS_VIOLATION; 200 } 201 202 /* Someone was trying to read from a valid PTE, that's fine too */ 203 return STATUS_SUCCESS; 204 } 205 206 /* Check if the protection on the page allows what is being attempted */ 207 if (!MiIsAccessAllowed(ProtectionMask, StoreInstruction, FALSE)) 208 { 209 return STATUS_ACCESS_VIOLATION; 210 } 211 212 /* Check if this is a guard page */ 213 if ((ProtectionMask & MM_PROTECT_SPECIAL) == MM_GUARDPAGE) 214 { 215 ASSERT(ProtectionMask != MM_DECOMMIT); 216 217 /* Attached processes can't expand their stack */ 218 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION; 219 220 /* No support for prototype PTEs yet */ 221 ASSERT(TempPte.u.Soft.Prototype == 0); 222 223 /* Remove the guard page bit, and return a guard page violation */ 224 TempPte.u.Soft.Protection = ProtectionMask & ~MM_GUARDPAGE; 225 ASSERT(TempPte.u.Long != 0); 226 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 227 return STATUS_GUARD_PAGE_VIOLATION; 228 } 229 230 /* Nothing to do */ 231 return STATUS_SUCCESS; 232 } 233 234 static 235 PMMPTE 236 NTAPI 237 MiCheckVirtualAddress(IN PVOID VirtualAddress, 238 OUT PULONG ProtectCode, 239 OUT PMMVAD *ProtoVad) 240 { 241 PMMVAD Vad; 242 PMMPTE PointerPte; 243 244 /* No prototype/section support for now */ 245 *ProtoVad = NULL; 246 247 /* User or kernel fault? */ 248 if (VirtualAddress <= MM_HIGHEST_USER_ADDRESS) 249 { 250 /* Special case for shared data */ 251 if (PAGE_ALIGN(VirtualAddress) == (PVOID)MM_SHARED_USER_DATA_VA) 252 { 253 /* It's a read-only page */ 254 *ProtectCode = MM_READONLY; 255 return MmSharedUserDataPte; 256 } 257 258 /* Find the VAD, it might not exist if the address is bogus */ 259 Vad = MiLocateAddress(VirtualAddress); 260 if (!Vad) 261 { 262 /* Bogus virtual address */ 263 *ProtectCode = MM_NOACCESS; 264 return NULL; 265 } 266 267 /* ReactOS does not handle physical memory VADs yet */ 268 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory); 269 270 /* Check if it's a section, or just an allocation */ 271 if (Vad->u.VadFlags.PrivateMemory) 272 { 273 /* ReactOS does not handle AWE VADs yet */ 274 ASSERT(Vad->u.VadFlags.VadType != VadAwe); 275 276 /* This must be a TEB/PEB VAD */ 277 if (Vad->u.VadFlags.MemCommit) 278 { 279 /* It's committed, so return the VAD protection */ 280 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection; 281 } 282 else 283 { 284 /* It has not yet been committed, so return no access */ 285 *ProtectCode = MM_NOACCESS; 286 } 287 288 /* In both cases, return no PTE */ 289 return NULL; 290 } 291 else 292 { 293 /* ReactOS does not supoprt these VADs yet */ 294 ASSERT(Vad->u.VadFlags.VadType != VadImageMap); 295 ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0); 296 297 /* Return the proto VAD */ 298 *ProtoVad = Vad; 299 300 /* Get the prototype PTE for this page */ 301 PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte; 302 ASSERT(PointerPte != NULL); 303 ASSERT(PointerPte <= Vad->LastContiguousPte); 304 305 /* Return the Prototype PTE and the protection for the page mapping */ 306 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection; 307 return PointerPte; 308 } 309 } 310 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress)) 311 { 312 /* This should never happen, as these addresses are handled by the double-maping */ 313 if (((PMMPTE)VirtualAddress >= MiAddressToPte(MmPagedPoolStart)) && 314 ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool)) 315 { 316 /* Fail such access */ 317 *ProtectCode = MM_NOACCESS; 318 return NULL; 319 } 320 321 /* Return full access rights */ 322 *ProtectCode = MM_EXECUTE_READWRITE; 323 return NULL; 324 } 325 else if (MI_IS_SESSION_ADDRESS(VirtualAddress)) 326 { 327 /* ReactOS does not have an image list yet, so bail out to failure case */ 328 ASSERT(IsListEmpty(&MmSessionSpace->ImageList)); 329 } 330 331 /* Default case -- failure */ 332 *ProtectCode = MM_NOACCESS; 333 return NULL; 334 } 335 336 #if (_MI_PAGING_LEVELS == 2) 337 static 338 NTSTATUS 339 FASTCALL 340 MiCheckPdeForSessionSpace(IN PVOID Address) 341 { 342 MMPTE TempPde; 343 PMMPDE PointerPde; 344 PVOID SessionAddress; 345 ULONG Index; 346 347 /* Is this a session PTE? */ 348 if (MI_IS_SESSION_PTE(Address)) 349 { 350 /* Make sure the PDE for session space is valid */ 351 PointerPde = MiAddressToPde(MmSessionSpace); 352 if (!PointerPde->u.Hard.Valid) 353 { 354 /* This means there's no valid session, bail out */ 355 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n", 356 Address); 357 DbgBreakPoint(); 358 return STATUS_ACCESS_VIOLATION; 359 } 360 361 /* Now get the session-specific page table for this address */ 362 SessionAddress = MiPteToAddress(Address); 363 PointerPde = MiAddressToPte(Address); 364 if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1; 365 366 /* It's not valid, so find it in the page table array */ 367 Index = ((ULONG_PTR)SessionAddress - (ULONG_PTR)MmSessionBase) >> 22; 368 TempPde.u.Long = MmSessionSpace->PageTables[Index].u.Long; 369 if (TempPde.u.Hard.Valid) 370 { 371 /* The copy is valid, so swap it in */ 372 InterlockedExchange((PLONG)PointerPde, TempPde.u.Long); 373 return STATUS_WAIT_1; 374 } 375 376 /* We don't seem to have allocated a page table for this address yet? */ 377 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n", 378 PointerPde->u.Long, SessionAddress); 379 DbgBreakPoint(); 380 return STATUS_ACCESS_VIOLATION; 381 } 382 383 /* Is the address also a session address? If not, we're done */ 384 if (!MI_IS_SESSION_ADDRESS(Address)) return STATUS_SUCCESS; 385 386 /* It is, so again get the PDE for session space */ 387 PointerPde = MiAddressToPde(MmSessionSpace); 388 if (!PointerPde->u.Hard.Valid) 389 { 390 /* This means there's no valid session, bail out */ 391 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n", 392 Address); 393 DbgBreakPoint(); 394 return STATUS_ACCESS_VIOLATION; 395 } 396 397 /* Now get the PDE for the address itself */ 398 PointerPde = MiAddressToPde(Address); 399 if (!PointerPde->u.Hard.Valid) 400 { 401 /* Do the swap, we should be good to go */ 402 Index = ((ULONG_PTR)Address - (ULONG_PTR)MmSessionBase) >> 22; 403 PointerPde->u.Long = MmSessionSpace->PageTables[Index].u.Long; 404 if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1; 405 406 /* We had not allocated a page table for this session address yet, fail! */ 407 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n", 408 PointerPde->u.Long, Address); 409 DbgBreakPoint(); 410 return STATUS_ACCESS_VIOLATION; 411 } 412 413 /* It's valid, so there's nothing to do */ 414 return STATUS_SUCCESS; 415 } 416 417 NTSTATUS 418 FASTCALL 419 MiCheckPdeForPagedPool(IN PVOID Address) 420 { 421 PMMPDE PointerPde; 422 NTSTATUS Status = STATUS_SUCCESS; 423 424 /* Check session PDE */ 425 if (MI_IS_SESSION_ADDRESS(Address)) return MiCheckPdeForSessionSpace(Address); 426 if (MI_IS_SESSION_PTE(Address)) return MiCheckPdeForSessionSpace(Address); 427 428 // 429 // Check if this is a fault while trying to access the page table itself 430 // 431 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) 432 { 433 // 434 // Send a hint to the page fault handler that this is only a valid fault 435 // if we already detected this was access within the page table range 436 // 437 PointerPde = (PMMPDE)MiAddressToPte(Address); 438 Status = STATUS_WAIT_1; 439 } 440 else if (Address < MmSystemRangeStart) 441 { 442 // 443 // This is totally illegal 444 // 445 return STATUS_ACCESS_VIOLATION; 446 } 447 else 448 { 449 // 450 // Get the PDE for the address 451 // 452 PointerPde = MiAddressToPde(Address); 453 } 454 455 // 456 // Check if it's not valid 457 // 458 if (PointerPde->u.Hard.Valid == 0) 459 { 460 // 461 // Copy it from our double-mapped system page directory 462 // 463 InterlockedExchangePte(PointerPde, 464 MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long); 465 } 466 467 // 468 // Return status 469 // 470 return Status; 471 } 472 #else 473 NTSTATUS 474 FASTCALL 475 MiCheckPdeForPagedPool(IN PVOID Address) 476 { 477 return STATUS_ACCESS_VIOLATION; 478 } 479 #endif 480 481 VOID 482 NTAPI 483 MiZeroPfn(IN PFN_NUMBER PageFrameNumber) 484 { 485 PMMPTE ZeroPte; 486 MMPTE TempPte; 487 PMMPFN Pfn1; 488 PVOID ZeroAddress; 489 490 /* Get the PFN for this page */ 491 Pfn1 = MiGetPfnEntry(PageFrameNumber); 492 ASSERT(Pfn1); 493 494 /* Grab a system PTE we can use to zero the page */ 495 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace); 496 ASSERT(ZeroPte); 497 498 /* Initialize the PTE for it */ 499 TempPte = ValidKernelPte; 500 TempPte.u.Hard.PageFrameNumber = PageFrameNumber; 501 502 /* Setup caching */ 503 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined) 504 { 505 /* Write combining, no caching */ 506 MI_PAGE_DISABLE_CACHE(&TempPte); 507 MI_PAGE_WRITE_COMBINED(&TempPte); 508 } 509 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached) 510 { 511 /* Write through, no caching */ 512 MI_PAGE_DISABLE_CACHE(&TempPte); 513 MI_PAGE_WRITE_THROUGH(&TempPte); 514 } 515 516 /* Make the system PTE valid with our PFN */ 517 MI_WRITE_VALID_PTE(ZeroPte, TempPte); 518 519 /* Get the address it maps to, and zero it out */ 520 ZeroAddress = MiPteToAddress(ZeroPte); 521 KeZeroPages(ZeroAddress, PAGE_SIZE); 522 523 /* Now get rid of it */ 524 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace); 525 } 526 527 VOID 528 NTAPI 529 MiCopyPfn( 530 _In_ PFN_NUMBER DestPage, 531 _In_ PFN_NUMBER SrcPage) 532 { 533 PMMPTE SysPtes; 534 MMPTE TempPte; 535 PMMPFN DestPfn, SrcPfn; 536 PVOID DestAddress; 537 const VOID* SrcAddress; 538 539 /* Get the PFNs */ 540 DestPfn = MiGetPfnEntry(DestPage); 541 ASSERT(DestPfn); 542 SrcPfn = MiGetPfnEntry(SrcPage); 543 ASSERT(SrcPfn); 544 545 /* Grab 2 system PTEs */ 546 SysPtes = MiReserveSystemPtes(2, SystemPteSpace); 547 ASSERT(SysPtes); 548 549 /* Initialize the destination PTE */ 550 TempPte = ValidKernelPte; 551 TempPte.u.Hard.PageFrameNumber = DestPage; 552 553 /* Setup caching */ 554 if (DestPfn->u3.e1.CacheAttribute == MiWriteCombined) 555 { 556 /* Write combining, no caching */ 557 MI_PAGE_DISABLE_CACHE(&TempPte); 558 MI_PAGE_WRITE_COMBINED(&TempPte); 559 } 560 else if (DestPfn->u3.e1.CacheAttribute == MiNonCached) 561 { 562 /* Write through, no caching */ 563 MI_PAGE_DISABLE_CACHE(&TempPte); 564 MI_PAGE_WRITE_THROUGH(&TempPte); 565 } 566 567 /* Make the system PTE valid with our PFN */ 568 MI_WRITE_VALID_PTE(&SysPtes[0], TempPte); 569 570 /* Initialize the source PTE */ 571 TempPte = ValidKernelPte; 572 TempPte.u.Hard.PageFrameNumber = SrcPage; 573 574 /* Setup caching */ 575 if (SrcPfn->u3.e1.CacheAttribute == MiNonCached) 576 { 577 MI_PAGE_DISABLE_CACHE(&TempPte); 578 } 579 580 /* Make the system PTE valid with our PFN */ 581 MI_WRITE_VALID_PTE(&SysPtes[1], TempPte); 582 583 /* Get the addresses and perform the copy */ 584 DestAddress = MiPteToAddress(&SysPtes[0]); 585 SrcAddress = MiPteToAddress(&SysPtes[1]); 586 RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE); 587 588 /* Now get rid of it */ 589 MiReleaseSystemPtes(SysPtes, 2, SystemPteSpace); 590 } 591 592 static 593 NTSTATUS 594 NTAPI 595 MiResolveDemandZeroFault(IN PVOID Address, 596 IN PMMPTE PointerPte, 597 IN ULONG Protection, 598 IN PEPROCESS Process, 599 IN KIRQL OldIrql) 600 { 601 PFN_NUMBER PageFrameNumber = 0; 602 MMPTE TempPte; 603 BOOLEAN NeedZero = FALSE, HaveLock = FALSE; 604 ULONG Color; 605 PMMPFN Pfn1; 606 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n", 607 Address, 608 Process); 609 610 /* Must currently only be called by paging path */ 611 if ((Process > HYDRA_PROCESS) && (OldIrql == MM_NOIRQL)) 612 { 613 /* Sanity check */ 614 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte)); 615 616 /* No forking yet */ 617 ASSERT(Process->ForkInProgress == NULL); 618 619 /* Get process color */ 620 Color = MI_GET_NEXT_PROCESS_COLOR(Process); 621 ASSERT(Color != 0xFFFFFFFF); 622 623 /* We'll need a zero page */ 624 NeedZero = TRUE; 625 } 626 else 627 { 628 /* Check if we need a zero page */ 629 NeedZero = (OldIrql != MM_NOIRQL); 630 631 /* Session-backed image views must be zeroed */ 632 if ((Process == HYDRA_PROCESS) && 633 ((MI_IS_SESSION_IMAGE_ADDRESS(Address)) || 634 ((Address >= MiSessionViewStart) && (Address < MiSessionSpaceWs)))) 635 { 636 NeedZero = TRUE; 637 } 638 639 /* Hardcode unknown color */ 640 Color = 0xFFFFFFFF; 641 } 642 643 /* Check if the PFN database should be acquired */ 644 if (OldIrql == MM_NOIRQL) 645 { 646 /* Acquire it and remember we should release it after */ 647 OldIrql = MiAcquirePfnLock(); 648 HaveLock = TRUE; 649 } 650 651 /* We either manually locked the PFN DB, or already came with it locked */ 652 MI_ASSERT_PFN_LOCK_HELD(); 653 ASSERT(PointerPte->u.Hard.Valid == 0); 654 655 /* Assert we have enough pages */ 656 ASSERT(MmAvailablePages >= 32); 657 658 #if MI_TRACE_PFNS 659 if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE); 660 if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO); 661 #endif 662 if (Process == HYDRA_PROCESS) MI_SET_PROCESS2("Hydra"); 663 else if (Process) MI_SET_PROCESS2(Process->ImageFileName); 664 else MI_SET_PROCESS2("Kernel Demand 0"); 665 666 /* Do we need a zero page? */ 667 if (Color != 0xFFFFFFFF) 668 { 669 /* Try to get one, if we couldn't grab a free page and zero it */ 670 PageFrameNumber = MiRemoveZeroPageSafe(Color); 671 if (!PageFrameNumber) 672 { 673 /* We'll need a free page and zero it manually */ 674 PageFrameNumber = MiRemoveAnyPage(Color); 675 NeedZero = TRUE; 676 } 677 } 678 else 679 { 680 /* Get a color, and see if we should grab a zero or non-zero page */ 681 Color = MI_GET_NEXT_COLOR(); 682 if (!NeedZero) 683 { 684 /* Process or system doesn't want a zero page, grab anything */ 685 PageFrameNumber = MiRemoveAnyPage(Color); 686 } 687 else 688 { 689 /* System wants a zero page, obtain one */ 690 PageFrameNumber = MiRemoveZeroPage(Color); 691 } 692 } 693 694 /* Initialize it */ 695 MiInitializePfn(PageFrameNumber, PointerPte, TRUE); 696 697 /* Increment demand zero faults */ 698 KeGetCurrentPrcb()->MmDemandZeroCount++; 699 700 /* Zero the page if need be */ 701 if (NeedZero) MiZeroPfn(PageFrameNumber); 702 703 /* Fault on user PDE, or fault on user PTE? */ 704 if (PointerPte <= MiHighestUserPte) 705 { 706 /* User fault, build a user PTE */ 707 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 708 PointerPte, 709 Protection, 710 PageFrameNumber); 711 } 712 else 713 { 714 /* This is a user-mode PDE, create a kernel PTE for it */ 715 MI_MAKE_HARDWARE_PTE(&TempPte, 716 PointerPte, 717 Protection, 718 PageFrameNumber); 719 } 720 721 /* Set it dirty if it's a writable page */ 722 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte); 723 724 /* Write it */ 725 MI_WRITE_VALID_PTE(PointerPte, TempPte); 726 727 /* Did we manually acquire the lock */ 728 if (HaveLock) 729 { 730 /* Get the PFN entry */ 731 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber); 732 733 /* Windows does these sanity checks */ 734 ASSERT(Pfn1->u1.Event == 0); 735 ASSERT(Pfn1->u3.e1.PrototypePte == 0); 736 737 /* Release it */ 738 MiReleasePfnLock(OldIrql); 739 740 /* Update performance counters */ 741 if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++; 742 } 743 744 /* Add the page to our working set, if it's not a proto PTE */ 745 if ((Process > HYDRA_PROCESS) && (PointerPte == MiAddressToPte(Address))) 746 { 747 /* FIXME: Also support session VM scenario */ 748 MiInsertInWorkingSetList(&Process->Vm, Address, Protection); 749 } 750 751 // 752 // It's all good now 753 // 754 DPRINT("Demand zero page has now been paged in\n"); 755 return STATUS_PAGE_FAULT_DEMAND_ZERO; 756 } 757 758 static 759 NTSTATUS 760 NTAPI 761 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction, 762 IN PVOID Address, 763 IN PMMPTE PointerPte, 764 IN PMMPTE PointerProtoPte, 765 IN KIRQL OldIrql, 766 IN PMMPFN* LockedProtoPfn) 767 { 768 MMPTE TempPte; 769 PMMPTE OriginalPte, PageTablePte; 770 ULONG_PTR Protection; 771 PFN_NUMBER PageFrameIndex; 772 PMMPFN Pfn1, Pfn2; 773 BOOLEAN OriginalProtection, DirtyPage; 774 775 /* Must be called with an valid prototype PTE, with the PFN lock held */ 776 MI_ASSERT_PFN_LOCK_HELD(); 777 ASSERT(PointerProtoPte->u.Hard.Valid == 1); 778 779 /* Get the page */ 780 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte); 781 782 /* Get the PFN entry and set it as a prototype PTE */ 783 Pfn1 = MiGetPfnEntry(PageFrameIndex); 784 Pfn1->u3.e1.PrototypePte = 1; 785 786 /* Increment the share count for the page table */ 787 PageTablePte = MiAddressToPte(PointerPte); 788 Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber); 789 Pfn2->u2.ShareCount++; 790 791 /* Check where we should be getting the protection information from */ 792 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) 793 { 794 /* Get the protection from the PTE, there's no real Proto PTE data */ 795 Protection = PointerPte->u.Soft.Protection; 796 797 /* Remember that we did not use the proto protection */ 798 OriginalProtection = FALSE; 799 } 800 else 801 { 802 /* Get the protection from the original PTE link */ 803 OriginalPte = &Pfn1->OriginalPte; 804 Protection = OriginalPte->u.Soft.Protection; 805 806 /* Remember that we used the original protection */ 807 OriginalProtection = TRUE; 808 809 /* Check if this was a write on a read only proto */ 810 if ((StoreInstruction) && !(Protection & MM_READWRITE)) 811 { 812 /* Clear the flag */ 813 StoreInstruction = 0; 814 } 815 } 816 817 /* Check if this was a write on a non-COW page */ 818 DirtyPage = FALSE; 819 if ((StoreInstruction) && ((Protection & MM_WRITECOPY) != MM_WRITECOPY)) 820 { 821 /* Then the page should be marked dirty */ 822 DirtyPage = TRUE; 823 824 /* ReactOS check */ 825 ASSERT(Pfn1->OriginalPte.u.Soft.Prototype != 0); 826 } 827 828 /* Did we get a locked incoming PFN? */ 829 if (*LockedProtoPfn) 830 { 831 /* Drop a reference */ 832 ASSERT((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1); 833 MiDereferencePfnAndDropLockCount(*LockedProtoPfn); 834 *LockedProtoPfn = NULL; 835 } 836 837 /* Release the PFN lock */ 838 MiReleasePfnLock(OldIrql); 839 840 /* Remove special/caching bits */ 841 Protection &= ~MM_PROTECT_SPECIAL; 842 843 /* Setup caching */ 844 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined) 845 { 846 /* Write combining, no caching */ 847 MI_PAGE_DISABLE_CACHE(&TempPte); 848 MI_PAGE_WRITE_COMBINED(&TempPte); 849 } 850 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached) 851 { 852 /* Write through, no caching */ 853 MI_PAGE_DISABLE_CACHE(&TempPte); 854 MI_PAGE_WRITE_THROUGH(&TempPte); 855 } 856 857 /* Check if this is a kernel or user address */ 858 if (Address < MmSystemRangeStart) 859 { 860 /* Build the user PTE */ 861 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex); 862 } 863 else 864 { 865 /* Build the kernel PTE */ 866 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex); 867 } 868 869 /* Set the dirty flag if needed */ 870 if (DirtyPage) MI_MAKE_DIRTY_PAGE(&TempPte); 871 872 /* Write the PTE */ 873 MI_WRITE_VALID_PTE(PointerPte, TempPte); 874 875 /* Reset the protection if needed */ 876 if (OriginalProtection) Protection = MM_ZERO_ACCESS; 877 878 /* Return success */ 879 ASSERT(PointerPte == MiAddressToPte(Address)); 880 return STATUS_SUCCESS; 881 } 882 883 static 884 NTSTATUS 885 NTAPI 886 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction, 887 _In_ PVOID FaultingAddress, 888 _In_ PMMPTE PointerPte, 889 _In_ PEPROCESS CurrentProcess, 890 _Inout_ KIRQL *OldIrql) 891 { 892 ULONG Color; 893 PFN_NUMBER Page; 894 NTSTATUS Status; 895 MMPTE TempPte = *PointerPte; 896 PMMPFN Pfn1; 897 ULONG PageFileIndex = TempPte.u.Soft.PageFileLow; 898 ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh; 899 ULONG Protection = TempPte.u.Soft.Protection; 900 901 /* Things we don't support yet */ 902 ASSERT(CurrentProcess > HYDRA_PROCESS); 903 ASSERT(*OldIrql != MM_NOIRQL); 904 905 MI_SET_USAGE(MI_USAGE_PAGE_FILE); 906 MI_SET_PROCESS(CurrentProcess); 907 908 /* We must hold the PFN lock */ 909 MI_ASSERT_PFN_LOCK_HELD(); 910 911 /* Some sanity checks */ 912 ASSERT(TempPte.u.Hard.Valid == 0); 913 ASSERT(TempPte.u.Soft.PageFileHigh != 0); 914 ASSERT(TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED); 915 916 /* Get any page, it will be overwritten */ 917 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess); 918 Page = MiRemoveAnyPage(Color); 919 920 /* Initialize this PFN */ 921 MiInitializePfn(Page, PointerPte, StoreInstruction); 922 923 /* Sets the PFN as being in IO operation */ 924 Pfn1 = MI_PFN_ELEMENT(Page); 925 ASSERT(Pfn1->u1.Event == NULL); 926 ASSERT(Pfn1->u3.e1.ReadInProgress == 0); 927 ASSERT(Pfn1->u3.e1.WriteInProgress == 0); 928 Pfn1->u3.e1.ReadInProgress = 1; 929 930 /* We must write the PTE now as the PFN lock will be released while performing the IO operation */ 931 MI_MAKE_TRANSITION_PTE(&TempPte, Page, Protection); 932 933 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 934 935 /* Release the PFN lock while we proceed */ 936 MiReleasePfnLock(*OldIrql); 937 938 /* Do the paging IO */ 939 Status = MiReadPageFile(Page, PageFileIndex, PageFileOffset); 940 941 /* Lock the PFN database again */ 942 *OldIrql = MiAcquirePfnLock(); 943 944 /* Nobody should have changed that while we were not looking */ 945 ASSERT(Pfn1->u3.e1.ReadInProgress == 1); 946 ASSERT(Pfn1->u3.e1.WriteInProgress == 0); 947 948 if (!NT_SUCCESS(Status)) 949 { 950 /* Malheur! */ 951 ASSERT(FALSE); 952 Pfn1->u4.InPageError = 1; 953 Pfn1->u1.ReadStatus = Status; 954 } 955 956 /* And the PTE can finally be valid */ 957 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, Page); 958 MI_WRITE_VALID_PTE(PointerPte, TempPte); 959 960 Pfn1->u3.e1.ReadInProgress = 0; 961 /* Did someone start to wait on us while we proceeded ? */ 962 if (Pfn1->u1.Event) 963 { 964 /* Tell them we're done */ 965 KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE); 966 } 967 968 /* And we can insert this into the working set */ 969 MiInsertInWorkingSetList(&CurrentProcess->Vm, FaultingAddress, Protection); 970 971 return Status; 972 } 973 974 static 975 NTSTATUS 976 NTAPI 977 MiResolveTransitionFault(IN BOOLEAN StoreInstruction, 978 IN PVOID FaultingAddress, 979 IN PMMPTE PointerPte, 980 IN PEPROCESS CurrentProcess, 981 IN KIRQL OldIrql, 982 OUT PKEVENT **InPageBlock) 983 { 984 PFN_NUMBER PageFrameIndex; 985 PMMPFN Pfn1; 986 MMPTE TempPte; 987 PMMPTE PointerToPteForProtoPage; 988 ULONG Protection; 989 990 DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n", 991 FaultingAddress, PointerPte, CurrentProcess->ImageFileName); 992 993 /* Windowss does this check */ 994 ASSERT(*InPageBlock == NULL); 995 996 /* ARM3 doesn't support this path */ 997 ASSERT(OldIrql != MM_NOIRQL); 998 999 /* Capture the PTE and make sure it's in transition format */ 1000 TempPte = *PointerPte; 1001 ASSERT((TempPte.u.Soft.Valid == 0) && 1002 (TempPte.u.Soft.Prototype == 0) && 1003 (TempPte.u.Soft.Transition == 1)); 1004 1005 /* Get the PFN and the PFN entry */ 1006 PageFrameIndex = TempPte.u.Trans.PageFrameNumber; 1007 DPRINT("Transition PFN: %lx\n", PageFrameIndex); 1008 Pfn1 = MiGetPfnEntry(PageFrameIndex); 1009 1010 /* One more transition fault! */ 1011 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); 1012 1013 /* This is from ARM3 -- Windows normally handles this here */ 1014 ASSERT(Pfn1->u4.InPageError == 0); 1015 1016 /* See if we should wait before terminating the fault */ 1017 if ((Pfn1->u3.e1.ReadInProgress == 1) 1018 || ((Pfn1->u3.e1.WriteInProgress == 1) && StoreInstruction)) 1019 { 1020 DPRINT1("The page is currently in a page transition !\n"); 1021 *InPageBlock = &Pfn1->u1.Event; 1022 if (PointerPte == Pfn1->PteAddress) 1023 { 1024 DPRINT1("And this if for this particular PTE.\n"); 1025 /* The PTE will be made valid by the thread serving the fault */ 1026 return STATUS_SUCCESS; // FIXME: Maybe something more descriptive 1027 } 1028 } 1029 1030 /* Windows checks there's some free pages and this isn't an in-page error */ 1031 ASSERT(MmAvailablePages > 0); 1032 ASSERT(Pfn1->u4.InPageError == 0); 1033 1034 /* ReactOS checks for this */ 1035 ASSERT(MmAvailablePages > 32); 1036 1037 /* Was this a transition page in the valid list, or free/zero list? */ 1038 if (Pfn1->u3.e1.PageLocation == ActiveAndValid) 1039 { 1040 /* All Windows does here is a bunch of sanity checks */ 1041 DPRINT("Transition in active list\n"); 1042 ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) && 1043 (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd))); 1044 ASSERT(Pfn1->u2.ShareCount != 0); 1045 ASSERT(Pfn1->u3.e2.ReferenceCount != 0); 1046 } 1047 else 1048 { 1049 /* Otherwise, the page is removed from its list */ 1050 DPRINT("Transition page in free/zero list\n"); 1051 MiUnlinkPageFromList(Pfn1); 1052 MiReferenceUnusedPageAndBumpLockCount(Pfn1); 1053 } 1054 1055 /* At this point, there should no longer be any in-page errors */ 1056 ASSERT(Pfn1->u4.InPageError == 0); 1057 1058 /* Check if this was a PFN with no more share references */ 1059 if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1); 1060 1061 /* Bump the share count and make the page valid */ 1062 Pfn1->u2.ShareCount++; 1063 Pfn1->u3.e1.PageLocation = ActiveAndValid; 1064 1065 /* Prototype PTEs are in paged pool, which itself might be in transition */ 1066 if (FaultingAddress >= MmSystemRangeStart) 1067 { 1068 /* Check if this is a paged pool PTE in transition state */ 1069 PointerToPteForProtoPage = MiAddressToPte(PointerPte); 1070 TempPte = *PointerToPteForProtoPage; 1071 if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1)) 1072 { 1073 /* This isn't yet supported */ 1074 DPRINT1("Double transition fault not yet supported\n"); 1075 ASSERT(FALSE); 1076 } 1077 } 1078 1079 /* Build the final PTE */ 1080 ASSERT(PointerPte->u.Hard.Valid == 0); 1081 ASSERT(PointerPte->u.Trans.Prototype == 0); 1082 ASSERT(PointerPte->u.Trans.Transition == 1); 1083 Protection = TempPte.u.Trans.Protection; 1084 TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) | 1085 (MmProtectToPteMask[Protection]) | 1086 MiDetermineUserGlobalPteMask(PointerPte); 1087 1088 /* Is the PTE writeable? */ 1089 if ((Pfn1->u3.e1.Modified) && 1090 MI_IS_PAGE_WRITEABLE(&TempPte) && 1091 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 1092 { 1093 /* Make it dirty */ 1094 MI_MAKE_DIRTY_PAGE(&TempPte); 1095 } 1096 else 1097 { 1098 /* Make it clean */ 1099 MI_MAKE_CLEAN_PAGE(&TempPte); 1100 } 1101 1102 /* Write the valid PTE */ 1103 MI_WRITE_VALID_PTE(PointerPte, TempPte); 1104 1105 /* If this was a user fault, add it to the working set */ 1106 if (CurrentProcess > HYDRA_PROCESS) 1107 MiInsertInWorkingSetList(&CurrentProcess->Vm, FaultingAddress, Protection); 1108 1109 /* Return success */ 1110 return STATUS_PAGE_FAULT_TRANSITION; 1111 } 1112 1113 static 1114 NTSTATUS 1115 NTAPI 1116 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction, 1117 IN PVOID Address, 1118 IN PMMPTE PointerPte, 1119 IN PMMPTE PointerProtoPte, 1120 IN OUT PMMPFN *OutPfn, 1121 OUT PVOID *PageFileData, 1122 OUT PMMPTE PteValue, 1123 IN PEPROCESS Process, 1124 IN KIRQL OldIrql, 1125 IN PVOID TrapInformation) 1126 { 1127 MMPTE TempPte, PteContents; 1128 PMMPFN Pfn1; 1129 PFN_NUMBER PageFrameIndex; 1130 NTSTATUS Status; 1131 PKEVENT* InPageBlock = NULL; 1132 ULONG Protection; 1133 1134 /* Must be called with an invalid, prototype PTE, with the PFN lock held */ 1135 MI_ASSERT_PFN_LOCK_HELD(); 1136 ASSERT(PointerPte->u.Hard.Valid == 0); 1137 ASSERT(PointerPte->u.Soft.Prototype == 1); 1138 1139 /* Read the prototype PTE and check if it's valid */ 1140 TempPte = *PointerProtoPte; 1141 if (TempPte.u.Hard.Valid == 1) 1142 { 1143 /* One more user of this mapped page */ 1144 PageFrameIndex = PFN_FROM_PTE(&TempPte); 1145 Pfn1 = MiGetPfnEntry(PageFrameIndex); 1146 Pfn1->u2.ShareCount++; 1147 1148 /* Call it a transition */ 1149 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); 1150 1151 /* Complete the prototype PTE fault -- this will release the PFN lock */ 1152 return MiCompleteProtoPteFault(StoreInstruction, 1153 Address, 1154 PointerPte, 1155 PointerProtoPte, 1156 OldIrql, 1157 OutPfn); 1158 } 1159 1160 /* Make sure there's some protection mask */ 1161 if (TempPte.u.Long == 0) 1162 { 1163 /* Release the lock */ 1164 DPRINT1("Access on reserved section?\n"); 1165 MiReleasePfnLock(OldIrql); 1166 return STATUS_ACCESS_VIOLATION; 1167 } 1168 1169 /* There is no such thing as a decommitted prototype PTE */ 1170 ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long); 1171 1172 /* Check for access rights on the PTE proper */ 1173 PteContents = *PointerPte; 1174 if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) 1175 { 1176 if (!PteContents.u.Proto.ReadOnly) 1177 { 1178 Protection = TempPte.u.Soft.Protection; 1179 } 1180 else 1181 { 1182 Protection = MM_READONLY; 1183 } 1184 /* Check for page acess in software */ 1185 Status = MiAccessCheck(PointerProtoPte, 1186 StoreInstruction, 1187 KernelMode, 1188 TempPte.u.Soft.Protection, 1189 TrapInformation, 1190 TRUE); 1191 ASSERT(Status == STATUS_SUCCESS); 1192 } 1193 else 1194 { 1195 Protection = PteContents.u.Soft.Protection; 1196 } 1197 1198 /* Check for writing copy on write page */ 1199 if (((Protection & MM_WRITECOPY) == MM_WRITECOPY) && StoreInstruction) 1200 { 1201 PFN_NUMBER PageFrameIndex, ProtoPageFrameIndex; 1202 ULONG Color; 1203 1204 /* Resolve the proto fault as if it was a read operation */ 1205 Status = MiResolveProtoPteFault(FALSE, 1206 Address, 1207 PointerPte, 1208 PointerProtoPte, 1209 OutPfn, 1210 PageFileData, 1211 PteValue, 1212 Process, 1213 OldIrql, 1214 TrapInformation); 1215 1216 if (!NT_SUCCESS(Status)) 1217 { 1218 return Status; 1219 } 1220 1221 /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */ 1222 OldIrql = MiAcquirePfnLock(); 1223 1224 /* And re-read the proto PTE */ 1225 TempPte = *PointerProtoPte; 1226 ASSERT(TempPte.u.Hard.Valid == 1); 1227 ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte); 1228 1229 MI_SET_USAGE(MI_USAGE_COW); 1230 MI_SET_PROCESS(Process); 1231 1232 /* Get a new page for the private copy */ 1233 if (Process > HYDRA_PROCESS) 1234 Color = MI_GET_NEXT_PROCESS_COLOR(Process); 1235 else 1236 Color = MI_GET_NEXT_COLOR(); 1237 1238 PageFrameIndex = MiRemoveAnyPage(Color); 1239 1240 /* Perform the copy */ 1241 MiCopyPfn(PageFrameIndex, ProtoPageFrameIndex); 1242 1243 /* This will drop everything MiResolveProtoPteFault referenced */ 1244 MiDeletePte(PointerPte, Address, Process, PointerProtoPte); 1245 1246 /* Because now we use this */ 1247 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 1248 MiInitializePfn(PageFrameIndex, PointerPte, TRUE); 1249 1250 /* The caller expects us to release the PFN lock */ 1251 MiReleasePfnLock(OldIrql); 1252 1253 /* Fix the protection */ 1254 Protection &= ~MM_WRITECOPY; 1255 Protection |= MM_READWRITE; 1256 if (Address < MmSystemRangeStart) 1257 { 1258 /* Build the user PTE */ 1259 MI_MAKE_HARDWARE_PTE_USER(&PteContents, PointerPte, Protection, PageFrameIndex); 1260 } 1261 else 1262 { 1263 /* Build the kernel PTE */ 1264 MI_MAKE_HARDWARE_PTE(&PteContents, PointerPte, Protection, PageFrameIndex); 1265 } 1266 1267 /* And finally, write the valid PTE */ 1268 MI_WRITE_VALID_PTE(PointerPte, PteContents); 1269 1270 /* Add the page to our working set */ 1271 if (Process > HYDRA_PROCESS) 1272 { 1273 /* FIXME: Also support session VM scenario */ 1274 MiInsertInWorkingSetList(&Process->Vm, Address, Protection); 1275 } 1276 return Status; 1277 } 1278 1279 /* Check for clone PTEs */ 1280 if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL); 1281 1282 /* We don't support mapped files yet */ 1283 ASSERT(TempPte.u.Soft.Prototype == 0); 1284 1285 /* We might however have transition PTEs */ 1286 if (TempPte.u.Soft.Transition == 1) 1287 { 1288 /* Resolve the transition fault */ 1289 ASSERT(OldIrql != MM_NOIRQL); 1290 Status = MiResolveTransitionFault(StoreInstruction, 1291 Address, 1292 PointerProtoPte, 1293 Process, 1294 OldIrql, 1295 &InPageBlock); 1296 ASSERT(NT_SUCCESS(Status)); 1297 } 1298 else 1299 { 1300 /* We also don't support paged out pages */ 1301 ASSERT(TempPte.u.Soft.PageFileHigh == 0); 1302 1303 /* Resolve the demand zero fault */ 1304 Status = MiResolveDemandZeroFault(Address, 1305 PointerProtoPte, 1306 (ULONG)TempPte.u.Soft.Protection, 1307 Process, 1308 OldIrql); 1309 ASSERT(NT_SUCCESS(Status)); 1310 } 1311 1312 /* Complete the prototype PTE fault -- this will release the PFN lock */ 1313 ASSERT(PointerPte->u.Hard.Valid == 0); 1314 return MiCompleteProtoPteFault(StoreInstruction, 1315 Address, 1316 PointerPte, 1317 PointerProtoPte, 1318 OldIrql, 1319 OutPfn); 1320 } 1321 1322 NTSTATUS 1323 NTAPI 1324 MiDispatchFault(IN ULONG FaultCode, 1325 IN PVOID Address, 1326 IN PMMPTE PointerPte, 1327 IN PMMPTE PointerProtoPte, 1328 IN BOOLEAN Recursive, 1329 IN PEPROCESS Process, 1330 IN PVOID TrapInformation, 1331 IN PMMVAD Vad) 1332 { 1333 MMPTE TempPte; 1334 KIRQL OldIrql, LockIrql; 1335 NTSTATUS Status; 1336 PMMPTE SuperProtoPte; 1337 PMMPFN Pfn1, OutPfn = NULL; 1338 PFN_NUMBER PageFrameIndex; 1339 PFN_COUNT PteCount, ProcessedPtes; 1340 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n", 1341 Address, 1342 Process); 1343 1344 /* Make sure the addresses are ok */ 1345 ASSERT(PointerPte == MiAddressToPte(Address)); 1346 1347 // 1348 // Make sure APCs are off and we're not at dispatch 1349 // 1350 OldIrql = KeGetCurrentIrql(); 1351 ASSERT(OldIrql <= APC_LEVEL); 1352 ASSERT(KeAreAllApcsDisabled() == TRUE); 1353 1354 // 1355 // Grab a copy of the PTE 1356 // 1357 TempPte = *PointerPte; 1358 1359 /* Do we have a prototype PTE? */ 1360 if (PointerProtoPte) 1361 { 1362 /* This should never happen */ 1363 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte)); 1364 1365 /* Check if this is a kernel-mode address */ 1366 SuperProtoPte = MiAddressToPte(PointerProtoPte); 1367 if (Address >= MmSystemRangeStart) 1368 { 1369 /* Lock the PFN database */ 1370 LockIrql = MiAcquirePfnLock(); 1371 1372 /* Has the PTE been made valid yet? */ 1373 if (!SuperProtoPte->u.Hard.Valid) 1374 { 1375 ASSERT(FALSE); 1376 } 1377 else if (PointerPte->u.Hard.Valid == 1) 1378 { 1379 ASSERT(FALSE); 1380 } 1381 1382 /* Resolve the fault -- this will release the PFN lock */ 1383 Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), 1384 Address, 1385 PointerPte, 1386 PointerProtoPte, 1387 &OutPfn, 1388 NULL, 1389 NULL, 1390 Process, 1391 LockIrql, 1392 TrapInformation); 1393 ASSERT(Status == STATUS_SUCCESS); 1394 1395 /* Complete this as a transition fault */ 1396 ASSERT(OldIrql == KeGetCurrentIrql()); 1397 ASSERT(OldIrql <= APC_LEVEL); 1398 ASSERT(KeAreAllApcsDisabled() == TRUE); 1399 return Status; 1400 } 1401 else 1402 { 1403 /* We only handle the lookup path */ 1404 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED); 1405 1406 /* Is there a non-image VAD? */ 1407 if ((Vad) && 1408 (Vad->u.VadFlags.VadType != VadImageMap) && 1409 !(Vad->u2.VadFlags2.ExtendableFile)) 1410 { 1411 /* One day, ReactOS will cluster faults */ 1412 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS); 1413 DPRINT("Should cluster fault, but won't\n"); 1414 } 1415 1416 /* Only one PTE to handle for now */ 1417 PteCount = 1; 1418 ProcessedPtes = 0; 1419 1420 /* Lock the PFN database */ 1421 LockIrql = MiAcquirePfnLock(); 1422 1423 /* We only handle the valid path */ 1424 ASSERT(SuperProtoPte->u.Hard.Valid == 1); 1425 1426 /* Capture the PTE */ 1427 TempPte = *PointerProtoPte; 1428 1429 /* Loop to handle future case of clustered faults */ 1430 while (TRUE) 1431 { 1432 /* For our current usage, this should be true */ 1433 if (TempPte.u.Hard.Valid == 1) 1434 { 1435 /* Bump the share count on the PTE */ 1436 PageFrameIndex = PFN_FROM_PTE(&TempPte); 1437 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 1438 Pfn1->u2.ShareCount++; 1439 } 1440 else if ((TempPte.u.Soft.Prototype == 0) && 1441 (TempPte.u.Soft.Transition == 1)) 1442 { 1443 /* This is a standby page, bring it back from the cache */ 1444 PageFrameIndex = TempPte.u.Trans.PageFrameNumber; 1445 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex); 1446 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 1447 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); 1448 1449 /* Should not yet happen in ReactOS */ 1450 ASSERT(Pfn1->u3.e1.ReadInProgress == 0); 1451 ASSERT(Pfn1->u4.InPageError == 0); 1452 1453 /* Get the page */ 1454 MiUnlinkPageFromList(Pfn1); 1455 1456 /* Bump its reference count */ 1457 ASSERT(Pfn1->u2.ShareCount == 0); 1458 InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount); 1459 Pfn1->u2.ShareCount++; 1460 1461 /* Make it valid again */ 1462 /* This looks like another macro.... */ 1463 Pfn1->u3.e1.PageLocation = ActiveAndValid; 1464 ASSERT(PointerProtoPte->u.Hard.Valid == 0); 1465 ASSERT(PointerProtoPte->u.Trans.Prototype == 0); 1466 ASSERT(PointerProtoPte->u.Trans.Transition == 1); 1467 TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) | 1468 MmProtectToPteMask[PointerProtoPte->u.Trans.Protection]; 1469 TempPte.u.Hard.Valid = 1; 1470 MI_MAKE_ACCESSED_PAGE(&TempPte); 1471 1472 /* Is the PTE writeable? */ 1473 if ((Pfn1->u3.e1.Modified) && 1474 MI_IS_PAGE_WRITEABLE(&TempPte) && 1475 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 1476 { 1477 /* Make it dirty */ 1478 MI_MAKE_DIRTY_PAGE(&TempPte); 1479 } 1480 else 1481 { 1482 /* Make it clean */ 1483 MI_MAKE_CLEAN_PAGE(&TempPte); 1484 } 1485 1486 /* Write the valid PTE */ 1487 MI_WRITE_VALID_PTE(PointerProtoPte, TempPte); 1488 ASSERT(PointerPte->u.Hard.Valid == 0); 1489 } 1490 else 1491 { 1492 /* Page is invalid, get out of the loop */ 1493 break; 1494 } 1495 1496 /* One more done, was it the last? */ 1497 if (++ProcessedPtes == PteCount) 1498 { 1499 /* Complete the fault */ 1500 MiCompleteProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), 1501 Address, 1502 PointerPte, 1503 PointerProtoPte, 1504 LockIrql, 1505 &OutPfn); 1506 1507 /* THIS RELEASES THE PFN LOCK! */ 1508 break; 1509 } 1510 1511 /* No clustered faults yet */ 1512 ASSERT(FALSE); 1513 } 1514 1515 /* Did we resolve the fault? */ 1516 if (ProcessedPtes) 1517 { 1518 /* Bump the transition count */ 1519 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes); 1520 ProcessedPtes--; 1521 1522 /* Loop all the processing we did */ 1523 ASSERT(ProcessedPtes == 0); 1524 1525 /* Complete this as a transition fault */ 1526 ASSERT(OldIrql == KeGetCurrentIrql()); 1527 ASSERT(OldIrql <= APC_LEVEL); 1528 ASSERT(KeAreAllApcsDisabled() == TRUE); 1529 return STATUS_PAGE_FAULT_TRANSITION; 1530 } 1531 1532 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */ 1533 OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber); 1534 MiReferenceUsedPageAndBumpLockCount(OutPfn); 1535 ASSERT(OutPfn->u3.e2.ReferenceCount > 1); 1536 ASSERT(PointerPte->u.Hard.Valid == 0); 1537 1538 /* Resolve the fault -- this will release the PFN lock */ 1539 Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), 1540 Address, 1541 PointerPte, 1542 PointerProtoPte, 1543 &OutPfn, 1544 NULL, 1545 NULL, 1546 Process, 1547 LockIrql, 1548 TrapInformation); 1549 //ASSERT(Status != STATUS_ISSUE_PAGING_IO); 1550 //ASSERT(Status != STATUS_REFAULT); 1551 //ASSERT(Status != STATUS_PTE_CHANGED); 1552 1553 /* Did the routine clean out the PFN or should we? */ 1554 if (OutPfn) 1555 { 1556 /* We had a locked PFN, so acquire the PFN lock to dereference it */ 1557 ASSERT(PointerProtoPte != NULL); 1558 OldIrql = MiAcquirePfnLock(); 1559 1560 /* Dereference the locked PFN */ 1561 MiDereferencePfnAndDropLockCount(OutPfn); 1562 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1); 1563 1564 /* And now release the lock */ 1565 MiReleasePfnLock(OldIrql); 1566 } 1567 1568 /* Complete this as a transition fault */ 1569 ASSERT(OldIrql == KeGetCurrentIrql()); 1570 ASSERT(OldIrql <= APC_LEVEL); 1571 ASSERT(KeAreAllApcsDisabled() == TRUE); 1572 return Status; 1573 } 1574 } 1575 1576 /* Is this a transition PTE */ 1577 if (TempPte.u.Soft.Transition) 1578 { 1579 PKEVENT* InPageBlock = NULL; 1580 PKEVENT PreviousPageEvent; 1581 KEVENT CurrentPageEvent; 1582 1583 /* Lock the PFN database */ 1584 LockIrql = MiAcquirePfnLock(); 1585 1586 /* Resolve */ 1587 Status = MiResolveTransitionFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, LockIrql, &InPageBlock); 1588 1589 ASSERT(NT_SUCCESS(Status)); 1590 1591 if (InPageBlock != NULL) 1592 { 1593 /* Another thread is reading or writing this page. Put us into the waiting queue. */ 1594 KeInitializeEvent(&CurrentPageEvent, NotificationEvent, FALSE); 1595 PreviousPageEvent = *InPageBlock; 1596 *InPageBlock = &CurrentPageEvent; 1597 } 1598 1599 /* And now release the lock and leave*/ 1600 MiReleasePfnLock(LockIrql); 1601 1602 if (InPageBlock != NULL) 1603 { 1604 KeWaitForSingleObject(&CurrentPageEvent, WrPageIn, KernelMode, FALSE, NULL); 1605 1606 /* Let's the chain go on */ 1607 if (PreviousPageEvent) 1608 { 1609 KeSetEvent(PreviousPageEvent, IO_NO_INCREMENT, FALSE); 1610 } 1611 } 1612 1613 ASSERT(OldIrql == KeGetCurrentIrql()); 1614 ASSERT(OldIrql <= APC_LEVEL); 1615 ASSERT(KeAreAllApcsDisabled() == TRUE); 1616 return Status; 1617 } 1618 1619 /* Should we page the data back in ? */ 1620 if (TempPte.u.Soft.PageFileHigh != 0) 1621 { 1622 /* Lock the PFN database */ 1623 LockIrql = MiAcquirePfnLock(); 1624 1625 /* Resolve */ 1626 Status = MiResolvePageFileFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, &LockIrql); 1627 1628 /* And now release the lock and leave*/ 1629 MiReleasePfnLock(LockIrql); 1630 1631 ASSERT(OldIrql == KeGetCurrentIrql()); 1632 ASSERT(OldIrql <= APC_LEVEL); 1633 ASSERT(KeAreAllApcsDisabled() == TRUE); 1634 return Status; 1635 } 1636 1637 // 1638 // The PTE must be invalid but not completely empty. It must also not be a 1639 // prototype a transition or a paged-out PTE as those scenarii should've been handled above. 1640 // These are all Windows checks 1641 // 1642 ASSERT(TempPte.u.Hard.Valid == 0); 1643 ASSERT(TempPte.u.Soft.Prototype == 0); 1644 ASSERT(TempPte.u.Soft.Transition == 0); 1645 ASSERT(TempPte.u.Soft.PageFileHigh == 0); 1646 ASSERT(TempPte.u.Long != 0); 1647 1648 // 1649 // If we got this far, the PTE can only be a demand zero PTE, which is what 1650 // we want. Go handle it! 1651 // 1652 Status = MiResolveDemandZeroFault(Address, 1653 PointerPte, 1654 (ULONG)TempPte.u.Soft.Protection, 1655 Process, 1656 MM_NOIRQL); 1657 ASSERT(KeAreAllApcsDisabled() == TRUE); 1658 if (NT_SUCCESS(Status)) 1659 { 1660 // 1661 // Make sure we're returning in a sane state and pass the status down 1662 // 1663 ASSERT(OldIrql == KeGetCurrentIrql()); 1664 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 1665 return Status; 1666 } 1667 1668 // 1669 // Generate an access fault 1670 // 1671 return STATUS_ACCESS_VIOLATION; 1672 } 1673 1674 NTSTATUS 1675 NTAPI 1676 MmArmAccessFault(IN ULONG FaultCode, 1677 IN PVOID Address, 1678 IN KPROCESSOR_MODE Mode, 1679 IN PVOID TrapInformation) 1680 { 1681 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql; 1682 PMMPTE ProtoPte = NULL; 1683 PMMPTE PointerPte = MiAddressToPte(Address); 1684 PMMPDE PointerPde = MiAddressToPde(Address); 1685 #if (_MI_PAGING_LEVELS >= 3) 1686 PMMPDE PointerPpe = MiAddressToPpe(Address); 1687 #if (_MI_PAGING_LEVELS == 4) 1688 PMMPDE PointerPxe = MiAddressToPxe(Address); 1689 #endif 1690 #endif 1691 MMPTE TempPte; 1692 PETHREAD CurrentThread; 1693 PEPROCESS CurrentProcess; 1694 NTSTATUS Status; 1695 PMMSUPPORT WorkingSet; 1696 ULONG ProtectionCode; 1697 PMMVAD Vad = NULL; 1698 PFN_NUMBER PageFrameIndex; 1699 ULONG Color; 1700 BOOLEAN IsSessionAddress; 1701 PMMPFN Pfn1; 1702 DPRINT("ARM3 FAULT AT: %p\n", Address); 1703 1704 /* Check for page fault on high IRQL */ 1705 if (OldIrql > APC_LEVEL) 1706 { 1707 #if (_MI_PAGING_LEVELS < 3) 1708 /* Could be a page table for paged pool, which we'll allow */ 1709 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte); 1710 MiCheckPdeForPagedPool(Address); 1711 #endif 1712 /* Check if any of the top-level pages are invalid */ 1713 if ( 1714 #if (_MI_PAGING_LEVELS == 4) 1715 (PointerPxe->u.Hard.Valid == 0) || 1716 #endif 1717 #if (_MI_PAGING_LEVELS >= 3) 1718 (PointerPpe->u.Hard.Valid == 0) || 1719 #endif 1720 (PointerPde->u.Hard.Valid == 0) || 1721 (PointerPte->u.Hard.Valid == 0)) 1722 { 1723 /* This fault is not valid, print out some debugging help */ 1724 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n", 1725 Address, 1726 OldIrql); 1727 if (TrapInformation) 1728 { 1729 PKTRAP_FRAME TrapFrame = TrapInformation; 1730 #ifdef _M_IX86 1731 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags); 1732 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx); 1733 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi); 1734 #elif defined(_M_AMD64) 1735 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags); 1736 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx); 1737 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi); 1738 #elif defined(_M_ARM) 1739 DbgPrint("MM:***PC %p\n", TrapFrame->Pc); 1740 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame->R0, TrapFrame->R1, TrapFrame->R2, TrapFrame->R3); 1741 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame->R11, TrapFrame->R12, TrapFrame->Sp, TrapFrame->Lr); 1742 #endif 1743 } 1744 1745 /* Tell the trap handler to fail */ 1746 return STATUS_IN_PAGE_ERROR | 0x10000000; 1747 } 1748 1749 /* Not yet implemented in ReactOS */ 1750 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE); 1751 ASSERT((!MI_IS_NOT_PRESENT_FAULT(FaultCode) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte)) == FALSE); 1752 1753 /* Check if this was a write */ 1754 if (MI_IS_WRITE_ACCESS(FaultCode)) 1755 { 1756 /* Was it to a read-only page? */ 1757 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber); 1758 if (!(PointerPte->u.Long & PTE_READWRITE) && 1759 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE)) 1760 { 1761 /* Crash with distinguished bugcheck code */ 1762 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 1763 (ULONG_PTR)Address, 1764 PointerPte->u.Long, 1765 (ULONG_PTR)TrapInformation, 1766 10); 1767 } 1768 } 1769 1770 /* Nothing is actually wrong */ 1771 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address); 1772 return STATUS_SUCCESS; 1773 } 1774 1775 /* Check for kernel fault address */ 1776 if (Address >= MmSystemRangeStart) 1777 { 1778 /* Bail out, if the fault came from user mode */ 1779 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION; 1780 1781 #if (_MI_PAGING_LEVELS == 2) 1782 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte); 1783 MiCheckPdeForPagedPool(Address); 1784 #endif 1785 1786 /* Check if the higher page table entries are invalid */ 1787 if ( 1788 #if (_MI_PAGING_LEVELS == 4) 1789 /* AMD64 system, check if PXE is invalid */ 1790 (PointerPxe->u.Hard.Valid == 0) || 1791 #endif 1792 #if (_MI_PAGING_LEVELS >= 3) 1793 /* PAE/AMD64 system, check if PPE is invalid */ 1794 (PointerPpe->u.Hard.Valid == 0) || 1795 #endif 1796 /* Always check if the PDE is valid */ 1797 (PointerPde->u.Hard.Valid == 0)) 1798 { 1799 /* PXE/PPE/PDE (still) not valid, kill the system */ 1800 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 1801 (ULONG_PTR)Address, 1802 FaultCode, 1803 (ULONG_PTR)TrapInformation, 1804 2); 1805 } 1806 1807 /* Not handling session faults yet */ 1808 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address); 1809 1810 /* The PDE is valid, so read the PTE */ 1811 TempPte = *PointerPte; 1812 if (TempPte.u.Hard.Valid == 1) 1813 { 1814 /* Check if this was system space or session space */ 1815 if (!IsSessionAddress) 1816 { 1817 /* Check if the PTE is still valid under PFN lock */ 1818 OldIrql = MiAcquirePfnLock(); 1819 TempPte = *PointerPte; 1820 if (TempPte.u.Hard.Valid) 1821 { 1822 /* Check if this was a write */ 1823 if (MI_IS_WRITE_ACCESS(FaultCode)) 1824 { 1825 /* Was it to a read-only page? */ 1826 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber); 1827 if (!(PointerPte->u.Long & PTE_READWRITE) && 1828 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE)) 1829 { 1830 /* Crash with distinguished bugcheck code */ 1831 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 1832 (ULONG_PTR)Address, 1833 PointerPte->u.Long, 1834 (ULONG_PTR)TrapInformation, 1835 11); 1836 } 1837 } 1838 1839 /* Check for execution of non-executable memory */ 1840 if (MI_IS_INSTRUCTION_FETCH(FaultCode) && 1841 !MI_IS_PAGE_EXECUTABLE(&TempPte)) 1842 { 1843 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY, 1844 (ULONG_PTR)Address, 1845 (ULONG_PTR)TempPte.u.Long, 1846 (ULONG_PTR)TrapInformation, 1847 1); 1848 } 1849 } 1850 1851 /* Release PFN lock and return all good */ 1852 MiReleasePfnLock(OldIrql); 1853 return STATUS_SUCCESS; 1854 } 1855 } 1856 #if (_MI_PAGING_LEVELS == 2) 1857 /* Check if this was a session PTE that needs to remap the session PDE */ 1858 if (MI_IS_SESSION_PTE(Address)) 1859 { 1860 /* Do the remapping */ 1861 Status = MiCheckPdeForSessionSpace(Address); 1862 if (!NT_SUCCESS(Status)) 1863 { 1864 /* It failed, this address is invalid */ 1865 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 1866 (ULONG_PTR)Address, 1867 FaultCode, 1868 (ULONG_PTR)TrapInformation, 1869 6); 1870 } 1871 } 1872 #else 1873 1874 _WARN("Session space stuff is not implemented yet!") 1875 1876 #endif 1877 1878 /* Check for a fault on the page table or hyperspace */ 1879 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address)) 1880 { 1881 #if (_MI_PAGING_LEVELS < 3) 1882 /* Windows does this check but I don't understand why -- it's done above! */ 1883 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1); 1884 #endif 1885 /* Handle this as a user mode fault */ 1886 goto UserFault; 1887 } 1888 1889 /* Get the current thread */ 1890 CurrentThread = PsGetCurrentThread(); 1891 1892 /* What kind of address is this */ 1893 if (!IsSessionAddress) 1894 { 1895 /* Use the system working set */ 1896 WorkingSet = &MmSystemCacheWs; 1897 CurrentProcess = NULL; 1898 1899 /* Make sure we don't have a recursive working set lock */ 1900 if ((CurrentThread->OwnsProcessWorkingSetExclusive) || 1901 (CurrentThread->OwnsProcessWorkingSetShared) || 1902 (CurrentThread->OwnsSystemWorkingSetExclusive) || 1903 (CurrentThread->OwnsSystemWorkingSetShared) || 1904 (CurrentThread->OwnsSessionWorkingSetExclusive) || 1905 (CurrentThread->OwnsSessionWorkingSetShared)) 1906 { 1907 /* Fail */ 1908 return STATUS_IN_PAGE_ERROR | 0x10000000; 1909 } 1910 } 1911 else 1912 { 1913 /* Use the session process and working set */ 1914 CurrentProcess = HYDRA_PROCESS; 1915 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm; 1916 1917 /* Make sure we don't have a recursive working set lock */ 1918 if ((CurrentThread->OwnsSessionWorkingSetExclusive) || 1919 (CurrentThread->OwnsSessionWorkingSetShared)) 1920 { 1921 /* Fail */ 1922 return STATUS_IN_PAGE_ERROR | 0x10000000; 1923 } 1924 } 1925 1926 /* Acquire the working set lock */ 1927 KeRaiseIrql(APC_LEVEL, &LockIrql); 1928 MiLockWorkingSet(CurrentThread, WorkingSet); 1929 1930 /* Re-read PTE now that we own the lock */ 1931 TempPte = *PointerPte; 1932 if (TempPte.u.Hard.Valid == 1) 1933 { 1934 /* Check if this was a write */ 1935 if (MI_IS_WRITE_ACCESS(FaultCode)) 1936 { 1937 /* Was it to a read-only page that is not copy on write? */ 1938 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber); 1939 if (!(TempPte.u.Long & PTE_READWRITE) && 1940 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) && 1941 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 1942 { 1943 /* Case not yet handled */ 1944 ASSERT(!IsSessionAddress); 1945 1946 /* Crash with distinguished bugcheck code */ 1947 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 1948 (ULONG_PTR)Address, 1949 TempPte.u.Long, 1950 (ULONG_PTR)TrapInformation, 1951 12); 1952 } 1953 } 1954 1955 /* Check for execution of non-executable memory */ 1956 if (MI_IS_INSTRUCTION_FETCH(FaultCode) && 1957 !MI_IS_PAGE_EXECUTABLE(&TempPte)) 1958 { 1959 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY, 1960 (ULONG_PTR)Address, 1961 (ULONG_PTR)TempPte.u.Long, 1962 (ULONG_PTR)TrapInformation, 1963 2); 1964 } 1965 1966 /* Check for read-only write in session space */ 1967 if ((IsSessionAddress) && 1968 MI_IS_WRITE_ACCESS(FaultCode) && 1969 !MI_IS_PAGE_WRITEABLE(&TempPte)) 1970 { 1971 /* Sanity check */ 1972 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address)); 1973 1974 /* Was this COW? */ 1975 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 1976 { 1977 /* Then this is not allowed */ 1978 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 1979 (ULONG_PTR)Address, 1980 (ULONG_PTR)TempPte.u.Long, 1981 (ULONG_PTR)TrapInformation, 1982 13); 1983 } 1984 1985 /* Otherwise, handle COW */ 1986 ASSERT(FALSE); 1987 } 1988 1989 /* Release the working set */ 1990 MiUnlockWorkingSet(CurrentThread, WorkingSet); 1991 KeLowerIrql(LockIrql); 1992 1993 /* Otherwise, the PDE was probably invalid, and all is good now */ 1994 return STATUS_SUCCESS; 1995 } 1996 1997 /* Check one kind of prototype PTE */ 1998 if (TempPte.u.Soft.Prototype) 1999 { 2000 /* Make sure protected pool is on, and that this is a pool address */ 2001 if ((MmProtectFreedNonPagedPool) && 2002 (((Address >= MmNonPagedPoolStart) && 2003 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart + 2004 MmSizeOfNonPagedPoolInBytes))) || 2005 ((Address >= MmNonPagedPoolExpansionStart) && 2006 (Address < MmNonPagedPoolEnd)))) 2007 { 2008 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */ 2009 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL, 2010 (ULONG_PTR)Address, 2011 FaultCode, 2012 Mode, 2013 4); 2014 } 2015 2016 /* Get the prototype PTE! */ 2017 ProtoPte = MiProtoPteToPte(&TempPte); 2018 2019 /* Do we need to locate the prototype PTE in session space? */ 2020 if ((IsSessionAddress) && 2021 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)) 2022 { 2023 /* Yep, go find it as well as the VAD for it */ 2024 ProtoPte = MiCheckVirtualAddress(Address, 2025 &ProtectionCode, 2026 &Vad); 2027 ASSERT(ProtoPte != NULL); 2028 } 2029 } 2030 else 2031 { 2032 /* We don't implement transition PTEs */ 2033 ASSERT(TempPte.u.Soft.Transition == 0); 2034 2035 /* Check for no-access PTE */ 2036 if (TempPte.u.Soft.Protection == MM_NOACCESS) 2037 { 2038 /* Bugcheck the system! */ 2039 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 2040 (ULONG_PTR)Address, 2041 FaultCode, 2042 (ULONG_PTR)TrapInformation, 2043 1); 2044 } 2045 2046 /* Check for no protecton at all */ 2047 if (TempPte.u.Soft.Protection == MM_ZERO_ACCESS) 2048 { 2049 /* Bugcheck the system! */ 2050 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 2051 (ULONG_PTR)Address, 2052 FaultCode, 2053 (ULONG_PTR)TrapInformation, 2054 0); 2055 } 2056 } 2057 2058 /* Check for demand page */ 2059 if (MI_IS_WRITE_ACCESS(FaultCode) && 2060 !(ProtoPte) && 2061 !(IsSessionAddress) && 2062 !(TempPte.u.Hard.Valid)) 2063 { 2064 /* Get the protection code */ 2065 ASSERT(TempPte.u.Soft.Transition == 0); 2066 if (!(TempPte.u.Soft.Protection & MM_READWRITE)) 2067 { 2068 /* Bugcheck the system! */ 2069 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 2070 (ULONG_PTR)Address, 2071 TempPte.u.Long, 2072 (ULONG_PTR)TrapInformation, 2073 14); 2074 } 2075 } 2076 2077 /* Now do the real fault handling */ 2078 Status = MiDispatchFault(FaultCode, 2079 Address, 2080 PointerPte, 2081 ProtoPte, 2082 FALSE, 2083 CurrentProcess, 2084 TrapInformation, 2085 NULL); 2086 2087 /* Release the working set */ 2088 ASSERT(KeAreAllApcsDisabled() == TRUE); 2089 MiUnlockWorkingSet(CurrentThread, WorkingSet); 2090 KeLowerIrql(LockIrql); 2091 2092 /* We are done! */ 2093 DPRINT("Fault resolved with status: %lx\n", Status); 2094 return Status; 2095 } 2096 2097 /* This is a user fault */ 2098 UserFault: 2099 CurrentThread = PsGetCurrentThread(); 2100 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process; 2101 2102 /* Lock the working set */ 2103 MiLockProcessWorkingSet(CurrentProcess, CurrentThread); 2104 2105 ProtectionCode = MM_INVALID_PROTECTION; 2106 2107 #if (_MI_PAGING_LEVELS == 4) 2108 /* Check if the PXE is valid */ 2109 if (PointerPxe->u.Hard.Valid == 0) 2110 { 2111 /* Right now, we only handle scenarios where the PXE is totally empty */ 2112 ASSERT(PointerPxe->u.Long == 0); 2113 2114 /* This is only possible for user mode addresses! */ 2115 ASSERT(PointerPte <= MiHighestUserPte); 2116 2117 /* Check if we have a VAD */ 2118 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2119 if (ProtectionCode == MM_NOACCESS) 2120 { 2121 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2122 return STATUS_ACCESS_VIOLATION; 2123 } 2124 2125 /* Resolve a demand zero fault */ 2126 MiResolveDemandZeroFault(PointerPpe, 2127 PointerPxe, 2128 MM_EXECUTE_READWRITE, 2129 CurrentProcess, 2130 MM_NOIRQL); 2131 2132 /* We should come back with a valid PXE */ 2133 ASSERT(PointerPxe->u.Hard.Valid == 1); 2134 } 2135 #endif 2136 2137 #if (_MI_PAGING_LEVELS >= 3) 2138 /* Check if the PPE is valid */ 2139 if (PointerPpe->u.Hard.Valid == 0) 2140 { 2141 /* Right now, we only handle scenarios where the PPE is totally empty */ 2142 ASSERT(PointerPpe->u.Long == 0); 2143 2144 /* This is only possible for user mode addresses! */ 2145 ASSERT(PointerPte <= MiHighestUserPte); 2146 2147 /* Check if we have a VAD, unless we did this already */ 2148 if (ProtectionCode == MM_INVALID_PROTECTION) 2149 { 2150 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2151 } 2152 2153 if (ProtectionCode == MM_NOACCESS) 2154 { 2155 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2156 return STATUS_ACCESS_VIOLATION; 2157 } 2158 2159 /* Resolve a demand zero fault */ 2160 MiResolveDemandZeroFault(PointerPde, 2161 PointerPpe, 2162 MM_EXECUTE_READWRITE, 2163 CurrentProcess, 2164 MM_NOIRQL); 2165 2166 /* We should come back with a valid PPE */ 2167 ASSERT(PointerPpe->u.Hard.Valid == 1); 2168 } 2169 #endif 2170 2171 /* Check if the PDE is invalid */ 2172 if (PointerPde->u.Hard.Valid == 0) 2173 { 2174 /* Right now, we only handle scenarios where the PDE is totally empty */ 2175 ASSERT(PointerPde->u.Long == 0); 2176 2177 /* And go dispatch the fault on the PDE. This should handle the demand-zero */ 2178 #if MI_TRACE_PFNS 2179 UserPdeFault = TRUE; 2180 #endif 2181 /* Check if we have a VAD, unless we did this already */ 2182 if (ProtectionCode == MM_INVALID_PROTECTION) 2183 { 2184 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2185 } 2186 2187 if (ProtectionCode == MM_NOACCESS) 2188 { 2189 #if (_MI_PAGING_LEVELS == 2) 2190 /* Could be a page table for paged pool */ 2191 MiCheckPdeForPagedPool(Address); 2192 #endif 2193 /* Has the code above changed anything -- is this now a valid PTE? */ 2194 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION; 2195 2196 /* Either this was a bogus VA or we've fixed up a paged pool PDE */ 2197 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2198 return Status; 2199 } 2200 2201 /* Resolve a demand zero fault */ 2202 MiResolveDemandZeroFault(PointerPte, 2203 PointerPde, 2204 MM_EXECUTE_READWRITE, 2205 CurrentProcess, 2206 MM_NOIRQL); 2207 #if MI_TRACE_PFNS 2208 UserPdeFault = FALSE; 2209 #endif 2210 /* We should come back with APCs enabled, and with a valid PDE */ 2211 ASSERT(KeAreAllApcsDisabled() == TRUE); 2212 ASSERT(PointerPde->u.Hard.Valid == 1); 2213 } 2214 else 2215 { 2216 /* Not yet implemented in ReactOS */ 2217 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE); 2218 } 2219 2220 /* Now capture the PTE. */ 2221 TempPte = *PointerPte; 2222 2223 /* Check if the PTE is valid */ 2224 if (TempPte.u.Hard.Valid) 2225 { 2226 /* Check if this is a write on a readonly PTE */ 2227 if (MI_IS_WRITE_ACCESS(FaultCode)) 2228 { 2229 /* Is this a copy on write PTE? */ 2230 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 2231 { 2232 PFN_NUMBER PageFrameIndex, OldPageFrameIndex; 2233 PMMPFN Pfn1; 2234 ProtectionCode = TempPte.u.Soft.Protection; 2235 2236 LockIrql = MiAcquirePfnLock(); 2237 2238 ASSERT(MmAvailablePages > 0); 2239 2240 MI_SET_USAGE(MI_USAGE_COW); 2241 MI_SET_PROCESS(CurrentProcess); 2242 2243 /* Allocate a new page and copy it */ 2244 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess)); 2245 OldPageFrameIndex = PFN_FROM_PTE(&TempPte); 2246 2247 MiCopyPfn(PageFrameIndex, OldPageFrameIndex); 2248 2249 /* Dereference whatever this PTE is referencing */ 2250 Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex); 2251 ASSERT(Pfn1->u3.e1.PrototypePte == 1); 2252 ASSERT(!MI_IS_PFN_DELETED(Pfn1)); 2253 ProtoPte = Pfn1->PteAddress; 2254 MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte); 2255 2256 /* And make a new shiny one with our page */ 2257 MiInitializePfn(PageFrameIndex, PointerPte, TRUE); 2258 2259 MiReleasePfnLock(LockIrql); 2260 2261 TempPte.u.Hard.PageFrameNumber = PageFrameIndex; 2262 TempPte.u.Hard.Write = 1; 2263 TempPte.u.Hard.CopyOnWrite = 0; 2264 2265 MI_WRITE_VALID_PTE(PointerPte, TempPte); 2266 2267 /* We can now add it to our working set */ 2268 MiInsertInWorkingSetList(&CurrentProcess->Vm, Address, ProtectionCode); 2269 2270 2271 /* Return the status */ 2272 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2273 return STATUS_PAGE_FAULT_COPY_ON_WRITE; 2274 } 2275 2276 /* Is this a read-only PTE? */ 2277 if (!MI_IS_PAGE_WRITEABLE(&TempPte)) 2278 { 2279 /* Return the status */ 2280 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2281 return STATUS_ACCESS_VIOLATION; 2282 } 2283 } 2284 2285 /* Check for execution of non-executable memory */ 2286 if (MI_IS_INSTRUCTION_FETCH(FaultCode) && 2287 !MI_IS_PAGE_EXECUTABLE(&TempPte)) 2288 { 2289 /* Return the status */ 2290 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2291 return STATUS_ACCESS_VIOLATION; 2292 } 2293 2294 /* The fault has already been resolved by a different thread */ 2295 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2296 return STATUS_SUCCESS; 2297 } 2298 2299 /* Quick check for demand-zero */ 2300 if ((TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)) || 2301 (TempPte.u.Long == (MM_EXECUTE_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))) 2302 { 2303 /* Resolve the fault */ 2304 MiResolveDemandZeroFault(Address, 2305 PointerPte, 2306 TempPte.u.Soft.Protection, 2307 CurrentProcess, 2308 MM_NOIRQL); 2309 2310 /* Return the status */ 2311 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2312 return STATUS_PAGE_FAULT_DEMAND_ZERO; 2313 } 2314 2315 /* Check for zero PTE */ 2316 if (TempPte.u.Long == 0) 2317 { 2318 /* Check if this address range belongs to a valid allocation (VAD) */ 2319 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2320 if (ProtectionCode == MM_NOACCESS) 2321 { 2322 #if (_MI_PAGING_LEVELS == 2) 2323 /* Could be a page table for paged pool */ 2324 MiCheckPdeForPagedPool(Address); 2325 #endif 2326 /* Has the code above changed anything -- is this now a valid PTE? */ 2327 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION; 2328 2329 /* Either this was a bogus VA or we've fixed up a paged pool PDE */ 2330 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2331 return Status; 2332 } 2333 2334 /* 2335 * Check if this is a real user-mode address or actually a kernel-mode 2336 * page table for a user mode address 2337 */ 2338 if (Address <= MM_HIGHEST_USER_ADDRESS) 2339 { 2340 /* Add an additional page table reference */ 2341 MiIncrementPageTableReferences(Address); 2342 } 2343 2344 /* Is this a guard page? */ 2345 if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE) 2346 { 2347 /* The VAD protection cannot be MM_DECOMMIT! */ 2348 ASSERT(ProtectionCode != MM_DECOMMIT); 2349 2350 /* Remove the bit */ 2351 TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE; 2352 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2353 2354 /* Not supported */ 2355 ASSERT(ProtoPte == NULL); 2356 ASSERT(CurrentThread->ApcNeeded == 0); 2357 2358 /* Drop the working set lock */ 2359 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2360 ASSERT(KeGetCurrentIrql() == OldIrql); 2361 2362 /* Handle stack expansion */ 2363 return MiCheckForUserStackOverflow(Address, TrapInformation); 2364 } 2365 2366 /* Did we get a prototype PTE back? */ 2367 if (!ProtoPte) 2368 { 2369 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */ 2370 if (PointerPde == MiAddressToPde(PTE_BASE)) 2371 { 2372 /* Then it's really a demand-zero PDE (on behalf of user-mode) */ 2373 #ifdef _M_ARM 2374 _WARN("This is probably completely broken!"); 2375 MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde); 2376 #else 2377 MI_WRITE_INVALID_PDE(PointerPte, DemandZeroPde); 2378 #endif 2379 } 2380 else 2381 { 2382 /* No, create a new PTE. First, write the protection */ 2383 TempPte.u.Soft.Protection = ProtectionCode; 2384 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2385 } 2386 ProtectionCode = PointerPte->u.Soft.Protection; 2387 2388 /* Lock the PFN database since we're going to grab a page */ 2389 OldIrql = MiAcquirePfnLock(); 2390 2391 /* Make sure we have enough pages */ 2392 ASSERT(MmAvailablePages >= 32); 2393 2394 /* Try to get a zero page */ 2395 MI_SET_USAGE(MI_USAGE_PEB_TEB); 2396 MI_SET_PROCESS2(CurrentProcess->ImageFileName); 2397 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess); 2398 PageFrameIndex = MiRemoveZeroPageSafe(Color); 2399 if (!PageFrameIndex) 2400 { 2401 /* Grab a page out of there. Later we should grab a colored zero page */ 2402 PageFrameIndex = MiRemoveAnyPage(Color); 2403 ASSERT(PageFrameIndex); 2404 2405 /* Release the lock since we need to do some zeroing */ 2406 MiReleasePfnLock(OldIrql); 2407 2408 /* Zero out the page, since it's for user-mode */ 2409 MiZeroPfn(PageFrameIndex); 2410 2411 /* Grab the lock again so we can initialize the PFN entry */ 2412 OldIrql = MiAcquirePfnLock(); 2413 } 2414 2415 /* Initialize the PFN entry now */ 2416 MiInitializePfn(PageFrameIndex, PointerPte, 1); 2417 2418 /* And be done with the lock */ 2419 MiReleasePfnLock(OldIrql); 2420 2421 /* Increment the count of pages in the process */ 2422 CurrentProcess->NumberOfPrivatePages++; 2423 2424 /* One more demand-zero fault */ 2425 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount); 2426 2427 /* Fault on user PDE, or fault on user PTE? */ 2428 if (PointerPte <= MiHighestUserPte) 2429 { 2430 /* User fault, build a user PTE */ 2431 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 2432 PointerPte, 2433 PointerPte->u.Soft.Protection, 2434 PageFrameIndex); 2435 } 2436 else 2437 { 2438 /* This is a user-mode PDE, create a kernel PTE for it */ 2439 MI_MAKE_HARDWARE_PTE(&TempPte, 2440 PointerPte, 2441 PointerPte->u.Soft.Protection, 2442 PageFrameIndex); 2443 } 2444 2445 /* Write the dirty bit for writeable pages */ 2446 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte); 2447 2448 /* And now write down the PTE, making the address valid */ 2449 MI_WRITE_VALID_PTE(PointerPte, TempPte); 2450 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 2451 ASSERT(Pfn1->u1.Event == NULL); 2452 2453 /* We can now insert it into the working set */ 2454 MiInsertInWorkingSetList(&CurrentProcess->Vm, Address, ProtectionCode); 2455 2456 /* Demand zero */ 2457 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2458 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2459 return STATUS_PAGE_FAULT_DEMAND_ZERO; 2460 } 2461 2462 /* We should have a valid protection here */ 2463 ASSERT(ProtectionCode != 0x100); 2464 2465 /* Write the prototype PTE */ 2466 TempPte = PrototypePte; 2467 TempPte.u.Soft.Protection = ProtectionCode; 2468 ASSERT(TempPte.u.Long != 0); 2469 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2470 } 2471 else 2472 { 2473 /* Get the protection code and check if this is a proto PTE */ 2474 ProtectionCode = (ULONG)TempPte.u.Soft.Protection; 2475 if (TempPte.u.Soft.Prototype) 2476 { 2477 /* Do we need to go find the real PTE? */ 2478 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) 2479 { 2480 /* Get the prototype pte and VAD for it */ 2481 ProtoPte = MiCheckVirtualAddress(Address, 2482 &ProtectionCode, 2483 &Vad); 2484 if (!ProtoPte) 2485 { 2486 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2487 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2488 return STATUS_ACCESS_VIOLATION; 2489 } 2490 } 2491 else 2492 { 2493 /* Get the prototype PTE! */ 2494 ProtoPte = MiProtoPteToPte(&TempPte); 2495 2496 /* Is it read-only */ 2497 if (TempPte.u.Proto.ReadOnly) 2498 { 2499 /* Set read-only code */ 2500 ProtectionCode = MM_READONLY; 2501 } 2502 else 2503 { 2504 /* Set unknown protection */ 2505 ProtectionCode = 0x100; 2506 ASSERT(CurrentProcess->CloneRoot != NULL); 2507 } 2508 } 2509 } 2510 } 2511 2512 /* Do we have a valid protection code? */ 2513 if (ProtectionCode != 0x100) 2514 { 2515 /* Run a software access check first, including to detect guard pages */ 2516 Status = MiAccessCheck(PointerPte, 2517 !MI_IS_NOT_PRESENT_FAULT(FaultCode), 2518 Mode, 2519 ProtectionCode, 2520 TrapInformation, 2521 FALSE); 2522 if (Status != STATUS_SUCCESS) 2523 { 2524 /* Not supported */ 2525 ASSERT(CurrentThread->ApcNeeded == 0); 2526 2527 /* Drop the working set lock */ 2528 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2529 ASSERT(KeGetCurrentIrql() == OldIrql); 2530 2531 /* Did we hit a guard page? */ 2532 if (Status == STATUS_GUARD_PAGE_VIOLATION) 2533 { 2534 /* Handle stack expansion */ 2535 return MiCheckForUserStackOverflow(Address, TrapInformation); 2536 } 2537 2538 /* Otherwise, fail back to the caller directly */ 2539 return Status; 2540 } 2541 } 2542 2543 /* Dispatch the fault */ 2544 Status = MiDispatchFault(FaultCode, 2545 Address, 2546 PointerPte, 2547 ProtoPte, 2548 FALSE, 2549 CurrentProcess, 2550 TrapInformation, 2551 Vad); 2552 2553 /* Return the status */ 2554 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2555 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2556 return Status; 2557 } 2558 2559 NTSTATUS 2560 NTAPI 2561 MmGetExecuteOptions(IN PULONG ExecuteOptions) 2562 { 2563 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb; 2564 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 2565 2566 *ExecuteOptions = 0; 2567 2568 if (CurrentProcess->Flags.ExecuteDisable) 2569 { 2570 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE; 2571 } 2572 2573 if (CurrentProcess->Flags.ExecuteEnable) 2574 { 2575 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE; 2576 } 2577 2578 if (CurrentProcess->Flags.DisableThunkEmulation) 2579 { 2580 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION; 2581 } 2582 2583 if (CurrentProcess->Flags.Permanent) 2584 { 2585 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT; 2586 } 2587 2588 if (CurrentProcess->Flags.ExecuteDispatchEnable) 2589 { 2590 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE; 2591 } 2592 2593 if (CurrentProcess->Flags.ImageDispatchEnable) 2594 { 2595 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE; 2596 } 2597 2598 return STATUS_SUCCESS; 2599 } 2600 2601 NTSTATUS 2602 NTAPI 2603 MmSetExecuteOptions(IN ULONG ExecuteOptions) 2604 { 2605 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb; 2606 KLOCK_QUEUE_HANDLE ProcessLock; 2607 NTSTATUS Status = STATUS_ACCESS_DENIED; 2608 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 2609 2610 /* Only accept valid flags */ 2611 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS) 2612 { 2613 /* Fail */ 2614 DPRINT1("Invalid no-execute options\n"); 2615 return STATUS_INVALID_PARAMETER; 2616 } 2617 2618 /* Change the NX state in the process lock */ 2619 KiAcquireProcessLockRaiseToSynch(CurrentProcess, &ProcessLock); 2620 2621 /* Don't change anything if the permanent flag was set */ 2622 if (!CurrentProcess->Flags.Permanent) 2623 { 2624 /* Start by assuming it's not disabled */ 2625 CurrentProcess->Flags.ExecuteDisable = FALSE; 2626 2627 /* Now process each flag and turn the equivalent bit on */ 2628 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE) 2629 { 2630 CurrentProcess->Flags.ExecuteDisable = TRUE; 2631 } 2632 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE) 2633 { 2634 CurrentProcess->Flags.ExecuteEnable = TRUE; 2635 } 2636 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) 2637 { 2638 CurrentProcess->Flags.DisableThunkEmulation = TRUE; 2639 } 2640 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT) 2641 { 2642 CurrentProcess->Flags.Permanent = TRUE; 2643 } 2644 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE) 2645 { 2646 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE; 2647 } 2648 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE) 2649 { 2650 CurrentProcess->Flags.ImageDispatchEnable = TRUE; 2651 } 2652 2653 /* These are turned on by default if no-execution is also eanbled */ 2654 if (CurrentProcess->Flags.ExecuteEnable) 2655 { 2656 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE; 2657 CurrentProcess->Flags.ImageDispatchEnable = TRUE; 2658 } 2659 2660 /* All good */ 2661 Status = STATUS_SUCCESS; 2662 } 2663 2664 /* Release the lock and return status */ 2665 KiReleaseProcessLock(&ProcessLock); 2666 return Status; 2667 } 2668 2669 /* EOF */ 2670