#include #include #include #include #include #include #include #include static void halt_forever(void) { arch_halt_forever(); } static volatile int g_panic_in_progress = 0; int panic_is_in_progress(void) { return g_panic_in_progress; } static unsigned fnv1a_hash(const char* a, const char* b2, int line) { unsigned int h = 2166136261u; if (a) { for (const char* p=a; *p; ++p) { h ^= (unsigned char)(*p); h *= 16777619u; } } if (b2) { for (const char* p=b2; *p; ++p) { h ^= (unsigned char)(*p); h *= 16777619u; } } h ^= (unsigned)line; h *= 16777619u; return h; } typedef enum { CAT_GENERIC=0, CAT_ASSERT, CAT_PAGING, CAT_FILESYSTEM, CAT_IRQ } panic_cat_t; static panic_cat_t classify_category(const char* title, const char* msg) { // Simple keyword scan; future: pass explicit reason codes const char* t = title ? title : ""; const char* m = msg ? msg : ""; if (strstr(t, "ASSERT") || strstr(m, "assert")) return CAT_ASSERT; if (strstr(m, "page fault") || strstr(m, "CR2") || strstr(m, "paging")) return CAT_PAGING; if (strstr(m, "EYNFS") || strstr(m, "FAT32") || strstr(m, "filesystem")) return CAT_FILESYSTEM; if (strstr(m, "IRQ") || strstr(m, "IDT") || strstr(m, "ISR")) return CAT_IRQ; return CAT_GENERIC; } static const char* category_label(panic_cat_t c) { switch(c) { case CAT_GENERIC: return "GENERAL"; case CAT_ASSERT: return "ASSERT"; case CAT_PAGING: return "PAGING"; case CAT_FILESYSTEM: return "FILESYSTEM"; case CAT_IRQ: return "IRQ"; default: return "GENERAL"; } } static void pick_bg_colour_for_category(panic_cat_t c, unsigned hash, int* pr, int* pg, int* pb) { (void)c; (void)hash; // Background colour *pr = 80; *pg = 80; *pb = 80; } static void render_bsod(const char* title, const char* msg, const char* file, int line) { // Suspend all interrupts immediately so no other UI updates race with BSOD arch_disable_interrupts(); // Make sure UI overlays and cursor exclusions don't interfere vga_clear_swap_exclude(); stop_shell_redirect(); // Hard reset any prior console/window artifacts clearScreen(); // Ensure the software backbuffer exists; safe no-op if already initialized vga_init_double_buffer(); vga_begin_frame(); // Choose a colour by category (sort of unused, currently only one category) int br=16, bg=32, bb=96; unsigned h = fnv1a_hash(msg, file, line); panic_cat_t cat = classify_category(title, msg); pick_bg_colour_for_category(cat, h, &br, &bg, &bb); // Determine full-screen dimensions from framebuffer extern multiboot_info_t *g_mbi; int sw = (g_mbi && g_mbi->framebuffer_width) ? (int)g_mbi->framebuffer_width : 640; int sh = (g_mbi && g_mbi->framebuffer_height) ? (int)g_mbi->framebuffer_height : 480; if (sw <= 0) sw = 640; if (sh <= 0) sh = 480; // Draw background to backbuffer and also paint directly to framebuffer so it's immediately visible drawRect(0, 0, sw, sh, br, bg, bb); for (int yy = 0; yy < sh; ++yy) { for (int xx = 0; xx < sw; ++xx) { vga_drawPixel_fb(xx, yy, br, bg, bb); } } // Title and summary drawTextAt(24, 24, "EYN-OS encountered a critical error.", 255, 255, 255); if (title && *title) { drawTextAt(24, 24 + 18, title, 255, 255, 255); } // Diagnostic lines (build text without relying on full snprintf formatting) char buf[256]; if (msg && *msg) { // Reason // "Reason: " + msg int bi = 0; const char* p = "Reason: "; while (*p && bi < 255) buf[bi++] = *p++; p = msg; while (*p && bi < 255) buf[bi++] = *p++; buf[bi] = '\0'; drawTextAt(24, 24 + 44, buf, 255, 255, 255); } if (file && *file) { // Location: file:line int bi = 0; const char* p = "Location: "; while (*p && bi < 255) buf[bi++] = *p++; p = file; while (*p && bi < 255) buf[bi++] = *p++; if (bi < 254) buf[bi++]=':'; char* ls = int_to_string(line); p = ls; while (*p && bi < 255) buf[bi++] = *p++; buf[bi] = '\0'; drawTextAt(24, 24 + 62, buf, 255, 255, 255); } // Stop code (manual hex formatting) - computed here for later diagnostics section char hex[9]; for (int i=0;i<8;i++){ int nib = (h >> ((7-i)*4)) & 0xF; hex[i] = (char)(nib < 10 ? ('0'+nib) : ('A'+(nib-10))); } hex[8]='\0'; // Right-side plain text diagnostics instead of QR code const char* cl = category_label(cat); // Place diagnostics beneath the summary block on the left int infoX = 24; int infoY = 24 + 132; // below Category line with extra spacing drawTextAt(infoX, infoY, "Diagnostics:", 255, 255, 255); infoY += 18; // Stop code line { int bi = 0; const char* p = "Stop code: EYNOS_"; while (*p && bi < 255) buf[bi++] = *p++; p = hex; while (*p && bi < 255) buf[bi++] = *p++; buf[bi] = '\0'; drawTextAt(infoX, infoY, buf, 255, 255, 255); infoY += 16; } // Category line { int bi = 0; const char* p = "Category: "; while (*p && bi < 255) buf[bi++] = *p++; const char* pcat = cl; while (*pcat && bi < 255) buf[bi++] = *pcat++; buf[bi] = '\0'; drawTextAt(infoX, infoY, buf, 255, 255, 255); infoY += 16; } // Source file line (shortened to last path segment) if (file && *file) { const char* last = file; for (const char* s = file; *s; ++s) { if (*s=='/' || *s=='\\') last = s+1; } int bi = 0; const char* p = "Source: "; while (*p && bi < 255) buf[bi++] = *p++; p = last; while (*p && bi < 255) buf[bi++] = *p++; if (bi < 254) { buf[bi++]=':'; } char* ls = int_to_string(line); p = ls; while (*p && bi < 255) buf[bi++] = *p++; buf[bi] = '\0'; drawTextAt(infoX, infoY, buf, 255, 255, 255); infoY += 16; } // Instruction: power button drawTextAt(24, sh - 66, "Please press the power button to turn off the machine.", 255, 255, 255); drawTextAt(24, sh - 48, "A backtrace has been written to the serial log (COM1).", 255, 255, 255); drawTextAt(24, sh - 30, "See docs/stop-codes.md to interpret stop codes.", 255, 255, 255); // Logo omitted // Make sure everything is considered dirty and pushed vga_mark_dirty_rect(0, 0, sw, sh); // Try to align with vblank to avoid tearing (best-effort) vga_wait_vblank(); vga_swap_buffers(); // And force-copy the entire backbuffer to framebuffer to fully override any UI remnants vga_blit_backbuffer_region_to_fb(0, 0, sw, sh); } // Minimal backtrace to serial only (avoid printf to keep BSOD pristine) static void backtrace_to_serial(void) { uint32_t ebp; __asm__ volatile ("movl %%ebp, %0" : "=r"(ebp)); serial_write(SERIAL_COM1, "Backtrace:\n", 11); for (int i = 0; i < 16 && ebp; ++i) { // Be defensive: a corrupted EBP chain can otherwise fault while panicking. if ((ebp & 0x3) != 0) break; // must be 4-byte aligned if (ebp < 0x1000) break; // never dereference near-null uint32_t* frame = (uint32_t*)ebp; uint32_t next_ebp = frame[0]; uint32_t ret = frame[1]; // Format: " #nn 0xXXXXXXXX\n" (no snprintf) char linebuf[32]; int pos = 0; linebuf[pos++] = ' '; linebuf[pos++] = ' '; linebuf[pos++] = '#'; linebuf[pos++] = (char)('0' + ((i / 10) % 10)); linebuf[pos++] = (char)('0' + (i % 10)); linebuf[pos++] = ' '; linebuf[pos++] = '0'; linebuf[pos++] = 'x'; for (int n = 7; n >= 0; --n) { uint32_t nib = (ret >> (n * 4)) & 0xFu; linebuf[pos++] = (char)((nib < 10) ? ('0' + nib) : ('A' + (nib - 10))); } linebuf[pos++] = '\n'; serial_write(SERIAL_COM1, linebuf, pos); // EBP should monotonically increase as we unwind (stack grows down). if (next_ebp <= ebp) break; // Prevent pathological jumps/loops. if ((next_ebp - ebp) > 0x10000) break; ebp = next_ebp; } } void panic(const char* msg, const char* file, int line) { if (g_panic_in_progress) { halt_forever(); } g_panic_in_progress = 1; // Render BSOD first render_bsod("KERNEL PANIC", msg, file, line); // Mirror to serial with details serial_write(SERIAL_COM1, "\nKERNEL PANIC: ", 15); if (msg) serial_write(SERIAL_COM1, msg, (int)strlen(msg)); serial_write(SERIAL_COM1, "\n", 1); if (file) { char loc[256]; snprintf(loc, sizeof(loc), "At %s:%d\n", file, line); serial_write(SERIAL_COM1, loc, (int)strlen(loc)); } backtrace_to_serial(); halt_forever(); } void panicf(const char* file, int line, const char* fmt, ...) { if (g_panic_in_progress) { halt_forever(); } g_panic_in_progress = 1; // Format the message once char buf[256]; va_list ap; va_start(ap, fmt); int pos = 0; const char* p = fmt; while (*p && pos < (int)sizeof(buf) - 1) { if (*p != '%') { buf[pos++] = *p++; continue; } // '%' sequence ++p; if (*p == '%') { buf[pos++] = '%'; ++p; continue; } // Optional zero-padding and width (supports %08X, %02d) char pad = ' '; int width = 0; if (*p == '0') { pad = '0'; ++p; } while (*p >= '0' && *p <= '9') { width = (width * 10) + (int)(*p - '0'); ++p; if (width > 32) { width = 32; break; } } char spec = *p ? *p++ : '\0'; if (spec == 's') { const char* s = va_arg(ap, const char*); if (!s) s = "(null)"; while (*s && pos < (int)sizeof(buf) - 1) buf[pos++] = *s++; continue; } if (spec == 'c') { buf[pos++] = (char)va_arg(ap, int); continue; } if (spec == 'd' || spec == 'u' || spec == 'x' || spec == 'X' || spec == 'p') { uint32_t v; int base = (spec == 'x' || spec == 'X' || spec == 'p') ? 16 : 10; int upper = (spec == 'X' || spec == 'p'); if (spec == 'd') { int iv = va_arg(ap, int); if (iv < 0) { if (pos < (int)sizeof(buf) - 1) buf[pos++] = '-'; v = (uint32_t)(-iv); } else { v = (uint32_t)iv; } } else if (spec == 'p') { v = (uint32_t)va_arg(ap, void*); if (width == 0) { width = 8; pad = '0'; } } else { v = va_arg(ap, unsigned); } char tmp[32]; int ti = 0; if (v == 0) { tmp[ti++] = '0'; } else { while (v && ti < (int)sizeof(tmp)) { uint32_t digit = (base == 16) ? (v & 0xFu) : (v % 10u); v = (base == 16) ? (v >> 4) : (v / 10u); if (digit < 10) tmp[ti++] = (char)('0' + digit); else tmp[ti++] = (char)((upper ? 'A' : 'a') + (digit - 10)); } } while (ti < width && pos < (int)sizeof(buf) - 1) { buf[pos++] = pad; --width; } while (ti-- > 0 && pos < (int)sizeof(buf) - 1) { buf[pos++] = tmp[ti]; } continue; } // Unknown specifier: print literally. if (pos < (int)sizeof(buf) - 1) buf[pos++] = '%'; if (pos < (int)sizeof(buf) - 1 && spec) buf[pos++] = spec; } buf[pos] = '\0'; va_end(ap); render_bsod("KERNEL PANIC", buf, file, line); // Serial mirror serial_write(SERIAL_COM1, "\nKERNEL PANIC: ", 15); serial_write(SERIAL_COM1, buf, (int)strlen(buf)); serial_write(SERIAL_COM1, "\n", 1); if (file) { char loc[256]; snprintf(loc, sizeof(loc), "At %s:%d\n", file, line); serial_write(SERIAL_COM1, loc, (int)strlen(loc)); } backtrace_to_serial(); halt_forever(); } void assert_fail(const char* expr, const char* file, int line) { render_bsod("ASSERTION FAILED", expr, file, line); serial_write(SERIAL_COM1, "Assertion failed: ", 18); if (expr) serial_write(SERIAL_COM1, expr, (int)strlen(expr)); serial_write(SERIAL_COM1, "\n", 1); if (file) { char loc[256]; snprintf(loc, sizeof(loc), "At %s:%d\n", file, line); serial_write(SERIAL_COM1, loc, (int)strlen(loc)); } backtrace_to_serial(); halt_forever(); }