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