debug: coredump: add new backend using flash partition
This adds another simple backend to use flash partition as backing storage. Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
parent
6c569142ea
commit
2b53f3ded2
7 changed files with 884 additions and 10 deletions
|
|
@ -14,6 +14,16 @@ enum coredump_query_id {
|
|||
*/
|
||||
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,
|
||||
|
||||
COREDUMP_QUERY_MAX
|
||||
};
|
||||
|
||||
|
|
@ -26,6 +36,25 @@ enum coredump_cmd_id {
|
|||
*/
|
||||
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,
|
||||
|
||||
COREDUMP_CMD_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,3 +17,8 @@ zephyr_library_sources_ifdef(
|
|||
CONFIG_DEBUG_COREDUMP_BACKEND_LOGGING
|
||||
coredump_backend_logging.c
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_DEBUG_COREDUMP_BACKEND_FLASH_PARTITION
|
||||
coredump_backend_flash_partition.c
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@ config DEBUG_COREDUMP_BACKEND_LOGGING
|
|||
help
|
||||
Core dump is done via logging subsystem.
|
||||
|
||||
config DEBUG_COREDUMP_BACKEND_FLASH_PARTITION
|
||||
bool "Use flash partition for coredump"
|
||||
depends on FLASH
|
||||
select FLASH_MAP
|
||||
select STREAM_FLASH
|
||||
help
|
||||
Core dump is saved to a flash partition with DTS alias
|
||||
"coredump-partition".
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
|
|
|
|||
827
subsys/debug/coredump/coredump_backend_flash_partition.c
Normal file
827
subsys/debug/coredump/coredump_backend_flash_partition.c
Normal file
|
|
@ -0,0 +1,827 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <kernel.h>
|
||||
#include <string.h>
|
||||
#include <toolchain.h>
|
||||
#include <storage/flash_map.h>
|
||||
#include <storage/stream_flash.h>
|
||||
#include <sys/util.h>
|
||||
|
||||
#include <debug/coredump.h>
|
||||
#include "coredump_internal.h"
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(coredump, CONFIG_KERNEL_LOG_LEVEL);
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Simple coredump backend to store data in flash partition.
|
||||
*
|
||||
* This provides a simple backend to store coredump data in a flash
|
||||
* partition, labeled "coredump-partition" in devicetree.
|
||||
*
|
||||
* On the partition, a header is stored at the beginning with padding
|
||||
* at the end to align with flash write size. Then the actual
|
||||
* coredump data follows. The padding is to simplify the data read
|
||||
* function so that the first read of a data stream is always
|
||||
* aligned to flash write size.
|
||||
*/
|
||||
|
||||
#if !FLASH_AREA_LABEL_EXISTS(coredump_partition)
|
||||
#error "Need a fixed partition named 'coredump-partition'!"
|
||||
|
||||
#else
|
||||
|
||||
#define FLASH_CONTROLLER \
|
||||
DT_PARENT(DT_PARENT(DT_NODELABEL(coredump_partition)))
|
||||
|
||||
#define FLASH_WRITE_SIZE DT_PROP(FLASH_CONTROLLER, write_block_size)
|
||||
#define FLASH_BUF_SIZE FLASH_WRITE_SIZE
|
||||
|
||||
#define FLASH_PARTITION FLASH_AREA_ID(coredump_partition)
|
||||
|
||||
#define HDR_VER 1
|
||||
|
||||
typedef int (*data_read_cb_t)(void *arg, uint8_t *buf, size_t len);
|
||||
|
||||
static struct {
|
||||
/* For use with flash map */
|
||||
const struct flash_area *flash_area;
|
||||
|
||||
/* For use with streaming flash */
|
||||
struct stream_flash_ctx stream_ctx;
|
||||
|
||||
/* Checksum of data so far */
|
||||
uint16_t checksum;
|
||||
|
||||
/* Error encountered */
|
||||
int error;
|
||||
} backend_ctx;
|
||||
|
||||
/* Buffer used in stream flash context */
|
||||
static uint8_t stream_flash_buf[FLASH_BUF_SIZE];
|
||||
|
||||
/* Buffer used in data_read() */
|
||||
static uint8_t data_read_buf[FLASH_BUF_SIZE];
|
||||
|
||||
/* Semaphore for exclusive flash access */
|
||||
K_SEM_DEFINE(flash_sem, 1, 1);
|
||||
|
||||
|
||||
struct flash_hdr_t {
|
||||
/* 'C', 'D' */
|
||||
char id[2];
|
||||
|
||||
/* Header version */
|
||||
uint16_t hdr_version;
|
||||
|
||||
/* Coredump size, excluding this header */
|
||||
size_t size;
|
||||
|
||||
/* Flags */
|
||||
uint16_t flags;
|
||||
|
||||
/* Checksum */
|
||||
uint16_t checksum;
|
||||
|
||||
/* Error */
|
||||
int error;
|
||||
} __packed;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Open the flash partition.
|
||||
*
|
||||
* @return Same as flash_area_open().
|
||||
*/
|
||||
static int partition_open(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
(void)k_sem_take(&flash_sem, K_FOREVER);
|
||||
|
||||
ret = flash_area_open(FLASH_PARTITION, &backend_ctx.flash_area);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error opening flash partition for coredump!");
|
||||
|
||||
backend_ctx.flash_area = NULL;
|
||||
k_sem_give(&flash_sem);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close the flash partition.
|
||||
*/
|
||||
static void partition_close(void)
|
||||
{
|
||||
if (backend_ctx.flash_area == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
flash_area_close(backend_ctx.flash_area);
|
||||
backend_ctx.flash_area = NULL;
|
||||
|
||||
k_sem_give(&flash_sem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read data from flash partition.
|
||||
*
|
||||
* This reads @p len bytes in the flash partition starting
|
||||
* from @p off and put into buffer pointed by @p dst if
|
||||
* @p dst is not NULL.
|
||||
*
|
||||
* If @p cb is not NULL, data being read are passed to
|
||||
* the callback for processing. Note that the data being
|
||||
* passed to callback may only be part of the data requested.
|
||||
* The end of read is signaled to the callback with NULL
|
||||
* buffer pointer and zero length as arguments.
|
||||
*
|
||||
* @param off offset of partition to begin reading
|
||||
* @param dst buffer to read data into (can be NULL)
|
||||
* @param len number of bytes to read
|
||||
* @param cb callback to process read data (can be NULL)
|
||||
* @param cb_arg argument passed to callback
|
||||
* @return 0 if successful, error otherwise.
|
||||
*/
|
||||
static int data_read(off_t off, uint8_t *dst, size_t len,
|
||||
data_read_cb_t cb, void *cb_arg)
|
||||
{
|
||||
int ret = 0;
|
||||
off_t offset = off;
|
||||
size_t remaining = len;
|
||||
size_t copy_sz;
|
||||
uint8_t *ptr = dst;
|
||||
|
||||
if (backend_ctx.flash_area == NULL) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
copy_sz = FLASH_BUF_SIZE;
|
||||
while (remaining > 0) {
|
||||
if (remaining < FLASH_BUF_SIZE) {
|
||||
copy_sz = remaining;
|
||||
}
|
||||
|
||||
ret = flash_area_read(backend_ctx.flash_area, offset,
|
||||
data_read_buf, FLASH_BUF_SIZE);
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (dst != NULL) {
|
||||
(void)memcpy(ptr, data_read_buf, copy_sz);
|
||||
}
|
||||
|
||||
if (cb != NULL) {
|
||||
ret = (*cb)(cb_arg, data_read_buf, copy_sz);
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ptr += copy_sz;
|
||||
offset += copy_sz;
|
||||
remaining -= copy_sz;
|
||||
}
|
||||
|
||||
if (cb != NULL) {
|
||||
ret = (*cb)(cb_arg, NULL, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback to calculate checksum.
|
||||
*
|
||||
* @param arg callback argument (not being used)
|
||||
* @param buf data buffer
|
||||
* @param len number of bytes in buffer to process
|
||||
* @return 0
|
||||
*/
|
||||
static int cb_calc_buf_checksum(void *arg, uint8_t *buf, size_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
ARG_UNUSED(arg);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
backend_ctx.checksum += buf[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the stored coredump in flash partition.
|
||||
*
|
||||
* This reads the stored coredump data and processes it via
|
||||
* the callback function.
|
||||
*
|
||||
* @param cb callback to process the stored coredump data
|
||||
* @param cb_arg argument passed to callback
|
||||
* @return 1 if successful; 0 if stored coredump is not found
|
||||
* or is not valid; error otherwise
|
||||
*/
|
||||
static int process_stored_dump(data_read_cb_t cb, void *cb_arg)
|
||||
{
|
||||
int ret;
|
||||
struct flash_hdr_t hdr;
|
||||
off_t offset;
|
||||
|
||||
ret = partition_open();
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read header */
|
||||
ret = data_read(0, (uint8_t *)&hdr, sizeof(hdr), NULL, NULL);
|
||||
|
||||
/* Verify header signature */
|
||||
if ((hdr.id[0] != 'C') && (hdr.id[1] != 'D')) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Error encountered while dumping, so non-existent */
|
||||
if (hdr.error != 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
backend_ctx.checksum = 0;
|
||||
|
||||
offset = ROUND_UP(sizeof(struct flash_hdr_t), FLASH_WRITE_SIZE);
|
||||
ret = data_read(offset, NULL, hdr.size, cb, cb_arg);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = (backend_ctx.checksum == hdr.checksum) ? 1 : 0;
|
||||
}
|
||||
|
||||
out:
|
||||
partition_close();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the stored coredump in flash partition.
|
||||
*
|
||||
* This reads the stored coredump data and processes it via
|
||||
* the callback function.
|
||||
*
|
||||
* @return 0 if successful; error otherwise
|
||||
*/
|
||||
static int erase_flash_partition(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = partition_open();
|
||||
if (ret == 0) {
|
||||
/* Erase whole flash partition */
|
||||
ret = flash_area_erase(backend_ctx.flash_area, 0,
|
||||
backend_ctx.flash_area->fa_size);
|
||||
}
|
||||
|
||||
partition_close();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start of coredump session.
|
||||
*
|
||||
* This opens the flash partition for processing.
|
||||
*/
|
||||
static void coredump_flash_backend_start(void)
|
||||
{
|
||||
const struct device *flash_dev;
|
||||
size_t offset;
|
||||
int ret;
|
||||
|
||||
ret = partition_open();
|
||||
|
||||
if (ret == 0) {
|
||||
/* Erase whole flash partition */
|
||||
ret = flash_area_erase(backend_ctx.flash_area, 0,
|
||||
backend_ctx.flash_area->fa_size);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
backend_ctx.checksum = 0;
|
||||
|
||||
flash_dev = flash_area_get_device(backend_ctx.flash_area);
|
||||
|
||||
/*
|
||||
* Reserve space for header from beginning of flash device.
|
||||
* The header size is rounded up so the beginning of coredump
|
||||
* is aligned to write size (for easier read and seek).
|
||||
*/
|
||||
offset = backend_ctx.flash_area->fa_off;
|
||||
offset += ROUND_UP(sizeof(struct flash_hdr_t),
|
||||
FLASH_WRITE_SIZE);
|
||||
|
||||
ret = stream_flash_init(&backend_ctx.stream_ctx, flash_dev,
|
||||
stream_flash_buf,
|
||||
sizeof(stream_flash_buf),
|
||||
offset,
|
||||
backend_ctx.flash_area->fa_size,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Cannot start coredump!");
|
||||
backend_ctx.error = ret;
|
||||
partition_close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End of coredump session.
|
||||
*
|
||||
* This ends the coredump session by flushing coredump data
|
||||
* flash, and writes the header in the beginning of flash
|
||||
* related to the stored coredump data.
|
||||
*/
|
||||
static void coredump_flash_backend_end(void)
|
||||
{
|
||||
int ret;
|
||||
const struct device *flash_dev;
|
||||
|
||||
struct flash_hdr_t hdr = {
|
||||
.id = {'C', 'D'},
|
||||
.hdr_version = HDR_VER,
|
||||
};
|
||||
|
||||
if (backend_ctx.flash_area == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Flush buffer */
|
||||
backend_ctx.error = stream_flash_buffered_write(
|
||||
&backend_ctx.stream_ctx,
|
||||
stream_flash_buf, 0, true);
|
||||
|
||||
/* Write header */
|
||||
hdr.size = stream_flash_bytes_written(&backend_ctx.stream_ctx);
|
||||
hdr.checksum = backend_ctx.checksum;
|
||||
hdr.error = backend_ctx.error;
|
||||
hdr.flags = 0;
|
||||
|
||||
flash_dev = flash_area_get_device(backend_ctx.flash_area);
|
||||
|
||||
/* Need to re-init context to write at beginning of flash */
|
||||
ret = stream_flash_init(&backend_ctx.stream_ctx, flash_dev,
|
||||
stream_flash_buf,
|
||||
sizeof(stream_flash_buf),
|
||||
backend_ctx.flash_area->fa_off,
|
||||
backend_ctx.flash_area->fa_size, NULL);
|
||||
if (ret == 0) {
|
||||
ret = stream_flash_buffered_write(&backend_ctx.stream_ctx,
|
||||
(void *)&hdr, sizeof(hdr),
|
||||
true);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Cannot write coredump header!");
|
||||
backend_ctx.error = ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (backend_ctx.error != 0) {
|
||||
LOG_ERR("Error in coredump backend (%d)!",
|
||||
backend_ctx.error);
|
||||
}
|
||||
|
||||
partition_close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a buffer to flash partition.
|
||||
*
|
||||
* This writes @p buf into the flash partition. Note that this is
|
||||
* using the stream flash interface, so there is no need to keep
|
||||
* track of where on flash to write next.
|
||||
*
|
||||
* @param buf buffer of data to write to flash
|
||||
* @param buflen number of bytes to write
|
||||
*/
|
||||
static void coredump_flash_backend_buffer_output(uint8_t *buf, size_t buflen)
|
||||
{
|
||||
int i;
|
||||
size_t remaining = buflen;
|
||||
size_t copy_sz;
|
||||
uint8_t *ptr = buf;
|
||||
uint8_t tmp_buf[FLASH_BUF_SIZE];
|
||||
|
||||
if ((backend_ctx.error != 0) || (backend_ctx.flash_area == NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the system is still running, memory content is constantly
|
||||
* changing (e.g. stack of this thread). We need to make a copy of
|
||||
* part of the buffer, so that the checksum corresponds to what is
|
||||
* being written.
|
||||
*/
|
||||
copy_sz = FLASH_BUF_SIZE;
|
||||
while (remaining > 0) {
|
||||
if (remaining < FLASH_BUF_SIZE) {
|
||||
copy_sz = remaining;
|
||||
}
|
||||
|
||||
(void)memcpy(tmp_buf, ptr, copy_sz);
|
||||
|
||||
for (i = 0; i < copy_sz; i++) {
|
||||
backend_ctx.checksum += tmp_buf[i];
|
||||
}
|
||||
|
||||
backend_ctx.error = stream_flash_buffered_write(
|
||||
&backend_ctx.stream_ctx,
|
||||
tmp_buf, copy_sz, false);
|
||||
if (backend_ctx.error != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += copy_sz;
|
||||
remaining -= copy_sz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform query on this backend.
|
||||
*
|
||||
* @param query_id ID of query
|
||||
* @param arg argument of query
|
||||
* @return depends on query
|
||||
*/
|
||||
static int coredump_flash_backend_query(enum coredump_query_id query_id,
|
||||
void *arg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (query_id) {
|
||||
case COREDUMP_QUERY_GET_ERROR:
|
||||
ret = backend_ctx.error;
|
||||
break;
|
||||
case COREDUMP_QUERY_HAS_STORED_DUMP:
|
||||
ret = process_stored_dump(cb_calc_buf_checksum, NULL);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform command on this backend.
|
||||
*
|
||||
* @param cmd_id command ID
|
||||
* @param arg argument of command
|
||||
* @return depends on query
|
||||
*/
|
||||
static int coredump_flash_backend_cmd(enum coredump_cmd_id cmd_id,
|
||||
void *arg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (cmd_id) {
|
||||
case COREDUMP_CMD_CLEAR_ERROR:
|
||||
ret = 0;
|
||||
backend_ctx.error = 0;
|
||||
break;
|
||||
case COREDUMP_CMD_VERIFY_STORED_DUMP:
|
||||
ret = process_stored_dump(cb_calc_buf_checksum, NULL);
|
||||
break;
|
||||
case COREDUMP_CMD_ERASE_STORED_DUMP:
|
||||
ret = erase_flash_partition();
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
struct z_coredump_backend_api z_coredump_backend_flash_partition = {
|
||||
.start = coredump_flash_backend_start,
|
||||
.end = coredump_flash_backend_end,
|
||||
.buffer_output = coredump_flash_backend_buffer_output,
|
||||
.query = coredump_flash_backend_query,
|
||||
.cmd = coredump_flash_backend_cmd,
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_DEBUG_COREDUMP_SHELL
|
||||
#include <shell/shell.h>
|
||||
|
||||
/* Length of buffer of printable size */
|
||||
#define PRINT_BUF_SZ 64
|
||||
|
||||
/* Length of buffer of printable size plus null character */
|
||||
#define PRINT_BUF_SZ_RAW (PRINT_BUF_SZ + 1)
|
||||
|
||||
/* Print buffer */
|
||||
static char print_buf[PRINT_BUF_SZ_RAW];
|
||||
static off_t print_buf_ptr;
|
||||
|
||||
/**
|
||||
* @brief Shell command to get backend error.
|
||||
*
|
||||
* @param shell shell instance
|
||||
* @param argc (not used)
|
||||
* @param argv (not used)
|
||||
* @return 0
|
||||
*/
|
||||
static int cmd_coredump_error_get(const struct shell *shell,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
ARG_UNUSED(argc);
|
||||
ARG_UNUSED(argv);
|
||||
|
||||
if (backend_ctx.error == 0) {
|
||||
shell_print(shell, "No error.");
|
||||
} else {
|
||||
shell_print(shell, "Error: %d", backend_ctx.error);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell command to clear backend error.
|
||||
*
|
||||
* @param shell shell instance
|
||||
* @param argc (not used)
|
||||
* @param argv (not used)
|
||||
* @return 0
|
||||
*/
|
||||
static int cmd_coredump_error_clear(const struct shell *shell,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
backend_ctx.error = 0;
|
||||
|
||||
shell_print(shell, "Error cleared.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell command to see if there is a stored coredump in flash.
|
||||
*
|
||||
* @param shell shell instance
|
||||
* @param argc (not used)
|
||||
* @param argv (not used)
|
||||
* @return 0
|
||||
*/
|
||||
static int cmd_coredump_has_stored_dump(const struct shell *shell,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ARG_UNUSED(argc);
|
||||
ARG_UNUSED(argv);
|
||||
|
||||
ret = coredump_flash_backend_query(COREDUMP_QUERY_HAS_STORED_DUMP,
|
||||
NULL);
|
||||
|
||||
if (ret == 1) {
|
||||
shell_print(shell, "Stored coredump found.");
|
||||
} else if (ret == 0) {
|
||||
shell_print(shell, "Stored coredump NOT found.");
|
||||
} else {
|
||||
shell_print(shell, "Failed to perform query: %d", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell command to verify if the stored coredump is valid.
|
||||
*
|
||||
* @param shell shell instance
|
||||
* @param argc (not used)
|
||||
* @param argv (not used)
|
||||
* @return 0
|
||||
*/
|
||||
static int cmd_coredump_verify_stored_dump(const struct shell *shell,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ARG_UNUSED(argc);
|
||||
ARG_UNUSED(argv);
|
||||
|
||||
ret = coredump_flash_backend_cmd(COREDUMP_CMD_VERIFY_STORED_DUMP,
|
||||
NULL);
|
||||
|
||||
if (ret == 1) {
|
||||
shell_print(shell, "Stored coredump verified.");
|
||||
} else if (ret == 0) {
|
||||
shell_print(shell, "Stored coredump verification failed "
|
||||
"or there is no stored coredump.");
|
||||
} else {
|
||||
shell_print(shell, "Failed to perform verify command: %d",
|
||||
ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Flush the print buffer to shell.
|
||||
*
|
||||
* This prints what is in the print buffer to the shell.
|
||||
*
|
||||
* @param shell shell instance.
|
||||
*/
|
||||
static void flush_print_buf(const struct shell *shell)
|
||||
{
|
||||
shell_print(shell, "%s%s", COREDUMP_PREFIX_STR, print_buf);
|
||||
print_buf_ptr = 0;
|
||||
(void)memset(print_buf, 0, sizeof(print_buf));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback to print stored coredump to shell
|
||||
*
|
||||
* This converts the binary data in @p buf to hexadecimal digits
|
||||
* which can be printed to the shell.
|
||||
*
|
||||
* @param arg shell instance
|
||||
* @param buf binary data buffer
|
||||
* @param len number of bytes in buffer to be printed
|
||||
* @return 0 if no issues; -EINVAL if error converting data
|
||||
*/
|
||||
static int cb_print_stored_dump(void *arg, uint8_t *buf, size_t len)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t i = 0;
|
||||
size_t remaining = len;
|
||||
const struct shell *shell = (const struct shell *)arg;
|
||||
|
||||
if (len == 0) {
|
||||
/* Flush print buffer */
|
||||
flush_print_buf(shell);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Do checksum for process_stored_dump() */
|
||||
cb_calc_buf_checksum(arg, buf, len);
|
||||
|
||||
while (remaining > 0) {
|
||||
if (hex2char(buf[i] >> 4, &print_buf[print_buf_ptr]) < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
print_buf_ptr++;
|
||||
|
||||
if (hex2char(buf[i] & 0xf, &print_buf[print_buf_ptr]) < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
print_buf_ptr++;
|
||||
|
||||
remaining--;
|
||||
i++;
|
||||
|
||||
if (print_buf_ptr == PRINT_BUF_SZ) {
|
||||
flush_print_buf(shell);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell command to print stored coredump data to shell
|
||||
*
|
||||
* @param shell shell instance
|
||||
* @param argc (not used)
|
||||
* @param argv (not used)
|
||||
* @return 0
|
||||
*/
|
||||
static int cmd_coredump_print_stored_dump(const struct shell *shell,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ARG_UNUSED(argc);
|
||||
ARG_UNUSED(argv);
|
||||
|
||||
/* Verify first to see if stored dump is valid */
|
||||
ret = coredump_flash_backend_cmd(COREDUMP_CMD_VERIFY_STORED_DUMP,
|
||||
NULL);
|
||||
|
||||
if (ret == 0) {
|
||||
shell_print(shell, "Stored coredump verification failed "
|
||||
"or there is no stored coredump.");
|
||||
goto out;
|
||||
} else if (ret != 1) {
|
||||
shell_print(shell, "Failed to perform verify command: %d",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If valid, start printing to shell */
|
||||
print_buf_ptr = 0;
|
||||
(void)memset(print_buf, 0, sizeof(print_buf));
|
||||
|
||||
shell_print(shell, "%s%s", COREDUMP_PREFIX_STR, COREDUMP_BEGIN_STR);
|
||||
|
||||
ret = process_stored_dump(cb_print_stored_dump, (void *)shell);
|
||||
if (print_buf_ptr != 0) {
|
||||
shell_print(shell, "%s%s", COREDUMP_PREFIX_STR, print_buf);
|
||||
}
|
||||
|
||||
if (backend_ctx.error != 0) {
|
||||
shell_print(shell, "%s%s", COREDUMP_PREFIX_STR,
|
||||
COREDUMP_ERROR_STR);
|
||||
}
|
||||
|
||||
shell_print(shell, "%s%s", COREDUMP_PREFIX_STR, COREDUMP_END_STR);
|
||||
|
||||
if (ret == 1) {
|
||||
shell_print(shell, "Stored coredump printed.");
|
||||
} else if (ret == 0) {
|
||||
shell_print(shell, "Stored coredump verification failed "
|
||||
"or there is no stored coredump.");
|
||||
} else {
|
||||
shell_print(shell, "Failed to print: %d", ret);
|
||||
}
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell command to erase stored coredump.
|
||||
*
|
||||
* @param shell shell instance
|
||||
* @param argc (not used)
|
||||
* @param argv (not used)
|
||||
* @return 0
|
||||
*/
|
||||
static int cmd_coredump_erase_stored_dump(const struct shell *shell,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ARG_UNUSED(argc);
|
||||
ARG_UNUSED(argv);
|
||||
|
||||
ret = coredump_flash_backend_cmd(COREDUMP_CMD_ERASE_STORED_DUMP,
|
||||
NULL);
|
||||
|
||||
if (ret == 0) {
|
||||
shell_print(shell, "Stored coredump erased.");
|
||||
} else {
|
||||
shell_print(shell, "Failed to perform erase command: %d", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(sub_coredump_error,
|
||||
SHELL_CMD(clear, NULL, "Clear Coredump error",
|
||||
cmd_coredump_error_clear),
|
||||
SHELL_CMD(get, NULL, "Get Coredump error", cmd_coredump_error_get),
|
||||
SHELL_SUBCMD_SET_END /* Array terminated. */
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(sub_coredump,
|
||||
SHELL_CMD(error, &sub_coredump_error,
|
||||
"Get/clear backend error.", NULL),
|
||||
SHELL_CMD(erase, NULL,
|
||||
"Erase stored coredump",
|
||||
cmd_coredump_erase_stored_dump),
|
||||
SHELL_CMD(find, NULL,
|
||||
"Query if there is a stored coredump",
|
||||
cmd_coredump_has_stored_dump),
|
||||
SHELL_CMD(print, NULL,
|
||||
"Print stored coredump to shell",
|
||||
cmd_coredump_print_stored_dump),
|
||||
SHELL_CMD(verify, NULL,
|
||||
"Verify stored coredump",
|
||||
cmd_coredump_verify_stored_dump),
|
||||
SHELL_SUBCMD_SET_END /* Array terminated. */
|
||||
);
|
||||
|
||||
SHELL_CMD_REGISTER(coredump, &sub_coredump,
|
||||
"Coredump commands (flash partition backend)", NULL);
|
||||
|
||||
#endif /* CONFIG_DEBUG_COREDUMP_SHELL */
|
||||
|
||||
#endif /* FLASH_AREA_LABEL_EXISTS(coredump_partition) */
|
||||
|
|
@ -13,16 +13,6 @@
|
|||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(coredump, CONFIG_KERNEL_LOG_LEVEL);
|
||||
|
||||
#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:"
|
||||
|
||||
/* Length of buffer of printable size */
|
||||
#define LOG_BUF_SZ 64
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@
|
|||
extern struct z_coredump_backend_api z_coredump_backend_logging;
|
||||
static struct z_coredump_backend_api
|
||||
*backend_api = &z_coredump_backend_logging;
|
||||
#elif defined(CONFIG_DEBUG_COREDUMP_BACKEND_FLASH_PARTITION)
|
||||
extern struct z_coredump_backend_api z_coredump_backend_flash_partition;
|
||||
static struct z_coredump_backend_api
|
||||
*backend_api = &z_coredump_backend_flash_partition;
|
||||
#else
|
||||
#error "Need to select a coredump backend"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,6 +16,16 @@
|
|||
* public documentation.
|
||||
*/
|
||||
|
||||
#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:"
|
||||
|
||||
struct z_coredump_memory_region_t {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
|
|
|
|||
Loading…
Reference in a new issue