mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/coredump_refactor_riscv_support' into 'master'
espcoredump: code refactoring and add support for RISC-V implementation Closes ESP-1758 See merge request espressif/esp-idf!11850
This commit is contained in:
commit
b4612a2f14
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
30
components/espcoredump/include_core_dump/core_dump_binary.h
Normal file
30
components/espcoredump/include_core_dump/core_dump_binary.h
Normal file
@ -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
|
117
components/espcoredump/include_core_dump/core_dump_checksum.h
Normal file
117
components/espcoredump/include_core_dump/core_dump_checksum.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#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
|
@ -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
|
||||
|
155
components/espcoredump/include_core_dump/esp_core_dump_common.h
Normal file
155
components/espcoredump/include_core_dump/esp_core_dump_common.h
Normal file
@ -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
|
@ -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
|
||||
}
|
||||
|
@ -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
|
188
components/espcoredump/include_core_dump/esp_core_dump_types.h
Normal file
188
components/espcoredump/include_core_dump/esp_core_dump_types.h
Normal file
@ -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
|
@ -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
|
@ -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 <xtensa/xtruntime.h>
|
||||
#include <xt_instr_macros.h>
|
||||
|
||||
/**
|
||||
* @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
|
279
components/espcoredump/src/core_dump_binary.c
Normal file
279
components/espcoredump/src/core_dump_binary.c
Normal file
@ -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 <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#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
|
189
components/espcoredump/src/core_dump_checksum.c
Normal file
189
components/espcoredump/src/core_dump_checksum.c
Normal file
@ -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 <stddef.h>
|
||||
#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
|
@ -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 <string.h>
|
||||
#include <stdbool.h>
|
||||
#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
|
||||
|
@ -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);
|
||||
|
@ -14,7 +14,8 @@
|
||||
#include <string.h>
|
||||
#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
|
||||
|
@ -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;
|
||||
}
|
||||
|
378
components/espcoredump/src/port/riscv/core_dump_port.c
Normal file
378
components/espcoredump/src/port/riscv/core_dump_port.c
Normal file
@ -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 <string.h>
|
||||
#include <stdbool.h>
|
||||
#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
|
@ -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 <string.h>
|
||||
#include <stdbool.h>
|
||||
#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
|
Loading…
Reference in New Issue
Block a user