diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6af6f64e273a16cd9f7b3ecd2496c4f79d9d8f4a..f03a238905893d1e377549c038c948a97ffee6a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,10 @@ if(${CONFIG_OS_GDB_STUB}) add_subdirectory(component/gdbstub) endif() +if(${CONFIG_OS_OPTION_COREDUMP}) + add_subdirectory(component/coredump) +endif() + if(${CONFIG_OS_SUPPORT_CXX}) add_subdirectory(component/cxx) endif() @@ -35,4 +39,4 @@ if(${CONFIG_OS_SUPPORT_RPMSGLITE}) add_subdirectory(component/rpmsg-lite) endif() -add_subdirectory(extended) \ No newline at end of file +add_subdirectory(extended) diff --git a/src/arch/cpu/armv7-r/common/exc/prt_exc.c b/src/arch/cpu/armv7-r/common/exc/prt_exc.c index d8b1a428822f6d284662552f807957b6b83f759d..5118d332c31120f58a63d4e31c1c0889a4253009 100755 --- a/src/arch/cpu/armv7-r/common/exc/prt_exc.c +++ b/src/arch/cpu/armv7-r/common/exc/prt_exc.c @@ -14,6 +14,7 @@ */ #include "prt_exc_internal.h" +#include "prt_coredump.h" OS_SEC_BSS ExcTaskInfoFunc g_excTaskInfoGet; extern uintptr_t __exc_stack_top; @@ -147,6 +148,11 @@ INIT_SEC_L4_TEXT void OsExcHandleEntry(U32 excType, struct ExcRegInfo *excRegs) /* 记录异常信息 */ OsExcSaveInfo(excInfo, excRegs); + #if defined(OS_OPTION_COREDUMP) + excInfo->threadId = RUNNING_TASK->taskPid; + coredump(excInfo); + #endif + /* 回调异常钩子函数 */ OsExcHookHandle(); } diff --git a/src/arch/cpu/armv8/common/exc/prt_exc_init.c b/src/arch/cpu/armv8/common/exc/prt_exc_init.c index 48accd3c06ab54fa3554474fc5c082b15272163b..4914c46a1c25ded7a2185deaa4ec45e79670e37d 100644 --- a/src/arch/cpu/armv8/common/exc/prt_exc_init.c +++ b/src/arch/cpu/armv8/common/exc/prt_exc_init.c @@ -173,6 +173,10 @@ INIT_SEC_L4_TEXT void OsExcHandleEntry(U32 excType, struct ExcRegInfo *excRegs) OsErrRecord(OsExcCallstackPrintf(excInfo)); #endif +#if defined(OS_OPTION_COREDUMP) + coredump(excInfo); +#endif + /* 回调异常钩子函数 */ OsExcHookHandle(); } diff --git a/src/component/coredump/CMakeLists.txt b/src/component/coredump/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d9114ee5bd77888310306a7588979ffe64038b87 --- /dev/null +++ b/src/component/coredump/CMakeLists.txt @@ -0,0 +1,4 @@ +include_directories(.) +add_library_ex(coredump_core.c) +add_library_ex(coredump_memory_regions.c) +add_library_ex(backend/coredump_backend_print.c) diff --git a/src/component/coredump/backend/coredump_backend_print.c b/src/component/coredump/backend/coredump_backend_print.c new file mode 100644 index 0000000000000000000000000000000000000000..0d3c4b00f1e411b7b35f5ae9d25e1a4680678b3e --- /dev/null +++ b/src/component/coredump/backend/coredump_backend_print.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "coredump_internal.h" + +/* Length of buffer of printable size */ +#define LOG_BUF_SZ 64 + +/* Length of buffer of printable size plus null character */ +#define LOG_BUF_SZ_RAW (LOG_BUF_SZ + 1) + +/* Log Buffer */ +static char log_buf[LOG_BUF_SZ_RAW]; + +static int error; + +static int hex2char(U8 x, char *c) +{ + if (x <= 9) { + *c = x + '0'; + } else if (x <= 15) { + *c = x - 10 + 'a'; + } else { + return -1; + } + + return 0; +} + +static void coredump_logging_backend_start(void) +{ + PRT_Printf(COREDUMP_PREFIX_STR COREDUMP_BEGIN_STR "\n"); +} + +static void coredump_logging_backend_end(void) +{ + PRT_Printf(COREDUMP_PREFIX_STR COREDUMP_END_STR "\n"); +} + +static void coredump_logging_backend_buffer_output(uint8_t *buf, size_t buflen) +{ + uint8_t log_ptr = 0; + size_t remaining = buflen; + size_t i = 0; + + if ((buf == NULL) || (buflen == 0)) { + error = -EINVAL; + remaining = 0; + } + + while (remaining > 0) { + if (hex2char(buf[i] >> 4, &log_buf[log_ptr]) < 0) { + error = -EINVAL; + break; + } + log_ptr++; + + if (hex2char(buf[i] & 0xf, &log_buf[log_ptr]) < 0) { + error = -EINVAL; + break; + } + log_ptr++; + + i++; + remaining--; + + if ((log_ptr >= LOG_BUF_SZ) || (remaining == 0)) { + log_buf[log_ptr] = '\0'; + PRT_Printf(COREDUMP_PREFIX_STR "%s\n", log_buf); + log_ptr = 0; + } + } +} + +static int coredump_logging_backend_query(enum coredump_query_id query_id, + void *arg) +{ + int ret; + + switch (query_id) { + case COREDUMP_QUERY_GET_ERROR: + ret = error; + break; + default: + ret = -ENOTSUP; + break; + } + + return ret; +} + +static int coredump_logging_backend_cmd(enum coredump_cmd_id cmd_id, + void *arg) +{ + int ret; + + switch (cmd_id) { + case COREDUMP_CMD_CLEAR_ERROR: + ret = 0; + error = 0; + break; + default: + ret = -ENOTSUP; + break; + } + + return ret; +} + + +struct coredump_backend_api coredump_backend_print = { + .start = coredump_logging_backend_start, + .end = coredump_logging_backend_end, + .buffer_output = coredump_logging_backend_buffer_output, + .query = coredump_logging_backend_query, + .cmd = coredump_logging_backend_cmd, +}; diff --git a/src/component/coredump/coredump_core.c b/src/component/coredump/coredump_core.c new file mode 100644 index 0000000000000000000000000000000000000000..f26c9305704e8ee4362757ffd812c19a51b6e608 --- /dev/null +++ b/src/component/coredump/coredump_core.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2020 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "coredump_internal.h" +#include "prt_sys_external.h" +#include "prt_task_external.h" +#include "prt_irq_external.h" +#include "prt_asm_cpu_external.h" + +extern struct coredump_backend_api coredump_backend_print; + +static struct coredump_backend_api *backend_api = &coredump_backend_print; + + +#define INVALIDPID 0xFFFFFFFFUL +/* +* Register block takes up too much stack space +* if defined within function. So define it here. +*/ +#ifdef OS_ARCH_ARMV8 +/* Identify the version of this block (in case of architecture changes). +* To be interpreted by the target architecture specific block parser. +*/ +#define ARCH_HDR_VER 1 +static uint64_t arch_blk[22]; +#elif defined OS_ARCH_ARMV7_R +/* Identify the version of this block (in case of architecture changes). +* To be interpreted by the target architecture specific block parser. +*/ +#define ARCH_HDR_VER 2 +static uint32_t arch_blk[17]; +#endif + +void arch_coredump_info_dump(const struct ExcRegInfo *reg) +{ + /* Target architecture information header */ + /* Information just relevant to the python parser */ + struct coredump_arch_hdr_t hdr = { + .id = COREDUMP_ARCH_HDR_ID, + .hdr_version = ARCH_HDR_VER, + .num_bytes = sizeof(arch_blk), + }; + + (void)memset(&arch_blk, 0, sizeof(arch_blk)); + + /* + * Copies the thread registers to a memory block that will be printed out + * The thread registers are already provided by structure struct arch_esf + */ +#ifdef OS_ARCH_ARMV8 + arch_blk[0] = reg->xregs[30]; + arch_blk[1] = reg->xregs[29]; + arch_blk[2] = reg->xregs[28]; + arch_blk[3] = reg->xregs[27]; + arch_blk[4] = reg->xregs[26]; + arch_blk[5] = reg->xregs[25]; + arch_blk[6] = reg->xregs[24]; + arch_blk[7] = reg->xregs[23]; + arch_blk[8] = reg->xregs[22]; + arch_blk[9] = reg->xregs[21]; + arch_blk[10] = reg->xregs[20]; + arch_blk[11] = reg->xregs[19]; + arch_blk[12] = reg->xregs[18]; + arch_blk[13] = reg->xregs[17]; + arch_blk[14] = reg->xregs[16]; + arch_blk[15] = reg->xregs[15]; + arch_blk[16] = reg->xregs[14]; + arch_blk[17] = reg->xregs[13]; + arch_blk[18] = reg->xregs[12]; + arch_blk[19] = reg->elr; + arch_blk[20] = reg->sp; + arch_blk[21] = reg->elr; +#elif defined OS_ARCH_ARMV7_R + arch_blk[0] = reg->R0; + arch_blk[1] = reg->R1; + arch_blk[2] = reg->R2; + arch_blk[3] = reg->R3; + arch_blk[4] = reg->R12; + arch_blk[5] = reg->LR; + arch_blk[6] = reg->PC; + arch_blk[7] = reg->regCPSR; + arch_blk[8] = reg->SP; + arch_blk[9] = reg->R4; + arch_blk[10] = reg->R5; + arch_blk[11] = reg->R6; + arch_blk[12] = reg->R7; + arch_blk[13] = reg->R8; + arch_blk[14] = reg->R9; + arch_blk[15] = reg->R10; + arch_blk[16] = reg->R11; +#endif + + /* Send for output */ + coredump_buffer_output((uint8_t *)&hdr, sizeof(hdr)); + coredump_buffer_output((uint8_t *)arch_blk, sizeof(arch_blk)); +} + + +static void dump_header(const struct ExcInfo *info) +{ + struct coredump_hdr_t hdr = { + .id = {'Z', 'E'}, + .hdr_version = COREDUMP_HDR_VER, + .reason = info->excCause, + }; + + if (sizeof(uintptr_t) == 8) { + hdr.ptr_size_bits = 6; /* 2^6 = 64 */ + } else if (sizeof(uintptr_t) == 4) { + hdr.ptr_size_bits = 5; /* 2^5 = 32 */ + } else { + hdr.ptr_size_bits = 0; /* Unknown */ + } + + hdr.tgt_code = OS_HARDWARE_PLATFORM; + + backend_api->buffer_output((uint8_t *)&hdr, sizeof(hdr)); +} + +static void dump_thread(const struct ExcInfo *info) +{ + struct TskInfo taskInfo; + + uintptr_t end_addr; + uintptr_t start_addr; + + U32 core = THIS_CORE(); + uintptr_t sysStackHigh = OsGetSysStackEnd(core); + uintptr_t sysStackLow = OsGetSysStackStart(core); + + if(info->sp <= sysStackHigh && info->sp >= sysStackLow) + { + end_addr = sysStackHigh; + start_addr = sysStackLow; + } else { + end_addr = RUNNING_TASK->topOfStack + RUNNING_TASK->stackSize; + start_addr = RUNNING_TASK->topOfStack; + PRT_TaskGetInfo(RUNNING_TASK->taskPid, &taskInfo); + } + + coredump_memory_dump((uintptr_t)(&taskInfo), (uintptr_t)(&taskInfo) + sizeof(taskInfo)); + + coredump_memory_dump(start_addr, end_addr); +} + +void process_memory_region_list(void) +{ + unsigned int idx = 0; + + while (true) { + struct z_coredump_memory_region_t *r = + &z_coredump_memory_regions[idx]; + + if (r->end == (uintptr_t)(NULL)) { + break; + } + + coredump_memory_dump(r->start, r->end); + + idx++; + } +} + +void coredump(const struct ExcInfo *info) +{ + z_coredump_start(); + + dump_header(info); + + arch_coredump_info_dump(&info->regInfo); + + dump_thread(info); + + process_memory_region_list(); + + z_coredump_end(); +} + +void z_coredump_start(void) +{ + backend_api->start(); +} + +void z_coredump_end(void) +{ + backend_api->end(); +} + +void coredump_buffer_output(uint8_t *buf, size_t buflen) +{ + if ((buf == NULL) || (buflen == 0)) { + /* Invalid buffer, skip */ + return; + } + + backend_api->buffer_output(buf, buflen); +} + +void coredump_memory_dump(uintptr_t start_addr, uintptr_t end_addr) +{ + struct coredump_mem_hdr_t m; + size_t len; + + if ((start_addr == (uintptr_t)(NULL)) || + (end_addr == (uintptr_t)(NULL))) { + return; + } + + if (start_addr >= end_addr) { + return; + } + + len = end_addr - start_addr; + + m.id = COREDUMP_MEM_HDR_ID; + m.hdr_version = COREDUMP_MEM_HDR_VER; + + if (sizeof(uintptr_t) == 8) { + m.start = (start_addr); + m.end = (end_addr); + } else if (sizeof(uintptr_t) == 4) { + m.start = (start_addr); + m.end = (end_addr); + } + + coredump_buffer_output((uint8_t *)&m, sizeof(m)); + + coredump_buffer_output((uint8_t *)start_addr, len); +} + +int coredump_query(enum coredump_query_id query_id, void *arg) +{ + int ret; + + if (backend_api->query == NULL) { + ret = -1; + } else { + ret = backend_api->query(query_id, arg); + } + + return ret; +} + +int coredump_cmd(enum coredump_cmd_id cmd_id, void *arg) +{ + int ret; + + if (backend_api->cmd == NULL) { + ret = -1; + } else { + ret = backend_api->cmd(cmd_id, arg); + } + + return ret; +} diff --git a/src/component/coredump/coredump_internal.h b/src/component/coredump/coredump_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..239906f2f5488250deb2133855c66ce2339da315 --- /dev/null +++ b/src/component/coredump/coredump_internal.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef DEBUG_COREDUMP_INTERNAL_H_ +#define DEBUG_COREDUMP_INTERNAL_H_ + +#include "prt_typedef.h" +#include "prt_coredump.h" + +extern U32 PRT_Printf(const char *format, ...); +/** + * @cond INTERNAL_HIDDEN + * + * These are for internal use only, so skip these in + * public documentation. + */ + +struct z_coredump_memory_region_t { + uintptr_t start; + uintptr_t end; +}; + +extern struct z_coredump_memory_region_t z_coredump_memory_regions[]; + +/** + * @brief Mark the start of coredump + * + * This sets up coredump subsys so coredump can be commenced. + * + * For example, backend needs to be initialized before any + * output can be stored. + */ +void z_coredump_start(void); + +/** + * @brief Mark the end of coredump + * + * This tells the coredump subsys to finalize the coredump + * session. + * + * For example, backend may need to flush the output. + */ +void z_coredump_end(void); + +/** + * @endcond + */ + +#endif /* DEBUG_COREDUMP_INTERNAL_H_ */ diff --git a/src/component/coredump/coredump_memory_regions.c b/src/component/coredump/coredump_memory_regions.c new file mode 100644 index 0000000000000000000000000000000000000000..1df230aee7a2095ab68d24b21fba49aecec0bb4b --- /dev/null +++ b/src/component/coredump/coredump_memory_regions.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "coredump_internal.h" + +extern uintptr_t __data_start; +extern uintptr_t __data_end; +extern uintptr_t __bss_end__; +extern uintptr_t __bss_start__; +//填入需保存的内存区域,比如如下的数据段与bss段 +struct z_coredump_memory_region_t z_coredump_memory_regions[] = { + // {(uintptr_t)&__data_start, (uintptr_t)&__data_end}, + // {(uintptr_t)&__bss_start__, (uintptr_t)&__bss_end__}, + {0, 0} /* End of list */ +}; diff --git a/src/include/uapi/prt_coredump.h b/src/include/uapi/prt_coredump.h new file mode 100644 index 0000000000000000000000000000000000000000..79928e65c9a8d15ad7bb717c82ae1e967d12cc50 --- /dev/null +++ b/src/include/uapi/prt_coredump.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2020 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef INCLUDE_DEBUG_COREDUMP_H_ +#define INCLUDE_DEBUG_COREDUMP_H_ + +#include +#include +#include + +/* + * Define COREDUMP_*_STR as public to allow coredump_backend_other to re-use + * these strings if necessary + */ +#define COREDUMP_BEGIN_STR "BEGIN#" +#define COREDUMP_END_STR "END#" +#define COREDUMP_ERROR_STR "ERROR CANNOT DUMP#" + +/* + * Need to prefix coredump strings to make it easier to parse + * as log module adds its own prefixes. + */ +#define COREDUMP_PREFIX_STR "#CD:" + +#ifndef __packed +#define __packed /*__attribute__((packed))*/ +#endif + +/** + * @file + * + * @defgroup coredump_apis Coredump APIs + * @ingroup os_services + * @brief Coredump APIs + * @{ + */ + + +/** Query ID */ +enum coredump_query_id { + /** + * Returns error code from backend. + */ + COREDUMP_QUERY_GET_ERROR, + + /** + * Check if there is a stored coredump from backend. + * + * Returns: + * - 1 if there is a stored coredump, 0 if none. + * - -ENOTSUP if this query is not supported. + * - Otherwise, error code from backend. + */ + COREDUMP_QUERY_HAS_STORED_DUMP, + + /** + * Returns: + * - coredump raw size from backend, 0 if none. + * - -ENOTSUP if this query is not supported. + * - Otherwise, error code from backend. + */ + COREDUMP_QUERY_GET_STORED_DUMP_SIZE, + + /** + * Max value for query ID. + */ + COREDUMP_QUERY_MAX +}; + +/** Command ID */ +enum coredump_cmd_id { + /** + * Clear error code from backend. + * + * Returns 0 if successful, failed otherwise. + */ + COREDUMP_CMD_CLEAR_ERROR, + + /** + * Verify that the stored coredump is valid. + * + * Returns: + * - 1 if valid. + * - 0 if not valid or no stored coredump. + * - -ENOTSUP if this command is not supported. + * - Otherwise, error code from backend. + */ + COREDUMP_CMD_VERIFY_STORED_DUMP, + + /** + * Erase the stored coredump. + * + * Returns: + * - 0 if successful. + * - -ENOTSUP if this command is not supported. + * - Otherwise, error code from backend. + */ + COREDUMP_CMD_ERASE_STORED_DUMP, + + /** + * Copy the raw stored coredump. + * + * Returns: + * - copied size if successful + * - 0 if stored coredump is not found + * - -ENOTSUP if this command is not supported. + * - Otherwise, error code from backend. + */ + COREDUMP_CMD_COPY_STORED_DUMP, + + /** + * Invalidate the stored coredump. This is faster than + * erasing the whole partition. + * + * Returns: + * - 0 if successful. + * - -ENOTSUP if this command is not supported. + * - Otherwise, error code from backend. + */ + COREDUMP_CMD_INVALIDATE_STORED_DUMP, + + /** + * Max value for command ID. + */ + COREDUMP_CMD_MAX +}; + +/** Coredump copy command (@ref COREDUMP_CMD_COPY_STORED_DUMP) argument definition */ +struct coredump_cmd_copy_arg { + /** Copy offset */ + off_t offset; + + /** Copy destination buffer */ + uint8_t *buffer; + + /** Copy length */ + size_t length; +}; + +#include "prt_typedef.h" +#include "prt_exc.h" + +#define COREDUMP_HDR_VER 1 + +#define COREDUMP_ARCH_HDR_ID 'A' + +#define COREDUMP_MEM_HDR_ID 'M' +#define COREDUMP_MEM_HDR_VER 1 + +#pragma pack(1) +/* Coredump header */ +struct coredump_hdr_t { + /* 'Z', 'E' */ + char id[2]; + + /* Header version */ + uint16_t hdr_version; + + /* Target code */ + uint16_t tgt_code; + + /* Pointer size in Log2 */ + uint8_t ptr_size_bits; + + uint8_t flag; + + /* Coredump Reason given */ + unsigned int reason; +} __packed; + +/* Architecture-specific block header */ +struct coredump_arch_hdr_t { + /* COREDUMP_ARCH_HDR_ID */ + char id; + + /* Header version */ + uint16_t hdr_version; + + /* Number of bytes in this block (excluding header) */ + uint16_t num_bytes; +} __packed; + +/* Memory block header */ +struct coredump_mem_hdr_t { + /* COREDUMP_MEM_HDR_ID */ + char id; + + /* Header version */ + uint16_t hdr_version; + + /* Address of start of memory region */ + uintptr_t start; + + /* Address of end of memory region */ + uintptr_t end; +} __packed; +#pragma pack() + +typedef void (*coredump_backend_start_t)(void); +typedef void (*coredump_backend_end_t)(void); +typedef void (*coredump_backend_buffer_output_t)(uint8_t *buf, size_t buflen); +typedef int (*coredump_backend_query_t)(enum coredump_query_id query_id, + void *arg); +typedef int (*coredump_backend_cmd_t)(enum coredump_cmd_id cmd_id, + void *arg); + +struct coredump_backend_api { + /* Signal to backend of the start of coredump. */ + coredump_backend_start_t start; + + /* Signal to backend of the end of coredump. */ + coredump_backend_end_t end; + + /* Raw buffer output */ + coredump_backend_buffer_output_t buffer_output; + + /* Perform query on backend */ + coredump_backend_query_t query; + + /* Perform command on backend */ + coredump_backend_cmd_t cmd; +}; +/** + * @fn void coredump(unsigned int reason, const struct arch_esf *esf, struct k_thread *thread); + * @brief Perform coredump. + * + * Normally, this is called inside z_fatal_error() to generate coredump + * when a fatal error is encountered. This can also be called on demand + * whenever a coredump is desired. + * + * @param info Exception context + */ +void coredump(const struct ExcInfo *info); +/** + * @fn void coredump_memory_dump(uintptr_t start_addr, uintptr_t end_addr); + * @brief Dump memory region + * + * @param start_addr Start address of memory region to be dumped + * @param end_addr End address of memory region to be dumped + */ +void coredump_memory_dump(uintptr_t start_addr, uintptr_t end_addr); +/** + * @fn int coredump_buffer_output(uint8_t *buf, size_t buflen); + * @brief Output the buffer via coredump + * + * This outputs the buffer of byte array to the coredump backend. + * For example, this can be called to output the coredump section + * containing registers, or a section for memory dump. + * + * @param buf Buffer to be send to coredump output + * @param buflen Buffer length + */ +void coredump_buffer_output(uint8_t *buf, size_t buflen); +/** + * @fn int coredump_query(enum coredump_query_id query_id, void *arg); + * @brief Perform query on coredump subsystem. + * + * Query the coredump subsystem for information, for example, if there is + * an error. + * + * @param[in] query_id Query ID + * @param[in,out] arg Pointer to argument for exchanging information + * @return Depends on the query + */ +int coredump_query(enum coredump_query_id query_id, void *arg); +/** + * @fn int coredump_cmd(enum coredump_cmd_id cmd_id, void *arg); + * @brief Perform command on coredump subsystem. + * + * Perform command on coredump subsystem, for example, output the stored + * coredump via logging. + * + * @param[in] cmd_id Command ID + * @param[in,out] arg Pointer to argument for exchanging information + * @return Depends on the command + */ +int coredump_cmd(enum coredump_cmd_id cmd_id, void *arg); + +#endif /* INCLUDE_DEBUG_COREDUMP_H_ */