Skip to content

Commit 83f733c

Browse files
tbr-ttioannisg
authored andcommitted
SPARC: improve fatal log
The fatal log now contains - Trap type in human readable representation - Integer registers visible to the program when trap was taken - Special register values such as PC and PSR - Backtrace with PC and SP If CONFIG_EXTRA_EXCEPTION_INFO is enabled, then all the above is logged. If not, only the special registers are logged. The format is inspired by the GRMON debug monitor and TSIM simulator. A quick guide on how to use the values is in fatal.c. It now looks like this: E: tt = 0x02, illegal_instruction E: E: INS LOCALS OUTS GLOBALS E: 0: 00000000 f3900fc0 40007c50 00000000 E: 1: 00000000 40004bf0 40008d30 40008c00 E: 2: 00000000 40004bf4 40008000 00000003 E: 3: 40009158 00000000 40009000 00000002 E: 4: 40008fa8 40003c00 40008fa8 00000008 E: 5: 40009000 f3400fc0 00000000 00000080 E: 6: 4000a1f8 40000050 4000a190 00000000 E: 7: 40002308 00000000 40001fb8 000000c1 E: E: psr: f30000c7 wim: 00000008 tbr: 40000020 y: 00000000 E: pc: 4000a1f4 npc: 4000a1f8 E: E: pc sp E: #0 4000a1f4 4000a190 E: #1 40002308 4000a1f8 E: #2 40003b24 4000a258 Signed-off-by: Martin Åberg <[email protected]>
1 parent c2b1e8d commit 83f733c

File tree

7 files changed

+243
-17
lines changed

7 files changed

+243
-17
lines changed

arch/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ config SPARC
4646
select ATOMIC_OPERATIONS_BUILTIN if SPARC_CASA
4747
select ATOMIC_OPERATIONS_C if !SPARC_CASA
4848
select ARCH_HAS_THREAD_LOCAL_STORAGE
49+
select ARCH_HAS_EXTRA_EXCEPTION_INFO
4950
help
5051
SPARC architecture
5152

arch/sparc/core/fatal.c

Lines changed: 200 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,211 @@
88
#include <logging/log.h>
99
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
1010

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+
11206
FUNC_NORETURN void z_sparc_fatal_error(unsigned int reason,
12207
const z_arch_esf_t *esf)
13208
{
14209
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+
}
21215
}
22-
23216
z_fatal_error(reason, esf);
24217
CODE_UNREACHABLE;
25218
}

arch/sparc/core/fault_trap.S

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,13 @@ GTEXT(__sparc_trap_fault)
3131
SECTION_FUNC(TEXT, __sparc_trap_except_reason)
3232
mov %g1, %l7
3333
.Ldoit:
34+
/* %g2, %g3 are used at manual window overflow so save temporarily */
35+
mov %g2, %l4
36+
mov %g3, %l5
37+
3438
/* We may have trapped into the invalid window. If so, make it valid. */
3539
rd %wim, %g2
40+
mov %g2, %l3
3641
srl %g2, %l0, %g3
3742
cmp %g3, 1
3843
bne .Lwodone
@@ -63,6 +68,9 @@ SECTION_FUNC(TEXT, __sparc_trap_except_reason)
6368
restore
6469

