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 ((Address
>= (PVOID
)MiAddressToPte(MmSystemRangeStart
)) &&
38 (Address
< (PVOID
)PTE_TOP
))
41 // Send a hint to the page fault handler that this is only a valid fault
42 // if we already detected this was access within the page table range
44 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
45 Status
= STATUS_WAIT_1
;
47 else if (Address
< MmSystemRangeStart
)
50 // This is totally illegal
52 return STATUS_ACCESS_VIOLATION
;
57 // Get the PDE for the address
59 PointerPde
= MiAddressToPde(Address
);
63 // Check if it's not valid
65 if (PointerPde
->u
.Hard
.Valid
== 0)
67 /* This seems to be making the assumption that one PDE is one page long */
68 ASSERT(PAGE_SIZE
== (PD_COUNT
* (sizeof(MMPTE
) * PDE_COUNT
)));
71 // Copy it from our double-mapped system page directory
73 InterlockedExchangePte(PointerPde
,
74 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
&
76 sizeof(MMPTE
)].u
.Long
);
87 MiResolveDemandZeroFault(IN PVOID Address
,
92 PFN_NUMBER PageFrameNumber
;
94 DPRINT1("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
98 /* Must currently only be called by paging path, for system addresses only */
99 ASSERT(OldIrql
== MM_NOIRQL
);
100 ASSERT(Process
== NULL
);
103 // Lock the PFN database
105 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
106 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
109 PageFrameNumber
= MiRemoveAnyPage(0);
110 DPRINT1("New pool page: %lx\n", PageFrameNumber
);
113 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
118 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
121 // Increment demand zero faults
123 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
125 /* Shouldn't see faults for user PTEs yet */
126 ASSERT(PointerPte
> MiHighestUserPte
);
129 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, PointerPte
->u
.Soft
.Protection
, PageFrameNumber
);
130 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
131 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
132 *PointerPte
= TempPte
;
133 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
138 DPRINT1("Paged pool page has now been paged in\n");
139 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
144 MiDispatchFault(IN BOOLEAN StoreInstruction
,
146 IN PMMPTE PointerPte
,
147 IN PMMPTE PrototypePte
,
148 IN BOOLEAN Recursive
,
149 IN PEPROCESS Process
,
150 IN PVOID TrapInformation
,
156 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
161 // Make sure APCs are off and we're not at dispatch
163 OldIrql
= KeGetCurrentIrql ();
164 ASSERT(OldIrql
<= APC_LEVEL
);
165 ASSERT(KeAreAllApcsDisabled () == TRUE
);
168 // Grab a copy of the PTE
170 TempPte
= *PointerPte
;
173 ASSERT(PrototypePte
== NULL
);
176 // The PTE must be invalid, but not totally blank
178 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
179 ASSERT(TempPte
.u
.Long
!= 0);
182 // No prototype, transition or page file software PTEs in ARM3 yet
184 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
185 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
186 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
189 // If we got this far, the PTE can only be a demand zero PTE, which is what
190 // we want. Go handle it!
192 Status
= MiResolveDemandZeroFault(Address
,
196 ASSERT(KeAreAllApcsDisabled () == TRUE
);
197 if (NT_SUCCESS(Status
))
200 // Make sure we're returning in a sane state and pass the status down
202 ASSERT(OldIrql
== KeGetCurrentIrql ());
203 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
208 // Generate an access fault
210 return STATUS_ACCESS_VIOLATION
;
215 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
217 IN KPROCESSOR_MODE Mode
,
218 IN PVOID TrapInformation
)
220 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
224 PETHREAD CurrentThread
;
226 DPRINT("ARM3 FAULT AT: %p\n", Address
);
229 // Get the PTE and PDE
231 PointerPte
= MiAddressToPte(Address
);
232 PointerPde
= MiAddressToPde(Address
);
235 // Check for dispatch-level snafu
237 if (OldIrql
> APC_LEVEL
)
240 // There are some special cases where this is okay, but not in ARM3 yet
242 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
245 ASSERT(OldIrql
<= APC_LEVEL
);
249 // Check for kernel fault
251 if (Address
>= MmSystemRangeStart
)
254 // What are you even DOING here?
256 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
261 if (!PointerPde
->u
.Hard
.Valid
== 0)
266 DPRINT("Invalid PDE\n");
269 // Handle mapping in "Special" PDE directoreis
271 MiCheckPdeForPagedPool(Address
);
274 // Now we SHOULD be good
276 if (PointerPde
->u
.Hard
.Valid
== 0)
279 // FIXFIX: Do the S-LIST hack
285 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
288 (ULONG_PTR
)TrapInformation
,
294 // The PDE is valid, so read the PTE
296 TempPte
= *PointerPte
;
297 if (TempPte
.u
.Hard
.Valid
== 1)
300 // Only two things can go wrong here:
301 // Executing NX page (we couldn't care less)
302 // Writing to a read-only page (the stuff ARM3 works with is write,
303 // so again, moot point).
305 if (StoreInstruction
)
307 DPRINT1("Should NEVER happen on ARM3!!!\n");
308 return STATUS_ACCESS_VIOLATION
;
312 // Otherwise, the PDE was probably invalid, and all is good now
314 return STATUS_SUCCESS
;
318 // Check for a fault on the page table or hyperspace itself
320 if ((Address
>= (PVOID
)PTE_BASE
) && (Address
<= MmHyperSpaceEnd
))
323 // This might happen...not sure yet
325 DPRINT1("FAULT ON PAGE TABLES: %p %lx %lx!\n", Address
, *PointerPte
, *PointerPde
);
328 // Map in the page table
330 if (MiCheckPdeForPagedPool(Address
) == STATUS_WAIT_1
)
332 DPRINT1("PAGE TABLES FAULTED IN!\n");
333 return STATUS_SUCCESS
;
337 // Otherwise the page table doesn't actually exist
339 DPRINT1("FAILING\n");
340 return STATUS_ACCESS_VIOLATION
;
344 // Now we must raise to APC_LEVEL and mark the thread as owner
345 // We don't actually implement a working set pushlock, so this is only
346 // for internal consistency (and blocking APCs)
348 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
349 CurrentThread
= PsGetCurrentThread();
350 KeEnterGuardedRegion();
351 ASSERT((CurrentThread
->OwnsSystemWorkingSetExclusive
== 0) &&
352 (CurrentThread
->OwnsSystemWorkingSetShared
== 0));
353 CurrentThread
->OwnsSystemWorkingSetExclusive
= 1;
356 // Re-read PTE now that the IRQL has been raised
358 TempPte
= *PointerPte
;
359 if (TempPte
.u
.Hard
.Valid
== 1)
362 // Only two things can go wrong here:
363 // Executing NX page (we couldn't care less)
364 // Writing to a read-only page (the stuff ARM3 works with is write,
365 // so again, moot point.
367 if (StoreInstruction
)
369 DPRINT1("Should NEVER happen on ARM3!!!\n");
370 return STATUS_ACCESS_VIOLATION
;
374 // Otherwise, the PDE was probably invalid, and all is good now
376 return STATUS_SUCCESS
;
380 // We don't implement prototype PTEs
382 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
385 // We don't implement transition PTEs
387 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
390 // Now do the real fault handling
392 Status
= MiDispatchFault(StoreInstruction
,
404 ASSERT(KeAreAllApcsDisabled() == TRUE
);
405 CurrentThread
->OwnsSystemWorkingSetExclusive
= 0;
406 KeLeaveGuardedRegion();
407 KeLowerIrql(LockIrql
);
412 DPRINT("Fault resolved with status: %lx\n", Status
);
419 DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
420 return STATUS_ACCESS_VIOLATION
;