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
;
31 // Check if this is a fault while trying to access the page table itself
33 if ((Address
>= (PVOID
)MiAddressToPte(MmSystemRangeStart
)) &&
34 (Address
< (PVOID
)PTE_TOP
))
37 // Send a hint to the page fault handler that this is only a valid fault
38 // if we already detected this was access within the page table range
40 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
41 Status
= STATUS_WAIT_1
;
43 else if (Address
< MmSystemRangeStart
)
46 // This is totally illegal
48 return STATUS_ACCESS_VIOLATION
;
53 // Get the PDE for the address
55 PointerPde
= MiAddressToPde(Address
);
59 // Check if it's not valid
61 if (PointerPde
->u
.Hard
.Valid
== 0)
64 // Copy it from our double-mapped system page directory
66 InterlockedExchangePte(PointerPde
,
67 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
&
69 sizeof(MMPTE
)].u
.Long
);
80 MiResolveDemandZeroFault(IN PVOID Address
,
85 PFN_NUMBER PageFrameNumber
;
87 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
92 // Lock the PFN database
94 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
95 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
100 PageFrameNumber
= MmAllocPage(MC_PPOOL
, 0);
101 DPRINT("New pool page: %lx\n", PageFrameNumber
);
106 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
109 // Increment demand zero faults
111 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
116 TempPte
= HyperTemplatePte
;
117 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
118 *PointerPte
= TempPte
;
119 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
124 DPRINT("Paged pool page has now been paged in\n");
125 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
130 MiDispatchFault(IN BOOLEAN StoreInstruction
,
132 IN PMMPTE PointerPte
,
133 IN PMMPTE PrototypePte
,
134 IN BOOLEAN Recursive
,
135 IN PEPROCESS Process
,
136 IN PVOID TrapInformation
,
142 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
147 // Make sure APCs are off and we're not at dispatch
149 OldIrql
= KeGetCurrentIrql ();
150 ASSERT(OldIrql
<= APC_LEVEL
);
151 ASSERT(KeAreAllApcsDisabled () == TRUE
);
154 // Grab a copy of the PTE
156 TempPte
= *PointerPte
;
159 // The PTE must be invalid, but not totally blank
161 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
162 ASSERT(TempPte
.u
.Long
!= 0);
165 // No prototype, transition or page file software PTEs in ARM3 yet
167 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
168 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
169 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
172 // If we got this far, the PTE can only be a demand zero PTE, which is what
173 // we want. Go handle it!
175 Status
= MiResolveDemandZeroFault(Address
,
179 if (NT_SUCCESS(Status
))
182 // Make sure we're returning in a sane state and pass the status down
184 ASSERT(OldIrql
== KeGetCurrentIrql ());
185 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
190 // Generate an access fault
192 return STATUS_ACCESS_VIOLATION
;
197 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
199 IN KPROCESSOR_MODE Mode
,
200 IN PVOID TrapInformation
)
202 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
206 PETHREAD CurrentThread
;
208 DPRINT("ARM3 FAULT AT: %p\n", Address
);
211 // Get the PTE and PDE
213 PointerPte
= MiAddressToPte(Address
);
214 PointerPde
= MiAddressToPde(Address
);
217 // Check for dispatch-level snafu
219 if (OldIrql
> APC_LEVEL
)
222 // There are some special cases where this is okay, but not in ARM3 yet
224 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
227 ASSERT(OldIrql
<= APC_LEVEL
);
231 // Check for kernel fault
233 if (Address
>= MmSystemRangeStart
)
236 // What are you even DOING here?
238 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
243 if (!PointerPde
->u
.Hard
.Valid
== 0)
248 DPRINT("Invalid PDE\n");
251 // Handle mapping in "Special" PDE directoreis
253 MiCheckPdeForPagedPool(Address
);
256 // Now we SHOULD be good
258 if (PointerPde
->u
.Hard
.Valid
== 0)
261 // FIXFIX: Do the S-LIST hack
267 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
270 (ULONG_PTR
)TrapInformation
,
276 // The PDE is valid, so read the PTE
278 TempPte
= *PointerPte
;
279 if (TempPte
.u
.Hard
.Valid
== 1)
282 // Only two things can go wrong here:
283 // Executing NX page (we couldn't care less)
284 // Writing to a read-only page (the stuff ARM3 works with is write,
285 // so again, moot point).
287 if (StoreInstruction
)
289 DPRINT1("Should NEVER happen on ARM3!!!\n");
290 return STATUS_ACCESS_VIOLATION
;
294 // Otherwise, the PDE was probably invalid, and all is good now
296 return STATUS_SUCCESS
;
300 // Check for a fault on the page table or hyperspace itself
302 if ((Address
>= (PVOID
)PTE_BASE
) && (Address
<= MmHyperSpaceEnd
))
305 // This might happen...not sure yet
307 DPRINT1("FAULT ON PAGE TABLES!\n");
310 // Map in the page table
312 if (MiCheckPdeForPagedPool(Address
) == STATUS_WAIT_1
)
314 DPRINT1("PAGE TABLES FAULTED IN!\n");
315 return STATUS_SUCCESS
;
319 // Otherwise the page table doesn't actually exist
321 DPRINT1("FAILING\n");
322 return STATUS_ACCESS_VIOLATION
;
326 // Now we must raise to APC_LEVEL and mark the thread as owner
327 // We don't actually implement a working set pushlock, so this is only
328 // for internal consistency (and blocking APCs)
330 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
331 CurrentThread
= PsGetCurrentThread();
332 KeEnterGuardedRegion();
333 ASSERT((CurrentThread
->OwnsSystemWorkingSetExclusive
== 0) &&
334 (CurrentThread
->OwnsSystemWorkingSetShared
== 0));
335 CurrentThread
->OwnsSystemWorkingSetExclusive
= 1;
338 // Re-read PTE now that the IRQL has been raised
340 TempPte
= *PointerPte
;
341 if (TempPte
.u
.Hard
.Valid
== 1)
344 // Only two things can go wrong here:
345 // Executing NX page (we couldn't care less)
346 // Writing to a read-only page (the stuff ARM3 works with is write,
347 // so again, moot point.
349 if (StoreInstruction
)
351 DPRINT1("Should NEVER happen on ARM3!!!\n");
352 return STATUS_ACCESS_VIOLATION
;
356 // Otherwise, the PDE was probably invalid, and all is good now
358 return STATUS_SUCCESS
;
362 // We don't implement prototype PTEs
364 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
367 // We don't implement transition PTEs
369 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
372 // Now do the real fault handling
374 Status
= MiDispatchFault(StoreInstruction
,
386 ASSERT(KeAreAllApcsDisabled() == TRUE
);
387 CurrentThread
->OwnsSystemWorkingSetExclusive
= 0;
388 KeLeaveGuardedRegion();
389 KeLowerIrql(LockIrql
);
394 DPRINT("Fault resolved with status: %lx\n", Status
);
401 DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
402 return STATUS_ACCESS_VIOLATION
;