6570
.Lwodone:
71+
mov %l4, %g2
72+
mov %l5, %g3
73+
6674
/* Allocate an ABI stack frame and exception stack frame */
6775
sub %fp, 96 + __z_arch_esf_t_SIZEOF, %sp
6876
/*
@@ -72,20 +80,38 @@ SECTION_FUNC(TEXT, __sparc_trap_except_reason)
7280

7381
mov %l7, %o0
7482
/* Fill in the content of the exception stack frame */
75-
st %l1, [%sp + 96 + __z_arch_esf_t_pc_OFFSET]
76-
st %l2, [%sp + 96 + __z_arch_esf_t_npc_OFFSET]
77-
st %l0, [%sp + 96 + __z_arch_esf_t_psr_OFFSET]
78-
st %l6, [%sp + 96 + __z_arch_esf_t_tbr_OFFSET]
79-
st %fp, [%sp + 96 + __z_arch_esf_t_sp_OFFSET]
80-
rd %y, %g1
81-
st %g1, [%sp + 96 + __z_arch_esf_t_y_OFFSET]
83+
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
84+
std %i0, [%sp + 96 + __z_arch_esf_t_out_OFFSET + 0x00]
85+
std %i2, [%sp + 96 + __z_arch_esf_t_out_OFFSET + 0x08]
86+
std %i4, [%sp + 96 + __z_arch_esf_t_out_OFFSET + 0x10]
87+
std %i6, [%sp + 96 + __z_arch_esf_t_out_OFFSET + 0x18]
88+
std %g0, [%sp + 96 + __z_arch_esf_t_global_OFFSET + 0x00]
89+
std %g2, [%sp + 96 + __z_arch_esf_t_global_OFFSET + 0x08]
90+
std %g4, [%sp + 96 + __z_arch_esf_t_global_OFFSET + 0x10]
91+
std %g6, [%sp + 96 + __z_arch_esf_t_global_OFFSET + 0x18]
92+
#endif
93+
std %l0, [%sp + 96 + __z_arch_esf_t_psr_OFFSET] /* psr pc */
94+
std %l2, [%sp + 96 + __z_arch_esf_t_npc_OFFSET] /* npc wim */
95+
rd %y, %l7
96+
std %l6, [%sp + 96 + __z_arch_esf_t_tbr_OFFSET] /* tbr y */
8297

8398
/* Enable traps, raise PIL to mask all maskable interrupts. */
8499
or %l0, PSR_PIL, %o2
85100
wr %o2, PSR_ET, %psr
86101
nop
87102
nop
88103
nop
104+
105+
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
106+
/* Flush all register windows to the stack. */
107+
.rept CONFIG_SPARC_NWIN-1
108+
save %sp, -64, %sp
109+
.endr
110+
.rept CONFIG_SPARC_NWIN-1
111+
restore
112+
.endr
113+
#endif
114+
89115
/*
90116
* reason is the first argument.
91117
* Exception stack frame prepared earlier is the second argument.

arch/sparc/core/offsets/offsets.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ GEN_OFFSET_SYM(_callee_saved_t, o6);
3939
GEN_OFFSET_SYM(_callee_saved_t, o7);
4040

4141
/* esf member offsets */
42+
GEN_OFFSET_SYM(z_arch_esf_t, out);
43+
GEN_OFFSET_SYM(z_arch_esf_t, global);
4244
GEN_OFFSET_SYM(z_arch_esf_t, pc);
4345
GEN_OFFSET_SYM(z_arch_esf_t, npc);
4446
GEN_OFFSET_SYM(z_arch_esf_t, psr);
47+
GEN_OFFSET_SYM(z_arch_esf_t, wim);
4548
GEN_OFFSET_SYM(z_arch_esf_t, tbr);
46-
GEN_OFFSET_SYM(z_arch_esf_t, sp);
4749
GEN_OFFSET_SYM(z_arch_esf_t, y);
4850
GEN_ABSOLUTE_SYM(__z_arch_esf_t_SIZEOF, STACK_ROUND_UP(sizeof(z_arch_esf_t)));
4951

boards/sparc/generic_leon3/generic_leon3_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ CONFIG_SERIAL=y
88
CONFIG_UART_APBUART=y
99
CONFIG_UART_CONSOLE=y
1010
CONFIG_LEON_GPTIMER=y
11+
CONFIG_EXTRA_EXCEPTION_INFO=y

boards/sparc/qemu_leon3/qemu_leon3_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ CONFIG_SERIAL=y
88
CONFIG_UART_APBUART=y
99
CONFIG_UART_CONSOLE=y
1010
CONFIG_LEON_GPTIMER=y
11+
CONFIG_EXTRA_EXCEPTION_INFO=y
1112
CONFIG_QEMU_ICOUNT_SHIFT=6

include/arch/sparc/arch.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,13 @@ static inline uint32_t arch_k_cycle_get_32(void)
102102

103103

104104
struct __esf {
105+
uint32_t out[8];
106+
uint32_t global[8];
107+
uint32_t psr;
105108
uint32_t pc;
106109
uint32_t npc;
107-
uint32_t psr;
110+
uint32_t wim;
108111
uint32_t tbr;
109-
uint32_t sp;
110112
uint32_t y;
111113
};
112114

0 commit comments

Comments
 (0)