[NTOS:MM] Pass page fault code to MmAccessFault
[reactos.git] / ntoskrnl / ke / arm / trapc.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/ke/arm/trapc.c
5 * PURPOSE: Implements the various trap handlers for ARM exceptions
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* FUNCTIONS ******************************************************************/
16
17 #if 0
18 VOID
19 KiIdleLoop(VOID)
20 {
21 PKPCR Pcr = (PKPCR)KeGetPcr();
22 PKPRCB Prcb = Pcr->Prcb;
23 PKTHREAD OldThread, NewThread;
24
25 //
26 // Loop forever... that's why this is an idle loop
27 //
28 DPRINT1("[IDLE LOOP]\n");
29 while (TRUE);
30
31 while (TRUE)
32 {
33 //
34 // Cycle interrupts
35 //
36 _disable();
37 _enable();
38
39 //
40 // Check if there's DPC work to do
41 //
42 if ((Prcb->DpcData[0].DpcQueueDepth) ||
43 (Prcb->TimerRequest) ||
44 (Prcb->DeferredReadyListHead.Next))
45 {
46 //
47 // Clear the pending interrupt
48 //
49 HalClearSoftwareInterrupt(DISPATCH_LEVEL);
50
51 //
52 // Retire DPCs
53 //
54 KiRetireDpcList(Prcb);
55 }
56
57 //
58 // Check if there's a thread to schedule
59 //
60 if (Prcb->NextThread)
61 {
62 //
63 // Out with the old, in with the new...
64 //
65 OldThread = Prcb->CurrentThread;
66 NewThread = Prcb->NextThread;
67 Prcb->CurrentThread = NewThread;
68 Prcb->NextThread = NULL;
69
70 //
71 // Update thread state
72 //
73 NewThread->State = Running;
74
75 //
76 // Swap to the new thread
77 // On ARM we call KiSwapContext instead of KiSwapContextInternal,
78 // because we're calling this from C code and not assembly.
79 // This is similar to how it gets called for unwaiting, on x86
80 //
81 KiSwapContext(OldThread, NewThread);
82 }
83 else
84 {
85 //
86 // Go into WFI (sleep more)
87 //
88 KeArmWaitForInterrupt();
89 }
90 }
91 }
92 #endif
93
94 VOID
95 NTAPI
96 KiSwapProcess(IN PKPROCESS NewProcess,
97 IN PKPROCESS OldProcess)
98 {
99 ARM_TTB_REGISTER TtbRegister;
100 DPRINT1("Swapping from: %p (%16s) to %p (%16s)\n",
101 OldProcess, ((PEPROCESS)OldProcess)->ImageFileName,
102 NewProcess, ((PEPROCESS)NewProcess)->ImageFileName);
103
104 //
105 // Update the page directory base
106 //
107 TtbRegister.AsUlong = NewProcess->DirectoryTableBase[0];
108 ASSERT(TtbRegister.Reserved == 0);
109 KeArmTranslationTableRegisterSet(TtbRegister);
110
111 //
112 // FIXME: Flush the TLB
113 //
114
115
116 DPRINT1("Survived!\n");
117 while (TRUE);
118 }
119
120 #if 0
121 BOOLEAN
122 KiSwapContextInternal(IN PKTHREAD OldThread,
123 IN PKTHREAD NewThread)
124 {
125 PKIPCR Pcr = (PKIPCR)KeGetPcr();
126 PKPRCB Prcb = Pcr->Prcb;
127 PKPROCESS OldProcess, NewProcess;
128
129 DPRINT1("SWAP\n");
130 while (TRUE);
131
132 //
133 // Increase context switch count
134 //
135 Pcr->ContextSwitches++;
136
137 //
138 // Check if WMI tracing is enabled
139 //
140 if (Pcr->PerfGlobalGroupMask)
141 {
142 //
143 // We don't support this yet on x86 either
144 //
145 DPRINT1("WMI Tracing not supported\n");
146 ASSERT(FALSE);
147 }
148
149 //
150 // Check if the processes are also different
151 //
152 OldProcess = OldThread->ApcState.Process;
153 NewProcess = NewThread->ApcState.Process;
154 if (OldProcess != NewProcess)
155 {
156 //
157 // Check if address space switch is needed
158 //
159 if (OldProcess->DirectoryTableBase[0] !=
160 NewProcess->DirectoryTableBase[0])
161 {
162 //
163 // FIXME-USER: Support address space switch
164 //
165 DPRINT1("Address space switch not implemented\n");
166 ASSERT(FALSE);
167 }
168 }
169
170 //
171 // Increase thread context switches
172 //
173 NewThread->ContextSwitches++;
174 #if 0 // I don't buy this
175 //
176 // Set us as the current thread
177 // NOTE: On RISC Platforms, there is both a KPCR CurrentThread, and a
178 // KPRCB CurrentThread.
179 // The latter is set just like on x86-based builds, the former is only set
180 // when actually doing the context switch (here).
181 // Recall that the reason for the latter is due to the fact that the KPCR
182 // is shared with user-mode (read-only), so that information is exposed
183 // there as well.
184 //
185 Pcr->CurrentThread = NewThread;
186 #endif
187 //
188 // DPCs shouldn't be active
189 //
190 if (Prcb->DpcRoutineActive)
191 {
192 //
193 // Crash the machine
194 //
195 KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC,
196 (ULONG_PTR)OldThread,
197 (ULONG_PTR)NewThread,
198 (ULONG_PTR)OldThread->InitialStack,
199 0);
200 }
201
202 //
203 // Kernel APCs may be pending
204 //
205 if (NewThread->ApcState.KernelApcPending)
206 {
207 //
208 // Are APCs enabled?
209 //
210 if (NewThread->SpecialApcDisable == 0)
211 {
212 //
213 // Request APC delivery
214 //
215 HalRequestSoftwareInterrupt(APC_LEVEL);
216 return TRUE;
217 }
218 }
219
220 //
221 // Return
222 //
223 return FALSE;
224 }
225 #endif
226
227 VOID
228 KiApcInterrupt(VOID)
229 {
230 KPROCESSOR_MODE PreviousMode;
231 KEXCEPTION_FRAME ExceptionFrame;
232 PKTRAP_FRAME TrapFrame = KeGetCurrentThread()->TrapFrame;
233
234 DPRINT1("[APC TRAP]\n");
235 while (TRUE);
236
237 //
238 // Isolate previous mode
239 //
240 PreviousMode = KiGetPreviousMode(TrapFrame);
241
242 //
243 // FIXME-USER: Handle APC interrupt while in user-mode
244 //
245 if (PreviousMode == UserMode) ASSERT(FALSE);
246
247 //
248 // Disable interrupts
249 //
250 _disable();
251
252 //
253 // Clear APC interrupt
254 //
255 HalClearSoftwareInterrupt(APC_LEVEL);
256
257 //
258 // Re-enable interrupts
259 //
260 _enable();
261
262 //
263 // Deliver APCs
264 //
265 KiDeliverApc(PreviousMode, &ExceptionFrame, TrapFrame);
266 }
267
268 #if 0
269 VOID
270 KiDispatchInterrupt(VOID)
271 {
272 PKIPCR Pcr;
273 PKPRCB Prcb;
274 PKTHREAD NewThread, OldThread;
275
276 DPRINT1("[DPC TRAP]\n");
277 while (TRUE);
278
279 //
280 // Get the PCR and disable interrupts
281 //
282 Pcr = (PKIPCR)KeGetPcr();
283 Prcb = Pcr->Prcb;
284 _disable();
285
286 //
287 //Check if we have to deliver DPCs, timers, or deferred threads
288 //
289 if ((Prcb->DpcData[0].DpcQueueDepth) ||
290 (Prcb->TimerRequest) ||
291 (Prcb->DeferredReadyListHead.Next))
292 {
293 //
294 // Retire DPCs
295 //
296 KiRetireDpcList(Prcb);
297 }
298
299 //
300 // Re-enable interrupts
301 //
302 _enable();
303
304 //
305 // Check for quantum end
306 //
307 if (Prcb->QuantumEnd)
308 {
309 //
310 // Handle quantum end
311 //
312 Prcb->QuantumEnd = FALSE;
313 KiQuantumEnd();
314 return;
315 }
316
317 //
318 // Check if we have a thread to swap to
319 //
320 if (Prcb->NextThread)
321 {
322 //
323 // Next is now current
324 //
325 OldThread = Prcb->CurrentThread;
326 NewThread = Prcb->NextThread;
327 Prcb->CurrentThread = NewThread;
328 Prcb->NextThread = NULL;
329
330 //
331 // Update thread states
332 //
333 NewThread->State = Running;
334 OldThread->WaitReason = WrDispatchInt;
335
336 //
337 // Make the old thread ready
338 //
339 KxQueueReadyThread(OldThread, Prcb);
340
341 //
342 // Swap to the new thread
343 // On ARM we call KiSwapContext instead of KiSwapContextInternal,
344 // because we're calling this from C code and not assembly.
345 // This is similar to how it gets called for unwaiting, on x86
346 //
347 KiSwapContext(OldThread, NewThread);
348 }
349 }
350 #endif
351
352 VOID
353 KiInterruptHandler(IN PKTRAP_FRAME TrapFrame,
354 IN ULONG Reserved)
355 {
356 KIRQL OldIrql, Irql;
357 ULONG InterruptCause;//, InterruptMask;
358 PKIPCR Pcr;
359 PKTRAP_FRAME OldTrapFrame;
360 ASSERT(TrapFrame->Reserved == 0xBADB0D00);
361
362 //
363 // Increment interrupt count
364 //
365 Pcr = (PKIPCR)KeGetPcr();
366 Pcr->Prcb.InterruptCount++;
367
368 //
369 // Get the old IRQL
370 //
371 OldIrql = KeGetCurrentIrql();
372 TrapFrame->PreviousIrql = OldIrql;
373
374 //
375 // Get the interrupt source
376 //
377 InterruptCause = HalGetInterruptSource();
378 //DPRINT1("[INT] (%x) @ %p %p\n", InterruptCause, TrapFrame->SvcLr, TrapFrame->Pc);
379
380 //
381 // Get the new IRQL and Interrupt Mask
382 //
383 /// FIXME: use a global table in ntoskrnl instead of HAL?
384 //Irql = Pcr->IrqlMask[InterruptCause];
385 //InterruptMask = Pcr->IrqlTable[Irql];
386 Irql = 0;
387 __debugbreak();
388
389 //
390 // Raise to the new IRQL
391 //
392 KfRaiseIrql(Irql);
393
394 //
395 // The clock ISR wants the trap frame as a parameter
396 //
397 OldTrapFrame = KeGetCurrentThread()->TrapFrame;
398 KeGetCurrentThread()->TrapFrame = TrapFrame;
399
400 //
401 // Check if this interrupt is at DISPATCH or higher
402 //
403 if (Irql > DISPATCH_LEVEL)
404 {
405 //
406 // FIXME-TODO: Switch to interrupt stack
407 //
408 //DPRINT1("[ISR]\n");
409 }
410 else
411 {
412 //
413 // We know this is APC or DPC.
414 //
415 //DPRINT1("[DPC/APC]\n");
416 HalClearSoftwareInterrupt(Irql);
417 }
418
419 //
420 // Call the registered interrupt routine
421 //
422 /// FIXME: this should probably go into a table in ntoskrnl
423 //Pcr->InterruptRoutine[Irql]();
424 __debugbreak();
425 ASSERT(KeGetCurrentThread()->TrapFrame == TrapFrame);
426 KeGetCurrentThread()->TrapFrame = OldTrapFrame;
427 // DPRINT1("[ISR RETURN]\n");
428
429 //
430 // Restore IRQL and interrupts
431 //
432 KeLowerIrql(OldIrql);
433 _enable();
434 }
435
436 NTSTATUS
437 KiPrefetchAbortHandler(IN PKTRAP_FRAME TrapFrame)
438 {
439 PVOID Address = (PVOID)KeArmFaultAddressRegisterGet();
440 ASSERT(TrapFrame->Reserved == 0xBADB0D00);
441 ULONG Instruction = *(PULONG)TrapFrame->Pc;
442 ULONG DebugType, Parameter0;
443 EXCEPTION_RECORD ExceptionRecord;
444
445 DPRINT1("[PREFETCH ABORT] (%x) @ %p/%p/%p\n",
446 KeArmInstructionFaultStatusRegisterGet(), Address, TrapFrame->Lr, TrapFrame->Pc);
447 while (TRUE);
448
449 //
450 // What we *SHOULD* do is look at the instruction fault status register
451 // and see if it's equal to 2 (debug trap). Unfortunately QEMU doesn't seem
452 // to emulate this behaviour properly, so we use a workaround.
453 //
454 //if (KeArmInstructionFaultStatusRegisterGet() == 2)
455 if (Instruction & 0xE1200070) // BKPT
456 {
457 //
458 // Okay, we know this is a breakpoint, extract the index
459 //
460 DebugType = Instruction & 0xF;
461 if (DebugType == BREAKPOINT_PRINT)
462 {
463 //
464 // Debug Service
465 //
466 Parameter0 = TrapFrame->R0;
467 TrapFrame->Pc += sizeof(ULONG);
468 }
469 else
470 {
471 //
472 // Standard INT3 (emulate x86 behavior)
473 //
474 Parameter0 = STATUS_SUCCESS;
475 }
476
477 //
478 // Build the exception record
479 //
480 ExceptionRecord.ExceptionCode = STATUS_BREAKPOINT;
481 ExceptionRecord.ExceptionFlags = 0;
482 ExceptionRecord.ExceptionRecord = NULL;
483 ExceptionRecord.ExceptionAddress = (PVOID)TrapFrame->Pc;
484 ExceptionRecord.NumberParameters = 3;
485
486 //
487 // Build the parameters
488 //
489 ExceptionRecord.ExceptionInformation[0] = Parameter0;
490 ExceptionRecord.ExceptionInformation[1] = TrapFrame->R1;
491 ExceptionRecord.ExceptionInformation[2] = TrapFrame->R2;
492
493 //
494 // Dispatch the exception
495 //
496 KiDispatchException(&ExceptionRecord,
497 NULL,
498 TrapFrame,
499 KiGetPreviousMode(TrapFrame),
500 TRUE);
501
502 //
503 // We're done
504 //
505 return STATUS_SUCCESS;
506 }
507
508 //
509 // Unhandled
510 //
511 UNIMPLEMENTED;
512 ASSERT(FALSE);
513 return STATUS_SUCCESS;
514 }
515
516 NTSTATUS
517 KiDataAbortHandler(IN PKTRAP_FRAME TrapFrame)
518 {
519 NTSTATUS Status;
520 PVOID Address = (PVOID)KeArmFaultAddressRegisterGet();
521 ASSERT(TrapFrame->Reserved == 0xBADB0D00);
522
523 DPRINT1("[ABORT] (%x) @ %p/%p/%p\n",
524 KeArmFaultStatusRegisterGet(), Address, TrapFrame->Lr, TrapFrame->Pc);
525 while (TRUE);
526
527 //
528 // Check if this is a page fault
529 //
530 if (KeArmFaultStatusRegisterGet() == 21 || KeArmFaultStatusRegisterGet() == 23)
531 {
532 Status = MmAccessFault(KeArmFaultStatusRegisterGet(),
533 Address,
534 KiGetPreviousMode(TrapFrame),
535 TrapFrame);
536 if (NT_SUCCESS(Status)) return Status;
537 }
538
539 //
540 // Unhandled
541 //
542 UNIMPLEMENTED;
543 ASSERT(FALSE);
544 return STATUS_SUCCESS;
545 }
546
547 VOID
548 KiSoftwareInterruptHandler(IN PKTRAP_FRAME TrapFrame)
549 {
550 PKTHREAD Thread;
551 KPROCESSOR_MODE PreviousMode;
552 ULONG Instruction;
553 ASSERT(TrapFrame->Reserved == 0xBADB0D00);
554
555 DPRINT1("[SWI] @ %p/%p\n", TrapFrame->Lr, TrapFrame->Pc);
556 while (TRUE);
557
558 //
559 // Get the current thread
560 //
561 Thread = KeGetCurrentThread();
562
563 //
564 // Isolate previous mode
565 //
566 PreviousMode = KiGetPreviousMode(TrapFrame);
567
568 //
569 // Save old previous mode
570 //
571 TrapFrame->PreviousMode = PreviousMode;
572 TrapFrame->TrapFrame = (ULONG_PTR)Thread->TrapFrame;
573
574 //
575 // Save previous mode and trap frame
576 //
577 Thread->TrapFrame = TrapFrame;
578 Thread->PreviousMode = PreviousMode;
579
580 //
581 // Read the opcode
582 //
583 Instruction = *(PULONG)(TrapFrame->Pc - sizeof(ULONG));
584
585 //
586 // Call the service call dispatcher
587 //
588 KiSystemService(Thread, TrapFrame, Instruction);
589 }
590
591 NTSTATUS
592 KiUndefinedExceptionHandler(IN PKTRAP_FRAME TrapFrame)
593 {
594 ASSERT(TrapFrame->Reserved == 0xBADB0D00);
595
596 //
597 // This should never happen
598 //
599 DPRINT1("[UNDEF] @ %p/%p\n", TrapFrame->Lr, TrapFrame->Pc);
600 UNIMPLEMENTED;
601 ASSERT(FALSE);
602 return STATUS_SUCCESS;
603 }