sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
crashscreen.c
1#include <kernel.h>
2#include <deci2.h>
3#include <stddef.h>
4#include <unistd.h>
5#include <stdint.h>
6#include <crashscreen.h>
7
8#include "crash_font.h"
9
10#define COLS 76
11#define LINES_MAX 30
12#define REPORT_SIZE 3072
13#define BT_MAX 24
14#define STACK_SCAN_BYTES 16384
15#define FB_WIDTH 640
16#define UNCACHED(p) ((uintptr_t)(p) | 0x20000000u)
17
19 uint32_t gpr[32];
20 uint32_t status;
21 uint32_t cause;
22 uint32_t epc;
23 uint32_t badvaddr;
24};
25
26struct crash_frame __crash_frame;
27uint8_t __crash_stack[16384] __attribute__((aligned(16)));
28
29extern void __crash_vector(void);
30extern char _ftext[];
31extern char _etext[];
32
33typedef void (*terminate_fn)(void);
34extern terminate_fn std_set_terminate(terminate_fn) __asm__("_ZSt13set_terminatePFvvE") __attribute__((weak));
35extern void *__cxa_current_primary_exception(void) __attribute__((weak));
36extern const void *__cxa_current_exception_type(void) __attribute__((weak));
37extern const char _ZTISt9exception[] __attribute__((weak));
38extern int _Unwind_Backtrace(int (*)(void *, void *), void *) __attribute__((weak));
39extern uintptr_t _Unwind_GetIP(void *) __attribute__((weak));
40
41static const char *const exception_names[16] = {
42 "Interrupt", "TLB modification", "TLB miss on load or fetch", "TLB miss on store",
43 "Address error on load or fetch (AdEL)", "Address error on store (AdES)",
44 "Bus error on fetch (IBE)", "Bus error on data (DBE)", "Syscall", "Breakpoint (BREAK)",
45 "Reserved instruction (RI)", "Coprocessor unusable (CpU)", "Arithmetic overflow (Ov)", "Trap",
46 "Unknown", "Unknown"
47};
48
49static const char *const gpr_names[32] = {
50 "zr", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
51 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
52};
53
54static const int hooked_causes[] = {1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13};
55
56static void *old_handlers[16];
57static int installed;
58static terminate_fn old_terminate;
59static crashscreen_hook_t user_hook;
60static int in_crash;
61static int chain_previous;
62
63extern void __crash_chain(const struct crash_frame *f);
64
65static char report[REPORT_SIZE];
66static int report_len;
67static uint32_t bt[BT_MAX];
68static int bt_count;
69
70static uint32_t pkt[(6 + FB_WIDTH) * 4] __attribute__((aligned(16)));
71
72static void rc(char c)
73{
74 if (report_len < REPORT_SIZE - 1)
75 report[report_len++] = c;
76}
77
78static void rs(const char *s)
79{
80 while (s && *s)
81 rc(*s++);
82}
83
84static void rhex(uint32_t v)
85{
86 for (int i = 28; i >= 0; i -= 4)
87 rc("0123456789ABCDEF"[(v >> i) & 15]);
88}
89
90static int addr_in_ram(uint32_t a)
91{
92 return a >= 0x00100000u && a < 0x02000000u;
93}
94
95static int in_text(uint32_t a)
96{
97 return a >= (uint32_t)_ftext && a < (uint32_t)_etext;
98}
99
100static void bt_add(uint32_t a)
101{
102 if (bt_count < BT_MAX)
103 bt[bt_count++] = a;
104}
105
106static int looks_like_return(uint32_t a)
107{
108 if (!in_text(a) || (a & 3) || !in_text(a - 8))
109 return 0;
110 uint32_t insn = *(const uint32_t *)(a - 8);
111 return (insn >> 26) == 3 || (insn & 0xFC00003Fu) == 0x00000009u;
112}
113
114static void scan_stack(uint32_t sp)
115{
116 if (!addr_in_ram(sp))
117 return;
118 uint32_t end = sp + STACK_SCAN_BYTES;
119 if (end > 0x02000000u)
120 end = 0x02000000u;
121 for (uint32_t p = sp & ~3u; p < end && bt_count < BT_MAX; p += 4) {
122 uint32_t v = *(const uint32_t *)p;
123 if (looks_like_return(v))
124 bt_add(v);
125 }
126}
127
128static int unwind_cb(void *ctx, void *arg)
129{
130 (void)arg;
131 bt_add((uint32_t)_Unwind_GetIP(ctx));
132 return 0;
133}
134
135static void emit_report(const char *reason, const char *detail, const struct crash_frame *f, uint32_t sp)
136{
137 report_len = 0;
138 rs("PROGRAM CRASHED\n");
139 rs("Reason: ");
140 rs(reason);
141 rc('\n');
142 if (detail && *detail) {
143 rs(detail);
144 rc('\n');
145 }
146 rc('\n');
147 if (f) {
148 rs("PC=");
149 rhex(f->epc);
150 rs(" RA=");
151 rhex(f->gpr[31]);
152 rs(" SP=");
153 rhex(f->gpr[29]);
154 rs("\nBadVAddr=");
155 rhex(f->badvaddr);
156 rs(" Cause=");
157 rhex(f->cause);
158 rs(" Status=");
159 rhex(f->status);
160 rs("\n\n");
161 for (int i = 1; i < 32; i++) {
162 rs(gpr_names[i]);
163 rc('=');
164 rhex(f->gpr[i]);
165 rc((i % 6) == 0 || i == 31 ? '\n' : ' ');
166 }
167 } else {
168 rs("SP=");
169 rhex(sp);
170 rc('\n');
171 }
172 rs("\nBacktrace (return addresses):\n");
173 for (int i = 0; i < bt_count; i++) {
174 rhex(bt[i]);
175 rc((i % 8) == 7 || i == bt_count - 1 ? '\n' : ' ');
176 }
177 if (bt_count == 0)
178 rs("none found\n");
179 rs("Resolve with: ps2symbolize <elf> <address...>\n");
180 report[report_len] = 0;
181}
182
183static void send_console(void)
184{
185 char line[COLS + 4];
186 int n = 0;
187 kputs("\n");
188 for (int i = 0; i <= report_len; i++) {
189 char c = report[i];
190 if (c == '\n' || c == 0 || n >= COLS) {
191 line[n++] = '\n';
192 line[n] = 0;
193 kputs(line);
194 n = 0;
195 if (c != '\n' && c != 0)
196 line[n++] = c;
197 } else {
198 line[n++] = c;
199 }
200 }
201}
202
203static void send_stdout(void)
204{
205 uint32_t st;
206 __asm__ volatile("mfc0 %0, $12" : "=r"(st));
207 __asm__ volatile("mtc0 %0, $12\n\tsync.p" ::"r"(st | 1u | (1u << 16)));
208 write(1, report, report_len);
209 write(1, "\n", 1);
210 __asm__ volatile("mtc0 %0, $12\n\tsync.p" ::"r"(st));
211}
212
213static void gs_setup(int pal)
214{
215 volatile uint64_t *gs = (volatile uint64_t *)0x12000000;
216 volatile uint32_t *dma = (volatile uint32_t *)0x1000A000;
217 int height = pal ? 256 : 224;
218
219 dma[0] = 0;
220 *(volatile uint32_t *)0x1000E000 = 1;
221 *(volatile uint32_t *)0x10003000 = 1;
222 *(volatile uint32_t *)0x10003000 = 0;
223 *(volatile uint64_t *)0x12001000 = 0x200;
224 __asm__ volatile("sync.p");
225 *(volatile uint64_t *)0x12001000 = 0;
226 GsPutIMR(0xFF00);
227 SetGsCrt(0, pal ? 3 : 2, 0);
228
229 gs[0x00 / 8] = 0x8046;
230 uint64_t dispfb = (uint64_t)(FB_WIDTH / 64) << 9 | (uint64_t)2 << 15;
231 uint64_t display = (uint64_t)(pal ? 656 : 636) | (uint64_t)(pal ? 36 : 25) << 12 | (uint64_t)3 << 23 |
232 (uint64_t)2559 << 32 | (uint64_t)(height - 1) << 44;
233 gs[0x70 / 8] = dispfb;
234 gs[0x80 / 8] = display;
235 gs[0x90 / 8] = dispfb;
236 gs[0xA0 / 8] = display;
237 gs[0xE0 / 8] = 0;
238}
239
240static void gs_send_row(int y, const char *text, int len, int color_red)
241{
242 volatile uint32_t *q = (volatile uint32_t *)UNCACHED(pkt);
243 volatile uint16_t *px = (volatile uint16_t *)UNCACHED(&pkt[6 * 4]);
244 volatile uint32_t *dma = (volatile uint32_t *)0x1000A000;
245
246 q[0] = 4 | (1 << 15);
247 q[1] = 0x10000000;
248 q[2] = 0xE;
249 q[3] = 0;
250 q[4] = 0;
251 q[5] = (10u << 16) | (2u << 24);
252 q[6] = 0x50;
253 q[7] = 0;
254 q[8] = 0;
255 q[9] = (uint32_t)y << 16;
256 q[10] = 0x51;
257 q[11] = 0;
258 q[12] = FB_WIDTH;
259 q[13] = 8;
260 q[14] = 0x52;
261 q[15] = 0;
262 q[16] = 0;
263 q[17] = 0;
264 q[18] = 0x53;
265 q[19] = 0;
266 q[20] = FB_WIDTH | (1 << 15);
267 q[21] = 2u << 26;
268 q[22] = 0;
269 q[23] = 0;
270
271 for (int i = 0; i < FB_WIDTH * 8; i++)
272 px[i] = 0x8000;
273 uint16_t color = color_red ? 0x801F : 0xFFFF;
274 for (int k = 0; k < len; k++) {
275 unsigned char c = (unsigned char)text[k];
276 if (c < 32 || c > 126)
277 c = '?';
278 const unsigned char *g = crash_font[c - 32];
279 int x0 = 16 + k * 8;
280 for (int row = 0; row < 8; row++)
281 for (int b = 0; b < 8; b++)
282 if ((g[row] >> b) & 1)
283 px[row * FB_WIDTH + x0 + b] = color;
284 }
285
286 __asm__ volatile("sync.l" ::: "memory");
287 dma[0x20 / 4] = 6 + FB_WIDTH;
288 dma[0x10 / 4] = (uint32_t)((uintptr_t)pkt & 0x0FFFFFFF);
289 dma[0] = 0x101;
290 for (int t = 0; t < 20000000 && (dma[0] & 0x100); t++)
291 ;
292}
293
294static void draw_report(int pal)
295{
296 int height = pal ? 256 : 224;
297 int pixel_rows = height / 8;
298 int text_rows = pixel_rows - 2;
299 char line[COLS + 1];
300 int pos = 0;
301
302 uint32_t d_ctrl = *(volatile uint32_t *)0x1000E000;
303
304 gs_setup(pal);
305 for (int r = 0; r < pixel_rows; r++) {
306 int len = 0;
307 int red = (r == 1);
308 if (r >= 1 && r <= text_rows) {
309 while (pos < report_len && report[pos] != '\n' && len < COLS)
310 line[len++] = report[pos++];
311 if (pos < report_len && report[pos] == '\n')
312 pos++;
313 }
314 gs_send_row(r * 8, line, len, red);
315 }
316 *(volatile uint32_t *)0x1000E010 = 1u << 2;
317 *(volatile uint32_t *)0x1000E000 = d_ctrl;
318}
319
320static int console_is_pal(void)
321{
322 return *(volatile char *)0xBFC7FF52 == 'E';
323}
324
325static void quiesce(void)
326{
327 uint32_t st;
328 __asm__ volatile("mfc0 %0, $12" : "=r"(st));
329 st &= ~((1u << 0) | (1u << 1) | (3u << 3) | (1u << 16));
330 __asm__ volatile("mtc0 %0, $12\n\tsync.p" ::"r"(st));
331}
332
333void __crash_sleep(void)
334{
335 for (;;)
336 SleepThread();
337}
338
339extern void __crash_leave(void (*fn)(void));
340
341static void idle(void)
342{
343 __crash_leave(__crash_sleep);
344}
345
346static void finish(const char *reason, const char *detail, const struct crash_frame *f, uint32_t sp)
347{
348 if (in_crash)
349 for (;;)
350 ;
351 in_crash = 1;
352 quiesce();
353 emit_report(reason, detail, f, sp);
354 draw_report(console_is_pal());
355 send_console();
356 send_stdout();
357 if (user_hook)
358 user_hook(report);
359 if (chain_previous && f) {
360 int c = (f->cause >> 2) & 15;
361 if (old_handlers[c]) {
362 if (c <= 3)
363 SetVTLBRefillHandler(c, old_handlers[c]);
364 else
365 SetVCommonHandler(c, old_handlers[c]);
366 FlushCache(0);
367 FlushCache(2);
368 __crash_chain(f);
369 }
370 }
371 idle();
372}
373
374void __crash_cpu_exception(struct crash_frame *f)
375{
376 int code = (f->cause >> 2) & 31;
377 bt_count = 0;
378 if (looks_like_return(f->gpr[31]))
379 bt_add(f->gpr[31]);
380 scan_stack(f->gpr[29]);
381 finish(exception_names[code & 15], NULL, f, f->gpr[29]);
382}
383
384static const char *exception_what(char *name_out, size_t name_size)
385{
386 name_out[0] = 0;
387 if (!__cxa_current_primary_exception || !__cxa_current_exception_type)
388 return NULL;
389 const void *type = __cxa_current_exception_type();
390 void *obj = __cxa_current_primary_exception();
391 if (!type)
392 return NULL;
393 const char *tn = *(const char *const *)((const char *)type + 4);
394 if (addr_in_ram((uint32_t)tn)) {
395 size_t i = 0;
396 for (; tn[i] && i + 1 < name_size; i++)
397 name_out[i] = tn[i];
398 name_out[i] = 0;
399 }
400 if (!obj || !_ZTISt9exception || !addr_in_ram((uint32_t)obj))
401 return NULL;
402 const uint32_t *t = (const uint32_t *)type;
403 for (int depth = 0; t != (const uint32_t *)_ZTISt9exception; depth++) {
404 if (depth == 8 || !addr_in_ram(t[2]) || !addr_in_ram(((const uint32_t *)t[2])[1]))
405 return NULL;
406 t = (const uint32_t *)t[2];
407 }
408 void **svptr = *(void ***)obj;
409 if (!addr_in_ram((uint32_t)svptr))
410 return NULL;
411 const char *(*what)(void *) = (const char *(*)(void *))svptr[2];
412 return what(obj);
413}
414
415static void terminate_handler(void)
416{
417 char name[96];
418 char detail[200];
419 int n = 0;
420 uint32_t sp;
421 __asm__ volatile("move %0, $29" : "=r"(sp));
422
423 const char *what = exception_what(name, sizeof(name));
424 const char *prefix = name[0] ? "Type: " : "";
425 for (const char *s = prefix; *s && n < (int)sizeof(detail) - 1; s++)
426 detail[n++] = *s;
427 for (const char *s = name; *s && n < (int)sizeof(detail) - 1; s++)
428 detail[n++] = *s;
429 if (what) {
430 const char *sep = name[0] ? " what(): " : "what(): ";
431 for (const char *s = sep; *s && n < (int)sizeof(detail) - 1; s++)
432 detail[n++] = *s;
433 for (int i = 0; what[i] && i < 120 && n < (int)sizeof(detail) - 1; i++)
434 detail[n++] = what[i];
435 }
436 detail[n] = 0;
437
438 bt_count = 0;
439 if (_Unwind_Backtrace && _Unwind_GetIP) {
440 _Unwind_Backtrace(unwind_cb, NULL);
441 if (bt_count > 0) {
442 for (int i = 1; i < bt_count; i++)
443 bt[i - 1] = bt[i];
444 bt_count--;
445 }
446 }
447 if (bt_count == 0)
448 scan_stack(sp);
449 finish("std::terminate (uncaught exception)", detail, NULL, sp);
450}
451
452void crashscreen_abort(const char *reason)
453{
454 uint32_t sp;
455 __asm__ volatile("move %0, $29" : "=r"(sp));
456 bt_count = 0;
457 scan_stack(sp);
458 finish(reason ? reason : "crashscreen_abort", NULL, NULL, sp);
459 for (;;)
460 ;
461}
462
463void crashscreen_set_chain(int enable)
464{
465 chain_previous = enable;
466}
467
469{
470 user_hook = hook;
471}
472
474{
475 if (installed)
476 return;
477 for (unsigned i = 0; i < sizeof(hooked_causes) / sizeof(hooked_causes[0]); i++) {
478 int c = hooked_causes[i];
479 old_handlers[c] = GetExceptionHandler(c);
480 if (c <= 3)
481 SetVTLBRefillHandler(c, __crash_vector);
482 else
483 SetVCommonHandler(c, __crash_vector);
484 }
485 if (std_set_terminate)
486 old_terminate = std_set_terminate(terminate_handler);
487 FlushCache(0);
488 FlushCache(2);
489 installed = 1;
490}
491
493{
494 if (!installed)
495 return;
496 for (unsigned i = 0; i < sizeof(hooked_causes) / sizeof(hooked_causes[0]); i++) {
497 int c = hooked_causes[i];
498 if (!old_handlers[c])
499 continue;
500 if (c <= 3)
501 SetVTLBRefillHandler(c, old_handlers[c]);
502 else
503 SetVCommonHandler(c, old_handlers[c]);
504 old_handlers[c] = NULL;
505 }
506 if (std_set_terminate)
507 std_set_terminate(old_terminate);
508 FlushCache(0);
509 FlushCache(2);
510 installed = 0;
511}
void crashscreen_set_chain(int enable)
void crashscreen_abort(const char *reason) __attribute__((noreturn))
void crashscreen_set_hook(crashscreen_hook_t hook)
void crashscreen_remove(void)
void crashscreen_install(void)
void(*) crashscreen_hook_t(const char *report)
Definition crashscreen.h:15
void * GetExceptionHandler(int except_no)
Definition getkernel.c:74