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 *******************************************************************/
15 /* FUNCTIONS ******************************************************************/
21 PKPCR Pcr
= (PKPCR
)KeGetPcr();
22 PKPRCB Prcb
= Pcr
->Prcb
;
23 PKTHREAD OldThread
, NewThread
;
26 // Loop forever... that's why this is an idle loop
28 DPRINT1("[IDLE LOOP]\n");
40 // Check if there's DPC work to do
42 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
43 (Prcb
->TimerRequest
) ||
44 (Prcb
->DeferredReadyListHead
.Next
))
47 // Clear the pending interrupt
49 HalClearSoftwareInterrupt(DISPATCH_LEVEL
);
54 KiRetireDpcList(Prcb
);
58 // Check if there's a thread to schedule
63 // Out with the old, in with the new...
65 OldThread
= Prcb
->CurrentThread
;
66 NewThread
= Prcb
->NextThread
;
67 Prcb
->CurrentThread
= NewThread
;
68 Prcb
->NextThread
= NULL
;
71 // Update thread state
73 NewThread
->State
= Running
;
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
81 KiSwapContext(OldThread
, NewThread
);
86 // Go into WFI (sleep more)
88 KeArmWaitForInterrupt();
96 KiSwapProcess(IN PKPROCESS NewProcess
,
97 IN PKPROCESS OldProcess
)
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
);
105 // Update the page directory base
107 TtbRegister
.AsUlong
= NewProcess
->DirectoryTableBase
[0];
108 ASSERT(TtbRegister
.Reserved
== 0);
109 KeArmTranslationTableRegisterSet(TtbRegister
);
112 // FIXME: Flush the TLB
116 DPRINT1("Survived!\n");
122 KiSwapContextInternal(IN PKTHREAD OldThread
,
123 IN PKTHREAD NewThread
)
125 PKIPCR Pcr
= (PKIPCR
)KeGetPcr();
126 PKPRCB Prcb
= Pcr
->Prcb
;
127 PKPROCESS OldProcess
, NewProcess
;
133 // Increase context switch count
135 Pcr
->ContextSwitches
++;
138 // Check if WMI tracing is enabled
140 if (Pcr
->PerfGlobalGroupMask
)
143 // We don't support this yet on x86 either
145 DPRINT1("WMI Tracing not supported\n");
150 // Check if the processes are also different
152 OldProcess
= OldThread
->ApcState
.Process
;
153 NewProcess
= NewThread
->ApcState
.Process
;
154 if (OldProcess
!= NewProcess
)
157 // Check if address space switch is needed
159 if (OldProcess
->DirectoryTableBase
[0] !=
160 NewProcess
->DirectoryTableBase
[0])
163 // FIXME-USER: Support address space switch
165 DPRINT1("Address space switch not implemented\n");
171 // Increase thread context switches
173 NewThread
->ContextSwitches
++;
174 #if 0 // I don't buy this
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
185 Pcr
->CurrentThread
= NewThread
;
188 // DPCs shouldn't be active
190 if (Prcb
->DpcRoutineActive
)
195 KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC
,
196 (ULONG_PTR
)OldThread
,
197 (ULONG_PTR
)NewThread
,
198 (ULONG_PTR
)OldThread
->InitialStack
,
203 // Kernel APCs may be pending
205 if (NewThread
->ApcState
.KernelApcPending
)
210 if (NewThread
->SpecialApcDisable
== 0)
213 // Request APC delivery
215 HalRequestSoftwareInterrupt(APC_LEVEL
);
230 KPROCESSOR_MODE PreviousMode
;
231 KEXCEPTION_FRAME ExceptionFrame
;
232 PKTRAP_FRAME TrapFrame
= KeGetCurrentThread()->TrapFrame
;
234 DPRINT1("[APC TRAP]\n");
238 // Isolate previous mode
240 PreviousMode
= KiGetPreviousMode(TrapFrame
);
243 // FIXME-USER: Handle APC interrupt while in user-mode
245 if (PreviousMode
== UserMode
) ASSERT(FALSE
);
248 // Disable interrupts
253 // Clear APC interrupt
255 HalClearSoftwareInterrupt(APC_LEVEL
);
258 // Re-enable interrupts
265 KiDeliverApc(PreviousMode
, &ExceptionFrame
, TrapFrame
);
270 KiDispatchInterrupt(VOID
)
274 PKTHREAD NewThread
, OldThread
;
276 DPRINT1("[DPC TRAP]\n");
280 // Get the PCR and disable interrupts
282 Pcr
= (PKIPCR
)KeGetPcr();
287 //Check if we have to deliver DPCs, timers, or deferred threads
289 if ((Prcb
->DpcData
[0].DpcQueueDepth
) ||
290 (Prcb
->TimerRequest
) ||
291 (Prcb
->DeferredReadyListHead
.Next
))
296 KiRetireDpcList(Prcb
);
300 // Re-enable interrupts
305 // Check for quantum end
307 if (Prcb
->QuantumEnd
)
310 // Handle quantum end
312 Prcb
->QuantumEnd
= FALSE
;
318 // Check if we have a thread to swap to
320 if (Prcb
->NextThread
)
323 // Next is now current
325 OldThread
= Prcb
->CurrentThread
;
326 NewThread
= Prcb
->NextThread
;
327 Prcb
->CurrentThread
= NewThread
;
328 Prcb
->NextThread
= NULL
;
331 // Update thread states
333 NewThread
->State
= Running
;
334 OldThread
->WaitReason
= WrDispatchInt
;
337 // Make the old thread ready
339 KxQueueReadyThread(OldThread
, Prcb
);
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
347 KiSwapContext(OldThread
, NewThread
);
353 KiInterruptHandler(IN PKTRAP_FRAME TrapFrame
,
357 ULONG InterruptCause
;//, InterruptMask;
359 PKTRAP_FRAME OldTrapFrame
;
360 ASSERT(TrapFrame
->Reserved
== 0xBADB0D00);
363 // Increment interrupt count
365 Pcr
= (PKIPCR
)KeGetPcr();
366 Pcr
->Prcb
.InterruptCount
++;
371 OldIrql
= KeGetCurrentIrql();
372 TrapFrame
->PreviousIrql
= OldIrql
;
375 // Get the interrupt source
377 InterruptCause
= HalGetInterruptSource();
378 //DPRINT1("[INT] (%x) @ %p %p\n", InterruptCause, TrapFrame->SvcLr, TrapFrame->Pc);
381 // Get the new IRQL and Interrupt Mask
383 /// FIXME: use a global table in ntoskrnl instead of HAL?
384 //Irql = Pcr->IrqlMask[InterruptCause];
385 //InterruptMask = Pcr->IrqlTable[Irql];
390 // Raise to the new IRQL
395 // The clock ISR wants the trap frame as a parameter
397 OldTrapFrame
= KeGetCurrentThread()->TrapFrame
;
398 KeGetCurrentThread()->TrapFrame
= TrapFrame
;
401 // Check if this interrupt is at DISPATCH or higher
403 if (Irql
> DISPATCH_LEVEL
)
406 // FIXME-TODO: Switch to interrupt stack
408 //DPRINT1("[ISR]\n");
413 // We know this is APC or DPC.
415 //DPRINT1("[DPC/APC]\n");
416 HalClearSoftwareInterrupt(Irql
);
420 // Call the registered interrupt routine
422 /// FIXME: this should probably go into a table in ntoskrnl
423 //Pcr->InterruptRoutine[Irql]();
425 ASSERT(KeGetCurrentThread()->TrapFrame
== TrapFrame
);
426 KeGetCurrentThread()->TrapFrame
= OldTrapFrame
;
427 // DPRINT1("[ISR RETURN]\n");
430 // Restore IRQL and interrupts
432 KeLowerIrql(OldIrql
);
437 KiPrefetchAbortHandler(IN PKTRAP_FRAME TrapFrame
)
439 PVOID Address
= (PVOID
)KeArmFaultAddressRegisterGet();
440 ASSERT(TrapFrame
->Reserved
== 0xBADB0D00);
441 ULONG Instruction
= *(PULONG
)TrapFrame
->Pc
;
442 ULONG DebugType
, Parameter0
;
443 EXCEPTION_RECORD ExceptionRecord
;
445 DPRINT1("[PREFETCH ABORT] (%x) @ %p/%p/%p\n",
446 KeArmInstructionFaultStatusRegisterGet(), Address
, TrapFrame
->Lr
, TrapFrame
->Pc
);
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.
454 //if (KeArmInstructionFaultStatusRegisterGet() == 2)
455 if (Instruction
& 0xE1200070) // BKPT
458 // Okay, we know this is a breakpoint, extract the index
460 DebugType
= Instruction
& 0xF;
461 if (DebugType
== BREAKPOINT_PRINT
)
466 Parameter0
= TrapFrame
->R0
;
467 TrapFrame
->Pc
+= sizeof(ULONG
);
472 // Standard INT3 (emulate x86 behavior)
474 Parameter0
= STATUS_SUCCESS
;
478 // Build the exception record
480 ExceptionRecord
.ExceptionCode
= STATUS_BREAKPOINT
;
481 ExceptionRecord
.ExceptionFlags
= 0;
482 ExceptionRecord
.ExceptionRecord
= NULL
;
483 ExceptionRecord
.ExceptionAddress
= (PVOID
)TrapFrame
->Pc
;
484 ExceptionRecord
.NumberParameters
= 3;
487 // Build the parameters
489 ExceptionRecord
.ExceptionInformation
[0] = Parameter0
;
490 ExceptionRecord
.ExceptionInformation
[1] = TrapFrame
->R1
;
491 ExceptionRecord
.ExceptionInformation
[2] = TrapFrame
->R2
;
494 // Dispatch the exception
496 KiDispatchException(&ExceptionRecord
,
499 KiGetPreviousMode(TrapFrame
),
505 return STATUS_SUCCESS
;
513 return STATUS_SUCCESS
;
517 KiDataAbortHandler(IN PKTRAP_FRAME TrapFrame
)
520 PVOID Address
= (PVOID
)KeArmFaultAddressRegisterGet();
521 ASSERT(TrapFrame
->Reserved
== 0xBADB0D00);
523 DPRINT1("[ABORT] (%x) @ %p/%p/%p\n",
524 KeArmFaultStatusRegisterGet(), Address
, TrapFrame
->Lr
, TrapFrame
->Pc
);
528 // Check if this is a page fault
530 if (KeArmFaultStatusRegisterGet() == 21 || KeArmFaultStatusRegisterGet() == 23)
532 Status
= MmAccessFault(FALSE
,
534 KiGetPreviousMode(TrapFrame
),
536 if (NT_SUCCESS(Status
)) return Status
;
544 return STATUS_SUCCESS
;
548 KiSoftwareInterruptHandler(IN PKTRAP_FRAME TrapFrame
)
551 KPROCESSOR_MODE PreviousMode
;
553 ASSERT(TrapFrame
->Reserved
== 0xBADB0D00);
555 DPRINT1("[SWI] @ %p/%p\n", TrapFrame
->Lr
, TrapFrame
->Pc
);
559 // Get the current thread
561 Thread
= KeGetCurrentThread();
564 // Isolate previous mode
566 PreviousMode
= KiGetPreviousMode(TrapFrame
);
569 // Save old previous mode
571 TrapFrame
->PreviousMode
= PreviousMode
;
572 TrapFrame
->TrapFrame
= (ULONG_PTR
)Thread
->TrapFrame
;
575 // Save previous mode and trap frame
577 Thread
->TrapFrame
= TrapFrame
;
578 Thread
->PreviousMode
= PreviousMode
;
583 Instruction
= *(PULONG
)(TrapFrame
->Pc
- sizeof(ULONG
));
586 // Call the service call dispatcher
588 KiSystemService(Thread
, TrapFrame
, Instruction
);
592 KiUndefinedExceptionHandler(IN PKTRAP_FRAME TrapFrame
)
594 ASSERT(TrapFrame
->Reserved
== 0xBADB0D00);
597 // This should never happen
599 DPRINT1("[UNDEF] @ %p/%p\n", TrapFrame
->Lr
, TrapFrame
->Pc
);
602 return STATUS_SUCCESS
;