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