f4e00396ec3d21d0315db932f82929bf1d379a47
[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 PEPROCESS CurrentProcess;
301 NTSTATUS Status;
302 PMMSUPPORT WorkingSet;
303 DPRINT("ARM3 FAULT AT: %p\n", Address);
304
305 //
306 // Get the PTE and PDE
307 //
308 PointerPte = MiAddressToPte(Address);
309 PointerPde = MiAddressToPde(Address);
310
311 //
312 // Check for dispatch-level snafu
313 //
314 if (OldIrql > APC_LEVEL)
315 {
316 //
317 // There are some special cases where this is okay, but not in ARM3 yet
318 //
319 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
320 Address,
321 OldIrql);
322 ASSERT(OldIrql <= APC_LEVEL);
323 }
324
325 //
326 // Check for kernel fault
327 //
328 if (Address >= MmSystemRangeStart)
329 {
330 //
331 // What are you even DOING here?
332 //
333 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
334
335 //
336 // Is the PDE valid?
337 //
338 if (!PointerPde->u.Hard.Valid == 0)
339 {
340 //
341 // Debug spew (eww!)
342 //
343 DPRINT("Invalid PDE\n");
344
345 //
346 // Handle mapping in "Special" PDE directoreis
347 //
348 MiCheckPdeForPagedPool(Address);
349
350 //
351 // Now we SHOULD be good
352 //
353 if (PointerPde->u.Hard.Valid == 0)
354 {
355 //
356 // FIXFIX: Do the S-LIST hack
357 //
358
359 //
360 // Kill the system
361 //
362 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
363 (ULONG_PTR)Address,
364 StoreInstruction,
365 (ULONG_PTR)TrapInformation,
366 2);
367 }
368 }
369
370 //
371 // The PDE is valid, so read the PTE
372 //
373 TempPte = *PointerPte;
374 if (TempPte.u.Hard.Valid == 1)
375 {
376 //
377 // Only two things can go wrong here:
378 // Executing NX page (we couldn't care less)
379 // Writing to a read-only page (the stuff ARM3 works with is write,
380 // so again, moot point).
381 //
382 if (StoreInstruction)
383 {
384 DPRINT1("Should NEVER happen on ARM3!!!\n");
385 return STATUS_ACCESS_VIOLATION;
386 }
387
388 //
389 // Otherwise, the PDE was probably invalid, and all is good now
390 //
391 return STATUS_SUCCESS;
392 }
393
394 //
395 // Check for a fault on the page table or hyperspace itself
396 //
397 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
398 {
399 //
400 // This might happen...not sure yet
401 //
402 DPRINT1("FAULT ON PAGE TABLES: %p %lx %lx!\n", Address, *PointerPte, *PointerPde);
403
404 //
405 // Map in the page table
406 //
407 if (MiCheckPdeForPagedPool(Address) == STATUS_WAIT_1)
408 {
409 DPRINT1("PAGE TABLES FAULTED IN!\n");
410 return STATUS_SUCCESS;
411 }
412
413 //
414 // Otherwise the page table doesn't actually exist
415 //
416 DPRINT1("FAILING\n");
417 return STATUS_ACCESS_VIOLATION;
418 }
419
420 /* In this path, we are using the system working set */
421 CurrentThread = PsGetCurrentThread();
422 WorkingSet = &MmSystemCacheWs;
423
424 /* Acquire it */
425 KeRaiseIrql(APC_LEVEL, &LockIrql);
426 MiLockWorkingSet(CurrentThread, WorkingSet);
427
428 //
429 // Re-read PTE now that the IRQL has been raised
430 //
431 TempPte = *PointerPte;
432 if (TempPte.u.Hard.Valid == 1)
433 {
434 //
435 // Only two things can go wrong here:
436 // Executing NX page (we couldn't care less)
437 // Writing to a read-only page (the stuff ARM3 works with is write,
438 // so again, moot point.
439 //
440 if (StoreInstruction)
441 {
442 DPRINT1("Should NEVER happen on ARM3!!!\n");
443 return STATUS_ACCESS_VIOLATION;
444 }
445
446 /* Release the working set */
447 MiUnlockWorkingSet(CurrentThread, WorkingSet);
448 KeLowerIrql(LockIrql);
449
450 //
451 // Otherwise, the PDE was probably invalid, and all is good now
452 //
453 return STATUS_SUCCESS;
454 }
455
456 //
457 // We don't implement prototype PTEs
458 //
459 ASSERT(TempPte.u.Soft.Prototype == 0);
460
461 //
462 // We don't implement transition PTEs
463 //
464 ASSERT(TempPte.u.Soft.Transition == 0);
465
466 //
467 // Now do the real fault handling
468 //
469 Status = MiDispatchFault(StoreInstruction,
470 Address,
471 PointerPte,
472 NULL,
473 FALSE,
474 NULL,
475 TrapInformation,
476 NULL);
477
478 /* Release the working set */
479 ASSERT(KeAreAllApcsDisabled() == TRUE);
480 MiUnlockWorkingSet(CurrentThread, WorkingSet);
481 KeLowerIrql(LockIrql);
482
483 //
484 // We are done!
485 //
486 DPRINT("Fault resolved with status: %lx\n", Status);
487 return Status;
488 }
489
490 /* This is a user fault */
491 CurrentThread = PsGetCurrentThread();
492 CurrentProcess = PsGetCurrentProcess();
493
494 /* Lock the working set */
495 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
496
497 /* Do something */
498
499 /* Release the working set */
500 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
501 DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
502 return STATUS_ACCESS_VIOLATION;
503 }
504
505 /* EOF */