4#ifdef USE_ESP8266_CRASH_HANDLER
13#include <user_interface.h>
21static constexpr uint32_t XTENSA_ADDR_MASK = 0x3FFFFFFF;
22static constexpr uint32_t XTENSA_CODE_BASE = 0x40000000;
25static constexpr uint32_t IRAM_START = 0x40100000;
26static constexpr uint32_t IRAM_END = 0x40108000;
41static inline bool IRAM_ATTR is_code_addr(
uint32_t val) {
42 uint32_t addr = (
val & XTENSA_ADDR_MASK) | XTENSA_CODE_BASE;
43 return (addr >= IRAM_START && addr < IRAM_END) ||
48static inline uint32_t IRAM_ATTR recover_code_addr(
uint32_t val) {
return (
val & XTENSA_ADDR_MASK) | XTENSA_CODE_BASE; }
52static constexpr size_t MAX_BACKTRACE = 16;
58static constexpr uint8_t CRASH_SENTINEL_BITS = 16;
59static constexpr uint8_t CRASH_VERSION_BITS = 8;
61static constexpr uint16_t CRASH_SENTINEL_VALUE = 0xDEAD;
62static constexpr uint8_t CRASH_VERSION_VALUE = 1;
64static constexpr uint32_t CRASH_SENTINEL =
static_cast<uint32_t>(CRASH_SENTINEL_VALUE) << CRASH_SENTINEL_BITS;
65static constexpr uint32_t CRASH_VERSION =
static_cast<uint32_t>(CRASH_VERSION_VALUE) << CRASH_VERSION_BITS;
66static constexpr uint32_t CRASH_SENTINEL_MASK =
static_cast<uint32_t>(0xFFFF) << CRASH_SENTINEL_BITS;
67static constexpr uint32_t CRASH_VERSION_MASK =
static_cast<uint32_t>(0xFF) << CRASH_VERSION_BITS;
68static constexpr uint32_t CRASH_COUNT_MASK = 0xFF;
79static_assert(
sizeof(RtcCrashData) == esphome::esp8266::RTC_CRASH_WORDS * 4,
"RtcCrashData must fill its RTC words");
85static inline bool is_crash_reason(
uint32_t reason) {
86 return reason == REASON_WDT_RST || reason == REASON_EXCEPTION_RST || reason == REASON_SOFT_WDT_RST;
96static const LogString *get_exception_cause(
uint32_t cause) {
98 return LOG_STR(
"IllegalInst");
100 return LOG_STR(
"InstFetchErr");
102 return LOG_STR(
"LoadStoreErr");
104 return LOG_STR(
"Level1Int");
106 return LOG_STR(
"DivByZero");
108 return LOG_STR(
"Alignment");
110 return LOG_STR(
"InstFetchProhibit");
112 return LOG_STR(
"LoadProhibit");
114 return LOG_STR(
"StoreProhibit");
118static const LogString *get_reset_reason(
uint32_t reason) {
119 if (reason == REASON_EXCEPTION_RST)
120 return LOG_STR(
"Exception");
121 if (reason == REASON_SOFT_WDT_RST)
122 return LOG_STR(
"Soft WDT");
123 return LOG_STR(
"Unknown");
128static uint8_t read_rtc_backtrace(
uint32_t *backtrace,
size_t max_entries) {
129 RtcCrashData rtc_data;
130 if (!system_rtc_mem_read(esphome::esp8266::RTC_CRASH_BASE, &rtc_data,
sizeof(rtc_data)))
133 if ((magic & CRASH_SENTINEL_MASK) != CRASH_SENTINEL || (magic & CRASH_VERSION_MASK) != CRASH_VERSION)
135 uint8_t raw_count = magic & CRASH_COUNT_MASK;
136 if (raw_count > MAX_BACKTRACE)
137 raw_count = MAX_BACKTRACE;
144 for (uint8_t i = 0; i < raw_count &&
out < max_entries; i++) {
145 uint32_t addr = rtc_data.backtrace[i];
146 if (addr != rtc_data.epc1)
147 backtrace[
out++] = addr;
161 ESP_LOGE(TAG,
"*** CRASH DETECTED ON PREVIOUS BOOT ***");
162 if (
resetInfo.reason == REASON_WDT_RST) {
166 ESP_LOGE(TAG,
" Reason: Hardware WDT (no crash state is recorded for hardware WDT resets)");
174 uint8_t
bt_count = read_rtc_backtrace(backtrace, MAX_BACKTRACE);
178 static constexpr uint32_t EXCCAUSE_ILLEGAL_INSTRUCTION = 0;
179 static constexpr uint32_t EXCCAUSE_INTEGER_DIVIDE_BY_ZERO = 6;
180 static constexpr uint32_t ROM_DIV_ZERO_ADDR_1 = 0x4000dce5;
181 static constexpr uint32_t ROM_DIV_ZERO_ADDR_2 = 0x4000dd3d;
183 if (exccause == EXCCAUSE_ILLEGAL_INSTRUCTION &&
185 exccause = EXCCAUSE_INTEGER_DIVIDE_BY_ZERO;
187 const LogString *cause = get_exception_cause(exccause);
188 if (cause !=
nullptr) {
189 ESP_LOGE(TAG,
" Reason: %s - %s (exccause=%" PRIu32
")", LOG_STR_ARG(get_reset_reason(
resetInfo.reason)),
190 LOG_STR_ARG(cause), exccause);
192 ESP_LOGE(TAG,
" Reason: %s (exccause=%" PRIu32
")", LOG_STR_ARG(get_reset_reason(
resetInfo.reason)), exccause);
194 ESP_LOGE(TAG,
" PC: 0x%08" PRIX32,
resetInfo.epc1);
195 if (
resetInfo.reason == REASON_EXCEPTION_RST) {
196 ESP_LOGE(TAG,
" EXCVADDR: 0x%08" PRIX32,
resetInfo.excvaddr);
198 for (uint8_t i = 0; i <
bt_count; i++) {
199 ESP_LOGE(TAG,
" BT%d: 0x%08" PRIX32, i, backtrace[i]);
221 for (; scan <
end &&
count < MAX_BACKTRACE; scan++) {
223 if (is_code_addr(
val)) {
227 data.backtrace[
count++] = addr;
232 data.magic = CRASH_SENTINEL | CRASH_VERSION |
count;
234 system_rtc_mem_write(esphome::esp8266::RTC_CRASH_BASE, &data,
sizeof(data));
struct rst_info resetInfo
void _irom0_text_end(void)
struct rst_info resetInfo
void IRAM_ATTR custom_crash_callback(struct rst_info *rst_info, uint32_t stack, uint32_t stack_end)
void _irom0_text_start(void)
std::vector< uint8_t > & out
bool crash_handler_has_data()
Returns true if the previous boot was a crash (exception, WDT, or soft WDT).
void crash_handler_log()
Log crash data if a crash was detected on previous boot.
ESPHOME_LOG_TAG(TAG, "esp8266")