| /* |
| * Copyright (c) 2019-2020 Cobham Gaisler AB |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/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 investigating 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 opposite. |
| */ |
| |
| |
| /* |
| * 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" registers 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 == 0U && pc == 0U) { |
| break; |
| } |
| LOG_ERR(" #%-2d %08x %08x", i, pc, sp); |
| if (sp == 0U || sp & 7U) { |
| 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) { |
| if (IS_ENABLED(CONFIG_EXTRA_EXCEPTION_INFO)) { |
| print_all(esf); |
| } else { |
| print_special_registers(esf); |
| } |
| } |
| z_fatal_error(reason, esf); |
| CODE_UNREACHABLE; |
| } |