316 lines
12 KiB
C
316 lines
12 KiB
C
/*
|
|
* 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 <types.h>
|
|
#include <bits.h>
|
|
#include <param.h>
|
|
#include <ctype.h>
|
|
#include <debug.h>
|
|
|
|
#include <armv7-r/register.h>
|
|
#include <armv7-r/exceptions.h>
|
|
#include <armv7-r/arm.h>
|
|
|
|
#if CONFIG_OS_FREERTOS
|
|
#include <FreeRTOS.h>
|
|
#include <task.h>
|
|
#endif
|
|
|
|
static void dump_stack(const void *ptr, size_t len)
|
|
{
|
|
addr_t address = (addr_t)ptr;
|
|
size_t count;
|
|
|
|
for (count = 0 ; count < len; count += 16) {
|
|
union {
|
|
uint32_t buf[4];
|
|
uint8_t cbuf[16];
|
|
} u;
|
|
size_t s = ROUNDUP(MIN(len - count, 16), 4);
|
|
size_t i;
|
|
|
|
ssdk_printf(SSDK_EMERG, "0x%08x: ", address);
|
|
for (i = 0; i < s / 4; i++) {
|
|
u.buf[i] = ((const uint32_t *)address)[i];
|
|
ssdk_printf(SSDK_EMERG, "%08x ", u.buf[i]);
|
|
}
|
|
for (; i < 4; i++) {
|
|
ssdk_printf(SSDK_EMERG, " ");
|
|
}
|
|
ssdk_printf(SSDK_EMERG, "|");
|
|
|
|
for (i=0; i < 16; i++) {
|
|
unsigned char c = u.cbuf[i];
|
|
if (i < s && isprint(c)) {
|
|
ssdk_printf(SSDK_EMERG, "%c", c);
|
|
} else {
|
|
ssdk_printf(SSDK_EMERG, ".");
|
|
}
|
|
}
|
|
ssdk_printf(SSDK_EMERG, "|\r\n");
|
|
address += 16;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
ssdk_printf(SSDK_EMERG, "%c%s r13 0x%08x r14 0x%08x\r\n", ((spsr & MODE_MASK) == MODE_USR) ? '*' : ' ', "usr", regs.usr_r13, regs.usr_r14);
|
|
ssdk_printf(SSDK_EMERG, "%c%s r13 0x%08x r14 0x%08x\r\n", ((spsr & MODE_MASK) == MODE_FIQ) ? '*' : ' ', "fiq", regs.fiq_r13, regs.fiq_r14);
|
|
ssdk_printf(SSDK_EMERG, "%c%s r13 0x%08x r14 0x%08x\r\n", ((spsr & MODE_MASK) == MODE_IRQ) ? '*' : ' ', "irq", regs.irq_r13, regs.irq_r14);
|
|
ssdk_printf(SSDK_EMERG, "%c%s r13 0x%08x r14 0x%08x\r\n", 'a', "svc", regs.svc_r13, regs.svc_r14);
|
|
ssdk_printf(SSDK_EMERG, "%c%s r13 0x%08x r14 0x%08x\r\n", ((spsr & MODE_MASK) == MODE_SVC) ? '*' : ' ', "svc", svc_r13, svc_r14);
|
|
ssdk_printf(SSDK_EMERG, "%c%s r13 0x%08x r14 0x%08x\r\n", ((spsr & MODE_MASK) == MODE_UND) ? '*' : ' ', "und", regs.und_r13, regs.und_r14);
|
|
ssdk_printf(SSDK_EMERG, "%c%s r13 0x%08x r14 0x%08x\r\n", ((spsr & MODE_MASK) == MODE_SYS) ? '*' : ' ', "sys", regs.sys_r13, regs.sys_r14);
|
|
|
|
// dump the bottom of the current stack
|
|
addr_t stack;
|
|
switch (spsr & MODE_MASK) {
|
|
case MODE_FIQ:
|
|
stack = regs.fiq_r13;
|
|
break;
|
|
case MODE_IRQ:
|
|
stack = regs.irq_r13;
|
|
break;
|
|
case MODE_SVC:
|
|
stack = svc_r13;
|
|
break;
|
|
case MODE_UND:
|
|
stack = regs.und_r13;
|
|
break;
|
|
case MODE_SYS:
|
|
stack = regs.sys_r13;
|
|
break;
|
|
default:
|
|
stack = 0;
|
|
}
|
|
|
|
if (stack != 0) {
|
|
ssdk_printf(SSDK_EMERG, "bottom of stack at 0x%08x:\r\n", (unsigned int)stack);
|
|
dump_stack((void *)stack, 128);
|
|
}
|
|
}
|
|
|
|
static void dump_fault_frame(struct arm_fault_frame *frame)
|
|
{
|
|
#if CONFIG_OS_FREERTOS
|
|
TaskHandle_t current_thread = xTaskGetCurrentTaskHandle();
|
|
|
|
ssdk_printf(SSDK_EMERG, "current_thread %p, name %s\r\n",
|
|
current_thread, current_thread ? pcTaskGetName(current_thread) : "");
|
|
#endif
|
|
|
|
ssdk_printf(SSDK_EMERG, "r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\r\n", frame->r[0], frame->r[1], frame->r[2], frame->r[3]);
|
|
ssdk_printf(SSDK_EMERG, "r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\r\n", frame->r[4], frame->r[5], frame->r[6], frame->r[7]);
|
|
ssdk_printf(SSDK_EMERG, "r8 0x%08x r9 0x%08x r10 0x%08x r11 0x%08x\r\n", frame->r[8], frame->r[9], frame->r[10], frame->r[11]);
|
|
ssdk_printf(SSDK_EMERG, "r12 0x%08x usp 0x%08x ulr 0x%08x pc 0x%08x\r\n", frame->r[12], frame->usp, frame->ulr, frame->pc);
|
|
ssdk_printf(SSDK_EMERG, "spsr 0x%08x\r\n", frame->spsr);
|
|
|
|
dump_mode_regs(frame->spsr, (uintptr_t)(frame + 1), frame->lr);
|
|
}
|
|
|
|
static void dump_iframe(struct arm_iframe *frame)
|
|
{
|
|
ssdk_printf(SSDK_EMERG, "r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\r\n", frame->r0, frame->r1, frame->r2, frame->r3);
|
|
ssdk_printf(SSDK_EMERG, "r12 0x%08x usp 0x%08x ulr 0x%08x pc 0x%08x\r\n", frame->r12, frame->usp, frame->ulr, frame->pc);
|
|
ssdk_printf(SSDK_EMERG, "spsr 0x%08x\r\n", 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)
|
|
{
|
|
ssdk_printf(SSDK_EMERG, "%s", msg);
|
|
dump_fault_frame(frame);
|
|
|
|
for (;;);
|
|
}
|
|
|
|
static void exception_die_iframe(struct arm_iframe *frame, const char *msg)
|
|
{
|
|
ssdk_printf(SSDK_EMERG, "%s", msg);
|
|
dump_iframe(frame);
|
|
|
|
for (;;);
|
|
}
|
|
|
|
void arm_undefined_handler(struct arm_iframe *frame)
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
__UNUSED uint32_t opcode = *(uint32_t *)frame->pc;
|
|
ssdk_printf(SSDK_EMERG, "undefined opcode 0x%x\r\n", opcode);
|
|
|
|
#if CONFIG_ARCH_WITH_FPU
|
|
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
|
|
|
|
ssdk_printf(SSDK_EMERG, "vfp/neon thumb instruction 0x%08x at 0x%x\r\n", opcode, frame->pc);
|
|
}
|
|
} else {
|
|
/* look for arm vfp/neon coprocessor instructions */
|
|
if (((opcode & 0x0c000e00) == 0x0c000a00) || // vfp
|
|
((opcode & 0xfe000000) == 0xf2000000) || // advanced simd data processing
|
|
((opcode & 0xff100000) == 0xf4000000)) { // VLD
|
|
ssdk_printf(SSDK_EMERG, "vfp/neon arm instruction 0x%08x at 0x%x\r\n", opcode, frame->pc);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
exception_die_iframe(frame, "undefined abort, halting\r\n");
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
ssdk_printf(SSDK_EMERG, "\r\n\ncpu data abort, ");
|
|
__UNUSED bool write = !!BIT(fsr, 11);
|
|
|
|
/* decode the fault status (from table B3-23) */
|
|
switch (fault_status) {
|
|
case 0x01: // alignment fault
|
|
ssdk_printf(SSDK_EMERG, "alignment fault on %s\r\n", write ? "write" : "read");
|
|
break;
|
|
case 0x05:
|
|
case 0x07: // translation fault
|
|
ssdk_printf(SSDK_EMERG, "translation fault on %s\r\n", write ? "write" : "read");
|
|
break;
|
|
case 0x03:
|
|
case 0x06: // access flag fault
|
|
ssdk_printf(SSDK_EMERG, "access flag fault on %s\r\n", write ? "write" : "read");
|
|
break;
|
|
case 0x09:
|
|
case 0x0B: // domain fault
|
|
ssdk_printf(SSDK_EMERG, "domain fault, domain %lu\r\n", BITS_SHIFT(fsr, 7, 4));
|
|
break;
|
|
case 0x0D:
|
|
case 0x0F: // permission fault
|
|
ssdk_printf(SSDK_EMERG, "permission fault on %s\r\n", write ? "write" : "read");
|
|
break;
|
|
case 0x02: // debug event
|
|
ssdk_printf(SSDK_EMERG, "debug event\r\n");
|
|
break;
|
|
case 0x08: // synchronous external abort
|
|
ssdk_printf(SSDK_EMERG, "synchronous external abort on %s\r\n", write ? "write" : "read");
|
|
break;
|
|
case 0x16: // asynchronous external abort
|
|
ssdk_printf(SSDK_EMERG, "asynchronous external abort on %s\r\n", write ? "write" : "read");
|
|
break;
|
|
case 0x10: // TLB conflict event
|
|
case 0x19: // synchronous parity error on memory access
|
|
case 0x04: // fault on instruction cache maintenance
|
|
case 0x0C: // synchronous external abort on translation table walk
|
|
case 0x0E: // "
|
|
case 0x1C: // synchronous parity error on translation table walk
|
|
case 0x1E: // "
|
|
case 0x18: // asynchronous parity error on memory access
|
|
default:
|
|
ssdk_printf(SSDK_EMERG, "unhandled fault\r\n");
|
|
break;
|
|
}
|
|
|
|
ssdk_printf(SSDK_EMERG, "DFAR 0x%x (fault address)\r\n", far);
|
|
ssdk_printf(SSDK_EMERG, "DFSR 0x%x (fault status register)\r\n", fsr);
|
|
|
|
exception_die(frame, "halting\r\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);
|
|
|
|
ssdk_printf(SSDK_EMERG, "\r\n\ncpu prefetch abort, ");
|
|
|
|
/* decode the fault status (from table B3-23) */
|
|
switch (fault_status) {
|
|
case 0x01: // alignment fault
|
|
ssdk_printf(SSDK_EMERG, "alignment fault\r\n");
|
|
break;
|
|
case 0x05:
|
|
case 0x07: // translation fault
|
|
ssdk_printf(SSDK_EMERG, "translation fault\r\n");
|
|
break;
|
|
case 0x03:
|
|
case 0x06: // access flag fault
|
|
ssdk_printf(SSDK_EMERG, "access flag fault\r\n");
|
|
break;
|
|
case 0x09:
|
|
case 0x0B: // domain fault
|
|
ssdk_printf(SSDK_EMERG, "domain fault, domain %lu\r\n", BITS_SHIFT(fsr, 7, 4));
|
|
break;
|
|
case 0x0D:
|
|
case 0x0F: // permission fault
|
|
ssdk_printf(SSDK_EMERG, "permission fault\r\n");
|
|
break;
|
|
case 0x02: // debug event
|
|
ssdk_printf(SSDK_EMERG, "debug event\r\n");
|
|
break;
|
|
case 0x08: // synchronous external abort
|
|
ssdk_printf(SSDK_EMERG, "synchronous external abort\r\n");
|
|
break;
|
|
case 0x16: // asynchronous external abort
|
|
ssdk_printf(SSDK_EMERG, "asynchronous external abort\r\n");
|
|
break;
|
|
case 0x10: // TLB conflict event
|
|
case 0x19: // synchronous parity error on memory access
|
|
case 0x04: // fault on instruction cache maintenance
|
|
case 0x0C: // synchronous external abort on translation table walk
|
|
case 0x0E: // "
|
|
case 0x1C: // synchronous parity error on translation table walk
|
|
case 0x1E: // "
|
|
case 0x18: // asynchronous parity error on memory access
|
|
default:
|
|
ssdk_printf(SSDK_EMERG, "unhandled fault\r\n");
|
|
break;
|
|
}
|
|
|
|
ssdk_printf(SSDK_EMERG, "IFAR 0x%x (fault address)\r\n", far);
|
|
ssdk_printf(SSDK_EMERG, "IFSR 0x%x (fault status register)\r\n", fsr);
|
|
|
|
exception_die(frame, "halting\r\n");
|
|
}
|