Skip to content

Commit

Permalink
esp8266: Handle exceptions in the exception_handler().
Browse files Browse the repository at this point in the history
When an exception occurs while handling an exception with
exception_handler() this function will be called again, increasing the
exception stack until the whole stack and the memory before it is
exhausted and a DoubleException occurs when trying to use an address in
the `0x3ff7ffxx` range, far away from any clue to the initial exception.

A common cause for an exception in exception_handler() is to try to
execute a function from IROM while the cache is disabled. This normally
results in an illegal instruction exception. A few of the functions
called from the exception_handler() are in IROM, triggering this
situation if an exception occurs while the flash cache is disabled.

This patch breaks to the debugger immediately if exception_handler is
called twice, so the location of the function causing the double
exception can be obtained in frame->pc, and the frame in that context
can be inspected too.

Second, to address the issue of printing the error message in the
terminal this patch enables the flash cache before calling ets_printf().
While exception_handler() is in IRAM, its read-only literal strings are
not, so calling ets_printf() will not work.

With this patch and without the fix in RIOT-OS#17080, reproducing the
conditions in issue RIOT-OS#16281 allows us to quickly identify the PC of the
function that was causing the issue (`vTaskEnterCritical`) on the first
place.
  • Loading branch information
iosabi committed Oct 31, 2021
1 parent 9256970 commit 17ed80a
Showing 1 changed file with 40 additions and 0 deletions.
40 changes: 40 additions & 0 deletions cpu/esp_common/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
#include "xtensa/corebits.h"
#include "xtensa/xtensa_api.h"

#ifdef MCU_ESP8266
#include "esp8266/rom_functions.h"
#include "esp8266/spi_register.h"
#endif

#define ENABLE_DEBUG 0
#include "debug.h"

Expand Down Expand Up @@ -80,8 +85,42 @@ static const char* exception_names [] =
"Coprocessor7Disabled", /* 39 */
};

#ifdef MCU_ESP8266

/* Enables the IROM cache if needed. */
static void IRAM ensure_flash_cache_enabled(void)
{
if (!(READ_PERI_REG(CACHE_FLASH_CTRL_REG) & CACHE_READ_EN_BIT)) {
/* Wait_SPI_Idle() is in the bootrom. */
Wait_SPI_Idle(&flashchip);
SET_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_ENABLE_AHB);
SET_PERI_REG_MASK(CACHE_FLASH_CTRL_REG, CACHE_READ_EN_BIT);
}
}

#else /* MCU_ESP8266 */

static void IRAM ensure_flash_cache_enabled(void) {}

#endif /* MCU_ESP8266 */

void IRAM NORETURN exception_handler (XtExcFrame *frame)
{
static uint8_t exception_handler_calls = 0;
if (exception_handler_calls > 0) {
/* Break to the debugger if the exception handler was called more than
* once. This normally indicates an exception while handling the
* exception, for example trying to use a function from IROM while the
* cache is disabled.
*/
__asm__("break 8, 0");
}
exception_handler_calls++;
/* Several functions here, including the string literals passed to
* ets_printf(), require the flash cache to be enabled.
*/
ensure_flash_cache_enabled();

uint32_t excsave1;
uint32_t epc1;
RSR(excsave1, excsave1);
Expand Down Expand Up @@ -212,6 +251,7 @@ void IRAM NORETURN panic_arch(void)

void _panic_handler(uint32_t addr)
{
ensure_flash_cache_enabled();
ets_printf("#! _xt_panic called from 0x%08x: powering off\n", addr);
pm_off();
while (1) { };
Expand Down

0 comments on commit 17ed80a

Please sign in to comment.