add ntfs.sys standalone driver target, change default usb vhd layout formatting to ntfs, implement Fat32CreateDir for directory creation support in the fat32 file system driver, add a dedicated HAL serial output endpoint for COM1 writes in hal_serial.c and subscribe serial to HAL text streams, introduce waitable kernel timers with KeTimerCreate, KeTimerSet, and KeTimerCancel alongside spinlock-protected global timer list initialization and scheduler tick tracking, implement SyscallWriteFileAsync system call for asynchronous non-blocking file writes, refactor NetSocketConnect and NetSocketSendTo to use generic SYS_SOCKET_ADDRESS_K structures instead of being hardcoded to IPv4, update gethostbyname and DNS name resolution to support IPv6 parsing via ResolveNameFamily, update tcpserve.exe with -4 and -6 command-line flags to accept and display IPv6 addresses, append size and version headers to all kernel telemetry and statistics structures including SYS_PMM_STATS, SYS_CPU_STATS, and SYS_SCHEDULER_STATS, update winmm timeGetTime to look up high-resolution hardware frequencies via SysGetTickFrequency, integrate 14 native smoke test applications.
This commit is contained in:
1 parent
2c4a68a75f
commit
ed3d065abd
156 files changed
+10509
-2643
No files matched your search
+229
-349
@@ -7,46 +7,35 @@
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/hal/hal_apic.h>
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_disable)
|
||||
#pragma intrinsic(_enable)
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_work.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ldr_image.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/ke_system_root.h>
|
||||
#include <common/printf.h>
|
||||
int _fltused; // MSVC floating-point helper anchor
|
||||
int _fltused; // MSVC floating-point helper anchor.
|
||||
|
||||
int gDebugEnabled = 0;
|
||||
static uint32_t gDebugFlags = 0;
|
||||
void (*PrintChar)(char, uint32_t, uint32_t);
|
||||
static uint32_t gDebugFlags;
|
||||
static const HAL_API* gDebugHal;
|
||||
|
||||
#define KE_DEBUG_LOG_BUFFER_SIZE (50u * 1024u)
|
||||
#define KE_DEBUG_SCREEN_BUFFER_SIZE (16u * 1024u)
|
||||
#define KE_DEBUG_LOG_LINE_SIZE 1024u
|
||||
#define KE_DEBUG_SCREEN_DRAIN_SIZE 512u
|
||||
#define KE_DEBUG_LOG_BUFFER_SIZE (50u * 1024u)
|
||||
#define KE_DEBUG_LOG_LINE_SIZE 1024u
|
||||
#define KE_FATAL_OUTPUT_LINE_SIZE 2048u
|
||||
|
||||
static SPINLOCK gDebugPrintLock = SPINLOCK_INIT;
|
||||
static char* gDebugLogBuffer;
|
||||
static size_t gDebugLogSize;
|
||||
static size_t gDebugLogWrite;
|
||||
static size_t gDebugLogUsed;
|
||||
static char* gDebugScreenBuffer;
|
||||
static size_t gDebugScreenSize;
|
||||
static size_t gDebugScreenRead;
|
||||
static size_t gDebugScreenWrite;
|
||||
static size_t gDebugScreenUsed;
|
||||
static char* gDebugLogBuffer;
|
||||
static size_t gDebugLogSize;
|
||||
static size_t gDebugLogWrite;
|
||||
static size_t gDebugLogUsed;
|
||||
static volatile long gDebugLogFlushing;
|
||||
static volatile long gDebugConsoleOwnsSerial;
|
||||
static volatile long gDebugConsoleWorkReady;
|
||||
static KWORK_ITEM gDebugConsoleWorkItem;
|
||||
|
||||
// Implements the debug Log Append Locked operation.
|
||||
// Appends bytes to the in-memory debug log while its lock is held.
|
||||
static void KiDebugLogAppendLocked(const char* text, size_t len) {
|
||||
if (!gDebugLogBuffer || gDebugLogSize == 0 || !text || len == 0)
|
||||
return;
|
||||
@@ -57,7 +46,8 @@ static void KiDebugLogAppendLocked(const char* text, size_t len) {
|
||||
}
|
||||
|
||||
size_t first = gDebugLogSize - gDebugLogWrite;
|
||||
if (first > len) first = len;
|
||||
if (first > len)
|
||||
first = len;
|
||||
memcpy(gDebugLogBuffer + gDebugLogWrite, text, first);
|
||||
if (len > first)
|
||||
memcpy(gDebugLogBuffer, text + first, len - first);
|
||||
@@ -69,137 +59,52 @@ static void KiDebugLogAppendLocked(const char* text, size_t len) {
|
||||
gDebugLogUsed += len;
|
||||
}
|
||||
|
||||
// Implements the debug Screen Append Locked operation.
|
||||
static void KiDebugScreenAppendLocked(const char* text, size_t len) {
|
||||
if (!gDebugScreenBuffer || gDebugScreenSize == 0 || !text || len == 0)
|
||||
// Writes one record through the HAL output router.
|
||||
static void KiOutputWrite(uint32_t stream, uint32_t flags,
|
||||
uint32_t foreground, uint32_t background,
|
||||
const char* text, size_t length) {
|
||||
if (!gDebugHal || !gDebugHal->Output.Write || !text || length == 0)
|
||||
return;
|
||||
|
||||
if (len >= gDebugScreenSize) {
|
||||
text += len - (gDebugScreenSize - 1u);
|
||||
len = gDebugScreenSize - 1u;
|
||||
}
|
||||
|
||||
while (gDebugScreenUsed + len > gDebugScreenSize) {
|
||||
gDebugScreenRead = (gDebugScreenRead + 1u) % gDebugScreenSize;
|
||||
gDebugScreenUsed--;
|
||||
}
|
||||
|
||||
size_t first = gDebugScreenSize - gDebugScreenWrite;
|
||||
if (first > len) first = len;
|
||||
memcpy(gDebugScreenBuffer + gDebugScreenWrite, text, first);
|
||||
if (len > first)
|
||||
memcpy(gDebugScreenBuffer, text + first, len - first);
|
||||
|
||||
gDebugScreenWrite = (gDebugScreenWrite + len) % gDebugScreenSize;
|
||||
gDebugScreenUsed += len;
|
||||
HAL_OUTPUT_RECORD record;
|
||||
record.Stream = stream;
|
||||
record.Flags = flags;
|
||||
record.Foreground = foreground;
|
||||
record.Background = background;
|
||||
record.Buffer = text;
|
||||
record.Length = length;
|
||||
gDebugHal->Output.Write(&record);
|
||||
}
|
||||
|
||||
// Implements the debug Screen Drain Locked operation.
|
||||
static size_t KiDebugScreenDrainLocked(char* out, size_t max) {
|
||||
if (!out || max == 0 || !gDebugScreenBuffer || gDebugScreenSize == 0 || gDebugScreenUsed == 0)
|
||||
return 0;
|
||||
|
||||
size_t len = gDebugScreenUsed;
|
||||
if (len > max) len = max;
|
||||
|
||||
size_t first = gDebugScreenSize - gDebugScreenRead;
|
||||
if (first > len) first = len;
|
||||
memcpy(out, gDebugScreenBuffer + gDebugScreenRead, first);
|
||||
if (len > first)
|
||||
memcpy(out + first, gDebugScreenBuffer, len - first);
|
||||
|
||||
gDebugScreenRead = (gDebugScreenRead + len) % gDebugScreenSize;
|
||||
gDebugScreenUsed -= len;
|
||||
return len;
|
||||
}
|
||||
|
||||
// Ensures the asynchronous screen ring exists before console output starts using it.
|
||||
static bool KiDebugEnsureScreenBuffer(void) {
|
||||
if (gDebugScreenBuffer && gDebugScreenSize != 0)
|
||||
return true;
|
||||
|
||||
char* buffer = (char*)MmAllocatePoolTag(KE_DEBUG_SCREEN_BUFFER_SIZE, MM_POOL_TAG('d', 's', 'c', 'r'));
|
||||
if (!buffer)
|
||||
return false;
|
||||
memset(buffer, 0, KE_DEBUG_SCREEN_BUFFER_SIZE);
|
||||
|
||||
bool installed = false;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock);
|
||||
if (!gDebugScreenBuffer) {
|
||||
gDebugScreenBuffer = buffer;
|
||||
gDebugScreenSize = KE_DEBUG_SCREEN_BUFFER_SIZE;
|
||||
gDebugScreenRead = 0;
|
||||
gDebugScreenWrite = 0;
|
||||
gDebugScreenUsed = 0;
|
||||
installed = true;
|
||||
}
|
||||
KeReleaseSpinlock(&gDebugPrintLock, flags);
|
||||
|
||||
if (!installed)
|
||||
MmFreePool(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements the debug Serial Write Char operation.
|
||||
static void KiDebugSerialWriteChar(char c) {
|
||||
while ((__inbyte(0x3FD) & 0x20) == 0) { }
|
||||
if (c == '\n') {
|
||||
__outbyte(0x3F8, '\r');
|
||||
while ((__inbyte(0x3FD) & 0x20) == 0) { }
|
||||
}
|
||||
__outbyte(0x3F8, (uint8_t)c);
|
||||
}
|
||||
|
||||
// Implements the debug Serial Write Locked operation.
|
||||
static void KiDebugSerialWriteLocked(const char* text, size_t len) {
|
||||
if (!text)
|
||||
// Formats one bounded output record and routes it through the HAL.
|
||||
static void KiOutputVPrintf(uint32_t stream, uint32_t flags,
|
||||
uint32_t foreground, uint32_t background,
|
||||
const char* format, va_list args) {
|
||||
if (!format)
|
||||
return;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
KiDebugSerialWriteChar(text[i]);
|
||||
}
|
||||
|
||||
// Implements the debug Console Write operation.
|
||||
static void KiDebugConsoleWrite(const char* text, size_t len) {
|
||||
if (!PrintChar || !text)
|
||||
return;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
PrintChar(text[i], 0xCCCCCC, 0x012456);
|
||||
}
|
||||
|
||||
// Implements the debug Console Work operation.
|
||||
static void KiDebugConsoleWork(void* context) {
|
||||
(void)context;
|
||||
char chunk[KE_DEBUG_SCREEN_DRAIN_SIZE];
|
||||
|
||||
for (;;) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock);
|
||||
size_t len = KiDebugScreenDrainLocked(chunk, sizeof(chunk));
|
||||
KeReleaseSpinlock(&gDebugPrintLock, flags);
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
KiDebugConsoleWrite(chunk, len);
|
||||
char line[KE_FATAL_OUTPUT_LINE_SIZE];
|
||||
int count = vsnprintf_(line, sizeof(line), format, args);
|
||||
size_t length = 0;
|
||||
if (count > 0) {
|
||||
length = (size_t)count;
|
||||
if (length >= sizeof(line))
|
||||
length = sizeof(line) - 1u;
|
||||
}
|
||||
KiOutputWrite(stream, flags, foreground, background, line, length);
|
||||
}
|
||||
|
||||
// Queues the debug Console Work operation if the passive work queue is ready.
|
||||
static void KiDebugQueueConsoleWork(void) {
|
||||
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleWorkReady, 1, 1) && KeIsWorkQueueStarted())
|
||||
(void)KeQueueWorkItem(&gDebugConsoleWorkItem);
|
||||
}
|
||||
static volatile long gPanicInProgress;
|
||||
static volatile uintptr_t gPanicOwnerToken;
|
||||
static volatile long gPanicStoppedCpuCount;
|
||||
|
||||
static volatile long gPanicInProgress = 0;
|
||||
static volatile uintptr_t gPanicOwnerToken = 0;
|
||||
static volatile long gPanicStoppedCpuCount = 0;
|
||||
|
||||
// Implements the panic Secondary Halt operation.
|
||||
// Permanently stops a secondary processor that entered an active panic.
|
||||
static _Noreturn void KiPanicSecondaryHalt(void) {
|
||||
_disable();
|
||||
_InterlockedIncrement(&gPanicStoppedCpuCount);
|
||||
for (;;) __halt();
|
||||
HalStopProcessor();
|
||||
}
|
||||
|
||||
// Implements the panic Current Token operation.
|
||||
// Returns a stable token for the current execution context.
|
||||
static uintptr_t KiPanicCurrentToken(void) {
|
||||
THREAD* thread = KeGetCurrentThread();
|
||||
if (thread)
|
||||
@@ -210,20 +115,20 @@ static uintptr_t KiPanicCurrentToken(void) {
|
||||
return 1u;
|
||||
}
|
||||
|
||||
// Implements the panic Is Active operation.
|
||||
// Returns whether a fatal report is active.
|
||||
static int KiPanicIsActive(void) {
|
||||
return _InterlockedCompareExchange((volatile long*)&gPanicInProgress, 0, 0) != 0;
|
||||
}
|
||||
|
||||
// Implements the panic Current Cpu Owns Report operation.
|
||||
// Returns whether the current processor owns the active fatal report.
|
||||
static int KiPanicCurrentCpuOwnsReport(void) {
|
||||
uintptr_t token = KiPanicCurrentToken();
|
||||
return token != 0 && gPanicOwnerToken == token;
|
||||
}
|
||||
|
||||
// Implements the begin Fatal Report operation.
|
||||
// Claims fatal-report ownership and masks interrupts on this processor.
|
||||
static void KiBeginFatalReport(void) {
|
||||
_disable();
|
||||
(void)HalDisableInterrupts();
|
||||
uintptr_t token = KiPanicCurrentToken();
|
||||
if (_InterlockedCompareExchange((volatile long*)&gPanicInProgress, 1, 0) == 0) {
|
||||
gPanicOwnerToken = token;
|
||||
@@ -233,10 +138,7 @@ static void KiBeginFatalReport(void) {
|
||||
KiPanicSecondaryHalt();
|
||||
}
|
||||
|
||||
// Returning NMI handler used for SMP control IPIs. Panic NMIs stop the
|
||||
// target CPU permanently; TLB shootdown NMIs flush and return. A genuinely
|
||||
// unexpected NMI is not handled here and will fall through to the normal fault
|
||||
// path in the assembly stub.
|
||||
// Handles NMI-based panic stops and TLB shootdowns.
|
||||
__declspec(dllexport) int KeHandleNmiIpi(void) {
|
||||
if (KiPanicIsActive())
|
||||
KiPanicSecondaryHalt();
|
||||
@@ -247,23 +149,11 @@ __declspec(dllexport) int KeHandleNmiIpi(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the kernel Put Char operation.
|
||||
void KiKernelPutChar(char c) {
|
||||
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
|
||||
KiDebugSerialWriteChar(c);
|
||||
if (PrintChar)
|
||||
PrintChar(c, 0xCCCCCC, 0x012456);
|
||||
}
|
||||
|
||||
// Initializes debug.
|
||||
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), uint32_t flags) {
|
||||
// Initializes debug logging and initial output subscriptions.
|
||||
void KeInitDebug(const HAL_API* hal, uint32_t flags) {
|
||||
gDebugHal = hal;
|
||||
gDebugFlags = flags;
|
||||
gDebugEnabled = (flags & (KE_DEBUG_CONSOLE | KE_DEBUG_FILE_LOG)) != 0;
|
||||
PrintChar = PrintPtr;
|
||||
printf_set_putchar(KiKernelPutChar);
|
||||
|
||||
if (!_InterlockedCompareExchange((volatile long*)&gDebugConsoleWorkReady, 1, 0))
|
||||
KeInitializeWorkItem(&gDebugConsoleWorkItem, KiDebugConsoleWork, (void*)0);
|
||||
|
||||
if ((flags & (KE_DEBUG_CONSOLE | KE_DEBUG_FILE_LOG)) && !gDebugLogBuffer) {
|
||||
gDebugLogBuffer = (char*)MmAllocatePoolTag(KE_DEBUG_LOG_BUFFER_SIZE, MM_POOL_TAG('d', 'l', 'o', 'g'));
|
||||
@@ -275,81 +165,56 @@ void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), uint32_t flags) {
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & KE_DEBUG_CONSOLE)
|
||||
(void)KiDebugEnsureScreenBuffer();
|
||||
if (hal && hal->Output.SetEndpointStreams &&
|
||||
hal->Output.BootFramebufferEndpointId != HAL_OUTPUT_ENDPOINT_INVALID) {
|
||||
uint32_t streams = HAL_OUTPUT_STREAM_CONSOLE |
|
||||
HAL_OUTPUT_STREAM_CRASH |
|
||||
HAL_OUTPUT_STREAM_PANIC;
|
||||
if (flags & KE_DEBUG_CONSOLE)
|
||||
streams |= HAL_OUTPUT_STREAM_DEBUG;
|
||||
(void)hal->Output.SetEndpointStreams(hal->Output.BootFramebufferEndpointId, streams);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns debug Flags.
|
||||
// Returns the active debug flags.
|
||||
uint32_t KeGetDebugFlags(void) {
|
||||
return gDebugFlags;
|
||||
}
|
||||
|
||||
// Updates debug Console Writer.
|
||||
void KeSetDebugConsoleWriter(void (*PrintPtr)(char, uint32_t, uint32_t)) {
|
||||
if (!PrintPtr)
|
||||
return;
|
||||
PrintChar = PrintPtr;
|
||||
printf_set_putchar(KiKernelPutChar);
|
||||
|
||||
// After the display manager owns debug drawing, DbgPrint and user console
|
||||
// writes mirror to COM1 themselves. The display writer is screen-only so
|
||||
// asynchronous screen flushing does not duplicate serial bytes.
|
||||
(void)KiDebugEnsureScreenBuffer();
|
||||
_InterlockedExchange((volatile long*)&gDebugConsoleOwnsSerial, 1);
|
||||
KiDebugQueueConsoleWork();
|
||||
}
|
||||
|
||||
// Implements the dbg Print operation.
|
||||
// Writes one formatted kernel diagnostic.
|
||||
__declspec(dllexport) void DbgPrint(const char* fmt, ...) {
|
||||
if (!gDebugEnabled || !fmt) return;
|
||||
if (!gDebugEnabled || !fmt)
|
||||
return;
|
||||
|
||||
char line[KE_DEBUG_LOG_LINE_SIZE];
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
int n = vsnprintf_(line, sizeof(line), fmt, va);
|
||||
va_end(va);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int count = vsnprintf_(line, sizeof(line), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
size_t len = 0;
|
||||
if (n > 0) {
|
||||
len = (size_t)n;
|
||||
if (len >= sizeof(line))
|
||||
len = sizeof(line) - 1u;
|
||||
size_t length = 0;
|
||||
if (count > 0) {
|
||||
length = (size_t)count;
|
||||
if (length >= sizeof(line))
|
||||
length = sizeof(line) - 1u;
|
||||
}
|
||||
|
||||
if (KiPanicIsActive()) {
|
||||
if (!KiPanicCurrentCpuOwnsReport())
|
||||
KiPanicSecondaryHalt();
|
||||
if (gDebugFlags & KE_DEBUG_CONSOLE) {
|
||||
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
|
||||
KiDebugSerialWriteLocked(line, len);
|
||||
KiDebugConsoleWrite(line, len);
|
||||
}
|
||||
return;
|
||||
bool panic = KiPanicIsActive();
|
||||
if (panic && !KiPanicCurrentCpuOwnsReport())
|
||||
KiPanicSecondaryHalt();
|
||||
|
||||
if (!panic) {
|
||||
uintptr_t lockFlags = KeAcquireSpinlock(&gDebugPrintLock);
|
||||
if (!_InterlockedCompareExchange((volatile long*)&gDebugLogFlushing, 0, 0))
|
||||
KiDebugLogAppendLocked(line, length);
|
||||
KeReleaseSpinlock(&gDebugPrintLock, lockFlags);
|
||||
}
|
||||
|
||||
bool queueConsole = false;
|
||||
bool writeConsoleSync = false;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock);
|
||||
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
|
||||
KiDebugSerialWriteLocked(line, len);
|
||||
if (!_InterlockedCompareExchange((volatile long*)&gDebugLogFlushing, 0, 0))
|
||||
KiDebugLogAppendLocked(line, len);
|
||||
|
||||
if (gDebugFlags & KE_DEBUG_CONSOLE) {
|
||||
if (KeIsWorkQueueStarted() && gDebugScreenBuffer) {
|
||||
KiDebugScreenAppendLocked(line, len);
|
||||
queueConsole = true;
|
||||
} else {
|
||||
writeConsoleSync = true;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gDebugPrintLock, flags);
|
||||
|
||||
if (queueConsole)
|
||||
KiDebugQueueConsoleWork();
|
||||
else if (writeConsoleSync)
|
||||
KiDebugConsoleWrite(line, len);
|
||||
KiOutputWrite(panic ? HAL_OUTPUT_STREAM_PANIC : HAL_OUTPUT_STREAM_DEBUG,
|
||||
panic ? HAL_OUTPUT_WRITE_EMERGENCY : 0,
|
||||
panic ? 0x00FF2222u : 0x00CCCCCCu,
|
||||
panic ? 0x00000000u : 0x00012456u,
|
||||
line, length);
|
||||
}
|
||||
|
||||
// Flushes debug Log To File.
|
||||
@@ -402,7 +267,6 @@ __declspec(dllexport) KSTATUS KeFlushDebugLogToFile(SYS_STRING_VIEW path) {
|
||||
return (KSUCCESS(st) && totalWritten == used) ? STATUS_SUCCESS : st;
|
||||
}
|
||||
|
||||
|
||||
// Attempts to persist the debug log during a panic when the filesystem is usable.
|
||||
static void KiTryFlushPanicDebugLog(void) {
|
||||
if (!(gDebugFlags & KE_DEBUG_FILE_LOG))
|
||||
@@ -419,76 +283,69 @@ static void KiTryFlushPanicDebugLog(void) {
|
||||
DbgPrint("debuglog: panic flush to %s failed st=0x%x\n", path, st);
|
||||
}
|
||||
|
||||
// KePanic.
|
||||
// Writes one formatted panic record through panic-safe endpoints.
|
||||
static void KiPanicPrintf(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
KiOutputVPrintf(HAL_OUTPUT_STREAM_PANIC, HAL_OUTPUT_WRITE_EMERGENCY,
|
||||
0x00FF2222u, 0x00000000u, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Stops the system after reporting a fatal kernel error.
|
||||
__declspec(dllexport) _Noreturn void KePanic(int code, const char* fmt, ...) {
|
||||
KiBeginFatalReport();
|
||||
|
||||
// Stop every other CPU for the final panic banner. Kernel fault dispatch
|
||||
// claims panic ownership before its detailed dump, so this does not steal
|
||||
// ownership from the CPU that found the fatal error.
|
||||
// Stop every other CPU before writing the final report. Panic-safe endpoints
|
||||
// do not depend on scheduler locks held by processors stopped by this IPI.
|
||||
HalApicSendPanicIpi();
|
||||
|
||||
if (PrintChar) {
|
||||
// Print a bright-red banner so the framebuffer is unmistakable.
|
||||
static const char banner[] = "\n*** KERNEL PANIC ***\n";
|
||||
const char* p;
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
THREAD* thread = KeGetCurrentThread();
|
||||
static const char banner[] = "\n*** KERNEL PANIC ***\n";
|
||||
KiOutputWrite(HAL_OUTPUT_STREAM_PANIC, HAL_OUTPUT_WRITE_EMERGENCY,
|
||||
0x00FF2222u, 0x00000000u, banner, sizeof(banner) - 1u);
|
||||
KiPanicPrintf("Panic code: %d\n", code);
|
||||
|
||||
for (p = banner; *p; p++)
|
||||
PrintChar(*p, 0xFF2222, 0x000000);
|
||||
|
||||
printf("Panic code: %d\n", code);
|
||||
if (cpu) {
|
||||
printf("Panic CPU: %u LAPIC=%u",
|
||||
(unsigned)cpu->CpuIndex,
|
||||
(unsigned)cpu->LapicId);
|
||||
if (thread)
|
||||
printf(" thread=0x%016llX tid=%llu",
|
||||
thread,
|
||||
(unsigned long long)thread->ThreadId);
|
||||
printf("\n");
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
THREAD* thread = KeGetCurrentThread();
|
||||
if (cpu) {
|
||||
KiPanicPrintf("Panic CPU: %u LAPIC=%u",
|
||||
(unsigned)cpu->CpuIndex,
|
||||
(unsigned)cpu->LapicId);
|
||||
if (thread) {
|
||||
KiPanicPrintf(" thread=0x%016llX tid=%llu",
|
||||
thread,
|
||||
(unsigned long long)thread->ThreadId);
|
||||
}
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
vprintf(fmt, va);
|
||||
va_end(va);
|
||||
KiPanicPrintf("\n");
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
KiOutputVPrintf(HAL_OUTPUT_STREAM_PANIC, HAL_OUTPUT_WRITE_EMERGENCY,
|
||||
0x00FF2222u, 0x00000000u, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
KiTryFlushPanicDebugLog();
|
||||
|
||||
for (;;) __halt();
|
||||
HalStopProcessor();
|
||||
}
|
||||
|
||||
// KePrintConsole - write user-visible console text using the default console palette.
|
||||
// User text mirrors to COM1 immediately, then queues expensive screen drawing
|
||||
// through the passive debug-console worker when that path is available.
|
||||
// Writes user-visible console text through subscribed output endpoints.
|
||||
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len) {
|
||||
if (!buf || len == 0) return;
|
||||
KiOutputWrite(HAL_OUTPUT_STREAM_CONSOLE, 0,
|
||||
0x00CCCCCCu, 0x00012456u, buf, len);
|
||||
}
|
||||
|
||||
bool queueConsole = false;
|
||||
bool writeConsoleSync = false;
|
||||
bool canQueueScreen = PrintChar && KeIsWorkQueueStarted() && KiDebugEnsureScreenBuffer();
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock);
|
||||
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
|
||||
KiDebugSerialWriteLocked(buf, len);
|
||||
|
||||
if (PrintChar) {
|
||||
if (!KiPanicIsActive() && canQueueScreen) {
|
||||
KiDebugScreenAppendLocked(buf, len);
|
||||
queueConsole = true;
|
||||
} else {
|
||||
writeConsoleSync = true;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gDebugPrintLock, flags);
|
||||
|
||||
if (queueConsole)
|
||||
KiDebugQueueConsoleWork();
|
||||
else if (writeConsoleSync)
|
||||
KiDebugConsoleWrite(buf, len);
|
||||
// Writes one formatted process-fault or kernel-fault diagnostic.
|
||||
static void KiFaultPrintf(const char* format, ...) {
|
||||
bool panic = KiPanicIsActive();
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
KiOutputVPrintf(panic ? HAL_OUTPUT_STREAM_PANIC : HAL_OUTPUT_STREAM_CRASH,
|
||||
panic ? HAL_OUTPUT_WRITE_EMERGENCY : 0,
|
||||
panic ? 0x00FF2222u : 0x00FFAA00u,
|
||||
0x00000000u,
|
||||
format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// KeBugCheckProcess - user-mode fault handler
|
||||
@@ -585,6 +442,15 @@ static const char* KiResolveModuleEx(PROCESS* proc, uintptr_t addr,
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t kernelBase = 0;
|
||||
size_t kernelSize = 0;
|
||||
const char* kernelModule = LdrFindKernelModuleByAddress(addr, &kernelBase, &kernelSize);
|
||||
if (kernelBase) {
|
||||
if (baseOut) *baseOut = kernelBase;
|
||||
if (sizeOut) *sizeOut = kernelSize;
|
||||
return kernelModule;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@@ -600,71 +466,96 @@ static void KiPrintAddress(PROCESS* proc, const char* label, uintptr_t addr) {
|
||||
const char* mod = KiResolveModuleEx(proc, addr, &base, &size);
|
||||
|
||||
if (base) {
|
||||
printf("%s%016llx <%s+0x%016llX>\n",
|
||||
KiFaultPrintf("%s%016llx <%s+0x%016llX>\n",
|
||||
label,
|
||||
(unsigned long long)addr,
|
||||
mod,
|
||||
(unsigned long long)(addr - base));
|
||||
} else {
|
||||
printf("%s%016llx <unknown>\n",
|
||||
KiFaultPrintf("%s%016llx <unknown>\n",
|
||||
label,
|
||||
(unsigned long long)addr);
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the user Read U64 operation.
|
||||
static int KiUserReadU64(uintptr_t addr, uint64_t* out) {
|
||||
if (!out) return 0;
|
||||
if (!addr) return 0;
|
||||
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(uint64_t))))
|
||||
return 0;
|
||||
*out = *(const uint64_t*)addr;
|
||||
// Checks whether a kernel virtual range is mapped before diagnostic reads.
|
||||
static int KiKernelRangeReadable(uintptr_t addr, size_t size) {
|
||||
if (!addr || size == 0) return 0;
|
||||
if (size - 1u > (size_t)(UINTPTR_MAX - addr)) return 0;
|
||||
|
||||
uintptr_t start = addr & PAGE_MASK;
|
||||
uintptr_t last = (addr + size - 1u) & PAGE_MASK;
|
||||
|
||||
for (uintptr_t page = start;; page += PAGE_SIZE) {
|
||||
PHYSICAL_ADDRESS phys = 0;
|
||||
uint64_t flags = 0;
|
||||
if (!KSUCCESS(MmPteQueryPageFor(MmPteQueryDebug, page, &phys, &flags)))
|
||||
return 0;
|
||||
if ((flags & PAGE_PRESENT) == 0)
|
||||
return 0;
|
||||
if (page == last)
|
||||
break;
|
||||
if (page > UINTPTR_MAX - PAGE_SIZE)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Implements the user Read U8 operation.
|
||||
static int KiUserReadU8(uintptr_t addr, uint8_t* out) {
|
||||
if (!out) return 0;
|
||||
if (!addr) return 0;
|
||||
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(uint8_t))))
|
||||
// Reads diagnostic memory from either user space or a mapped kernel range.
|
||||
static int KiReadDiagnosticMemory(uintptr_t addr, void* out, size_t size) {
|
||||
if (!out || !addr || size == 0) return 0;
|
||||
|
||||
if (addr >= KERNEL_BASE) {
|
||||
if (!KiKernelRangeReadable(addr, size))
|
||||
return 0;
|
||||
memcpy(out, (const void*)addr, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!KSUCCESS(MmProbeUserRead((const void*)addr, size)))
|
||||
return 0;
|
||||
*out = *(const uint8_t*)addr;
|
||||
memcpy(out, (const void*)addr, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Reads one diagnostic uint64_t.
|
||||
static int KiUserReadU64(uintptr_t addr, uint64_t* out) {
|
||||
return KiReadDiagnosticMemory(addr, out, sizeof(*out));
|
||||
}
|
||||
|
||||
// Reads one diagnostic byte.
|
||||
static int KiUserReadU8(uintptr_t addr, uint8_t* out) {
|
||||
return KiReadDiagnosticMemory(addr, out, sizeof(*out));
|
||||
}
|
||||
|
||||
// Dumps bytes.
|
||||
static void KiDumpBytes(PROCESS* proc, const char* label, uintptr_t center) {
|
||||
if (!center) {
|
||||
printf("%s: <null>\n", label);
|
||||
KiFaultPrintf("%s: <null>\n", label);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s around ", label);
|
||||
KiFaultPrintf("%s around ", label);
|
||||
KiPrintAddress(proc, "", center);
|
||||
|
||||
uintptr_t start = center >= 16 ? center - 16 : 0;
|
||||
for (int row = 0; row < 2; row++) {
|
||||
uintptr_t addr = start + (uintptr_t)row * 16u;
|
||||
printf(" %016llx:", (unsigned long long)addr);
|
||||
KiFaultPrintf(" %016llx:", (unsigned long long)addr);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint8_t b = 0;
|
||||
if (KiUserReadU8(addr + (uintptr_t)i, &b))
|
||||
printf(" %02x", b);
|
||||
KiFaultPrintf(" %02x", b);
|
||||
else
|
||||
printf(" ??");
|
||||
KiFaultPrintf(" ??");
|
||||
}
|
||||
printf("\n");
|
||||
KiFaultPrintf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the user Read I32 operation.
|
||||
// Reads one diagnostic int32_t.
|
||||
static int KiUserReadI32(uintptr_t addr, int32_t* out) {
|
||||
if (!out) return 0;
|
||||
if (!addr) return 0;
|
||||
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(int32_t))))
|
||||
return 0;
|
||||
*out = *(const int32_t*)addr;
|
||||
return 1;
|
||||
return KiReadDiagnosticMemory(addr, out, sizeof(*out));
|
||||
}
|
||||
|
||||
// Dumps indirect Call Slot.
|
||||
@@ -693,21 +584,21 @@ static void KiDumpIndirectCallSlot(PROCESS* proc, uintptr_t ret) {
|
||||
KiUserReadI32(ret - 4, &disp)) {
|
||||
insn = ret - 7;
|
||||
} else {
|
||||
printf("No RIP-relative indirect call pattern found before return address.\n");
|
||||
KiFaultPrintf("No RIP-relative indirect call pattern found before return address.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uintptr_t slot = ret + (intptr_t)disp;
|
||||
uint64_t target = 0;
|
||||
|
||||
printf("Decoded null indirect call:\n");
|
||||
KiFaultPrintf("Decoded null indirect call:\n");
|
||||
KiPrintAddress(proc, " insn=", insn);
|
||||
KiPrintAddress(proc, " slot=", slot);
|
||||
|
||||
if (KiUserReadU64(slot, &target)) {
|
||||
KiPrintAddress(proc, " *slot=", (uintptr_t)target);
|
||||
} else {
|
||||
printf(" *slot=<unreadable>\n");
|
||||
KiFaultPrintf(" *slot=<unreadable>\n");
|
||||
}
|
||||
|
||||
KiDumpBytes(proc, "Bytes near indirect slot", slot);
|
||||
@@ -717,9 +608,9 @@ static void KiDumpIndirectCallSlot(PROCESS* proc, uintptr_t ret) {
|
||||
static void KiDumpModules(PROCESS* proc) {
|
||||
if (!proc) return;
|
||||
|
||||
printf("Images:\n");
|
||||
KiFaultPrintf("Images:\n");
|
||||
if (proc->ImageBase && proc->ImageSize) {
|
||||
printf(" %-31s %016llx-%016llx size=%llx\n",
|
||||
KiFaultPrintf(" %-31s %016llx-%016llx size=%llx\n",
|
||||
proc->Name,
|
||||
(unsigned long long)proc->ImageBase,
|
||||
(unsigned long long)(proc->ImageBase + proc->ImageSize),
|
||||
@@ -729,7 +620,7 @@ static void KiDumpModules(PROCESS* proc) {
|
||||
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
|
||||
uintptr_t base = proc->Modules[i].Base;
|
||||
size_t size = proc->Modules[i].Size;
|
||||
printf(" %-31s %016llx-%016llx size=%llx\n",
|
||||
KiFaultPrintf(" %-31s %016llx-%016llx size=%llx\n",
|
||||
proc->Modules[i].Name,
|
||||
(unsigned long long)base,
|
||||
(unsigned long long)(base + size),
|
||||
@@ -739,9 +630,9 @@ static void KiDumpModules(PROCESS* proc) {
|
||||
|
||||
// Dumps user Stack.
|
||||
static void KiDumpUserStack(PROCESS* proc, uintptr_t rsp) {
|
||||
printf("User stack @ %016llx:\n", (unsigned long long)rsp);
|
||||
KiFaultPrintf("User stack @ %016llx:\n", (unsigned long long)rsp);
|
||||
if (!rsp) {
|
||||
printf(" <no user RSP captured>\n");
|
||||
KiFaultPrintf(" <no user RSP captured>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -753,20 +644,20 @@ static void KiDumpUserStack(PROCESS* proc, uintptr_t rsp) {
|
||||
size_t size = 0;
|
||||
const char* mod = KiResolveModuleEx(proc, (uintptr_t)value, &base, &size);
|
||||
if (base) {
|
||||
printf(" [%02d] %016llx : %016llx <%s+0x%016llX>\n",
|
||||
KiFaultPrintf(" [%02d] %016llx : %016llx <%s+0x%016llX>\n",
|
||||
i,
|
||||
(unsigned long long)slot,
|
||||
(unsigned long long)value,
|
||||
mod,
|
||||
(unsigned long long)((uintptr_t)value - base));
|
||||
} else {
|
||||
printf(" [%02d] %016llx : %016llx\n",
|
||||
KiFaultPrintf(" [%02d] %016llx : %016llx\n",
|
||||
i,
|
||||
(unsigned long long)slot,
|
||||
(unsigned long long)value);
|
||||
}
|
||||
} else {
|
||||
printf(" [%02d] %016llx : <unreadable>\n",
|
||||
KiFaultPrintf(" [%02d] %016llx : <unreadable>\n",
|
||||
i,
|
||||
(unsigned long long)slot);
|
||||
}
|
||||
@@ -777,20 +668,20 @@ static void KiDumpUserStack(PROCESS* proc, uintptr_t rsp) {
|
||||
static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) {
|
||||
if (!f) return;
|
||||
|
||||
printf("Regs:\n");
|
||||
printf(" RAX=%016llx RBX=%016llx RCX=%016llx RDX=%016llx\n",
|
||||
KiFaultPrintf("Regs:\n");
|
||||
KiFaultPrintf(" RAX=%016llx RBX=%016llx RCX=%016llx RDX=%016llx\n",
|
||||
(unsigned long long)f->Rax, (unsigned long long)f->Rbx,
|
||||
(unsigned long long)f->Rcx, (unsigned long long)f->Rdx);
|
||||
printf(" RSI=%016llx RDI=%016llx RBP=%016llx RSP=%016llx\n",
|
||||
KiFaultPrintf(" RSI=%016llx RDI=%016llx RBP=%016llx RSP=%016llx\n",
|
||||
(unsigned long long)f->Rsi, (unsigned long long)f->Rdi,
|
||||
(unsigned long long)f->Rbp, (unsigned long long)f->Rsp);
|
||||
printf(" R8 =%016llx R9 =%016llx R10=%016llx R11=%016llx\n",
|
||||
KiFaultPrintf(" R8 =%016llx R9 =%016llx R10=%016llx R11=%016llx\n",
|
||||
(unsigned long long)f->R8, (unsigned long long)f->R9,
|
||||
(unsigned long long)f->R10, (unsigned long long)f->R11);
|
||||
printf(" R12=%016llx R13=%016llx R14=%016llx R15=%016llx\n",
|
||||
KiFaultPrintf(" R12=%016llx R13=%016llx R14=%016llx R15=%016llx\n",
|
||||
(unsigned long long)f->R12, (unsigned long long)f->R13,
|
||||
(unsigned long long)f->R14, (unsigned long long)f->R15);
|
||||
printf(" CS=%04llx SS=%04llx RFLAGS=%016llx\n",
|
||||
KiFaultPrintf(" CS=%04llx SS=%04llx RFLAGS=%016llx\n",
|
||||
(unsigned long long)f->Cs,
|
||||
(unsigned long long)f->Ss,
|
||||
(unsigned long long)f->Rflags);
|
||||
@@ -798,12 +689,12 @@ static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) {
|
||||
if (f->Rip == 0) {
|
||||
uint64_t ret = 0;
|
||||
if (KiUserReadU64((uintptr_t)f->Rsp, &ret)) {
|
||||
printf("Likely null indirect call. Return address from [RSP]:\n");
|
||||
KiFaultPrintf("Likely null indirect call. Return address from [RSP]:\n");
|
||||
KiPrintAddress(proc, " ret=", (uintptr_t)ret);
|
||||
KiDumpBytes(proc, "Bytes near return address", (uintptr_t)ret);
|
||||
KiDumpIndirectCallSlot(proc, (uintptr_t)ret);
|
||||
} else {
|
||||
printf("Likely null indirect call, but [RSP] is unreadable.\n");
|
||||
KiFaultPrintf("Likely null indirect call, but [RSP] is unreadable.\n");
|
||||
}
|
||||
} else {
|
||||
KiDumpBytes(proc, "Bytes near RIP", (uintptr_t)f->Rip);
|
||||
@@ -812,14 +703,6 @@ static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) {
|
||||
KiDumpUserStack(proc, (uintptr_t)f->Rsp);
|
||||
}
|
||||
|
||||
// Prints crash Char.
|
||||
static void KiPrintCrashChar(char c) {
|
||||
if (_InterlockedCompareExchange((volatile long*)&gDebugConsoleOwnsSerial, 1, 1))
|
||||
KiDebugSerialWriteChar(c);
|
||||
if (PrintChar)
|
||||
PrintChar(c, 0xFFAA00, 0x000000);
|
||||
}
|
||||
|
||||
static KSTATUS KiFaultExitStatus(int vector) {
|
||||
switch (vector) {
|
||||
case 0: return STATUS_PROCESS_DIVIDE_BY_ZERO;
|
||||
@@ -840,13 +723,11 @@ static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
|
||||
const char* modName = proc ? KiResolveModule(proc, rip) : "unknown";
|
||||
|
||||
static const char banner[] = "\n*** APPLICATION CRASH ***\n";
|
||||
for (const char* p = banner; *p; p++)
|
||||
KiPrintCrashChar(*p);
|
||||
|
||||
printf_set_putchar(KiPrintCrashChar);
|
||||
KiOutputWrite(HAL_OUTPUT_STREAM_CRASH, 0,
|
||||
0x00FFAA00u, 0x00000000u, banner, sizeof(banner) - 1u);
|
||||
|
||||
if (vector == 14)
|
||||
printf("Process: %s (PID %u) | Fault: %s\n"
|
||||
KiFaultPrintf("Process: %s (PID %u) | Fault: %s\n"
|
||||
"Module: %s | RIP: %016llx | Code: %04llx | CR2: %016llx\n",
|
||||
procName, pid, faultName,
|
||||
modName,
|
||||
@@ -854,7 +735,7 @@ static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
|
||||
(unsigned long long)errCode,
|
||||
(unsigned long long)cr2);
|
||||
else
|
||||
printf("Process: %s (PID %u) | Fault: %s\n"
|
||||
KiFaultPrintf("Process: %s (PID %u) | Fault: %s\n"
|
||||
"Module: %s | RIP: %016llx | Code: %04llx\n",
|
||||
procName, pid, faultName,
|
||||
modName,
|
||||
@@ -864,21 +745,19 @@ static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
|
||||
|
||||
// Implements the finish Faulted Process operation.
|
||||
static _Noreturn void KiFinishFaultedProcess(PROCESS* proc, THREAD* thread, KSTATUS exitStatus) {
|
||||
printf_set_putchar(KiKernelPutChar);
|
||||
|
||||
int exitCode = (int)exitStatus;
|
||||
if (proc) (void)KeTerminateProcess(proc, exitCode);
|
||||
if (thread) KeExitThread(thread, exitCode);
|
||||
|
||||
// KeExitThread normally switches away and never resumes this context. If
|
||||
// KeExitThread normally switches away and never resumes this context. If
|
||||
// a fault path re-enters here during scheduler bring-up or with no runnable
|
||||
// peer yet, do not fall back to a local CLI/HLT dead stop: keep offering the
|
||||
// dead thread to the scheduler with interrupts enabled so waiters such as
|
||||
// the foreground shell can observe the process exit event.
|
||||
while (true) {
|
||||
_enable();
|
||||
HalEnableInterrupts();
|
||||
KeYield();
|
||||
__halt();
|
||||
HalWaitForInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -932,6 +811,7 @@ __declspec(dllexport) KSTATUS KeFaultDispatchFrame(const KFAULT_FRAME* frame) {
|
||||
KiDumpFaultFrame(proc, frame);
|
||||
if (vector == 14) {
|
||||
MmDebugDescribeAddress((uintptr_t)frame->Cr2);
|
||||
if (frame->Rip) MmDebugDescribeAddress((uintptr_t)frame->Rip);
|
||||
if (frame->Rsp) MmDebugDescribeAddress((uintptr_t)frame->Rsp);
|
||||
MmVadDebugDumpProcess(proc);
|
||||
}
|
||||
@@ -994,5 +874,5 @@ __declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip,
|
||||
frame.Rip = rip;
|
||||
frame.Cs = cs;
|
||||
(void)KeFaultDispatchFrame(&frame);
|
||||
while (1) __halt();
|
||||
HalStopProcessor();
|
||||
}
|
||||
Reference in new issue
Block a user