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