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