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)
105 /* This seems to be making the assumption that one PDE is one page long */
106 C_ASSERT(PAGE_SIZE
== (PD_COUNT
* (sizeof(MMPTE
) * PDE_COUNT
)));
109 // Copy it from our double-mapped system page directory
111 InterlockedExchangePte(PointerPde
,
112 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
&
114 sizeof(MMPTE
)].u
.Long
);
126 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
133 /* Get the PFN for this page */
134 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
137 /* Grab a system PTE we can use to zero the page */
138 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
141 /* Initialize the PTE for it */
142 TempPte
= ValidKernelPte
;
143 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
146 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
148 /* Write combining, no caching */
149 MI_PAGE_DISABLE_CACHE(&TempPte
);
150 MI_PAGE_WRITE_COMBINED(&TempPte
);
152 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
154 /* Write through, no caching */
155 MI_PAGE_DISABLE_CACHE(&TempPte
);
156 MI_PAGE_WRITE_THROUGH(&TempPte
);
159 /* Make the system PTE valid with our PFN */
160 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
162 /* Get the address it maps to, and zero it out */
163 ZeroAddress
= MiPteToAddress(ZeroPte
);
164 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
166 /* Now get rid of it */
167 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
172 MiResolveDemandZeroFault(IN PVOID Address
,
173 IN PMMPTE PointerPte
,
174 IN PEPROCESS Process
,
177 PFN_NUMBER PageFrameNumber
;
179 BOOLEAN NeedZero
= FALSE
;
180 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
184 /* Must currently only be called by paging path */
185 ASSERT(OldIrql
== MM_NOIRQL
);
189 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
192 ASSERT(Process
->ForkInProgress
== NULL
);
194 /* We'll need a zero page */
199 // Lock the PFN database
201 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
202 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
205 PageFrameNumber
= MiRemoveAnyPage(0);
206 DPRINT("New pool page: %lx\n", PageFrameNumber
);
209 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
214 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
217 // Increment demand zero faults
219 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
221 /* Zero the page if need be */
222 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
225 if (PointerPte
<= MiHighestUserPte
)
228 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
230 PointerPte
->u
.Soft
.Protection
,
235 /* For kernel mode */
236 MI_MAKE_HARDWARE_PTE(&TempPte
,
238 PointerPte
->u
.Soft
.Protection
,
242 /* Set it dirty if it's a writable page */
243 if (TempPte
.u
.Hard
.Write
) TempPte
.u
.Hard
.Dirty
= TRUE
;
246 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
251 DPRINT("Paged pool page has now been paged in\n");
252 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
257 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
259 IN PMMPTE PointerPte
,
260 IN PMMPTE PointerProtoPte
,
265 PFN_NUMBER PageFrameIndex
;
267 /* Must be called with an valid prototype PTE, with the PFN lock held */
268 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
269 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
272 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0xFFFFF);
275 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
277 /* Release the PFN lock */
278 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
280 /* Build the user PTE */
281 ASSERT(Address
< MmSystemRangeStart
);
282 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, MM_READONLY
, PageFrameIndex
);
285 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
288 return STATUS_SUCCESS
;
293 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
295 IN PMMPTE PointerPte
,
296 IN PMMPTE PointerProtoPte
,
297 IN OUT PMMPFN
*OutPfn
,
298 OUT PVOID
*PageFileData
,
300 IN PEPROCESS Process
,
302 IN PVOID TrapInformation
)
306 PFN_NUMBER PageFrameIndex
;
308 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
309 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
310 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
311 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
313 /* Read the prototype PTE -- it must be valid since we only handle shared data */
314 TempPte
= *PointerProtoPte
;
315 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
317 /* One more user of this mapped page */
318 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
319 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
320 Pfn1
->u2
.ShareCount
++;
322 /* Call it a transition */
323 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
325 /* Complete the prototype PTE fault -- this will release the PFN lock */
326 return MiCompleteProtoPteFault(StoreInstruction
,
336 MiDispatchFault(IN BOOLEAN StoreInstruction
,
338 IN PMMPTE PointerPte
,
339 IN PMMPTE PointerProtoPte
,
340 IN BOOLEAN Recursive
,
341 IN PEPROCESS Process
,
342 IN PVOID TrapInformation
,
346 KIRQL OldIrql
, LockIrql
;
348 PMMPTE SuperProtoPte
;
349 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
354 // Make sure APCs are off and we're not at dispatch
356 OldIrql
= KeGetCurrentIrql();
357 ASSERT(OldIrql
<= APC_LEVEL
);
358 ASSERT(KeAreAllApcsDisabled() == TRUE
);
361 // Grab a copy of the PTE
363 TempPte
= *PointerPte
;
365 /* Do we have a prototype PTE? */
368 /* This should never happen */
369 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
371 /* We currently only handle the shared user data PTE path */
372 ASSERT(Address
< MmSystemRangeStart
);
373 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
374 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0xFFFFF);
377 /* Lock the PFN database */
378 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
380 /* For the shared data page, this should be true */
381 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
382 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
383 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
385 /* Resolve the fault -- this will release the PFN lock */
386 Status
= MiResolveProtoPteFault(StoreInstruction
,
396 ASSERT(Status
== STATUS_SUCCESS
);
398 /* Complete this as a transition fault */
399 ASSERT(OldIrql
== KeGetCurrentIrql());
400 ASSERT(OldIrql
<= APC_LEVEL
);
401 ASSERT(KeAreAllApcsDisabled() == TRUE
);
402 return STATUS_PAGE_FAULT_TRANSITION
;
406 // The PTE must be invalid, but not totally blank
408 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
409 ASSERT(TempPte
.u
.Long
!= 0);
412 // No prototype, transition or page file software PTEs in ARM3 yet
414 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
415 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
416 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
419 // If we got this far, the PTE can only be a demand zero PTE, which is what
420 // we want. Go handle it!
422 Status
= MiResolveDemandZeroFault(Address
,
426 ASSERT(KeAreAllApcsDisabled () == TRUE
);
427 if (NT_SUCCESS(Status
))
430 // Make sure we're returning in a sane state and pass the status down
432 ASSERT(OldIrql
== KeGetCurrentIrql ());
433 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
438 // Generate an access fault
440 return STATUS_ACCESS_VIOLATION
;
445 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
447 IN KPROCESSOR_MODE Mode
,
448 IN PVOID TrapInformation
)
450 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
451 PMMPTE PointerPte
, ProtoPte
;
454 PETHREAD CurrentThread
;
455 PEPROCESS CurrentProcess
;
457 PMMSUPPORT WorkingSet
;
458 ULONG ProtectionCode
;
460 PFN_NUMBER PageFrameIndex
;
461 DPRINT("ARM3 FAULT AT: %p\n", Address
);
464 // Get the PTE and PDE
466 PointerPte
= MiAddressToPte(Address
);
467 PointerPde
= MiAddressToPde(Address
);
468 #if (_MI_PAGING_LEVELS >= 3)
469 /* We need the PPE and PXE addresses */
474 // Check for dispatch-level snafu
476 if (OldIrql
> APC_LEVEL
)
479 // There are some special cases where this is okay, but not in ARM3 yet
481 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
484 ASSERT(OldIrql
<= APC_LEVEL
);
488 // Check for kernel fault
490 if (Address
>= MmSystemRangeStart
)
493 // What are you even DOING here?
495 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
497 #if (_MI_PAGING_LEVELS >= 3)
498 /* Need to check PXE and PDE validity */
505 if (!PointerPde
->u
.Hard
.Valid
== 0)
510 DPRINT("Invalid PDE\n");
511 #if (_MI_PAGING_LEVELS == 2)
513 // Handle mapping in "Special" PDE directoreis
515 MiCheckPdeForPagedPool(Address
);
518 // Now we SHOULD be good
520 if (PointerPde
->u
.Hard
.Valid
== 0)
523 // FIXFIX: Do the S-LIST hack
529 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
532 (ULONG_PTR
)TrapInformation
,
538 // The PDE is valid, so read the PTE
540 TempPte
= *PointerPte
;
541 if (TempPte
.u
.Hard
.Valid
== 1)
544 // Only two things can go wrong here:
545 // Executing NX page (we couldn't care less)
546 // Writing to a read-only page (the stuff ARM3 works with is write,
547 // so again, moot point).
549 if (StoreInstruction
)
551 DPRINT1("Should NEVER happen on ARM3!!!\n");
552 return STATUS_ACCESS_VIOLATION
;
556 // Otherwise, the PDE was probably invalid, and all is good now
558 return STATUS_SUCCESS
;
562 // Check for a fault on the page table or hyperspace itself
564 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
567 // This might happen...not sure yet
569 DPRINT1("FAULT ON PAGE TABLES: %p %lx %lx!\n", Address
, *PointerPte
, *PointerPde
);
570 #if (_MI_PAGING_LEVELS == 2)
572 // Map in the page table
574 if (MiCheckPdeForPagedPool(Address
) == STATUS_WAIT_1
)
576 DPRINT1("PAGE TABLES FAULTED IN!\n");
577 return STATUS_SUCCESS
;
581 // Otherwise the page table doesn't actually exist
583 DPRINT1("FAILING\n");
584 return STATUS_ACCESS_VIOLATION
;
587 /* In this path, we are using the system working set */
588 CurrentThread
= PsGetCurrentThread();
589 WorkingSet
= &MmSystemCacheWs
;
592 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
593 MiLockWorkingSet(CurrentThread
, WorkingSet
);
596 // Re-read PTE now that the IRQL has been raised
598 TempPte
= *PointerPte
;
599 if (TempPte
.u
.Hard
.Valid
== 1)
602 // Only two things can go wrong here:
603 // Executing NX page (we couldn't care less)
604 // Writing to a read-only page (the stuff ARM3 works with is write,
605 // so again, moot point.
607 if (StoreInstruction
)
609 DPRINT1("Should NEVER happen on ARM3!!!\n");
610 return STATUS_ACCESS_VIOLATION
;
613 /* Release the working set */
614 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
615 KeLowerIrql(LockIrql
);
618 // Otherwise, the PDE was probably invalid, and all is good now
620 return STATUS_SUCCESS
;
624 // We don't implement prototype PTEs
626 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
629 // We don't implement transition PTEs
631 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
634 // Now do the real fault handling
636 Status
= MiDispatchFault(StoreInstruction
,
645 /* Release the working set */
646 ASSERT(KeAreAllApcsDisabled() == TRUE
);
647 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
648 KeLowerIrql(LockIrql
);
653 DPRINT("Fault resolved with status: %lx\n", Status
);
657 /* This is a user fault */
658 CurrentThread
= PsGetCurrentThread();
659 CurrentProcess
= PsGetCurrentProcess();
661 /* Lock the working set */
662 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
664 #if (_MI_PAGING_LEVELS >= 3)
665 /* Need to check/handle PPE and PXE validity too */
669 /* First things first, is the PDE valid? */
670 ASSERT(PointerPde
!= MiAddressToPde(PTE_BASE
));
671 ASSERT(PointerPde
->u
.Hard
.LargePage
== 0);
672 if (PointerPde
->u
.Hard
.Valid
== 0)
674 /* Right now, we only handle scenarios where the PDE is totally empty */
675 ASSERT(PointerPde
->u
.Long
== 0);
677 /* Check if this address range belongs to a valid allocation (VAD) */
678 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
680 /* Right now, we expect a valid protection mask on the VAD */
681 ASSERT(ProtectionCode
!= MM_NOACCESS
);
683 /* Make the PDE demand-zero */
684 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
686 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
687 Status
= MiDispatchFault(TRUE
,
692 PsGetCurrentProcess(),
696 /* We should come back with APCs enabled, and with a valid PDE */
697 ASSERT(KeAreAllApcsDisabled() == TRUE
);
698 #if (_MI_PAGING_LEVELS >= 3)
699 /* Need to check/handle PPE and PXE validity too */
702 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
705 /* Now capture the PTE. We only handle cases where it's totally empty */
706 TempPte
= *PointerPte
;
707 ASSERT(TempPte
.u
.Long
== 0);
709 /* Check if this address range belongs to a valid allocation (VAD) */
710 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
711 ASSERT(ProtectionCode
!= MM_NOACCESS
);
713 /* Did we get a prototype PTE back? */
716 /* No, create a new PTE. First, write the protection */
717 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
719 /* Lock the PFN database since we're going to grab a page */
720 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
722 /* Grab a page out of there. Later we should grab a colored zero page */
723 PageFrameIndex
= MiRemoveAnyPage(0);
724 ASSERT(PageFrameIndex
);
726 /* Release the lock since we need to do some zeroing */
727 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
729 /* Zero out the page, since it's for user-mode */
730 MiZeroPfn(PageFrameIndex
);
732 /* Grab the lock again so we can initialize the PFN entry */
733 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
735 /* Initialize the PFN entry now */
736 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
738 /* And we're done with the lock */
739 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
741 /* One more demand-zero fault */
742 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
744 /* Was the fault on an actual user page, or a kernel page for the user? */
745 if (PointerPte
<= MiHighestUserPte
)
747 /* User fault, build a user PTE */
748 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
750 PointerPte
->u
.Soft
.Protection
,
755 /* Session, kernel, or user PTE, figure it out and build it */
756 MI_MAKE_HARDWARE_PTE(&TempPte
,
758 PointerPte
->u
.Soft
.Protection
,
762 /* Write the dirty bit for writeable pages */
763 if (TempPte
.u
.Hard
.Write
) TempPte
.u
.Hard
.Dirty
= TRUE
;
765 /* And now write down the PTE, making the address valid */
766 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
769 Status
= STATUS_PAGE_FAULT_DEMAND_ZERO
;
773 /* The only "prototype PTE" we support is the shared user data path */
774 ASSERT(ProtectionCode
== MM_READONLY
);
776 /* Write the prototype PTE */
777 TempPte
= PrototypePte
;
778 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
779 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
781 /* Handle the fault */
782 Status
= MiDispatchFault(StoreInstruction
,
790 ASSERT(Status
== STATUS_PAGE_FAULT_TRANSITION
);
791 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
792 ASSERT(PointerPte
->u
.Hard
.PageFrameNumber
== MmSharedUserDataPte
->u
.Hard
.PageFrameNumber
);
795 /* Release the working set */
796 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);