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