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 DPRINT("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. Address=%p, StackBase=%p, DeallocationStack=%p\n",
73 Address
, StackBase
, DeallocationStack
);
74 return STATUS_GUARD_PAGE_VIOLATION
;
77 /* This is where the stack will start now */
78 NextStackAddress
= (PVOID
)((ULONG_PTR
)PAGE_ALIGN(Address
) - GuranteedSize
);
80 /* Do we have at least one page between here and the end of the stack? */
81 if (((ULONG_PTR
)NextStackAddress
- PAGE_SIZE
) <= (ULONG_PTR
)DeallocationStack
)
83 /* We don't -- Windows would try to make this guard page valid now */
84 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
;
117 MiAccessCheck(IN PMMPTE PointerPte
,
118 IN BOOLEAN StoreInstruction
,
119 IN KPROCESSOR_MODE PreviousMode
,
120 IN ULONG_PTR ProtectionCode
,
126 /* Check for invalid user-mode access */
127 if ((PreviousMode
== UserMode
) && (PointerPte
> MiHighestUserPte
))
129 return STATUS_ACCESS_VIOLATION
;
132 /* Capture the PTE -- is it valid? */
133 TempPte
= *PointerPte
;
134 if (TempPte
.u
.Hard
.Valid
)
136 /* Was someone trying to write to it? */
137 if (StoreInstruction
)
140 if ((TempPte
.u
.Hard
.Write
) || (TempPte
.u
.Hard
.CopyOnWrite
))
142 /* Then there's nothing to worry about */
143 return STATUS_SUCCESS
;
146 /* Oops! This isn't allowed */
147 return STATUS_ACCESS_VIOLATION
;
150 /* Someone was trying to read from a valid PTE, that's fine too */
151 return STATUS_SUCCESS
;
154 /* Convert any fault flag to 1 only */
155 if (StoreInstruction
) StoreInstruction
= 1;
158 /* Check if the protection on the page allows what is being attempted */
159 if ((MmReadWrite
[Protection
] - StoreInstruction
) < 10)
161 return STATUS_ACCESS_VIOLATION
;
165 /* Check if this is a guard page */
166 if (ProtectionCode
& MM_DECOMMIT
)
168 /* Attached processes can't expand their stack */
169 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION
;
171 /* No support for transition PTEs yet */
172 ASSERT(((TempPte
.u
.Soft
.Transition
== 1) &&
173 (TempPte
.u
.Soft
.Prototype
== 0)) == FALSE
);
175 /* Remove the guard page bit, and return a guard page violation */
176 PointerPte
->u
.Soft
.Protection
= ProtectionCode
& ~MM_DECOMMIT
;
177 return STATUS_GUARD_PAGE_VIOLATION
;
181 return STATUS_SUCCESS
;
186 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
187 OUT PULONG ProtectCode
,
188 OUT PMMVAD
*ProtoVad
)
193 /* No prototype/section support for now */
196 /* User or kernel fault? */
197 if (VirtualAddress
<= MM_HIGHEST_USER_ADDRESS
)
199 /* Special case for shared data */
200 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
)
202 /* It's a read-only page */
203 *ProtectCode
= MM_READONLY
;
204 return MmSharedUserDataPte
;
207 /* Find the VAD, it might not exist if the address is bogus */
208 Vad
= MiLocateAddress(VirtualAddress
);
211 /* Bogus virtual address */
212 *ProtectCode
= MM_NOACCESS
;
216 /* ReactOS does not handle physical memory VADs yet */
217 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
219 /* Check if it's a section, or just an allocation */
220 if (Vad
->u
.VadFlags
.PrivateMemory
)
222 /* ReactOS does not handle AWE VADs yet */
223 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
225 /* This must be a TEB/PEB VAD */
226 if (Vad
->u
.VadFlags
.MemCommit
)
228 /* It's committed, so return the VAD protection */
229 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
233 /* It has not yet been committed, so return no access */
234 *ProtectCode
= MM_NOACCESS
;
237 /* In both cases, return no PTE */
242 /* ReactOS does not supoprt these VADs yet */
243 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
244 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
246 /* Return the proto VAD */
249 /* Get the prototype PTE for this page */
250 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
251 ASSERT(PointerPte
!= NULL
);
252 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
254 /* Return the Prototype PTE and the protection for the page mapping */
255 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
259 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
261 /* This should never happen, as these addresses are handled by the double-maping */
262 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
263 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
265 /* Fail such access */
266 *ProtectCode
= MM_NOACCESS
;
270 /* Return full access rights */
271 *ProtectCode
= MM_READWRITE
;
274 else if (MI_IS_SESSION_ADDRESS(VirtualAddress
))
276 /* ReactOS does not have an image list yet, so bail out to failure case */
277 ASSERT(IsListEmpty(&MmSessionSpace
->ImageList
));
280 /* Default case -- failure */
281 *ProtectCode
= MM_NOACCESS
;
285 #if (_MI_PAGING_LEVELS == 2)
288 MiSynchronizeSystemPde(PMMPDE PointerPde
)
293 /* Get the Index from the PDE */
294 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
296 /* Copy the PDE from the double-mapped system page directory */
297 SystemPde
= MmSystemPagePtes
[Index
];
298 *PointerPde
= SystemPde
;
300 /* Make sure we re-read the PDE and PTE */
301 KeMemoryBarrierWithoutFence();
303 /* Return, if we had success */
304 return (BOOLEAN
)SystemPde
.u
.Hard
.Valid
;
309 MiCheckPdeForSessionSpace(IN PVOID Address
)
313 PVOID SessionAddress
;
316 /* Is this a session PTE? */
317 if (MI_IS_SESSION_PTE(Address
))
319 /* Make sure the PDE for session space is valid */
320 PointerPde
= MiAddressToPde(MmSessionSpace
);
321 if (!PointerPde
->u
.Hard
.Valid
)
323 /* This means there's no valid session, bail out */
324 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
327 return STATUS_ACCESS_VIOLATION
;
330 /* Now get the session-specific page table for this address */
331 SessionAddress
= MiPteToAddress(Address
);
332 PointerPde
= MiAddressToPte(Address
);
333 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
335 /* It's not valid, so find it in the page table array */
336 Index
= ((ULONG_PTR
)SessionAddress
- (ULONG_PTR
)MmSessionBase
) >> 22;
337 TempPde
.u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
338 if (TempPde
.u
.Hard
.Valid
)
340 /* The copy is valid, so swap it in */
341 InterlockedExchange((PLONG
)PointerPde
, TempPde
.u
.Long
);
342 return STATUS_WAIT_1
;
345 /* We don't seem to have allocated a page table for this address yet? */
346 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
347 PointerPde
->u
.Long
, SessionAddress
);
349 return STATUS_ACCESS_VIOLATION
;
352 /* Is the address also a session address? If not, we're done */
353 if (!MI_IS_SESSION_ADDRESS(Address
)) return STATUS_SUCCESS
;
355 /* It is, so again get the PDE for session space */
356 PointerPde
= MiAddressToPde(MmSessionSpace
);
357 if (!PointerPde
->u
.Hard
.Valid
)
359 /* This means there's no valid session, bail out */
360 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
363 return STATUS_ACCESS_VIOLATION
;
366 /* Now get the PDE for the address itself */
367 PointerPde
= MiAddressToPde(Address
);
368 if (!PointerPde
->u
.Hard
.Valid
)
370 /* Do the swap, we should be good to go */
371 Index
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MmSessionBase
) >> 22;
372 PointerPde
->u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
373 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
375 /* We had not allocated a page table for this session address yet, fail! */
376 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
377 PointerPde
->u
.Long
, Address
);
379 return STATUS_ACCESS_VIOLATION
;
382 /* It's valid, so there's nothing to do */
383 return STATUS_SUCCESS
;
388 MiCheckPdeForPagedPool(IN PVOID Address
)
391 NTSTATUS Status
= STATUS_SUCCESS
;
393 /* Check session PDE */
394 if (MI_IS_SESSION_ADDRESS(Address
)) return MiCheckPdeForSessionSpace(Address
);
395 if (MI_IS_SESSION_PTE(Address
)) return MiCheckPdeForSessionSpace(Address
);
398 // Check if this is a fault while trying to access the page table itself
400 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
403 // Send a hint to the page fault handler that this is only a valid fault
404 // if we already detected this was access within the page table range
406 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
407 Status
= STATUS_WAIT_1
;
409 else if (Address
< MmSystemRangeStart
)
412 // This is totally illegal
414 return STATUS_ACCESS_VIOLATION
;
419 // Get the PDE for the address
421 PointerPde
= MiAddressToPde(Address
);
425 // Check if it's not valid
427 if (PointerPde
->u
.Hard
.Valid
== 0)
430 // Copy it from our double-mapped system page directory
432 InterlockedExchangePte(PointerPde
,
433 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
444 MiCheckPdeForPagedPool(IN PVOID Address
)
446 return STATUS_ACCESS_VIOLATION
;
452 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
459 /* Get the PFN for this page */
460 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
463 /* Grab a system PTE we can use to zero the page */
464 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
467 /* Initialize the PTE for it */
468 TempPte
= ValidKernelPte
;
469 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
472 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
474 /* Write combining, no caching */
475 MI_PAGE_DISABLE_CACHE(&TempPte
);
476 MI_PAGE_WRITE_COMBINED(&TempPte
);
478 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
480 /* Write through, no caching */
481 MI_PAGE_DISABLE_CACHE(&TempPte
);
482 MI_PAGE_WRITE_THROUGH(&TempPte
);
485 /* Make the system PTE valid with our PFN */
486 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
488 /* Get the address it maps to, and zero it out */
489 ZeroAddress
= MiPteToAddress(ZeroPte
);
490 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
492 /* Now get rid of it */
493 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
498 MiResolveDemandZeroFault(IN PVOID Address
,
499 IN PMMPTE PointerPte
,
500 IN PEPROCESS Process
,
503 PFN_NUMBER PageFrameNumber
= 0;
505 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
508 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
512 /* Must currently only be called by paging path */
513 if ((Process
> HYDRA_PROCESS
) && (OldIrql
== MM_NOIRQL
))
516 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
519 ASSERT(Process
->ForkInProgress
== NULL
);
521 /* Get process color */
522 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
523 ASSERT(Color
!= 0xFFFFFFFF);
525 /* We'll need a zero page */
530 /* Check if we need a zero page */
531 NeedZero
= (OldIrql
!= MM_NOIRQL
);
533 /* Session-backed image views must be zeroed */
534 if ((Process
== HYDRA_PROCESS
) &&
535 ((MI_IS_SESSION_IMAGE_ADDRESS(Address
)) ||
536 ((Address
>= MiSessionViewStart
) && (Address
< MiSessionSpaceWs
))))
541 /* Hardcode unknown color */
545 /* Check if the PFN database should be acquired */
546 if (OldIrql
== MM_NOIRQL
)
548 /* Acquire it and remember we should release it after */
549 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
553 /* We either manually locked the PFN DB, or already came with it locked */
554 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
555 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
557 /* Assert we have enough pages */
558 ASSERT(MmAvailablePages
>= 32);
561 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
562 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
564 if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
565 if (!Process
) MI_SET_PROCESS2("Kernel Demand 0");
567 /* Do we need a zero page? */
568 if (Color
!= 0xFFFFFFFF)
570 /* Try to get one, if we couldn't grab a free page and zero it */
571 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
572 if (!PageFrameNumber
)
574 /* We'll need a free page and zero it manually */
575 PageFrameNumber
= MiRemoveAnyPage(Color
);
581 /* Get a color, and see if we should grab a zero or non-zero page */
582 Color
= MI_GET_NEXT_COLOR();
585 /* Process or system doesn't want a zero page, grab anything */
586 PageFrameNumber
= MiRemoveAnyPage(Color
);
590 /* System wants a zero page, obtain one */
591 PageFrameNumber
= MiRemoveZeroPage(Color
);
596 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
598 /* Do we have the lock? */
602 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
604 /* Update performance counters */
605 if (Process
> HYDRA_PROCESS
) Process
->NumberOfPrivatePages
++;
608 /* Increment demand zero faults */
609 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
611 /* Zero the page if need be */
612 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
614 /* Fault on user PDE, or fault on user PTE? */
615 if (PointerPte
<= MiHighestUserPte
)
617 /* User fault, build a user PTE */
618 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
620 PointerPte
->u
.Soft
.Protection
,
625 /* This is a user-mode PDE, create a kernel PTE for it */
626 MI_MAKE_HARDWARE_PTE(&TempPte
,
628 PointerPte
->u
.Soft
.Protection
,
632 /* Set it dirty if it's a writable page */
633 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
636 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
638 /* Did we manually acquire the lock */
641 /* Get the PFN entry */
642 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
644 /* Windows does these sanity checks */
645 ASSERT(Pfn1
->u1
.Event
== 0);
646 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
652 DPRINT("Demand zero page has now been paged in\n");
653 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
658 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
660 IN PMMPTE PointerPte
,
661 IN PMMPTE PointerProtoPte
,
663 IN PMMPFN
* LockedProtoPfn
)
666 PMMPTE OriginalPte
, PageTablePte
;
667 ULONG_PTR Protection
;
668 PFN_NUMBER PageFrameIndex
;
670 BOOLEAN OriginalProtection
, DirtyPage
;
672 /* Must be called with an valid prototype PTE, with the PFN lock held */
673 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
674 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
677 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
679 /* Get the PFN entry and set it as a prototype PTE */
680 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
681 Pfn1
->u3
.e1
.PrototypePte
= 1;
683 /* Increment the share count for the page table */
684 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
685 // This could be beause MiDeletePte is now being called from strange code in Rosmm
686 PageTablePte
= MiAddressToPte(PointerPte
);
687 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
688 //Pfn2->u2.ShareCount++;
689 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2
);
691 /* Check where we should be getting the protection information from */
692 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
694 /* Get the protection from the PTE, there's no real Proto PTE data */
695 Protection
= PointerPte
->u
.Soft
.Protection
;
697 /* Remember that we did not use the proto protection */
698 OriginalProtection
= FALSE
;
702 /* Get the protection from the original PTE link */
703 OriginalPte
= &Pfn1
->OriginalPte
;
704 Protection
= OriginalPte
->u
.Soft
.Protection
;
706 /* Remember that we used the original protection */
707 OriginalProtection
= TRUE
;
709 /* Check if this was a write on a read only proto */
710 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
713 StoreInstruction
= 0;
717 /* Check if this was a write on a non-COW page */
719 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
721 /* Then the page should be marked dirty */
725 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
728 /* Did we get a locked incoming PFN? */
731 /* Drop a reference */
732 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
733 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
734 *LockedProtoPfn
= NULL
;
737 /* Release the PFN lock */
738 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
740 /* Remove caching bits */
741 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
744 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
746 /* Write combining, no caching */
747 MI_PAGE_DISABLE_CACHE(&TempPte
);
748 MI_PAGE_WRITE_COMBINED(&TempPte
);
750 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
752 /* Write through, no caching */
753 MI_PAGE_DISABLE_CACHE(&TempPte
);
754 MI_PAGE_WRITE_THROUGH(&TempPte
);
757 /* Check if this is a kernel or user address */
758 if (Address
< MmSystemRangeStart
)
760 /* Build the user PTE */
761 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
765 /* Build the kernel PTE */
766 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
769 /* Set the dirty flag if needed */
770 if (DirtyPage
) TempPte
.u
.Hard
.Dirty
= TRUE
;
773 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
775 /* Reset the protection if needed */
776 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
779 ASSERT(PointerPte
== MiAddressToPte(Address
));
780 return STATUS_SUCCESS
;
785 MiResolveTransitionFault(IN PVOID FaultingAddress
,
786 IN PMMPTE PointerPte
,
787 IN PEPROCESS CurrentProcess
,
789 OUT PVOID
*InPageBlock
)
791 PFN_NUMBER PageFrameIndex
;
794 PMMPTE PointerToPteForProtoPage
;
795 DPRINT1("Transition fault on 0x%p with PTE 0x%p in process %s\n", FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
797 /* Windowss does this check */
798 ASSERT(*InPageBlock
== NULL
);
800 /* ARM3 doesn't support this path */
801 ASSERT(OldIrql
!= MM_NOIRQL
);
803 /* Capture the PTE and make sure it's in transition format */
804 TempPte
= *PointerPte
;
805 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
806 (TempPte
.u
.Soft
.Prototype
== 0) &&
807 (TempPte
.u
.Soft
.Transition
== 1));
809 /* Get the PFN and the PFN entry */
810 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
811 DPRINT1("Transition PFN: %lx\n", PageFrameIndex
);
812 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
814 /* One more transition fault! */
815 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
817 /* This is from ARM3 -- Windows normally handles this here */
818 ASSERT(Pfn1
->u4
.InPageError
== 0);
820 /* Not supported in ARM3 */
821 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
823 /* Windows checks there's some free pages and this isn't an in-page error */
824 ASSERT(MmAvailablePages
> 0);
825 ASSERT(Pfn1
->u4
.InPageError
== 0);
827 /* ReactOS checks for this */
828 ASSERT(MmAvailablePages
> 32);
830 /* Was this a transition page in the valid list, or free/zero list? */
831 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
833 /* All Windows does here is a bunch of sanity checks */
834 DPRINT1("Transition in active list\n");
835 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
836 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
837 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
838 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
842 /* Otherwise, the page is removed from its list */
843 DPRINT1("Transition page in free/zero list\n");
844 MiUnlinkPageFromList(Pfn1
);
845 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
848 /* At this point, there should no longer be any in-page errors */
849 ASSERT(Pfn1
->u4
.InPageError
== 0);
851 /* Check if this was a PFN with no more share references */
852 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
854 /* Bump the share count and make the page valid */
855 Pfn1
->u2
.ShareCount
++;
856 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
858 /* Prototype PTEs are in paged pool, which itself might be in transition */
859 if (FaultingAddress
>= MmSystemRangeStart
)
861 /* Check if this is a paged pool PTE in transition state */
862 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
863 TempPte
= *PointerToPteForProtoPage
;
864 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
866 /* This isn't yet supported */
867 DPRINT1("Double transition fault not yet supported\n");
872 /* Build the transition PTE -- maybe a macro? */
873 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
874 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
875 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
876 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
877 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
878 MiDetermineUserGlobalPteMask(PointerPte
);
880 /* Is the PTE writeable? */
881 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
882 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
885 TempPte
.u
.Hard
.Dirty
= TRUE
;
890 TempPte
.u
.Hard
.Dirty
= FALSE
;
893 /* Write the valid PTE */
894 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
897 return STATUS_PAGE_FAULT_TRANSITION
;
902 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
904 IN PMMPTE PointerPte
,
905 IN PMMPTE PointerProtoPte
,
906 IN OUT PMMPFN
*OutPfn
,
907 OUT PVOID
*PageFileData
,
909 IN PEPROCESS Process
,
911 IN PVOID TrapInformation
)
913 MMPTE TempPte
, PteContents
;
915 PFN_NUMBER PageFrameIndex
;
917 PVOID InPageBlock
= NULL
;
919 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
920 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
921 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
922 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
924 /* Read the prototype PTE and check if it's valid */
925 TempPte
= *PointerProtoPte
;
926 if (TempPte
.u
.Hard
.Valid
== 1)
928 /* One more user of this mapped page */
929 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
930 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
931 Pfn1
->u2
.ShareCount
++;
933 /* Call it a transition */
934 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
936 /* Complete the prototype PTE fault -- this will release the PFN lock */
937 return MiCompleteProtoPteFault(StoreInstruction
,
945 /* Make sure there's some protection mask */
946 if (TempPte
.u
.Long
== 0)
948 /* Release the lock */
949 DPRINT1("Access on reserved section?\n");
950 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
951 return STATUS_ACCESS_VIOLATION
;
954 /* Check for access rights on the PTE proper */
955 PteContents
= *PointerPte
;
956 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
958 if (!PteContents
.u
.Proto
.ReadOnly
)
960 /* Check for page acess in software */
961 Status
= MiAccessCheck(PointerProtoPte
,
964 TempPte
.u
.Soft
.Protection
,
967 ASSERT(Status
== STATUS_SUCCESS
);
969 /* Check for copy on write page */
970 if ((TempPte
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
972 /* Not yet supported */
979 /* Check for copy on write page */
980 if ((PteContents
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
982 /* Not yet supported */
987 /* Check for clone PTEs */
988 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
990 /* We don't support mapped files yet */
991 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
993 /* We might however have transition PTEs */
994 if (TempPte
.u
.Soft
.Transition
== 1)
996 /* Resolve the transition fault */
997 ASSERT(OldIrql
!= MM_NOIRQL
);
998 Status
= MiResolveTransitionFault(Address
,
1003 ASSERT(NT_SUCCESS(Status
));
1007 /* We also don't support paged out pages */
1008 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1010 /* Resolve the demand zero fault */
1011 Status
= MiResolveDemandZeroFault(Address
,
1015 ASSERT(NT_SUCCESS(Status
));
1018 /* Complete the prototype PTE fault -- this will release the PFN lock */
1019 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1020 return MiCompleteProtoPteFault(StoreInstruction
,
1030 MiDispatchFault(IN BOOLEAN StoreInstruction
,
1032 IN PMMPTE PointerPte
,
1033 IN PMMPTE PointerProtoPte
,
1034 IN BOOLEAN Recursive
,
1035 IN PEPROCESS Process
,
1036 IN PVOID TrapInformation
,
1040 KIRQL OldIrql
, LockIrql
;
1042 PMMPTE SuperProtoPte
;
1043 PMMPFN Pfn1
, OutPfn
= NULL
;
1044 PFN_NUMBER PageFrameIndex
;
1045 PFN_COUNT PteCount
, ProcessedPtes
;
1046 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1050 /* Make sure the addresses are ok */
1051 ASSERT(PointerPte
== MiAddressToPte(Address
));
1054 // Make sure APCs are off and we're not at dispatch
1056 OldIrql
= KeGetCurrentIrql();
1057 ASSERT(OldIrql
<= APC_LEVEL
);
1058 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1061 // Grab a copy of the PTE
1063 TempPte
= *PointerPte
;
1065 /* Do we have a prototype PTE? */
1066 if (PointerProtoPte
)
1068 /* This should never happen */
1069 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
1071 /* Check if this is a kernel-mode address */
1072 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
1073 if (Address
>= MmSystemRangeStart
)
1075 /* Lock the PFN database */
1076 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1078 /* Has the PTE been made valid yet? */
1079 if (!SuperProtoPte
->u
.Hard
.Valid
)
1083 else if (PointerPte
->u
.Hard
.Valid
== 1)
1088 /* Resolve the fault -- this will release the PFN lock */
1089 Status
= MiResolveProtoPteFault(StoreInstruction
,
1099 ASSERT(Status
== STATUS_SUCCESS
);
1101 /* Complete this as a transition fault */
1102 ASSERT(OldIrql
== KeGetCurrentIrql());
1103 ASSERT(OldIrql
<= APC_LEVEL
);
1104 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1109 /* We only handle the lookup path */
1110 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
1112 /* Is there a non-image VAD? */
1114 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
1115 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
1117 /* One day, ReactOS will cluster faults */
1118 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
1119 DPRINT("Should cluster fault, but won't\n");
1122 /* Only one PTE to handle for now */
1126 /* Lock the PFN database */
1127 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1129 /* We only handle the valid path */
1130 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
1132 /* Capture the PTE */
1133 TempPte
= *PointerProtoPte
;
1135 /* Loop to handle future case of clustered faults */
1138 /* For our current usage, this should be true */
1139 if (TempPte
.u
.Hard
.Valid
== 1)
1141 /* Bump the share count on the PTE */
1142 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1143 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1144 Pfn1
->u2
.ShareCount
++;
1146 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
1147 (TempPte
.u
.Soft
.Transition
== 1))
1149 /* This is a standby page, bring it back from the cache */
1150 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
1151 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
1152 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1153 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
1155 /* Should not yet happen in ReactOS */
1156 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
1157 ASSERT(Pfn1
->u4
.InPageError
== 0);
1160 MiUnlinkPageFromList(Pfn1
);
1162 /* Bump its reference count */
1163 ASSERT(Pfn1
->u2
.ShareCount
== 0);
1164 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1165 Pfn1
->u2
.ShareCount
++;
1167 /* Make it valid again */
1168 /* This looks like another macro.... */
1169 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1170 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1171 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1172 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1173 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1174 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1175 TempPte
.u
.Hard
.Valid
= 1;
1176 TempPte
.u
.Hard
.Accessed
= 1;
1178 /* Is the PTE writeable? */
1179 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
1180 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
1183 TempPte
.u
.Hard
.Dirty
= TRUE
;
1188 TempPte
.u
.Hard
.Dirty
= FALSE
;
1191 /* Write the valid PTE */
1192 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1193 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1197 /* Page is invalid, get out of the loop */
1201 /* One more done, was it the last? */
1202 if (++ProcessedPtes
== PteCount
)
1204 /* Complete the fault */
1205 MiCompleteProtoPteFault(StoreInstruction
,
1212 /* THIS RELEASES THE PFN LOCK! */
1216 /* No clustered faults yet */
1220 /* Did we resolve the fault? */
1223 /* Bump the transition count */
1224 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1227 /* Loop all the processing we did */
1228 ASSERT(ProcessedPtes
== 0);
1230 /* Complete this as a transition fault */
1231 ASSERT(OldIrql
== KeGetCurrentIrql());
1232 ASSERT(OldIrql
<= APC_LEVEL
);
1233 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1234 return STATUS_PAGE_FAULT_TRANSITION
;
1237 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1238 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1239 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1240 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1241 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1243 /* Resolve the fault -- this will release the PFN lock */
1244 Status
= MiResolveProtoPteFault(StoreInstruction
,
1254 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1255 //ASSERT(Status != STATUS_REFAULT);
1256 //ASSERT(Status != STATUS_PTE_CHANGED);
1258 /* Did the routine clean out the PFN or should we? */
1261 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1262 ASSERT(PointerProtoPte
!= NULL
);
1263 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1265 /* Dereference the locked PFN */
1266 MiDereferencePfnAndDropLockCount(OutPfn
);
1267 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1269 /* And now release the lock */
1270 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1273 /* Complete this as a transition fault */
1274 ASSERT(OldIrql
== KeGetCurrentIrql());
1275 ASSERT(OldIrql
<= APC_LEVEL
);
1276 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1282 // The PTE must be invalid but not completely empty. It must also not be a
1283 // prototype PTE as that scenario should've been handled above. These are
1284 // all Windows checks
1286 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1287 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1288 ASSERT(TempPte
.u
.Long
!= 0);
1291 // No transition or page file software PTEs in ARM3 yet, so this must be a
1292 // demand zero page. These are all ReactOS checks
1294 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1295 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1298 // If we got this far, the PTE can only be a demand zero PTE, which is what
1299 // we want. Go handle it!
1301 Status
= MiResolveDemandZeroFault(Address
,
1305 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1306 if (NT_SUCCESS(Status
))
1309 // Make sure we're returning in a sane state and pass the status down
1311 ASSERT(OldIrql
== KeGetCurrentIrql());
1312 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1317 // Generate an access fault
1319 return STATUS_ACCESS_VIOLATION
;
1324 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1326 IN KPROCESSOR_MODE Mode
,
1327 IN PVOID TrapInformation
)
1329 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1330 PMMPTE ProtoPte
= NULL
;
1331 PMMPTE PointerPte
= MiAddressToPte(Address
);
1332 PMMPDE PointerPde
= MiAddressToPde(Address
);
1333 #if (_MI_PAGING_LEVELS >= 3)
1334 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1335 #if (_MI_PAGING_LEVELS == 4)
1336 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1340 PETHREAD CurrentThread
;
1341 PEPROCESS CurrentProcess
;
1343 PMMSUPPORT WorkingSet
;
1344 ULONG ProtectionCode
;
1346 PFN_NUMBER PageFrameIndex
;
1348 BOOLEAN IsSessionAddress
;
1350 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1352 /* Check for page fault on high IRQL */
1353 if (OldIrql
> APC_LEVEL
)
1355 #if (_MI_PAGING_LEVELS < 3)
1356 /* Could be a page table for paged pool, which we'll allow */
1357 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1358 MiCheckPdeForPagedPool(Address
);
1360 /* Check if any of the top-level pages are invalid */
1362 #if (_MI_PAGING_LEVELS == 4)
1363 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1365 #if (_MI_PAGING_LEVELS >= 3)
1366 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1368 (PointerPde
->u
.Hard
.Valid
== 0) ||
1369 (PointerPte
->u
.Hard
.Valid
== 0))
1371 /* This fault is not valid, print out some debugging help */
1372 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1375 if (TrapInformation
)
1377 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1379 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1380 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1381 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1382 #elif defined(_M_AMD64)
1383 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1384 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1385 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1389 /* Tell the trap handler to fail */
1390 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1393 /* Not yet implemented in ReactOS */
1394 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1395 ASSERT(((StoreInstruction
) && (PointerPte
->u
.Hard
.CopyOnWrite
)) == FALSE
);
1397 /* Check if this was a write */
1398 if (StoreInstruction
)
1400 /* Was it to a read-only page? */
1401 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1402 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1403 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1405 /* Crash with distinguished bugcheck code */
1406 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1409 (ULONG_PTR
)TrapInformation
,
1414 /* Nothing is actually wrong */
1415 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql
, Address
);
1416 return STATUS_SUCCESS
;
1419 /* Check for kernel fault address */
1420 if (Address
>= MmSystemRangeStart
)
1422 /* Bail out, if the fault came from user mode */
1423 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1425 #if (_MI_PAGING_LEVELS == 4)
1426 /* AMD64 system, check if PXE is invalid */
1427 if (PointerPxe
->u
.Hard
.Valid
== 0)
1429 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1432 (ULONG_PTR
)TrapInformation
,
1436 #if (_MI_PAGING_LEVELS == 4)
1437 /* PAE/AMD64 system, check if PPE is invalid */
1438 if (PointerPpe
->u
.Hard
.Valid
== 0)
1440 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1443 (ULONG_PTR
)TrapInformation
,
1447 #if (_MI_PAGING_LEVELS == 2)
1448 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1449 MiCheckPdeForPagedPool(Address
);
1452 /* Check if the PDE is invalid */
1453 if (PointerPde
->u
.Hard
.Valid
== 0)
1455 /* PDE (still) not valid, kill the system */
1456 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1459 (ULONG_PTR
)TrapInformation
,
1463 /* Not handling session faults yet */
1464 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1466 /* The PDE is valid, so read the PTE */
1467 TempPte
= *PointerPte
;
1468 if (TempPte
.u
.Hard
.Valid
== 1)
1470 /* Check if this was system space or session space */
1471 if (!IsSessionAddress
)
1473 /* Check if the PTE is still valid under PFN lock */
1474 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1475 TempPte
= *PointerPte
;
1476 if (TempPte
.u
.Hard
.Valid
)
1478 /* Check if this was a write */
1479 if (StoreInstruction
)
1481 /* Was it to a read-only page? */
1482 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1483 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1484 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1486 /* Crash with distinguished bugcheck code */
1487 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1490 (ULONG_PTR
)TrapInformation
,
1496 /* Release PFN lock and return all good */
1497 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1498 return STATUS_SUCCESS
;
1501 #if (_MI_PAGING_LEVELS == 2)
1502 /* Check if this was a session PTE that needs to remap the session PDE */
1503 if (MI_IS_SESSION_PTE(Address
))
1505 /* Do the remapping */
1506 Status
= MiCheckPdeForSessionSpace(Address
);
1507 if (!NT_SUCCESS(Status
))
1509 /* It failed, this address is invalid */
1510 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1513 (ULONG_PTR
)TrapInformation
,
1519 _WARN("Session space stuff is not implemented yet!")
1523 /* Check for a fault on the page table or hyperspace */
1524 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1526 #if (_MI_PAGING_LEVELS < 3)
1527 /* Windows does this check but I don't understand why -- it's done above! */
1528 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1530 /* Handle this as a user mode fault */
1534 /* Get the current thread */
1535 CurrentThread
= PsGetCurrentThread();
1537 /* What kind of address is this */
1538 if (!IsSessionAddress
)
1540 /* Use the system working set */
1541 WorkingSet
= &MmSystemCacheWs
;
1542 CurrentProcess
= NULL
;
1544 /* Make sure we don't have a recursive working set lock */
1545 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1546 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1547 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1548 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1549 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1550 (CurrentThread
->OwnsSessionWorkingSetShared
))
1553 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1558 /* Use the session process and working set */
1559 CurrentProcess
= HYDRA_PROCESS
;
1560 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1562 /* Make sure we don't have a recursive working set lock */
1563 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1564 (CurrentThread
->OwnsSessionWorkingSetShared
))
1567 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1571 /* Acquire the working set lock */
1572 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1573 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1575 /* Re-read PTE now that we own the lock */
1576 TempPte
= *PointerPte
;
1577 if (TempPte
.u
.Hard
.Valid
== 1)
1579 /* Check if this was a write */
1580 if (StoreInstruction
)
1582 /* Was it to a read-only page that is not copy on write? */
1583 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1584 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1585 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1586 !(TempPte
.u
.Hard
.CopyOnWrite
))
1588 /* Case not yet handled */
1589 ASSERT(!IsSessionAddress
);
1591 /* Crash with distinguished bugcheck code */
1592 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1595 (ULONG_PTR
)TrapInformation
,
1600 /* Check for read-only write in session space */
1601 if ((IsSessionAddress
) &&
1602 (StoreInstruction
) &&
1603 !(TempPte
.u
.Hard
.Write
))
1606 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1609 if (TempPte
.u
.Hard
.CopyOnWrite
== 0)
1611 /* Then this is not allowed */
1612 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1614 (ULONG_PTR
)TempPte
.u
.Long
,
1615 (ULONG_PTR
)TrapInformation
,
1619 /* Otherwise, handle COW */
1623 /* Release the working set */
1624 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1625 KeLowerIrql(LockIrql
);
1627 /* Otherwise, the PDE was probably invalid, and all is good now */
1628 return STATUS_SUCCESS
;
1631 /* Check one kind of prototype PTE */
1632 if (TempPte
.u
.Soft
.Prototype
)
1634 /* Make sure protected pool is on, and that this is a pool address */
1635 if ((MmProtectFreedNonPagedPool
) &&
1636 (((Address
>= MmNonPagedPoolStart
) &&
1637 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1638 MmSizeOfNonPagedPoolInBytes
))) ||
1639 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1640 (Address
< MmNonPagedPoolEnd
))))
1642 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1643 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1650 /* Get the prototype PTE! */
1651 ProtoPte
= MiProtoPteToPte(&TempPte
);
1653 /* Do we need to locate the prototype PTE in session space? */
1654 if ((IsSessionAddress
) &&
1655 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1657 /* Yep, go find it as well as the VAD for it */
1658 ProtoPte
= MiCheckVirtualAddress(Address
,
1661 ASSERT(ProtoPte
!= NULL
);
1666 /* We don't implement transition PTEs */
1667 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1669 /* Check for no-access PTE */
1670 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1672 /* Bugcheck the system! */
1673 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1676 (ULONG_PTR
)TrapInformation
,
1681 /* Check for demand page */
1682 if ((StoreInstruction
) &&
1684 !(IsSessionAddress
) &&
1685 !(TempPte
.u
.Hard
.Valid
))
1687 /* Get the protection code */
1688 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1689 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1691 /* Bugcheck the system! */
1692 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1695 (ULONG_PTR
)TrapInformation
,
1700 /* Now do the real fault handling */
1701 Status
= MiDispatchFault(StoreInstruction
,
1710 /* Release the working set */
1711 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1712 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1713 KeLowerIrql(LockIrql
);
1716 DPRINT("Fault resolved with status: %lx\n", Status
);
1720 /* This is a user fault */
1722 CurrentThread
= PsGetCurrentThread();
1723 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
1725 /* Lock the working set */
1726 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1728 #if (_MI_PAGING_LEVELS == 4)
1729 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1730 // also this is missing the page count increment
1731 /* Check if the PXE is valid */
1732 if (PointerPxe
->u
.Hard
.Valid
== 0)
1734 /* Right now, we only handle scenarios where the PXE is totally empty */
1735 ASSERT(PointerPxe
->u
.Long
== 0);
1737 /* Resolve a demand zero fault */
1738 Status
= MiResolveDemandZeroFault(PointerPpe
,
1743 /* We should come back with a valid PXE */
1744 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1748 #if (_MI_PAGING_LEVELS >= 3)
1749 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1750 // also this is missing the page count increment
1751 /* Check if the PPE is valid */
1752 if (PointerPpe
->u
.Hard
.Valid
== 0)
1754 /* Right now, we only handle scenarios where the PPE is totally empty */
1755 ASSERT(PointerPpe
->u
.Long
== 0);
1757 /* Resolve a demand zero fault */
1758 Status
= MiResolveDemandZeroFault(PointerPde
,
1763 /* We should come back with a valid PPE */
1764 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1768 /* Check if the PDE is valid */
1769 if (PointerPde
->u
.Hard
.Valid
== 0)
1771 /* Right now, we only handle scenarios where the PDE is totally empty */
1772 ASSERT(PointerPde
->u
.Long
== 0);
1774 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1776 UserPdeFault
= TRUE
;
1778 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1779 if (ProtectionCode
== MM_NOACCESS
)
1781 #if (_MI_PAGING_LEVELS == 2)
1782 /* Could be a page table for paged pool */
1783 MiCheckPdeForPagedPool(Address
);
1785 /* Has the code above changed anything -- is this now a valid PTE? */
1786 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1788 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1789 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1793 /* Write a demand-zero PDE */
1794 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
1796 /* Dispatch the fault */
1797 Status
= MiDispatchFault(TRUE
,
1802 PsGetCurrentProcess(),
1806 UserPdeFault
= FALSE
;
1808 /* We should come back with APCs enabled, and with a valid PDE */
1809 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1810 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1814 /* Not yet implemented in ReactOS */
1815 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1818 /* Now capture the PTE. Ignore virtual faults for now */
1819 TempPte
= *PointerPte
;
1820 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1822 /* Quick check for demand-zero */
1823 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
1825 /* Resolve the fault */
1826 MiResolveDemandZeroFault(Address
,
1831 /* Return the status */
1832 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1833 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1836 /* Check for zero PTE */
1837 if (TempPte
.u
.Long
== 0)
1839 /* Check if this address range belongs to a valid allocation (VAD) */
1840 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1841 if (ProtectionCode
== MM_NOACCESS
)
1843 #if (_MI_PAGING_LEVELS == 2)
1844 /* Could be a page table for paged pool */
1845 MiCheckPdeForPagedPool(Address
);
1847 /* Has the code above changed anything -- is this now a valid PTE? */
1848 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1850 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1851 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1856 * Check if this is a real user-mode address or actually a kernel-mode
1857 * page table for a user mode address
1859 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
1861 /* Add an additional page table reference */
1862 MiIncrementPageTableReferences(Address
);
1865 /* Is this a guard page? */
1866 if (ProtectionCode
& MM_DECOMMIT
)
1868 /* Remove the bit */
1869 PointerPte
->u
.Soft
.Protection
= ProtectionCode
& ~MM_DECOMMIT
;
1872 ASSERT(ProtoPte
== NULL
);
1873 ASSERT(CurrentThread
->ApcNeeded
== 0);
1875 /* Drop the working set lock */
1876 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1877 ASSERT(KeGetCurrentIrql() == OldIrql
);
1879 /* Handle stack expansion */
1880 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
1883 /* Did we get a prototype PTE back? */
1886 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1887 if (PointerPde
== MiAddressToPde(PTE_BASE
))
1889 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1890 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
1894 /* No, create a new PTE. First, write the protection */
1895 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
1898 /* Lock the PFN database since we're going to grab a page */
1899 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1901 /* Make sure we have enough pages */
1902 ASSERT(MmAvailablePages
>= 32);
1904 /* Try to get a zero page */
1905 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1906 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1907 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1908 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1909 if (!PageFrameIndex
)
1911 /* Grab a page out of there. Later we should grab a colored zero page */
1912 PageFrameIndex
= MiRemoveAnyPage(Color
);
1913 ASSERT(PageFrameIndex
);
1915 /* Release the lock since we need to do some zeroing */
1916 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1918 /* Zero out the page, since it's for user-mode */
1919 MiZeroPfn(PageFrameIndex
);
1921 /* Grab the lock again so we can initialize the PFN entry */
1922 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1925 /* Initialize the PFN entry now */
1926 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1928 /* And we're done with the lock */
1929 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1931 /* Increment the count of pages in the process */
1932 CurrentProcess
->NumberOfPrivatePages
++;
1934 /* One more demand-zero fault */
1935 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
1937 /* Fault on user PDE, or fault on user PTE? */
1938 if (PointerPte
<= MiHighestUserPte
)
1940 /* User fault, build a user PTE */
1941 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1943 PointerPte
->u
.Soft
.Protection
,
1948 /* This is a user-mode PDE, create a kernel PTE for it */
1949 MI_MAKE_HARDWARE_PTE(&TempPte
,
1951 PointerPte
->u
.Soft
.Protection
,
1955 /* Write the dirty bit for writeable pages */
1956 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1958 /* And now write down the PTE, making the address valid */
1959 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1960 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1961 ASSERT(Pfn1
->u1
.Event
== NULL
);
1964 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1965 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1966 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1969 /* We should have a valid protection here */
1970 ASSERT(ProtectionCode
!= 0x100);
1972 /* Write the prototype PTE */
1973 TempPte
= PrototypePte
;
1974 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1975 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1979 /* Get the protection code and check if this is a proto PTE */
1980 ProtectionCode
= (ULONG
)TempPte
.u
.Soft
.Protection
;
1981 if (TempPte
.u
.Soft
.Prototype
)
1983 /* Do we need to go find the real PTE? */
1984 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1986 /* Get the prototype pte and VAD for it */
1987 ProtoPte
= MiCheckVirtualAddress(Address
,
1992 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1993 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1994 return STATUS_ACCESS_VIOLATION
;
1999 /* Get the prototype PTE! */
2000 ProtoPte
= MiProtoPteToPte(&TempPte
);
2002 /* Is it read-only */
2003 if (TempPte
.u
.Proto
.ReadOnly
)
2005 /* Set read-only code */
2006 ProtectionCode
= MM_READONLY
;
2010 /* Set unknown protection */
2011 ProtectionCode
= 0x100;
2012 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
2018 /* Do we have a valid protection code? */
2019 if (ProtectionCode
!= 0x100)
2021 /* Run a software access check first, including to detect guard pages */
2022 Status
= MiAccessCheck(PointerPte
,
2028 if (Status
!= STATUS_SUCCESS
)
2031 ASSERT(CurrentThread
->ApcNeeded
== 0);
2033 /* Drop the working set lock */
2034 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2035 ASSERT(KeGetCurrentIrql() == OldIrql
);
2037 /* Did we hit a guard page? */
2038 if (Status
== STATUS_GUARD_PAGE_VIOLATION
)
2040 /* Handle stack expansion */
2041 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2044 /* Otherwise, fail back to the caller directly */
2049 /* Dispatch the fault */
2050 Status
= MiDispatchFault(StoreInstruction
,
2059 /* Return the status */
2060 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2061 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2067 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
2069 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2070 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2072 *ExecuteOptions
= 0;
2074 if (CurrentProcess
->Flags
.ExecuteDisable
)
2076 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
2079 if (CurrentProcess
->Flags
.ExecuteEnable
)
2081 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
2084 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
2086 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
2089 if (CurrentProcess
->Flags
.Permanent
)
2091 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
2094 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
2096 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
2099 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
2101 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
2104 return STATUS_SUCCESS
;
2109 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
2111 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2112 KLOCK_QUEUE_HANDLE ProcessLock
;
2113 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
2114 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2116 /* Only accept valid flags */
2117 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
2120 DPRINT1("Invalid no-execute options\n");
2121 return STATUS_INVALID_PARAMETER
;
2124 /* Change the NX state in the process lock */
2125 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
2127 /* Don't change anything if the permanent flag was set */
2128 if (!CurrentProcess
->Flags
.Permanent
)
2130 /* Start by assuming it's not disabled */
2131 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
2133 /* Now process each flag and turn the equivalent bit on */
2134 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
2136 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
2138 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
2140 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
2142 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
2144 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
2146 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
2148 CurrentProcess
->Flags
.Permanent
= TRUE
;
2150 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
2152 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2154 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
2156 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2159 /* These are turned on by default if no-execution is also eanbled */
2160 if (CurrentProcess
->Flags
.ExecuteEnable
)
2162 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2163 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2167 Status
= STATUS_SUCCESS
;
2170 /* Release the lock and return status */
2171 KiReleaseProcessLock(&ProcessLock
);