Add abort() implementation

Also reduces the IRAM footprint of the fatal exception handler, as only
the prelude (which disables interrupts & enables the flash mapping) is
in IRAM now.

Closes #54, relevant to #133.
This commit is contained in:
Angus Gratton 2016-05-07 18:21:13 +10:00
parent cf350efd8a
commit 36886412e6
9 changed files with 106 additions and 33 deletions

View file

@ -40,8 +40,6 @@ void user_init(void);
#define RTCMEM_BACKUP_PHY_VER 31 #define RTCMEM_BACKUP_PHY_VER 31
#define RTCMEM_SYSTEM_PP_VER 62 #define RTCMEM_SYSTEM_PP_VER 62
#define halt() while (1) {}
extern uint32_t _bss_start; extern uint32_t _bss_start;
extern uint32_t _bss_end; extern uint32_t _bss_end;
@ -100,11 +98,11 @@ static void IRAM get_otp_mac_address(uint8_t *buf) {
if (!(otp_flags & 0x8000)) { if (!(otp_flags & 0x8000)) {
//FIXME: do we really need this check? //FIXME: do we really need this check?
printf("Firmware ONLY supports ESP8266!!!\n"); printf("Firmware ONLY supports ESP8266!!!\n");
halt(); abort();
} }
if (otp_id0 == 0 && otp_id1 == 0) { if (otp_id0 == 0 && otp_id1 == 0) {
printf("empty otp\n"); printf("empty otp\n");
halt(); abort();
} }
if (otp_flags & 0x1000) { if (otp_flags & 0x1000) {
// If bit 12 is set, it indicates that the vendor portion of the MAC // If bit 12 is set, it indicates that the vendor portion of the MAC
@ -258,7 +256,7 @@ static void zero_bss(void) {
static void init_networking(uint8_t *phy_info, uint8_t *mac_addr) { static void init_networking(uint8_t *phy_info, uint8_t *mac_addr) {
if (sdk_register_chipv6_phy(phy_info)) { if (sdk_register_chipv6_phy(phy_info)) {
printf("FATAL: sdk_register_chipv6_phy failed"); printf("FATAL: sdk_register_chipv6_phy failed");
halt(); abort();
} }
uart_set_baud(0, 74906); uart_set_baud(0, 74906);
uart_set_baud(1, 74906); uart_set_baud(1, 74906);

View file

@ -1,5 +1,5 @@
/* Code for dumping status/debug output/etc, including fatal /* Code for dumping status/debug output/etc, including fatal
* exception handling. * exception handling & abort implementation.
* *
* Part of esp-open-rtos * Part of esp-open-rtos
* *
@ -20,7 +20,49 @@
#include "espressif/esp_common.h" #include "espressif/esp_common.h"
#include "sdk_internal.h" #include "sdk_internal.h"
void dump_excinfo(void) { /* Forward declarations */
static void IRAM fatal_handler_prelude(void);
/* Inner parts of crash handlers marked noinline to ensure they don't inline into IRAM. */
static void __attribute__((noinline)) __attribute__((noreturn)) fatal_exception_handler_inner(uint32_t *sp);
static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_inner(uint32_t *caller, uint32_t *sp);
/* fatal_exception_handler called from any unhandled user exception
*
* (similar to a hard fault on other processor architectures)
*
* This function is run from IRAM, but the majority of the handler
* runs from flash after fatal_handler_prelude ensures it is mapped
* safely.
*/
void IRAM __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp) {
fatal_handler_prelude();
fatal_exception_handler_inner(sp);
}
/* Abort implementation
*
* Replaces the weak-linked abort implementation provided by newlib libc.
*
* Disable interrupts, enable flash mapping, dump stack & caller
* address, restart.
*
* This function is run from IRAM, but the majority of the abort
* handler runs from flash after fatal_handler_prelude ensures it is
* mapped safely.
*
*/
void IRAM abort(void) {
uint32_t *sp, *caller;
RETADDR(caller);
/* abort() caller is one instruction before our return address */
caller = (uint32_t *)((intptr_t)caller - 3);
SP(sp);
fatal_handler_prelude();
abort_handler_inner(caller, sp);
}
/* Dump exception information from special function registers */
static void dump_excinfo(void) {
uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1; uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1;
uint32_t excinfo[8]; uint32_t excinfo[8];
@ -50,9 +92,13 @@ void dump_excinfo(void) {
sdk_system_rtc_mem_write(0, excinfo, 32); sdk_system_rtc_mem_write(0, excinfo, 32);
} }
/* There's a lot of smart stuff we could do while dumping stack /* dump stack memory (frames above sp) to stdout
but for now we just dump a likely looking section of stack
memory There's a lot of smart stuff we could do while dumping stack
but for now we just dump what looks like our stack region.
Probably dumps more memory than it needs to, the first instance of
0xa5a5a5a5 probably constitutes the end of our stack.
*/ */
void dump_stack(uint32_t *sp) { void dump_stack(uint32_t *sp) {
printf("\nStack: SP=%p\n", sp); printf("\nStack: SP=%p\n", sp);
@ -68,10 +114,10 @@ void dump_stack(uint32_t *sp) {
} }
} }
/* Dump exception status registers as stored above 'sp' /* Dump normal registers that were stored above 'sp'
by the interrupt handler preamble by the exception handler preamble
*/ */
void dump_exception_registers(uint32_t *sp) { void dump_registers_in_exception_handler(uint32_t *sp) {
uint32_t excsave1; uint32_t excsave1;
uint32_t *saved = sp - (0x50 / sizeof(uint32_t)); uint32_t *saved = sp - (0x50 / sizeof(uint32_t));
printf("Registers:\n"); printf("Registers:\n");
@ -84,7 +130,11 @@ void dump_exception_registers(uint32_t *sp) {
printf("SAR %08x\n", saved[0x13]); printf("SAR %08x\n", saved[0x13]);
} }
void IRAM fatal_exception_handler(uint32_t *sp) {
/* Prelude ensures exceptions/NMI off and flash is mapped, allowing
calls to non-IRAM functions.
*/
static void IRAM fatal_handler_prelude(void) {
if (!sdk_NMIIrqIsOn) { if (!sdk_NMIIrqIsOn) {
vPortEnterCritical(); vPortEnterCritical();
do { do {
@ -93,9 +143,15 @@ void IRAM fatal_exception_handler(uint32_t *sp) {
} }
Cache_Read_Disable(); Cache_Read_Disable();
Cache_Read_Enable(0, 0, 1); Cache_Read_Enable(0, 0, 1);
}
/* Main part of fatal exception handler, is run from flash to save
some IRAM.
*/
static void fatal_exception_handler_inner(uint32_t *sp) {
dump_excinfo(); dump_excinfo();
if (sp) { if (sp) {
dump_exception_registers(sp); dump_registers_in_exception_handler(sp);
dump_stack(sp); dump_stack(sp);
} }
uart_flush_txfifo(0); uart_flush_txfifo(0);
@ -103,3 +159,15 @@ void IRAM fatal_exception_handler(uint32_t *sp) {
sdk_system_restart_in_nmi(); sdk_system_restart_in_nmi();
while(1) {} while(1) {}
} }
/* Main part of abort handler, can be run from flash to save some
IRAM.
*/
static void abort_handler_inner(uint32_t *caller, uint32_t *sp) {
printf("abort() invoked at %p.\n", caller);
dump_stack(sp);
uart_flush_txfifo(0);
uart_flush_txfifo(1);
sdk_system_restart_in_nmi();
while(1) {}
}

View file

@ -17,13 +17,6 @@ void dump_stack(uint32_t *sp);
Probably not useful to be called in other contexts. Probably not useful to be called in other contexts.
*/ */
void fatal_exception_handler(uint32_t *sp); void __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp);
/* Dump the current exception register state.
Probably mostly useful when called from fatal exception handler.
*/
void dump_excinfo(void);
#endif #endif

View file

@ -14,6 +14,20 @@
// GCC macros for reading, writing, and exchanging Xtensa processor special // GCC macros for reading, writing, and exchanging Xtensa processor special
// registers: // registers:
/* Read stack pointer to variable.
*
* Note that the compiler will push a stack frame (minimum 16 bytes)
* in the prelude of a C function that calls any other functions.
*/
#define SP(var) asm volatile ("mov %0, a1" : "=r" (var));
/* Read the function return address to a variable.
*
* Depends on the containing function being simple enough that a0 is
* being used as a working register.
*/
#define RETADDR(var) asm volatile ("mov %0, a0" : "=r" (var))
#define RSR(var, reg) asm volatile ("rsr %0, " #reg : "=r" (var)); #define RSR(var, reg) asm volatile ("rsr %0, " #reg : "=r" (var));
#define WSR(var, reg) asm volatile ("wsr %0, " #reg : : "r" (var)); #define WSR(var, reg) asm volatile ("wsr %0, " #reg : : "r" (var));
#define XSR(var, reg) asm volatile ("xsr %0, " #reg : "+r" (var)); #define XSR(var, reg) asm volatile ("xsr %0, " #reg : "+r" (var));

View file

@ -25,7 +25,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
if (heap_end + incr > stack_ptr) if (heap_end + incr > stack_ptr)
{ {
_write (1, "_sbrk: Heap collided with stack\n", 32); _write (1, "_sbrk: Heap collided with stack\n", 32);
while(1) {} abort();
} }
*/ */
heap_end += incr; heap_end += incr;

View file

@ -305,7 +305,7 @@ static void test_system_interaction()
} }
uint32_t ticks = xTaskGetTickCount() - start; uint32_t ticks = xTaskGetTickCount() - start;
printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_RATE_MS); printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_RATE_MS);
while(1) {} abort();
} }
/* The following "sanity tests" are designed to try to execute every code path /* The following "sanity tests" are designed to try to execute every code path

View file

@ -115,7 +115,7 @@ void http_get_task(void *pvParameters)
strlen(pers))) != 0) strlen(pers))) != 0)
{ {
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret); printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
while(1) {} /* todo: replace with abort() */ abort();
} }
printf(" ok\n"); printf(" ok\n");
@ -129,7 +129,7 @@ void http_get_task(void *pvParameters)
if(ret < 0) if(ret < 0)
{ {
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
while(1) {} /* todo: replace with abort() */ abort();
} }
printf(" ok (%d skipped)\n", ret); printf(" ok (%d skipped)\n", ret);
@ -138,7 +138,7 @@ void http_get_task(void *pvParameters)
if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0) if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
{ {
printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
while(1) {} /* todo: replace with abort() */ abort();
} }
/* /*

View file

@ -85,7 +85,7 @@ void tls_server_task(void *pvParameters)
strlen(pers))) != 0) strlen(pers))) != 0)
{ {
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret); printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
while(1) {} /* todo: replace with abort() */ abort();
} }
printf(" ok\n"); printf(" ok\n");
@ -99,7 +99,7 @@ void tls_server_task(void *pvParameters)
if(ret < 0) if(ret < 0)
{ {
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
while(1) {} /* todo: replace with abort() */ abort();
} }
printf(" ok (%d skipped)\n", ret); printf(" ok (%d skipped)\n", ret);
@ -109,7 +109,7 @@ void tls_server_task(void *pvParameters)
if(ret != 0) if(ret != 0)
{ {
printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret); printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret);
while(1) { } /*todo: replace with abort() */ abort();
} }
printf(" ok\n"); printf(" ok\n");
@ -134,7 +134,7 @@ void tls_server_task(void *pvParameters)
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 ) if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
{ {
printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret ); printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret );
while(1) { } abort();
} }
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);

View file

@ -94,7 +94,7 @@ typedef int sys_prot_t;
_Pragma("GCC diagnostic pop") \ _Pragma("GCC diagnostic pop") \
} while(0) } while(0)
#define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \ #define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \
x, __LINE__, __FILE__); while(1) {} } while(0) x, __LINE__, __FILE__); abort(); } while(0)
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ #define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \ printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \