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