xref: /reactos/ntoskrnl/ke/arm/trapc.c (revision 8540ab04)
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 }
604