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++;
532 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2
);
534 /* Check where we should be getting the protection information from */
535 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
537 /* Get the protection from the PTE, there's no real Proto PTE data */
538 Protection
= PointerPte
->u
.Soft
.Protection
;
540 /* Remember that we did not use the proto protection */
541 OriginalProtection
= FALSE
;
545 /* Get the protection from the original PTE link */
546 OriginalPte
= &Pfn1
->OriginalPte
;
547 Protection
= OriginalPte
->u
.Soft
.Protection
;
549 /* Remember that we used the original protection */
550 OriginalProtection
= TRUE
;
552 /* Check if this was a write on a read only proto */
553 if ((StoreInstruction
) && !(Protection
& MM_READWRITE
))
556 StoreInstruction
= 0;
560 /* Check if this was a write on a non-COW page */
562 if ((StoreInstruction
) && ((Protection
& MM_WRITECOPY
) != MM_WRITECOPY
))
564 /* Then the page should be marked dirty */
568 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
!= 0);
571 /* Did we get a locked incoming PFN? */
574 /* Drop a reference */
575 ASSERT((*LockedProtoPfn
)->u3
.e2
.ReferenceCount
>= 1);
576 MiDereferencePfnAndDropLockCount(*LockedProtoPfn
);
577 *LockedProtoPfn
= NULL
;
580 /* Release the PFN lock */
581 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
583 /* Remove caching bits */
584 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
587 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
589 /* Write combining, no caching */
590 MI_PAGE_DISABLE_CACHE(&TempPte
);
591 MI_PAGE_WRITE_COMBINED(&TempPte
);
593 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
595 /* Write through, no caching */
596 MI_PAGE_DISABLE_CACHE(&TempPte
);
597 MI_PAGE_WRITE_THROUGH(&TempPte
);
600 /* Check if this is a kernel or user address */
601 if (Address
< MmSystemRangeStart
)
603 /* Build the user PTE */
604 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
608 /* Build the kernel PTE */
609 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
612 /* Set the dirty flag if needed */
613 if (DirtyPage
) TempPte
.u
.Hard
.Dirty
= TRUE
;
616 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
618 /* Reset the protection if needed */
619 if (OriginalProtection
) Protection
= MM_ZERO_ACCESS
;
622 ASSERT(PointerPte
== MiAddressToPte(Address
));
623 return STATUS_SUCCESS
;
628 MiResolveTransitionFault(IN PVOID FaultingAddress
,
629 IN PMMPTE PointerPte
,
630 IN PEPROCESS CurrentProcess
,
632 OUT PVOID
*InPageBlock
)
634 PFN_NUMBER PageFrameIndex
;
637 PMMPTE PointerToPteForProtoPage
;
638 DPRINT1("Transition fault on 0x%p with PTE 0x%lx in process %s\n", FaultingAddress
, PointerPte
, CurrentProcess
->ImageFileName
);
640 /* Windowss does this check */
641 ASSERT(*InPageBlock
== NULL
);
643 /* ARM3 doesn't support this path */
644 ASSERT(OldIrql
!= MM_NOIRQL
);
646 /* Capture the PTE and make sure it's in transition format */
647 TempPte
= *PointerPte
;
648 ASSERT((TempPte
.u
.Soft
.Valid
== 0) &&
649 (TempPte
.u
.Soft
.Prototype
== 0) &&
650 (TempPte
.u
.Soft
.Transition
== 1));
652 /* Get the PFN and the PFN entry */
653 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
654 DPRINT1("Transition PFN: %lx\n", PageFrameIndex
);
655 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
657 /* One more transition fault! */
658 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
660 /* This is from ARM3 -- Windows normally handles this here */
661 ASSERT(Pfn1
->u4
.InPageError
== 0);
663 /* Not supported in ARM3 */
664 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
666 /* Windows checks there's some free pages and this isn't an in-page error */
667 ASSERT(MmAvailablePages
> 0);
668 ASSERT(Pfn1
->u4
.InPageError
== 0);
670 /* ReactOS checks for this */
671 ASSERT(MmAvailablePages
> 32);
673 /* Was this a transition page in the valid list, or free/zero list? */
674 if (Pfn1
->u3
.e1
.PageLocation
== ActiveAndValid
)
676 /* All Windows does here is a bunch of sanity checks */
677 DPRINT1("Transition in active list\n");
678 ASSERT((Pfn1
->PteAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
679 (Pfn1
->PteAddress
<= MiAddressToPte(MmPagedPoolEnd
)));
680 ASSERT(Pfn1
->u2
.ShareCount
!= 0);
681 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
685 /* Otherwise, the page is removed from its list */
686 DPRINT1("Transition page in free/zero list\n");
687 MiUnlinkPageFromList(Pfn1
);
688 MiReferenceUnusedPageAndBumpLockCount(Pfn1
);
691 /* At this point, there should no longer be any in-page errors */
692 ASSERT(Pfn1
->u4
.InPageError
== 0);
694 /* Check if this was a PFN with no more share references */
695 if (Pfn1
->u2
.ShareCount
== 0) MiDropLockCount(Pfn1
);
697 /* Bump the share count and make the page valid */
698 Pfn1
->u2
.ShareCount
++;
699 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
701 /* Prototype PTEs are in paged pool, which itself might be in transition */
702 if (FaultingAddress
>= MmSystemRangeStart
)
704 /* Check if this is a paged pool PTE in transition state */
705 PointerToPteForProtoPage
= MiAddressToPte(PointerPte
);
706 TempPte
= *PointerToPteForProtoPage
;
707 if ((TempPte
.u
.Hard
.Valid
== 0) && (TempPte
.u
.Soft
.Transition
== 1))
709 /* This isn't yet supported */
710 DPRINT1("Double transition fault not yet supported\n");
715 /* Build the transition PTE -- maybe a macro? */
716 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
717 ASSERT(PointerPte
->u
.Trans
.Prototype
== 0);
718 ASSERT(PointerPte
->u
.Trans
.Transition
== 1);
719 TempPte
.u
.Long
= (PointerPte
->u
.Long
& ~0xFFF) |
720 (MmProtectToPteMask
[PointerPte
->u
.Trans
.Protection
]) |
721 MiDetermineUserGlobalPteMask(PointerPte
);
723 /* Is the PTE writeable? */
724 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
725 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
728 TempPte
.u
.Hard
.Dirty
= TRUE
;
733 TempPte
.u
.Hard
.Dirty
= FALSE
;
736 /* Write the valid PTE */
737 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
740 return STATUS_PAGE_FAULT_TRANSITION
;
745 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
747 IN PMMPTE PointerPte
,
748 IN PMMPTE PointerProtoPte
,
749 IN OUT PMMPFN
*OutPfn
,
750 OUT PVOID
*PageFileData
,
752 IN PEPROCESS Process
,
754 IN PVOID TrapInformation
)
756 MMPTE TempPte
, PteContents
;
758 PFN_NUMBER PageFrameIndex
;
760 PVOID InPageBlock
= NULL
;
762 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
763 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
764 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
765 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
767 /* Read the prototype PTE and check if it's valid */
768 TempPte
= *PointerProtoPte
;
769 if (TempPte
.u
.Hard
.Valid
== 1)
771 /* One more user of this mapped page */
772 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
773 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
774 Pfn1
->u2
.ShareCount
++;
776 /* Call it a transition */
777 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
779 /* Complete the prototype PTE fault -- this will release the PFN lock */
780 return MiCompleteProtoPteFault(StoreInstruction
,
788 /* Make sure there's some protection mask */
789 if (TempPte
.u
.Long
== 0)
791 /* Release the lock */
792 DPRINT1("Access on reserved section?\n");
793 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
794 return STATUS_ACCESS_VIOLATION
;
797 /* Check for access rights on the PTE proper */
798 PteContents
= *PointerPte
;
799 if (PteContents
.u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)
801 if (!PteContents
.u
.Proto
.ReadOnly
)
803 /* FIXME: CHECK FOR ACCESS */
805 /* Check for copy on write page */
806 if ((TempPte
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
808 /* Not yet supported */
815 /* Check for copy on write page */
816 if ((PteContents
.u
.Soft
.Protection
& MM_WRITECOPY
) == MM_WRITECOPY
)
818 /* Not yet supported */
823 /* Check for clone PTEs */
824 if (PointerPte
<= MiHighestUserPte
) ASSERT(Process
->CloneRoot
== NULL
);
826 /* We don't support mapped files yet */
827 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
829 /* We might however have transition PTEs */
830 if (TempPte
.u
.Soft
.Transition
== 1)
832 /* Resolve the transition fault */
833 ASSERT(OldIrql
!= MM_NOIRQL
);
834 Status
= MiResolveTransitionFault(Address
,
839 ASSERT(NT_SUCCESS(Status
));
843 /* We also don't support paged out pages */
844 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
846 /* Resolve the demand zero fault */
847 Status
= MiResolveDemandZeroFault(Address
,
851 ASSERT(NT_SUCCESS(Status
));
854 /* Complete the prototype PTE fault -- this will release the PFN lock */
855 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
856 return MiCompleteProtoPteFault(StoreInstruction
,
866 MiDispatchFault(IN BOOLEAN StoreInstruction
,
868 IN PMMPTE PointerPte
,
869 IN PMMPTE PointerProtoPte
,
870 IN BOOLEAN Recursive
,
871 IN PEPROCESS Process
,
872 IN PVOID TrapInformation
,
876 KIRQL OldIrql
, LockIrql
;
878 PMMPTE SuperProtoPte
;
879 PMMPFN Pfn1
, OutPfn
= NULL
;
880 PFN_NUMBER PageFrameIndex
;
881 PFN_COUNT PteCount
, ProcessedPtes
;
882 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
886 /* Make sure the addresses are ok */
887 ASSERT(PointerPte
== MiAddressToPte(Address
));
890 // Make sure APCs are off and we're not at dispatch
892 OldIrql
= KeGetCurrentIrql();
893 ASSERT(OldIrql
<= APC_LEVEL
);
894 ASSERT(KeAreAllApcsDisabled() == TRUE
);
897 // Grab a copy of the PTE
899 TempPte
= *PointerPte
;
901 /* Do we have a prototype PTE? */
904 /* This should never happen */
905 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
907 /* Check if this is a kernel-mode address */
908 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
909 if (Address
>= MmSystemRangeStart
)
911 /* Lock the PFN database */
912 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
914 /* Has the PTE been made valid yet? */
915 if (!SuperProtoPte
->u
.Hard
.Valid
)
919 else if (PointerPte
->u
.Hard
.Valid
== 1)
924 /* Resolve the fault -- this will release the PFN lock */
925 Status
= MiResolveProtoPteFault(StoreInstruction
,
935 ASSERT(Status
== STATUS_SUCCESS
);
937 /* Complete this as a transition fault */
938 ASSERT(OldIrql
== KeGetCurrentIrql());
939 ASSERT(OldIrql
<= APC_LEVEL
);
940 ASSERT(KeAreAllApcsDisabled() == TRUE
);
945 /* We only handle the lookup path */
946 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
948 /* Is there a non-image VAD? */
950 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
951 !(Vad
->u2
.VadFlags2
.ExtendableFile
))
953 /* One day, ReactOS will cluster faults */
954 ASSERT(Address
<= MM_HIGHEST_USER_ADDRESS
);
955 DPRINT("Should cluster fault, but won't\n");
958 /* Only one PTE to handle for now */
962 /* Lock the PFN database */
963 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
965 /* We only handle the valid path */
966 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
968 /* Capture the PTE */
969 TempPte
= *PointerProtoPte
;
971 /* Loop to handle future case of clustered faults */
974 /* For our current usage, this should be true */
975 if (TempPte
.u
.Hard
.Valid
== 1)
977 /* Bump the share count on the PTE */
978 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
979 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
980 Pfn1
->u2
.ShareCount
++;
982 else if ((TempPte
.u
.Soft
.Prototype
== 0) &&
983 (TempPte
.u
.Soft
.Transition
== 1))
985 /* This is a standby page, bring it back from the cache */
986 PageFrameIndex
= TempPte
.u
.Trans
.PageFrameNumber
;
987 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex
);
988 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
989 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
991 /* Should not yet happen in ReactOS */
992 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
993 ASSERT(Pfn1
->u4
.InPageError
== 0);
996 MiUnlinkPageFromList(Pfn1
);
998 /* Bump its reference count */
999 ASSERT(Pfn1
->u2
.ShareCount
== 0);
1000 InterlockedIncrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1001 Pfn1
->u2
.ShareCount
++;
1003 /* Make it valid again */
1004 /* This looks like another macro.... */
1005 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1006 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 0);
1007 ASSERT(PointerProtoPte
->u
.Trans
.Prototype
== 0);
1008 ASSERT(PointerProtoPte
->u
.Trans
.Transition
== 1);
1009 TempPte
.u
.Long
= (PointerProtoPte
->u
.Long
& ~0xFFF) |
1010 MmProtectToPteMask
[PointerProtoPte
->u
.Trans
.Protection
];
1011 TempPte
.u
.Hard
.Valid
= 1;
1012 TempPte
.u
.Hard
.Accessed
= 1;
1014 /* Is the PTE writeable? */
1015 if (((Pfn1
->u3
.e1
.Modified
) && (TempPte
.u
.Hard
.Write
)) &&
1016 (TempPte
.u
.Hard
.CopyOnWrite
== 0))
1019 TempPte
.u
.Hard
.Dirty
= TRUE
;
1024 TempPte
.u
.Hard
.Dirty
= FALSE
;
1027 /* Write the valid PTE */
1028 MI_WRITE_VALID_PTE(PointerProtoPte
, TempPte
);
1029 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1033 /* Page is invalid, get out of the loop */
1037 /* One more done, was it the last? */
1038 if (++ProcessedPtes
== PteCount
)
1040 /* Complete the fault */
1041 MiCompleteProtoPteFault(StoreInstruction
,
1048 /* THIS RELEASES THE PFN LOCK! */
1052 /* No clustered faults yet */
1056 /* Did we resolve the fault? */
1059 /* Bump the transition count */
1060 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount
, ProcessedPtes
);
1063 /* Loop all the processing we did */
1064 ASSERT(ProcessedPtes
== 0);
1066 /* Complete this as a transition fault */
1067 ASSERT(OldIrql
== KeGetCurrentIrql());
1068 ASSERT(OldIrql
<= APC_LEVEL
);
1069 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1070 return STATUS_PAGE_FAULT_TRANSITION
;
1073 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1074 OutPfn
= MI_PFN_ELEMENT(SuperProtoPte
->u
.Hard
.PageFrameNumber
);
1075 MiReferenceUsedPageAndBumpLockCount(OutPfn
);
1076 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
> 1);
1077 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1079 /* Resolve the fault -- this will release the PFN lock */
1080 Status
= MiResolveProtoPteFault(StoreInstruction
,
1090 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1091 //ASSERT(Status != STATUS_REFAULT);
1092 //ASSERT(Status != STATUS_PTE_CHANGED);
1094 /* Did the routine clean out the PFN or should we? */
1097 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1098 ASSERT(PointerProtoPte
!= NULL
);
1099 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1101 /* Dereference the locked PFN */
1102 MiDereferencePfnAndDropLockCount(OutPfn
);
1103 ASSERT(OutPfn
->u3
.e2
.ReferenceCount
>= 1);
1105 /* And now release the lock */
1106 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1109 /* Complete this as a transition fault */
1110 ASSERT(OldIrql
== KeGetCurrentIrql());
1111 ASSERT(OldIrql
<= APC_LEVEL
);
1112 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1118 // The PTE must be invalid but not completely empty. It must also not be a
1119 // prototype PTE as that scenario should've been handled above. These are
1120 // all Windows checks
1122 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1123 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1124 ASSERT(TempPte
.u
.Long
!= 0);
1127 // No transition or page file software PTEs in ARM3 yet, so this must be a
1128 // demand zero page. These are all ReactOS checks
1130 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1131 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
1134 // If we got this far, the PTE can only be a demand zero PTE, which is what
1135 // we want. Go handle it!
1137 Status
= MiResolveDemandZeroFault(Address
,
1141 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1142 if (NT_SUCCESS(Status
))
1145 // Make sure we're returning in a sane state and pass the status down
1147 ASSERT(OldIrql
== KeGetCurrentIrql());
1148 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1153 // Generate an access fault
1155 return STATUS_ACCESS_VIOLATION
;
1160 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
1162 IN KPROCESSOR_MODE Mode
,
1163 IN PVOID TrapInformation
)
1165 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
1166 PMMPTE ProtoPte
= NULL
;
1167 PMMPTE PointerPte
= MiAddressToPte(Address
);
1168 PMMPDE PointerPde
= MiAddressToPde(Address
);
1169 #if (_MI_PAGING_LEVELS >= 3)
1170 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
1171 #if (_MI_PAGING_LEVELS == 4)
1172 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
1176 PETHREAD CurrentThread
;
1177 PEPROCESS CurrentProcess
;
1179 PMMSUPPORT WorkingSet
;
1180 ULONG ProtectionCode
;
1182 PFN_NUMBER PageFrameIndex
;
1184 BOOLEAN IsSessionAddress
;
1186 DPRINT("ARM3 FAULT AT: %p\n", Address
);
1188 /* Check for page fault on high IRQL */
1189 if (OldIrql
> APC_LEVEL
)
1191 #if (_MI_PAGING_LEVELS < 3)
1192 /* Could be a page table for paged pool, which we'll allow */
1193 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1194 MiCheckPdeForPagedPool(Address
);
1196 /* Check if any of the top-level pages are invalid */
1198 #if (_MI_PAGING_LEVELS == 4)
1199 (PointerPxe
->u
.Hard
.Valid
== 0) ||
1201 #if (_MI_PAGING_LEVELS >= 3)
1202 (PointerPpe
->u
.Hard
.Valid
== 0) ||
1204 (PointerPde
->u
.Hard
.Valid
== 0))
1206 /* This fault is not valid, printf out some debugging help */
1207 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1210 if (TrapInformation
)
1212 PKTRAP_FRAME TrapFrame
= TrapInformation
;
1214 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame
->Eip
, TrapFrame
->EFlags
);
1215 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1216 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame
->Ebx
, TrapFrame
->Esi
, TrapFrame
->Edi
);
1217 #elif defined(_M_AMD64)
1218 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame
->Rip
, TrapFrame
->EFlags
);
1219 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame
->Rax
, TrapFrame
->Rcx
, TrapFrame
->Rdx
);
1220 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame
->Rbx
, TrapFrame
->Rsi
, TrapFrame
->Rdi
);
1224 /* Tell the trap handler to fail */
1225 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1228 /* Not yet implemented in ReactOS */
1229 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1230 ASSERT(((StoreInstruction
) && (PointerPte
->u
.Hard
.CopyOnWrite
)) == FALSE
);
1232 /* Check if this was a write */
1233 if (StoreInstruction
)
1235 /* Was it to a read-only page? */
1236 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1237 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1238 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1240 /* Crash with distinguished bugcheck code */
1241 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1244 (ULONG_PTR
)TrapInformation
,
1249 /* Nothing is actually wrong */
1250 DPRINT1("Fault at IRQL1 is ok\n");
1251 return STATUS_SUCCESS
;
1254 /* Check for kernel fault address */
1255 if (Address
>= MmSystemRangeStart
)
1257 /* Bail out, if the fault came from user mode */
1258 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
1260 #if (_MI_PAGING_LEVELS == 4)
1261 /* AMD64 system, check if PXE is invalid */
1262 if (PointerPxe
->u
.Hard
.Valid
== 0)
1264 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1267 (ULONG_PTR
)TrapInformation
,
1271 #if (_MI_PAGING_LEVELS == 4)
1272 /* PAE/AMD64 system, check if PPE is invalid */
1273 if (PointerPpe
->u
.Hard
.Valid
== 0)
1275 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1278 (ULONG_PTR
)TrapInformation
,
1282 #if (_MI_PAGING_LEVELS == 2)
1283 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
)) MiSynchronizeSystemPde((PMMPDE
)PointerPte
);
1284 MiCheckPdeForPagedPool(Address
);
1287 /* Check if the PDE is invalid */
1288 if (PointerPde
->u
.Hard
.Valid
== 0)
1290 /* PDE (still) not valid, kill the system */
1291 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1294 (ULONG_PTR
)TrapInformation
,
1298 /* Not handling session faults yet */
1299 IsSessionAddress
= MI_IS_SESSION_ADDRESS(Address
);
1301 /* The PDE is valid, so read the PTE */
1302 TempPte
= *PointerPte
;
1303 if (TempPte
.u
.Hard
.Valid
== 1)
1305 /* Check if this was system space or session space */
1306 if (!IsSessionAddress
)
1308 /* Check if the PTE is still valid under PFN lock */
1309 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1310 TempPte
= *PointerPte
;
1311 if (TempPte
.u
.Hard
.Valid
)
1313 /* Check if this was a write */
1314 if (StoreInstruction
)
1316 /* Was it to a read-only page? */
1317 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1318 if (!(PointerPte
->u
.Long
& PTE_READWRITE
) &&
1319 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
))
1321 /* Crash with distinguished bugcheck code */
1322 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1325 (ULONG_PTR
)TrapInformation
,
1331 /* Release PFN lock and return all good */
1332 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1333 return STATUS_SUCCESS
;
1337 /* Check if this was a session PTE that needs to remap the session PDE */
1338 if (MI_IS_SESSION_PTE(Address
))
1340 /* Do the remapping */
1341 Status
= MiCheckPdeForSessionSpace(Address
);
1342 if (!NT_SUCCESS(Status
))
1344 /* It failed, this address is invalid */
1345 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1348 (ULONG_PTR
)TrapInformation
,
1353 /* Check for a fault on the page table or hyperspace */
1354 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
1356 #if (_MI_PAGING_LEVELS < 3)
1357 /* Windows does this check but I don't understand why -- it's done above! */
1358 ASSERT(MiCheckPdeForPagedPool(Address
) != STATUS_WAIT_1
);
1360 /* Handle this as a user mode fault */
1364 /* Get the current thread */
1365 CurrentThread
= PsGetCurrentThread();
1367 /* What kind of address is this */
1368 if (!IsSessionAddress
)
1370 /* Use the system working set */
1371 WorkingSet
= &MmSystemCacheWs
;
1372 CurrentProcess
= NULL
;
1374 /* Make sure we don't have a recursive working set lock */
1375 if ((CurrentThread
->OwnsProcessWorkingSetExclusive
) ||
1376 (CurrentThread
->OwnsProcessWorkingSetShared
) ||
1377 (CurrentThread
->OwnsSystemWorkingSetExclusive
) ||
1378 (CurrentThread
->OwnsSystemWorkingSetShared
) ||
1379 (CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1380 (CurrentThread
->OwnsSessionWorkingSetShared
))
1383 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1388 /* Use the session process and working set */
1389 CurrentProcess
= HYDRA_PROCESS
;
1390 WorkingSet
= &MmSessionSpace
->GlobalVirtualAddress
->Vm
;
1392 /* Make sure we don't have a recursive working set lock */
1393 if ((CurrentThread
->OwnsSessionWorkingSetExclusive
) ||
1394 (CurrentThread
->OwnsSessionWorkingSetShared
))
1397 return STATUS_IN_PAGE_ERROR
| 0x10000000;
1401 /* Acquire the working set lock */
1402 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
1403 MiLockWorkingSet(CurrentThread
, WorkingSet
);
1405 /* Re-read PTE now that we own the lock */
1406 TempPte
= *PointerPte
;
1407 if (TempPte
.u
.Hard
.Valid
== 1)
1409 /* Check if this was a write */
1410 if (StoreInstruction
)
1412 /* Was it to a read-only page that is not copy on write? */
1413 Pfn1
= MI_PFN_ELEMENT(PointerPte
->u
.Hard
.PageFrameNumber
);
1414 if (!(TempPte
.u
.Long
& PTE_READWRITE
) &&
1415 !(Pfn1
->OriginalPte
.u
.Soft
.Protection
& MM_READWRITE
) &&
1416 !(TempPte
.u
.Hard
.CopyOnWrite
))
1418 /* Case not yet handled */
1419 ASSERT(!IsSessionAddress
);
1421 /* Crash with distinguished bugcheck code */
1422 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1425 (ULONG_PTR
)TrapInformation
,
1430 /* Check for read-only write in session space */
1431 if ((IsSessionAddress
) &&
1432 (StoreInstruction
) &&
1433 !(TempPte
.u
.Hard
.Write
))
1436 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address
));
1439 if (TempPte
.u
.Hard
.CopyOnWrite
== 0)
1441 /* Then this is not allowed */
1442 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1444 (ULONG_PTR
)TempPte
.u
.Long
,
1445 (ULONG_PTR
)TrapInformation
,
1449 /* Otherwise, handle COW */
1453 /* Release the working set */
1454 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1455 KeLowerIrql(LockIrql
);
1457 /* Otherwise, the PDE was probably invalid, and all is good now */
1458 return STATUS_SUCCESS
;
1461 /* Check one kind of prototype PTE */
1462 if (TempPte
.u
.Soft
.Prototype
)
1464 /* Make sure protected pool is on, and that this is a pool address */
1465 if ((MmProtectFreedNonPagedPool
) &&
1466 (((Address
>= MmNonPagedPoolStart
) &&
1467 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
1468 MmSizeOfNonPagedPoolInBytes
))) ||
1469 ((Address
>= MmNonPagedPoolExpansionStart
) &&
1470 (Address
< MmNonPagedPoolEnd
))))
1472 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1473 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
1480 /* Get the prototype PTE! */
1481 ProtoPte
= MiProtoPteToPte(&TempPte
);
1483 /* Do we need to locate the prototype PTE in session space? */
1484 if ((IsSessionAddress
) &&
1485 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
))
1487 /* Yep, go find it as well as the VAD for it */
1488 ProtoPte
= MiCheckVirtualAddress(Address
,
1491 ASSERT(ProtoPte
!= NULL
);
1496 /* We don't implement transition PTEs */
1497 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1499 /* Check for no-access PTE */
1500 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
1502 /* Bugcheck the system! */
1503 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
1506 (ULONG_PTR
)TrapInformation
,
1511 /* Check for demand page */
1512 if ((StoreInstruction
) &&
1514 !(IsSessionAddress
) &&
1515 !(TempPte
.u
.Hard
.Valid
))
1517 /* Get the protection code */
1518 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
1519 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
1521 /* Bugcheck the system! */
1522 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
1525 (ULONG_PTR
)TrapInformation
,
1530 /* Now do the real fault handling */
1531 Status
= MiDispatchFault(StoreInstruction
,
1540 /* Release the working set */
1541 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1542 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
1543 KeLowerIrql(LockIrql
);
1546 DPRINT("Fault resolved with status: %lx\n", Status
);
1550 /* This is a user fault */
1552 CurrentThread
= PsGetCurrentThread();
1553 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
1555 /* Lock the working set */
1556 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1558 #if (_MI_PAGING_LEVELS == 4)
1559 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1560 // also this is missing the page count increment
1561 /* Check if the PXE is valid */
1562 if (PointerPxe
->u
.Hard
.Valid
== 0)
1564 /* Right now, we only handle scenarios where the PXE is totally empty */
1565 ASSERT(PointerPxe
->u
.Long
== 0);
1567 /* Resolve a demand zero fault */
1568 Status
= MiResolveDemandZeroFault(PointerPpe
,
1573 /* We should come back with a valid PXE */
1574 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1578 #if (_MI_PAGING_LEVELS >= 3)
1579 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1580 // also this is missing the page count increment
1581 /* Check if the PPE is valid */
1582 if (PointerPpe
->u
.Hard
.Valid
== 0)
1584 /* Right now, we only handle scenarios where the PPE is totally empty */
1585 ASSERT(PointerPpe
->u
.Long
== 0);
1587 /* Resolve a demand zero fault */
1588 Status
= MiResolveDemandZeroFault(PointerPde
,
1593 /* We should come back with a valid PPE */
1594 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1598 /* Check if the PDE is valid */
1599 if (PointerPde
->u
.Hard
.Valid
== 0)
1601 /* Right now, we only handle scenarios where the PDE is totally empty */
1602 ASSERT(PointerPde
->u
.Long
== 0);
1604 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1606 UserPdeFault
= TRUE
;
1608 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1609 if (ProtectionCode
== MM_NOACCESS
)
1611 #if (_MI_PAGING_LEVELS == 2)
1612 /* Could be a page table for paged pool */
1613 MiCheckPdeForPagedPool(Address
);
1615 /* Has the code above changed anything -- is this now a valid PTE? */
1616 Status
= (PointerPde
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1618 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1619 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1623 /* Write a demand-zero PDE */
1624 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
1626 /* Dispatch the fault */
1627 Status
= MiDispatchFault(TRUE
,
1632 PsGetCurrentProcess(),
1636 UserPdeFault
= FALSE
;
1638 /* We should come back with APCs enabled, and with a valid PDE */
1639 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1640 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1644 /* Not yet implemented in ReactOS */
1645 ASSERT(MI_IS_PAGE_LARGE(PointerPde
) == FALSE
);
1648 /* Now capture the PTE. Ignore virtual faults for now */
1649 TempPte
= *PointerPte
;
1650 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
1652 /* Quick check for demand-zero */
1653 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
1655 /* Resolve the fault */
1656 MiResolveDemandZeroFault(Address
,
1661 /* Return the status */
1662 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1663 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1666 /* Check for zero PTE */
1667 if (TempPte
.u
.Long
== 0)
1669 /* Check if this address range belongs to a valid allocation (VAD) */
1670 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1671 if (ProtectionCode
== MM_NOACCESS
)
1673 #if (_MI_PAGING_LEVELS == 2)
1674 /* Could be a page table for paged pool */
1675 MiCheckPdeForPagedPool(Address
);
1677 /* Has the code above changed anything -- is this now a valid PTE? */
1678 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1680 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1681 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1685 /* No guard page support yet */
1686 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1689 * Check if this is a real user-mode address or actually a kernel-mode
1690 * page table for a user mode address
1692 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
1694 /* Add an additional page table reference */
1695 MiIncrementPageTableReferences(Address
);
1698 /* Did we get a prototype PTE back? */
1701 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1702 if (PointerPde
== MiAddressToPde(PTE_BASE
))
1704 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1705 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
1709 /* No, create a new PTE. First, write the protection */
1710 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
1713 /* Lock the PFN database since we're going to grab a page */
1714 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1716 /* Make sure we have enough pages */
1717 ASSERT(MmAvailablePages
>= 32);
1719 /* Try to get a zero page */
1720 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1721 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1722 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1723 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1724 if (!PageFrameIndex
)
1726 /* Grab a page out of there. Later we should grab a colored zero page */
1727 PageFrameIndex
= MiRemoveAnyPage(Color
);
1728 ASSERT(PageFrameIndex
);
1730 /* Release the lock since we need to do some zeroing */
1731 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1733 /* Zero out the page, since it's for user-mode */
1734 MiZeroPfn(PageFrameIndex
);
1736 /* Grab the lock again so we can initialize the PFN entry */
1737 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1740 /* Initialize the PFN entry now */
1741 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1743 /* And we're done with the lock */
1744 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1746 /* Increment the count of pages in the process */
1747 CurrentProcess
->NumberOfPrivatePages
++;
1749 /* One more demand-zero fault */
1750 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
1752 /* Fault on user PDE, or fault on user PTE? */
1753 if (PointerPte
<= MiHighestUserPte
)
1755 /* User fault, build a user PTE */
1756 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1758 PointerPte
->u
.Soft
.Protection
,
1763 /* This is a user-mode PDE, create a kernel PTE for it */
1764 MI_MAKE_HARDWARE_PTE(&TempPte
,
1766 PointerPte
->u
.Soft
.Protection
,
1770 /* Write the dirty bit for writeable pages */
1771 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1773 /* And now write down the PTE, making the address valid */
1774 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1775 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1776 ASSERT(Pfn1
->u1
.Event
== NULL
);
1779 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1780 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1781 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1784 /* No guard page support yet */
1785 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1786 ASSERT(ProtectionCode
!= 0x100);
1788 /* Write the prototype PTE */
1789 TempPte
= PrototypePte
;
1790 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1791 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1795 /* Get the protection code and check if this is a proto PTE */
1796 ProtectionCode
= TempPte
.u
.Soft
.Protection
;
1797 if (TempPte
.u
.Soft
.Prototype
)
1799 /* Do we need to go find the real PTE? */
1800 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1802 /* Get the prototype pte and VAD for it */
1803 ProtoPte
= MiCheckVirtualAddress(Address
,
1808 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1809 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1810 return STATUS_ACCESS_VIOLATION
;
1815 /* Get the prototype PTE! */
1816 ProtoPte
= MiProtoPteToPte(&TempPte
);
1818 /* Is it read-only */
1819 if (TempPte
.u
.Proto
.ReadOnly
)
1821 /* Set read-only code */
1822 ProtectionCode
= MM_READONLY
;
1826 /* Set unknown protection */
1827 ProtectionCode
= 0x100;
1828 ASSERT(CurrentProcess
->CloneRoot
!= NULL
);
1834 /* FIXME: Run MiAccessCheck */
1836 /* Dispatch the fault */
1837 Status
= MiDispatchFault(StoreInstruction
,
1846 /* Return the status */
1847 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1848 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1854 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
1856 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1857 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1859 *ExecuteOptions
= 0;
1861 if (CurrentProcess
->Flags
.ExecuteDisable
)
1863 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
1866 if (CurrentProcess
->Flags
.ExecuteEnable
)
1868 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
1871 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
1873 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
1876 if (CurrentProcess
->Flags
.Permanent
)
1878 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
1881 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
1883 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
1886 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
1888 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
1891 return STATUS_SUCCESS
;
1896 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
1898 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1899 KLOCK_QUEUE_HANDLE ProcessLock
;
1900 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
1901 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1903 /* Only accept valid flags */
1904 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
1907 DPRINT1("Invalid no-execute options\n");
1908 return STATUS_INVALID_PARAMETER
;
1911 /* Change the NX state in the process lock */
1912 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
1914 /* Don't change anything if the permanent flag was set */
1915 if (!CurrentProcess
->Flags
.Permanent
)
1917 /* Start by assuming it's not disabled */
1918 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
1920 /* Now process each flag and turn the equivalent bit on */
1921 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
1923 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
1925 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
1927 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
1929 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
1931 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
1933 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
1935 CurrentProcess
->Flags
.Permanent
= TRUE
;
1937 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
1939 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1941 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
1943 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1946 /* These are turned on by default if no-execution is also eanbled */
1947 if (CurrentProcess
->Flags
.ExecuteEnable
)
1949 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1950 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1954 Status
= STATUS_SUCCESS
;
1957 /* Release the lock and return status */
1958 KiReleaseProcessLock(&ProcessLock
);