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