| /* |
| * Copyright (c) 2008-2014 Travis Geiselbrecht |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files |
| * (the "Software"), to deal in the Software without restriction, |
| * including without limitation the rights to use, copy, modify, merge, |
| * publish, distribute, sublicense, and/or sell copies of the Software, |
| * and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| #include <debug.h> |
| #include <bits.h> |
| #include <arch/arm.h> |
| #include <kernel/thread.h> |
| #include <platform.h> |
| #include <string.h> |
| #include <lib/backtrace/backtrace.h> |
| #include <lib/trusty/trusty_app.h> |
| |
| struct fault_handler_table_entry { |
| uint32_t pc; |
| uint32_t fault_handler; |
| }; |
| |
| extern struct fault_handler_table_entry __fault_handler_table_start[]; |
| extern struct fault_handler_table_entry __fault_handler_table_end[]; |
| |
| static bool check_fault_handler_table(struct arm_fault_frame *frame) |
| { |
| struct fault_handler_table_entry *fault_handler; |
| for (fault_handler = __fault_handler_table_start; |
| fault_handler < __fault_handler_table_end; |
| fault_handler++) { |
| if (fault_handler->pc == frame->pc) { |
| frame->pc = fault_handler->fault_handler; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static void dump_mode_regs(uint32_t spsr, uint32_t svc_r13, uint32_t svc_r14) |
| { |
| struct arm_mode_regs regs; |
| arm_save_mode_regs(®s); |
| |
| dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_USR) ? '*' : ' ', "usr", regs.usr_r13, regs.usr_r14); |
| dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_FIQ) ? '*' : ' ', "fiq", regs.fiq_r13, regs.fiq_r14); |
| dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_IRQ) ? '*' : ' ', "irq", regs.irq_r13, regs.irq_r14); |
| dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", 'a', "svc", regs.svc_r13, regs.svc_r14); |
| dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_SVC) ? '*' : ' ', "svc", svc_r13, svc_r14); |
| dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_UND) ? '*' : ' ', "und", regs.und_r13, regs.und_r14); |
| dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_SYS) ? '*' : ' ', "sys", regs.sys_r13, regs.sys_r14); |
| |
| // dump the bottom of the current stack |
| addr_t stack; |
| switch (spsr & CPSR_MODE_MASK) { |
| case CPSR_MODE_FIQ: |
| stack = regs.fiq_r13; |
| break; |
| case CPSR_MODE_IRQ: |
| stack = regs.irq_r13; |
| break; |
| case CPSR_MODE_SVC: |
| stack = svc_r13; |
| break; |
| case CPSR_MODE_UND: |
| stack = regs.und_r13; |
| break; |
| case CPSR_MODE_SYS: |
| stack = regs.sys_r13; |
| break; |
| default: |
| stack = 0; |
| } |
| |
| if (stack != 0) { |
| dprintf(CRITICAL, "bottom of stack at 0x%08x:\n", (unsigned int)stack); |
| hexdump((void *)stack, 128); |
| } |
| } |
| |
| static void dump_thread_info(void) |
| { |
| struct thread *current_thread = get_current_thread(); |
| |
| dprintf(CRITICAL, "current_thread %p, name %s\n", |
| current_thread, current_thread ? current_thread->name : ""); |
| |
| struct trusty_app *app = current_trusty_app(); |
| if (app) { |
| dprintf(CRITICAL, "load bias %lx\n", app->load_bias); |
| } |
| |
| } |
| |
| static void dump_fault_frame(struct arm_fault_frame *frame) |
| { |
| dump_thread_info(); |
| dprintf(CRITICAL, "r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n", frame->r[0], frame->r[1], frame->r[2], frame->r[3]); |
| dprintf(CRITICAL, "r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", frame->r[4], frame->r[5], frame->r[6], frame->r[7]); |
| dprintf(CRITICAL, "r8 0x%08x r9 0x%08x r10 0x%08x r11 0x%08x\n", frame->r[8], frame->r[9], frame->r[10], frame->r[11]); |
| dprintf(CRITICAL, "r12 0x%08x usp 0x%08x ulr 0x%08x fp 0x%08x\n", frame->r[12], frame->usp, frame->ulr, frame->fp); |
| dprintf(CRITICAL, "pc 0x%08x spsr 0x%08x\n", frame->pc, frame->spsr); |
| |
| dump_mode_regs(frame->spsr, (uintptr_t)(frame + 1), frame->lr); |
| } |
| |
| static void dump_iframe(struct arm_iframe *frame) |
| { |
| dump_thread_info(); |
| dprintf(CRITICAL, "r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n", frame->r0, frame->r1, frame->r2, frame->r3); |
| dprintf(CRITICAL, "r12 0x%08x usp 0x%08x ulr 0x%08x fp 0x%08x\n", frame->r12, frame->usp, frame->ulr, frame->fp); |
| dprintf(CRITICAL, "pc 0x%08x spsr 0x%08x\n", frame->pc, frame->spsr); |
| |
| dump_mode_regs(frame->spsr, (uintptr_t)(frame + 1), frame->lr); |
| } |
| |
| static void exception_die(struct arm_fault_frame *frame, const char *msg) |
| { |
| dprintf(CRITICAL, "%s", msg); |
| dump_fault_frame(frame); |
| dump_backtrace(); |
| |
| platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC); |
| for (;;); |
| } |
| |
| static void exception_die_iframe(struct arm_iframe *frame, const char *msg) |
| { |
| dprintf(CRITICAL, "%s", msg); |
| dump_iframe(frame); |
| dump_backtrace(); |
| |
| platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC); |
| for (;;); |
| } |
| |
| __WEAK void arm_syscall_handler(struct arm_fault_frame *frame) |
| { |
| exception_die(frame, "unhandled syscall, halting\n"); |
| } |
| |
| void arm_undefined_handler(struct arm_iframe *frame) |
| { |
| #if ARM_WITH_VFP |
| /* look at the undefined instruction, figure out if it's something we can handle */ |
| bool in_thumb = frame->spsr & (1<<5); |
| if (in_thumb) { |
| frame->pc -= 2; |
| } else { |
| frame->pc -= 4; |
| } |
| |
| // pc may be unaligned, need to construct uint32_t byte by byte |
| uint32_t opcode; |
| memcpy(&opcode, (uint8_t *)frame->pc, sizeof(opcode)); |
| |
| if (in_thumb) { |
| /* look for a 32bit thumb instruction */ |
| if (opcode & 0x0000e800) { |
| /* swap the 16bit words */ |
| opcode = (opcode >> 16) | (opcode << 16); |
| } |
| |
| if (((opcode & 0xec000e00) == 0xec000a00) || // vfp |
| ((opcode & 0xef000000) == 0xef000000) || // advanced simd data processing |
| ((opcode & 0xff100000) == 0xf9000000)) { // VLD |
| |
| //dprintf(CRITICAL, "vfp/neon thumb instruction 0x%08x at 0x%x\n", opcode, frame->pc); |
| arm_fpu_undefined_instruction(frame); |
| return; |
| } |
| } else { |
| /* look for arm vfp/neon coprocessor instructions */ |
| if (((opcode & 0x0c000e00) == 0x0c000a00) || // vfp |
| ((opcode & 0xfe000000) == 0xf2000000) || // advanced simd data processing |
| ((opcode & 0xff100000) == 0xf4000000)) { // VLD |
| //dprintf(CRITICAL, "vfp/neon arm instruction 0x%08x at 0x%x\n", opcode, frame->pc); |
| arm_fpu_undefined_instruction(frame); |
| return; |
| } |
| } |
| #endif |
| |
| exception_die_iframe(frame, "undefined abort, halting\n"); |
| } |
| |
| void arm_data_abort_handler(struct arm_fault_frame *frame) |
| { |
| uint32_t fsr = arm_read_dfsr(); |
| uint32_t far = arm_read_dfar(); |
| |
| uint32_t fault_status = (BIT(fsr, 10) ? (1<<4) : 0) | BITS(fsr, 3, 0); |
| |
| if (check_fault_handler_table(frame)) { |
| return; |
| } |
| |
| dprintf(CRITICAL, "\n\ncpu %u data abort, ", arch_curr_cpu_num()); |
| bool write = !!BIT(fsr, 11); |
| |
| /* decode the fault status (from table B3-23) */ |
| switch (fault_status) { |
| case 0b00001: // alignment fault |
| dprintf(CRITICAL, "alignment fault on %s\n", write ? "write" : "read"); |
| break; |
| case 0b00101: |
| case 0b00111: // translation fault |
| dprintf(CRITICAL, "translation fault on %s\n", write ? "write" : "read"); |
| break; |
| case 0b00011: |
| case 0b00110: // access flag fault |
| dprintf(CRITICAL, "access flag fault on %s\n", write ? "write" : "read"); |
| break; |
| case 0b01001: |
| case 0b01011: // domain fault |
| dprintf(CRITICAL, "domain fault, domain %lu\n", BITS_SHIFT(fsr, 7, 4)); |
| break; |
| case 0b01101: |
| case 0b01111: // permission fault |
| dprintf(CRITICAL, "permission fault on %s\n", write ? "write" : "read"); |
| break; |
| case 0b00010: // debug event |
| dprintf(CRITICAL, "debug event\n"); |
| break; |
| case 0b01000: // synchronous external abort |
| dprintf(CRITICAL, "synchronous external abort on %s\n", write ? "write" : "read"); |
| break; |
| case 0b10110: // asynchronous external abort |
| dprintf(CRITICAL, "asynchronous external abort on %s\n", write ? "write" : "read"); |
| break; |
| case 0b10000: // TLB conflict event |
| case 0b11001: // synchronous parity error on memory access |
| case 0b00100: // fault on instruction cache maintenance |
| case 0b01100: // synchronous external abort on translation table walk |
| case 0b01110: // " |
| case 0b11100: // synchronous parity error on translation table walk |
| case 0b11110: // " |
| case 0b11000: // asynchronous parity error on memory access |
| default: |
| dprintf(CRITICAL, "unhandled fault\n"); |
| ; |
| } |
| |
| dprintf(CRITICAL, "DFAR 0x%x (fault address)\n", far); |
| dprintf(CRITICAL, "DFSR 0x%x (fault status register)\n", fsr); |
| |
| exception_die(frame, "halting\n"); |
| } |
| |
| void arm_prefetch_abort_handler(struct arm_fault_frame *frame) |
| { |
| uint32_t fsr = arm_read_ifsr(); |
| uint32_t far = arm_read_ifar(); |
| |
| uint32_t fault_status = (BIT(fsr, 10) ? (1<<4) : 0) | BITS(fsr, 3, 0); |
| |
| if (check_fault_handler_table(frame)) { |
| return; |
| } |
| |
| dprintf(CRITICAL, "\n\ncpu %u prefetch abort, ", arch_curr_cpu_num()); |
| |
| /* decode the fault status (from table B3-23) */ |
| switch (fault_status) { |
| case 0b00001: // alignment fault |
| dprintf(CRITICAL, "alignment fault\n"); |
| break; |
| case 0b00101: |
| case 0b00111: // translation fault |
| dprintf(CRITICAL, "translation fault\n"); |
| break; |
| case 0b00011: |
| case 0b00110: // access flag fault |
| dprintf(CRITICAL, "access flag fault\n"); |
| break; |
| case 0b01001: |
| case 0b01011: // domain fault |
| dprintf(CRITICAL, "domain fault, domain %lu\n", BITS_SHIFT(fsr, 7, 4)); |
| break; |
| case 0b01101: |
| case 0b01111: // permission fault |
| dprintf(CRITICAL, "permission fault\n"); |
| break; |
| case 0b00010: // debug event |
| dprintf(CRITICAL, "debug event\n"); |
| break; |
| case 0b01000: // synchronous external abort |
| dprintf(CRITICAL, "synchronous external abort\n"); |
| break; |
| case 0b10110: // asynchronous external abort |
| dprintf(CRITICAL, "asynchronous external abort\n"); |
| break; |
| case 0b10000: // TLB conflict event |
| case 0b11001: // synchronous parity error on memory access |
| case 0b00100: // fault on instruction cache maintenance |
| case 0b01100: // synchronous external abort on translation table walk |
| case 0b01110: // " |
| case 0b11100: // synchronous parity error on translation table walk |
| case 0b11110: // " |
| case 0b11000: // asynchronous parity error on memory access |
| default: |
| dprintf(CRITICAL, "unhandled fault\n"); |
| ; |
| } |
| |
| dprintf(CRITICAL, "IFAR 0x%x (fault address)\n", far); |
| dprintf(CRITICAL, "IFSR 0x%x (fault status register)\n", fsr); |
| |
| exception_die(frame, "halting\n"); |
| } |