implement proper IRQL, DPCs, kernel and user APCs, alertable waits, async file I/O with cancellation, trap frames, selftests, a real driver/device model, native async FAT32, VFAT dotfile fixes, syscall ABI cleanup, async_apc and async_cancel tests, POSIX multi-volume paths, and pimp the readme.
This commit is contained in:
1 parent
f5fd7bdd75
commit
529f816166
75 files changed
+9969
-5675
No files matched your search
+187
-187
@@ -1,187 +1,187 @@
|
||||
// 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);
|
||||
}
|
||||
// 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 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);
|
||||
}
|
||||
Reference in new issue
Block a user