From: aleks Date: Tue, 29 Jan 2019 14:49:56 +0000 (+0100) Subject: coredump: improvements refactoring X-Git-Tag: v3.3-beta2~32^2~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=10fe158f677f2727f5c6d8a68047c257173a481e;p=esp-idf coredump: improvements refactoring Move existing core dump files into espcoredump component folder Add KConfig, linker.lf, make and CMakeList.txt for new component Existing functionality separated into core_dump_common, core_dump_flash, core_dump_uart Update test_core_dump.c and make files to link it as unit test Update according to review: Move target and RTOS related functionality into separated file (core_dump_port.c). --- diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 36ccbd55ed..938f77ef1e 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -14,7 +14,6 @@ else() "cache_sram_mmu.c" "clk.c" "coexist.c" - "core_dump.c" "cpu_start.c" "crosscore_int.c" "dbg_stubs.c" diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index aecd26a655..df8b31c73d 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -285,51 +285,6 @@ menu "ESP32-specific" default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS default 0x0 - menu "Core dump" - - choice ESP32_COREDUMP_TO_FLASH_OR_UART - prompt "Data destination" - default ESP32_ENABLE_COREDUMP_TO_NONE - help - Select place to store core dump: flash, uart or none (to disable core dumps generation). - - If core dump is configured to be stored in flash and custom partition table is used add - corresponding entry to your CSV. For examples, please see predefined partition table CSV descriptions - in the components/partition_table directory. - - config ESP32_ENABLE_COREDUMP_TO_FLASH - bool "Flash" - select ESP32_ENABLE_COREDUMP - config ESP32_ENABLE_COREDUMP_TO_UART - bool "UART" - select ESP32_ENABLE_COREDUMP - config ESP32_ENABLE_COREDUMP_TO_NONE - bool "None" - endchoice - - config ESP32_ENABLE_COREDUMP - bool - default F - help - Enables/disable core dump module. - - config ESP32_CORE_DUMP_MAX_TASKS_NUM - int "Maximum number of tasks" - depends on ESP32_ENABLE_COREDUMP - default 64 - help - Maximum number of tasks snapshots in core dump. - - config ESP32_CORE_DUMP_UART_DELAY - int "Delay before print to UART" - depends on ESP32_ENABLE_COREDUMP_TO_UART - default 0 - help - Config delay (in ms) before printing core dump to UART. - Delay can be interrupted by pressing Enter key. - - endmenu - choice NUMBER_OF_UNIVERSAL_MAC_ADDRESS bool "Number of universally administered (by IEEE) MAC address" default FOUR_UNIVERSAL_MAC_ADDRESS diff --git a/components/esp32/core_dump.c b/components/esp32/core_dump.c deleted file mode 100644 index b8988c40b8..0000000000 --- a/components/esp32/core_dump.c +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "soc/uart_reg.h" -#include "soc/io_mux_reg.h" -#include "soc/timer_group_struct.h" -#include "soc/timer_group_reg.h" -#include "driver/gpio.h" -#include "rom/crc.h" - -#include "esp_panic.h" -#include "esp_partition.h" -#include "esp_clk.h" -#include "esp_core_dump.h" - -#include "esp_log.h" -const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump"; - -typedef uint32_t core_dump_crc_t; - -#if CONFIG_ESP32_ENABLE_COREDUMP -#define ESP_COREDUMP_LOG( level, format, ... ) if (LOG_LOCAL_LEVEL >= level) { ets_printf(DRAM_STR(format), esp_log_early_timestamp(), (const char *)TAG, ##__VA_ARGS__); } -#define ESP_COREDUMP_LOGE( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_ERROR, LOG_FORMAT(E, format), ##__VA_ARGS__) -#define ESP_COREDUMP_LOGW( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_WARN, LOG_FORMAT(W, format), ##__VA_ARGS__) -#define ESP_COREDUMP_LOGI( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_INFO, LOG_FORMAT(I, format), ##__VA_ARGS__) -#define ESP_COREDUMP_LOGD( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_DEBUG, LOG_FORMAT(D, format), ##__VA_ARGS__) -#define ESP_COREDUMP_LOGV( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_VERBOSE, LOG_FORMAT(V, format), ##__VA_ARGS__) - -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH -#define ESP_COREDUMP_LOG_PROCESS( format, ... ) ESP_COREDUMP_LOGD(format, ##__VA_ARGS__) -#else -#define ESP_COREDUMP_LOG_PROCESS( format, ... ) do{/*(__VA_ARGS__);*/}while(0) -#endif - -#define COREDUMP_MAX_TASK_STACK_SIZE (64*1024) -#define COREDUMP_VERSION 1 - - -typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); -typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); -typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); -typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); - -/** core dump emitter control structure */ -typedef struct _core_dump_write_config_t -{ - // this function is called before core dump data writing - // used for sanity checks - esp_core_dump_write_prepare_t prepare; - // this function is called at the beginning of data writing - esp_core_dump_write_start_t start; - // this function is called when all dump data are written - esp_core_dump_write_end_t end; - // this function is called to write data chunk - esp_core_dump_flash_write_data_t write; - // number of tasks with corrupted TCBs - uint32_t bad_tasks_num; - // pointer to data which are specific for particular core dump emitter - void * priv; -} core_dump_write_config_t; - -/** core dump data header */ -typedef struct _core_dump_header_t -{ - uint32_t data_len; // data length - uint32_t version; // core dump struct version - uint32_t tasks_num; // number of tasks - uint32_t tcb_sz; // size of TCB -} core_dump_header_t; - -/** core dump task data header */ -typedef struct _core_dump_task_header_t -{ - void * tcb_addr; // TCB address - uint32_t stack_start; // stack start address - uint32_t stack_end; // stack end address -} core_dump_task_header_t; - -static inline bool esp_task_stack_start_is_sane(uint32_t sp) -{ - return !(sp < 0x3ffae010UL || sp > 0x3fffffffUL); -} - -static inline bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz) -{ - //TODO: currently core dump supports TCBs in DRAM only, external SRAM not supported yet - return !(addr < 0x3ffae000UL || (addr + sz) > 0x40000000UL); -} - -static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *write_cfg) -{ - int cur_task_bad = 0; - esp_err_t err; - TaskSnapshot_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM]; - UBaseType_t tcb_sz, tcb_sz_padded, task_num; - uint32_t data_len = 0, i, len; - union - { - core_dump_header_t hdr; - core_dump_task_header_t task_hdr; - } dump_data; - - task_num = uxTaskGetSnapshotAll(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM, &tcb_sz); - // take TCB padding into account, actual TCB size will be stored in header - if (tcb_sz % sizeof(uint32_t)) - tcb_sz_padded = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); - else - tcb_sz_padded = tcb_sz; - // header + tasknum*(tcb + stack start/end + tcb addr) - data_len = sizeof(core_dump_header_t) + task_num*(tcb_sz_padded + sizeof(core_dump_task_header_t)); - for (i = 0; i < task_num; i++) { - if (!esp_tcb_addr_is_sane((uint32_t)tasks[i].pxTCB, tcb_sz)) { - ESP_COREDUMP_LOG_PROCESS("Bad TCB addr %x!", tasks[i].pxTCB); - write_cfg->bad_tasks_num++; - continue; - } - if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { - // set correct stack top for current task - tasks[i].pxTopOfStack = (StackType_t *)frame; - // This field is not initialized for crashed task, but stack frame has the structure of interrupt one, - // so make workaround to allow espcoredump to parse it properly. - if (frame->exit == 0) - frame->exit = -1; - ESP_COREDUMP_LOG_PROCESS("Current task EXIT/PC/PS/A0/SP %x %x %x %x %x", - frame->exit, frame->pc, frame->ps, frame->a0, frame->a1); - } - else { - XtSolFrame *task_frame = (XtSolFrame *)tasks[i].pxTopOfStack; - if (task_frame->exit == 0) { - ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", - task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); - } - else { -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH - XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack; - ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x", - task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); -#endif - } - } - len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack; - // check task's stack - if (!esp_stack_ptr_is_sane((uint32_t)tasks[i].pxTopOfStack) || !esp_task_stack_start_is_sane((uint32_t)tasks[i].pxEndOfStack) - || len > COREDUMP_MAX_TASK_STACK_SIZE) { - if (tasks[i].pxTCB == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { - cur_task_bad = 1; - } - ESP_COREDUMP_LOG_PROCESS("Corrupted TCB %x: stack len %lu, top %x, end %x!", - tasks[i].pxTCB, len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); - tasks[i].pxTCB = 0; // make TCB addr invalid to skip it in dump - write_cfg->bad_tasks_num++; - } else { - ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, tasks[i].pxTopOfStack, tasks[i].pxEndOfStack); - // take stack padding into account - len = (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1); - data_len += len; - } - } - data_len -= write_cfg->bad_tasks_num*(tcb_sz_padded + sizeof(core_dump_task_header_t)); - - ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu (%d %d)", data_len, task_num, write_cfg->bad_tasks_num); - - // prepare write - if (write_cfg->prepare) { - err = write_cfg->prepare(write_cfg->priv, &data_len); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err); - return; - } - } - // write start - if (write_cfg->start) { - err = write_cfg->start(write_cfg->priv); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err); - return; - } - } - // write header - dump_data.hdr.data_len = data_len; - dump_data.hdr.version = COREDUMP_VERSION; - dump_data.hdr.tasks_num = task_num - write_cfg->bad_tasks_num; - dump_data.hdr.tcb_sz = tcb_sz; - err = write_cfg->write(write_cfg->priv, &dump_data, sizeof(core_dump_header_t)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err); - return; - } - // write tasks - for (i = 0; i < task_num; i++) { - if (!esp_tcb_addr_is_sane((uint32_t)tasks[i].pxTCB, tcb_sz)) { - ESP_COREDUMP_LOG_PROCESS("Skip TCB with bad addr %x!", tasks[i].pxTCB); - continue; - } - ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].pxTCB); - // save TCB address, stack base and stack top addr - dump_data.task_hdr.tcb_addr = tasks[i].pxTCB; - dump_data.task_hdr.stack_start = (uint32_t)tasks[i].pxTopOfStack; - dump_data.task_hdr.stack_end = (uint32_t)tasks[i].pxEndOfStack; - err = write_cfg->write(write_cfg->priv, &dump_data, sizeof(core_dump_task_header_t)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err); - return; - } - // save TCB - err = write_cfg->write(write_cfg->priv, tasks[i].pxTCB, tcb_sz); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err); - return; - } - // save task stack - if (tasks[i].pxTopOfStack != 0 && tasks[i].pxEndOfStack != 0) { - err = write_cfg->write(write_cfg->priv, tasks[i].pxTopOfStack, - (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err); - return; - } - } else { - ESP_COREDUMP_LOG_PROCESS("Skip corrupted task %x stack!", tasks[i].pxTCB); - } - } - - // write end - if (write_cfg->end) { - err = write_cfg->end(write_cfg->priv); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err); - return; - } - } - if (write_cfg->bad_tasks_num) { - ESP_COREDUMP_LOGE("Skipped %d tasks with bad TCB!", write_cfg->bad_tasks_num); - if (cur_task_bad) { - ESP_COREDUMP_LOGE("Crashed task has been skipped!"); - } - } -} - -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH - -typedef struct _core_dump_write_flash_data_t -{ - uint32_t off; // current offset in partition - core_dump_crc_t crc; // CRC of dumped data -} core_dump_write_flash_data_t; - -typedef struct _core_dump_partition_t -{ - // core dump partition start - uint32_t start; - // core dump partition size - uint32_t size; -} core_dump_partition_t; - -typedef struct _core_dump_flash_config_t -{ - // core dump partition config - core_dump_partition_t partition; - // CRC of core dump partition config - core_dump_crc_t partition_config_crc; -} core_dump_flash_config_t; - -// core dump flash data -static core_dump_flash_config_t s_core_flash_config; - -static inline core_dump_crc_t esp_core_dump_calc_flash_config_crc(void) -{ - return crc32_le(0, (uint8_t const *)&s_core_flash_config.partition, sizeof(s_core_flash_config.partition)); -} - -static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size) -{ - esp_err_t err; - uint32_t data_len = 0, k, len; - union - { - uint8_t data8[4]; - uint32_t data32; - } rom_data; - - data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); - - assert(off >= s_core_flash_config.partition.start); - assert((off + data_len + (data_size % sizeof(uint32_t) ? sizeof(uint32_t) : 0)) <= - s_core_flash_config.partition.start + s_core_flash_config.partition.size); - - err = spi_flash_write(off, data, data_len); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err); - return 0; - } - - len = data_size % sizeof(uint32_t); - if (len) { - // write last bytes with padding, actual TCB len can be retrieved by esptool from core dump header - rom_data.data32 = 0; - for (k = 0; k < len; k++) { - rom_data.data8[k] = *(data + data_len + k); - } - err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err); - return 0; - } - data_len += sizeof(uint32_t); - } - - return data_len; -} - -static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) -{ - esp_err_t err; - uint32_t sec_num; - core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; - - // check for available space in partition - if ((*data_len + sizeof(uint32_t)) > s_core_flash_config.partition.size) { - ESP_COREDUMP_LOGE("Not enough space to save core dump!"); - return ESP_ERR_NO_MEM; - } - // add space for CRC - *data_len += sizeof(core_dump_crc_t); - - memset(wr_data, 0, sizeof(*wr_data)); - - sec_num = *data_len / SPI_FLASH_SEC_SIZE; - if (*data_len % SPI_FLASH_SEC_SIZE) { - sec_num++; - } - assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size); - err = spi_flash_erase_range(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err); - return err; - } - - return err; -} - -static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr_data, uint32_t word) -{ - esp_err_t err = ESP_OK; - uint32_t data32 = word; - - assert(wr_data->off + sizeof(uint32_t) <= s_core_flash_config.partition.size); - err = spi_flash_write(s_core_flash_config.partition.start + wr_data->off, &data32, sizeof(uint32_t)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err); - return err; - } - wr_data->off += sizeof(uint32_t); - - return err; -} - -static esp_err_t esp_core_dump_flash_write_start(void *priv) -{ - return ESP_OK; -} - -static esp_err_t esp_core_dump_flash_write_end(void *priv) -{ - core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; -#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG - union - { - uint8_t data8[16]; - uint32_t data32[4]; - } rom_data; - - esp_err_t err = spi_flash_read(s_core_flash_config.partition.start + 0, &rom_data, sizeof(rom_data)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err); - return err; - } else { - ESP_COREDUMP_LOG_PROCESS("Data from flash:"); - for (uint32_t i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { - ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]); - } - } -#endif - // write core dump CRC - ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x", wr_data->crc); - return esp_core_dump_flash_write_word(wr_data, wr_data->crc); -} - -static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) -{ - esp_err_t err = ESP_OK; - core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; - - uint32_t len = esp_core_dump_write_flash_padded(s_core_flash_config.partition.start + wr_data->off, data, data_len); - if (len != data_len) { - return ESP_FAIL; - } - - wr_data->off += len; - wr_data->crc = crc32_le(wr_data->crc, data, data_len); - - return err; -} - -void esp_core_dump_to_flash(XtExcFrame *frame) -{ - core_dump_write_config_t wr_cfg; - core_dump_write_flash_data_t wr_data; - - core_dump_crc_t crc = esp_core_dump_calc_flash_config_crc(); - if (s_core_flash_config.partition_config_crc != crc) { - ESP_COREDUMP_LOGE("Core dump flash config is corrupted! CRC=0x%x instead of 0x%x", crc, s_core_flash_config.partition_config_crc); - return; - } - // check that partition can hold at least core dump data length - if (s_core_flash_config.partition.start == 0 || s_core_flash_config.partition.size < sizeof(uint32_t)) { - ESP_COREDUMP_LOGE("Invalid flash partition config!"); - return; - } - - /* init non-OS flash access critical section */ - spi_flash_guard_set(&g_flash_guard_no_os_ops); - - memset(&wr_cfg, 0, sizeof(wr_cfg)); - wr_cfg.prepare = esp_core_dump_flash_write_prepare; - wr_cfg.start = esp_core_dump_flash_write_start; - wr_cfg.end = esp_core_dump_flash_write_end; - wr_cfg.write = esp_core_dump_flash_write_data; - wr_cfg.priv = &wr_data; - - ESP_COREDUMP_LOGI("Save core dump to flash..."); - esp_core_dump_write(frame, &wr_cfg); - ESP_COREDUMP_LOGI("Core dump has been saved to flash."); -} -#endif - -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART - -static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { - const static DRAM_ATTR char b64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i, j, a, b, c; - - for (i = j = 0; i < src_len; i += 3) { - a = src[i]; - b = i + 1 >= src_len ? 0 : src[i + 1]; - c = i + 2 >= src_len ? 0 : src[i + 2]; - - dst[j++] = b64[a >> 2]; - dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; - if (i + 1 < src_len) { - dst[j++] = b64[(b & 0x0F) << 2 | (c >> 6)]; - } - if (i + 2 < src_len) { - dst[j++] = b64[c & 0x3F]; - } - } - while (j % 4 != 0) { - dst[j++] = '='; - } - dst[j++] = '\0'; -} - -static esp_err_t esp_core_dump_uart_write_start(void *priv) -{ - esp_err_t err = ESP_OK; - ets_printf(DRAM_STR("================= CORE DUMP START =================\r\n")); - return err; -} - -static esp_err_t esp_core_dump_uart_write_end(void *priv) -{ - esp_err_t err = ESP_OK; - ets_printf(DRAM_STR("================= CORE DUMP END =================\r\n")); - return err; -} - -static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) -{ - esp_err_t err = ESP_OK; - char buf[64 + 4], *addr = data; - char *end = addr + data_len; - - while (addr < end) { - size_t len = end - addr; - if (len > 48) len = 48; - /* Copy to stack to avoid alignment restrictions. */ - char *tmp = buf + (sizeof(buf) - len); - memcpy(tmp, addr, len); - esp_core_dump_b64_encode((const uint8_t *)tmp, len, (uint8_t *)buf); - addr += len; - ets_printf(DRAM_STR("%s\r\n"), buf); - } - - return err; -} - -static int esp_core_dump_uart_get_char() { - int i; - uint32_t reg = (READ_PERI_REG(UART_STATUS_REG(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT; - if (reg) { - i = READ_PERI_REG(UART_FIFO_REG(0)); - } else { - i = -1; - } - return i; -} - -void esp_core_dump_to_uart(XtExcFrame *frame) -{ - core_dump_write_config_t wr_cfg; - uint32_t tm_end, tm_cur; - int ch; - - memset(&wr_cfg, 0, sizeof(wr_cfg)); - wr_cfg.prepare = NULL; - wr_cfg.start = esp_core_dump_uart_write_start; - wr_cfg.end = esp_core_dump_uart_write_end; - wr_cfg.write = esp_core_dump_uart_write_data; - wr_cfg.priv = NULL; - - //Make sure txd/rxd are enabled - // use direct reg access instead of gpio_pullup_dis which can cause exception when flash cache is disabled - REG_CLR_BIT(GPIO_PIN_REG_1, FUN_PU); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); - - ESP_COREDUMP_LOGI("Press Enter to print core dump to UART..."); - const int cpu_ticks_per_ms = esp_clk_cpu_freq() / 1000; - tm_end = xthal_get_ccount() / cpu_ticks_per_ms + CONFIG_ESP32_CORE_DUMP_UART_DELAY; - ch = esp_core_dump_uart_get_char(); - while (!(ch == '\n' || ch == '\r')) { - tm_cur = xthal_get_ccount() / cpu_ticks_per_ms; - if (tm_cur >= tm_end){ - break; - } - ch = esp_core_dump_uart_get_char(); - } - ESP_COREDUMP_LOGI("Print core dump to uart..."); - esp_core_dump_write(frame, &wr_cfg); - ESP_COREDUMP_LOGI("Core dump has been written to uart."); -} -#endif - -void esp_core_dump_init() -{ -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH - const esp_partition_t *core_part; - - ESP_COREDUMP_LOGI("Init core dump to flash"); - core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); - if (!core_part) { - ESP_COREDUMP_LOGE("No core dump partition found!"); - return; - } - ESP_COREDUMP_LOGI("Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size); - s_core_flash_config.partition.start = core_part->address; - s_core_flash_config.partition.size = core_part->size; - s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc(); -#endif -#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART - ESP_COREDUMP_LOGI("Init core dump to UART"); -#endif -} - -esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size) -{ - esp_err_t err; - const void *core_data; - spi_flash_mmap_handle_t core_data_handle; - - - if (out_addr == NULL || out_size == NULL) { - return ESP_ERR_INVALID_ARG; - } - - const esp_partition_t *core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); - if (!core_part) { - ESP_LOGE(TAG, "No core dump partition found!"); - return ESP_FAIL; - } - if (core_part->size < sizeof(uint32_t)) { - ESP_LOGE(TAG, "Too small core dump partition!"); - return ESP_FAIL; - } - - err = esp_partition_mmap(core_part, 0, sizeof(uint32_t), - SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err); - return err; - } - uint32_t *dw = (uint32_t *)core_data; - *out_size = *dw; - spi_flash_munmap(core_data_handle); - - // remap full core dump with CRC - err = esp_partition_mmap(core_part, 0, *out_size, - SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err); - return err; - } - uint32_t *crc = (uint32_t *)(((uint8_t *)core_data) + *out_size); - crc--; // Point to CRC field - // Calc CRC over core dump data except for CRC field - core_dump_crc_t cur_crc = crc32_le(0, (uint8_t const *)core_data, *out_size - sizeof(core_dump_crc_t)); - if (*crc != cur_crc) { - ESP_LOGE(TAG, "Core dump data CRC check failed: 0x%x -> 0x%x!", *crc, cur_crc); - spi_flash_munmap(core_data_handle); - return ESP_FAIL; - } - - spi_flash_munmap(core_data_handle); - - *out_addr = core_part->address; - return ESP_OK; -} -#endif diff --git a/components/esp32/linker.lf b/components/esp32/linker.lf index 53f4501af8..c9a1c275dd 100644 --- a/components/esp32/linker.lf +++ b/components/esp32/linker.lf @@ -1,7 +1,6 @@ [mapping] archive: libesp32.a entries: - core_dump (noflash_text) panic (noflash) [mapping] diff --git a/components/espcoredump/CMakeLists.txt b/components/espcoredump/CMakeLists.txt new file mode 100644 index 0000000000..a58b197288 --- /dev/null +++ b/components/espcoredump/CMakeLists.txt @@ -0,0 +1,11 @@ +set(COMPONENT_PRIV_INCLUDEDIRS "include_core_dump") +set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES) +set(COMPONENT_PRIV_REQUIRES spi_flash) +set(COMPONENT_ADD_LDFRAGMENTS linker.lf) +set(COMPONENT_SRCS "src/core_dump_common.c" + "src/core_dump_flash.c" + "src/core_dump_port.c" + "src/core_dump_uart.c") + +register_component() diff --git a/components/espcoredump/Kconfig b/components/espcoredump/Kconfig new file mode 100644 index 0000000000..991bf18f7b --- /dev/null +++ b/components/espcoredump/Kconfig @@ -0,0 +1,45 @@ +menu "Core dump" + + choice ESP32_COREDUMP_TO_FLASH_OR_UART + prompt "Data destination" + default ESP32_ENABLE_COREDUMP_TO_NONE + help + Select place to store core dump: flash, uart or none (to disable core dumps generation). + + If core dump is configured to be stored in flash and custom partition table is used add + corresponding entry to your CSV. For examples, please see predefined partition table CSV descriptions + in the components/partition_table directory. + + config ESP32_ENABLE_COREDUMP_TO_FLASH + bool "Flash" + select ESP32_ENABLE_COREDUMP + config ESP32_ENABLE_COREDUMP_TO_UART + bool "UART" + select ESP32_ENABLE_COREDUMP + config ESP32_ENABLE_COREDUMP_TO_NONE + bool "None" + endchoice + + config ESP32_ENABLE_COREDUMP + bool + default F + help + Enables/disable core dump module. + + config ESP32_CORE_DUMP_MAX_TASKS_NUM + int "Maximum number of tasks" + depends on ESP32_ENABLE_COREDUMP + default 64 + help + Maximum number of tasks snapshots in core dump. + + config ESP32_CORE_DUMP_UART_DELAY + int "Delay before print to UART" + depends on ESP32_ENABLE_COREDUMP_TO_UART + default 0 + help + Config delay (in ms) before printing core dump to UART. + Delay can be interrupted by pressing Enter key. + +endmenu + diff --git a/components/espcoredump/component.mk b/components/espcoredump/component.mk new file mode 100644 index 0000000000..0db2bfdd24 --- /dev/null +++ b/components/espcoredump/component.mk @@ -0,0 +1,4 @@ +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_SRCDIRS := src +COMPONENT_PRIV_INCLUDEDIRS := include_core_dump +COMPONENT_ADD_LDFRAGMENTS += linker.lf \ No newline at end of file diff --git a/components/espcoredump/espcoredump.py b/components/espcoredump/espcoredump.py index 47dabed381..fb79185f90 100755 --- a/components/espcoredump/espcoredump.py +++ b/components/espcoredump/espcoredump.py @@ -628,6 +628,7 @@ class ESPCoreDumpLoader(object): logging.warning("Skip task's (%x) stack %d bytes @ 0x%x. (Reason: %s)" % (tcb_addr, stack_len_aligned, stack_base, e)) core_off += stack_len_aligned try: + logging.info("Stack start_end: 0x%x @ 0x%x" % (stack_top, stack_end)) task_regs = self._get_registers_from_stack(data, stack_end > stack_top) except Exception as e: print(e) diff --git a/components/espcoredump/include/esp_core_dump.h b/components/espcoredump/include/esp_core_dump.h new file mode 100644 index 0000000000..5201f1e94d --- /dev/null +++ b/components/espcoredump/include/esp_core_dump.h @@ -0,0 +1,85 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef ESP_CORE_DUMP_H_ +#define ESP_CORE_DUMP_H_ + +/**************************************************************************************/ +/******************************** EXCEPTION MODE API **********************************/ +/**************************************************************************************/ + +/** + * @brief Initializes core dump module internal data. + * + * @note Should be called at system startup. + */ +void esp_core_dump_init(); + +/** + * @brief Saves core dump to flash. + * + * The structure of data stored in flash is as follows: + * + * | TOTAL_LEN | VERSION | TASKS_NUM | TCB_SIZE | + * | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 | + * . . . . + * . . . . + * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | + * | CRC32 | + * + * Core dump in flash consists of header and data for every task in the system at the moment of crash. + * For flash data integrity control CRC is used at the end of core the dump data. + * The structure of core dump data is described below in details. + * 1) Core dump starts with header: + * 1.1) TOTAL_LEN is total length of core dump data in flash including CRC. Size is 4 bytes. + * 1.2) VERSION field keeps 4 byte version of core dump. + * 1.2) TASKS_NUM is the number of tasks for which data are stored. Size is 4 bytes. + * 1.3) TCB_SIZE is the size of task's TCB structure. Size is 4 bytes. + * 2) Core dump header is followed by the data for every task in the system. + * Task data are started with task header: + * 2.1) TCB_ADDR is the address of TCB in memory. Size is 4 bytes. + * 2.2) STACK_TOP is the top of task's stack (address of the topmost stack item). Size is 4 bytes. + * 2.2) STACK_END is the end of task's stack (address from which task's stack starts). Size is 4 bytes. + * 3) Task header is followed by TCB data. Size is TCB_SIZE bytes. + * 4) Task's stack is placed after TCB data. Size is (STACK_END - STACK_TOP) bytes. + * 5) CRC is placed at the end of the data. + */ +void esp_core_dump_to_flash(); + +/** + * @brief Print base64-encoded core dump to UART. + * + * The structure of core dump data is the same as for data stored in flash (@see esp_core_dump_to_flash) with some notes: + * 1) CRC is not present in core dump printed to UART. + * 2) Since CRC is omitted TOTAL_LEN does not include its size. + * 3) Printed base64 data are surrounded with special messages to help user recognize the start and end of actual data. + */ +void esp_core_dump_to_uart(); + + +/**************************************************************************************/ +/*********************************** USER MODE API ************************************/ +/**************************************************************************************/ + +/** + * @brief Retrieves address and size of coredump data in flash. + * This function is always available, even when core dump is disabled in menuconfig. + * + * @param out_addr pointer to store image address in flash. + * @param out_size pointer to store image size in flash (including CRC). In bytes. + * + * @return ESP_OK on success, otherwise \see esp_err_t + */ +esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size); + +#endif diff --git a/components/espcoredump/include_core_dump/esp_core_dump_priv.h b/components/espcoredump/include_core_dump/esp_core_dump_priv.h new file mode 100644 index 0000000000..a13dd1707d --- /dev/null +++ b/components/espcoredump/include_core_dump/esp_core_dump_priv.h @@ -0,0 +1,105 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef ESP_CORE_DUMP_H_ +#define ESP_CORE_DUMP_H_ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" +#include "esp_log.h" + +#define ESP_COREDUMP_LOG( level, format, ... ) if (LOG_LOCAL_LEVEL >= level) { ets_printf(DRAM_STR(format), esp_log_early_timestamp(), (const char *)TAG, ##__VA_ARGS__); } +#define ESP_COREDUMP_LOGE( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_ERROR, LOG_FORMAT(E, format), ##__VA_ARGS__) +#define ESP_COREDUMP_LOGW( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_WARN, LOG_FORMAT(W, format), ##__VA_ARGS__) +#define ESP_COREDUMP_LOGI( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_INFO, LOG_FORMAT(I, format), ##__VA_ARGS__) +#define ESP_COREDUMP_LOGD( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_DEBUG, LOG_FORMAT(D, format), ##__VA_ARGS__) +#define ESP_COREDUMP_LOGV( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_VERBOSE, LOG_FORMAT(V, format), ##__VA_ARGS__) + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH +#define ESP_COREDUMP_LOG_PROCESS( format, ... ) ESP_COREDUMP_LOGD(format, ##__VA_ARGS__) +#else +#define ESP_COREDUMP_LOG_PROCESS( format, ... ) do{/*(__VA_ARGS__);*/}while(0) +#endif + +#define COREDUMP_MAX_TASK_STACK_SIZE (64*1024) +#define COREDUMP_VERSION 1 + +typedef uint32_t core_dump_crc_t; + +#if CONFIG_ESP32_ENABLE_COREDUMP + +typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len); +typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv); +typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len); + +/** core dump emitter control structure */ +typedef struct _core_dump_write_config_t +{ + // this function is called before core dump data writing + // used for sanity checks + esp_core_dump_write_prepare_t prepare; + // this function is called at the beginning of data writing + esp_core_dump_write_start_t start; + // this function is called when all dump data are written + esp_core_dump_write_end_t end; + // this function is called to write data chunk + esp_core_dump_flash_write_data_t write; + // number of tasks with corrupted TCBs + uint32_t bad_tasks_num; + // pointer to data which are specific for particular core dump emitter + void * priv; +} core_dump_write_config_t; + +/** core dump data header */ +typedef struct _core_dump_header_t +{ + uint32_t data_len; // data length + uint32_t version; // core dump struct version + uint32_t tasks_num; // number of tasks + uint32_t tcb_sz; // size of TCB +} core_dump_header_t; + +/** core dump task data header */ +typedef struct _core_dump_task_header_t +{ + void * tcb_addr; // TCB address + uint32_t stack_start; // stack start address + uint32_t stack_end; // stack end address +} core_dump_task_header_t; + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + +// Core dump flash init function +void esp_core_dump_flash_init(); + +#endif + +// Common core dump write function +void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg); + +// Gets RTOS tasks snapshot +uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks, + const uint32_t snapshot_size, uint32_t* const tcb_sz); + +// Checks TCB consistency +bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz); + +bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, uint32_t tcb_sz); + +bool esp_core_dump_process_stack(core_dump_task_header_t* task_snaphort, uint32_t *length); + +#endif + +#endif diff --git a/components/espcoredump/linker.lf b/components/espcoredump/linker.lf new file mode 100644 index 0000000000..71072ab0a6 --- /dev/null +++ b/components/espcoredump/linker.lf @@ -0,0 +1,6 @@ +[mapping] +archive: libespcoredump.a +entries: + core_dump_uart (noflash_text) + core_dump_flash (noflash_text) + core_dump_common (noflash_text) \ No newline at end of file diff --git a/components/espcoredump/src/core_dump_common.c b/components/espcoredump/src/core_dump_common.c new file mode 100644 index 0000000000..7e00c1b40b --- /dev/null +++ b/components/espcoredump/src/core_dump_common.c @@ -0,0 +1,217 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include "rom/crc.h" +#include "esp_panic.h" +#include "esp_partition.h" +#include "esp_core_dump_priv.h" + +const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_common"; + +#if CONFIG_ESP32_ENABLE_COREDUMP + +static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_t *write_cfg) +{ + esp_err_t err; + core_dump_task_header_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM]; + uint32_t tcb_sz, task_num, tcb_sz_padded; + bool task_is_valid = false; + uint32_t data_len = 0, i; + union + { + core_dump_header_t hdr; + core_dump_task_header_t task_hdr; + } dump_data; + + task_num = esp_core_dump_get_tasks_snapshot(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM, &tcb_sz); + ESP_COREDUMP_LOGI("Found tasks: (%d)!", task_num); + + // Take TCB padding into account, actual TCB size will be stored in header + if (tcb_sz % sizeof(uint32_t)) + tcb_sz_padded = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t); + else + tcb_sz_padded = tcb_sz; + + // Verifies all tasks in the snapshot + for (i = 0; i < task_num; i++) { + task_is_valid = esp_core_dump_process_tcb(frame, &tasks[i], tcb_sz); + // Check if task tcb is corrupted + if (!task_is_valid) { + write_cfg->bad_tasks_num++; + continue; + } else { + data_len += (tcb_sz_padded + sizeof(core_dump_task_header_t)); + } + uint32_t len = 0; + task_is_valid = esp_core_dump_process_stack(&tasks[i], &len); + if (task_is_valid) { + // Increase core dump size by task stack size + data_len += len; + } else { + // If task tcb is ok but stack is corrupted + write_cfg->bad_tasks_num++; + } + } + // Add core dump header size + data_len += sizeof(core_dump_header_t); + ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu (%d %d)", data_len, task_num, write_cfg->bad_tasks_num); + + // Prepare write + if (write_cfg->prepare) { + err = write_cfg->prepare(write_cfg->priv, &data_len); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err); + return err; + } + } + // Write start + if (write_cfg->start) { + err = write_cfg->start(write_cfg->priv); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err); + return err; + } + } + // Write header + dump_data.hdr.data_len = data_len; + dump_data.hdr.version = COREDUMP_VERSION; + dump_data.hdr.tasks_num = task_num - write_cfg->bad_tasks_num; + dump_data.hdr.tcb_sz = tcb_sz; + err = write_cfg->write(write_cfg->priv, &dump_data, sizeof(core_dump_header_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err); + return err; + } + // Write tasks + for (i = 0; i < task_num; i++) { + if (!esp_tcb_addr_is_sane((uint32_t)tasks[i].tcb_addr, tcb_sz)) { + ESP_COREDUMP_LOG_PROCESS("Skip TCB with bad addr %x!", tasks[i].tcb_addr); + continue; + } + ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].tcb_addr); + // Save TCB address, stack base and stack top addr + dump_data.task_hdr.tcb_addr = tasks[i].tcb_addr; + dump_data.task_hdr.stack_start = tasks[i].stack_start; + dump_data.task_hdr.stack_end = tasks[i].stack_end; + err = write_cfg->write(write_cfg->priv, (void*)&dump_data, sizeof(core_dump_task_header_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err); + return err; + } + // Save TCB + err = write_cfg->write(write_cfg->priv, tasks[i].tcb_addr, tcb_sz); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err); + return err; + } + // Save task stack + if (tasks[i].stack_start != 0 && tasks[i].stack_end != 0) { + err = write_cfg->write(write_cfg->priv, (void*)tasks[i].stack_start, + tasks[i].stack_end - tasks[i].stack_start); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err); + return err; + } + } else { + ESP_COREDUMP_LOG_PROCESS("Skip corrupted task %x stack!", tasks[i].tcb_addr); + } + } + + // write end + if (write_cfg->end) { + err = write_cfg->end(write_cfg->priv); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err); + return err; + } + } + if (write_cfg->bad_tasks_num) { + ESP_COREDUMP_LOGE("Skipped %d tasks with bad TCB!", write_cfg->bad_tasks_num); + } + return err; +} + +inline void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg) +{ + esp_err_t err = esp_core_dump_write_binary(frame, write_cfg); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Core dump write binary failed with error: %d", err); + } +} + +#endif + +void esp_core_dump_init() +{ +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + esp_core_dump_flash_init(); +#endif +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART + ESP_COREDUMP_LOGI("Init core dump to UART"); +#endif +} + +esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size) +{ + esp_err_t err; + const void *core_data; + spi_flash_mmap_handle_t core_data_handle; + + if (out_addr == NULL || out_size == NULL) { + return ESP_ERR_INVALID_ARG; + } + + const esp_partition_t *core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); + if (!core_part) { + ESP_LOGE(TAG, "No core dump partition found!"); + return ESP_FAIL; + } + if (core_part->size < sizeof(uint32_t)) { + ESP_LOGE(TAG, "Too small core dump partition!"); + return ESP_FAIL; + } + + err = esp_partition_mmap(core_part, 0, sizeof(uint32_t), + SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err); + return err; + } + uint32_t *dw = (uint32_t *)core_data; + *out_size = *dw; + spi_flash_munmap(core_data_handle); + + // remap full core dump with CRC + err = esp_partition_mmap(core_part, 0, *out_size, + SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err); + return err; + } + uint32_t *crc = (uint32_t *)(((uint8_t *)core_data) + *out_size); + crc--; // Point to CRC field + // Calc CRC over core dump data except for CRC field + core_dump_crc_t cur_crc = crc32_le(0, (uint8_t const *)core_data, *out_size - sizeof(core_dump_crc_t)); + if (*crc != cur_crc) { + ESP_LOGE(TAG, "Core dump data CRC check failed: 0x%x -> 0x%x!", *crc, cur_crc); + spi_flash_munmap(core_data_handle); + return ESP_FAIL; + } + + spi_flash_munmap(core_data_handle); + + *out_addr = core_part->address; + return ESP_OK; +} diff --git a/components/espcoredump/src/core_dump_flash.c b/components/espcoredump/src/core_dump_flash.c new file mode 100644 index 0000000000..0aa23e9663 --- /dev/null +++ b/components/espcoredump/src/core_dump_flash.c @@ -0,0 +1,232 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include "rom/crc.h" +#include "esp_partition.h" +#include "esp_core_dump_priv.h" + +const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_flash"; + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + +typedef struct _core_dump_write_flash_data_t +{ + uint32_t off; // current offset in partition + core_dump_crc_t crc; // CRC of dumped data +} core_dump_write_flash_data_t; + +typedef struct _core_dump_partition_t +{ + // core dump partition start + uint32_t start; + // core dump partition size + uint32_t size; +} core_dump_partition_t; + +typedef struct _core_dump_flash_config_t +{ + // core dump partition config + core_dump_partition_t partition; + // CRC of core dump partition config + core_dump_crc_t partition_config_crc; +} core_dump_flash_config_t; + +// core dump flash data +static core_dump_flash_config_t s_core_flash_config; + +static inline core_dump_crc_t esp_core_dump_calc_flash_config_crc(void) +{ + return crc32_le(0, (uint8_t const *)&s_core_flash_config.partition, sizeof(s_core_flash_config.partition)); +} + +void esp_core_dump_flash_init() +{ + const esp_partition_t *core_part; + + ESP_COREDUMP_LOGI("Init core dump to flash"); + core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL); + if (!core_part) { + ESP_COREDUMP_LOGE("No core dump partition found!"); + return; + } + ESP_COREDUMP_LOGI("Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size); + s_core_flash_config.partition.start = core_part->address; + s_core_flash_config.partition.size = core_part->size; + s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc(); +} + +static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size) +{ + esp_err_t err; + uint32_t data_len = 0, k, len; + union + { + uint8_t data8[4]; + uint32_t data32; + } rom_data; + + data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t); + + assert(off >= s_core_flash_config.partition.start); + assert((off + data_len + (data_size % sizeof(uint32_t) ? sizeof(uint32_t) : 0)) <= + s_core_flash_config.partition.start + s_core_flash_config.partition.size); + + err = spi_flash_write(off, data, data_len); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err); + return 0; + } + + len = data_size % sizeof(uint32_t); + if (len) { + // write last bytes with padding, actual TCB len can be retrieved by esptool from core dump header + rom_data.data32 = 0; + for (k = 0; k < len; k++) { + rom_data.data8[k] = *(data + data_len + k); + } + err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err); + return 0; + } + data_len += sizeof(uint32_t); + } + + return data_len; +} + +static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) +{ + esp_err_t err; + uint32_t sec_num; + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + + // check for available space in partition + if ((*data_len + sizeof(uint32_t)) > s_core_flash_config.partition.size) { + ESP_COREDUMP_LOGE("Not enough space to save core dump!"); + return ESP_ERR_NO_MEM; + } + // add space for CRC + *data_len += sizeof(core_dump_crc_t); + + memset(wr_data, 0, sizeof(*wr_data)); + + sec_num = *data_len / SPI_FLASH_SEC_SIZE; + if (*data_len % SPI_FLASH_SEC_SIZE) { + sec_num++; + } + assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size); + err = spi_flash_erase_range(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err); + return err; + } + return err; +} + +static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr_data, uint32_t word) +{ + esp_err_t err = ESP_OK; + uint32_t data32 = word; + + assert(wr_data->off + sizeof(uint32_t) <= s_core_flash_config.partition.size); + err = spi_flash_write(s_core_flash_config.partition.start + wr_data->off, &data32, sizeof(uint32_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err); + return err; + } + wr_data->off += sizeof(uint32_t); + + return err; +} + +static esp_err_t esp_core_dump_flash_write_start(void *priv) +{ + return ESP_OK; +} + +static esp_err_t esp_core_dump_flash_write_end(void *priv) +{ + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; +#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG + union + { + uint8_t data8[16]; + uint32_t data32[4]; + } rom_data; + + esp_err_t err = spi_flash_read(s_core_flash_config.partition.start + 0, &rom_data, sizeof(rom_data)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err); + return err; + } else { + ESP_COREDUMP_LOG_PROCESS("Data from flash:"); + for (uint32_t i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) { + ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]); + } + } +#endif + // write core dump CRC + ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x", wr_data->crc); + return esp_core_dump_flash_write_word(wr_data, wr_data->crc); +} + +static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len) +{ + esp_err_t err = ESP_OK; + core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv; + + uint32_t len = esp_core_dump_write_flash_padded(s_core_flash_config.partition.start + wr_data->off, data, data_len); + if (len != data_len) { + return ESP_FAIL; + } + + wr_data->off += len; + wr_data->crc = crc32_le(wr_data->crc, data, data_len); + + return err; +} + +void esp_core_dump_to_flash(XtExcFrame *frame) +{ + core_dump_write_config_t wr_cfg; + core_dump_write_flash_data_t wr_data; + + core_dump_crc_t crc = esp_core_dump_calc_flash_config_crc(); + if (s_core_flash_config.partition_config_crc != crc) { + ESP_COREDUMP_LOGE("Core dump flash config is corrupted! CRC=0x%x instead of 0x%x", crc, s_core_flash_config.partition_config_crc); + return; + } + // check that partition can hold at least core dump data length + if (s_core_flash_config.partition.start == 0 || s_core_flash_config.partition.size < sizeof(uint32_t)) { + ESP_COREDUMP_LOGE("Invalid flash partition config!"); + return; + } + + /* init non-OS flash access critical section */ + spi_flash_guard_set(&g_flash_guard_no_os_ops); + + memset(&wr_cfg, 0, sizeof(wr_cfg)); + wr_cfg.prepare = esp_core_dump_flash_write_prepare; + wr_cfg.start = esp_core_dump_flash_write_start; + wr_cfg.end = esp_core_dump_flash_write_end; + wr_cfg.write = esp_core_dump_flash_write_data; + wr_cfg.priv = &wr_data; + + ESP_COREDUMP_LOGI("Save core dump to flash..."); + esp_core_dump_write((void*)frame, &wr_cfg); + ESP_COREDUMP_LOGI("Core dump has been saved to flash."); +} +#endif + diff --git a/components/espcoredump/src/core_dump_port.c b/components/espcoredump/src/core_dump_port.c new file mode 100644 index 0000000000..e045b19a4e --- /dev/null +++ b/components/espcoredump/src/core_dump_port.c @@ -0,0 +1,103 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include "esp_panic.h" +#include "esp_core_dump_priv.h" + +const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port"; + +#if CONFIG_ESP32_ENABLE_COREDUMP + +inline bool esp_task_stack_start_is_sane(uint32_t sp) +{ + return !(sp < 0x3ffae010UL || sp > 0x3fffffffUL); +} + +inline bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz) +{ + //TODO: currently core dump supports TCBs in DRAM only, external SRAM not supported yet + return !(addr < 0x3ffae000UL || (addr + sz) > 0x40000000UL); +} + +uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks, + const uint32_t snapshot_size, uint32_t* const tcb_sz) +{ + uint32_t task_num = (uint32_t)uxTaskGetSnapshotAll((TaskSnapshot_t*)tasks, (UBaseType_t)snapshot_size, (UBaseType_t*)tcb_sz); + return task_num; +} + +bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, uint32_t tcb_sz) +{ + XtExcFrame *exc_frame = (XtExcFrame*)frame; + + if (!esp_tcb_addr_is_sane((uint32_t)task_snaphort->tcb_addr, tcb_sz)) { + ESP_COREDUMP_LOG_PROCESS("Bad TCB addr %x!", task_snaphort->tcb_addr); + return false; + } + if (task_snaphort->tcb_addr == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { + // Set correct stack top for current task + task_snaphort->stack_start = (uint32_t)exc_frame; + // This field is not initialized for crashed task, but stack frame has the structure of interrupt one, + // so make workaround to allow espcoredump to parse it properly. + if (exc_frame->exit == 0) + exc_frame->exit = -1; + ESP_COREDUMP_LOG_PROCESS("Current task %x EXIT/PC/PS/A0/SP %x %x %x %x %x", + task_snaphort->tcb_addr, exc_frame->exit, exc_frame->pc, exc_frame->ps, exc_frame->a0, exc_frame->a1); + } + else { + XtSolFrame *task_frame = (XtSolFrame *)task_snaphort->stack_start; + if (task_frame->exit == 0) { + ESP_COREDUMP_LOG_PROCESS("Task %x EXIT/PC/PS/A0/SP %x %x %x %x %x", + task_snaphort->tcb_addr, task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1); + } + else { +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH + XtExcFrame *task_frame2 = (XtExcFrame *)task_snaphort->stack_start; + ESP_COREDUMP_LOG_PROCESS("Task %x EXIT/PC/PS/A0/SP %x %x %x %x %x", + task_snaphort->tcb_addr, task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1); +#endif + } + } + return true; +} + +bool esp_core_dump_process_stack(core_dump_task_header_t* task_snaphort, uint32_t *length) +{ + uint32_t len = 0; + bool task_is_valid = false; + len = (uint32_t)task_snaphort->stack_end - (uint32_t)task_snaphort->stack_start; + // Check task's stack + if (!esp_stack_ptr_is_sane(task_snaphort->stack_start) || + !esp_task_stack_start_is_sane((uint32_t)task_snaphort->stack_end) || + (len > COREDUMP_MAX_TASK_STACK_SIZE)) { + // Check if current task stack corrupted + if (task_snaphort->tcb_addr == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) { + ESP_COREDUMP_LOG_PROCESS("Crashed task will be skipped!"); + } + ESP_COREDUMP_LOG_PROCESS("Corrupted TCB %x: stack len %lu, top %x, end %x!", + task_snaphort->tcb_addr, len, task_snaphort->stack_start, task_snaphort->stack_end); + task_snaphort->tcb_addr = 0; // make TCB addr invalid to skip it in dump + task_is_valid = false; + } else { + ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, + task_snaphort->stack_start, task_snaphort->stack_end); + // Take stack padding into account + *length = (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1); + task_is_valid = true; + } + return task_is_valid; +} + +#endif diff --git a/components/espcoredump/src/core_dump_uart.c b/components/espcoredump/src/core_dump_uart.c new file mode 100644 index 0000000000..2c1f4cbfc2 --- /dev/null +++ b/components/espcoredump/src/core_dump_uart.c @@ -0,0 +1,129 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include "soc/uart_reg.h" +#include "soc/io_mux_reg.h" +#include "driver/gpio.h" +#include "esp_clk.h" +#include "esp_core_dump_priv.h" + +const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_uart"; + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART + +static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8_t *dst) { + const static DRAM_ATTR char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 0x0F) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 0x3F]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} + +static esp_err_t esp_core_dump_uart_write_start(void *priv) +{ + esp_err_t err = ESP_OK; + ets_printf(DRAM_STR("================= CORE DUMP START =================\r\n")); + return err; +} + +static esp_err_t esp_core_dump_uart_write_end(void *priv) +{ + esp_err_t err = ESP_OK; + ets_printf(DRAM_STR("================= CORE DUMP END =================\r\n")); + return err; +} + +static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) +{ + esp_err_t err = ESP_OK; + char buf[64 + 4], *addr = data; + char *end = addr + data_len; + + while (addr < end) { + size_t len = end - addr; + if (len > 48) len = 48; + /* Copy to stack to avoid alignment restrictions. */ + char *tmp = buf + (sizeof(buf) - len); + memcpy(tmp, addr, len); + esp_core_dump_b64_encode((const uint8_t *)tmp, len, (uint8_t *)buf); + addr += len; + ets_printf(DRAM_STR("%s\r\n"), buf); + } + + return err; +} + +static int esp_core_dump_uart_get_char() { + int i; + uint32_t reg = (READ_PERI_REG(UART_STATUS_REG(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT; + if (reg) { + i = READ_PERI_REG(UART_FIFO_REG(0)); + } else { + i = -1; + } + return i; +} + +void esp_core_dump_to_uart(XtExcFrame *frame) +{ + core_dump_write_config_t wr_cfg; + uint32_t tm_end, tm_cur; + int ch; + + memset(&wr_cfg, 0, sizeof(wr_cfg)); + wr_cfg.prepare = NULL; + wr_cfg.start = esp_core_dump_uart_write_start; + wr_cfg.end = esp_core_dump_uart_write_end; + wr_cfg.write = esp_core_dump_uart_write_data; + wr_cfg.priv = NULL; + + //Make sure txd/rxd are enabled + // use direct reg access instead of gpio_pullup_dis which can cause exception when flash cache is disabled + REG_CLR_BIT(GPIO_PIN_REG_1, FUN_PU); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); + + ESP_COREDUMP_LOGI("Press Enter to print core dump to UART..."); + const int cpu_ticks_per_ms = esp_clk_cpu_freq() / 1000; + tm_end = xthal_get_ccount() / cpu_ticks_per_ms + CONFIG_ESP32_CORE_DUMP_UART_DELAY; + ch = esp_core_dump_uart_get_char(); + while (!(ch == '\n' || ch == '\r')) { + tm_cur = xthal_get_ccount() / cpu_ticks_per_ms; + if (tm_cur >= tm_end){ + break; + } + ch = esp_core_dump_uart_get_char(); + } + ESP_COREDUMP_LOGI("Print core dump to uart..."); + esp_core_dump_write((void*)frame, &wr_cfg); + ESP_COREDUMP_LOGI("Core dump has been written to uart."); +} +#endif diff --git a/components/espcoredump/test/CMakeLists.txt b/components/espcoredump/test/CMakeLists.txt index 884ca8b6da..6704c238b9 100644 --- a/components/espcoredump/test/CMakeLists.txt +++ b/components/espcoredump/test/CMakeLists.txt @@ -1,6 +1,8 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity) - -register_component() \ No newline at end of file +if(TESTS_ALL EQUAL 1) + message("not linking coredump test from CI.") +else() + set(COMPONENT_SRCDIRS ".") + set(COMPONENT_ADD_INCLUDEDIRS ".") + set(COMPONENT_REQUIRES unity nvs_flash) + register_component() +endif() \ No newline at end of file diff --git a/components/espcoredump/test/component.mk b/components/espcoredump/test/component.mk index e69de29bb2..3db0ee499f 100644 --- a/components/espcoredump/test/component.mk +++ b/components/espcoredump/test/component.mk @@ -0,0 +1,13 @@ +ifeq ($(TESTS_ALL),1) + $(info not linking coredump tests from CI) +else + COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive + COMPONENT_SRCDIRS := . + COMPONENT_ADD_INCLUDEDIRS := . + COMPONENT_PRIV_INCLUDEDIRS := . + COMPONENT_REQUIRES := unity nvs_flash +endif # TESTS_ALL + + + + diff --git a/components/espcoredump/test/test_core_dump.c b/components/espcoredump/test/test_core_dump.c index 41137f9302..cbc2328cee 100644 --- a/components/espcoredump/test/test_core_dump.c +++ b/components/espcoredump/test/test_core_dump.c @@ -11,7 +11,7 @@ #include "freertos/task.h" #include "esp_system.h" #include "nvs_flash.h" - +#include "unity.h" // task crash indicators #define TCI_NULL_PTR 0x1 @@ -96,7 +96,7 @@ void failed_assert_task(void *pvParameter) fflush(stdout); } -void app_main() +TEST_CASE("verify coredump functionality", "[coredump][ignore]") { nvs_flash_init(); xTaskCreate(&bad_ptr_task, "bad_ptr_task", 2048, NULL, 5, NULL);