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 "../ARM3/miarm.h"
18 /* GLOBALS ********************************************************************/
20 #define HYDRA_PROCESS (PEPROCESS)1
22 BOOLEAN UserPdeFault
= FALSE
;
25 /* PRIVATE FUNCTIONS **********************************************************/
29 MiCheckForUserStackOverflow(IN PVOID Address
,
30 IN PVOID TrapInformation
)
32 PETHREAD CurrentThread
= PsGetCurrentThread();
33 PTEB Teb
= CurrentThread
->Tcb
.Teb
;
34 PVOID StackBase
, DeallocationStack
, NextStackAddress
;
38 /* Do we own the address space lock? */
39 if (CurrentThread
->AddressSpaceOwner
== 1)
41 /* This isn't valid */
42 DPRINT1("Process owns address space lock\n");
43 ASSERT(KeAreAllApcsDisabled() == TRUE
);
44 return STATUS_GUARD_PAGE_VIOLATION
;
47 /* Are we attached? */
48 if (KeIsAttachedProcess())
50 /* This isn't valid */
51 DPRINT1("Process is attached\n");
52 return STATUS_GUARD_PAGE_VIOLATION
;
55 /* Read the current settings */
56 StackBase
= Teb
->NtTib
.StackBase
;
57 DeallocationStack
= Teb
->DeallocationStack
;
58 GuranteedSize
= Teb
->GuaranteedStackBytes
;
59 DPRINT1("Handling guard page fault with Stacks Addresses 0x%p and 0x%p, guarantee: %lx\n",
60 StackBase
, DeallocationStack
, GuranteedSize
);
62 /* Guarantees make this code harder, for now, assume there aren't any */
63 ASSERT(GuranteedSize
== 0);
65 /* So allocate only the minimum guard page size */
66 GuranteedSize
= PAGE_SIZE
;
68 /* Does this faulting stack address actually exist in the stack? */
69 if ((Address
>= StackBase
) || (Address
< DeallocationStack
))
72 DPRINT1("Faulting address outside of stack bounds\n");
73 return STATUS_GUARD_PAGE_VIOLATION
;
76 /* This is where the stack will start now */
77 NextStackAddress
= (PVOID
)((ULONG_PTR
)PAGE_ALIGN(Address
) - GuranteedSize
);
79 /* Do we have at least one page between here and the end of the stack? */
80 if (((ULONG_PTR
)NextStackAddress
- PAGE_SIZE
) <= (ULONG_PTR
)DeallocationStack
)
82 /* We don't -- Windows would try to make this guard page valid now */
83 DPRINT1("Close to our death...\n");
85 return STATUS_STACK_OVERFLOW
;
88 /* Don't handle this flag yet */
89 ASSERT((PsGetCurrentProcess()->Peb
->NtGlobalFlag
& FLG_DISABLE_STACK_EXTENSION
) == 0);
91 /* Update the stack limit */
92 Teb
->NtTib
.StackLimit
= (PVOID
)((ULONG_PTR
)NextStackAddress
+ GuranteedSize
);
94 /* Now move the guard page to the next page */
95 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
100 PAGE_READWRITE
| PAGE_GUARD
);
101 if ((NT_SUCCESS(Status
) || (Status
== STATUS_ALREADY_COMMITTED
)))
104 DPRINT1("Guard page handled successfully for %p\n", Address
);
105 return STATUS_PAGE_FAULT_GUARD_PAGE
;
108 /* Fail, we couldn't move the guard page */
109 DPRINT1("Guard page failure: %lx\n", Status
);
111 return STATUS_STACK_OVERFLOW
;
116 MiAccessCheck(IN PMMPTE PointerPte
,
117 IN BOOLEAN StoreInstruction
,
118 IN KPROCESSOR_MODE PreviousMode
,
119 IN ULONG ProtectionCode
,
125 /* Check for invalid user-mode access */
126 if ((PreviousMode
== UserMode
) && (PointerPte
> MiHighestUserPte
))
128 return STATUS_ACCESS_VIOLATION
;
131 /* Capture the PTE -- is it valid? */
132 TempPte
= *PointerPte
;
133 if (TempPte
.u
.Hard
.Valid
)
135 /* Was someone trying to write to it? */
136 if (StoreInstruction
)
139 if ((TempPte
.u
.Hard
.Write
) || (TempPte
.u
.Hard
.CopyOnWrite
))
141 /* Then there's nothing to worry about */
142 return STATUS_SUCCESS
;
145 /* Oops! This isn't allowed */
146 return STATUS_ACCESS_VIOLATION
;
149 /* Someone was trying to read from a valid PTE, that's fine too */
150 return STATUS_SUCCESS
;
153 /* Convert any fault flag to 1 only */
154 if (StoreInstruction
) StoreInstruction
= 1;
157 /* Check if the protection on the page allows what is being attempted */
158 if ((MmReadWrite
[Protection
] - StoreInstruction
) < 10)
160 return STATUS_ACCESS_VIOLATION
;
164 /* Check if this is a guard page */
165 if (ProtectionCode
& MM_DECOMMIT
)
167 /* Attached processes can't expand their stack */
168 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION
;
170 /* No support for transition PTEs yet */
171 ASSERT(((TempPte
.u
.Soft
.Transition
== 1) &&
172 (TempPte
.u
.Soft
.Prototype
== 0)) == FALSE
);
174 /* Remove the guard page bit, and return a guard page violation */
175 PointerPte
->u
.Soft
.Protection
= ProtectionCode
& ~MM_DECOMMIT
;
176 return STATUS_GUARD_PAGE_VIOLATION
;
180 return STATUS_SUCCESS
;
185 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
186 OUT PULONG ProtectCode
,
187 OUT PMMVAD
*ProtoVad
)
192 /* No prototype/section support for now */
195 /* User or kernel fault? */
196 if (VirtualAddress
<= MM_HIGHEST_USER_ADDRESS
)
198 /* Special case for shared data */
199 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
)
201 /* It's a read-only page */
202 *ProtectCode
= MM_READONLY
;
203 return MmSharedUserDataPte
;
206 /* Find the VAD, it might not exist if the address is bogus */
207 Vad
= MiLocateAddress(VirtualAddress
);
210 /* Bogus virtual address */
211 *ProtectCode
= MM_NOACCESS
;
215 /* ReactOS does not handle physical memory VADs yet */
216 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
218 /* Check if it's a section, or just an allocation */
219 if (Vad
->u
.VadFlags
.PrivateMemory
)
221 /* ReactOS does not handle AWE VADs yet */
222 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
224 /* This must be a TEB/PEB VAD */
225 if (Vad
->u
.VadFlags
.MemCommit
)
227 /* It's committed, so return the VAD protection */
228 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
232 /* It has not yet been committed, so return no access */
233 *ProtectCode
= MM_NOACCESS
;
236 /* In both cases, return no PTE */
241 /* ReactOS does not supoprt these VADs yet */
242 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
243 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
245 /* Return the proto VAD */
248 /* Get the prototype PTE for this page */
249 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
250 ASSERT(PointerPte
!= NULL
);
251 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
253 /* Return the Prototype PTE and the protection for the page mapping */
254 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
258 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
260 /* This should never happen, as these addresses are handled by the double-maping */
261 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
262 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
264 /* Fail such access */
265 *ProtectCode
= MM_NOACCESS
;
269 /* Return full access rights */
270 *ProtectCode
= MM_READWRITE
;
273 else if (MI_IS_SESSION_ADDRESS(VirtualAddress
))
275 /* ReactOS does not have an image list yet, so bail out to failure case */
276 ASSERT(IsListEmpty(&MmSessionSpace
->ImageList
));
279 /* Default case -- failure */
280 *ProtectCode
= MM_NOACCESS
;
284 #if (_MI_PAGING_LEVELS == 2)
287 MiSynchronizeSystemPde(PMMPDE PointerPde
)
292 /* Get the Index from the PDE */
293 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
295 /* Copy the PDE from the double-mapped system page directory */
296 SystemPde
= MmSystemPagePtes
[Index
];
297 *PointerPde
= SystemPde
;
299 /* Make sure we re-read the PDE and PTE */
300 KeMemoryBarrierWithoutFence();
302 /* Return, if we had success */
303 return (BOOLEAN
)SystemPde
.u
.Hard
.Valid
;
308 MiCheckPdeForSessionSpace(IN PVOID Address
)
312 PVOID SessionAddress
;
315 /* Is this a session PTE? */
316 if (MI_IS_SESSION_PTE(Address
))
318 /* Make sure the PDE for session space is valid */
319 PointerPde
= MiAddressToPde(MmSessionSpace
);
320 if (!PointerPde
->u
.Hard
.Valid
)
322 /* This means there's no valid session, bail out */
323 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
326 return STATUS_ACCESS_VIOLATION
;
329 /* Now get the session-specific page table for this address */
330 SessionAddress
= MiPteToAddress(Address
);
331 PointerPde
= MiAddressToPte(Address
);
332 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
334 /* It's not valid, so find it in the page table array */
335 Index
= ((ULONG_PTR
)SessionAddress
- (ULONG_PTR
)MmSessionBase
) >> 22;
336 TempPde
.u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
337 if (TempPde
.u
.Hard
.Valid
)
339 /* The copy is valid, so swap it in */
340 InterlockedExchange((PLONG
)PointerPde
, TempPde
.u
.Long
);
341 return STATUS_WAIT_1
;
344 /* We don't seem to have allocated a page table for this address yet? */
345 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
346 PointerPde
->u
.Long
, SessionAddress
);
348 return STATUS_ACCESS_VIOLATION
;
351 /* Is the address also a session address? If not, we're done */
352 if (!MI_IS_SESSION_ADDRESS(Address
)) return STATUS_SUCCESS
;
354 /* It is, so again get the PDE for session space */
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 VA %p\n",
362 return STATUS_ACCESS_VIOLATION
;
365 /* Now get the PDE for the address itself */
366 PointerPde
= MiAddressToPde(Address
);
367 if (!PointerPde
->u
.Hard
.Valid
)
369 /* Do the swap, we should be good to go */
370 Index
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MmSessionBase
) >> 22;
371 PointerPde
->u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
372 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
374 /* We had not allocated a page table for this session address yet, fail! */
375 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
376 PointerPde
->u
.Long
, Address
);
378 return STATUS_ACCESS_VIOLATION
;
381 /* It's valid, so there's nothing to do */
382 return STATUS_SUCCESS
;
387 MiCheckPdeForPagedPool(IN PVOID Address
)
390 NTSTATUS Status
= STATUS_SUCCESS
;
392 /* Check session PDE */
393 if (MI_IS_SESSION_ADDRESS(Address
)) return MiCheckPdeForSessionSpace(Address
);
394 if (MI_IS_SESSION_PTE(Address
)) return MiCheckPdeForSessionSpace(Address
);
397 // Check if this is a fault while trying to access the page table itself
399 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
402 // Send a hint to the page fault handler that this is only a valid fault
403 // if we already detected this was access within the page table range
405 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
406 Status
= STATUS_WAIT_1
;
408 else if (Address
< MmSystemRangeStart
)
411 // This is totally illegal
413 return STATUS_ACCESS_VIOLATION
;
418 // Get the PDE for the address
420 PointerPde
= MiAddressToPde(Address
);
424 // Check if it's not valid
426 if (PointerPde
->u
.Hard
.Valid
== 0)
429 // Copy it from our double-mapped system page directory
431 InterlockedExchangePte(PointerPde
,
432 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
443 MiCheckPdeForPagedPool(IN PVOID Address
)
445 return STATUS_ACCESS_VIOLATION
;
451 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
458 /* Get the PFN for this page */
459 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
462 /* Grab a system PTE we can use to zero the page */
463 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
466 /* Initialize the PTE for it */
467 TempPte
= ValidKernelPte
;
468 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
471 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
473 /* Write combining, no caching */
474 MI_PAGE_DISABLE_CACHE(&TempPte
);
475 MI_PAGE_WRITE_COMBINED(&TempPte
);
477 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
479 /* Write through, no caching */
480 MI_PAGE_DISABLE_CACHE(&TempPte
);
481 MI_PAGE_WRITE_THROUGH(&TempPte
);
484 /* Make the system PTE valid with our PFN */
485 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
487 /* Get the address it maps to, and zero it out */
488 ZeroAddress
= MiPteToAddress(ZeroPte
);
489 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
491 /* Now get rid of it */
492 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
497 MiResolveDemandZeroFault(IN PVOID Address
,
498 IN PMMPTE PointerPte
,
499 IN PEPROCESS Process
,
502 PFN_NUMBER PageFrameNumber
= 0;
504 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
507 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
511 /* Must currently only be called by paging path */
512 if ((Process
> HYDRA_PROCESS
) && (OldIrql
== MM_NOIRQL
))
515 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
518 ASSERT(Process
->ForkInProgress
== NULL
);
520 /* Get process color */
521 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
522 ASSERT(Color
!= 0xFFFFFFFF);
524 /* We'll need a zero page */
529 /* Check if we need a zero page */
530 NeedZero
= (OldIrql
!= MM_NOIRQL
);
532 /* Session-backed image views must be zeroed */
533 if ((Process
== HYDRA_PROCESS
) &&
534 ((MI_IS_SESSION_IMAGE_ADDRESS(Address
)) ||
535 ((Address
>= MiSessionViewStart
) && (Address
< MiSessionSpaceWs
))))
540 /* Hardcode unknown color */
544 /* Check if the PFN database should be acquired */
545 if (OldIrql
== MM_NOIRQL
)
547 /* Acquire it and remember we should release it after */
548 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
552 /* We either manually locked the PFN DB, or already came with it locked */
553 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
554 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
556 /* Assert we have enough pages */
557 ASSERT(MmAvailablePages
>= 32);
560 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
561 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
563 if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
564 if (!Process
) MI_SET_PROCESS2("Kernel Demand 0");
566 /* Do we need a zero page? */
567 if (Color
!= 0xFFFFFFFF)
569 /* Try to get one, if we couldn't grab a free page and zero it */
570 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
571 if (!PageFrameNumber
)
573 /* We'll need a free page and zero it manually */
574 PageFrameNumber
= MiRemoveAnyPage(Color
);
580 /* Get a color, and see if we should grab a zero or non-zero page */
581 Color
= MI_GET_NEXT_COLOR();
584 /* Process or system doesn't want a zero page, grab anything */
585 PageFrameNumber
= MiRemoveAnyPage(Color
);
589 /* System wants a zero page, obtain one */
590 PageFrameNumber
= MiRemoveZeroPage(Color
);
595 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
597 /* Do we have the lock? */
601 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
603 /* Update performance counters */
604 if (Process
> HYDRA_PROCESS
) Process
->NumberOfPrivatePages
++;
607 /* Increment demand zero faults */
608 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
610 /* Zero the page if need be */
611 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
613 /* Fault on user PDE, or fault on user PTE? */
614 if (PointerPte
<= MiHighestUserPte
)
616 /* User fault, build a user PTE */
617 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
619 PointerPte
->u
.Soft
.Protection
,
624 /* This is a user-mode PDE, create a kernel PTE for it */
625 MI_MAKE_HARDWARE_PTE(&TempPte
,
627 PointerPte
->u
.Soft
.Protection
,
631 /* Set it dirty if it's a writable page */
632 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
635 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
637 /* Did we manually acquire the lock */
640 /* Get the PFN entry */
641 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
643 /* Windows does these sanity checks */
644 ASSERT(Pfn1
->u1
.Event
== 0);
645 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
651 DPRINT("Demand zero page has now been paged in\n");
652 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
657 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
659 IN PMMPTE PointerPte
,
660 IN PMMPTE PointerProtoPte
,
662 IN PMMPFN
* LockedProtoPfn
)
665 PMMPTE OriginalPte
, PageTablePte
;
666 ULONG_PTR Protection
;
667 PFN_NUMBER PageFrameIndex
;
669 BOOLEAN OriginalProtection
, DirtyPage
;
671 /* Must be called with an valid prototype PTE, with the PFN lock held */
672 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
673 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
676 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
678 /* Get the PFN entry and set it as a prototype PTE */
679 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
680 Pfn1
->u3
.e1
.PrototypePte
= 1;
682 /* Increment the share count for the page table */
683 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
684 // This could be beause MiDeletePte is now being called from strange code in Rosmm
685 PageTablePte
= MiAddressToPte(PointerPte
);
686 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
687 //Pfn2->u2.ShareCount++;
688 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2
);
690 /* Check where we should be getting the protection information from */
691 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
693 /* Get the protection from the PTE, there's no real Proto PTE data */
694 Protection
= PointerPte
->u
.Soft
.Protection
;
696 /* Remember that we did not use the proto protection */
697 OriginalProtection
= FALSE
;
701 /* Get the protection from the original PTE link */
702 OriginalPte
= &Pfn1
->OriginalPte
;
703 Protection
= OriginalPte
->u
.Soft
.Protection
;
705 /* Remember that we used the original protection */
706 OriginalProtection
= TRUE
;
708 /* Check if this was a write on a read only proto */
709 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
712 StoreInstruction
= 0;
716 /* Check if this was a write on a non-COW page */
718 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
720 /* Then the page should be marked dirty */
724 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
727 /* Did we get a locked incoming PFN? */
730 /* Drop a reference */
731 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
732 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
733 *LockedProtoPfn
= NULL
;
736 /* Release the PFN lock */
737 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
739 /* Remove caching bits */
740 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
743 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
745 /* Write combining, no caching */
746 MI_PAGE_DISABLE_CACHE(&TempPte
);
747 MI_PAGE_WRITE_COMBINED(&TempPte
);
749 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
751 /* Write through, no caching */
752 MI_PAGE_DISABLE_CACHE(&TempPte
);
753 MI_PAGE_WRITE_THROUGH(&TempPte
);
756 /* Check if this is a kernel or user address */
757 if (Address
< MmSystemRangeStart
)
759 /* Build the user PTE */
760 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
764 /* Build the kernel PTE */
765 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
768 /* Set the dirty flag if needed */
769 if (DirtyPage
) TempPte
.u
.Hard
.Dirty
= TRUE
;
772 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
774 /* Reset the protection if needed */
775 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
778 ASSERT(PointerPte
== MiAddressToPte(Address
));
779 return STATUS_SUCCESS
;
784 MiResolveTransitionFault(IN PVOID FaultingAddress
,
785 IN PMMPTE PointerPte
,
786 IN PEPROCESS CurrentProcess
,
788 OUT PVOID
*InPageBlock
)
790 PFN_NUMBER PageFrameIndex
;
793 PMMPTE PointerToPteForProtoPage
;
794 DPRINT1("Transition fault on 0x%p with PTE 0x%p in process %s\n", FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
796 /* Windowss does this check */
797 ASSERT(*InPageBlock
== NULL
);
799 /* ARM3 doesn't support this path */
800 ASSERT(OldIrql
!= MM_NOIRQL
);
802 /* Capture the PTE and make sure it's in transition format */
803 TempPte
= *PointerPte
;
804 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
805 (TempPte
.u
.Soft
.Prototype
== 0) &&
806 (TempPte
.u
.Soft
.Transition
== 1));
808 /* Get the PFN and the PFN entry */
809 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
810 DPRINT1("Transition PFN: %lx\n", PageFrameIndex
);
811 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
813 /* One more transition fault! */
814 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
816 /* This is from ARM3 -- Windows normally handles this here */
817 ASSERT(Pfn1
->u4
.InPageError
== 0);
819 /* Not supported in ARM3 */
820 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
822 /* Windows checks there's some free pages and this isn't an in-page error */
823 ASSERT(MmAvailablePages
> 0);
824 ASSERT(Pfn1
->u4
.InPageError
== 0);
826 /* ReactOS checks for this */
827 ASSERT(MmAvailablePages
> 32);
829 /* Was this a transition page in the valid list, or free/zero list? */
830 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
832 /* All Windows does here is a bunch of sanity checks */
833 DPRINT1("Transition in active list\n");
834 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
835 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
836 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
837 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
841 /* Otherwise, the page is removed from its list */
842 DPRINT1("Transition page in free/zero list\n");
843 MiUnlinkPageFromList(Pfn1
);
844 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
847 /* At this point, there should no longer be any in-page errors */
848 ASSERT(Pfn1
->u4
.InPageError
== 0);
850 /* Check if this was a PFN with no more share references */
851 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
853 /* Bump the share count and make the page valid */
854 Pfn1
->u2
.ShareCount
++;
855 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
857 /* Prototype PTEs are in paged pool, which itself might be in transition */
858 if (FaultingAddress
>= MmSystemRangeStart
)
860 /* Check if this is a paged pool PTE in transition state */
861 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
862 TempPte
= *PointerToPteForProtoPage
;
863 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
865 /* This isn't yet supported */
866 DPRINT1("Double transition fault not yet supported\n");
871 /* Build the transition PTE -- maybe a macro? */
872 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
873 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
874 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
875 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
876 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
877 MiDetermineUserGlobalPteMask(PointerPte
);
879 /* Is the PTE writeable? */
880 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
881 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
884 TempPte
.u
.Hard
.Dirty
= TRUE
;
889 TempPte
.u
.Hard
.Dirty
= FALSE
;
892 /* Write the valid PTE */
893 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
896 return STATUS_PAGE_FAULT_TRANSITION
;
901 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
903 IN PMMPTE PointerPte
,
904 IN PMMPTE PointerProtoPte
,
905 IN OUT PMMPFN
*OutPfn
,
906 OUT PVOID
*PageFileData
,
908 IN PEPROCESS Process
,
910 IN PVOID TrapInformation
)
912 MMPTE TempPte
, PteContents
;
914 PFN_NUMBER PageFrameIndex
;
916 PVOID InPageBlock
= NULL
;
918 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
919 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
920 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
921 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
923 /* Read the prototype PTE and check if it's valid */
924 TempPte
= *PointerProtoPte
;
925 if (TempPte
.u
.Hard
.Valid
== 1)
927 /* One more user of this mapped page */
928 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
929 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
930 Pfn1
->u2
.ShareCount
++;
932 /* Call it a transition */
933 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
935 /* Complete the prototype PTE fault -- this will release the PFN lock */
936 return MiCompleteProtoPteFault(StoreInstruction
,
944 /* Make sure there's some protection mask */
945 if (TempPte
.u
.Long
== 0)
947 /* Release the lock */
948 DPRINT1("Access on reserved section?\n");
949 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
950 return STATUS_ACCESS_VIOLATION
;
953 /* Check for access rights on the PTE proper */
954 PteContents
= *PointerPte
;
955 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
957 if (!PteContents
.u
.Proto
.ReadOnly
)
959 /* Check for page acess in software */
960 Status
= MiAccessCheck(PointerProtoPte
,
963 TempPte
.u
.Soft
.Protection
,
966 ASSERT(Status
== STATUS_SUCCESS
);
968 /* Check for copy on write page */
969 if ((TempPte
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
971 /* Not yet supported */
978 /* Check for copy on write page */
979 if ((PteContents
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
981 /* Not yet supported */
986 /* Check for clone PTEs */
987 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
989 /* We don't support mapped files yet */
990 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
992 /* We might however have transition PTEs */
993 if (TempPte
.u
.Soft
.Transition
== 1)
995 /* Resolve the transition fault */
996 ASSERT(OldIrql
!= MM_NOIRQL
);
997 Status
= MiResolveTransitionFault(Address
,
1002 ASSERT(NT_SUCCESS(Status
));
1006 /* We also don't support paged out pages */
1007 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1009 /* Resolve the demand zero fault */
1010 Status
= MiResolveDemandZeroFault(Address
,
1014 ASSERT(NT_SUCCESS(Status
));
1017 /* Complete the prototype PTE fault -- this will release the PFN lock */
1018 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1019 return MiCompleteProtoPteFault(StoreInstruction
,
1029 MiDispatchFault(IN BOOLEAN StoreInstruction
,
1031 IN PMMPTE PointerPte
,
1032 IN PMMPTE PointerProtoPte
,
1033 IN BOOLEAN Recursive
,
1034 IN PEPROCESS Process
,
1035 IN PVOID TrapInformation
,
1039 KIRQL OldIrql
, LockIrql
;
1041 PMMPTE SuperProtoPte
;
1042 PMMPFN Pfn1
, OutPfn
= NULL
;
1043 PFN_NUMBER PageFrameIndex
;
1044 PFN_COUNT PteCount
, ProcessedPtes
;
1045 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1049 /* Make sure the addresses are ok */
1050 ASSERT(PointerPte
== MiAddressToPte(Address
));
1053 // Make sure APCs are off and we're not at dispatch
1055 OldIrql
= KeGetCurrentIrql();
1056 ASSERT(OldIrql
<= APC_LEVEL
);
1057 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1060 // Grab a copy of the PTE
1062 TempPte
= *PointerPte
;
1064 /* Do we have a prototype PTE? */
1065 if (PointerProtoPte
)
1067 /* This should never happen */
1068 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
1070 /* Check if this is a kernel-mode address */
1071 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
1072 if (Address
>= MmSystemRangeStart
)
1074 /* Lock the PFN database */
1075 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1077 /* Has the PTE been made valid yet? */
1078 if (!SuperProtoPte
->u
.Hard
.Valid
)
1082 else if (PointerPte
->u
.Hard
.Valid
== 1)
1087 /* Resolve the fault -- this will release the PFN lock */
1088 Status
= MiResolveProtoPteFault(StoreInstruction
,
1098 ASSERT(Status
== STATUS_SUCCESS
);
1100 /* Complete this as a transition fault */
1101 ASSERT(OldIrql
== KeGetCurrentIrql());
1102 ASSERT(OldIrql
<= APC_LEVEL
);
1103 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1108 /* We only handle the lookup path */
1109 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
1111 /* Is there a non-image VAD? */
1113 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
1114 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
1116 /* One day, ReactOS will cluster faults */
1117 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
1118 DPRINT("Should cluster fault, but won't\n");
1121 /* Only one PTE to handle for now */
1125 /* Lock the PFN database */
1126 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1128 /* We only handle the valid path */
1129 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
1131 /* Capture the PTE */
1132 TempPte
= *PointerProtoPte
;
1134 /* Loop to handle future case of clustered faults */
1137 /* For our current usage, this should be true */
1138 if (TempPte
.u
.Hard
.Valid
== 1)
1140 /* Bump the share count on the PTE */
1141 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1142 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1143 Pfn1
->u2
.ShareCount
++;
1145 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
1146 (TempPte
.u
.Soft
.Transition
== 1))
1148 /* This is a standby page, bring it back from the cache */
1149 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
1150 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
1151 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1152 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
1154 /* Should not yet happen in ReactOS */
1155 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
1156 ASSERT(Pfn1
->u4
.InPageError
== 0);
1159 MiUnlinkPageFromList(Pfn1
);
1161 /* Bump its reference count */
1162 ASSERT(Pfn1
->u2
.ShareCount
== 0);
1163 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1164 Pfn1
->u2
.ShareCount
++;
1166 /* Make it valid again */
1167 /* This looks like another macro.... */
1168 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1169 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1170 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1171 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1172 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1173 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1174 TempPte
.u
.Hard
.Valid
= 1;
1175 TempPte
.u
.Hard
.Accessed
= 1;
1177 /* Is the PTE writeable? */
1178 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
1179 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
1182 TempPte
.u
.Hard
.Dirty
= TRUE
;
1187 TempPte
.u
.Hard
.Dirty
= FALSE
;
1190 /* Write the valid PTE */
1191 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1192 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1196 /* Page is invalid, get out of the loop */
1200 /* One more done, was it the last? */
1201 if (++ProcessedPtes
== PteCount
)
1203 /* Complete the fault */
1204 MiCompleteProtoPteFault(StoreInstruction
,
1211 /* THIS RELEASES THE PFN LOCK! */
1215 /* No clustered faults yet */
1219 /* Did we resolve the fault? */
1222 /* Bump the transition count */
1223 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1226 /* Loop all the processing we did */
1227 ASSERT(ProcessedPtes
== 0);
1229 /* Complete this as a transition fault */
1230 ASSERT(OldIrql
== KeGetCurrentIrql());
1231 ASSERT(OldIrql
<= APC_LEVEL
);
1232 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1233 return STATUS_PAGE_FAULT_TRANSITION
;
1236 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1237 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1238 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1239 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1240 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1242 /* Resolve the fault -- this will release the PFN lock */
1243 Status
= MiResolveProtoPteFault(StoreInstruction
,
1253 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1254 //ASSERT(Status != STATUS_REFAULT);
1255 //ASSERT(Status != STATUS_PTE_CHANGED);
1257 /* Did the routine clean out the PFN or should we? */
1260 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1261 ASSERT(PointerProtoPte
!= NULL
);
1262 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1264 /* Dereference the locked PFN */
1265 MiDereferencePfnAndDropLockCount(OutPfn
);
1266 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1268 /* And now release the lock */
1269 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1272 /* Complete this as a transition fault */
1273 ASSERT(OldIrql
== KeGetCurrentIrql());
1274 ASSERT(OldIrql
<= APC_LEVEL
);
1275 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1281 // The PTE must be invalid but not completely empty. It must also not be a
1282 // prototype PTE as that scenario should've been handled above. These are
1283 // all Windows checks
1285 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1286 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1287 ASSERT(TempPte
.u
.Long
!= 0);
1290 // No transition or page file software PTEs in ARM3 yet, so this must be a
1291 // demand zero page. These are all ReactOS checks
1293 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1294 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1297 // If we got this far, the PTE can only be a demand zero PTE, which is what
1298 // we want. Go handle it!
1300 Status
= MiResolveDemandZeroFault(Address
,
1304 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1305 if (NT_SUCCESS(Status
))
1308 // Make sure we're returning in a sane state and pass the status down
1310 ASSERT(OldIrql
== KeGetCurrentIrql());
1311 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1316 // Generate an access fault
1318 return STATUS_ACCESS_VIOLATION
;
1323 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1325 IN KPROCESSOR_MODE Mode
,
1326 IN PVOID TrapInformation
)
1328 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1329 PMMPTE ProtoPte
= NULL
;
1330 PMMPTE PointerPte
= MiAddressToPte(Address
);
1331 PMMPDE PointerPde
= MiAddressToPde(Address
);
1332 #if (_MI_PAGING_LEVELS >= 3)
1333 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1334 #if (_MI_PAGING_LEVELS == 4)
1335 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1339 PETHREAD CurrentThread
;
1340 PEPROCESS CurrentProcess
;
1342 PMMSUPPORT WorkingSet
;
1343 ULONG ProtectionCode
;
1345 PFN_NUMBER PageFrameIndex
;
1347 BOOLEAN IsSessionAddress
;
1349 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1351 /* Check for page fault on high IRQL */
1352 if (OldIrql
> APC_LEVEL
)
1354 #if (_MI_PAGING_LEVELS < 3)
1355 /* Could be a page table for paged pool, which we'll allow */
1356 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1357 MiCheckPdeForPagedPool(Address
);
1359 /* Check if any of the top-level pages are invalid */
1361 #if (_MI_PAGING_LEVELS == 4)
1362 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1364 #if (_MI_PAGING_LEVELS >= 3)
1365 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1367 (PointerPde
->u
.Hard
.Valid
== 0))
1369 /* This fault is not valid, printf out some debugging help */
1370 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1373 if (TrapInformation
)
1375 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1377 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1378 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1379 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1380 #elif defined(_M_AMD64)
1381 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1382 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1383 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1387 /* Tell the trap handler to fail */
1388 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1391 /* Not yet implemented in ReactOS */
1392 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1393 ASSERT(((StoreInstruction
) && (PointerPte
->u
.Hard
.CopyOnWrite
)) == FALSE
);
1395 /* Check if this was a write */
1396 if (StoreInstruction
)
1398 /* Was it to a read-only page? */
1399 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1400 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1401 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1403 /* Crash with distinguished bugcheck code */
1404 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1407 (ULONG_PTR
)TrapInformation
,
1412 /* Nothing is actually wrong */
1413 DPRINT1("Fault at IRQL1 is ok\n");
1414 return STATUS_SUCCESS
;
1417 /* Check for kernel fault address */
1418 if (Address
>= MmSystemRangeStart
)
1420 /* Bail out, if the fault came from user mode */
1421 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1423 #if (_MI_PAGING_LEVELS == 4)
1424 /* AMD64 system, check if PXE is invalid */
1425 if (PointerPxe
->u
.Hard
.Valid
== 0)
1427 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1430 (ULONG_PTR
)TrapInformation
,
1434 #if (_MI_PAGING_LEVELS == 4)
1435 /* PAE/AMD64 system, check if PPE is invalid */
1436 if (PointerPpe
->u
.Hard
.Valid
== 0)
1438 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1441 (ULONG_PTR
)TrapInformation
,
1445 #if (_MI_PAGING_LEVELS == 2)
1446 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1447 MiCheckPdeForPagedPool(Address
);
1450 /* Check if the PDE is invalid */
1451 if (PointerPde
->u
.Hard
.Valid
== 0)
1453 /* PDE (still) not valid, kill the system */
1454 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1457 (ULONG_PTR
)TrapInformation
,
1461 /* Not handling session faults yet */
1462 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1464 /* The PDE is valid, so read the PTE */
1465 TempPte
= *PointerPte
;
1466 if (TempPte
.u
.Hard
.Valid
== 1)
1468 /* Check if this was system space or session space */
1469 if (!IsSessionAddress
)
1471 /* Check if the PTE is still valid under PFN lock */
1472 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1473 TempPte
= *PointerPte
;
1474 if (TempPte
.u
.Hard
.Valid
)
1476 /* Check if this was a write */
1477 if (StoreInstruction
)
1479 /* Was it to a read-only page? */
1480 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1481 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1482 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1484 /* Crash with distinguished bugcheck code */
1485 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1488 (ULONG_PTR
)TrapInformation
,
1494 /* Release PFN lock and return all good */
1495 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1496 return STATUS_SUCCESS
;
1500 /* Check if this was a session PTE that needs to remap the session PDE */
1501 if (MI_IS_SESSION_PTE(Address
))
1503 /* Do the remapping */
1504 Status
= MiCheckPdeForSessionSpace(Address
);
1505 if (!NT_SUCCESS(Status
))
1507 /* It failed, this address is invalid */
1508 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1511 (ULONG_PTR
)TrapInformation
,
1516 /* Check for a fault on the page table or hyperspace */
1517 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1519 #if (_MI_PAGING_LEVELS < 3)
1520 /* Windows does this check but I don't understand why -- it's done above! */
1521 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1523 /* Handle this as a user mode fault */
1527 /* Get the current thread */
1528 CurrentThread
= PsGetCurrentThread();
1530 /* What kind of address is this */
1531 if (!IsSessionAddress
)
1533 /* Use the system working set */
1534 WorkingSet
= &MmSystemCacheWs
;
1535 CurrentProcess
= NULL
;
1537 /* Make sure we don't have a recursive working set lock */
1538 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1539 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1540 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1541 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1542 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1543 (CurrentThread
->OwnsSessionWorkingSetShared
))
1546 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1551 /* Use the session process and working set */
1552 CurrentProcess
= HYDRA_PROCESS
;
1553 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1555 /* Make sure we don't have a recursive working set lock */
1556 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1557 (CurrentThread
->OwnsSessionWorkingSetShared
))
1560 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1564 /* Acquire the working set lock */
1565 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1566 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1568 /* Re-read PTE now that we own the lock */
1569 TempPte
= *PointerPte
;
1570 if (TempPte
.u
.Hard
.Valid
== 1)
1572 /* Check if this was a write */
1573 if (StoreInstruction
)
1575 /* Was it to a read-only page that is not copy on write? */
1576 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1577 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1578 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1579 !(TempPte
.u
.Hard
.CopyOnWrite
))
1581 /* Case not yet handled */
1582 ASSERT(!IsSessionAddress
);
1584 /* Crash with distinguished bugcheck code */
1585 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1588 (ULONG_PTR
)TrapInformation
,
1593 /* Check for read-only write in session space */
1594 if ((IsSessionAddress
) &&
1595 (StoreInstruction
) &&
1596 !(TempPte
.u
.Hard
.Write
))
1599 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1602 if (TempPte
.u
.Hard
.CopyOnWrite
== 0)
1604 /* Then this is not allowed */
1605 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1607 (ULONG_PTR
)TempPte
.u
.Long
,
1608 (ULONG_PTR
)TrapInformation
,
1612 /* Otherwise, handle COW */
1616 /* Release the working set */
1617 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1618 KeLowerIrql(LockIrql
);
1620 /* Otherwise, the PDE was probably invalid, and all is good now */
1621 return STATUS_SUCCESS
;
1624 /* Check one kind of prototype PTE */
1625 if (TempPte
.u
.Soft
.Prototype
)
1627 /* Make sure protected pool is on, and that this is a pool address */
1628 if ((MmProtectFreedNonPagedPool
) &&
1629 (((Address
>= MmNonPagedPoolStart
) &&
1630 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1631 MmSizeOfNonPagedPoolInBytes
))) ||
1632 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1633 (Address
< MmNonPagedPoolEnd
))))
1635 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1636 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1643 /* Get the prototype PTE! */
1644 ProtoPte
= MiProtoPteToPte(&TempPte
);
1646 /* Do we need to locate the prototype PTE in session space? */
1647 if ((IsSessionAddress
) &&
1648 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1650 /* Yep, go find it as well as the VAD for it */
1651 ProtoPte
= MiCheckVirtualAddress(Address
,
1654 ASSERT(ProtoPte
!= NULL
);
1659 /* We don't implement transition PTEs */
1660 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1662 /* Check for no-access PTE */
1663 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1665 /* Bugcheck the system! */
1666 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1669 (ULONG_PTR
)TrapInformation
,
1674 /* Check for demand page */
1675 if ((StoreInstruction
) &&
1677 !(IsSessionAddress
) &&
1678 !(TempPte
.u
.Hard
.Valid
))
1680 /* Get the protection code */
1681 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1682 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1684 /* Bugcheck the system! */
1685 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1688 (ULONG_PTR
)TrapInformation
,
1693 /* Now do the real fault handling */
1694 Status
= MiDispatchFault(StoreInstruction
,
1703 /* Release the working set */
1704 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1705 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1706 KeLowerIrql(LockIrql
);
1709 DPRINT("Fault resolved with status: %lx\n", Status
);
1713 /* This is a user fault */
1715 CurrentThread
= PsGetCurrentThread();
1716 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
1718 /* Lock the working set */
1719 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1721 #if (_MI_PAGING_LEVELS == 4)
1722 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1723 // also this is missing the page count increment
1724 /* Check if the PXE is valid */
1725 if (PointerPxe
->u
.Hard
.Valid
== 0)
1727 /* Right now, we only handle scenarios where the PXE is totally empty */
1728 ASSERT(PointerPxe
->u
.Long
== 0);
1730 /* Resolve a demand zero fault */
1731 Status
= MiResolveDemandZeroFault(PointerPpe
,
1736 /* We should come back with a valid PXE */
1737 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1741 #if (_MI_PAGING_LEVELS >= 3)
1742 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1743 // also this is missing the page count increment
1744 /* Check if the PPE is valid */
1745 if (PointerPpe
->u
.Hard
.Valid
== 0)
1747 /* Right now, we only handle scenarios where the PPE is totally empty */
1748 ASSERT(PointerPpe
->u
.Long
== 0);
1750 /* Resolve a demand zero fault */
1751 Status
= MiResolveDemandZeroFault(PointerPde
,
1756 /* We should come back with a valid PPE */
1757 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1761 /* Check if the PDE is valid */
1762 if (PointerPde
->u
.Hard
.Valid
== 0)
1764 /* Right now, we only handle scenarios where the PDE is totally empty */
1765 ASSERT(PointerPde
->u
.Long
== 0);
1767 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1769 UserPdeFault
= TRUE
;
1771 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1772 if (ProtectionCode
== MM_NOACCESS
)
1774 #if (_MI_PAGING_LEVELS == 2)
1775 /* Could be a page table for paged pool */
1776 MiCheckPdeForPagedPool(Address
);
1778 /* Has the code above changed anything -- is this now a valid PTE? */
1779 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1781 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1782 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1786 /* Write a demand-zero PDE */
1787 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
1789 /* Dispatch the fault */
1790 Status
= MiDispatchFault(TRUE
,
1795 PsGetCurrentProcess(),
1799 UserPdeFault
= FALSE
;
1801 /* We should come back with APCs enabled, and with a valid PDE */
1802 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1803 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1807 /* Not yet implemented in ReactOS */
1808 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1811 /* Now capture the PTE. Ignore virtual faults for now */
1812 TempPte
= *PointerPte
;
1813 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1815 /* Quick check for demand-zero */
1816 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
1818 /* Resolve the fault */
1819 MiResolveDemandZeroFault(Address
,
1824 /* Return the status */
1825 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1826 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1829 /* Check for zero PTE */
1830 if (TempPte
.u
.Long
== 0)
1832 /* Check if this address range belongs to a valid allocation (VAD) */
1833 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1834 if (ProtectionCode
== MM_NOACCESS
)
1836 #if (_MI_PAGING_LEVELS == 2)
1837 /* Could be a page table for paged pool */
1838 MiCheckPdeForPagedPool(Address
);
1840 /* Has the code above changed anything -- is this now a valid PTE? */
1841 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1843 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1844 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1849 * Check if this is a real user-mode address or actually a kernel-mode
1850 * page table for a user mode address
1852 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
1854 /* Add an additional page table reference */
1855 MiIncrementPageTableReferences(Address
);
1858 /* Is this a guard page? */
1859 if (ProtectionCode
& MM_DECOMMIT
)
1861 /* Remove the bit */
1862 PointerPte
->u
.Soft
.Protection
= ProtectionCode
& ~MM_DECOMMIT
;
1865 ASSERT(ProtoPte
== NULL
);
1866 ASSERT(CurrentThread
->ApcNeeded
== 0);
1868 /* Drop the working set lock */
1869 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1870 ASSERT(KeGetCurrentIrql() == OldIrql
);
1872 /* Handle stack expansion */
1873 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
1876 /* Did we get a prototype PTE back? */
1879 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1880 if (PointerPde
== MiAddressToPde(PTE_BASE
))
1882 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1883 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
1887 /* No, create a new PTE. First, write the protection */
1888 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
1891 /* Lock the PFN database since we're going to grab a page */
1892 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1894 /* Make sure we have enough pages */
1895 ASSERT(MmAvailablePages
>= 32);
1897 /* Try to get a zero page */
1898 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1899 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1900 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1901 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1902 if (!PageFrameIndex
)
1904 /* Grab a page out of there. Later we should grab a colored zero page */
1905 PageFrameIndex
= MiRemoveAnyPage(Color
);
1906 ASSERT(PageFrameIndex
);
1908 /* Release the lock since we need to do some zeroing */
1909 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1911 /* Zero out the page, since it's for user-mode */
1912 MiZeroPfn(PageFrameIndex
);
1914 /* Grab the lock again so we can initialize the PFN entry */
1915 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1918 /* Initialize the PFN entry now */
1919 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1921 /* And we're done with the lock */
1922 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1924 /* Increment the count of pages in the process */
1925 CurrentProcess
->NumberOfPrivatePages
++;
1927 /* One more demand-zero fault */
1928 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
1930 /* Fault on user PDE, or fault on user PTE? */
1931 if (PointerPte
<= MiHighestUserPte
)
1933 /* User fault, build a user PTE */
1934 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1936 PointerPte
->u
.Soft
.Protection
,
1941 /* This is a user-mode PDE, create a kernel PTE for it */
1942 MI_MAKE_HARDWARE_PTE(&TempPte
,
1944 PointerPte
->u
.Soft
.Protection
,
1948 /* Write the dirty bit for writeable pages */
1949 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1951 /* And now write down the PTE, making the address valid */
1952 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1953 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1954 ASSERT(Pfn1
->u1
.Event
== NULL
);
1957 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1958 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1959 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1962 /* We should have a valid protection here */
1963 ASSERT(ProtectionCode
!= 0x100);
1965 /* Write the prototype PTE */
1966 TempPte
= PrototypePte
;
1967 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1968 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1972 /* Get the protection code and check if this is a proto PTE */
1973 ProtectionCode
= TempPte
.u
.Soft
.Protection
;
1974 if (TempPte
.u
.Soft
.Prototype
)
1976 /* Do we need to go find the real PTE? */
1977 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1979 /* Get the prototype pte and VAD for it */
1980 ProtoPte
= MiCheckVirtualAddress(Address
,
1985 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1986 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1987 return STATUS_ACCESS_VIOLATION
;
1992 /* Get the prototype PTE! */
1993 ProtoPte
= MiProtoPteToPte(&TempPte
);
1995 /* Is it read-only */
1996 if (TempPte
.u
.Proto
.ReadOnly
)
1998 /* Set read-only code */
1999 ProtectionCode
= MM_READONLY
;
2003 /* Set unknown protection */
2004 ProtectionCode
= 0x100;
2005 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
2011 /* Do we have a valid protection code? */
2012 if (ProtectionCode
!= 0x100)
2014 /* Run a software access check first, including to detect guard pages */
2015 Status
= MiAccessCheck(PointerPte
,
2021 if (Status
!= STATUS_SUCCESS
)
2024 ASSERT(CurrentThread
->ApcNeeded
== 0);
2026 /* Drop the working set lock */
2027 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2028 ASSERT(KeGetCurrentIrql() == OldIrql
);
2030 /* Did we hit a guard page? */
2031 if (Status
== STATUS_GUARD_PAGE_VIOLATION
)
2033 /* Handle stack expansion */
2034 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2037 /* Otherwise, fail back to the caller directly */
2042 /* Dispatch the fault */
2043 Status
= MiDispatchFault(StoreInstruction
,
2052 /* Return the status */
2053 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2054 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2060 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
2062 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2063 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2065 *ExecuteOptions
= 0;
2067 if (CurrentProcess
->Flags
.ExecuteDisable
)
2069 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
2072 if (CurrentProcess
->Flags
.ExecuteEnable
)
2074 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
2077 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
2079 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
2082 if (CurrentProcess
->Flags
.Permanent
)
2084 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
2087 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
2089 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
2092 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
2094 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
2097 return STATUS_SUCCESS
;
2102 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
2104 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2105 KLOCK_QUEUE_HANDLE ProcessLock
;
2106 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
2107 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2109 /* Only accept valid flags */
2110 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
2113 DPRINT1("Invalid no-execute options\n");
2114 return STATUS_INVALID_PARAMETER
;
2117 /* Change the NX state in the process lock */
2118 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
2120 /* Don't change anything if the permanent flag was set */
2121 if (!CurrentProcess
->Flags
.Permanent
)
2123 /* Start by assuming it's not disabled */
2124 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
2126 /* Now process each flag and turn the equivalent bit on */
2127 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
2129 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
2131 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
2133 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
2135 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
2137 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
2139 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
2141 CurrentProcess
->Flags
.Permanent
= TRUE
;
2143 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
2145 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2147 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
2149 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2152 /* These are turned on by default if no-execution is also eanbled */
2153 if (CurrentProcess
->Flags
.ExecuteEnable
)
2155 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2156 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2160 Status
= STATUS_SUCCESS
;
2163 /* Release the lock and return status */
2164 KiReleaseProcessLock(&ProcessLock
);