2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/pagfault.c
5 * PURPOSE: ARM Memory Manager Page Fault Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
18 /* GLOBALS ********************************************************************/
20 #define HYDRA_PROCESS (PEPROCESS)1
22 BOOLEAN UserPdeFault
= FALSE
;
25 /* PRIVATE FUNCTIONS **********************************************************/
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");
85 return STATUS_STACK_OVERFLOW
;
88 /* Don't handle this flag yet */
89 ASSERT((PsGetCurrentProcess()->Peb
->NtGlobalFlag
& FLG_DISABLE_STACK_EXTENSION
) == 0);
91 /* Update the stack limit */
92 Teb
->NtTib
.StackLimit
= (PVOID
)((ULONG_PTR
)NextStackAddress
+ GuranteedSize
);
94 /* Now move the guard page to the next page */
95 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
100 PAGE_READWRITE
| PAGE_GUARD
);
101 if ((NT_SUCCESS(Status
) || (Status
== STATUS_ALREADY_COMMITTED
)))
104 DPRINT("Guard page handled successfully for %p\n", Address
);
105 return STATUS_PAGE_FAULT_GUARD_PAGE
;
108 /* Fail, we couldn't move the guard page */
109 DPRINT1("Guard page failure: %lx\n", Status
);
111 return STATUS_STACK_OVERFLOW
;
117 _In_ ULONG ProtectionMask
,
119 _In_ BOOLEAN Execute
)
121 #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
122 (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
123 ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
124 static const UCHAR AccessAllowedMask
[2][2] =
126 { // Protect 0 1 2 3 4 5 6 7
127 _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
128 _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
131 _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
132 _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
136 /* We want only the lower access bits */
137 ProtectionMask
&= MM_PROTECT_ACCESS
;
139 /* Look it up in the table */
140 return (AccessAllowedMask
[Write
!= 0][Execute
!= 0] >> ProtectionMask
) & 1;
145 MiAccessCheck(IN PMMPTE PointerPte
,
146 IN BOOLEAN StoreInstruction
,
147 IN KPROCESSOR_MODE PreviousMode
,
148 IN ULONG_PTR ProtectionMask
,
154 /* Check for invalid user-mode access */
155 if ((PreviousMode
== UserMode
) && (PointerPte
> MiHighestUserPte
))
157 return STATUS_ACCESS_VIOLATION
;
160 /* Capture the PTE -- is it valid? */
161 TempPte
= *PointerPte
;
162 if (TempPte
.u
.Hard
.Valid
)
164 /* Was someone trying to write to it? */
165 if (StoreInstruction
)
168 if (MI_IS_PAGE_WRITEABLE(&TempPte
) ||
169 MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
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_PROTECT_SPECIAL
) == MM_GUARDPAGE
)
192 ASSERT(ProtectionMask
!= MM_DECOMMIT
);
194 /* Attached processes can't expand their stack */
195 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION
;
197 /* No support for prototype PTEs yet */
198 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
200 /* Remove the guard page bit, and return a guard page violation */
201 TempPte
.u
.Soft
.Protection
= ProtectionMask
& ~MM_GUARDPAGE
;
202 ASSERT(TempPte
.u
.Long
!= 0);
203 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
204 return STATUS_GUARD_PAGE_VIOLATION
;
208 return STATUS_SUCCESS
;
213 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
214 OUT PULONG ProtectCode
,
215 OUT PMMVAD
*ProtoVad
)
220 /* No prototype/section support for now */
223 /* User or kernel fault? */
224 if (VirtualAddress
<= MM_HIGHEST_USER_ADDRESS
)
226 /* Special case for shared data */
227 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
)
229 /* It's a read-only page */
230 *ProtectCode
= MM_READONLY
;
231 return MmSharedUserDataPte
;
234 /* Find the VAD, it might not exist if the address is bogus */
235 Vad
= MiLocateAddress(VirtualAddress
);
238 /* Bogus virtual address */
239 *ProtectCode
= MM_NOACCESS
;
243 /* ReactOS does not handle physical memory VADs yet */
244 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
246 /* Check if it's a section, or just an allocation */
247 if (Vad
->u
.VadFlags
.PrivateMemory
)
249 /* ReactOS does not handle AWE VADs yet */
250 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
252 /* This must be a TEB/PEB VAD */
253 if (Vad
->u
.VadFlags
.MemCommit
)
255 /* It's committed, so return the VAD protection */
256 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
260 /* It has not yet been committed, so return no access */
261 *ProtectCode
= MM_NOACCESS
;
264 /* In both cases, return no PTE */
269 /* ReactOS does not supoprt these VADs yet */
270 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
271 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
273 /* Return the proto VAD */
276 /* Get the prototype PTE for this page */
277 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
278 ASSERT(PointerPte
!= NULL
);
279 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
281 /* Return the Prototype PTE and the protection for the page mapping */
282 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
286 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
288 /* This should never happen, as these addresses are handled by the double-maping */
289 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
290 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
292 /* Fail such access */
293 *ProtectCode
= MM_NOACCESS
;
297 /* Return full access rights */
298 *ProtectCode
= MM_READWRITE
;
301 else if (MI_IS_SESSION_ADDRESS(VirtualAddress
))
303 /* ReactOS does not have an image list yet, so bail out to failure case */
304 ASSERT(IsListEmpty(&MmSessionSpace
->ImageList
));
307 /* Default case -- failure */
308 *ProtectCode
= MM_NOACCESS
;
312 #if (_MI_PAGING_LEVELS == 2)
315 MiCheckPdeForSessionSpace(IN PVOID Address
)
319 PVOID SessionAddress
;
322 /* Is this a session PTE? */
323 if (MI_IS_SESSION_PTE(Address
))
325 /* Make sure the PDE for session space is valid */
326 PointerPde
= MiAddressToPde(MmSessionSpace
);
327 if (!PointerPde
->u
.Hard
.Valid
)
329 /* This means there's no valid session, bail out */
330 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
333 return STATUS_ACCESS_VIOLATION
;
336 /* Now get the session-specific page table for this address */
337 SessionAddress
= MiPteToAddress(Address
);
338 PointerPde
= MiAddressToPte(Address
);
339 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
341 /* It's not valid, so find it in the page table array */
342 Index
= ((ULONG_PTR
)SessionAddress
- (ULONG_PTR
)MmSessionBase
) >> 22;
343 TempPde
.u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
344 if (TempPde
.u
.Hard
.Valid
)
346 /* The copy is valid, so swap it in */
347 InterlockedExchange((PLONG
)PointerPde
, TempPde
.u
.Long
);
348 return STATUS_WAIT_1
;
351 /* We don't seem to have allocated a page table for this address yet? */
352 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
353 PointerPde
->u
.Long
, SessionAddress
);
355 return STATUS_ACCESS_VIOLATION
;
358 /* Is the address also a session address? If not, we're done */
359 if (!MI_IS_SESSION_ADDRESS(Address
)) return STATUS_SUCCESS
;
361 /* It is, so again get the PDE for session space */
362 PointerPde
= MiAddressToPde(MmSessionSpace
);
363 if (!PointerPde
->u
.Hard
.Valid
)
365 /* This means there's no valid session, bail out */
366 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
369 return STATUS_ACCESS_VIOLATION
;
372 /* Now get the PDE for the address itself */
373 PointerPde
= MiAddressToPde(Address
);
374 if (!PointerPde
->u
.Hard
.Valid
)
376 /* Do the swap, we should be good to go */
377 Index
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MmSessionBase
) >> 22;
378 PointerPde
->u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
379 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
381 /* We had not allocated a page table for this session address yet, fail! */
382 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
383 PointerPde
->u
.Long
, Address
);
385 return STATUS_ACCESS_VIOLATION
;
388 /* It's valid, so there's nothing to do */
389 return STATUS_SUCCESS
;
394 MiCheckPdeForPagedPool(IN PVOID Address
)
397 NTSTATUS Status
= STATUS_SUCCESS
;
399 /* Check session PDE */
400 if (MI_IS_SESSION_ADDRESS(Address
)) return MiCheckPdeForSessionSpace(Address
);
401 if (MI_IS_SESSION_PTE(Address
)) return MiCheckPdeForSessionSpace(Address
);
404 // Check if this is a fault while trying to access the page table itself
406 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
409 // Send a hint to the page fault handler that this is only a valid fault
410 // if we already detected this was access within the page table range
412 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
413 Status
= STATUS_WAIT_1
;
415 else if (Address
< MmSystemRangeStart
)
418 // This is totally illegal
420 return STATUS_ACCESS_VIOLATION
;
425 // Get the PDE for the address
427 PointerPde
= MiAddressToPde(Address
);
431 // Check if it's not valid
433 if (PointerPde
->u
.Hard
.Valid
== 0)
436 // Copy it from our double-mapped system page directory
438 InterlockedExchangePte(PointerPde
,
439 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
450 MiCheckPdeForPagedPool(IN PVOID Address
)
452 return STATUS_ACCESS_VIOLATION
;
458 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
465 /* Get the PFN for this page */
466 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
469 /* Grab a system PTE we can use to zero the page */
470 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
473 /* Initialize the PTE for it */
474 TempPte
= ValidKernelPte
;
475 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
478 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
480 /* Write combining, no caching */
481 MI_PAGE_DISABLE_CACHE(&TempPte
);
482 MI_PAGE_WRITE_COMBINED(&TempPte
);
484 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
486 /* Write through, no caching */
487 MI_PAGE_DISABLE_CACHE(&TempPte
);
488 MI_PAGE_WRITE_THROUGH(&TempPte
);
491 /* Make the system PTE valid with our PFN */
492 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
494 /* Get the address it maps to, and zero it out */
495 ZeroAddress
= MiPteToAddress(ZeroPte
);
496 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
498 /* Now get rid of it */
499 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
505 _In_ PFN_NUMBER DestPage
,
506 _In_ PFN_NUMBER SrcPage
)
510 PMMPFN DestPfn
, SrcPfn
;
512 const VOID
* SrcAddress
;
515 DestPfn
= MiGetPfnEntry(DestPage
);
517 SrcPfn
= MiGetPfnEntry(SrcPage
);
520 /* Grab 2 system PTEs */
521 SysPtes
= MiReserveSystemPtes(2, SystemPteSpace
);
524 /* Initialize the destination PTE */
525 TempPte
= ValidKernelPte
;
526 TempPte
.u
.Hard
.PageFrameNumber
= DestPage
;
529 if (DestPfn
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
531 /* Write combining, no caching */
532 MI_PAGE_DISABLE_CACHE(&TempPte
);
533 MI_PAGE_WRITE_COMBINED(&TempPte
);
535 else if (DestPfn
->u3
.e1
.CacheAttribute
== MiNonCached
)
537 /* Write through, no caching */
538 MI_PAGE_DISABLE_CACHE(&TempPte
);
539 MI_PAGE_WRITE_THROUGH(&TempPte
);
542 /* Make the system PTE valid with our PFN */
543 MI_WRITE_VALID_PTE(&SysPtes
[0], TempPte
);
545 /* Initialize the source PTE */
546 TempPte
= ValidKernelPte
;
547 TempPte
.u
.Hard
.PageFrameNumber
= SrcPage
;
550 if (SrcPfn
->u3
.e1
.CacheAttribute
== MiNonCached
)
552 MI_PAGE_DISABLE_CACHE(&TempPte
);
555 /* Make the system PTE valid with our PFN */
556 MI_WRITE_VALID_PTE(&SysPtes
[1], TempPte
);
558 /* Get the addresses and perform the copy */
559 DestAddress
= MiPteToAddress(&SysPtes
[0]);
560 SrcAddress
= MiPteToAddress(&SysPtes
[1]);
561 RtlCopyMemory(DestAddress
, SrcAddress
, PAGE_SIZE
);
563 /* Now get rid of it */
564 MiReleaseSystemPtes(SysPtes
, 2, SystemPteSpace
);
570 MiResolveDemandZeroFault(IN PVOID Address
,
571 IN PMMPTE PointerPte
,
573 IN PEPROCESS Process
,
576 PFN_NUMBER PageFrameNumber
= 0;
578 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
581 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
585 /* Must currently only be called by paging path */
586 if ((Process
> HYDRA_PROCESS
) && (OldIrql
== MM_NOIRQL
))
589 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
592 ASSERT(Process
->ForkInProgress
== NULL
);
594 /* Get process color */
595 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
596 ASSERT(Color
!= 0xFFFFFFFF);
598 /* We'll need a zero page */
603 /* Check if we need a zero page */
604 NeedZero
= (OldIrql
!= MM_NOIRQL
);
606 /* Session-backed image views must be zeroed */
607 if ((Process
== HYDRA_PROCESS
) &&
608 ((MI_IS_SESSION_IMAGE_ADDRESS(Address
)) ||
609 ((Address
>= MiSessionViewStart
) && (Address
< MiSessionSpaceWs
))))
614 /* Hardcode unknown color */
618 /* Check if the PFN database should be acquired */
619 if (OldIrql
== MM_NOIRQL
)
621 /* Acquire it and remember we should release it after */
622 OldIrql
= MiAcquirePfnLock();
626 /* We either manually locked the PFN DB, or already came with it locked */
627 MI_ASSERT_PFN_LOCK_HELD();
628 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
630 /* Assert we have enough pages */
631 ASSERT(MmAvailablePages
>= 32);
634 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
635 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
637 if (Process
== HYDRA_PROCESS
) MI_SET_PROCESS2("Hydra");
638 else if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
639 else MI_SET_PROCESS2("Kernel Demand 0");
641 /* Do we need a zero page? */
642 if (Color
!= 0xFFFFFFFF)
644 /* Try to get one, if we couldn't grab a free page and zero it */
645 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
646 if (!PageFrameNumber
)
648 /* We'll need a free page and zero it manually */
649 PageFrameNumber
= MiRemoveAnyPage(Color
);
655 /* Get a color, and see if we should grab a zero or non-zero page */
656 Color
= MI_GET_NEXT_COLOR();
659 /* Process or system doesn't want a zero page, grab anything */
660 PageFrameNumber
= MiRemoveAnyPage(Color
);
664 /* System wants a zero page, obtain one */
665 PageFrameNumber
= MiRemoveZeroPage(Color
);
670 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
672 /* Increment demand zero faults */
673 KeGetCurrentPrcb()->MmDemandZeroCount
++;
675 /* Do we have the lock? */
679 MiReleasePfnLock(OldIrql
);
681 /* Update performance counters */
682 if (Process
> HYDRA_PROCESS
) Process
->NumberOfPrivatePages
++;
685 /* Zero the page if need be */
686 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
688 /* Fault on user PDE, or fault on user PTE? */
689 if (PointerPte
<= MiHighestUserPte
)
691 /* User fault, build a user PTE */
692 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
699 /* This is a user-mode PDE, create a kernel PTE for it */
700 MI_MAKE_HARDWARE_PTE(&TempPte
,
706 /* Set it dirty if it's a writable page */
707 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
710 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
712 /* Did we manually acquire the lock */
715 /* Get the PFN entry */
716 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
718 /* Windows does these sanity checks */
719 ASSERT(Pfn1
->u1
.Event
== 0);
720 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
726 DPRINT("Demand zero page has now been paged in\n");
727 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
732 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
734 IN PMMPTE PointerPte
,
735 IN PMMPTE PointerProtoPte
,
737 IN PMMPFN
* LockedProtoPfn
)
740 PMMPTE OriginalPte
, PageTablePte
;
741 ULONG_PTR Protection
;
742 PFN_NUMBER PageFrameIndex
;
744 BOOLEAN OriginalProtection
, DirtyPage
;
746 /* Must be called with an valid prototype PTE, with the PFN lock held */
747 MI_ASSERT_PFN_LOCK_HELD();
748 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
751 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
753 /* Get the PFN entry and set it as a prototype PTE */
754 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
755 Pfn1
->u3
.e1
.PrototypePte
= 1;
757 /* Increment the share count for the page table */
758 PageTablePte
= MiAddressToPte(PointerPte
);
759 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
760 Pfn2
->u2
.ShareCount
++;
762 /* Check where we should be getting the protection information from */
763 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
765 /* Get the protection from the PTE, there's no real Proto PTE data */
766 Protection
= PointerPte
->u
.Soft
.Protection
;
768 /* Remember that we did not use the proto protection */
769 OriginalProtection
= FALSE
;
773 /* Get the protection from the original PTE link */
774 OriginalPte
= &Pfn1
->OriginalPte
;
775 Protection
= OriginalPte
->u
.Soft
.Protection
;
777 /* Remember that we used the original protection */
778 OriginalProtection
= TRUE
;
780 /* Check if this was a write on a read only proto */
781 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
784 StoreInstruction
= 0;
788 /* Check if this was a write on a non-COW page */
790 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
792 /* Then the page should be marked dirty */
796 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
799 /* Did we get a locked incoming PFN? */
802 /* Drop a reference */
803 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
804 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
805 *LockedProtoPfn
= NULL
;
808 /* Release the PFN lock */
809 MiReleasePfnLock(OldIrql
);
811 /* Remove special/caching bits */
812 Protection
&= ~MM_PROTECT_SPECIAL
;
815 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
817 /* Write combining, no caching */
818 MI_PAGE_DISABLE_CACHE(&TempPte
);
819 MI_PAGE_WRITE_COMBINED(&TempPte
);
821 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
823 /* Write through, no caching */
824 MI_PAGE_DISABLE_CACHE(&TempPte
);
825 MI_PAGE_WRITE_THROUGH(&TempPte
);
828 /* Check if this is a kernel or user address */
829 if (Address
< MmSystemRangeStart
)
831 /* Build the user PTE */
832 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
836 /* Build the kernel PTE */
837 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
840 /* Set the dirty flag if needed */
841 if (DirtyPage
) MI_MAKE_DIRTY_PAGE(&TempPte
);
844 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
846 /* Reset the protection if needed */
847 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
850 ASSERT(PointerPte
== MiAddressToPte(Address
));
851 return STATUS_SUCCESS
;
856 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction
,
857 _In_ PVOID FaultingAddress
,
858 _In_ PMMPTE PointerPte
,
859 _In_ PEPROCESS CurrentProcess
,
860 _Inout_ KIRQL
*OldIrql
)
865 MMPTE TempPte
= *PointerPte
;
867 ULONG PageFileIndex
= TempPte
.u
.Soft
.PageFileLow
;
868 ULONG_PTR PageFileOffset
= TempPte
.u
.Soft
.PageFileHigh
;
869 ULONG Protection
= TempPte
.u
.Soft
.Protection
;
871 /* Things we don't support yet */
872 ASSERT(CurrentProcess
> HYDRA_PROCESS
);
873 ASSERT(*OldIrql
!= MM_NOIRQL
);
875 /* We must hold the PFN lock */
876 MI_ASSERT_PFN_LOCK_HELD();
878 /* Some sanity checks */
879 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
880 ASSERT(TempPte
.u
.Soft
.PageFileHigh
!= 0);
881 ASSERT(TempPte
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
);
883 /* Get any page, it will be overwritten */
884 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
885 Page
= MiRemoveAnyPage(Color
);
887 /* Initialize this PFN */
888 MiInitializePfn(Page
, PointerPte
, StoreInstruction
);
890 /* Sets the PFN as being in IO operation */
891 Pfn1
= MI_PFN_ELEMENT(Page
);
892 ASSERT(Pfn1
->u1
.Event
== NULL
);
893 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
894 ASSERT(Pfn1
->u3
.e1
.WriteInProgress
== 0);
895 Pfn1
->u3
.e1
.ReadInProgress
= 1;
897 /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
898 MI_MAKE_TRANSITION_PTE(&TempPte
, Page
, Protection
);
900 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
902 /* Release the PFN lock while we proceed */
903 MiReleasePfnLock(*OldIrql
);
905 /* Do the paging IO */
906 Status
= MiReadPageFile(Page
, PageFileIndex
, PageFileOffset
);
908 /* Lock the PFN database again */
909 *OldIrql
= MiAcquirePfnLock();
911 /* Nobody should have changed that while we were not looking */
912 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 1);
913 ASSERT(Pfn1
->u3
.e1
.WriteInProgress
== 0);
915 if (!NT_SUCCESS(Status
))
919 Pfn1
->u4
.InPageError
= 1;
920 Pfn1
->u1
.ReadStatus
= Status
;
923 /* And the PTE can finally be valid */
924 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, Page
);
925 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
927 Pfn1
->u3
.e1
.ReadInProgress
= 0;
928 /* Did someone start to wait on us while we proceeded ? */
931 /* Tell them we're done */
932 KeSetEvent(Pfn1
->u1
.Event
, IO_NO_INCREMENT
, FALSE
);
940 MiResolveTransitionFault(IN BOOLEAN StoreInstruction
,
941 IN PVOID FaultingAddress
,
942 IN PMMPTE PointerPte
,
943 IN PEPROCESS CurrentProcess
,
945 OUT PKEVENT
**InPageBlock
)
947 PFN_NUMBER PageFrameIndex
;
950 PMMPTE PointerToPteForProtoPage
;
951 DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
952 FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
954 /* Windowss does this check */
955 ASSERT(*InPageBlock
== NULL
);
957 /* ARM3 doesn't support this path */
958 ASSERT(OldIrql
!= MM_NOIRQL
);
960 /* Capture the PTE and make sure it's in transition format */
961 TempPte
= *PointerPte
;
962 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
963 (TempPte
.u
.Soft
.Prototype
== 0) &&
964 (TempPte
.u
.Soft
.Transition
== 1));
966 /* Get the PFN and the PFN entry */
967 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
968 DPRINT("Transition PFN: %lx\n", PageFrameIndex
);
969 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
971 /* One more transition fault! */
972 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
974 /* This is from ARM3 -- Windows normally handles this here */
975 ASSERT(Pfn1
->u4
.InPageError
== 0);
977 /* See if we should wait before terminating the fault */
978 if ((Pfn1
->u3
.e1
.ReadInProgress
== 1)
979 || ((Pfn1
->u3
.e1
.WriteInProgress
== 1) && StoreInstruction
))
981 DPRINT1("The page is currently in a page transition !\n");
982 *InPageBlock
= &Pfn1
->u1
.Event
;
983 if (PointerPte
== Pfn1
->PteAddress
)
985 DPRINT1("And this if for this particular PTE.\n");
986 /* The PTE will be made valid by the thread serving the fault */
987 return STATUS_SUCCESS
; // FIXME: Maybe something more descriptive
991 /* Windows checks there's some free pages and this isn't an in-page error */
992 ASSERT(MmAvailablePages
> 0);
993 ASSERT(Pfn1
->u4
.InPageError
== 0);
995 /* ReactOS checks for this */
996 ASSERT(MmAvailablePages
> 32);
998 /* Was this a transition page in the valid list, or free/zero list? */
999 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
1001 /* All Windows does here is a bunch of sanity checks */
1002 DPRINT("Transition in active list\n");
1003 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
1004 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
1005 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
1006 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
1010 /* Otherwise, the page is removed from its list */
1011 DPRINT("Transition page in free/zero list\n");
1012 MiUnlinkPageFromList(Pfn1
);
1013 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
1016 /* At this point, there should no longer be any in-page errors */
1017 ASSERT(Pfn1
->u4
.InPageError
== 0);
1019 /* Check if this was a PFN with no more share references */
1020 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
1022 /* Bump the share count and make the page valid */
1023 Pfn1
->u2
.ShareCount
++;
1024 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1026 /* Prototype PTEs are in paged pool, which itself might be in transition */
1027 if (FaultingAddress
>= MmSystemRangeStart
)
1029 /* Check if this is a paged pool PTE in transition state */
1030 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
1031 TempPte
= *PointerToPteForProtoPage
;
1032 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
1034 /* This isn't yet supported */
1035 DPRINT1("Double transition fault not yet supported\n");
1040 /* Build the final PTE */
1041 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1042 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
1043 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
1044 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
1045 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
1046 MiDetermineUserGlobalPteMask(PointerPte
);
1048 /* Is the PTE writeable? */
1049 if ((Pfn1
->u3
.e1
.Modified
) &&
1050 MI_IS_PAGE_WRITEABLE(&TempPte
) &&
1051 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1054 MI_MAKE_DIRTY_PAGE(&TempPte
);
1059 MI_MAKE_CLEAN_PAGE(&TempPte
);
1062 /* Write the valid PTE */
1063 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1065 /* Return success */
1066 return STATUS_PAGE_FAULT_TRANSITION
;
1071 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
1073 IN PMMPTE PointerPte
,
1074 IN PMMPTE PointerProtoPte
,
1075 IN OUT PMMPFN
*OutPfn
,
1076 OUT PVOID
*PageFileData
,
1077 OUT PMMPTE PteValue
,
1078 IN PEPROCESS Process
,
1080 IN PVOID TrapInformation
)
1082 MMPTE TempPte
, PteContents
;
1084 PFN_NUMBER PageFrameIndex
;
1086 PKEVENT
* InPageBlock
= NULL
;
1089 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
1090 MI_ASSERT_PFN_LOCK_HELD();
1091 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1092 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
1094 /* Read the prototype PTE and check if it's valid */
1095 TempPte
= *PointerProtoPte
;
1096 if (TempPte
.u
.Hard
.Valid
== 1)
1098 /* One more user of this mapped page */
1099 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1100 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1101 Pfn1
->u2
.ShareCount
++;
1103 /* Call it a transition */
1104 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
1106 /* Complete the prototype PTE fault -- this will release the PFN lock */
1107 return MiCompleteProtoPteFault(StoreInstruction
,
1115 /* Make sure there's some protection mask */
1116 if (TempPte
.u
.Long
== 0)
1118 /* Release the lock */
1119 DPRINT1("Access on reserved section?\n");
1120 MiReleasePfnLock(OldIrql
);
1121 return STATUS_ACCESS_VIOLATION
;
1124 /* There is no such thing as a decommitted prototype PTE */
1125 ASSERT(TempPte
.u
.Long
!= MmDecommittedPte
.u
.Long
);
1127 /* Check for access rights on the PTE proper */
1128 PteContents
= *PointerPte
;
1129 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
1131 if (!PteContents
.u
.Proto
.ReadOnly
)
1133 Protection
= TempPte
.u
.Soft
.Protection
;
1137 Protection
= MM_READONLY
;
1139 /* Check for page acess in software */
1140 Status
= MiAccessCheck(PointerProtoPte
,
1143 TempPte
.u
.Soft
.Protection
,
1146 ASSERT(Status
== STATUS_SUCCESS
);
1150 Protection
= PteContents
.u
.Soft
.Protection
;
1153 /* Check for writing copy on write page */
1154 if (((Protection
& MM_WRITECOPY
) == MM_WRITECOPY
) && StoreInstruction
)
1156 PFN_NUMBER PageFrameIndex
, ProtoPageFrameIndex
;
1159 /* Resolve the proto fault as if it was a read operation */
1160 Status
= MiResolveProtoPteFault(FALSE
,
1171 if (!NT_SUCCESS(Status
))
1176 /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
1177 OldIrql
= MiAcquirePfnLock();
1179 /* And re-read the proto PTE */
1180 TempPte
= *PointerProtoPte
;
1181 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
1182 ProtoPageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1184 /* Get a new page for the private copy */
1185 if (Process
> HYDRA_PROCESS
)
1186 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1188 Color
= MI_GET_NEXT_COLOR();
1190 PageFrameIndex
= MiRemoveAnyPage(Color
);
1192 /* Perform the copy */
1193 MiCopyPfn(PageFrameIndex
, ProtoPageFrameIndex
);
1195 /* This will drop everything MiResolveProtoPteFault referenced */
1196 MiDeletePte(PointerPte
, Address
, Process
, PointerProtoPte
);
1198 /* Because now we use this */
1199 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1200 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
1202 /* Fix the protection */
1203 Protection
&= ~MM_WRITECOPY
;
1204 Protection
|= MM_READWRITE
;
1205 if (Address
< MmSystemRangeStart
)
1207 /* Build the user PTE */
1208 MI_MAKE_HARDWARE_PTE_USER(&PteContents
, PointerPte
, Protection
, PageFrameIndex
);
1212 /* Build the kernel PTE */
1213 MI_MAKE_HARDWARE_PTE(&PteContents
, PointerPte
, Protection
, PageFrameIndex
);
1216 /* And finally, write the valid PTE */
1217 MI_WRITE_VALID_PTE(PointerPte
, PteContents
);
1219 /* The caller expects us to release the PFN lock */
1220 MiReleasePfnLock(OldIrql
);
1224 /* Check for clone PTEs */
1225 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
1227 /* We don't support mapped files yet */
1228 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1230 /* We might however have transition PTEs */
1231 if (TempPte
.u
.Soft
.Transition
== 1)
1233 /* Resolve the transition fault */
1234 ASSERT(OldIrql
!= MM_NOIRQL
);
1235 Status
= MiResolveTransitionFault(StoreInstruction
,
1241 ASSERT(NT_SUCCESS(Status
));
1245 /* We also don't support paged out pages */
1246 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1248 /* Resolve the demand zero fault */
1249 Status
= MiResolveDemandZeroFault(Address
,
1251 (ULONG
)TempPte
.u
.Soft
.Protection
,
1254 ASSERT(NT_SUCCESS(Status
));
1257 /* Complete the prototype PTE fault -- this will release the PFN lock */
1258 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1259 return MiCompleteProtoPteFault(StoreInstruction
,
1269 MiDispatchFault(IN BOOLEAN StoreInstruction
,
1271 IN PMMPTE PointerPte
,
1272 IN PMMPTE PointerProtoPte
,
1273 IN BOOLEAN Recursive
,
1274 IN PEPROCESS Process
,
1275 IN PVOID TrapInformation
,
1279 KIRQL OldIrql
, LockIrql
;
1281 PMMPTE SuperProtoPte
;
1282 PMMPFN Pfn1
, OutPfn
= NULL
;
1283 PFN_NUMBER PageFrameIndex
;
1284 PFN_COUNT PteCount
, ProcessedPtes
;
1285 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1289 /* Make sure the addresses are ok */
1290 ASSERT(PointerPte
== MiAddressToPte(Address
));
1293 // Make sure APCs are off and we're not at dispatch
1295 OldIrql
= KeGetCurrentIrql();
1296 ASSERT(OldIrql
<= APC_LEVEL
);
1297 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1300 // Grab a copy of the PTE
1302 TempPte
= *PointerPte
;
1304 /* Do we have a prototype PTE? */
1305 if (PointerProtoPte
)
1307 /* This should never happen */
1308 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
1310 /* Check if this is a kernel-mode address */
1311 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
1312 if (Address
>= MmSystemRangeStart
)
1314 /* Lock the PFN database */
1315 LockIrql
= MiAcquirePfnLock();
1317 /* Has the PTE been made valid yet? */
1318 if (!SuperProtoPte
->u
.Hard
.Valid
)
1322 else if (PointerPte
->u
.Hard
.Valid
== 1)
1327 /* Resolve the fault -- this will release the PFN lock */
1328 Status
= MiResolveProtoPteFault(StoreInstruction
,
1338 ASSERT(Status
== STATUS_SUCCESS
);
1340 /* Complete this as a transition fault */
1341 ASSERT(OldIrql
== KeGetCurrentIrql());
1342 ASSERT(OldIrql
<= APC_LEVEL
);
1343 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1348 /* We only handle the lookup path */
1349 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
1351 /* Is there a non-image VAD? */
1353 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
1354 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
1356 /* One day, ReactOS will cluster faults */
1357 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
1358 DPRINT("Should cluster fault, but won't\n");
1361 /* Only one PTE to handle for now */
1365 /* Lock the PFN database */
1366 LockIrql
= MiAcquirePfnLock();
1368 /* We only handle the valid path */
1369 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
1371 /* Capture the PTE */
1372 TempPte
= *PointerProtoPte
;
1374 /* Loop to handle future case of clustered faults */
1377 /* For our current usage, this should be true */
1378 if (TempPte
.u
.Hard
.Valid
== 1)
1380 /* Bump the share count on the PTE */
1381 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1382 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1383 Pfn1
->u2
.ShareCount
++;
1385 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
1386 (TempPte
.u
.Soft
.Transition
== 1))
1388 /* This is a standby page, bring it back from the cache */
1389 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
1390 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
1391 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1392 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
1394 /* Should not yet happen in ReactOS */
1395 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
1396 ASSERT(Pfn1
->u4
.InPageError
== 0);
1399 MiUnlinkPageFromList(Pfn1
);
1401 /* Bump its reference count */
1402 ASSERT(Pfn1
->u2
.ShareCount
== 0);
1403 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1404 Pfn1
->u2
.ShareCount
++;
1406 /* Make it valid again */
1407 /* This looks like another macro.... */
1408 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1409 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1410 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1411 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1412 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1413 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1414 TempPte
.u
.Hard
.Valid
= 1;
1415 MI_MAKE_ACCESSED_PAGE(&TempPte
);
1417 /* Is the PTE writeable? */
1418 if ((Pfn1
->u3
.e1
.Modified
) &&
1419 MI_IS_PAGE_WRITEABLE(&TempPte
) &&
1420 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1423 MI_MAKE_DIRTY_PAGE(&TempPte
);
1428 MI_MAKE_CLEAN_PAGE(&TempPte
);
1431 /* Write the valid PTE */
1432 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1433 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1437 /* Page is invalid, get out of the loop */
1441 /* One more done, was it the last? */
1442 if (++ProcessedPtes
== PteCount
)
1444 /* Complete the fault */
1445 MiCompleteProtoPteFault(StoreInstruction
,
1452 /* THIS RELEASES THE PFN LOCK! */
1456 /* No clustered faults yet */
1460 /* Did we resolve the fault? */
1463 /* Bump the transition count */
1464 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1467 /* Loop all the processing we did */
1468 ASSERT(ProcessedPtes
== 0);
1470 /* Complete this as a transition fault */
1471 ASSERT(OldIrql
== KeGetCurrentIrql());
1472 ASSERT(OldIrql
<= APC_LEVEL
);
1473 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1474 return STATUS_PAGE_FAULT_TRANSITION
;
1477 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1478 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1479 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1480 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1481 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1483 /* Resolve the fault -- this will release the PFN lock */
1484 Status
= MiResolveProtoPteFault(StoreInstruction
,
1494 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1495 //ASSERT(Status != STATUS_REFAULT);
1496 //ASSERT(Status != STATUS_PTE_CHANGED);
1498 /* Did the routine clean out the PFN or should we? */
1501 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1502 ASSERT(PointerProtoPte
!= NULL
);
1503 OldIrql
= MiAcquirePfnLock();
1505 /* Dereference the locked PFN */
1506 MiDereferencePfnAndDropLockCount(OutPfn
);
1507 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1509 /* And now release the lock */
1510 MiReleasePfnLock(OldIrql
);
1513 /* Complete this as a transition fault */
1514 ASSERT(OldIrql
== KeGetCurrentIrql());
1515 ASSERT(OldIrql
<= APC_LEVEL
);
1516 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1521 /* Is this a transition PTE */
1522 if (TempPte
.u
.Soft
.Transition
)
1524 PKEVENT
* InPageBlock
= NULL
;
1525 PKEVENT PreviousPageEvent
;
1526 KEVENT CurrentPageEvent
;
1528 /* Lock the PFN database */
1529 LockIrql
= MiAcquirePfnLock();
1532 Status
= MiResolveTransitionFault(StoreInstruction
, Address
, PointerPte
, Process
, LockIrql
, &InPageBlock
);
1534 ASSERT(NT_SUCCESS(Status
));
1536 if (InPageBlock
!= NULL
)
1538 /* Another thread is reading or writing this page. Put us into the waiting queue. */
1539 KeInitializeEvent(&CurrentPageEvent
, NotificationEvent
, FALSE
);
1540 PreviousPageEvent
= *InPageBlock
;
1541 *InPageBlock
= &CurrentPageEvent
;
1544 /* And now release the lock and leave*/
1545 MiReleasePfnLock(LockIrql
);
1547 if (InPageBlock
!= NULL
)
1549 KeWaitForSingleObject(&CurrentPageEvent
, WrPageIn
, KernelMode
, FALSE
, NULL
);
1551 /* Let's the chain go on */
1552 if (PreviousPageEvent
)
1554 KeSetEvent(PreviousPageEvent
, IO_NO_INCREMENT
, FALSE
);
1558 ASSERT(OldIrql
== KeGetCurrentIrql());
1559 ASSERT(OldIrql
<= APC_LEVEL
);
1560 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1564 /* Should we page the data back in ? */
1565 if (TempPte
.u
.Soft
.PageFileHigh
!= 0)
1567 /* Lock the PFN database */
1568 LockIrql
= MiAcquirePfnLock();
1571 Status
= MiResolvePageFileFault(StoreInstruction
, Address
, PointerPte
, Process
, &LockIrql
);
1573 /* And now release the lock and leave*/
1574 MiReleasePfnLock(LockIrql
);
1576 ASSERT(OldIrql
== KeGetCurrentIrql());
1577 ASSERT(OldIrql
<= APC_LEVEL
);
1578 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1583 // The PTE must be invalid but not completely empty. It must also not be a
1584 // prototype a transition or a paged-out PTE as those scenarii should've been handled above.
1585 // These are all Windows checks
1587 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1588 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1589 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1590 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1591 ASSERT(TempPte
.u
.Long
!= 0);
1594 // If we got this far, the PTE can only be a demand zero PTE, which is what
1595 // we want. Go handle it!
1597 Status
= MiResolveDemandZeroFault(Address
,
1599 (ULONG
)TempPte
.u
.Soft
.Protection
,
1602 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1603 if (NT_SUCCESS(Status
))
1606 // Make sure we're returning in a sane state and pass the status down
1608 ASSERT(OldIrql
== KeGetCurrentIrql());
1609 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1614 // Generate an access fault
1616 return STATUS_ACCESS_VIOLATION
;
1621 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1623 IN KPROCESSOR_MODE Mode
,
1624 IN PVOID TrapInformation
)
1626 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1627 PMMPTE ProtoPte
= NULL
;
1628 PMMPTE PointerPte
= MiAddressToPte(Address
);
1629 PMMPDE PointerPde
= MiAddressToPde(Address
);
1630 #if (_MI_PAGING_LEVELS >= 3)
1631 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1632 #if (_MI_PAGING_LEVELS == 4)
1633 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1637 PETHREAD CurrentThread
;
1638 PEPROCESS CurrentProcess
;
1640 PMMSUPPORT WorkingSet
;
1641 ULONG ProtectionCode
;
1643 PFN_NUMBER PageFrameIndex
;
1645 BOOLEAN IsSessionAddress
;
1647 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1649 /* Check for page fault on high IRQL */
1650 if (OldIrql
> APC_LEVEL
)
1652 #if (_MI_PAGING_LEVELS < 3)
1653 /* Could be a page table for paged pool, which we'll allow */
1654 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1655 MiCheckPdeForPagedPool(Address
);
1657 /* Check if any of the top-level pages are invalid */
1659 #if (_MI_PAGING_LEVELS == 4)
1660 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1662 #if (_MI_PAGING_LEVELS >= 3)
1663 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1665 (PointerPde
->u
.Hard
.Valid
== 0) ||
1666 (PointerPte
->u
.Hard
.Valid
== 0))
1668 /* This fault is not valid, print out some debugging help */
1669 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1672 if (TrapInformation
)
1674 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1676 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1677 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1678 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1679 #elif defined(_M_AMD64)
1680 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1681 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1682 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1683 #elif defined(_M_ARM)
1684 DbgPrint("MM:***PC %p\n", TrapFrame
->Pc
);
1685 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame
->R0
, TrapFrame
->R1
, TrapFrame
->R2
, TrapFrame
->R3
);
1686 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame
->R11
, TrapFrame
->R12
, TrapFrame
->Sp
, TrapFrame
->Lr
);
1690 /* Tell the trap handler to fail */
1691 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1694 /* Not yet implemented in ReactOS */
1695 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1696 ASSERT(((StoreInstruction
) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte
)) == FALSE
);
1698 /* Check if this was a write */
1699 if (StoreInstruction
)
1701 /* Was it to a read-only page? */
1702 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1703 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1704 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1706 /* Crash with distinguished bugcheck code */
1707 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1710 (ULONG_PTR
)TrapInformation
,
1715 /* Nothing is actually wrong */
1716 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql
, Address
);
1717 return STATUS_SUCCESS
;
1720 /* Check for kernel fault address */
1721 if (Address
>= MmSystemRangeStart
)
1723 /* Bail out, if the fault came from user mode */
1724 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1726 #if (_MI_PAGING_LEVELS == 2)
1727 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1728 MiCheckPdeForPagedPool(Address
);
1731 /* Check if the higher page table entries are invalid */
1733 #if (_MI_PAGING_LEVELS == 4)
1734 /* AMD64 system, check if PXE is invalid */
1735 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1737 #if (_MI_PAGING_LEVELS >= 3)
1738 /* PAE/AMD64 system, check if PPE is invalid */
1739 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1741 /* Always check if the PDE is valid */
1742 (PointerPde
->u
.Hard
.Valid
== 0))
1744 /* PXE/PPE/PDE (still) not valid, kill the system */
1745 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1748 (ULONG_PTR
)TrapInformation
,
1752 /* Not handling session faults yet */
1753 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1755 /* The PDE is valid, so read the PTE */
1756 TempPte
= *PointerPte
;
1757 if (TempPte
.u
.Hard
.Valid
== 1)
1759 /* Check if this was system space or session space */
1760 if (!IsSessionAddress
)
1762 /* Check if the PTE is still valid under PFN lock */
1763 OldIrql
= MiAcquirePfnLock();
1764 TempPte
= *PointerPte
;
1765 if (TempPte
.u
.Hard
.Valid
)
1767 /* Check if this was a write */
1768 if (StoreInstruction
)
1770 /* Was it to a read-only page? */
1771 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1772 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1773 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1775 /* Crash with distinguished bugcheck code */
1776 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1779 (ULONG_PTR
)TrapInformation
,
1785 /* Release PFN lock and return all good */
1786 MiReleasePfnLock(OldIrql
);
1787 return STATUS_SUCCESS
;
1790 #if (_MI_PAGING_LEVELS == 2)
1791 /* Check if this was a session PTE that needs to remap the session PDE */
1792 if (MI_IS_SESSION_PTE(Address
))
1794 /* Do the remapping */
1795 Status
= MiCheckPdeForSessionSpace(Address
);
1796 if (!NT_SUCCESS(Status
))
1798 /* It failed, this address is invalid */
1799 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1802 (ULONG_PTR
)TrapInformation
,
1808 _WARN("Session space stuff is not implemented yet!")
1812 /* Check for a fault on the page table or hyperspace */
1813 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1815 #if (_MI_PAGING_LEVELS < 3)
1816 /* Windows does this check but I don't understand why -- it's done above! */
1817 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1819 /* Handle this as a user mode fault */
1823 /* Get the current thread */
1824 CurrentThread
= PsGetCurrentThread();
1826 /* What kind of address is this */
1827 if (!IsSessionAddress
)
1829 /* Use the system working set */
1830 WorkingSet
= &MmSystemCacheWs
;
1831 CurrentProcess
= NULL
;
1833 /* Make sure we don't have a recursive working set lock */
1834 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1835 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1836 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1837 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1838 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1839 (CurrentThread
->OwnsSessionWorkingSetShared
))
1842 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1847 /* Use the session process and working set */
1848 CurrentProcess
= HYDRA_PROCESS
;
1849 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1851 /* Make sure we don't have a recursive working set lock */
1852 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1853 (CurrentThread
->OwnsSessionWorkingSetShared
))
1856 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1860 /* Acquire the working set lock */
1861 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1862 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1864 /* Re-read PTE now that we own the lock */
1865 TempPte
= *PointerPte
;
1866 if (TempPte
.u
.Hard
.Valid
== 1)
1868 /* Check if this was a write */
1869 if (StoreInstruction
)
1871 /* Was it to a read-only page that is not copy on write? */
1872 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1873 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1874 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1875 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1877 /* Case not yet handled */
1878 ASSERT(!IsSessionAddress
);
1880 /* Crash with distinguished bugcheck code */
1881 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1884 (ULONG_PTR
)TrapInformation
,
1889 /* Check for read-only write in session space */
1890 if ((IsSessionAddress
) &&
1891 (StoreInstruction
) &&
1892 !MI_IS_PAGE_WRITEABLE(&TempPte
))
1895 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1898 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1900 /* Then this is not allowed */
1901 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1903 (ULONG_PTR
)TempPte
.u
.Long
,
1904 (ULONG_PTR
)TrapInformation
,
1908 /* Otherwise, handle COW */
1912 /* Release the working set */
1913 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1914 KeLowerIrql(LockIrql
);
1916 /* Otherwise, the PDE was probably invalid, and all is good now */
1917 return STATUS_SUCCESS
;
1920 /* Check one kind of prototype PTE */
1921 if (TempPte
.u
.Soft
.Prototype
)
1923 /* Make sure protected pool is on, and that this is a pool address */
1924 if ((MmProtectFreedNonPagedPool
) &&
1925 (((Address
>= MmNonPagedPoolStart
) &&
1926 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1927 MmSizeOfNonPagedPoolInBytes
))) ||
1928 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1929 (Address
< MmNonPagedPoolEnd
))))
1931 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1932 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1939 /* Get the prototype PTE! */
1940 ProtoPte
= MiProtoPteToPte(&TempPte
);
1942 /* Do we need to locate the prototype PTE in session space? */
1943 if ((IsSessionAddress
) &&
1944 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1946 /* Yep, go find it as well as the VAD for it */
1947 ProtoPte
= MiCheckVirtualAddress(Address
,
1950 ASSERT(ProtoPte
!= NULL
);
1955 /* We don't implement transition PTEs */
1956 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1958 /* Check for no-access PTE */
1959 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1961 /* Bugcheck the system! */
1962 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1965 (ULONG_PTR
)TrapInformation
,
1969 /* Check for no protecton at all */
1970 if (TempPte
.u
.Soft
.Protection
== MM_ZERO_ACCESS
)
1972 /* Bugcheck the system! */
1973 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1976 (ULONG_PTR
)TrapInformation
,
1981 /* Check for demand page */
1982 if ((StoreInstruction
) &&
1984 !(IsSessionAddress
) &&
1985 !(TempPte
.u
.Hard
.Valid
))
1987 /* Get the protection code */
1988 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1989 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1991 /* Bugcheck the system! */
1992 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1995 (ULONG_PTR
)TrapInformation
,
2000 /* Now do the real fault handling */
2001 Status
= MiDispatchFault(StoreInstruction
,
2010 /* Release the working set */
2011 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2012 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
2013 KeLowerIrql(LockIrql
);
2016 DPRINT("Fault resolved with status: %lx\n", Status
);
2020 /* This is a user fault */
2022 CurrentThread
= PsGetCurrentThread();
2023 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
2025 /* Lock the working set */
2026 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2028 ProtectionCode
= MM_INVALID_PROTECTION
;
2030 #if (_MI_PAGING_LEVELS == 4)
2031 /* Check if the PXE is valid */
2032 if (PointerPxe
->u
.Hard
.Valid
== 0)
2034 /* Right now, we only handle scenarios where the PXE is totally empty */
2035 ASSERT(PointerPxe
->u
.Long
== 0);
2037 /* This is only possible for user mode addresses! */
2038 ASSERT(PointerPte
<= MiHighestUserPte
);
2040 /* Check if we have a VAD */
2041 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2042 if (ProtectionCode
== MM_NOACCESS
)
2044 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2045 return STATUS_ACCESS_VIOLATION
;
2048 /* Resolve a demand zero fault */
2049 MiResolveDemandZeroFault(PointerPpe
,
2055 /* We should come back with a valid PXE */
2056 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2060 #if (_MI_PAGING_LEVELS >= 3)
2061 /* Check if the PPE is valid */
2062 if (PointerPpe
->u
.Hard
.Valid
== 0)
2064 /* Right now, we only handle scenarios where the PPE is totally empty */
2065 ASSERT(PointerPpe
->u
.Long
== 0);
2067 /* This is only possible for user mode addresses! */
2068 ASSERT(PointerPte
<= MiHighestUserPte
);
2070 /* Check if we have a VAD, unless we did this already */
2071 if (ProtectionCode
== MM_INVALID_PROTECTION
)
2073 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2076 if (ProtectionCode
== MM_NOACCESS
)
2078 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2079 return STATUS_ACCESS_VIOLATION
;
2082 /* Resolve a demand zero fault */
2083 MiResolveDemandZeroFault(PointerPde
,
2089 /* We should come back with a valid PPE */
2090 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2094 /* Check if the PDE is invalid */
2095 if (PointerPde
->u
.Hard
.Valid
== 0)
2097 /* Right now, we only handle scenarios where the PDE is totally empty */
2098 ASSERT(PointerPde
->u
.Long
== 0);
2100 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2102 UserPdeFault
= TRUE
;
2104 /* Check if we have a VAD, unless we did this already */
2105 if (ProtectionCode
== MM_INVALID_PROTECTION
)
2107 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2110 if (ProtectionCode
== MM_NOACCESS
)
2112 #if (_MI_PAGING_LEVELS == 2)
2113 /* Could be a page table for paged pool */
2114 MiCheckPdeForPagedPool(Address
);
2116 /* Has the code above changed anything -- is this now a valid PTE? */
2117 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
2119 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2120 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2124 /* Resolve a demand zero fault */
2125 MiResolveDemandZeroFault(PointerPte
,
2131 UserPdeFault
= FALSE
;
2133 /* We should come back with APCs enabled, and with a valid PDE */
2134 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2135 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2139 /* Not yet implemented in ReactOS */
2140 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
2143 /* Now capture the PTE. */
2144 TempPte
= *PointerPte
;
2146 /* Check if the PTE is valid */
2147 if (TempPte
.u
.Hard
.Valid
)
2149 /* Check if this is a write on a readonly PTE */
2150 if (StoreInstruction
)
2152 /* Is this a copy on write PTE? */
2153 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
2155 PFN_NUMBER PageFrameIndex
, OldPageFrameIndex
;
2158 LockIrql
= MiAcquirePfnLock();
2160 ASSERT(MmAvailablePages
> 0);
2162 /* Allocate a new page and copy it */
2163 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
));
2164 OldPageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2166 MiCopyPfn(PageFrameIndex
, OldPageFrameIndex
);
2168 /* Dereference whatever this PTE is referencing */
2169 Pfn1
= MI_PFN_ELEMENT(OldPageFrameIndex
);
2170 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 1);
2171 ASSERT(!MI_IS_PFN_DELETED(Pfn1
));
2172 ProtoPte
= Pfn1
->PteAddress
;
2173 MiDeletePte(PointerPte
, Address
, CurrentProcess
, ProtoPte
);
2175 /* And make a new shiny one with our page */
2176 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
2177 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
2178 TempPte
.u
.Hard
.Write
= 1;
2179 TempPte
.u
.Hard
.CopyOnWrite
= 0;
2181 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
2183 MiReleasePfnLock(LockIrql
);
2185 /* Return the status */
2186 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2187 return STATUS_PAGE_FAULT_COPY_ON_WRITE
;
2190 /* Is this a read-only PTE? */
2191 if (!MI_IS_PAGE_WRITEABLE(&TempPte
))
2193 /* Return the status */
2194 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2195 return STATUS_ACCESS_VIOLATION
;
2199 /* FIXME: Execution is ignored for now, since we don't have no-execute pages yet */
2201 /* The fault has already been resolved by a different thread */
2202 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2203 return STATUS_SUCCESS
;
2206 /* Quick check for demand-zero */
2207 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
2209 /* Resolve the fault */
2210 MiResolveDemandZeroFault(Address
,
2216 /* Return the status */
2217 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2218 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2221 /* Check for zero PTE */
2222 if (TempPte
.u
.Long
== 0)
2224 /* Check if this address range belongs to a valid allocation (VAD) */
2225 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2226 if (ProtectionCode
== MM_NOACCESS
)
2228 #if (_MI_PAGING_LEVELS == 2)
2229 /* Could be a page table for paged pool */
2230 MiCheckPdeForPagedPool(Address
);
2232 /* Has the code above changed anything -- is this now a valid PTE? */
2233 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
2235 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2236 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2241 * Check if this is a real user-mode address or actually a kernel-mode
2242 * page table for a user mode address
2244 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
2246 /* Add an additional page table reference */
2247 MiIncrementPageTableReferences(Address
);
2250 /* Is this a guard page? */
2251 if ((ProtectionCode
& MM_PROTECT_SPECIAL
) == MM_GUARDPAGE
)
2253 /* The VAD protection cannot be MM_DECOMMIT! */
2254 ASSERT(ProtectionCode
!= MM_DECOMMIT
);
2256 /* Remove the bit */
2257 TempPte
.u
.Soft
.Protection
= ProtectionCode
& ~MM_GUARDPAGE
;
2258 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2261 ASSERT(ProtoPte
== NULL
);
2262 ASSERT(CurrentThread
->ApcNeeded
== 0);
2264 /* Drop the working set lock */
2265 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2266 ASSERT(KeGetCurrentIrql() == OldIrql
);
2268 /* Handle stack expansion */
2269 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2272 /* Did we get a prototype PTE back? */
2275 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2276 if (PointerPde
== MiAddressToPde(PTE_BASE
))
2278 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2280 _WARN("This is probably completely broken!");
2281 MI_WRITE_INVALID_PDE((PMMPDE
)PointerPte
, DemandZeroPde
);
2283 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
2288 /* No, create a new PTE. First, write the protection */
2289 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2290 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2293 /* Lock the PFN database since we're going to grab a page */
2294 OldIrql
= MiAcquirePfnLock();
2296 /* Make sure we have enough pages */
2297 ASSERT(MmAvailablePages
>= 32);
2299 /* Try to get a zero page */
2300 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
2301 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
2302 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
2303 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
2304 if (!PageFrameIndex
)
2306 /* Grab a page out of there. Later we should grab a colored zero page */
2307 PageFrameIndex
= MiRemoveAnyPage(Color
);
2308 ASSERT(PageFrameIndex
);
2310 /* Release the lock since we need to do some zeroing */
2311 MiReleasePfnLock(OldIrql
);
2313 /* Zero out the page, since it's for user-mode */
2314 MiZeroPfn(PageFrameIndex
);
2316 /* Grab the lock again so we can initialize the PFN entry */
2317 OldIrql
= MiAcquirePfnLock();
2320 /* Initialize the PFN entry now */
2321 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
2323 /* Increment the count of pages in the process */
2324 CurrentProcess
->NumberOfPrivatePages
++;
2326 /* One more demand-zero fault */
2327 KeGetCurrentPrcb()->MmDemandZeroCount
++;
2329 /* And we're done with the lock */
2330 MiReleasePfnLock(OldIrql
);
2332 /* Fault on user PDE, or fault on user PTE? */
2333 if (PointerPte
<= MiHighestUserPte
)
2335 /* User fault, build a user PTE */
2336 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
2338 PointerPte
->u
.Soft
.Protection
,
2343 /* This is a user-mode PDE, create a kernel PTE for it */
2344 MI_MAKE_HARDWARE_PTE(&TempPte
,
2346 PointerPte
->u
.Soft
.Protection
,
2350 /* Write the dirty bit for writeable pages */
2351 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
2353 /* And now write down the PTE, making the address valid */
2354 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
2355 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
2356 ASSERT(Pfn1
->u1
.Event
== NULL
);
2359 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2360 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2361 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2364 /* We should have a valid protection here */
2365 ASSERT(ProtectionCode
!= 0x100);
2367 /* Write the prototype PTE */
2368 TempPte
= PrototypePte
;
2369 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2370 ASSERT(TempPte
.u
.Long
!= 0);
2371 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2375 /* Get the protection code and check if this is a proto PTE */
2376 ProtectionCode
= (ULONG
)TempPte
.u
.Soft
.Protection
;
2377 if (TempPte
.u
.Soft
.Prototype
)
2379 /* Do we need to go find the real PTE? */
2380 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
2382 /* Get the prototype pte and VAD for it */
2383 ProtoPte
= MiCheckVirtualAddress(Address
,
2388 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2389 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2390 return STATUS_ACCESS_VIOLATION
;
2395 /* Get the prototype PTE! */
2396 ProtoPte
= MiProtoPteToPte(&TempPte
);
2398 /* Is it read-only */
2399 if (TempPte
.u
.Proto
.ReadOnly
)
2401 /* Set read-only code */
2402 ProtectionCode
= MM_READONLY
;
2406 /* Set unknown protection */
2407 ProtectionCode
= 0x100;
2408 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
2414 /* Do we have a valid protection code? */
2415 if (ProtectionCode
!= 0x100)
2417 /* Run a software access check first, including to detect guard pages */
2418 Status
= MiAccessCheck(PointerPte
,
2424 if (Status
!= STATUS_SUCCESS
)
2427 ASSERT(CurrentThread
->ApcNeeded
== 0);
2429 /* Drop the working set lock */
2430 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2431 ASSERT(KeGetCurrentIrql() == OldIrql
);
2433 /* Did we hit a guard page? */
2434 if (Status
== STATUS_GUARD_PAGE_VIOLATION
)
2436 /* Handle stack expansion */
2437 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2440 /* Otherwise, fail back to the caller directly */
2445 /* Dispatch the fault */
2446 Status
= MiDispatchFault(StoreInstruction
,
2455 /* Return the status */
2456 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2457 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2463 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
2465 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2466 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2468 *ExecuteOptions
= 0;
2470 if (CurrentProcess
->Flags
.ExecuteDisable
)
2472 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
2475 if (CurrentProcess
->Flags
.ExecuteEnable
)
2477 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
2480 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
2482 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
2485 if (CurrentProcess
->Flags
.Permanent
)
2487 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
2490 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
2492 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
2495 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
2497 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
2500 return STATUS_SUCCESS
;
2505 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
2507 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2508 KLOCK_QUEUE_HANDLE ProcessLock
;
2509 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
2510 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2512 /* Only accept valid flags */
2513 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
2516 DPRINT1("Invalid no-execute options\n");
2517 return STATUS_INVALID_PARAMETER
;
2520 /* Change the NX state in the process lock */
2521 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
2523 /* Don't change anything if the permanent flag was set */
2524 if (!CurrentProcess
->Flags
.Permanent
)
2526 /* Start by assuming it's not disabled */
2527 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
2529 /* Now process each flag and turn the equivalent bit on */
2530 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
2532 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
2534 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
2536 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
2538 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
2540 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
2542 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
2544 CurrentProcess
->Flags
.Permanent
= TRUE
;
2546 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
2548 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2550 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
2552 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2555 /* These are turned on by default if no-execution is also eanbled */
2556 if (CurrentProcess
->Flags
.ExecuteEnable
)
2558 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2559 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2563 Status
= STATUS_SUCCESS
;
2566 /* Release the lock and return status */
2567 KiReleaseProcessLock(&ProcessLock
);