Files
nt-lite/code/kernel/ke_debug.c
T

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);
}