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