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