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