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 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 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
);
527 _In_ PFN_NUMBER DestPage
,
528 _In_ PFN_NUMBER SrcPage
)
532 PMMPFN DestPfn
, SrcPfn
;
534 const VOID
* SrcAddress
;
537 DestPfn
= MiGetPfnEntry(DestPage
);
539 SrcPfn
= MiGetPfnEntry(SrcPage
);
542 /* Grab 2 system PTEs */
543 SysPtes
= MiReserveSystemPtes(2, SystemPteSpace
);
546 /* Initialize the destination PTE */
547 TempPte
= ValidKernelPte
;
548 TempPte
.u
.Hard
.PageFrameNumber
= DestPage
;
551 if (DestPfn
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
553 /* Write combining, no caching */
554 MI_PAGE_DISABLE_CACHE(&TempPte
);
555 MI_PAGE_WRITE_COMBINED(&TempPte
);
557 else if (DestPfn
->u3
.e1
.CacheAttribute
== MiNonCached
)
559 /* Write through, no caching */
560 MI_PAGE_DISABLE_CACHE(&TempPte
);
561 MI_PAGE_WRITE_THROUGH(&TempPte
);
564 /* Make the system PTE valid with our PFN */
565 MI_WRITE_VALID_PTE(&SysPtes
[0], TempPte
);
567 /* Initialize the source PTE */
568 TempPte
= ValidKernelPte
;
569 TempPte
.u
.Hard
.PageFrameNumber
= SrcPage
;
572 if (SrcPfn
->u3
.e1
.CacheAttribute
== MiNonCached
)
574 MI_PAGE_DISABLE_CACHE(&TempPte
);
577 /* Make the system PTE valid with our PFN */
578 MI_WRITE_VALID_PTE(&SysPtes
[1], TempPte
);
580 /* Get the addresses and perform the copy */
581 DestAddress
= MiPteToAddress(&SysPtes
[0]);
582 SrcAddress
= MiPteToAddress(&SysPtes
[1]);
583 RtlCopyMemory(DestAddress
, SrcAddress
, PAGE_SIZE
);
585 /* Now get rid of it */
586 MiReleaseSystemPtes(SysPtes
, 2, SystemPteSpace
);
591 MiResolveDemandZeroFault(IN PVOID Address
,
592 IN PMMPTE PointerPte
,
593 IN PEPROCESS Process
,
596 PFN_NUMBER PageFrameNumber
= 0;
598 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
601 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
605 /* Must currently only be called by paging path */
606 if ((Process
> HYDRA_PROCESS
) && (OldIrql
== MM_NOIRQL
))
609 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
612 ASSERT(Process
->ForkInProgress
== NULL
);
614 /* Get process color */
615 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
616 ASSERT(Color
!= 0xFFFFFFFF);
618 /* We'll need a zero page */
623 /* Check if we need a zero page */
624 NeedZero
= (OldIrql
!= MM_NOIRQL
);
626 /* Session-backed image views must be zeroed */
627 if ((Process
== HYDRA_PROCESS
) &&
628 ((MI_IS_SESSION_IMAGE_ADDRESS(Address
)) ||
629 ((Address
>= MiSessionViewStart
) && (Address
< MiSessionSpaceWs
))))
634 /* Hardcode unknown color */
638 /* Check if the PFN database should be acquired */
639 if (OldIrql
== MM_NOIRQL
)
641 /* Acquire it and remember we should release it after */
642 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
646 /* We either manually locked the PFN DB, or already came with it locked */
647 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
648 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
650 /* Assert we have enough pages */
651 ASSERT(MmAvailablePages
>= 32);
654 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
655 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
657 if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
658 if (!Process
) MI_SET_PROCESS2("Kernel Demand 0");
660 /* Do we need a zero page? */
661 if (Color
!= 0xFFFFFFFF)
663 /* Try to get one, if we couldn't grab a free page and zero it */
664 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
665 if (!PageFrameNumber
)
667 /* We'll need a free page and zero it manually */
668 PageFrameNumber
= MiRemoveAnyPage(Color
);
674 /* Get a color, and see if we should grab a zero or non-zero page */
675 Color
= MI_GET_NEXT_COLOR();
678 /* Process or system doesn't want a zero page, grab anything */
679 PageFrameNumber
= MiRemoveAnyPage(Color
);
683 /* System wants a zero page, obtain one */
684 PageFrameNumber
= MiRemoveZeroPage(Color
);
689 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
691 /* Do we have the lock? */
695 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
697 /* Update performance counters */
698 if (Process
> HYDRA_PROCESS
) Process
->NumberOfPrivatePages
++;
701 /* Increment demand zero faults */
702 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
704 /* Zero the page if need be */
705 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
707 /* Fault on user PDE, or fault on user PTE? */
708 if (PointerPte
<= MiHighestUserPte
)
710 /* User fault, build a user PTE */
711 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
713 PointerPte
->u
.Soft
.Protection
,
718 /* This is a user-mode PDE, create a kernel PTE for it */
719 MI_MAKE_HARDWARE_PTE(&TempPte
,
721 PointerPte
->u
.Soft
.Protection
,
725 /* Set it dirty if it's a writable page */
726 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
729 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
731 /* Did we manually acquire the lock */
734 /* Get the PFN entry */
735 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
737 /* Windows does these sanity checks */
738 ASSERT(Pfn1
->u1
.Event
== 0);
739 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
745 DPRINT("Demand zero page has now been paged in\n");
746 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
751 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
753 IN PMMPTE PointerPte
,
754 IN PMMPTE PointerProtoPte
,
756 IN PMMPFN
* LockedProtoPfn
)
759 PMMPTE OriginalPte
, PageTablePte
;
760 ULONG_PTR Protection
;
761 PFN_NUMBER PageFrameIndex
;
763 BOOLEAN OriginalProtection
, DirtyPage
;
765 /* Must be called with an valid prototype PTE, with the PFN lock held */
766 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
767 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
770 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
772 /* Get the PFN entry and set it as a prototype PTE */
773 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
774 Pfn1
->u3
.e1
.PrototypePte
= 1;
776 /* Increment the share count for the page table */
777 PageTablePte
= MiAddressToPte(PointerPte
);
778 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
779 Pfn2
->u2
.ShareCount
++;
781 /* Check where we should be getting the protection information from */
782 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
784 /* Get the protection from the PTE, there's no real Proto PTE data */
785 Protection
= PointerPte
->u
.Soft
.Protection
;
787 /* Remember that we did not use the proto protection */
788 OriginalProtection
= FALSE
;
792 /* Get the protection from the original PTE link */
793 OriginalPte
= &Pfn1
->OriginalPte
;
794 Protection
= OriginalPte
->u
.Soft
.Protection
;
796 /* Remember that we used the original protection */
797 OriginalProtection
= TRUE
;
799 /* Check if this was a write on a read only proto */
800 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
803 StoreInstruction
= 0;
807 /* Check if this was a write on a non-COW page */
809 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
811 /* Then the page should be marked dirty */
815 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
818 /* Did we get a locked incoming PFN? */
821 /* Drop a reference */
822 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
823 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
824 *LockedProtoPfn
= NULL
;
827 /* Release the PFN lock */
828 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
830 /* Remove special/caching bits */
831 Protection
&= ~MM_PROTECT_SPECIAL
;
834 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
836 /* Write combining, no caching */
837 MI_PAGE_DISABLE_CACHE(&TempPte
);
838 MI_PAGE_WRITE_COMBINED(&TempPte
);
840 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
842 /* Write through, no caching */
843 MI_PAGE_DISABLE_CACHE(&TempPte
);
844 MI_PAGE_WRITE_THROUGH(&TempPte
);
847 /* Check if this is a kernel or user address */
848 if (Address
< MmSystemRangeStart
)
850 /* Build the user PTE */
851 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
855 /* Build the kernel PTE */
856 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
859 /* Set the dirty flag if needed */
860 if (DirtyPage
) MI_MAKE_DIRTY_PAGE(&TempPte
);
863 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
865 /* Reset the protection if needed */
866 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
869 ASSERT(PointerPte
== MiAddressToPte(Address
));
870 return STATUS_SUCCESS
;
875 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction
,
876 _In_ PVOID FaultingAddress
,
877 _In_ PMMPTE PointerPte
,
878 _In_ PEPROCESS CurrentProcess
,
879 _Inout_ KIRQL
*OldIrql
)
884 MMPTE TempPte
= *PointerPte
;
887 ULONG PageFileIndex
= TempPte
.u
.Soft
.PageFileLow
;
888 ULONG_PTR PageFileOffset
= TempPte
.u
.Soft
.PageFileHigh
;
890 /* Things we don't support yet */
891 ASSERT(CurrentProcess
> HYDRA_PROCESS
);
892 ASSERT(*OldIrql
!= MM_NOIRQL
);
894 /* We must hold the PFN lock */
895 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
897 /* Some sanity checks */
898 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
899 ASSERT(TempPte
.u
.Soft
.PageFileHigh
!= 0);
900 ASSERT(TempPte
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
);
902 /* Get any page, it will be overwritten */
903 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
904 Page
= MiRemoveAnyPage(Color
);
906 /* Initialize this PFN */
907 MiInitializePfn(Page
, PointerPte
, StoreInstruction
);
909 /* Sets the PFN as being in IO operation */
910 Pfn1
= MI_PFN_ELEMENT(Page
);
911 ASSERT(Pfn1
->u1
.Event
== NULL
);
912 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
913 ASSERT(Pfn1
->u3
.e1
.WriteInProgress
== 0);
915 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
916 Pfn1
->u1
.Event
= &Event
;
917 Pfn1
->u3
.e1
.ReadInProgress
= 1;
919 /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
920 TempPte
.u
.Soft
.Transition
= 1;
921 TempPte
.u
.Soft
.PageFileLow
= 0;
922 TempPte
.u
.Soft
.Prototype
= 0;
923 TempPte
.u
.Trans
.PageFrameNumber
= Page
;
925 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
927 /* Release the PFN lock while we proceed */
928 KeReleaseQueuedSpinLock(LockQueuePfnLock
, *OldIrql
);
930 /* Do the paging IO */
931 Status
= MiReadPageFile(Page
, PageFileIndex
, PageFileOffset
);
933 /* Lock the PFN database again */
934 *OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
936 /* Nobody should have changed that while we were not looking */
937 ASSERT(Pfn1
->u1
.Event
== &Event
);
938 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 1);
939 ASSERT(Pfn1
->u3
.e1
.WriteInProgress
== 0);
941 if (!NT_SUCCESS(Status
))
945 Pfn1
->u4
.InPageError
= 1;
946 Pfn1
->u1
.ReadStatus
= Status
;
949 /* This is now a nice and normal PFN */
950 Pfn1
->u1
.Event
= NULL
;
951 Pfn1
->u3
.e1
.ReadInProgress
= 0;
953 /* And the PTE can finally be valid */
954 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, TempPte
.u
.Trans
.Protection
, Page
);
955 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
957 /* Waiters gonna wait */
958 KeSetEvent(&Event
, IO_NO_INCREMENT
, FALSE
);
965 MiResolveTransitionFault(IN PVOID FaultingAddress
,
966 IN PMMPTE PointerPte
,
967 IN PEPROCESS CurrentProcess
,
969 OUT PVOID
*InPageBlock
)
971 PFN_NUMBER PageFrameIndex
;
974 PMMPTE PointerToPteForProtoPage
;
975 DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
976 FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
978 /* Windowss does this check */
979 ASSERT(*InPageBlock
== NULL
);
981 /* ARM3 doesn't support this path */
982 ASSERT(OldIrql
!= MM_NOIRQL
);
984 /* Capture the PTE and make sure it's in transition format */
985 TempPte
= *PointerPte
;
986 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
987 (TempPte
.u
.Soft
.Prototype
== 0) &&
988 (TempPte
.u
.Soft
.Transition
== 1));
990 /* Get the PFN and the PFN entry */
991 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
992 DPRINT("Transition PFN: %lx\n", PageFrameIndex
);
993 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
995 /* One more transition fault! */
996 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
998 /* This is from ARM3 -- Windows normally handles this here */
999 ASSERT(Pfn1
->u4
.InPageError
== 0);
1001 /* See if we should wait before terminating the fault */
1002 if (Pfn1
->u3
.e1
.ReadInProgress
== 1)
1004 DPRINT1("The page is currently being read!\n");
1005 ASSERT(Pfn1
->u1
.Event
!= NULL
);
1006 *InPageBlock
= Pfn1
->u1
.Event
;
1007 if (PointerPte
== Pfn1
->PteAddress
)
1009 DPRINT1("And this if for this particular PTE.\n");
1010 /* The PTE will be made valid by the thread serving the fault */
1011 return STATUS_SUCCESS
; // FIXME: Maybe something more descriptive
1015 /* Windows checks there's some free pages and this isn't an in-page error */
1016 ASSERT(MmAvailablePages
> 0);
1017 ASSERT(Pfn1
->u4
.InPageError
== 0);
1019 /* ReactOS checks for this */
1020 ASSERT(MmAvailablePages
> 32);
1022 /* Was this a transition page in the valid list, or free/zero list? */
1023 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
1025 /* All Windows does here is a bunch of sanity checks */
1026 DPRINT("Transition in active list\n");
1027 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
1028 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
1029 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
1030 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
1034 /* Otherwise, the page is removed from its list */
1035 DPRINT("Transition page in free/zero list\n");
1036 MiUnlinkPageFromList(Pfn1
);
1037 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
1040 /* At this point, there should no longer be any in-page errors */
1041 ASSERT(Pfn1
->u4
.InPageError
== 0);
1043 /* Check if this was a PFN with no more share references */
1044 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
1046 /* Bump the share count and make the page valid */
1047 Pfn1
->u2
.ShareCount
++;
1048 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1050 /* Prototype PTEs are in paged pool, which itself might be in transition */
1051 if (FaultingAddress
>= MmSystemRangeStart
)
1053 /* Check if this is a paged pool PTE in transition state */
1054 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
1055 TempPte
= *PointerToPteForProtoPage
;
1056 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
1058 /* This isn't yet supported */
1059 DPRINT1("Double transition fault not yet supported\n");
1064 /* Build the transition PTE -- maybe a macro? */
1065 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1066 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
1067 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
1068 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
1069 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
1070 MiDetermineUserGlobalPteMask(PointerPte
);
1072 /* Is the PTE writeable? */
1073 if ((Pfn1
->u3
.e1
.Modified
) &&
1074 MI_IS_PAGE_WRITEABLE(&TempPte
) &&
1075 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1078 MI_MAKE_DIRTY_PAGE(&TempPte
);
1083 MI_MAKE_CLEAN_PAGE(&TempPte
);
1086 /* Write the valid PTE */
1087 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1089 /* Return success */
1090 return STATUS_PAGE_FAULT_TRANSITION
;
1095 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
1097 IN PMMPTE PointerPte
,
1098 IN PMMPTE PointerProtoPte
,
1099 IN OUT PMMPFN
*OutPfn
,
1100 OUT PVOID
*PageFileData
,
1101 OUT PMMPTE PteValue
,
1102 IN PEPROCESS Process
,
1104 IN PVOID TrapInformation
)
1106 MMPTE TempPte
, PteContents
;
1108 PFN_NUMBER PageFrameIndex
;
1110 PVOID InPageBlock
= NULL
;
1113 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
1114 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
1115 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1116 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
1118 /* Read the prototype PTE and check if it's valid */
1119 TempPte
= *PointerProtoPte
;
1120 if (TempPte
.u
.Hard
.Valid
== 1)
1122 /* One more user of this mapped page */
1123 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1124 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1125 Pfn1
->u2
.ShareCount
++;
1127 /* Call it a transition */
1128 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
1130 /* Complete the prototype PTE fault -- this will release the PFN lock */
1131 return MiCompleteProtoPteFault(StoreInstruction
,
1139 /* Make sure there's some protection mask */
1140 if (TempPte
.u
.Long
== 0)
1142 /* Release the lock */
1143 DPRINT1("Access on reserved section?\n");
1144 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1145 return STATUS_ACCESS_VIOLATION
;
1148 /* There is no such thing as a decommitted prototype PTE */
1149 ASSERT(TempPte
.u
.Long
!= MmDecommittedPte
.u
.Long
);
1151 /* Check for access rights on the PTE proper */
1152 PteContents
= *PointerPte
;
1153 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
1155 if (!PteContents
.u
.Proto
.ReadOnly
)
1157 Protection
= TempPte
.u
.Soft
.Protection
;
1161 Protection
= MM_READONLY
;
1163 /* Check for page acess in software */
1164 Status
= MiAccessCheck(PointerProtoPte
,
1167 TempPte
.u
.Soft
.Protection
,
1170 ASSERT(Status
== STATUS_SUCCESS
);
1174 Protection
= PteContents
.u
.Soft
.Protection
;
1177 /* Check for writing copy on write page */
1178 if (((Protection
& MM_WRITECOPY
) == MM_WRITECOPY
) && StoreInstruction
)
1180 PFN_NUMBER PageFrameIndex
, ProtoPageFrameIndex
;
1183 /* Resolve the proto fault as if it was a read operation */
1184 Status
= MiResolveProtoPteFault(FALSE
,
1195 if (!NT_SUCCESS(Status
))
1200 /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
1201 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1203 /* And re-read the proto PTE */
1204 TempPte
= *PointerProtoPte
;
1205 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
1206 ProtoPageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1208 /* Get a new page for the private copy */
1209 if (Process
> HYDRA_PROCESS
)
1210 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1212 Color
= MI_GET_NEXT_COLOR();
1214 PageFrameIndex
= MiRemoveAnyPage(Color
);
1216 /* Perform the copy */
1217 MiCopyPfn(PageFrameIndex
, ProtoPageFrameIndex
);
1219 /* This will drop everything MiResolveProtoPteFault referenced */
1220 MiDeletePte(PointerPte
, Address
, Process
, PointerProtoPte
);
1222 /* Because now we use this */
1223 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1224 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
1226 /* Fix the protection */
1227 Protection
&= ~MM_WRITECOPY
;
1228 Protection
|= MM_READWRITE
;
1229 if (Address
< MmSystemRangeStart
)
1231 /* Build the user PTE */
1232 MI_MAKE_HARDWARE_PTE_USER(&PteContents
, PointerPte
, Protection
, PageFrameIndex
);
1236 /* Build the kernel PTE */
1237 MI_MAKE_HARDWARE_PTE(&PteContents
, PointerPte
, Protection
, PageFrameIndex
);
1240 /* And finally, write the valid PTE */
1241 MI_WRITE_VALID_PTE(PointerPte
, PteContents
);
1243 /* The caller expects us to release the PFN lock */
1244 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1248 /* Check for clone PTEs */
1249 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
1251 /* We don't support mapped files yet */
1252 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1254 /* We might however have transition PTEs */
1255 if (TempPte
.u
.Soft
.Transition
== 1)
1257 /* Resolve the transition fault */
1258 ASSERT(OldIrql
!= MM_NOIRQL
);
1259 Status
= MiResolveTransitionFault(Address
,
1264 ASSERT(NT_SUCCESS(Status
));
1268 /* We also don't support paged out pages */
1269 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1271 /* Resolve the demand zero fault */
1272 Status
= MiResolveDemandZeroFault(Address
,
1276 ASSERT(NT_SUCCESS(Status
));
1279 /* Complete the prototype PTE fault -- this will release the PFN lock */
1280 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1281 return MiCompleteProtoPteFault(StoreInstruction
,
1291 MiDispatchFault(IN BOOLEAN StoreInstruction
,
1293 IN PMMPTE PointerPte
,
1294 IN PMMPTE PointerProtoPte
,
1295 IN BOOLEAN Recursive
,
1296 IN PEPROCESS Process
,
1297 IN PVOID TrapInformation
,
1301 KIRQL OldIrql
, LockIrql
;
1303 PMMPTE SuperProtoPte
;
1304 PMMPFN Pfn1
, OutPfn
= NULL
;
1305 PFN_NUMBER PageFrameIndex
;
1306 PFN_COUNT PteCount
, ProcessedPtes
;
1307 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1311 /* Make sure the addresses are ok */
1312 ASSERT(PointerPte
== MiAddressToPte(Address
));
1315 // Make sure APCs are off and we're not at dispatch
1317 OldIrql
= KeGetCurrentIrql();
1318 ASSERT(OldIrql
<= APC_LEVEL
);
1319 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1322 // Grab a copy of the PTE
1324 TempPte
= *PointerPte
;
1326 /* Do we have a prototype PTE? */
1327 if (PointerProtoPte
)
1329 /* This should never happen */
1330 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
1332 /* Check if this is a kernel-mode address */
1333 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
1334 if (Address
>= MmSystemRangeStart
)
1336 /* Lock the PFN database */
1337 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1339 /* Has the PTE been made valid yet? */
1340 if (!SuperProtoPte
->u
.Hard
.Valid
)
1344 else if (PointerPte
->u
.Hard
.Valid
== 1)
1349 /* Resolve the fault -- this will release the PFN lock */
1350 Status
= MiResolveProtoPteFault(StoreInstruction
,
1360 ASSERT(Status
== STATUS_SUCCESS
);
1362 /* Complete this as a transition fault */
1363 ASSERT(OldIrql
== KeGetCurrentIrql());
1364 ASSERT(OldIrql
<= APC_LEVEL
);
1365 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1370 /* We only handle the lookup path */
1371 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
1373 /* Is there a non-image VAD? */
1375 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
1376 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
1378 /* One day, ReactOS will cluster faults */
1379 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
1380 DPRINT("Should cluster fault, but won't\n");
1383 /* Only one PTE to handle for now */
1387 /* Lock the PFN database */
1388 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1390 /* We only handle the valid path */
1391 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
1393 /* Capture the PTE */
1394 TempPte
= *PointerProtoPte
;
1396 /* Loop to handle future case of clustered faults */
1399 /* For our current usage, this should be true */
1400 if (TempPte
.u
.Hard
.Valid
== 1)
1402 /* Bump the share count on the PTE */
1403 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1404 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1405 Pfn1
->u2
.ShareCount
++;
1407 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
1408 (TempPte
.u
.Soft
.Transition
== 1))
1410 /* This is a standby page, bring it back from the cache */
1411 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
1412 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
1413 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1414 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
1416 /* Should not yet happen in ReactOS */
1417 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
1418 ASSERT(Pfn1
->u4
.InPageError
== 0);
1421 MiUnlinkPageFromList(Pfn1
);
1423 /* Bump its reference count */
1424 ASSERT(Pfn1
->u2
.ShareCount
== 0);
1425 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1426 Pfn1
->u2
.ShareCount
++;
1428 /* Make it valid again */
1429 /* This looks like another macro.... */
1430 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1431 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1432 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1433 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1434 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1435 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1436 TempPte
.u
.Hard
.Valid
= 1;
1437 MI_MAKE_ACCESSED_PAGE(&TempPte
);
1439 /* Is the PTE writeable? */
1440 if ((Pfn1
->u3
.e1
.Modified
) &&
1441 MI_IS_PAGE_WRITEABLE(&TempPte
) &&
1442 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1445 MI_MAKE_DIRTY_PAGE(&TempPte
);
1450 MI_MAKE_CLEAN_PAGE(&TempPte
);
1453 /* Write the valid PTE */
1454 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1455 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1459 /* Page is invalid, get out of the loop */
1463 /* One more done, was it the last? */
1464 if (++ProcessedPtes
== PteCount
)
1466 /* Complete the fault */
1467 MiCompleteProtoPteFault(StoreInstruction
,
1474 /* THIS RELEASES THE PFN LOCK! */
1478 /* No clustered faults yet */
1482 /* Did we resolve the fault? */
1485 /* Bump the transition count */
1486 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1489 /* Loop all the processing we did */
1490 ASSERT(ProcessedPtes
== 0);
1492 /* Complete this as a transition fault */
1493 ASSERT(OldIrql
== KeGetCurrentIrql());
1494 ASSERT(OldIrql
<= APC_LEVEL
);
1495 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1496 return STATUS_PAGE_FAULT_TRANSITION
;
1499 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1500 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1501 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1502 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1503 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1505 /* Resolve the fault -- this will release the PFN lock */
1506 Status
= MiResolveProtoPteFault(StoreInstruction
,
1516 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1517 //ASSERT(Status != STATUS_REFAULT);
1518 //ASSERT(Status != STATUS_PTE_CHANGED);
1520 /* Did the routine clean out the PFN or should we? */
1523 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1524 ASSERT(PointerProtoPte
!= NULL
);
1525 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1527 /* Dereference the locked PFN */
1528 MiDereferencePfnAndDropLockCount(OutPfn
);
1529 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1531 /* And now release the lock */
1532 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1535 /* Complete this as a transition fault */
1536 ASSERT(OldIrql
== KeGetCurrentIrql());
1537 ASSERT(OldIrql
<= APC_LEVEL
);
1538 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1543 /* Is this a transition PTE */
1544 if (TempPte
.u
.Soft
.Transition
)
1546 PVOID InPageBlock
= NULL
;
1547 /* Lock the PFN database */
1548 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1551 Status
= MiResolveTransitionFault(Address
, PointerPte
, Process
, LockIrql
, &InPageBlock
);
1553 ASSERT(NT_SUCCESS(Status
));
1555 /* And now release the lock and leave*/
1556 KeReleaseQueuedSpinLock(LockQueuePfnLock
, LockIrql
);
1558 if (InPageBlock
!= NULL
)
1560 /* The page is being paged in by another process */
1561 KeWaitForSingleObject(InPageBlock
, WrPageIn
, KernelMode
, FALSE
, NULL
);
1564 ASSERT(OldIrql
== KeGetCurrentIrql());
1565 ASSERT(OldIrql
<= APC_LEVEL
);
1566 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1570 /* Should we page the data back in ? */
1571 if (TempPte
.u
.Soft
.PageFileHigh
!= 0)
1573 /* Lock the PFN database */
1574 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1577 Status
= MiResolvePageFileFault(StoreInstruction
, Address
, PointerPte
, Process
, &LockIrql
);
1579 /* And now release the lock and leave*/
1580 KeReleaseQueuedSpinLock(LockQueuePfnLock
, LockIrql
);
1582 ASSERT(OldIrql
== KeGetCurrentIrql());
1583 ASSERT(OldIrql
<= APC_LEVEL
);
1584 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1589 // The PTE must be invalid but not completely empty. It must also not be a
1590 // prototype a transition or a paged-out PTE as those scenarii should've been handled above.
1591 // These are all Windows checks
1593 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1594 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1595 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1596 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1597 ASSERT(TempPte
.u
.Long
!= 0);
1600 // If we got this far, the PTE can only be a demand zero PTE, which is what
1601 // we want. Go handle it!
1603 Status
= MiResolveDemandZeroFault(Address
,
1607 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1608 if (NT_SUCCESS(Status
))
1611 // Make sure we're returning in a sane state and pass the status down
1613 ASSERT(OldIrql
== KeGetCurrentIrql());
1614 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1619 // Generate an access fault
1621 return STATUS_ACCESS_VIOLATION
;
1626 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1628 IN KPROCESSOR_MODE Mode
,
1629 IN PVOID TrapInformation
)
1631 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1632 PMMPTE ProtoPte
= NULL
;
1633 PMMPTE PointerPte
= MiAddressToPte(Address
);
1634 PMMPDE PointerPde
= MiAddressToPde(Address
);
1635 #if (_MI_PAGING_LEVELS >= 3)
1636 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1637 #if (_MI_PAGING_LEVELS == 4)
1638 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1642 PETHREAD CurrentThread
;
1643 PEPROCESS CurrentProcess
;
1645 PMMSUPPORT WorkingSet
;
1646 ULONG ProtectionCode
;
1648 PFN_NUMBER PageFrameIndex
;
1650 BOOLEAN IsSessionAddress
;
1652 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1654 /* Check for page fault on high IRQL */
1655 if (OldIrql
> APC_LEVEL
)
1657 #if (_MI_PAGING_LEVELS < 3)
1658 /* Could be a page table for paged pool, which we'll allow */
1659 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1660 MiCheckPdeForPagedPool(Address
);
1662 /* Check if any of the top-level pages are invalid */
1664 #if (_MI_PAGING_LEVELS == 4)
1665 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1667 #if (_MI_PAGING_LEVELS >= 3)
1668 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1670 (PointerPde
->u
.Hard
.Valid
== 0) ||
1671 (PointerPte
->u
.Hard
.Valid
== 0))
1673 /* This fault is not valid, print out some debugging help */
1674 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1677 if (TrapInformation
)
1679 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1681 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1682 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1683 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1684 #elif defined(_M_AMD64)
1685 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1686 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1687 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1688 #elif defined(_M_ARM)
1689 DbgPrint("MM:***PC %p\n", TrapFrame
->Pc
);
1690 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame
->R0
, TrapFrame
->R1
, TrapFrame
->R2
, TrapFrame
->R3
);
1691 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame
->R11
, TrapFrame
->R12
, TrapFrame
->Sp
, TrapFrame
->Lr
);
1695 /* Tell the trap handler to fail */
1696 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1699 /* Not yet implemented in ReactOS */
1700 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1701 ASSERT(((StoreInstruction
) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte
)) == FALSE
);
1703 /* Check if this was a write */
1704 if (StoreInstruction
)
1706 /* Was it to a read-only page? */
1707 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1708 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1709 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1711 /* Crash with distinguished bugcheck code */
1712 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1715 (ULONG_PTR
)TrapInformation
,
1720 /* Nothing is actually wrong */
1721 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql
, Address
);
1722 return STATUS_SUCCESS
;
1725 /* Check for kernel fault address */
1726 if (Address
>= MmSystemRangeStart
)
1728 /* Bail out, if the fault came from user mode */
1729 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1731 #if (_MI_PAGING_LEVELS == 2)
1732 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1733 MiCheckPdeForPagedPool(Address
);
1736 /* Check if the higher page table entries are invalid */
1738 #if (_MI_PAGING_LEVELS == 4)
1739 /* AMD64 system, check if PXE is invalid */
1740 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1742 #if (_MI_PAGING_LEVELS >= 3)
1743 /* PAE/AMD64 system, check if PPE is invalid */
1744 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1746 /* Always check if the PDE is valid */
1747 (PointerPde
->u
.Hard
.Valid
== 0))
1749 /* PXE/PPE/PDE (still) not valid, kill the system */
1750 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1753 (ULONG_PTR
)TrapInformation
,
1757 /* Not handling session faults yet */
1758 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1760 /* The PDE is valid, so read the PTE */
1761 TempPte
= *PointerPte
;
1762 if (TempPte
.u
.Hard
.Valid
== 1)
1764 /* Check if this was system space or session space */
1765 if (!IsSessionAddress
)
1767 /* Check if the PTE is still valid under PFN lock */
1768 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1769 TempPte
= *PointerPte
;
1770 if (TempPte
.u
.Hard
.Valid
)
1772 /* Check if this was a write */
1773 if (StoreInstruction
)
1775 /* Was it to a read-only page? */
1776 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1777 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1778 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1780 /* Crash with distinguished bugcheck code */
1781 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1784 (ULONG_PTR
)TrapInformation
,
1790 /* Release PFN lock and return all good */
1791 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1792 return STATUS_SUCCESS
;
1795 #if (_MI_PAGING_LEVELS == 2)
1796 /* Check if this was a session PTE that needs to remap the session PDE */
1797 if (MI_IS_SESSION_PTE(Address
))
1799 /* Do the remapping */
1800 Status
= MiCheckPdeForSessionSpace(Address
);
1801 if (!NT_SUCCESS(Status
))
1803 /* It failed, this address is invalid */
1804 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1807 (ULONG_PTR
)TrapInformation
,
1813 _WARN("Session space stuff is not implemented yet!")
1817 /* Check for a fault on the page table or hyperspace */
1818 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1820 #if (_MI_PAGING_LEVELS < 3)
1821 /* Windows does this check but I don't understand why -- it's done above! */
1822 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1824 /* Handle this as a user mode fault */
1828 /* Get the current thread */
1829 CurrentThread
= PsGetCurrentThread();
1831 /* What kind of address is this */
1832 if (!IsSessionAddress
)
1834 /* Use the system working set */
1835 WorkingSet
= &MmSystemCacheWs
;
1836 CurrentProcess
= NULL
;
1838 /* Make sure we don't have a recursive working set lock */
1839 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1840 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1841 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1842 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1843 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1844 (CurrentThread
->OwnsSessionWorkingSetShared
))
1847 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1852 /* Use the session process and working set */
1853 CurrentProcess
= HYDRA_PROCESS
;
1854 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1856 /* Make sure we don't have a recursive working set lock */
1857 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1858 (CurrentThread
->OwnsSessionWorkingSetShared
))
1861 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1865 /* Acquire the working set lock */
1866 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1867 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1869 /* Re-read PTE now that we own the lock */
1870 TempPte
= *PointerPte
;
1871 if (TempPte
.u
.Hard
.Valid
== 1)
1873 /* Check if this was a write */
1874 if (StoreInstruction
)
1876 /* Was it to a read-only page that is not copy on write? */
1877 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1878 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1879 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1880 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1882 /* Case not yet handled */
1883 ASSERT(!IsSessionAddress
);
1885 /* Crash with distinguished bugcheck code */
1886 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1889 (ULONG_PTR
)TrapInformation
,
1894 /* Check for read-only write in session space */
1895 if ((IsSessionAddress
) &&
1896 (StoreInstruction
) &&
1897 !MI_IS_PAGE_WRITEABLE(&TempPte
))
1900 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1903 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
1905 /* Then this is not allowed */
1906 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1908 (ULONG_PTR
)TempPte
.u
.Long
,
1909 (ULONG_PTR
)TrapInformation
,
1913 /* Otherwise, handle COW */
1917 /* Release the working set */
1918 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1919 KeLowerIrql(LockIrql
);
1921 /* Otherwise, the PDE was probably invalid, and all is good now */
1922 return STATUS_SUCCESS
;
1925 /* Check one kind of prototype PTE */
1926 if (TempPte
.u
.Soft
.Prototype
)
1928 /* Make sure protected pool is on, and that this is a pool address */
1929 if ((MmProtectFreedNonPagedPool
) &&
1930 (((Address
>= MmNonPagedPoolStart
) &&
1931 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1932 MmSizeOfNonPagedPoolInBytes
))) ||
1933 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1934 (Address
< MmNonPagedPoolEnd
))))
1936 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1937 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1944 /* Get the prototype PTE! */
1945 ProtoPte
= MiProtoPteToPte(&TempPte
);
1947 /* Do we need to locate the prototype PTE in session space? */
1948 if ((IsSessionAddress
) &&
1949 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1951 /* Yep, go find it as well as the VAD for it */
1952 ProtoPte
= MiCheckVirtualAddress(Address
,
1955 ASSERT(ProtoPte
!= NULL
);
1960 /* We don't implement transition PTEs */
1961 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1963 /* Check for no-access PTE */
1964 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1966 /* Bugcheck the system! */
1967 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1970 (ULONG_PTR
)TrapInformation
,
1974 /* Check for no protecton at all */
1975 if (TempPte
.u
.Soft
.Protection
== MM_ZERO_ACCESS
)
1977 /* Bugcheck the system! */
1978 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1981 (ULONG_PTR
)TrapInformation
,
1986 /* Check for demand page */
1987 if ((StoreInstruction
) &&
1989 !(IsSessionAddress
) &&
1990 !(TempPte
.u
.Hard
.Valid
))
1992 /* Get the protection code */
1993 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1994 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1996 /* Bugcheck the system! */
1997 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
2000 (ULONG_PTR
)TrapInformation
,
2005 /* Now do the real fault handling */
2006 Status
= MiDispatchFault(StoreInstruction
,
2015 /* Release the working set */
2016 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2017 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
2018 KeLowerIrql(LockIrql
);
2021 DPRINT("Fault resolved with status: %lx\n", Status
);
2025 /* This is a user fault */
2027 CurrentThread
= PsGetCurrentThread();
2028 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
2030 /* Lock the working set */
2031 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2033 #if (_MI_PAGING_LEVELS == 4)
2034 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
2035 // also this is missing the page count increment
2036 /* Check if the PXE is valid */
2037 if (PointerPxe
->u
.Hard
.Valid
== 0)
2039 /* Right now, we only handle scenarios where the PXE is totally empty */
2040 ASSERT(PointerPxe
->u
.Long
== 0);
2042 /* Resolve a demand zero fault */
2043 Status
= MiResolveDemandZeroFault(PointerPpe
,
2048 /* We should come back with a valid PXE */
2049 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2053 #if (_MI_PAGING_LEVELS >= 3)
2054 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
2055 // also this is missing the page count increment
2056 /* Check if the PPE is valid */
2057 if (PointerPpe
->u
.Hard
.Valid
== 0)
2059 /* Right now, we only handle scenarios where the PPE is totally empty */
2060 ASSERT(PointerPpe
->u
.Long
== 0);
2062 /* Resolve a demand zero fault */
2063 Status
= MiResolveDemandZeroFault(PointerPde
,
2068 /* We should come back with a valid PPE */
2069 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2073 /* Check if the PDE is valid */
2074 if (PointerPde
->u
.Hard
.Valid
== 0)
2076 /* Right now, we only handle scenarios where the PDE is totally empty */
2077 ASSERT(PointerPde
->u
.Long
== 0);
2079 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2081 UserPdeFault
= TRUE
;
2083 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2084 if (ProtectionCode
== MM_NOACCESS
)
2086 #if (_MI_PAGING_LEVELS == 2)
2087 /* Could be a page table for paged pool */
2088 MiCheckPdeForPagedPool(Address
);
2090 /* Has the code above changed anything -- is this now a valid PTE? */
2091 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
2093 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2094 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2098 /* Write a demand-zero PDE */
2099 MI_WRITE_INVALID_PDE(PointerPde
, DemandZeroPde
);
2101 /* Dispatch the fault */
2102 Status
= MiDispatchFault(TRUE
,
2107 PsGetCurrentProcess(),
2111 UserPdeFault
= FALSE
;
2113 /* We should come back with APCs enabled, and with a valid PDE */
2114 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2115 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2119 /* Not yet implemented in ReactOS */
2120 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
2123 /* Now capture the PTE. */
2124 TempPte
= *PointerPte
;
2126 /* Check if the PTE is valid */
2127 if (TempPte
.u
.Hard
.Valid
)
2129 /* Check if this is a write on a readonly PTE */
2130 if (StoreInstruction
)
2132 /* Is this a copy on write PTE? */
2133 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte
))
2135 PFN_NUMBER PageFrameIndex
, OldPageFrameIndex
;
2138 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2140 ASSERT(MmAvailablePages
> 0);
2142 /* Allocate a new page and copy it */
2143 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
));
2144 OldPageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2146 MiCopyPfn(PageFrameIndex
, OldPageFrameIndex
);
2148 /* Dereference whatever this PTE is referencing */
2149 Pfn1
= MI_PFN_ELEMENT(OldPageFrameIndex
);
2150 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 1);
2151 ASSERT(!MI_IS_PFN_DELETED(Pfn1
));
2152 ProtoPte
= Pfn1
->PteAddress
;
2153 MiDeletePte(PointerPte
, Address
, CurrentProcess
, ProtoPte
);
2155 /* And make a new shiny one with our page */
2156 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
2157 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
2158 TempPte
.u
.Hard
.Write
= 1;
2159 TempPte
.u
.Hard
.CopyOnWrite
= 0;
2161 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
2163 KeReleaseQueuedSpinLock(LockQueuePfnLock
, LockIrql
);
2165 /* Return the status */
2166 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2167 return STATUS_PAGE_FAULT_COPY_ON_WRITE
;
2170 /* Is this a read-only PTE? */
2171 if (!MI_IS_PAGE_WRITEABLE(&TempPte
))
2173 /* Return the status */
2174 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2175 return STATUS_ACCESS_VIOLATION
;
2179 /* FIXME: Execution is ignored for now, since we don't have no-execute pages yet */
2181 /* The fault has already been resolved by a different thread */
2182 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2183 return STATUS_SUCCESS
;
2186 /* Quick check for demand-zero */
2187 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
2189 /* Resolve the fault */
2190 MiResolveDemandZeroFault(Address
,
2195 /* Return the status */
2196 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2197 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2200 /* Check for zero PTE */
2201 if (TempPte
.u
.Long
== 0)
2203 /* Check if this address range belongs to a valid allocation (VAD) */
2204 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
2205 if (ProtectionCode
== MM_NOACCESS
)
2207 #if (_MI_PAGING_LEVELS == 2)
2208 /* Could be a page table for paged pool */
2209 MiCheckPdeForPagedPool(Address
);
2211 /* Has the code above changed anything -- is this now a valid PTE? */
2212 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
2214 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2215 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2220 * Check if this is a real user-mode address or actually a kernel-mode
2221 * page table for a user mode address
2223 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
2225 /* Add an additional page table reference */
2226 MiIncrementPageTableReferences(Address
);
2229 /* Is this a guard page? */
2230 if ((ProtectionCode
& MM_PROTECT_SPECIAL
) == MM_GUARDPAGE
)
2232 /* The VAD protection cannot be MM_DECOMMIT! */
2233 ASSERT(ProtectionCode
!= MM_DECOMMIT
);
2235 /* Remove the bit */
2236 TempPte
.u
.Soft
.Protection
= ProtectionCode
& ~MM_GUARDPAGE
;
2237 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2240 ASSERT(ProtoPte
== NULL
);
2241 ASSERT(CurrentThread
->ApcNeeded
== 0);
2243 /* Drop the working set lock */
2244 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2245 ASSERT(KeGetCurrentIrql() == OldIrql
);
2247 /* Handle stack expansion */
2248 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2251 /* Did we get a prototype PTE back? */
2254 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2255 if (PointerPde
== MiAddressToPde(PTE_BASE
))
2257 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2259 _WARN("This is probably completely broken!");
2260 MI_WRITE_INVALID_PDE((PMMPDE
)PointerPte
, DemandZeroPde
);
2262 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
2267 /* No, create a new PTE. First, write the protection */
2268 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2269 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2272 /* Lock the PFN database since we're going to grab a page */
2273 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2275 /* Make sure we have enough pages */
2276 ASSERT(MmAvailablePages
>= 32);
2278 /* Try to get a zero page */
2279 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
2280 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
2281 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
2282 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
2283 if (!PageFrameIndex
)
2285 /* Grab a page out of there. Later we should grab a colored zero page */
2286 PageFrameIndex
= MiRemoveAnyPage(Color
);
2287 ASSERT(PageFrameIndex
);
2289 /* Release the lock since we need to do some zeroing */
2290 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2292 /* Zero out the page, since it's for user-mode */
2293 MiZeroPfn(PageFrameIndex
);
2295 /* Grab the lock again so we can initialize the PFN entry */
2296 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2299 /* Initialize the PFN entry now */
2300 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
2302 /* And we're done with the lock */
2303 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2305 /* Increment the count of pages in the process */
2306 CurrentProcess
->NumberOfPrivatePages
++;
2308 /* One more demand-zero fault */
2309 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
2311 /* Fault on user PDE, or fault on user PTE? */
2312 if (PointerPte
<= MiHighestUserPte
)
2314 /* User fault, build a user PTE */
2315 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
2317 PointerPte
->u
.Soft
.Protection
,
2322 /* This is a user-mode PDE, create a kernel PTE for it */
2323 MI_MAKE_HARDWARE_PTE(&TempPte
,
2325 PointerPte
->u
.Soft
.Protection
,
2329 /* Write the dirty bit for writeable pages */
2330 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
2332 /* And now write down the PTE, making the address valid */
2333 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
2334 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
2335 ASSERT(Pfn1
->u1
.Event
== NULL
);
2338 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2339 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2340 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
2343 /* We should have a valid protection here */
2344 ASSERT(ProtectionCode
!= 0x100);
2346 /* Write the prototype PTE */
2347 TempPte
= PrototypePte
;
2348 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
2349 ASSERT(TempPte
.u
.Long
!= 0);
2350 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2354 /* Get the protection code and check if this is a proto PTE */
2355 ProtectionCode
= (ULONG
)TempPte
.u
.Soft
.Protection
;
2356 if (TempPte
.u
.Soft
.Prototype
)
2358 /* Do we need to go find the real PTE? */
2359 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
2361 /* Get the prototype pte and VAD for it */
2362 ProtoPte
= MiCheckVirtualAddress(Address
,
2367 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2368 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2369 return STATUS_ACCESS_VIOLATION
;
2374 /* Get the prototype PTE! */
2375 ProtoPte
= MiProtoPteToPte(&TempPte
);
2377 /* Is it read-only */
2378 if (TempPte
.u
.Proto
.ReadOnly
)
2380 /* Set read-only code */
2381 ProtectionCode
= MM_READONLY
;
2385 /* Set unknown protection */
2386 ProtectionCode
= 0x100;
2387 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
2393 /* Do we have a valid protection code? */
2394 if (ProtectionCode
!= 0x100)
2396 /* Run a software access check first, including to detect guard pages */
2397 Status
= MiAccessCheck(PointerPte
,
2403 if (Status
!= STATUS_SUCCESS
)
2406 ASSERT(CurrentThread
->ApcNeeded
== 0);
2408 /* Drop the working set lock */
2409 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2410 ASSERT(KeGetCurrentIrql() == OldIrql
);
2412 /* Did we hit a guard page? */
2413 if (Status
== STATUS_GUARD_PAGE_VIOLATION
)
2415 /* Handle stack expansion */
2416 return MiCheckForUserStackOverflow(Address
, TrapInformation
);
2419 /* Otherwise, fail back to the caller directly */
2424 /* Dispatch the fault */
2425 Status
= MiDispatchFault(StoreInstruction
,
2434 /* Return the status */
2435 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
2436 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
2442 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
2444 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2445 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2447 *ExecuteOptions
= 0;
2449 if (CurrentProcess
->Flags
.ExecuteDisable
)
2451 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
2454 if (CurrentProcess
->Flags
.ExecuteEnable
)
2456 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
2459 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
2461 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
2464 if (CurrentProcess
->Flags
.Permanent
)
2466 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
2469 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
2471 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
2474 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
2476 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
2479 return STATUS_SUCCESS
;
2484 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
2486 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
2487 KLOCK_QUEUE_HANDLE ProcessLock
;
2488 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
2489 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2491 /* Only accept valid flags */
2492 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
2495 DPRINT1("Invalid no-execute options\n");
2496 return STATUS_INVALID_PARAMETER
;
2499 /* Change the NX state in the process lock */
2500 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
2502 /* Don't change anything if the permanent flag was set */
2503 if (!CurrentProcess
->Flags
.Permanent
)
2505 /* Start by assuming it's not disabled */
2506 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
2508 /* Now process each flag and turn the equivalent bit on */
2509 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
2511 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
2513 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
2515 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
2517 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
2519 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
2521 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
2523 CurrentProcess
->Flags
.Permanent
= TRUE
;
2525 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
2527 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2529 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
2531 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2534 /* These are turned on by default if no-execution is also eanbled */
2535 if (CurrentProcess
->Flags
.ExecuteEnable
)
2537 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
2538 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
2542 Status
= STATUS_SUCCESS
;
2545 /* Release the lock and return status */
2546 KiReleaseProcessLock(&ProcessLock
);