188 lines
7.0 KiB
C
188 lines
7.0 KiB
C
// ke_debug.c - debugging utilities
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <hal_api.h>
|
|
#include <ke_spinlock.h>
|
|
#include <ke_process.h>
|
|
#include <ke_thread.h>
|
|
#include <ke_sched.h>
|
|
#include <printf.h>
|
|
int _fltused; // fuckass
|
|
|
|
int gDebugEnabled = 0;
|
|
void (*PrintChar)(char, uint32_t, uint32_t);
|
|
|
|
void kernel_putchar(char c) {
|
|
PrintChar(c, 0x00FF00, 0x000000);
|
|
}
|
|
|
|
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v) {
|
|
gDebugEnabled = v;
|
|
PrintChar = PrintPtr;
|
|
printf_set_putchar(kernel_putchar);
|
|
}
|
|
|
|
__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* FaultVectorName(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* ResolveModule(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 PrintCrashChar(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 = FaultVectorName(vector);
|
|
const char* procName = proc ? proc->Name : "unknown";
|
|
uint32_t pid = proc ? proc->ProcessId : 0;
|
|
const char* modName = proc ? ResolveModule(proc, rip) : "unknown";
|
|
|
|
// Amber crash banner.
|
|
static const char banner[] = "\n*** APPLICATION CRASH ***\n";
|
|
for (const char* p = banner; *p; p++)
|
|
PrintCrashChar(*p);
|
|
|
|
printf_set_putchar(PrintCrashChar);
|
|
|
|
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(kernel_putchar);
|
|
|
|
// 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);
|
|
}
|