1 /*
2 * Copyright (c) 2019-2020 Cobham Gaisler AB
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <kernel.h>
8 #include <logging/log.h>
9 LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
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
print_trap_type(const z_arch_esf_t * esf)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
print_integer_registers(const z_arch_esf_t * esf)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
print_special_registers(const z_arch_esf_t * esf)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
print_backtrace(const z_arch_esf_t * esf)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 == 0U && pc == 0U) {
183 break;
184 }
185 LOG_ERR(" #%-2d %08x %08x", i, pc, sp);
186 if (sp == 0U || sp & 7U) {
187 break;
188 }
189 s = (const struct savearea *) sp;
190 }
191 }
192
print_all(const z_arch_esf_t * esf)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
z_sparc_fatal_error(unsigned int reason,const z_arch_esf_t * esf)206 FUNC_NORETURN void z_sparc_fatal_error(unsigned int reason,
207 const z_arch_esf_t *esf)
208 {
209 if (esf != NULL) {
210 if (IS_ENABLED(CONFIG_EXTRA_EXCEPTION_INFO)) {
211 print_all(esf);
212 } else {
213 print_special_registers(esf);
214 }
215 }
216 z_fatal_error(reason, esf);
217 CODE_UNREACHABLE;
218 }
219