SPARC: improve fatal log
The fatal log now contains - Trap type in human readable representation - Integer registers visible to the program when trap was taken - Special register values such as PC and PSR - Backtrace with PC and SP If CONFIG_EXTRA_EXCEPTION_INFO is enabled, then all the above is logged. If not, only the special registers are logged. The format is inspired by the GRMON debug monitor and TSIM simulator. A quick guide on how to use the values is in fatal.c. It now looks like this: E: tt = 0x02, illegal_instruction E: E: INS LOCALS OUTS GLOBALS E: 0: 00000000 f3900fc0 40007c50 00000000 E: 1: 00000000 40004bf0 40008d30 40008c00 E: 2: 00000000 40004bf4 40008000 00000003 E: 3: 40009158 00000000 40009000 00000002 E: 4: 40008fa8 40003c00 40008fa8 00000008 E: 5: 40009000 f3400fc0 00000000 00000080 E: 6: 4000a1f8 40000050 4000a190 00000000 E: 7: 40002308 00000000 40001fb8 000000c1 E: E: psr: f30000c7 wim: 00000008 tbr: 40000020 y: 00000000 E: pc: 4000a1f4 npc: 4000a1f8 E: E: pc sp E: #0 4000a1f4 4000a190 E: #1 40002308 4000a1f8 E: #2 40003b24 4000a258 Signed-off-by: Martin Åberg <martin.aberg@gaisler.com>
This commit is contained in:
parent
c2b1e8d2f5
commit
83f733ce59
7 changed files with 243 additions and 17 deletions
|
@ -46,6 +46,7 @@ config SPARC
|
|||
select ATOMIC_OPERATIONS_BUILTIN if SPARC_CASA
|
||||
select ATOMIC_OPERATIONS_C if !SPARC_CASA
|
||||
select ARCH_HAS_THREAD_LOCAL_STORAGE
|
||||
select ARCH_HAS_EXTRA_EXCEPTION_INFO
|
||||
help
|
||||
SPARC architecture
|
||||
|
||||
|
|
|
@ -8,18 +8,211 @@
|
|||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* EXAMPLE OUTPUT
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* tt = 0x02, illegal_instruction
|
||||
*
|
||||
* INS LOCALS OUTS GLOBALS
|
||||
* 0: 00000000 f3900fc0 40007c50 00000000
|
||||
* 1: 00000000 40004bf0 40008d30 40008c00
|
||||
* 2: 00000000 40004bf4 40008000 00000003
|
||||
* 3: 40009158 00000000 40009000 00000002
|
||||
* 4: 40008fa8 40003c00 40008fa8 00000008
|
||||
* 5: 40009000 f3400fc0 00000000 00000080
|
||||
* 6: 4000a1f8 40000050 4000a190 00000000
|
||||
* 7: 40002308 00000000 40001fb8 000000c1
|
||||
*
|
||||
* psr: f30000c7 wim: 00000008 tbr: 40000020 y: 00000000
|
||||
* pc: 4000a1f4 npc: 4000a1f8
|
||||
*
|
||||
* pc sp
|
||||
* #0 4000a1f4 4000a190
|
||||
* #1 40002308 4000a1f8
|
||||
* #2 40003b24 4000a258
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
*
|
||||
* INTERPRETATION
|
||||
*
|
||||
* INS, LOCALS, OUTS and GLOBALS represent the %i, %l, %o and %g
|
||||
* registers before the trap was taken.
|
||||
*
|
||||
* wim, y, pc and npc are the values before the trap was taken.
|
||||
* tbr has the tbr.tt field (bits 11..4) filled in by hardware
|
||||
* representing the current trap type. psr is read immediately
|
||||
* after the trap was taken so it will have the new CWP and ET=0.
|
||||
*
|
||||
* The "#i pc sp" rows is the stack backtrace. All register
|
||||
* windows are flushed to the stack prior to printing. First row
|
||||
* is the trapping pc and sp (o6).
|
||||
*
|
||||
*
|
||||
* HOW TO USE
|
||||
*
|
||||
* When invesetigating a crashed program, the first things to look
|
||||
* at is typically the tt, pc and sp (o6). You can lookup the pc
|
||||
* in the assembly list file or use addr2line. In the listing, the
|
||||
* register values in the table above can be used. The linker map
|
||||
* file will give a hint on which stack is active and if it has
|
||||
* overflowed.
|
||||
*
|
||||
* psr bits 11..8 is the processor interrupt (priority) level. 0
|
||||
* is lowest priority level (all can be taken), and 0xf is the
|
||||
* highest level where only non-maskable interrupts are taken.
|
||||
*
|
||||
* g0 is always zero. g5, g6 are never accessed by the compiler.
|
||||
* g7 is the TLS pointer if enabled. A SAVE instruction decreases
|
||||
* the current window pointer (psr bits 4..0) which results in %o
|
||||
* registers becoming %i registers and a new set of %l registers
|
||||
* appear. RESTORE does the oppposite.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* The SPARC V8 ABI guarantees that the stack pointer register
|
||||
* (o6) points to an area organized as "struct savearea" below at
|
||||
* all times when traps are enabled. This is the register save
|
||||
* area where register window registers can be flushed to the
|
||||
* stack.
|
||||
*
|
||||
* We flushed registers to this space in the fault trap entry
|
||||
* handler. Note that the space is allocated by the ABI (compiler)
|
||||
* for each stack frame.
|
||||
*
|
||||
* When printing the registers, we get the "local" and "in"
|
||||
* registers from the ABI stack save area, while the "out" and
|
||||
* "global" registares are taken from the exception stack frame
|
||||
* generated in the fault trap entry.
|
||||
*/
|
||||
struct savearea {
|
||||
uint32_t local[8];
|
||||
uint32_t in[8];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Exception trap type (tt) values according to The SPARC V8
|
||||
* manual, Table 7-1.
|
||||
*/
|
||||
static const struct {
|
||||
int tt;
|
||||
const char *desc;
|
||||
} TTDESC[] = {
|
||||
{ .tt = 0x02, .desc = "illegal_instruction", },
|
||||
{ .tt = 0x07, .desc = "mem_address_not_aligned", },
|
||||
{ .tt = 0x2B, .desc = "data_store_error", },
|
||||
{ .tt = 0x29, .desc = "data_access_error", },
|
||||
{ .tt = 0x09, .desc = "data_access_exception", },
|
||||
{ .tt = 0x21, .desc = "instruction_access_error", },
|
||||
{ .tt = 0x01, .desc = "instruction_access_exception", },
|
||||
{ .tt = 0x04, .desc = "fp_disabled", },
|
||||
{ .tt = 0x08, .desc = "fp_exception", },
|
||||
{ .tt = 0x2A, .desc = "division_by_zero", },
|
||||
{ .tt = 0x03, .desc = "privileged_instruction", },
|
||||
{ .tt = 0x20, .desc = "r_register_access_error", },
|
||||
{ .tt = 0x0B, .desc = "watchpoint_detected", },
|
||||
{ .tt = 0x2C, .desc = "data_access_MMU_miss", },
|
||||
{ .tt = 0x3C, .desc = "instruction_access_MMU_miss", },
|
||||
{ .tt = 0x05, .desc = "window_overflow", },
|
||||
{ .tt = 0x06, .desc = "window_underflow", },
|
||||
{ .tt = 0x0A, .desc = "tag_overflow", },
|
||||
};
|
||||
|
||||
static void print_trap_type(const z_arch_esf_t *esf)
|
||||
{
|
||||
const int tt = (esf->tbr & TBR_TT) >> TBR_TT_BIT;
|
||||
const char *desc = "unknown";
|
||||
|
||||
if (tt & 0x80) {
|
||||
desc = "trap_instruction";
|
||||
} else if (tt >= 0x11 && tt <= 0x1F) {
|
||||
desc = "interrupt";
|
||||
} else {
|
||||
for (int i = 0; i < ARRAY_SIZE(TTDESC); i++) {
|
||||
if (TTDESC[i].tt == tt) {
|
||||
desc = TTDESC[i].desc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_ERR("tt = 0x%02X, %s", tt, desc);
|
||||
}
|
||||
|
||||
static void print_integer_registers(const z_arch_esf_t *esf)
|
||||
{
|
||||
const struct savearea *flushed = (struct savearea *) esf->out[6];
|
||||
|
||||
LOG_ERR(" INS LOCALS OUTS GLOBALS");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
LOG_ERR(
|
||||
" %d: %08x %08x %08x %08x",
|
||||
i,
|
||||
flushed ? flushed->in[i] : 0,
|
||||
flushed ? flushed->local[i] : 0,
|
||||
esf->out[i],
|
||||
esf->global[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_special_registers(const z_arch_esf_t *esf)
|
||||
{
|
||||
LOG_ERR(
|
||||
"psr: %08x wim: %08x tbr: %08x y: %08x",
|
||||
esf->psr, esf->wim, esf->tbr, esf->y
|
||||
);
|
||||
LOG_ERR(" pc: %08x npc: %08x", esf->pc, esf->npc);
|
||||
}
|
||||
|
||||
static void print_backtrace(const z_arch_esf_t *esf)
|
||||
{
|
||||
const int MAX_LOGLINES = 40;
|
||||
const struct savearea *s = (struct savearea *) esf->out[6];
|
||||
|
||||
LOG_ERR(" pc sp");
|
||||
LOG_ERR(" #0 %08x %08x", esf->pc, (unsigned int) s);
|
||||
for (int i = 1; s && i < MAX_LOGLINES; i++) {
|
||||
const uint32_t pc = s->in[7];
|
||||
const uint32_t sp = s->in[6];
|
||||
|
||||
if (sp == 0 && pc == 0) {
|
||||
break;
|
||||
}
|
||||
LOG_ERR(" #%-2d %08x %08x", i, pc, sp);
|
||||
if (sp == 0 || sp & 7) {
|
||||
break;
|
||||
}
|
||||
s = (const struct savearea *) sp;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_all(const z_arch_esf_t *esf)
|
||||
{
|
||||
LOG_ERR("");
|
||||
print_trap_type(esf);
|
||||
LOG_ERR("");
|
||||
print_integer_registers(esf);
|
||||
LOG_ERR("");
|
||||
print_special_registers(esf);
|
||||
LOG_ERR("");
|
||||
print_backtrace(esf);
|
||||
LOG_ERR("");
|
||||
}
|
||||
|
||||
FUNC_NORETURN void z_sparc_fatal_error(unsigned int reason,
|
||||
const z_arch_esf_t *esf)
|
||||
{
|
||||
if (esf != NULL) {
|
||||
LOG_ERR(" pc: %08x", esf->pc);
|
||||
LOG_ERR("npc: %08x", esf->npc);
|
||||
LOG_ERR("psr: %08x", esf->psr);
|
||||
LOG_ERR("tbr: %08x", esf->tbr);
|
||||
LOG_ERR(" sp: %08x", esf->sp);
|
||||
LOG_ERR(" y: %08x", esf->y);
|
||||
if (IS_ENABLED(CONFIG_EXTRA_EXCEPTION_INFO)) {
|
||||
print_all(esf);
|
||||
} else {
|
||||
print_special_registers(esf);
|
||||
}
|
||||
}
|
||||
|
||||
z_fatal_error(reason, esf);
|
||||
CODE_UNREACHABLE;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,13 @@ GTEXT(__sparc_trap_fault)
|
|||
SECTION_FUNC(TEXT, __sparc_trap_except_reason)
|
||||
mov %g1, %l7
|
||||
.Ldoit:
|
||||
/* %g2, %g3 are used at manual window overflow so save temporarily */
|
||||
mov %g2, %l4
|
||||
mov %g3, %l5
|
||||
|
||||
/* We may have trapped into the invalid window. If so, make it valid. */
|
||||
rd %wim, %g2
|
||||
mov %g2, %l3
|
||||
srl %g2, %l0, %g3
|
||||
cmp %g3, 1
|
||||
bne .Lwodone
|
||||
|
@ -63,6 +68,9 @@ SECTION_FUNC(TEXT, __sparc_trap_except_reason)
|
|||
restore
|
||||
|
||||
.Lwodone:
|
||||
mov %l4, %g2
|
||||
mov %l5, %g3
|
||||
|
||||
/* Allocate an ABI stack frame and exception stack frame */
|
||||
sub %fp, 96 + __z_arch_esf_t_SIZEOF, %sp
|
||||
/*
|
||||
|
@ -72,13 +80,20 @@ SECTION_FUNC(TEXT, __sparc_trap_except_reason)
|
|||
|
||||
mov %l7, %o0
|
||||
/* Fill in the content of the exception stack frame */
|
||||
st %l1, [%sp + 96 + __z_arch_esf_t_pc_OFFSET]
|
||||
st %l2, [%sp + 96 + __z_arch_esf_t_npc_OFFSET]
|
||||
st %l0, [%sp + 96 + __z_arch_esf_t_psr_OFFSET]
|
||||
st %l6, [%sp + 96 + __z_arch_esf_t_tbr_OFFSET]
|
||||
st %fp, [%sp + 96 + __z_arch_esf_t_sp_OFFSET]
|
||||
rd %y, %g1
|
||||
st %g1, [%sp + 96 + __z_arch_esf_t_y_OFFSET]
|
||||
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
|
||||
std %i0, [%sp + 96 + __z_arch_esf_t_out_OFFSET + 0x00]
|
||||
std %i2, [%sp + 96 + __z_arch_esf_t_out_OFFSET + 0x08]
|
||||
std %i4, [%sp + 96 + __z_arch_esf_t_out_OFFSET + 0x10]
|
||||
std %i6, [%sp + 96 + __z_arch_esf_t_out_OFFSET + 0x18]
|
||||
std %g0, [%sp + 96 + __z_arch_esf_t_global_OFFSET + 0x00]
|
||||
std %g2, [%sp + 96 + __z_arch_esf_t_global_OFFSET + 0x08]
|
||||
std %g4, [%sp + 96 + __z_arch_esf_t_global_OFFSET + 0x10]
|
||||
std %g6, [%sp + 96 + __z_arch_esf_t_global_OFFSET + 0x18]
|
||||
#endif
|
||||
std %l0, [%sp + 96 + __z_arch_esf_t_psr_OFFSET] /* psr pc */
|
||||
std %l2, [%sp + 96 + __z_arch_esf_t_npc_OFFSET] /* npc wim */
|
||||
rd %y, %l7
|
||||
std %l6, [%sp + 96 + __z_arch_esf_t_tbr_OFFSET] /* tbr y */
|
||||
|
||||
/* Enable traps, raise PIL to mask all maskable interrupts. */
|
||||
or %l0, PSR_PIL, %o2
|
||||
|
@ -86,6 +101,17 @@ SECTION_FUNC(TEXT, __sparc_trap_except_reason)
|
|||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
|
||||
/* Flush all register windows to the stack. */
|
||||
.rept CONFIG_SPARC_NWIN-1
|
||||
save %sp, -64, %sp
|
||||
.endr
|
||||
.rept CONFIG_SPARC_NWIN-1
|
||||
restore
|
||||
.endr
|
||||
#endif
|
||||
|
||||
/*
|
||||
* reason is the first argument.
|
||||
* Exception stack frame prepared earlier is the second argument.
|
||||
|
|
|
@ -39,11 +39,13 @@ GEN_OFFSET_SYM(_callee_saved_t, o6);
|
|||
GEN_OFFSET_SYM(_callee_saved_t, o7);
|
||||
|
||||
/* esf member offsets */
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, out);
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, global);
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, pc);
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, npc);
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, psr);
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, wim);
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, tbr);
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, sp);
|
||||
GEN_OFFSET_SYM(z_arch_esf_t, y);
|
||||
GEN_ABSOLUTE_SYM(__z_arch_esf_t_SIZEOF, STACK_ROUND_UP(sizeof(z_arch_esf_t)));
|
||||
|
||||
|
|
|
@ -8,3 +8,4 @@ CONFIG_SERIAL=y
|
|||
CONFIG_UART_APBUART=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_LEON_GPTIMER=y
|
||||
CONFIG_EXTRA_EXCEPTION_INFO=y
|
||||
|
|
|
@ -8,4 +8,5 @@ CONFIG_SERIAL=y
|
|||
CONFIG_UART_APBUART=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_LEON_GPTIMER=y
|
||||
CONFIG_EXTRA_EXCEPTION_INFO=y
|
||||
CONFIG_QEMU_ICOUNT_SHIFT=6
|
||||
|
|
|
@ -102,11 +102,13 @@ static inline uint32_t arch_k_cycle_get_32(void)
|
|||
|
||||
|
||||
struct __esf {
|
||||
uint32_t out[8];
|
||||
uint32_t global[8];
|
||||
uint32_t psr;
|
||||
uint32_t pc;
|
||||
uint32_t npc;
|
||||
uint32_t psr;
|
||||
uint32_t wim;
|
||||
uint32_t tbr;
|
||||
uint32_t sp;
|
||||
uint32_t y;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue