| 
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