eeb82a7712f0267319762c32d8880f84927db928
[reactos.git] / reactos / 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 (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
38 {
39 //
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
42 //
43 PointerPde = (PMMPDE)MiAddressToPte(Address);
44 Status = STATUS_WAIT_1;
45 }
46 else if (Address < MmSystemRangeStart)
47 {
48 //
49 // This is totally illegal
50 //
51 return STATUS_ACCESS_VIOLATION;
52 }
53 else
54 {
55 //
56 // Get the PDE for the address
57 //
58 PointerPde = MiAddressToPde(Address);
59 }
60
61 //
62 // Check if it's not valid
63 //
64 if (PointerPde->u.Hard.Valid == 0)
65 {
66 #ifndef _M_AMD64
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 #endif
70
71 //
72 // Copy it from our double-mapped system page directory
73 //
74 InterlockedExchangePte(PointerPde,
75 MmSystemPagePtes[((ULONG_PTR)PointerPde &
76 (PAGE_SIZE - 1)) /
77 sizeof(MMPTE)].u.Long);
78 }
79
80 //
81 // Return status
82 //
83 return Status;
84 }
85
86 VOID
87 NTAPI
88 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
89 {
90 PMMPTE ZeroPte;
91 MMPTE TempPte;
92 PMMPFN Pfn1;
93 PVOID ZeroAddress;
94
95 /* Get the PFN for this page */
96 Pfn1 = MiGetPfnEntry(PageFrameNumber);
97 ASSERT(Pfn1);
98
99 /* Grab a system PTE we can use to zero the page */
100 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
101 ASSERT(ZeroPte);
102
103 /* Initialize the PTE for it */
104 TempPte = ValidKernelPte;
105 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
106
107 /* Setup caching */
108 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
109 {
110 /* Write combining, no caching */
111 MI_PAGE_DISABLE_CACHE(&TempPte);
112 MI_PAGE_WRITE_COMBINED(&TempPte);
113 }
114 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
115 {
116 /* Write through, no caching */
117 MI_PAGE_DISABLE_CACHE(&TempPte);
118 MI_PAGE_WRITE_THROUGH(&TempPte);
119 }
120
121 /* Make the system PTE valid with our PFN */
122 MI_WRITE_VALID_PTE(ZeroPte, TempPte);
123
124 /* Get the address it maps to, and zero it out */
125 ZeroAddress = MiPteToAddress(ZeroPte);
126 KeZeroPages(ZeroAddress, PAGE_SIZE);
127
128 /* Now get rid of it */
129 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
130 }
131
132 NTSTATUS
133 NTAPI
134 MiResolveDemandZeroFault(IN PVOID Address,
135 IN PMMPTE PointerPte,
136 IN PEPROCESS Process,
137 IN KIRQL OldIrql)
138 {
139 PFN_NUMBER PageFrameNumber;
140 MMPTE TempPte;
141 BOOLEAN NeedZero = FALSE;
142 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
143 Address,
144 Process);
145
146 /* Must currently only be called by paging path */
147 ASSERT(OldIrql == MM_NOIRQL);
148 if (Process)
149 {
150 /* Sanity check */
151 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
152
153 /* No forking yet */
154 ASSERT(Process->ForkInProgress == NULL);
155
156 /* We'll need a zero page */
157 NeedZero = TRUE;
158 }
159
160 //
161 // Lock the PFN database
162 //
163 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
164 ASSERT(PointerPte->u.Hard.Valid == 0);
165
166 /* Get a page */
167 PageFrameNumber = MiRemoveAnyPage(0);
168 DPRINT("New pool page: %lx\n", PageFrameNumber);
169
170 /* Initialize it */
171 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
172
173 //
174 // Release PFN lock
175 //
176 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
177
178 //
179 // Increment demand zero faults
180 //
181 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
182
183 /* Zero the page if need be */
184 if (NeedZero) MiZeroPfn(PageFrameNumber);
185
186 /* Build the PTE */
187 if (PointerPte <= MiHighestUserPte)
188 {
189 /* For user mode */
190 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
191 PointerPte,
192 PointerPte->u.Soft.Protection,
193 PageFrameNumber);
194 }
195 else
196 {
197 /* For kernel mode */
198 MI_MAKE_HARDWARE_PTE(&TempPte,
199 PointerPte,
200 PointerPte->u.Soft.Protection,
201 PageFrameNumber);
202 }
203
204 /* Set it dirty if it's a writable page */
205 if (TempPte.u.Hard.Write) TempPte.u.Hard.Dirty = TRUE;
206
207 /* Write it */
208 MI_WRITE_VALID_PTE(PointerPte, TempPte);
209
210 //
211 // It's all good now
212 //
213 DPRINT("Paged pool page has now been paged in\n");
214 return STATUS_PAGE_FAULT_DEMAND_ZERO;
215 }
216
217 NTSTATUS
218 NTAPI
219 MiDispatchFault(IN BOOLEAN StoreInstruction,
220 IN PVOID Address,
221 IN PMMPTE PointerPte,
222 IN PMMPTE PrototypePte,
223 IN BOOLEAN Recursive,
224 IN PEPROCESS Process,
225 IN PVOID TrapInformation,
226 IN PVOID Vad)
227 {
228 MMPTE TempPte;
229 KIRQL OldIrql;
230 NTSTATUS Status;
231 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
232 Address,
233 Process);
234
235 //
236 // Make sure APCs are off and we're not at dispatch
237 //
238 OldIrql = KeGetCurrentIrql ();
239 ASSERT(OldIrql <= APC_LEVEL);
240 ASSERT(KeAreAllApcsDisabled () == TRUE);
241
242 //
243 // Grab a copy of the PTE
244 //
245 TempPte = *PointerPte;
246
247 /* No prototype */
248 ASSERT(PrototypePte == NULL);
249
250 //
251 // The PTE must be invalid, but not totally blank
252 //
253 ASSERT(TempPte.u.Hard.Valid == 0);
254 ASSERT(TempPte.u.Long != 0);
255
256 //
257 // No prototype, transition or page file software PTEs in ARM3 yet
258 //
259 ASSERT(TempPte.u.Soft.Prototype == 0);
260 ASSERT(TempPte.u.Soft.Transition == 0);
261 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
262
263 //
264 // If we got this far, the PTE can only be a demand zero PTE, which is what
265 // we want. Go handle it!
266 //
267 Status = MiResolveDemandZeroFault(Address,
268 PointerPte,
269 Process,
270 MM_NOIRQL);
271 ASSERT(KeAreAllApcsDisabled () == TRUE);
272 if (NT_SUCCESS(Status))
273 {
274 //
275 // Make sure we're returning in a sane state and pass the status down
276 //
277 ASSERT(OldIrql == KeGetCurrentIrql ());
278 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
279 return Status;
280 }
281
282 //
283 // Generate an access fault
284 //
285 return STATUS_ACCESS_VIOLATION;
286 }
287
288 NTSTATUS
289 NTAPI
290 MmArmAccessFault(IN BOOLEAN StoreInstruction,
291 IN PVOID Address,
292 IN KPROCESSOR_MODE Mode,
293 IN PVOID TrapInformation)
294 {
295 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
296 PMMPTE PointerPte;
297 PMMPDE PointerPde;
298 MMPTE TempPte;
299 PETHREAD CurrentThread;
300 NTSTATUS Status;
301 DPRINT("ARM3 FAULT AT: %p\n", Address);
302
303 //
304 // Get the PTE and PDE
305 //
306 PointerPte = MiAddressToPte(Address);
307 PointerPde = MiAddressToPde(Address);
308
309 //
310 // Check for dispatch-level snafu
311 //
312 if (OldIrql > APC_LEVEL)
313 {
314 //
315 // There are some special cases where this is okay, but not in ARM3 yet
316 //
317 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
318 Address,
319 OldIrql);
320 ASSERT(OldIrql <= APC_LEVEL);
321 }
322
323 //
324 // Check for kernel fault
325 //
326 if (Address >= MmSystemRangeStart)
327 {
328 //
329 // What are you even DOING here?
330 //
331 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
332
333 //
334 // Is the PDE valid?
335 //
336 if (!PointerPde->u.Hard.Valid == 0)
337 {
338 //
339 // Debug spew (eww!)
340 //
341 DPRINT("Invalid PDE\n");
342
343 //
344 // Handle mapping in "Special" PDE directoreis
345 //
346 MiCheckPdeForPagedPool(Address);
347
348 //
349 // Now we SHOULD be good
350 //
351 if (PointerPde->u.Hard.Valid == 0)
352 {
353 //
354 // FIXFIX: Do the S-LIST hack
355 //
356
357 //
358 // Kill the system
359 //
360 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
361 (ULONG_PTR)Address,
362 StoreInstruction,
363 (ULONG_PTR)TrapInformation,
364 2);
365 }
366 }
367
368 //
369 // The PDE is valid, so read the PTE
370 //
371 TempPte = *PointerPte;
372 if (TempPte.u.Hard.Valid == 1)
373 {
374 //
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).
379 //
380 if (StoreInstruction)
381 {
382 DPRINT1("Should NEVER happen on ARM3!!!\n");
383 return STATUS_ACCESS_VIOLATION;
384 }
385
386 //
387 // Otherwise, the PDE was probably invalid, and all is good now
388 //
389 return STATUS_SUCCESS;
390 }
391
392 //
393 // Check for a fault on the page table or hyperspace itself
394 //
395 if ((Address >= (PVOID)PTE_BASE) && (Address <= MmHyperSpaceEnd))
396 {
397 //
398 // This might happen...not sure yet
399 //
400 DPRINT1("FAULT ON PAGE TABLES: %p %lx %lx!\n", Address, *PointerPte, *PointerPde);
401
402 //
403 // Map in the page table
404 //
405 if (MiCheckPdeForPagedPool(Address) == STATUS_WAIT_1)
406 {
407 DPRINT1("PAGE TABLES FAULTED IN!\n");
408 return STATUS_SUCCESS;
409 }
410
411 //
412 // Otherwise the page table doesn't actually exist
413 //
414 DPRINT1("FAILING\n");
415 return STATUS_ACCESS_VIOLATION;
416 }
417
418 //
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)
422 //
423 KeRaiseIrql(APC_LEVEL, &LockIrql);
424 CurrentThread = PsGetCurrentThread();
425 KeEnterGuardedRegion();
426 ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
427 (CurrentThread->OwnsSystemWorkingSetShared == 0));
428 CurrentThread->OwnsSystemWorkingSetExclusive = 1;
429
430 //
431 // Re-read PTE now that the IRQL has been raised
432 //
433 TempPte = *PointerPte;
434 if (TempPte.u.Hard.Valid == 1)
435 {
436 //
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.
441 //
442 if (StoreInstruction)
443 {
444 DPRINT1("Should NEVER happen on ARM3!!!\n");
445 return STATUS_ACCESS_VIOLATION;
446 }
447
448 //
449 // Otherwise, the PDE was probably invalid, and all is good now
450 //
451 return STATUS_SUCCESS;
452 }
453
454 //
455 // We don't implement prototype PTEs
456 //
457 ASSERT(TempPte.u.Soft.Prototype == 0);
458
459 //
460 // We don't implement transition PTEs
461 //
462 ASSERT(TempPte.u.Soft.Transition == 0);
463
464 //
465 // Now do the real fault handling
466 //
467 Status = MiDispatchFault(StoreInstruction,
468 Address,
469 PointerPte,
470 NULL,
471 FALSE,
472 NULL,
473 TrapInformation,
474 NULL);
475
476 //
477 // Re-enable APCs
478 //
479 ASSERT(KeAreAllApcsDisabled() == TRUE);
480 CurrentThread->OwnsSystemWorkingSetExclusive = 0;
481 KeLeaveGuardedRegion();
482 KeLowerIrql(LockIrql);
483
484 //
485 // We are done!
486 //
487 DPRINT("Fault resolved with status: %lx\n", Status);
488 return Status;
489 }
490
491 //
492 // DIE DIE DIE
493 //
494 DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
495 return STATUS_ACCESS_VIOLATION;
496 }
497
498 /* EOF */