diff --git a/components/espcoredump/CMakeLists.txt b/components/espcoredump/CMakeLists.txt index 110e2d9554..b5cc82410c 100644 --- a/components/espcoredump/CMakeLists.txt +++ b/components/espcoredump/CMakeLists.txt @@ -5,20 +5,23 @@ "src/core_dump_elf.c" "src/core_dump_binary.c") +set(includes "include") set(priv_includes "include_core_dump") idf_build_get_property(target IDF_TARGET) if(CONFIG_IDF_TARGET_ARCH_XTENSA) - set(srcs ${srcs} "src/port/xtensa/core_dump_port.c") - set(priv_includes ${priv_includes} "include_core_dump/port/xtensa") + list(APPEND srcs "src/port/xtensa/core_dump_port.c") + list(APPEND includes "include/port/xtensa") + list(APPEND priv_includes "include_core_dump/port/xtensa") elseif(CONFIG_IDF_TARGET_ARCH_RISCV) - set(srcs ${srcs} "src/port/riscv/core_dump_port.c") - set(priv_includes ${priv_includes} "include_core_dump/port/riscv") + list(APPEND srcs "src/port/riscv/core_dump_port.c") + list(APPEND includes "include/port/riscv") + list(APPEND priv_includes "include_core_dump/port/riscv") endif() idf_component_register(SRCS ${srcs} - INCLUDE_DIRS "include" + INCLUDE_DIRS ${includes} PRIV_INCLUDE_DIRS ${priv_includes} LDFRAGMENTS linker.lf PRIV_REQUIRES spi_flash app_update mbedtls esp_rom soc) diff --git a/components/espcoredump/component.mk b/components/espcoredump/component.mk index 8a2c1d439f..439f80a44d 100644 --- a/components/espcoredump/component.mk +++ b/components/espcoredump/component.mk @@ -5,10 +5,12 @@ COMPONENT_ADD_LDFRAGMENTS += linker.lf ifdef CONFIG_IDF_TARGET_ARCH_XTENSA COMPONENT_SRCDIRS += src/port/xtensa + COMPONENT_ADD_INCLUDEDIRS += include/port/xtensa COMPONENT_PRIV_INCLUDEDIRS += include_core_dump/port/xtensa endif ifdef CONFIG_IDF_TARGET_ARCH_RISCV COMPONENT_SRCDIRS += src/port/riscv + COMPONENT_ADD_INCLUDEDIRS += include/port/riscv COMPONENT_PRIV_INCLUDEDIRS += include_core_dump/port/riscv endif diff --git a/components/espcoredump/include/esp_core_dump.h b/components/espcoredump/include/esp_core_dump.h index 7854d4a51d..7d41b97e57 100644 --- a/components/espcoredump/include/esp_core_dump.h +++ b/components/espcoredump/include/esp_core_dump.h @@ -17,11 +17,37 @@ #include #include "esp_err.h" #include "esp_private/panic_internal.h" +#include "esp_core_dump_summary_extra_info.h" #ifdef __cplusplus extern "C" { #endif +#define APP_ELF_SHA256_SZ (CONFIG_APP_RETRIEVE_LEN_ELF_SHA + 1) + +/** + * @brief Backtrace information + */ +typedef struct { + uint32_t bt[16]; /*!< Backtrace (array of PC) */ + uint32_t depth; /*!< Number of backtrace entries */ + bool corrupted; /*!< Status flag for backtrace is corrupt or not */ +} esp_core_dump_bt_info_t; + +/** + * @brief Core dump summary, Most meaningful contents of the core dump + * are accommodated in this structure + */ +typedef struct { + uint32_t exc_tcb; /*!< TCB pointer to the task causing exception */ + char exc_task[16]; /*!< Name of the task that caused exception */ + uint32_t exc_pc; /*!< Program counter for exception */ + esp_core_dump_bt_info_t exc_bt_info; /*!< Backtrace information for task causing exception */ + uint32_t core_dump_version; /*!< Core dump version */ + uint8_t app_elf_sha256[APP_ELF_SHA256_SZ]; /*!< Crashing application's SHA256 sum as a string */ + esp_core_dump_summary_extra_info_t ex_info; /*!< Architecture specific extra data */ +} esp_core_dump_summary_t; + /**************************************************************************************/ /******************************** EXCEPTION MODE API **********************************/ /**************************************************************************************/ @@ -111,6 +137,15 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size); */ esp_err_t esp_core_dump_image_erase(void); +/** + * @brief Get the summary of a core dump. This function works only with ELF format core dumps. + * + * @param summary Summary of the core dump + * + * @return ESP_OK on success, otherwise \see esp_err_t + */ +esp_err_t esp_core_dump_get_summary(esp_core_dump_summary_t *summary); + #ifdef __cplusplus } #endif diff --git a/components/espcoredump/include/port/riscv/esp_core_dump_summary_extra_info.h b/components/espcoredump/include/port/riscv/esp_core_dump_summary_extra_info.h new file mode 100644 index 0000000000..510fdc06af --- /dev/null +++ b/components/espcoredump/include/port/riscv/esp_core_dump_summary_extra_info.h @@ -0,0 +1,35 @@ +// Copyright 2021 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. +#pragma once +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief RISC-V architecture specific extra information + */ +typedef struct { + uint32_t mstatus; /* Machine Status */ + uint32_t mtvec; /* Machine Trap-Vector Base Address */ + uint32_t mcause; /* Machine Trap Cause */ + uint32_t mtval; /* Machine Trap Value */ + uint32_t exc_a[8]; /*!< a register set when the exception caused */ +} esp_core_dump_summary_extra_info_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/espcoredump/include/port/xtensa/esp_core_dump_summary_extra_info.h b/components/espcoredump/include/port/xtensa/esp_core_dump_summary_extra_info.h new file mode 100644 index 0000000000..3db7a70be8 --- /dev/null +++ b/components/espcoredump/include/port/xtensa/esp_core_dump_summary_extra_info.h @@ -0,0 +1,38 @@ +// Copyright 2021 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. +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define EPCx_REGISTER_COUNT XCHAL_NUM_INTLEVELS + +/** + * @brief Xtensa architecture specific extra information + */ +typedef struct { + uint32_t exc_cause; /*!< Cause of exception */ + uint32_t exc_vaddr; /*!< Virtual address of exception */ + uint32_t exc_a[16]; /*!< a register set when the exception caused */ + uint32_t epcx[EPCx_REGISTER_COUNT]; /*!< PC register address at exception level(1 to 7) */ + uint8_t epcx_reg_bits; /*!< Bit mask of available EPCx registers */ +} esp_core_dump_summary_extra_info_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/espcoredump/include_core_dump/esp_core_dump_port.h b/components/espcoredump/include_core_dump/esp_core_dump_port.h index 1dd6cd7ab9..5c0058ca49 100644 --- a/components/espcoredump/include_core_dump/esp_core_dump_port.h +++ b/components/espcoredump/include_core_dump/esp_core_dump_port.h @@ -30,6 +30,7 @@ #include "esp_app_format.h" #include "esp_core_dump_types.h" #include "esp_core_dump_port_impl.h" +#include "esp_core_dump.h" #ifdef __cplusplus extern "C" { @@ -164,6 +165,33 @@ void esp_core_dump_port_set_crashed_tcb(uint32_t handle); */ uint32_t esp_core_dump_get_extra_info(void **info); +/** + * @brief Parse extra information into summary + * + * @param summary Pointer to core dump summary structure + * @param ei_data Pointer to data of EXTRA_INFO note read from flash + */ +void esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t *summary, void *ei_data); + +/** + * @brief Parse exception registers into summary + * + * @param summary Pointer to core dump summary structure + * @param stack_data Pointer to data of crashed task's stack read from flash + */ +void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void *stack_data); + +/** + * @brief Parse backtrace into bt_info + * + * @param bt_info Pointer to store backtrace info + * @param vaddr Pointer to crashed task's stack vaddr + * @param paddr Pointe to crashed task's stack paddr + * @param stack_size Stack size + */ +void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info, const void *vaddr, + const void *paddr, uint32_t stack_size); + #ifdef __cplusplus } #endif diff --git a/components/espcoredump/src/core_dump_elf.c b/components/espcoredump/src/core_dump_elf.c index 5c6bf1eb85..d0d31a978f 100644 --- a/components/espcoredump/src/core_dump_elf.c +++ b/components/espcoredump/src/core_dump_elf.c @@ -15,6 +15,8 @@ #include "esp_attr.h" #include "esp_partition.h" #include "esp_ota_ops.h" +#include "esp_spi_flash.h" +#include "esp_flash_encrypt.h" #include "sdkconfig.h" #include "core_dump_checksum.h" #include "core_dump_elf.h" @@ -644,4 +646,131 @@ esp_err_t esp_core_dump_write_elf(core_dump_write_config_t *write_cfg) return err; } +/* Below are the helper function to parse the core dump ELF stored in flash */ + +static esp_err_t elf_core_dump_image_mmap(spi_flash_mmap_handle_t* core_data_handle, const void **map_addr) +{ + size_t out_size; + assert (core_data_handle); + assert(map_addr); + + /* Find the partition that could potentially contain a (previous) core dump. */ + const esp_partition_t *core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, + ESP_PARTITION_SUBTYPE_DATA_COREDUMP, + NULL); + if (!core_part) { + ESP_COREDUMP_LOGE("Core dump partition not found!"); + return ESP_ERR_NOT_FOUND; + } + if (core_part->size < sizeof(uint32_t)) { + ESP_COREDUMP_LOGE("Core dump partition too small!"); + return ESP_ERR_INVALID_SIZE; + } + /* Data read from the mmapped core dump partition will be garbage if flash + * encryption is enabled in hardware and core dump partition is not encrypted + */ + if (esp_flash_encryption_enabled() && !core_part->encrypted) { + ESP_COREDUMP_LOGE("Flash encryption enabled in hardware and core dump partition is not encrypted!"); + return ESP_ERR_NOT_SUPPORTED; + } + /* Read the size of the core dump file from the partition */ + esp_err_t ret = esp_partition_read(core_part, 0, &out_size, sizeof(uint32_t)); + if (ret != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to read core dump data size"); + return ret; + } + /* map the full core dump parition, including the checksum. */ + return esp_partition_mmap(core_part, 0, out_size, SPI_FLASH_MMAP_DATA, + map_addr, core_data_handle); +} + +static void elf_parse_version_info(esp_core_dump_summary_t *summary, void *data) +{ + core_dump_elf_version_info_t *version = (core_dump_elf_version_info_t *)data; + summary->core_dump_version = version->version; + memcpy(summary->app_elf_sha256, version->app_elf_sha256, ELF_APP_SHA256_SIZE); + ESP_COREDUMP_LOGD("Core dump version 0x%x", summary->core_dump_version); + ESP_COREDUMP_LOGD("App ELF SHA2 %s", (char *)summary->app_elf_sha256); +} + +static void elf_parse_exc_task_name(esp_core_dump_summary_t *summary, void *tcb_data) +{ + StaticTask_t *tcb = (StaticTask_t *) tcb_data; + /* An ugly way to get the task name. We could possibly use pcTaskGetTaskName here. + * But that has assumption that TCB pointer can be used as TaskHandle. So let's + * keep it this way. */ + memset(summary->exc_task, 0, sizeof(summary->exc_task)); + strncpy(summary->exc_task, (char *)tcb->ucDummy7, sizeof(summary->exc_task) - 1); + ESP_COREDUMP_LOGD("Crashing task %s", summary->exc_task); +} + +esp_err_t esp_core_dump_get_summary(esp_core_dump_summary_t *summary) +{ + int i; + elf_phdr *ph; + elf_note *note; + const void *map_addr; + size_t consumed_note_sz; + spi_flash_mmap_handle_t core_data_handle; + + if (!summary) { + return ESP_ERR_INVALID_ARG; + } + esp_err_t err = elf_core_dump_image_mmap(&core_data_handle, &map_addr); + if (err != ESP_OK) { + return err; + } + uint8_t *ptr = (uint8_t *) map_addr + sizeof(core_dump_header_t); + elfhdr *eh = (elfhdr *)ptr; + + ESP_COREDUMP_LOGD("ELF ident %02x %c %c %c", eh->e_ident[0], eh->e_ident[1], eh->e_ident[2], eh->e_ident[3]); + ESP_COREDUMP_LOGD("Ph_num %d offset %x", eh->e_phnum, eh->e_phoff); + + for (i = 0; i < eh->e_phnum; i++) { + ph = (elf_phdr *)((ptr + i * sizeof(*ph)) + eh->e_phoff); + ESP_COREDUMP_LOGD("PHDR type %d off %x vaddr %x paddr %x filesz %x memsz %x flags %x align %x", + ph->p_type, ph->p_offset, ph->p_vaddr, ph->p_paddr, ph->p_filesz, ph->p_memsz, + ph->p_flags, ph->p_align); + if (ph->p_type == PT_NOTE) { + consumed_note_sz = 0; + while(consumed_note_sz < ph->p_memsz) { + note = (elf_note *)(ptr + ph->p_offset + consumed_note_sz); + char *nm = (char *)(ptr + ph->p_offset + consumed_note_sz + sizeof(elf_note)); + ESP_COREDUMP_LOGD("Note NameSZ %x DescSZ %x Type %x name %s", note->n_namesz, + note->n_descsz, note->n_type, nm); + if (strncmp(nm, "EXTRA_INFO", note->n_namesz) == 0 ) { + esp_core_dump_summary_parse_extra_info(summary, (void *)(nm + note->n_namesz)); + } + if (strncmp(nm, "ESP_CORE_DUMP_INFO", note->n_namesz) == 0 ) { + elf_parse_version_info(summary, (void *)(nm + note->n_namesz)); + } + consumed_note_sz += note->n_namesz + note->n_descsz + sizeof(elf_note); + ALIGN(4, consumed_note_sz); + } + } + } + /* Following code assumes that task stack segment follows the TCB segment for the respective task. + * In general ELF does not impose any restrictions on segments' order so this can be changed without impacting core dump version. + * More universal and flexible way would be to retrieve stack start address from crashed task TCB segment and then look for the stack segment with that address. + */ + int flag = 0; + for (i = 0; i < eh->e_phnum; i++) { + ph = (elf_phdr *)((ptr + i * sizeof(*ph)) + eh->e_phoff); + if (ph->p_type == PT_LOAD) { + if (flag) { + esp_core_dump_summary_parse_exc_regs(summary, (void *)(ptr + ph->p_offset)); + esp_core_dump_summary_parse_backtrace_info(&summary->exc_bt_info, (void *) ph->p_vaddr, + (void *)(ptr + ph->p_offset), ph->p_memsz); + break; + } + if (ph->p_vaddr == summary->exc_tcb) { + elf_parse_exc_task_name(summary, (void *)(ptr + ph->p_offset)); + flag = 1; + } + } + } + spi_flash_munmap(core_data_handle); + return ESP_OK; +} + #endif //CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF diff --git a/components/espcoredump/src/port/riscv/core_dump_port.c b/components/espcoredump/src/port/riscv/core_dump_port.c index f03159ff0b..837427d802 100644 --- a/components/espcoredump/src/port/riscv/core_dump_port.c +++ b/components/espcoredump/src/port/riscv/core_dump_port.c @@ -381,4 +381,38 @@ uint32_t esp_core_dump_get_extra_info(void **info) return size; } +void esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t *summary, void *ei_data) +{ + riscv_extra_info_t *ei = (riscv_extra_info_t *)ei_data; + summary->exc_tcb = ei->crashed_task_tcb; + ESP_COREDUMP_LOGD("Crash TCB 0x%x", summary->exc_tcb); +} + +void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void *stack_data) +{ + int i; + long *a_reg; + RvExcFrame *stack = (RvExcFrame *)stack_data; + summary->exc_pc = stack->mepc; + ESP_COREDUMP_LOGD("Crashing PC 0x%x", summary->exc_pc); + + summary->ex_info.mstatus = stack->mstatus; + summary->ex_info.mtvec = stack->mtvec; + summary->ex_info.mcause = stack->mcause; + summary->ex_info.mtval = stack->mtval; + ESP_COREDUMP_LOGD("mstatus:0x%x mtvec:0x%x mcause:0x%x mval:0x%x", + stack->mstatus, stack->mtvec, stack->mcause, stack->mtval); + a_reg = &stack->a0; + for (i = 0; i < 8; i++) { + summary->ex_info.exc_a[i] = a_reg[i]; + ESP_COREDUMP_LOGD("A[%d] 0x%x", i, summary->ex_info.exc_a[i]); + } +} + +void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info, const void *vaddr, + const void *paddr, uint32_t stack_size) +{ + return; +} + #endif diff --git a/components/espcoredump/src/port/xtensa/core_dump_port.c b/components/espcoredump/src/port/xtensa/core_dump_port.c index deb7f313b9..35b74d1968 100644 --- a/components/espcoredump/src/port/xtensa/core_dump_port.c +++ b/components/espcoredump/src/port/xtensa/core_dump_port.c @@ -26,6 +26,7 @@ #include "esp_rom_sys.h" #include "esp_core_dump_common.h" #include "esp_core_dump_port.h" +#include "esp_debug_helpers.h" const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port"; @@ -472,4 +473,90 @@ uint32_t esp_core_dump_get_extra_info(void **info) return sizeof(s_extra_info); } +void esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t *summary, void *ei_data) +{ + int i; + xtensa_extra_info_t *ei = (xtensa_extra_info_t *) ei_data; + summary->exc_tcb = ei->crashed_task_tcb; + summary->ex_info.exc_vaddr = ei->excvaddr.reg_val; + summary->ex_info.exc_cause = ei->exccause.reg_val; + ESP_COREDUMP_LOGD("Crash TCB 0x%x", summary->exc_tcb); + ESP_COREDUMP_LOGD("excvaddr 0x%x", summary->ex_info.exc_vaddr); + ESP_COREDUMP_LOGD("exccause 0x%x", summary->ex_info.exc_cause); + + memset(summary->ex_info.epcx, 0, sizeof(summary->ex_info.epcx)); + summary->ex_info.epcx_reg_bits = 0; + for (i = 0; i < COREDUMP_EXTRA_REG_NUM; i++ ) { + if (ei->extra_regs[i].reg_index >= EPC_1 + && ei->extra_regs[i].reg_index < (EPC_1 + XCHAL_NUM_INTLEVELS)) { + summary->ex_info.epcx[ei->extra_regs[i].reg_index - EPC_1] = ei->extra_regs[i].reg_val; + summary->ex_info.epcx_reg_bits |= (1 << (ei->extra_regs[i].reg_index - EPC_1)); + } + } +} + +void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void *stack_data) +{ + int i; + long *a_reg; + XtExcFrame *stack = (XtExcFrame *) stack_data; + summary->exc_pc = esp_cpu_process_stack_pc(stack->pc); + ESP_COREDUMP_LOGD("Crashing PC 0x%x", summary->exc_pc); + + a_reg = &stack->a0; + for (i = 0; i < 16; i++) { + summary->ex_info.exc_a[i] = a_reg[i]; + ESP_COREDUMP_LOGD("A[%d] 0x%x", i, summary->ex_info.exc_a[i]); + } +} + +void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info, const void *vaddr, + const void *paddr, uint32_t stack_size) +{ + int offset; + bool corrupted; + esp_backtrace_frame_t frame; + XtExcFrame *stack = (XtExcFrame *) paddr; + int max_depth = (int) (sizeof(bt_info->bt) / sizeof(bt_info->bt[0])); + int index = 0; + + frame.pc = stack->pc; + frame.sp = stack->a1; + frame.next_pc = stack->a0; + + corrupted = !(esp_stack_ptr_is_sane(frame.sp) && + esp_ptr_executable((void *)esp_cpu_process_stack_pc(frame.pc))); + + /* vaddr is actual stack address when crash occurred. However that stack is now saved + * in the flash at a different location. Hence for each SP, we need to adjust the offset + * to point to next frame in the flash */ + offset = (uint32_t) stack - (uint32_t) vaddr; + + ESP_COREDUMP_LOGD("Crash Backtrace"); + bt_info->bt[index] = esp_cpu_process_stack_pc(frame.pc); + ESP_COREDUMP_LOGD(" 0x%x", bt_info->bt[index]); + index++; + + while (max_depth-- > 0 && frame.next_pc && !corrupted) { + /* Check if the Stack Pointer is in valid address range */ + if (!((uint32_t)frame.sp >= (uint32_t)vaddr && + ((uint32_t)frame.sp <= (uint32_t)vaddr + stack_size))) { + corrupted = true; + break; + } + /* Adjusting the SP to address in flash than in actual RAM */ + frame.sp += offset; + if (!esp_backtrace_get_next_frame(&frame)) { + corrupted = true; + } + if (corrupted == false) { + bt_info->bt[index] = esp_cpu_process_stack_pc(frame.pc); + ESP_COREDUMP_LOGD(" 0x%x", bt_info->bt[index]); + index++; + } + } + bt_info->depth = index; + bt_info->corrupted = corrupted; +} + #endif