Sync to trunk head (r47736)
[reactos.git] / ntoskrnl / mm / ARM3 / pagfault.c
1 /*
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
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #line 15 "ARMĀ³::PAGFAULT"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
18
19 /* GLOBALS ********************************************************************/
20
21 /* PRIVATE FUNCTIONS **********************************************************/
22
23 NTSTATUS
24 FASTCALL
25 MiCheckPdeForPagedPool(IN PVOID Address)
26 {
27 PMMPDE PointerPde;
28 NTSTATUS Status = STATUS_SUCCESS;
29
30 /* No session support in ReactOS yet */
31 ASSERT(MI_IS_SESSION_ADDRESS(Address) == FALSE);
32 ASSERT(MI_IS_SESSION_PTE(Address) == FALSE);
33
34 //
35 // Check if this is a fault while trying to access the page table itself
36 //
37 if ((Address >= (PVOID)MiAddressToPte(MmSystemRangeStart)) &&
38 (Address < (PVOID)PTE_TOP))
39 {
40 //
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
43 //
44 PointerPde = (PMMPDE)MiAddressToPte(Address);
45 Status = STATUS_WAIT_1;
46 }
47 else if (Address < MmSystemRangeStart)
48 {
49 //
50 // This is totally illegal
51 //
52 return STATUS_ACCESS_VIOLATION;
53 }
54 else
55 {
56 //
57 // Get the PDE for the address
58 //
59 PointerPde = MiAddressToPde(Address);
60 }
61
62 //
63 // Check if it's not valid
64 //
65 if (PointerPde->u.Hard.Valid == 0)
66 {
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)));
69
70 //
71 // Copy it from our double-mapped system page directory
72 //
73 InterlockedExchangePte(PointerPde,
74 MmSystemPagePtes[((ULONG_PTR)PointerPde &
75 (PAGE_SIZE - 1)) /
76 sizeof(MMPTE)].u.Long);
77 }
78
79 //
80 // Return status
81 //
82 return Status;
83 }
84
85 NTSTATUS
86 NTAPI
87 MiResolveDemandZeroFault(IN PVOID Address,
88 IN PMMPTE PointerPte,
89 IN PEPROCESS Process,
90 IN KIRQL OldIrql)
91 {
92 PFN_NUMBER PageFrameNumber;
93 MMPTE TempPte;
94 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
95 Address,
96 Process);
97
98 /* Must currently only be called by paging path, for system addresses only */
99 ASSERT(OldIrql == MM_NOIRQL);
100 ASSERT(Process == NULL);
101
102 //
103 // Lock the PFN database
104 //
105 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
106 ASSERT(PointerPte->u.Hard.Valid == 0);
107
108 /* Get a page */
109 PageFrameNumber = MiRemoveAnyPage(0);
110 DPRINT("New pool page: %lx\n", PageFrameNumber);
111
112 /* Initialize it */
113 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
114
115 //
116 // Release PFN lock
117 //
118 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
119
120 //
121 // Increment demand zero faults
122 //
123 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
124
125 /* Shouldn't see faults for user PTEs yet */
126 ASSERT(PointerPte > MiHighestUserPte);
127
128 /* Build the PTE */
129 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, PointerPte->u.Soft.Protection, PageFrameNumber);
130 MI_WRITE_VALID_PTE(PointerPte, TempPte);
131
132 //
133 // It's all good now
134 //
135 DPRINT("Paged pool page has now been paged in\n");
136 return STATUS_PAGE_FAULT_DEMAND_ZERO;
137 }
138
139 NTSTATUS
140 NTAPI
141 MiDispatchFault(IN BOOLEAN StoreInstruction,
142 IN PVOID Address,
143 IN PMMPTE PointerPte,
144 IN PMMPTE PrototypePte,
145 IN BOOLEAN Recursive,
146 IN PEPROCESS Process,
147 IN PVOID TrapInformation,
148 IN PVOID Vad)
149 {
150 MMPTE TempPte;
151 KIRQL OldIrql;
152 NTSTATUS Status;
153 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
154 Address,
155 Process);
156
157 //
158 // Make sure APCs are off and we're not at dispatch
159 //
160 OldIrql = KeGetCurrentIrql ();
161 ASSERT(OldIrql <= APC_LEVEL);
162 ASSERT(KeAreAllApcsDisabled () == TRUE);
163
164 //
165 // Grab a copy of the PTE
166 //
167 TempPte = *PointerPte;
168
169 /* No prototype */
170 ASSERT(PrototypePte == NULL);
171
172 //
173 // The PTE must be invalid, but not totally blank
174 //
175 ASSERT(TempPte.u.Hard.Valid == 0);
176 ASSERT(TempPte.u.Long != 0);
177
178 //
179 // No prototype, transition or page file software PTEs in ARM3 yet
180 //
181 ASSERT(TempPte.u.Soft.Prototype == 0);
182 ASSERT(TempPte.u.Soft.Transition == 0);
183 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
184
185 //
186 // If we got this far, the PTE can only be a demand zero PTE, which is what
187 // we want. Go handle it!
188 //
189 Status = MiResolveDemandZeroFault(Address,
190 PointerPte,
191 Process,
192 MM_NOIRQL);
193 ASSERT(KeAreAllApcsDisabled () == TRUE);
194 if (NT_SUCCESS(Status))
195 {
196 //
197 // Make sure we're returning in a sane state and pass the status down
198 //
199 ASSERT(OldIrql == KeGetCurrentIrql ());
200 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
201 return Status;
202 }
203
204 //
205 // Generate an access fault
206 //
207 return STATUS_ACCESS_VIOLATION;
208 }
209
210 NTSTATUS
211 NTAPI
212 MmArmAccessFault(IN BOOLEAN StoreInstruction,
213 IN PVOID Address,
214 IN KPROCESSOR_MODE Mode,
215 IN PVOID TrapInformation)
216 {
217 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
218 PMMPTE PointerPte;
219 PMMPDE PointerPde;
220 MMPTE TempPte;
221 PETHREAD CurrentThread;
222 NTSTATUS Status;
223 DPRINT("ARM3 FAULT AT: %p\n", Address);
224
225 //
226 // Get the PTE and PDE
227 //
228 PointerPte = MiAddressToPte(Address);
229 PointerPde = MiAddressToPde(Address);
230
231 //
232 // Check for dispatch-level snafu
233 //
234 if (OldIrql > APC_LEVEL)
235 {
236 //
237 // There are some special cases where this is okay, but not in ARM3 yet
238 //
239 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
240 Address,
241 OldIrql);
242 ASSERT(OldIrql <= APC_LEVEL);
243 }
244
245 //
246 // Check for kernel fault
247 //
248 if (Address >= MmSystemRangeStart)
249 {
250 //
251 // What are you even DOING here?
252 //
253 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
254
255 //
256 // Is the PDE valid?
257 //
258 if (!PointerPde->u.Hard.Valid == 0)
259 {
260 //
261 // Debug spew (eww!)
262 //
263 DPRINT("Invalid PDE\n");
264
265 //
266 // Handle mapping in "Special" PDE directoreis
267 //
268 MiCheckPdeForPagedPool(Address);
269
270 //
271 // Now we SHOULD be good
272 //
273 if (PointerPde->u.Hard.Valid == 0)
274 {
275 //
276 // FIXFIX: Do the S-LIST hack
277 //
278
279 //
280 // Kill the system
281 //
282 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
283 (ULONG_PTR)Address,
284 StoreInstruction,
285 (ULONG_PTR)TrapInformation,
286 2);
287 }
288 }
289
290 //
291 // The PDE is valid, so read the PTE
292 //
293 TempPte = *PointerPte;
294 if (TempPte.u.Hard.Valid == 1)
295 {
296 //
297 // Only two things can go wrong here:
298 // Executing NX page (we couldn't care less)
299 // Writing to a read-only page (the stuff ARM3 works with is write,
300 // so again, moot point).
301 //
302 if (StoreInstruction)
303 {
304 DPRINT1("Should NEVER happen on ARM3!!!\n");
305 return STATUS_ACCESS_VIOLATION;
306 }
307
308 //
309 // Otherwise, the PDE was probably invalid, and all is good now
310 //
311 return STATUS_SUCCESS;
312 }
313
314 //
315 // Check for a fault on the page table or hyperspace itself
316 //
317 if ((Address >= (PVOID)PTE_BASE) && (Address <= MmHyperSpaceEnd))
318 {
319 //
320 // This might happen...not sure yet
321 //
322 DPRINT1("FAULT ON PAGE TABLES: %p %lx %lx!\n", Address, *PointerPte, *PointerPde);
323
324 //
325 // Map in the page table
326 //
327 if (MiCheckPdeForPagedPool(Address) == STATUS_WAIT_1)
328 {
329 DPRINT1("PAGE TABLES FAULTED IN!\n");
330 return STATUS_SUCCESS;
331 }
332
333 //
334 // Otherwise the page table doesn't actually exist
335 //
336 DPRINT1("FAILING\n");
337 return STATUS_ACCESS_VIOLATION;
338 }
339
340 //
341 // Now we must raise to APC_LEVEL and mark the thread as owner
342 // We don't actually implement a working set pushlock, so this is only
343 // for internal consistency (and blocking APCs)
344 //
345 KeRaiseIrql(APC_LEVEL, &LockIrql);
346 CurrentThread = PsGetCurrentThread();
347 KeEnterGuardedRegion();
348 ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
349 (CurrentThread->OwnsSystemWorkingSetShared == 0));
350 CurrentThread->OwnsSystemWorkingSetExclusive = 1;
351
352 //
353 // Re-read PTE now that the IRQL has been raised
354 //
355 TempPte = *PointerPte;
356 if (TempPte.u.Hard.Valid == 1)
357 {
358 //
359 // Only two things can go wrong here:
360 // Executing NX page (we couldn't care less)
361 // Writing to a read-only page (the stuff ARM3 works with is write,
362 // so again, moot point.
363 //
364 if (StoreInstruction)
365 {
366 DPRINT1("Should NEVER happen on ARM3!!!\n");
367 return STATUS_ACCESS_VIOLATION;
368 }
369
370 //
371 // Otherwise, the PDE was probably invalid, and all is good now
372 //
373 return STATUS_SUCCESS;
374 }
375
376 //
377 // We don't implement prototype PTEs
378 //
379 ASSERT(TempPte.u.Soft.Prototype == 0);
380
381 //
382 // We don't implement transition PTEs
383 //
384 ASSERT(TempPte.u.Soft.Transition == 0);
385
386 //
387 // Now do the real fault handling
388 //
389 Status = MiDispatchFault(StoreInstruction,
390 Address,
391 PointerPte,
392 NULL,
393 FALSE,
394 NULL,
395 TrapInformation,
396 NULL);
397
398 //
399 // Re-enable APCs
400 //
401 ASSERT(KeAreAllApcsDisabled() == TRUE);
402 CurrentThread->OwnsSystemWorkingSetExclusive = 0;
403 KeLeaveGuardedRegion();
404 KeLowerIrql(LockIrql);
405
406 //
407 // We are done!
408 //
409 DPRINT("Fault resolved with status: %lx\n", Status);
410 return Status;
411 }
412
413 //
414 // DIE DIE DIE
415 //
416 DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
417 return STATUS_ACCESS_VIOLATION;
418 }
419
420 /* EOF */