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