// ke_debug.c - debugging utilities #include #include #include #include #include #include #include #include #include int _fltused; // fuckass int gDebugEnabled = 0; void (*PrintChar)(char, uint32_t, uint32_t); void KiKernelPutChar(char c) { PrintChar(c, 0x00FF00, 0x000000); } void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v) { gDebugEnabled = v; PrintChar = PrintPtr; printf_set_putchar(KiKernelPutChar); } __declspec(dllexport) void DbgPrint(const char* fmt, ...) { if (!gDebugEnabled) return; static SPINLOCK sl = {0}; uintptr_t flags = KeAcquireSpinlock(&sl); va_list va; va_start(va, fmt); vprintf(fmt, va); va_end(va); KeReleaseSpinlock(&sl, flags); } // KePanic __declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...) { // Silence! Stop the world! _disable(); // Print a bright-red banner so the framebuffer is unmistakable. // We call PrintChar directly so we can set a red foreground. // (DbgPrint uses green; we want this visually distinct.) static const char banner[] = "\n*** KERNEL PANIC ***\n"; for (const char* p = banner; *p; p++) PrintChar(*p, 0xFF2222, 0x000000); // Now print the caller's message through the normal vprintf path. va_list va; va_start(va, fmt); vprintf(fmt, va); va_end(va); // Die! while (true) __halt(); } // KePrintConsole - write user-visible console text in white-on-black. // Used by SYS_WRITE_CONSOLE so user programs get a distinct color from // kernel debug output (green) and panic output (red). __declspec(dllexport) void KePrintConsole(const char* buf, size_t len) { if (!buf || !PrintChar) return; for (size_t i = 0; i < len; i++) PrintChar(buf[i], 0xFFFFFF, 0x000000); } // --------------------------------------------------------------------------- // KeBugCheckProcess - user-mode fault handler // // Resolves the faulting RIP to a module name, prints a yellow crash banner, // kills the offending process, then exits the current thread so the scheduler // can continue running everything else. // --------------------------------------------------------------------------- // Return the short exception name for a vector number. static const char* KiFaultVectorName(int vector) { switch (vector) { case 0: return "#DE Divide Error"; case 13: return "#GP General Protection"; case 14: return "#PF Page Fault"; default: return "Unknown Exception"; } } // Walk the process module list to find which image owns rip. // Returns the module name string, or "unknown" if rip is outside all regions. static const char* KiResolveModule(PROCESS* proc, uintptr_t rip) { // Check the main executable image first. if (proc->ImageBase && proc->ImageSize && rip >= proc->ImageBase && rip < proc->ImageBase + proc->ImageSize) return proc->Name; // Walk the per-process module table (DLLs / shared libraries). for (uint32_t i = 0; i < proc->ModuleCount; i++) { uintptr_t base = proc->Modules[i].Base; size_t size = proc->Modules[i].Size; if (base && size && rip >= base && rip < base + size) return proc->Modules[i].Name; } return "unknown"; } static void KiPrintCrashChar(char c) { PrintChar(c, 0xFFAA00, 0x000000); // amber - distinct from green debug / red panic } _Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2) { THREAD* thread = KeGetCurrentThread(); PROCESS* proc = thread ? thread->Owner : (PROCESS*)0; // Snapshot everything we need before touching process state. const char* faultName = KiFaultVectorName(vector); const char* procName = proc ? proc->Name : "unknown"; uint32_t pid = proc ? proc->ProcessId : 0; const char* modName = proc ? KiResolveModule(proc, rip) : "unknown"; // Amber crash banner. static const char banner[] = "\n*** APPLICATION CRASH ***\n"; for (const char* p = banner; *p; p++) KiPrintCrashChar(*p); printf_set_putchar(KiPrintCrashChar); if (vector == 14) printf("Process: %s (PID %u) | Fault: %s\n" "Module: %s | RIP: %016llx | Code: %04llx | CR2: %016llx\n", procName, pid, faultName, modName, (unsigned long long)rip, (unsigned long long)errCode, (unsigned long long)cr2); else printf("Process: %s (PID %u) | Fault: %s\n" "Module: %s | RIP: %016llx | Code: %04llx\n", procName, pid, faultName, modName, (unsigned long long)rip, (unsigned long long)errCode); // Restore normal (green) output for subsequent kernel messages. printf_set_putchar(KiKernelPutChar); // Kill the process and exit this thread. // KeExitThread sets ThreadDead then calls KeYield; the scheduler will // never return here, so the process crash cannot affect anything else. if (proc) KeExitProcess(proc, -1); if (thread) KeExitThread(thread, -1); // Unreachable - KeExitThread never returns for a dead thread. while (1) __halt(); } // --------------------------------------------------------------------------- // KeFaultDispatch - single C entry point for all three IDT fault stubs. // // The asm stubs (ke_syscall.asm) collect the CPU exception frame values and // call here. We check the CS RPL to decide: kernel fault → KePanic (halt), // user fault → KeBugCheckProcess (kill process, resume scheduler). // --------------------------------------------------------------------------- __declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2, uintptr_t cs) { if ((cs & 3) == 0) { // Kernel-mode fault: unrecoverable. switch (vector) { case 0: KePanic("PANIC: #DE Divide Error | RIP: %016llx\n", (unsigned long long)rip); case 13: KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n", (unsigned long long)rip, (unsigned long long)errCode); case 14: KePanic("PANIC: #PF Page Fault | RIP: %016llx | Code: %04llx | CR2: %016llx\n", (unsigned long long)rip, (unsigned long long)errCode, (unsigned long long)cr2); default: KePanic("PANIC: Exception #%d | RIP: %016llx\n", vector, (unsigned long long)rip); } } // User-mode fault: recoverable - kill the process, resume the scheduler. KeBugCheckProcess(vector, rip, errCode, cr2); }