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