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 MiCheckPdeForPagedPool(IN PVOID Address
)
28 NTSTATUS Status
= STATUS_SUCCESS
;
30 /* No session support in ReactOS yet */
31 ASSERT(MI_IS_SESSION_ADDRESS(Address
) == FALSE
);
32 ASSERT(MI_IS_SESSION_PTE(Address
) == FALSE
);
35 // Check if this is a fault while trying to access the page table itself
37 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
40 // Send a hint to the page fault handler that this is only a valid fault
41 // if we already detected this was access within the page table range
43 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
44 Status
= STATUS_WAIT_1
;
46 else if (Address
< MmSystemRangeStart
)
49 // This is totally illegal
51 return STATUS_ACCESS_VIOLATION
;
56 // Get the PDE for the address
58 PointerPde
= MiAddressToPde(Address
);
62 // Check if it's not valid
64 if (PointerPde
->u
.Hard
.Valid
== 0)
67 /* This seems to be making the assumption that one PDE is one page long */
68 C_ASSERT(PAGE_SIZE
== (PD_COUNT
* (sizeof(MMPTE
) * PDE_COUNT
)));
72 // Copy it from our double-mapped system page directory
74 InterlockedExchangePte(PointerPde
,
75 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
&
77 sizeof(MMPTE
)].u
.Long
);
88 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
95 /* Get the PFN for this page */
96 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
99 /* Grab a system PTE we can use to zero the page */
100 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
103 /* Initialize the PTE for it */
104 TempPte
= ValidKernelPte
;
105 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
108 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
110 /* Write combining, no caching */
111 MI_PAGE_DISABLE_CACHE(&TempPte
);
112 MI_PAGE_WRITE_COMBINED(&TempPte
);
114 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
116 /* Write through, no caching */
117 MI_PAGE_DISABLE_CACHE(&TempPte
);
118 MI_PAGE_WRITE_THROUGH(&TempPte
);
121 /* Make the system PTE valid with our PFN */
122 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
124 /* Get the address it maps to, and zero it out */
125 ZeroAddress
= MiPteToAddress(ZeroPte
);
126 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
128 /* Now get rid of it */
129 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
134 MiResolveDemandZeroFault(IN PVOID Address
,
135 IN PMMPTE PointerPte
,
136 IN PEPROCESS Process
,
139 PFN_NUMBER PageFrameNumber
;
141 BOOLEAN NeedZero
= FALSE
;
142 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
146 /* Must currently only be called by paging path */
147 ASSERT(OldIrql
== MM_NOIRQL
);
151 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
154 ASSERT(Process
->ForkInProgress
== NULL
);
156 /* We'll need a zero page */
161 // Lock the PFN database
163 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
164 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
167 PageFrameNumber
= MiRemoveAnyPage(0);
168 DPRINT("New pool page: %lx\n", PageFrameNumber
);
171 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
176 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
179 // Increment demand zero faults
181 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
183 /* Zero the page if need be */
184 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
187 if (PointerPte
<= MiHighestUserPte
)
190 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
192 PointerPte
->u
.Soft
.Protection
,
197 /* For kernel mode */
198 MI_MAKE_HARDWARE_PTE(&TempPte
,
200 PointerPte
->u
.Soft
.Protection
,
204 /* Set it dirty if it's a writable page */
205 if (TempPte
.u
.Hard
.Write
) TempPte
.u
.Hard
.Dirty
= TRUE
;
208 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
213 DPRINT("Paged pool page has now been paged in\n");
214 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
219 MiDispatchFault(IN BOOLEAN StoreInstruction
,
221 IN PMMPTE PointerPte
,
222 IN PMMPTE PrototypePte
,
223 IN BOOLEAN Recursive
,
224 IN PEPROCESS Process
,
225 IN PVOID TrapInformation
,
231 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
236 // Make sure APCs are off and we're not at dispatch
238 OldIrql
= KeGetCurrentIrql ();
239 ASSERT(OldIrql
<= APC_LEVEL
);
240 ASSERT(KeAreAllApcsDisabled () == TRUE
);
243 // Grab a copy of the PTE
245 TempPte
= *PointerPte
;
248 ASSERT(PrototypePte
== NULL
);
251 // The PTE must be invalid, but not totally blank
253 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
254 ASSERT(TempPte
.u
.Long
!= 0);
257 // No prototype, transition or page file software PTEs in ARM3 yet
259 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
260 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
261 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
264 // If we got this far, the PTE can only be a demand zero PTE, which is what
265 // we want. Go handle it!
267 Status
= MiResolveDemandZeroFault(Address
,
271 ASSERT(KeAreAllApcsDisabled () == TRUE
);
272 if (NT_SUCCESS(Status
))
275 // Make sure we're returning in a sane state and pass the status down
277 ASSERT(OldIrql
== KeGetCurrentIrql ());
278 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
283 // Generate an access fault
285 return STATUS_ACCESS_VIOLATION
;
290 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
292 IN KPROCESSOR_MODE Mode
,
293 IN PVOID TrapInformation
)
295 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
299 PETHREAD CurrentThread
;
301 DPRINT("ARM3 FAULT AT: %p\n", Address
);
304 // Get the PTE and PDE
306 PointerPte
= MiAddressToPte(Address
);
307 PointerPde
= MiAddressToPde(Address
);
310 // Check for dispatch-level snafu
312 if (OldIrql
> APC_LEVEL
)
315 // There are some special cases where this is okay, but not in ARM3 yet
317 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
320 ASSERT(OldIrql
<= APC_LEVEL
);
324 // Check for kernel fault
326 if (Address
>= MmSystemRangeStart
)
329 // What are you even DOING here?
331 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
336 if (!PointerPde
->u
.Hard
.Valid
== 0)
341 DPRINT("Invalid PDE\n");
344 // Handle mapping in "Special" PDE directoreis
346 MiCheckPdeForPagedPool(Address
);
349 // Now we SHOULD be good
351 if (PointerPde
->u
.Hard
.Valid
== 0)
354 // FIXFIX: Do the S-LIST hack
360 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
363 (ULONG_PTR
)TrapInformation
,
369 // The PDE is valid, so read the PTE
371 TempPte
= *PointerPte
;
372 if (TempPte
.u
.Hard
.Valid
== 1)
375 // Only two things can go wrong here:
376 // Executing NX page (we couldn't care less)
377 // Writing to a read-only page (the stuff ARM3 works with is write,
378 // so again, moot point).
380 if (StoreInstruction
)
382 DPRINT1("Should NEVER happen on ARM3!!!\n");
383 return STATUS_ACCESS_VIOLATION
;
387 // Otherwise, the PDE was probably invalid, and all is good now
389 return STATUS_SUCCESS
;
393 // Check for a fault on the page table or hyperspace itself
395 if ((Address
>= (PVOID
)PTE_BASE
) && (Address
<= MmHyperSpaceEnd
))
398 // This might happen...not sure yet
400 DPRINT1("FAULT ON PAGE TABLES: %p %lx %lx!\n", Address
, *PointerPte
, *PointerPde
);
403 // Map in the page table
405 if (MiCheckPdeForPagedPool(Address
) == STATUS_WAIT_1
)
407 DPRINT1("PAGE TABLES FAULTED IN!\n");
408 return STATUS_SUCCESS
;
412 // Otherwise the page table doesn't actually exist
414 DPRINT1("FAILING\n");
415 return STATUS_ACCESS_VIOLATION
;
419 // Now we must raise to APC_LEVEL and mark the thread as owner
420 // We don't actually implement a working set pushlock, so this is only
421 // for internal consistency (and blocking APCs)
423 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
424 CurrentThread
= PsGetCurrentThread();
425 KeEnterGuardedRegion();
426 ASSERT((CurrentThread
->OwnsSystemWorkingSetExclusive
== 0) &&
427 (CurrentThread
->OwnsSystemWorkingSetShared
== 0));
428 CurrentThread
->OwnsSystemWorkingSetExclusive
= 1;
431 // Re-read PTE now that the IRQL has been raised
433 TempPte
= *PointerPte
;
434 if (TempPte
.u
.Hard
.Valid
== 1)
437 // Only two things can go wrong here:
438 // Executing NX page (we couldn't care less)
439 // Writing to a read-only page (the stuff ARM3 works with is write,
440 // so again, moot point.
442 if (StoreInstruction
)
444 DPRINT1("Should NEVER happen on ARM3!!!\n");
445 return STATUS_ACCESS_VIOLATION
;
449 // Otherwise, the PDE was probably invalid, and all is good now
451 return STATUS_SUCCESS
;
455 // We don't implement prototype PTEs
457 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
460 // We don't implement transition PTEs
462 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
465 // Now do the real fault handling
467 Status
= MiDispatchFault(StoreInstruction
,
479 ASSERT(KeAreAllApcsDisabled() == TRUE
);
480 CurrentThread
->OwnsSystemWorkingSetExclusive
= 0;
481 KeLeaveGuardedRegion();
482 KeLowerIrql(LockIrql
);
487 DPRINT("Fault resolved with status: %lx\n", Status
);
494 DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
495 return STATUS_ACCESS_VIOLATION
;