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