823 lines
30 KiB
C
823 lines
30 KiB
C
// Kernel entry point. This file connects boot information to core subsystem initialization and scheduler start.
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <intrin.h>
|
|
#pragma intrinsic(_disable)
|
|
#include <ntlite/boot_info.h>
|
|
#include <kernel/ke_hal.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>
|
|
#include <kernel/ke_sched.h>
|
|
#include <kernel/ke_cpu.h>
|
|
#include <kernel/ke_idt.h>
|
|
#include <kernel/ke_irq.h>
|
|
#include <kernel/ke_dpc.h>
|
|
#include <kernel/ke_syscall.h>
|
|
#include <ntlite/ddi/net_device.h>
|
|
#include <kernel/ke_debug.h>
|
|
#include <kernel/ldr_image.h>
|
|
#include <kernel/hal/hal_api.h>
|
|
#include <kernel/io_file.h>
|
|
#include <kernel/io_volume.h>
|
|
#include <kernel/io_partition.h>
|
|
#include <kernel/io_ddk.h>
|
|
#include <kernel/ke_selftest.h>
|
|
#include <kernel/ke_smp.h>
|
|
#include <kernel/ke_input.h>
|
|
#include <kernel/ke_time.h>
|
|
#include <kernel/ke_display.h>
|
|
#include <kernel/ke_audio.h>
|
|
#include <kernel/ke_settings.h>
|
|
#include <kernel/ke_work.h>
|
|
#include <kernel/ke_system_root.h>
|
|
#include <kernel/se_security.h>
|
|
#include <kernel/ke_boot_config.h>
|
|
|
|
typedef struct {
|
|
BOOT_INFO* BootInfo;
|
|
const HAL_API* Hal;
|
|
KE_BOOT_CONFIG BootConfig;
|
|
} 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);
|
|
}
|
|
|
|
uint64_t swapBytes = swapMb * 1024ull * 1024ull;
|
|
KSTATUS s = MmSwapInitialize("pagefile.sys", swapBytes);
|
|
if (!KSUCCESS(s)) {
|
|
KePanic(KE_PANIC_CODE_INIT,
|
|
"swap: failed to initialize %llu MB system-root pagefile, status=0x%x\n",
|
|
(unsigned long long)swapMb,
|
|
s);
|
|
}
|
|
}
|
|
|
|
typedef KSTATUS (*DRIVER_ENTRY_FN)(const NTL_DDK* ddk, NTL_DRIVER_INIT* init);
|
|
|
|
// HAL pointer cached at boot for subsystems that still need platform services.
|
|
static const HAL_API* gKmainHal;
|
|
|
|
// Implements the page In Bootloader Region operation.
|
|
static int KiPageInBootloaderRegion(const BOOT_INFO* bootInfo, PHYSICAL_ADDRESS page) {
|
|
for (uint32_t i = 0; i < bootInfo->MemoryMap.RegionCount; i++) {
|
|
const MEMORY_REGION* r = &bootInfo->MemoryMap.Regions[i];
|
|
if (r->Type != MemoryBootloader) continue;
|
|
|
|
uintptr_t start = r->Base & PAGE_MASK;
|
|
uintptr_t end = (r->Base + r->Length + PAGE_SIZE - 1) & PAGE_MASK;
|
|
if ((uintptr_t)page >= start && (uintptr_t)page < end) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Reclaims one exact page-backed blob after its boot consumer has copied or materialized it.
|
|
static void KiReclaimBootBlob(const BOOT_INFO* bootInfo, uintptr_t physicalBase, uint32_t pageCount) {
|
|
for (uint32_t index = 0; index < pageCount; ++index) {
|
|
PHYSICAL_ADDRESS page = (PHYSICAL_ADDRESS)(physicalBase + (uintptr_t)index * PAGE_SIZE);
|
|
if (KiPageInBootloaderRegion(bootInfo, page))
|
|
MmPfnFree(page);
|
|
}
|
|
}
|
|
|
|
// Reclaims raw boot-driver/configuration pages while leaving mapped kernel/HAL/page-table memory owned by bootstrap state.
|
|
static void KiReclaimBootloaderMemory(const BOOT_INFO* bootInfo) {
|
|
const BOOT_FILE_LIST* list = &bootInfo->BootDrivers;
|
|
const BOOT_FILE* files = list->FilesPhysical
|
|
? (const BOOT_FILE*)(bootInfo->PhysicalMapBase + list->FilesPhysical)
|
|
: NULL;
|
|
|
|
for (uint32_t index = 0; files && index < list->Count; ++index) {
|
|
if (files[index].PhysicalBase && files[index].PageCount)
|
|
KiReclaimBootBlob(bootInfo, files[index].PhysicalBase, files[index].PageCount);
|
|
}
|
|
|
|
if (list->FilesPhysical && list->PageCount)
|
|
KiReclaimBootBlob(bootInfo, list->FilesPhysical, list->PageCount);
|
|
if (bootInfo->BootIni.PhysicalBase && bootInfo->BootIni.PageCount)
|
|
KiReclaimBootBlob(bootInfo, bootInfo->BootIni.PhysicalBase, bootInfo->BootIni.PageCount);
|
|
}
|
|
|
|
// Returns whether one drivers.ini line character is horizontal whitespace.
|
|
static int KiDriverConfigSpace(char c) {
|
|
return c == ' ' || c == '\t' || c == '\r';
|
|
}
|
|
|
|
// Trims horizontal whitespace from one immutable drivers.ini line span.
|
|
static void KiDriverConfigTrim(const char** begin, const char** end) {
|
|
while (*begin < *end && KiDriverConfigSpace(**begin))
|
|
++*begin;
|
|
while (*end > *begin && KiDriverConfigSpace((*end)[-1]))
|
|
--*end;
|
|
}
|
|
|
|
// Returns whether one immutable drivers.ini span exactly matches an ASCII string.
|
|
static int KiDriverConfigSpanEquals(const char* begin, const char* end, const char* text) {
|
|
while (begin < end && *text) {
|
|
if (*begin++ != *text++)
|
|
return 0;
|
|
}
|
|
return begin == end && *text == '\0';
|
|
}
|
|
|
|
// Loads one runtime driver filename from the system-root drivers directory.
|
|
static KSTATUS KiLoadRuntimeDriverByName(const char* name, const NTL_DDK* ddk) {
|
|
char relative[512];
|
|
size_t length = strlen(name);
|
|
if (length + 9u > sizeof(relative))
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
|
|
memcpy(relative, "drivers/", 8);
|
|
memcpy(relative + 8, name, length + 1);
|
|
FILE_OBJECT* file = NULL;
|
|
KSTATUS status = KeOpenSystemRootFile(relative, &file);
|
|
if (!KSUCCESS(status))
|
|
return status;
|
|
|
|
uint64_t driverSize = IoGetFileSize(file);
|
|
if (driverSize == 0 || driverSize > SIZE_MAX) {
|
|
IoCloseFile(file);
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
void* driverMemory = MmAllocatePool((size_t)driverSize);
|
|
if (!driverMemory) {
|
|
IoCloseFile(file);
|
|
return NTLSTATUS_NO_MEMORY;
|
|
}
|
|
|
|
size_t got = 0;
|
|
status = IoReadFile(file, driverMemory, (size_t)driverSize, &got);
|
|
IoCloseFile(file);
|
|
if (!KSUCCESS(status) || got != (size_t)driverSize) {
|
|
MmFreePool(driverMemory);
|
|
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
}
|
|
|
|
uintptr_t mappedBase = 0;
|
|
status = LdrLoadKernelImage(driverMemory, (size_t)driverSize, &mappedBase);
|
|
MmFreePool(driverMemory);
|
|
if (!KSUCCESS(status))
|
|
return status;
|
|
|
|
status = LdrResolveImports(mappedBase);
|
|
if (!KSUCCESS(status))
|
|
return status;
|
|
|
|
status = LdrRegisterModule(name, mappedBase);
|
|
if (!KSUCCESS(status))
|
|
return status;
|
|
|
|
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)LdrGetEntryPoint(mappedBase);
|
|
if (!entry)
|
|
return NTLSTATUS_FAILURE;
|
|
|
|
NTL_DRIVER_INIT init = {0};
|
|
return entry(ddk, &init);
|
|
}
|
|
|
|
// Scans only the [runtime] section of drivers.ini and loads each filename immediately.
|
|
static KSTATUS KiLoadRuntimeDriversFromConfig(const char* text, size_t length, const NTL_DDK* ddk) {
|
|
int inRuntime = 0;
|
|
size_t offset = 0;
|
|
|
|
while (offset < length) {
|
|
size_t lineStart = offset;
|
|
while (offset < length && text[offset] != '\n')
|
|
++offset;
|
|
size_t lineEnd = offset;
|
|
if (offset < length)
|
|
++offset;
|
|
|
|
const char* begin = text + lineStart;
|
|
const char* end = text + lineEnd;
|
|
KiDriverConfigTrim(&begin, &end);
|
|
if (begin == end || *begin == ';' || *begin == '#')
|
|
continue;
|
|
|
|
if (*begin == '[') {
|
|
if (end[-1] != ']')
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
const char* sectionBegin = begin + 1;
|
|
const char* sectionEnd = end - 1;
|
|
KiDriverConfigTrim(§ionBegin, §ionEnd);
|
|
inRuntime = KiDriverConfigSpanEquals(sectionBegin, sectionEnd, "runtime");
|
|
continue;
|
|
}
|
|
|
|
if (!inRuntime)
|
|
continue;
|
|
|
|
size_t nameLength = (size_t)(end - begin);
|
|
if (nameLength == 0 || nameLength >= BOOT_FILE_NAME_MAX)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
for (const char* p = begin; p < end; ++p) {
|
|
if (*p == '=' || *p == '/' || *p == '\\')
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
char name[BOOT_FILE_NAME_MAX];
|
|
memcpy(name, begin, nameLength);
|
|
name[nameLength] = '\0';
|
|
DbgPrint("Found runtime driver: %s\n", name);
|
|
|
|
KSTATUS status = KiLoadRuntimeDriverByName(name, ddk);
|
|
if (!KSUCCESS(status))
|
|
DbgPrint("KiInitializeDrivers: runtime driver %s failed, status=0x%x\n", name, status);
|
|
}
|
|
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Initializes drivers.
|
|
static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
|
(void)hal;
|
|
const NTL_DDK* ddk = IoGetDdk();
|
|
DbgPrint("KiInitializeDrivers: Initializing drivers...\n");
|
|
|
|
// Boot drivers are page-backed raw files supplied by the bootloader.
|
|
const BOOT_FILE* bootFiles = bootInfo->BootDrivers.FilesPhysical
|
|
? (const BOOT_FILE*)(bootInfo->PhysicalMapBase + bootInfo->BootDrivers.FilesPhysical)
|
|
: NULL;
|
|
|
|
for (uint32_t i = 0; bootFiles && i < bootInfo->BootDrivers.Count; ++i) {
|
|
const BOOT_FILE* file = &bootFiles[i];
|
|
if (!file->PhysicalBase || !file->Length) {
|
|
DbgPrint("KiInitializeDrivers: invalid boot driver descriptor at index %u\n", i);
|
|
continue;
|
|
}
|
|
|
|
DbgPrint("KiInitializeDrivers: Loading boot driver %s\n", file->Name);
|
|
void* rawPeBuffer = (void*)(bootInfo->PhysicalMapBase + file->PhysicalBase);
|
|
|
|
uintptr_t mappedBase = 0;
|
|
KSTATUS st = LdrLoadKernelImage(rawPeBuffer, file->Length, &mappedBase);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("ERROR: Failed to map driver %s (0x%X)\n", file->Name, st);
|
|
continue;
|
|
}
|
|
|
|
if (!KSUCCESS(LdrResolveImports(mappedBase))) {
|
|
DbgPrint("ERROR: Failed to resolve imports for %s\n", file->Name);
|
|
continue;
|
|
}
|
|
|
|
KSTATUS registerStatus = LdrRegisterModule(file->Name, mappedBase);
|
|
if (!KSUCCESS(registerStatus)) {
|
|
DbgPrint("ERROR: Failed to register driver %s (0x%X)\n", file->Name, registerStatus);
|
|
continue;
|
|
}
|
|
|
|
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)LdrGetEntryPoint(mappedBase);
|
|
if (entry) {
|
|
NTL_DRIVER_INIT init = {0};
|
|
KSTATUS ds = entry(ddk, &init);
|
|
if (!KSUCCESS(ds))
|
|
DbgPrint("WARNING: Driver %s returned failure status 0x%X\n", file->Name, ds);
|
|
}
|
|
}
|
|
|
|
// runtime drivers.
|
|
KSTATUS s;
|
|
FILE_OBJECT* f;
|
|
|
|
s = KeInitializeSystemRootVolume();
|
|
if (s != NTLSTATUS_SUCCESS) {
|
|
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to find system root volume (status %d)\n", s);
|
|
}
|
|
|
|
|
|
s = KeOpenSystemRootFile("drivers/drivers.ini", &f);
|
|
if (s != NTLSTATUS_SUCCESS) {
|
|
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to open drivers.ini on system root (status %d)\n", s);
|
|
}
|
|
|
|
uint64_t fileSize = IoGetFileSize(f);
|
|
if (fileSize == 0 || fileSize >= SIZE_MAX) {
|
|
IoCloseFile(f);
|
|
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: drivers.ini has an invalid size.\n");
|
|
}
|
|
|
|
size_t size = (size_t)fileSize;
|
|
char* mem = (char*)MmAllocatePool(size + 1u);
|
|
if (!mem) {
|
|
IoCloseFile(f);
|
|
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to allocate drivers.ini buffer.\n");
|
|
}
|
|
|
|
size_t got = 0;
|
|
KSTATUS readStatus = IoReadFile(f, mem, size, &got);
|
|
IoCloseFile(f);
|
|
if (!KSUCCESS(readStatus) || got != size) {
|
|
MmFreePool(mem);
|
|
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to read drivers.ini.\n");
|
|
}
|
|
mem[got] = '\0';
|
|
|
|
KSTATUS runtimeStatus = KiLoadRuntimeDriversFromConfig(mem, got, ddk);
|
|
MmFreePool(mem);
|
|
if (!KSUCCESS(runtimeStatus)) {
|
|
KePanic(KE_PANIC_CODE_INIT,
|
|
"KiInitializeDrivers: drivers.ini [runtime] is malformed, status=0x%x\n",
|
|
runtimeStatus);
|
|
}
|
|
}
|
|
|
|
// Phase Helper: Load and launch a process from the filesystem.
|
|
// Returns the new PROCESS* with one reference owned by the caller.
|
|
// Returns NULL on failure.
|
|
static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
|
KSTATUS s;
|
|
FILE_OBJECT* f;
|
|
|
|
// Kernel-launched user images are rooted at the selected system volume.
|
|
s = KeOpenSystemRootFile(filePath, &f);
|
|
if (s != NTLSTATUS_SUCCESS) {
|
|
DbgPrint("Failed to load system file: \"%s\" (status %d)!\n", filePath, s);
|
|
return 0;
|
|
}
|
|
|
|
uint64_t size = IoGetFileSize(f);
|
|
(void)size;
|
|
|
|
// Create the process object.
|
|
PROCESS* proc;
|
|
THREAD* thread;
|
|
|
|
s = KeCreateProcess(filePath, SeGetRootSecurityContext(), &proc);
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("Failed to create process. System halted.\n");
|
|
IoCloseFile(f);
|
|
return 0;
|
|
}
|
|
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, (args && args[0]) ? args : filePath);
|
|
|
|
s = KeSetupUserProcessEnvironment(proc,
|
|
SysSvFromCstr(filePath),
|
|
SysSvFromCstr(args));
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
|
|
IoCloseFile(f);
|
|
ObDereferenceObject(&proc->Header);
|
|
return 0;
|
|
}
|
|
|
|
// Bootstrap init receives read-only system-volume authority, separate namespace-seeding
|
|
// authority, and the volume-manager capability used to supervise RSSRV publication.
|
|
IO_VOLUME* systemVolume = (IO_VOLUME*)0;
|
|
SYS_HANDLE systemVolumeHandle = SYS_HANDLE_INVALID;
|
|
SYS_HANDLE namespaceVolumeHandle = SYS_HANDLE_INVALID;
|
|
SYS_HANDLE volumeManagerHandle = SYS_HANDLE_INVALID;
|
|
s = KeReferenceSystemRootVolume(&systemVolume);
|
|
if (KSUCCESS(s)) {
|
|
s = KeCreateHandle(proc, &systemVolume->Header,
|
|
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN,
|
|
&systemVolumeHandle);
|
|
if (KSUCCESS(s)) {
|
|
s = KeCreateHandle(proc, &systemVolume->Header,
|
|
SYS_RIGHT_DUPLICATE | SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY |
|
|
SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY,
|
|
&namespaceVolumeHandle);
|
|
}
|
|
IoReleaseVolume(systemVolume);
|
|
}
|
|
if (KSUCCESS(s)) {
|
|
IO_VOLUME_MANAGER* volumeManager = (IO_VOLUME_MANAGER*)0;
|
|
s = IoReferenceVolumeManager(&volumeManager);
|
|
if (KSUCCESS(s)) {
|
|
s = KeCreateHandle(proc, &volumeManager->Header,
|
|
SYS_RIGHT_DUPLICATE | SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY |
|
|
SYS_VOLUME_MANAGER_RIGHT_ENUMERATE | SYS_VOLUME_MANAGER_RIGHT_OPEN |
|
|
SYS_VOLUME_MANAGER_RIGHT_WAIT,
|
|
&volumeManagerHandle);
|
|
ObDereferenceObject(&volumeManager->Header);
|
|
}
|
|
}
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: Failed to create bootstrap startup capabilities. Status: %d\n", s);
|
|
IoCloseFile(f);
|
|
ObDereferenceObject(&proc->Header);
|
|
return 0;
|
|
}
|
|
SYS_STARTUP_CAPABILITY startupCapabilities[3] = {
|
|
{ SYS_STARTUP_CAP_SYSTEM_VOLUME, systemVolumeHandle },
|
|
{ SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE, namespaceVolumeHandle },
|
|
{ SYS_STARTUP_CAP_VOLUME_MANAGER, volumeManagerHandle },
|
|
};
|
|
s = KeSetProcessStartupCapabilities(proc, startupCapabilities, 3);
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: Failed to install startup capabilities. Status: %d\n", s);
|
|
IoCloseFile(f);
|
|
ObDereferenceObject(&proc->Header);
|
|
return 0;
|
|
}
|
|
|
|
// Map the executable through an image section backed by the open file.
|
|
uintptr_t imageBase = 0;
|
|
s = LdrLoadImageFileForProcess(proc, f, &imageBase);
|
|
IoCloseFile(f);
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: LdrLoadImageFileForProcess failed with status: %d\n", s);
|
|
ObDereferenceObject(&proc->Header);
|
|
return 0;
|
|
}
|
|
|
|
s = LdrRequireNoImports(imageBase);
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: bootstrap image %s must be import-free (status %d)\n", filePath, s);
|
|
ObDereferenceObject(&proc->Header);
|
|
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);
|
|
|
|
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
|
|
|
// Create the main thread last. KeCreateThread queues it before returning.
|
|
const KE_THREAD_CREATE_PARAMETERS threadParameters = KE_THREAD_CREATE_NORMAL(PriorityNormal);
|
|
s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, &threadParameters, &thread);
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: Failed to create thread. Status: %d\n", s);
|
|
proc->ImageBase = 0;
|
|
proc->ImageSize = 0;
|
|
ObDereferenceObject(&proc->Header);
|
|
return 0;
|
|
}
|
|
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;
|
|
}
|
|
|
|
// Implements the system Init Thread operation.
|
|
void SystemInitThread(void* arg) {
|
|
SYSTEM_INIT_ARGS* args = (SYSTEM_INIT_ARGS*)arg;
|
|
|
|
DbgPrint("SystemInitThread: Thread started. Igniting the system...\n");
|
|
|
|
KiRunEarlyKernelSelfTests();
|
|
|
|
KiInitializeDrivers(args->BootInfo, args->Hal);
|
|
|
|
KeTimeReloadSettings();
|
|
|
|
KiInitializeSwapFromSettings();
|
|
|
|
KiReclaimBootloaderMemory(args->BootInfo);
|
|
|
|
if (args->BootConfig.Debug & KE_DEBUG_SELFTEST)
|
|
KiRunKernelSelfTests(args->Hal);
|
|
|
|
// Launch User-Mode init.exe.
|
|
PROCESS* shellProc = KiLaunchUserProcess("system/init.exe", args->BootConfig.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");
|
|
}
|
|
|
|
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->BootConfig.Debug & (KE_DEBUG_CONSOLE | KE_DEBUG_FILE_LOG)) {
|
|
KSTATUS logStatus = KeFlushDebugLogToSystemRoot("kernel.log");
|
|
if (!KSUCCESS(logStatus))
|
|
DbgPrint("debuglog: shutdown flush failed st=0x%x\n", logStatus);
|
|
}
|
|
gKmainHal->Power.Shutdown();
|
|
MmFreePool(args);
|
|
}
|
|
|
|
// Returns whether one page-backed boot allocation is aligned, nonempty, and does not overflow.
|
|
static int KiValidateBootPageRange(uintptr_t physicalBase, uint32_t pageCount) {
|
|
if (!physicalBase || (physicalBase & (PAGE_SIZE - 1u)) != 0 || pageCount == 0)
|
|
return 0;
|
|
|
|
uint64_t byteCount = (uint64_t)pageCount * PAGE_SIZE;
|
|
return byteCount != 0 && physicalBase <= UINTPTR_MAX - byteCount;
|
|
}
|
|
|
|
// Returns whether one page-backed raw boot file is fully contained in its recorded allocation.
|
|
static int KiValidateBootFile(const BOOT_FILE* file) {
|
|
if (!file || !KiValidateBootPageRange(file->PhysicalBase, file->PageCount) || file->Length == 0)
|
|
return 0;
|
|
|
|
uint64_t capacity = (uint64_t)file->PageCount * PAGE_SIZE;
|
|
if ((uint64_t)file->Length + 1u > capacity || file->Name[0] == '\0')
|
|
return 0;
|
|
return memchr(file->Name, '\0', sizeof(file->Name)) != NULL;
|
|
}
|
|
|
|
// Returns whether one fixed core-image descriptor fits inside its page-backed physical allocation.
|
|
static int KiValidateBootImage(const BOOT_IMAGE* image, int requireEntry) {
|
|
if (!image || !image->VirtualBase || (image->VirtualBase & (PAGE_SIZE - 1u)) != 0 ||
|
|
!KiValidateBootPageRange(image->PhysicalBase, image->PageCount) || !image->ImageSize) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t capacity = (uint64_t)image->PageCount * PAGE_SIZE;
|
|
if (image->ImageSize > capacity || image->VirtualBase > UINTPTR_MAX - image->ImageSize)
|
|
return 0;
|
|
if (requireEntry && image->EntryRva >= image->ImageSize)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
// Validates the exact bootloader/kernel handoff contract before dereferencing variable physical blobs.
|
|
static int KiValidateBootInfo(const BOOT_INFO* bootInfo) {
|
|
if (!bootInfo || bootInfo->Magic != BOOT_INFO_MAGIC ||
|
|
bootInfo->Version != BOOT_INFO_VERSION || bootInfo->Size != sizeof(BOOT_INFO) ||
|
|
bootInfo->MemoryMap.Magic != MEMORY_MAP_MAGIC ||
|
|
bootInfo->MemoryMap.RegionCount > MEMORY_MAX_REGIONS ||
|
|
bootInfo->PhysicalMapBase == 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t bootIniCapacity = (uint64_t)bootInfo->BootIni.PageCount * PAGE_SIZE;
|
|
if (!KiValidateBootPageRange(bootInfo->BootIni.PhysicalBase, bootInfo->BootIni.PageCount) ||
|
|
bootInfo->BootIni.Length == 0 || (uint64_t)bootInfo->BootIni.Length + 1u > bootIniCapacity ||
|
|
bootInfo->BootIni.PhysicalBase > UINTPTR_MAX - bootInfo->PhysicalMapBase) {
|
|
return 0;
|
|
}
|
|
|
|
uint64_t driverListCapacity = (uint64_t)bootInfo->BootDrivers.PageCount * PAGE_SIZE;
|
|
uint64_t driverListBytes = (uint64_t)bootInfo->BootDrivers.Count * sizeof(BOOT_FILE);
|
|
if (!KiValidateBootPageRange(bootInfo->BootDrivers.FilesPhysical, bootInfo->BootDrivers.PageCount) ||
|
|
bootInfo->BootDrivers.Count == 0 || driverListBytes > driverListCapacity ||
|
|
bootInfo->BootDrivers.FilesPhysical > UINTPTR_MAX - bootInfo->PhysicalMapBase) {
|
|
return 0;
|
|
}
|
|
|
|
const BOOT_FILE* bootFiles =
|
|
(const BOOT_FILE*)(bootInfo->PhysicalMapBase + bootInfo->BootDrivers.FilesPhysical);
|
|
for (uint32_t index = 0; index < bootInfo->BootDrivers.Count; ++index) {
|
|
if (!KiValidateBootFile(&bootFiles[index]) ||
|
|
bootFiles[index].PhysicalBase > UINTPTR_MAX - bootInfo->PhysicalMapBase) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!KiValidateBootImage(&bootInfo->KernelImage, 1) ||
|
|
!KiValidateBootImage(&bootInfo->HalImage, 0)) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Main Kernel Entry.
|
|
extern void KernelMain(BOOT_INFO* bootInfo) {
|
|
|
|
// Firmware is not required to transfer control with IF clear. Keep the
|
|
// bootstrap CPU masked until the interrupt manager and scheduler are ready.
|
|
_disable();
|
|
|
|
if (!KiValidateBootInfo(bootInfo)) {
|
|
for (;;)
|
|
__halt();
|
|
}
|
|
|
|
if (!KSUCCESS(KeBindBootstrapHal())) {
|
|
for (;;)
|
|
__halt();
|
|
}
|
|
const HAL_API* hal = KeGetHal();
|
|
|
|
// Physical & Virtual Memory initialization.
|
|
|
|
if (!MmInitializePhysical(&bootInfo->MemoryMap, bootInfo->PhysicalMapBase))
|
|
KeStopProcessor();
|
|
|
|
PHYSICAL_ADDRESS pml4 = (PHYSICAL_ADDRESS)__readcr3();
|
|
// The early virtual allocator starts small because it only needs bootstrap page tables before the PMM is online.
|
|
MmInitializeVirtual(pml4, 1 * 1024 * 1024);
|
|
|
|
// Bootstrap CPU and architecture setup (GDT/IDT/Faults).
|
|
|
|
KeInitializeBootstrapCpu();
|
|
KeInitializeGdt();
|
|
KeInitializeIdt();
|
|
|
|
// Register boot modules (Allows LdrGetExport to work for drivers and the HAL)
|
|
// Note: Kept to a single call here to avoid the duplicate in your original code.
|
|
|
|
KSTATUS moduleRegistryStatus = LdrRegisterModulesFromBootInfo(bootInfo);
|
|
if (!KSUCCESS(moduleRegistryStatus))
|
|
KeStopProcessor();
|
|
|
|
// Parse the raw mandatory boot.ini handoff once before HAL policy consumes debug flags.
|
|
KE_BOOT_CONFIG bootConfig = {0};
|
|
const char* bootIniText = (const char*)(bootInfo->PhysicalMapBase + bootInfo->BootIni.PhysicalBase);
|
|
KSTATUS bootConfigStatus = KeParseBootConfiguration(bootIniText, bootInfo->BootIni.Length, &bootConfig);
|
|
|
|
// HAL Initialization.
|
|
|
|
HAL_INIT_PARAMS halParams = {
|
|
.Size = sizeof(HAL_INIT_PARAMS),
|
|
.Flags = 0,
|
|
.PhysicalWindowBase = bootInfo->PhysicalMapBase,
|
|
.FramebufferBase = bootInfo->FramebufferBase,
|
|
.FramebufferWidth = bootInfo->FramebufferWidth,
|
|
.FramebufferHeight = bootInfo->FramebufferHeight,
|
|
.FramebufferStride = bootInfo->FramebufferStride,
|
|
.RsdpPhysical = bootInfo->RsdpPhysical,
|
|
.Debug = KSUCCESS(bootConfigStatus) ? (int)(bootConfig.Debug & 1u) : 0,
|
|
.AllocatePool = MmAllocatePool
|
|
};
|
|
|
|
KSTATUS s = KeInitializeHal(&halParams);
|
|
if (!KSUCCESS(s))
|
|
KeStopProcessor();
|
|
|
|
IoInitializeDdk(hal, bootInfo->PhysicalMapBase);
|
|
gKmainHal = hal;
|
|
|
|
KeInitDebug(hal, KSUCCESS(bootConfigStatus) ? bootConfig.Debug : 0);
|
|
if (!KSUCCESS(bootConfigStatus)) {
|
|
KePanic(KE_PANIC_CODE_INIT,
|
|
"boot.ini: malformed or missing mandatory configuration, status=0x%x\n",
|
|
bootConfigStatus);
|
|
}
|
|
DbgPrint("Boot: system volume selector=%s\n", bootConfig.SystemVolume);
|
|
|
|
// Bind IRQ dispatch while IF remains clear. Interrupt sources are armed
|
|
// later, after their kernel consumers have registered.
|
|
KeIrqInitialize(hal);
|
|
DbgPrint("CPU: PAT owns WB/WC/UC policy; MTRR default=WB fixed=off variable=off.\n");
|
|
KeTimeInitialize(bootInfo, hal);
|
|
KeDisplayInitialize(hal);
|
|
|
|
// CPU startup intentionally lives after HAL initialization but before IRQs
|
|
// and scheduler startup. HAL owns firmware processor discovery and platform
|
|
// startup signaling; the kernel owns KCPU records, per-CPU stacks, GDT/TSS/IDT state, and the
|
|
// AP park loop. External device IRQs remain routed to the boot processor during this
|
|
// step; APs only become runnable after scheduler admission below.
|
|
KeSetApplicationProcessorTrampoline((uint64_t)bootInfo->ApTrampolinePhys);
|
|
KSTATUS smpStatus = KeStartApplicationProcessors();
|
|
if (!KSUCCESS(smpStatus)) {
|
|
KePanic(KE_PANIC_CODE_INIT, "SMP: failed to start application processors, status=0x%x\n", smpStatus);
|
|
}
|
|
|
|
KeInitializeDpcSystem();
|
|
|
|
KeInputInitialize(hal);
|
|
|
|
KeIrqRegister(hal->Timer.IrqNumber, hal->Timer.HandleInterrupt);
|
|
|
|
KeSyscallInit(hal);
|
|
|
|
KSTATUS securityStatus = SeInitializeSecurity();
|
|
if (!KSUCCESS(securityStatus)) {
|
|
KePanic(KE_PANIC_CODE_INIT,
|
|
"security: failed to initialize root context, status=0x%x\n",
|
|
securityStatus);
|
|
}
|
|
|
|
// Dynamic Linking & Driver Execution.
|
|
|
|
|
|
// Scheduler Prep & Status Update.
|
|
|
|
KeInitializeScheduler();
|
|
KeIrqRegister(hal->Timer.IrqNumber, KeSchedulerTick);
|
|
KeConfigureCurrentProcessorTimer();
|
|
|
|
KSTATUS volumeManagerStatus = IoInitializeVolumeManager();
|
|
if (!KSUCCESS(volumeManagerStatus)) {
|
|
KePanic(KE_PANIC_CODE_INIT,
|
|
"I/O: failed to initialize volume manager notifications, status=0x%x\n",
|
|
volumeManagerStatus);
|
|
}
|
|
|
|
KSTATUS partitionStatus = IoInitializePartitionManager(bootConfig.SystemVolume);
|
|
if (!KSUCCESS(partitionStatus)) {
|
|
KePanic(KE_PANIC_CODE_INIT,
|
|
"I/O: failed to initialize partition discovery, status=0x%x\n",
|
|
partitionStatus);
|
|
}
|
|
|
|
KeAudioInitialize();
|
|
|
|
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);
|
|
}
|
|
|
|
KiRunCoreHardeningSelfTests();
|
|
|
|
// Pack the arguments for the init thread.
|
|
SYSTEM_INIT_ARGS* initArgs = MmAllocatePool(sizeof(SYSTEM_INIT_ARGS));
|
|
if (!initArgs)
|
|
KePanic(KE_PANIC_CODE_INIT, "SystemInitThread: failed to allocate initialization arguments.\n");
|
|
initArgs->BootInfo = bootInfo;
|
|
initArgs->Hal = hal;
|
|
initArgs->BootConfig = bootConfig;
|
|
|
|
// Spawn the Initialization Thread. No creator reference is needed; the
|
|
// scheduler owns the live thread lifetime.
|
|
const KE_THREAD_CREATE_PARAMETERS initThreadParameters = KE_THREAD_CREATE_NORMAL(PriorityNormal);
|
|
KSTATUS st = KeCreateKernelThread((uintptr_t)SystemInitThread,
|
|
(uintptr_t)initArgs,
|
|
&initThreadParameters, (THREAD**)0);
|
|
|
|
if (!KSUCCESS(st)) {
|
|
KePanic(KE_PANIC_CODE_INIT, "KernelMain: Failed to create SystemInitThread (status 0x%x)\n", st);
|
|
}
|
|
|
|
DbgPrint("KernelMain: Handing over to scheduler...\n");
|
|
|
|
// The bootstrap CPU becomes the scheduler idle context once interrupts are enabled.
|
|
while (true)
|
|
KeWaitForInterrupt();
|
|
}
|