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
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
20 MmRebalanceMemoryConsumersAndWait(VOID
);
22 /* GLOBALS ********************************************************************/
24 #define HYDRA_PROCESS (PEPROCESS)1
26 BOOLEAN UserPdeFault
= FALSE
;
29 /* PRIVATE FUNCTIONS **********************************************************/
34 MiCheckForUserStackOverflow(IN PVOID Address
,
35 IN PVOID TrapInformation
)
37 PETHREAD CurrentThread
= PsGetCurrentThread();
38 PTEB Teb
= CurrentThread
->Tcb
.Teb
;
39 PVOID StackBase
, DeallocationStack
, NextStackAddress
;
40 SIZE_T GuaranteedSize
;
43 /* Do we own the address space lock? */
44 if (CurrentThread
->AddressSpaceOwner
== 1)
46 /* This isn't valid */
47 DPRINT1("Process owns address space lock\n");
48 ASSERT(KeAreAllApcsDisabled() == TRUE
);
49 return STATUS_GUARD_PAGE_VIOLATION
;
52 /* Are we attached? */
53 if (KeIsAttachedProcess())
55 /* This isn't valid */
56 DPRINT1("Process is attached\n");
57 return STATUS_GUARD_PAGE_VIOLATION
;
60 /* Read the current settings */
61 StackBase
= Teb
->NtTib
.StackBase
;
62 DeallocationStack
= Teb
->DeallocationStack
;
63 GuaranteedSize
= Teb
->GuaranteedStackBytes
;
64 DPRINT("Handling guard page fault with Stacks Addresses 0x%p and 0x%p, guarantee: %lx\n",
65 StackBase
, DeallocationStack
, GuaranteedSize
);
67 /* Guarantees make this code harder, for now, assume there aren't any */
68 ASSERT(GuaranteedSize
== 0);
70 /* So allocate only the minimum guard page size */
71 GuaranteedSize
= PAGE_SIZE
;
73 /* Does this faulting stack address actually exist in the stack? */
74 if ((Address
>= StackBase
) || (Address
< DeallocationStack
))
77 DPRINT1("Faulting address outside of stack bounds. Address=%p, StackBase=%p, DeallocationStack=%p\n",
78 Address
, StackBase
, DeallocationStack
);
79 return STATUS_GUARD_PAGE_VIOLATION
;
82 /* This is where the stack will start now */
83 NextStackAddress
= (PVOID
)((ULONG_PTR
)PAGE_ALIGN(Address
) - GuaranteedSize
);
85 /* Do we have at least one page between here and the end of the stack? */
86 if (((ULONG_PTR
)NextStackAddress
- PAGE_SIZE
) <= (ULONG_PTR
)DeallocationStack
)
88 /* We don't -- Trying to make this guard page valid now */
89 DPRINT1("Close to our death...\n");
91 /* Calculate the next memory address */
92 NextStackAddress
= (PVOID
)((ULONG_PTR
)PAGE_ALIGN(DeallocationStack
) + GuaranteedSize
);
94 /* Allocate the memory */
95 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
101 if (NT_SUCCESS(Status
))
104 Teb
->NtTib
.StackLimit
= NextStackAddress
;
108 DPRINT1("Failed to allocate memory\n");
111 return STATUS_STACK_OVERFLOW
;
114 /* Don't handle this flag yet */
115 ASSERT((PsGetCurrentProcess()->Peb
->NtGlobalFlag
& FLG_DISABLE_STACK_EXTENSION
) == 0);
117 /* Update the stack limit */
118 Teb
->NtTib
.StackLimit
= (PVOID
)((ULONG_PTR
)NextStackAddress
+ GuaranteedSize
);
120 /* Now move the guard page to the next page */
121 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
126 PAGE_READWRITE
| PAGE_GUARD
);
127 if ((NT_SUCCESS(Status
) || (Status
== STATUS_ALREADY_COMMITTED
)))
130 DPRINT("Guard page handled successfully for %p\n", Address
);
131 return STATUS_PAGE_FAULT_GUARD_PAGE
;
134 /* Fail, we couldn't move the guard page */
135 DPRINT1("Guard page failure: %lx\n", Status
);
137 return STATUS_STACK_OVERFLOW
;
143 _In_ ULONG ProtectionMask
,
145 _In_ BOOLEAN Execute
)
147 #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
148 (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
149 ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
150 static const UCHAR AccessAllowedMask
[2][2] =
152 { // Protect 0 1 2 3 4 5 6 7
153 _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
154 _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
157 _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
158 _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
162 /* We want only the lower access bits */
163 ProtectionMask
&= MM_PROTECT_ACCESS
;
165 /* Look it up in the table */
166 return (AccessAllowedMask
[Write
!= 0][Execute
!= 0] >> ProtectionMask
) & 1;
172 MiAccessCheck(IN PMMPTE PointerPte
,
173 IN BOOLEAN StoreInstruction
,
174 IN KPROCESSOR_MODE PreviousMode
,
175 IN ULONG_PTR ProtectionMask
,
181 /* Check for invalid user-mode access */
182 if ((PreviousMode
== UserMode
) && (PointerPte
> MiHighestUserPte
))
184 return STATUS_ACCESS_VIOLATION
;
187 /* Capture the PTE -- is it valid? */
188 TempPte
= *PointerPte
;
189 if (TempPte
.u
.Hard
.Valid
)
191 /* Was someone trying to write to it? */
192 if (StoreInstruction
)
195 if (MI_IS_PAGE_WRITEABLE(&TempPte
) ||
196 MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
198 /* Then there's nothing to worry about */
199 return STATUS_SUCCESS
;
202 /* Oops! This isn't allowed */
203 return STATUS_ACCESS_VIOLATION
;
206 /* Someone was trying to read from a valid PTE, that's fine too */
207 return STATUS_SUCCESS
;
210 /* Check if the protection on the page allows what is being attempted */
211 if (!MiIsAccessAllowed(ProtectionMask
, StoreInstruction
, FALSE
))
213 return STATUS_ACCESS_VIOLATION
;
216 /* Check if this is a guard page */
217 if ((ProtectionMask
& MM_PROTECT_SPECIAL
) == MM_GUARDPAGE
)
219 ASSERT(ProtectionMask
!= MM_DECOMMIT
);
221 /* Attached processes can't expand their stack */
222 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION
;
224 /* No support for prototype PTEs yet */
225 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
227 /* Remove the guard page bit, and return a guard page violation */
228 TempPte
.u
.Soft
.Protection
= ProtectionMask
& ~MM_GUARDPAGE
;
229 ASSERT(TempPte
.u
.Long
!= 0);
230 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
231 return STATUS_GUARD_PAGE_VIOLATION
;
235 return STATUS_SUCCESS
;
241 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
242 OUT PULONG ProtectCode
,
243 OUT PMMVAD
*ProtoVad
)
248 /* No prototype/section support for now */
251 /* User or kernel fault? */
252 if (VirtualAddress
<= MM_HIGHEST_USER_ADDRESS
)
254 /* Special case for shared data */
255 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
)
257 /* It's a read-only page */
258 *ProtectCode
= MM_READONLY
;
259 return MmSharedUserDataPte
;
262 /* Find the VAD, it might not exist if the address is bogus */
263 Vad
= MiLocateAddress(VirtualAddress
);
266 /* Bogus virtual address */
267 *ProtectCode
= MM_NOACCESS
;
271 /* ReactOS does not handle physical memory VADs yet */
272 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
274 /* Check if it's a section, or just an allocation */
275 if (Vad
->u
.VadFlags
.PrivateMemory
)
277 /* ReactOS does not handle AWE VADs yet */
278 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
280 /* This must be a TEB/PEB VAD */
281 if (Vad
->u
.VadFlags
.MemCommit
)
283 /* It's committed, so return the VAD protection */
284 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
288 /* It has not yet been committed, so return no access */
289 *ProtectCode
= MM_NOACCESS
;
292 /* In both cases, return no PTE */
297 /* ReactOS does not supoprt these VADs yet */
298 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
299 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
301 /* Return the proto VAD */
304 /* Get the prototype PTE for this page */
305 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
306 ASSERT(PointerPte
!= NULL
);
307 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
309 /* Return the Prototype PTE and the protection for the page mapping */
310 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
314 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
316 /* This should never happen, as these addresses are handled by the double-maping */
317 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
318 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
320 /* Fail such access */
321 *ProtectCode
= MM_NOACCESS
;
325 /* Return full access rights */
326 *ProtectCode
= MM_EXECUTE_READWRITE
;
329 else if (MI_IS_SESSION_ADDRESS(VirtualAddress
))
331 /* ReactOS does not have an image list yet, so bail out to failure case */
332 ASSERT(IsListEmpty(&MmSessionSpace
->ImageList
));
335 /* Default case -- failure */
336 *ProtectCode
= MM_NOACCESS
;
340 #if (_MI_PAGING_LEVELS == 2)
344 MiCheckPdeForSessionSpace(IN PVOID Address
)
348 PVOID SessionAddress
;
351 /* Is this a session PTE? */
352 if (MI_IS_SESSION_PTE(Address
))
354 /* Make sure the PDE for session space is valid */
355 PointerPde
= MiAddressToPde(MmSessionSpace
);
356 if (!PointerPde
->u
.Hard
.Valid
)
358 /* This means there's no valid session, bail out */
359 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
362 return STATUS_ACCESS_VIOLATION
;
365 /* Now get the session-specific page table for this address */
366 SessionAddress
= MiPteToAddress(Address
);
367 PointerPde
= MiAddressToPte(Address
);
368 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
370 /* It's not valid, so find it in the page table array */
371 Index
= ((ULONG_PTR
)SessionAddress
- (ULONG_PTR
)MmSessionBase
) >> 22;
372 TempPde
.u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
373 if (TempPde
.u
.Hard
.Valid
)
375 /* The copy is valid, so swap it in */
376 InterlockedExchange((PLONG
)PointerPde
, TempPde
.u
.Long
);
377 return STATUS_WAIT_1
;
380 /* We don't seem to have allocated a page table for this address yet? */
381 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
382 PointerPde
->u
.Long
, SessionAddress
);
384 return STATUS_ACCESS_VIOLATION
;
387 /* Is the address also a session address? If not, we're done */
388 if (!MI_IS_SESSION_ADDRESS(Address
)) return STATUS_SUCCESS
;
390 /* It is, so again get the PDE for session space */
391 PointerPde
= MiAddressToPde(MmSessionSpace
);
392 if (!PointerPde
->u
.Hard
.Valid
)
394 /* This means there's no valid session, bail out */
395 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
398 return STATUS_ACCESS_VIOLATION
;
401 /* Now get the PDE for the address itself */
402 PointerPde
= MiAddressToPde(Address
);
403 if (!PointerPde
->u
.Hard
.Valid
)
405 /* Do the swap, we should be good to go */
406 Index
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MmSessionBase
) >> 22;
407 PointerPde
->u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
408 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
410 /* We had not allocated a page table for this session address yet, fail! */
411 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
412 PointerPde
->u
.Long
, Address
);
414 return STATUS_ACCESS_VIOLATION
;
417 /* It's valid, so there's nothing to do */
418 return STATUS_SUCCESS
;
423 MiCheckPdeForPagedPool(IN PVOID Address
)
426 NTSTATUS Status
= STATUS_SUCCESS
;
428 /* Check session PDE */
429 if (MI_IS_SESSION_ADDRESS(Address
)) return MiCheckPdeForSessionSpace(Address
);
430 if (MI_IS_SESSION_PTE(Address
)) return MiCheckPdeForSessionSpace(Address
);
433 // Check if this is a fault while trying to access the page table itself
435 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
438 // Send a hint to the page fault handler that this is only a valid fault
439 // if we already detected this was access within the page table range
441 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
442 Status
= STATUS_WAIT_1
;
444 else if (Address
< MmSystemRangeStart
)
447 // This is totally illegal
449 return STATUS_ACCESS_VIOLATION
;
454 // Get the PDE for the address
456 PointerPde
= MiAddressToPde(Address
);
460 // Check if it's not valid
462 if (PointerPde
->u
.Hard
.Valid
== 0)
465 // Copy it from our double-mapped system page directory
467 InterlockedExchangePte(PointerPde
,
468 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
479 MiCheckPdeForPagedPool(IN PVOID Address
)
481 return STATUS_ACCESS_VIOLATION
;
487 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
494 /* Get the PFN for this page */
495 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
498 /* Grab a system PTE we can use to zero the page */
499 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
502 /* Initialize the PTE for it */
503 TempPte
= ValidKernelPte
;
504 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
507 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
509 /* Write combining, no caching */
510 MI_PAGE_DISABLE_CACHE(&TempPte
);
511 MI_PAGE_WRITE_COMBINED(&TempPte
);
513 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
515 /* Write through, no caching */
516 MI_PAGE_DISABLE_CACHE(&TempPte
);
517 MI_PAGE_WRITE_THROUGH(&TempPte
);
520 /* Make the system PTE valid with our PFN */
521 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
523 /* Get the address it maps to, and zero it out */
524 ZeroAddress
= MiPteToAddress(ZeroPte
);
525 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
527 /* Now get rid of it */
528 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
534 _In_ PFN_NUMBER DestPage
,
535 _In_ PFN_NUMBER SrcPage
)
539 PMMPFN DestPfn
, SrcPfn
;
541 const VOID
* SrcAddress
;
544 DestPfn
= MiGetPfnEntry(DestPage
);
546 SrcPfn
= MiGetPfnEntry(SrcPage
);
549 /* Grab 2 system PTEs */
550 SysPtes
= MiReserveSystemPtes(2, SystemPteSpace
);
553 /* Initialize the destination PTE */
554 TempPte
= ValidKernelPte
;
555 TempPte
.u
.Hard
.PageFrameNumber
= DestPage
;
558 if (DestPfn
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
560 /* Write combining, no caching */
561 MI_PAGE_DISABLE_CACHE(&TempPte
);
562 MI_PAGE_WRITE_COMBINED(&TempPte
);
564 else if (DestPfn
->u3
.e1
.CacheAttribute
== MiNonCached
)
566 /* Write through, no caching */
567 MI_PAGE_DISABLE_CACHE(&TempPte
);
568 MI_PAGE_WRITE_THROUGH(&TempPte
);
571 /* Make the system PTE valid with our PFN */
572 MI_WRITE_VALID_PTE(&SysPtes
[0], TempPte
);
574 /* Initialize the source PTE */
575 TempPte
= ValidKernelPte
;
576 TempPte
.u
.Hard
.PageFrameNumber
= SrcPage
;
579 if (SrcPfn
->u3
.e1
.CacheAttribute
== MiNonCached
)
581 MI_PAGE_DISABLE_CACHE(&TempPte
);
584 /* Make the system PTE valid with our PFN */
585 MI_WRITE_VALID_PTE(&SysPtes
[1], TempPte
);
587 /* Get the addresses and perform the copy */
588 DestAddress
= MiPteToAddress(&SysPtes
[0]);
589 SrcAddress
= MiPteToAddress(&SysPtes
[1]);
590 RtlCopyMemory(DestAddress
, SrcAddress
, PAGE_SIZE
);
592 /* Now get rid of it */
593 MiReleaseSystemPtes(SysPtes
, 2, SystemPteSpace
);
599 MiResolveDemandZeroFault(IN PVOID Address
,
600 IN PMMPTE PointerPte
,
602 IN PEPROCESS Process
,
605 PFN_NUMBER PageFrameNumber
= 0;
607 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
610 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
614 /* Must currently only be called by paging path */
615 if ((Process
> HYDRA_PROCESS
) && (OldIrql
== MM_NOIRQL
))
618 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
621 ASSERT(Process
->ForkInProgress
== NULL
);
623 /* Get process color */
624 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
625 ASSERT(Color
!= 0xFFFFFFFF);
627 /* We'll need a zero page */
632 /* Check if we need a zero page */
633 NeedZero
= (OldIrql
!= MM_NOIRQL
);
635 /* Session-backed image views must be zeroed */
636 if ((Process
== HYDRA_PROCESS
) &&
637 ((MI_IS_SESSION_IMAGE_ADDRESS(Address
)) ||
638 ((Address
>= MiSessionViewStart
) && (Address
< MiSessionSpaceWs
))))
643 /* Hardcode unknown color */
647 /* Check if the PFN database should be acquired */
648 if (OldIrql
== MM_NOIRQL
)
650 /* Acquire it and remember we should release it after */
651 OldIrql
= MiAcquirePfnLock();
655 /* We either manually locked the PFN DB, or already came with it locked */
656 MI_ASSERT_PFN_LOCK_HELD();
657 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
659 /* Assert we have enough pages */
660 //ASSERT(MmAvailablePages >= 32);
663 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
664 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
666 if (Process
== HYDRA_PROCESS
) MI_SET_PROCESS2("Hydra");
667 else if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
668 else MI_SET_PROCESS2("Kernel Demand 0");
670 /* Do we need a zero page? */
671 if (Color
!= 0xFFFFFFFF)
673 /* Try to get one, if we couldn't grab a free page and zero it */
674 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
675 if (!PageFrameNumber
)
677 /* We'll need a free page and zero it manually */
678 PageFrameNumber
= MiRemoveAnyPage(Color
);
683 /* Page guaranteed to be zero-filled */
689 /* Get a color, and see if we should grab a zero or non-zero page */
690 Color
= MI_GET_NEXT_COLOR();
693 /* Process or system doesn't want a zero page, grab anything */
694 PageFrameNumber
= MiRemoveAnyPage(Color
);
698 /* System wants a zero page, obtain one */
699 PageFrameNumber
= MiRemoveZeroPage(Color
);
700 /* No need to zero-fill it */
705 if (PageFrameNumber
== 0)
707 MiReleasePfnLock(OldIrql
);
708 return STATUS_NO_MEMORY
;
712 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
714 /* Increment demand zero faults */
715 KeGetCurrentPrcb()->MmDemandZeroCount
++;
717 /* Do we have the lock? */
721 MiReleasePfnLock(OldIrql
);
723 /* Update performance counters */
724 if (Process
> HYDRA_PROCESS
) Process
->NumberOfPrivatePages
++;
727 /* Zero the page if need be */
728 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
730 /* Fault on user PDE, or fault on user PTE? */
731 if (PointerPte
<= MiHighestUserPte
)
733 /* User fault, build a user PTE */
734 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
741 /* This is a user-mode PDE, create a kernel PTE for it */
742 MI_MAKE_HARDWARE_PTE(&TempPte
,
748 /* Set it dirty if it's a writable page */
749 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
752 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
754 /* Did we manually acquire the lock */
757 /* Get the PFN entry */
758 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
760 /* Windows does these sanity checks */
761 ASSERT(Pfn1
->u1
.Event
== 0);
762 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
768 DPRINT("Demand zero page has now been paged in\n");
769 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
775 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
777 IN PMMPTE PointerPte
,
778 IN PMMPTE PointerProtoPte
,
780 IN PMMPFN
* LockedProtoPfn
)
783 PMMPTE OriginalPte
, PageTablePte
;
784 ULONG_PTR Protection
;
785 PFN_NUMBER PageFrameIndex
;
787 BOOLEAN OriginalProtection
, DirtyPage
;
789 /* Must be called with an valid prototype PTE, with the PFN lock held */
790 MI_ASSERT_PFN_LOCK_HELD();
791 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
794 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
796 /* Get the PFN entry and set it as a prototype PTE */
797 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
798 Pfn1
->u3
.e1
.PrototypePte
= 1;
800 /* Increment the share count for the page table */
801 PageTablePte
= MiAddressToPte(PointerPte
);
802 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
803 Pfn2
->u2
.ShareCount
++;
805 /* Check where we should be getting the protection information from */
806 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
808 /* Get the protection from the PTE, there's no real Proto PTE data */
809 Protection
= PointerPte
->u
.Soft
.Protection
;
811 /* Remember that we did not use the proto protection */
812 OriginalProtection
= FALSE
;
816 /* Get the protection from the original PTE link */
817 OriginalPte
= &Pfn1
->OriginalPte
;
818 Protection
= OriginalPte
->u
.Soft
.Protection
;
820 /* Remember that we used the original protection */
821 OriginalProtection
= TRUE
;
823 /* Check if this was a write on a read only proto */
824 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
827 StoreInstruction
= 0;
831 /* Check if this was a write on a non-COW page */
833 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
835 /* Then the page should be marked dirty */
839 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
842 /* Did we get a locked incoming PFN? */
845 /* Drop a reference */
846 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
847 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
848 *LockedProtoPfn
= NULL
;
851 /* Release the PFN lock */
852 MiReleasePfnLock(OldIrql
);
854 /* Remove special/caching bits */
855 Protection
&= ~MM_PROTECT_SPECIAL
;
858 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
860 /* Write combining, no caching */
861 MI_PAGE_DISABLE_CACHE(&TempPte
);
862 MI_PAGE_WRITE_COMBINED(&TempPte
);
864 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
866 /* Write through, no caching */
867 MI_PAGE_DISABLE_CACHE(&TempPte
);
868 MI_PAGE_WRITE_THROUGH(&TempPte
);
871 /* Check if this is a kernel or user address */
872 if (Address
< MmSystemRangeStart
)
874 /* Build the user PTE */
875 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
879 /* Build the kernel PTE */
880 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
883 /* Set the dirty flag if needed */
884 if (DirtyPage
) MI_MAKE_DIRTY_PAGE(&TempPte
);
887 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
889 /* Reset the protection if needed */
890 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
893 ASSERT(PointerPte
== MiAddressToPte(Address
));
894 return STATUS_SUCCESS
;
900 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction
,
901 _In_ PVOID FaultingAddress
,
902 _In_ PMMPTE PointerPte
,
903 _In_ PEPROCESS CurrentProcess
,
904 _Inout_ KIRQL
*OldIrql
)
909 MMPTE TempPte
= *PointerPte
;
911 ULONG PageFileIndex
= TempPte
.u
.Soft
.PageFileLow
;
912 ULONG_PTR PageFileOffset
= TempPte
.u
.Soft
.PageFileHigh
;
913 ULONG Protection
= TempPte
.u
.Soft
.Protection
;
915 /* Things we don't support yet */
916 ASSERT(CurrentProcess
> HYDRA_PROCESS
);
917 ASSERT(*OldIrql
!= MM_NOIRQL
);
919 MI_SET_USAGE(MI_USAGE_PAGE_FILE
);
920 MI_SET_PROCESS(CurrentProcess
);
922 /* We must hold the PFN lock */
923 MI_ASSERT_PFN_LOCK_HELD();
925 /* Some sanity checks */
926 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
927 ASSERT(TempPte
.u
.Soft
.PageFileHigh
!= 0);
928 ASSERT(TempPte
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
);
930 /* Get any page, it will be overwritten */
931 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
932 Page
= MiRemoveAnyPage(Color
);
935 return STATUS_NO_MEMORY
;
938 /* Initialize this PFN */
939 MiInitializePfn(Page
, PointerPte
, StoreInstruction
);
941 /* Sets the PFN as being in IO operation */
942 Pfn1
= MI_PFN_ELEMENT(Page
);
943 ASSERT(Pfn1
->u1
.Event
== NULL
);
944 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
945 ASSERT(Pfn1
->u3
.e1
.WriteInProgress
== 0);
946 Pfn1
->u3
.e1
.ReadInProgress
= 1;
948 /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
949 MI_MAKE_TRANSITION_PTE(&TempPte
, Page
, Protection
);
951 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
953 /* Release the PFN lock while we proceed */
954 MiReleasePfnLock(*OldIrql
);
956 /* Do the paging IO */
957 Status
= MiReadPageFile(Page
, PageFileIndex
, PageFileOffset
);
959 /* Lock the PFN database again */
960 *OldIrql
= MiAcquirePfnLock();
962 /* Nobody should have changed that while we were not looking */
963 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 1);
964 ASSERT(Pfn1
->u3
.e1
.WriteInProgress
== 0);
966 if (!NT_SUCCESS(Status
))
970 Pfn1
->u4
.InPageError
= 1;
971 Pfn1
->u1
.ReadStatus
= Status
;
974 /* And the PTE can finally be valid */
975 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, Page
);
976 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
978 Pfn1
->u3
.e1
.ReadInProgress
= 0;
979 /* Did someone start to wait on us while we proceeded ? */
982 /* Tell them we're done */
983 KeSetEvent(Pfn1
->u1
.Event
, IO_NO_INCREMENT
, FALSE
);
992 MiResolveTransitionFault(IN BOOLEAN StoreInstruction
,
993 IN PVOID FaultingAddress
,
994 IN PMMPTE PointerPte
,
995 IN PEPROCESS CurrentProcess
,
997 OUT PKEVENT
**InPageBlock
)
999 PFN_NUMBER PageFrameIndex
;
1002 PMMPTE PointerToPteForProtoPage
;
1003 DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
1004 FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
1006 /* Windowss does this check */
1007 ASSERT(*InPageBlock
== NULL
);
1009 /* ARM3 doesn't support this path */
1010 ASSERT(OldIrql
!= MM_NOIRQL
);
1012 /* Capture the PTE and make sure it's in transition format */
1013 TempPte
= *PointerPte
;
1014 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
1015 (TempPte
.u
.Soft
.Prototype
== 0) &&
1016 (TempPte
.u
.Soft
.Transition
== 1));
1018 /* Get the PFN and the PFN entry */
1019 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
1020 DPRINT("Transition PFN: %lx\n", PageFrameIndex
);
1021 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1023 /* One more transition fault! */
1024 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
1026 /* This is from ARM3 -- Windows normally handles this here */
1027 ASSERT(Pfn1
->u4
.InPageError
== 0);
1029 /* See if we should wait before terminating the fault */
1030 if ((Pfn1
->u3
.e1
.ReadInProgress
== 1)
1031 || ((Pfn1
->u3
.e1
.WriteInProgress
== 1) && StoreInstruction
))
1033 DPRINT1("The page is currently in a page transition !\n");
1034 *InPageBlock
= &Pfn1
->u1
.Event
;
1035 if (PointerPte
== Pfn1
->PteAddress
)
1037 DPRINT1("And this if for this particular PTE.\n");
1038 /* The PTE will be made valid by the thread serving the fault */
1039 return STATUS_SUCCESS
; // FIXME: Maybe something more descriptive
1043 /* Windows checks there's some free pages and this isn't an in-page error */
1044 ASSERT(MmAvailablePages
> 0);
1045 ASSERT(Pfn1
->u4
.InPageError
== 0);
1047 /* ReactOS checks for this */
1048 ASSERT(MmAvailablePages
> 32);
1050 /* Was this a transition page in the valid list, or free/zero list? */
1051 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
1053 /* All Windows does here is a bunch of sanity checks */
1054 DPRINT("Transition in active list\n");
1055 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
1056 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
1057 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
1058 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
1062 /* Otherwise, the page is removed from its list */
1063 DPRINT("Transition page in free/zero list\n");
1064 MiUnlinkPageFromList(Pfn1
);
1065 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
1068 /* At this point, there should no longer be any in-page errors */
1069 ASSERT(Pfn1
->u4
.InPageError
== 0);
1071 /* Check if this was a PFN with no more share references */
1072 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
1074 /* Bump the share count and make the page valid */
1075 Pfn1
->u2
.ShareCount
++;
1076 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1078 /* Prototype PTEs are in paged pool, which itself might be in transition */
1079 if (FaultingAddress
>= MmSystemRangeStart
)
1081 /* Check if this is a paged pool PTE in transition state */
1082 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
1083 TempPte
= *PointerToPteForProtoPage
;
1084 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
1086 /* This isn't yet supported */
1087 DPRINT1("Double transition fault not yet supported\n");
1092 /* Build the final PTE */
1093 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1094 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
1095 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
1096 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
1097 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
1098 MiDetermineUserGlobalPteMask(PointerPte
);
1100 /* Is the PTE writeable? */
1101 if ((Pfn1
->u3
.e1
.Modified
) &&
1102 MI_IS_PAGE_WRITEABLE(&TempPte
) &&
1103 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1106 MI_MAKE_DIRTY_PAGE(&TempPte
);
1111 MI_MAKE_CLEAN_PAGE(&TempPte
);
1114 /* Write the valid PTE */
1115 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1117 /* Return success */
1118 return STATUS_PAGE_FAULT_TRANSITION
;
1124 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
1126 IN PMMPTE PointerPte
,
1127 IN PMMPTE PointerProtoPte
,
1128 IN OUT PMMPFN
*OutPfn
,
1129 OUT PVOID
*PageFileData
,
1130 OUT PMMPTE PteValue
,
1131 IN PEPROCESS Process
,
1133 IN PVOID TrapInformation
)
1135 MMPTE TempPte
, PteContents
;
1137 PFN_NUMBER PageFrameIndex
;
1139 PKEVENT
* InPageBlock
= NULL
;
1142 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
1143 MI_ASSERT_PFN_LOCK_HELD();
1144 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1145 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
1147 /* Read the prototype PTE and check if it's valid */
1148 TempPte
= *PointerProtoPte
;
1149 if (TempPte
.u
.Hard
.Valid
== 1)
1151 /* One more user of this mapped page */
1152 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1153 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1154 Pfn1
->u2
.ShareCount
++;
1156 /* Call it a transition */
1157 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
1159 /* Complete the prototype PTE fault -- this will release the PFN lock */
1160 return MiCompleteProtoPteFault(StoreInstruction
,
1168 /* Make sure there's some protection mask */
1169 if (TempPte
.u
.Long
== 0)
1171 /* Release the lock */
1172 DPRINT1("Access on reserved section?\n");
1173 MiReleasePfnLock(OldIrql
);
1174 return STATUS_ACCESS_VIOLATION
;
1177 /* There is no such thing as a decommitted prototype PTE */
1178 ASSERT(TempPte
.u
.Long
!= MmDecommittedPte
.u
.Long
);
1180 /* Check for access rights on the PTE proper */
1181 PteContents
= *PointerPte
;
1182 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
1184 if (!PteContents
.u
.Proto
.ReadOnly
)
1186 Protection
= TempPte
.u
.Soft
.Protection
;
1190 Protection
= MM_READONLY
;
1192 /* Check for page acess in software */
1193 Status
= MiAccessCheck(PointerProtoPte
,
1196 TempPte
.u
.Soft
.Protection
,
1199 ASSERT(Status
== STATUS_SUCCESS
);
1203 Protection
= PteContents
.u
.Soft
.Protection
;
1206 /* Check for writing copy on write page */
1207 if (((Protection
& MM_WRITECOPY
) == MM_WRITECOPY
) && StoreInstruction
)
1209 PFN_NUMBER PageFrameIndex
, ProtoPageFrameIndex
;
1212 /* Resolve the proto fault as if it was a read operation */
1213 Status
= MiResolveProtoPteFault(FALSE
,
1224 if (!NT_SUCCESS(Status
))
1229 /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
1230 OldIrql
= MiAcquirePfnLock();
1232 /* And re-read the proto PTE */
1233 TempPte
= *PointerProtoPte
;
1234 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
1235 ProtoPageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1237 MI_SET_USAGE(MI_USAGE_COW
);
1238 MI_SET_PROCESS(Process
);
1240 /* Get a new page for the private copy */
1241 if (Process
> HYDRA_PROCESS
)
1242 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1244 Color
= MI_GET_NEXT_COLOR();
1246 PageFrameIndex
= MiRemoveAnyPage(Color
);
1247 if (PageFrameIndex
== 0)
1249 MiReleasePfnLock(OldIrql
);
1250 return STATUS_NO_MEMORY
;
1253 /* Perform the copy */
1254 MiCopyPfn(PageFrameIndex
, ProtoPageFrameIndex
);
1256 /* This will drop everything MiResolveProtoPteFault referenced */
1257 MiDeletePte(PointerPte
, Address
, Process
, PointerProtoPte
);
1259 /* Because now we use this */
1260 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1261 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
1263 /* Fix the protection */
1264 Protection
&= ~MM_WRITECOPY
;
1265 Protection
|= MM_READWRITE
;
1266 if (Address
< MmSystemRangeStart
)
1268 /* Build the user PTE */
1269 MI_MAKE_HARDWARE_PTE_USER(&PteContents
, PointerPte
, Protection
, PageFrameIndex
);
1273 /* Build the kernel PTE */
1274 MI_MAKE_HARDWARE_PTE(&PteContents
, PointerPte
, Protection
, PageFrameIndex
);
1277 /* And finally, write the valid PTE */
1278 MI_WRITE_VALID_PTE(PointerPte
, PteContents
);
1280 /* The caller expects us to release the PFN lock */
1281 MiReleasePfnLock(OldIrql
);
1285 /* Check for clone PTEs */
1286 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
1288 /* We don't support mapped files yet */
1289 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1291 /* We might however have transition PTEs */
1292 if (TempPte
.u
.Soft
.Transition
== 1)
1294 /* Resolve the transition fault */
1295 ASSERT(OldIrql
!= MM_NOIRQL
);
1296 Status
= MiResolveTransitionFault(StoreInstruction
,
1302 ASSERT(NT_SUCCESS(Status
));
1306 /* We also don't support paged out pages */
1307 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1309 /* Resolve the demand zero fault */
1310 Status
= MiResolveDemandZeroFault(Address
,
1312 (ULONG
)TempPte
.u
.Soft
.Protection
,
1316 /* Update debug info */
1317 if (TrapInformation
)
1318 MiGetPfnEntry(PointerProtoPte
->u
.Hard
.PageFrameNumber
)->CallSite
= (PVOID
)((PKTRAP_FRAME
)TrapInformation
)->Eip
;
1320 MiGetPfnEntry(PointerProtoPte
->u
.Hard
.PageFrameNumber
)->CallSite
= _ReturnAddress();
1323 ASSERT(NT_SUCCESS(Status
));
1326 /* Complete the prototype PTE fault -- this will release the PFN lock */
1327 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1328 return MiCompleteProtoPteFault(StoreInstruction
,
1338 MiDispatchFault(IN ULONG FaultCode
,
1340 IN PMMPTE PointerPte
,
1341 IN PMMPTE PointerProtoPte
,
1342 IN BOOLEAN Recursive
,
1343 IN PEPROCESS Process
,
1344 IN PVOID TrapInformation
,
1348 KIRQL OldIrql
, LockIrql
;
1350 PMMPTE SuperProtoPte
;
1351 PMMPFN Pfn1
, OutPfn
= NULL
;
1352 PFN_NUMBER PageFrameIndex
;
1353 PFN_COUNT PteCount
, ProcessedPtes
;
1354 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1358 /* Make sure the addresses are ok */
1359 ASSERT(PointerPte
== MiAddressToPte(Address
));
1362 // Make sure APCs are off and we're not at dispatch
1364 OldIrql
= KeGetCurrentIrql();
1365 ASSERT(OldIrql
<= APC_LEVEL
);
1366 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1369 // Grab a copy of the PTE
1371 TempPte
= *PointerPte
;
1373 /* Do we have a prototype PTE? */
1374 if (PointerProtoPte
)
1376 /* This should never happen */
1377 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
1379 /* Check if this is a kernel-mode address */
1380 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
1381 if (Address
>= MmSystemRangeStart
)
1383 /* Lock the PFN database */
1384 LockIrql
= MiAcquirePfnLock();
1386 /* Has the PTE been made valid yet? */
1387 if (!SuperProtoPte
->u
.Hard
.Valid
)
1391 else if (PointerPte
->u
.Hard
.Valid
== 1)
1396 /* Resolve the fault -- this will release the PFN lock */
1397 Status
= MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
),
1407 ASSERT(Status
== STATUS_SUCCESS
);
1409 /* Complete this as a transition fault */
1410 ASSERT(OldIrql
== KeGetCurrentIrql());
1411 ASSERT(OldIrql
<= APC_LEVEL
);
1412 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1417 /* We only handle the lookup path */
1418 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
1420 /* Is there a non-image VAD? */
1422 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
1423 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
1425 /* One day, ReactOS will cluster faults */
1426 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
1427 DPRINT("Should cluster fault, but won't\n");
1430 /* Only one PTE to handle for now */
1434 /* Lock the PFN database */
1435 LockIrql
= MiAcquirePfnLock();
1437 /* We only handle the valid path */
1438 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
1440 /* Capture the PTE */
1441 TempPte
= *PointerProtoPte
;
1443 /* Loop to handle future case of clustered faults */
1446 /* For our current usage, this should be true */
1447 if (TempPte
.u
.Hard
.Valid
== 1)
1449 /* Bump the share count on the PTE */
1450 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1451 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1452 Pfn1
->u2
.ShareCount
++;
1454 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
1455 (TempPte
.u
.Soft
.Transition
== 1))
1457 /* This is a standby page, bring it back from the cache */
1458 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
1459 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
1460 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1461 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
1463 /* Should not yet happen in ReactOS */
1464 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
1465 ASSERT(Pfn1
->u4
.InPageError
== 0);
1468 MiUnlinkPageFromList(Pfn1
);
1470 /* Bump its reference count */
1471 ASSERT(Pfn1
->u2
.ShareCount
== 0);
1472 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1473 Pfn1
->u2
.ShareCount
++;
1475 /* Make it valid again */
1476 /* This looks like another macro.... */
1477 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1478 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1479 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1480 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1481 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1482 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1483 TempPte
.u
.Hard
.Valid
= 1;
1484 MI_MAKE_ACCESSED_PAGE(&TempPte
);
1486 /* Is the PTE writeable? */
1487 if ((Pfn1
->u3
.e1
.Modified
) &&
1488 MI_IS_PAGE_WRITEABLE(&TempPte
) &&
1489 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1492 MI_MAKE_DIRTY_PAGE(&TempPte
);
1497 MI_MAKE_CLEAN_PAGE(&TempPte
);
1500 /* Write the valid PTE */
1501 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1502 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1506 /* Page is invalid, get out of the loop */
1510 /* One more done, was it the last? */
1511 if (++ProcessedPtes
== PteCount
)
1513 /* Complete the fault */
1514 MiCompleteProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
),
1521 /* THIS RELEASES THE PFN LOCK! */
1525 /* No clustered faults yet */
1529 /* Did we resolve the fault? */
1532 /* Bump the transition count */
1533 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1536 /* Loop all the processing we did */
1537 ASSERT(ProcessedPtes
== 0);
1539 /* Complete this as a transition fault */
1540 ASSERT(OldIrql
== KeGetCurrentIrql());
1541 ASSERT(OldIrql
<= APC_LEVEL
);
1542 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1543 return STATUS_PAGE_FAULT_TRANSITION
;
1546 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1547 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1548 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1549 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1550 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1552 /* Resolve the fault -- this will release the PFN lock */
1553 Status
= MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
),
1563 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1564 //ASSERT(Status != STATUS_REFAULT);
1565 //ASSERT(Status != STATUS_PTE_CHANGED);
1567 /* Did the routine clean out the PFN or should we? */
1570 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1571 ASSERT(PointerProtoPte
!= NULL
);
1572 OldIrql
= MiAcquirePfnLock();
1574 /* Dereference the locked PFN */
1575 MiDereferencePfnAndDropLockCount(OutPfn
);
1576 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1578 /* And now release the lock */
1579 MiReleasePfnLock(OldIrql
);
1582 /* Complete this as a transition fault */
1583 ASSERT(OldIrql
== KeGetCurrentIrql());
1584 ASSERT(OldIrql
<= APC_LEVEL
);
1585 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1590 /* Is this a transition PTE */
1591 if (TempPte
.u
.Soft
.Transition
)
1593 PKEVENT
* InPageBlock
= NULL
;
1594 PKEVENT PreviousPageEvent
;
1595 KEVENT CurrentPageEvent
;
1597 /* Lock the PFN database */
1598 LockIrql
= MiAcquirePfnLock();
1601 Status
= MiResolveTransitionFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
), Address
, PointerPte
, Process
, LockIrql
, &InPageBlock
);
1603 ASSERT(NT_SUCCESS(Status
));
1605 if (InPageBlock
!= NULL
)
1607 /* Another thread is reading or writing this page. Put us into the waiting queue. */
1608 KeInitializeEvent(&CurrentPageEvent
, NotificationEvent
, FALSE
);
1609 PreviousPageEvent
= *InPageBlock
;
1610 *InPageBlock
= &CurrentPageEvent
;
1613 /* And now release the lock and leave*/
1614 MiReleasePfnLock(LockIrql
);
1616 if (InPageBlock
!= NULL
)
1618 KeWaitForSingleObject(&CurrentPageEvent
, WrPageIn
, KernelMode
, FALSE
, NULL
);
1620 /* Let's the chain go on */
1621 if (PreviousPageEvent
)
1623 KeSetEvent(PreviousPageEvent
, IO_NO_INCREMENT
, FALSE
);
1627 ASSERT(OldIrql
== KeGetCurrentIrql());
1628 ASSERT(OldIrql
<= APC_LEVEL
);
1629 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1633 /* Should we page the data back in ? */
1634 if (TempPte
.u
.Soft
.PageFileHigh
!= 0)
1636 /* Lock the PFN database */
1637 LockIrql
= MiAcquirePfnLock();
1640 Status
= MiResolvePageFileFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
), Address
, PointerPte
, Process
, &LockIrql
);
1642 /* And now release the lock and leave*/
1643 MiReleasePfnLock(LockIrql
);
1645 ASSERT(OldIrql
== KeGetCurrentIrql());
1646 ASSERT(OldIrql
<= APC_LEVEL
);
1647 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1652 // The PTE must be invalid but not completely empty. It must also not be a
1653 // prototype a transition or a paged-out PTE as those scenarii should've been handled above.
1654 // These are all Windows checks
1656 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1657 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1658 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1659 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1660 ASSERT(TempPte
.u
.Long
!= 0);
1663 // If we got this far, the PTE can only be a demand zero PTE, which is what
1664 // we want. Go handle it!
1666 Status
= MiResolveDemandZeroFault(Address
,
1668 (ULONG
)TempPte
.u
.Soft
.Protection
,
1671 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1672 if (NT_SUCCESS(Status
))
1675 /* Update debug info */
1676 if (TrapInformation
)
1677 MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
)->CallSite
= (PVOID
)((PKTRAP_FRAME
)TrapInformation
)->Eip
;
1679 MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
)->CallSite
= _ReturnAddress();
1683 // Make sure we're returning in a sane state and pass the status down
1685 ASSERT(OldIrql
== KeGetCurrentIrql());
1686 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1698 MmArmAccessFault(IN ULONG FaultCode
,
1700 IN KPROCESSOR_MODE Mode
,
1701 IN PVOID TrapInformation
)
1703 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1704 PMMPTE ProtoPte
= NULL
;
1705 PMMPTE PointerPte
= MiAddressToPte(Address
);
1706 PMMPDE PointerPde
= MiAddressToPde(Address
);
1707 #if (_MI_PAGING_LEVELS >= 3)
1708 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1709 #if (_MI_PAGING_LEVELS == 4)
1710 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1714 PETHREAD CurrentThread
;
1715 PEPROCESS CurrentProcess
;
1717 PMMSUPPORT WorkingSet
;
1718 ULONG ProtectionCode
;
1720 PFN_NUMBER PageFrameIndex
;
1722 BOOLEAN IsSessionAddress
;
1724 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1726 /* Check for page fault on high IRQL */
1727 if (OldIrql
> APC_LEVEL
)
1729 #if (_MI_PAGING_LEVELS < 3)
1730 /* Could be a page table for paged pool, which we'll allow */
1731 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1732 MiCheckPdeForPagedPool(Address
);
1734 /* Check if any of the top-level pages are invalid */
1736 #if (_MI_PAGING_LEVELS == 4)
1737 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1739 #if (_MI_PAGING_LEVELS >= 3)
1740 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1742 (PointerPde
->u
.Hard
.Valid
== 0) ||
1743 (PointerPte
->u
.Hard
.Valid
== 0))
1745 /* This fault is not valid, print out some debugging help */
1746 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1749 if (TrapInformation
)
1751 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1753 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1754 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1755 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1756 #elif defined(_M_AMD64)
1757 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1758 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1759 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1760 #elif defined(_M_ARM)
1761 DbgPrint("MM:***PC %p\n", TrapFrame
->Pc
);
1762 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame
->R0
, TrapFrame
->R1
, TrapFrame
->R2
, TrapFrame
->R3
);
1763 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame
->R11
, TrapFrame
->R12
, TrapFrame
->Sp
, TrapFrame
->Lr
);
1767 /* Tell the trap handler to fail */
1768 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1771 /* Not yet implemented in ReactOS */
1772 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1773 ASSERT((!MI_IS_NOT_PRESENT_FAULT(FaultCode
) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte
)) == FALSE
);
1775 /* Check if this was a write */
1776 if (MI_IS_WRITE_ACCESS(FaultCode
))
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
))
1783 /* Crash with distinguished bugcheck code */
1784 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1787 (ULONG_PTR
)TrapInformation
,
1792 /* Nothing is actually wrong */
1793 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql
, Address
);
1794 return STATUS_SUCCESS
;
1797 /* Check for kernel fault address */
1798 if (Address
>= MmSystemRangeStart
)
1800 /* Bail out, if the fault came from user mode */
1801 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1803 #if (_MI_PAGING_LEVELS == 2)
1804 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1805 MiCheckPdeForPagedPool(Address
);
1808 /* Check if the higher page table entries are invalid */
1810 #if (_MI_PAGING_LEVELS == 4)
1811 /* AMD64 system, check if PXE is invalid */
1812 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1814 #if (_MI_PAGING_LEVELS >= 3)
1815 /* PAE/AMD64 system, check if PPE is invalid */
1816 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1818 /* Always check if the PDE is valid */
1819 (PointerPde
->u
.Hard
.Valid
== 0))
1821 /* PXE/PPE/PDE (still) not valid, kill the system */
1822 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1825 (ULONG_PTR
)TrapInformation
,
1829 /* Not handling session faults yet */
1830 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1832 /* The PDE is valid, so read the PTE */
1833 TempPte
= *PointerPte
;
1834 if (TempPte
.u
.Hard
.Valid
== 1)
1836 /* Check if this was system space or session space */
1837 if (!IsSessionAddress
)
1839 /* Check if the PTE is still valid under PFN lock */
1840 OldIrql
= MiAcquirePfnLock();
1841 TempPte
= *PointerPte
;
1842 if (TempPte
.u
.Hard
.Valid
)
1844 /* Check if this was a write */
1845 if (MI_IS_WRITE_ACCESS(FaultCode
))
1847 /* Was it to a read-only page? */
1848 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1849 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1850 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1852 /* Crash with distinguished bugcheck code */
1853 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1856 (ULONG_PTR
)TrapInformation
,
1861 /* Check for execution of non-executable memory */
1862 if (MI_IS_INSTRUCTION_FETCH(FaultCode
) &&
1863 !MI_IS_PAGE_EXECUTABLE(&TempPte
))
1865 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY
,
1867 (ULONG_PTR
)TempPte
.u
.Long
,
1868 (ULONG_PTR
)TrapInformation
,
1873 /* Release PFN lock and return all good */
1874 MiReleasePfnLock(OldIrql
);
1875 return STATUS_SUCCESS
;
1878 #if (_MI_PAGING_LEVELS == 2)
1879 /* Check if this was a session PTE that needs to remap the session PDE */
1880 if (MI_IS_SESSION_PTE(Address
))
1882 /* Do the remapping */
1883 Status
= MiCheckPdeForSessionSpace(Address
);
1884 if (!NT_SUCCESS(Status
))
1886 /* It failed, this address is invalid */
1887 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1890 (ULONG_PTR
)TrapInformation
,
1896 _WARN("Session space stuff is not implemented yet!")
1900 /* Check for a fault on the page table or hyperspace */
1901 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1903 #if (_MI_PAGING_LEVELS < 3)
1904 /* Windows does this check but I don't understand why -- it's done above! */
1905 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1907 /* Handle this as a user mode fault */
1911 /* Get the current thread */
1912 CurrentThread
= PsGetCurrentThread();
1914 /* What kind of address is this */
1915 if (!IsSessionAddress
)
1917 /* Use the system working set */
1918 WorkingSet
= &MmSystemCacheWs
;
1919 CurrentProcess
= NULL
;
1921 /* Make sure we don't have a recursive working set lock */
1922 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1923 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1924 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1925 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1926 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1927 (CurrentThread
->OwnsSessionWorkingSetShared
))
1930 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1935 /* Use the session process and working set */
1936 CurrentProcess
= HYDRA_PROCESS
;
1937 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1939 /* Make sure we don't have a recursive working set lock */
1940 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1941 (CurrentThread
->OwnsSessionWorkingSetShared
))
1944 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1948 /* Acquire the working set lock */
1949 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1950 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1952 /* Re-read PTE now that we own the lock */
1953 TempPte
= *PointerPte
;
1954 if (TempPte
.u
.Hard
.Valid
== 1)
1956 /* Check if this was a write */
1957 if (MI_IS_WRITE_ACCESS(FaultCode
))
1959 /* Was it to a read-only page that is not copy on write? */
1960 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1961 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1962 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1963 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1965 /* Case not yet handled */
1966 ASSERT(!IsSessionAddress
);
1968 /* Crash with distinguished bugcheck code */
1969 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1972 (ULONG_PTR
)TrapInformation
,
1977 /* Check for execution of non-executable memory */
1978 if (MI_IS_INSTRUCTION_FETCH(FaultCode
) &&
1979 !MI_IS_PAGE_EXECUTABLE(&TempPte
))
1981 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY
,
1983 (ULONG_PTR
)TempPte
.u
.Long
,
1984 (ULONG_PTR
)TrapInformation
,
1988 /* Check for read-only write in session space */
1989 if ((IsSessionAddress
) &&
1990 MI_IS_WRITE_ACCESS(FaultCode
) &&
1991 !MI_IS_PAGE_WRITEABLE(&TempPte
))
1994 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1997 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1999 /* Then this is not allowed */
2000 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
2002 (ULONG_PTR
)TempPte
.u
.Long
,
2003 (ULONG_PTR
)TrapInformation
,
2007 /* Otherwise, handle COW */
2011 /* Release the working set */
2012 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
2013 KeLowerIrql(LockIrql
);
2015 /* Otherwise, the PDE was probably invalid, and all is good now */
2016 return STATUS_SUCCESS
;
2019 /* Check one kind of prototype PTE */
2020 if (TempPte
.u
.Soft
.Prototype
)
2022 /* Make sure protected pool is on, and that this is a pool address */
2023 if ((MmProtectFreedNonPagedPool
) &&
2024 (((Address
>= MmNonPagedPoolStart
) &&
2025 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
2026 MmSizeOfNonPagedPoolInBytes
))) ||
2027 ((Address
>= MmNonPagedPoolExpansionStart
) &&
2028 (Address
< MmNonPagedPoolEnd
))))
2030 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
2031 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
2038 /* Get the prototype PTE! */
2039 ProtoPte
= MiProtoPteToPte(&TempPte
);
2041 /* Do we need to locate the prototype PTE in session space? */
2042 if ((IsSessionAddress
) &&
2043 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
2045 /* Yep, go find it as well as the VAD for it */
2046 ProtoPte
= MiCheckVirtualAddress(Address
,
2049 ASSERT(ProtoPte
!= NULL
);
2054 /* We don't implement transition PTEs */
2055 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
2057 /* Check for no-access PTE */
2058 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
2060 /* Bugcheck the system! */
2061 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
2064 (ULONG_PTR
)TrapInformation
,
2068 /* Check for no protecton at all */
2069 if (TempPte
.u
.Soft
.Protection
== MM_ZERO_ACCESS
)
2071 /* Bugcheck the system! */
2072 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
2075 (ULONG_PTR
)TrapInformation
,
2080 /* Check for demand page */
2081 if (MI_IS_WRITE_ACCESS(FaultCode
) &&
2083 !(IsSessionAddress
) &&
2084 !(TempPte
.u
.Hard
.Valid
))
2086 /* Get the protection code */
2087 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
2088 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
2090 /* Bugcheck the system! */
2091 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
2094 (ULONG_PTR
)TrapInformation
,
2099 /* Now do the real fault handling */
2100 Status
= MiDispatchFault(FaultCode
,
2109 /* Release the working set */
2110 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2111 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
2112 KeLowerIrql(LockIrql
);
2114 if (Status
== STATUS_NO_MEMORY
)
2116 MmRebalanceMemoryConsumersAndWait();
2121 DPRINT("Fault resolved with status: %lx\n", Status
);
2125 /* This is a user fault */
2127 CurrentThread
= PsGetCurrentThread();
2128 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
2130 /* Lock the working set */
2131 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2133 ProtectionCode
= MM_INVALID_PROTECTION
;
2135 #if (_MI_PAGING_LEVELS == 4)
2136 /* Check if the PXE is valid */
2137 if (PointerPxe
->u
.Hard
.Valid
== 0)
2139 /* Right now, we only handle scenarios where the PXE is totally empty */
2140 ASSERT(PointerPxe
->u
.Long
== 0);
2142 /* This is only possible for user mode addresses! */
2143 ASSERT(PointerPte
<= MiHighestUserPte
);
2145 /* Check if we have a VAD */
2146 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2147 if (ProtectionCode
== MM_NOACCESS
)
2149 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2150 return STATUS_ACCESS_VIOLATION
;
2153 /* Resolve a demand zero fault */
2154 Status
= MiResolveDemandZeroFault(PointerPpe
,
2156 MM_EXECUTE_READWRITE
,
2159 if (!NT_SUCCESS(Status
))
2164 /* We should come back with a valid PXE */
2165 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2169 #if (_MI_PAGING_LEVELS >= 3)
2170 /* Check if the PPE is valid */
2171 if (PointerPpe
->u
.Hard
.Valid
== 0)
2173 /* Right now, we only handle scenarios where the PPE is totally empty */
2174 ASSERT(PointerPpe
->u
.Long
== 0);
2176 /* This is only possible for user mode addresses! */
2177 ASSERT(PointerPte
<= MiHighestUserPte
);
2179 /* Check if we have a VAD, unless we did this already */
2180 if (ProtectionCode
== MM_INVALID_PROTECTION
)
2182 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2185 if (ProtectionCode
== MM_NOACCESS
)
2187 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2188 return STATUS_ACCESS_VIOLATION
;
2191 /* Resolve a demand zero fault */
2192 Status
= MiResolveDemandZeroFault(PointerPde
,
2194 MM_EXECUTE_READWRITE
,
2197 if (!NT_SUCCESS(Status
))
2202 /* We should come back with a valid PPE */
2203 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2204 MiIncrementPageTableReferences(PointerPde
);
2208 /* Check if the PDE is invalid */
2209 if (PointerPde
->u
.Hard
.Valid
== 0)
2211 /* Right now, we only handle scenarios where the PDE is totally empty */
2212 ASSERT(PointerPde
->u
.Long
== 0);
2214 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2216 UserPdeFault
= TRUE
;
2218 /* Check if we have a VAD, unless we did this already */
2219 if (ProtectionCode
== MM_INVALID_PROTECTION
)
2221 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2224 if (ProtectionCode
== MM_NOACCESS
)
2226 #if (_MI_PAGING_LEVELS == 2)
2227 /* Could be a page table for paged pool */
2228 MiCheckPdeForPagedPool(Address
);
2230 /* Has the code above changed anything -- is this now a valid PTE? */
2231 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
2233 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2234 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2238 /* Resolve a demand zero fault */
2239 Status
= MiResolveDemandZeroFault(PointerPte
,
2241 MM_EXECUTE_READWRITE
,
2244 if (!NT_SUCCESS(Status
))
2249 #if _MI_PAGING_LEVELS >= 3
2250 MiIncrementPageTableReferences(PointerPte
);
2254 UserPdeFault
= FALSE
;
2255 /* Update debug info */
2256 if (TrapInformation
)
2257 MiGetPfnEntry(PointerPde
->u
.Hard
.PageFrameNumber
)->CallSite
= (PVOID
)((PKTRAP_FRAME
)TrapInformation
)->Eip
;
2259 MiGetPfnEntry(PointerPde
->u
.Hard
.PageFrameNumber
)->CallSite
= _ReturnAddress();
2261 /* We should come back with APCs enabled, and with a valid PDE */
2262 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2263 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2267 /* Not yet implemented in ReactOS */
2268 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
2271 /* Now capture the PTE. */
2272 TempPte
= *PointerPte
;
2274 /* Check if the PTE is valid */
2275 if (TempPte
.u
.Hard
.Valid
)
2277 /* Check if this is a write on a readonly PTE */
2278 if (MI_IS_WRITE_ACCESS(FaultCode
))
2280 /* Is this a copy on write PTE? */
2281 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
2283 PFN_NUMBER PageFrameIndex
, OldPageFrameIndex
;
2286 LockIrql
= MiAcquirePfnLock();
2288 ASSERT(MmAvailablePages
> 0);
2290 MI_SET_USAGE(MI_USAGE_COW
);
2291 MI_SET_PROCESS(CurrentProcess
);
2293 /* Allocate a new page and copy it */
2294 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
));
2295 if (PageFrameIndex
== 0)
2297 MiReleasePfnLock(LockIrql
);
2298 Status
= STATUS_NO_MEMORY
;
2301 OldPageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2303 MiCopyPfn(PageFrameIndex
, OldPageFrameIndex
);
2305 /* Dereference whatever this PTE is referencing */
2306 Pfn1
= MI_PFN_ELEMENT(OldPageFrameIndex
);
2307 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 1);
2308 ASSERT(!MI_IS_PFN_DELETED(Pfn1
));
2309 ProtoPte
= Pfn1
->PteAddress
;
2310 MiDeletePte(PointerPte
, Address
, CurrentProcess
, ProtoPte
);
2312 /* And make a new shiny one with our page */
2313 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
2314 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
2315 TempPte
.u
.Hard
.Write
= 1;
2316 TempPte
.u
.Hard
.CopyOnWrite
= 0;
2318 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
2320 MiReleasePfnLock(LockIrql
);
2322 /* Return the status */
2323 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2324 return STATUS_PAGE_FAULT_COPY_ON_WRITE
;
2327 /* Is this a read-only PTE? */
2328 if (!MI_IS_PAGE_WRITEABLE(&TempPte
))
2330 /* Return the status */
2331 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2332 return STATUS_ACCESS_VIOLATION
;
2336 #if _MI_HAS_NO_EXECUTE
2337 /* Check for execution of non-executable memory */
2338 if (MI_IS_INSTRUCTION_FETCH(FaultCode
) &&
2339 !MI_IS_PAGE_EXECUTABLE(&TempPte
))
2341 /* Check if execute enable was set */
2342 if (CurrentProcess
->Pcb
.Flags
.ExecuteEnable
)
2344 /* Fix up the PTE to be executable */
2345 TempPte
.u
.Hard
.NoExecute
= 0;
2346 MI_UPDATE_VALID_PTE(PointerPte
, TempPte
);
2347 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2348 return STATUS_SUCCESS
;
2351 /* Return the status */
2352 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2353 return STATUS_ACCESS_VIOLATION
;
2357 /* The fault has already been resolved by a different thread */
2358 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2359 return STATUS_SUCCESS
;
2362 /* Quick check for demand-zero */
2363 if ((TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
)) ||
2364 (TempPte
.u
.Long
== (MM_EXECUTE_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
)))
2366 /* Resolve the fault */
2367 Status
= MiResolveDemandZeroFault(Address
,
2369 TempPte
.u
.Soft
.Protection
,
2372 if (!NT_SUCCESS(Status
))
2378 /* Update debug info */
2379 if (TrapInformation
)
2380 MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
)->CallSite
= (PVOID
)((PKTRAP_FRAME
)TrapInformation
)->Eip
;
2382 MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
)->CallSite
= _ReturnAddress();
2385 /* Return the status */
2386 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2387 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2390 /* Check for zero PTE */
2391 if (TempPte
.u
.Long
== 0)
2393 /* Check if this address range belongs to a valid allocation (VAD) */
2394 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2395 if (ProtectionCode
== MM_NOACCESS
)
2397 #if (_MI_PAGING_LEVELS == 2)
2398 /* Could be a page table for paged pool */
2399 MiCheckPdeForPagedPool(Address
);
2401 /* Has the code above changed anything -- is this now a valid PTE? */
2402 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
2404 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2405 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2410 * Check if this is a real user-mode address or actually a kernel-mode
2411 * page table for a user mode address
2413 if (Address
<= MM_HIGHEST_USER_ADDRESS
2414 #if _MI_PAGING_LEVELS >= 3
2415 || MiIsUserPte(Address
)
2416 #if _MI_PAGING_LEVELS == 4
2417 || MiIsUserPde(Address
)
2422 /* Add an additional page table reference */
2423 MiIncrementPageTableReferences(Address
);
2426 /* Is this a guard page? */
2427 if ((ProtectionCode
& MM_PROTECT_SPECIAL
) == MM_GUARDPAGE
)
2429 /* The VAD protection cannot be MM_DECOMMIT! */
2430 ASSERT(ProtectionCode
!= MM_DECOMMIT
);
2432 /* Remove the bit */
2433 TempPte
.u
.Soft
.Protection
= ProtectionCode
& ~MM_GUARDPAGE
;
2434 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2437 ASSERT(ProtoPte
== NULL
);
2438 ASSERT(CurrentThread
->ApcNeeded
== 0);
2440 /* Drop the working set lock */
2441 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2442 ASSERT(KeGetCurrentIrql() == OldIrql
);
2444 /* Handle stack expansion */
2445 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2448 /* Did we get a prototype PTE back? */
2451 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2452 if (PointerPde
== MiAddressToPde(PTE_BASE
))
2454 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2456 _WARN("This is probably completely broken!");
2457 MI_WRITE_INVALID_PDE((PMMPDE
)PointerPte
, DemandZeroPde
);
2459 MI_WRITE_INVALID_PDE(PointerPte
, DemandZeroPde
);
2464 /* No, create a new PTE. First, write the protection */
2465 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2466 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2469 /* Lock the PFN database since we're going to grab a page */
2470 OldIrql
= MiAcquirePfnLock();
2472 /* Make sure we have enough pages */
2473 //ASSERT(MmAvailablePages >= 32);
2475 /* Try to get a zero page */
2476 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
2477 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
2478 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
2479 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
2480 if (!PageFrameIndex
)
2482 /* Grab a page out of there. Later we should grab a colored zero page */
2483 PageFrameIndex
= MiRemoveAnyPage(Color
);
2485 /* Release the lock since we need to do some zeroing */
2486 MiReleasePfnLock(OldIrql
);
2488 if (PageFrameIndex
== 0)
2490 Status
= STATUS_NO_MEMORY
;
2494 /* Zero out the page, since it's for user-mode */
2495 MiZeroPfn(PageFrameIndex
);
2497 /* Grab the lock again so we can initialize the PFN entry */
2498 OldIrql
= MiAcquirePfnLock();
2501 /* Initialize the PFN entry now */
2502 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
2504 /* Increment the count of pages in the process */
2505 CurrentProcess
->NumberOfPrivatePages
++;
2507 /* One more demand-zero fault */
2508 KeGetCurrentPrcb()->MmDemandZeroCount
++;
2510 /* And we're done with the lock */
2511 MiReleasePfnLock(OldIrql
);
2513 /* Fault on user PDE, or fault on user PTE? */
2514 if (PointerPte
<= MiHighestUserPte
)
2516 /* User fault, build a user PTE */
2517 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
2519 PointerPte
->u
.Soft
.Protection
,
2524 /* This is a user-mode PDE, create a kernel PTE for it */
2525 MI_MAKE_HARDWARE_PTE(&TempPte
,
2527 PointerPte
->u
.Soft
.Protection
,
2531 /* Write the dirty bit for writeable pages */
2532 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
2534 /* And now write down the PTE, making the address valid */
2535 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
2536 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
2537 ASSERT(Pfn1
->u1
.Event
== NULL
);
2540 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2541 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2542 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2545 /* We should have a valid protection here */
2546 ASSERT(ProtectionCode
!= 0x100);
2548 /* Write the prototype PTE */
2549 TempPte
= PrototypePte
;
2550 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2551 ASSERT(TempPte
.u
.Long
!= 0);
2552 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2556 /* Get the protection code and check if this is a proto PTE */
2557 ProtectionCode
= (ULONG
)TempPte
.u
.Soft
.Protection
;
2558 if (TempPte
.u
.Soft
.Prototype
)
2560 /* Do we need to go find the real PTE? */
2561 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
2563 /* Get the prototype pte and VAD for it */
2564 ProtoPte
= MiCheckVirtualAddress(Address
,
2569 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2570 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2571 return STATUS_ACCESS_VIOLATION
;
2576 /* Get the prototype PTE! */
2577 ProtoPte
= MiProtoPteToPte(&TempPte
);
2579 /* Is it read-only */
2580 if (TempPte
.u
.Proto
.ReadOnly
)
2582 /* Set read-only code */
2583 ProtectionCode
= MM_READONLY
;
2587 /* Set unknown protection */
2588 ProtectionCode
= 0x100;
2589 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
2595 /* Do we have a valid protection code? */
2596 if (ProtectionCode
!= 0x100)
2598 /* Run a software access check first, including to detect guard pages */
2599 Status
= MiAccessCheck(PointerPte
,
2600 !MI_IS_NOT_PRESENT_FAULT(FaultCode
),
2605 if (Status
!= STATUS_SUCCESS
)
2608 ASSERT(CurrentThread
->ApcNeeded
== 0);
2610 /* Drop the working set lock */
2611 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2612 ASSERT(KeGetCurrentIrql() == OldIrql
);
2614 /* Did we hit a guard page? */
2615 if (Status
== STATUS_GUARD_PAGE_VIOLATION
)
2617 /* Handle stack expansion */
2618 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2621 /* Otherwise, fail back to the caller directly */
2626 /* Dispatch the fault */
2627 Status
= MiDispatchFault(FaultCode
,
2638 /* Return the status */
2639 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2640 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2642 if (Status
== STATUS_NO_MEMORY
)
2644 MmRebalanceMemoryConsumersAndWait();
2653 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
2655 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2656 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2658 *ExecuteOptions
= 0;
2660 if (CurrentProcess
->Flags
.ExecuteDisable
)
2662 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
2665 if (CurrentProcess
->Flags
.ExecuteEnable
)
2667 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
2670 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
2672 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
2675 if (CurrentProcess
->Flags
.Permanent
)
2677 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
2680 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
2682 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
2685 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
2687 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
2690 return STATUS_SUCCESS
;
2695 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
2697 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2698 KLOCK_QUEUE_HANDLE ProcessLock
;
2699 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
2700 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2702 /* Only accept valid flags */
2703 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
2706 DPRINT1("Invalid no-execute options\n");
2707 return STATUS_INVALID_PARAMETER
;
2710 /* Change the NX state in the process lock */
2711 KiAcquireProcessLockRaiseToSynch(CurrentProcess
, &ProcessLock
);
2713 /* Don't change anything if the permanent flag was set */
2714 if (!CurrentProcess
->Flags
.Permanent
)
2716 /* Start by assuming it's not disabled */
2717 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
2719 /* Now process each flag and turn the equivalent bit on */
2720 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
2722 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
2724 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
2726 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
2728 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
2730 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
2732 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
2734 CurrentProcess
->Flags
.Permanent
= TRUE
;
2736 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
2738 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2740 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
2742 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2745 /* These are turned on by default if no-execution is also enabled */
2746 if (CurrentProcess
->Flags
.ExecuteEnable
)
2748 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2749 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2753 Status
= STATUS_SUCCESS
;
2756 /* Release the lock and return status */
2757 KiReleaseProcessLock(&ProcessLock
);