1/* 2 * Copyright 2013, winocm. <winocm@icloud.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright notice, this 12 * list of conditions and the following disclaimer in the documentation and/or 13 * other materials provided with the distribution. 14 * 15 * If you are going to use this software in any form that does not involve 16 * releasing the source to this project or improving it, let me know beforehand. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29/* 30 * ARM processor abort handlers. 31 */ 32 33#include <arm/arch.h> 34#include <arm/asm_help.h> 35#include <assym.s> 36#include <mach/arm/asm.h> 37 38/** 39 * fleh_reset 40 * 41 * Just halt the processor right here with a forever loop. 42 */ 43EnterARM(fleh_reset) 44 b . 45 46/** 47 * fleh_undef 48 * 49 * Call the undefined handler. 50 */ 51EnterARM(fleh_undef) 52 /* Get current mode */ 53 mrs sp, spsr 54 tst sp, #0x20 55 56 /* Subtract lr based on cpsr bit */ 57 subeq lr, lr, #4 58 subne lr, lr, #2 59 60 /* See if it's in the kernel. */ 61 mrs sp, spsr 62 tst sp, #0xf 63 bne undef_from_kernel 64undef_from_user: 65 66 /* Usermode undefined, maybe it's a VFP instruction, HUH?! */ 67 LoadThreadRegister(sp) 68 ldr sp, [sp, TH_PCB_USS] 69 stmea sp, {r0-lr}^ 70 71 str lr, [sp, #0x3C] 72 mrs r2, spsr 73 str r2, [sp, #0x40] 74 mov r0, sp 75 76 /* Change to supervisor */ 77 cpsid i, #0x13 78 LoadThreadRegister(r1) 79 ldr sp, [r1, TH_PCB_ISS] 80 blx _sleh_undef 81 b _thread_exception_return 82 83undef_from_kernel: 84 /* Oops. */ 85 cpsid i, #0x13 86 87 sub sp, sp, #0x50 88 stmea sp, {r0-r12} 89 90 str lr, [sp, #0x38] 91 mov r0, sp 92 93 /* Undefined */ 94 cpsid i, #0x1b 95 mrs r2, spsr 96 str lr, [r0, #0x3C] 97 str r2, [r0, #0x40] 98 99 /* Supervisor */ 100 cpsid i, #0x13 101 102 add r3, sp, #0x50 103 str r3, [r0, #0x34] 104 105 blx _sleh_undef 106 107 /* NEVER COME BACK */ 108 b . 109 110/** 111 * fleh_swi 112 * 113 * Call the software interrupt handler. 114 */ 115EnterARM(fleh_swi) 116 cmn r12, #3 117 beq swi_trap_tb 118 119 /* Save current registers and spsr */ 120 LoadThreadRegister(sp) 121 ldr sp, [sp, TH_PCB_USS] 122 stmea sp, {r0-lr}^ 123 124 str lr, [sp, #0x3C] 125 mrs r2, spsr 126 str r2, [sp, #0x40] 127 128 mov r8, sp 129 130 LoadThreadRegister(sp) 131 ldr sp, [sp, TH_PCB_ISS] 132 133 mov r11, r12 134 cpsie i 135 /* Is it a trap? */ 136 cmp r11, #0x80000000 137 beq swi_trap 138 139swi_trap_ret: 140 rsbs r5, r11, #0 141 ble swi_unix 142 143swi_mach: 144 /* Load the mach function from the mach trap table and call it. */ 145 adr lr, swi_exit 146 mov r4, r5 147 cmp r5, #0x80 148 bge swi_mach_error 149 LOAD_ADDR(r1, mach_trap_table) 150 151 add r11, r5, r5, lsl#1 152 add r1, r1, r11, lsl#2 153 154 ldr r1, [r1, #4] 155 LOAD_ADDR(r2, kern_invalid) 156 157 mov r0, r8 158 teq r1, r2 159 beq swi_mach_error 160 bx r1 161swi_exit64: 162 str r1, [r8, #4] 163swi_exit: 164 /* Exit and go back. */ 165 str r0, [r8] 166 mov r0, r8 167 blx _mach_syscall_trace 168 bl _thread_exception_return 169 170 mov r0, #0x80 171 b irqvec_panic 172 173swi_mach_error: 174 /* There was an error processing the Mach system call, panic. */ 175 mov r0, #7 176 mov r1, r4 177 mov r2, #1 178 blx _doexception 179 180 mov r0, #0x81 181 b irqvec_panic 182 183swi_unix: 184 /* Unix syscall, call unix_syscall to dispatch */ 185 mov r0, r8 186 blx _unix_syscall 187 mov r0, #0x82 188 b irqvec_panic 189 190swi_trap: 191 /* Switch outcome based on our input value */ 192 cmp r3, #3 193 addls pc, pc, r3, lsl#2 194swi_trap_table: 195 b swi_trap_ret 196 b xxx_trap /* icache clean */ 197 b xxx_trap /* dcache clean */ 198 b thread_set_cthread_trap 199 b thread_get_cthread_trap 200 201swi_trap_tb: 202 /* Fast return */ 203 movs pc, lr 204 205xxx_trap: 206 /* Just return. */ 207 bl _thread_exception_return 208 209thread_set_cthread_trap: 210 /* Set the current cthread value. */ 211 blx _thread_set_cthread_self 212 bl _thread_exception_return 213 mov r0, #0x82 214 b irqvec_panic 215 216thread_get_cthread_trap: 217 /* Get the current cthread value and return to user. */ 218 blx _thread_get_cthread_self 219 LoadThreadRegister(r1) 220 ldr r1, [r1, TH_PCB_USS] 221 str r0, [r1] 222 bl _thread_exception_return 223 mov r0, #0x83 224 b irqvec_panic 225 226/** 227 * fleh_prefabt 228 * 229 * Save registers and call the prefetch handler 230 */ 231EnterARM(fleh_prefabt) 232 sub lr, lr, #4 233 234 mrs sp, cpsr 235 bic sp, sp, #0x100 236 msr cpsr_c, sp 237 238 mrs sp, spsr 239 tst sp, #0xf 240 bne prefetch_abort_in_kernel 241 242prefetch_abort_in_user: 243 /* Oh well, not now. */ 244 LoadThreadRegister(sp) 245 ldr sp, [sp, TH_PCB_USS] 246 stmea sp, {r0-lr}^ 247 248 /* Save lr */ 249 str lr, [sp, #0x3C] 250 251 /* Save SPSR */ 252 mrs r0, spsr 253 str r0, [sp, #0x40] 254 255 /* Save IFSR */ 256 mrc p15, 0, r0, c5, c0, 1 257 str r0, [sp, #0x44] 258 259 /* Save IFAR */ 260 mrc p15, 0, r0, c6, c0, 2 261 str r0, [sp, #0x48] 262 263 /* Supervisor mode */ 264 cpsid i, #0x13 265 266 mov r0, sp 267 268 LoadThreadRegister(r1) 269 ldr sp, [r1, TH_PCB_ISS] 270 271 mov r1, #3 /* Prefetch Abort */ 272 blx _sleh_abort 273 b _thread_exception_return 274 275prefetch_abort_in_kernel: 276 /* Supervisor mode */ 277 msr cpsr_c, #0x93 278 279 /* Make space on the stack for the registers. */ 280 sub sp, sp, #0x50 281 stmea sp, {r0-r12} 282 283 /* Save the remaining registers. */ 284 str lr, [sp, #0x38] 285 mov r12, sp 286 287 /* Abort mode */ 288 msr cpsr_c, #0x97 289 290 /* Save lr */ 291 str lr, [r12, #0x3C] 292 293 /* Save SPSR */ 294 mrs r4, spsr 295 str r4, [r12, #0x40] 296 297 /* Save IFSR */ 298 mrc p15, 0, r5, c5, c0, 1 299 str r5, [r12, #0x44] 300 301 /* Save IFAR */ 302 mrc p15, 0, r6, c6, c0, 2 303 str r6, [r12, #0x48] 304 305 /* Supervisor mode */ 306 msr cpsr_c, #0x93 307 308 add r12, r12, #0x50 309 str r12, [sp, #0x34] 310 sub r12, r12, #0x50 311 312 mov r0, sp 313 mov r1, #3 /* Prefetch Abort */ 314 bl _sleh_abort 315 b restore_kernel_context 316 317/** 318 * fleh_dataabt 319 * 320 * Handle data aborts. 321 */ 322EnterARM(fleh_dataabt) 323 /* Make sure the data abort was in the kernel. */ 324 sub lr, lr, #8 325 mrs sp, spsr 326 tst sp, #0xf 327 bne data_abort_crash_in_kernel 328 329data_abort_crash_in_usermode: 330 /* Oh well, not now. */ 331 LoadThreadRegister(sp) 332 ldr sp, [sp, TH_PCB_USS] 333 stmea sp, {r0-lr}^ 334 335 /* Save lr */ 336 str lr, [sp, #0x3C] 337 338 /* Save SPSR */ 339 mrs r0, spsr 340 str r0, [sp, #0x40] 341 342 /* Save DFSR */ 343 mrc p15, 0, r0, c5, c0, 0 344 str r0, [sp, #0x44] 345 346 /* Save DFAR */ 347 mrc p15, 0, r0, c6, c0, 0 348 str r0, [sp, #0x48] 349 350 mov r0, sp 351 352 /* Supervisor mode */ 353 cpsid i, #0x13 354 355 LoadThreadRegister(r1) 356 ldr sp, [r1, TH_PCB_ISS] 357 358 mov r1, #4 /* Data Abort */ 359 blx _sleh_abort 360 b _thread_exception_return 361 362data_abort_crash_in_kernel: 363 /* Supervisor mode */ 364 cpsid i, #0x13 365 366 /* Make space on the stack for the registers. */ 367 sub sp, sp, #0x50 368 stmea sp, {r0-r12} 369 370 /* Save the remaining registers. */ 371 str lr, [sp, #0x38] 372 mov r12, sp 373 374 /* Abort mode */ 375 cpsid i, #0x17 376 377 /* Save lr */ 378 str lr, [r12, #0x3C] 379 380 /* Save SPSR */ 381 mrs r4, spsr 382 str r4, [r12, #0x40] 383 384 /* Supervisor mode */ 385 cpsid i, #0x13 386 387 /* Save DFSR */ 388 mrc p15, 0, r5, c5, c0, 0 389 str r5, [sp, #0x44] 390 391 /* Save DFAR */ 392 mrc p15, 0, r6, c6, c0, 0 393 str r6, [sp, #0x48] 394 395 /* Go to abort handler. */ 396 add r12, r12, #0x50 397 str r12, [sp, #0x34] 398 sub r12, r12, #0x50 399 400 mov r0, sp 401 mov r1, #4 /* Data Abort */ 402 bl _sleh_abort 403 b return_to_kernel 404 405/** 406 * fleh_dataexc 407 * 408 * "Reserved" by ARM standards. 409 */ 410EnterARM(fleh_dataexc) 411 b . 412 413/** 414 * fleh_irq 415 * 416 * Dispatch timer and IRQ events. 417 */ 418EnterARM(fleh_irq) 419 /* Check to see if the IRQ ocurred in user or in kernel mode */ 420 sub lr, lr, #4 421 mrs sp, spsr 422 tst sp, #0xF 423 bne irqhandler_from_kernel 424 425irqhandler_from_user: 426 LoadThreadRegister(sp) 427 ldr sp, [sp, TH_PCB_USS] 428 stmea sp, {r0-lr}^ 429 str lr, [sp, #0x3C] 430 mrs r0, spsr 431 str r0, [sp, #0x40] 432 mov r5, sp 433 LOAD_ADDR(sp, irqstack_top) 434 b irq_join 435 436irqhandler_from_kernel: 437 /* Set up IRQ stack */ 438 LOAD_ADDR(sp, irqstack_top) 439 440 /* Now save the registers */ 441 sub sp, sp, #0x50 442 stmea sp, {r0-r12} 443 444 /* Set t-bit */ 445 orr lr, lr, #1 446 447 /* Save lr */ 448 str lr, [sp, #0x3C] 449 450 /* Save SPSR */ 451 mrs r4, spsr 452 str r4, [sp, #0x40] 453 454 /* Change modes */ 455 mov r5, sp 456 and r4, r4, #0x1F 457 orr r4, r4, #0xC0 458 msr cpsr_c, r4 459 460 /* Save sp */ 461 str sp, [r5, #0x34] 462 str lr, [r5, #0x38] 463 464 /* Disable interrupts */ 465 cpsid i, #0x12 466 467irq_join: 468 mov r0, r5 469 blx _irq_handler 470 mrs r4, spsr 471 tst r4, #0xf 472 beq irq_restore_user 473 474irq_restore_kernel: 475 /* Restore kernel registers and threads */ 476 ldr r4, [sp, #0x40] 477 tst r4, #0x80 478 movne r0, #1 479 bne irqvec_panic 480 481 /* Threads. */ 482 LoadThreadRegister(r12) 483 ldr r0, [r12, MACHINE_THREAD_PREEMPT_COUNT] 484 485 /* Preempt thread if necessary */ 486 bne restore_kernel_context 487 488irq_preempt: 489 /* Prreempt thread */ 490 ldmfd sp, {r0-r12} 491 mrs lr, spsr 492 493 /* Switch modes */ 494 and lr, lr, #0x1F 495 orr lr, lr, #0xC0 496 msr cpsr_c, lr 497 498 /* Save registers */ 499 sub sp, sp, #0x50 500 stmea sp, {r0-r12} 501 mov r5, sp 502 503 /* Switch modes, disable interrupts */ 504 cpsid i, #0x12 505 506 /* Set r0-r3 */ 507 ldr r0, [sp, #0x34] 508 str r0, [r5, #0x34] 509 510 ldr r0, [sp, #0x38] 511 str r0, [r5, #0x38] 512 513 ldr r0, [sp, #0x3c] 514 str r0, [r5, #0x3c] 515 516 ldr r0, [sp, #0x40] 517 str r0, [r5, #0x40] 518 519 mov sp, r5 520 521 /* Return to the kernel */ 522 b return_to_kernel 523irq_restore_user: 524 cpsid i, #0x13 525 LoadThreadRegister(sp) 526 ldr sp, [sp, TH_PCB_ISS] 527 b _thread_exception_return 528 529return_to_kernel: 530 mov r5, sp 531 532 /* Switch modes */ 533 cpsid i, #0x13 534 535 /* Check preemption */ 536 ldr r4, [sp, #0x40] 537 tst r4, #0x80 538 bne restart_kernel_execution 539 540 /* Check preemption count */ 541 LoadThreadRegister(r12) 542 ldr r0, [r12, MACHINE_THREAD_PREEMPT_COUNT] 543 cmp r0, #0 544 bne restart_kernel_execution 545 546preempt_kernel: 547 mov r0, #7 548 mov r1, #0 549 blx _ast_taken 550 551restart_kernel_execution: 552 ldr r6, [r5, #0x40] 553 554 /* Switch modes */ 555 and r6, r6, #0x1F 556 orr r6, r6, #0xC0 557 msr cpsr_c, r6 558 ldr lr, [r5, #0x38] 559 ldr sp, [r5, #0x34] 560 cpsid i, #0x17 561 mov sp, r5 562 563 /* The following routine should also be shared with data abort. */ 564restore_kernel_context: 565 ldmfd sp, {r0-r12} 566 add sp, sp, #0x3C 567 rfefd sp! 568 b . 569 570/* 571 * vector panic helper. 572 */ 573irqvec_panic: 574 LOAD_ADDR(sp, irqstack_top) 575 mov r1, r0 576 adr r0, vecPanicString 577 blx _panic 578 b . 579 580vecPanicString: 581 .asciz "fleh_vectors: exception in exception vectors, 0x%08x" 582 583LOAD_ADDR_GEN_DEF(irqstack_top) 584LOAD_ADDR_GEN_DEF(kern_invalid) 585LOAD_ADDR_GEN_DEF(mach_trap_table) 586 587