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 #line 15 "ARMĀ³::PAGFAULT"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 /* GLOBALS ********************************************************************/
21 /* PRIVATE FUNCTIONS **********************************************************/
25 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
26 OUT PULONG ProtectCode
,
31 /* No prototype/section support for now */
34 /* Only valid for user VADs for now */
35 ASSERT(VirtualAddress
<= MM_HIGHEST_USER_ADDRESS
);
37 /* Special case for shared data */
38 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)USER_SHARED_DATA
)
40 /* It's a read-only page */
41 *ProtectCode
= MM_READONLY
;
42 return MmSharedUserDataPte
;
45 /* Find the VAD, it must exist, since we only handle PEB/TEB */
46 Vad
= MiLocateAddress(VirtualAddress
);
49 /* This must be a TEB/PEB VAD */
50 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
== TRUE
);
51 ASSERT(Vad
->u
.VadFlags
.MemCommit
== TRUE
);
52 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
54 /* Return the protection on it */
55 *ProtectCode
= Vad
->u
.VadFlags
.Protection
;
61 MiCheckPdeForPagedPool(IN PVOID Address
)
64 NTSTATUS Status
= STATUS_SUCCESS
;
66 /* No session support in ReactOS yet */
67 ASSERT(MI_IS_SESSION_ADDRESS(Address
) == FALSE
);
68 ASSERT(MI_IS_SESSION_PTE(Address
) == FALSE
);
71 // Check if this is a fault while trying to access the page table itself
73 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
76 // Send a hint to the page fault handler that this is only a valid fault
77 // if we already detected this was access within the page table range
79 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
80 Status
= STATUS_WAIT_1
;
82 else if (Address
< MmSystemRangeStart
)
85 // This is totally illegal
87 return STATUS_ACCESS_VIOLATION
;
92 // Get the PDE for the address
94 PointerPde
= MiAddressToPde(Address
);
98 // Check if it's not valid
100 if (PointerPde
->u
.Hard
.Valid
== 0)
103 /* This seems to be making the assumption that one PDE is one page long */
104 C_ASSERT(PAGE_SIZE
== (PD_COUNT
* (sizeof(MMPTE
) * PDE_COUNT
)));
108 // Copy it from our double-mapped system page directory
110 InterlockedExchangePte(PointerPde
,
111 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
&
113 sizeof(MMPTE
)].u
.Long
);
124 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
131 /* Get the PFN for this page */
132 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
135 /* Grab a system PTE we can use to zero the page */
136 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
139 /* Initialize the PTE for it */
140 TempPte
= ValidKernelPte
;
141 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
144 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
146 /* Write combining, no caching */
147 MI_PAGE_DISABLE_CACHE(&TempPte
);
148 MI_PAGE_WRITE_COMBINED(&TempPte
);
150 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
152 /* Write through, no caching */
153 MI_PAGE_DISABLE_CACHE(&TempPte
);
154 MI_PAGE_WRITE_THROUGH(&TempPte
);
157 /* Make the system PTE valid with our PFN */
158 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
160 /* Get the address it maps to, and zero it out */
161 ZeroAddress
= MiPteToAddress(ZeroPte
);
162 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
164 /* Now get rid of it */
165 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
170 MiResolveDemandZeroFault(IN PVOID Address
,
171 IN PMMPTE PointerPte
,
172 IN PEPROCESS Process
,
175 PFN_NUMBER PageFrameNumber
;
177 BOOLEAN NeedZero
= FALSE
;
178 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
182 /* Must currently only be called by paging path */
183 ASSERT(OldIrql
== MM_NOIRQL
);
187 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
190 ASSERT(Process
->ForkInProgress
== NULL
);
192 /* We'll need a zero page */
197 // Lock the PFN database
199 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
200 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
203 PageFrameNumber
= MiRemoveAnyPage(0);
204 DPRINT("New pool page: %lx\n", PageFrameNumber
);
207 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
212 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
215 // Increment demand zero faults
217 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
219 /* Zero the page if need be */
220 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
223 if (PointerPte
<= MiHighestUserPte
)
226 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
228 PointerPte
->u
.Soft
.Protection
,
233 /* For kernel mode */
234 MI_MAKE_HARDWARE_PTE(&TempPte
,
236 PointerPte
->u
.Soft
.Protection
,
240 /* Set it dirty if it's a writable page */
241 if (TempPte
.u
.Hard
.Write
) TempPte
.u
.Hard
.Dirty
= TRUE
;
244 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
249 DPRINT("Paged pool page has now been paged in\n");
250 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
255 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
257 IN PMMPTE PointerPte
,
258 IN PMMPTE PointerProtoPte
,
263 PFN_NUMBER PageFrameIndex
;
265 /* Must be called with an valid prototype PTE, with the PFN lock held */
266 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
267 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
270 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0xFFFFF);
273 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
275 /* Release the PFN lock */
276 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
278 /* Build the user PTE */
279 ASSERT(Address
< MmSystemRangeStart
);
280 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, MM_READONLY
, PageFrameIndex
);
283 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
286 return STATUS_SUCCESS
;
291 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
293 IN PMMPTE PointerPte
,
294 IN PMMPTE PointerProtoPte
,
295 IN OUT PMMPFN
*OutPfn
,
296 OUT PVOID
*PageFileData
,
298 IN PEPROCESS Process
,
300 IN PVOID TrapInformation
)
304 PFN_NUMBER PageFrameIndex
;
306 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
307 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
308 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
309 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
311 /* Read the prototype PTE -- it must be valid since we only handle shared data */
312 TempPte
= *PointerProtoPte
;
313 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
315 /* One more user of this mapped page */
316 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
317 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
318 Pfn1
->u2
.ShareCount
++;
320 /* Call it a transition */
321 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
323 /* Complete the prototype PTE fault -- this will release the PFN lock */
324 return MiCompleteProtoPteFault(StoreInstruction
,
334 MiDispatchFault(IN BOOLEAN StoreInstruction
,
336 IN PMMPTE PointerPte
,
337 IN PMMPTE PointerProtoPte
,
338 IN BOOLEAN Recursive
,
339 IN PEPROCESS Process
,
340 IN PVOID TrapInformation
,
344 KIRQL OldIrql
, LockIrql
;
346 PMMPTE SuperProtoPte
;
347 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
352 // Make sure APCs are off and we're not at dispatch
354 OldIrql
= KeGetCurrentIrql();
355 ASSERT(OldIrql
<= APC_LEVEL
);
356 ASSERT(KeAreAllApcsDisabled() == TRUE
);
359 // Grab a copy of the PTE
361 TempPte
= *PointerPte
;
363 /* Do we have a prototype PTE? */
366 /* This should never happen */
367 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
369 /* We currently only handle the shared user data PTE path */
370 ASSERT(Address
< MmSystemRangeStart
);
371 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
372 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0xFFFFF);
375 /* Lock the PFN database */
376 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
378 /* For the shared data page, this should be true */
379 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
380 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
381 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
383 /* Resolve the fault -- this will release the PFN lock */
384 Status
= MiResolveProtoPteFault(StoreInstruction
,
394 ASSERT(Status
== STATUS_SUCCESS
);
396 /* Complete this as a transition fault */
397 ASSERT(OldIrql
== KeGetCurrentIrql());
398 ASSERT(OldIrql
<= APC_LEVEL
);
399 ASSERT(KeAreAllApcsDisabled() == TRUE
);
400 return STATUS_PAGE_FAULT_TRANSITION
;
404 // The PTE must be invalid, but not totally blank
406 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
407 ASSERT(TempPte
.u
.Long
!= 0);
410 // No prototype, transition or page file software PTEs in ARM3 yet
412 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
413 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
414 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
417 // If we got this far, the PTE can only be a demand zero PTE, which is what
418 // we want. Go handle it!
420 Status
= MiResolveDemandZeroFault(Address
,
424 ASSERT(KeAreAllApcsDisabled () == TRUE
);
425 if (NT_SUCCESS(Status
))
428 // Make sure we're returning in a sane state and pass the status down
430 ASSERT(OldIrql
== KeGetCurrentIrql ());
431 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
436 // Generate an access fault
438 return STATUS_ACCESS_VIOLATION
;
443 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
445 IN KPROCESSOR_MODE Mode
,
446 IN PVOID TrapInformation
)
448 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
449 PMMPTE PointerPte
, ProtoPte
;
452 PETHREAD CurrentThread
;
453 PEPROCESS CurrentProcess
;
455 PMMSUPPORT WorkingSet
;
456 ULONG ProtectionCode
;
458 PFN_NUMBER PageFrameIndex
;
459 DPRINT("ARM3 FAULT AT: %p\n", Address
);
462 // Get the PTE and PDE
464 PointerPte
= MiAddressToPte(Address
);
465 PointerPde
= MiAddressToPde(Address
);
466 #if (_MI_PAGING_LEVELS >= 3)
467 /* We need the PPE and PXE addresses */
472 // Check for dispatch-level snafu
474 if (OldIrql
> APC_LEVEL
)
477 // There are some special cases where this is okay, but not in ARM3 yet
479 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
482 ASSERT(OldIrql
<= APC_LEVEL
);
486 // Check for kernel fault
488 if (Address
>= MmSystemRangeStart
)
491 // What are you even DOING here?
493 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
495 #if (_MI_PAGING_LEVELS >= 3)
496 /* Need to check PXE and PDE validity */
503 if (!PointerPde
->u
.Hard
.Valid
== 0)
508 DPRINT("Invalid PDE\n");
509 #if (_MI_PAGING_LEVELS == 2)
511 // Handle mapping in "Special" PDE directoreis
513 MiCheckPdeForPagedPool(Address
);
516 // Now we SHOULD be good
518 if (PointerPde
->u
.Hard
.Valid
== 0)
521 // FIXFIX: Do the S-LIST hack
527 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
530 (ULONG_PTR
)TrapInformation
,
536 // The PDE is valid, so read the PTE
538 TempPte
= *PointerPte
;
539 if (TempPte
.u
.Hard
.Valid
== 1)
542 // Only two things can go wrong here:
543 // Executing NX page (we couldn't care less)
544 // Writing to a read-only page (the stuff ARM3 works with is write,
545 // so again, moot point).
547 if (StoreInstruction
)
549 DPRINT1("Should NEVER happen on ARM3!!!\n");
550 return STATUS_ACCESS_VIOLATION
;
554 // Otherwise, the PDE was probably invalid, and all is good now
556 return STATUS_SUCCESS
;
560 // Check for a fault on the page table or hyperspace itself
562 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
565 // This might happen...not sure yet
567 DPRINT1("FAULT ON PAGE TABLES: %p %lx %lx!\n", Address
, *PointerPte
, *PointerPde
);
568 #if (_MI_PAGING_LEVELS == 2)
570 // Map in the page table
572 if (MiCheckPdeForPagedPool(Address
) == STATUS_WAIT_1
)
574 DPRINT1("PAGE TABLES FAULTED IN!\n");
575 return STATUS_SUCCESS
;
579 // Otherwise the page table doesn't actually exist
581 DPRINT1("FAILING\n");
582 return STATUS_ACCESS_VIOLATION
;
585 /* In this path, we are using the system working set */
586 CurrentThread
= PsGetCurrentThread();
587 WorkingSet
= &MmSystemCacheWs
;
590 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
591 MiLockWorkingSet(CurrentThread
, WorkingSet
);
594 // Re-read PTE now that the IRQL has been raised
596 TempPte
= *PointerPte
;
597 if (TempPte
.u
.Hard
.Valid
== 1)
600 // Only two things can go wrong here:
601 // Executing NX page (we couldn't care less)
602 // Writing to a read-only page (the stuff ARM3 works with is write,
603 // so again, moot point.
605 if (StoreInstruction
)
607 DPRINT1("Should NEVER happen on ARM3!!!\n");
608 return STATUS_ACCESS_VIOLATION
;
611 /* Release the working set */
612 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
613 KeLowerIrql(LockIrql
);
616 // Otherwise, the PDE was probably invalid, and all is good now
618 return STATUS_SUCCESS
;
622 // We don't implement prototype PTEs
624 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
627 // We don't implement transition PTEs
629 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
632 // Now do the real fault handling
634 Status
= MiDispatchFault(StoreInstruction
,
643 /* Release the working set */
644 ASSERT(KeAreAllApcsDisabled() == TRUE
);
645 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
646 KeLowerIrql(LockIrql
);
651 DPRINT("Fault resolved with status: %lx\n", Status
);
655 /* This is a user fault */
656 CurrentThread
= PsGetCurrentThread();
657 CurrentProcess
= PsGetCurrentProcess();
659 /* Lock the working set */
660 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
662 #if (_MI_PAGING_LEVELS >= 3)
663 /* Need to check/handle PPE and PXE validity too */
667 /* First things first, is the PDE valid? */
668 ASSERT(PointerPde
!= MiAddressToPde(PTE_BASE
));
669 ASSERT(PointerPde
->u
.Hard
.LargePage
== 0);
670 if (PointerPde
->u
.Hard
.Valid
== 0)
672 /* Right now, we only handle scenarios where the PDE is totally empty */
673 ASSERT(PointerPde
->u
.Long
== 0);
675 /* Check if this address range belongs to a valid allocation (VAD) */
676 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
678 /* Right now, we expect a valid protection mask on the VAD */
679 ASSERT(ProtectionCode
!= MM_NOACCESS
);
681 /* Make the PDE demand-zero */
682 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
684 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
685 Status
= MiDispatchFault(TRUE
,
690 PsGetCurrentProcess(),
694 /* We should come back with APCs enabled, and with a valid PDE */
695 ASSERT(KeAreAllApcsDisabled() == TRUE
);
696 #if (_MI_PAGING_LEVELS >= 3)
697 /* Need to check/handle PPE and PXE validity too */
700 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
703 /* Now capture the PTE. We only handle cases where it's totally empty */
704 TempPte
= *PointerPte
;
705 ASSERT(TempPte
.u
.Long
== 0);
707 /* Check if this address range belongs to a valid allocation (VAD) */
708 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
709 ASSERT(ProtectionCode
!= MM_NOACCESS
);
711 /* Did we get a prototype PTE back? */
714 /* No, create a new PTE. First, write the protection */
715 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
717 /* Lock the PFN database since we're going to grab a page */
718 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
720 /* Grab a page out of there. Later we should grab a colored zero page */
721 PageFrameIndex
= MiRemoveAnyPage(0);
722 ASSERT(PageFrameIndex
);
724 /* Release the lock since we need to do some zeroing */
725 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
727 /* Zero out the page, since it's for user-mode */
728 MiZeroPfn(PageFrameIndex
);
730 /* Grab the lock again so we can initialize the PFN entry */
731 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
733 /* Initialize the PFN entry now */
734 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
736 /* And we're done with the lock */
737 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
739 /* One more demand-zero fault */
740 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
742 /* Was the fault on an actual user page, or a kernel page for the user? */
743 if (PointerPte
<= MiHighestUserPte
)
745 /* User fault, build a user PTE */
746 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
748 PointerPte
->u
.Soft
.Protection
,
753 /* Session, kernel, or user PTE, figure it out and build it */
754 MI_MAKE_HARDWARE_PTE(&TempPte
,
756 PointerPte
->u
.Soft
.Protection
,
760 /* Write the dirty bit for writeable pages */
761 if (TempPte
.u
.Hard
.Write
) TempPte
.u
.Hard
.Dirty
= TRUE
;
763 /* And now write down the PTE, making the address valid */
764 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
767 Status
= STATUS_PAGE_FAULT_DEMAND_ZERO
;
771 /* The only "prototype PTE" we support is the shared user data path */
772 ASSERT(ProtectionCode
== MM_READONLY
);
774 /* Write the prototype PTE */
775 TempPte
= PrototypePte
;
776 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
777 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
779 /* Handle the fault */
780 Status
= MiDispatchFault(StoreInstruction
,
788 ASSERT(Status
== STATUS_PAGE_FAULT_TRANSITION
);
789 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
790 ASSERT(PointerPte
->u
.Hard
.PageFrameNumber
== MmSharedUserDataPte
->u
.Hard
.PageFrameNumber
);
793 /* Release the working set */
794 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);