Fatal exceptions: Cleanly deal with exceptions that occur inside fatal_exception_handler_inner()

In case of heap corruption or some other major problem, dumping details
in the exception handler can cause a crash loop - so fail out if we seem
to be going in circles.
This commit is contained in:
Angus Gratton 2016-05-17 09:08:53 +10:00
parent 981c87899b
commit 1e9296f60c
2 changed files with 33 additions and 18 deletions

View file

@ -24,10 +24,14 @@
/* Forward declarations */ /* Forward declarations */
static void IRAM fatal_handler_prelude(void); static void IRAM fatal_handler_prelude(void);
/* Inner parts of crash handlers marked noinline to ensure they don't inline into IRAM. */ /* Inner parts of crash handlers */
static void __attribute__((noinline)) __attribute__((noreturn)) fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack); typedef void __attribute__((noreturn)) (*fatal_exception_handler_fn)(uint32_t *sp, bool registers_saved_on_stack);
static void __attribute__((noreturn)) standard_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack);
static void __attribute__((noreturn)) second_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack);
static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_inner(uint32_t *caller, uint32_t *sp); static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_inner(uint32_t *caller, uint32_t *sp);
static IRAM_DATA fatal_exception_handler_fn fatal_exception_handler_inner = standard_fatal_exception_handler_inner;
/* fatal_exception_handler called from any unhandled user exception /* fatal_exception_handler called from any unhandled user exception
* *
* (similar to a hard fault on other processor architectures) * (similar to a hard fault on other processor architectures)
@ -38,7 +42,8 @@ static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_in
*/ */
void IRAM __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack) { void IRAM __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack) {
fatal_handler_prelude(); fatal_handler_prelude();
fatal_exception_handler_inner(sp, registers_saved_on_stack); fatal_exception_handler_fn inner_fn = fatal_exception_handler_inner;
inner_fn(sp, registers_saved_on_stack);
} }
/* Abort implementation /* Abort implementation
@ -132,6 +137,12 @@ void dump_registers_in_exception_handler(uint32_t *sp) {
printf("SAR %08x\n", saved[0x13]); printf("SAR %08x\n", saved[0x13]);
} }
static void __attribute__((noreturn)) post_crash_reset(void) {
uart_flush_txfifo(0);
uart_flush_txfifo(1);
sdk_system_restart_in_nmi();
while(1) {}
}
/* Prelude ensures exceptions/NMI off and flash is mapped, allowing /* Prelude ensures exceptions/NMI off and flash is mapped, allowing
calls to non-IRAM functions. calls to non-IRAM functions.
@ -150,7 +161,10 @@ static void IRAM fatal_handler_prelude(void) {
/* Main part of fatal exception handler, is run from flash to save /* Main part of fatal exception handler, is run from flash to save
some IRAM. some IRAM.
*/ */
static void fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) { static void standard_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) {
/* Replace the fatal exception handler 'inner' function so we
don't end up in a crash loop if this handler crashes. */
fatal_exception_handler_inner = second_fatal_exception_handler_inner;
dump_excinfo(); dump_excinfo();
if (sp) { if (sp) {
if (registers_saved_on_stack) { if (registers_saved_on_stack) {
@ -159,10 +173,16 @@ static void fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_
dump_stack(sp); dump_stack(sp);
} }
dump_heapinfo(); dump_heapinfo();
uart_flush_txfifo(0); post_crash_reset();
uart_flush_txfifo(1); }
sdk_system_restart_in_nmi();
while(1) {} /* This is the exception handler that gets called if a crash occurs inside the standard handler,
so we don't end up in a crash loop. It doesn't rely on contents of stack or heap.
*/
static void second_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) {
dump_excinfo();
printf("Second fatal exception occured inside fatal exception handler. Can't continue.\n");
post_crash_reset();
} }
void dump_heapinfo(void) void dump_heapinfo(void)
@ -207,8 +227,5 @@ static void abort_handler_inner(uint32_t *caller, uint32_t *sp) {
printf("abort() invoked at %p.\n", caller); printf("abort() invoked at %p.\n", caller);
dump_stack(sp); dump_stack(sp);
dump_heapinfo(); dump_heapinfo();
uart_flush_txfifo(0); post_crash_reset();
uart_flush_txfifo(1);
sdk_system_restart_in_nmi();
while(1) {}
} }

View file

@ -65,18 +65,16 @@
*/ */
#define IRAM __attribute__((section(".iram1.text"))) #define IRAM __attribute__((section(".iram1.text")))
/* Use this macro to place read-only data into Instruction RAM (IRAM) /* Use this macro to place data into Instruction RAM (IRAM)
instead of loaded into rodata which resides in DRAM. instead of loaded into rodata which resides in DRAM.
(IRAM can also be written to as necessary.)
This may be useful to free up data RAM. However all data read from This may be useful to free up data RAM. However all data read from
the instruction space must be 32-bit aligned word reads the instruction space must be 32-bit aligned word reads
(non-aligned reads will use an interrupt routine to "fix" them and (non-aligned reads will use an interrupt routine to "fix" them and
still work, but are very slow.. still work, but are very slow..
*/ */
#ifdef __cplusplus #define IRAM_DATA __attribute__((section(".iram1.rodata")))
#define IRAM_DATA __attribute__((section(".iram1.rodata")))
#else
#define IRAM_DATA __attribute__((section(".iram1.rodata"))) const
#endif
#endif #endif