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>
18 /* GLOBALS ********************************************************************/
20 #define HYDRA_PROCESS (PEPROCESS)1
22 BOOLEAN UserPdeFault
= FALSE
;
25 /* PRIVATE FUNCTIONS **********************************************************/
30 MiCheckForUserStackOverflow(IN PVOID Address
,
31 IN PVOID TrapInformation
)
33 PETHREAD CurrentThread
= PsGetCurrentThread();
34 PTEB Teb
= CurrentThread
->Tcb
.Teb
;
35 PVOID StackBase
, DeallocationStack
, NextStackAddress
;
39 /* Do we own the address space lock? */
40 if (CurrentThread
->AddressSpaceOwner
== 1)
42 /* This isn't valid */
43 DPRINT1("Process owns address space lock\n");
44 ASSERT(KeAreAllApcsDisabled() == TRUE
);
45 return STATUS_GUARD_PAGE_VIOLATION
;
48 /* Are we attached? */
49 if (KeIsAttachedProcess())
51 /* This isn't valid */
52 DPRINT1("Process is attached\n");
53 return STATUS_GUARD_PAGE_VIOLATION
;
56 /* Read the current settings */
57 StackBase
= Teb
->NtTib
.StackBase
;
58 DeallocationStack
= Teb
->DeallocationStack
;
59 GuranteedSize
= Teb
->GuaranteedStackBytes
;
60 DPRINT("Handling guard page fault with Stacks Addresses 0x%p and 0x%p, guarantee: %lx\n",
61 StackBase
, DeallocationStack
, GuranteedSize
);
63 /* Guarantees make this code harder, for now, assume there aren't any */
64 ASSERT(GuranteedSize
== 0);
66 /* So allocate only the minimum guard page size */
67 GuranteedSize
= PAGE_SIZE
;
69 /* Does this faulting stack address actually exist in the stack? */
70 if ((Address
>= StackBase
) || (Address
< DeallocationStack
))
73 DPRINT1("Faulting address outside of stack bounds. Address=%p, StackBase=%p, DeallocationStack=%p\n",
74 Address
, StackBase
, DeallocationStack
);
75 return STATUS_GUARD_PAGE_VIOLATION
;
78 /* This is where the stack will start now */
79 NextStackAddress
= (PVOID
)((ULONG_PTR
)PAGE_ALIGN(Address
) - GuranteedSize
);
81 /* Do we have at least one page between here and the end of the stack? */
82 if (((ULONG_PTR
)NextStackAddress
- PAGE_SIZE
) <= (ULONG_PTR
)DeallocationStack
)
84 /* We don't -- Windows would try to make this guard page valid now */
85 DPRINT1("Close to our death...\n");
86 return STATUS_STACK_OVERFLOW
;
89 /* Don't handle this flag yet */
90 ASSERT((PsGetCurrentProcess()->Peb
->NtGlobalFlag
& FLG_DISABLE_STACK_EXTENSION
) == 0);
92 /* Update the stack limit */
93 Teb
->NtTib
.StackLimit
= (PVOID
)((ULONG_PTR
)NextStackAddress
+ GuranteedSize
);
95 /* Now move the guard page to the next page */
96 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
101 PAGE_READWRITE
| PAGE_GUARD
);
102 if ((NT_SUCCESS(Status
) || (Status
== STATUS_ALREADY_COMMITTED
)))
105 DPRINT("Guard page handled successfully for %p\n", Address
);
106 return STATUS_PAGE_FAULT_GUARD_PAGE
;
109 /* Fail, we couldn't move the guard page */
110 DPRINT1("Guard page failure: %lx\n", Status
);
112 return STATUS_STACK_OVERFLOW
;
118 _In_ ULONG ProtectionMask
,
120 _In_ BOOLEAN Execute
)
122 #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
123 (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
124 ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
125 static const UCHAR AccessAllowedMask
[2][2] =
127 { // Protect 0 1 2 3 4 5 6 7
128 _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
129 _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
132 _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
133 _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
137 /* We want only the lower access bits */
138 ProtectionMask
&= MM_PROTECT_ACCESS
;
140 /* Look it up in the table */
141 return (AccessAllowedMask
[Write
!= 0][Execute
!= 0] >> ProtectionMask
) & 1;
147 MiAccessCheck(IN PMMPTE PointerPte
,
148 IN BOOLEAN StoreInstruction
,
149 IN KPROCESSOR_MODE PreviousMode
,
150 IN ULONG_PTR ProtectionMask
,
156 /* Check for invalid user-mode access */
157 if ((PreviousMode
== UserMode
) && (PointerPte
> MiHighestUserPte
))
159 return STATUS_ACCESS_VIOLATION
;
162 /* Capture the PTE -- is it valid? */
163 TempPte
= *PointerPte
;
164 if (TempPte
.u
.Hard
.Valid
)
166 /* Was someone trying to write to it? */
167 if (StoreInstruction
)
170 if (MI_IS_PAGE_WRITEABLE(&TempPte
) ||
171 MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
173 /* Then there's nothing to worry about */
174 return STATUS_SUCCESS
;
177 /* Oops! This isn't allowed */
178 return STATUS_ACCESS_VIOLATION
;
181 /* Someone was trying to read from a valid PTE, that's fine too */
182 return STATUS_SUCCESS
;
185 /* Check if the protection on the page allows what is being attempted */
186 if (!MiIsAccessAllowed(ProtectionMask
, StoreInstruction
, FALSE
))
188 return STATUS_ACCESS_VIOLATION
;
191 /* Check if this is a guard page */
192 if ((ProtectionMask
& MM_PROTECT_SPECIAL
) == MM_GUARDPAGE
)
194 ASSERT(ProtectionMask
!= MM_DECOMMIT
);
196 /* Attached processes can't expand their stack */
197 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION
;
199 /* No support for prototype PTEs yet */
200 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
202 /* Remove the guard page bit, and return a guard page violation */
203 TempPte
.u
.Soft
.Protection
= ProtectionMask
& ~MM_GUARDPAGE
;
204 ASSERT(TempPte
.u
.Long
!= 0);
205 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
206 return STATUS_GUARD_PAGE_VIOLATION
;
210 return STATUS_SUCCESS
;
216 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
217 OUT PULONG ProtectCode
,
218 OUT PMMVAD
*ProtoVad
)
223 /* No prototype/section support for now */
226 /* User or kernel fault? */
227 if (VirtualAddress
<= MM_HIGHEST_USER_ADDRESS
)
229 /* Special case for shared data */
230 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
)
232 /* It's a read-only page */
233 *ProtectCode
= MM_READONLY
;
234 return MmSharedUserDataPte
;
237 /* Find the VAD, it might not exist if the address is bogus */
238 Vad
= MiLocateAddress(VirtualAddress
);
241 /* Bogus virtual address */
242 *ProtectCode
= MM_NOACCESS
;
246 /* ReactOS does not handle physical memory VADs yet */
247 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
249 /* Check if it's a section, or just an allocation */
250 if (Vad
->u
.VadFlags
.PrivateMemory
)
252 /* ReactOS does not handle AWE VADs yet */
253 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
255 /* This must be a TEB/PEB VAD */
256 if (Vad
->u
.VadFlags
.MemCommit
)
258 /* It's committed, so return the VAD protection */
259 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
263 /* It has not yet been committed, so return no access */
264 *ProtectCode
= MM_NOACCESS
;
267 /* In both cases, return no PTE */
272 /* ReactOS does not supoprt these VADs yet */
273 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
274 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
276 /* Return the proto VAD */
279 /* Get the prototype PTE for this page */
280 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
281 ASSERT(PointerPte
!= NULL
);
282 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
284 /* Return the Prototype PTE and the protection for the page mapping */
285 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
289 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
291 /* This should never happen, as these addresses are handled by the double-maping */
292 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
293 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
295 /* Fail such access */
296 *ProtectCode
= MM_NOACCESS
;
300 /* Return full access rights */
301 *ProtectCode
= MM_READWRITE
;
304 else if (MI_IS_SESSION_ADDRESS(VirtualAddress
))
306 /* ReactOS does not have an image list yet, so bail out to failure case */
307 ASSERT(IsListEmpty(&MmSessionSpace
->ImageList
));
310 /* Default case -- failure */
311 *ProtectCode
= MM_NOACCESS
;
315 #if (_MI_PAGING_LEVELS == 2)
319 MiCheckPdeForSessionSpace(IN PVOID Address
)
323 PVOID SessionAddress
;
326 /* Is this a session PTE? */
327 if (MI_IS_SESSION_PTE(Address
))
329 /* Make sure the PDE for session space is valid */
330 PointerPde
= MiAddressToPde(MmSessionSpace
);
331 if (!PointerPde
->u
.Hard
.Valid
)
333 /* This means there's no valid session, bail out */
334 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
337 return STATUS_ACCESS_VIOLATION
;
340 /* Now get the session-specific page table for this address */
341 SessionAddress
= MiPteToAddress(Address
);
342 PointerPde
= MiAddressToPte(Address
);
343 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
345 /* It's not valid, so find it in the page table array */
346 Index
= ((ULONG_PTR
)SessionAddress
- (ULONG_PTR
)MmSessionBase
) >> 22;
347 TempPde
.u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
348 if (TempPde
.u
.Hard
.Valid
)
350 /* The copy is valid, so swap it in */
351 InterlockedExchange((PLONG
)PointerPde
, TempPde
.u
.Long
);
352 return STATUS_WAIT_1
;
355 /* We don't seem to have allocated a page table for this address yet? */
356 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
357 PointerPde
->u
.Long
, SessionAddress
);
359 return STATUS_ACCESS_VIOLATION
;
362 /* Is the address also a session address? If not, we're done */
363 if (!MI_IS_SESSION_ADDRESS(Address
)) return STATUS_SUCCESS
;
365 /* It is, so again get the PDE for session space */
366 PointerPde
= MiAddressToPde(MmSessionSpace
);
367 if (!PointerPde
->u
.Hard
.Valid
)
369 /* This means there's no valid session, bail out */
370 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
373 return STATUS_ACCESS_VIOLATION
;
376 /* Now get the PDE for the address itself */
377 PointerPde
= MiAddressToPde(Address
);
378 if (!PointerPde
->u
.Hard
.Valid
)
380 /* Do the swap, we should be good to go */
381 Index
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MmSessionBase
) >> 22;
382 PointerPde
->u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
383 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
385 /* We had not allocated a page table for this session address yet, fail! */
386 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
387 PointerPde
->u
.Long
, Address
);
389 return STATUS_ACCESS_VIOLATION
;
392 /* It's valid, so there's nothing to do */
393 return STATUS_SUCCESS
;
398 MiCheckPdeForPagedPool(IN PVOID Address
)
401 NTSTATUS Status
= STATUS_SUCCESS
;
403 /* Check session PDE */
404 if (MI_IS_SESSION_ADDRESS(Address
)) return MiCheckPdeForSessionSpace(Address
);
405 if (MI_IS_SESSION_PTE(Address
)) return MiCheckPdeForSessionSpace(Address
);
408 // Check if this is a fault while trying to access the page table itself
410 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
413 // Send a hint to the page fault handler that this is only a valid fault
414 // if we already detected this was access within the page table range
416 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
417 Status
= STATUS_WAIT_1
;
419 else if (Address
< MmSystemRangeStart
)
422 // This is totally illegal
424 return STATUS_ACCESS_VIOLATION
;
429 // Get the PDE for the address
431 PointerPde
= MiAddressToPde(Address
);
435 // Check if it's not valid
437 if (PointerPde
->u
.Hard
.Valid
== 0)
440 // Copy it from our double-mapped system page directory
442 InterlockedExchangePte(PointerPde
,
443 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
454 MiCheckPdeForPagedPool(IN PVOID Address
)
456 return STATUS_ACCESS_VIOLATION
;
462 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
469 /* Get the PFN for this page */
470 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
473 /* Grab a system PTE we can use to zero the page */
474 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
477 /* Initialize the PTE for it */
478 TempPte
= ValidKernelPte
;
479 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
482 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
484 /* Write combining, no caching */
485 MI_PAGE_DISABLE_CACHE(&TempPte
);
486 MI_PAGE_WRITE_COMBINED(&TempPte
);
488 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
490 /* Write through, no caching */
491 MI_PAGE_DISABLE_CACHE(&TempPte
);
492 MI_PAGE_WRITE_THROUGH(&TempPte
);
495 /* Make the system PTE valid with our PFN */
496 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
498 /* Get the address it maps to, and zero it out */
499 ZeroAddress
= MiPteToAddress(ZeroPte
);
500 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
502 /* Now get rid of it */
503 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
509 _In_ PFN_NUMBER DestPage
,
510 _In_ PFN_NUMBER SrcPage
)
514 PMMPFN DestPfn
, SrcPfn
;
516 const VOID
* SrcAddress
;
519 DestPfn
= MiGetPfnEntry(DestPage
);
521 SrcPfn
= MiGetPfnEntry(SrcPage
);
524 /* Grab 2 system PTEs */
525 SysPtes
= MiReserveSystemPtes(2, SystemPteSpace
);
528 /* Initialize the destination PTE */
529 TempPte
= ValidKernelPte
;
530 TempPte
.u
.Hard
.PageFrameNumber
= DestPage
;
533 if (DestPfn
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
535 /* Write combining, no caching */
536 MI_PAGE_DISABLE_CACHE(&TempPte
);
537 MI_PAGE_WRITE_COMBINED(&TempPte
);
539 else if (DestPfn
->u3
.e1
.CacheAttribute
== MiNonCached
)
541 /* Write through, no caching */
542 MI_PAGE_DISABLE_CACHE(&TempPte
);
543 MI_PAGE_WRITE_THROUGH(&TempPte
);
546 /* Make the system PTE valid with our PFN */
547 MI_WRITE_VALID_PTE(&SysPtes
[0], TempPte
);
549 /* Initialize the source PTE */
550 TempPte
= ValidKernelPte
;
551 TempPte
.u
.Hard
.PageFrameNumber
= SrcPage
;
554 if (SrcPfn
->u3
.e1
.CacheAttribute
== MiNonCached
)
556 MI_PAGE_DISABLE_CACHE(&TempPte
);
559 /* Make the system PTE valid with our PFN */
560 MI_WRITE_VALID_PTE(&SysPtes
[1], TempPte
);
562 /* Get the addresses and perform the copy */
563 DestAddress
= MiPteToAddress(&SysPtes
[0]);
564 SrcAddress
= MiPteToAddress(&SysPtes
[1]);
565 RtlCopyMemory(DestAddress
, SrcAddress
, PAGE_SIZE
);
567 /* Now get rid of it */
568 MiReleaseSystemPtes(SysPtes
, 2, SystemPteSpace
);
574 MiResolveDemandZeroFault(IN PVOID Address
,
575 IN PMMPTE PointerPte
,
577 IN PEPROCESS Process
,
580 PFN_NUMBER PageFrameNumber
= 0;
582 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
585 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
589 /* Must currently only be called by paging path */
590 if ((Process
> HYDRA_PROCESS
) && (OldIrql
== MM_NOIRQL
))
593 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
596 ASSERT(Process
->ForkInProgress
== NULL
);
598 /* Get process color */
599 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
600 ASSERT(Color
!= 0xFFFFFFFF);
602 /* We'll need a zero page */
607 /* Check if we need a zero page */
608 NeedZero
= (OldIrql
!= MM_NOIRQL
);
610 /* Session-backed image views must be zeroed */
611 if ((Process
== HYDRA_PROCESS
) &&
612 ((MI_IS_SESSION_IMAGE_ADDRESS(Address
)) ||
613 ((Address
>= MiSessionViewStart
) && (Address
< MiSessionSpaceWs
))))
618 /* Hardcode unknown color */
622 /* Check if the PFN database should be acquired */
623 if (OldIrql
== MM_NOIRQL
)
625 /* Acquire it and remember we should release it after */
626 OldIrql
= MiAcquirePfnLock();
630 /* We either manually locked the PFN DB, or already came with it locked */
631 MI_ASSERT_PFN_LOCK_HELD();
632 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
634 /* Assert we have enough pages */
635 ASSERT(MmAvailablePages
>= 32);
638 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
639 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
641 if (Process
== HYDRA_PROCESS
) MI_SET_PROCESS2("Hydra");
642 else if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
643 else MI_SET_PROCESS2("Kernel Demand 0");
645 /* Do we need a zero page? */
646 if (Color
!= 0xFFFFFFFF)
648 /* Try to get one, if we couldn't grab a free page and zero it */
649 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
650 if (!PageFrameNumber
)
652 /* We'll need a free page and zero it manually */
653 PageFrameNumber
= MiRemoveAnyPage(Color
);
659 /* Get a color, and see if we should grab a zero or non-zero page */
660 Color
= MI_GET_NEXT_COLOR();
663 /* Process or system doesn't want a zero page, grab anything */
664 PageFrameNumber
= MiRemoveAnyPage(Color
);
668 /* System wants a zero page, obtain one */
669 PageFrameNumber
= MiRemoveZeroPage(Color
);
674 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
676 /* Increment demand zero faults */
677 KeGetCurrentPrcb()->MmDemandZeroCount
++;
679 /* Do we have the lock? */
683 MiReleasePfnLock(OldIrql
);
685 /* Update performance counters */
686 if (Process
> HYDRA_PROCESS
) Process
->NumberOfPrivatePages
++;
689 /* Zero the page if need be */
690 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
692 /* Fault on user PDE, or fault on user PTE? */
693 if (PointerPte
<= MiHighestUserPte
)
695 /* User fault, build a user PTE */
696 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
703 /* This is a user-mode PDE, create a kernel PTE for it */
704 MI_MAKE_HARDWARE_PTE(&TempPte
,
710 /* Set it dirty if it's a writable page */
711 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
714 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
716 /* Did we manually acquire the lock */
719 /* Get the PFN entry */
720 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
722 /* Windows does these sanity checks */
723 ASSERT(Pfn1
->u1
.Event
== 0);
724 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
730 DPRINT("Demand zero page has now been paged in\n");
731 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
737 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
739 IN PMMPTE PointerPte
,
740 IN PMMPTE PointerProtoPte
,
742 IN PMMPFN
* LockedProtoPfn
)
745 PMMPTE OriginalPte
, PageTablePte
;
746 ULONG_PTR Protection
;
747 PFN_NUMBER PageFrameIndex
;
749 BOOLEAN OriginalProtection
, DirtyPage
;
751 /* Must be called with an valid prototype PTE, with the PFN lock held */
752 MI_ASSERT_PFN_LOCK_HELD();
753 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
756 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
758 /* Get the PFN entry and set it as a prototype PTE */
759 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
760 Pfn1
->u3
.e1
.PrototypePte
= 1;
762 /* Increment the share count for the page table */
763 PageTablePte
= MiAddressToPte(PointerPte
);
764 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
765 Pfn2
->u2
.ShareCount
++;
767 /* Check where we should be getting the protection information from */
768 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
770 /* Get the protection from the PTE, there's no real Proto PTE data */
771 Protection
= PointerPte
->u
.Soft
.Protection
;
773 /* Remember that we did not use the proto protection */
774 OriginalProtection
= FALSE
;
778 /* Get the protection from the original PTE link */
779 OriginalPte
= &Pfn1
->OriginalPte
;
780 Protection
= OriginalPte
->u
.Soft
.Protection
;
782 /* Remember that we used the original protection */
783 OriginalProtection
= TRUE
;
785 /* Check if this was a write on a read only proto */
786 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
789 StoreInstruction
= 0;
793 /* Check if this was a write on a non-COW page */
795 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
797 /* Then the page should be marked dirty */
801 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
804 /* Did we get a locked incoming PFN? */
807 /* Drop a reference */
808 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
809 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
810 *LockedProtoPfn
= NULL
;
813 /* Release the PFN lock */
814 MiReleasePfnLock(OldIrql
);
816 /* Remove special/caching bits */
817 Protection
&= ~MM_PROTECT_SPECIAL
;
820 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
822 /* Write combining, no caching */
823 MI_PAGE_DISABLE_CACHE(&TempPte
);
824 MI_PAGE_WRITE_COMBINED(&TempPte
);
826 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
828 /* Write through, no caching */
829 MI_PAGE_DISABLE_CACHE(&TempPte
);
830 MI_PAGE_WRITE_THROUGH(&TempPte
);
833 /* Check if this is a kernel or user address */
834 if (Address
< MmSystemRangeStart
)
836 /* Build the user PTE */
837 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
841 /* Build the kernel PTE */
842 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
845 /* Set the dirty flag if needed */
846 if (DirtyPage
) MI_MAKE_DIRTY_PAGE(&TempPte
);
849 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
851 /* Reset the protection if needed */
852 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
855 ASSERT(PointerPte
== MiAddressToPte(Address
));
856 return STATUS_SUCCESS
;
862 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction
,
863 _In_ PVOID FaultingAddress
,
864 _In_ PMMPTE PointerPte
,
865 _In_ PEPROCESS CurrentProcess
,
866 _Inout_ KIRQL
*OldIrql
)
871 MMPTE TempPte
= *PointerPte
;
873 ULONG PageFileIndex
= TempPte
.u
.Soft
.PageFileLow
;
874 ULONG_PTR PageFileOffset
= TempPte
.u
.Soft
.PageFileHigh
;
875 ULONG Protection
= TempPte
.u
.Soft
.Protection
;
877 /* Things we don't support yet */
878 ASSERT(CurrentProcess
> HYDRA_PROCESS
);
879 ASSERT(*OldIrql
!= MM_NOIRQL
);
881 /* We must hold the PFN lock */
882 MI_ASSERT_PFN_LOCK_HELD();
884 /* Some sanity checks */
885 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
886 ASSERT(TempPte
.u
.Soft
.PageFileHigh
!= 0);
887 ASSERT(TempPte
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
);
889 /* Get any page, it will be overwritten */
890 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
891 Page
= MiRemoveAnyPage(Color
);
893 /* Initialize this PFN */
894 MiInitializePfn(Page
, PointerPte
, StoreInstruction
);
896 /* Sets the PFN as being in IO operation */
897 Pfn1
= MI_PFN_ELEMENT(Page
);
898 ASSERT(Pfn1
->u1
.Event
== NULL
);
899 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
900 ASSERT(Pfn1
->u3
.e1
.WriteInProgress
== 0);
901 Pfn1
->u3
.e1
.ReadInProgress
= 1;
903 /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
904 MI_MAKE_TRANSITION_PTE(&TempPte
, Page
, Protection
);
906 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
908 /* Release the PFN lock while we proceed */
909 MiReleasePfnLock(*OldIrql
);
911 /* Do the paging IO */
912 Status
= MiReadPageFile(Page
, PageFileIndex
, PageFileOffset
);
914 /* Lock the PFN database again */
915 *OldIrql
= MiAcquirePfnLock();
917 /* Nobody should have changed that while we were not looking */
918 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 1);
919 ASSERT(Pfn1
->u3
.e1
.WriteInProgress
== 0);
921 if (!NT_SUCCESS(Status
))
925 Pfn1
->u4
.InPageError
= 1;
926 Pfn1
->u1
.ReadStatus
= Status
;
929 /* And the PTE can finally be valid */
930 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, Page
);
931 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
933 Pfn1
->u3
.e1
.ReadInProgress
= 0;
934 /* Did someone start to wait on us while we proceeded ? */
937 /* Tell them we're done */
938 KeSetEvent(Pfn1
->u1
.Event
, IO_NO_INCREMENT
, FALSE
);
947 MiResolveTransitionFault(IN BOOLEAN StoreInstruction
,
948 IN PVOID FaultingAddress
,
949 IN PMMPTE PointerPte
,
950 IN PEPROCESS CurrentProcess
,
952 OUT PKEVENT
**InPageBlock
)
954 PFN_NUMBER PageFrameIndex
;
957 PMMPTE PointerToPteForProtoPage
;
958 DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
959 FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
961 /* Windowss does this check */
962 ASSERT(*InPageBlock
== NULL
);
964 /* ARM3 doesn't support this path */
965 ASSERT(OldIrql
!= MM_NOIRQL
);
967 /* Capture the PTE and make sure it's in transition format */
968 TempPte
= *PointerPte
;
969 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
970 (TempPte
.u
.Soft
.Prototype
== 0) &&
971 (TempPte
.u
.Soft
.Transition
== 1));
973 /* Get the PFN and the PFN entry */
974 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
975 DPRINT("Transition PFN: %lx\n", PageFrameIndex
);
976 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
978 /* One more transition fault! */
979 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
981 /* This is from ARM3 -- Windows normally handles this here */
982 ASSERT(Pfn1
->u4
.InPageError
== 0);
984 /* See if we should wait before terminating the fault */
985 if ((Pfn1
->u3
.e1
.ReadInProgress
== 1)
986 || ((Pfn1
->u3
.e1
.WriteInProgress
== 1) && StoreInstruction
))
988 DPRINT1("The page is currently in a page transition !\n");
989 *InPageBlock
= &Pfn1
->u1
.Event
;
990 if (PointerPte
== Pfn1
->PteAddress
)
992 DPRINT1("And this if for this particular PTE.\n");
993 /* The PTE will be made valid by the thread serving the fault */
994 return STATUS_SUCCESS
; // FIXME: Maybe something more descriptive
998 /* Windows checks there's some free pages and this isn't an in-page error */
999 ASSERT(MmAvailablePages
> 0);
1000 ASSERT(Pfn1
->u4
.InPageError
== 0);
1002 /* ReactOS checks for this */
1003 ASSERT(MmAvailablePages
> 32);
1005 /* Was this a transition page in the valid list, or free/zero list? */
1006 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
1008 /* All Windows does here is a bunch of sanity checks */
1009 DPRINT("Transition in active list\n");
1010 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
1011 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
1012 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
1013 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
1017 /* Otherwise, the page is removed from its list */
1018 DPRINT("Transition page in free/zero list\n");
1019 MiUnlinkPageFromList(Pfn1
);
1020 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
1023 /* At this point, there should no longer be any in-page errors */
1024 ASSERT(Pfn1
->u4
.InPageError
== 0);
1026 /* Check if this was a PFN with no more share references */
1027 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
1029 /* Bump the share count and make the page valid */
1030 Pfn1
->u2
.ShareCount
++;
1031 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1033 /* Prototype PTEs are in paged pool, which itself might be in transition */
1034 if (FaultingAddress
>= MmSystemRangeStart
)
1036 /* Check if this is a paged pool PTE in transition state */
1037 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
1038 TempPte
= *PointerToPteForProtoPage
;
1039 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
1041 /* This isn't yet supported */
1042 DPRINT1("Double transition fault not yet supported\n");
1047 /* Build the final PTE */
1048 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1049 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
1050 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
1051 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
1052 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
1053 MiDetermineUserGlobalPteMask(PointerPte
);
1055 /* Is the PTE writeable? */
1056 if ((Pfn1
->u3
.e1
.Modified
) &&
1057 MI_IS_PAGE_WRITEABLE(&TempPte
) &&
1058 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1061 MI_MAKE_DIRTY_PAGE(&TempPte
);
1066 MI_MAKE_CLEAN_PAGE(&TempPte
);
1069 /* Write the valid PTE */
1070 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1072 /* Return success */
1073 return STATUS_PAGE_FAULT_TRANSITION
;
1079 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
1081 IN PMMPTE PointerPte
,
1082 IN PMMPTE PointerProtoPte
,
1083 IN OUT PMMPFN
*OutPfn
,
1084 OUT PVOID
*PageFileData
,
1085 OUT PMMPTE PteValue
,
1086 IN PEPROCESS Process
,
1088 IN PVOID TrapInformation
)
1090 MMPTE TempPte
, PteContents
;
1092 PFN_NUMBER PageFrameIndex
;
1094 PKEVENT
* InPageBlock
= NULL
;
1097 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
1098 MI_ASSERT_PFN_LOCK_HELD();
1099 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1100 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
1102 /* Read the prototype PTE and check if it's valid */
1103 TempPte
= *PointerProtoPte
;
1104 if (TempPte
.u
.Hard
.Valid
== 1)
1106 /* One more user of this mapped page */
1107 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1108 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1109 Pfn1
->u2
.ShareCount
++;
1111 /* Call it a transition */
1112 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
1114 /* Complete the prototype PTE fault -- this will release the PFN lock */
1115 return MiCompleteProtoPteFault(StoreInstruction
,
1123 /* Make sure there's some protection mask */
1124 if (TempPte
.u
.Long
== 0)
1126 /* Release the lock */
1127 DPRINT1("Access on reserved section?\n");
1128 MiReleasePfnLock(OldIrql
);
1129 return STATUS_ACCESS_VIOLATION
;
1132 /* There is no such thing as a decommitted prototype PTE */
1133 ASSERT(TempPte
.u
.Long
!= MmDecommittedPte
.u
.Long
);
1135 /* Check for access rights on the PTE proper */
1136 PteContents
= *PointerPte
;
1137 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
1139 if (!PteContents
.u
.Proto
.ReadOnly
)
1141 Protection
= TempPte
.u
.Soft
.Protection
;
1145 Protection
= MM_READONLY
;
1147 /* Check for page acess in software */
1148 Status
= MiAccessCheck(PointerProtoPte
,
1151 TempPte
.u
.Soft
.Protection
,
1154 ASSERT(Status
== STATUS_SUCCESS
);
1158 Protection
= PteContents
.u
.Soft
.Protection
;
1161 /* Check for writing copy on write page */
1162 if (((Protection
& MM_WRITECOPY
) == MM_WRITECOPY
) && StoreInstruction
)
1164 PFN_NUMBER PageFrameIndex
, ProtoPageFrameIndex
;
1167 /* Resolve the proto fault as if it was a read operation */
1168 Status
= MiResolveProtoPteFault(FALSE
,
1179 if (!NT_SUCCESS(Status
))
1184 /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
1185 OldIrql
= MiAcquirePfnLock();
1187 /* And re-read the proto PTE */
1188 TempPte
= *PointerProtoPte
;
1189 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
1190 ProtoPageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1192 /* Get a new page for the private copy */
1193 if (Process
> HYDRA_PROCESS
)
1194 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1196 Color
= MI_GET_NEXT_COLOR();
1198 PageFrameIndex
= MiRemoveAnyPage(Color
);
1200 /* Perform the copy */
1201 MiCopyPfn(PageFrameIndex
, ProtoPageFrameIndex
);
1203 /* This will drop everything MiResolveProtoPteFault referenced */
1204 MiDeletePte(PointerPte
, Address
, Process
, PointerProtoPte
);
1206 /* Because now we use this */
1207 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1208 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
1210 /* Fix the protection */
1211 Protection
&= ~MM_WRITECOPY
;
1212 Protection
|= MM_READWRITE
;
1213 if (Address
< MmSystemRangeStart
)
1215 /* Build the user PTE */
1216 MI_MAKE_HARDWARE_PTE_USER(&PteContents
, PointerPte
, Protection
, PageFrameIndex
);
1220 /* Build the kernel PTE */
1221 MI_MAKE_HARDWARE_PTE(&PteContents
, PointerPte
, Protection
, PageFrameIndex
);
1224 /* And finally, write the valid PTE */
1225 MI_WRITE_VALID_PTE(PointerPte
, PteContents
);
1227 /* The caller expects us to release the PFN lock */
1228 MiReleasePfnLock(OldIrql
);
1232 /* Check for clone PTEs */
1233 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
1235 /* We don't support mapped files yet */
1236 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1238 /* We might however have transition PTEs */
1239 if (TempPte
.u
.Soft
.Transition
== 1)
1241 /* Resolve the transition fault */
1242 ASSERT(OldIrql
!= MM_NOIRQL
);
1243 Status
= MiResolveTransitionFault(StoreInstruction
,
1249 ASSERT(NT_SUCCESS(Status
));
1253 /* We also don't support paged out pages */
1254 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1256 /* Resolve the demand zero fault */
1257 Status
= MiResolveDemandZeroFault(Address
,
1259 (ULONG
)TempPte
.u
.Soft
.Protection
,
1262 ASSERT(NT_SUCCESS(Status
));
1265 /* Complete the prototype PTE fault -- this will release the PFN lock */
1266 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1267 return MiCompleteProtoPteFault(StoreInstruction
,
1277 MiDispatchFault(IN ULONG FaultCode
,
1279 IN PMMPTE PointerPte
,
1280 IN PMMPTE PointerProtoPte
,
1281 IN BOOLEAN Recursive
,
1282 IN PEPROCESS Process
,
1283 IN PVOID TrapInformation
,
1287 KIRQL OldIrql
, LockIrql
;
1289 PMMPTE SuperProtoPte
;
1290 PMMPFN Pfn1
, OutPfn
= NULL
;
1291 PFN_NUMBER PageFrameIndex
;
1292 PFN_COUNT PteCount
, ProcessedPtes
;
1293 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1297 /* Make sure the addresses are ok */
1298 ASSERT(PointerPte
== MiAddressToPte(Address
));
1301 // Make sure APCs are off and we're not at dispatch
1303 OldIrql
= KeGetCurrentIrql();
1304 ASSERT(OldIrql
<= APC_LEVEL
);
1305 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1308 // Grab a copy of the PTE
1310 TempPte
= *PointerPte
;
1312 /* Do we have a prototype PTE? */
1313 if (PointerProtoPte
)
1315 /* This should never happen */
1316 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
1318 /* Check if this is a kernel-mode address */
1319 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
1320 if (Address
>= MmSystemRangeStart
)
1322 /* Lock the PFN database */
1323 LockIrql
= MiAcquirePfnLock();
1325 /* Has the PTE been made valid yet? */
1326 if (!SuperProtoPte
->u
.Hard
.Valid
)
1330 else if (PointerPte
->u
.Hard
.Valid
== 1)
1335 /* Resolve the fault -- this will release the PFN lock */
1336 Status
= MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
),
1346 ASSERT(Status
== STATUS_SUCCESS
);
1348 /* Complete this as a transition fault */
1349 ASSERT(OldIrql
== KeGetCurrentIrql());
1350 ASSERT(OldIrql
<= APC_LEVEL
);
1351 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1356 /* We only handle the lookup path */
1357 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
1359 /* Is there a non-image VAD? */
1361 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
1362 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
1364 /* One day, ReactOS will cluster faults */
1365 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
1366 DPRINT("Should cluster fault, but won't\n");
1369 /* Only one PTE to handle for now */
1373 /* Lock the PFN database */
1374 LockIrql
= MiAcquirePfnLock();
1376 /* We only handle the valid path */
1377 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
1379 /* Capture the PTE */
1380 TempPte
= *PointerProtoPte
;
1382 /* Loop to handle future case of clustered faults */
1385 /* For our current usage, this should be true */
1386 if (TempPte
.u
.Hard
.Valid
== 1)
1388 /* Bump the share count on the PTE */
1389 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1390 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1391 Pfn1
->u2
.ShareCount
++;
1393 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
1394 (TempPte
.u
.Soft
.Transition
== 1))
1396 /* This is a standby page, bring it back from the cache */
1397 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
1398 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
1399 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1400 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
1402 /* Should not yet happen in ReactOS */
1403 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
1404 ASSERT(Pfn1
->u4
.InPageError
== 0);
1407 MiUnlinkPageFromList(Pfn1
);
1409 /* Bump its reference count */
1410 ASSERT(Pfn1
->u2
.ShareCount
== 0);
1411 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1412 Pfn1
->u2
.ShareCount
++;
1414 /* Make it valid again */
1415 /* This looks like another macro.... */
1416 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1417 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1418 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1419 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1420 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1421 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1422 TempPte
.u
.Hard
.Valid
= 1;
1423 MI_MAKE_ACCESSED_PAGE(&TempPte
);
1425 /* Is the PTE writeable? */
1426 if ((Pfn1
->u3
.e1
.Modified
) &&
1427 MI_IS_PAGE_WRITEABLE(&TempPte
) &&
1428 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1431 MI_MAKE_DIRTY_PAGE(&TempPte
);
1436 MI_MAKE_CLEAN_PAGE(&TempPte
);
1439 /* Write the valid PTE */
1440 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1441 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1445 /* Page is invalid, get out of the loop */
1449 /* One more done, was it the last? */
1450 if (++ProcessedPtes
== PteCount
)
1452 /* Complete the fault */
1453 MiCompleteProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
),
1460 /* THIS RELEASES THE PFN LOCK! */
1464 /* No clustered faults yet */
1468 /* Did we resolve the fault? */
1471 /* Bump the transition count */
1472 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1475 /* Loop all the processing we did */
1476 ASSERT(ProcessedPtes
== 0);
1478 /* Complete this as a transition fault */
1479 ASSERT(OldIrql
== KeGetCurrentIrql());
1480 ASSERT(OldIrql
<= APC_LEVEL
);
1481 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1482 return STATUS_PAGE_FAULT_TRANSITION
;
1485 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1486 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1487 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1488 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1489 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1491 /* Resolve the fault -- this will release the PFN lock */
1492 Status
= MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
),
1502 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1503 //ASSERT(Status != STATUS_REFAULT);
1504 //ASSERT(Status != STATUS_PTE_CHANGED);
1506 /* Did the routine clean out the PFN or should we? */
1509 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1510 ASSERT(PointerProtoPte
!= NULL
);
1511 OldIrql
= MiAcquirePfnLock();
1513 /* Dereference the locked PFN */
1514 MiDereferencePfnAndDropLockCount(OutPfn
);
1515 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1517 /* And now release the lock */
1518 MiReleasePfnLock(OldIrql
);
1521 /* Complete this as a transition fault */
1522 ASSERT(OldIrql
== KeGetCurrentIrql());
1523 ASSERT(OldIrql
<= APC_LEVEL
);
1524 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1529 /* Is this a transition PTE */
1530 if (TempPte
.u
.Soft
.Transition
)
1532 PKEVENT
* InPageBlock
= NULL
;
1533 PKEVENT PreviousPageEvent
;
1534 KEVENT CurrentPageEvent
;
1536 /* Lock the PFN database */
1537 LockIrql
= MiAcquirePfnLock();
1540 Status
= MiResolveTransitionFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
), Address
, PointerPte
, Process
, LockIrql
, &InPageBlock
);
1542 ASSERT(NT_SUCCESS(Status
));
1544 if (InPageBlock
!= NULL
)
1546 /* Another thread is reading or writing this page. Put us into the waiting queue. */
1547 KeInitializeEvent(&CurrentPageEvent
, NotificationEvent
, FALSE
);
1548 PreviousPageEvent
= *InPageBlock
;
1549 *InPageBlock
= &CurrentPageEvent
;
1552 /* And now release the lock and leave*/
1553 MiReleasePfnLock(LockIrql
);
1555 if (InPageBlock
!= NULL
)
1557 KeWaitForSingleObject(&CurrentPageEvent
, WrPageIn
, KernelMode
, FALSE
, NULL
);
1559 /* Let's the chain go on */
1560 if (PreviousPageEvent
)
1562 KeSetEvent(PreviousPageEvent
, IO_NO_INCREMENT
, FALSE
);
1566 ASSERT(OldIrql
== KeGetCurrentIrql());
1567 ASSERT(OldIrql
<= APC_LEVEL
);
1568 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1572 /* Should we page the data back in ? */
1573 if (TempPte
.u
.Soft
.PageFileHigh
!= 0)
1575 /* Lock the PFN database */
1576 LockIrql
= MiAcquirePfnLock();
1579 Status
= MiResolvePageFileFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode
), Address
, PointerPte
, Process
, &LockIrql
);
1581 /* And now release the lock and leave*/
1582 MiReleasePfnLock(LockIrql
);
1584 ASSERT(OldIrql
== KeGetCurrentIrql());
1585 ASSERT(OldIrql
<= APC_LEVEL
);
1586 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1591 // The PTE must be invalid but not completely empty. It must also not be a
1592 // prototype a transition or a paged-out PTE as those scenarii should've been handled above.
1593 // These are all Windows checks
1595 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1596 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1597 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1598 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1599 ASSERT(TempPte
.u
.Long
!= 0);
1602 // If we got this far, the PTE can only be a demand zero PTE, which is what
1603 // we want. Go handle it!
1605 Status
= MiResolveDemandZeroFault(Address
,
1607 (ULONG
)TempPte
.u
.Soft
.Protection
,
1610 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1611 if (NT_SUCCESS(Status
))
1614 // Make sure we're returning in a sane state and pass the status down
1616 ASSERT(OldIrql
== KeGetCurrentIrql());
1617 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1622 // Generate an access fault
1624 return STATUS_ACCESS_VIOLATION
;
1629 MmArmAccessFault(IN ULONG FaultCode
,
1631 IN KPROCESSOR_MODE Mode
,
1632 IN PVOID TrapInformation
)
1634 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1635 PMMPTE ProtoPte
= NULL
;
1636 PMMPTE PointerPte
= MiAddressToPte(Address
);
1637 PMMPDE PointerPde
= MiAddressToPde(Address
);
1638 #if (_MI_PAGING_LEVELS >= 3)
1639 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1640 #if (_MI_PAGING_LEVELS == 4)
1641 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1645 PETHREAD CurrentThread
;
1646 PEPROCESS CurrentProcess
;
1648 PMMSUPPORT WorkingSet
;
1649 ULONG ProtectionCode
;
1651 PFN_NUMBER PageFrameIndex
;
1653 BOOLEAN IsSessionAddress
;
1655 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1657 /* Check for page fault on high IRQL */
1658 if (OldIrql
> APC_LEVEL
)
1660 #if (_MI_PAGING_LEVELS < 3)
1661 /* Could be a page table for paged pool, which we'll allow */
1662 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1663 MiCheckPdeForPagedPool(Address
);
1665 /* Check if any of the top-level pages are invalid */
1667 #if (_MI_PAGING_LEVELS == 4)
1668 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1670 #if (_MI_PAGING_LEVELS >= 3)
1671 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1673 (PointerPde
->u
.Hard
.Valid
== 0) ||
1674 (PointerPte
->u
.Hard
.Valid
== 0))
1676 /* This fault is not valid, print out some debugging help */
1677 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1680 if (TrapInformation
)
1682 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1684 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1685 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1686 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1687 #elif defined(_M_AMD64)
1688 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1689 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1690 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1691 #elif defined(_M_ARM)
1692 DbgPrint("MM:***PC %p\n", TrapFrame
->Pc
);
1693 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame
->R0
, TrapFrame
->R1
, TrapFrame
->R2
, TrapFrame
->R3
);
1694 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame
->R11
, TrapFrame
->R12
, TrapFrame
->Sp
, TrapFrame
->Lr
);
1698 /* Tell the trap handler to fail */
1699 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1702 /* Not yet implemented in ReactOS */
1703 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1704 ASSERT((!MI_IS_NOT_PRESENT_FAULT(FaultCode
) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte
)) == FALSE
);
1706 /* Check if this was a write */
1707 if (MI_IS_WRITE_ACCESS(FaultCode
))
1709 /* Was it to a read-only page? */
1710 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1711 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1712 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1714 /* Crash with distinguished bugcheck code */
1715 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1718 (ULONG_PTR
)TrapInformation
,
1723 /* Nothing is actually wrong */
1724 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql
, Address
);
1725 return STATUS_SUCCESS
;
1728 /* Check for kernel fault address */
1729 if (Address
>= MmSystemRangeStart
)
1731 /* Bail out, if the fault came from user mode */
1732 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1734 #if (_MI_PAGING_LEVELS == 2)
1735 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1736 MiCheckPdeForPagedPool(Address
);
1739 /* Check if the higher page table entries are invalid */
1741 #if (_MI_PAGING_LEVELS == 4)
1742 /* AMD64 system, check if PXE is invalid */
1743 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1745 #if (_MI_PAGING_LEVELS >= 3)
1746 /* PAE/AMD64 system, check if PPE is invalid */
1747 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1749 /* Always check if the PDE is valid */
1750 (PointerPde
->u
.Hard
.Valid
== 0))
1752 /* PXE/PPE/PDE (still) not valid, kill the system */
1753 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1756 (ULONG_PTR
)TrapInformation
,
1760 /* Not handling session faults yet */
1761 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1763 /* The PDE is valid, so read the PTE */
1764 TempPte
= *PointerPte
;
1765 if (TempPte
.u
.Hard
.Valid
== 1)
1767 /* Check if this was system space or session space */
1768 if (!IsSessionAddress
)
1770 /* Check if the PTE is still valid under PFN lock */
1771 OldIrql
= MiAcquirePfnLock();
1772 TempPte
= *PointerPte
;
1773 if (TempPte
.u
.Hard
.Valid
)
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 /* Check for execution of non-executable memory */
1793 if (MI_IS_INSTRUCTION_FETCH(FaultCode
) &&
1794 !MI_IS_PAGE_EXECUTABLE(&TempPte
))
1796 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY
,
1798 (ULONG_PTR
)TempPte
.u
.Long
,
1799 (ULONG_PTR
)TrapInformation
,
1804 /* Release PFN lock and return all good */
1805 MiReleasePfnLock(OldIrql
);
1806 return STATUS_SUCCESS
;
1809 #if (_MI_PAGING_LEVELS == 2)
1810 /* Check if this was a session PTE that needs to remap the session PDE */
1811 if (MI_IS_SESSION_PTE(Address
))
1813 /* Do the remapping */
1814 Status
= MiCheckPdeForSessionSpace(Address
);
1815 if (!NT_SUCCESS(Status
))
1817 /* It failed, this address is invalid */
1818 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1821 (ULONG_PTR
)TrapInformation
,
1827 _WARN("Session space stuff is not implemented yet!")
1831 /* Check for a fault on the page table or hyperspace */
1832 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1834 #if (_MI_PAGING_LEVELS < 3)
1835 /* Windows does this check but I don't understand why -- it's done above! */
1836 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1838 /* Handle this as a user mode fault */
1842 /* Get the current thread */
1843 CurrentThread
= PsGetCurrentThread();
1845 /* What kind of address is this */
1846 if (!IsSessionAddress
)
1848 /* Use the system working set */
1849 WorkingSet
= &MmSystemCacheWs
;
1850 CurrentProcess
= NULL
;
1852 /* Make sure we don't have a recursive working set lock */
1853 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1854 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1855 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1856 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1857 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1858 (CurrentThread
->OwnsSessionWorkingSetShared
))
1861 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1866 /* Use the session process and working set */
1867 CurrentProcess
= HYDRA_PROCESS
;
1868 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1870 /* Make sure we don't have a recursive working set lock */
1871 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1872 (CurrentThread
->OwnsSessionWorkingSetShared
))
1875 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1879 /* Acquire the working set lock */
1880 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1881 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1883 /* Re-read PTE now that we own the lock */
1884 TempPte
= *PointerPte
;
1885 if (TempPte
.u
.Hard
.Valid
== 1)
1887 /* Check if this was a write */
1888 if (MI_IS_WRITE_ACCESS(FaultCode
))
1890 /* Was it to a read-only page that is not copy on write? */
1891 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1892 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1893 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1894 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1896 /* Case not yet handled */
1897 ASSERT(!IsSessionAddress
);
1899 /* Crash with distinguished bugcheck code */
1900 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1903 (ULONG_PTR
)TrapInformation
,
1908 /* Check for execution of non-executable memory */
1909 if (MI_IS_INSTRUCTION_FETCH(FaultCode
) &&
1910 !MI_IS_PAGE_EXECUTABLE(&TempPte
))
1912 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY
,
1914 (ULONG_PTR
)TempPte
.u
.Long
,
1915 (ULONG_PTR
)TrapInformation
,
1919 /* Check for read-only write in session space */
1920 if ((IsSessionAddress
) &&
1921 MI_IS_WRITE_ACCESS(FaultCode
) &&
1922 !MI_IS_PAGE_WRITEABLE(&TempPte
))
1925 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1928 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1930 /* Then this is not allowed */
1931 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1933 (ULONG_PTR
)TempPte
.u
.Long
,
1934 (ULONG_PTR
)TrapInformation
,
1938 /* Otherwise, handle COW */
1942 /* Release the working set */
1943 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1944 KeLowerIrql(LockIrql
);
1946 /* Otherwise, the PDE was probably invalid, and all is good now */
1947 return STATUS_SUCCESS
;
1950 /* Check one kind of prototype PTE */
1951 if (TempPte
.u
.Soft
.Prototype
)
1953 /* Make sure protected pool is on, and that this is a pool address */
1954 if ((MmProtectFreedNonPagedPool
) &&
1955 (((Address
>= MmNonPagedPoolStart
) &&
1956 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1957 MmSizeOfNonPagedPoolInBytes
))) ||
1958 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1959 (Address
< MmNonPagedPoolEnd
))))
1961 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1962 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1969 /* Get the prototype PTE! */
1970 ProtoPte
= MiProtoPteToPte(&TempPte
);
1972 /* Do we need to locate the prototype PTE in session space? */
1973 if ((IsSessionAddress
) &&
1974 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1976 /* Yep, go find it as well as the VAD for it */
1977 ProtoPte
= MiCheckVirtualAddress(Address
,
1980 ASSERT(ProtoPte
!= NULL
);
1985 /* We don't implement transition PTEs */
1986 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1988 /* Check for no-access PTE */
1989 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1991 /* Bugcheck the system! */
1992 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1995 (ULONG_PTR
)TrapInformation
,
1999 /* Check for no protecton at all */
2000 if (TempPte
.u
.Soft
.Protection
== MM_ZERO_ACCESS
)
2002 /* Bugcheck the system! */
2003 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
2006 (ULONG_PTR
)TrapInformation
,
2011 /* Check for demand page */
2012 if (MI_IS_WRITE_ACCESS(FaultCode
) &&
2014 !(IsSessionAddress
) &&
2015 !(TempPte
.u
.Hard
.Valid
))
2017 /* Get the protection code */
2018 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
2019 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
2021 /* Bugcheck the system! */
2022 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
2025 (ULONG_PTR
)TrapInformation
,
2030 /* Now do the real fault handling */
2031 Status
= MiDispatchFault(FaultCode
,
2040 /* Release the working set */
2041 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2042 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
2043 KeLowerIrql(LockIrql
);
2046 DPRINT("Fault resolved with status: %lx\n", Status
);
2050 /* This is a user fault */
2052 CurrentThread
= PsGetCurrentThread();
2053 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
2055 /* Lock the working set */
2056 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2058 ProtectionCode
= MM_INVALID_PROTECTION
;
2060 #if (_MI_PAGING_LEVELS == 4)
2061 /* Check if the PXE is valid */
2062 if (PointerPxe
->u
.Hard
.Valid
== 0)
2064 /* Right now, we only handle scenarios where the PXE is totally empty */
2065 ASSERT(PointerPxe
->u
.Long
== 0);
2067 /* This is only possible for user mode addresses! */
2068 ASSERT(PointerPte
<= MiHighestUserPte
);
2070 /* Check if we have a VAD */
2071 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2072 if (ProtectionCode
== MM_NOACCESS
)
2074 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2075 return STATUS_ACCESS_VIOLATION
;
2078 /* Resolve a demand zero fault */
2079 MiResolveDemandZeroFault(PointerPpe
,
2085 /* We should come back with a valid PXE */
2086 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2090 #if (_MI_PAGING_LEVELS >= 3)
2091 /* Check if the PPE is valid */
2092 if (PointerPpe
->u
.Hard
.Valid
== 0)
2094 /* Right now, we only handle scenarios where the PPE is totally empty */
2095 ASSERT(PointerPpe
->u
.Long
== 0);
2097 /* This is only possible for user mode addresses! */
2098 ASSERT(PointerPte
<= MiHighestUserPte
);
2100 /* Check if we have a VAD, unless we did this already */
2101 if (ProtectionCode
== MM_INVALID_PROTECTION
)
2103 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2106 if (ProtectionCode
== MM_NOACCESS
)
2108 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2109 return STATUS_ACCESS_VIOLATION
;
2112 /* Resolve a demand zero fault */
2113 MiResolveDemandZeroFault(PointerPde
,
2119 /* We should come back with a valid PPE */
2120 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2124 /* Check if the PDE is invalid */
2125 if (PointerPde
->u
.Hard
.Valid
== 0)
2127 /* Right now, we only handle scenarios where the PDE is totally empty */
2128 ASSERT(PointerPde
->u
.Long
== 0);
2130 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2132 UserPdeFault
= TRUE
;
2134 /* Check if we have a VAD, unless we did this already */
2135 if (ProtectionCode
== MM_INVALID_PROTECTION
)
2137 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2140 if (ProtectionCode
== MM_NOACCESS
)
2142 #if (_MI_PAGING_LEVELS == 2)
2143 /* Could be a page table for paged pool */
2144 MiCheckPdeForPagedPool(Address
);
2146 /* Has the code above changed anything -- is this now a valid PTE? */
2147 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
2149 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2150 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2154 /* Resolve a demand zero fault */
2155 MiResolveDemandZeroFault(PointerPte
,
2161 UserPdeFault
= FALSE
;
2163 /* We should come back with APCs enabled, and with a valid PDE */
2164 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2165 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2169 /* Not yet implemented in ReactOS */
2170 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
2173 /* Now capture the PTE. */
2174 TempPte
= *PointerPte
;
2176 /* Check if the PTE is valid */
2177 if (TempPte
.u
.Hard
.Valid
)
2179 /* Check if this is a write on a readonly PTE */
2180 if (MI_IS_WRITE_ACCESS(FaultCode
))
2182 /* Is this a copy on write PTE? */
2183 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
2185 PFN_NUMBER PageFrameIndex
, OldPageFrameIndex
;
2188 LockIrql
= MiAcquirePfnLock();
2190 ASSERT(MmAvailablePages
> 0);
2192 /* Allocate a new page and copy it */
2193 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
));
2194 OldPageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2196 MiCopyPfn(PageFrameIndex
, OldPageFrameIndex
);
2198 /* Dereference whatever this PTE is referencing */
2199 Pfn1
= MI_PFN_ELEMENT(OldPageFrameIndex
);
2200 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 1);
2201 ASSERT(!MI_IS_PFN_DELETED(Pfn1
));
2202 ProtoPte
= Pfn1
->PteAddress
;
2203 MiDeletePte(PointerPte
, Address
, CurrentProcess
, ProtoPte
);
2205 /* And make a new shiny one with our page */
2206 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
2207 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
2208 TempPte
.u
.Hard
.Write
= 1;
2209 TempPte
.u
.Hard
.CopyOnWrite
= 0;
2211 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
2213 MiReleasePfnLock(LockIrql
);
2215 /* Return the status */
2216 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2217 return STATUS_PAGE_FAULT_COPY_ON_WRITE
;
2220 /* Is this a read-only PTE? */
2221 if (!MI_IS_PAGE_WRITEABLE(&TempPte
))
2223 /* Return the status */
2224 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2225 return STATUS_ACCESS_VIOLATION
;
2229 /* Check for execution of non-executable memory */
2230 if (MI_IS_INSTRUCTION_FETCH(FaultCode
) &&
2231 !MI_IS_PAGE_EXECUTABLE(&TempPte
))
2233 /* Return the status */
2234 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2235 return STATUS_ACCESS_VIOLATION
;
2238 /* The fault has already been resolved by a different thread */
2239 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2240 return STATUS_SUCCESS
;
2243 /* Quick check for demand-zero */
2244 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
2246 /* Resolve the fault */
2247 MiResolveDemandZeroFault(Address
,
2253 /* Return the status */
2254 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2255 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2258 /* Check for zero PTE */
2259 if (TempPte
.u
.Long
== 0)
2261 /* Check if this address range belongs to a valid allocation (VAD) */
2262 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2263 if (ProtectionCode
== MM_NOACCESS
)
2265 #if (_MI_PAGING_LEVELS == 2)
2266 /* Could be a page table for paged pool */
2267 MiCheckPdeForPagedPool(Address
);
2269 /* Has the code above changed anything -- is this now a valid PTE? */
2270 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
2272 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2273 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2278 * Check if this is a real user-mode address or actually a kernel-mode
2279 * page table for a user mode address
2281 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
2283 /* Add an additional page table reference */
2284 MiIncrementPageTableReferences(Address
);
2287 /* Is this a guard page? */
2288 if ((ProtectionCode
& MM_PROTECT_SPECIAL
) == MM_GUARDPAGE
)
2290 /* The VAD protection cannot be MM_DECOMMIT! */
2291 ASSERT(ProtectionCode
!= MM_DECOMMIT
);
2293 /* Remove the bit */
2294 TempPte
.u
.Soft
.Protection
= ProtectionCode
& ~MM_GUARDPAGE
;
2295 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2298 ASSERT(ProtoPte
== NULL
);
2299 ASSERT(CurrentThread
->ApcNeeded
== 0);
2301 /* Drop the working set lock */
2302 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2303 ASSERT(KeGetCurrentIrql() == OldIrql
);
2305 /* Handle stack expansion */
2306 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2309 /* Did we get a prototype PTE back? */
2312 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2313 if (PointerPde
== MiAddressToPde(PTE_BASE
))
2315 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2317 _WARN("This is probably completely broken!");
2318 MI_WRITE_INVALID_PDE((PMMPDE
)PointerPte
, DemandZeroPde
);
2320 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
2325 /* No, create a new PTE. First, write the protection */
2326 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2327 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2330 /* Lock the PFN database since we're going to grab a page */
2331 OldIrql
= MiAcquirePfnLock();
2333 /* Make sure we have enough pages */
2334 ASSERT(MmAvailablePages
>= 32);
2336 /* Try to get a zero page */
2337 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
2338 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
2339 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
2340 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
2341 if (!PageFrameIndex
)
2343 /* Grab a page out of there. Later we should grab a colored zero page */
2344 PageFrameIndex
= MiRemoveAnyPage(Color
);
2345 ASSERT(PageFrameIndex
);
2347 /* Release the lock since we need to do some zeroing */
2348 MiReleasePfnLock(OldIrql
);
2350 /* Zero out the page, since it's for user-mode */
2351 MiZeroPfn(PageFrameIndex
);
2353 /* Grab the lock again so we can initialize the PFN entry */
2354 OldIrql
= MiAcquirePfnLock();
2357 /* Initialize the PFN entry now */
2358 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
2360 /* Increment the count of pages in the process */
2361 CurrentProcess
->NumberOfPrivatePages
++;
2363 /* One more demand-zero fault */
2364 KeGetCurrentPrcb()->MmDemandZeroCount
++;
2366 /* And we're done with the lock */
2367 MiReleasePfnLock(OldIrql
);
2369 /* Fault on user PDE, or fault on user PTE? */
2370 if (PointerPte
<= MiHighestUserPte
)
2372 /* User fault, build a user PTE */
2373 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
2375 PointerPte
->u
.Soft
.Protection
,
2380 /* This is a user-mode PDE, create a kernel PTE for it */
2381 MI_MAKE_HARDWARE_PTE(&TempPte
,
2383 PointerPte
->u
.Soft
.Protection
,
2387 /* Write the dirty bit for writeable pages */
2388 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
2390 /* And now write down the PTE, making the address valid */
2391 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
2392 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
2393 ASSERT(Pfn1
->u1
.Event
== NULL
);
2396 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2397 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2398 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2401 /* We should have a valid protection here */
2402 ASSERT(ProtectionCode
!= 0x100);
2404 /* Write the prototype PTE */
2405 TempPte
= PrototypePte
;
2406 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2407 ASSERT(TempPte
.u
.Long
!= 0);
2408 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2412 /* Get the protection code and check if this is a proto PTE */
2413 ProtectionCode
= (ULONG
)TempPte
.u
.Soft
.Protection
;
2414 if (TempPte
.u
.Soft
.Prototype
)
2416 /* Do we need to go find the real PTE? */
2417 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
2419 /* Get the prototype pte and VAD for it */
2420 ProtoPte
= MiCheckVirtualAddress(Address
,
2425 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2426 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2427 return STATUS_ACCESS_VIOLATION
;
2432 /* Get the prototype PTE! */
2433 ProtoPte
= MiProtoPteToPte(&TempPte
);
2435 /* Is it read-only */
2436 if (TempPte
.u
.Proto
.ReadOnly
)
2438 /* Set read-only code */
2439 ProtectionCode
= MM_READONLY
;
2443 /* Set unknown protection */
2444 ProtectionCode
= 0x100;
2445 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
2451 /* Do we have a valid protection code? */
2452 if (ProtectionCode
!= 0x100)
2454 /* Run a software access check first, including to detect guard pages */
2455 Status
= MiAccessCheck(PointerPte
,
2456 !MI_IS_NOT_PRESENT_FAULT(FaultCode
),
2461 if (Status
!= STATUS_SUCCESS
)
2464 ASSERT(CurrentThread
->ApcNeeded
== 0);
2466 /* Drop the working set lock */
2467 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2468 ASSERT(KeGetCurrentIrql() == OldIrql
);
2470 /* Did we hit a guard page? */
2471 if (Status
== STATUS_GUARD_PAGE_VIOLATION
)
2473 /* Handle stack expansion */
2474 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2477 /* Otherwise, fail back to the caller directly */
2482 /* Dispatch the fault */
2483 Status
= MiDispatchFault(FaultCode
,
2492 /* Return the status */
2493 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2494 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2500 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
2502 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2503 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2505 *ExecuteOptions
= 0;
2507 if (CurrentProcess
->Flags
.ExecuteDisable
)
2509 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
2512 if (CurrentProcess
->Flags
.ExecuteEnable
)
2514 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
2517 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
2519 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
2522 if (CurrentProcess
->Flags
.Permanent
)
2524 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
2527 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
2529 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
2532 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
2534 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
2537 return STATUS_SUCCESS
;
2542 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
2544 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2545 KLOCK_QUEUE_HANDLE ProcessLock
;
2546 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
2547 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2549 /* Only accept valid flags */
2550 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
2553 DPRINT1("Invalid no-execute options\n");
2554 return STATUS_INVALID_PARAMETER
;
2557 /* Change the NX state in the process lock */
2558 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
2560 /* Don't change anything if the permanent flag was set */
2561 if (!CurrentProcess
->Flags
.Permanent
)
2563 /* Start by assuming it's not disabled */
2564 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
2566 /* Now process each flag and turn the equivalent bit on */
2567 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
2569 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
2571 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
2573 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
2575 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
2577 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
2579 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
2581 CurrentProcess
->Flags
.Permanent
= TRUE
;
2583 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
2585 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2587 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
2589 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2592 /* These are turned on by default if no-execution is also eanbled */
2593 if (CurrentProcess
->Flags
.ExecuteEnable
)
2595 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2596 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2600 Status
= STATUS_SUCCESS
;
2603 /* Release the lock and return status */
2604 KiReleaseProcessLock(&ProcessLock
);