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 SessionAddress
;
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 SessionAddress
= MiPteToAddress(Address
);
175 PointerPde
= MiAddressToPte(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
)SessionAddress
- (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
, SessionAddress
);
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 /* This is a standby page, bring it back from the cache */
984 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
985 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
986 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
987 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
989 /* Should not yet happen in ReactOS */
990 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
991 ASSERT(Pfn1
->u4
.InPageError
== 0);
994 MiUnlinkPageFromList(Pfn1
);
996 /* Bump its reference count */
997 ASSERT(Pfn1
->u2
.ShareCount
== 0);
998 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
999 Pfn1
->u2
.ShareCount
++;
1001 /* Make it valid again */
1002 /* This looks like another macro.... */
1003 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1004 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1005 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1006 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1007 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1008 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1009 TempPte
.u
.Hard
.Valid
= 1;
1010 TempPte
.u
.Hard
.Accessed
= 1;
1012 /* Is the PTE writeable? */
1013 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
1014 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
1017 TempPte
.u
.Hard
.Dirty
= TRUE
;
1022 TempPte
.u
.Hard
.Dirty
= FALSE
;
1025 /* Write the valid PTE */
1026 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1027 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1031 /* Page is invalid, get out of the loop */
1035 /* One more done, was it the last? */
1036 if (++ProcessedPtes
== PteCount
)
1038 /* Complete the fault */
1039 MiCompleteProtoPteFault(StoreInstruction
,
1046 /* THIS RELEASES THE PFN LOCK! */
1050 /* No clustered faults yet */
1054 /* Did we resolve the fault? */
1057 /* Bump the transition count */
1058 InterlockedExchangeAdd(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1061 /* Loop all the processing we did */
1062 ASSERT(ProcessedPtes
== 0);
1064 /* Complete this as a transition fault */
1065 ASSERT(OldIrql
== KeGetCurrentIrql());
1066 ASSERT(OldIrql
<= APC_LEVEL
);
1067 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1068 return STATUS_PAGE_FAULT_TRANSITION
;
1071 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1072 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1073 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1074 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1075 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1077 /* Resolve the fault -- this will release the PFN lock */
1078 Status
= MiResolveProtoPteFault(StoreInstruction
,
1088 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1089 //ASSERT(Status != STATUS_REFAULT);
1090 //ASSERT(Status != STATUS_PTE_CHANGED);
1092 /* Did the routine clean out the PFN or should we? */
1095 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1096 ASSERT(PointerProtoPte
!= NULL
);
1097 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1099 /* Dereference the locked PFN */
1100 MiDereferencePfnAndDropLockCount(OutPfn
);
1101 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1103 /* And now release the lock */
1104 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1107 /* Complete this as a transition fault */
1108 ASSERT(OldIrql
== KeGetCurrentIrql());
1109 ASSERT(OldIrql
<= APC_LEVEL
);
1110 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1116 // The PTE must be invalid but not completely empty. It must also not be a
1117 // prototype PTE as that scenario should've been handled above. These are
1118 // all Windows checks
1120 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1121 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1122 ASSERT(TempPte
.u
.Long
!= 0);
1125 // No transition or page file software PTEs in ARM3 yet, so this must be a
1126 // demand zero page. These are all ReactOS checks
1128 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1129 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1132 // If we got this far, the PTE can only be a demand zero PTE, which is what
1133 // we want. Go handle it!
1135 Status
= MiResolveDemandZeroFault(Address
,
1139 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1140 if (NT_SUCCESS(Status
))
1143 // Make sure we're returning in a sane state and pass the status down
1145 ASSERT(OldIrql
== KeGetCurrentIrql());
1146 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1151 // Generate an access fault
1153 return STATUS_ACCESS_VIOLATION
;
1158 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1160 IN KPROCESSOR_MODE Mode
,
1161 IN PVOID TrapInformation
)
1163 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1164 PMMPTE ProtoPte
= NULL
;
1165 PMMPTE PointerPte
= MiAddressToPte(Address
);
1166 PMMPDE PointerPde
= MiAddressToPde(Address
);
1167 #if (_MI_PAGING_LEVELS >= 3)
1168 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1169 #if (_MI_PAGING_LEVELS == 4)
1170 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1174 PETHREAD CurrentThread
;
1175 PEPROCESS CurrentProcess
;
1177 PMMSUPPORT WorkingSet
;
1178 ULONG ProtectionCode
;
1180 PFN_NUMBER PageFrameIndex
;
1182 BOOLEAN IsSessionAddress
;
1184 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1186 /* Check for page fault on high IRQL */
1187 if (OldIrql
> APC_LEVEL
)
1189 #if (_MI_PAGING_LEVELS < 3)
1190 /* Could be a page table for paged pool, which we'll allow */
1191 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1192 MiCheckPdeForPagedPool(Address
);
1194 /* Check if any of the top-level pages are invalid */
1196 #if (_MI_PAGING_LEVELS == 4)
1197 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1199 #if (_MI_PAGING_LEVELS >= 3)
1200 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1202 (PointerPde
->u
.Hard
.Valid
== 0))
1204 /* This fault is not valid, printf out some debugging help */
1205 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1208 if (TrapInformation
)
1210 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1211 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1212 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1213 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1216 /* Tell the trap handler to fail */
1217 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1220 /* Not yet implemented in ReactOS */
1221 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1222 ASSERT(((StoreInstruction
) && (PointerPte
->u
.Hard
.CopyOnWrite
)) == FALSE
);
1224 /* Check if this was a write */
1225 if (StoreInstruction
)
1227 /* Was it to a read-only page? */
1228 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1229 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1230 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1232 /* Crash with distinguished bugcheck code */
1233 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1236 (ULONG_PTR
)TrapInformation
,
1241 /* Nothing is actually wrong */
1242 DPRINT1("Fault at IRQL1 is ok\n");
1243 return STATUS_SUCCESS
;
1246 /* Check for kernel fault address */
1247 if (Address
>= MmSystemRangeStart
)
1249 /* Bail out, if the fault came from user mode */
1250 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1252 #if (_MI_PAGING_LEVELS == 4)
1253 /* AMD64 system, check if PXE is invalid */
1254 if (PointerPxe
->u
.Hard
.Valid
== 0)
1256 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1259 (ULONG_PTR
)TrapInformation
,
1263 #if (_MI_PAGING_LEVELS == 4)
1264 /* PAE/AMD64 system, check if PPE is invalid */
1265 if (PointerPpe
->u
.Hard
.Valid
== 0)
1267 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1270 (ULONG_PTR
)TrapInformation
,
1274 #if (_MI_PAGING_LEVELS == 2)
1275 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1276 MiCheckPdeForPagedPool(Address
);
1279 /* Check if the PDE is invalid */
1280 if (PointerPde
->u
.Hard
.Valid
== 0)
1282 /* PDE (still) not valid, kill the system */
1283 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1286 (ULONG_PTR
)TrapInformation
,
1290 /* Not handling session faults yet */
1291 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1293 /* The PDE is valid, so read the PTE */
1294 TempPte
= *PointerPte
;
1295 if (TempPte
.u
.Hard
.Valid
== 1)
1297 /* Check if this was system space or session space */
1298 if (!IsSessionAddress
)
1300 /* Check if the PTE is still valid under PFN lock */
1301 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1302 TempPte
= *PointerPte
;
1303 if (TempPte
.u
.Hard
.Valid
)
1305 /* Check if this was a write */
1306 if (StoreInstruction
)
1308 /* Was it to a read-only page? */
1309 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1310 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1311 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1313 /* Crash with distinguished bugcheck code */
1314 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1317 (ULONG_PTR
)TrapInformation
,
1323 /* Release PFN lock and return all good */
1324 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1325 return STATUS_SUCCESS
;
1329 /* Check if this was a session PTE that needs to remap the session PDE */
1330 if (MI_IS_SESSION_PTE(Address
))
1332 /* Do the remapping */
1333 Status
= MiCheckPdeForSessionSpace(Address
);
1334 if (!NT_SUCCESS(Status
))
1336 /* It failed, this address is invalid */
1337 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1340 (ULONG_PTR
)TrapInformation
,
1345 /* Check for a fault on the page table or hyperspace */
1346 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1348 #if (_MI_PAGING_LEVELS < 3)
1349 /* Windows does this check but I don't understand why -- it's done above! */
1350 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1352 /* Handle this as a user mode fault */
1356 /* Get the current thread */
1357 CurrentThread
= PsGetCurrentThread();
1359 /* What kind of address is this */
1360 if (!IsSessionAddress
)
1362 /* Use the system working set */
1363 WorkingSet
= &MmSystemCacheWs
;
1364 CurrentProcess
= NULL
;
1366 /* Make sure we don't have a recursive working set lock */
1367 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1368 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1369 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1370 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1371 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1372 (CurrentThread
->OwnsSessionWorkingSetShared
))
1375 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1380 /* Use the session process and working set */
1381 CurrentProcess
= HYDRA_PROCESS
;
1382 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1384 /* Make sure we don't have a recursive working set lock */
1385 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1386 (CurrentThread
->OwnsSessionWorkingSetShared
))
1389 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1393 /* Acquire the working set lock */
1394 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1395 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1397 /* Re-read PTE now that we own the lock */
1398 TempPte
= *PointerPte
;
1399 if (TempPte
.u
.Hard
.Valid
== 1)
1401 /* Check if this was a write */
1402 if (StoreInstruction
)
1404 /* Was it to a read-only page that is not copy on write? */
1405 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1406 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1407 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1408 !(TempPte
.u
.Hard
.CopyOnWrite
))
1410 /* Case not yet handled */
1411 ASSERT(!IsSessionAddress
);
1413 /* Crash with distinguished bugcheck code */
1414 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1417 (ULONG_PTR
)TrapInformation
,
1422 /* Check for read-only write in session space */
1423 if ((IsSessionAddress
) &&
1424 (StoreInstruction
) &&
1425 !(TempPte
.u
.Hard
.Write
))
1428 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1431 if (TempPte
.u
.Hard
.CopyOnWrite
== 0)
1433 /* Then this is not allowed */
1434 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1436 (ULONG_PTR
)TempPte
.u
.Long
,
1437 (ULONG_PTR
)TrapInformation
,
1441 /* Otherwise, handle COW */
1445 /* Release the working set */
1446 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1447 KeLowerIrql(LockIrql
);
1449 /* Otherwise, the PDE was probably invalid, and all is good now */
1450 return STATUS_SUCCESS
;
1453 /* Check one kind of prototype PTE */
1454 if (TempPte
.u
.Soft
.Prototype
)
1456 /* Make sure protected pool is on, and that this is a pool address */
1457 if ((MmProtectFreedNonPagedPool
) &&
1458 (((Address
>= MmNonPagedPoolStart
) &&
1459 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1460 MmSizeOfNonPagedPoolInBytes
))) ||
1461 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1462 (Address
< MmNonPagedPoolEnd
))))
1464 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1465 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1472 /* Get the prototype PTE! */
1473 ProtoPte
= MiProtoPteToPte(&TempPte
);
1475 /* Do we need to locate the prototype PTE in session space? */
1476 if ((IsSessionAddress
) &&
1477 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1479 /* Yep, go find it as well as the VAD for it */
1480 ProtoPte
= MiCheckVirtualAddress(Address
,
1483 ASSERT(ProtoPte
!= NULL
);
1488 /* We don't implement transition PTEs */
1489 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1491 /* Check for no-access PTE */
1492 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1494 /* Bugcheck the system! */
1495 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1498 (ULONG_PTR
)TrapInformation
,
1503 /* Check for demand page */
1504 if ((StoreInstruction
) &&
1506 !(IsSessionAddress
) &&
1507 !(TempPte
.u
.Hard
.Valid
))
1509 /* Get the protection code */
1510 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1511 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1513 /* Bugcheck the system! */
1514 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1517 (ULONG_PTR
)TrapInformation
,
1522 /* Now do the real fault handling */
1523 Status
= MiDispatchFault(StoreInstruction
,
1532 /* Release the working set */
1533 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1534 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1535 KeLowerIrql(LockIrql
);
1538 DPRINT("Fault resolved with status: %lx\n", Status
);
1542 /* This is a user fault */
1544 CurrentThread
= PsGetCurrentThread();
1545 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
1547 /* Lock the working set */
1548 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1550 #if (_MI_PAGING_LEVELS == 4)
1551 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1552 // also this is missing the page count increment
1553 /* Check if the PXE is valid */
1554 if (PointerPxe
->u
.Hard
.Valid
== 0)
1556 /* Right now, we only handle scenarios where the PXE is totally empty */
1557 ASSERT(PointerPxe
->u
.Long
== 0);
1559 /* Resolve a demand zero fault */
1560 Status
= MiResolveDemandZeroFault(PointerPpe
,
1565 /* We should come back with a valid PXE */
1566 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1570 #if (_MI_PAGING_LEVELS >= 3)
1571 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1572 // also this is missing the page count increment
1573 /* Check if the PPE is valid */
1574 if (PointerPpe
->u
.Hard
.Valid
== 0)
1576 /* Right now, we only handle scenarios where the PPE is totally empty */
1577 ASSERT(PointerPpe
->u
.Long
== 0);
1579 /* Resolve a demand zero fault */
1580 Status
= MiResolveDemandZeroFault(PointerPde
,
1585 /* We should come back with a valid PPE */
1586 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1590 /* Check if the PDE is valid */
1591 if (PointerPde
->u
.Hard
.Valid
== 0)
1593 /* Right now, we only handle scenarios where the PDE is totally empty */
1594 ASSERT(PointerPde
->u
.Long
== 0);
1596 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1598 UserPdeFault
= TRUE
;
1600 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1601 if (ProtectionCode
== MM_NOACCESS
)
1603 #if (_MI_PAGING_LEVELS == 2)
1604 /* Could be a page table for paged pool */
1605 MiCheckPdeForPagedPool(Address
);
1607 /* Has the code above changed anything -- is this now a valid PTE? */
1608 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1610 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1611 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1615 /* Write a demand-zero PDE */
1616 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
1618 /* Dispatch the fault */
1619 Status
= MiDispatchFault(TRUE
,
1624 PsGetCurrentProcess(),
1628 UserPdeFault
= FALSE
;
1630 /* We should come back with APCs enabled, and with a valid PDE */
1631 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1632 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1636 /* Not yet implemented in ReactOS */
1637 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1640 /* Now capture the PTE. Ignore virtual faults for now */
1641 TempPte
= *PointerPte
;
1642 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1644 /* Quick check for demand-zero */
1645 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
1647 /* Resolve the fault */
1648 MiResolveDemandZeroFault(Address
,
1653 /* Return the status */
1654 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1655 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1658 /* Check for zero PTE */
1659 if (TempPte
.u
.Long
== 0)
1661 /* Check if this address range belongs to a valid allocation (VAD) */
1662 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1663 if (ProtectionCode
== MM_NOACCESS
)
1665 #if (_MI_PAGING_LEVELS == 2)
1666 /* Could be a page table for paged pool */
1667 MiCheckPdeForPagedPool(Address
);
1669 /* Has the code above changed anything -- is this now a valid PTE? */
1670 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1672 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1673 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1677 /* No guard page support yet */
1678 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1681 * Check if this is a real user-mode address or actually a kernel-mode
1682 * page table for a user mode address
1684 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
1686 /* Add an additional page table reference */
1687 MiIncrementPageTableReferences(Address
);
1690 /* Did we get a prototype PTE back? */
1693 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1694 if (PointerPde
== MiAddressToPde(PTE_BASE
))
1696 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1697 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
1701 /* No, create a new PTE. First, write the protection */
1702 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
1705 /* Lock the PFN database since we're going to grab a page */
1706 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1708 /* Make sure we have enough pages */
1709 ASSERT(MmAvailablePages
>= 32);
1711 /* Try to get a zero page */
1712 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1713 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1714 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1715 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1716 if (!PageFrameIndex
)
1718 /* Grab a page out of there. Later we should grab a colored zero page */
1719 PageFrameIndex
= MiRemoveAnyPage(Color
);
1720 ASSERT(PageFrameIndex
);
1722 /* Release the lock since we need to do some zeroing */
1723 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1725 /* Zero out the page, since it's for user-mode */
1726 MiZeroPfn(PageFrameIndex
);
1728 /* Grab the lock again so we can initialize the PFN entry */
1729 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1732 /* Initialize the PFN entry now */
1733 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1735 /* And we're done with the lock */
1736 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1738 /* Increment the count of pages in the process */
1739 CurrentProcess
->NumberOfPrivatePages
++;
1741 /* One more demand-zero fault */
1742 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
1744 /* Fault on user PDE, or fault on user PTE? */
1745 if (PointerPte
<= MiHighestUserPte
)
1747 /* User fault, build a user PTE */
1748 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1750 PointerPte
->u
.Soft
.Protection
,
1755 /* This is a user-mode PDE, create a kernel PTE for it */
1756 MI_MAKE_HARDWARE_PTE(&TempPte
,
1758 PointerPte
->u
.Soft
.Protection
,
1762 /* Write the dirty bit for writeable pages */
1763 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1765 /* And now write down the PTE, making the address valid */
1766 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1767 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1768 ASSERT(Pfn1
->u1
.Event
== NULL
);
1771 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1772 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1773 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1776 /* No guard page support yet */
1777 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1778 ASSERT(ProtectionCode
!= 0x100);
1780 /* Write the prototype PTE */
1781 TempPte
= PrototypePte
;
1782 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1783 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1787 /* Get the protection code and check if this is a proto PTE */
1788 ProtectionCode
= TempPte
.u
.Soft
.Protection
;
1789 if (TempPte
.u
.Soft
.Prototype
)
1791 /* Do we need to go find the real PTE? */
1792 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1794 /* Get the prototype pte and VAD for it */
1795 ProtoPte
= MiCheckVirtualAddress(Address
,
1800 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1801 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1802 return STATUS_ACCESS_VIOLATION
;
1807 /* Get the prototype PTE! */
1808 ProtoPte
= MiProtoPteToPte(&TempPte
);
1810 /* Is it read-only */
1811 if (TempPte
.u
.Proto
.ReadOnly
)
1813 /* Set read-only code */
1814 ProtectionCode
= MM_READONLY
;
1818 /* Set unknown protection */
1819 ProtectionCode
= 0x100;
1820 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
1826 /* FIXME: Run MiAccessCheck */
1828 /* Dispatch the fault */
1829 Status
= MiDispatchFault(StoreInstruction
,
1838 /* Return the status */
1839 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1840 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1846 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
1848 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1849 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1851 *ExecuteOptions
= 0;
1853 if (CurrentProcess
->Flags
.ExecuteDisable
)
1855 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
1858 if (CurrentProcess
->Flags
.ExecuteEnable
)
1860 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
1863 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
1865 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
1868 if (CurrentProcess
->Flags
.Permanent
)
1870 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
1873 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
1875 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
1878 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
1880 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
1883 return STATUS_SUCCESS
;
1888 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
1890 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1891 KLOCK_QUEUE_HANDLE ProcessLock
;
1892 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
1893 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1895 /* Only accept valid flags */
1896 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
1899 DPRINT1("Invalid no-execute options\n");
1900 return STATUS_INVALID_PARAMETER
;
1903 /* Change the NX state in the process lock */
1904 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
1906 /* Don't change anything if the permanent flag was set */
1907 if (!CurrentProcess
->Flags
.Permanent
)
1909 /* Start by assuming it's not disabled */
1910 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
1912 /* Now process each flag and turn the equivalent bit on */
1913 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
1915 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
1917 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
1919 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
1921 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
1923 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
1925 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
1927 CurrentProcess
->Flags
.Permanent
= TRUE
;
1929 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
1931 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1933 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
1935 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1938 /* These are turned on by default if no-execution is also eanbled */
1939 if (CurrentProcess
->Flags
.ExecuteEnable
)
1941 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1942 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1946 Status
= STATUS_SUCCESS
;
1949 /* Release the lock and return status */
1950 KiReleaseProcessLock(&ProcessLock
);