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