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