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