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
;
118 _In_ ULONG ProtectionMask
,
120 _In_ BOOLEAN Execute
)
122 #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
123 (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
124 ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
125 static const UCHAR MiAccessAllowedMask
[2][2] =
127 { // Protect 0 1 2 3 4 5 6 7
128 _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
129 _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
132 _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
133 _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
137 /* We want only the low 3 bits */
140 /* Look it up in the table */
141 return (MiAccessAllowedMask
[Write
!= 0][Execute
!= 0] >> ProtectionMask
) & 1;
146 MiAccessCheck(IN PMMPTE PointerPte
,
147 IN BOOLEAN StoreInstruction
,
148 IN KPROCESSOR_MODE PreviousMode
,
149 IN ULONG_PTR ProtectionMask
,
155 /* Check for invalid user-mode access */
156 if ((PreviousMode
== UserMode
) && (PointerPte
> MiHighestUserPte
))
158 return STATUS_ACCESS_VIOLATION
;
161 /* Capture the PTE -- is it valid? */
162 TempPte
= *PointerPte
;
163 if (TempPte
.u
.Hard
.Valid
)
165 /* Was someone trying to write to it? */
166 if (StoreInstruction
)
169 if ((TempPte
.u
.Hard
.Write
) || (TempPte
.u
.Hard
.CopyOnWrite
))
171 /* Then there's nothing to worry about */
172 return STATUS_SUCCESS
;
175 /* Oops! This isn't allowed */
176 return STATUS_ACCESS_VIOLATION
;
179 /* Someone was trying to read from a valid PTE, that's fine too */
180 return STATUS_SUCCESS
;
183 /* Check if the protection on the page allows what is being attempted */
184 if (!MiIsAccessAllowed(ProtectionMask
, StoreInstruction
, FALSE
))
186 return STATUS_ACCESS_VIOLATION
;
189 /* Check if this is a guard page */
190 if (ProtectionMask
& MM_GUARDPAGE
)
192 NT_ASSERT(ProtectionMask
!= MM_DECOMMIT
);
194 /* Attached processes can't expand their stack */
195 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION
;
197 /* No support for transition PTEs yet */
198 ASSERT(((TempPte
.u
.Soft
.Transition
== 1) &&
199 (TempPte
.u
.Soft
.Prototype
== 0)) == FALSE
);
201 /* Remove the guard page bit, and return a guard page violation */
202 TempPte
.u
.Soft
.Protection
= ProtectionMask
& ~MM_GUARDPAGE
;
203 NT_ASSERT(TempPte
.u
.Long
!= 0);
204 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
205 return STATUS_GUARD_PAGE_VIOLATION
;
209 return STATUS_SUCCESS
;
214 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
215 OUT PULONG ProtectCode
,
216 OUT PMMVAD
*ProtoVad
)
221 /* No prototype/section support for now */
224 /* User or kernel fault? */
225 if (VirtualAddress
<= MM_HIGHEST_USER_ADDRESS
)
227 /* Special case for shared data */
228 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
)
230 /* It's a read-only page */
231 *ProtectCode
= MM_READONLY
;
232 return MmSharedUserDataPte
;
235 /* Find the VAD, it might not exist if the address is bogus */
236 Vad
= MiLocateAddress(VirtualAddress
);
239 /* Bogus virtual address */
240 *ProtectCode
= MM_NOACCESS
;
244 /* ReactOS does not handle physical memory VADs yet */
245 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
247 /* Check if it's a section, or just an allocation */
248 if (Vad
->u
.VadFlags
.PrivateMemory
)
250 /* ReactOS does not handle AWE VADs yet */
251 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
253 /* This must be a TEB/PEB VAD */
254 if (Vad
->u
.VadFlags
.MemCommit
)
256 /* It's committed, so return the VAD protection */
257 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
261 /* It has not yet been committed, so return no access */
262 *ProtectCode
= MM_NOACCESS
;
265 /* In both cases, return no PTE */
270 /* ReactOS does not supoprt these VADs yet */
271 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
272 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
274 /* Return the proto VAD */
277 /* Get the prototype PTE for this page */
278 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
279 ASSERT(PointerPte
!= NULL
);
280 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
282 /* Return the Prototype PTE and the protection for the page mapping */
283 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
287 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
289 /* This should never happen, as these addresses are handled by the double-maping */
290 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
291 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
293 /* Fail such access */
294 *ProtectCode
= MM_NOACCESS
;
298 /* Return full access rights */
299 *ProtectCode
= MM_READWRITE
;
302 else if (MI_IS_SESSION_ADDRESS(VirtualAddress
))
304 /* ReactOS does not have an image list yet, so bail out to failure case */
305 ASSERT(IsListEmpty(&MmSessionSpace
->ImageList
));
308 /* Default case -- failure */
309 *ProtectCode
= MM_NOACCESS
;
313 #if (_MI_PAGING_LEVELS == 2)
316 MiSynchronizeSystemPde(PMMPDE PointerPde
)
321 /* Get the Index from the PDE */
322 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
324 /* Copy the PDE from the double-mapped system page directory */
325 SystemPde
= MmSystemPagePtes
[Index
];
326 *PointerPde
= SystemPde
;
328 /* Make sure we re-read the PDE and PTE */
329 KeMemoryBarrierWithoutFence();
331 /* Return, if we had success */
332 return (BOOLEAN
)SystemPde
.u
.Hard
.Valid
;
337 MiCheckPdeForSessionSpace(IN PVOID Address
)
341 PVOID SessionAddress
;
344 /* Is this a session PTE? */
345 if (MI_IS_SESSION_PTE(Address
))
347 /* Make sure the PDE for session space is valid */
348 PointerPde
= MiAddressToPde(MmSessionSpace
);
349 if (!PointerPde
->u
.Hard
.Valid
)
351 /* This means there's no valid session, bail out */
352 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
355 return STATUS_ACCESS_VIOLATION
;
358 /* Now get the session-specific page table for this address */
359 SessionAddress
= MiPteToAddress(Address
);
360 PointerPde
= MiAddressToPte(Address
);
361 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
363 /* It's not valid, so find it in the page table array */
364 Index
= ((ULONG_PTR
)SessionAddress
- (ULONG_PTR
)MmSessionBase
) >> 22;
365 TempPde
.u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
366 if (TempPde
.u
.Hard
.Valid
)
368 /* The copy is valid, so swap it in */
369 InterlockedExchange((PLONG
)PointerPde
, TempPde
.u
.Long
);
370 return STATUS_WAIT_1
;
373 /* We don't seem to have allocated a page table for this address yet? */
374 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
375 PointerPde
->u
.Long
, SessionAddress
);
377 return STATUS_ACCESS_VIOLATION
;
380 /* Is the address also a session address? If not, we're done */
381 if (!MI_IS_SESSION_ADDRESS(Address
)) return STATUS_SUCCESS
;
383 /* It is, so again get the PDE for session space */
384 PointerPde
= MiAddressToPde(MmSessionSpace
);
385 if (!PointerPde
->u
.Hard
.Valid
)
387 /* This means there's no valid session, bail out */
388 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
391 return STATUS_ACCESS_VIOLATION
;
394 /* Now get the PDE for the address itself */
395 PointerPde
= MiAddressToPde(Address
);
396 if (!PointerPde
->u
.Hard
.Valid
)
398 /* Do the swap, we should be good to go */
399 Index
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MmSessionBase
) >> 22;
400 PointerPde
->u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
401 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
403 /* We had not allocated a page table for this session address yet, fail! */
404 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
405 PointerPde
->u
.Long
, Address
);
407 return STATUS_ACCESS_VIOLATION
;
410 /* It's valid, so there's nothing to do */
411 return STATUS_SUCCESS
;
416 MiCheckPdeForPagedPool(IN PVOID Address
)
419 NTSTATUS Status
= STATUS_SUCCESS
;
421 /* Check session PDE */
422 if (MI_IS_SESSION_ADDRESS(Address
)) return MiCheckPdeForSessionSpace(Address
);
423 if (MI_IS_SESSION_PTE(Address
)) return MiCheckPdeForSessionSpace(Address
);
426 // Check if this is a fault while trying to access the page table itself
428 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
431 // Send a hint to the page fault handler that this is only a valid fault
432 // if we already detected this was access within the page table range
434 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
435 Status
= STATUS_WAIT_1
;
437 else if (Address
< MmSystemRangeStart
)
440 // This is totally illegal
442 return STATUS_ACCESS_VIOLATION
;
447 // Get the PDE for the address
449 PointerPde
= MiAddressToPde(Address
);
453 // Check if it's not valid
455 if (PointerPde
->u
.Hard
.Valid
== 0)
458 // Copy it from our double-mapped system page directory
460 InterlockedExchangePte(PointerPde
,
461 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
472 MiCheckPdeForPagedPool(IN PVOID Address
)
474 return STATUS_ACCESS_VIOLATION
;
480 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
487 /* Get the PFN for this page */
488 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
491 /* Grab a system PTE we can use to zero the page */
492 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
495 /* Initialize the PTE for it */
496 TempPte
= ValidKernelPte
;
497 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
500 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
502 /* Write combining, no caching */
503 MI_PAGE_DISABLE_CACHE(&TempPte
);
504 MI_PAGE_WRITE_COMBINED(&TempPte
);
506 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
508 /* Write through, no caching */
509 MI_PAGE_DISABLE_CACHE(&TempPte
);
510 MI_PAGE_WRITE_THROUGH(&TempPte
);
513 /* Make the system PTE valid with our PFN */
514 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
516 /* Get the address it maps to, and zero it out */
517 ZeroAddress
= MiPteToAddress(ZeroPte
);
518 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
520 /* Now get rid of it */
521 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
526 MiResolveDemandZeroFault(IN PVOID Address
,
527 IN PMMPTE PointerPte
,
528 IN PEPROCESS Process
,
531 PFN_NUMBER PageFrameNumber
= 0;
533 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
536 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
540 /* Must currently only be called by paging path */
541 if ((Process
> HYDRA_PROCESS
) && (OldIrql
== MM_NOIRQL
))
544 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
547 ASSERT(Process
->ForkInProgress
== NULL
);
549 /* Get process color */
550 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
551 ASSERT(Color
!= 0xFFFFFFFF);
553 /* We'll need a zero page */
558 /* Check if we need a zero page */
559 NeedZero
= (OldIrql
!= MM_NOIRQL
);
561 /* Session-backed image views must be zeroed */
562 if ((Process
== HYDRA_PROCESS
) &&
563 ((MI_IS_SESSION_IMAGE_ADDRESS(Address
)) ||
564 ((Address
>= MiSessionViewStart
) && (Address
< MiSessionSpaceWs
))))
569 /* Hardcode unknown color */
573 /* Check if the PFN database should be acquired */
574 if (OldIrql
== MM_NOIRQL
)
576 /* Acquire it and remember we should release it after */
577 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
581 /* We either manually locked the PFN DB, or already came with it locked */
582 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
583 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
585 /* Assert we have enough pages */
586 ASSERT(MmAvailablePages
>= 32);
589 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
590 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
592 if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
593 if (!Process
) MI_SET_PROCESS2("Kernel Demand 0");
595 /* Do we need a zero page? */
596 if (Color
!= 0xFFFFFFFF)
598 /* Try to get one, if we couldn't grab a free page and zero it */
599 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
600 if (!PageFrameNumber
)
602 /* We'll need a free page and zero it manually */
603 PageFrameNumber
= MiRemoveAnyPage(Color
);
609 /* Get a color, and see if we should grab a zero or non-zero page */
610 Color
= MI_GET_NEXT_COLOR();
613 /* Process or system doesn't want a zero page, grab anything */
614 PageFrameNumber
= MiRemoveAnyPage(Color
);
618 /* System wants a zero page, obtain one */
619 PageFrameNumber
= MiRemoveZeroPage(Color
);
624 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
626 /* Do we have the lock? */
630 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
632 /* Update performance counters */
633 if (Process
> HYDRA_PROCESS
) Process
->NumberOfPrivatePages
++;
636 /* Increment demand zero faults */
637 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
639 /* Zero the page if need be */
640 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
642 /* Fault on user PDE, or fault on user PTE? */
643 if (PointerPte
<= MiHighestUserPte
)
645 /* User fault, build a user PTE */
646 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
648 PointerPte
->u
.Soft
.Protection
,
653 /* This is a user-mode PDE, create a kernel PTE for it */
654 MI_MAKE_HARDWARE_PTE(&TempPte
,
656 PointerPte
->u
.Soft
.Protection
,
660 /* Set it dirty if it's a writable page */
661 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
664 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
666 /* Did we manually acquire the lock */
669 /* Get the PFN entry */
670 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
672 /* Windows does these sanity checks */
673 ASSERT(Pfn1
->u1
.Event
== 0);
674 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
680 DPRINT("Demand zero page has now been paged in\n");
681 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
686 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
688 IN PMMPTE PointerPte
,
689 IN PMMPTE PointerProtoPte
,
691 IN PMMPFN
* LockedProtoPfn
)
694 PMMPTE OriginalPte
, PageTablePte
;
695 ULONG_PTR Protection
;
696 PFN_NUMBER PageFrameIndex
;
698 BOOLEAN OriginalProtection
, DirtyPage
;
700 /* Must be called with an valid prototype PTE, with the PFN lock held */
701 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
702 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
705 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
707 /* Get the PFN entry and set it as a prototype PTE */
708 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
709 Pfn1
->u3
.e1
.PrototypePte
= 1;
711 /* Increment the share count for the page table */
712 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
713 // This could be beause MiDeletePte is now being called from strange code in Rosmm
714 PageTablePte
= MiAddressToPte(PointerPte
);
715 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
716 //Pfn2->u2.ShareCount++;
717 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2
);
719 /* Check where we should be getting the protection information from */
720 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
722 /* Get the protection from the PTE, there's no real Proto PTE data */
723 Protection
= PointerPte
->u
.Soft
.Protection
;
725 /* Remember that we did not use the proto protection */
726 OriginalProtection
= FALSE
;
730 /* Get the protection from the original PTE link */
731 OriginalPte
= &Pfn1
->OriginalPte
;
732 Protection
= OriginalPte
->u
.Soft
.Protection
;
734 /* Remember that we used the original protection */
735 OriginalProtection
= TRUE
;
737 /* Check if this was a write on a read only proto */
738 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
741 StoreInstruction
= 0;
745 /* Check if this was a write on a non-COW page */
747 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
749 /* Then the page should be marked dirty */
753 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
756 /* Did we get a locked incoming PFN? */
759 /* Drop a reference */
760 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
761 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
762 *LockedProtoPfn
= NULL
;
765 /* Release the PFN lock */
766 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
768 /* Remove caching bits */
769 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
772 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
774 /* Write combining, no caching */
775 MI_PAGE_DISABLE_CACHE(&TempPte
);
776 MI_PAGE_WRITE_COMBINED(&TempPte
);
778 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
780 /* Write through, no caching */
781 MI_PAGE_DISABLE_CACHE(&TempPte
);
782 MI_PAGE_WRITE_THROUGH(&TempPte
);
785 /* Check if this is a kernel or user address */
786 if (Address
< MmSystemRangeStart
)
788 /* Build the user PTE */
789 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
793 /* Build the kernel PTE */
794 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
797 /* Set the dirty flag if needed */
798 if (DirtyPage
) TempPte
.u
.Hard
.Dirty
= TRUE
;
801 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
803 /* Reset the protection if needed */
804 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
807 ASSERT(PointerPte
== MiAddressToPte(Address
));
808 return STATUS_SUCCESS
;
813 MiResolveTransitionFault(IN PVOID FaultingAddress
,
814 IN PMMPTE PointerPte
,
815 IN PEPROCESS CurrentProcess
,
817 OUT PVOID
*InPageBlock
)
819 PFN_NUMBER PageFrameIndex
;
822 PMMPTE PointerToPteForProtoPage
;
823 DPRINT1("Transition fault on 0x%p with PTE 0x%p in process %s\n", FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
825 /* Windowss does this check */
826 ASSERT(*InPageBlock
== NULL
);
828 /* ARM3 doesn't support this path */
829 ASSERT(OldIrql
!= MM_NOIRQL
);
831 /* Capture the PTE and make sure it's in transition format */
832 TempPte
= *PointerPte
;
833 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
834 (TempPte
.u
.Soft
.Prototype
== 0) &&
835 (TempPte
.u
.Soft
.Transition
== 1));
837 /* Get the PFN and the PFN entry */
838 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
839 DPRINT1("Transition PFN: %lx\n", PageFrameIndex
);
840 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
842 /* One more transition fault! */
843 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
845 /* This is from ARM3 -- Windows normally handles this here */
846 ASSERT(Pfn1
->u4
.InPageError
== 0);
848 /* Not supported in ARM3 */
849 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
851 /* Windows checks there's some free pages and this isn't an in-page error */
852 ASSERT(MmAvailablePages
> 0);
853 ASSERT(Pfn1
->u4
.InPageError
== 0);
855 /* ReactOS checks for this */
856 ASSERT(MmAvailablePages
> 32);
858 /* Was this a transition page in the valid list, or free/zero list? */
859 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
861 /* All Windows does here is a bunch of sanity checks */
862 DPRINT1("Transition in active list\n");
863 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
864 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
865 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
866 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
870 /* Otherwise, the page is removed from its list */
871 DPRINT1("Transition page in free/zero list\n");
872 MiUnlinkPageFromList(Pfn1
);
873 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
876 /* At this point, there should no longer be any in-page errors */
877 ASSERT(Pfn1
->u4
.InPageError
== 0);
879 /* Check if this was a PFN with no more share references */
880 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
882 /* Bump the share count and make the page valid */
883 Pfn1
->u2
.ShareCount
++;
884 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
886 /* Prototype PTEs are in paged pool, which itself might be in transition */
887 if (FaultingAddress
>= MmSystemRangeStart
)
889 /* Check if this is a paged pool PTE in transition state */
890 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
891 TempPte
= *PointerToPteForProtoPage
;
892 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
894 /* This isn't yet supported */
895 DPRINT1("Double transition fault not yet supported\n");
900 /* Build the transition PTE -- maybe a macro? */
901 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
902 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
903 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
904 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
905 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
906 MiDetermineUserGlobalPteMask(PointerPte
);
908 /* Is the PTE writeable? */
909 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
910 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
913 TempPte
.u
.Hard
.Dirty
= TRUE
;
918 TempPte
.u
.Hard
.Dirty
= FALSE
;
921 /* Write the valid PTE */
922 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
925 return STATUS_PAGE_FAULT_TRANSITION
;
930 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
932 IN PMMPTE PointerPte
,
933 IN PMMPTE PointerProtoPte
,
934 IN OUT PMMPFN
*OutPfn
,
935 OUT PVOID
*PageFileData
,
937 IN PEPROCESS Process
,
939 IN PVOID TrapInformation
)
941 MMPTE TempPte
, PteContents
;
943 PFN_NUMBER PageFrameIndex
;
945 PVOID InPageBlock
= NULL
;
947 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
948 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
949 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
950 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
952 /* Read the prototype PTE and check if it's valid */
953 TempPte
= *PointerProtoPte
;
954 if (TempPte
.u
.Hard
.Valid
== 1)
956 /* One more user of this mapped page */
957 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
958 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
959 Pfn1
->u2
.ShareCount
++;
961 /* Call it a transition */
962 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
964 /* Complete the prototype PTE fault -- this will release the PFN lock */
965 return MiCompleteProtoPteFault(StoreInstruction
,
973 /* Make sure there's some protection mask */
974 if (TempPte
.u
.Long
== 0)
976 /* Release the lock */
977 DPRINT1("Access on reserved section?\n");
978 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
979 return STATUS_ACCESS_VIOLATION
;
982 /* There is no such thing as a decommitted prototype PTE */
983 NT_ASSERT(TempPte
.u
.Long
!= MmDecommittedPte
.u
.Long
);
985 /* Check for access rights on the PTE proper */
986 PteContents
= *PointerPte
;
987 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
989 if (!PteContents
.u
.Proto
.ReadOnly
)
991 /* Check for page acess in software */
992 Status
= MiAccessCheck(PointerProtoPte
,
995 TempPte
.u
.Soft
.Protection
,
998 ASSERT(Status
== STATUS_SUCCESS
);
1000 /* Check for copy on write page */
1001 if ((TempPte
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
1003 /* Not yet supported */
1010 /* Check for copy on write page */
1011 if ((PteContents
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
1013 /* Not yet supported */
1018 /* Check for clone PTEs */
1019 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
1021 /* We don't support mapped files yet */
1022 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1024 /* We might however have transition PTEs */
1025 if (TempPte
.u
.Soft
.Transition
== 1)
1027 /* Resolve the transition fault */
1028 ASSERT(OldIrql
!= MM_NOIRQL
);
1029 Status
= MiResolveTransitionFault(Address
,
1034 ASSERT(NT_SUCCESS(Status
));
1038 /* We also don't support paged out pages */
1039 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1041 /* Resolve the demand zero fault */
1042 Status
= MiResolveDemandZeroFault(Address
,
1046 ASSERT(NT_SUCCESS(Status
));
1049 /* Complete the prototype PTE fault -- this will release the PFN lock */
1050 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1051 return MiCompleteProtoPteFault(StoreInstruction
,
1061 MiDispatchFault(IN BOOLEAN StoreInstruction
,
1063 IN PMMPTE PointerPte
,
1064 IN PMMPTE PointerProtoPte
,
1065 IN BOOLEAN Recursive
,
1066 IN PEPROCESS Process
,
1067 IN PVOID TrapInformation
,
1071 KIRQL OldIrql
, LockIrql
;
1073 PMMPTE SuperProtoPte
;
1074 PMMPFN Pfn1
, OutPfn
= NULL
;
1075 PFN_NUMBER PageFrameIndex
;
1076 PFN_COUNT PteCount
, ProcessedPtes
;
1077 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1081 /* Make sure the addresses are ok */
1082 ASSERT(PointerPte
== MiAddressToPte(Address
));
1085 // Make sure APCs are off and we're not at dispatch
1087 OldIrql
= KeGetCurrentIrql();
1088 ASSERT(OldIrql
<= APC_LEVEL
);
1089 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1092 // Grab a copy of the PTE
1094 TempPte
= *PointerPte
;
1096 /* Do we have a prototype PTE? */
1097 if (PointerProtoPte
)
1099 /* This should never happen */
1100 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
1102 /* Check if this is a kernel-mode address */
1103 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
1104 if (Address
>= MmSystemRangeStart
)
1106 /* Lock the PFN database */
1107 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1109 /* Has the PTE been made valid yet? */
1110 if (!SuperProtoPte
->u
.Hard
.Valid
)
1114 else if (PointerPte
->u
.Hard
.Valid
== 1)
1119 /* Resolve the fault -- this will release the PFN lock */
1120 Status
= MiResolveProtoPteFault(StoreInstruction
,
1130 ASSERT(Status
== STATUS_SUCCESS
);
1132 /* Complete this as a transition fault */
1133 ASSERT(OldIrql
== KeGetCurrentIrql());
1134 ASSERT(OldIrql
<= APC_LEVEL
);
1135 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1140 /* We only handle the lookup path */
1141 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
1143 /* Is there a non-image VAD? */
1145 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
1146 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
1148 /* One day, ReactOS will cluster faults */
1149 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
1150 DPRINT("Should cluster fault, but won't\n");
1153 /* Only one PTE to handle for now */
1157 /* Lock the PFN database */
1158 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1160 /* We only handle the valid path */
1161 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
1163 /* Capture the PTE */
1164 TempPte
= *PointerProtoPte
;
1166 /* Loop to handle future case of clustered faults */
1169 /* For our current usage, this should be true */
1170 if (TempPte
.u
.Hard
.Valid
== 1)
1172 /* Bump the share count on the PTE */
1173 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1174 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1175 Pfn1
->u2
.ShareCount
++;
1177 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
1178 (TempPte
.u
.Soft
.Transition
== 1))
1180 /* This is a standby page, bring it back from the cache */
1181 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
1182 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
1183 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1184 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
1186 /* Should not yet happen in ReactOS */
1187 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
1188 ASSERT(Pfn1
->u4
.InPageError
== 0);
1191 MiUnlinkPageFromList(Pfn1
);
1193 /* Bump its reference count */
1194 ASSERT(Pfn1
->u2
.ShareCount
== 0);
1195 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1196 Pfn1
->u2
.ShareCount
++;
1198 /* Make it valid again */
1199 /* This looks like another macro.... */
1200 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1201 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1202 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1203 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1204 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1205 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1206 TempPte
.u
.Hard
.Valid
= 1;
1207 TempPte
.u
.Hard
.Accessed
= 1;
1209 /* Is the PTE writeable? */
1210 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
1211 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
1214 TempPte
.u
.Hard
.Dirty
= TRUE
;
1219 TempPte
.u
.Hard
.Dirty
= FALSE
;
1222 /* Write the valid PTE */
1223 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1224 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1228 /* Page is invalid, get out of the loop */
1232 /* One more done, was it the last? */
1233 if (++ProcessedPtes
== PteCount
)
1235 /* Complete the fault */
1236 MiCompleteProtoPteFault(StoreInstruction
,
1243 /* THIS RELEASES THE PFN LOCK! */
1247 /* No clustered faults yet */
1251 /* Did we resolve the fault? */
1254 /* Bump the transition count */
1255 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1258 /* Loop all the processing we did */
1259 ASSERT(ProcessedPtes
== 0);
1261 /* Complete this as a transition fault */
1262 ASSERT(OldIrql
== KeGetCurrentIrql());
1263 ASSERT(OldIrql
<= APC_LEVEL
);
1264 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1265 return STATUS_PAGE_FAULT_TRANSITION
;
1268 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1269 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1270 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1271 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1272 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1274 /* Resolve the fault -- this will release the PFN lock */
1275 Status
= MiResolveProtoPteFault(StoreInstruction
,
1285 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1286 //ASSERT(Status != STATUS_REFAULT);
1287 //ASSERT(Status != STATUS_PTE_CHANGED);
1289 /* Did the routine clean out the PFN or should we? */
1292 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1293 ASSERT(PointerProtoPte
!= NULL
);
1294 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1296 /* Dereference the locked PFN */
1297 MiDereferencePfnAndDropLockCount(OutPfn
);
1298 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1300 /* And now release the lock */
1301 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1304 /* Complete this as a transition fault */
1305 ASSERT(OldIrql
== KeGetCurrentIrql());
1306 ASSERT(OldIrql
<= APC_LEVEL
);
1307 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1313 // The PTE must be invalid but not completely empty. It must also not be a
1314 // prototype PTE as that scenario should've been handled above. These are
1315 // all Windows checks
1317 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1318 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1319 ASSERT(TempPte
.u
.Long
!= 0);
1322 // No transition or page file software PTEs in ARM3 yet, so this must be a
1323 // demand zero page. These are all ReactOS checks
1325 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1326 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1329 // If we got this far, the PTE can only be a demand zero PTE, which is what
1330 // we want. Go handle it!
1332 Status
= MiResolveDemandZeroFault(Address
,
1336 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1337 if (NT_SUCCESS(Status
))
1340 // Make sure we're returning in a sane state and pass the status down
1342 ASSERT(OldIrql
== KeGetCurrentIrql());
1343 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1348 // Generate an access fault
1350 return STATUS_ACCESS_VIOLATION
;
1355 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1357 IN KPROCESSOR_MODE Mode
,
1358 IN PVOID TrapInformation
)
1360 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1361 PMMPTE ProtoPte
= NULL
;
1362 PMMPTE PointerPte
= MiAddressToPte(Address
);
1363 PMMPDE PointerPde
= MiAddressToPde(Address
);
1364 #if (_MI_PAGING_LEVELS >= 3)
1365 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1366 #if (_MI_PAGING_LEVELS == 4)
1367 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1371 PETHREAD CurrentThread
;
1372 PEPROCESS CurrentProcess
;
1374 PMMSUPPORT WorkingSet
;
1375 ULONG ProtectionCode
;
1377 PFN_NUMBER PageFrameIndex
;
1379 BOOLEAN IsSessionAddress
;
1381 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1383 /* Check for page fault on high IRQL */
1384 if (OldIrql
> APC_LEVEL
)
1386 #if (_MI_PAGING_LEVELS < 3)
1387 /* Could be a page table for paged pool, which we'll allow */
1388 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1389 MiCheckPdeForPagedPool(Address
);
1391 /* Check if any of the top-level pages are invalid */
1393 #if (_MI_PAGING_LEVELS == 4)
1394 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1396 #if (_MI_PAGING_LEVELS >= 3)
1397 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1399 (PointerPde
->u
.Hard
.Valid
== 0) ||
1400 (PointerPte
->u
.Hard
.Valid
== 0))
1402 /* This fault is not valid, print out some debugging help */
1403 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1406 if (TrapInformation
)
1408 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1410 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1411 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1412 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1413 #elif defined(_M_AMD64)
1414 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1415 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1416 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1420 /* Tell the trap handler to fail */
1421 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1424 /* Not yet implemented in ReactOS */
1425 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1426 ASSERT(((StoreInstruction
) && (PointerPte
->u
.Hard
.CopyOnWrite
)) == FALSE
);
1428 /* Check if this was a write */
1429 if (StoreInstruction
)
1431 /* Was it to a read-only page? */
1432 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1433 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1434 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1436 /* Crash with distinguished bugcheck code */
1437 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1440 (ULONG_PTR
)TrapInformation
,
1445 /* Nothing is actually wrong */
1446 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql
, Address
);
1447 return STATUS_SUCCESS
;
1450 /* Check for kernel fault address */
1451 if (Address
>= MmSystemRangeStart
)
1453 /* Bail out, if the fault came from user mode */
1454 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1456 #if (_MI_PAGING_LEVELS == 4)
1457 /* AMD64 system, check if PXE is invalid */
1458 if (PointerPxe
->u
.Hard
.Valid
== 0)
1460 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1463 (ULONG_PTR
)TrapInformation
,
1467 #if (_MI_PAGING_LEVELS == 4)
1468 /* PAE/AMD64 system, check if PPE is invalid */
1469 if (PointerPpe
->u
.Hard
.Valid
== 0)
1471 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1474 (ULONG_PTR
)TrapInformation
,
1478 #if (_MI_PAGING_LEVELS == 2)
1479 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1480 MiCheckPdeForPagedPool(Address
);
1483 /* Check if the PDE is invalid */
1484 if (PointerPde
->u
.Hard
.Valid
== 0)
1486 /* PDE (still) not valid, kill the system */
1487 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1490 (ULONG_PTR
)TrapInformation
,
1494 /* Not handling session faults yet */
1495 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1497 /* The PDE is valid, so read the PTE */
1498 TempPte
= *PointerPte
;
1499 if (TempPte
.u
.Hard
.Valid
== 1)
1501 /* Check if this was system space or session space */
1502 if (!IsSessionAddress
)
1504 /* Check if the PTE is still valid under PFN lock */
1505 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1506 TempPte
= *PointerPte
;
1507 if (TempPte
.u
.Hard
.Valid
)
1509 /* Check if this was a write */
1510 if (StoreInstruction
)
1512 /* Was it to a read-only page? */
1513 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1514 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1515 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1517 /* Crash with distinguished bugcheck code */
1518 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1521 (ULONG_PTR
)TrapInformation
,
1527 /* Release PFN lock and return all good */
1528 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1529 return STATUS_SUCCESS
;
1532 #if (_MI_PAGING_LEVELS == 2)
1533 /* Check if this was a session PTE that needs to remap the session PDE */
1534 if (MI_IS_SESSION_PTE(Address
))
1536 /* Do the remapping */
1537 Status
= MiCheckPdeForSessionSpace(Address
);
1538 if (!NT_SUCCESS(Status
))
1540 /* It failed, this address is invalid */
1541 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1544 (ULONG_PTR
)TrapInformation
,
1550 _WARN("Session space stuff is not implemented yet!")
1554 /* Check for a fault on the page table or hyperspace */
1555 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1557 #if (_MI_PAGING_LEVELS < 3)
1558 /* Windows does this check but I don't understand why -- it's done above! */
1559 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1561 /* Handle this as a user mode fault */
1565 /* Get the current thread */
1566 CurrentThread
= PsGetCurrentThread();
1568 /* What kind of address is this */
1569 if (!IsSessionAddress
)
1571 /* Use the system working set */
1572 WorkingSet
= &MmSystemCacheWs
;
1573 CurrentProcess
= NULL
;
1575 /* Make sure we don't have a recursive working set lock */
1576 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1577 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1578 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1579 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1580 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1581 (CurrentThread
->OwnsSessionWorkingSetShared
))
1584 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1589 /* Use the session process and working set */
1590 CurrentProcess
= HYDRA_PROCESS
;
1591 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1593 /* Make sure we don't have a recursive working set lock */
1594 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1595 (CurrentThread
->OwnsSessionWorkingSetShared
))
1598 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1602 /* Acquire the working set lock */
1603 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1604 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1606 /* Re-read PTE now that we own the lock */
1607 TempPte
= *PointerPte
;
1608 if (TempPte
.u
.Hard
.Valid
== 1)
1610 /* Check if this was a write */
1611 if (StoreInstruction
)
1613 /* Was it to a read-only page that is not copy on write? */
1614 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1615 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1616 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1617 !(TempPte
.u
.Hard
.CopyOnWrite
))
1619 /* Case not yet handled */
1620 ASSERT(!IsSessionAddress
);
1622 /* Crash with distinguished bugcheck code */
1623 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1626 (ULONG_PTR
)TrapInformation
,
1631 /* Check for read-only write in session space */
1632 if ((IsSessionAddress
) &&
1633 (StoreInstruction
) &&
1634 !(TempPte
.u
.Hard
.Write
))
1637 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1640 if (TempPte
.u
.Hard
.CopyOnWrite
== 0)
1642 /* Then this is not allowed */
1643 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1645 (ULONG_PTR
)TempPte
.u
.Long
,
1646 (ULONG_PTR
)TrapInformation
,
1650 /* Otherwise, handle COW */
1654 /* Release the working set */
1655 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1656 KeLowerIrql(LockIrql
);
1658 /* Otherwise, the PDE was probably invalid, and all is good now */
1659 return STATUS_SUCCESS
;
1662 /* Check one kind of prototype PTE */
1663 if (TempPte
.u
.Soft
.Prototype
)
1665 /* Make sure protected pool is on, and that this is a pool address */
1666 if ((MmProtectFreedNonPagedPool
) &&
1667 (((Address
>= MmNonPagedPoolStart
) &&
1668 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1669 MmSizeOfNonPagedPoolInBytes
))) ||
1670 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1671 (Address
< MmNonPagedPoolEnd
))))
1673 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1674 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1681 /* Get the prototype PTE! */
1682 ProtoPte
= MiProtoPteToPte(&TempPte
);
1684 /* Do we need to locate the prototype PTE in session space? */
1685 if ((IsSessionAddress
) &&
1686 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1688 /* Yep, go find it as well as the VAD for it */
1689 ProtoPte
= MiCheckVirtualAddress(Address
,
1692 ASSERT(ProtoPte
!= NULL
);
1697 /* We don't implement transition PTEs */
1698 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1700 /* Check for no-access PTE */
1701 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1703 /* Bugcheck the system! */
1704 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1707 (ULONG_PTR
)TrapInformation
,
1712 /* Check for demand page */
1713 if ((StoreInstruction
) &&
1715 !(IsSessionAddress
) &&
1716 !(TempPte
.u
.Hard
.Valid
))
1718 /* Get the protection code */
1719 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1720 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1722 /* Bugcheck the system! */
1723 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1726 (ULONG_PTR
)TrapInformation
,
1731 /* Now do the real fault handling */
1732 Status
= MiDispatchFault(StoreInstruction
,
1741 /* Release the working set */
1742 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1743 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1744 KeLowerIrql(LockIrql
);
1747 DPRINT("Fault resolved with status: %lx\n", Status
);
1751 /* This is a user fault */
1753 CurrentThread
= PsGetCurrentThread();
1754 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
1756 /* Lock the working set */
1757 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1759 #if (_MI_PAGING_LEVELS == 4)
1760 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1761 // also this is missing the page count increment
1762 /* Check if the PXE is valid */
1763 if (PointerPxe
->u
.Hard
.Valid
== 0)
1765 /* Right now, we only handle scenarios where the PXE is totally empty */
1766 ASSERT(PointerPxe
->u
.Long
== 0);
1768 /* Resolve a demand zero fault */
1769 Status
= MiResolveDemandZeroFault(PointerPpe
,
1774 /* We should come back with a valid PXE */
1775 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1779 #if (_MI_PAGING_LEVELS >= 3)
1780 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1781 // also this is missing the page count increment
1782 /* Check if the PPE is valid */
1783 if (PointerPpe
->u
.Hard
.Valid
== 0)
1785 /* Right now, we only handle scenarios where the PPE is totally empty */
1786 ASSERT(PointerPpe
->u
.Long
== 0);
1788 /* Resolve a demand zero fault */
1789 Status
= MiResolveDemandZeroFault(PointerPde
,
1794 /* We should come back with a valid PPE */
1795 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1799 /* Check if the PDE is valid */
1800 if (PointerPde
->u
.Hard
.Valid
== 0)
1802 /* Right now, we only handle scenarios where the PDE is totally empty */
1803 ASSERT(PointerPde
->u
.Long
== 0);
1805 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1807 UserPdeFault
= TRUE
;
1809 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1810 if (ProtectionCode
== MM_NOACCESS
)
1812 #if (_MI_PAGING_LEVELS == 2)
1813 /* Could be a page table for paged pool */
1814 MiCheckPdeForPagedPool(Address
);
1816 /* Has the code above changed anything -- is this now a valid PTE? */
1817 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1819 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1820 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1824 /* Write a demand-zero PDE */
1825 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
1827 /* Dispatch the fault */
1828 Status
= MiDispatchFault(TRUE
,
1833 PsGetCurrentProcess(),
1837 UserPdeFault
= FALSE
;
1839 /* We should come back with APCs enabled, and with a valid PDE */
1840 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1841 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1845 /* Not yet implemented in ReactOS */
1846 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1849 /* Now capture the PTE. Ignore virtual faults for now */
1850 TempPte
= *PointerPte
;
1851 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1853 /* Quick check for demand-zero */
1854 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
1856 /* Resolve the fault */
1857 MiResolveDemandZeroFault(Address
,
1862 /* Return the status */
1863 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1864 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1867 /* Check for zero PTE */
1868 if (TempPte
.u
.Long
== 0)
1870 /* Check if this address range belongs to a valid allocation (VAD) */
1871 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1872 if (ProtectionCode
== MM_NOACCESS
)
1874 #if (_MI_PAGING_LEVELS == 2)
1875 /* Could be a page table for paged pool */
1876 MiCheckPdeForPagedPool(Address
);
1878 /* Has the code above changed anything -- is this now a valid PTE? */
1879 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1881 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1882 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1887 * Check if this is a real user-mode address or actually a kernel-mode
1888 * page table for a user mode address
1890 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
1892 /* Add an additional page table reference */
1893 MiIncrementPageTableReferences(Address
);
1896 /* Is this a guard page? */
1897 if (ProtectionCode
& MM_GUARDPAGE
)
1899 /* The VAD protection cannot be MM_DECOMMIT! */
1900 NT_ASSERT(ProtectionCode
!= MM_DECOMMIT
);
1902 /* Remove the bit */
1903 TempPte
.u
.Soft
.Protection
= ProtectionCode
& ~MM_GUARDPAGE
;
1904 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1907 ASSERT(ProtoPte
== NULL
);
1908 ASSERT(CurrentThread
->ApcNeeded
== 0);
1910 /* Drop the working set lock */
1911 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1912 ASSERT(KeGetCurrentIrql() == OldIrql
);
1914 /* Handle stack expansion */
1915 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
1918 /* Did we get a prototype PTE back? */
1921 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1922 if (PointerPde
== MiAddressToPde(PTE_BASE
))
1924 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1925 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
1929 /* No, create a new PTE. First, write the protection */
1930 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1931 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1934 /* Lock the PFN database since we're going to grab a page */
1935 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1937 /* Make sure we have enough pages */
1938 ASSERT(MmAvailablePages
>= 32);
1940 /* Try to get a zero page */
1941 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1942 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1943 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1944 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1945 if (!PageFrameIndex
)
1947 /* Grab a page out of there. Later we should grab a colored zero page */
1948 PageFrameIndex
= MiRemoveAnyPage(Color
);
1949 ASSERT(PageFrameIndex
);
1951 /* Release the lock since we need to do some zeroing */
1952 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1954 /* Zero out the page, since it's for user-mode */
1955 MiZeroPfn(PageFrameIndex
);
1957 /* Grab the lock again so we can initialize the PFN entry */
1958 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1961 /* Initialize the PFN entry now */
1962 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1964 /* And we're done with the lock */
1965 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1967 /* Increment the count of pages in the process */
1968 CurrentProcess
->NumberOfPrivatePages
++;
1970 /* One more demand-zero fault */
1971 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
1973 /* Fault on user PDE, or fault on user PTE? */
1974 if (PointerPte
<= MiHighestUserPte
)
1976 /* User fault, build a user PTE */
1977 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1979 PointerPte
->u
.Soft
.Protection
,
1984 /* This is a user-mode PDE, create a kernel PTE for it */
1985 MI_MAKE_HARDWARE_PTE(&TempPte
,
1987 PointerPte
->u
.Soft
.Protection
,
1991 /* Write the dirty bit for writeable pages */
1992 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1994 /* And now write down the PTE, making the address valid */
1995 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1996 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1997 ASSERT(Pfn1
->u1
.Event
== NULL
);
2000 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2001 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2002 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2005 /* We should have a valid protection here */
2006 ASSERT(ProtectionCode
!= 0x100);
2008 /* Write the prototype PTE */
2009 TempPte
= PrototypePte
;
2010 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2011 NT_ASSERT(TempPte
.u
.Long
!= 0);
2012 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2016 /* Get the protection code and check if this is a proto PTE */
2017 ProtectionCode
= (ULONG
)TempPte
.u
.Soft
.Protection
;
2018 if (TempPte
.u
.Soft
.Prototype
)
2020 /* Do we need to go find the real PTE? */
2021 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
2023 /* Get the prototype pte and VAD for it */
2024 ProtoPte
= MiCheckVirtualAddress(Address
,
2029 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2030 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2031 return STATUS_ACCESS_VIOLATION
;
2036 /* Get the prototype PTE! */
2037 ProtoPte
= MiProtoPteToPte(&TempPte
);
2039 /* Is it read-only */
2040 if (TempPte
.u
.Proto
.ReadOnly
)
2042 /* Set read-only code */
2043 ProtectionCode
= MM_READONLY
;
2047 /* Set unknown protection */
2048 ProtectionCode
= 0x100;
2049 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
2055 /* Do we have a valid protection code? */
2056 if (ProtectionCode
!= 0x100)
2058 /* Run a software access check first, including to detect guard pages */
2059 Status
= MiAccessCheck(PointerPte
,
2065 if (Status
!= STATUS_SUCCESS
)
2068 ASSERT(CurrentThread
->ApcNeeded
== 0);
2070 /* Drop the working set lock */
2071 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2072 ASSERT(KeGetCurrentIrql() == OldIrql
);
2074 /* Did we hit a guard page? */
2075 if (Status
== STATUS_GUARD_PAGE_VIOLATION
)
2077 /* Handle stack expansion */
2078 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2081 /* Otherwise, fail back to the caller directly */
2086 /* Dispatch the fault */
2087 Status
= MiDispatchFault(StoreInstruction
,
2096 /* Return the status */
2097 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2098 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2104 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
2106 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2107 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2109 *ExecuteOptions
= 0;
2111 if (CurrentProcess
->Flags
.ExecuteDisable
)
2113 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
2116 if (CurrentProcess
->Flags
.ExecuteEnable
)
2118 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
2121 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
2123 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
2126 if (CurrentProcess
->Flags
.Permanent
)
2128 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
2131 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
2133 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
2136 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
2138 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
2141 return STATUS_SUCCESS
;
2146 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
2148 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2149 KLOCK_QUEUE_HANDLE ProcessLock
;
2150 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
2151 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2153 /* Only accept valid flags */
2154 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
2157 DPRINT1("Invalid no-execute options\n");
2158 return STATUS_INVALID_PARAMETER
;
2161 /* Change the NX state in the process lock */
2162 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
2164 /* Don't change anything if the permanent flag was set */
2165 if (!CurrentProcess
->Flags
.Permanent
)
2167 /* Start by assuming it's not disabled */
2168 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
2170 /* Now process each flag and turn the equivalent bit on */
2171 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
2173 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
2175 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
2177 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
2179 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
2181 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
2183 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
2185 CurrentProcess
->Flags
.Permanent
= TRUE
;
2187 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
2189 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2191 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
2193 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2196 /* These are turned on by default if no-execution is also eanbled */
2197 if (CurrentProcess
->Flags
.ExecuteEnable
)
2199 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2200 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2204 Status
= STATUS_SUCCESS
;
2207 /* Release the lock and return status */
2208 KiReleaseProcessLock(&ProcessLock
);