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