494 lines
17 KiB
C
494 lines
17 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)
|
|
#pragma intrinsic(_enable)
|
|
#include <ntlite/boot_info.h>
|
|
#include <kernel/mm_phys.h>
|
|
#include <kernel/mm_virt.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/ke_util.h>
|
|
#include <kernel/ldr_image.h>
|
|
#include <ntlite/ddi/hal_api.h>
|
|
#include <kernel/io_file.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 <common/ini_parser.h>
|
|
|
|
typedef struct {
|
|
BOOT_INFO* bootInfo;
|
|
const HAL_API* hal;
|
|
} SYSTEM_INIT_ARGS;
|
|
|
|
typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API *);
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Implements the reclaim Bootloader Memory operation.
|
|
static void KiReclaimBootloaderMemory(const BOOT_INFO* bootInfo) {
|
|
// Reclaim only the raw buffers backing boot-loaded .sys modules.
|
|
// The kernel/HAL images, page tables, and bootloader code remain live.
|
|
for (uint32_t i = 2; i < bootInfo->ModuleCount; i++) {
|
|
const BOOT_MODULE* mod = &bootInfo->Modules[i];
|
|
if (!mod->PhysBase || !mod->ImageSize) continue;
|
|
|
|
uintptr_t base = (uintptr_t)(mod->PhysBase & PAGE_MASK);
|
|
uintptr_t end = (uintptr_t)((mod->PhysBase + mod->ImageSize + PAGE_SIZE - 1) & PAGE_MASK);
|
|
|
|
for (uintptr_t page = base; page < end; page += PAGE_SIZE) {
|
|
if (!KiPageInBootloaderRegion(bootInfo, (PHYSICAL_ADDRESS)page)) continue;
|
|
MmFreeFrame((PHYSICAL_ADDRESS)page);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Phase Helper: Resolve Imports for core modules.
|
|
static void KiResolveSystemImports(BOOT_INFO* bootInfo) {
|
|
// Only resolve imports for kernel.exe and hal.dll (typically indices 0 and 1).
|
|
// The bootloader maps these, but drivers are now passed as raw physical files.
|
|
uint32_t coreModuleCount = (bootInfo->ModuleCount > 2) ? 2 : bootInfo->ModuleCount;
|
|
|
|
for (uint32_t i = 0; i < coreModuleCount; i++) {
|
|
if (bootInfo->Modules[i].VirtBase != 0) {
|
|
LdrResolveImports(bootInfo->Modules[i].VirtBase);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Phase Helper: Initialize dynamically loaded .sys drivers.
|
|
static void KiDriversIniCb(const char* section, const char* key,
|
|
const char* val, void* ud) {
|
|
(void)val;
|
|
if (!strcmp(section, "runtime")) {
|
|
char*** pArray = (char***)ud;
|
|
size_t len = strlen(key) + 1;
|
|
char* keyCopy = MmAllocatePool(len);
|
|
if (keyCopy) {
|
|
memcpy(keyCopy, key, len);
|
|
arr_push(*pArray, keyCopy);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initializes drivers.
|
|
static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
|
DbgPrint("KiInitializeDrivers: Initializing drivers...\n");
|
|
|
|
// boot drivers (provided by the bootloader).
|
|
for (uint32_t i = 0; i < bootInfo->ModuleCount; i++) {
|
|
BOOT_MODULE* mod = &bootInfo->Modules[i];
|
|
|
|
// Skip modules that are already mapped (Kernel/HAL)
|
|
// or have no physical data (invalid).
|
|
if (mod->VirtBase != 0 || mod->PhysBase == 0) continue;
|
|
|
|
DbgPrint("KiInitializeDrivers: Loading boot driver %s\n", mod->Name);
|
|
|
|
// Calculate virtual address of the raw file in the physical window.
|
|
void* rawPeBuffer = (void*)(bootInfo->PhysicalMapBase + mod->PhysBase);
|
|
|
|
// Map the PE image into kernel space.
|
|
uintptr_t mappedBase = 0;
|
|
KSTATUS st = LdrLoadImage(rawPeBuffer, mod->ImageSize, &mappedBase, false);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("ERROR: Failed to map driver %s (0x%X)\n", mod->Name, st);
|
|
continue;
|
|
}
|
|
|
|
// Update the struct so other modules can find it.
|
|
mod->VirtBase = mappedBase;
|
|
|
|
// Resolve imports (can now see Kernel, HAL, and previous drivers).
|
|
if (!KSUCCESS(LdrResolveImports(mappedBase))) {
|
|
DbgPrint("ERROR: Failed to resolve imports for %s\n", mod->Name);
|
|
continue;
|
|
}
|
|
|
|
// Register with the global module list so subsequent drivers can import from this one.
|
|
LdrRegisterModule(mod->Name, mod->VirtBase);
|
|
|
|
// Execute Entry Point.
|
|
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)LdrGetEntryPoint(mappedBase);
|
|
if (entry) {
|
|
KSTATUS ds = entry(hal);
|
|
if (!KSUCCESS(ds)) {
|
|
DbgPrint("WARNING: Driver %s returned failure status 0x%X\n", mod->Name, ds);
|
|
}
|
|
}
|
|
}
|
|
|
|
// runtime drivers.
|
|
KSTATUS s;
|
|
FILE_OBJECT* f;
|
|
|
|
s = IoOpenFile(SYS_SV_LITERAL("drivers\\drivers.ini"), &f);
|
|
if (s != STATUS_SUCCESS) {
|
|
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to open drivers.ini (status %d)\n", s);
|
|
}
|
|
|
|
uint64_t size = IoGetFileSize(f) + 1;
|
|
char* mem = MmAllocatePool(size);
|
|
size_t got;
|
|
if (!KSUCCESS(IoReadFile(f, mem, size, &got))) {
|
|
IoCloseFile(f);
|
|
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to read drivers.ini\n");
|
|
}
|
|
mem[got] = '\0';
|
|
|
|
IoCloseFile(f);
|
|
|
|
char** runtimeKeys = NULL;
|
|
IniParse(mem, KiDriversIniCb, &runtimeKeys);
|
|
|
|
for (uint32_t i = 0; i < arr_len(runtimeKeys); i++) {
|
|
DbgPrint("Found runtime driver: %s\n", runtimeKeys[i]);
|
|
|
|
// Load the driver.
|
|
size_t len = strlen(runtimeKeys[i]);
|
|
char* path = MmAllocatePool(len + 10);
|
|
if (!path) {
|
|
DbgPrint("KernelMain: Failed to allocate memory for driver path. How?\n");
|
|
continue;
|
|
}
|
|
|
|
// memcpy maneuvers.
|
|
memcpy(path, "drivers\\", 8);
|
|
memcpy(path + 8, runtimeKeys[i], len + 1);
|
|
|
|
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
|
|
}
|
|
|
|
MmFreePool(path);
|
|
|
|
uint64_t drvSize = IoGetFileSize(f);
|
|
void* drvMem = MmAllocatePool(drvSize);
|
|
if (!KSUCCESS(IoReadFile(f, drvMem, drvSize, &got))) {
|
|
DbgPrint("KiInitializeDrivers: Failed to read runtime driver: %s\n", runtimeKeys[i]);
|
|
IoCloseFile(f);
|
|
continue;
|
|
}
|
|
IoCloseFile(f);
|
|
|
|
uintptr_t mappedBase = 0;
|
|
if (!KSUCCESS(LdrLoadImage(drvMem, drvSize, &mappedBase, false))) { // Load into kernel space
|
|
DbgPrint("KiInitializeDrivers: Failed to load runtime driver: %s\n", runtimeKeys[i]);
|
|
MmFreePool(drvMem);
|
|
continue;
|
|
}
|
|
MmFreePool(drvMem);
|
|
|
|
if (!KSUCCESS(LdrResolveImports(mappedBase))) {
|
|
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);
|
|
|
|
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)(LdrGetEntryPoint(mappedBase));
|
|
|
|
KSTATUS ds = entry(hal);
|
|
if (!KSUCCESS(ds)) {
|
|
DbgPrint("KiInitializeDrivers: Driver %s failed to init!\n", runtimeKeys[i]);
|
|
}
|
|
}
|
|
|
|
// Free the individual strings allocated via strdup in the callback.
|
|
if (runtimeKeys) {
|
|
for (uint32_t i = 0; i < arr_len(runtimeKeys); i++) {
|
|
if (runtimeKeys[i]) {
|
|
MmFreePool(runtimeKeys[i]);
|
|
}
|
|
}
|
|
// Free the dynamic array structure itself.
|
|
arr_free(runtimeKeys);
|
|
}
|
|
|
|
// Free the file buffer.
|
|
MmFreePool(mem);
|
|
}
|
|
|
|
// 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;
|
|
|
|
// Read the executable file into a raw buffer.
|
|
s = IoOpenFile(SysSvFromCstr(filePath), &f);
|
|
if (s != STATUS_SUCCESS) {
|
|
DbgPrint("Failed to load file: \"%s\"!\n", filePath);
|
|
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;
|
|
}
|
|
|
|
// Create the process object.
|
|
PROCESS* proc;
|
|
THREAD* thread;
|
|
|
|
s = KeCreateProcess(filePath, &proc);
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("Failed to create process. System halted.\n");
|
|
MmFreePool(mem);
|
|
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),
|
|
SysSvFromCstr(proc->CurrentDirectory));
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
|
|
MmFreePool(mem);
|
|
ObDereferenceObject(&proc->Header);
|
|
return 0;
|
|
}
|
|
|
|
// Parse the raw PE file and map it into executable memory.
|
|
uintptr_t imageBase = 0;
|
|
s = LdrLoadImageForProcess(proc, mem, size, &imageBase);
|
|
MmFreePool(mem);
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: LdrLoadImage failed with status: %d\n", s);
|
|
ObDereferenceObject(&proc->Header);
|
|
return 0;
|
|
}
|
|
|
|
s = LdrResolveImportsForProcess(proc, imageBase);
|
|
if (!KSUCCESS(s)) {
|
|
DbgPrint("KiLaunchUserProcess: Failed to resolve EXE imports 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.
|
|
s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &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;
|
|
}
|
|
DbgPrint("KiLaunchUserProcess: Thread created and queued!\n");
|
|
|
|
// 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();
|
|
|
|
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");
|
|
}
|
|
|
|
DbgPrint("SystemInitThread: init.exe completed successfully; shutting down.\n");
|
|
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);
|
|
}
|
|
gKmainHal->Power.Shutdown();
|
|
MmFreePool(args);
|
|
}
|
|
|
|
// Main Kernel Entry.
|
|
extern void KernelMain(BOOT_INFO* bootInfo) {
|
|
|
|
// Physical & Virtual Memory initialization.
|
|
|
|
if (!MmInitializePhysical(&bootInfo->MemoryMap, bootInfo->PhysicalMapBase)) {
|
|
_disable(); while (true) __halt(); // there's nothing we can do
|
|
}
|
|
|
|
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.
|
|
|
|
LdrRegisterModulesFromBootInfo(bootInfo);
|
|
|
|
// HAL Initialization.
|
|
|
|
HAL_INIT_PARAMS halParams = {
|
|
.PhysicalWindowBase = bootInfo->PhysicalMapBase,
|
|
.FramebufferBase = bootInfo->FramebufferBase,
|
|
.FramebufferWidth = bootInfo->FramebufferWidth,
|
|
.FramebufferHeight = bootInfo->FramebufferHeight,
|
|
.FramebufferStride = bootInfo->FramebufferStride,
|
|
.RsdpPhysical = bootInfo->RsdpPhysical,
|
|
.Debug = bootInfo->Debug & 1, // bitmask 1 debug prints
|
|
.AllocatePool = MmAllocatePool
|
|
};
|
|
|
|
KSTATUS s = HalInitialize(&halParams);
|
|
if (!KSUCCESS(s)) {
|
|
_disable(); while (true) __halt(); // there's nothing we can do.
|
|
}
|
|
|
|
const HAL_API* hal = HalGetApi();
|
|
gKmainHal = hal;
|
|
|
|
KeInitDebug(hal->Screen.PrintChar, bootInfo->Debug);
|
|
KeTimeInitialize(bootInfo, hal);
|
|
KeDisplayInitialize(hal);
|
|
KeSetDebugConsoleWriter(KeDisplayDebugPrintChar);
|
|
|
|
// CPU startup intentionally lives after HAL initialization but before IRQs
|
|
// and scheduler startup. HAL owns ACPI/MADT discovery and LAPIC INIT/SIPI;
|
|
// the kernel owns KCPU records, per-CPU stacks, GDT/TSS/IDT state, and the
|
|
// AP park loop. External device IRQs remain routed to the BSP 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);
|
|
}
|
|
|
|
KeIrqInitialize(hal);
|
|
KeInitializeDpcSystem();
|
|
|
|
KeInputInitialize(hal);
|
|
|
|
KeIrqRegister(hal->Timer.IrqNumber, hal->Timer.Isr);
|
|
|
|
HalRegisterScheduler(KeSchedulerTick);
|
|
|
|
KeSyscallInit(hal);
|
|
|
|
// Dynamic Linking & Driver Execution.
|
|
|
|
KiResolveSystemImports(bootInfo);
|
|
//KiInitializeDrivers(bootInfo, hal);.
|
|
|
|
// Scheduler Prep & Status Update.
|
|
|
|
KeInitializeScheduler();
|
|
|
|
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));
|
|
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);
|
|
|
|
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");
|
|
|
|
// Ignite the system!
|
|
_enable();
|
|
|
|
// The Idle Loop: The CPU rests here whenever all other threads are waiting on events.
|
|
while (true) {
|
|
__halt();
|
|
}
|
|
}
|