2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/pagfault.c
5 * PURPOSE: ARM Memory Manager Page Fault Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "../ARM3/miarm.h"
18 /* GLOBALS ********************************************************************/
20 #define HYDRA_PROCESS (PEPROCESS)1
22 BOOLEAN UserPdeFault
= FALSE
;
25 /* PRIVATE FUNCTIONS **********************************************************/
29 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
30 OUT PULONG ProtectCode
,
36 /* No prototype/section support for now */
39 /* User or kernel fault? */
40 if (VirtualAddress
<= MM_HIGHEST_USER_ADDRESS
)
42 /* Special case for shared data */
43 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
)
45 /* It's a read-only page */
46 *ProtectCode
= MM_READONLY
;
47 return MmSharedUserDataPte
;
50 /* Find the VAD, it might not exist if the address is bogus */
51 Vad
= MiLocateAddress(VirtualAddress
);
54 /* Bogus virtual address */
55 *ProtectCode
= MM_NOACCESS
;
59 /* ReactOS does not handle physical memory VADs yet */
60 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
62 /* Check if it's a section, or just an allocation */
63 if (Vad
->u
.VadFlags
.PrivateMemory
)
65 /* ReactOS does not handle AWE VADs yet */
66 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
68 /* This must be a TEB/PEB VAD */
69 if (Vad
->u
.VadFlags
.MemCommit
)
71 /* It's committed, so return the VAD protection */
72 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
76 /* It has not yet been committed, so return no access */
77 *ProtectCode
= MM_NOACCESS
;
80 /* In both cases, return no PTE */
85 /* ReactOS does not supoprt these VADs yet */
86 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
87 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
89 /* Return the proto VAD */
92 /* Get the prototype PTE for this page */
93 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
94 ASSERT(PointerPte
!= NULL
);
95 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
97 /* Return the Prototype PTE and the protection for the page mapping */
98 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
102 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
104 /* This should never happen, as these addresses are handled by the double-maping */
105 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
106 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
108 /* Fail such access */
109 *ProtectCode
= MM_NOACCESS
;
113 /* Return full access rights */
114 *ProtectCode
= MM_READWRITE
;
117 else if (MI_IS_SESSION_ADDRESS(VirtualAddress
))
119 /* ReactOS does not have an image list yet, so bail out to failure case */
120 ASSERT(IsListEmpty(&MmSessionSpace
->ImageList
));
123 /* Default case -- failure */
124 *ProtectCode
= MM_NOACCESS
;
128 #if (_MI_PAGING_LEVELS == 2)
131 MiSynchronizeSystemPde(PMMPDE PointerPde
)
136 /* Get the Index from the PDE */
137 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
139 /* Copy the PDE from the double-mapped system page directory */
140 SystemPde
= MmSystemPagePtes
[Index
];
141 *PointerPde
= SystemPde
;
143 /* Make sure we re-read the PDE and PTE */
144 KeMemoryBarrierWithoutFence();
146 /* Return, if we had success */
147 return (BOOLEAN
)SystemPde
.u
.Hard
.Valid
;
152 MiCheckPdeForSessionSpace(IN PVOID Address
)
156 PVOID SessionPageTable
;
159 /* Is this a session PTE? */
160 if (MI_IS_SESSION_PTE(Address
))
162 /* Make sure the PDE for session space is valid */
163 PointerPde
= MiAddressToPde(MmSessionSpace
);
164 if (!PointerPde
->u
.Hard
.Valid
)
166 /* This means there's no valid session, bail out */
167 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
170 return STATUS_ACCESS_VIOLATION
;
173 /* Now get the session-specific page table for this address */
174 SessionPageTable
= MiPteToAddress(Address
);
175 PointerPde
= MiPteToAddress(Address
);
176 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
178 /* It's not valid, so find it in the page table array */
179 Index
= ((ULONG_PTR
)SessionPageTable
- (ULONG_PTR
)MmSessionBase
) >> 22;
180 TempPde
.u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
181 if (TempPde
.u
.Hard
.Valid
)
183 /* The copy is valid, so swap it in */
184 InterlockedExchange((PLONG
)PointerPde
, TempPde
.u
.Long
);
185 return STATUS_WAIT_1
;
188 /* We don't seem to have allocated a page table for this address yet? */
189 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
190 PointerPde
->u
.Long
, SessionPageTable
);
192 return STATUS_ACCESS_VIOLATION
;
195 /* Is the address also a session address? If not, we're done */
196 if (!MI_IS_SESSION_ADDRESS(Address
)) return STATUS_SUCCESS
;
198 /* It is, so again get the PDE for session space */
199 PointerPde
= MiAddressToPde(MmSessionSpace
);
200 if (!PointerPde
->u
.Hard
.Valid
)
202 /* This means there's no valid session, bail out */
203 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
206 return STATUS_ACCESS_VIOLATION
;
209 /* Now get the PDE for the address itself */
210 PointerPde
= MiAddressToPde(Address
);
211 if (!PointerPde
->u
.Hard
.Valid
)
213 /* Do the swap, we should be good to go */
214 Index
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MmSessionBase
) >> 22;
215 PointerPde
->u
.Long
= MmSessionSpace
->PageTables
[Index
].u
.Long
;
216 if (PointerPde
->u
.Hard
.Valid
) return STATUS_WAIT_1
;
218 /* We had not allocated a page table for this session address yet, fail! */
219 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
220 PointerPde
->u
.Long
, Address
);
222 return STATUS_ACCESS_VIOLATION
;
225 /* It's valid, so there's nothing to do */
226 return STATUS_SUCCESS
;
231 MiCheckPdeForPagedPool(IN PVOID Address
)
234 NTSTATUS Status
= STATUS_SUCCESS
;
236 /* Check session PDE */
237 if (MI_IS_SESSION_ADDRESS(Address
)) return MiCheckPdeForSessionSpace(Address
);
238 if (MI_IS_SESSION_PTE(Address
)) return MiCheckPdeForSessionSpace(Address
);
241 // Check if this is a fault while trying to access the page table itself
243 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
246 // Send a hint to the page fault handler that this is only a valid fault
247 // if we already detected this was access within the page table range
249 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
250 Status
= STATUS_WAIT_1
;
252 else if (Address
< MmSystemRangeStart
)
255 // This is totally illegal
257 return STATUS_ACCESS_VIOLATION
;
262 // Get the PDE for the address
264 PointerPde
= MiAddressToPde(Address
);
268 // Check if it's not valid
270 if (PointerPde
->u
.Hard
.Valid
== 0)
273 // Copy it from our double-mapped system page directory
275 InterlockedExchangePte(PointerPde
,
276 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
287 MiCheckPdeForPagedPool(IN PVOID Address
)
289 return STATUS_ACCESS_VIOLATION
;
295 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
302 /* Get the PFN for this page */
303 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
306 /* Grab a system PTE we can use to zero the page */
307 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
310 /* Initialize the PTE for it */
311 TempPte
= ValidKernelPte
;
312 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
315 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
317 /* Write combining, no caching */
318 MI_PAGE_DISABLE_CACHE(&TempPte
);
319 MI_PAGE_WRITE_COMBINED(&TempPte
);
321 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
323 /* Write through, no caching */
324 MI_PAGE_DISABLE_CACHE(&TempPte
);
325 MI_PAGE_WRITE_THROUGH(&TempPte
);
328 /* Make the system PTE valid with our PFN */
329 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
331 /* Get the address it maps to, and zero it out */
332 ZeroAddress
= MiPteToAddress(ZeroPte
);
333 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
335 /* Now get rid of it */
336 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
341 MiResolveDemandZeroFault(IN PVOID Address
,
342 IN PMMPTE PointerPte
,
343 IN PEPROCESS Process
,
346 PFN_NUMBER PageFrameNumber
= 0;
348 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
351 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
355 /* Must currently only be called by paging path */
356 if ((Process
> HYDRA_PROCESS
) && (OldIrql
== MM_NOIRQL
))
359 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
362 ASSERT(Process
->ForkInProgress
== NULL
);
364 /* Get process color */
365 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
366 ASSERT(Color
!= 0xFFFFFFFF);
368 /* We'll need a zero page */
373 /* Check if we need a zero page */
374 NeedZero
= (OldIrql
!= MM_NOIRQL
);
376 /* Session-backed image views must be zeroed */
377 if ((Process
== HYDRA_PROCESS
) &&
378 ((MI_IS_SESSION_IMAGE_ADDRESS(Address
)) ||
379 ((Address
>= MiSessionViewStart
) && (Address
< MiSessionSpaceWs
))))
384 /* Hardcode unknown color */
388 /* Check if the PFN database should be acquired */
389 if (OldIrql
== MM_NOIRQL
)
391 /* Acquire it and remember we should release it after */
392 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
396 /* We either manually locked the PFN DB, or already came with it locked */
397 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
398 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
400 /* Assert we have enough pages */
401 ASSERT(MmAvailablePages
>= 32);
404 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
405 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
407 if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
408 if (!Process
) MI_SET_PROCESS2("Kernel Demand 0");
410 /* Do we need a zero page? */
411 if (Color
!= 0xFFFFFFFF)
413 /* Try to get one, if we couldn't grab a free page and zero it */
414 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
415 if (!PageFrameNumber
)
417 /* We'll need a free page and zero it manually */
418 PageFrameNumber
= MiRemoveAnyPage(Color
);
424 /* Get a color, and see if we should grab a zero or non-zero page */
425 Color
= MI_GET_NEXT_COLOR();
428 /* Process or system doesn't want a zero page, grab anything */
429 PageFrameNumber
= MiRemoveAnyPage(Color
);
433 /* System wants a zero page, obtain one */
434 PageFrameNumber
= MiRemoveZeroPage(Color
);
439 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
441 /* Do we have the lock? */
445 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
447 /* Update performance counters */
448 if (Process
> HYDRA_PROCESS
) Process
->NumberOfPrivatePages
++;
451 /* Increment demand zero faults */
452 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
454 /* Zero the page if need be */
455 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
457 /* Fault on user PDE, or fault on user PTE? */
458 if (PointerPte
<= MiHighestUserPte
)
460 /* User fault, build a user PTE */
461 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
463 PointerPte
->u
.Soft
.Protection
,
468 /* This is a user-mode PDE, create a kernel PTE for it */
469 MI_MAKE_HARDWARE_PTE(&TempPte
,
471 PointerPte
->u
.Soft
.Protection
,
475 /* Set it dirty if it's a writable page */
476 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
479 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
481 /* Did we manually acquire the lock */
484 /* Get the PFN entry */
485 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
487 /* Windows does these sanity checks */
488 ASSERT(Pfn1
->u1
.Event
== 0);
489 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
495 DPRINT("Demand zero page has now been paged in\n");
496 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
501 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
503 IN PMMPTE PointerPte
,
504 IN PMMPTE PointerProtoPte
,
506 IN PMMPFN
* LockedProtoPfn
)
509 PMMPTE OriginalPte
, PageTablePte
;
510 ULONG_PTR Protection
;
511 PFN_NUMBER PageFrameIndex
;
513 BOOLEAN OriginalProtection
, DirtyPage
;
515 /* Must be called with an valid prototype PTE, with the PFN lock held */
516 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
517 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
520 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
522 /* Get the PFN entry and set it as a prototype PTE */
523 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
524 Pfn1
->u3
.e1
.PrototypePte
= 1;
526 /* Increment the share count for the page table */
527 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
528 // This could be beause MiDeletePte is now being called from strange code in Rosmm
529 PageTablePte
= MiAddressToPte(PointerPte
);
530 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
531 //Pfn2->u2.ShareCount++;
533 /* Check where we should be getting the protection information from */
534 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
536 /* Get the protection from the PTE, there's no real Proto PTE data */
537 Protection
= PointerPte
->u
.Soft
.Protection
;
539 /* Remember that we did not use the proto protection */
540 OriginalProtection
= FALSE
;
544 /* Get the protection from the original PTE link */
545 OriginalPte
= &Pfn1
->OriginalPte
;
546 Protection
= OriginalPte
->u
.Soft
.Protection
;
548 /* Remember that we used the original protection */
549 OriginalProtection
= TRUE
;
551 /* Check if this was a write on a read only proto */
552 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
555 StoreInstruction
= 0;
559 /* Check if this was a write on a non-COW page */
561 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
563 /* Then the page should be marked dirty */
567 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
570 /* Did we get a locked incoming PFN? */
573 /* Drop a reference */
574 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
575 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
576 *LockedProtoPfn
= NULL
;
579 /* Release the PFN lock */
580 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
582 /* Remove caching bits */
583 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
586 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
588 /* Write combining, no caching */
589 MI_PAGE_DISABLE_CACHE(&TempPte
);
590 MI_PAGE_WRITE_COMBINED(&TempPte
);
592 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
594 /* Write through, no caching */
595 MI_PAGE_DISABLE_CACHE(&TempPte
);
596 MI_PAGE_WRITE_THROUGH(&TempPte
);
599 /* Check if this is a kernel or user address */
600 if (Address
< MmSystemRangeStart
)
602 /* Build the user PTE */
603 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
607 /* Build the kernel PTE */
608 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
611 /* Set the dirty flag if needed */
612 if (DirtyPage
) TempPte
.u
.Hard
.Dirty
= TRUE
;
615 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
617 /* Reset the protection if needed */
618 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
621 ASSERT(PointerPte
== MiAddressToPte(Address
));
622 return STATUS_SUCCESS
;
627 MiResolveTransitionFault(IN PVOID FaultingAddress
,
628 IN PMMPTE PointerPte
,
629 IN PEPROCESS CurrentProcess
,
631 OUT PVOID
*InPageBlock
)
633 PFN_NUMBER PageFrameIndex
;
636 PMMPTE PointerToPteForProtoPage
;
637 DPRINT1("Transition fault on 0x%p with PTE 0x%lx in process %s\n", FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
639 /* Windowss does this check */
640 ASSERT(*InPageBlock
== NULL
);
642 /* ARM3 doesn't support this path */
643 ASSERT(OldIrql
!= MM_NOIRQL
);
645 /* Capture the PTE and make sure it's in transition format */
646 TempPte
= *PointerPte
;
647 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
648 (TempPte
.u
.Soft
.Prototype
== 0) &&
649 (TempPte
.u
.Soft
.Transition
== 1));
651 /* Get the PFN and the PFN entry */
652 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
653 DPRINT1("Transition PFN: %lx\n", PageFrameIndex
);
654 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
656 /* One more transition fault! */
657 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
659 /* This is from ARM3 -- Windows normally handles this here */
660 ASSERT(Pfn1
->u4
.InPageError
== 0);
662 /* Not supported in ARM3 */
663 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
665 /* Windows checks there's some free pages and this isn't an in-page error */
666 ASSERT(MmAvailablePages
>= 0);
667 ASSERT(Pfn1
->u4
.InPageError
== 0);
669 /* ReactOS checks for this */
670 ASSERT(MmAvailablePages
> 32);
672 /* Was this a transition page in the valid list, or free/zero list? */
673 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
675 /* All Windows does here is a bunch of sanity checks */
676 DPRINT1("Transition in active list\n");
677 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
678 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
679 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
680 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
684 /* Otherwise, the page is removed from its list */
685 DPRINT1("Transition page in free/zero list\n");
686 MiUnlinkPageFromList(Pfn1
);
687 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
690 /* At this point, there should no longer be any in-page errors */
691 ASSERT(Pfn1
->u4
.InPageError
== 0);
693 /* Check if this was a PFN with no more share references */
694 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
696 /* Bump the share count and make the page valid */
697 Pfn1
->u2
.ShareCount
++;
698 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
700 /* Prototype PTEs are in paged pool, which itself might be in transition */
701 if (FaultingAddress
>= MmSystemRangeStart
)
703 /* Check if this is a paged pool PTE in transition state */
704 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
705 TempPte
= *PointerToPteForProtoPage
;
706 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
708 /* This isn't yet supported */
709 DPRINT1("Double transition fault not yet supported\n");
714 /* Build the transition PTE -- maybe a macro? */
715 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
716 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
717 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
718 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
719 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
720 MiDetermineUserGlobalPteMask(PointerPte
);
722 /* Is the PTE writeable? */
723 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
724 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
727 TempPte
.u
.Hard
.Dirty
= TRUE
;
732 TempPte
.u
.Hard
.Dirty
= FALSE
;
735 /* Write the valid PTE */
736 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
739 return STATUS_PAGE_FAULT_TRANSITION
;
744 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
746 IN PMMPTE PointerPte
,
747 IN PMMPTE PointerProtoPte
,
748 IN OUT PMMPFN
*OutPfn
,
749 OUT PVOID
*PageFileData
,
751 IN PEPROCESS Process
,
753 IN PVOID TrapInformation
)
755 MMPTE TempPte
, PteContents
;
757 PFN_NUMBER PageFrameIndex
;
759 PVOID InPageBlock
= NULL
;
761 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
762 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
763 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
764 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
766 /* Read the prototype PTE and check if it's valid */
767 TempPte
= *PointerProtoPte
;
768 if (TempPte
.u
.Hard
.Valid
== 1)
770 /* One more user of this mapped page */
771 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
772 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
773 Pfn1
->u2
.ShareCount
++;
775 /* Call it a transition */
776 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
778 /* Complete the prototype PTE fault -- this will release the PFN lock */
779 return MiCompleteProtoPteFault(StoreInstruction
,
787 /* Make sure there's some protection mask */
788 if (TempPte
.u
.Long
== 0)
790 /* Release the lock */
791 DPRINT1("Access on reserved section?\n");
792 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
793 return STATUS_ACCESS_VIOLATION
;
796 /* Check for access rights on the PTE proper */
797 PteContents
= *PointerPte
;
798 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
800 if (!PteContents
.u
.Proto
.ReadOnly
)
802 /* FIXME: CHECK FOR ACCESS */
804 /* Check for copy on write page */
805 if ((TempPte
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
807 /* Not yet supported */
814 /* Check for copy on write page */
815 if ((PteContents
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
817 /* Not yet supported */
822 /* Check for clone PTEs */
823 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
825 /* We don't support mapped files yet */
826 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
828 /* We might however have transition PTEs */
829 if (TempPte
.u
.Soft
.Transition
== 1)
831 /* Resolve the transition fault */
832 ASSERT(OldIrql
!= MM_NOIRQL
);
833 Status
= MiResolveTransitionFault(Address
,
838 ASSERT(NT_SUCCESS(Status
));
842 /* We also don't support paged out pages */
843 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
845 /* Resolve the demand zero fault */
846 Status
= MiResolveDemandZeroFault(Address
,
850 ASSERT(NT_SUCCESS(Status
));
853 /* Complete the prototype PTE fault -- this will release the PFN lock */
854 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
855 return MiCompleteProtoPteFault(StoreInstruction
,
865 MiDispatchFault(IN BOOLEAN StoreInstruction
,
867 IN PMMPTE PointerPte
,
868 IN PMMPTE PointerProtoPte
,
869 IN BOOLEAN Recursive
,
870 IN PEPROCESS Process
,
871 IN PVOID TrapInformation
,
875 KIRQL OldIrql
, LockIrql
;
877 PMMPTE SuperProtoPte
;
878 PMMPFN Pfn1
, OutPfn
= NULL
;
879 PFN_NUMBER PageFrameIndex
, PteCount
, ProcessedPtes
;
880 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
884 /* Make sure the addresses are ok */
885 ASSERT(PointerPte
== MiAddressToPte(Address
));
888 // Make sure APCs are off and we're not at dispatch
890 OldIrql
= KeGetCurrentIrql();
891 ASSERT(OldIrql
<= APC_LEVEL
);
892 ASSERT(KeAreAllApcsDisabled() == TRUE
);
895 // Grab a copy of the PTE
897 TempPte
= *PointerPte
;
899 /* Do we have a prototype PTE? */
902 /* This should never happen */
903 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
905 /* Check if this is a kernel-mode address */
906 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
907 if (Address
>= MmSystemRangeStart
)
909 /* Lock the PFN database */
910 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
912 /* Has the PTE been made valid yet? */
913 if (!SuperProtoPte
->u
.Hard
.Valid
)
917 else if (PointerPte
->u
.Hard
.Valid
== 1)
922 /* Resolve the fault -- this will release the PFN lock */
923 Status
= MiResolveProtoPteFault(StoreInstruction
,
933 ASSERT(Status
== STATUS_SUCCESS
);
935 /* Complete this as a transition fault */
936 ASSERT(OldIrql
== KeGetCurrentIrql());
937 ASSERT(OldIrql
<= APC_LEVEL
);
938 ASSERT(KeAreAllApcsDisabled() == TRUE
);
943 /* We only handle the lookup path */
944 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
946 /* Is there a non-image VAD? */
948 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
949 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
951 /* One day, ReactOS will cluster faults */
952 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
953 DPRINT("Should cluster fault, but won't\n");
956 /* Only one PTE to handle for now */
960 /* Lock the PFN database */
961 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
963 /* We only handle the valid path */
964 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
966 /* Capture the PTE */
967 TempPte
= *PointerProtoPte
;
969 /* Loop to handle future case of clustered faults */
972 /* For our current usage, this should be true */
973 if (TempPte
.u
.Hard
.Valid
== 1)
975 /* Bump the share count on the PTE */
976 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
977 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
978 Pfn1
->u2
.ShareCount
++;
980 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
981 (TempPte
.u
.Soft
.Transition
== 1))
983 /* No standby support yet */
988 /* Page is invalid, get out of the loop */
992 /* One more done, was it the last? */
993 if (++ProcessedPtes
== PteCount
)
995 /* Complete the fault */
996 MiCompleteProtoPteFault(StoreInstruction
,
1003 /* THIS RELEASES THE PFN LOCK! */
1007 /* No clustered faults yet */
1011 /* Did we resolve the fault? */
1014 /* Bump the transition count */
1015 InterlockedExchangeAdd(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1018 /* Loop all the processing we did */
1019 ASSERT(ProcessedPtes
== 0);
1021 /* Complete this as a transition fault */
1022 ASSERT(OldIrql
== KeGetCurrentIrql());
1023 ASSERT(OldIrql
<= APC_LEVEL
);
1024 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1025 return STATUS_PAGE_FAULT_TRANSITION
;
1028 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1029 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1030 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1031 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1032 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1034 /* Resolve the fault -- this will release the PFN lock */
1035 Status
= MiResolveProtoPteFault(StoreInstruction
,
1045 ASSERT(Status
== STATUS_SUCCESS
);
1047 /* Did the routine clean out the PFN or should we? */
1050 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1051 ASSERT(PointerProtoPte
!= NULL
);
1052 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1054 /* Dereference the locked PFN */
1055 MiDereferencePfnAndDropLockCount(OutPfn
);
1056 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1058 /* And now release the lock */
1059 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1062 /* Complete this as a transition fault */
1063 ASSERT(OldIrql
== KeGetCurrentIrql());
1064 ASSERT(OldIrql
<= APC_LEVEL
);
1065 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1071 // The PTE must be invalid but not completely empty. It must also not be a
1072 // prototype PTE as that scenario should've been handled above. These are
1073 // all Windows checks
1075 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1076 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1077 ASSERT(TempPte
.u
.Long
!= 0);
1080 // No transition or page file software PTEs in ARM3 yet, so this must be a
1081 // demand zero page. These are all ReactOS checks
1083 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1084 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1087 // If we got this far, the PTE can only be a demand zero PTE, which is what
1088 // we want. Go handle it!
1090 Status
= MiResolveDemandZeroFault(Address
,
1094 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1095 if (NT_SUCCESS(Status
))
1098 // Make sure we're returning in a sane state and pass the status down
1100 ASSERT(OldIrql
== KeGetCurrentIrql());
1101 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1106 // Generate an access fault
1108 return STATUS_ACCESS_VIOLATION
;
1113 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1115 IN KPROCESSOR_MODE Mode
,
1116 IN PVOID TrapInformation
)
1118 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1119 PMMPTE ProtoPte
= NULL
;
1120 PMMPTE PointerPte
= MiAddressToPte(Address
);
1121 PMMPDE PointerPde
= MiAddressToPde(Address
);
1122 #if (_MI_PAGING_LEVELS >= 3)
1123 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1124 #if (_MI_PAGING_LEVELS == 4)
1125 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1129 PETHREAD CurrentThread
;
1130 PEPROCESS CurrentProcess
;
1132 PMMSUPPORT WorkingSet
;
1133 ULONG ProtectionCode
;
1135 PFN_NUMBER PageFrameIndex
;
1137 BOOLEAN IsSessionAddress
;
1139 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1141 /* Check for page fault on high IRQL */
1142 if (OldIrql
> APC_LEVEL
)
1144 #if (_MI_PAGING_LEVELS < 3)
1145 /* Could be a page table for paged pool, which we'll allow */
1146 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1147 MiCheckPdeForPagedPool(Address
);
1149 /* Check if any of the top-level pages are invalid */
1151 #if (_MI_PAGING_LEVELS == 4)
1152 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1154 #if (_MI_PAGING_LEVELS >= 3)
1155 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1157 (PointerPde
->u
.Hard
.Valid
== 0))
1159 /* This fault is not valid, printf out some debugging help */
1160 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1163 if (TrapInformation
)
1165 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1166 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1167 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1168 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1171 /* Tell the trap handler to fail */
1172 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1175 /* Not yet implemented in ReactOS */
1176 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1177 ASSERT(((StoreInstruction
) && (PointerPte
->u
.Hard
.CopyOnWrite
)) == FALSE
);
1179 /* Check if this was a write */
1180 if (StoreInstruction
)
1182 /* Was it to a read-only page? */
1183 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1184 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1185 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1187 /* Crash with distinguished bugcheck code */
1188 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1191 (ULONG_PTR
)TrapInformation
,
1196 /* Nothing is actually wrong */
1197 DPRINT1("Fault at IRQL1 is ok\n");
1198 return STATUS_SUCCESS
;
1201 /* Check for kernel fault address */
1202 if (Address
>= MmSystemRangeStart
)
1204 /* Bail out, if the fault came from user mode */
1205 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1207 #if (_MI_PAGING_LEVELS == 4)
1208 /* AMD64 system, check if PXE is invalid */
1209 if (PointerPxe
->u
.Hard
.Valid
== 0)
1211 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1214 (ULONG_PTR
)TrapInformation
,
1218 #if (_MI_PAGING_LEVELS == 4)
1219 /* PAE/AMD64 system, check if PPE is invalid */
1220 if (PointerPpe
->u
.Hard
.Valid
== 0)
1222 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1225 (ULONG_PTR
)TrapInformation
,
1229 #if (_MI_PAGING_LEVELS == 2)
1230 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1231 MiCheckPdeForPagedPool(Address
);
1234 /* Check if the PDE is invalid */
1235 if (PointerPde
->u
.Hard
.Valid
== 0)
1237 /* PDE (still) not valid, kill the system */
1238 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1241 (ULONG_PTR
)TrapInformation
,
1245 /* Not handling session faults yet */
1246 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1248 /* The PDE is valid, so read the PTE */
1249 TempPte
= *PointerPte
;
1250 if (TempPte
.u
.Hard
.Valid
== 1)
1252 /* Check if this was system space or session space */
1253 if (!IsSessionAddress
)
1255 /* Check if the PTE is still valid under PFN lock */
1256 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1257 TempPte
= *PointerPte
;
1258 if (TempPte
.u
.Hard
.Valid
)
1260 /* Check if this was a write */
1261 if (StoreInstruction
)
1263 /* Was it to a read-only page? */
1264 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1265 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1266 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1268 /* Crash with distinguished bugcheck code */
1269 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1272 (ULONG_PTR
)TrapInformation
,
1278 /* Release PFN lock and return all good */
1279 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1280 return STATUS_SUCCESS
;
1284 /* Check if this was a session PTE that needs to remap the session PDE */
1285 if (MI_IS_SESSION_PTE(Address
))
1287 /* Do the remapping */
1288 Status
= MiCheckPdeForSessionSpace(Address
);
1289 if (!NT_SUCCESS(Status
))
1291 /* It failed, this address is invalid */
1292 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1295 (ULONG_PTR
)TrapInformation
,
1300 /* Check for a fault on the page table or hyperspace */
1301 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1303 #if (_MI_PAGING_LEVELS < 3)
1304 /* Windows does this check but I don't understand why -- it's done above! */
1305 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1307 /* Handle this as a user mode fault */
1311 /* Get the current thread */
1312 CurrentThread
= PsGetCurrentThread();
1314 /* What kind of address is this */
1315 if (!IsSessionAddress
)
1317 /* Use the system working set */
1318 WorkingSet
= &MmSystemCacheWs
;
1319 CurrentProcess
= NULL
;
1321 /* Make sure we don't have a recursive working set lock */
1322 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1323 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1324 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1325 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1326 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1327 (CurrentThread
->OwnsSessionWorkingSetShared
))
1330 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1335 /* Use the session process and working set */
1336 CurrentProcess
= HYDRA_PROCESS
;
1337 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1339 /* Make sure we don't have a recursive working set lock */
1340 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1341 (CurrentThread
->OwnsSessionWorkingSetShared
))
1344 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1348 /* Acquire the working set lock */
1349 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1350 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1352 /* Re-read PTE now that we own the lock */
1353 TempPte
= *PointerPte
;
1354 if (TempPte
.u
.Hard
.Valid
== 1)
1356 /* Check if this was a write */
1357 if (StoreInstruction
)
1359 /* Was it to a read-only page that is not copy on write? */
1360 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1361 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1362 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1363 !(TempPte
.u
.Hard
.CopyOnWrite
))
1365 /* Case not yet handled */
1366 ASSERT(!IsSessionAddress
);
1368 /* Crash with distinguished bugcheck code */
1369 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1372 (ULONG_PTR
)TrapInformation
,
1377 /* Check for read-only write in session space */
1378 if ((IsSessionAddress
) &&
1379 (StoreInstruction
) &&
1380 !(TempPte
.u
.Hard
.Write
))
1383 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1386 if (TempPte
.u
.Hard
.CopyOnWrite
== 0)
1388 /* Then this is not allowed */
1389 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1391 (ULONG_PTR
)TempPte
.u
.Long
,
1392 (ULONG_PTR
)TrapInformation
,
1396 /* Otherwise, handle COW */
1400 /* Release the working set */
1401 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1402 KeLowerIrql(LockIrql
);
1404 /* Otherwise, the PDE was probably invalid, and all is good now */
1405 return STATUS_SUCCESS
;
1408 /* Check one kind of prototype PTE */
1409 if (TempPte
.u
.Soft
.Prototype
)
1411 /* Make sure protected pool is on, and that this is a pool address */
1412 if ((MmProtectFreedNonPagedPool
) &&
1413 (((Address
>= MmNonPagedPoolStart
) &&
1414 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1415 MmSizeOfNonPagedPoolInBytes
))) ||
1416 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1417 (Address
< MmNonPagedPoolEnd
))))
1419 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1420 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1427 /* Get the prototype PTE! */
1428 ProtoPte
= MiProtoPteToPte(&TempPte
);
1430 /* Do we need to locate the prototype PTE in session space? */
1431 if ((IsSessionAddress
) &&
1432 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1434 /* Yep, go find it as well as the VAD for it */
1435 ProtoPte
= MiCheckVirtualAddress(Address
,
1438 ASSERT(ProtoPte
!= NULL
);
1443 /* We don't implement transition PTEs */
1444 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1446 /* Check for no-access PTE */
1447 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1449 /* Bugcheck the system! */
1450 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1453 (ULONG_PTR
)TrapInformation
,
1458 /* Check for demand page */
1459 if ((StoreInstruction
) &&
1461 !(IsSessionAddress
) &&
1462 !(TempPte
.u
.Hard
.Valid
))
1464 /* Get the protection code */
1465 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1466 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1468 /* Bugcheck the system! */
1469 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1472 (ULONG_PTR
)TrapInformation
,
1477 /* Now do the real fault handling */
1478 Status
= MiDispatchFault(StoreInstruction
,
1487 /* Release the working set */
1488 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1489 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1490 KeLowerIrql(LockIrql
);
1493 DPRINT("Fault resolved with status: %lx\n", Status
);
1497 /* This is a user fault */
1499 CurrentThread
= PsGetCurrentThread();
1500 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
1502 /* Lock the working set */
1503 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1505 #if (_MI_PAGING_LEVELS == 4)
1506 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1507 // also this is missing the page count increment
1508 /* Check if the PXE is valid */
1509 if (PointerPxe
->u
.Hard
.Valid
== 0)
1511 /* Right now, we only handle scenarios where the PXE is totally empty */
1512 ASSERT(PointerPxe
->u
.Long
== 0);
1514 /* Resolve a demand zero fault */
1515 Status
= MiResolveDemandZeroFault(PointerPpe
,
1520 /* We should come back with a valid PXE */
1521 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1525 #if (_MI_PAGING_LEVELS >= 3)
1526 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1527 // also this is missing the page count increment
1528 /* Check if the PPE is valid */
1529 if (PointerPpe
->u
.Hard
.Valid
== 0)
1531 /* Right now, we only handle scenarios where the PPE is totally empty */
1532 ASSERT(PointerPpe
->u
.Long
== 0);
1534 /* Resolve a demand zero fault */
1535 Status
= MiResolveDemandZeroFault(PointerPde
,
1540 /* We should come back with a valid PPE */
1541 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1545 /* Check if the PDE is valid */
1546 if (PointerPde
->u
.Hard
.Valid
== 0)
1548 /* Right now, we only handle scenarios where the PDE is totally empty */
1549 ASSERT(PointerPde
->u
.Long
== 0);
1551 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1553 UserPdeFault
= TRUE
;
1555 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1556 if (ProtectionCode
== MM_NOACCESS
)
1558 #if (_MI_PAGING_LEVELS == 2)
1559 /* Could be a page table for paged pool */
1560 MiCheckPdeForPagedPool(Address
);
1562 /* Has the code above changed anything -- is this now a valid PTE? */
1563 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1565 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1566 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1570 /* Write a demand-zero PDE */
1571 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
1573 /* Dispatch the fault */
1574 Status
= MiDispatchFault(TRUE
,
1579 PsGetCurrentProcess(),
1583 UserPdeFault
= FALSE
;
1585 /* We should come back with APCs enabled, and with a valid PDE */
1586 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1587 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1591 /* Not yet implemented in ReactOS */
1592 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1595 /* Now capture the PTE. Ignore virtual faults for now */
1596 TempPte
= *PointerPte
;
1597 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1599 /* Quick check for demand-zero */
1600 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
1602 /* Resolve the fault */
1603 MiResolveDemandZeroFault(Address
,
1608 /* Return the status */
1609 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1610 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1613 /* Check for zero PTE */
1614 if (TempPte
.u
.Long
== 0)
1616 /* Check if this address range belongs to a valid allocation (VAD) */
1617 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1618 if (ProtectionCode
== MM_NOACCESS
)
1620 #if (_MI_PAGING_LEVELS == 2)
1621 /* Could be a page table for paged pool */
1622 MiCheckPdeForPagedPool(Address
);
1624 /* Has the code above changed anything -- is this now a valid PTE? */
1625 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1627 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1628 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1632 /* No guard page support yet */
1633 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1636 * Check if this is a real user-mode address or actually a kernel-mode
1637 * page table for a user mode address
1639 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
1641 /* Add an additional page table reference */
1642 MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
1643 ASSERT(MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
1646 /* Did we get a prototype PTE back? */
1649 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1650 if (PointerPde
== MiAddressToPde(PTE_BASE
))
1652 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1653 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
1657 /* No, create a new PTE. First, write the protection */
1658 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
1661 /* Lock the PFN database since we're going to grab a page */
1662 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1664 /* Make sure we have enough pages */
1665 ASSERT(MmAvailablePages
>= 32);
1667 /* Try to get a zero page */
1668 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1669 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1670 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1671 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1672 if (!PageFrameIndex
)
1674 /* Grab a page out of there. Later we should grab a colored zero page */
1675 PageFrameIndex
= MiRemoveAnyPage(Color
);
1676 ASSERT(PageFrameIndex
);
1678 /* Release the lock since we need to do some zeroing */
1679 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1681 /* Zero out the page, since it's for user-mode */
1682 MiZeroPfn(PageFrameIndex
);
1684 /* Grab the lock again so we can initialize the PFN entry */
1685 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1688 /* Initialize the PFN entry now */
1689 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1691 /* And we're done with the lock */
1692 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1694 /* Increment the count of pages in the process */
1695 CurrentProcess
->NumberOfPrivatePages
++;
1697 /* One more demand-zero fault */
1698 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
1700 /* Fault on user PDE, or fault on user PTE? */
1701 if (PointerPte
<= MiHighestUserPte
)
1703 /* User fault, build a user PTE */
1704 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1706 PointerPte
->u
.Soft
.Protection
,
1711 /* This is a user-mode PDE, create a kernel PTE for it */
1712 MI_MAKE_HARDWARE_PTE(&TempPte
,
1714 PointerPte
->u
.Soft
.Protection
,
1718 /* Write the dirty bit for writeable pages */
1719 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1721 /* And now write down the PTE, making the address valid */
1722 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1723 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1724 ASSERT(Pfn1
->u1
.Event
== NULL
);
1727 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1728 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1729 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1732 /* No guard page support yet */
1733 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1734 ASSERT(ProtectionCode
!= 0x100);
1736 /* Write the prototype PTE */
1737 TempPte
= PrototypePte
;
1738 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1739 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1743 /* Get the protection code and check if this is a proto PTE */
1744 ProtectionCode
= TempPte
.u
.Soft
.Protection
;
1745 DPRINT1("Code: %lx\n", ProtectionCode
);
1746 if (TempPte
.u
.Soft
.Prototype
)
1748 /* Do we need to go find the real PTE? */
1749 DPRINT1("Soft: %lx\n", TempPte
.u
.Soft
.PageFileHigh
);
1750 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1752 /* Get the prototype pte and VAD for it */
1753 ProtoPte
= MiCheckVirtualAddress(Address
,
1756 DPRINT1("Address: %p ProtoP %p Code: %lx Vad: %p\n", Address
, ProtoPte
, ProtectionCode
, Vad
);
1759 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1760 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1761 return STATUS_ACCESS_VIOLATION
;
1766 /* Get the prototype PTE! */
1767 ProtoPte
= MiProtoPteToPte(&TempPte
);
1769 /* Is it read-only */
1770 if (TempPte
.u
.Proto
.ReadOnly
)
1772 /* Set read-only code */
1773 ProtectionCode
= MM_READONLY
;
1777 /* Set unknown protection */
1778 ProtectionCode
= 0x100;
1779 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
1785 /* FIXME: Run MiAccessCheck */
1787 /* Dispatch the fault */
1788 Status
= MiDispatchFault(StoreInstruction
,
1797 /* Return the status */
1798 ASSERT(NT_SUCCESS(Status
));
1799 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1800 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1806 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
1808 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1809 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1811 *ExecuteOptions
= 0;
1813 if (CurrentProcess
->Flags
.ExecuteDisable
)
1815 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
1818 if (CurrentProcess
->Flags
.ExecuteEnable
)
1820 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
1823 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
1825 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
1828 if (CurrentProcess
->Flags
.Permanent
)
1830 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
1833 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
1835 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
1838 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
1840 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
1843 return STATUS_SUCCESS
;
1848 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
1850 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1851 KLOCK_QUEUE_HANDLE ProcessLock
;
1852 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
1853 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1855 /* Only accept valid flags */
1856 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
1859 DPRINT1("Invalid no-execute options\n");
1860 return STATUS_INVALID_PARAMETER
;
1863 /* Change the NX state in the process lock */
1864 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
1866 /* Don't change anything if the permanent flag was set */
1867 if (!CurrentProcess
->Flags
.Permanent
)
1869 /* Start by assuming it's not disabled */
1870 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
1872 /* Now process each flag and turn the equivalent bit on */
1873 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
1875 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
1877 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
1879 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
1881 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
1883 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
1885 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
1887 CurrentProcess
->Flags
.Permanent
= TRUE
;
1889 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
1891 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1893 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
1895 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1898 /* These are turned on by default if no-execution is also eanbled */
1899 if (CurrentProcess
->Flags
.ExecuteEnable
)
1901 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1902 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1906 Status
= STATUS_SUCCESS
;
1909 /* Release the lock and return status */
1910 KiReleaseProcessLock(&ProcessLock
);