/*************************************************************************** * sam7fc - Abort Handler with Register/Stackdump * * * * Copyright (C) 01/2008 by Olaf Rempel * * razzor@kopf-tisch.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; version 2 of the License * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "AT91SAM7S256.h" #include "board.h" static void dbgu_putchar(char c) { while (!(*AT91C_DBGU_CSR & AT91C_US_TXEMPTY)); *AT91C_DBGU_THR = c; if (c == '\n') { while (!(*AT91C_DBGU_CSR & AT91C_US_TXEMPTY)); *AT91C_DBGU_THR = '\r'; } } static void dbgu_puts(const char *p) { while (*p != '\0') dbgu_putchar(*p++); } static void dbgu_hexvar(const char *name, uint32_t var) { dbgu_puts(name); char buf[11]; buf[0] = '0'; buf[1] = 'x'; uint32_t i; for (i = 9; i > 1; i--) { buf[i] = (var & 0x0F); buf[i] += (buf[i] < 0xa) ? '0' : ('a' - 0x0a); var >>= 4; } buf[10] = '\0'; dbgu_puts(buf); } /* ARM7TDMI 32bit Undefined Address Instruction Fetch Abort (ASR:0x02020a01 AASR:0x80000008) --- Registerdump (cpsr:0xa0000013 - SVC): r0:0x00000002 r1:0x0000000c r2:0x00200038 r3:0x80000000 r4:0x0020fffc r5:0x00101788 r6:0x00000180 r7:0x41000000 r8:0x00008400 r9:0x00100004 sl:0x03000294 fp:0x0020fe84 ip:0x0020fe10 sp:0x0020fe70 lr:0x00102174 pc:0x80000004 --- Stackdump: 0x0020fe70: 0x0020fe7c 0x00101648 0x001015a8 0x0020feaf 0x0020fe80: 0x0020fec0 0x0020fe90 0x00101780 0x00101620 0x0020fe90: 0x00000002 0x00000000 0x00000030 0x00000000 0x0020fea0: 0x001029a8 0x307830b0 0x33383730 0x00303337 */ void at91_abt_handler(uint32_t cpsr, uint32_t *registers) { /* enable Debug Port with 115200 Baud (+0.16%) */ AT91S_DBGU *dbgu = AT91C_BASE_DBGU; dbgu->DBGU_BRGR = BAUD_TO_DIV(115200); dbgu->DBGU_MR = AT91C_US_PAR_NONE | AT91C_US_CHMODE_NORMAL; dbgu->DBGU_CR = AT91C_US_RXEN | AT91C_US_TXEN | AT91C_US_RSTSTA; /* switch pins to UART */ *AT91C_PIOA_PDR = AT91C_PA9_DRXD | AT91C_PA10_DTXD; AT91_REG asr = *AT91C_MC_ASR; if (asr & AT91C_MC_MST0) dbgu_puts("PDC "); else if (asr & AT91C_MC_MST1) dbgu_puts("ARM7TDMI "); switch (asr & AT91C_MC_ABTSZ) { case AT91C_MC_ABTSZ_BYTE: dbgu_puts("8bit "); break; case AT91C_MC_ABTSZ_HWORD: dbgu_puts("16bit "); break; case AT91C_MC_ABTSZ_WORD: dbgu_puts("32bit "); break; } if (asr & AT91C_MC_UNDADD) dbgu_puts("Undefined Address "); else if (asr & AT91C_MC_MISADD) dbgu_puts("Misaliged Address "); switch (asr & AT91C_MC_ABTTYP) { case AT91C_MC_ABTTYP_DATAR: dbgu_puts("Data Read "); break; case AT91C_MC_ABTTYP_DATAW: dbgu_puts("Data Write "); break; case AT91C_MC_ABTTYP_FETCH: dbgu_puts("Prefetch "); break; } dbgu_hexvar("Abort\n(ASR:", asr); dbgu_hexvar(" AASR:", *AT91C_MC_AASR); dbgu_puts(")\n"); dbgu_hexvar("--- Registerdump (cpsr:", cpsr); dbgu_puts(" - "); switch (cpsr & 0x1F) { case 0x10: dbgu_puts("USR"); break; case 0x11: dbgu_puts("FIQ"); break; case 0x12: dbgu_puts("IRQ"); break; case 0x13: dbgu_puts("SVC"); break; case 0x17: dbgu_puts("ABT"); break; case 0x1B: dbgu_puts("UND"); break; case 0x1F: dbgu_puts("SYS"); break; } dbgu_hexvar("):\n r0:", registers[3]); dbgu_hexvar(" r1:", registers[4]); dbgu_hexvar(" r2:", registers[5]); dbgu_hexvar(" r3:", registers[6]); dbgu_hexvar("\n r4:", registers[7]); dbgu_hexvar(" r5:", registers[8]); dbgu_hexvar(" r6:", registers[9]); dbgu_hexvar(" r7:", registers[10]); dbgu_hexvar("\n r8:", registers[11]); dbgu_hexvar(" r9:", registers[12]); dbgu_hexvar(" sl:", registers[13]); dbgu_hexvar(" fp:", registers[14]); dbgu_hexvar("\n ip:", registers[15]); dbgu_hexvar(" sp:", registers[0]); dbgu_hexvar(" lr:", registers[1]); dbgu_hexvar(" pc:", registers[2]); dbgu_puts("\n--- Stackdump:"); uint32_t *val = (uint32_t *)(registers[0] & ~0x0F); for ( ; val < (uint32_t *)(registers[0] & ~0x0F) +16; val++) { if (!((uint32_t)val & 0x0F)) { dbgu_hexvar("\n", (uint32_t)val); dbgu_putchar(':'); } dbgu_hexvar(" ", *val); } dbgu_putchar('\n'); } __attribute__((naked)) void ABT_Handler(void) { asm volatile ( ".equ ARM_MODE_ABT, 0x17 \n\t" ".equ I_BIT, 0x80 \n\t" ".equ F_BIT, 0x40 \n\t" /* disable interrupts (F_BIT not set on entry) */ "msr CPSR_c, #ARM_MODE_ABT | I_BIT | F_BIT \n\t" /* store all registers */ "stmfd sp!, { r0-r12 } \n\t" /* saved cpsr (from aborted mode) */ "mrs r0, SPSR \n\t" /* address of abort (pc) */ "mov r3, lr \n\t" /* enter previous mode and get lr(r14), sp(r13) */ "orr r1, r0, #I_BIT | F_BIT \n\t" "msr CPSR_c, r1 \n\t" "mov r1, sp \n\t" "mov r2, lr \n\t" /* return to abort mode */ "msr CPSR_c, #ARM_MODE_ABT | I_BIT | F_BIT \n\t" /* store remaining registers (r1-r3 == r13-r15) */ "stmfd sp!, { r1-r3 } \n\t" "mov r1, sp \n\t" /* execute C Handler (cpsr, *registers) */ "ldr r5, =at91_abt_handler \n\t" "mov lr, pc \n\t" "bx r5 \n\t" "b . \n\t" ); }