1 /*
2 * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "esp_err.h"
10 #include "esp_attr.h"
11
12 #include "esp_private/system_internal.h"
13 #include "esp_private/usb_console.h"
14
15 #include "esp_cpu.h"
16 #include "soc/rtc.h"
17 #include "hal/timer_hal.h"
18 #include "hal/wdt_types.h"
19 #include "hal/wdt_hal.h"
20 #include "hal/mwdt_ll.h"
21 #include "esp_private/esp_int_wdt.h"
22
23 #include "esp_private/panic_internal.h"
24 #include "port/panic_funcs.h"
25 #include "esp_rom_sys.h"
26
27 #include "sdkconfig.h"
28
29 #if __has_include("esp_app_desc.h")
30 #define WITH_ELF_SHA256
31 #include "esp_app_desc.h"
32 #endif
33
34 #if CONFIG_ESP_COREDUMP_ENABLE
35 #include "esp_core_dump.h"
36 #endif
37
38 #if CONFIG_APPTRACE_ENABLE
39 #include "esp_app_trace.h"
40 #if CONFIG_APPTRACE_SV_ENABLE
41 #include "SEGGER_RTT.h"
42 #endif
43
44 #if CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO == -1
45 #define APPTRACE_ONPANIC_HOST_FLUSH_TMO ESP_APPTRACE_TMO_INFINITE
46 #else
47 #define APPTRACE_ONPANIC_HOST_FLUSH_TMO (1000*CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO)
48 #endif
49 #endif // CONFIG_APPTRACE_ENABLE
50
51 #if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
52 #include "hal/uart_hal.h"
53 #endif
54
55 #if CONFIG_ESP_SYSTEM_PANIC_GDBSTUB
56 #include "esp_gdbstub.h"
57 #endif
58
59 #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
60 #include "hal/usb_serial_jtag_ll.h"
61 #endif
62
63 #define MWDT_DEFAULT_TICKS_PER_US 500
64
65 bool g_panic_abort = false;
66 static char *s_panic_abort_details = NULL;
67
68 static wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT();
69
70 #if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
71
72 #if CONFIG_ESP_CONSOLE_UART
73 static uart_hal_context_t s_panic_uart = { .dev = CONFIG_ESP_CONSOLE_UART_NUM == 0 ? &UART0 :&UART1 };
74
panic_print_char_uart(const char c)75 static void panic_print_char_uart(const char c)
76 {
77 uint32_t sz = 0;
78 while (!uart_hal_get_txfifo_len(&s_panic_uart));
79 uart_hal_write_txfifo(&s_panic_uart, (uint8_t *) &c, 1, &sz);
80 }
81 #endif // CONFIG_ESP_CONSOLE_UART
82
83
84 #if CONFIG_ESP_CONSOLE_USB_CDC
panic_print_char_usb_cdc(const char c)85 static void panic_print_char_usb_cdc(const char c)
86 {
87 esp_usb_console_write_buf(&c, 1);
88 /* result ignored */
89 }
90 #endif // CONFIG_ESP_CONSOLE_USB_CDC
91
92 #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
93 //Timeout; if there's no host listening, the txfifo won't ever
94 //be writable after the first packet.
95
96 #define USBSERIAL_TIMEOUT_MAX_US 50000
97 static int s_usbserial_timeout = 0;
98
panic_print_char_usb_serial_jtag(const char c)99 static void panic_print_char_usb_serial_jtag(const char c)
100 {
101 while (!usb_serial_jtag_ll_txfifo_writable() && s_usbserial_timeout < (USBSERIAL_TIMEOUT_MAX_US / 100)) {
102 esp_rom_delay_us(100);
103 s_usbserial_timeout++;
104 }
105 if (usb_serial_jtag_ll_txfifo_writable()) {
106 usb_serial_jtag_ll_write_txfifo((const uint8_t *)&c, 1);
107 s_usbserial_timeout = 0;
108 }
109 }
110 #endif //CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
111
112
panic_print_char(const char c)113 void panic_print_char(const char c)
114 {
115 #if CONFIG_ESP_CONSOLE_UART
116 panic_print_char_uart(c);
117 #endif
118 #if CONFIG_ESP_CONSOLE_USB_CDC
119 panic_print_char_usb_cdc(c);
120 #endif
121 #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
122 panic_print_char_usb_serial_jtag(c);
123 #endif
124 }
125
panic_print_str(const char * str)126 void panic_print_str(const char *str)
127 {
128 for (int i = 0; str[i] != 0; i++) {
129 panic_print_char(str[i]);
130 }
131 }
132
panic_print_hex(int h)133 void panic_print_hex(int h)
134 {
135 int x;
136 int c;
137 // Does not print '0x', only the digits (8 digits to print)
138 for (x = 0; x < 8; x++) {
139 c = (h >> 28) & 0xf; // extract the leftmost byte
140 if (c < 10) {
141 panic_print_char('0' + c);
142 } else {
143 panic_print_char('a' + c - 10);
144 }
145 h <<= 4; // move the 2nd leftmost byte to the left, to be extracted next
146 }
147 }
148
panic_print_dec(int d)149 void panic_print_dec(int d)
150 {
151 // can print at most 2 digits!
152 int n1, n2;
153 n1 = d % 10; // extract ones digit
154 n2 = d / 10; // extract tens digit
155 if (n2 == 0) {
156 panic_print_char(' ');
157 } else {
158 panic_print_char(n2 + '0');
159 }
160 panic_print_char(n1 + '0');
161 }
162 #endif // CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
163
164 /*
165 If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because
166 an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run
167 the risk of somehow halting in the panic handler and not resetting. That is why this routine kills
168 all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after
169 one second.
170
171 We have to do this before we do anything that might cause issues in the WDT interrupt handlers,
172 for example stalling the other core on ESP32 may cause the ESP32_ECO3_CACHE_LOCK_FIX
173 handler to get stuck.
174 */
esp_panic_handler_reconfigure_wdts(uint32_t timeout_ms)175 void esp_panic_handler_reconfigure_wdts(uint32_t timeout_ms)
176 {
177 wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0};
178 #if SOC_TIMER_GROUPS >= 2
179 // IDF-3825
180 wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1};
181 #endif
182
183 //Todo: Refactor to use Interrupt or Task Watchdog API, and a system level WDT context
184 //Reconfigure TWDT (Timer Group 0)
185 wdt_hal_init(&wdt0_context, WDT_MWDT0, MWDT_LL_DEFAULT_CLK_PRESCALER, false); //Prescaler: wdt counts in ticks of TG0_WDT_TICK_US
186 wdt_hal_write_protect_disable(&wdt0_context);
187 wdt_hal_config_stage(&wdt0_context, 0, timeout_ms * 1000 / MWDT_DEFAULT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); //1 second before reset
188 wdt_hal_enable(&wdt0_context);
189 wdt_hal_write_protect_enable(&wdt0_context);
190
191 #if SOC_TIMER_GROUPS >= 2
192 //Disable IWDT (Timer Group 1)
193 wdt_hal_write_protect_disable(&wdt1_context);
194 wdt_hal_disable(&wdt1_context);
195 wdt_hal_write_protect_enable(&wdt1_context);
196 #endif
197 }
198
199 /*
200 This disables all the watchdogs for when we call the gdbstub.
201 */
disable_all_wdts(void)202 static inline void disable_all_wdts(void)
203 {
204 wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0};
205 #if SOC_TIMER_GROUPS >= 2
206 wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1};
207 #endif
208
209 //Todo: Refactor to use Interrupt or Task Watchdog API, and a system level WDT context
210 //Task WDT is the Main Watchdog Timer of Timer Group 0
211 wdt_hal_write_protect_disable(&wdt0_context);
212 wdt_hal_disable(&wdt0_context);
213 wdt_hal_write_protect_enable(&wdt0_context);
214
215 #if SOC_TIMER_GROUPS >= 2
216 //Interupt WDT is the Main Watchdog Timer of Timer Group 1
217 wdt_hal_write_protect_disable(&wdt1_context);
218 wdt_hal_disable(&wdt1_context);
219 wdt_hal_write_protect_enable(&wdt1_context);
220 #endif
221 }
222
print_abort_details(const void * f)223 static void print_abort_details(const void *f)
224 {
225 panic_print_str(s_panic_abort_details);
226 }
227
228 // Control arrives from chip-specific panic handler, environment prepared for
229 // the 'main' logic of panic handling. This means that chip-specific stuff have
230 // already been done, and panic_info_t has been filled.
esp_panic_handler(panic_info_t * info)231 void esp_panic_handler(panic_info_t *info)
232 {
233 // The port-level panic handler has already called this, but call it again
234 // to reset the TG0WDT period
235 esp_panic_handler_reconfigure_wdts(1000);
236
237 // If the exception was due to an abort, override some of the panic info
238 if (g_panic_abort) {
239 info->description = NULL;
240 info->details = s_panic_abort_details ? print_abort_details : NULL;
241 info->reason = NULL;
242 info->exception = PANIC_EXCEPTION_ABORT;
243 }
244
245 /*
246 * For any supported chip, the panic handler prints the contents of panic_info_t in the following format:
247 *
248 *
249 * Guru Meditation Error: Core <core> (<exception>). <description>
250 * <details>
251 *
252 * <state>
253 *
254 * <elf_info>
255 *
256 *
257 * ----------------------------------------------------------------------------------------
258 * core - core where exception was triggered
259 * exception - what kind of exception occurred
260 * description - a short description regarding the exception that occurred
261 * details - more details about the exception
262 * state - processor state like register contents, and backtrace
263 * elf_info - details about the image currently running
264 *
265 * NULL fields in panic_info_t are not printed.
266 *
267 * */
268 if (info->reason) {
269 panic_print_str("Guru Meditation Error: Core ");
270 panic_print_dec(info->core);
271 panic_print_str(" panic'ed (");
272 panic_print_str(info->reason);
273 panic_print_str("). ");
274 }
275
276 if (info->description) {
277 panic_print_str(info->description);
278 }
279
280 panic_print_str("\r\n");
281
282 PANIC_INFO_DUMP(info, details);
283
284 panic_print_str("\r\n");
285
286 // If on-chip-debugger is attached, and system is configured to be aware of this,
287 // then only print up to details. Users should be able to probe for the other information
288 // in debug mode.
289 if (esp_cpu_dbgr_is_attached()) {
290 panic_print_str("Setting breakpoint at 0x");
291 panic_print_hex((uint32_t)info->addr);
292 panic_print_str(" and returning...\r\n");
293 disable_all_wdts();
294 #if CONFIG_APPTRACE_ENABLE
295 #if CONFIG_APPTRACE_SV_ENABLE
296 SEGGER_RTT_ESP_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
297 #else
298 esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
299 APPTRACE_ONPANIC_HOST_FLUSH_TMO);
300 #endif
301 #endif
302
303 esp_cpu_set_breakpoint(0, info->addr); // use breakpoint 0
304 return;
305 }
306
307 // start panic WDT to restart system if we hang in this handler
308 if (!wdt_hal_is_enabled(&rtc_wdt_ctx)) {
309 wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
310 uint32_t stage_timeout_ticks = (uint32_t)(7000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
311 wdt_hal_write_protect_disable(&rtc_wdt_ctx);
312 wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM);
313 // 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data.
314 // @ 115200 UART speed it will take more than 6 sec to print them out.
315 wdt_hal_enable(&rtc_wdt_ctx);
316 wdt_hal_write_protect_enable(&rtc_wdt_ctx);
317
318 }
319
320 esp_panic_handler_reconfigure_wdts(1000); // Restart WDT again
321
322 PANIC_INFO_DUMP(info, state);
323 panic_print_str("\r\n");
324
325 /* No matter if we come here from abort or an exception, this variable must be reset.
326 * Else, any exception/error occurring during the current panic handler would considered
327 * an abort. Do this after PANIC_INFO_DUMP(info, state) as it also checks this variable.
328 * For example, if coredump triggers a stack overflow and this variable is not reset,
329 * the second panic would be still be marked as the result of an abort, even the previous
330 * message reason would be kept. */
331 g_panic_abort = false;
332
333 #ifdef WITH_ELF_SHA256
334 panic_print_str("\r\nELF file SHA256: ");
335 char sha256_buf[65];
336 esp_app_get_elf_sha256(sha256_buf, sizeof(sha256_buf));
337 panic_print_str(sha256_buf);
338 panic_print_str("\r\n");
339 #endif
340
341 panic_print_str("\r\n");
342
343 #if CONFIG_APPTRACE_ENABLE
344 disable_all_wdts();
345 #if CONFIG_APPTRACE_SV_ENABLE
346 SEGGER_RTT_ESP_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
347 #else
348 esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
349 APPTRACE_ONPANIC_HOST_FLUSH_TMO);
350 #endif
351 esp_panic_handler_reconfigure_wdts(1000); // restore WDT config
352 #endif // CONFIG_APPTRACE_ENABLE
353
354 #if CONFIG_ESP_COREDUMP_ENABLE
355 static bool s_dumping_core;
356 if (s_dumping_core) {
357 panic_print_str("Re-entered core dump! Exception happened during core dump!\r\n");
358 } else {
359 disable_all_wdts();
360 s_dumping_core = true;
361 #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
362 esp_core_dump_to_flash(info);
363 #endif
364 #if CONFIG_ESP_COREDUMP_ENABLE_TO_UART && !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
365 esp_core_dump_to_uart(info);
366 #endif
367 s_dumping_core = false;
368
369 esp_panic_handler_reconfigure_wdts(1000);
370 }
371 #endif /* CONFIG_ESP_COREDUMP_ENABLE */
372
373 #if CONFIG_ESP_SYSTEM_PANIC_GDBSTUB
374 disable_all_wdts();
375 wdt_hal_write_protect_disable(&rtc_wdt_ctx);
376 wdt_hal_disable(&rtc_wdt_ctx);
377 wdt_hal_write_protect_enable(&rtc_wdt_ctx);
378 panic_print_str("Entering gdb stub now.\r\n");
379 esp_gdbstub_panic_handler((void *)info->frame);
380 #else
381 #if CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS
382 // start RTC WDT if it hasn't been started yet and set the timeout to more than the delay time
383 wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
384 uint32_t stage_timeout_ticks = (uint32_t)(((CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS + 1) * 1000
385 * rtc_clk_slow_freq_get_hz()) / 1000ULL);
386 wdt_hal_write_protect_disable(&rtc_wdt_ctx);
387 wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM);
388 // 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data.
389 // @ 115200 UART speed it will take more than 6 sec to print them out.
390 wdt_hal_enable(&rtc_wdt_ctx);
391 wdt_hal_write_protect_enable(&rtc_wdt_ctx);
392
393 esp_panic_handler_reconfigure_wdts((CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS + 1) * 1000);
394
395 panic_print_str("Rebooting in ");
396 panic_print_dec(CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS);
397 panic_print_str(" seconds...\r\n");
398
399 esp_rom_delay_us(CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS * 1000000);
400
401 esp_panic_handler_reconfigure_wdts(1000);
402 #endif /* CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS */
403
404 wdt_hal_write_protect_disable(&rtc_wdt_ctx);
405 wdt_hal_disable(&rtc_wdt_ctx);
406 wdt_hal_write_protect_enable(&rtc_wdt_ctx);
407
408 #if CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT || CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
409
410 if (esp_reset_reason_get_hint() == ESP_RST_UNKNOWN) {
411 switch (info->exception) {
412 case PANIC_EXCEPTION_IWDT:
413 esp_reset_reason_set_hint(ESP_RST_INT_WDT);
414 break;
415 case PANIC_EXCEPTION_TWDT:
416 esp_reset_reason_set_hint(ESP_RST_TASK_WDT);
417 break;
418 case PANIC_EXCEPTION_ABORT:
419 case PANIC_EXCEPTION_FAULT:
420 default:
421 esp_reset_reason_set_hint(ESP_RST_PANIC);
422 break; // do not touch the previously set reset reason hint
423 }
424 }
425
426 panic_print_str("Rebooting...\r\n");
427 panic_restart();
428 #else /* CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT || CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT */
429 disable_all_wdts();
430 panic_print_str("CPU halted.\r\n");
431 esp_system_reset_modules_on_exit();
432 while (1);
433 #endif /* CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT || CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT */
434 #endif /* CONFIG_ESP_SYSTEM_PANIC_GDBSTUB */
435 }
436
437
panic_abort(const char * details)438 void IRAM_ATTR __attribute__((noreturn, no_sanitize_undefined)) panic_abort(const char *details)
439 {
440 g_panic_abort = true;
441 s_panic_abort_details = (char *) details;
442
443 #if CONFIG_APPTRACE_ENABLE
444 #if CONFIG_APPTRACE_SV_ENABLE
445 SEGGER_RTT_ESP_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
446 #else
447 esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
448 APPTRACE_ONPANIC_HOST_FLUSH_TMO);
449 #endif
450 #endif
451
452 *((volatile int *) 0) = 0; // NOLINT(clang-analyzer-core.NullDereference) should be an invalid operation on targets
453 while (1);
454 }
455
456 /* Weak versions of reset reason hint functions.
457 * If these weren't provided, reset reason code would be linked into the app
458 * even if the app never called esp_reset_reason().
459 */
esp_reset_reason_set_hint(esp_reset_reason_t hint)460 void IRAM_ATTR __attribute__((weak)) esp_reset_reason_set_hint(esp_reset_reason_t hint)
461 {
462 }
463
esp_reset_reason_get_hint(void)464 esp_reset_reason_t IRAM_ATTR __attribute__((weak)) esp_reset_reason_get_hint(void)
465 {
466 return ESP_RST_UNKNOWN;
467 }
468