/* * Copyright 2013, winocm. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * If you are going to use this software in any form that does not involve * releasing the source to this project or improving it, let me know beforehand. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * ARM processor abort handlers. */ #include #include #include #include /** * fleh_reset * * Just halt the processor right here with a forever loop. */ EnterARM(fleh_reset) b . /** * fleh_undef * * Call the undefined handler. */ EnterARM(fleh_undef) /* Get current mode */ mrs sp, spsr tst sp, #0x20 /* Subtract lr based on cpsr bit */ subeq lr, lr, #4 subne lr, lr, #2 /* See if it's in the kernel. */ mrs sp, spsr tst sp, #0xf bne undef_from_kernel undef_from_user: /* Usermode undefined, maybe it's a VFP instruction, HUH?! */ LoadThreadRegister(sp) ldr sp, [sp, TH_PCB_USS] stmea sp, {r0-lr}^ str lr, [sp, #0x3C] mrs r2, spsr str r2, [sp, #0x40] mov r0, sp /* Change to supervisor */ cpsid i, #0x13 LoadThreadRegister(r1) ldr sp, [r1, TH_PCB_ISS] blx _sleh_undef b _thread_exception_return undef_from_kernel: /* Oops. */ cpsid i, #0x13 sub sp, sp, #0x50 stmea sp, {r0-r12} str lr, [sp, #0x38] mov r0, sp /* Undefined */ cpsid i, #0x1b mrs r2, spsr str lr, [r0, #0x3C] str r2, [r0, #0x40] /* Supervisor */ cpsid i, #0x13 add r3, sp, #0x50 str r3, [r0, #0x34] blx _sleh_undef /* NEVER COME BACK */ b . /** * fleh_swi * * Call the software interrupt handler. */ EnterARM(fleh_swi) cmn r12, #3 beq swi_trap_tb /* Save current registers and spsr */ LoadThreadRegister(sp) ldr sp, [sp, TH_PCB_USS] stmea sp, {r0-lr}^ str lr, [sp, #0x3C] mrs r2, spsr str r2, [sp, #0x40] mov r8, sp LoadThreadRegister(sp) ldr sp, [sp, TH_PCB_ISS] mov r11, r12 cpsie i /* Is it a trap? */ cmp r11, #0x80000000 beq swi_trap swi_trap_ret: rsbs r5, r11, #0 ble swi_unix swi_mach: /* Load the mach function from the mach trap table and call it. */ adr lr, swi_exit mov r4, r5 cmp r5, #0x80 bge swi_mach_error LOAD_ADDR(r1, mach_trap_table) add r11, r5, r5, lsl#1 add r1, r1, r11, lsl#2 ldr r1, [r1, #4] LOAD_ADDR(r2, kern_invalid) mov r0, r8 teq r1, r2 beq swi_mach_error bx r1 swi_exit64: str r1, [r8, #4] swi_exit: /* Exit and go back. */ str r0, [r8] mov r0, r8 blx _mach_syscall_trace bl _thread_exception_return mov r0, #0x80 b irqvec_panic swi_mach_error: /* There was an error processing the Mach system call, panic. */ mov r0, #7 mov r1, r4 mov r2, #1 blx _doexception mov r0, #0x81 b irqvec_panic swi_unix: /* Unix syscall, call unix_syscall to dispatch */ mov r0, r8 blx _unix_syscall mov r0, #0x82 b irqvec_panic swi_trap: /* Switch outcome based on our input value */ cmp r3, #3 addls pc, pc, r3, lsl#2 swi_trap_table: b swi_trap_ret b xxx_trap /* icache clean */ b xxx_trap /* dcache clean */ b thread_set_cthread_trap b thread_get_cthread_trap swi_trap_tb: /* Fast return */ movs pc, lr xxx_trap: /* Just return. */ bl _thread_exception_return thread_set_cthread_trap: /* Set the current cthread value. */ blx _thread_set_cthread_self bl _thread_exception_return mov r0, #0x82 b irqvec_panic thread_get_cthread_trap: /* Get the current cthread value and return to user. */ blx _thread_get_cthread_self LoadThreadRegister(r1) ldr r1, [r1, TH_PCB_USS] str r0, [r1] bl _thread_exception_return mov r0, #0x83 b irqvec_panic /** * fleh_prefabt * * Save registers and call the prefetch handler */ EnterARM(fleh_prefabt) sub lr, lr, #4 mrs sp, cpsr bic sp, sp, #0x100 msr cpsr_c, sp mrs sp, spsr tst sp, #0xf bne prefetch_abort_in_kernel prefetch_abort_in_user: /* Oh well, not now. */ LoadThreadRegister(sp) ldr sp, [sp, TH_PCB_USS] stmea sp, {r0-lr}^ /* Save lr */ str lr, [sp, #0x3C] /* Save SPSR */ mrs r0, spsr str r0, [sp, #0x40] /* Save IFSR */ mrc p15, 0, r0, c5, c0, 1 str r0, [sp, #0x44] /* Save IFAR */ mrc p15, 0, r0, c6, c0, 2 str r0, [sp, #0x48] /* Supervisor mode */ cpsid i, #0x13 mov r0, sp LoadThreadRegister(r1) ldr sp, [r1, TH_PCB_ISS] mov r1, #3 /* Prefetch Abort */ blx _sleh_abort b _thread_exception_return prefetch_abort_in_kernel: /* Supervisor mode */ msr cpsr_c, #0x93 /* Make space on the stack for the registers. */ sub sp, sp, #0x50 stmea sp, {r0-r12} /* Save the remaining registers. */ str lr, [sp, #0x38] mov r12, sp /* Abort mode */ msr cpsr_c, #0x97 /* Save lr */ str lr, [r12, #0x3C] /* Save SPSR */ mrs r4, spsr str r4, [r12, #0x40] /* Save IFSR */ mrc p15, 0, r5, c5, c0, 1 str r5, [r12, #0x44] /* Save IFAR */ mrc p15, 0, r6, c6, c0, 2 str r6, [r12, #0x48] /* Supervisor mode */ msr cpsr_c, #0x93 add r12, r12, #0x50 str r12, [sp, #0x34] sub r12, r12, #0x50 mov r0, sp mov r1, #3 /* Prefetch Abort */ bl _sleh_abort b restore_kernel_context /** * fleh_dataabt * * Handle data aborts. */ EnterARM(fleh_dataabt) /* Make sure the data abort was in the kernel. */ sub lr, lr, #8 mrs sp, spsr tst sp, #0xf bne data_abort_crash_in_kernel data_abort_crash_in_usermode: /* Oh well, not now. */ LoadThreadRegister(sp) ldr sp, [sp, TH_PCB_USS] stmea sp, {r0-lr}^ /* Save lr */ str lr, [sp, #0x3C] /* Save SPSR */ mrs r0, spsr str r0, [sp, #0x40] /* Save DFSR */ mrc p15, 0, r0, c5, c0, 0 str r0, [sp, #0x44] /* Save DFAR */ mrc p15, 0, r0, c6, c0, 0 str r0, [sp, #0x48] mov r0, sp /* Supervisor mode */ cpsid i, #0x13 LoadThreadRegister(r1) ldr sp, [r1, TH_PCB_ISS] mov r1, #4 /* Data Abort */ blx _sleh_abort b _thread_exception_return data_abort_crash_in_kernel: /* Supervisor mode */ cpsid i, #0x13 /* Make space on the stack for the registers. */ sub sp, sp, #0x50 stmea sp, {r0-r12} /* Save the remaining registers. */ str lr, [sp, #0x38] mov r12, sp /* Abort mode */ cpsid i, #0x17 /* Save lr */ str lr, [r12, #0x3C] /* Save SPSR */ mrs r4, spsr str r4, [r12, #0x40] /* Supervisor mode */ cpsid i, #0x13 /* Save DFSR */ mrc p15, 0, r5, c5, c0, 0 str r5, [sp, #0x44] /* Save DFAR */ mrc p15, 0, r6, c6, c0, 0 str r6, [sp, #0x48] /* Go to abort handler. */ add r12, r12, #0x50 str r12, [sp, #0x34] sub r12, r12, #0x50 mov r0, sp mov r1, #4 /* Data Abort */ bl _sleh_abort b return_to_kernel /** * fleh_dataexc * * "Reserved" by ARM standards. */ EnterARM(fleh_dataexc) b . /** * fleh_irq * * Dispatch timer and IRQ events. */ EnterARM(fleh_irq) /* Check to see if the IRQ ocurred in user or in kernel mode */ sub lr, lr, #4 mrs sp, spsr tst sp, #0xF bne irqhandler_from_kernel irqhandler_from_user: LoadThreadRegister(sp) ldr sp, [sp, TH_PCB_USS] stmea sp, {r0-lr}^ str lr, [sp, #0x3C] mrs r0, spsr str r0, [sp, #0x40] mov r5, sp LOAD_ADDR(sp, irqstack_top) b irq_join irqhandler_from_kernel: /* Set up IRQ stack */ LOAD_ADDR(sp, irqstack_top) /* Now save the registers */ sub sp, sp, #0x50 stmea sp, {r0-r12} /* Set t-bit */ orr lr, lr, #1 /* Save lr */ str lr, [sp, #0x3C] /* Save SPSR */ mrs r4, spsr str r4, [sp, #0x40] /* Change modes */ mov r5, sp and r4, r4, #0x1F orr r4, r4, #0xC0 msr cpsr_c, r4 /* Save sp */ str sp, [r5, #0x34] str lr, [r5, #0x38] /* Disable interrupts */ cpsid i, #0x12 irq_join: mov r0, r5 blx _irq_handler mrs r4, spsr tst r4, #0xf beq irq_restore_user irq_restore_kernel: /* Restore kernel registers and threads */ ldr r4, [sp, #0x40] tst r4, #0x80 movne r0, #1 bne irqvec_panic /* Threads. */ LoadThreadRegister(r12) ldr r0, [r12, MACHINE_THREAD_PREEMPT_COUNT] /* Preempt thread if necessary */ bne restore_kernel_context irq_preempt: /* Prreempt thread */ ldmfd sp, {r0-r12} mrs lr, spsr /* Switch modes */ and lr, lr, #0x1F orr lr, lr, #0xC0 msr cpsr_c, lr /* Save registers */ sub sp, sp, #0x50 stmea sp, {r0-r12} mov r5, sp /* Switch modes, disable interrupts */ cpsid i, #0x12 /* Set r0-r3 */ ldr r0, [sp, #0x34] str r0, [r5, #0x34] ldr r0, [sp, #0x38] str r0, [r5, #0x38] ldr r0, [sp, #0x3c] str r0, [r5, #0x3c] ldr r0, [sp, #0x40] str r0, [r5, #0x40] mov sp, r5 /* Return to the kernel */ b return_to_kernel irq_restore_user: cpsid i, #0x13 LoadThreadRegister(sp) ldr sp, [sp, TH_PCB_ISS] b _thread_exception_return return_to_kernel: mov r5, sp /* Switch modes */ cpsid i, #0x13 /* Check preemption */ ldr r4, [sp, #0x40] tst r4, #0x80 bne restart_kernel_execution /* Check preemption count */ LoadThreadRegister(r12) ldr r0, [r12, MACHINE_THREAD_PREEMPT_COUNT] cmp r0, #0 bne restart_kernel_execution preempt_kernel: mov r0, #7 mov r1, #0 blx _ast_taken restart_kernel_execution: ldr r6, [r5, #0x40] /* Switch modes */ and r6, r6, #0x1F orr r6, r6, #0xC0 msr cpsr_c, r6 ldr lr, [r5, #0x38] ldr sp, [r5, #0x34] cpsid i, #0x17 mov sp, r5 /* The following routine should also be shared with data abort. */ restore_kernel_context: ldmfd sp, {r0-r12} add sp, sp, #0x3C rfefd sp! b . /* * vector panic helper. */ irqvec_panic: LOAD_ADDR(sp, irqstack_top) mov r1, r0 adr r0, vecPanicString blx _panic b . vecPanicString: .asciz "fleh_vectors: exception in exception vectors, 0x%08x" LOAD_ADDR_GEN_DEF(irqstack_top) LOAD_ADDR_GEN_DEF(kern_invalid) LOAD_ADDR_GEN_DEF(mach_trap_table)