add page cache with read-ahead and dirty writeback, file-backed and anonymous mmap with CoW, VAD-based virtual memory replacing the old user region model, PFN database with eviction and reclaim, file identity for cross-open section sharing, FAT32 path cache and per-file cluster chain cache, serial.sys for COM1 keyboard input, xHCI MSI with INTx and polled fallback, PciEnableMsiAuto, waitable input queue, KeWaitForAnyEvent, per-process thread info syscall, expanded kernel stats (PFN/PTE/VAD/file-cache/disk), pool allocation tags, async I/O user buffer locking, file flush on close, add docs.md.
This commit is contained in:
1 parent
61ec2442eb
commit
2c4a68a75f
114 files changed
+18069
-2620
No files matched your search
+178
-66
@@ -7,7 +7,9 @@
|
||||
#pragma intrinsic(_enable)
|
||||
#include <ntlite/boot_info.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_pfn.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/mm_swap.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
@@ -28,6 +30,9 @@
|
||||
#include <kernel/ke_input.h>
|
||||
#include <kernel/ke_time.h>
|
||||
#include <kernel/ke_display.h>
|
||||
#include <kernel/ke_settings.h>
|
||||
#include <kernel/ke_work.h>
|
||||
#include <kernel/ke_system_root.h>
|
||||
#include <common/ini_parser.h>
|
||||
|
||||
typedef struct {
|
||||
@@ -35,6 +40,90 @@ typedef struct {
|
||||
const HAL_API* hal;
|
||||
} SYSTEM_INIT_ARGS;
|
||||
|
||||
#define KI_SWAP_MIN_MB 4ull
|
||||
#define KI_SWAP_MAX_MB 8192ull
|
||||
|
||||
// Parses a required unsigned decimal integer from settings.ini.
|
||||
// Configuration errors are treated as boot-time corruption instead of guessed defaults.
|
||||
static uint64_t KiParseRequiredUInt64Setting(const char* section, const char* key) {
|
||||
char value[64];
|
||||
KSTATUS s = KeGetSettingString(section, key, value, sizeof(value));
|
||||
if (!KSUCCESS(s)) {
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"settings: missing required [%s] %s entry, status=0x%x\n",
|
||||
section,
|
||||
key,
|
||||
s);
|
||||
}
|
||||
|
||||
uint64_t result = 0;
|
||||
const char* p = value;
|
||||
if (!*p) {
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"settings: empty numeric value for [%s] %s\n",
|
||||
section,
|
||||
key);
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
if (*p < '0' || *p > '9') {
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"settings: invalid numeric value for [%s] %s: %s\n",
|
||||
section,
|
||||
key,
|
||||
value);
|
||||
}
|
||||
|
||||
uint64_t digit = (uint64_t)(*p - '0');
|
||||
if (result > (UINT64_MAX - digit) / 10ull) {
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"settings: numeric value for [%s] %s is too large: %s\n",
|
||||
section,
|
||||
key,
|
||||
value);
|
||||
}
|
||||
|
||||
result = result * 10ull + digit;
|
||||
++p;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Initializes swap according to settings.ini.
|
||||
// A size of zero is the explicit opt-out. Any missing or invalid size is a broken configuration.
|
||||
static void KiInitializeSwapFromSettings(void) {
|
||||
uint64_t swapMb = KiParseRequiredUInt64Setting("vm", "swap_size_mb");
|
||||
if (swapMb == 0) {
|
||||
DbgPrint("swap: disabled by settings\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (swapMb < KI_SWAP_MIN_MB || swapMb > KI_SWAP_MAX_MB) {
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"settings: [vm] swap_size_mb=%llu is outside the allowed range %llu..%llu MB\n",
|
||||
(unsigned long long)swapMb,
|
||||
(unsigned long long)KI_SWAP_MIN_MB,
|
||||
(unsigned long long)KI_SWAP_MAX_MB);
|
||||
}
|
||||
|
||||
char pagefilePath[1024];
|
||||
if (!KeBuildSystemRootPath("pagefile.sys", pagefilePath, sizeof(pagefilePath))) {
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"swap: failed to build system-root pagefile path\n");
|
||||
}
|
||||
|
||||
uint64_t swapBytes = swapMb * 1024ull * 1024ull;
|
||||
KSTATUS s = MmSwapInitialize(SysSvFromCstr(pagefilePath), swapBytes);
|
||||
if (!KSUCCESS(s)) {
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"swap: failed to initialize %llu MB pagefile at %s, status=0x%x\n",
|
||||
(unsigned long long)swapMb,
|
||||
pagefilePath,
|
||||
s);
|
||||
}
|
||||
}
|
||||
|
||||
typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API *);
|
||||
|
||||
// HAL pointer cached at boot for subsystems that still need platform services.
|
||||
@@ -68,7 +157,7 @@ static void KiReclaimBootloaderMemory(const BOOT_INFO* bootInfo) {
|
||||
|
||||
for (uintptr_t page = base; page < end; page += PAGE_SIZE) {
|
||||
if (!KiPageInBootloaderRegion(bootInfo, (PHYSICAL_ADDRESS)page)) continue;
|
||||
MmFreeFrame((PHYSICAL_ADDRESS)page);
|
||||
MmPfnFree((PHYSICAL_ADDRESS)page);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +175,7 @@ static void KiResolveSystemImports(BOOT_INFO* bootInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Phase Helper: Initialize dynamically loaded .sys drivers.
|
||||
static void KiDriversIniCb(const char* section, const char* key,
|
||||
const char* val, void* ud) {
|
||||
@@ -120,7 +210,7 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
|
||||
// Map the PE image into kernel space.
|
||||
uintptr_t mappedBase = 0;
|
||||
KSTATUS st = LdrLoadImage(rawPeBuffer, mod->ImageSize, &mappedBase, false);
|
||||
KSTATUS st = LdrLoadKernelImage(rawPeBuffer, mod->ImageSize, &mappedBase);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("ERROR: Failed to map driver %s (0x%X)\n", mod->Name, st);
|
||||
continue;
|
||||
@@ -129,7 +219,9 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
// Update the struct so other modules can find it.
|
||||
mod->VirtBase = mappedBase;
|
||||
|
||||
// Resolve imports (can now see Kernel, HAL, and previous drivers).
|
||||
// Resolve imports while the image is still writable. Kernel driver images
|
||||
// stay writable until the loader grows real section-backed mappings; the
|
||||
// loader still clears NX for the whole image before returning it.
|
||||
if (!KSUCCESS(LdrResolveImports(mappedBase))) {
|
||||
DbgPrint("ERROR: Failed to resolve imports for %s\n", mod->Name);
|
||||
continue;
|
||||
@@ -152,9 +244,24 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
KSTATUS s;
|
||||
FILE_OBJECT* f;
|
||||
|
||||
s = IoOpenFile(SYS_SV_LITERAL("drivers\\drivers.ini"), &f);
|
||||
s = KeInitializeSystemRootVolume();
|
||||
if (s != STATUS_SUCCESS) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to open drivers.ini (status %d)\n", s);
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to find system root volume (status %d)\n", s);
|
||||
}
|
||||
|
||||
char root[4];
|
||||
root[0] = (char)('0' + KeGetSystemRootDrive());
|
||||
root[1] = ':';
|
||||
root[2] = '/';
|
||||
root[3] = '\0';
|
||||
s = IoSetCwd(SysSvFromCstr(root));
|
||||
if (s != STATUS_SUCCESS) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to set system CWD to %s (status %d)\n", root, s);
|
||||
}
|
||||
|
||||
s = KeOpenSystemRootFile("drivers\\drivers.ini", &f);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to open drivers.ini on system root (status %d)\n", s);
|
||||
}
|
||||
|
||||
uint64_t size = IoGetFileSize(f) + 1;
|
||||
@@ -174,26 +281,27 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
for (uint32_t i = 0; i < arr_len(runtimeKeys); i++) {
|
||||
DbgPrint("Found runtime driver: %s\n", runtimeKeys[i]);
|
||||
|
||||
// Load the driver.
|
||||
char path[1024];
|
||||
char relative[512];
|
||||
size_t len = strlen(runtimeKeys[i]);
|
||||
char* path = MmAllocatePool(len + 10);
|
||||
if (!path) {
|
||||
DbgPrint("KernelMain: Failed to allocate memory for driver path. How?\n");
|
||||
if (len + 9 > sizeof(relative)) {
|
||||
DbgPrint("KiInitializeDrivers: runtime driver path too long: %s\n", runtimeKeys[i]);
|
||||
continue;
|
||||
}
|
||||
memcpy(relative, "drivers\\", 8);
|
||||
memcpy(relative + 8, runtimeKeys[i], len + 1);
|
||||
|
||||
// memcpy maneuvers.
|
||||
memcpy(path, "drivers\\", 8);
|
||||
memcpy(path + 8, runtimeKeys[i], len + 1);
|
||||
if (!KeBuildSystemRootPath(relative, path, sizeof(path))) {
|
||||
DbgPrint("KiInitializeDrivers: failed to build runtime driver path: %s\n", runtimeKeys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
s = IoOpenFile(SysSvFromCstr(path), &f);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
DbgPrint("KiInitializeDrivers: Failed to open runtime driver: %s (%s)\n", runtimeKeys[i], path);
|
||||
continue; // Skip this driver but keep going with others
|
||||
continue;
|
||||
}
|
||||
|
||||
MmFreePool(path);
|
||||
|
||||
uint64_t drvSize = IoGetFileSize(f);
|
||||
void* drvMem = MmAllocatePool(drvSize);
|
||||
if (!KSUCCESS(IoReadFile(f, drvMem, drvSize, &got))) {
|
||||
@@ -204,7 +312,7 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
IoCloseFile(f);
|
||||
|
||||
uintptr_t mappedBase = 0;
|
||||
if (!KSUCCESS(LdrLoadImage(drvMem, drvSize, &mappedBase, false))) { // Load into kernel space
|
||||
if (!KSUCCESS(LdrLoadKernelImage(drvMem, drvSize, &mappedBase))) { // Load into kernel space
|
||||
DbgPrint("KiInitializeDrivers: Failed to load runtime driver: %s\n", runtimeKeys[i]);
|
||||
MmFreePool(drvMem);
|
||||
continue;
|
||||
@@ -215,7 +323,6 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
DbgPrint("KiInitializeDrivers: Failed to resolve imports for runtime driver: %s\n", runtimeKeys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Runtime modules may also export DDIs to drivers loaded later. Boot
|
||||
// drivers were already registered above; keep the module model uniform.
|
||||
LdrRegisterModule(runtimeKeys[i], mappedBase);
|
||||
@@ -250,28 +357,15 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
KSTATUS s;
|
||||
FILE_OBJECT* f;
|
||||
|
||||
// Read the executable file into a raw buffer.
|
||||
s = IoOpenFile(SysSvFromCstr(filePath), &f);
|
||||
// Kernel-launched user images are rooted at the selected system volume.
|
||||
s = KeOpenSystemRootFile(filePath, &f);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
DbgPrint("Failed to load file: \"%s\"!\n", filePath);
|
||||
DbgPrint("Failed to load system file: \"%s\" (status %d)!\n", filePath, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t size = IoGetFileSize(f);
|
||||
|
||||
void* mem = MmAllocatePool(size);
|
||||
if (!mem) {
|
||||
IoCloseFile(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t got = 0;
|
||||
s = IoReadFile(f, mem, size, &got);
|
||||
IoCloseFile(f);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreePool(mem);
|
||||
return 0;
|
||||
}
|
||||
(void)size;
|
||||
|
||||
// Create the process object.
|
||||
PROCESS* proc;
|
||||
@@ -280,7 +374,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
s = KeCreateProcess(filePath, &proc);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("Failed to create process. System halted.\n");
|
||||
MmFreePool(mem);
|
||||
IoCloseFile(f);
|
||||
return 0;
|
||||
}
|
||||
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, (args && args[0]) ? args : filePath);
|
||||
@@ -291,17 +385,17 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
SysSvFromCstr(proc->CurrentDirectory));
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("KiLaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
|
||||
MmFreePool(mem);
|
||||
IoCloseFile(f);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse the raw PE file and map it into executable memory.
|
||||
// Map the executable through an image section backed by the open file.
|
||||
uintptr_t imageBase = 0;
|
||||
s = LdrLoadImageForProcess(proc, mem, size, &imageBase);
|
||||
MmFreePool(mem);
|
||||
s = LdrLoadImageFileForProcess(proc, f, &imageBase);
|
||||
IoCloseFile(f);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("KiLaunchUserProcess: LdrLoadImage failed with status: %d\n", s);
|
||||
DbgPrint("KiLaunchUserProcess: LdrLoadImageFileForProcess failed with status: %d\n", s);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
@@ -313,6 +407,13 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = LdrProtectImageForProcess(proc, imageBase, true);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("KiLaunchUserProcess: Failed to protect EXE image for %s (status %d)\n", filePath, s);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
|
||||
proc->ImageBase = imageBase;
|
||||
proc->ImageSize = LdrGetMappedImageSize(proc, imageBase);
|
||||
|
||||
@@ -327,7 +428,9 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
DbgPrint("KiLaunchUserProcess: Thread created and queued!\n");
|
||||
uint32_t tid = thread->ThreadId;
|
||||
DbgPrint("KiLaunchUserProcess: Thread created and queued! TID=%u\n", tid);
|
||||
ObDereferenceObject(&thread->Header);
|
||||
|
||||
// Caller owns the reference returned by KeCreateProcess.
|
||||
return proc;
|
||||
@@ -344,35 +447,39 @@ void SystemInitThread(void* arg) {
|
||||
KiInitializeDrivers(args->bootInfo, args->hal);
|
||||
KeTimeReloadSettings();
|
||||
|
||||
KiInitializeSwapFromSettings();
|
||||
|
||||
KiResolveSystemImports(args->bootInfo);
|
||||
KiReclaimBootloaderMemory(args->bootInfo);
|
||||
|
||||
if (args->bootInfo->Debug & KE_DEBUG_SELFTEST)
|
||||
KiRunKernelSelfTests(args->hal);
|
||||
|
||||
// Launch User-Mode init.exe and restart it until it exits cleanly.
|
||||
while (true) {
|
||||
PROCESS* shellProc = KiLaunchUserProcess("system/init.exe", args->bootInfo->CommandLine);
|
||||
if (!shellProc) {
|
||||
DbgPrint("SystemInitThread: Failed to start system/init.exe; retrying.\n");
|
||||
KeYield();
|
||||
continue;
|
||||
}
|
||||
|
||||
KeWaitForEvent(shellProc->ExitEvent, 0);
|
||||
int exitCode = shellProc->ExitCode;
|
||||
DbgPrint("SystemInitThread: init.exe exited with code %d.\n", exitCode);
|
||||
ObDereferenceObject(&shellProc->Header);
|
||||
|
||||
if (exitCode == 0) break;
|
||||
DbgPrint("SystemInitThread: Restarting init.exe.\n");
|
||||
// Launch User-Mode init.exe.
|
||||
PROCESS* shellProc = KiLaunchUserProcess("system/init.exe", args->bootInfo->CommandLine);
|
||||
if (!shellProc) {
|
||||
DbgPrint("SystemInitThread: Failed to start system/init.exe.\n");
|
||||
KePanic(KE_PANIC_CODE_INIT, "SystemInitThread: Failed to start system/init.exe.\n");
|
||||
}
|
||||
|
||||
DbgPrint("SystemInitThread: init.exe completed successfully; shutting down.\n");
|
||||
KeWaitForEvent(shellProc->ExitEvent, 0);
|
||||
int exitCode = shellProc->ExitCode;
|
||||
if (exitCode != 0) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "SystemInitThread: init.exe exited with code %d.\n", exitCode);
|
||||
}
|
||||
DbgPrint("SystemInitThread: init.exe exited with code %d.\n", exitCode);
|
||||
ObDereferenceObject(&shellProc->Header);
|
||||
|
||||
if (args->bootInfo->Debug & (KE_DEBUG_CONSOLE | KE_DEBUG_FILE_LOG)) {
|
||||
KSTATUS logStatus = KeFlushDebugLogToFile(SYS_SV_LITERAL("0:/kernel.log"));
|
||||
if (!KSUCCESS(logStatus))
|
||||
DbgPrint("debuglog: shutdown flush to 0:/kernel.log failed st=0x%x\n", logStatus);
|
||||
char logPath[1024];
|
||||
KSTATUS logStatus = STATUS_NOT_FOUND;
|
||||
if (KeBuildSystemRootPath("kernel.log", logPath, sizeof(logPath))) {
|
||||
logStatus = KeFlushDebugLogToFile(SysSvFromCstr(logPath));
|
||||
if (!KSUCCESS(logStatus))
|
||||
DbgPrint("debuglog: shutdown flush to %s failed st=0x%x\n", logPath, logStatus);
|
||||
} else {
|
||||
DbgPrint("debuglog: shutdown flush skipped; system root path unavailable\n");
|
||||
}
|
||||
}
|
||||
gKmainHal->Power.Shutdown();
|
||||
MmFreePool(args);
|
||||
@@ -459,6 +566,11 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
|
||||
KeInitializeScheduler();
|
||||
|
||||
KSTATUS workStatus = KeStartWorkQueue();
|
||||
if (!KSUCCESS(workStatus)) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "workq: failed to start passive work queue, status=0x%x\n", workStatus);
|
||||
}
|
||||
|
||||
KSTATUS smpSchedStatus = KeAdmitApplicationProcessorsToScheduler();
|
||||
if (!KSUCCESS(smpSchedStatus)) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "SMP: failed to admit APs to scheduler, status=0x%x\n", smpSchedStatus);
|
||||
@@ -471,11 +583,11 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
initArgs->bootInfo = bootInfo;
|
||||
initArgs->hal = hal;
|
||||
|
||||
// Spawn the Initialization Thread.
|
||||
THREAD* initThread = (THREAD*)0;
|
||||
KSTATUS st = KeCreateKernelThread((uintptr_t)SystemInitThread,
|
||||
(uintptr_t)initArgs,
|
||||
PriorityNormal, &initThread);
|
||||
// Spawn the Initialization Thread. No creator reference is needed; the
|
||||
// scheduler owns the live thread lifetime.
|
||||
KSTATUS st = KeCreateKernelThread((uintptr_t)SystemInitThread,
|
||||
(uintptr_t)initArgs,
|
||||
PriorityNormal, (THREAD**)0);
|
||||
|
||||
if (!KSUCCESS(st)) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "KernelMain: Failed to create SystemInitThread (status 0x%x)\n", st);
|
||||
|
||||
Reference in new issue
Block a user