// kmain.c - kernel entry point #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 so kernel threads can reach it without // threading a parameter through every call chain. static const HAL_API* gKmainHal; static int PageInBootloaderRegion(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; } static void ReclaimBootloaderMemory(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 (!PageInBootloaderRegion(bootInfo, (PHYSICAL_ADDRESS)page)) continue; MmFreeFrame((PHYSICAL_ADDRESS)page); } } } // --------------------------------------------------------------------------- // Phase Helper: Resolve Imports for core modules // --------------------------------------------------------------------------- static void ResolveSystemImports(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 DriversIniCb(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); } } } static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) { DbgPrint("InitializeDrivers: 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("InitializeDrivers: 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("drivers\\drivers.ini", &f); if (s != STATUS_SUCCESS) { KePanic("InitializeDrivers: 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("InitializeDrivers: Failed to read drivers.ini\n"); } mem[got] = '\0'; IoCloseFile(f); char** runtimeKeys = NULL; IniParse(mem, DriversIniCb, &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(path, &f); if (s != STATUS_SUCCESS) { DbgPrint("InitializeDrivers: 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("InitializeDrivers: 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("InitializeDrivers: Failed to load runtime driver: %s\n", runtimeKeys[i]); MmFreePool(drvMem); continue; } MmFreePool(drvMem); if (!KSUCCESS(LdrResolveImports(mappedBase))) { DbgPrint("InitializeDrivers: Failed to resolve imports for runtime driver: %s\n", runtimeKeys[i]); continue; } DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)(LdrGetEntryPoint(mappedBase)); KSTATUS ds = entry(hal); if (!KSUCCESS(ds)) { DbgPrint("InitializeDrivers: 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* LaunchUserProcess(const char* filePath, const char* args) { KSTATUS s; FILE_OBJECT* f; // Read the executable file into a raw buffer. s = IoOpenFile(filePath, &f); DbgPrint("LaunchUserProcess: IoOpenFile: %d\n", s); if (s != STATUS_SUCCESS) { DbgPrint("Failed to load file: \"%s\"!\n", filePath); return 0; } uint64_t size = IoGetFileSize(f); DbgPrint("LaunchUserProcess: IoGetFileSize: %llu\n", size); void* mem = MmAllocateUserMemory(size); DbgPrint("LaunchUserProcess: MmAllocateUserMemory: 0x%llX\n", mem); if (!mem) { IoCloseFile(f); return 0; } size_t got = 0; s = IoReadFile(f, mem, size, &got); DbgPrint("LaunchUserProcess: IoReadFile: %d\n", s); IoCloseFile(f); if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); 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"); MmFreeUserMemory(mem, size); return 0; } DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath); if (args && args[0]) strncpy(proc->Cmdline, args, sizeof(proc->Cmdline) - 1); else strncpy(proc->Cmdline, filePath, sizeof(proc->Cmdline) - 1); proc->Cmdline[sizeof(proc->Cmdline) - 1] = '\0'; // proc->Cmdline is kernel pool (ffffc000...) - not readable at CPL=3. // Copy into user memory before handing the pointer to the initial thread. size_t cmdlineLen = strlen(proc->Cmdline) + 1; char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen); if (!userCmdline) { DbgPrint("LaunchUserProcess: Failed to allocate user cmdline buffer.\n"); MmFreeUserMemory(mem, size); ObDereferenceObject(&proc->Header); return 0; } memcpy(userCmdline, proc->Cmdline, cmdlineLen); proc->UserCmdlineBase = (uintptr_t)userCmdline; proc->UserCmdlineSize = cmdlineLen; // Parse the raw PE file and map it into executable memory. uintptr_t imageBase = 0; s = LdrLoadImage(mem, size, &imageBase, true); MmFreeUserMemory(mem, size); if (!KSUCCESS(s)) { DbgPrint("LaunchUserProcess: LdrLoadImage failed with status: %d\n", s); ObDereferenceObject(&proc->Header); return 0; } s = LdrResolveImportsForProcess(proc, imageBase); if (!KSUCCESS(s)) { DbgPrint("LaunchUserProcess: Failed to resolve EXE imports for %s (status %d)\n", filePath, s); MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase)); ObDereferenceObject(&proc->Header); return 0; } uintptr_t entryPoint = LdrGetEntryPoint(imageBase); DbgPrint("LaunchUserProcess: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint); // Create the main thread, pointing it to the PE Entry Point. s = KeCreateThread(proc, (uintptr_t)entryPoint, (uintptr_t)userCmdline, PriorityNormal, &thread); if (!KSUCCESS(s)) { DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s); MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase)); ObDereferenceObject(&proc->Header); return 0; } proc->ImageBase = imageBase; proc->ImageSize = LdrGetImageSize(imageBase); DbgPrint("LaunchUserProcess: Thread created and queued!\n"); // Caller owns the reference returned by KeCreateProcess. return proc; } static void PrintProcessList() { LIST_ENTRY* listHead = KeGetProcessList(); LIST_ENTRY* current; DbgPrint("\nPID | Name | Threads | ExitCode\n"); DbgPrint("---------------------------------------------\n"); for (current = listHead->Fwd; current != listHead; current = current->Fwd) { // Translate the list entry back to the PROCESS structure PROCESS* proc = CONTAINING_RECORD(current, PROCESS, GlobalLink); DbgPrint("%-5u | %-16s | %-7u | %d\n", proc->ProcessId, proc->Name, proc->ThreadCount, proc->ExitCode); } } void SystemInitThread(void* arg) { SYSTEM_INIT_ARGS* args = (SYSTEM_INIT_ARGS*)arg; DbgPrint("SystemInitThread: Thread started. Igniting the system...\n"); InitializeDrivers(args->bootInfo, args->hal); ResolveSystemImports(args->bootInfo); ReclaimBootloaderMemory(args->bootInfo); args->hal->Screen.Clear(0x000000); // Print memory status now that the system is fully alive DbgPrint("Memory Status:\n"); DbgPrint(" -> Free (PMM): %.2f MB\n", (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f)); DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f)); DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f)); // Launch User-Mode init.exe and restart it until it exits cleanly. while (true) { PROCESS* shellProc = LaunchUserProcess("system/init.exe", args->bootInfo->CommandLine); if (!shellProc) { DbgPrint("SystemInitThread: Failed to start system/init.exe; retrying.\n"); KeYield(); continue; } KeWaitForEvent(shellProc->ExitEvent); 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"); 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(); MmInitializeVirtual(pml4, 4 * 1024 * 1024 /* 4 MB initial pool */); // // CPU Architecture setup (GDT/IDT/Faults) // 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, .DebugEnabled = bootInfo->DebugEnabled }; 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->DebugEnabled); // Enable debug KeIrqInitialize(hal); KeIrqRegister(hal->Timer.IrqNumber, hal->Timer.Isr); KeIrqRegister(hal->Keyboard.IrqNumber, hal->Keyboard.Isr); KeIrqRegister(hal->Mouse.IrqNumber, hal->Mouse.Isr); HalRegisterScheduler(KeSchedulerTick); KeSyscallSetHal(hal); // // Dynamic Linking & Driver Execution // ResolveSystemImports(bootInfo); //InitializeDrivers(bootInfo, hal); // // Scheduler Prep & Status Update // KeInitializeScheduler(); // 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("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(); } }