|
8 | 8 | #include <logging/log.h>
|
9 | 9 | LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
10 | 10 |
|
| 11 | +/* |
| 12 | + * EXAMPLE OUTPUT |
| 13 | + * |
| 14 | + * --------------------------------------------------------------------- |
| 15 | + * |
| 16 | + * tt = 0x02, illegal_instruction |
| 17 | + * |
| 18 | + * INS LOCALS OUTS GLOBALS |
| 19 | + * 0: 00000000 f3900fc0 40007c50 00000000 |
| 20 | + * 1: 00000000 40004bf0 40008d30 40008c00 |
| 21 | + * 2: 00000000 40004bf4 40008000 00000003 |
| 22 | + * 3: 40009158 00000000 40009000 00000002 |
| 23 | + * 4: 40008fa8 40003c00 40008fa8 00000008 |
| 24 | + * 5: 40009000 f3400fc0 00000000 00000080 |
| 25 | + * 6: 4000a1f8 40000050 4000a190 00000000 |
| 26 | + * 7: 40002308 00000000 40001fb8 000000c1 |
| 27 | + * |
| 28 | + * psr: f30000c7 wim: 00000008 tbr: 40000020 y: 00000000 |
| 29 | + * pc: 4000a1f4 npc: 4000a1f8 |
| 30 | + * |
| 31 | + * pc sp |
| 32 | + * #0 4000a1f4 4000a190 |
| 33 | + * #1 40002308 4000a1f8 |
| 34 | + * #2 40003b24 4000a258 |
| 35 | + * |
| 36 | + * --------------------------------------------------------------------- |
| 37 | + * |
| 38 | + * |
| 39 | + * INTERPRETATION |
| 40 | + * |
| 41 | + * INS, LOCALS, OUTS and GLOBALS represent the %i, %l, %o and %g |
| 42 | + * registers before the trap was taken. |
| 43 | + * |
| 44 | + * wim, y, pc and npc are the values before the trap was taken. |
| 45 | + * tbr has the tbr.tt field (bits 11..4) filled in by hardware |
| 46 | + * representing the current trap type. psr is read immediately |
| 47 | + * after the trap was taken so it will have the new CWP and ET=0. |
| 48 | + * |
| 49 | + * The "#i pc sp" rows is the stack backtrace. All register |
| 50 | + * windows are flushed to the stack prior to printing. First row |
| 51 | + * is the trapping pc and sp (o6). |
| 52 | + * |
| 53 | + * |
| 54 | + * HOW TO USE |
| 55 | + * |
| 56 | + * When invesetigating a crashed program, the first things to look |
| 57 | + * at is typically the tt, pc and sp (o6). You can lookup the pc |
| 58 | + * in the assembly list file or use addr2line. In the listing, the |
| 59 | + * register values in the table above can be used. The linker map |
| 60 | + * file will give a hint on which stack is active and if it has |
| 61 | + * overflowed. |
| 62 | + * |
| 63 | + * psr bits 11..8 is the processor interrupt (priority) level. 0 |
| 64 | + * is lowest priority level (all can be taken), and 0xf is the |
| 65 | + * highest level where only non-maskable interrupts are taken. |
| 66 | + * |
| 67 | + * g0 is always zero. g5, g6 are never accessed by the compiler. |
| 68 | + * g7 is the TLS pointer if enabled. A SAVE instruction decreases |
| 69 | + * the current window pointer (psr bits 4..0) which results in %o |
| 70 | + * registers becoming %i registers and a new set of %l registers |
| 71 | + * appear. RESTORE does the oppposite. |
| 72 | + */ |
| 73 | + |
| 74 | + |
| 75 | +/* |
| 76 | + * The SPARC V8 ABI guarantees that the stack pointer register |
| 77 | + * (o6) points to an area organized as "struct savearea" below at |
| 78 | + * all times when traps are enabled. This is the register save |
| 79 | + * area where register window registers can be flushed to the |
| 80 | + * stack. |
| 81 | + * |
| 82 | + * We flushed registers to this space in the fault trap entry |
| 83 | + * handler. Note that the space is allocated by the ABI (compiler) |
| 84 | + * for each stack frame. |
| 85 | + * |
| 86 | + * When printing the registers, we get the "local" and "in" |
| 87 | + * registers from the ABI stack save area, while the "out" and |
| 88 | + * "global" registares are taken from the exception stack frame |
| 89 | + * generated in the fault trap entry. |
| 90 | + */ |
| 91 | +struct savearea { |
| 92 | + uint32_t local[8]; |
| 93 | + uint32_t in[8]; |
| 94 | +}; |
| 95 | + |
| 96 | + |
| 97 | +/* |
| 98 | + * Exception trap type (tt) values according to The SPARC V8 |
| 99 | + * manual, Table 7-1. |
| 100 | + */ |
| 101 | +static const struct { |
| 102 | + int tt; |
| 103 | + const char *desc; |
| 104 | +} TTDESC[] = { |
| 105 | + { .tt = 0x02, .desc = "illegal_instruction", }, |
| 106 | + { .tt = 0x07, .desc = "mem_address_not_aligned", }, |
| 107 | + { .tt = 0x2B, .desc = "data_store_error", }, |
| 108 | + { .tt = 0x29, .desc = "data_access_error", }, |
| 109 | + { .tt = 0x09, .desc = "data_access_exception", }, |
| 110 | + { .tt = 0x21, .desc = "instruction_access_error", }, |
| 111 | + { .tt = 0x01, .desc = "instruction_access_exception", }, |
| 112 | + { .tt = 0x04, .desc = "fp_disabled", }, |
| 113 | + { .tt = 0x08, .desc = "fp_exception", }, |
| 114 | + { .tt = 0x2A, .desc = "division_by_zero", }, |
| 115 | + { .tt = 0x03, .desc = "privileged_instruction", }, |
| 116 | + { .tt = 0x20, .desc = "r_register_access_error", }, |
| 117 | + { .tt = 0x0B, .desc = "watchpoint_detected", }, |
| 118 | + { .tt = 0x2C, .desc = "data_access_MMU_miss", }, |
| 119 | + { .tt = 0x3C, .desc = "instruction_access_MMU_miss", }, |
| 120 | + { .tt = 0x05, .desc = "window_overflow", }, |
| 121 | + { .tt = 0x06, .desc = "window_underflow", }, |
| 122 | + { .tt = 0x0A, .desc = "tag_overflow", }, |
| 123 | +}; |
| 124 | + |
| 125 | +static void print_trap_type(const z_arch_esf_t *esf) |
| 126 | +{ |
| 127 | + const int tt = (esf->tbr & TBR_TT) >> TBR_TT_BIT; |
| 128 | + const char *desc = "unknown"; |
| 129 | + |
| 130 | + if (tt & 0x80) { |
| 131 | + desc = "trap_instruction"; |
| 132 | + } else if (tt >= 0x11 && tt <= 0x1F) { |
| 133 | + desc = "interrupt"; |
| 134 | + } else { |
| 135 | + for (int i = 0; i < ARRAY_SIZE(TTDESC); i++) { |
| 136 | + if (TTDESC[i].tt == tt) { |
| 137 | + desc = TTDESC[i].desc; |
| 138 | + break; |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + LOG_ERR("tt = 0x%02X, %s", tt, desc); |
| 143 | +} |
| 144 | + |
| 145 | +static void print_integer_registers(const z_arch_esf_t *esf) |
| 146 | +{ |
| 147 | + const struct savearea *flushed = (struct savearea *) esf->out[6]; |
| 148 | + |
| 149 | + LOG_ERR(" INS LOCALS OUTS GLOBALS"); |
| 150 | + for (int i = 0; i < 8; i++) { |
| 151 | + LOG_ERR( |
| 152 | + " %d: %08x %08x %08x %08x", |
| 153 | + i, |
| 154 | + flushed ? flushed->in[i] : 0, |
| 155 | + flushed ? flushed->local[i] : 0, |
| 156 | + esf->out[i], |
| 157 | + esf->global[i] |
| 158 | + ); |
| 159 | + } |
| 160 | +} |
| 161 | + |
| 162 | +static void print_special_registers(const z_arch_esf_t *esf) |
| 163 | +{ |
| 164 | + LOG_ERR( |
| 165 | + "psr: %08x wim: %08x tbr: %08x y: %08x", |
| 166 | + esf->psr, esf->wim, esf->tbr, esf->y |
| 167 | + ); |
| 168 | + LOG_ERR(" pc: %08x npc: %08x", esf->pc, esf->npc); |
| 169 | +} |
| 170 | + |
| 171 | +static void print_backtrace(const z_arch_esf_t *esf) |
| 172 | +{ |
| 173 | + const int MAX_LOGLINES = 40; |
| 174 | + const struct savearea *s = (struct savearea *) esf->out[6]; |
| 175 | + |
| 176 | + LOG_ERR(" pc sp"); |
| 177 | + LOG_ERR(" #0 %08x %08x", esf->pc, (unsigned int) s); |
| 178 | + for (int i = 1; s && i < MAX_LOGLINES; i++) { |
| 179 | + const uint32_t pc = s->in[7]; |
| 180 | + const uint32_t sp = s->in[6]; |
| 181 | + |
| 182 | + if (sp == 0 && pc == 0) { |
| 183 | + break; |
| 184 | + } |
| 185 | + LOG_ERR(" #%-2d %08x %08x", i, pc, sp); |
| 186 | + if (sp == 0 || sp & 7) { |
| 187 | + break; |
| 188 | + } |
| 189 | + s = (const struct savearea *) sp; |
| 190 | + } |
| 191 | +} |
| 192 | + |
| 193 | +static void print_all(const z_arch_esf_t *esf) |
| 194 | +{ |
| 195 | + LOG_ERR(""); |
| 196 | + print_trap_type(esf); |
| 197 | + LOG_ERR(""); |
| 198 | + print_integer_registers(esf); |
| 199 | + LOG_ERR(""); |
| 200 | + print_special_registers(esf); |
| 201 | + LOG_ERR(""); |
| 202 | + print_backtrace(esf); |
| 203 | + LOG_ERR(""); |
| 204 | +} |
| 205 | + |
11 | 206 | FUNC_NORETURN void z_sparc_fatal_error(unsigned int reason,
|
12 | 207 | const z_arch_esf_t *esf)
|
13 | 208 | {
|
14 | 209 | if (esf != NULL) {
|
15 |
| - LOG_ERR(" pc: %08x", esf->pc); |
16 |
| - LOG_ERR("npc: %08x", esf->npc); |
17 |
| - LOG_ERR("psr: %08x", esf->psr); |
18 |
| - LOG_ERR("tbr: %08x", esf->tbr); |
19 |
| - LOG_ERR(" sp: %08x", esf->sp); |
20 |
| - LOG_ERR(" y: %08x", esf->y); |
| 210 | + if (IS_ENABLED(CONFIG_EXTRA_EXCEPTION_INFO)) { |
| 211 | + print_all(esf); |
| 212 | + } else { |
| 213 | + print_special_registers(esf); |
| 214 | + } |
21 | 215 | }
|
22 |
| - |
23 | 216 | z_fatal_error(reason, esf);
|
24 | 217 | CODE_UNREACHABLE;
|
25 | 218 | }
|
0 commit comments