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 MiIncrementPageTableReferences(PointerPde); 2149 } 2150 #endif 2151 2152 /* Check if the PDE is invalid */ 2153 if (PointerPde->u.Hard.Valid == 0) 2154 { 2155 /* Right now, we only handle scenarios where the PDE is totally empty */ 2156 ASSERT(PointerPde->u.Long == 0); 2157 2158 /* And go dispatch the fault on the PDE. This should handle the demand-zero */ 2159 #if MI_TRACE_PFNS 2160 UserPdeFault = TRUE; 2161 #endif 2162 /* Check if we have a VAD, unless we did this already */ 2163 if (ProtectionCode == MM_INVALID_PROTECTION) 2164 { 2165 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2166 } 2167 2168 if (ProtectionCode == MM_NOACCESS) 2169 { 2170 #if (_MI_PAGING_LEVELS == 2) 2171 /* Could be a page table for paged pool */ 2172 MiCheckPdeForPagedPool(Address); 2173 #endif 2174 /* Has the code above changed anything -- is this now a valid PTE? */ 2175 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION; 2176 2177 /* Either this was a bogus VA or we've fixed up a paged pool PDE */ 2178 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2179 return Status; 2180 } 2181 2182 /* Resolve a demand zero fault */ 2183 MiResolveDemandZeroFault(PointerPte, 2184 PointerPde, 2185 MM_EXECUTE_READWRITE, 2186 CurrentProcess, 2187 MM_NOIRQL); 2188 #if _MI_PAGING_LEVELS >= 3 2189 MiIncrementPageTableReferences(PointerPte); 2190 #endif 2191 2192 #if MI_TRACE_PFNS 2193 UserPdeFault = FALSE; 2194 #endif 2195 /* We should come back with APCs enabled, and with a valid PDE */ 2196 ASSERT(KeAreAllApcsDisabled() == TRUE); 2197 ASSERT(PointerPde->u.Hard.Valid == 1); 2198 } 2199 else 2200 { 2201 /* Not yet implemented in ReactOS */ 2202 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE); 2203 } 2204 2205 /* Now capture the PTE. */ 2206 TempPte = *PointerPte; 2207 2208 /* Check if the PTE is valid */ 2209 if (TempPte.u.Hard.Valid) 2210 { 2211 /* Check if this is a write on a readonly PTE */ 2212 if (MI_IS_WRITE_ACCESS(FaultCode)) 2213 { 2214 /* Is this a copy on write PTE? */ 2215 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte)) 2216 { 2217 PFN_NUMBER PageFrameIndex, OldPageFrameIndex; 2218 PMMPFN Pfn1; 2219 2220 LockIrql = MiAcquirePfnLock(); 2221 2222 ASSERT(MmAvailablePages > 0); 2223 2224 MI_SET_USAGE(MI_USAGE_COW); 2225 MI_SET_PROCESS(CurrentProcess); 2226 2227 /* Allocate a new page and copy it */ 2228 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess)); 2229 OldPageFrameIndex = PFN_FROM_PTE(&TempPte); 2230 2231 MiCopyPfn(PageFrameIndex, OldPageFrameIndex); 2232 2233 /* Dereference whatever this PTE is referencing */ 2234 Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex); 2235 ASSERT(Pfn1->u3.e1.PrototypePte == 1); 2236 ASSERT(!MI_IS_PFN_DELETED(Pfn1)); 2237 ProtoPte = Pfn1->PteAddress; 2238 MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte); 2239 2240 /* And make a new shiny one with our page */ 2241 MiInitializePfn(PageFrameIndex, PointerPte, TRUE); 2242 TempPte.u.Hard.PageFrameNumber = PageFrameIndex; 2243 TempPte.u.Hard.Write = 1; 2244 TempPte.u.Hard.CopyOnWrite = 0; 2245 2246 MI_WRITE_VALID_PTE(PointerPte, TempPte); 2247 2248 MiReleasePfnLock(LockIrql); 2249 2250 /* Return the status */ 2251 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2252 return STATUS_PAGE_FAULT_COPY_ON_WRITE; 2253 } 2254 2255 /* Is this a read-only PTE? */ 2256 if (!MI_IS_PAGE_WRITEABLE(&TempPte)) 2257 { 2258 /* Return the status */ 2259 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2260 return STATUS_ACCESS_VIOLATION; 2261 } 2262 } 2263 2264 /* Check for execution of non-executable memory */ 2265 if (MI_IS_INSTRUCTION_FETCH(FaultCode) && 2266 !MI_IS_PAGE_EXECUTABLE(&TempPte)) 2267 { 2268 /* Return the status */ 2269 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2270 return STATUS_ACCESS_VIOLATION; 2271 } 2272 2273 /* The fault has already been resolved by a different thread */ 2274 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2275 return STATUS_SUCCESS; 2276 } 2277 2278 /* Quick check for demand-zero */ 2279 if ((TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)) || 2280 (TempPte.u.Long == (MM_EXECUTE_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))) 2281 { 2282 /* Resolve the fault */ 2283 MiResolveDemandZeroFault(Address, 2284 PointerPte, 2285 TempPte.u.Soft.Protection, 2286 CurrentProcess, 2287 MM_NOIRQL); 2288 2289 /* Return the status */ 2290 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2291 return STATUS_PAGE_FAULT_DEMAND_ZERO; 2292 } 2293 2294 /* Check for zero PTE */ 2295 if (TempPte.u.Long == 0) 2296 { 2297 /* Check if this address range belongs to a valid allocation (VAD) */ 2298 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 2299 if (ProtectionCode == MM_NOACCESS) 2300 { 2301 #if (_MI_PAGING_LEVELS == 2) 2302 /* Could be a page table for paged pool */ 2303 MiCheckPdeForPagedPool(Address); 2304 #endif 2305 /* Has the code above changed anything -- is this now a valid PTE? */ 2306 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION; 2307 2308 /* Either this was a bogus VA or we've fixed up a paged pool PDE */ 2309 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2310 return Status; 2311 } 2312 2313 /* 2314 * Check if this is a real user-mode address or actually a kernel-mode 2315 * page table for a user mode address 2316 */ 2317 if (Address <= MM_HIGHEST_USER_ADDRESS 2318 #if _MI_PAGING_LEVELS >= 3 2319 || MiIsUserPte(Address) 2320 #if _MI_PAGING_LEVELS == 4 2321 || MiIsUserPde(Address) 2322 #endif 2323 #endif 2324 ) 2325 { 2326 /* Add an additional page table reference */ 2327 MiIncrementPageTableReferences(Address); 2328 } 2329 2330 /* Is this a guard page? */ 2331 if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE) 2332 { 2333 /* The VAD protection cannot be MM_DECOMMIT! */ 2334 ASSERT(ProtectionCode != MM_DECOMMIT); 2335 2336 /* Remove the bit */ 2337 TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE; 2338 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2339 2340 /* Not supported */ 2341 ASSERT(ProtoPte == NULL); 2342 ASSERT(CurrentThread->ApcNeeded == 0); 2343 2344 /* Drop the working set lock */ 2345 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2346 ASSERT(KeGetCurrentIrql() == OldIrql); 2347 2348 /* Handle stack expansion */ 2349 return MiCheckForUserStackOverflow(Address, TrapInformation); 2350 } 2351 2352 /* Did we get a prototype PTE back? */ 2353 if (!ProtoPte) 2354 { 2355 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */ 2356 if (PointerPde == MiAddressToPde(PTE_BASE)) 2357 { 2358 /* Then it's really a demand-zero PDE (on behalf of user-mode) */ 2359 #ifdef _M_ARM 2360 _WARN("This is probably completely broken!"); 2361 MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde); 2362 #else 2363 MI_WRITE_INVALID_PDE(PointerPte, DemandZeroPde); 2364 #endif 2365 } 2366 else 2367 { 2368 /* No, create a new PTE. First, write the protection */ 2369 TempPte.u.Soft.Protection = ProtectionCode; 2370 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2371 } 2372 2373 /* Lock the PFN database since we're going to grab a page */ 2374 OldIrql = MiAcquirePfnLock(); 2375 2376 /* Make sure we have enough pages */ 2377 ASSERT(MmAvailablePages >= 32); 2378 2379 /* Try to get a zero page */ 2380 MI_SET_USAGE(MI_USAGE_PEB_TEB); 2381 MI_SET_PROCESS2(CurrentProcess->ImageFileName); 2382 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess); 2383 PageFrameIndex = MiRemoveZeroPageSafe(Color); 2384 if (!PageFrameIndex) 2385 { 2386 /* Grab a page out of there. Later we should grab a colored zero page */ 2387 PageFrameIndex = MiRemoveAnyPage(Color); 2388 ASSERT(PageFrameIndex); 2389 2390 /* Release the lock since we need to do some zeroing */ 2391 MiReleasePfnLock(OldIrql); 2392 2393 /* Zero out the page, since it's for user-mode */ 2394 MiZeroPfn(PageFrameIndex); 2395 2396 /* Grab the lock again so we can initialize the PFN entry */ 2397 OldIrql = MiAcquirePfnLock(); 2398 } 2399 2400 /* Initialize the PFN entry now */ 2401 MiInitializePfn(PageFrameIndex, PointerPte, 1); 2402 2403 /* Increment the count of pages in the process */ 2404 CurrentProcess->NumberOfPrivatePages++; 2405 2406 /* One more demand-zero fault */ 2407 KeGetCurrentPrcb()->MmDemandZeroCount++; 2408 2409 /* And we're done with the lock */ 2410 MiReleasePfnLock(OldIrql); 2411 2412 /* Fault on user PDE, or fault on user PTE? */ 2413 if (PointerPte <= MiHighestUserPte) 2414 { 2415 /* User fault, build a user PTE */ 2416 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 2417 PointerPte, 2418 PointerPte->u.Soft.Protection, 2419 PageFrameIndex); 2420 } 2421 else 2422 { 2423 /* This is a user-mode PDE, create a kernel PTE for it */ 2424 MI_MAKE_HARDWARE_PTE(&TempPte, 2425 PointerPte, 2426 PointerPte->u.Soft.Protection, 2427 PageFrameIndex); 2428 } 2429 2430 /* Write the dirty bit for writeable pages */ 2431 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte); 2432 2433 /* And now write down the PTE, making the address valid */ 2434 MI_WRITE_VALID_PTE(PointerPte, TempPte); 2435 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 2436 ASSERT(Pfn1->u1.Event == NULL); 2437 2438 /* Demand zero */ 2439 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2440 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2441 return STATUS_PAGE_FAULT_DEMAND_ZERO; 2442 } 2443 2444 /* We should have a valid protection here */ 2445 ASSERT(ProtectionCode != 0x100); 2446 2447 /* Write the prototype PTE */ 2448 TempPte = PrototypePte; 2449 TempPte.u.Soft.Protection = ProtectionCode; 2450 ASSERT(TempPte.u.Long != 0); 2451 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 2452 } 2453 else 2454 { 2455 /* Get the protection code and check if this is a proto PTE */ 2456 ProtectionCode = (ULONG)TempPte.u.Soft.Protection; 2457 if (TempPte.u.Soft.Prototype) 2458 { 2459 /* Do we need to go find the real PTE? */ 2460 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) 2461 { 2462 /* Get the prototype pte and VAD for it */ 2463 ProtoPte = MiCheckVirtualAddress(Address, 2464 &ProtectionCode, 2465 &Vad); 2466 if (!ProtoPte) 2467 { 2468 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2469 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2470 return STATUS_ACCESS_VIOLATION; 2471 } 2472 } 2473 else 2474 { 2475 /* Get the prototype PTE! */ 2476 ProtoPte = MiProtoPteToPte(&TempPte); 2477 2478 /* Is it read-only */ 2479 if (TempPte.u.Proto.ReadOnly) 2480 { 2481 /* Set read-only code */ 2482 ProtectionCode = MM_READONLY; 2483 } 2484 else 2485 { 2486 /* Set unknown protection */ 2487 ProtectionCode = 0x100; 2488 ASSERT(CurrentProcess->CloneRoot != NULL); 2489 } 2490 } 2491 } 2492 } 2493 2494 /* Do we have a valid protection code? */ 2495 if (ProtectionCode != 0x100) 2496 { 2497 /* Run a software access check first, including to detect guard pages */ 2498 Status = MiAccessCheck(PointerPte, 2499 !MI_IS_NOT_PRESENT_FAULT(FaultCode), 2500 Mode, 2501 ProtectionCode, 2502 TrapInformation, 2503 FALSE); 2504 if (Status != STATUS_SUCCESS) 2505 { 2506 /* Not supported */ 2507 ASSERT(CurrentThread->ApcNeeded == 0); 2508 2509 /* Drop the working set lock */ 2510 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2511 ASSERT(KeGetCurrentIrql() == OldIrql); 2512 2513 /* Did we hit a guard page? */ 2514 if (Status == STATUS_GUARD_PAGE_VIOLATION) 2515 { 2516 /* Handle stack expansion */ 2517 return MiCheckForUserStackOverflow(Address, TrapInformation); 2518 } 2519 2520 /* Otherwise, fail back to the caller directly */ 2521 return Status; 2522 } 2523 } 2524 2525 /* Dispatch the fault */ 2526 Status = MiDispatchFault(FaultCode, 2527 Address, 2528 PointerPte, 2529 ProtoPte, 2530 FALSE, 2531 CurrentProcess, 2532 TrapInformation, 2533 Vad); 2534 2535 /* Return the status */ 2536 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 2537 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 2538 return Status; 2539 } 2540 2541 NTSTATUS 2542 NTAPI 2543 MmGetExecuteOptions(IN PULONG ExecuteOptions) 2544 { 2545 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb; 2546 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 2547 2548 *ExecuteOptions = 0; 2549 2550 if (CurrentProcess->Flags.ExecuteDisable) 2551 { 2552 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE; 2553 } 2554 2555 if (CurrentProcess->Flags.ExecuteEnable) 2556 { 2557 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE; 2558 } 2559 2560 if (CurrentProcess->Flags.DisableThunkEmulation) 2561 { 2562 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION; 2563 } 2564 2565 if (CurrentProcess->Flags.Permanent) 2566 { 2567 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT; 2568 } 2569 2570 if (CurrentProcess->Flags.ExecuteDispatchEnable) 2571 { 2572 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE; 2573 } 2574 2575 if (CurrentProcess->Flags.ImageDispatchEnable) 2576 { 2577 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE; 2578 } 2579 2580 return STATUS_SUCCESS; 2581 } 2582 2583 NTSTATUS 2584 NTAPI 2585 MmSetExecuteOptions(IN ULONG ExecuteOptions) 2586 { 2587 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb; 2588 KLOCK_QUEUE_HANDLE ProcessLock; 2589 NTSTATUS Status = STATUS_ACCESS_DENIED; 2590 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 2591 2592 /* Only accept valid flags */ 2593 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS) 2594 { 2595 /* Fail */ 2596 DPRINT1("Invalid no-execute options\n"); 2597 return STATUS_INVALID_PARAMETER; 2598 } 2599 2600 /* Change the NX state in the process lock */ 2601 KiAcquireProcessLockRaiseToSynch(CurrentProcess, &ProcessLock); 2602 2603 /* Don't change anything if the permanent flag was set */ 2604 if (!CurrentProcess->Flags.Permanent) 2605 { 2606 /* Start by assuming it's not disabled */ 2607 CurrentProcess->Flags.ExecuteDisable = FALSE; 2608 2609 /* Now process each flag and turn the equivalent bit on */ 2610 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE) 2611 { 2612 CurrentProcess->Flags.ExecuteDisable = TRUE; 2613 } 2614 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE) 2615 { 2616 CurrentProcess->Flags.ExecuteEnable = TRUE; 2617 } 2618 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) 2619 { 2620 CurrentProcess->Flags.DisableThunkEmulation = TRUE; 2621 } 2622 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT) 2623 { 2624 CurrentProcess->Flags.Permanent = TRUE; 2625 } 2626 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE) 2627 { 2628 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE; 2629 } 2630 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE) 2631 { 2632 CurrentProcess->Flags.ImageDispatchEnable = TRUE; 2633 } 2634 2635 /* These are turned on by default if no-execution is also eanbled */ 2636 if (CurrentProcess->Flags.ExecuteEnable) 2637 { 2638 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE; 2639 CurrentProcess->Flags.ImageDispatchEnable = TRUE; 2640 } 2641 2642 /* All good */ 2643 Status = STATUS_SUCCESS; 2644 } 2645 2646 /* Release the lock and return status */ 2647 KiReleaseProcessLock(&ProcessLock); 2648 return Status; 2649 } 2650 2651 /* EOF */ 2652