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
;
880 PFN_COUNT PteCount
, ProcessedPtes
;
881 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
885 /* Make sure the addresses are ok */
886 ASSERT(PointerPte
== MiAddressToPte(Address
));
889 // Make sure APCs are off and we're not at dispatch
891 OldIrql
= KeGetCurrentIrql();
892 ASSERT(OldIrql
<= APC_LEVEL
);
893 ASSERT(KeAreAllApcsDisabled() == TRUE
);
896 // Grab a copy of the PTE
898 TempPte
= *PointerPte
;
900 /* Do we have a prototype PTE? */
903 /* This should never happen */
904 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
906 /* Check if this is a kernel-mode address */
907 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
908 if (Address
>= MmSystemRangeStart
)
910 /* Lock the PFN database */
911 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
913 /* Has the PTE been made valid yet? */
914 if (!SuperProtoPte
->u
.Hard
.Valid
)
918 else if (PointerPte
->u
.Hard
.Valid
== 1)
923 /* Resolve the fault -- this will release the PFN lock */
924 Status
= MiResolveProtoPteFault(StoreInstruction
,
934 ASSERT(Status
== STATUS_SUCCESS
);
936 /* Complete this as a transition fault */
937 ASSERT(OldIrql
== KeGetCurrentIrql());
938 ASSERT(OldIrql
<= APC_LEVEL
);
939 ASSERT(KeAreAllApcsDisabled() == TRUE
);
944 /* We only handle the lookup path */
945 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
947 /* Is there a non-image VAD? */
949 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
950 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
952 /* One day, ReactOS will cluster faults */
953 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
954 DPRINT("Should cluster fault, but won't\n");
957 /* Only one PTE to handle for now */
961 /* Lock the PFN database */
962 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
964 /* We only handle the valid path */
965 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
967 /* Capture the PTE */
968 TempPte
= *PointerProtoPte
;
970 /* Loop to handle future case of clustered faults */
973 /* For our current usage, this should be true */
974 if (TempPte
.u
.Hard
.Valid
== 1)
976 /* Bump the share count on the PTE */
977 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
978 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
979 Pfn1
->u2
.ShareCount
++;
981 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
982 (TempPte
.u
.Soft
.Transition
== 1))
984 /* This is a standby page, bring it back from the cache */
985 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
986 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
987 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
988 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
990 /* Should not yet happen in ReactOS */
991 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
992 ASSERT(Pfn1
->u4
.InPageError
== 0);
995 MiUnlinkPageFromList(Pfn1
);
997 /* Bump its reference count */
998 ASSERT(Pfn1
->u2
.ShareCount
== 0);
999 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1000 Pfn1
->u2
.ShareCount
++;
1002 /* Make it valid again */
1003 /* This looks like another macro.... */
1004 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1005 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1006 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1007 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1008 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1009 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1010 TempPte
.u
.Hard
.Valid
= 1;
1011 TempPte
.u
.Hard
.Accessed
= 1;
1013 /* Is the PTE writeable? */
1014 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
1015 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
1018 TempPte
.u
.Hard
.Dirty
= TRUE
;
1023 TempPte
.u
.Hard
.Dirty
= FALSE
;
1026 /* Write the valid PTE */
1027 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1028 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1032 /* Page is invalid, get out of the loop */
1036 /* One more done, was it the last? */
1037 if (++ProcessedPtes
== PteCount
)
1039 /* Complete the fault */
1040 MiCompleteProtoPteFault(StoreInstruction
,
1047 /* THIS RELEASES THE PFN LOCK! */
1051 /* No clustered faults yet */
1055 /* Did we resolve the fault? */
1058 /* Bump the transition count */
1059 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1062 /* Loop all the processing we did */
1063 ASSERT(ProcessedPtes
== 0);
1065 /* Complete this as a transition fault */
1066 ASSERT(OldIrql
== KeGetCurrentIrql());
1067 ASSERT(OldIrql
<= APC_LEVEL
);
1068 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1069 return STATUS_PAGE_FAULT_TRANSITION
;
1072 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1073 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1074 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1075 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1076 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1078 /* Resolve the fault -- this will release the PFN lock */
1079 Status
= MiResolveProtoPteFault(StoreInstruction
,
1089 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1090 //ASSERT(Status != STATUS_REFAULT);
1091 //ASSERT(Status != STATUS_PTE_CHANGED);
1093 /* Did the routine clean out the PFN or should we? */
1096 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1097 ASSERT(PointerProtoPte
!= NULL
);
1098 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1100 /* Dereference the locked PFN */
1101 MiDereferencePfnAndDropLockCount(OutPfn
);
1102 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1104 /* And now release the lock */
1105 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1108 /* Complete this as a transition fault */
1109 ASSERT(OldIrql
== KeGetCurrentIrql());
1110 ASSERT(OldIrql
<= APC_LEVEL
);
1111 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1117 // The PTE must be invalid but not completely empty. It must also not be a
1118 // prototype PTE as that scenario should've been handled above. These are
1119 // all Windows checks
1121 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1122 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1123 ASSERT(TempPte
.u
.Long
!= 0);
1126 // No transition or page file software PTEs in ARM3 yet, so this must be a
1127 // demand zero page. These are all ReactOS checks
1129 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1130 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1133 // If we got this far, the PTE can only be a demand zero PTE, which is what
1134 // we want. Go handle it!
1136 Status
= MiResolveDemandZeroFault(Address
,
1140 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1141 if (NT_SUCCESS(Status
))
1144 // Make sure we're returning in a sane state and pass the status down
1146 ASSERT(OldIrql
== KeGetCurrentIrql());
1147 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1152 // Generate an access fault
1154 return STATUS_ACCESS_VIOLATION
;
1159 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1161 IN KPROCESSOR_MODE Mode
,
1162 IN PVOID TrapInformation
)
1164 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1165 PMMPTE ProtoPte
= NULL
;
1166 PMMPTE PointerPte
= MiAddressToPte(Address
);
1167 PMMPDE PointerPde
= MiAddressToPde(Address
);
1168 #if (_MI_PAGING_LEVELS >= 3)
1169 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1170 #if (_MI_PAGING_LEVELS == 4)
1171 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1175 PETHREAD CurrentThread
;
1176 PEPROCESS CurrentProcess
;
1178 PMMSUPPORT WorkingSet
;
1179 ULONG ProtectionCode
;
1181 PFN_NUMBER PageFrameIndex
;
1183 BOOLEAN IsSessionAddress
;
1185 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1187 /* Check for page fault on high IRQL */
1188 if (OldIrql
> APC_LEVEL
)
1190 #if (_MI_PAGING_LEVELS < 3)
1191 /* Could be a page table for paged pool, which we'll allow */
1192 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1193 MiCheckPdeForPagedPool(Address
);
1195 /* Check if any of the top-level pages are invalid */
1197 #if (_MI_PAGING_LEVELS == 4)
1198 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1200 #if (_MI_PAGING_LEVELS >= 3)
1201 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1203 (PointerPde
->u
.Hard
.Valid
== 0))
1205 /* This fault is not valid, printf out some debugging help */
1206 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1209 if (TrapInformation
)
1211 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1213 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1214 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1215 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1216 #elif defined(_M_AMD64)
1217 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1218 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1219 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1223 /* Tell the trap handler to fail */
1224 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1227 /* Not yet implemented in ReactOS */
1228 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1229 ASSERT(((StoreInstruction
) && (PointerPte
->u
.Hard
.CopyOnWrite
)) == FALSE
);
1231 /* Check if this was a write */
1232 if (StoreInstruction
)
1234 /* Was it to a read-only page? */
1235 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1236 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1237 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1239 /* Crash with distinguished bugcheck code */
1240 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1243 (ULONG_PTR
)TrapInformation
,
1248 /* Nothing is actually wrong */
1249 DPRINT1("Fault at IRQL1 is ok\n");
1250 return STATUS_SUCCESS
;
1253 /* Check for kernel fault address */
1254 if (Address
>= MmSystemRangeStart
)
1256 /* Bail out, if the fault came from user mode */
1257 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1259 #if (_MI_PAGING_LEVELS == 4)
1260 /* AMD64 system, check if PXE is invalid */
1261 if (PointerPxe
->u
.Hard
.Valid
== 0)
1263 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1266 (ULONG_PTR
)TrapInformation
,
1270 #if (_MI_PAGING_LEVELS == 4)
1271 /* PAE/AMD64 system, check if PPE is invalid */
1272 if (PointerPpe
->u
.Hard
.Valid
== 0)
1274 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1277 (ULONG_PTR
)TrapInformation
,
1281 #if (_MI_PAGING_LEVELS == 2)
1282 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1283 MiCheckPdeForPagedPool(Address
);
1286 /* Check if the PDE is invalid */
1287 if (PointerPde
->u
.Hard
.Valid
== 0)
1289 /* PDE (still) not valid, kill the system */
1290 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1293 (ULONG_PTR
)TrapInformation
,
1297 /* Not handling session faults yet */
1298 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1300 /* The PDE is valid, so read the PTE */
1301 TempPte
= *PointerPte
;
1302 if (TempPte
.u
.Hard
.Valid
== 1)
1304 /* Check if this was system space or session space */
1305 if (!IsSessionAddress
)
1307 /* Check if the PTE is still valid under PFN lock */
1308 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1309 TempPte
= *PointerPte
;
1310 if (TempPte
.u
.Hard
.Valid
)
1312 /* Check if this was a write */
1313 if (StoreInstruction
)
1315 /* Was it to a read-only page? */
1316 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1317 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1318 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1320 /* Crash with distinguished bugcheck code */
1321 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1324 (ULONG_PTR
)TrapInformation
,
1330 /* Release PFN lock and return all good */
1331 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1332 return STATUS_SUCCESS
;
1336 /* Check if this was a session PTE that needs to remap the session PDE */
1337 if (MI_IS_SESSION_PTE(Address
))
1339 /* Do the remapping */
1340 Status
= MiCheckPdeForSessionSpace(Address
);
1341 if (!NT_SUCCESS(Status
))
1343 /* It failed, this address is invalid */
1344 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1347 (ULONG_PTR
)TrapInformation
,
1352 /* Check for a fault on the page table or hyperspace */
1353 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1355 #if (_MI_PAGING_LEVELS < 3)
1356 /* Windows does this check but I don't understand why -- it's done above! */
1357 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1359 /* Handle this as a user mode fault */
1363 /* Get the current thread */
1364 CurrentThread
= PsGetCurrentThread();
1366 /* What kind of address is this */
1367 if (!IsSessionAddress
)
1369 /* Use the system working set */
1370 WorkingSet
= &MmSystemCacheWs
;
1371 CurrentProcess
= NULL
;
1373 /* Make sure we don't have a recursive working set lock */
1374 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1375 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1376 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1377 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1378 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1379 (CurrentThread
->OwnsSessionWorkingSetShared
))
1382 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1387 /* Use the session process and working set */
1388 CurrentProcess
= HYDRA_PROCESS
;
1389 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1391 /* Make sure we don't have a recursive working set lock */
1392 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1393 (CurrentThread
->OwnsSessionWorkingSetShared
))
1396 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1400 /* Acquire the working set lock */
1401 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1402 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1404 /* Re-read PTE now that we own the lock */
1405 TempPte
= *PointerPte
;
1406 if (TempPte
.u
.Hard
.Valid
== 1)
1408 /* Check if this was a write */
1409 if (StoreInstruction
)
1411 /* Was it to a read-only page that is not copy on write? */
1412 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1413 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1414 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1415 !(TempPte
.u
.Hard
.CopyOnWrite
))
1417 /* Case not yet handled */
1418 ASSERT(!IsSessionAddress
);
1420 /* Crash with distinguished bugcheck code */
1421 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1424 (ULONG_PTR
)TrapInformation
,
1429 /* Check for read-only write in session space */
1430 if ((IsSessionAddress
) &&
1431 (StoreInstruction
) &&
1432 !(TempPte
.u
.Hard
.Write
))
1435 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1438 if (TempPte
.u
.Hard
.CopyOnWrite
== 0)
1440 /* Then this is not allowed */
1441 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1443 (ULONG_PTR
)TempPte
.u
.Long
,
1444 (ULONG_PTR
)TrapInformation
,
1448 /* Otherwise, handle COW */
1452 /* Release the working set */
1453 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1454 KeLowerIrql(LockIrql
);
1456 /* Otherwise, the PDE was probably invalid, and all is good now */
1457 return STATUS_SUCCESS
;
1460 /* Check one kind of prototype PTE */
1461 if (TempPte
.u
.Soft
.Prototype
)
1463 /* Make sure protected pool is on, and that this is a pool address */
1464 if ((MmProtectFreedNonPagedPool
) &&
1465 (((Address
>= MmNonPagedPoolStart
) &&
1466 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1467 MmSizeOfNonPagedPoolInBytes
))) ||
1468 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1469 (Address
< MmNonPagedPoolEnd
))))
1471 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1472 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1479 /* Get the prototype PTE! */
1480 ProtoPte
= MiProtoPteToPte(&TempPte
);
1482 /* Do we need to locate the prototype PTE in session space? */
1483 if ((IsSessionAddress
) &&
1484 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1486 /* Yep, go find it as well as the VAD for it */
1487 ProtoPte
= MiCheckVirtualAddress(Address
,
1490 ASSERT(ProtoPte
!= NULL
);
1495 /* We don't implement transition PTEs */
1496 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1498 /* Check for no-access PTE */
1499 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1501 /* Bugcheck the system! */
1502 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1505 (ULONG_PTR
)TrapInformation
,
1510 /* Check for demand page */
1511 if ((StoreInstruction
) &&
1513 !(IsSessionAddress
) &&
1514 !(TempPte
.u
.Hard
.Valid
))
1516 /* Get the protection code */
1517 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1518 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1520 /* Bugcheck the system! */
1521 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1524 (ULONG_PTR
)TrapInformation
,
1529 /* Now do the real fault handling */
1530 Status
= MiDispatchFault(StoreInstruction
,
1539 /* Release the working set */
1540 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1541 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1542 KeLowerIrql(LockIrql
);
1545 DPRINT("Fault resolved with status: %lx\n", Status
);
1549 /* This is a user fault */
1551 CurrentThread
= PsGetCurrentThread();
1552 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
1554 /* Lock the working set */
1555 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1557 #if (_MI_PAGING_LEVELS == 4)
1558 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1559 // also this is missing the page count increment
1560 /* Check if the PXE is valid */
1561 if (PointerPxe
->u
.Hard
.Valid
== 0)
1563 /* Right now, we only handle scenarios where the PXE is totally empty */
1564 ASSERT(PointerPxe
->u
.Long
== 0);
1566 /* Resolve a demand zero fault */
1567 Status
= MiResolveDemandZeroFault(PointerPpe
,
1572 /* We should come back with a valid PXE */
1573 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1577 #if (_MI_PAGING_LEVELS >= 3)
1578 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1579 // also this is missing the page count increment
1580 /* Check if the PPE is valid */
1581 if (PointerPpe
->u
.Hard
.Valid
== 0)
1583 /* Right now, we only handle scenarios where the PPE is totally empty */
1584 ASSERT(PointerPpe
->u
.Long
== 0);
1586 /* Resolve a demand zero fault */
1587 Status
= MiResolveDemandZeroFault(PointerPde
,
1592 /* We should come back with a valid PPE */
1593 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1597 /* Check if the PDE is valid */
1598 if (PointerPde
->u
.Hard
.Valid
== 0)
1600 /* Right now, we only handle scenarios where the PDE is totally empty */
1601 ASSERT(PointerPde
->u
.Long
== 0);
1603 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1605 UserPdeFault
= TRUE
;
1607 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1608 if (ProtectionCode
== MM_NOACCESS
)
1610 #if (_MI_PAGING_LEVELS == 2)
1611 /* Could be a page table for paged pool */
1612 MiCheckPdeForPagedPool(Address
);
1614 /* Has the code above changed anything -- is this now a valid PTE? */
1615 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1617 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1618 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1622 /* Write a demand-zero PDE */
1623 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
1625 /* Dispatch the fault */
1626 Status
= MiDispatchFault(TRUE
,
1631 PsGetCurrentProcess(),
1635 UserPdeFault
= FALSE
;
1637 /* We should come back with APCs enabled, and with a valid PDE */
1638 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1639 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1643 /* Not yet implemented in ReactOS */
1644 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1647 /* Now capture the PTE. Ignore virtual faults for now */
1648 TempPte
= *PointerPte
;
1649 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1651 /* Quick check for demand-zero */
1652 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
1654 /* Resolve the fault */
1655 MiResolveDemandZeroFault(Address
,
1660 /* Return the status */
1661 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1662 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1665 /* Check for zero PTE */
1666 if (TempPte
.u
.Long
== 0)
1668 /* Check if this address range belongs to a valid allocation (VAD) */
1669 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1670 if (ProtectionCode
== MM_NOACCESS
)
1672 #if (_MI_PAGING_LEVELS == 2)
1673 /* Could be a page table for paged pool */
1674 MiCheckPdeForPagedPool(Address
);
1676 /* Has the code above changed anything -- is this now a valid PTE? */
1677 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1679 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1680 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1684 /* No guard page support yet */
1685 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1688 * Check if this is a real user-mode address or actually a kernel-mode
1689 * page table for a user mode address
1691 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
1693 /* Add an additional page table reference */
1694 MiIncrementPageTableReferences(Address
);
1697 /* Did we get a prototype PTE back? */
1700 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1701 if (PointerPde
== MiAddressToPde(PTE_BASE
))
1703 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1704 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
1708 /* No, create a new PTE. First, write the protection */
1709 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
1712 /* Lock the PFN database since we're going to grab a page */
1713 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1715 /* Make sure we have enough pages */
1716 ASSERT(MmAvailablePages
>= 32);
1718 /* Try to get a zero page */
1719 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1720 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1721 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1722 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1723 if (!PageFrameIndex
)
1725 /* Grab a page out of there. Later we should grab a colored zero page */
1726 PageFrameIndex
= MiRemoveAnyPage(Color
);
1727 ASSERT(PageFrameIndex
);
1729 /* Release the lock since we need to do some zeroing */
1730 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1732 /* Zero out the page, since it's for user-mode */
1733 MiZeroPfn(PageFrameIndex
);
1735 /* Grab the lock again so we can initialize the PFN entry */
1736 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1739 /* Initialize the PFN entry now */
1740 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1742 /* And we're done with the lock */
1743 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1745 /* Increment the count of pages in the process */
1746 CurrentProcess
->NumberOfPrivatePages
++;
1748 /* One more demand-zero fault */
1749 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
1751 /* Fault on user PDE, or fault on user PTE? */
1752 if (PointerPte
<= MiHighestUserPte
)
1754 /* User fault, build a user PTE */
1755 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1757 PointerPte
->u
.Soft
.Protection
,
1762 /* This is a user-mode PDE, create a kernel PTE for it */
1763 MI_MAKE_HARDWARE_PTE(&TempPte
,
1765 PointerPte
->u
.Soft
.Protection
,
1769 /* Write the dirty bit for writeable pages */
1770 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1772 /* And now write down the PTE, making the address valid */
1773 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1774 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1775 ASSERT(Pfn1
->u1
.Event
== NULL
);
1778 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1779 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1780 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1783 /* No guard page support yet */
1784 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1785 ASSERT(ProtectionCode
!= 0x100);
1787 /* Write the prototype PTE */
1788 TempPte
= PrototypePte
;
1789 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1790 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1794 /* Get the protection code and check if this is a proto PTE */
1795 ProtectionCode
= TempPte
.u
.Soft
.Protection
;
1796 if (TempPte
.u
.Soft
.Prototype
)
1798 /* Do we need to go find the real PTE? */
1799 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1801 /* Get the prototype pte and VAD for it */
1802 ProtoPte
= MiCheckVirtualAddress(Address
,
1807 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1808 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1809 return STATUS_ACCESS_VIOLATION
;
1814 /* Get the prototype PTE! */
1815 ProtoPte
= MiProtoPteToPte(&TempPte
);
1817 /* Is it read-only */
1818 if (TempPte
.u
.Proto
.ReadOnly
)
1820 /* Set read-only code */
1821 ProtectionCode
= MM_READONLY
;
1825 /* Set unknown protection */
1826 ProtectionCode
= 0x100;
1827 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
1833 /* FIXME: Run MiAccessCheck */
1835 /* Dispatch the fault */
1836 Status
= MiDispatchFault(StoreInstruction
,
1845 /* Return the status */
1846 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1847 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1853 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
1855 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1856 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1858 *ExecuteOptions
= 0;
1860 if (CurrentProcess
->Flags
.ExecuteDisable
)
1862 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
1865 if (CurrentProcess
->Flags
.ExecuteEnable
)
1867 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
1870 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
1872 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
1875 if (CurrentProcess
->Flags
.Permanent
)
1877 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
1880 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
1882 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
1885 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
1887 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
1890 return STATUS_SUCCESS
;
1895 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
1897 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1898 KLOCK_QUEUE_HANDLE ProcessLock
;
1899 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
1900 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1902 /* Only accept valid flags */
1903 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
1906 DPRINT1("Invalid no-execute options\n");
1907 return STATUS_INVALID_PARAMETER
;
1910 /* Change the NX state in the process lock */
1911 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
1913 /* Don't change anything if the permanent flag was set */
1914 if (!CurrentProcess
->Flags
.Permanent
)
1916 /* Start by assuming it's not disabled */
1917 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
1919 /* Now process each flag and turn the equivalent bit on */
1920 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
1922 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
1924 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
1926 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
1928 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
1930 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
1932 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
1934 CurrentProcess
->Flags
.Permanent
= TRUE
;
1936 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
1938 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1940 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
1942 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1945 /* These are turned on by default if no-execution is also eanbled */
1946 if (CurrentProcess
->Flags
.ExecuteEnable
)
1948 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1949 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1953 Status
= STATUS_SUCCESS
;
1956 /* Release the lock and return status */
1957 KiReleaseProcessLock(&ProcessLock
);