/* * Panic.cpp * Copyright (c) 2025 Daniel Hammer */ #include "Panic.hpp" #include "../CppLib/BoxUI.hpp" #include "../Drivers/Graphics/IntelGPU.hpp" static constexpr int BoxWidth = 72; static void PrintHorizontalEdge(const char* left, const char* right) { kerr << left; for (int i = 0; i < BoxWidth - 2; ++i) kerr << BOXUI_H; kerr << right << "\n"; } static void PrintPageFaultInfo(System::PanicFrame*& frame) { auto* pf = (System::PageFaultPanicFrame*)frame; frame = (System::PanicFrame*)&pf->IP; uint64_t cr2; asm volatile("mov %%cr2, %0" : "=r"(cr2)); PrintBoxedHex(kerr, "Faulting Address (CR2)", cr2, BoxWidth); PrintBoxedLine(kerr, "Page Fault Error:", BoxWidth, true); PrintBoxedDec(kerr, "Present", pf->PageFaultError.Present, BoxWidth); PrintBoxedDec(kerr, "Write", pf->PageFaultError.Write, BoxWidth); PrintBoxedDec(kerr, "User", pf->PageFaultError.User, BoxWidth); PrintBoxedDec(kerr, "Reserved Write", pf->PageFaultError.ReservedWrite, BoxWidth); PrintBoxedDec(kerr, "Instruction Fetch",pf->PageFaultError.InstructionFetch, BoxWidth); PrintBoxedDec(kerr, "Protection Key", pf->PageFaultError.ProtectionKey, BoxWidth); PrintBoxedDec(kerr, "Shadow Stack", pf->PageFaultError.ShadowStack, BoxWidth); PrintBoxedDec(kerr, "SGX", pf->PageFaultError.SGX, BoxWidth); } static void PrintGPFInfo(System::PanicFrame*& frame) { auto* gpf = (System::GPFPanicFrame*)frame; frame = (System::PanicFrame*)&gpf->IP; PrintBoxedLine(kerr, "General Protection Fault:", BoxWidth, true); PrintBoxedDec(kerr, "Error Code", gpf->GeneralProtectionFaultError, BoxWidth); } // Link-time base of the kernel image (see linker-x86_64.ld). static constexpr uint64_t KernelBaseVirt = 0xFFFFFFFF80000000ULL; static void PrintRegisters(System::PanicFrame* frame) { PrintBoxedSeparator(kerr, BoxWidth); PrintBoxedLine(kerr, "Registers:", BoxWidth, true); PrintBoxedHex(kerr, "Instruction Pointer", frame->IP, BoxWidth); // The absolute address means nothing without the link map. Its offset from // the kernel's load base is what `nm bin-x86_64/kernel` can be searched // for directly, so print that next to it. if (frame->IP >= KernelBaseVirt) PrintBoxedHex(kerr, " kernel+", frame->IP - KernelBaseVirt, BoxWidth); PrintBoxedHex(kerr, "Code Segment", frame->CS, BoxWidth); PrintBoxedHex(kerr, "Flags", frame->Flags, BoxWidth); PrintBoxedHex(kerr, "Stack Pointer", frame->SP, BoxWidth); PrintBoxedHex(kerr, "Stack Segment", frame->SS, BoxWidth); } [[noreturn]] static void Halt() { while (true) { #if defined (__x86_64__) asm ("cli"); asm ("hlt"); #elif defined (__aarch64__) || defined (__riscv) asm ("wfi"); #elif defined (__loongarch64) asm ("idle 0"); #endif } } void Panic(const char *meditationString, System::PanicFrame* frame) { // Bring scanout back to buffer 0 first: if a page-flipped client was // showing the other buffer, the panic box would land on an invisible // surface. Safe no-op when the GPU driver is inactive. Drivers::Graphics::IntelGPU::PanicRestoreScanout(); Kt::EnablePanicOutput(); // Header kerr << BOXUI_ANSI_RED_BG << BOXUI_ANSI_WHITE_FG << BOXUI_ANSI_BOLD << "\n"; PrintHorizontalEdge(BOXUI_TL, BOXUI_TR); PrintBoxedLine(kerr, "!!! KERNEL PANIC !!!", BoxWidth, true); PrintBoxedLine(kerr, "", BoxWidth); PrintBoxedLine(kerr, "System halted. Please reboot.", BoxWidth, true); PrintBoxedLine(kerr, "", BoxWidth); // Meditation string PrintBoxedSeparator(kerr, BoxWidth); PrintBoxedLine(kerr, meditationString, BoxWidth); PrintBoxedLine(kerr, "", BoxWidth); // CPU state (x86_64 only) #if defined (__x86_64__) if (frame != nullptr) { PrintBoxedSeparator(kerr, BoxWidth); PrintBoxedLine(kerr, "CPU State:", BoxWidth, true); PrintBoxedHex(kerr, "Interrupt Vector", frame->InterruptVector, BoxWidth); if (frame->InterruptVector == 0xE) PrintPageFaultInfo(frame); else if (frame->InterruptVector == 0xD) PrintGPFInfo(frame); PrintRegisters(frame); } #endif // Footer PrintBoxedLine(kerr, "", BoxWidth); PrintHorizontalEdge(BOXUI_BL, BOXUI_BR); kerr << BOXUI_ANSI_RESET; #if defined (__x86_64__) // The framebuffer is write-combining. Drain the final panic text before // halting all forward progress so it cannot remain in a CPU write buffer. asm volatile("sfence" ::: "memory"); #endif Halt(); }