diff --git a/components/espcoredump/CMakeLists.txt b/components/espcoredump/CMakeLists.txt index fd24aac727..110e2d9554 100644 --- a/components/espcoredump/CMakeLists.txt +++ b/components/espcoredump/CMakeLists.txt @@ -1,9 +1,24 @@ -idf_component_register(SRCS "src/core_dump_common.c" - "src/core_dump_flash.c" - "src/core_dump_port.c" - "src/core_dump_uart.c" - "src/core_dump_elf.c" +set(srcs "src/core_dump_common.c" + "src/core_dump_checksum.c" + "src/core_dump_flash.c" + "src/core_dump_uart.c" + "src/core_dump_elf.c" + "src/core_dump_binary.c") + +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") +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") +endif() + +idf_component_register(SRCS ${srcs} INCLUDE_DIRS "include" - PRIV_INCLUDE_DIRS "include_core_dump" + PRIV_INCLUDE_DIRS ${priv_includes} LDFRAGMENTS linker.lf PRIV_REQUIRES spi_flash app_update mbedtls esp_rom soc) diff --git a/components/espcoredump/Kconfig b/components/espcoredump/Kconfig index 58f4d79104..83811c76cd 100644 --- a/components/espcoredump/Kconfig +++ b/components/espcoredump/Kconfig @@ -72,6 +72,9 @@ menu "Core dump" config ESP_COREDUMP_STACK_SIZE int "Reserved stack size" depends on ESP_COREDUMP_ENABLE + # Temporarily disable this feature on Xtensa boards as switching stack + # pointer triggers an exception (IDF-2797) + depends on IDF_TARGET_ARCH_RISCV default 0 help Size of the memory to be reserved for core dump stack. If 0 core dump process will run on diff --git a/components/espcoredump/component.mk b/components/espcoredump/component.mk index 8104ff7ee5..64fff02566 100644 --- a/components/espcoredump/component.mk +++ b/components/espcoredump/component.mk @@ -2,3 +2,11 @@ COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_SRCDIRS := src COMPONENT_PRIV_INCLUDEDIRS := include_core_dump COMPONENT_ADD_LDFRAGMENTS += linker.lf + +ifdef CONFIG_IDF_TARGET_ARCH_XTENSA + COMPONENT_PRIV_INCLUDEDIRS += include_core_dump/port/xtensa +endif + +ifdef CONFIG_IDF_TARGET_ARCH_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 9945b58cea..fb0d44d2c8 100644 --- a/components/espcoredump/include/esp_core_dump.h +++ b/components/espcoredump/include/esp_core_dump.h @@ -43,13 +43,13 @@ void esp_core_dump_init(void); * . . . . * . . . . * | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N | - * | CRC32 | + * | CHECKSUM | * * 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. + * For flash data integrity, a checksum 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.1) TOTAL_LEN is total length of core dump data in flash including the checksum. 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. @@ -60,7 +60,7 @@ void esp_core_dump_init(void); * 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. + * 5) The checksum is placed at the end of the data. */ void esp_core_dump_to_flash(panic_info_t *info); @@ -68,8 +68,8 @@ void esp_core_dump_to_flash(panic_info_t *info); * @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. + * 1) The checksum is not present in core dump printed to UART. + * 2) Since checksum 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(panic_info_t *info); diff --git a/components/espcoredump/include_core_dump/core_dump_binary.h b/components/espcoredump/include_core_dump/core_dump_binary.h new file mode 100644 index 0000000000..ff7c018871 --- /dev/null +++ b/components/espcoredump/include_core_dump/core_dump_binary.h @@ -0,0 +1,30 @@ +// Copyright 2015-2020 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_BINARY_H_ +#define ESP_CORE_DUMP_BINARY_H_ + +#include "esp_core_dump_types.h" + +/** + * @brief Initiate the binary core dump generation. + * + * @param info Exception frame info generated when the panic occured. + * @param write_cfg Structure containing the callbacks that will be called to + * write the generated core dump file. + * + * @return ESP_OK on success, otherwise \see esp_err_t. + */ +esp_err_t esp_core_dump_write_binary(core_dump_write_config_t *write_cfg); + +#endif diff --git a/components/espcoredump/include_core_dump/core_dump_checksum.h b/components/espcoredump/include_core_dump/core_dump_checksum.h new file mode 100644 index 0000000000..961dd38a05 --- /dev/null +++ b/components/espcoredump/include_core_dump/core_dump_checksum.h @@ -0,0 +1,117 @@ +// Copyright 2015-2020 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. + +/** + * @file + * @brief Core dump checksum interface. + * + * This file contains all the functions required by the core dump component to + * calculate checksums for data to write (or already written) on the flash. + * Currently, both CRC32 and SHA256 are supported, but this interface is + * implementation independent. + */ + +#ifndef CORE_DUMP_CHECKSUM_H_ +#define CORE_DUMP_CHECKSUM_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum possible length for a checksum (regardless of the + * implentation). This can be modified in the future if a new implementation + * requires a larger size. + */ +#define COREDUMP_CHECKSUM_MAX_LEN 32 + +/** + * @brief Type describing a checksum context. It is an abstract type as it is + * implementation independent, it is defined in the C source counterpart. + */ +typedef struct core_dump_checksum_ctx core_dump_checksum_ctx; + +/** + * @brief Type returned by `esp_core_dump_checksum_finish()`. It describes a + * checksum as an array of bytes. It can also be provided to `esp_core_dump_print_checksum()`. + */ +typedef uint8_t* core_dump_checksum_bytes; + + +/** + * @brief Get ELF core dump version. + * + * @note Currently, this is used in the core dump header to recognize the + * checksum used for a certain dump, as the version varies with the checksum. + * + * @return Version of the core dump used. + */ +uint32_t esp_core_dump_elf_version(void); + +/** + * @brief Initialize checksum calculation for the given context. + * + * @param wr_data Core dump checksum context to fill. + */ +void esp_core_dump_checksum_init(core_dump_checksum_ctx** wr_data); + +/** + * @brief Update checksum calculation by integrating the given data in the context. + * + * @param wr_data Core dump checksum context. + * @param data Pointer to the data to integrate in the checksum calculation. + * This is usually the new data to write (or already written) on + * the flash. + */ +void esp_core_dump_checksum_update(core_dump_checksum_ctx* wr_data, void* data, size_t data_len); + +/** + * @brief Terminate and return checksum calculated for the given context. + * + * @param wr_data Core dump checksum context. It can be NULL only if chs_ptr is + * also NULL. + * @param chs_ptr Pointer used to return the checksum calculated. It can be + * NULL, in this case, it will be ignored but the correct size + * of the checksum will be returned. + * + * @return The size, in bytes, of the checksum. + */ +uint32_t esp_core_dump_checksum_finish(core_dump_checksum_ctx* wr_data, core_dump_checksum_bytes* chs_ptr); + +/** + * @brief Return the size of the checksums. + * + * @note This is equivalent to `esp_core_dump_checksum_finish(NULL, NULL)`. + * + * @return The size, in bytes, of the checksums. + */ +uint32_t esp_core_dump_checksum_size(void); + +/** + * @brief Print a message followed by the checksum given as a parameter. + * + * @note The checksum will be printed in hex format and followed by \r\n. + * + * @param msg Message to print before the checksum. Can be NULL. + * @param checksum Checksum to print. Must not be NULL. + */ +void esp_core_dump_print_checksum(const char* msg, core_dump_checksum_bytes checksum); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/espcoredump/include_core_dump/core_dump_elf.h b/components/espcoredump/include_core_dump/core_dump_elf.h index bb9f0194cd..77c7675525 100644 --- a/components/espcoredump/include_core_dump/core_dump_elf.h +++ b/components/espcoredump/include_core_dump/core_dump_elf.h @@ -14,8 +14,17 @@ #ifndef ESP_CORE_DUMP_ELF_H_ #define ESP_CORE_DUMP_ELF_H_ -#include "esp_core_dump_priv.h" +#include "esp_core_dump_types.h" +/** + * @brief Initiate the ELF core dump generation. + * + * @param info Exception frame info generated when the panic occured. + * @param write_cfg Structre containing the callbacks that will be called to + * write the generated core dump data. + * + * @return ESP_OK on success, otherwise \see esp_err_t. + */ esp_err_t esp_core_dump_write_elf(core_dump_write_config_t *write_cfg); #endif diff --git a/components/espcoredump/include_core_dump/esp_core_dump_common.h b/components/espcoredump/include_core_dump/esp_core_dump_common.h new file mode 100644 index 0000000000..e2986abe16 --- /dev/null +++ b/components/espcoredump/include_core_dump/esp_core_dump_common.h @@ -0,0 +1,155 @@ +// Copyright 2015-2019 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_COMMON_H_ +#define ESP_CORE_DUMP_COMMON_H_ + +#include "freertos/FreeRTOS.h" +#include "soc/cpu.h" +#include "esp_debug_helpers.h" +#include "esp_app_format.h" +#include "esp_core_dump_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enumeration of the existing memory regions. + * One can use these definitions to retrieve the start address and/or the size + * of a specific region using the functions below. + */ +typedef enum { + COREDUMP_MEMORY_DRAM, + COREDUMP_MEMORY_IRAM, + COREDUMP_MEMORY_RTC, + COREDUMP_MEMORY_RTC_FAST, + COREDUMP_MEMORY_MAX, + COREDUMP_MEMORY_START = COREDUMP_MEMORY_DRAM +} coredump_region_t; + +/** + * @brief Get the (FreeRTOS) task handle for the current task. + * + * @return Task handle of the current task. + */ +core_dump_task_handle_t esp_core_dump_get_current_task_handle(void); + +/** + * @brief Get next task handle of a given handle. + * + * @param handle Task handle to get the next handle from. + * + * @return Next task handle. + */ +core_dump_task_handle_t esp_core_dump_get_next_task(core_dump_task_handle_t handle); + +/** + * @brief Get a task snapshot from a given handle. + * + * @param handle Task handle to get the snapshot from. + * @param task Returned task header. + * @param interrupted_stack Backup of the task stack if the handle passed is the task + * that crashed and if it crashed within an ISR context. + * + * @return false is the task is broken, true else. + */ +bool esp_core_dump_get_task_snapshot(core_dump_task_handle_t handle, + core_dump_task_header_t *task, + core_dump_mem_seg_header_t *interrupted_stack); + +/** + * @brief Reset tasks snapshot iterator. + */ +void esp_core_dump_reset_tasks_snapshots_iter(void); + + +/** + * @brief Check if the TCB passed as a parameter is sane. + * + * @param address Address of the TCB to check. + * + * @return true if the TCB is sane, false else. + */ +bool esp_core_dump_tcb_addr_is_sane(uint32_t addr); + +/** + * @brief Get the number of RAM segments. + * + * @return Number of RAM segments. + */ +uint32_t esp_core_dump_get_user_ram_segments(void); + + +/** + * @brief Get start address and size of a memory region. + * + * @param region Memory region to get information about. + * @param start Pointer that will be filled with the region start address. + * Must **not** be NULL. + * + * @return Size, in bytes, of the memory region. + */ +int esp_core_dump_get_user_ram_info(coredump_region_t region, uint32_t *start); + + +/** + * @brief Check if the current task is in an ISR. + * + * @return true if task in an ISR, false else. + */ +bool esp_core_dump_in_isr_context(void); + + +/** + * @brief Get the size all the memory regions (DRAM, RTC, RTC_FAST, IRAM) + * + * @return Size, in bytes, of all the memory regions. + */ +uint32_t esp_core_dump_get_user_ram_size(void); + + +/** + * @brief Get TCB length, in bytes. + * + * @return Length of TCB, in bytes. + */ +static inline uint32_t esp_core_dump_get_tcb_len(void) +{ + return (sizeof(StaticTask_t) % sizeof(uint32_t)) ? + ((sizeof(StaticTask_t) / sizeof(uint32_t) + 1) * sizeof(uint32_t)) : + sizeof(StaticTask_t); +} + +/** + * @brief Get the length, in bytes, of a given memory location. Padding is + * taken into account in this calculation. + * + * @param start Start address of the momery location. + * @param end End address of the memory location. + * + * @return Size of the memory location, multiple of sizeof(uint32_t). + */ +static inline uint32_t esp_core_dump_get_memory_len(uint32_t start, uint32_t end) +{ + const uint32_t len = end - start; + // Take stack padding into account + return (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1); +} + + +#ifdef __cplusplus +} +#endif + +#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 afa6de69cd..1dd6cd7ab9 100644 --- a/components/espcoredump/include_core_dump/esp_core_dump_port.h +++ b/components/espcoredump/include_core_dump/esp_core_dump_port.h @@ -14,142 +14,155 @@ #ifndef ESP_CORE_DUMP_PORT_H_ #define ESP_CORE_DUMP_PORT_H_ +/** + * @file + * @brief Core dump port interface. + * + * This file contains all the functions required by the core dump component to + * get the information related to the board or the SoC itself. Currently, the + * implementations of this interface, located in `src/port/[arch]`, support + * both Xtensa and RISC-V architecture. + */ + #include "freertos/FreeRTOS.h" -#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 -#include "esp_rom_crc.h" -#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 -#include "mbedtls/sha256.h" -#endif -#include "esp_core_dump_priv.h" #include "soc/cpu.h" #include "esp_debug_helpers.h" #include "esp_app_format.h" +#include "esp_core_dump_types.h" +#include "esp_core_dump_port_impl.h" #ifdef __cplusplus extern "C" { #endif -#if CONFIG_IDF_TARGET_ESP32 -#define COREDUMP_VERSION_CHIP ESP_CHIP_ID_ESP32 -#elif CONFIG_IDF_TARGET_ESP32S2 -#define COREDUMP_VERSION_CHIP ESP_CHIP_ID_ESP32S2 -#endif - -typedef enum { - COREDUMP_MEMORY_DRAM, - COREDUMP_MEMORY_IRAM, - COREDUMP_MEMORY_RTC, - COREDUMP_MEMORY_RTC_FAST, - COREDUMP_MEMORY_MAX, - COREDUMP_MEMORY_START = COREDUMP_MEMORY_DRAM -} coredump_region_t; - -// RTOS tasks snapshots walk API -void esp_core_dump_reset_tasks_snapshots_iter(void); -void *esp_core_dump_get_next_task(void *handle); -bool esp_core_dump_get_task_snapshot(void *handle, core_dump_task_header_t *task, - core_dump_mem_seg_header_t *interrupted_stack); - -bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz); -void *esp_core_dump_get_current_task_handle(void); -uint32_t esp_core_dump_get_stack(core_dump_task_header_t* task_snapshot, uint32_t* stk_base, uint32_t* stk_len); - -static inline uint32_t esp_core_dump_get_tcb_len(void) -{ - if (sizeof(StaticTask_t) % sizeof(uint32_t)) { - return ((sizeof(StaticTask_t) / sizeof(uint32_t) + 1) * sizeof(uint32_t)); - } - return sizeof(StaticTask_t); -} - -static inline uint32_t esp_core_dump_get_memory_len(uint32_t start, uint32_t end) -{ - uint32_t len = end - start; - // Take stack padding into account - return (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1); -} - +/** + * @brief Get the architecture ID. + * + * @return Architecture ID, as described by ELF format. + */ uint16_t esp_core_dump_get_arch_id(void); -uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, void **reg_dump); -void esp_core_dump_init_extra_info(void); -uint32_t esp_core_dump_get_extra_info(void **info); - -uint32_t esp_core_dump_get_user_ram_segments(void); -uint32_t esp_core_dump_get_user_ram_size(void); -int esp_core_dump_get_user_ram_info(coredump_region_t region, uint32_t *start); - -// Data integrity check functions -void esp_core_dump_checksum_init(core_dump_write_data_t* wr_data); -void esp_core_dump_checksum_update(core_dump_write_data_t* wr_data, void* data, size_t data_len); -size_t esp_core_dump_checksum_finish(core_dump_write_data_t* wr_data, void** chs_ptr); -uint32_t esp_core_dump_checksum_size(void); - -#if CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 -void esp_core_dump_print_sha256(const char* msg, const uint8_t* sha_output); -int esp_core_dump_sha(mbedtls_sha256_context *ctx, - const unsigned char *input, size_t ilen, unsigned char output[32]); -#endif -void esp_core_dump_print_checksum(const char* msg, const void* checksum); +/** + * @brief Initialize the port module. This function is also in charge of + * initializing the extra information, if any. + * + * @param info Pointer to the panic information. It contains the execution + * frame. + */ void esp_core_dump_port_init(panic_info_t *info); -#if CONFIG_ESP_COREDUMP_STACK_SIZE > 0 -#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG -// increase stack size in verbose mode -#define ESP_COREDUMP_STACK_SIZE (CONFIG_ESP_COREDUMP_STACK_SIZE+100) -#else -#define ESP_COREDUMP_STACK_SIZE CONFIG_ESP_COREDUMP_STACK_SIZE -#endif -#endif +/** + * @brief Reset fake stacks allocator, if any. + * + * @note This function is called if we want to free all the previously + * allocated "fake" stacks, used in broken tasks. + */ +void esp_core_dump_reset_fake_stacks(void); -void esp_core_dump_report_stack_usage(void); +/** + * @brief Get ISR stack end address. + * + * @return End address of the ISR stack. + */ +uint32_t esp_core_dump_get_isr_stack_end(void); -#if ESP_COREDUMP_STACK_SIZE > 0 -#define COREDUMP_STACK_FILL_BYTE (0xa5U) -extern uint8_t s_coredump_stack[]; -extern uint8_t *s_core_dump_sp; -#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG -#define esp_core_dump_fill_stack() \ - memset(s_coredump_stack, COREDUMP_STACK_FILL_BYTE, ESP_COREDUMP_STACK_SIZE) -#else -#define esp_core_dump_fill_stack() -#endif +/** + * @brief Get the top of the ISR stack. + * + * @return Pointer to the top of the ISR stack. + */ +uint8_t* esp_core_dump_get_isr_stack_top(void); -#define esp_core_dump_setup_stack() \ -{ \ - s_core_dump_sp = (uint8_t *)((uint32_t)(s_coredump_stack + ESP_COREDUMP_STACK_SIZE - 1) & ~0xf); \ - esp_core_dump_fill_stack(); \ - /* watchpoint 1 can be used for task stack overflow detection, re-use it, it is no more necessary */ \ - esp_clear_watchpoint(1); \ - esp_set_watchpoint(1, s_coredump_stack, 1, ESP_WATCHPOINT_STORE); \ - asm volatile ("mov sp, %0" :: "r"(s_core_dump_sp)); \ - ESP_COREDUMP_LOGD("Use core dump stack @ 0x%x", get_sp()); \ -} -#else -#define esp_core_dump_setup_stack() \ -{ \ - /* if we are in ISR set watchpoint to the end of ISR stack */ \ - if (xPortInterruptedFromISRContext()) { \ - extern uint8_t port_IntStack; \ - esp_clear_watchpoint(1); \ - esp_set_watchpoint(1, &port_IntStack+xPortGetCoreID()*configISR_STACK_SIZE, 1, ESP_WATCHPOINT_STORE); \ - } else { \ - /* for tasks user should enable stack overflow detection in menuconfig - TODO: if not enabled in menuconfig enable it ourselves */ \ - } \ -} -#endif -// coredump memory regions defined during compile timing -extern int _coredump_dram_start; -extern int _coredump_dram_end; -extern int _coredump_iram_start; -extern int _coredump_iram_end; -extern int _coredump_rtc_start; -extern int _coredump_rtc_end; -extern int _coredump_rtc_fast_start; -extern int _coredump_rtc_fast_end; +/** + * @brief Check the stack defined by address given. + * + * @param task Task to check the stack of. + * + * @return true is the stack is sane, false else. + */ +bool esp_core_dump_check_stack(core_dump_task_header_t *task); + + +/** + * @brief Check if the memory segment is sane. + * + * @param addr Address of the memory segment to check. + * @param sz Size of the memory segment to check. + * + * @return true if the memory segment is sane, false else. + */ +bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz); + + +/** + * @brief Get the stack of a task. + * + * @param task_snapshot Pointer to the task snapshot. + * @param stk_vaddr Pointer which will be set to the stack's virtual address. + * Must **not** be NULL. + * @param stk_paddr Pointer which will be set to the stack's physical + * address. Must **not** be NULL. + * + * @return Size, in bytes, of the stack. + */ +uint32_t esp_core_dump_get_stack(core_dump_task_header_t* task_snapshot, + uint32_t* stk_vaddr, uint32_t* stk_paddr); + + +/** + * @brief Check the task passed as a parameter. + * + * @note The goal of this function is to check whether the task passed is the + * task that crashed or not. If this is the case and if it didn't crash + * within an ISR, its stack pointer will be set to the panic frame, + * containing all the registers values when the error occured. This + * function also checks if the TCB address is sane or not. + * + * @param task Pointer to the frame exception generated when the panic occured. + * + * @return True if the TCB is sane, false else. + */ +bool esp_core_dump_check_task(core_dump_task_header_t *task); + + +/** + * @brief Get a dump of the task's registers. + * + * @note In practice, this function is used to fill the ELF file with the + * PR_STATUS sections for all the existing tasks. This structure + * contains the CPU registers value when the exception occured. + * + * @param task Task to dump the registers from. + * @param reg_dump Pointer that will be filled with the registers dump. + * Must **not** be NULL. + * + * @return Size, in bytes, of the returned registers duump. + */ +uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, + void **reg_dump); + +/** + * @brief Transmit the crashed task handle. + * + * @param handle Crashed task handle. + * + * @note This function is used to give information about the crashed task to + * the port module. It can be ignored if not needed. + */ +void esp_core_dump_port_set_crashed_tcb(uint32_t handle); + +/** + * @brief Retrieve the extra information. + * + * @param info Pointer that will be filled with the extra information. + * Can be NULL, in that case, this function is used to get the + * extra information size. + * + * @return Size, in bytes, of the extra information. + */ +uint32_t esp_core_dump_get_extra_info(void **info); #ifdef __cplusplus } diff --git a/components/espcoredump/include_core_dump/esp_core_dump_priv.h b/components/espcoredump/include_core_dump/esp_core_dump_priv.h deleted file mode 100644 index d29d0c0416..0000000000 --- a/components/espcoredump/include_core_dump/esp_core_dump_priv.h +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2015-2019 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_PRIV_H_ -#define ESP_CORE_DUMP_PRIV_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_err.h" -#include "esp_attr.h" -#include "esp_log.h" -#include "esp_rom_sys.h" -#include "sdkconfig.h" -#include "esp_private/panic_internal.h" -#if CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 -// TODO: move this to portable part of the code -#include "mbedtls/sha256.h" -#endif - -#define ESP_COREDUMP_LOG( level, format, ... ) if (LOG_LOCAL_LEVEL >= level) { esp_rom_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_ESP_COREDUMP_ENABLE_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) -// COREDUMP_VERSION_CHIP is defined in ports -#define COREDUMP_VERSION_MAKE(_maj_, _min_) ((((COREDUMP_VERSION_CHIP)&0xFFFF) << 16) | (((_maj_)&0xFF) << 8) | (((_min_)&0xFF) << 0)) -#define COREDUMP_VERSION_BIN 0 -#define COREDUMP_VERSION_ELF 1 -// legacy bin coredumps (before IDF v4.1) has version set to 1 -#define COREDUMP_VERSION_BIN_LEGACY COREDUMP_VERSION_MAKE(COREDUMP_VERSION_BIN, 1) // -> 0x0001 -#define COREDUMP_VERSION_BIN_CURRENT COREDUMP_VERSION_MAKE(COREDUMP_VERSION_BIN, 2) // -> 0x0002 -#define COREDUMP_VERSION_ELF_CRC32 COREDUMP_VERSION_MAKE(COREDUMP_VERSION_ELF, 0) // -> 0x0100 -#define COREDUMP_VERSION_ELF_SHA256 COREDUMP_VERSION_MAKE(COREDUMP_VERSION_ELF, 1) // -> 0x0101 -#define COREDUMP_CURR_TASK_MARKER 0xDEADBEEF -#define COREDUMP_CURR_TASK_NOT_FOUND -1 - -#if CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF -#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 -#define COREDUMP_VERSION COREDUMP_VERSION_ELF_CRC32 -#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 -#define COREDUMP_VERSION COREDUMP_VERSION_ELF_SHA256 -#define COREDUMP_SHA256_LEN 32 -#endif -#else -#define COREDUMP_VERSION COREDUMP_VERSION_BIN_CURRENT -#endif - -#define COREDUMP_CHECKSUM_MAX_LEN 32 - -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); - -typedef uint32_t core_dump_crc_t; - -/** - * The following macro defines the size of the cache used to write the coredump - * to the flash. When the flash is encrypted, the smallest data block we can - * write to it is 16 bytes long. Thus, this macro MUST be a multiple of 16. - */ -#define COREDUMP_CACHE_SIZE 32 - -#if (COREDUMP_CACHE_SIZE % 16) != 0 - #error "Coredump cache size must be a multiple of 16" -#endif - -typedef struct _core_dump_write_data_t -{ - // TODO: move flash related data to flash-specific code - uint32_t off; // current offset in partition - uint8_t cached_data[COREDUMP_CACHE_SIZE]; - uint8_t cached_bytes; -#if CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 - // TODO: move this to portable part of the code - mbedtls_sha256_context ctx; - char sha_output[COREDUMP_SHA256_LEN]; -#elif CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 - core_dump_crc_t crc; // CRC of dumped data -#endif -} core_dump_write_data_t; - -// 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; - // 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 - uint32_t mem_segs_num; // number of memory segments -} 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; - -/** core dump memory segment header */ -typedef struct _core_dump_mem_seg_header_t -{ - uint32_t start; // memory region start address - uint32_t size; // memory region size -} core_dump_mem_seg_header_t; - -// Core dump flash init function -void esp_core_dump_flash_init(void); - -// Common core dump write function -void esp_core_dump_write(panic_info_t *info, core_dump_write_config_t *write_cfg); - -#include "esp_core_dump_port.h" - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/components/espcoredump/include_core_dump/esp_core_dump_types.h b/components/espcoredump/include_core_dump/esp_core_dump_types.h new file mode 100644 index 0000000000..3d625e3c19 --- /dev/null +++ b/components/espcoredump/include_core_dump/esp_core_dump_types.h @@ -0,0 +1,188 @@ +// Copyright 2015-2019 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_PRIV_H_ +#define ESP_CORE_DUMP_PRIV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_rom_sys.h" +#include "sdkconfig.h" +#include "esp_private/panic_internal.h" +#include "core_dump_checksum.h" + +#define ESP_COREDUMP_LOG( level, format, ... ) if (LOG_LOCAL_LEVEL >= level) { esp_rom_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__) + +/** + * @brief Assertion to be verified in a release context. Cannot be muted. + */ +#define ESP_COREDUMP_ASSERT( condition ) if(!(condition)){ abort(); } else { } + +/** + * @brief Assertion to be verified in a debug context. Can be muted. + */ +#define ESP_COREDUMP_DEBUG_ASSERT( condition ) assert(condition) + +/** + * @brief Logging should only be enabled if the core dump is not written to + * the UART. + */ +#if CONFIG_ESP_COREDUMP_ENABLE_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) + +/** + * @brief The following macros defined below are used to create a version + * numbering. This number is then used in the core dump header. + * + * @note COREDUMP_VERSION_CHIP is defined in ports header. + */ +#define COREDUMP_VERSION_MAKE(_maj_, _min_) ( \ + (((COREDUMP_VERSION_CHIP)&0xFFFF) << 16) | \ + (((_maj_)&0xFF) << 8) | \ + (((_min_)&0xFF) << 0) \ + ) +#define COREDUMP_VERSION_BIN 0 +#define COREDUMP_VERSION_ELF 1 + +/* legacy bin coredumps (before IDF v4.1) has version set to 1 */ +#define COREDUMP_VERSION_BIN_LEGACY COREDUMP_VERSION_MAKE(COREDUMP_VERSION_BIN, 1) // -> 0x0001 +#define COREDUMP_VERSION_BIN_CURRENT COREDUMP_VERSION_MAKE(COREDUMP_VERSION_BIN, 2) // -> 0x0002 +#define COREDUMP_VERSION_ELF_CRC32 COREDUMP_VERSION_MAKE(COREDUMP_VERSION_ELF, 0) // -> 0x0100 +#define COREDUMP_VERSION_ELF_SHA256 COREDUMP_VERSION_MAKE(COREDUMP_VERSION_ELF, 1) // -> 0x0101 +#define COREDUMP_CURR_TASK_MARKER 0xDEADBEEF +#define COREDUMP_CURR_TASK_NOT_FOUND -1 + +/** + * @brief Macro defining the size of the cache used to write the core dump. + */ +#define COREDUMP_CACHE_SIZE 32 + +/** + * @brief If the core dump has to be written to an encrypted flash, the + * smallest data block we can write to it is 16 bytes long. Thus, this macro + * MUST be a multiple of 16. + */ +#if (COREDUMP_CACHE_SIZE % 16) != 0 + #error "Coredump cache size must be a multiple of 16" +#endif + + +typedef struct _core_dump_write_data_t +{ + uint32_t off; /*!< Current offset of data being written */ + uint8_t cached_data[COREDUMP_CACHE_SIZE]; /*!< Cache used to write to flash */ + uint8_t cached_bytes; /*!< Number of bytes filled in the cached */ + core_dump_checksum_ctx* checksum_ctx; /*!< Checksum context */ +} core_dump_write_data_t; + + +/** + * @brief Types below define the signatures of the callbacks that are used + * to output a core dump. The destination of the dump is implementation + * dependant. + */ +typedef esp_err_t (*esp_core_dump_write_prepare_t)(core_dump_write_data_t* priv, uint32_t *data_len); +typedef esp_err_t (*esp_core_dump_write_start_t)(core_dump_write_data_t* priv); +typedef esp_err_t (*esp_core_dump_write_end_t)(core_dump_write_data_t* priv); +typedef esp_err_t (*esp_core_dump_flash_write_data_t)(core_dump_write_data_t* priv, + void * data, + uint32_t data_len); + + +/** + * @brief Core dump emitter control structure. + * This structure contains the functions that are called in order to write + * the core dump to the destination (UART or flash). + * The function are called in this order: + * - prepare + * - start + * - write (called once or more) + * - end + */ +typedef struct _core_dump_write_config_t +{ + esp_core_dump_write_prepare_t prepare; /*!< Function called for sanity checks */ + esp_core_dump_write_start_t start; /*!< Function called at the beginning of data writing */ + esp_core_dump_flash_write_data_t write; /*!< Function called to write data chunk */ + esp_core_dump_write_end_t end; /*!< Function called once all data have been written */ + core_dump_write_data_t* priv; /*!< Private context to pass to every function of this structure */ +} core_dump_write_config_t; + +/** + * @brief Core dump data header + * This header predecesses the actual core dump data (ELF or binary). */ +typedef struct _core_dump_header_t +{ + uint32_t data_len; /*!< Data length */ + uint32_t version; /*!< Core dump version */ + uint32_t tasks_num; /*!< Number of tasks */ + uint32_t tcb_sz; /*!< Size of a TCB, in bytes */ + uint32_t mem_segs_num; /*!< Number of memory segments */ +} core_dump_header_t; + +/** + * @brief Core dump task data header + * The main goal of this definition is to add typing to the code. + */ +typedef void* core_dump_task_handle_t; + +/** + * @brief Header for the tasks + */ +typedef struct _core_dump_task_header_t +{ + core_dump_task_handle_t tcb_addr; /*!< TCB address */ + uint32_t stack_start; /*!< Start of the stack address */ + uint32_t stack_end; /*!< End of the stack address */ +} core_dump_task_header_t; + +/** + * @brief Core dump memory segment header + */ +typedef struct _core_dump_mem_seg_header_t +{ + uint32_t start; /*!< Memory region start address */ + uint32_t size; /*!< Memory region size */ +} core_dump_mem_seg_header_t; + +/** + * @brief Core dump flash init function + */ +void esp_core_dump_flash_init(void); + + +/** + * @brief Common core dump write function + */ +void esp_core_dump_write(panic_info_t *info, core_dump_write_config_t *write_cfg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/espcoredump/include_core_dump/port/riscv/esp_core_dump_port_impl.h b/components/espcoredump/include_core_dump/port/riscv/esp_core_dump_port_impl.h new file mode 100644 index 0000000000..f23b3090e0 --- /dev/null +++ b/components/espcoredump/include_core_dump/port/riscv/esp_core_dump_port_impl.h @@ -0,0 +1,57 @@ +// Copyright 2015-2019 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_PORT_IMPL_H_ +#define ESP_CORE_DUMP_PORT_IMPL_H_ + +/** + * @file + * @brief Core dump port interface implementation for RISC-V. + */ + +#include "esp_core_dump_types.h" +#include "esp_app_format.h" + +/** + * @brief Chip ID associated to this implementation. + */ +#define COREDUMP_VERSION_CHIP ESP_CHIP_ID_ESP32C3 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set the stack pointer to the address passed as a parameter. + * @note This function must be inlined. + * + * @param new_sp New stack pointer to set in sp register. + * + * @return Former stack pointer address (sp register value). + */ +FORCE_INLINE_ATTR void* esp_core_dump_replace_sp(void* new_sp) +{ + void* current_sp = NULL; + asm volatile ("mv %0, sp \n\t\ + mv sp, %1 \n\t\ + " + : "=&r"(current_sp) + : "r"(new_sp)); + return current_sp; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/espcoredump/include_core_dump/port/xtensa/esp_core_dump_port_impl.h b/components/espcoredump/include_core_dump/port/xtensa/esp_core_dump_port_impl.h new file mode 100644 index 0000000000..82341b48cd --- /dev/null +++ b/components/espcoredump/include_core_dump/port/xtensa/esp_core_dump_port_impl.h @@ -0,0 +1,67 @@ +// Copyright 2015-2019 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_PORT_IMPL_H_ +#define ESP_CORE_DUMP_PORT_IMPL_H_ + +/** + * @file + * @brief Core dump port interface implementation for Xtensa boards. + */ +#include "esp_core_dump_types.h" +#include "esp_app_format.h" +/** + * Included for SET_STACK macro + */ +#include +#include + +/** + * @brief Chip ID associated to this implementation. + */ +#if CONFIG_IDF_TARGET_ESP32 + #define COREDUMP_VERSION_CHIP ESP_CHIP_ID_ESP32 +#elif CONFIG_IDF_TARGET_ESP32S2 + #define COREDUMP_VERSION_CHIP ESP_CHIP_ID_ESP32S2 +#elif CONFIG_IDF_TARGET_ESP32S3 + #define COREDUMP_VERSION_CHIP ESP_CHIP_ID_ESP32S3 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set the stack pointer to the address passed as a parameter. + * @note This function must be inlined. + * + * @param new_sp New stack pointer to set in sp register. + * + * @return Former stack pointer address (sp register value). + */ +FORCE_INLINE_ATTR void* esp_core_dump_replace_sp(void* new_sp) +{ + void* current_sp = NULL; + asm volatile ("mov %0, sp \n\t\ + " + : "=&r"(current_sp) + :); + SET_STACK(new_sp); + return current_sp; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/espcoredump/src/core_dump_binary.c b/components/espcoredump/src/core_dump_binary.c new file mode 100644 index 0000000000..c3b7f49457 --- /dev/null +++ b/components/espcoredump/src/core_dump_binary.c @@ -0,0 +1,279 @@ +// Copyright 2015-2020 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 +#include "sdkconfig.h" +#include "core_dump_binary.h" +#include "esp_core_dump_port.h" +#include "esp_core_dump_common.h" + +#if CONFIG_ESP_COREDUMP_DATA_FORMAT_BIN + +const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_binary"; + + +static esp_err_t esp_core_dump_save_task(core_dump_write_config_t *write_cfg, + core_dump_task_header_t *task) +{ + esp_err_t err = ESP_FAIL; + uint32_t stk_vaddr = 0; + uint32_t stk_paddr = 0; + uint32_t stk_len = esp_core_dump_get_stack(task, &stk_vaddr, &stk_paddr); + + stk_len = esp_core_dump_get_memory_len(stk_vaddr, stk_vaddr+stk_len); + + // Save memory segment header + err = write_cfg->write(write_cfg->priv, (void*)task, sizeof(core_dump_task_header_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write task header, error=%d!", err); + return err; + } + // Save TCB block + err = write_cfg->write(write_cfg->priv, task->tcb_addr, esp_core_dump_get_tcb_len()); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write TCB, error=%d!", err); + return err; + } + // Save task stack + err = write_cfg->write(write_cfg->priv, (void*)stk_paddr, stk_len); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write stack for task (TCB:%x), stack_start=%x, error=%d!", + task->tcb_addr, + stk_vaddr, + err); + return err; + } + + ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x) dump is saved.", + task->tcb_addr); + return ESP_OK; +} + +static esp_err_t esp_core_dump_save_mem_segment(core_dump_write_config_t* write_cfg, + core_dump_mem_seg_header_t* seg) +{ + esp_err_t err = ESP_FAIL; + + if (!esp_core_dump_mem_seg_is_sane(seg->start, seg->size)) { + ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu)!", + seg->start, seg->size); + return ESP_FAIL; + } + // Save TCB address, stack base and stack top addr + err = write_cfg->write(write_cfg->priv, (void*)seg, sizeof(core_dump_mem_seg_header_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write memory segment header, error=%d!", err); + return err; + } + // Save memory contents + err = write_cfg->write(write_cfg->priv, (void*)seg->start, seg->size); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu), error=%d!", + seg->start, seg->size, err); + return err; + } + ESP_COREDUMP_LOG_PROCESS("Memory segment (%x, %lu) is saved.", + seg->start, seg->size); + return ESP_OK; +} + +esp_err_t esp_core_dump_write_binary(core_dump_write_config_t *write_cfg) +{ + esp_err_t err = ESP_OK; + uint32_t tcb_sz = esp_core_dump_get_tcb_len(); + uint32_t data_len = 0; + uint32_t bad_tasks_num = 0; + core_dump_header_t hdr = { 0 }; + core_dump_task_header_t task_hdr = { 0 }; + core_dump_mem_seg_header_t mem_seg = { 0 }; + void *task = NULL; + void *cur_task = NULL; + + // Verifies all tasks in the snapshot + esp_core_dump_reset_tasks_snapshots_iter(); + while ((task = esp_core_dump_get_next_task(task))) { + if (!esp_core_dump_get_task_snapshot(task, &task_hdr, &mem_seg)) { + bad_tasks_num++; + continue; + } + hdr.tasks_num++; + if (task == esp_core_dump_get_current_task_handle()) { + cur_task = task; + ESP_COREDUMP_LOG_PROCESS("Task %x %x is first crashed task.", cur_task, task_hdr.tcb_addr); + } + ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", task_hdr.stack_end-task_hdr.stack_start, + task_hdr.stack_start, task_hdr.stack_end); + // Increase core dump size by task stack size + uint32_t stk_vaddr = 0; + uint32_t stk_paddr = 0; + uint32_t stk_len = esp_core_dump_get_stack(&task_hdr, &stk_vaddr, &stk_paddr); + data_len += esp_core_dump_get_memory_len(stk_vaddr, stk_vaddr+stk_len); + // Add tcb size + data_len += (tcb_sz + sizeof(core_dump_task_header_t)); + if (mem_seg.size > 0) { + ESP_COREDUMP_LOG_PROCESS("Add interrupted task stack %lu bytes @ %x", + mem_seg.size, mem_seg.start); + data_len += esp_core_dump_get_memory_len(mem_seg.start, mem_seg.start+mem_seg.size); + data_len += sizeof(core_dump_mem_seg_header_t); + hdr.mem_segs_num++; + } + } + ESP_COREDUMP_LOGI("Found tasks: good %d, bad %d, mem segs %d", hdr.tasks_num, bad_tasks_num, hdr.mem_segs_num); + + // Check if current task TCB is broken + if (cur_task == NULL) { + ESP_COREDUMP_LOG_PROCESS("The current crashed task is broken."); + cur_task = esp_core_dump_get_next_task(NULL); + if (cur_task == NULL) { + ESP_COREDUMP_LOGE("No valid tasks in the system!"); + return ESP_FAIL; + } + } + + // Add user memory regions data size + for (coredump_region_t i = COREDUMP_MEMORY_START; i < COREDUMP_MEMORY_MAX; i++) { + uint32_t start = 0; + int data_sz = esp_core_dump_get_user_ram_info(i, &start); + if (data_sz < 0) { + ESP_COREDUMP_LOGE("Invalid memory segment size!"); + return ESP_FAIL; + } + if (data_sz > 0) { + hdr.mem_segs_num++; + data_len += sizeof(core_dump_mem_seg_header_t) + esp_core_dump_get_memory_len(start, start + data_sz); + } + } + + // Add core dump header size + data_len += sizeof(core_dump_header_t); + + ESP_COREDUMP_LOG_PROCESS("Core dump length=%lu, tasks processed: %d, broken tasks: %d", + data_len, hdr.tasks_num, 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, error=%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, error=%d!", err); + return err; + } + } + + // Write header + hdr.data_len = data_len; + hdr.version = COREDUMP_VERSION_BIN_CURRENT; + hdr.tcb_sz = tcb_sz; + err = write_cfg->write(write_cfg->priv, &hdr, sizeof(core_dump_header_t)); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to write core dump header error=%d!", err); + return err; + } + + // Save tasks + esp_core_dump_reset_tasks_snapshots_iter(); + // Write first crashed task data first (not always first task in the snapshot) + ESP_COREDUMP_LOGD("Save first crashed task %x", cur_task); + if (esp_core_dump_get_task_snapshot(cur_task, &task_hdr, NULL)) { + err = esp_core_dump_save_task(write_cfg, &task_hdr); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to save first crashed task %x, error=%d!", + task_hdr.tcb_addr, err); + return err; + } + } + // Write all other tasks in the snapshot + task = NULL; + while ((task = esp_core_dump_get_next_task(task))) { + if (!esp_core_dump_get_task_snapshot(task, &task_hdr, NULL)) + continue; + // Skip first crashed task + if (task == cur_task) { + continue; + } + ESP_COREDUMP_LOGD("Save task %x (TCB:%x, stack:%x..%x)", task, task_hdr.tcb_addr, task_hdr.stack_start, task_hdr.stack_end); + err = esp_core_dump_save_task(write_cfg, &task_hdr); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to save core dump task %x, error=%d!", + task_hdr.tcb_addr, err); + return err; + } + } + + // Save interrupted stacks of the tasks + // Actually there can be tasks interrupted at the same time, one on every core including the crashed one. + task = NULL; + esp_core_dump_reset_tasks_snapshots_iter(); + while ((task = esp_core_dump_get_next_task(task))) { + if (!esp_core_dump_get_task_snapshot(task, &task_hdr, &mem_seg)) + continue; + if (mem_seg.size > 0) { + ESP_COREDUMP_LOG_PROCESS("Save interrupted task stack %lu bytes @ %x", + mem_seg.size, mem_seg.start); + err = esp_core_dump_save_mem_segment(write_cfg, &mem_seg); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to save interrupted task stack, error=%d!", err); + return err; + } + } + } + + // save user memory regions + if (esp_core_dump_get_user_ram_segments() > 0) { + for (coredump_region_t i = COREDUMP_MEMORY_START; i < COREDUMP_MEMORY_MAX; i++) { + uint32_t start = 0; + int data_sz = esp_core_dump_get_user_ram_info(i, &start); + + if (data_sz < 0) { + ESP_COREDUMP_LOGE("Invalid memory segment size"); + return ESP_FAIL; + } + + if (data_sz > 0) { + mem_seg.start = start; + mem_seg.size = esp_core_dump_get_memory_len(start, start + data_sz);; + ESP_COREDUMP_LOG_PROCESS("Save user memory region %lu bytes @ %x", + mem_seg.size, mem_seg.start); + err = esp_core_dump_save_mem_segment(write_cfg, &mem_seg); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Failed to save user memory region, error=%d!", err); + return err; + } + } + } + } + + // 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 error=%d!", err); + return err; + } + } + if (bad_tasks_num) { + ESP_COREDUMP_LOGE("Found %d broken tasks!", bad_tasks_num); + } + return err; +} + +#endif diff --git a/components/espcoredump/src/core_dump_checksum.c b/components/espcoredump/src/core_dump_checksum.c new file mode 100644 index 0000000000..a20c2ac53e --- /dev/null +++ b/components/espcoredump/src/core_dump_checksum.c @@ -0,0 +1,189 @@ +// Copyright 2015-2020 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. + +/** + * @file + * @brief Checksum interface implemetation + * + * This file is an implementation for the coredump checksum interface defined + * in "core_dump_checksum.h". + * Please refer to this file for more information about the functions. + */ + +#include +#include "esp_core_dump_port_impl.h" +#include "esp_core_dump_types.h" +#include "core_dump_checksum.h" +#include "esp_attr.h" + +#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 +#include "esp_rom_crc.h" +#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 +#include "mbedtls/sha256.h" +#endif + +#if CONFIG_ESP_COREDUMP_ENABLE + +const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_checksum"; + +#define COREDUMP_SHA256_LEN 32 + +typedef uint32_t core_dump_crc_t; + +struct core_dump_checksum_ctx { +#if CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 + mbedtls_sha256_context ctx; + uint8_t sha_output[COREDUMP_SHA256_LEN]; +#elif CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 + core_dump_crc_t crc; +#endif + /* Number of bytes used to calculate the checksum */ + uint32_t total_bytes_checksum; +}; + +static core_dump_checksum_ctx s_checksum_context = { 0 }; + +/** + * Get ELF core dump version. + * Please check esp checksum interface for more details. + */ +uint32_t esp_core_dump_elf_version(void) +{ +#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 + return COREDUMP_VERSION_ELF_CRC32; +#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 + return COREDUMP_VERSION_ELF_SHA256; +#endif +} + +void esp_core_dump_checksum_init(core_dump_checksum_ctx** out_ctx) +{ + if (out_ctx) { +#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 + s_checksum_context.crc = 0; +#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 + mbedtls_sha256_init(&s_checksum_context.ctx); + (void)mbedtls_sha256_starts_ret(&s_checksum_context.ctx, 0); +#endif + s_checksum_context.total_bytes_checksum = 0; + + *out_ctx = &s_checksum_context; + } +} + + +void esp_core_dump_checksum_update(core_dump_checksum_ctx* cks_ctx, void* data, size_t data_len) +{ + ESP_COREDUMP_DEBUG_ASSERT(cks_ctx); + + if (data) { +#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 + cks_ctx->crc = esp_rom_crc32_le(cks_ctx->crc, data, data_len); +#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 +#if CONFIG_MBEDTLS_HARDWARE_SHA + // set software mode of SHA calculation + cks_ctx->ctx.mode = ESP_MBEDTLS_SHA256_SOFTWARE; +#endif + (void)mbedtls_sha256_update_ret(&cks_ctx->ctx, data, data_len); +#endif + // keep counter of cashed bytes + cks_ctx->total_bytes_checksum += data_len; + } else { + ESP_COREDUMP_LOGE("Empty data to add to checksum calculation!"); + } +} + + +uint32_t esp_core_dump_checksum_finish(core_dump_checksum_ctx* cks_ctx, core_dump_checksum_bytes* chs_ptr) +{ + uint32_t chs_len = 0; + + /* cks_ctx pointer can be NULL only if chs_ptr is also NULL. */ + ESP_COREDUMP_DEBUG_ASSERT(cks_ctx != NULL || chs_ptr == NULL); + +#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 + if (chs_ptr != NULL) { + *chs_ptr = (core_dump_checksum_bytes) &cks_ctx->crc; + } + chs_len = sizeof(cks_ctx->crc); + +#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 + if (chs_ptr != NULL) { + (void)mbedtls_sha256_finish_ret(&cks_ctx->ctx, (uint8_t*)&cks_ctx->sha_output); + *chs_ptr = &cks_ctx->sha_output[0]; + mbedtls_sha256_free(&cks_ctx->ctx); + } + chs_len = sizeof(cks_ctx->sha_output); + +#endif + + ESP_COREDUMP_LOG_PROCESS("Total length of hashed data: %d!", cks_ctx->total_bytes_checksum); + return chs_len; +} + + +/** + * Returns the size, in bytes, of the checksums. + * Currently, this function is just an alias to esp_core_dump_checksum_finish + * function, which can return the size of the checksum if given parameters + * are NULL. However, the implementation can evolve in the future independently + * from esp_core_dump_checksum_finish function. + */ +uint32_t esp_core_dump_checksum_size(void) +{ + return esp_core_dump_checksum_finish(NULL, NULL); +} + + +#if CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 + +static void esp_core_dump_print_sha256(const char* msg, const uint8_t* sha_output) +{ + /* As this function is only called by `esp_core_dump_print_checksum`, we + * have the guarantee that sha_output is not NULL. */ + if (msg != NULL) { + esp_rom_printf(DRAM_STR("%s='"), msg); + } + + for (int i = 0; i < COREDUMP_SHA256_LEN; i++) { + esp_rom_printf(DRAM_STR("%02x"), sha_output[i]); + } + esp_rom_printf(DRAM_STR("'\r\n")); +} + +#endif + + +/** + * Prints a message and a checksum given as parameters. + * This function is useful when the caller isn't explicitly aware of which + * checksum type (CRC32, SHA256, etc) is being used. + */ +void esp_core_dump_print_checksum(const char* msg, core_dump_checksum_bytes checksum) +{ + ESP_COREDUMP_DEBUG_ASSERT(checksum != NULL); + +#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 + if (msg != NULL) { + esp_rom_printf(DRAM_STR("%s='"), msg); + } + esp_rom_printf(DRAM_STR("%08x"), *((const uint32_t*) checksum)); + esp_rom_printf(DRAM_STR("'\r\n")); +#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 + esp_core_dump_print_sha256(msg, (const uint8_t*) checksum); +#endif +} + + +#endif diff --git a/components/espcoredump/src/core_dump_common.c b/components/espcoredump/src/core_dump_common.c index 2d3e16a7c3..d67eda41e4 100644 --- a/components/espcoredump/src/core_dump_common.c +++ b/components/espcoredump/src/core_dump_common.c @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2020 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. @@ -14,266 +14,140 @@ #include #include #include "sdkconfig.h" +#include "soc/soc_memory_layout.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_rom_sys.h" +#include "esp_core_dump_port.h" +#include "esp_core_dump_common.h" #include "core_dump_elf.h" +#include "core_dump_binary.h" const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_common"; -#if CONFIG_ESP_COREDUMP_DATA_FORMAT_BIN +#if CONFIG_ESP_COREDUMP_ENABLE -static esp_err_t esp_core_dump_save_task(core_dump_write_config_t *write_cfg, - core_dump_task_header_t *task) -{ - esp_err_t err = ESP_FAIL; - uint32_t stk_vaddr, stk_len; - uint32_t stk_paddr = esp_core_dump_get_stack(task, &stk_vaddr, &stk_len); +#define COREDUMP_GET_MEMORY_SIZE(end, start) (end - start) - stk_len = esp_core_dump_get_memory_len(stk_vaddr, stk_vaddr+stk_len); +/** + * @brief Memory regions to dump, defined at compile time. + */ +extern int _coredump_dram_start; +extern int _coredump_dram_end; +extern int _coredump_iram_start; +extern int _coredump_iram_end; +extern int _coredump_rtc_start; +extern int _coredump_rtc_end; +extern int _coredump_rtc_fast_start; +extern int _coredump_rtc_fast_end; - // Save TCB address, stack base and stack top addr - err = write_cfg->write(write_cfg->priv, (void*)task, sizeof(core_dump_task_header_t)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write task header, error=%d!", err); - return err; - } - // Save TCB block - err = write_cfg->write(write_cfg->priv, task->tcb_addr, esp_core_dump_get_tcb_len()); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write TCB, error=%d!", err); - return err; - } - // Save task stack - err = write_cfg->write(write_cfg->priv, (void*)stk_paddr, stk_len); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write stack for task (TCB:%x), stack_start=%x, error=%d!", - task->tcb_addr, - stk_vaddr, - err); - return err; - } - - ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x) dump is saved.", - task->tcb_addr); - return ESP_OK; -} - -static esp_err_t esp_core_dump_save_mem_segment(core_dump_write_config_t* write_cfg, - core_dump_mem_seg_header_t* seg) -{ - esp_err_t err = ESP_FAIL; - - if (!esp_core_dump_mem_seg_is_sane(seg->start, seg->size)) { - ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu)!", - seg->start, seg->size); - return ESP_FAIL; - } - // Save TCB address, stack base and stack top addr - err = write_cfg->write(write_cfg->priv, (void*)seg, sizeof(core_dump_mem_seg_header_t)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write memory segment header, error=%d!", err); - return err; - } - // Save memory contents - err = write_cfg->write(write_cfg->priv, (void*)seg->start, seg->size); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu), error=%d!", - seg->start, seg->size, err); - return err; - } - ESP_COREDUMP_LOG_PROCESS("Memory segment (%x, %lu) is saved.", - seg->start, seg->size); - return ESP_OK; -} - -static esp_err_t esp_core_dump_write_binary(core_dump_write_config_t *write_cfg) -{ - esp_err_t err; - uint32_t tcb_sz = esp_core_dump_get_tcb_len(); - uint32_t data_len = 0, bad_tasks_num = 0; - core_dump_header_t hdr = {0}; - core_dump_task_header_t task_hdr; - core_dump_mem_seg_header_t mem_seg; - void *task = NULL, *cur_task = NULL; - - // Verifies all tasks in the snapshot - esp_core_dump_reset_tasks_snapshots_iter(); - while ((task = esp_core_dump_get_next_task(task))) { - if (!esp_core_dump_get_task_snapshot(task, &task_hdr, &mem_seg)) { - bad_tasks_num++; - continue; - } - hdr.tasks_num++; - if (task == esp_core_dump_get_current_task_handle()) { - cur_task = task; - ESP_COREDUMP_LOG_PROCESS("Task %x %x is first crashed task.", cur_task, task_hdr.tcb_addr); - } - ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", task_hdr.stack_end-task_hdr.stack_start, - task_hdr.stack_start, task_hdr.stack_end); - // Increase core dump size by task stack size - uint32_t stk_vaddr, stk_len; - esp_core_dump_get_stack(&task_hdr, &stk_vaddr, &stk_len); - data_len += esp_core_dump_get_memory_len(stk_vaddr, stk_vaddr+stk_len); - // Add tcb size - data_len += (tcb_sz + sizeof(core_dump_task_header_t)); - if (mem_seg.size > 0) { - ESP_COREDUMP_LOG_PROCESS("Add interrupted task stack %lu bytes @ %x", - mem_seg.size, mem_seg.start); - data_len += esp_core_dump_get_memory_len(mem_seg.start, mem_seg.start+mem_seg.size); - data_len += sizeof(core_dump_mem_seg_header_t); - hdr.mem_segs_num++; - } - } - ESP_COREDUMP_LOGI("Found tasks: good %d, bad %d, mem segs %d", hdr.tasks_num, bad_tasks_num, hdr.mem_segs_num); - - // Check if current task TCB is broken - if (cur_task == NULL) { - ESP_COREDUMP_LOG_PROCESS("The current crashed task is broken."); - cur_task = esp_core_dump_get_next_task(NULL); - if (cur_task == NULL) { - ESP_COREDUMP_LOGE("No valid tasks in the system!"); - return ESP_FAIL; - } - } - - // Add user memory regions data size - for (coredump_region_t i = COREDUMP_MEMORY_START; i < COREDUMP_MEMORY_MAX; i++) { - uint32_t start = 0; - int data_sz = esp_core_dump_get_user_ram_info(i, &start); - if (data_sz < 0) { - ESP_COREDUMP_LOGE("Invalid memory segment size!"); - return ESP_FAIL; - } - if (data_sz > 0) { - hdr.mem_segs_num++; - data_len += sizeof(core_dump_mem_seg_header_t) + esp_core_dump_get_memory_len(start, start + data_sz); - } - } - - // Add core dump header size - data_len += sizeof(core_dump_header_t); - - ESP_COREDUMP_LOG_PROCESS("Core dump length=%lu, tasks processed: %d, broken tasks: %d", - data_len, hdr.tasks_num, 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, error=%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, error=%d!", err); - return err; - } - } - - // Write header - hdr.data_len = data_len; - hdr.version = COREDUMP_VERSION; - hdr.tcb_sz = tcb_sz; - err = write_cfg->write(write_cfg->priv, &hdr, sizeof(core_dump_header_t)); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to write core dump header error=%d!", err); - return err; - } - - // Save tasks - esp_core_dump_reset_tasks_snapshots_iter(); - // Write first crashed task data first (not always first task in the snapshot) - ESP_COREDUMP_LOGD("Save first crashed task %x", cur_task); - if (esp_core_dump_get_task_snapshot(cur_task, &task_hdr, NULL)) { - err = esp_core_dump_save_task(write_cfg, &task_hdr); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to save first crashed task %x, error=%d!", - task_hdr.tcb_addr, err); - return err; - } - } - // Write all other tasks in the snapshot - task = NULL; - while ((task = esp_core_dump_get_next_task(task))) { - if (!esp_core_dump_get_task_snapshot(task, &task_hdr, NULL)) - continue; - // Skip first crashed task - if (task == cur_task) { - continue; - } - ESP_COREDUMP_LOGD("Save task %x (TCB:%x, stack:%x..%x)", task, task_hdr.tcb_addr, task_hdr.stack_start, task_hdr.stack_end); - err = esp_core_dump_save_task(write_cfg, &task_hdr); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to save core dump task %x, error=%d!", - task_hdr.tcb_addr, err); - return err; - } - } - - // Save interrupted stacks of the tasks - // Actually there can be tasks interrupted at the same time, one on every core including the crashed one. - task = NULL; - esp_core_dump_reset_tasks_snapshots_iter(); - while ((task = esp_core_dump_get_next_task(task))) { - if (!esp_core_dump_get_task_snapshot(task, &task_hdr, &mem_seg)) - continue; - if (mem_seg.size > 0) { - ESP_COREDUMP_LOG_PROCESS("Save interrupted task stack %lu bytes @ %x", - mem_seg.size, mem_seg.start); - err = esp_core_dump_save_mem_segment(write_cfg, &mem_seg); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to save interrupted task stack, error=%d!", err); - return err; - } - } - } - - // save user memory regions - if (esp_core_dump_get_user_ram_segments() > 0) { - for (coredump_region_t i = COREDUMP_MEMORY_START; i < COREDUMP_MEMORY_MAX; i++) { - uint32_t start = 0; - int data_sz = esp_core_dump_get_user_ram_info(i, &start); - - if (data_sz < 0) { - ESP_COREDUMP_LOGE("Invalid memory segment size"); - return ESP_FAIL; - } - - if (data_sz > 0) { - mem_seg.start = start; - mem_seg.size = esp_core_dump_get_memory_len(start, start + data_sz);; - ESP_COREDUMP_LOG_PROCESS("Save user memory region %lu bytes @ %x", - mem_seg.size, mem_seg.start); - err = esp_core_dump_save_mem_segment(write_cfg, &mem_seg); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Failed to save user memory region, error=%d!", err); - return err; - } - } - } - } - - // 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 error=%d!", err); - return err; - } - } - if (bad_tasks_num) { - ESP_COREDUMP_LOGE("Found %d broken tasks!", bad_tasks_num); - } - return err; -} +/** + * @brief In the menconfig, it is possible to specify a specific stack size for + * core dump generation. + */ +#if CONFIG_ESP_COREDUMP_STACK_SIZE > 0 +/** + * @brief If stack size has been specified for the core dump generation, create + * a stack that will be used during the whole core dump generation. + */ +#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG + /* Increase stack size in verbose mode */ + #define ESP_COREDUMP_STACK_SIZE (CONFIG_ESP_COREDUMP_STACK_SIZE+100) +#else + #define ESP_COREDUMP_STACK_SIZE CONFIG_ESP_COREDUMP_STACK_SIZE #endif + +#define COREDUMP_STACK_FILL_BYTE (0xa5U) + +static uint8_t s_coredump_stack[ESP_COREDUMP_STACK_SIZE]; +static uint8_t* s_core_dump_sp = NULL; +static uint8_t* s_core_dump_backup = NULL; + +/** + * @brief Function setting up the core dump stack. + * + * @note This function **must** be aligned as it modifies the + * stack pointer register. + */ +FORCE_INLINE_ATTR void esp_core_dump_setup_stack(void) +{ + s_core_dump_sp = (uint8_t *)((uint32_t)(s_coredump_stack + ESP_COREDUMP_STACK_SIZE - 1) & ~0xf); + memset(s_coredump_stack, COREDUMP_STACK_FILL_BYTE, ESP_COREDUMP_STACK_SIZE); + + /* watchpoint 1 can be used for task stack overflow detection, re-use it, it is no more necessary */ + //esp_clear_watchpoint(1); + //esp_set_watchpoint(1, s_coredump_stack, 1, ESP_WATCHPOINT_STORE); + + /* Replace the stack pointer depending on the architecture, but save the + * current stack pointer, in order to be able too restore it later. + * This function must be inlined. */ + s_core_dump_backup = esp_core_dump_replace_sp(s_core_dump_sp); + ESP_COREDUMP_LOGI("Backing up stack @ %p and use core dump stack @ %p", + s_core_dump_backup, get_sp()); +} + +/** + * @brief Calculate how many bytes are free on the stack set up earlier. + * + * @return Size, in bytes, of the available space on the stack. + */ +FORCE_INLINE_ATTR uint32_t esp_core_dump_free_stack_space(const uint8_t *pucStackByte) +{ + uint32_t ulCount = 0U; + while ( ulCount < ESP_COREDUMP_STACK_SIZE && + *pucStackByte == (uint8_t)COREDUMP_STACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + ulCount++; + } + ulCount /= sizeof(uint8_t); + return ulCount; +} + +/** + * @brief Print how many bytes have been used on the stack to create the core + * dump. + */ +FORCE_INLINE_ATTR void esp_core_dump_report_stack_usage(void) +{ + uint32_t bytes_free = esp_core_dump_free_stack_space(s_coredump_stack); + ESP_COREDUMP_LOGI("Core dump used %u bytes on stack. %u bytes left free.", + s_core_dump_sp - s_coredump_stack - bytes_free, bytes_free); + + /* Restore the stack pointer. */ + ESP_COREDUMP_LOGI("Restoring stack @ %p", s_core_dump_backup); + esp_core_dump_replace_sp(s_core_dump_backup); +} + +#else +FORCE_INLINE_ATTR void esp_core_dump_setup_stack(void) +{ + /* If we are in ISR set watchpoint to the end of ISR stack */ + if (esp_core_dump_in_isr_context()) { + uint8_t* topStack = esp_core_dump_get_isr_stack_top(); + esp_clear_watchpoint(1); + esp_set_watchpoint(1, topStack+xPortGetCoreID()*configISR_STACK_SIZE, 1, ESP_WATCHPOINT_STORE); + } else { + /* for tasks user should enable stack overflow detection in menuconfig + TODO: if not enabled in menuconfig enable it ourselves */ + } +} + + +FORCE_INLINE_ATTR void esp_core_dump_report_stack_usage(void) +{ +} +#endif + +static void* s_exc_frame = NULL; + inline void esp_core_dump_write(panic_info_t *info, core_dump_write_config_t *write_cfg) { #ifndef CONFIG_ESP_ENABLE_COREDUMP_TO_NONE esp_err_t err = ESP_ERR_NOT_SUPPORTED; + s_exc_frame = (void*) info->frame; esp_core_dump_setup_stack(); esp_core_dump_port_init(info); @@ -293,3 +167,139 @@ void __attribute__((weak)) esp_core_dump_init(void) { /* do nothing by default */ } + +/** + * Common functions related to core dump generation. + */ +static void esp_core_dump_switch_task_stack_to_isr(core_dump_task_header_t *task, + core_dump_mem_seg_header_t *stack) +{ + if (stack != NULL) { + stack->start = task->stack_start; + stack->size = esp_core_dump_get_memory_len(task->stack_start, task->stack_end); + } + task->stack_start = (uint32_t) s_exc_frame; + task->stack_end = esp_core_dump_get_isr_stack_end(); + ESP_COREDUMP_LOG_PROCESS("Switched task %x to ISR stack [%x...%x]", task->tcb_addr, + task->stack_start, + task->stack_end); +} + +inline void esp_core_dump_reset_tasks_snapshots_iter(void) +{ + esp_core_dump_reset_fake_stacks(); +} + +inline void *esp_core_dump_get_next_task(void *handle) +{ + return pxTaskGetNext(handle); +} + +bool esp_core_dump_get_task_snapshot(void *handle, core_dump_task_header_t *task, + core_dump_mem_seg_header_t *interrupted_stack) +{ + TaskSnapshot_t rtos_snapshot = { 0 }; + + if (interrupted_stack != NULL) { + interrupted_stack->size = 0; + } + + vTaskGetSnapshot(handle, &rtos_snapshot); + task->tcb_addr = handle; + task->stack_start = (uint32_t)rtos_snapshot.pxTopOfStack; + task->stack_end = (uint32_t)rtos_snapshot.pxEndOfStack; + + if (!xPortInterruptedFromISRContext() && handle == esp_core_dump_get_current_task_handle()) { + // Set correct stack top for current task; only modify if we came from the task, + // and not an ISR that crashed. + task->stack_start = (uint32_t) s_exc_frame; + } + if (!esp_core_dump_check_task(task)) { + ESP_COREDUMP_LOG_PROCESS("Task %x is broken!", handle); + return false; + } + if (handle == esp_core_dump_get_current_task_handle()) { + ESP_COREDUMP_LOG_PROCESS("Crashed task %x", handle); + esp_core_dump_port_set_crashed_tcb((uint32_t)handle); + if (xPortInterruptedFromISRContext()) { + esp_core_dump_switch_task_stack_to_isr(task, interrupted_stack); + } + } + return true; +} + +uint32_t esp_core_dump_get_user_ram_segments(void) +{ + uint32_t total_sz = 0; + + // count number of memory segments to insert into ELF structure + total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_dram_end, &_coredump_dram_start) > 0 ? 1 : 0; + total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_end, &_coredump_rtc_start) > 0 ? 1 : 0; + total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_fast_end, &_coredump_rtc_fast_start) > 0 ? 1 : 0; + total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_iram_end, &_coredump_iram_start) > 0 ? 1 : 0; + + return total_sz; +} + +uint32_t esp_core_dump_get_user_ram_size(void) +{ + uint32_t total_sz = 0; + + total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_dram_end, &_coredump_dram_start); + total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_end, &_coredump_rtc_start); + total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_fast_end, &_coredump_rtc_fast_start); + total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_iram_end, &_coredump_iram_start); + + return total_sz; +} + +int esp_core_dump_get_user_ram_info(coredump_region_t region, uint32_t *start) +{ + int total_sz = -1; + + ESP_COREDUMP_DEBUG_ASSERT(start != NULL); + + switch (region) { + case COREDUMP_MEMORY_DRAM: + *start = (uint32_t)&_coredump_dram_start; + total_sz = (uint8_t *)&_coredump_dram_end - (uint8_t *)&_coredump_dram_start; + break; + + case COREDUMP_MEMORY_IRAM: + *start = (uint32_t)&_coredump_iram_start; + total_sz = (uint8_t *)&_coredump_iram_end - (uint8_t *)&_coredump_iram_start; + break; + + case COREDUMP_MEMORY_RTC: + *start = (uint32_t)&_coredump_rtc_start; + total_sz = (uint8_t *)&_coredump_rtc_end - (uint8_t *)&_coredump_rtc_start; + break; + + case COREDUMP_MEMORY_RTC_FAST: + *start = (uint32_t)&_coredump_rtc_fast_start; + total_sz = (uint8_t *)&_coredump_rtc_fast_end - (uint8_t *)&_coredump_rtc_fast_start; + break; + + default: + break; + } + + return total_sz; +} + +inline bool esp_core_dump_tcb_addr_is_sane(uint32_t addr) +{ + return esp_core_dump_mem_seg_is_sane(addr, esp_core_dump_get_tcb_len()); +} + +inline bool esp_core_dump_in_isr_context(void) +{ + return xPortInterruptedFromISRContext(); +} + +inline core_dump_task_handle_t esp_core_dump_get_current_task_handle() +{ + return (core_dump_task_handle_t) xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID()); +} + +#endif diff --git a/components/espcoredump/src/core_dump_elf.c b/components/espcoredump/src/core_dump_elf.c index f5bbbcaa85..80e6034c6c 100644 --- a/components/espcoredump/src/core_dump_elf.c +++ b/components/espcoredump/src/core_dump_elf.c @@ -16,7 +16,11 @@ #include "esp_partition.h" #include "esp_ota_ops.h" #include "sdkconfig.h" +#include "core_dump_checksum.h" #include "core_dump_elf.h" +#include "esp_core_dump_port.h" +#include "esp_core_dump_port_impl.h" +#include "esp_core_dump_common.h" #define ELF_CLASS ELFCLASS32 @@ -277,7 +281,7 @@ static int elf_add_stack(core_dump_elf_t *self, core_dump_task_header_t *task) ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid task pointer."); - stack_paddr = esp_core_dump_get_stack(task, &stack_vaddr, &stack_len); + stack_len = esp_core_dump_get_stack(task, &stack_paddr, &stack_vaddr); ESP_COREDUMP_LOG_PROCESS("Add stack for task 0x%x: addr 0x%x, sz %u", task->tcb_addr, stack_vaddr, stack_len); int ret = elf_add_segment(self, PT_LOAD, @@ -296,7 +300,7 @@ static int elf_add_tcb(core_dump_elf_t *self, core_dump_task_header_t *task) esp_core_dump_get_tcb_len()); int ret = elf_add_segment(self, PT_LOAD, (uint32_t)task->tcb_addr, - (void*)task->tcb_addr, + task->tcb_addr, esp_core_dump_get_tcb_len()); return ret; } @@ -364,9 +368,10 @@ static int elf_process_note_segment(core_dump_elf_t *self, int notes_size) static int elf_process_tasks_regs(core_dump_elf_t *self) { - void *task; - int len = 0, ret; - core_dump_task_header_t task_hdr; + core_dump_task_header_t task_hdr = { 0 }; + void *task = NULL; + int len = 0; + int ret = 0; esp_core_dump_reset_tasks_snapshots_iter(); task = esp_core_dump_get_current_task_handle(); @@ -424,13 +429,13 @@ static int elf_save_task(core_dump_elf_t *self, core_dump_task_header_t *task) static int elf_write_tasks_data(core_dump_elf_t *self) { int elf_len = 0; - void *task; - core_dump_task_header_t task_hdr; - core_dump_mem_seg_header_t interrupted_stack; + void *task = NULL; + core_dump_task_header_t task_hdr = { 0 }; + core_dump_mem_seg_header_t interrupted_stack = { 0 }; int ret = ELF_PROC_ERR_OTHER; - uint16_t tasks_num = 0, bad_tasks_num = 0; + uint16_t tasks_num = 0; + uint16_t bad_tasks_num = 0; - bad_tasks_num = 0; ESP_COREDUMP_LOG_PROCESS("================ Processing task registers ================"); ret = elf_process_tasks_regs(self); ELF_CHECK_ERR((ret > 0), ret, "Tasks regs addition failed, return (%d).", ret); @@ -493,14 +498,14 @@ static int elf_write_core_dump_user_data(core_dump_elf_t *self) static int elf_write_core_dump_info(core_dump_elf_t *self) { - void *extra_info; + void *extra_info = NULL; ESP_COREDUMP_LOG_PROCESS("================ Processing coredump info ================"); int data_len = (int)sizeof(self->elf_version_info.app_elf_sha256); data_len = esp_ota_get_app_elf_sha256((char*)self->elf_version_info.app_elf_sha256, (size_t)data_len); ESP_COREDUMP_LOG_PROCESS("Application SHA256='%s', length=%d.", self->elf_version_info.app_elf_sha256, data_len); - self->elf_version_info.version = COREDUMP_VERSION; + self->elf_version_info.version = esp_core_dump_elf_version(); int ret = elf_add_note(self, "ESP_CORE_DUMP_INFO", ELF_ESP_CORE_DUMP_INFO_TYPE, @@ -561,9 +566,9 @@ static int esp_core_dump_do_write_elf_pass(core_dump_elf_t *self) esp_err_t esp_core_dump_write_elf(core_dump_write_config_t *write_cfg) { + static core_dump_elf_t self = { 0 }; + static core_dump_header_t dump_hdr = { 0 }; esp_err_t err = ESP_OK; - static core_dump_elf_t self; - static core_dump_header_t dump_hdr; int tot_len = sizeof(dump_hdr); int write_len = sizeof(dump_hdr); @@ -600,13 +605,13 @@ esp_err_t esp_core_dump_write_elf(core_dump_write_config_t *write_cfg) // Write core dump header dump_hdr.data_len = tot_len; - dump_hdr.version = COREDUMP_VERSION; + dump_hdr.version = esp_core_dump_elf_version(); dump_hdr.tasks_num = 0; // unused in ELF format dump_hdr.tcb_sz = 0; // unused in ELF format dump_hdr.mem_segs_num = 0; // unused in ELF format err = write_cfg->write(write_cfg->priv, - (void*)&dump_hdr, - sizeof(core_dump_header_t)); + (void*)&dump_hdr, + sizeof(core_dump_header_t)); if (err != ESP_OK) { ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err); return err; @@ -628,11 +633,6 @@ esp_err_t esp_core_dump_write_elf(core_dump_write_config_t *write_cfg) write_len += ret; ESP_COREDUMP_LOG_PROCESS("=========== Data written size = %d bytes ==========", write_len); - // Get checksum size - write_len += esp_core_dump_checksum_finish(write_cfg->priv, NULL); - if (write_len != tot_len) { - ESP_COREDUMP_LOGD("Write ELF failed (wrong length): %d != %d.", tot_len, write_len); - } // Write end, update checksum if (write_cfg->end) { err = write_cfg->end(write_cfg->priv); diff --git a/components/espcoredump/src/core_dump_flash.c b/components/espcoredump/src/core_dump_flash.c index 1103f4bc78..cf63d7539f 100644 --- a/components/espcoredump/src/core_dump_flash.c +++ b/components/espcoredump/src/core_dump_flash.c @@ -14,7 +14,8 @@ #include #include "esp_partition.h" #include "esp_log.h" -#include "esp_core_dump_priv.h" +#include "esp_core_dump_types.h" +#include "core_dump_checksum.h" #include "esp_flash_internal.h" #include "esp_flash_encrypt.h" #include "esp_rom_crc.h" @@ -33,6 +34,8 @@ typedef struct _core_dump_partition_t bool encrypted; } core_dump_partition_t; +typedef uint32_t core_dump_crc_t; + typedef struct _core_dump_flash_config_t { /* Core dump partition config. */ @@ -92,7 +95,7 @@ void esp_core_dump_flash_init(void) s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc(); } -static esp_err_t esp_core_dump_flash_write_data(void *priv, uint8_t *data, uint32_t data_size) +static esp_err_t esp_core_dump_flash_write_data(core_dump_write_data_t* priv, uint8_t* data, uint32_t data_size) { core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv; esp_err_t err = ESP_OK; @@ -100,7 +103,7 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, uint8_t *data, uint3 uint32_t wr_sz = 0; /* Make sure that the partition is large enough to hold the data. */ - assert((wr_data->off + data_size) < s_core_flash_config.partition.size); + ESP_COREDUMP_ASSERT((wr_data->off + data_size) < s_core_flash_config.partition.size); if (wr_data->cached_bytes) { /* Some bytes are in the cache, let's continue filling the cache @@ -129,7 +132,7 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, uint8_t *data, uint3 wr_data->off += COREDUMP_CACHE_SIZE; /* Update checksum with the newly written data on the flash. */ - esp_core_dump_checksum_update(wr_data, &wr_data->cached_data, COREDUMP_CACHE_SIZE); + esp_core_dump_checksum_update(wr_data->checksum_ctx, &wr_data->cached_data, COREDUMP_CACHE_SIZE); /* Reset cache from the next use. */ wr_data->cached_bytes = 0; @@ -167,7 +170,7 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, uint8_t *data, uint3 } /* Update the checksum with the newly written bytes */ - esp_core_dump_checksum_update(wr_data, data + written, wr_sz); + esp_core_dump_checksum_update(wr_data->checksum_ctx, data + written, wr_sz); wr_data->off += wr_sz; written += wr_sz; data_size -= wr_sz; @@ -184,7 +187,7 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, uint8_t *data, uint3 return ESP_OK; } -static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len) +static esp_err_t esp_core_dump_flash_write_prepare(core_dump_write_data_t *priv, uint32_t *data_len) { core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv; esp_err_t err = ESP_OK; @@ -192,7 +195,7 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le uint32_t cs_len = 0; /* Get the length, in bytes, of the checksum. */ - cs_len = esp_core_dump_checksum_finish(wr_data, NULL); + cs_len = esp_core_dump_checksum_size(); /* At the end of the core dump file, a padding may be added, according to the * cache size. We must take that padding into account. */ @@ -227,7 +230,7 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le /* Erase the amount of sectors needed. */ ESP_COREDUMP_LOGI("Erase flash %d bytes @ 0x%x", sec_num * SPI_FLASH_SEC_SIZE, s_core_flash_config.partition.start + 0); - assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size); + ESP_COREDUMP_ASSERT(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size); err = ESP_COREDUMP_FLASH_ERASE(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); @@ -236,22 +239,22 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le return err; } -static esp_err_t esp_core_dump_flash_write_start(void *priv) +static esp_err_t esp_core_dump_flash_write_start(core_dump_write_data_t* priv) { core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv; - esp_core_dump_checksum_init(wr_data); + esp_core_dump_checksum_init(&wr_data->checksum_ctx); return ESP_OK; } -static esp_err_t esp_core_dump_flash_write_end(void *priv) +static esp_err_t esp_core_dump_flash_write_end(core_dump_write_data_t* priv) { esp_err_t err = ESP_OK; - void* checksum = NULL; + core_dump_checksum_bytes checksum = NULL; uint32_t cs_len = 0; core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv; /* Get the size, in bytes of the checksum. */ - cs_len = esp_core_dump_checksum_finish(wr_data, NULL); + cs_len = esp_core_dump_checksum_size(); /* Flush cached bytes, including the zero padding at the end (if any). */ if (wr_data->cached_bytes) { @@ -265,14 +268,14 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv) } /* Update the checksum with the data written, including the padding. */ - esp_core_dump_checksum_update(wr_data, wr_data->cached_data, COREDUMP_CACHE_SIZE); + esp_core_dump_checksum_update(wr_data->checksum_ctx, wr_data->cached_data, COREDUMP_CACHE_SIZE); wr_data->off += COREDUMP_CACHE_SIZE; wr_data->cached_bytes = 0; } /* All data have been written to the flash, the cache is now empty, we can * terminate the checksum calculation. */ - esp_core_dump_checksum_finish(wr_data, &checksum); + esp_core_dump_checksum_finish(wr_data->checksum_ctx, &checksum); /* Use the cache to write the checksum if its size doesn't match the requirements. * (e.g. its size is not a multiple of 32) */ @@ -289,7 +292,7 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv) COREDUMP_CACHE_SIZE); } else { /* In that case, the length of the checksum must be a multiple of 16. */ - assert(cs_len % 16 == 0); + ESP_COREDUMP_ASSERT(cs_len % 16 == 0); err = esp_core_dump_flash_custom_write(s_core_flash_config.partition.start + wr_data->off, checksum, cs_len); } @@ -314,7 +317,7 @@ void esp_core_dump_to_flash(panic_info_t *info) return; } - /* Make sure that the partition can at least data length. */ + /* Make sure that the partition can at least hold the 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; @@ -345,7 +348,6 @@ void esp_core_dump_init(void) ESP_COREDUMP_LOGI("Found core dump %d bytes in flash @ 0x%x", core_data_sz, core_data_addr); } } -#endif esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size) { @@ -354,13 +356,13 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size) uint32_t size = 0; uint32_t offset = 0; const uint32_t checksum_size = esp_core_dump_checksum_size(); - void* checksum_calc = NULL; + core_dump_checksum_bytes checksum_calc = NULL; /* Initialize the checksum we have to read from the flash to the biggest * size we can have for a checksum. */ uint8_t checksum_read[COREDUMP_CHECKSUM_MAX_LEN] = { 0 }; /* Assert that we won't have any problems with our checksum size. */ - assert(checksum_size <= COREDUMP_CHECKSUM_MAX_LEN); + ESP_COREDUMP_DEBUG_ASSERT(checksum_size <= COREDUMP_CHECKSUM_MAX_LEN); /* Check the validity of the parameters. */ if (out_addr == NULL || out_size == NULL) { @@ -409,7 +411,7 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size) size -= checksum_size ; /* Initiate the checksum calculation for the coredump in the flash. */ - esp_core_dump_checksum_init(&wr_data); + esp_core_dump_checksum_init(&wr_data.checksum_ctx); while (size > 0) { /* Use the cache in core_dump_write_data_t structure to read the @@ -424,7 +426,7 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size) } /* Update the checksum according to what was just read. */ - esp_core_dump_checksum_update(&wr_data, wr_data.cached_data, toread); + esp_core_dump_checksum_update(wr_data.checksum_ctx, wr_data.cached_data, toread); /* Move the offset forward and decrease the remaining size. */ offset += toread; @@ -432,7 +434,7 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size) } /* The coredump has been totally read, finish the checksum calculation. */ - esp_core_dump_checksum_finish(&wr_data, &checksum_calc); + esp_core_dump_checksum_finish(wr_data.checksum_ctx, &checksum_calc); /* Read the checksum from the flash and compare to the one just * calculated. */ @@ -455,3 +457,5 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size) *out_addr = core_part->address; return ESP_OK; } + +#endif diff --git a/components/espcoredump/src/core_dump_uart.c b/components/espcoredump/src/core_dump_uart.c index b516dc4c5e..a04967d95c 100644 --- a/components/espcoredump/src/core_dump_uart.c +++ b/components/espcoredump/src/core_dump_uart.c @@ -15,19 +15,19 @@ #include "soc/uart_periph.h" #include "soc/gpio_periph.h" #include "driver/gpio.h" -#include "esp_core_dump_priv.h" +#include "esp_core_dump_types.h" +#include "esp_core_dump_port.h" +#include "esp_core_dump_common.h" #include "esp_rom_sys.h" -// TODO: move chip dependent part to portable code -#if CONFIG_IDF_TARGET_ESP32 -#include "esp32/clk.h" -#elif CONFIG_IDF_TARGET_ESP32S2 -#include "esp32s2/clk.h" -#endif const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_uart"; #if CONFIG_ESP_COREDUMP_ENABLE_TO_UART +/* This function exists on every board, thus, we don't need to specify + * explicitly the header for each board. */ +int esp_clk_cpu_freq(void); + 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+/"; @@ -53,53 +53,55 @@ static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8 dst[j++] = '\0'; } -static esp_err_t esp_core_dump_uart_write_start(void *priv) +static esp_err_t esp_core_dump_uart_write_start(core_dump_write_data_t *priv) { esp_err_t err = ESP_OK; core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv; - esp_core_dump_checksum_init(wr_data); + + ESP_COREDUMP_ASSERT(priv != NULL); + esp_core_dump_checksum_init(&wr_data->checksum_ctx); esp_rom_printf(DRAM_STR("================= CORE DUMP START =================\r\n")); return err; } -static esp_err_t esp_core_dump_uart_write_prepare(void *priv, uint32_t *data_len) +static esp_err_t esp_core_dump_uart_write_prepare(core_dump_write_data_t *priv, uint32_t *data_len) { - core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv; - uint32_t cs_len; - cs_len = esp_core_dump_checksum_finish(wr_data, NULL); - *data_len += cs_len; + ESP_COREDUMP_ASSERT(data_len != NULL); + *data_len += esp_core_dump_checksum_size(); return ESP_OK; } -static esp_err_t esp_core_dump_uart_write_end(void *priv) +static esp_err_t esp_core_dump_uart_write_end(core_dump_write_data_t *priv) { esp_err_t err = ESP_OK; - char buf[64 + 4]; - void* cs_addr = NULL; + char buf[64 + 4] = { 0 }; + core_dump_checksum_bytes cs_addr = NULL; core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv; if (wr_data) { - size_t cs_len = esp_core_dump_checksum_finish(wr_data, &cs_addr); + size_t cs_len = esp_core_dump_checksum_finish(wr_data->checksum_ctx, &cs_addr); wr_data->off += cs_len; esp_core_dump_b64_encode((const uint8_t *)cs_addr, cs_len, (uint8_t*)&buf[0]); esp_rom_printf(DRAM_STR("%s\r\n"), buf); } esp_rom_printf(DRAM_STR("================= CORE DUMP END =================\r\n")); -#if CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 + if (cs_addr) { - esp_core_dump_print_sha256(DRAM_STR("Coredump SHA256"), (uint8_t*)(cs_addr)); + esp_core_dump_print_checksum(DRAM_STR("Coredump checksum"), cs_addr); } -#endif + return err; } -static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len) +static esp_err_t esp_core_dump_uart_write_data(core_dump_write_data_t *priv, void * data, uint32_t data_len) { esp_err_t err = ESP_OK; - char buf[64 + 4]; + char buf[64 + 4] = { 0 }; char *addr = data; char *end = addr + data_len; core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv; + ESP_COREDUMP_ASSERT(data != NULL); + while (addr < end) { size_t len = end - addr; if (len > 48) len = 48; @@ -113,35 +115,33 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t if (wr_data) { wr_data->off += data_len; - esp_core_dump_checksum_update(wr_data, data, data_len); + esp_core_dump_checksum_update(wr_data->checksum_ctx, data, data_len); } return err; } static int esp_core_dump_uart_get_char(void) { - int i; + int i = -1; 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(panic_info_t *info) { - core_dump_write_config_t wr_cfg; - core_dump_write_data_t wr_data; - uint32_t tm_end, tm_cur; - int ch; - - memset(&wr_cfg, 0, sizeof(wr_cfg)); - wr_cfg.prepare = esp_core_dump_uart_write_prepare; - 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 = (void*)&wr_data; + core_dump_write_data_t wr_data = { 0 }; + core_dump_write_config_t wr_cfg = { + .prepare = esp_core_dump_uart_write_prepare, + .start = esp_core_dump_uart_write_start, + .end = esp_core_dump_uart_write_end, + .write = esp_core_dump_uart_write_data, + .priv = (void*)&wr_data + }; + uint32_t tm_end = 0; + uint32_t tm_cur = 0; + int ch = 0; // TODO: move chip dependent code to portable part //Make sure txd/rxd are enabled @@ -152,10 +152,10 @@ void esp_core_dump_to_uart(panic_info_t *info) 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_ESP_COREDUMP_UART_DELAY; + tm_end = esp_cpu_get_ccount() / cpu_ticks_per_ms + CONFIG_ESP_COREDUMP_UART_DELAY; ch = esp_core_dump_uart_get_char(); while (!(ch == '\n' || ch == '\r')) { - tm_cur = xthal_get_ccount() / cpu_ticks_per_ms; + tm_cur = esp_cpu_get_ccount() / cpu_ticks_per_ms; if (tm_cur >= tm_end){ break; } diff --git a/components/espcoredump/src/port/riscv/core_dump_port.c b/components/espcoredump/src/port/riscv/core_dump_port.c new file mode 100644 index 0000000000..898ddc3557 --- /dev/null +++ b/components/espcoredump/src/port/riscv/core_dump_port.c @@ -0,0 +1,378 @@ +// Copyright 2015-2019 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. + +/** + * @file + * @brief Core dump port implementation for RISC-V based boards. + */ + +#include +#include +#include "soc/soc_memory_layout.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "riscv/rvruntime-frames.h" +#include "esp_rom_sys.h" +#include "esp_core_dump_common.h" +#include "esp_core_dump_port.h" + +/* TAG used for logs */ +const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port"; + +/* Code associated to RISC-V in ELF format */ +#define COREDUMP_EM_RISCV 0xF3 + +#define COREDUMP_INVALID_CAUSE_VALUE 0xFFFF +#define COREDUMP_FAKE_STACK_START 0x20000000 +#define COREDUMP_FAKE_STACK_LIMIT 0x30000000 + +#if CONFIG_ESP_COREDUMP_ENABLE + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +/** + * Union representing the registers of the CPU as they will be written + * in the core dump. + * Registers can be adressed with their names thanks to the structure, or as + * an array of 32 words. + */ +#define RISCV_GP_REGS_COUNT 32 + +typedef union { + struct { + uint32_t pc; + uint32_t ra; + uint32_t sp; + uint32_t gp; + uint32_t tp; + uint32_t t0; + uint32_t t1; + uint32_t t2; + uint32_t s0; + uint32_t s1; + uint32_t a0; + uint32_t a1; + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; + uint32_t s2; + uint32_t s3; + uint32_t s4; + uint32_t s5; + uint32_t s6; + uint32_t s7; + uint32_t s8; + uint32_t s9; + uint32_t s10; + uint32_t s11; + uint32_t t3; + uint32_t t4; + uint32_t t5; + uint32_t t6; + }; + + uint32_t as_array[RISCV_GP_REGS_COUNT]; +} riscv_regs; + +/** + * The following structure represents the NOTE section in the coredump. + * Its type must be PR_STATUS as it contains the regsiters values and the + * program status. + * As our coredump will be used with GDB, we only need to fill the info + * it needs. We are going to use the macros taken from GDB's elf32-riscv.c + * Here is the structure GDB needs for PRSTATUS note: + * + * +---------------------------+--------------+---------------------------------+ + * | Offset | Size (bytes) | Data | + * +---------------------------+--------------+---------------------------------+ + * | PRSTATUS_OFFSET_PR_CURSIG | 2 | Signal that triggered the panic | + * | PRSTATUS_OFFSET_PR_PID | 4 | PID | + * | PRSTATUS_OFFSET_PR_REG | 32 | Registers value | + * +---------------------------+--------------+---------------------------------+ + * + * Other fields are not strictly required by GDB, we can then replace them + * by a padding. Among these fields, we can find PPID, SID or system time. + */ +#define PRSTATUS_SIZE 204 +#define PRSTATUS_OFFSET_PR_CURSIG 12 +#define PRSTATUS_OFFSET_PR_PID 24 +#define PRSTATUS_OFFSET_PR_REG 72 +#define ELF_GREGSET_T_SIZE 128 + +/* We can determine the padding thank to the previous macros */ +#define PRSTATUS_SIG_PADDING (PRSTATUS_OFFSET_PR_CURSIG) +#define PRSTATUS_PID_PADDING (PRSTATUS_OFFSET_PR_PID - PRSTATUS_OFFSET_PR_CURSIG - sizeof(uint32_t)) +#define PRSTATUS_REG_PADDING (PRSTATUS_OFFSET_PR_REG - PRSTATUS_OFFSET_PR_PID - sizeof(uint32_t)) +#define PRSTATUS_END_PADDING (PRSTATUS_SIZE - PRSTATUS_OFFSET_PR_REG - ELF_GREGSET_T_SIZE) + +typedef struct { + uint8_t _PADDING1[PRSTATUS_SIG_PADDING]; + uint16_t signal; + uint8_t _PADDING2[PRSTATUS_PID_PADDING]; + uint32_t pid; + uint8_t _PADDING3[PRSTATUS_REG_PADDING]; + riscv_regs regs; + uint8_t _PADDING4[PRSTATUS_END_PADDING]; +} riscv_prstatus; + +/** + * Assert that our structure is designed the way we are expecting it to be. + */ +_Static_assert(offsetof(riscv_prstatus, signal) == PRSTATUS_OFFSET_PR_CURSIG, + "Wrong offset for signal field in riscv_prstatus structure"); +_Static_assert(offsetof(riscv_prstatus, pid) == PRSTATUS_OFFSET_PR_PID, + "Wrong offset for pid field in riscv_prstatus structure"); +_Static_assert(offsetof(riscv_prstatus, regs) == PRSTATUS_OFFSET_PR_REG, + "Wrong offset for regs field in riscv_prstatus structure"); +_Static_assert(sizeof(riscv_regs) == ELF_GREGSET_T_SIZE, + "Wrong size for riscv_regs union"); +_Static_assert(sizeof(riscv_prstatus) == PRSTATUS_SIZE, + "Wrong size for riscv_prstatus structure"); + +/** + * Structure used to add some extra info inside core file. + */ +typedef struct { + uint32_t crashed_task_tcb; +} riscv_extra_info_t; + + +/* Allocate the fake stack that will be used by broken tasks. */ +static RvExcFrame s_fake_stack_frame = { + .mepc = COREDUMP_FAKE_STACK_START, + .sp = COREDUMP_FAKE_STACK_START + sizeof(RvExcFrame), +}; + +/* Keep a track of the number of fake stack distributed. Avoid giving the + * same fake address to two different tasks. */ +static uint32_t s_fake_stacks_num = 0; + +/* Statically initialize the extra information structure. */ +static riscv_extra_info_t s_extra_info = { 0 }; + +inline void esp_core_dump_port_init(panic_info_t *info) +{ + s_extra_info.crashed_task_tcb = COREDUMP_CURR_TASK_MARKER; +} + +/** + * Return the current architecture (RISC-V) ID. This will be written in the + * ELF file header. + */ +inline uint16_t esp_core_dump_get_arch_id() +{ + return COREDUMP_EM_RISCV; +} + +/** + * Reset fake tasks' stack counter. This lets use reuse the previously allocated + * fake stacks. + */ +void esp_core_dump_reset_fake_stacks(void) +{ + s_fake_stacks_num = 0; +} + +/** + * Function generating a fake stack address for task as deep as exception + * frame size. It is required for GDB to take task into account but avoid + * backtrace of stack. The espcoredump.py script is able to recognize + * that task is broken. + */ +static uint32_t esp_core_dump_generate_fake_stack(uint32_t *stk_addr) +{ + /* Offset of this new fake task stask. */ + const uint32_t offset = sizeof(s_fake_stack_frame) * s_fake_stacks_num++; + + /* Return the size and the fake address */ + *stk_addr = COREDUMP_FAKE_STACK_START + offset; + + return sizeof(s_fake_stack_frame); +} + +/** + * Return the top of the ISR stack. + */ +uint8_t* esp_core_dump_get_isr_stack_top(void) +{ + extern uint8_t* xIsrStackTop; + return xIsrStackTop; +} + +/** + * Return the end of the ISR stack . + */ +uint32_t esp_core_dump_get_isr_stack_end(void) +{ + uint8_t* isr_top_stack = esp_core_dump_get_isr_stack_top(); + return (uint32_t)(isr_top_stack + (xPortGetCoreID()+1)*configISR_STACK_SIZE); +} + +/** + * Check if the given stack is sane or not. + */ +static inline bool esp_core_dump_task_stack_end_is_sane(uint32_t sp) +{ + //TODO: currently core dump supports stacks in DRAM only, external SRAM not supported yet + return esp_ptr_in_dram((void *)sp); +} + +bool esp_core_dump_check_stack(core_dump_task_header_t *task) +{ + // Check task's stack + if (!esp_stack_ptr_is_sane(task->stack_start) || + !esp_core_dump_task_stack_end_is_sane(task->stack_end) || + (task->stack_start >= task->stack_end) || + ((task->stack_end-task->stack_start) > COREDUMP_MAX_TASK_STACK_SIZE)) { + // Check if current task stack is corrupted + ESP_COREDUMP_LOG_PROCESS("Invalid stack (%x...%x)!", task->stack_start, task->stack_end); + return false; + } + return true; +} + +/** + * Get the stack addresses (virtual and physical) and stack length of the task + * passed as argument. + * Returns the stack length. + */ +uint32_t esp_core_dump_get_stack(core_dump_task_header_t *task, + uint32_t* stk_vaddr, uint32_t* stk_paddr) +{ + const uint32_t stack_len = abs(task->stack_start - task->stack_end); + const uint32_t stack_addr = min(task->stack_start, task->stack_end); + + ESP_COREDUMP_DEBUG_ASSERT(stk_paddr != NULL && stk_vaddr != NULL); + + /* Provide the virtual stack address for any task. */ + *stk_vaddr = stack_addr; + + if (stack_addr >= COREDUMP_FAKE_STACK_START && + stack_addr < COREDUMP_FAKE_STACK_LIMIT) { + /* In this case, the stack address pointed by the task is a fake stack + * generated previously. So it doesn't really point to actual data. + * Thus, we must provide the address of the fake stack data. */ + *stk_paddr = (uint32_t) &s_fake_stack_frame; + } else { + *stk_paddr = stack_addr; + } + + return stack_len; +} + +/** + * Check the task passed as a parameter. + */ +bool esp_core_dump_check_task(core_dump_task_header_t *task) +{ + bool is_stack_sane = false; + + if (!esp_core_dump_tcb_addr_is_sane((uint32_t)task->tcb_addr)) { + ESP_COREDUMP_LOG_PROCESS("Bad TCB addr=%x!", task->tcb_addr); + return false; + } + + /* Check the sanity of the task's stack. If the stack is corrupted, replace + * it with a fake stack containing fake registers value. This is required + * by GDB. */ + is_stack_sane = esp_core_dump_check_stack(task); + + if (!is_stack_sane) { + const uint32_t stk_size = esp_core_dump_generate_fake_stack(&task->stack_start); + task->stack_end = (uint32_t)(task->stack_start + stk_size); + } + + return true; +} + +/** + * Check if the memory segment is sane + * + * Check the header file for more information. + */ +bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz) +{ + //TODO: external SRAM not supported yet + return (esp_ptr_in_dram((void *)addr) && esp_ptr_in_dram((void *)(addr+sz-1))) + || (esp_ptr_in_rtc_slow((void *)addr) && esp_ptr_in_rtc_slow((void *)(addr+sz-1))) + || (esp_ptr_in_rtc_dram_fast((void *)addr) && esp_ptr_in_rtc_dram_fast((void *)(addr+sz-1))) + || (esp_ptr_in_iram((void *)addr) && esp_ptr_in_iram((void *)(addr+sz-1))); +} + +/** + * Get the task's registers dump when the panic occured. + * Returns the size, in bytes, of the data pointed by reg_dumps. + * The data pointed by reg_dump are allocated statically, thus, they must be + * used (or copied) before calling this function again. + */ +uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, void **reg_dump) +{ + static riscv_prstatus prstatus = { 0 }; + RvExcFrame* exc_frame = NULL; + uint32_t stack_vaddr = 0; + uint32_t stack_paddr = 0; + uint32_t stack_len = 0; + + ESP_COREDUMP_LOG_PROCESS("Add regs for task 0x%x", task->tcb_addr); + + stack_len = esp_core_dump_get_stack(task, &stack_paddr, &stack_vaddr); + + if (stack_len < sizeof(RvExcFrame)) { + ESP_COREDUMP_LOGE("Too small stack to keep frame: %d bytes!", stack_len); + } + + /* The RISC-V frame has been saved at the top of the stack for the + * pre-empted tasks. We can then retrieve the registers by performing + * a cast on the stack address. For the current crashed task, the stack + * address has been adjusted by esp_core_dump_check_task function. */ + exc_frame = (RvExcFrame*) stack_paddr; + + /* Fill the PR_STATUS structure and copy the registers from the stack frame to it. */ + prstatus.signal = 0; + prstatus.pid = (uint32_t)task->tcb_addr; + memcpy(prstatus.regs.as_array, exc_frame, sizeof(riscv_regs)); + + *reg_dump = &prstatus; + return sizeof(riscv_prstatus); +} + +/** + * Save the crashed task handle in the extra info structure. + */ +void esp_core_dump_port_set_crashed_tcb(uint32_t handle) { + s_extra_info.crashed_task_tcb = handle; +} + +/** + * Function returning the extra info to be written in the dedicated section in + * the core file. + * info must not be NULL, it will be affected to the extra info data. + * The size, in bytes, of the data pointed by info is returned. + */ +uint32_t esp_core_dump_get_extra_info(void **info) +{ + uint32_t size = 0; + + if (info != NULL) { + size = sizeof(s_extra_info); + *info = &s_extra_info; + } + + return size; +} + +#endif diff --git a/components/espcoredump/src/core_dump_port.c b/components/espcoredump/src/port/xtensa/core_dump_port.c similarity index 52% rename from components/espcoredump/src/core_dump_port.c rename to components/espcoredump/src/port/xtensa/core_dump_port.c index 2b60e237ea..de1953d96b 100644 --- a/components/espcoredump/src/core_dump_port.c +++ b/components/espcoredump/src/port/xtensa/core_dump_port.c @@ -11,28 +11,31 @@ // 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. + +/** + * @file + * @brief Core dump port implementation for Xtensa based boards. + */ + #include #include #include "soc/soc_memory_layout.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "esp_core_dump_priv.h" - -#if __XTENSA__ #include "freertos/xtensa_context.h" -#else // __XTENSA__ -#define XCHAL_NUM_AREGS 64 // TODO-ESP32C3 coredump support IDF-1758 -#endif // __XTENSA__ - #include "esp_rom_sys.h" +#include "esp_core_dump_common.h" +#include "esp_core_dump_port.h" const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port"; +#define min(a,b) ((a) < (b) ? (a) : (b)) + #define COREDUMP_EM_XTENSA 0x5E #define COREDUMP_INVALID_CAUSE_VALUE 0xFFFF -#define COREDUMP_EXTRA_REG_NUM 16 #define COREDUMP_FAKE_STACK_START 0x20000000 #define COREDUMP_FAKE_STACK_LIMIT 0x30000000 +#define COREDUMP_EXTRA_REG_NUM 16 #define COREDUMP_GET_REG_PAIR(reg_idx, reg_ptr) { *(uint32_t*)(reg_ptr++) = (uint32_t)reg_idx; \ RSR(reg_idx, *(uint32_t*)(reg_ptr++)); \ @@ -44,8 +47,6 @@ const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port" #define COREDUMP_GET_EPS(reg, ptr) \ if (reg - EPS_2 + 2 <= XCHAL_NUM_INTLEVELS) COREDUMP_GET_REG_PAIR(reg, ptr) -#define COREDUMP_GET_MEMORY_SIZE(end, start) (end - start) - // Enumeration of registers of exception stack frame // and solicited stack frame typedef enum @@ -130,16 +131,8 @@ typedef struct uint32_t reserved; } __attribute__((packed)) xtensa_elf_reg_dump_t; -extern uint8_t port_IntStack; - #if CONFIG_ESP_COREDUMP_ENABLE -#if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) -#error Coredump functionality is not implemented for this target! -#endif - -static uint32_t s_total_length = 0; - static XtExcFrame s_fake_stack_frame = { .pc = (UBaseType_t) COREDUMP_FAKE_STACK_START, // task entrypoint fake_ptr .a0 = (UBaseType_t) 0, // to terminate GDB backtrace @@ -148,276 +141,24 @@ static XtExcFrame s_fake_stack_frame = { .ps = (PS_UM | PS_EXCM), .exccause = (UBaseType_t) COREDUMP_INVALID_CAUSE_VALUE, }; -static uint32_t s_fake_stacks_num; + +/* Keep a track of the number of fake stack distributed. Avoid giving the + * same fake address to two different tasks. */ +static uint32_t s_fake_stacks_num = 0; static xtensa_extra_info_t s_extra_info; -static XtExcFrame *s_exc_frame; - -static bool esp_core_dump_check_task(core_dump_task_header_t *task); -static bool esp_core_dump_check_stack(core_dump_task_header_t *task); -static void esp_core_dump_switch_task_stack_to_isr(core_dump_task_header_t *task, - core_dump_mem_seg_header_t *stack); - - -#if ESP_COREDUMP_STACK_SIZE > 0 -uint8_t s_coredump_stack[ESP_COREDUMP_STACK_SIZE]; -uint8_t *s_core_dump_sp; - -static uint32_t esp_core_dump_free_stack_space(const uint8_t *pucStackByte) -{ - uint32_t ulCount = 0U; - while( *pucStackByte == (uint8_t)COREDUMP_STACK_FILL_BYTE ) { - pucStackByte -= portSTACK_GROWTH; - ulCount++; - } - ulCount /= (uint32_t)sizeof(uint8_t); - return (uint32_t)ulCount; -} -#endif - -void esp_core_dump_report_stack_usage(void) -{ -#if ESP_COREDUMP_STACK_SIZE > 0 - uint32_t bytes_free = esp_core_dump_free_stack_space(s_coredump_stack); - ESP_COREDUMP_LOGD("Core dump used %u bytes on stack. %u bytes left free.", - s_core_dump_sp - s_coredump_stack - bytes_free, bytes_free); -#endif -} - -#if CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 - -// function to calculate SHA256 for solid data array -int esp_core_dump_sha(mbedtls_sha256_context *ctx, - const unsigned char *input, - size_t ilen, - unsigned char output[32] ) -{ - assert(input); - mbedtls_sha256_init(ctx); - if((mbedtls_sha256_starts_ret(ctx, 0) != 0)) goto exit; -#if CONFIG_MBEDTLS_HARDWARE_SHA - // set software mode for SHA calculation - ctx->mode = ESP_MBEDTLS_SHA256_SOFTWARE; -#endif - if((mbedtls_sha256_update_ret(ctx, input, ilen) != 0)) goto exit; - if((mbedtls_sha256_finish_ret(ctx, output) != 0)) goto exit; - esp_core_dump_print_sha256(DRAM_STR("Coredump SHA256"), (void*)output); - s_total_length = ilen; -exit: - mbedtls_sha256_free(ctx); - return ilen; -} - -void esp_core_dump_print_sha256(const char* msg, const uint8_t* sha_output) -{ - esp_rom_printf(DRAM_STR("%s='"), msg); - for (int i = 0; i < COREDUMP_SHA256_LEN; i++) { - esp_rom_printf(DRAM_STR("%02x"), sha_output[i]); - } - esp_rom_printf(DRAM_STR("'\r\n")); -} -#endif - /** - * Prints a message and a checksum given as parameters. - * This function is useful when the caller isn't explicitly aware of which - * checksum type (CRC32, SHA256, etc) is being used. + * The function creates small fake stack for task as deep as exception frame size + * It is required for gdb to take task into account but avoid back trace of stack. + * The espcoredump.py script is able to recognize that task is broken */ -void esp_core_dump_print_checksum(const char* msg, const void* checksum) -{ -#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 - esp_rom_printf(DRAM_STR("%s='"), msg); - esp_rom_printf(DRAM_STR("%08x"), *((const uint32_t*) checksum)); - esp_rom_printf(DRAM_STR("'\r\n")); -#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 - esp_core_dump_print_sha256(msg, (const uint8_t*) checksum); -#endif -} - -void esp_core_dump_checksum_init(core_dump_write_data_t* wr_data) -{ - if (wr_data) { -#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 - wr_data->crc = 0; -#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 - mbedtls_sha256_init(&wr_data->ctx); - (void)mbedtls_sha256_starts_ret(&wr_data->ctx, 0); -#endif - s_total_length = 0; - } -} - -void esp_core_dump_checksum_update(core_dump_write_data_t* wr_data, void* data, size_t data_len) -{ - if (wr_data && data) { -#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 - wr_data->crc = esp_rom_crc32_le(wr_data->crc, data, data_len); -#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 -#if CONFIG_MBEDTLS_HARDWARE_SHA - // set software mode of SHA calculation - wr_data->ctx.mode = ESP_MBEDTLS_SHA256_SOFTWARE; -#endif - (void)mbedtls_sha256_update_ret(&wr_data->ctx, data, data_len); -#endif - s_total_length += data_len; // keep counter of cashed bytes - } else { - ESP_COREDUMP_LOGE("Wrong write data info!"); - } -} - -/** - * Returns the size, in bytes, of the checksums. - * Currently, this function is just an alias to esp_core_dump_checksum_finish - * function, which can return the size of the checksum if given parameters - * are NULL. However, the implementation can evolve in the future independently - * from esp_core_dump_checksum_finish function. - */ -uint32_t esp_core_dump_checksum_size(void) -{ - return esp_core_dump_checksum_finish(NULL, NULL); -} - -uint32_t esp_core_dump_checksum_finish(core_dump_write_data_t* wr_data, void** chs_ptr) -{ - // get core dump checksum - uint32_t chs_len = 0; -#if CONFIG_ESP_COREDUMP_CHECKSUM_CRC32 - if (chs_ptr) { - wr_data->crc = wr_data->crc; - *chs_ptr = (void*)&wr_data->crc; - ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x, offset = 0x%x", wr_data->crc, wr_data->off); - } - chs_len = sizeof(wr_data->crc); -#elif CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 - if (chs_ptr) { - ESP_COREDUMP_LOG_PROCESS("Dump data offset = %d", wr_data->off); - (void)mbedtls_sha256_finish_ret(&wr_data->ctx, (uint8_t*)&wr_data->sha_output); - *chs_ptr = (void*)&wr_data->sha_output[0]; - mbedtls_sha256_free(&wr_data->ctx); - } - chs_len = sizeof(wr_data->sha_output); -#endif - ESP_COREDUMP_LOG_PROCESS("Total length of hashed data: %d!", s_total_length); - return chs_len; -} - -inline void esp_core_dump_port_init(panic_info_t *info) -{ - s_extra_info.crashed_task_tcb = COREDUMP_CURR_TASK_MARKER; - // Initialize exccause register to default value (required if current task corrupted) - s_extra_info.exccause.reg_val = COREDUMP_INVALID_CAUSE_VALUE; - s_extra_info.exccause.reg_index = EXCCAUSE; - - s_exc_frame = (void *)info->frame; - s_exc_frame->exit = COREDUMP_CURR_TASK_MARKER; - if (info->pseudo_excause) { - s_exc_frame->exccause += XCHAL_EXCCAUSE_NUM; - } -} - -inline uint16_t esp_core_dump_get_arch_id() -{ - return COREDUMP_EM_XTENSA; -} - -inline bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz) -{ - //TODO: external SRAM not supported yet - return (esp_ptr_in_dram((void *)addr) && esp_ptr_in_dram((void *)(addr+sz-1))) || - (esp_ptr_in_rtc_slow((void *)addr) && esp_ptr_in_rtc_slow((void *)(addr+sz-1))) || - (esp_ptr_in_rtc_dram_fast((void *)addr) && esp_ptr_in_rtc_dram_fast((void *)(addr+sz-1))) -#if CONFIG_IDF_TARGET_ESP32 && CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY - || (esp_ptr_in_iram((void *)addr) && esp_ptr_in_iram((void *)(addr+sz-1))) -#endif - ; -} - -static inline bool esp_core_dump_task_stack_end_is_sane(uint32_t sp) -{ - //TODO: currently core dump supports stacks in DRAM only, external SRAM not supported yet - return esp_ptr_in_dram((void *)sp); -} - -static inline bool esp_core_dump_tcb_addr_is_sane(uint32_t addr) -{ - return esp_core_dump_mem_seg_is_sane(addr, esp_core_dump_get_tcb_len()); -} - -inline void esp_core_dump_reset_tasks_snapshots_iter(void) -{ - s_fake_stacks_num = 0; -} - -inline void *esp_core_dump_get_next_task(void *handle) -{ - return pxTaskGetNext(handle); -} - -bool esp_core_dump_get_task_snapshot(void *handle, core_dump_task_header_t *task, - core_dump_mem_seg_header_t *interrupted_stack) -{ - TaskSnapshot_t rtos_snapshot; - - if (interrupted_stack != NULL) { - interrupted_stack->size = 0; - } - - vTaskGetSnapshot(handle, &rtos_snapshot); - task->tcb_addr = handle; - task->stack_start = (uint32_t)rtos_snapshot.pxTopOfStack; - task->stack_end = (uint32_t)rtos_snapshot.pxEndOfStack; - - if (!xPortInterruptedFromISRContext() && handle == esp_core_dump_get_current_task_handle()) { - // Set correct stack top for current task; only modify if we came from the task, - // and not an ISR that crashed. - task->stack_start = (uint32_t)s_exc_frame; - } - if (!esp_core_dump_check_task(task)) { - ESP_COREDUMP_LOG_PROCESS("Task %x is broken!", handle); - return false; - } - if (handle == esp_core_dump_get_current_task_handle()) { - ESP_COREDUMP_LOG_PROCESS("Crashed task %x", handle); - s_extra_info.crashed_task_tcb = (uint32_t)handle; - if (xPortInterruptedFromISRContext()) { - esp_core_dump_switch_task_stack_to_isr(task, interrupted_stack); - } - } - return true; -} - -inline uint32_t esp_core_dump_get_stack(core_dump_task_header_t *task, - uint32_t *stk_vaddr, uint32_t *stk_len) -{ - *stk_len = task->stack_end - task->stack_start; - *stk_vaddr = task->stack_start; - if (*stk_vaddr >= COREDUMP_FAKE_STACK_START && *stk_vaddr < COREDUMP_FAKE_STACK_LIMIT) { - return (uint32_t)&s_fake_stack_frame; - } - return *stk_vaddr; -} - -// The function creates small fake stack for task as deep as exception frame size -// It is required for gdb to take task into account but avoid back trace of stack. -// The espcoredump.py script is able to recognize that task is broken static void *esp_core_dump_get_fake_stack(uint32_t *stk_len) { *stk_len = sizeof(s_fake_stack_frame); return (uint8_t*)COREDUMP_FAKE_STACK_START + sizeof(s_fake_stack_frame)*s_fake_stacks_num++; } -static void esp_core_dump_switch_task_stack_to_isr(core_dump_task_header_t *task, core_dump_mem_seg_header_t *stack) -{ - if (stack != NULL) { - stack->start = task->stack_start; - stack->size = esp_core_dump_get_memory_len(task->stack_start, task->stack_end); - } - uint32_t isr_stk_end = (uint32_t)((uint8_t *)&port_IntStack + (xPortGetCoreID()+1)*configISR_STACK_SIZE); - task->stack_start = (uint32_t)s_exc_frame; - task->stack_end = isr_stk_end; - ESP_COREDUMP_LOG_PROCESS("Switched task %x to ISR stack [%x...%x]", task->tcb_addr, task->stack_start, task->stack_end); -} static core_dump_reg_pair_t *esp_core_dump_get_epc_regs(core_dump_reg_pair_t* src) { @@ -513,36 +254,117 @@ static esp_err_t esp_core_dump_get_regs_from_stack(void* stack_addr, return ESP_OK; } -uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, void **reg_dump) +inline void esp_core_dump_port_init(panic_info_t *info) { - uint32_t stack_vaddr, stack_paddr, stack_len; - static xtensa_elf_reg_dump_t s_reg_dump = { 0 }; + s_extra_info.crashed_task_tcb = COREDUMP_CURR_TASK_MARKER; + // Initialize exccause register to default value (required if current task corrupted) + s_extra_info.exccause.reg_val = COREDUMP_INVALID_CAUSE_VALUE; + s_extra_info.exccause.reg_index = EXCCAUSE; - stack_paddr = esp_core_dump_get_stack(task, &stack_vaddr, &stack_len); - - ESP_COREDUMP_LOG_PROCESS("Add regs for task 0x%x", task->tcb_addr); - - // initialize program status for the task - s_reg_dump.pr_status.pr_cursig = 0; - s_reg_dump.pr_status.pr_pid = (uint32_t)task->tcb_addr; - - // fill the gdb registers structure from stack - esp_err_t err = esp_core_dump_get_regs_from_stack((void*)stack_paddr, - stack_len, - &s_reg_dump.regs); - if (err != ESP_OK) { - ESP_COREDUMP_LOGE("Error while registers processing."); + XtExcFrame *s_exc_frame = (XtExcFrame *) info->frame; + s_exc_frame->exit = COREDUMP_CURR_TASK_MARKER; + if (info->pseudo_excause) { + s_exc_frame->exccause += XCHAL_EXCCAUSE_NUM; } - *reg_dump = &s_reg_dump; - return sizeof(s_reg_dump); } -inline void* esp_core_dump_get_current_task_handle() +/** + * Get the architecture ID. + * Check core dump port interface for more information about this function. + */ +inline uint16_t esp_core_dump_get_arch_id() { - return (void*)xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID()); + return COREDUMP_EM_XTENSA; } -static bool esp_core_dump_check_task(core_dump_task_header_t *task) +void esp_core_dump_reset_fake_stacks(void) +{ + s_fake_stacks_num = 0; +} + +/* Get the top of the ISR stack. + * Check core dump port interface for more information about this function. + */ +uint8_t* esp_core_dump_get_isr_stack_top(void) { + extern uint8_t port_IntStack; + return &port_IntStack; +} + + uint32_t esp_core_dump_get_isr_stack_end(void) + { + uint8_t* isr_top_stack = esp_core_dump_get_isr_stack_top(); + return (uint32_t)(isr_top_stack + (xPortGetCoreID()+1)*configISR_STACK_SIZE); + } + + +static inline bool esp_core_dump_task_stack_end_is_sane(uint32_t sp) +{ + //TODO: currently core dump supports stacks in DRAM only, external SRAM not supported yet + return esp_ptr_in_dram((void *)sp); +} + + +bool esp_core_dump_check_stack(core_dump_task_header_t *task) +{ + // Check task's stack + if (!esp_stack_ptr_is_sane(task->stack_start) || + !esp_core_dump_task_stack_end_is_sane(task->stack_end) || + (task->stack_start >= task->stack_end) || + ((task->stack_end-task->stack_start) > COREDUMP_MAX_TASK_STACK_SIZE)) { + // Check if current task stack is corrupted + ESP_COREDUMP_LOG_PROCESS("Invalid stack (%x...%x)!", task->stack_start, task->stack_end); + return false; + } + return true; +} + +/** + * Check if the memory segment is sane + * + * Check the header file for more information. + */ +bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz) +{ + //TODO: external SRAM not supported yet + return (esp_ptr_in_dram((void *)addr) && esp_ptr_in_dram((void *)(addr+sz-1))) + || (esp_ptr_in_rtc_slow((void *)addr) && esp_ptr_in_rtc_slow((void *)(addr+sz-1))) + || (esp_ptr_in_rtc_dram_fast((void *)addr) && esp_ptr_in_rtc_dram_fast((void *)(addr+sz-1))) + || (esp_ptr_in_iram((void *)addr) && esp_ptr_in_iram((void *)(addr+sz-1))); +} + +/** + * Get the stack of a task. + * Check core dump port interface for more information about this function. + */ +uint32_t esp_core_dump_get_stack(core_dump_task_header_t *task, + uint32_t* stk_vaddr, uint32_t* stk_paddr) +{ + const uint32_t stack_len = abs(task->stack_start - task->stack_end); + const uint32_t stack_addr = min(task->stack_start, task->stack_end); + + ESP_COREDUMP_DEBUG_ASSERT(stk_paddr != NULL && stk_vaddr != NULL); + + /* Provide the virtual stack address for any task. */ + *stk_vaddr = stack_addr; + + if (stack_addr >= COREDUMP_FAKE_STACK_START && + stack_addr < COREDUMP_FAKE_STACK_LIMIT) { + /* In this case, the stack address pointed by the task is a fake stack + * generated previously. So it doesn't really point to actual data. + * Thus, we must provide the address of the fake stack data. */ + *stk_paddr = (uint32_t) &s_fake_stack_frame; + } else { + *stk_paddr = stack_addr; + } + + return stack_len; +} + +/** + * Check the task passed as a parameter. + * Check core dump port interface for more information about this function. + */ +bool esp_core_dump_check_task(core_dump_task_header_t *task) { uint32_t stk_size = 0; bool stack_is_valid = false; @@ -591,80 +413,52 @@ static bool esp_core_dump_check_task(core_dump_task_header_t *task) return true; } -static bool esp_core_dump_check_stack(core_dump_task_header_t *task) + +/** + * Get a dump of the task's registers. + * Check core dump port interface for more information about this function. + */ +uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, void **reg_dump) { - // Check task's stack - if (!esp_stack_ptr_is_sane(task->stack_start) || !esp_core_dump_task_stack_end_is_sane(task->stack_end) || - (task->stack_start >= task->stack_end) || - ((task->stack_end-task->stack_start) > COREDUMP_MAX_TASK_STACK_SIZE)) { - // Check if current task stack is corrupted - ESP_COREDUMP_LOG_PROCESS("Invalid stack (%x...%x)!", task->stack_start, task->stack_end); - return false; + uint32_t stack_vaddr, stack_paddr, stack_len; + static xtensa_elf_reg_dump_t s_reg_dump = { 0 }; + + ESP_COREDUMP_DEBUG_ASSERT(task != NULL && reg_dump != NULL); + + stack_len = esp_core_dump_get_stack(task, &stack_paddr, &stack_vaddr); + + ESP_COREDUMP_LOG_PROCESS("Add regs for task 0x%x", task->tcb_addr); + + // initialize program status for the task + s_reg_dump.pr_status.pr_cursig = 0; + s_reg_dump.pr_status.pr_pid = (uint32_t)task->tcb_addr; + + // fill the gdb registers structure from stack + esp_err_t err = esp_core_dump_get_regs_from_stack((void*)stack_paddr, + stack_len, + &s_reg_dump.regs); + if (err != ESP_OK) { + ESP_COREDUMP_LOGE("Error while registers processing."); } - return true; + *reg_dump = &s_reg_dump; + return sizeof(s_reg_dump); } + +void esp_core_dump_port_set_crashed_tcb(uint32_t handle) { + s_extra_info.crashed_task_tcb = handle; +} + +/** + * Retrieve the extra information. + * Check core dump port interface for more information about this function. + */ uint32_t esp_core_dump_get_extra_info(void **info) { - *info = &s_extra_info; + if (info) { + *info = &s_extra_info; + } return sizeof(s_extra_info); } -uint32_t esp_core_dump_get_user_ram_segments(void) -{ - uint32_t total_sz = 0; - - // count number of memory segments to insert into ELF structure - total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_dram_end, &_coredump_dram_start) > 0 ? 1 : 0; - total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_end, &_coredump_rtc_start) > 0 ? 1 : 0; - total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_fast_end, &_coredump_rtc_fast_start) > 0 ? 1 : 0; - total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_iram_end, &_coredump_iram_start) > 0 ? 1 : 0; - - return total_sz; -} - -uint32_t esp_core_dump_get_user_ram_size(void) -{ - uint32_t total_sz = 0; - - total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_dram_end, &_coredump_dram_start); - total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_end, &_coredump_rtc_start); - total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_fast_end, &_coredump_rtc_fast_start); - total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_iram_end, &_coredump_iram_start); - - return total_sz; -} - -int esp_core_dump_get_user_ram_info(coredump_region_t region, uint32_t *start) { - - int total_sz = -1; - - switch (region) { - case COREDUMP_MEMORY_DRAM: - *start = (uint32_t)&_coredump_dram_start; - total_sz = (uint8_t *)&_coredump_dram_end - (uint8_t *)&_coredump_dram_start; - break; - - case COREDUMP_MEMORY_IRAM: - *start = (uint32_t)&_coredump_iram_start; - total_sz = (uint8_t *)&_coredump_iram_end - (uint8_t *)&_coredump_iram_start; - break; - - case COREDUMP_MEMORY_RTC: - *start = (uint32_t)&_coredump_rtc_start; - total_sz = (uint8_t *)&_coredump_rtc_end - (uint8_t *)&_coredump_rtc_start; - break; - - case COREDUMP_MEMORY_RTC_FAST: - *start = (uint32_t)&_coredump_rtc_fast_start; - total_sz = (uint8_t *)&_coredump_rtc_fast_end - (uint8_t *)&_coredump_rtc_fast_start; - break; - - default: - break; - } - - return total_sz; -} - #endif