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
9 /* INCLUDES *******************************************************************/
12 #include <internal/arm/ksarm.h>
16 /* FUNCTIONS ******************************************************************/
22 PKPCR Pcr
= (PKPCR
)KeGetPcr();
23 PKPRCB Prcb
= Pcr
->Prcb
;
24 PKTHREAD OldThread
, NewThread
;
27 // Loop forever... that's why this is an idle loop
29 DPRINT1("[IDLE LOOP]\n");
41 // Check if there's DPC work to do
43 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
44 (Prcb
->TimerRequest
) ||
45 (Prcb
->DeferredReadyListHead
.Next
))
48 // Clear the pending interrupt
50 HalClearSoftwareInterrupt(DISPATCH_LEVEL
);
55 KiRetireDpcList(Prcb
);
59 // Check if there's a thread to schedule
64 // Out with the old, in with the new...
66 OldThread
= Prcb
->CurrentThread
;
67 NewThread
= Prcb
->NextThread
;
68 Prcb
->CurrentThread
= NewThread
;
69 Prcb
->NextThread
= NULL
;
72 // Update thread state
74 NewThread
->State
= Running
;
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
82 KiSwapContext(OldThread
, NewThread
);
87 // Go into WFI (sleep more)
89 KeArmWaitForInterrupt();
97 KiSwapProcess(IN PKPROCESS NewProcess
,
98 IN PKPROCESS OldProcess
)
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
);
106 // Update the page directory base
108 TtbRegister
.AsUlong
= NewProcess
->DirectoryTableBase
[0];
109 ASSERT(TtbRegister
.Reserved
== 0);
110 KeArmTranslationTableRegisterSet(TtbRegister
);
113 // FIXME: Flush the TLB
117 DPRINT1("Survived!\n");
123 KiSwapContextInternal(IN PKTHREAD OldThread
,
124 IN PKTHREAD NewThread
)
126 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
127 PKPRCB Prcb
= Pcr
->Prcb
;
128 PKPROCESS OldProcess
, NewProcess
;
134 // Increase context switch count
136 Pcr
->ContextSwitches
++;
139 // Check if WMI tracing is enabled
141 if (Pcr
->PerfGlobalGroupMask
)
144 // We don't support this yet on x86 either
146 DPRINT1("WMI Tracing not supported\n");
151 // Check if the processes are also different
153 OldProcess
= OldThread
->ApcState
.Process
;
154 NewProcess
= NewThread
->ApcState
.Process
;
155 if (OldProcess
!= NewProcess
)
158 // Check if address space switch is needed
160 if (OldProcess
->DirectoryTableBase
[0] !=
161 NewProcess
->DirectoryTableBase
[0])
164 // FIXME-USER: Support address space switch
166 DPRINT1("Address space switch not implemented\n");
172 // Increase thread context switches
174 NewThread
->ContextSwitches
++;
175 #if 0 // I don't buy this
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
186 Pcr
->CurrentThread
= NewThread
;
189 // DPCs shouldn't be active
191 if (Prcb
->DpcRoutineActive
)
196 KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC
,
197 (ULONG_PTR
)OldThread
,
198 (ULONG_PTR
)NewThread
,
199 (ULONG_PTR
)OldThread
->InitialStack
,
204 // Kernel APCs may be pending
206 if (NewThread
->ApcState
.KernelApcPending
)
211 if (NewThread
->SpecialApcDisable
== 0)
214 // Request APC delivery
216 HalRequestSoftwareInterrupt(APC_LEVEL
);
231 KPROCESSOR_MODE PreviousMode
;
232 KEXCEPTION_FRAME ExceptionFrame
;
233 PKTRAP_FRAME TrapFrame
= KeGetCurrentThread()->TrapFrame
;
235 DPRINT1("[APC TRAP]\n");
239 // Isolate previous mode
241 PreviousMode
= KiGetPreviousMode(TrapFrame
);
244 // FIXME-USER: Handle APC interrupt while in user-mode
246 if (PreviousMode
== UserMode
) ASSERT(FALSE
);
249 // Disable interrupts
254 // Clear APC interrupt
256 HalClearSoftwareInterrupt(APC_LEVEL
);
259 // Re-enable interrupts
266 KiDeliverApc(PreviousMode
, &ExceptionFrame
, TrapFrame
);
271 KiDispatchInterrupt(VOID
)
275 PKTHREAD NewThread
, OldThread
;
277 DPRINT1("[DPC TRAP]\n");
281 // Get the PCR and disable interrupts
283 Pcr
= (PKIPCR
)KeGetPcr();
288 //Check if we have to deliver DPCs, timers, or deferred threads
290 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
291 (Prcb
->TimerRequest
) ||
292 (Prcb
->DeferredReadyListHead
.Next
))
297 KiRetireDpcList(Prcb
);
301 // Re-enable interrupts
306 // Check for quantum end
308 if (Prcb
->QuantumEnd
)
311 // Handle quantum end
313 Prcb
->QuantumEnd
= FALSE
;
319 // Check if we have a thread to swap to
321 if (Prcb
->NextThread
)
324 // Next is now current
326 OldThread
= Prcb
->CurrentThread
;
327 NewThread
= Prcb
->NextThread
;
328 Prcb
->CurrentThread
= NewThread
;
329 Prcb
->NextThread
= NULL
;
332 // Update thread states
334 NewThread
->State
= Running
;
335 OldThread
->WaitReason
= WrDispatchInt
;
338 // Make the old thread ready
340 KxQueueReadyThread(OldThread
, Prcb
);
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
348 KiSwapContext(OldThread
, NewThread
);
354 KiInterruptHandler(IN PKTRAP_FRAME TrapFrame
,
358 ULONG InterruptCause
, InterruptMask
;
360 PKTRAP_FRAME OldTrapFrame
;
361 ASSERT(TrapFrame
->DbgArgMark
== 0xBADB0D00);
364 // Increment interrupt count
366 Pcr
= (PKIPCR
)KeGetPcr();
367 Pcr
->Prcb
->InterruptCount
++;
372 OldIrql
= KeGetCurrentIrql();
373 TrapFrame
->OldIrql
= OldIrql
;
376 // Get the interrupt source
378 InterruptCause
= HalGetInterruptSource();
379 //DPRINT1("[INT] (%x) @ %p %p\n", InterruptCause, TrapFrame->SvcLr, TrapFrame->Pc);
382 // Get the new IRQL and Interrupt Mask
384 Irql
= Pcr
->IrqlMask
[InterruptCause
];
385 InterruptMask
= Pcr
->IrqlTable
[Irql
];
388 // Raise to the new IRQL
393 // The clock ISR wants the trap frame as a parameter
395 OldTrapFrame
= KeGetCurrentThread()->TrapFrame
;
396 KeGetCurrentThread()->TrapFrame
= TrapFrame
;
399 // Check if this interrupt is at DISPATCH or higher
401 if (Irql
> DISPATCH_LEVEL
)
404 // FIXME-TODO: Switch to interrupt stack
406 //DPRINT1("[ISR]\n");
411 // We know this is APC or DPC.
413 //DPRINT1("[DPC/APC]\n");
414 HalClearSoftwareInterrupt(Irql
);
418 // Call the registered interrupt routine
420 Pcr
->InterruptRoutine
[Irql
]();
421 ASSERT(KeGetCurrentThread()->TrapFrame
== TrapFrame
);
422 KeGetCurrentThread()->TrapFrame
= OldTrapFrame
;
423 // DPRINT1("[ISR RETURN]\n");
426 // Restore IRQL and interrupts
428 KeLowerIrql(OldIrql
);
433 KiPrefetchAbortHandler(IN PKTRAP_FRAME TrapFrame
)
435 PVOID Address
= (PVOID
)KeArmFaultAddressRegisterGet();
436 ASSERT(TrapFrame
->DbgArgMark
== 0xBADB0D00);
437 ULONG Instruction
= *(PULONG
)TrapFrame
->Pc
;
438 ULONG DebugType
, Parameter0
;
439 EXCEPTION_RECORD ExceptionRecord
;
441 DPRINT1("[PREFETCH ABORT] (%x) @ %p/%p/%p\n",
442 KeArmInstructionFaultStatusRegisterGet(), Address
, TrapFrame
->SvcLr
, TrapFrame
->Pc
);
446 // What we *SHOULD* do is look at the instruction fault status register
447 // and see if it's equal to 2 (debug trap). Unfortunately QEMU doesn't seem
448 // to emulate this behaviour properly, so we use a workaround.
450 //if (KeArmInstructionFaultStatusRegisterGet() == 2)
451 if (Instruction
& 0xE1200070) // BKPT
454 // Okay, we know this is a breakpoint, extract the index
456 DebugType
= Instruction
& 0xF;
457 if (DebugType
== BREAKPOINT_PRINT
)
462 Parameter0
= TrapFrame
->R0
;
463 TrapFrame
->Pc
+= sizeof(ULONG
);
468 // Standard INT3 (emulate x86 behavior)
470 Parameter0
= STATUS_SUCCESS
;
474 // Build the exception record
476 ExceptionRecord
.ExceptionCode
= STATUS_BREAKPOINT
;
477 ExceptionRecord
.ExceptionFlags
= 0;
478 ExceptionRecord
.ExceptionRecord
= NULL
;
479 ExceptionRecord
.ExceptionAddress
= (PVOID
)TrapFrame
->Pc
;
480 ExceptionRecord
.NumberParameters
= 3;
483 // Build the parameters
485 ExceptionRecord
.ExceptionInformation
[0] = Parameter0
;
486 ExceptionRecord
.ExceptionInformation
[1] = TrapFrame
->R1
;
487 ExceptionRecord
.ExceptionInformation
[2] = TrapFrame
->R2
;
490 // Dispatch the exception
492 KiDispatchException(&ExceptionRecord
,
495 KiGetPreviousMode(TrapFrame
),
501 return STATUS_SUCCESS
;
509 return STATUS_SUCCESS
;
513 KiDataAbortHandler(IN PKTRAP_FRAME TrapFrame
)
516 PVOID Address
= (PVOID
)KeArmFaultAddressRegisterGet();
517 ASSERT(TrapFrame
->DbgArgMark
== 0xBADB0D00);
519 DPRINT1("[ABORT] (%x) @ %p/%p/%p\n",
520 KeArmFaultStatusRegisterGet(), Address
, TrapFrame
->SvcLr
, TrapFrame
->Pc
);
524 // Check if this is a page fault
526 if (KeArmFaultStatusRegisterGet() == 21 || KeArmFaultStatusRegisterGet() == 23)
528 Status
= MmAccessFault(FALSE
,
530 KiGetPreviousMode(TrapFrame
),
532 if (Status
== STATUS_SUCCESS
) return Status
;
540 return STATUS_SUCCESS
;
544 KiSoftwareInterruptHandler(IN PKTRAP_FRAME TrapFrame
)
547 KPROCESSOR_MODE PreviousMode
;
549 ASSERT(TrapFrame
->DbgArgMark
== 0xBADB0D00);
551 DPRINT1("[SWI] @ %p/%p\n", TrapFrame
->SvcLr
, TrapFrame
->Pc
);
555 // Get the current thread
557 Thread
= KeGetCurrentThread();
560 // Isolate previous mode
562 PreviousMode
= KiGetPreviousMode(TrapFrame
);
565 // Save old previous mode
567 TrapFrame
->PreviousMode
= PreviousMode
;
568 TrapFrame
->PreviousTrapFrame
= (ULONG_PTR
)Thread
->TrapFrame
;
571 // Save previous mode and trap frame
573 Thread
->TrapFrame
= TrapFrame
;
574 Thread
->PreviousMode
= PreviousMode
;
579 Instruction
= *(PULONG
)(TrapFrame
->Pc
- sizeof(ULONG
));
582 // Call the service call dispatcher
584 KiSystemService(Thread
, TrapFrame
, Instruction
);
588 KiUndefinedExceptionHandler(IN PKTRAP_FRAME TrapFrame
)
590 ASSERT(TrapFrame
->DbgArgMark
== 0xBADB0D00);
593 // This should never happen
595 DPRINT1("[UNDEF] @ %p/%p\n", TrapFrame
->SvcLr
, TrapFrame
->Pc
);
598 return STATUS_SUCCESS
;