debug: coredump: add xtensa coredump
Adds Xtensa as supported architecture for coredump. Fixes a few typos in documentation, Kconfig and a C file. Dumps minimal set of registers shown by 'info registers' in GDB for the sample_controller and ESP32 SOCs. Updates tests. Signed-off-by: Lauren Murphy <lauren.murphy@intel.com>
This commit is contained in:
parent
71f17bcbc2
commit
c1711997bc
9 changed files with 174 additions and 12 deletions
|
@ -37,7 +37,7 @@ config XTENSA_NO_IPC
|
||||||
bool "Core has no IPC support"
|
bool "Core has no IPC support"
|
||||||
select ATOMIC_OPERATIONS_C
|
select ATOMIC_OPERATIONS_C
|
||||||
help
|
help
|
||||||
Uncheck this if you core does not implement "SCOMPARE1" register and "s32c1i"
|
Uncheck this if your core does not implement "SCOMPARE1" register and "s32c1i"
|
||||||
instruction.
|
instruction.
|
||||||
|
|
||||||
config XTENSA_RESET_VECTOR
|
config XTENSA_RESET_VECTOR
|
||||||
|
|
|
@ -18,6 +18,7 @@ zephyr_library_sources_ifdef(CONFIG_IRQ_OFFLOAD irq_offload.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c)
|
zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_XTENSA_ENABLE_BACKTRACE xtensa_backtrace.c)
|
zephyr_library_sources_ifdef(CONFIG_XTENSA_ENABLE_BACKTRACE xtensa_backtrace.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_XTENSA_ENABLE_BACKTRACE debug_helpers_asm.S)
|
zephyr_library_sources_ifdef(CONFIG_XTENSA_ENABLE_BACKTRACE debug_helpers_asm.S)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP coredump.c)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(CONFIG_GDBSTUB gdbstub.c)
|
zephyr_library_sources_ifdef(CONFIG_GDBSTUB gdbstub.c)
|
||||||
|
|
||||||
|
|
159
arch/xtensa/core/coredump.c
Normal file
159
arch/xtensa/core/coredump.c
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <debug/coredump.h>
|
||||||
|
#include <xtensa-asm2.h>
|
||||||
|
|
||||||
|
#define ARCH_HDR_VER 1
|
||||||
|
#define XTENSA_BLOCK_HDR_VER 1
|
||||||
|
|
||||||
|
enum xtensa_soc_code {
|
||||||
|
XTENSA_SOC_UNKNOWN = 0,
|
||||||
|
XTENSA_SOC_SAMPLE_CONTROLLER,
|
||||||
|
XTENSA_SOC_ESP32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xtensa_arch_block {
|
||||||
|
/* Each Xtensa SOC can omit registers (e.g. loop
|
||||||
|
* registers) or assign different index numbers
|
||||||
|
* in xtensa-config.c. GDB identifies registers
|
||||||
|
* based on these indices
|
||||||
|
*
|
||||||
|
* (This must be the first field or the GDB server
|
||||||
|
* won't be able to unpack the struct while parsing)
|
||||||
|
*/
|
||||||
|
uint8_t soc;
|
||||||
|
|
||||||
|
/* Future versions of Xtensa coredump
|
||||||
|
* may expand minimum set of registers
|
||||||
|
*
|
||||||
|
* (This should stay the second field for the same
|
||||||
|
* reason as the first once we have more versions)
|
||||||
|
*/
|
||||||
|
uint16_t version;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
/* Minimum set shown by GDB 'info registers',
|
||||||
|
* skipping user-defined register EXPSTATE
|
||||||
|
*
|
||||||
|
* WARNING: IF YOU CHANGE THE ORDER OF THE REGISTERS,
|
||||||
|
* YOU MUST UPDATE THE ORDER OF THE REGISTERS IN
|
||||||
|
* EACH OF THE XtensaSoc_ RegNum enums IN
|
||||||
|
* scripts/coredump/gdbstubs/arch/xtensa.py TO MATCH.
|
||||||
|
* See xtensa.py's map_register function for details
|
||||||
|
*/
|
||||||
|
uint32_t pc;
|
||||||
|
uint32_t exccause;
|
||||||
|
uint32_t excvaddr;
|
||||||
|
uint32_t sar;
|
||||||
|
uint32_t ps;
|
||||||
|
#if XCHAL_HAVE_S32C1I
|
||||||
|
uint32_t scompare1;
|
||||||
|
#endif
|
||||||
|
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 a8;
|
||||||
|
uint32_t a9;
|
||||||
|
uint32_t a10;
|
||||||
|
uint32_t a11;
|
||||||
|
uint32_t a12;
|
||||||
|
uint32_t a13;
|
||||||
|
uint32_t a14;
|
||||||
|
uint32_t a15;
|
||||||
|
#if XCHAL_HAVE_LOOPS
|
||||||
|
uint32_t lbeg;
|
||||||
|
uint32_t lend;
|
||||||
|
uint32_t lcount;
|
||||||
|
#endif
|
||||||
|
} r;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This might be too large for stack space if defined
|
||||||
|
* inside function. So do it here.
|
||||||
|
*/
|
||||||
|
static struct xtensa_arch_block arch_blk;
|
||||||
|
|
||||||
|
void arch_coredump_info_dump(const z_arch_esf_t *esf)
|
||||||
|
{
|
||||||
|
struct coredump_arch_hdr_t hdr = {
|
||||||
|
.id = COREDUMP_ARCH_HDR_ID,
|
||||||
|
.hdr_version = ARCH_HDR_VER,
|
||||||
|
.num_bytes = sizeof(arch_blk),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Nothing to process */
|
||||||
|
if (esf == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)memset(&arch_blk, 0, sizeof(arch_blk));
|
||||||
|
|
||||||
|
arch_blk.version = XTENSA_BLOCK_HDR_VER;
|
||||||
|
|
||||||
|
#if CONFIG_SOC_XTENSA_SAMPLE_CONTROLLER
|
||||||
|
arch_blk.soc = XTENSA_SOC_SAMPLE_CONTROLLER;
|
||||||
|
#elif CONFIG_SOC_ESP32
|
||||||
|
arch_blk.soc = XTENSA_SOC_ESP32;
|
||||||
|
#else
|
||||||
|
arch_blk.soc = XTENSA_SOC_UNKNOWN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__asm__ volatile("rsr.exccause %0" : "=r"(arch_blk.r.exccause));
|
||||||
|
|
||||||
|
int *bsa = *(int **)esf;
|
||||||
|
|
||||||
|
arch_blk.r.pc = bsa[BSA_PC_OFF/4];
|
||||||
|
__asm__ volatile("rsr.excvaddr %0" : "=r"(arch_blk.r.excvaddr));
|
||||||
|
arch_blk.r.ps = bsa[BSA_PS_OFF/4];
|
||||||
|
#if XCHAL_HAVE_S32C1I
|
||||||
|
arch_blk.r.scompare1 = bsa[BSA_SCOMPARE1_OFF];
|
||||||
|
#endif
|
||||||
|
arch_blk.r.sar = bsa[BSA_SAR_OFF/4];
|
||||||
|
arch_blk.r.a0 = bsa[BSA_A0_OFF/4];
|
||||||
|
arch_blk.r.a1 = (uint32_t)((char *)bsa) + BASE_SAVE_AREA_SIZE;
|
||||||
|
arch_blk.r.a2 = bsa[BSA_A2_OFF/4];
|
||||||
|
arch_blk.r.a3 = bsa[BSA_A3_OFF/4];
|
||||||
|
if (bsa - esf > 4) {
|
||||||
|
arch_blk.r.a4 = bsa[-4];
|
||||||
|
arch_blk.r.a5 = bsa[-3];
|
||||||
|
arch_blk.r.a6 = bsa[-2];
|
||||||
|
arch_blk.r.a7 = bsa[-1];
|
||||||
|
}
|
||||||
|
if (bsa - esf > 8) {
|
||||||
|
arch_blk.r.a8 = bsa[-8];
|
||||||
|
arch_blk.r.a9 = bsa[-7];
|
||||||
|
arch_blk.r.a10 = bsa[-6];
|
||||||
|
arch_blk.r.a11 = bsa[-5];
|
||||||
|
}
|
||||||
|
if (bsa - esf > 12) {
|
||||||
|
arch_blk.r.a12 = bsa[-12];
|
||||||
|
arch_blk.r.a13 = bsa[-11];
|
||||||
|
arch_blk.r.a14 = bsa[-10];
|
||||||
|
arch_blk.r.a15 = bsa[-9];
|
||||||
|
}
|
||||||
|
#if XCHAL_HAVE_LOOPS
|
||||||
|
arch_blk.r.lbeg = bsa[BSA_LBEG_OFF/4];
|
||||||
|
arch_blk.r.lend = bsa[BSA_LEND_OFF/4];
|
||||||
|
arch_blk.r.lcount = bsa[BSA_LCOUNT_OFF/4];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Send for output */
|
||||||
|
coredump_buffer_output((uint8_t *)&hdr, sizeof(hdr));
|
||||||
|
coredump_buffer_output((uint8_t *)&arch_blk, sizeof(arch_blk));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t arch_coredump_tgt_code_get(void)
|
||||||
|
{
|
||||||
|
return COREDUMP_TGT_XTENSA;
|
||||||
|
}
|
|
@ -4,8 +4,8 @@ Core Dump
|
||||||
#########
|
#########
|
||||||
|
|
||||||
The core dump module enables dumping the CPU registers and memory content
|
The core dump module enables dumping the CPU registers and memory content
|
||||||
for offline debugging. This module is called when fatal error is
|
for offline debugging. This module is called when a fatal error is
|
||||||
encountered, and the data is printed or stored according to which backends
|
encountered and prints or stores data according to which backends
|
||||||
are enabled.
|
are enabled.
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
|
@ -25,15 +25,15 @@ Here are the choices regarding memory dump:
|
||||||
|
|
||||||
* ``DEBUG_COREDUMP_MEMORY_DUMP_MIN``: only dumps the stack of the exception
|
* ``DEBUG_COREDUMP_MEMORY_DUMP_MIN``: only dumps the stack of the exception
|
||||||
thread, its thread struct, and some other bare minimal data to support
|
thread, its thread struct, and some other bare minimal data to support
|
||||||
walking the stack in debugger. Use this only if absolute minimum of data
|
walking the stack in the debugger. Use this only if absolute minimum of data
|
||||||
dump is desired.
|
dump is desired.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
When the core dump module is enabled, during fatal error, CPU registers
|
When the core dump module is enabled, during a fatal error, CPU registers
|
||||||
and memory content are being printed or stored according to which backends
|
and memory content are printed or stored according to which backends
|
||||||
are enabled. This core dump data can fed into a custom made GDB server as
|
are enabled. This core dump data can fed into a custom-made GDB server as
|
||||||
a remote target for GDB (and other GDB compatible debuggers). CPU registers,
|
a remote target for GDB (and other GDB compatible debuggers). CPU registers,
|
||||||
memory content and stack can be examined in the debugger.
|
memory content and stack can be examined in the debugger.
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ enum coredump_tgt_code {
|
||||||
COREDUMP_TGT_X86_64,
|
COREDUMP_TGT_X86_64,
|
||||||
COREDUMP_TGT_ARM_CORTEX_M,
|
COREDUMP_TGT_ARM_CORTEX_M,
|
||||||
COREDUMP_TGT_RISC_V,
|
COREDUMP_TGT_RISC_V,
|
||||||
|
COREDUMP_TGT_XTENSA,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Coredump header */
|
/* Coredump header */
|
||||||
|
|
|
@ -8,6 +8,7 @@ config SOC_ESP32
|
||||||
select CLOCK_CONTROL_ESP32
|
select CLOCK_CONTROL_ESP32
|
||||||
select DYNAMIC_INTERRUPTS
|
select DYNAMIC_INTERRUPTS
|
||||||
select ARCH_HAS_GDBSTUB
|
select ARCH_HAS_GDBSTUB
|
||||||
|
select ARCH_SUPPORTS_COREDUMP
|
||||||
|
|
||||||
if SOC_ESP32
|
if SOC_ESP32
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,4 @@ config SOC_XTENSA_SAMPLE_CONTROLLER
|
||||||
bool "Xtensa sample_controller core"
|
bool "Xtensa sample_controller core"
|
||||||
select XTENSA
|
select XTENSA
|
||||||
select XTENSA_HAL
|
select XTENSA_HAL
|
||||||
|
select ARCH_SUPPORTS_COREDUMP
|
||||||
|
|
|
@ -357,7 +357,7 @@ config GDBSTUB_SERIAL_BACKEND
|
||||||
bool "Use serial backend"
|
bool "Use serial backend"
|
||||||
depends on SERIAL
|
depends on SERIAL
|
||||||
help
|
help
|
||||||
Use serial as backenf for GDB
|
Use serial as backend for GDB
|
||||||
|
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
@ -366,7 +366,7 @@ config GDBSTUB_SERIAL_BACKEND_NAME
|
||||||
depends on GDBSTUB_SERIAL_BACKEND
|
depends on GDBSTUB_SERIAL_BACKEND
|
||||||
default "UART_0"
|
default "UART_0"
|
||||||
help
|
help
|
||||||
Use serial as backenf for GDB
|
Use serial as backend for GDB
|
||||||
|
|
||||||
config GDBSTUB_BUF_SZ
|
config GDBSTUB_BUF_SZ
|
||||||
int "GDB backend send/receive buffer size (in bytes)"
|
int "GDB backend send/receive buffer size (in bytes)"
|
||||||
|
|
|
@ -273,10 +273,9 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Process the stored coredump in flash partition.
|
* @brief Erase the stored coredump from flash partition.
|
||||||
*
|
*
|
||||||
* This reads the stored coredump data and processes it via
|
* This erases the stored coredump data from the flash partition.
|
||||||
* the callback function.
|
|
||||||
*
|
*
|
||||||
* @return 0 if successful; error otherwise
|
* @return 0 if successful; error otherwise
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue