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 /* Do we have the lock? */ 701 if (HaveLock) 702 { 703 /* Release it */ 704 MiReleasePfnLock(OldIrql); 705 706 /* Update performance counters */ 707 if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++; 708 } 709 710 /* Zero the page if need be */ 711 if (NeedZero) MiZeroPfn(PageFrameNumber); 712 713 /* Fault on user PDE, or fault on user PTE? */ 714 if (PointerPte <= MiHighestUserPte) 715 { 716 /* User fault, build a user PTE */ 717 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 718 PointerPte, 719 Protection, 720 PageFrameNumber); 721 } 722 else 723 { 724 /* This is a user-mode PDE, create a kernel PTE for it */ 725 MI_MAKE_HARDWARE_PTE(&TempPte, 726 PointerPte, 727 Protection, 728 PageFrameNumber); 729 } 730 731 /* Set it dirty if it's a writable page */ 732 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte); 733 734 /* Write it */ 735 MI_WRITE_VALID_PTE(PointerPte, TempPte); 736 737 /* Did we manually acquire the lock */ 738 if (HaveLock) 739 { 740 /* Get the PFN entry */ 741 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber); 742 743 /* Windows does these sanity checks */ 744 ASSERT(Pfn1->u1.Event == 0); 745 ASSERT(Pfn1->u3.e1.PrototypePte == 0); 746 } 747 748 // 749 // It's all good now 750 // 751 DPRINT("Demand zero page has now been paged in\n"); 752 return STATUS_PAGE_FAULT_DEMAND_ZERO; 753 } 754 755 static 756 NTSTATUS 757 NTAPI 758 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction, 759 IN PVOID Address, 760 IN PMMPTE PointerPte, 761 IN PMMPTE PointerProtoPte, 762 IN KIRQL OldIrql, 763 IN PMMPFN* LockedProtoPfn) 764 { 765 MMPTE TempPte; 766 PMMPTE OriginalPte, PageTablePte; 767 ULONG_PTR Protection; 768 PFN_NUMBER PageFrameIndex; 769 PMMPFN Pfn1, Pfn2; 770 BOOLEAN OriginalProtection, DirtyPage; 771 772 /* Must be called with an valid prototype PTE, with the PFN lock held */ 773 MI_ASSERT_PFN_LOCK_HELD(); 774 ASSERT(PointerProtoPte->u.Hard.Valid == 1); 775 776 /* Get the page */ 777 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte); 778 779 /* Get the PFN entry and set it as a prototype PTE */ 780 Pfn1 = MiGetPfnEntry(PageFrameIndex); 781 Pfn1->u3.e1.PrototypePte = 1; 782 783 /* Increment the share count for the page table */ 784 PageTablePte = MiAddressToPte(PointerPte); 785 Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber); 786 Pfn2->u2.ShareCount++; 787 788 /* Check where we should be getting the protection information from */ 789 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) 790 { 791 /* Get the protection from the PTE, there's no real Proto PTE data */ 792 Protection = PointerPte->u.Soft.Protection; 793 794 /* Remember that we did not use the proto protection */ 795 OriginalProtection = FALSE; 796 } 797 else 798 { 799 /* Get the protection from the original PTE link */ 800 OriginalPte = &Pfn1->OriginalPte; 801 Protection = OriginalPte->u.Soft.Protection; 802 803 /* Remember that we used the original protection */ 804 OriginalProtection = TRUE; 805 806 /* Check if this was a write on a read only proto */ 807 if ((StoreInstruction) && !(Protection & MM_READWRITE)) 808 { 809 /* Clear the flag */ 810 StoreInstruction = 0; 811 } 812 } 813 814 /* Check if this was a write on a non-COW page */ 815 DirtyPage = FALSE; 816 if ((StoreInstruction) && ((Protection & MM_WRITECOPY) != MM_WRITECOPY)) 817 { 818 /* Then the page should be marked dirty */ 819 DirtyPage = TRUE; 820 821 /* ReactOS check */ 822 ASSERT(Pfn1->OriginalPte.u.Soft.Prototype != 0); 823 } 824 825 /* Did we get a locked incoming PFN? */ 826 if (*LockedProtoPfn) 827 { 828 /* Drop a reference */ 829 ASSERT((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1); 830 MiDereferencePfnAndDropLockCount(*LockedProtoPfn); 831 *LockedProtoPfn = NULL; 832 } 833 834 /* Release the PFN lock */ 835 MiReleasePfnLock(OldIrql); 836 837 /* Remove special/caching bits */ 838 Protection &= ~MM_PROTECT_SPECIAL; 839 840 /* Setup caching */ 841 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined) 842 { 843 /* Write combining, no caching */ 844 MI_PAGE_DISABLE_CACHE(&TempPte); 845 MI_PAGE_WRITE_COMBINED(&TempPte); 846 } 847 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached) 848 { 849 /* Write through, no caching */ 850 MI_PAGE_DISABLE_CACHE(&TempPte); 851 MI_PAGE_WRITE_THROUGH(&TempPte); 852 } 853 854 /* Check if this is a kernel or user address */ 855 if (Address < MmSystemRangeStart) 856 { 857 /* Build the user PTE */ 858 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex); 859 } 860 else 861 { 862 /* Build the kernel PTE */ 863 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex); 864 } 865 866 /* Set the dirty flag if needed */ 867 if (DirtyPage) MI_MAKE_DIRTY_PAGE(&TempPte); 868 869 /* Write the PTE */ 870 MI_WRITE_VALID_PTE(PointerPte, TempPte); 871 872 /* Reset the protection if needed */ 873 if (OriginalProtection) Protection = MM_ZERO_ACCESS; 874 875 /* Return success */ 876 ASSERT(PointerPte == MiAddressToPte(Address)); 877 return STATUS_SUCCESS; 878 } 879 880 static 881 NTSTATUS 882 NTAPI 883 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction, 884 _In_ PVOID FaultingAddress, 885 _In_ PMMPTE PointerPte, 886 _In_ PEPROCESS CurrentProcess, 887 _Inout_ KIRQL *OldIrql) 888 { 889 ULONG Color; 890 PFN_NUMBER Page; 891 NTSTATUS Status; 892 MMPTE TempPte = *PointerPte; 893 PMMPFN Pfn1; 894 ULONG PageFileIndex = TempPte.u.Soft.PageFileLow; 895 ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh; 896 ULONG Protection = TempPte.u.Soft.Protection; 897 898 /* Things we don't support yet */ 899 ASSERT(CurrentProcess > HYDRA_PROCESS); 900 ASSERT(*OldIrql != MM_NOIRQL); 901 902 MI_SET_USAGE(MI_USAGE_PAGE_FILE); 903 MI_SET_PROCESS(CurrentProcess); 904 905 /* We must hold the PFN lock */ 906 MI_ASSERT_PFN_LOCK_HELD(); 907 908 /* Some sanity checks */ 909 ASSERT(TempPte.u.Hard.Valid == 0); 910 ASSERT(TempPte.u.Soft.PageFileHigh != 0); 911 ASSERT(TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED); 912 913 /* Get any page, it will be overwritten */ 914 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess); 915 Page = MiRemoveAnyPage(Color); 916 917 /* Initialize this PFN */ 918 MiInitializePfn(Page, PointerPte, StoreInstruction); 919 920 /* Sets the PFN as being in IO operation */ 921 Pfn1 = MI_PFN_ELEMENT(Page); 922 ASSERT(Pfn1->u1.Event == NULL); 923 ASSERT(Pfn1->u3.e1.ReadInProgress == 0); 924 ASSERT(Pfn1->u3.e1.WriteInProgress == 0); 925 Pfn1->u3.e1.ReadInProgress = 1; 926 927 /* We must write the PTE now as the PFN lock will be released while performing the IO operation */ 928 MI_MAKE_TRANSITION_PTE(&TempPte, Page, Protection); 929 930 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 931 932 /* Release the PFN lock while we proceed */ 933 MiReleasePfnLock(*OldIrql); 934 935 /* Do the paging IO */ 936 Status = MiReadPageFile(Page, PageFileIndex, PageFileOffset); 937 938 /* Lock the PFN database again */ 939 *OldIrql = MiAcquirePfnLock(); 940 941 /* Nobody should have changed that while we were not looking */ 942 ASSERT(Pfn1->u3.e1.ReadInProgress == 1); 943 ASSERT(Pfn1->u3.e1.WriteInProgress == 0); 944 945 if (!NT_SUCCESS(Status)) 946 { 947 /* Malheur! */ 948 ASSERT(FALSE); 949 Pfn1->u4.InPageError = 1; 950 Pfn1->u1.ReadStatus = Status; 951 } 952 953 /* And the PTE can finally be valid */ 954 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, Page); 955 MI_WRITE_VALID_PTE(PointerPte, TempPte); 956 957 Pfn1->u3.e1.ReadInProgress = 0; 958 /* Did someone start to wait on us while we proceeded ? */ 959 if (Pfn1->u1.Event) 960 { 961 /* Tell them we're done */ 962 KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE); 963 } 964 965 return Status; 966 } 967 968 static 969 NTSTATUS 970 NTAPI 971 MiResolveTransitionFault(IN BOOLEAN StoreInstruction, 972 IN PVOID FaultingAddress, 973 IN PMMPTE PointerPte, 974 IN PEPROCESS CurrentProcess, 975 IN KIRQL OldIrql, 976 OUT PKEVENT **InPageBlock) 977 { 978 PFN_NUMBER PageFrameIndex; 979 PMMPFN Pfn1; 980 MMPTE TempPte; 981 PMMPTE PointerToPteForProtoPage; 982 DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n", 983 FaultingAddress, PointerPte, CurrentProcess->ImageFileName); 984 985 /* Windowss does this check */ 986 ASSERT(*InPageBlock == NULL); 987 988 /* ARM3 doesn't support this path */ 989 ASSERT(OldIrql != MM_NOIRQL); 990 991 /* Capture the PTE and make sure it's in transition format */ 992 TempPte = *PointerPte; 993 ASSERT((TempPte.u.Soft.Valid == 0) && 994 (TempPte.u.Soft.Prototype == 0) && 995 (TempPte.u.Soft.Transition == 1)); 996 997 /* Get the PFN and the PFN entry */ 998 PageFrameIndex = TempPte.u.Trans.PageFrameNumber; 999 DPRINT("Transition PFN: %lx\n", PageFrameIndex); 1000 Pfn1 = MiGetPfnEntry(PageFrameIndex); 1001 1002 /* One more transition fault! */ 1003 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); 1004 1005 /* This is from ARM3 -- Windows normally handles this here */ 1006 ASSERT(Pfn1->u4.InPageError == 0); 1007 1008 /* See if we should wait before terminating the fault */ 1009 if ((Pfn1->u3.e1.ReadInProgress == 1) 1010 || ((Pfn1->u3.e1.WriteInProgress == 1) && StoreInstruction)) 1011 { 1012 DPRINT1("The page is currently in a page transition !\n"); 1013 *InPageBlock = &Pfn1->u1.Event; 1014 if (PointerPte == Pfn1->PteAddress) 1015 { 1016 DPRINT1("And this if for this particular PTE.\n"); 1017 /* The PTE will be made valid by the thread serving the fault */ 1018 return STATUS_SUCCESS; // FIXME: Maybe something more descriptive 1019 } 1020 } 1021 1022 /* Windows checks there's some free pages and this isn't an in-page error */ 1023 ASSERT(MmAvailablePages > 0); 1024 ASSERT(Pfn1->u4.InPageError == 0); 1025 1026 /* ReactOS checks for this */ 1027 ASSERT(MmAvailablePages > 32); 1028 1029 /* Was this a transition page in the valid list, or free/zero list? */ 1030 if (Pfn1->u3.e1.PageLocation == ActiveAndValid) 1031 { 1032 /* All Windows does here is a bunch of sanity checks */ 1033 DPRINT("Transition in active list\n"); 1034 ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) && 1035 (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd))); 1036 ASSERT(Pfn1->u2.ShareCount != 0); 1037 ASSERT(Pfn1->u3.e2.ReferenceCount != 0); 1038 } 1039 else 1040 { 1041 /* Otherwise, the page is removed from its list */ 1042 DPRINT("Transition page in free/zero list\n"); 1043 MiUnlinkPageFromList(Pfn1); 1044 MiReferenceUnusedPageAndBumpLockCount(Pfn1); 1045 } 1046 1047 /* At this point, there should no longer be any in-page errors */ 1048 ASSERT(Pfn1->u4.InPageError == 0); 1049 1050 /* Check if this was a PFN with no more share references */ 1051 if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1); 1052 1053 /* Bump the share count and make the page valid */ 1054 Pfn1->u2.ShareCount++; 1055 Pfn1->u3.e1.PageLocation = ActiveAndValid; 1056 1057 /* Prototype PTEs are in paged pool, which itself might be in transition */ 1058 if (FaultingAddress >= MmSystemRangeStart) 1059 { 1060 /* Check if this is a paged pool PTE in transition state */ 1061 PointerToPteForProtoPage = MiAddressToPte(PointerPte); 1062 TempPte = *PointerToPteForProtoPage; 1063 if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1)) 1064 { 1065 /* This isn't yet supported */ 1066 DPRINT1("Double transition fault not yet supported\n"); 1067 ASSERT(FALSE); 1068 } 1069 } 1070 1071 /* Build the final PTE */ 1072 ASSERT(PointerPte->u.Hard.Valid == 0); 1073 ASSERT(PointerPte->u.Trans.Prototype == 0); 1074 ASSERT(PointerPte->u.Trans.Transition == 1); 1075 TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) | 1076 (MmProtectToPteMask[PointerPte->u.Trans.Protection]) | 1077 MiDetermineUserGlobalPteMask(PointerPte); 1078 1079 /* Is the PTE writeable? */ 1080 if ((Pfn1->u3.e1.Modified) && 1081 MI_IS_PAGE_WRITEABLE(&TempPte) && 1082 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 1083 { 1084 /* Make it dirty */ 1085 MI_MAKE_DIRTY_PAGE(&TempPte); 1086 } 1087 else 1088 { 1089 /* Make it clean */ 1090 MI_MAKE_CLEAN_PAGE(&TempPte); 1091 } 1092 1093 /* Write the valid PTE */ 1094 MI_WRITE_VALID_PTE(PointerPte, TempPte); 1095 1096 /* Return success */ 1097 return STATUS_PAGE_FAULT_TRANSITION; 1098 } 1099 1100 static 1101 NTSTATUS 1102 NTAPI 1103 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction, 1104 IN PVOID Address, 1105 IN PMMPTE PointerPte, 1106 IN PMMPTE PointerProtoPte, 1107 IN OUT PMMPFN *OutPfn, 1108 OUT PVOID *PageFileData, 1109 OUT PMMPTE PteValue, 1110 IN PEPROCESS Process, 1111 IN KIRQL OldIrql, 1112 IN PVOID TrapInformation) 1113 { 1114 MMPTE TempPte, PteContents; 1115 PMMPFN Pfn1; 1116 PFN_NUMBER PageFrameIndex; 1117 NTSTATUS Status; 1118 PKEVENT* InPageBlock = NULL; 1119 ULONG Protection; 1120 1121 /* Must be called with an invalid, prototype PTE, with the PFN lock held */ 1122 MI_ASSERT_PFN_LOCK_HELD(); 1123 ASSERT(PointerPte->u.Hard.Valid == 0); 1124 ASSERT(PointerPte->u.Soft.Prototype == 1); 1125 1126 /* Read the prototype PTE and check if it's valid */ 1127 TempPte = *PointerProtoPte; 1128 if (TempPte.u.Hard.Valid == 1) 1129 { 1130 /* One more user of this mapped page */ 1131 PageFrameIndex = PFN_FROM_PTE(&TempPte); 1132 Pfn1 = MiGetPfnEntry(PageFrameIndex); 1133 Pfn1->u2.ShareCount++; 1134 1135 /* Call it a transition */ 1136 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); 1137 1138 /* Complete the prototype PTE fault -- this will release the PFN lock */ 1139 return MiCompleteProtoPteFault(StoreInstruction, 1140 Address, 1141 PointerPte, 1142 PointerProtoPte, 1143 OldIrql, 1144 OutPfn); 1145 } 1146 1147 /* Make sure there's some protection mask */ 1148 if (TempPte.u.Long == 0) 1149 { 1150 /* Release the lock */ 1151 DPRINT1("Access on reserved section?\n"); 1152 MiReleasePfnLock(OldIrql); 1153 return STATUS_ACCESS_VIOLATION; 1154 } 1155 1156 /* There is no such thing as a decommitted prototype PTE */ 1157 ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long); 1158 1159 /* Check for access rights on the PTE proper */ 1160 PteContents = *PointerPte; 1161 if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) 1162 { 1163 if (!PteContents.u.Proto.ReadOnly) 1164 { 1165 Protection = TempPte.u.Soft.Protection; 1166 } 1167 else 1168 { 1169 Protection = MM_READONLY; 1170 } 1171 /* Check for page acess in software */ 1172 Status = MiAccessCheck(PointerProtoPte, 1173 StoreInstruction, 1174 KernelMode, 1175 TempPte.u.Soft.Protection, 1176 TrapInformation, 1177 TRUE); 1178 ASSERT(Status == STATUS_SUCCESS); 1179 } 1180 else 1181 { 1182 Protection = PteContents.u.Soft.Protection; 1183 } 1184 1185 /* Check for writing copy on write page */ 1186 if (((Protection & MM_WRITECOPY) == MM_WRITECOPY) && StoreInstruction) 1187 { 1188 PFN_NUMBER PageFrameIndex, ProtoPageFrameIndex; 1189 ULONG Color; 1190 1191 /* Resolve the proto fault as if it was a read operation */ 1192 Status = MiResolveProtoPteFault(FALSE, 1193 Address, 1194 PointerPte, 1195 PointerProtoPte, 1196 OutPfn, 1197 PageFileData, 1198 PteValue, 1199 Process, 1200 OldIrql, 1201 TrapInformation); 1202 1203 if (!NT_SUCCESS(Status)) 1204 { 1205 return Status; 1206 } 1207 1208 /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */ 1209 OldIrql = MiAcquirePfnLock(); 1210 1211 /* And re-read the proto PTE */ 1212 TempPte = *PointerProtoPte; 1213 ASSERT(TempPte.u.Hard.Valid == 1); 1214 ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte); 1215 1216 MI_SET_USAGE(MI_USAGE_COW); 1217 MI_SET_PROCESS(Process); 1218 1219 /* Get a new page for the private copy */ 1220 if (Process > HYDRA_PROCESS) 1221 Color = MI_GET_NEXT_PROCESS_COLOR(Process); 1222 else 1223 Color = MI_GET_NEXT_COLOR(); 1224 1225 PageFrameIndex = MiRemoveAnyPage(Color); 1226 1227 /* Perform the copy */ 1228 MiCopyPfn(PageFrameIndex, ProtoPageFrameIndex); 1229 1230 /* This will drop everything MiResolveProtoPteFault referenced */ 1231 MiDeletePte(PointerPte, Address, Process, PointerProtoPte); 1232 1233 /* Because now we use this */ 1234 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 1235 MiInitializePfn(PageFrameIndex, PointerPte, TRUE); 1236 1237 /* Fix the protection */ 1238 Protection &= ~MM_WRITECOPY; 1239 Protection |= MM_READWRITE; 1240 if (Address < MmSystemRangeStart) 1241 { 1242 /* Build the user PTE */ 1243 MI_MAKE_HARDWARE_PTE_USER(&PteContents, PointerPte, Protection, PageFrameIndex); 1244 } 1245 else 1246 { 1247 /* Build the kernel PTE */ 1248 MI_MAKE_HARDWARE_PTE(&PteContents, PointerPte, Protection, PageFrameIndex); 1249 } 1250 1251 /* And finally, write the valid PTE */ 1252 MI_WRITE_VALID_PTE(PointerPte, PteContents); 1253 1254 /* The caller expects us to release the PFN lock */ 1255 MiReleasePfnLock(OldIrql); 1256 return Status; 1257 } 1258 1259 /* Check for clone PTEs */ 1260 if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL); 1261 1262 /* We don't support mapped files yet */ 1263 ASSERT(TempPte.u.Soft.Prototype == 0); 1264 1265 /* We might however have transition PTEs */ 1266 if (TempPte.u.Soft.Transition == 1) 1267 { 1268 /* Resolve the transition fault */ 1269 ASSERT(OldIrql != MM_NOIRQL); 1270 Status = MiResolveTransitionFault(StoreInstruction, 1271 Address, 1272 PointerProtoPte, 1273 Process, 1274 OldIrql, 1275 &InPageBlock); 1276 ASSERT(NT_SUCCESS(Status)); 1277 } 1278 else 1279 { 1280 /* We also don't support paged out pages */ 1281 ASSERT(TempPte.u.Soft.PageFileHigh == 0); 1282 1283 /* Resolve the demand zero fault */ 1284 Status = MiResolveDemandZeroFault(Address, 1285 PointerProtoPte, 1286 (ULONG)TempPte.u.Soft.Protection, 1287 Process, 1288 OldIrql); 1289 ASSERT(NT_SUCCESS(Status)); 1290 } 1291 1292 /* Complete the prototype PTE fault -- this will release the PFN lock */ 1293 ASSERT(PointerPte->u.Hard.Valid == 0); 1294 return MiCompleteProtoPteFault(StoreInstruction, 1295 Address, 1296 PointerPte, 1297 PointerProtoPte, 1298 OldIrql, 1299 OutPfn); 1300 } 1301 1302 NTSTATUS 1303 NTAPI 1304 MiDispatchFault(IN ULONG FaultCode, 1305 IN PVOID Address, 1306 IN PMMPTE PointerPte, 1307 IN PMMPTE PointerProtoPte, 1308 IN BOOLEAN Recursive, 1309 IN PEPROCESS Process, 1310 IN PVOID TrapInformation, 1311 IN PMMVAD Vad) 1312 { 1313 MMPTE TempPte; 1314 KIRQL OldIrql, LockIrql; 1315 NTSTATUS Status; 1316 PMMPTE SuperProtoPte; 1317 PMMPFN Pfn1, OutPfn = NULL; 1318 PFN_NUMBER PageFrameIndex; 1319 PFN_COUNT PteCount, ProcessedPtes; 1320 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n", 1321 Address, 1322 Process); 1323 1324 /* Make sure the addresses are ok */ 1325 ASSERT(PointerPte == MiAddressToPte(Address)); 1326 1327 // 1328 // Make sure APCs are off and we're not at dispatch 1329 // 1330 OldIrql = KeGetCurrentIrql(); 1331 ASSERT(OldIrql <= APC_LEVEL); 1332 ASSERT(KeAreAllApcsDisabled() == TRUE); 1333 1334 // 1335 // Grab a copy of the PTE 1336 // 1337 TempPte = *PointerPte; 1338 1339 /* Do we have a prototype PTE? */ 1340 if (PointerProtoPte) 1341 { 1342 /* This should never happen */ 1343 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte)); 1344 1345 /* Check if this is a kernel-mode address */ 1346 SuperProtoPte = MiAddressToPte(PointerProtoPte); 1347 if (Address >= MmSystemRangeStart) 1348 { 1349 /* Lock the PFN database */ 1350 LockIrql = MiAcquirePfnLock(); 1351 1352 /* Has the PTE been made valid yet? */ 1353 if (!SuperProtoPte->u.Hard.Valid) 1354 { 1355 ASSERT(FALSE); 1356 } 1357 else if (PointerPte->u.Hard.Valid == 1) 1358 { 1359 ASSERT(FALSE); 1360 } 1361 1362 /* Resolve the fault -- this will release the PFN lock */ 1363 Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), 1364 Address, 1365 PointerPte, 1366 PointerProtoPte, 1367 &OutPfn, 1368 NULL, 1369 NULL, 1370 Process, 1371 LockIrql, 1372 TrapInformation); 1373 ASSERT(Status == STATUS_SUCCESS); 1374 1375 /* Complete this as a transition fault */ 1376 ASSERT(OldIrql == KeGetCurrentIrql()); 1377 ASSERT(OldIrql <= APC_LEVEL); 1378 ASSERT(KeAreAllApcsDisabled() == TRUE); 1379 return Status; 1380 } 1381 else 1382 { 1383 /* We only handle the lookup path */ 1384 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED); 1385 1386 /* Is there a non-image VAD? */ 1387 if ((Vad) && 1388 (Vad->u.VadFlags.VadType != VadImageMap) && 1389 !(Vad->u2.VadFlags2.ExtendableFile)) 1390 { 1391 /* One day, ReactOS will cluster faults */ 1392 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS); 1393 DPRINT("Should cluster fault, but won't\n"); 1394 } 1395 1396 /* Only one PTE to handle for now */ 1397 PteCount = 1; 1398 ProcessedPtes = 0; 1399 1400 /* Lock the PFN database */ 1401 LockIrql = MiAcquirePfnLock(); 1402 1403 /* We only handle the valid path */ 1404 ASSERT(SuperProtoPte->u.Hard.Valid == 1); 1405 1406 /* Capture the PTE */ 1407 TempPte = *PointerProtoPte; 1408 1409 /* Loop to handle future case of clustered faults */ 1410 while (TRUE) 1411 { 1412 /* For our current usage, this should be true */ 1413 if (TempPte.u.Hard.Valid == 1) 1414 { 1415 /* Bump the share count on the PTE */ 1416 PageFrameIndex = PFN_FROM_PTE(&TempPte); 1417 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 1418 Pfn1->u2.ShareCount++; 1419 } 1420 else if ((TempPte.u.Soft.Prototype == 0) && 1421 (TempPte.u.Soft.Transition == 1)) 1422 { 1423 /* This is a standby page, bring it back from the cache */ 1424 PageFrameIndex = TempPte.u.Trans.PageFrameNumber; 1425 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex); 1426 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 1427 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); 1428 1429 /* Should not yet happen in ReactOS */ 1430 ASSERT(Pfn1->u3.e1.ReadInProgress == 0); 1431 ASSERT(Pfn1->u4.InPageError == 0); 1432 1433 /* Get the page */ 1434 MiUnlinkPageFromList(Pfn1); 1435 1436 /* Bump its reference count */ 1437 ASSERT(Pfn1->u2.ShareCount == 0); 1438 InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount); 1439 Pfn1->u2.ShareCount++; 1440 1441 /* Make it valid again */ 1442 /* This looks like another macro.... */ 1443 Pfn1->u3.e1.PageLocation = ActiveAndValid; 1444 ASSERT(PointerProtoPte->u.Hard.Valid == 0); 1445 ASSERT(PointerProtoPte->u.Trans.Prototype == 0); 1446 ASSERT(PointerProtoPte->u.Trans.Transition == 1); 1447 TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) | 1448 MmProtectToPteMask[PointerProtoPte->u.Trans.Protection]; 1449 TempPte.u.Hard.Valid = 1; 1450 MI_MAKE_ACCESSED_PAGE(&TempPte); 1451 1452 /* Is the PTE writeable? */ 1453 if ((Pfn1->u3.e1.Modified) && 1454 MI_IS_PAGE_WRITEABLE(&TempPte) && 1455 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 1456 { 1457 /* Make it dirty */ 1458 MI_MAKE_DIRTY_PAGE(&TempPte); 1459 } 1460 else 1461 { 1462 /* Make it clean */ 1463 MI_MAKE_CLEAN_PAGE(&TempPte); 1464 } 1465 1466 /* Write the valid PTE */ 1467 MI_WRITE_VALID_PTE(PointerProtoPte, TempPte); 1468 ASSERT(PointerPte->u.Hard.Valid == 0); 1469 } 1470 else 1471 { 1472 /* Page is invalid, get out of the loop */ 1473 break; 1474 } 1475 1476 /* One more done, was it the last? */ 1477 if (++ProcessedPtes == PteCount) 1478 { 1479 /* Complete the fault */ 1480 MiCompleteProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), 1481 Address, 1482 PointerPte, 1483 PointerProtoPte, 1484 LockIrql, 1485 &OutPfn); 1486 1487 /* THIS RELEASES THE PFN LOCK! */ 1488 break; 1489 } 1490 1491 /* No clustered faults yet */ 1492 ASSERT(FALSE); 1493 } 1494 1495 /* Did we resolve the fault? */ 1496 if (ProcessedPtes) 1497 { 1498 /* Bump the transition count */ 1499 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes); 1500 ProcessedPtes--; 1501 1502 /* Loop all the processing we did */ 1503 ASSERT(ProcessedPtes == 0); 1504 1505 /* Complete this as a transition fault */ 1506 ASSERT(OldIrql == KeGetCurrentIrql()); 1507 ASSERT(OldIrql <= APC_LEVEL); 1508 ASSERT(KeAreAllApcsDisabled() == TRUE); 1509 return STATUS_PAGE_FAULT_TRANSITION; 1510 } 1511 1512 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */ 1513 OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber); 1514 MiReferenceUsedPageAndBumpLockCount(OutPfn); 1515 ASSERT(OutPfn->u3.e2.ReferenceCount > 1); 1516 ASSERT(PointerPte->u.Hard.Valid == 0); 1517 1518 /* Resolve the fault -- this will release the PFN lock */ 1519 Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), 1520 Address, 1521 PointerPte, 1522 PointerProtoPte, 1523 &OutPfn, 1524 NULL, 1525 NULL, 1526 Process, 1527 LockIrql, 1528 TrapInformation); 1529 //ASSERT(Status != STATUS_ISSUE_PAGING_IO); 1530 //ASSERT(Status != STATUS_REFAULT); 1531 //ASSERT(Status != STATUS_PTE_CHANGED); 1532 1533 /* Did the routine clean out the PFN or should we? */ 1534 if (OutPfn) 1535 { 1536 /* We had a locked PFN, so acquire the PFN lock to dereference it */ 1537 ASSERT(PointerProtoPte != NULL); 1538 OldIrql = MiAcquirePfnLock(); 1539 1540 /* Dereference the locked PFN */ 1541 MiDereferencePfnAndDropLockCount(OutPfn); 1542 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1); 1543 1544 /* And now release the lock */ 1545 MiReleasePfnLock(OldIrql); 1546 } 1547 1548 /* Complete this as a transition fault */ 1549 ASSERT(OldIrql == KeGetCurrentIrql()); 1550 ASSERT(OldIrql <= APC_LEVEL); 1551 ASSERT(KeAreAllApcsDisabled() == TRUE); 1552 return Status; 1553 } 1554 } 1555 1556 /* Is this a transition PTE */ 1557 if (TempPte.u.Soft.Transition) 1558 { 1559 PKEVENT* InPageBlock = NULL; 1560 PKEVENT PreviousPageEvent; 1561 KEVENT CurrentPageEvent; 1562 1563 /* Lock the PFN database */ 1564 LockIrql = MiAcquirePfnLock(); 1565 1566 /* Resolve */ 1567 Status = MiResolveTransitionFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, LockIrql, &InPageBlock); 1568 1569 ASSERT(NT_SUCCESS(Status)); 1570 1571 if (InPageBlock != NULL) 1572 { 1573 /* Another thread is reading or writing this page. Put us into the waiting queue. */ 1574 KeInitializeEvent(&CurrentPageEvent, NotificationEvent, FALSE); 1575 PreviousPageEvent = *InPageBlock; 1576 *InPageBlock = &CurrentPageEvent; 1577 } 1578 1579 /* And now release the lock and leave*/ 1580 MiReleasePfnLock(LockIrql); 1581 1582 if (InPageBlock != NULL) 1583 { 1584 KeWaitForSingleObject(&CurrentPageEvent, WrPageIn, KernelMode, FALSE, NULL); 1585 1586 /* Let's the chain go on */ 1587 if (PreviousPageEvent) 1588 { 1589 KeSetEvent(PreviousPageEvent, IO_NO_INCREMENT, FALSE); 1590 } 1591 } 1592 1593 ASSERT(OldIrql == KeGetCurrentIrql()); 1594 ASSERT(OldIrql <= APC_LEVEL); 1595 ASSERT(KeAreAllApcsDisabled() == TRUE); 1596 return Status; 1597 } 1598 1599 /* Should we page the data back in ? */ 1600 if (TempPte.u.Soft.PageFileHigh != 0) 1601 { 1602 /* Lock the PFN database */ 1603 LockIrql = MiAcquirePfnLock(); 1604 1605 /* Resolve */ 1606 Status = MiResolvePageFileFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, &LockIrql); 1607 1608 /* And now release the lock and leave*/ 1609 MiReleasePfnLock(LockIrql); 1610 1611 ASSERT(OldIrql == KeGetCurrentIrql()); 1612 ASSERT(OldIrql <= APC_LEVEL); 1613 ASSERT(KeAreAllApcsDisabled() == TRUE); 1614 return Status; 1615 } 1616 1617 // 1618 // The PTE must be invalid but not completely empty. It must also not be a 1619 // prototype a transition or a paged-out PTE as those scenarii should've been handled above. 1620 // These are all Windows checks 1621 // 1622 ASSERT(TempPte.u.Hard.Valid == 0); 1623 ASSERT(TempPte.u.Soft.Prototype == 0); 1624 ASSERT(TempPte.u.Soft.Transition == 0); 1625 ASSERT(TempPte.u.Soft.PageFileHigh == 0); 1626 ASSERT(TempPte.u.Long != 0); 1627 1628 // 1629 // If we got this far, the PTE can only be a demand zero PTE, which is what 1630 // we want. Go handle it! 1631 // 1632 Status = MiResolveDemandZeroFault(Address, 1633 PointerPte, 1634 (ULONG)TempPte.u.Soft.Protection, 1635 Process, 1636 MM_NOIRQL); 1637 ASSERT(KeAreAllApcsDisabled() == TRUE); 1638 if (NT_SUCCESS(Status)) 1639 { 1640 // 1641 // Make sure we're returning in a sane state and pass the status down 1642 // 1643 ASSERT(OldIrql == KeGetCurrentIrql()); 1644 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 1645 return Status; 1646 } 1647 1648 // 1649 // Generate an access fault 1650 // 1651 return STATUS_ACCESS_VIOLATION; 1652 } 1653 1654 NTSTATUS 1655 NTAPI 1656 MmArmAccessFault(IN ULONG FaultCode, 1657 IN PVOID Address, 1658 IN KPROCESSOR_MODE Mode, 1659 IN PVOID TrapInformation) 1660 { 1661 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql; 1662 PMMPTE ProtoPte = NULL; 1663 PMMPTE PointerPte = MiAddressToPte(Address); 1664 PMMPDE PointerPde = MiAddressToPde(Address); 1665 #if (_MI_PAGING_LEVELS >= 3) 1666 PMMPDE PointerPpe = MiAddressToPpe(Address); 1667 #if (_MI_PAGING_LEVELS == 4) 1668 PMMPDE PointerPxe = MiAddressToPxe(Address); 1669 #endif 1670 #endif 1671 MMPTE TempPte; 1672 PETHREAD CurrentThread; 1673 PEPROCESS CurrentProcess; 1674 NTSTATUS Status; 1675 PMMSUPPORT WorkingSet; 1676 ULONG ProtectionCode; 1677 PMMVAD Vad = NULL; 1678 PFN_NUMBER PageFrameIndex; 1679 ULONG Color; 1680 BOOLEAN IsSessionAddress; 1681 PMMPFN Pfn1; 1682 DPRINT("ARM3 FAULT AT: %p\n", Address); 1683 1684 /* Check for page fault on high IRQL */ 1685 if (OldIrql > APC_LEVEL) 1686 { 1687 #if (_MI_PAGING_LEVELS < 3) 1688 /* Could be a page table for paged pool, which we'll allow */ 1689 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte); 1690 MiCheckPdeForPagedPool(Address); 1691 #endif 1692 /* Check if any of the top-level pages are invalid */ 1693 if ( 1694 #if (_MI_PAGING_LEVELS == 4) 1695 (PointerPxe->u.Hard.Valid == 0) || 1696 #endif 1697 #if (_MI_PAGING_LEVELS >= 3) 1698 (PointerPpe->u.Hard.Valid == 0) || 1699 #endif 1700 (PointerPde->u.Hard.Valid == 0) || 1701 (PointerPte->u.Hard.Valid == 0)) 1702 { 1703 /* This fault is not valid, print out some debugging help */ 1704 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n", 1705 Address, 1706 OldIrql); 1707 if (TrapInformation) 1708 { 1709 PKTRAP_FRAME TrapFrame = TrapInformation; 1710 #ifdef _M_IX86 1711 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags); 1712 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx); 1713 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi); 1714 #elif defined(_M_AMD64) 1715 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags); 1716 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx); 1717 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi); 1718 #elif defined(_M_ARM) 1719 DbgPrint("MM:***PC %p\n", TrapFrame->Pc); 1720 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame->R0, TrapFrame->R1, TrapFrame->R2, TrapFrame->R3); 1721 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame->R11, TrapFrame->R12, TrapFrame->Sp, TrapFrame->Lr); 1722 #endif 1723 } 1724 1725 /* Tell the trap handler to fail */ 1726 return STATUS_IN_PAGE_ERROR | 0x10000000; 1727 } 1728 1729 /* Not yet implemented in ReactOS */ 1730 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE); 1731 ASSERT((!MI_IS_NOT_PRESENT_FAULT(FaultCode) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte)) == FALSE); 1732 1733 /* Check if this was a write */ 1734 if (MI_IS_WRITE_ACCESS(FaultCode)) 1735 { 1736 /* Was it to a read-only page? */ 1737 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber); 1738 if (!(PointerPte->u.Long & PTE_READWRITE) && 1739 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE)) 1740 { 1741 /* Crash with distinguished bugcheck code */ 1742 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 1743 (ULONG_PTR)Address, 1744 PointerPte->u.Long, 1745 (ULONG_PTR)TrapInformation, 1746 10); 1747 } 1748 } 1749 1750 /* Nothing is actually wrong */ 1751 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address); 1752 return STATUS_SUCCESS; 1753 } 1754 1755 /* Check for kernel fault address */ 1756 if (Address >= MmSystemRangeStart) 1757 { 1758 /* Bail out, if the fault came from user mode */ 1759 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION; 1760 1761 #if (_MI_PAGING_LEVELS == 2) 1762 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte); 1763 MiCheckPdeForPagedPool(Address); 1764 #endif 1765 1766 /* Check if the higher page table entries are invalid */ 1767 if ( 1768 #if (_MI_PAGING_LEVELS == 4) 1769 /* AMD64 system, check if PXE is invalid */ 1770 (PointerPxe->u.Hard.Valid == 0) || 1771 #endif 1772 #if (_MI_PAGING_LEVELS >= 3) 1773 /* PAE/AMD64 system, check if PPE is invalid */ 1774 (PointerPpe->u.Hard.Valid == 0) || 1775 #endif 1776 /* Always check if the PDE is valid */ 1777 (PointerPde->u.Hard.Valid == 0)) 1778 { 1779 /* PXE/PPE/PDE (still) not valid, kill the system */ 1780 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 1781 (ULONG_PTR)Address, 1782 FaultCode, 1783 (ULONG_PTR)TrapInformation, 1784 2); 1785 } 1786 1787 /* Not handling session faults yet */ 1788 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address); 1789 1790 /* The PDE is valid, so read the PTE */ 1791 TempPte = *PointerPte; 1792 if (TempPte.u.Hard.Valid == 1) 1793 { 1794 /* Check if this was system space or session space */ 1795 if (!IsSessionAddress) 1796 { 1797 /* Check if the PTE is still valid under PFN lock */ 1798 OldIrql = MiAcquirePfnLock(); 1799 TempPte = *PointerPte; 1800 if (TempPte.u.Hard.Valid) 1801 { 1802 /* Check if this was a write */ 1803 if (MI_IS_WRITE_ACCESS(FaultCode)) 1804 { 1805 /* Was it to a read-only page? */ 1806 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber); 1807 if (!(PointerPte->u.Long & PTE_READWRITE) && 1808 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE)) 1809 { 1810 /* Crash with distinguished bugcheck code */ 1811 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 1812 (ULONG_PTR)Address, 1813 PointerPte->u.Long, 1814 (ULONG_PTR)TrapInformation, 1815 11); 1816 } 1817 } 1818 1819 /* Check for execution of non-executable memory */ 1820 if (MI_IS_INSTRUCTION_FETCH(FaultCode) && 1821 !MI_IS_PAGE_EXECUTABLE(&TempPte)) 1822 { 1823 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY, 1824 (ULONG_PTR)Address, 1825 (ULONG_PTR)TempPte.u.Long, 1826 (ULONG_PTR)TrapInformation, 1827 1); 1828 } 1829 } 1830 1831 /* Release PFN lock and return all good */ 1832 MiReleasePfnLock(OldIrql); 1833 return STATUS_SUCCESS; 1834 } 1835 } 1836 #if (_MI_PAGING_LEVELS == 2) 1837 /* Check if this was a session PTE that needs to remap the session PDE */ 1838 if (MI_IS_SESSION_PTE(Address)) 1839 { 1840 /* Do the remapping */ 1841 Status = MiCheckPdeForSessionSpace(Address); 1842 if (!NT_SUCCESS(Status)) 1843 { 1844 /* It failed, this address is invalid */ 1845 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 1846 (ULONG_PTR)Address, 1847 FaultCode, 1848 (ULONG_PTR)TrapInformation, 1849 6); 1850 } 1851 } 1852 #else 1853 1854 _WARN("Session space stuff is not implemented yet!") 1855 1856 #endif 1857 1858 /* Check for a fault on the page table or hyperspace */ 1859 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address)) 1860 { 1861 #if (_MI_PAGING_LEVELS < 3) 1862 /* Windows does this check but I don't understand why -- it's done above! */ 1863 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1); 1864 #endif 1865 /* Handle this as a user mode fault */ 1866 goto UserFault; 1867 } 1868 1869 /* Get the current thread */ 1870 CurrentThread = PsGetCurrentThread(); 1871 1872 /* What kind of address is this */ 1873 if (!IsSessionAddress) 1874 { 1875 /* Use the system working set */ 1876 WorkingSet = &MmSystemCacheWs; 1877 CurrentProcess = NULL; 1878 1879 /* Make sure we don't have a recursive working set lock */ 1880 if ((CurrentThread->OwnsProcessWorkingSetExclusive) || 1881 (CurrentThread->OwnsProcessWorkingSetShared) || 1882 (CurrentThread->OwnsSystemWorkingSetExclusive) || 1883 (CurrentThread->OwnsSystemWorkingSetShared) || 1884 (CurrentThread->OwnsSessionWorkingSetExclusive) || 1885 (CurrentThread->OwnsSessionWorkingSetShared)) 1886 { 1887 /* Fail */ 1888 return STATUS_IN_PAGE_ERROR | 0x10000000; 1889 } 1890 } 1891 else 1892 { 1893 /* Use the session process and working set */ 1894 CurrentProcess = HYDRA_PROCESS; 1895 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm; 1896 1897 /* Make sure we don't have a recursive working set lock */ 1898 if ((CurrentThread->OwnsSessionWorkingSetExclusive) || 1899 (CurrentThread->OwnsSessionWorkingSetShared)) 1900 { 1901 /* Fail */ 1902 return STATUS_IN_PAGE_ERROR | 0x10000000; 1903 } 1904 } 1905 1906 /* Acquire the working set lock */ 1907 KeRaiseIrql(APC_LEVEL, &LockIrql); 1908 MiLockWorkingSet(CurrentThread, WorkingSet); 1909 1910 /* Re-read PTE now that we own the lock */ 1911 TempPte = *PointerPte; 1912 if (TempPte.u.Hard.Valid == 1) 1913 { 1914 /* Check if this was a write */ 1915 if (MI_IS_WRITE_ACCESS(FaultCode)) 1916 { 1917 /* Was it to a read-only page that is not copy on write? */ 1918 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber); 1919 if (!(TempPte.u.Long & PTE_READWRITE) && 1920 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) && 1921 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 1922 { 1923 /* Case not yet handled */ 1924 ASSERT(!IsSessionAddress); 1925 1926 /* Crash with distinguished bugcheck code */ 1927 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 1928 (ULONG_PTR)Address, 1929 TempPte.u.Long, 1930 (ULONG_PTR)TrapInformation, 1931 12); 1932 } 1933 } 1934 1935 /* Check for execution of non-executable memory */ 1936 if (MI_IS_INSTRUCTION_FETCH(FaultCode) && 1937 !MI_IS_PAGE_EXECUTABLE(&TempPte)) 1938 { 1939 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY, 1940 (ULONG_PTR)Address, 1941 (ULONG_PTR)TempPte.u.Long, 1942 (ULONG_PTR)TrapInformation, 1943 2); 1944 } 1945 1946 /* Check for read-only write in session space */ 1947 if ((IsSessionAddress) && 1948 MI_IS_WRITE_ACCESS(FaultCode) && 1949 !MI_IS_PAGE_WRITEABLE(&TempPte)) 1950 { 1951 /* Sanity check */ 1952 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address)); 1953 1954 /* Was this COW? */ 1955 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 1956 { 1957 /* Then this is not allowed */ 1958 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 1959 (ULONG_PTR)Address, 1960 (ULONG_PTR)TempPte.u.Long, 1961 (ULONG_PTR)TrapInformation, 1962 13); 1963 } 1964 1965 /* Otherwise, handle COW */ 1966 ASSERT(FALSE); 1967 } 1968 1969 /* Release the working set */ 1970 MiUnlockWorkingSet(CurrentThread, WorkingSet); 1971 KeLowerIrql(LockIrql); 1972 1973 /* Otherwise, the PDE was probably invalid, and all is good now */ 1974 return STATUS_SUCCESS; 1975 } 1976 1977 /* Check one kind of prototype PTE */ 1978 if (TempPte.u.Soft.Prototype) 1979 { 1980 /* Make sure protected pool is on, and that this is a pool address */ 1981 if ((MmProtectFreedNonPagedPool) && 1982 (((Address >= MmNonPagedPoolStart) && 1983 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart + 1984 MmSizeOfNonPagedPoolInBytes))) || 1985 ((Address >= MmNonPagedPoolExpansionStart) && 1986 (Address < MmNonPagedPoolEnd)))) 1987 { 1988 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */ 1989 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL, 1990 (ULONG_PTR)Address, 1991 FaultCode, 1992 Mode, 1993 4); 1994 } 1995 1996 /* Get the prototype PTE! */ 1997 ProtoPte = MiProtoPteToPte(&TempPte); 1998 1999 /* Do we need to locate the prototype PTE in session space? */ 2000 if ((IsSessionAddress) && 2001 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)) 2002 { 2003 /* Yep, go find it as well as the VAD for it */ 2004 ProtoPte = MiCheckVirtualAddress(Address, 2005 &ProtectionCode, 2006 &Vad); 2007 ASSERT(ProtoPte != NULL); 2008 } 2009 } 2010 else 2011 { 2012 /* We don't implement transition PTEs */ 2013 ASSERT(TempPte.u.Soft.Transition == 0); 2014 2015 /* Check for no-access PTE */ 2016 if (TempPte.u.Soft.Protection == MM_NOACCESS) 2017 { 2018 /* Bugcheck the system! */ 2019 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 2020 (ULONG_PTR)Address, 2021 FaultCode, 2022 (ULONG_PTR)TrapInformation, 2023 1); 2024 } 2025 2026 /* Check for no protecton at all */ 2027 if (TempPte.u.Soft.Protection == MM_ZERO_ACCESS) 2028 { 2029 /* Bugcheck the system! */ 2030 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 2031 (ULONG_PTR)Address, 2032 FaultCode, 2033 (ULONG_PTR)TrapInformation, 2034 0); 2035 } 2036 } 2037 2038 /* Check for demand page */ 2039 if (MI_IS_WRITE_ACCESS(FaultCode) && 2040 !(ProtoPte) && 2041 !(IsSessionAddress) && 2042 !(TempPte.u.Hard.Valid)) 2043 { 2044 /* Get the protection code */ 2045 ASSERT(TempPte.u.Soft.Transition == 0); 2046 if (!(TempPte.u.Soft.Protection & MM_READWRITE)) 2047 { 2048 /* Bugcheck the system! */ 2049 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 2050 (ULONG_PTR)Address, 2051 TempPte.u.Long, 2052 (ULONG_PTR)TrapInformation, 2053 14); 2054 } 2055 } 2056 2057 /* Now do the real fault handling */ 2058 Status = MiDispatchFault(FaultCode, 2059 Address, 2060 PointerPte, 2061 ProtoPte, 2062 FALSE, 2063 CurrentProcess, 2064 TrapInformation, 2065 NULL); 2066 2067 /* Release the working set */ 2068 ASSERT(KeAreAllApcsDisabled() == TRUE); 2069 MiUnlockWorkingSet(CurrentThread, WorkingSet); 2070 KeLowerIrql(LockIrql); 2071 2072 /* We are done! */ 2073 DPRINT("Fault resolved with status: %lx\n", Status); 2074 return Status; 2075 } 2076 2077 /* This is a user fault */ 2078 UserFault: 2079 CurrentThread = PsGetCurrentThread(); 2080 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process; 2081 2082 /* Lock the working set */ 2083 MiLockProcessWorkingSet(CurrentProcess, CurrentThread); 2084 2085 ProtectionCode = MM_INVALID_PROTECTION; 2086 2087 #if (_MI_PAGING_LEVELS == 4) 2088 /* Check if the PXE is valid */ 2089 if (PointerPxe->u.Hard.Valid == 0) 2090 { 2091 /* Right now, we only handle scenarios where the PXE is totally empty */ 2092 ASSERT(PointerPxe->u.Long == 0); 2093 2094 /* This is only possible for user mode addresses! */ 2095 ASSERT(PointerPte <= MiHighestUserPte); 2096 2097 /* Check if we have a VAD */ 2098 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2099 if (ProtectionCode == MM_NOACCESS) 2100 { 2101 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2102 return STATUS_ACCESS_VIOLATION; 2103 } 2104 2105 /* Resolve a demand zero fault */ 2106 MiResolveDemandZeroFault(PointerPpe, 2107 PointerPxe, 2108 MM_EXECUTE_READWRITE, 2109 CurrentProcess, 2110 MM_NOIRQL); 2111 2112 /* We should come back with a valid PXE */ 2113 ASSERT(PointerPxe->u.Hard.Valid == 1); 2114 } 2115 #endif 2116 2117 #if (_MI_PAGING_LEVELS >= 3) 2118 /* Check if the PPE is valid */ 2119 if (PointerPpe->u.Hard.Valid == 0) 2120 { 2121 /* Right now, we only handle scenarios where the PPE is totally empty */ 2122 ASSERT(PointerPpe->u.Long == 0); 2123 2124 /* This is only possible for user mode addresses! */ 2125 ASSERT(PointerPte <= MiHighestUserPte); 2126 2127 /* Check if we have a VAD, unless we did this already */ 2128 if (ProtectionCode == MM_INVALID_PROTECTION) 2129 { 2130 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2131 } 2132 2133 if (ProtectionCode == MM_NOACCESS) 2134 { 2135 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2136 return STATUS_ACCESS_VIOLATION; 2137 } 2138 2139 /* Resolve a demand zero fault */ 2140 MiResolveDemandZeroFault(PointerPde, 2141 PointerPpe, 2142 MM_EXECUTE_READWRITE, 2143 CurrentProcess, 2144 MM_NOIRQL); 2145 2146 /* We should come back with a valid PPE */ 2147 ASSERT(PointerPpe->u.Hard.Valid == 1); 2148 } 2149 #endif 2150 2151 /* Check if the PDE is invalid */ 2152 if (PointerPde->u.Hard.Valid == 0) 2153 { 2154 /* Right now, we only handle scenarios where the PDE is totally empty */ 2155 ASSERT(PointerPde->u.Long == 0); 2156 2157 /* And go dispatch the fault on the PDE. This should handle the demand-zero */ 2158 #if MI_TRACE_PFNS 2159 UserPdeFault = TRUE; 2160 #endif 2161 /* Check if we have a VAD, unless we did this already */ 2162 if (ProtectionCode == MM_INVALID_PROTECTION) 2163 { 2164 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2165 } 2166 2167 if (ProtectionCode == MM_NOACCESS) 2168 { 2169 #if (_MI_PAGING_LEVELS == 2) 2170 /* Could be a page table for paged pool */ 2171 MiCheckPdeForPagedPool(Address); 2172 #endif 2173 /* Has the code above changed anything -- is this now a valid PTE? */ 2174 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION; 2175 2176 /* Either this was a bogus VA or we've fixed up a paged pool PDE */ 2177 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2178 return Status; 2179 } 2180 2181 /* Resolve a demand zero fault */ 2182 MiResolveDemandZeroFault(PointerPte, 2183 PointerPde, 2184 MM_EXECUTE_READWRITE, 2185 CurrentProcess, 2186 MM_NOIRQL); 2187 #if MI_TRACE_PFNS 2188 UserPdeFault = FALSE; 2189 #endif 2190 /* We should come back with APCs enabled, and with a valid PDE */ 2191 ASSERT(KeAreAllApcsDisabled() == TRUE); 2192 ASSERT(PointerPde->u.Hard.Valid == 1); 2193 } 2194 else 2195 { 2196 /* Not yet implemented in ReactOS */ 2197 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE); 2198 } 2199 2200 /* Now capture the PTE. */ 2201 TempPte = *PointerPte; 2202 2203 /* Check if the PTE is valid */ 2204 if (TempPte.u.Hard.Valid) 2205 { 2206 /* Check if this is a write on a readonly PTE */ 2207 if (MI_IS_WRITE_ACCESS(FaultCode)) 2208 { 2209 /* Is this a copy on write PTE? */ 2210 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 2211 { 2212 PFN_NUMBER PageFrameIndex, OldPageFrameIndex; 2213 PMMPFN Pfn1; 2214 2215 LockIrql = MiAcquirePfnLock(); 2216 2217 ASSERT(MmAvailablePages > 0); 2218 2219 MI_SET_USAGE(MI_USAGE_COW); 2220 MI_SET_PROCESS(CurrentProcess); 2221 2222 /* Allocate a new page and copy it */ 2223 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess)); 2224 OldPageFrameIndex = PFN_FROM_PTE(&TempPte); 2225 2226 MiCopyPfn(PageFrameIndex, OldPageFrameIndex); 2227 2228 /* Dereference whatever this PTE is referencing */ 2229 Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex); 2230 ASSERT(Pfn1->u3.e1.PrototypePte == 1); 2231 ASSERT(!MI_IS_PFN_DELETED(Pfn1)); 2232 ProtoPte = Pfn1->PteAddress; 2233 MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte); 2234 2235 /* And make a new shiny one with our page */ 2236 MiInitializePfn(PageFrameIndex, PointerPte, TRUE); 2237 TempPte.u.Hard.PageFrameNumber = PageFrameIndex; 2238 TempPte.u.Hard.Write = 1; 2239 TempPte.u.Hard.CopyOnWrite = 0; 2240 2241 MI_WRITE_VALID_PTE(PointerPte, TempPte); 2242 2243 MiReleasePfnLock(LockIrql); 2244 2245 /* Return the status */ 2246 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2247 return STATUS_PAGE_FAULT_COPY_ON_WRITE; 2248 } 2249 2250 /* Is this a read-only PTE? */ 2251 if (!MI_IS_PAGE_WRITEABLE(&TempPte)) 2252 { 2253 /* Return the status */ 2254 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2255 return STATUS_ACCESS_VIOLATION; 2256 } 2257 } 2258 2259 /* Check for execution of non-executable memory */ 2260 if (MI_IS_INSTRUCTION_FETCH(FaultCode) && 2261 !MI_IS_PAGE_EXECUTABLE(&TempPte)) 2262 { 2263 /* Return the status */ 2264 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2265 return STATUS_ACCESS_VIOLATION; 2266 } 2267 2268 /* The fault has already been resolved by a different thread */ 2269 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2270 return STATUS_SUCCESS; 2271 } 2272 2273 /* Quick check for demand-zero */ 2274 if ((TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)) || 2275 (TempPte.u.Long == (MM_EXECUTE_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))) 2276 { 2277 /* Resolve the fault */ 2278 MiResolveDemandZeroFault(Address, 2279 PointerPte, 2280 TempPte.u.Soft.Protection, 2281 CurrentProcess, 2282 MM_NOIRQL); 2283 2284 /* Return the status */ 2285 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2286 return STATUS_PAGE_FAULT_DEMAND_ZERO; 2287 } 2288 2289 /* Check for zero PTE */ 2290 if (TempPte.u.Long == 0) 2291 { 2292 /* Check if this address range belongs to a valid allocation (VAD) */ 2293 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2294 if (ProtectionCode == MM_NOACCESS) 2295 { 2296 #if (_MI_PAGING_LEVELS == 2) 2297 /* Could be a page table for paged pool */ 2298 MiCheckPdeForPagedPool(Address); 2299 #endif 2300 /* Has the code above changed anything -- is this now a valid PTE? */ 2301 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION; 2302 2303 /* Either this was a bogus VA or we've fixed up a paged pool PDE */ 2304 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2305 return Status; 2306 } 2307 2308 /* 2309 * Check if this is a real user-mode address or actually a kernel-mode 2310 * page table for a user mode address 2311 */ 2312 if (Address <= MM_HIGHEST_USER_ADDRESS) 2313 { 2314 /* Add an additional page table reference */ 2315 MiIncrementPageTableReferences(Address); 2316 } 2317 2318 /* Is this a guard page? */ 2319 if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE) 2320 { 2321 /* The VAD protection cannot be MM_DECOMMIT! */ 2322 ASSERT(ProtectionCode != MM_DECOMMIT); 2323 2324 /* Remove the bit */ 2325 TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE; 2326 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2327 2328 /* Not supported */ 2329 ASSERT(ProtoPte == NULL); 2330 ASSERT(CurrentThread->ApcNeeded == 0); 2331 2332 /* Drop the working set lock */ 2333 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2334 ASSERT(KeGetCurrentIrql() == OldIrql); 2335 2336 /* Handle stack expansion */ 2337 return MiCheckForUserStackOverflow(Address, TrapInformation); 2338 } 2339 2340 /* Did we get a prototype PTE back? */ 2341 if (!ProtoPte) 2342 { 2343 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */ 2344 if (PointerPde == MiAddressToPde(PTE_BASE)) 2345 { 2346 /* Then it's really a demand-zero PDE (on behalf of user-mode) */ 2347 #ifdef _M_ARM 2348 _WARN("This is probably completely broken!"); 2349 MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde); 2350 #else 2351 MI_WRITE_INVALID_PDE(PointerPte, DemandZeroPde); 2352 #endif 2353 } 2354 else 2355 { 2356 /* No, create a new PTE. First, write the protection */ 2357 TempPte.u.Soft.Protection = ProtectionCode; 2358 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2359 } 2360 2361 /* Lock the PFN database since we're going to grab a page */ 2362 OldIrql = MiAcquirePfnLock(); 2363 2364 /* Make sure we have enough pages */ 2365 ASSERT(MmAvailablePages >= 32); 2366 2367 /* Try to get a zero page */ 2368 MI_SET_USAGE(MI_USAGE_PEB_TEB); 2369 MI_SET_PROCESS2(CurrentProcess->ImageFileName); 2370 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess); 2371 PageFrameIndex = MiRemoveZeroPageSafe(Color); 2372 if (!PageFrameIndex) 2373 { 2374 /* Grab a page out of there. Later we should grab a colored zero page */ 2375 PageFrameIndex = MiRemoveAnyPage(Color); 2376 ASSERT(PageFrameIndex); 2377 2378 /* Release the lock since we need to do some zeroing */ 2379 MiReleasePfnLock(OldIrql); 2380 2381 /* Zero out the page, since it's for user-mode */ 2382 MiZeroPfn(PageFrameIndex); 2383 2384 /* Grab the lock again so we can initialize the PFN entry */ 2385 OldIrql = MiAcquirePfnLock(); 2386 } 2387 2388 /* Initialize the PFN entry now */ 2389 MiInitializePfn(PageFrameIndex, PointerPte, 1); 2390 2391 /* Increment the count of pages in the process */ 2392 CurrentProcess->NumberOfPrivatePages++; 2393 2394 /* One more demand-zero fault */ 2395 KeGetCurrentPrcb()->MmDemandZeroCount++; 2396 2397 /* And we're done with the lock */ 2398 MiReleasePfnLock(OldIrql); 2399 2400 /* Fault on user PDE, or fault on user PTE? */ 2401 if (PointerPte <= MiHighestUserPte) 2402 { 2403 /* User fault, build a user PTE */ 2404 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 2405 PointerPte, 2406 PointerPte->u.Soft.Protection, 2407 PageFrameIndex); 2408 } 2409 else 2410 { 2411 /* This is a user-mode PDE, create a kernel PTE for it */ 2412 MI_MAKE_HARDWARE_PTE(&TempPte, 2413 PointerPte, 2414 PointerPte->u.Soft.Protection, 2415 PageFrameIndex); 2416 } 2417 2418 /* Write the dirty bit for writeable pages */ 2419 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte); 2420 2421 /* And now write down the PTE, making the address valid */ 2422 MI_WRITE_VALID_PTE(PointerPte, TempPte); 2423 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 2424 ASSERT(Pfn1->u1.Event == NULL); 2425 2426 /* Demand zero */ 2427 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2428 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2429 return STATUS_PAGE_FAULT_DEMAND_ZERO; 2430 } 2431 2432 /* We should have a valid protection here */ 2433 ASSERT(ProtectionCode != 0x100); 2434 2435 /* Write the prototype PTE */ 2436 TempPte = PrototypePte; 2437 TempPte.u.Soft.Protection = ProtectionCode; 2438 ASSERT(TempPte.u.Long != 0); 2439 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2440 } 2441 else 2442 { 2443 /* Get the protection code and check if this is a proto PTE */ 2444 ProtectionCode = (ULONG)TempPte.u.Soft.Protection; 2445 if (TempPte.u.Soft.Prototype) 2446 { 2447 /* Do we need to go find the real PTE? */ 2448 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) 2449 { 2450 /* Get the prototype pte and VAD for it */ 2451 ProtoPte = MiCheckVirtualAddress(Address, 2452 &ProtectionCode, 2453 &Vad); 2454 if (!ProtoPte) 2455 { 2456 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2457 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2458 return STATUS_ACCESS_VIOLATION; 2459 } 2460 } 2461 else 2462 { 2463 /* Get the prototype PTE! */ 2464 ProtoPte = MiProtoPteToPte(&TempPte); 2465 2466 /* Is it read-only */ 2467 if (TempPte.u.Proto.ReadOnly) 2468 { 2469 /* Set read-only code */ 2470 ProtectionCode = MM_READONLY; 2471 } 2472 else 2473 { 2474 /* Set unknown protection */ 2475 ProtectionCode = 0x100; 2476 ASSERT(CurrentProcess->CloneRoot != NULL); 2477 } 2478 } 2479 } 2480 } 2481 2482 /* Do we have a valid protection code? */ 2483 if (ProtectionCode != 0x100) 2484 { 2485 /* Run a software access check first, including to detect guard pages */ 2486 Status = MiAccessCheck(PointerPte, 2487 !MI_IS_NOT_PRESENT_FAULT(FaultCode), 2488 Mode, 2489 ProtectionCode, 2490 TrapInformation, 2491 FALSE); 2492 if (Status != STATUS_SUCCESS) 2493 { 2494 /* Not supported */ 2495 ASSERT(CurrentThread->ApcNeeded == 0); 2496 2497 /* Drop the working set lock */ 2498 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2499 ASSERT(KeGetCurrentIrql() == OldIrql); 2500 2501 /* Did we hit a guard page? */ 2502 if (Status == STATUS_GUARD_PAGE_VIOLATION) 2503 { 2504 /* Handle stack expansion */ 2505 return MiCheckForUserStackOverflow(Address, TrapInformation); 2506 } 2507 2508 /* Otherwise, fail back to the caller directly */ 2509 return Status; 2510 } 2511 } 2512 2513 /* Dispatch the fault */ 2514 Status = MiDispatchFault(FaultCode, 2515 Address, 2516 PointerPte, 2517 ProtoPte, 2518 FALSE, 2519 CurrentProcess, 2520 TrapInformation, 2521 Vad); 2522 2523 /* Return the status */ 2524 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2525 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2526 return Status; 2527 } 2528 2529 NTSTATUS 2530 NTAPI 2531 MmGetExecuteOptions(IN PULONG ExecuteOptions) 2532 { 2533 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb; 2534 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 2535 2536 *ExecuteOptions = 0; 2537 2538 if (CurrentProcess->Flags.ExecuteDisable) 2539 { 2540 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE; 2541 } 2542 2543 if (CurrentProcess->Flags.ExecuteEnable) 2544 { 2545 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE; 2546 } 2547 2548 if (CurrentProcess->Flags.DisableThunkEmulation) 2549 { 2550 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION; 2551 } 2552 2553 if (CurrentProcess->Flags.Permanent) 2554 { 2555 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT; 2556 } 2557 2558 if (CurrentProcess->Flags.ExecuteDispatchEnable) 2559 { 2560 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE; 2561 } 2562 2563 if (CurrentProcess->Flags.ImageDispatchEnable) 2564 { 2565 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE; 2566 } 2567 2568 return STATUS_SUCCESS; 2569 } 2570 2571 NTSTATUS 2572 NTAPI 2573 MmSetExecuteOptions(IN ULONG ExecuteOptions) 2574 { 2575 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb; 2576 KLOCK_QUEUE_HANDLE ProcessLock; 2577 NTSTATUS Status = STATUS_ACCESS_DENIED; 2578 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 2579 2580 /* Only accept valid flags */ 2581 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS) 2582 { 2583 /* Fail */ 2584 DPRINT1("Invalid no-execute options\n"); 2585 return STATUS_INVALID_PARAMETER; 2586 } 2587 2588 /* Change the NX state in the process lock */ 2589 KiAcquireProcessLockRaiseToSynch(CurrentProcess, &ProcessLock); 2590 2591 /* Don't change anything if the permanent flag was set */ 2592 if (!CurrentProcess->Flags.Permanent) 2593 { 2594 /* Start by assuming it's not disabled */ 2595 CurrentProcess->Flags.ExecuteDisable = FALSE; 2596 2597 /* Now process each flag and turn the equivalent bit on */ 2598 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE) 2599 { 2600 CurrentProcess->Flags.ExecuteDisable = TRUE; 2601 } 2602 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE) 2603 { 2604 CurrentProcess->Flags.ExecuteEnable = TRUE; 2605 } 2606 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) 2607 { 2608 CurrentProcess->Flags.DisableThunkEmulation = TRUE; 2609 } 2610 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT) 2611 { 2612 CurrentProcess->Flags.Permanent = TRUE; 2613 } 2614 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE) 2615 { 2616 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE; 2617 } 2618 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE) 2619 { 2620 CurrentProcess->Flags.ImageDispatchEnable = TRUE; 2621 } 2622 2623 /* These are turned on by default if no-execution is also eanbled */ 2624 if (CurrentProcess->Flags.ExecuteEnable) 2625 { 2626 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE; 2627 CurrentProcess->Flags.ImageDispatchEnable = TRUE; 2628 } 2629 2630 /* All good */ 2631 Status = STATUS_SUCCESS; 2632 } 2633 2634 /* Release the lock and return status */ 2635 KiReleaseProcessLock(&ProcessLock); 2636 return Status; 2637 } 2638 2639 /* EOF */ 2640