build.bat can create the image if missing, split the shell into native, posix and win32, clean up the build by moving executables into directories, the bootloader is no longer responsible to resolve drivers, fix ResolvePath, every process gets a copy of a module (dll), the child always inherits the cwd, fix function exports in posix, add unlink and exit to posix, add path translator to win32, first bugcheck.

This commit is contained in:
maksw committed 2026-05-13 09:24:16 +02:00
1 parent 0c7f461c6d
commit f80e66ecde
38 files changed
+1620 -462

No files matched your search

+100 -76
View File
@@ -31,13 +31,17 @@ typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API *);
static const HAL_API* gKmainHal;
// ---------------------------------------------------------------------------
// Phase Helper: Resolve Imports for all registered modules
// Phase Helper: Resolve Imports for core modules
// ---------------------------------------------------------------------------
static void ResolveSystemImports(BOOT_INFO* bootInfo) {
// Resolve imports for all loaded modules (drivers may import from hal.dll, win32.dll imports from syscall.dll, etc).
// Starting at 2 to skip the kernel and HAL base images.
for (uint32_t i = 2; i < bootInfo->ModuleCount; i++) {
LdrResolveImports(bootInfo->Modules[i].VirtBase);
// 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);
}
}
}
@@ -63,19 +67,44 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
// boot drivers (provided by the bootloader)
for (uint32_t i = 0; i < bootInfo->ModuleCount; i++) {
const char* name = bootInfo->Modules[i].Name;
BOOT_MODULE* mod = &bootInfo->Modules[i];
// Identify .sys files by extension
size_t len = strlen(name);
if (len < 4 || strcmp(name + len - 4, ".sys")) continue;
// Skip modules that are already mapped (Kernel/HAL)
// or have no physical data (invalid)
if (mod->VirtBase != 0 || mod->PhysBase == 0) continue;
DbgPrint("InitializeDrivers: Executing DriverEntry for %s\n", name);
DbgPrint("InitializeDrivers: Loading boot driver %s\n", mod->Name);
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)(LdrGetEntryPoint(bootInfo->Modules[i].VirtBase));
// Calculate virtual address of the raw file in the physical window
void* rawPeBuffer = (void*)(bootInfo->PhysicalMapBase + mod->PhysBase);
KSTATUS ds = entry(hal);
if (!KSUCCESS(ds)) {
DbgPrint("InitializeDrivers: Driver %s failed to init!\n", name);
// 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);
}
}
}
@@ -137,6 +166,13 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
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;
}
@@ -163,41 +199,6 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
MmFreePool(mem);
}
// ---------------------------------------------------------------------------
// Phase Helper: Load a DLL from disk and register it
// ---------------------------------------------------------------------------
static void LoadAndRegisterDll(const char* filename) {
KSTATUS s;
FILE_OBJECT* f;
// Read the DLL from disk
s = IoOpenFile(filename, &f);
if (s != STATUS_SUCCESS) {
KePanic("LoadAndRegisterDll: Failed to open DLL: %s\n", filename);
}
uint64_t size = IoGetFileSize(f);
void* mem = MmAllocatePool(size); // Temporary pool for the raw file
size_t got;
if (!KSUCCESS(IoReadFile(f, mem, size, &got))) {
KePanic("LoadAndRegisterDll: Failed to read %s\n", filename);
IoCloseFile(f);
}
IoCloseFile(f);
// Map the DLL into memory
// Passing 'true' for user-mode
uintptr_t mappedBase = 0;
s = LdrLoadImage(mem, size, &mappedBase, true);
if (!KSUCCESS(s)) {
KePanic("LoadAndRegisterDll: Failed to map %s\n", filename);
}
MmFreePool(mem); // Free the raw file buffer, we only need the mapped image now
LdrRegisterModule(filename, mappedBase); // Register the module
}
// ---------------------------------------------------------------------------
// Phase Helper: Load and launch a process from the filesystem.
// Returns the new PROCESS* with one reference owned by the caller.
@@ -206,69 +207,102 @@ static void LoadAndRegisterDll(const char* filename) {
static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
KSTATUS s;
FILE_OBJECT* f;
// Read the executable file into a raw buffer
// 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 (PROCESS*)0;
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;
size_t got = 0;
s = IoReadFile(f, mem, size, &got);
DbgPrint("LaunchUserProcess: IoReadFile: %d\n", s);
IoCloseFile(f);
DbgPrint("LaunchUserProcess: IoCloseFile\n");
if (!KSUCCESS(s)) {
MmFreeUserMemory(mem, size);
return 0;
}
// Create the process object
// Create the process object.
PROCESS* proc;
THREAD* thread;
s = KeCreateProcess(filePath, &proc);
if (!KSUCCESS(s)) {
DbgPrint("Failed to create process. System halted.\n");
return (PROCESS*)0;
MmFreeUserMemory(mem, size);
return 0;
}
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath);
strncpy(proc->Cmdline, args, sizeof(proc->Cmdline) - 1);
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.
char* userCmdline = (char*)MmAllocateUserMemory(sizeof(proc->Cmdline));
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
if (!userCmdline) {
DbgPrint("LaunchUserProcess: Failed to allocate user cmdline buffer.\n");
return (PROCESS*)0;
MmFreeUserMemory(mem, size);
ObDereferenceObject(&proc->Header);
return 0;
}
memcpy(userCmdline, proc->Cmdline, sizeof(proc->Cmdline));
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
proc->UserCmdlineBase = (uintptr_t)userCmdline;
proc->UserCmdlineSize = cmdlineLen;
// Parse the raw PE file and map it into executable memory
// 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
// 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;
}
// ---------------------------------------------------------------------------
// Kernel thread: wait for a process to exit, then log a message and shut down.
// Takes ownership of the PROCESS* reference passed as arg; releases it on exit.
@@ -312,15 +346,6 @@ void SystemInitThread(void* arg) {
ResolveSystemImports(args->bootInfo);
DbgPrint("SystemInitThread: Loading system DLLs...\n");
LoadAndRegisterDll("syscall.dll");
LoadAndRegisterDll("win32.dll");
LoadAndRegisterDll("bink2w64.dll");
// Now that they are in memory and registered, we link them together
// (e.g., win32.dll resolving its imports against syscall.dll)
ResolveSystemImports(args->bootInfo);
args->hal->Screen.Clear(0x000000);
// Print memory status now that the system is fully alive
@@ -330,7 +355,7 @@ void SystemInitThread(void* arg) {
DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f));
// Launch User-Mode Shell (this will trigger AHCI & FAT32 which CAN safely block now)
PROCESS* shellProc = LaunchUserProcess("init.exe", args->bootInfo->CommandLine);
PROCESS* shellProc = LaunchUserProcess("system/init.exe", args->bootInfo->CommandLine);
if (shellProc) {
KSTATUS ws = KeCreateKernelThread((uintptr_t)ProcessWatcherThread,
@@ -340,10 +365,9 @@ void SystemInitThread(void* arg) {
DbgPrint("SystemInitThread: Failed to create watcher thread (%d) -- leaking proc ref.\n", ws);
}
}
MmFreePool(args);
}
// ---------------------------------------------------------------------------
// Main Kernel Entry
// ---------------------------------------------------------------------------