Sync to trunk (r44371)
[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 PMMPTE 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 = 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, 0);
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 = HyperTemplatePte;
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 MMPTE TempPte;
205 PETHREAD CurrentThread;
206 NTSTATUS Status;
207 DPRINT("ARM3 FAULT AT: %p\n", Address);
208
209 //
210 // Get the PTE and PDE
211 //
212 PointerPte = MiAddressToPte(Address);
213
214 //
215 // Check for dispatch-level snafu
216 //
217 if (OldIrql > APC_LEVEL)
218 {
219 //
220 // There are some special cases where this is okay, but not in ARM3 yet
221 //
222 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
223 Address,
224 OldIrql);
225 ASSERT(OldIrql <= APC_LEVEL);
226 }
227
228 //
229 // Check for kernel fault
230 //
231 if (Address >= MmSystemRangeStart)
232 {
233 //
234 // What are you even DOING here?
235 //
236 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
237
238 //
239 // Is the PDE valid?
240 //
241 if (!MiIsPdeForAddressValid(Address))
242 {
243 //
244 // Debug spew (eww!)
245 //
246 DPRINT("Invalid PDE\n");
247
248 //
249 // Handle mapping in "Special" PDE directoreis
250 //
251 MiCheckPdeForPagedPool(Address);
252
253 //
254 // Now we SHOULD be good
255 //
256 if (!MiIsPdeForAddressValid(Address))
257 {
258 //
259 // FIXFIX: Do the S-LIST hack
260 //
261
262 //
263 // Kill the system
264 //
265 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
266 (ULONG_PTR)Address,
267 StoreInstruction,
268 (ULONG_PTR)TrapInformation,
269 2);
270 }
271 }
272
273 //
274 // The PDE is valid, so read the PTE
275 //
276 TempPte = *PointerPte;
277 if (TempPte.u.Hard.Valid == 1)
278 {
279 //
280 // Only two things can go wrong here:
281 // Executing NX page (we couldn't care less)
282 // Writing to a read-only page (the stuff ARM3 works with is write,
283 // so again, moot point).
284 //
285 if (StoreInstruction)
286 {
287 DPRINT1("Should NEVER happen on ARM3!!!\n");
288 return STATUS_ACCESS_VIOLATION;
289 }
290
291 //
292 // Otherwise, the PDE was probably invalid, and all is good now
293 //
294 return STATUS_SUCCESS;
295 }
296
297 //
298 // Check for a fault on the page table or hyperspace itself
299 //
300 if ((Address >= (PVOID)PTE_BASE) && (Address <= (PVOID)HYPER_SPACE_END))
301 {
302 //
303 // This might happen...not sure yet
304 //
305 DPRINT1("FAULT ON PAGE TABLES!\n");
306 return STATUS_ACCESS_VIOLATION;
307 }
308
309 //
310 // Now we must raise to APC_LEVEL and mark the thread as owner
311 // We don't actually implement a working set pushlock, so this is only
312 // for internal consistency (and blocking APCs)
313 //
314 KeRaiseIrql(APC_LEVEL, &LockIrql);
315 CurrentThread = PsGetCurrentThread();
316 KeEnterGuardedRegion();
317 ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
318 (CurrentThread->OwnsSystemWorkingSetShared == 0));
319 CurrentThread->OwnsSystemWorkingSetExclusive = 1;
320
321 //
322 // Re-read PTE now that the IRQL has been raised
323 //
324 TempPte = *PointerPte;
325 if (TempPte.u.Hard.Valid == 1)
326 {
327 //
328 // Only two things can go wrong here:
329 // Executing NX page (we couldn't care less)
330 // Writing to a read-only page (the stuff ARM3 works with is write,
331 // so again, moot point.
332 //
333 if (StoreInstruction)
334 {
335 DPRINT1("Should NEVER happen on ARM3!!!\n");
336 return STATUS_ACCESS_VIOLATION;
337 }
338
339 //
340 // Otherwise, the PDE was probably invalid, and all is good now
341 //
342 return STATUS_SUCCESS;
343 }
344
345 //
346 // We don't implement prototype PTEs
347 //
348 ASSERT(TempPte.u.Soft.Prototype == 0);
349
350 //
351 // We don't implement transition PTEs
352 //
353 ASSERT(TempPte.u.Soft.Transition == 0);
354
355 //
356 // Now do the real fault handling
357 //
358 Status = MiDispatchFault(StoreInstruction,
359 Address,
360 PointerPte,
361 NULL,
362 FALSE,
363 NULL,
364 TrapInformation,
365 NULL);
366
367 //
368 // Re-enable APCs
369 //
370 ASSERT(KeAreAllApcsDisabled() == TRUE);
371 CurrentThread->OwnsSystemWorkingSetExclusive = 0;
372 KeLeaveGuardedRegion();
373 KeLowerIrql(LockIrql);
374
375 //
376 // We are done!
377 //
378 DPRINT("Fault resolved with status: %lx\n", Status);
379 return Status;
380 }
381
382 //
383 // DIE DIE DIE
384 //
385 DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
386 return STATUS_ACCESS_VIOLATION;
387 }
388
389 /* EOF */