diff --git a/OVMF/OVMF_VARS_4M_d.fd b/OVMF/OVMF_VARS_4M_d.fd index 4616f96..0dcfc4a 100644 Binary files a/OVMF/OVMF_VARS_4M_d.fd and b/OVMF/OVMF_VARS_4M_d.fd differ diff --git a/build.bat b/build.bat index a77a28d..e0e0e4c 100644 --- a/build.bat +++ b/build.bat @@ -6,8 +6,7 @@ set SRC=%ROOT%\code set VHD=%~dp0disk.vhd if not exist "%VHD%" ( - echo ERROR: disk.vhd not found. Run mkimage.bat first. - exit /b 1 + call mkimage.bat ) if exist "obj" rd /s /q "obj" @@ -21,12 +20,14 @@ mkdir "obj\win32" 2>nul mkdir "obj\posix" 2>nul mkdir "obj\drivers" 2>nul mkdir "obj\init" 2>nul -mkdir "obj\shell" 2>nul +mkdir "obj\shell_win32" 2>nul +mkdir "obj\shell_posix" 2>nul +mkdir "obj\shell_native" 2>nul mkdir "obj\doom" 2>nul mkdir "obj\video" 2>nul mkdir "bin" 2>nul -set CFLAGS=/nologo /W3 /Od /GS- /Gs999999 /Gz /Gy /EHsc- /GR- /Oi /Zi /std:c17 /D_CRT_SECURE_NO_WARNINGS /I"%SRC%\kernel" /I"%SRC%\hal" +set CFLAGS=/nologo /W3 /WX /Od /GS- /Gs999999 /Gz /Gy /EHsc- /GR- /Oi /Zi /std:c17 /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /I"%SRC%\kernel" /I"%SRC%\hal" /I"%SRC%\syscall" set LFLAGS=/nologo /NODEFAULTLIB /DEBUG :: ---- Assemble ASM files ---- @@ -124,16 +125,38 @@ for %%f in (%SRC%\init\*.c) do ( link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:_start ^ /OUT:"bin\init.exe" /PDB:"bin\init.pdb" bin\syscall.lib obj\libc.obj obj\init\init.obj || exit /b 1 -:: ---- Compile shell ---- -echo Compiling shell... -for %%f in (%SRC%\shell\*.c) do ( - cl %CFLAGS% /c /Fo"obj\shell\%%~nf.obj" "%%f" || exit /b 1 +:: ---- Compile shell_win32 ---- +echo Compiling shell_win32... +for %%f in (%SRC%\shell_win32\*.c) do ( + cl %CFLAGS% /c /Fo"obj\shell_win32\%%~nf.obj" "%%f" || exit /b 1 ) -:: ---- Link shell.exe ---- -echo Linking shell.exe... +:: ---- Compile shell_posix ---- +echo Compiling shell_posix... +for %%f in (%SRC%\shell_posix\*.c) do ( + cl %CFLAGS% /c /Fo"obj\shell_posix\%%~nf.obj" "%%f" || exit /b 1 +) + +:: ---- Compile shell_native ---- +echo Compiling shell_native... +for %%f in (%SRC%\shell_native\*.c) do ( + cl %CFLAGS% /c /Fo"obj\shell_native\%%~nf.obj" "%%f" || exit /b 1 +) + +:: ---- Link shell_win32.exe ---- +echo Linking shell_win32.exe... link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ - /OUT:"bin\shell.exe" /PDB:"bin\shell.pdb" bin\win32.lib obj\libc.obj obj\shell\*.obj || exit /b 1 + /OUT:"bin\shell_win32.exe" /PDB:"bin\shell_win32.pdb" bin\win32.lib obj\libc.obj obj\shell_win32\*.obj || exit /b 1 + +:: ---- Link shell_posix.exe ---- +echo Linking shell_posix.exe... +link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ + /OUT:"bin\shell_posix.exe" /PDB:"bin\shell_posix.pdb" bin\posix.lib obj\libc.obj obj\shell_posix\*.obj || exit /b 1 + +:: ---- Link shell_native.exe ---- +echo Linking shell_native.exe... +link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ + /OUT:"bin\shell_native.exe" /PDB:"bin\shell_native.pdb" bin\syscall.lib obj\libc.obj obj\shell_native\*.obj || exit /b 1 :: ---- Compile doom ---- echo Compiling doom... @@ -179,27 +202,31 @@ set DEST=Z: :: ---- Copy to ESP layout ---- echo Assembling ESP... -mkdir "%DEST%\drivers" 2>nul +mkdir "%DEST%\apps" 2>nul +mkdir "%DEST%\system" 2>nul +mkdir "%DEST%\drivers" 2>nul mkdir "%DEST%\EFI\BOOT" 2>nul copy /y "bin\bootx64.efi" "%DEST%\EFI\BOOT\" -copy /y "bin\kernel.exe" "%DEST%\colonel.exe" -copy /y "bin\hal.dll" "%DEST%\" -copy /y "bin\syscall.dll" "%DEST%\" -copy /y "bin\win32.dll" "%DEST%\" -copy /y "bin\posix.dll" "%DEST%\" +copy /y "bin\kernel.exe" "%DEST%\system\colonel.exe" +copy /y "bin\hal.dll" "%DEST%\system\" +copy /y "bin\syscall.dll" "%DEST%\system\" +copy /y "bin\win32.dll" "%DEST%\system\" +copy /y "bin\posix.dll" "%DEST%\system\" copy /y "bin\ide.sys" "%DEST%\drivers\" copy /y "bin\ahci.sys" "%DEST%\drivers\" copy /y "bin\fat32.sys" "%DEST%\drivers\" copy /y "bin\usb.sys" "%DEST%\drivers\" -copy /y "bin\init.exe" "%DEST%\" -copy /y "bin\shell.exe" "%DEST%\" -copy /y "bin\doom.exe" "%DEST%\" -copy /y "bin\video.exe" "%DEST%\" +copy /y "bin\init.exe" "%DEST%\system\" +copy /y "bin\shell_win32.exe" "%DEST%\apps\" +copy /y "bin\shell_posix.exe" "%DEST%\apps\" +copy /y "bin\shell_native.exe" "%DEST%\apps\" +copy /y "bin\doom.exe" "%DEST%\apps\" +copy /y "bin\video.exe" "%DEST%\apps\" copy /y "%SRC%\boot.ini" "%DEST%\" copy /y "%SRC%\drivers\drivers.ini" "%DEST%\drivers\" -if exist "%SRC%\video\bink2w64.dll" copy /y "%SRC%\video\bink2w64.dll" "%DEST%\" -if exist "%SRC%\video\video.bk2" copy /y "%SRC%\video\video.bk2" "%DEST%\" -if exist "%SRC%\doom\doom1.wad" copy /y "%SRC%\doom\doom1.wad" "%DEST%\" +if exist "%SRC%\video\bink2w64.dll" copy /y "%SRC%\video\bink2w64.dll" "%DEST%\apps\" +if exist "%SRC%\video\video.bk2" copy /y "%SRC%\video\video.bk2" "%DEST%\apps\" +if exist "%SRC%\doom\doom1.wad" copy /y "%SRC%\doom\doom1.wad" "%DEST%\apps\" :: ---- Unmount disk image ---- echo Unmounting disk.vhd... diff --git a/code/boot.ini b/code/boot.ini index 7c91c2e..b6e55ca 100644 --- a/code/boot.ini +++ b/code/boot.ini @@ -1,4 +1,4 @@ [kernel] debug = 1 [init] -commandline = shell.exe +commandline = apps/shell_native.exe diff --git a/code/boot/main.c b/code/boot/main.c index 900b28c..c6a528b 100644 --- a/code/boot/main.c +++ b/code/boot/main.c @@ -558,8 +558,8 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable } MODULE_LOAD_INFO; MODULE_LOAD_INFO core[] = { - { L"colonel.exe", "kernel.exe", NULL, KERNEL_VIRT_BASE }, - { L"hal.dll", "hal.dll", NULL, 0 /* Calculated dynamically */ }, + { L"system\\colonel.exe", "kernel.exe", NULL, KERNEL_VIRT_BASE }, + { L"system\\hal.dll", "hal.dll", NULL, 0 /* Calculated dynamically */ }, }; for (int i = 0; i < 2; i++) { @@ -615,45 +615,27 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable Print(L"5\n"); - // Load driver .sys files. + // Load driver .sys files into raw buffers for (int i = 0; i < ps.driverCount; i++) { CHAR16 path[128]; UnicodeSPrint(path, sizeof(path), L"drivers\\%s", ps.drivers[i]); + UINTN imgSz; UINT8* buf = LoadFile(path, &imgSz); - if (!buf) { Print(L"Driver not found: %s\n", ps.drivers[i]); continue; } - - UINT32 stored = *(UINT32 *)(buf + *(UINT32 *)(buf + 0x3C) + 4 + 20 + 64); - UINT32 computed = ComputePeChecksum(buf, imgSz); - if (stored && computed != stored) { - Print(L"Checksum mismatch: %s\n", ps.drivers[i]); - FreePool(buf); continue; + if (!buf) { + Print(L"Driver not found: %s\n", ps.drivers[i]); + continue; } - UINT32 peOff = *(UINT32 *)(buf + 0x3C); - UINT32 imgLen = *(UINT32 *)(buf + peOff + 4 + 20 + 56); - imgLen = (imgLen + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); - - EFI_PHYSICAL_ADDRESS phys; - EFI_STATUS status = LoadPeImage(buf, virtCursor, &phys); - if (EFI_ERROR(status)) { - Print(L"Driver PE load failed: %s\n", ps.drivers[i]); - FreePool(buf); - continue; - } - - UINT64 virt = virtCursor; - for (UINT64 off = 0; off < imgLen; off += PAGE_SIZE) - MapPage(virt + off, phys + off, 0x03); - + // Store file info for the kernel to process later char name[32]; UnicodeStrToAsciiStrS(ps.drivers[i], name, 32); - AsciiStrCpyS(bi->Modules[bi->ModuleCount].Name, 32, name); - bi->Modules[bi->ModuleCount].VirtBase = virt; - bi->Modules[bi->ModuleCount].ImageSize = imgLen; - bi->ModuleCount++; - virtCursor = (virtCursor + imgLen + 0xFFFFF) & ~0xFFFFF; - FreePool(buf); + + BOOT_MODULE* mod = &bi->Modules[bi->ModuleCount++]; + AsciiStrCpyS(mod->Name, 32, name); + mod->PhysBase = (uintptr_t)buf; // Raw physical buffer from LoadFile + mod->ImageSize = (size_t)imgSz; + mod->VirtBase = 0; // Kernel will map this later } Print(L"6\n"); diff --git a/code/init/init.c b/code/init/init.c index 8013ed0..a622c54 100644 --- a/code/init/init.c +++ b/code/init/init.c @@ -2,20 +2,52 @@ #include #include #include - -// --------------------------------------------------------------------------- -// Syscall stubs -// --------------------------------------------------------------------------- -extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut, - const char* cmdline); -extern uint32_t SysCloseHandle(uint32_t handle); -extern void SysDebugPrint(const char* str); -extern uint32_t SysWaitForProcess(uint32_t handle); -extern void SysExitProcess(int code); -extern void SysYield(void); +#include #define STATUS_SUCCESS 0u +void itoa(int num, char* str) { + int i = 0; + int is_negative = 0; + + // Handle 0 explicitly + if (num == 0) { + str[i++] = '0'; + str[i] = '\0'; + return; + } + + // Handle negative numbers + if (num < 0) { + is_negative = 1; + num = -num; + } + + // Process individual digits (result is in reverse) + while (num != 0) { + int rem = num % 10; + str[i++] = rem + '0'; + num = num / 10; + } + + if (is_negative) { + str[i++] = '-'; + } + + str[i] = '\0'; // Null terminate + + // Reverse the string + int start = 0; + int end = i - 1; + while (start < end) { + char temp = str[start]; + str[start] = str[end]; + str[end] = temp; + start++; + end--; + } +} + void _start(const char* args) { uint32_t h = 0; @@ -25,6 +57,9 @@ void _start(const char* args) s = SysCreateProcess(args, &h, NULL); if (s != STATUS_SUCCESS) { SysDebugPrint("[init] FATAL: failed to start!\n"); + char buffer[12]; + itoa(s, buffer); + SysDebugPrint(buffer); SysDebugPrint("\n"); SysExitProcess(1); } diff --git a/code/kernel/boot_info.h b/code/kernel/boot_info.h index 66f756e..c455ae7 100644 --- a/code/kernel/boot_info.h +++ b/code/kernel/boot_info.h @@ -9,8 +9,9 @@ typedef struct BOOT_MODULE { char Name[32]; /* e.g. "hal.dll", "ide.sys" */ - uintptr_t VirtBase; /* where it was mapped */ - size_t ImageSize; + uintptr_t VirtBase; /* Virtual address (mapped) */ + uintptr_t PhysBase; /* Raw physical address (file) */ + size_t ImageSize; /* File size / Image size */ } BOOT_MODULE; typedef struct BOOT_INFO { @@ -24,7 +25,6 @@ typedef struct BOOT_INFO { uint32_t FramebufferWidth; uint32_t FramebufferHeight; uint32_t FramebufferStride; /* pixels per row */ - uint32_t _pad0; /* Loaded modules. */ BOOT_MODULE Modules[MAX_BOOT_MODULES]; @@ -41,5 +41,4 @@ typedef struct BOOT_INFO { /* From boot.ini */ uint32_t DebugEnabled; - uint32_t _pad2; } BOOT_INFO; diff --git a/code/kernel/io_file.c b/code/kernel/io_file.c index d68a4cf..38a77b3 100644 --- a/code/kernel/io_file.c +++ b/code/kernel/io_file.c @@ -46,17 +46,19 @@ __declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size) { static uint8_t ResolvePath(const char* path, char* resolved, size_t resolvedSize) { // Strip explicit drive prefix "N:\" uint8_t drive = 0; + bool fromDriveRoot = false; if (path[0] >= '0' && path[0] <= '7' && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) { drive = (uint8_t)(path[0] - '0'); path = path + 3; + fromDriveRoot = true; } char work[CWD_MAX * 2]; work[0] = '\0'; // Initialize our working base - if (path[0] == '\\' || path[0] == '/') { + if (path[0] == '\\' || path[0] == '/' || fromDriveRoot) { // Absolute path: start from the root (empty work buffer) // The segment loop below will automatically skip the leading separators. } else { diff --git a/code/kernel/ke_debug.c b/code/kernel/ke_debug.c index 9736988..f42efd4 100644 --- a/code/kernel/ke_debug.c +++ b/code/kernel/ke_debug.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include int _fltused; // fuckass @@ -62,3 +65,123 @@ __declspec(dllexport) void KePrintConsole(const char* buf, size_t len) { for (size_t i = 0; i < len; i++) PrintChar(buf[i], 0xFFFFFF, 0x000000); } + +// --------------------------------------------------------------------------- +// KeBugCheckProcess - user-mode fault handler +// +// Resolves the faulting RIP to a module name, prints a yellow crash banner, +// kills the offending process, then exits the current thread so the scheduler +// can continue running everything else. +// --------------------------------------------------------------------------- + +// Return the short exception name for a vector number. +static const char* FaultVectorName(int vector) { + switch (vector) { + case 0: return "#DE Divide Error"; + case 13: return "#GP General Protection"; + case 14: return "#PF Page Fault"; + default: return "Unknown Exception"; + } +} + +// Walk the process module list to find which image owns rip. +// Returns the module name string, or "unknown" if rip is outside all regions. +static const char* ResolveModule(PROCESS* proc, uintptr_t rip) { + // Check the main executable image first. + if (proc->ImageBase && proc->ImageSize && + rip >= proc->ImageBase && rip < proc->ImageBase + proc->ImageSize) + return proc->Name; + + // Walk the per-process module table (DLLs / shared libraries). + for (uint32_t i = 0; i < proc->ModuleCount; i++) { + uintptr_t base = proc->Modules[i].Base; + size_t size = proc->Modules[i].Size; + if (base && size && rip >= base && rip < base + size) + return proc->Modules[i].Name; + } + + return "unknown"; +} + +static void PrintCrashChar(char c) { + PrintChar(c, 0xFFAA00, 0x000000); // amber - distinct from green debug / red panic +} + +_Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2) { + THREAD* thread = KeGetCurrentThread(); + PROCESS* proc = thread ? thread->Owner : (PROCESS*)0; + + // Snapshot everything we need before touching process state. + const char* faultName = FaultVectorName(vector); + const char* procName = proc ? proc->Name : "unknown"; + uint32_t pid = proc ? proc->ProcessId : 0; + const char* modName = proc ? ResolveModule(proc, rip) : "unknown"; + + // Amber crash banner. + static const char banner[] = "\n*** APPLICATION CRASH ***\n"; + for (const char* p = banner; *p; p++) + PrintCrashChar(*p); + + printf_set_putchar(PrintCrashChar); + + if (vector == 14) + printf("Process: %s (PID %u) | Fault: %s\n" + "Module: %s | RIP: %016llx | Code: %04llx | CR2: %016llx\n", + procName, pid, faultName, + modName, + (unsigned long long)rip, + (unsigned long long)errCode, + (unsigned long long)cr2); + else + printf("Process: %s (PID %u) | Fault: %s\n" + "Module: %s | RIP: %016llx | Code: %04llx\n", + procName, pid, faultName, + modName, + (unsigned long long)rip, + (unsigned long long)errCode); + + // Restore normal (green) output for subsequent kernel messages. + printf_set_putchar(kernel_putchar); + + // Kill the process and exit this thread. + // KeExitThread sets ThreadDead then calls KeYield; the scheduler will + // never return here, so the process crash cannot affect anything else. + if (proc) KeExitProcess(proc, -1); + if (thread) KeExitThread(thread, -1); + + // Unreachable - KeExitThread never returns for a dead thread. + while (1) __halt(); +} + +// --------------------------------------------------------------------------- +// KeFaultDispatch - single C entry point for all three IDT fault stubs. +// +// The asm stubs (ke_syscall.asm) collect the CPU exception frame values and +// call here. We check the CS RPL to decide: kernel fault → KePanic (halt), +// user fault → KeBugCheckProcess (kill process, resume scheduler). +// --------------------------------------------------------------------------- +__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip, + uintptr_t errCode, uintptr_t cr2, + uintptr_t cs) { + if ((cs & 3) == 0) { + // Kernel-mode fault: unrecoverable. + switch (vector) { + case 0: + KePanic("PANIC: #DE Divide Error | RIP: %016llx\n", + (unsigned long long)rip); + case 13: + KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n", + (unsigned long long)rip, (unsigned long long)errCode); + case 14: + KePanic("PANIC: #PF Page Fault | RIP: %016llx | Code: %04llx | CR2: %016llx\n", + (unsigned long long)rip, (unsigned long long)errCode, + (unsigned long long)cr2); + default: + KePanic("PANIC: Exception #%d | RIP: %016llx\n", + vector, (unsigned long long)rip); + } + } + + // User-mode fault: recoverable - kill the process, resume the scheduler. + KeBugCheckProcess(vector, rip, errCode, cr2); +} diff --git a/code/kernel/ke_debug.h b/code/kernel/ke_debug.h index d7a6e2b..f85e2f2 100644 --- a/code/kernel/ke_debug.h +++ b/code/kernel/ke_debug.h @@ -4,8 +4,24 @@ #include void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v); -__declspec(dllexport) void DbgPrint(const char* fmt, ...); +__declspec(dllexport) void DbgPrint(const char* fmt, ...); __declspec(dllexport) void KePrintConsole(const char* buf, size_t len); -__declspec(dllexport) void KePanic(const char *fmt, ...); +__declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...); + +// --------------------------------------------------------------------------- +// Fault dispatch - called from the three IDT fault stubs (ke_syscall.asm). +// +// vector : CPU exception number (0=#DE, 13=#GP, 14=#PF) +// rip : faulting instruction pointer (from the CPU exception frame) +// errCode : CPU error code (0 for #DE) +// cr2 : value of CR2 at fault time (meaningful only for #PF) +// cs : CS from the exception frame; RPL bits determine ring +// +// Kernel-mode faults (cs & 3 == 0) call KePanic and halt. +// User-mode faults (cs & 3 == 3) kill the offending process and yield. +// --------------------------------------------------------------------------- +__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip, + uintptr_t errCode, uintptr_t cr2, + uintptr_t cs); #endif // KE_DEBUG_H diff --git a/code/kernel/ke_process.c b/code/kernel/ke_process.c index c2366fd..08a0f7f 100644 --- a/code/kernel/ke_process.c +++ b/code/kernel/ke_process.c @@ -50,6 +50,31 @@ static void ProcessDestroy(OBJECT* obj) { } } + // Release the main executable image. + if (proc->ImageBase && proc->ImageSize) { + MmFreeUserMemory((void *)proc->ImageBase, proc->ImageSize); + proc->ImageBase = 0; + proc->ImageSize = 0; + } + + // Release per-process modules. + for (uint32_t i = 0; i < proc->ModuleCount; i++) { + if (proc->Modules[i].Base && proc->Modules[i].Size) { + MmFreeUserMemory((void *)proc->Modules[i].Base, proc->Modules[i].Size); + } + proc->Modules[i].Base = 0; + proc->Modules[i].Size = 0; + proc->Modules[i].Name[0] = '\0'; + } + proc->ModuleCount = 0; + + // Release the user command line buffer. + if (proc->UserCmdlineBase && proc->UserCmdlineSize) { + MmFreeUserMemory((void *)proc->UserCmdlineBase, proc->UserCmdlineSize); + proc->UserCmdlineBase = 0; + proc->UserCmdlineSize = 0; + } + // Release the exit event. if (proc->ExitEvent) { ObDereferenceObject(&proc->ExitEvent->Header); @@ -86,6 +111,11 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) { proc->ThreadCount = 0; proc->HandleLock = (SPINLOCK)SPINLOCK_INIT; proc->ExitCode = 0; + proc->ImageBase = 0; + proc->ImageSize = 0; + proc->UserCmdlineBase = 0; + proc->UserCmdlineSize = 0; + proc->ModuleCount = 0; // Slot 0 is the invalid handle - leave it NULL. memset(proc->Handles, 0, sizeof(proc->Handles)); diff --git a/code/kernel/ke_process.h b/code/kernel/ke_process.h index e662c22..9a560c8 100644 --- a/code/kernel/ke_process.h +++ b/code/kernel/ke_process.h @@ -6,10 +6,20 @@ #include #include #include +#include #define PROCESS_NAME_MAX 64 #define HANDLE_TABLE_SIZE 256 + +#define PROCESS_MODULE_MAX 16 + +typedef struct PROCESS_MODULE { + char Name[32]; + uintptr_t Base; + size_t Size; +} PROCESS_MODULE; + typedef struct PROCESS { OBJECT Header; /* must be first */ @@ -36,6 +46,18 @@ typedef struct PROCESS { int ExitCode; + /* Main executable image. Used for cleanup. */ + uintptr_t ImageBase; + size_t ImageSize; + + /* User command line buffer passed to the initial thread. */ + uintptr_t UserCmdlineBase; + size_t UserCmdlineSize; + + /* Per-process loaded modules (user processes only). */ + PROCESS_MODULE Modules[PROCESS_MODULE_MAX]; + uint32_t ModuleCount; + /* Signaled (manual-reset) when the process exits. Waiters block on this via KeWaitForEvent. */ EVENT* ExitEvent; @@ -43,13 +65,13 @@ typedef struct PROCESS { LIST_ENTRY GlobalLink; /* node in the global process list */ } PROCESS; -KSTATUS KeCreateProcess(const char* name, PROCESS **out); -void KeExitProcess(PROCESS* process, int exitCode); -KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut); +KSTATUS KeCreateProcess(const char* name, PROCESS **out); +void KeExitProcess(PROCESS* process, int exitCode); +KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut); /* Handle table operations. */ -KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut); -void KeCloseHandle(PROCESS* process, uint32_t handle); +KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut); +void KeCloseHandle(PROCESS* process, uint32_t handle); OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle); /* Return the global process list head (kernel-internal). */ diff --git a/code/kernel/ke_sched.c b/code/kernel/ke_sched.c index b246ad0..02924e7 100644 --- a/code/kernel/ke_sched.c +++ b/code/kernel/ke_sched.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,6 @@ void KeInitializeScheduler(void) { for (int i = 0; i < PriorityCount; i++) LIST_INIT(&gRunQueue[i]); gSchedLock = (SPINLOCK)SPINLOCK_INIT; - extern KSTATUS KeCreateProcess(const char *, PROCESS **); PROCESS* idleProc; KeCreateProcess("idle", &idleProc); diff --git a/code/kernel/ke_syscall.asm b/code/kernel/ke_syscall.asm index 7a9ccb5..6934ced 100644 --- a/code/kernel/ke_syscall.asm +++ b/code/kernel/ke_syscall.asm @@ -37,93 +37,76 @@ KeSyscallEntry PROC KeSyscallEntry ENDP ; --------------------------------------------------------------------------- -; Fault stubs - print a message and halt. +; Fault stubs - thin wrappers that collect the CPU exception frame and +; forward to KeFaultDispatch (C), which handles both kernel panics and +; user-process bugchecks. +; +; KeFaultDispatch signature (MS x64): +; void KeFaultDispatch(int vector, // rcx +; UINT64 rip, // rdx +; UINT64 errCode, // r8 +; UINT64 cr2, // r9 +; UINT64 cs) // [rsp+20h] (5th arg, on stack) ; --------------------------------------------------------------------------- -EXTERN KePanic:PROC +EXTERN KeFaultDispatch:PROC -.data -; We define separate strings to maximize the info we can print -StrKernel DB "Kernel Mode", 0 -StrUser DB "User Mode", 0 - -MsgFaultDE DB "PANIC: Divide Error (#0) | RIP: %016llx | Mode: %s", 0Ah, 0 -MsgFaultGP DB "PANIC: General Protection (#13) | RIP: %016llx | Code: %04x | Mode: %s", 0Ah, 0 -MsgFaultPF DB "PANIC: Page Fault (#14) | RIP: %016llx | Code: %04x | CR2: %016llx | %s", 0Ah, 0 - -.code +; Allocation for a 5-argument call (MS x64): +; 32 bytes shadow space (0x00..0x1F) +; 8 bytes for 5th arg (0x20..0x27) +; Total = 0x28 bytes. +; After "and rsp,-16" rsp%16==0; sub 0x28 (40) → rsp%16==8, +; so the subsequent "call" push makes rsp 16-byte aligned at callee entry. ✓ +FAULT_FRAME_SIZE EQU 28h ; --------------------------------------------------------- -; Divide Error (#0) - NO Error Code pushed by CPU -; Stack: [rsp] = RIP, [rsp+8] = CS, [rsp+16] = RFLAGS +; Divide Error (#0) - NO error code pushed by CPU +; Exception frame at entry: [rsp]=RIP [rsp+8]=CS [rsp+16]=RFLAGS ; --------------------------------------------------------- KeFaultDE PROC - mov rdx, [rsp] ; Arg 2: RIP + mov rdx, [rsp] ; Arg2: rip + xor r8, r8 ; Arg3: errCode = 0 (no error code for #DE) + xor r9, r9 ; Arg4: cr2 = 0 (not a page fault) + mov r10, [rsp+8] ; cs (save before we clobber rsp) + mov ecx, 0 ; Arg1: vector = 0 - ; Determine User/Kernel mode from CS - mov rax, [rsp + 8] ; Read CS - and rax, 3 ; Isolate the bottom 2 bits (RPL) - lea r8, StrKernel - lea r9, StrUser - test rax, rax ; If 0, it's Kernel. If >0, it's User. - cmovnz r8, r9 ; Arg 3: Mode String (moves r9 into r8 if Not Zero) - - lea rcx, MsgFaultDE ; Arg 1: Format string - - ; Safely align stack and allocate shadow space for the call - and rsp, -16 ; Force 16-byte ABI stack alignment - sub rsp, 20h ; Allocate 32 bytes of shadow space - call KePanic + and rsp, -16 + sub rsp, FAULT_FRAME_SIZE + mov [rsp+20h], r10 ; Arg5: cs + call KeFaultDispatch ; _Noreturn - never returns KeFaultDE ENDP ; --------------------------------------------------------- -; General Protection Fault (#13) - Error Code PUSHED -; Stack: [rsp] = ErrCode, [rsp+8] = RIP, [rsp+16] = CS +; General Protection Fault (#13) - error code pushed by CPU +; Exception frame at entry: [rsp]=ErrCode [rsp+8]=RIP [rsp+16]=CS ; --------------------------------------------------------- KeFaultGP PROC - mov r8, [rsp] ; Arg 3: Error Code - mov rdx, [rsp + 8] ; Arg 2: RIP + mov r8, [rsp] ; Arg3: errCode + mov rdx, [rsp+8] ; Arg2: rip + xor r9, r9 ; Arg4: cr2 = 0 (not a page fault) + mov r10, [rsp+16] ; cs + mov ecx, 13 ; Arg1: vector = 13 - ; Determine User/Kernel mode from CS - mov rax, [rsp + 16] ; Read CS - and rax, 3 - lea r9, StrKernel - lea r10, StrUser - test rax, rax - cmovnz r9, r10 ; Arg 4: Mode String - - lea rcx, MsgFaultGP ; Arg 1: Format string - - and rsp, -16 ; Align stack - sub rsp, 20h ; 32 bytes shadow space - call KePanic + and rsp, -16 + sub rsp, FAULT_FRAME_SIZE + mov [rsp+20h], r10 ; Arg5: cs + call KeFaultDispatch KeFaultGP ENDP ; --------------------------------------------------------- -; Page Fault (#14) - Error Code PUSHED -; Stack: [rsp] = ErrCode, [rsp+8] = RIP, [rsp+16] = CS +; Page Fault (#14) - error code pushed by CPU +; Exception frame at entry: [rsp]=ErrCode [rsp+8]=RIP [rsp+16]=CS ; --------------------------------------------------------- KeFaultPF PROC - mov r8, [rsp] ; Arg 3: Error Code - mov rdx, [rsp + 8] ; Arg 2: RIP - mov r9, cr2 ; Arg 4: CR2 (The faulting virtual address) + mov r8, [rsp] ; Arg3: errCode + mov rdx, [rsp+8] ; Arg2: rip + mov r9, cr2 ; Arg4: cr2 (faulting virtual address) + mov r10, [rsp+16] ; cs + mov ecx, 14 ; Arg1: vector = 14 - ; Determine User/Kernel mode from CS - mov rax, [rsp + 16] ; Read CS - and rax, 3 - lea r10, StrKernel - lea r11, StrUser - test rax, rax - cmovnz r10, r11 ; r10 now holds the Mode String - - lea rcx, MsgFaultPF ; Arg 1: Format string - - ; We have 5 arguments for PF! (rcx, rdx, r8, r9, and the stack) - ; x64 ABI dictates the 5th argument goes on the stack just above the shadow space. - and rsp, -16 ; Align stack - sub rsp, 30h ; 32 bytes shadow space + 16 bytes for 5th arg and padding - mov [rsp + 20h], r10 ; Arg 5: Mode String (placed exactly after shadow space) - - call KePanic + and rsp, -16 + sub rsp, FAULT_FRAME_SIZE + mov [rsp+20h], r10 ; Arg5: cs + call KeFaultDispatch KeFaultPF ENDP EXTERN KeIrqDispatch:PROC diff --git a/code/kernel/ke_syscall.c b/code/kernel/ke_syscall.c index e12d15a..c6b2931 100644 --- a/code/kernel/ke_syscall.c +++ b/code/kernel/ke_syscall.c @@ -769,6 +769,15 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2, s = KeCreateProcess(path, &proc); if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; } + // Inherit CWD from parent + PROCESS* parent = KeGetCurrentProcess(); + if (parent) { + uintptr_t flags = KeAcquireSpinlock(&parent->CwdLock); + strncpy(proc->CurrentDirectory, parent->CurrentDirectory, sizeof(proc->CurrentDirectory) - 1); + proc->CurrentDirectory[sizeof(proc->CurrentDirectory) - 1] = '\0'; + KeReleaseSpinlock(&parent->CwdLock, flags); + } + // Store the command line in the process so _start can pick it up. // If no cmdline was supplied default to just the image path. if (cmdline && cmdline[0]) @@ -781,8 +790,10 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2, // Copy into user memory so the initial thread can access it directly. size_t cmdlineLen = strlen(proc->Cmdline) + 1; char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen); - if (!userCmdline) { ObDereferenceObject(&proc->Header); return STATUS_NO_MEMORY; } + if (!userCmdline) { MmFreeUserMemory(mem, size); ObDereferenceObject(&proc->Header); return STATUS_NO_MEMORY; } memcpy(userCmdline, proc->Cmdline, cmdlineLen); + proc->UserCmdlineBase = (uintptr_t)userCmdline; + proc->UserCmdlineSize = cmdlineLen; // Map the PE image into memory. uintptr_t imageBase = 0; @@ -790,13 +801,26 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2, MmFreeUserMemory(mem, size); if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; } + s = LdrResolveImportsForProcess(proc, imageBase); + if (!KSUCCESS(s)) { + MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase)); + ObDereferenceObject(&proc->Header); + return s; + } + // Spin up the initial thread. Pass userCmdline as UserArg so _start // receives it directly in RCX without an extra syscall. THREAD* thread; uintptr_t entryPoint = LdrGetEntryPoint(imageBase); s = KeCreateThread(proc, entryPoint, (uintptr_t)userCmdline, PriorityNormal, &thread); - if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; } + if (!KSUCCESS(s)) { + MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase)); + ObDereferenceObject(&proc->Header); + return s; + } + proc->ImageBase = imageBase; + proc->ImageSize = LdrGetImageSize(imageBase); // Give the caller a handle to the new process so they can wait on it. PROCESS* caller = KeGetCurrentProcess(); @@ -809,7 +833,6 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2, *handleOut = handle; return STATUS_SUCCESS; } - // --------------------------------------------------------------------------- // SYS_WAIT_FOR_PROCESS (51) a0=handle // Blocks until the target process signals its ExitEvent. diff --git a/code/kernel/kmain.c b/code/kernel/kmain.c index 7f0aeba..59aa582 100644 --- a/code/kernel/kmain.c +++ b/code/kernel/kmain.c @@ -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 // --------------------------------------------------------------------------- diff --git a/code/kernel/ldr_image.c b/code/kernel/ldr_image.c index 4e43fda..3807481 100644 --- a/code/kernel/ldr_image.c +++ b/code/kernel/ldr_image.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -109,39 +111,72 @@ typedef struct { typedef struct { char Name[32]; uintptr_t Base; + size_t Size; } MODULE_ENTRY; static MODULE_ENTRY gModules[MAX_MODULES]; -static int gModuleCount = 0; +static uint32_t gModuleCount = 0; + +static uintptr_t FindModuleBaseInTable(const MODULE_ENTRY* modules, uint32_t count, const char* name) { + for (uint32_t i = 0; i < count; i++) { + if (_stricmp(modules[i].Name, name) == 0) return modules[i].Base; + } + return 0; +} + +static void RegisterModuleEntry(MODULE_ENTRY* entry, const char* name, uintptr_t base, size_t size) { + strncpy(entry->Name, name, 31); + entry->Name[31] = '\0'; + entry->Base = base; + entry->Size = size; +} void LdrRegisterModulesFromBootInfo(const BOOT_INFO* bootInfo) { - for (uint32_t i = 0; i < bootInfo->ModuleCount && i < MAX_MODULES; i++) { - strncpy(gModules[gModuleCount].Name, bootInfo->Modules[i].Name, 31); - gModules[gModuleCount].Base = bootInfo->Modules[i].VirtBase; - gModuleCount++; + for (uint32_t i = 0; i < bootInfo->ModuleCount && gModuleCount < MAX_MODULES; i++) { + // Only register modules that have a virtual base (Kernel, HAL) + if (bootInfo->Modules[i].VirtBase != 0) { + RegisterModuleEntry(&gModules[gModuleCount], + bootInfo->Modules[i].Name, + bootInfo->Modules[i].VirtBase, + bootInfo->Modules[i].ImageSize); + gModuleCount++; + } } } void LdrRegisterModule(const char* name, uintptr_t base) { if (gModuleCount < MAX_MODULES) { - strncpy(gModules[gModuleCount].Name, name, 31); - gModules[gModuleCount].Base = base; + RegisterModuleEntry(&gModules[gModuleCount], name, base, 0); gModuleCount++; } } -static uintptr_t FindModuleBase(const char* name) { - for (int i = 0; i < gModuleCount; i++) { - if (_stricmp(gModules[i].Name, name) == 0) return gModules[i].Base; +static uintptr_t FindGlobalModuleBase(const char* name) { + return FindModuleBaseInTable(gModules, gModuleCount, name); +} + +static uintptr_t FindProcessModuleBaseInTable( + const PROCESS_MODULE* modules, + uint32_t count, + const char* name +) { + for (uint32_t i = 0; i < count; i++) { + if (_stricmp(modules[i].Name, name) == 0) { + return modules[i].Base; + } } return 0; } -// --------------------------------------------------------------------------- -// Export lookup -// --------------------------------------------------------------------------- -void* LdrGetExport(const char* moduleName, const char* functionName) { - uintptr_t base = FindModuleBase(moduleName); +static uintptr_t FindProcessOrGlobalModuleBase(PROCESS* process, const char* name) { + if (process) { + uintptr_t base = FindProcessModuleBaseInTable(process->Modules, process->ModuleCount, name); + if (base) return base; + } + return FindGlobalModuleBase(name); +} + +static void* GetExportAtBase(uintptr_t base, const char* functionName) { if (!base) return (void *)0; uint32_t peOffset = *(uint32_t *)(base + 0x3C); @@ -169,6 +204,26 @@ void* LdrGetExport(const char* moduleName, const char* functionName) { return (void *)0; } +// --------------------------------------------------------------------------- +// Export lookup +// --------------------------------------------------------------------------- +void* LdrGetExport(const char* moduleName, const char* functionName) { + uintptr_t base = FindGlobalModuleBase(moduleName); + return GetExportAtBase(base, functionName); +} + +void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const char* functionName) { + uintptr_t base = FindProcessOrGlobalModuleBase(process, moduleName); + return GetExportAtBase(base, functionName); +} + +size_t LdrGetImageSize(uintptr_t base) { + uint32_t peOffset = *(uint32_t *)(base + 0x3C); + PE_HEADER* pe = (PE_HEADER *)(base + peOffset); + if (pe->Signature != PE_SIGNATURE) return 0; + return (size_t)pe->OptionalHeader.SizeOfImage; +} + // --------------------------------------------------------------------------- // Entry point lookup // --------------------------------------------------------------------------- @@ -216,43 +271,210 @@ static KSTATUS LdrApplyRelocations(uintptr_t base, uintptr_t delta) { // --------------------------------------------------------------------------- // Import resolution // --------------------------------------------------------------------------- -KSTATUS LdrResolveImports(uintptr_t imageBase) { +typedef void* (*EXPORT_LOOKUP_FN)(void* ctx, const char* moduleName, const char* functionName); + +static void* GlobalExportLookup(void* ctx, const char* moduleName, const char* functionName) { + (void)ctx; + return LdrGetExport(moduleName, functionName); +} + +static void* ProcessExportLookup(void* ctx, const char* moduleName, const char* functionName) { + return LdrGetExportForProcess((PROCESS*)ctx, moduleName, functionName); +} + +static KSTATUS LdrResolveImportsCommon( + PROCESS* process, + uintptr_t imageBase, + EXPORT_LOOKUP_FN lookup, + void* ctx +) { uint32_t peOffset = *(uint32_t *)(imageBase + 0x3C); PE_HEADER* pe = (PE_HEADER *)(imageBase + peOffset); OPT_HEADER64* opt = &pe->OptionalHeader; - if (opt->NumberOfRvaAndSizes < 2) return STATUS_SUCCESS; // no imports + if (opt->NumberOfRvaAndSizes < 2) + return STATUS_SUCCESS; + uint32_t impRva = opt->DataDirectory[1].VirtualAddress; - if (!impRva) return STATUS_SUCCESS; + if (!impRva) + return STATUS_SUCCESS; IMPORT_DESC* imp = (IMPORT_DESC *)(imageBase + impRva); + while (imp->Name) { const char* dllName = (const char *)(imageBase + imp->Name); - if (!_stricmp(dllName, "kernel32.dll") || !_stricmp(dllName, "user32.dll") - || !_stricmp(dllName, "ole32.dll") || !_stricmp(dllName, "winmm.dll")) + + // Win32 compatibility remap + if (!_stricmp(dllName, "kernel32.dll") || + !_stricmp(dllName, "user32.dll") || + !_stricmp(dllName, "ole32.dll") || + !_stricmp(dllName, "winmm.dll")) { dllName = "win32.dll"; - uint64_t* thunk = (uint64_t *)(imageBase + imp->FirstThunk); - uint64_t* origThunk = (uint64_t *)(imageBase + (imp->OriginalFirstThunk - ? imp->OriginalFirstThunk - : imp->FirstThunk)); + } + + // + // Ensure dependency is loaded into the process + // + if (process) { + uintptr_t modBase = + FindProcessOrGlobalModuleBase(process, dllName); + + if (!modBase) { + KSTATUS s = + LdrLoadModuleForProcess(process, dllName, &modBase); + + if (!KSUCCESS(s)) { + DbgPrint( + "LdrResolveImports: Failed to load dependency %s (%d)\n", + dllName, + s + ); + return s; + } + } + } + + uint64_t* thunk = + (uint64_t *)(imageBase + imp->FirstThunk); + + uint64_t* origThunk = + (uint64_t *)(imageBase + + (imp->OriginalFirstThunk + ? imp->OriginalFirstThunk + : imp->FirstThunk)); + while (*origThunk) { if (*origThunk & (1ULL << 63)) { - // Import by ordinal - not supported. - DbgPrint("LdrResolveImports: Ordinal imports not supported! (%s)\n", dllName); + DbgPrint( + "LdrResolveImports: Ordinal imports not supported! (%s)\n", + dllName + ); return STATUS_NOT_SUPPORTED; } - const char* funcName = (const char *)(imageBase + (*origThunk & 0xFFFFFFFF) + 2); - void* addr = LdrGetExport(dllName, funcName); + + const char* funcName = + (const char *)(imageBase + + (*origThunk & 0xFFFFFFFF) + 2); + + void* addr = lookup(ctx, dllName, funcName); + if (!addr) { - DbgPrint("LdrResolveImports: Function not found! (%s)\n", funcName); + DbgPrint( + "LdrResolveImports: Function not found! (%s!%s)\n", + dllName, + funcName + ); return STATUS_NOT_FOUND; } + *thunk = (uint64_t)addr; + thunk++; origThunk++; } + imp++; } + + return STATUS_SUCCESS; +} + +KSTATUS LdrResolveImports(uintptr_t imageBase) { + return LdrResolveImportsCommon( + (PROCESS*)0, + imageBase, + GlobalExportLookup, + (void*)0 + ); +} + +KSTATUS LdrResolveImportsForProcess(PROCESS* process, uintptr_t imageBase) { + return LdrResolveImportsCommon( + process, + imageBase, + ProcessExportLookup, + process + ); +} + +static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base) { + if (!process || !name || !base) return STATUS_INVALID_PARAMETER; + + for (uint32_t i = 0; i < process->ModuleCount; i++) { + if (_stricmp(process->Modules[i].Name, name) == 0) { + return STATUS_SUCCESS; + } + } + + if (process->ModuleCount >= PROCESS_MODULE_MAX) return STATUS_NO_MEMORY; + + PROCESS_MODULE* mod = &process->Modules[process->ModuleCount++]; + strncpy(mod->Name, name, 31); + mod->Name[31] = '\0'; + mod->Base = base; + mod->Size = LdrGetImageSize(base); + return STATUS_SUCCESS; +} + +KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_t* baseOut) { + if (!process || !filename) return STATUS_INVALID_PARAMETER; + + for (uint32_t i = 0; i < process->ModuleCount; i++) { + if (_stricmp(process->Modules[i].Name, filename) == 0) { + if (baseOut) *baseOut = process->Modules[i].Base; + return STATUS_SUCCESS; + } + } + + FILE_OBJECT* f; + KSTATUS s = IoOpenFile(filename, &f); + if (!KSUCCESS(s)) { + char path[260]; + memcpy(path, "0:/system/", 10); + memcpy(path + 10, filename, strnlen(filename, 248) + 1); + path[259] = '\0'; + s = IoOpenFile(path, &f); + if (!KSUCCESS(s)) { + DbgPrint("LdrLoadModuleForProcess: Failed to open %s (%d)\n", filename, s); + return s; + } + } + + uint64_t fileSize = IoGetFileSize(f); + void* mem = MmAllocatePool((size_t)fileSize); + if (!mem) { + IoCloseFile(f); + return STATUS_NO_MEMORY; + } + + size_t got = 0; + s = IoReadFile(f, mem, (size_t)fileSize, &got); + IoCloseFile(f); + if (!KSUCCESS(s)) { + MmFreePool(mem); + return s; + } + + uintptr_t imageBase = 0; + s = LdrLoadImage(mem, (size_t)fileSize, &imageBase, true); + MmFreePool(mem); + if (!KSUCCESS(s)) { + return s; + } + + s = LdrResolveImportsForProcess(process, imageBase); + if (!KSUCCESS(s)) { + MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase)); + return s; + } + + s = LdrRegisterProcessModule(process, filename, imageBase); + if (!KSUCCESS(s)) { + MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase)); + return s; + } + + if (baseOut) *baseOut = imageBase; return STATUS_SUCCESS; } @@ -271,7 +493,6 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u if (!mem) return STATUS_NO_MEMORY; memset(mem, 0, imageSize); - // Copy headers and sections memcpy(mem, buffer, opt->SizeOfHeaders); SECTION_HEADER* sec = (SECTION_HEADER *)((uint8_t *)&pe->OptionalHeader + pe->SizeOfOptionalHeader); for (int i = 0; i < pe->NumberOfSections; i++) { @@ -280,21 +501,16 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u if (sec[i].SizeOfRawData) { memcpy(dst, src, sec[i].SizeOfRawData < sec[i].VirtualSize ? sec[i].SizeOfRawData : sec[i].VirtualSize); } - + uint64_t prot = PAGE_PRESENT | PAGE_WRITE | (user ? PAGE_USER : 0); MmSetPageProtect((uintptr_t)mem + sec[i].VirtualAddress, sec[i].VirtualSize, prot); } - // Apply Relocations uintptr_t delta = (uintptr_t)mem - opt->ImageBase; if (delta != 0) { LdrApplyRelocations((uintptr_t)mem, delta); } - // Resolve Imports - KSTATUS status = LdrResolveImports((uintptr_t)mem); - if (status != STATUS_SUCCESS) return status; - *baseOut = (uintptr_t)mem; return STATUS_SUCCESS; } diff --git a/code/kernel/ldr_image.h b/code/kernel/ldr_image.h index 3b93aa8..45ae5f7 100644 --- a/code/kernel/ldr_image.h +++ b/code/kernel/ldr_image.h @@ -10,6 +10,9 @@ // On success, *baseOut receives the virtual base address. KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user); +// Return the SizeOfImage recorded in the PE header for an already-mapped image. +size_t LdrGetImageSize(uintptr_t base); + // Look up a named export in a loaded module. // 'moduleName' must match a name in BOOT_INFO.Modules (e.g., "hal.dll"). void* LdrGetExport(const char* moduleName, const char* functionName); @@ -17,9 +20,17 @@ void* LdrGetExport(const char* moduleName, const char* functionName); // Get the entry point address of a loaded image. uintptr_t LdrGetEntryPoint(uintptr_t base); -// Resolve the import table of an already-loaded image. +// Resolve the import table of an already-loaded image using the global registry. KSTATUS LdrResolveImports(uintptr_t imageBase); +// Resolve imports using a process-local module registry. +struct PROCESS; +KSTATUS LdrResolveImportsForProcess(struct PROCESS* process, uintptr_t imageBase); + +// Load a DLL for a specific process, resolve it against that process's +// loaded modules, and register it in the process-local registry. +KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut); + // Called by KernelMain to register all modules from BOOT_INFO so that // LdrGetExport can find them. struct BOOT_INFO; // forward declare for the compiler to shut the fuck up diff --git a/code/kernel/ob_object.h b/code/kernel/ob_object.h index 64f63ba..5a79a4c 100644 --- a/code/kernel/ob_object.h +++ b/code/kernel/ob_object.h @@ -19,7 +19,7 @@ typedef enum OBJECT_TYPE { typedef struct OBJECT { OBJECT_TYPE Type; - volatile long RefCount; // interlocked; long = 4 bytes on MSVC x64 + volatile long RefCount; // interlocked void (*Destroy)(struct OBJECT* self); } OBJECT; diff --git a/code/libc.c b/code/libc.c index 892ef72..de38fcd 100644 --- a/code/libc.c +++ b/code/libc.c @@ -73,6 +73,16 @@ size_t strlen(const char* s) { return n; } +size_t strnlen(const char *s, size_t maxlen) { + size_t len; + for (len = 0; len < maxlen; len++) { + if (s[len] == '\0') { + break; + } + } + return len; +} + char* strchr(const char* s, int c) { while (*s) { if (*s == (char)c) return (char *)s; diff --git a/code/posix/posix.h b/code/posix/posix.h index 6203acf..7ffebc1 100644 --- a/code/posix/posix.h +++ b/code/posix/posix.h @@ -32,6 +32,7 @@ extern int errno; // defined in posix_fd.c; single global in v1 #define ENODEV 19 #define ENOTDIR 20 #define EINVAL 22 +#define ESPIPE 29 #define EMFILE 24 #define ENOSPC 28 #define ERANGE 34 diff --git a/code/posix/posix_dir.c b/code/posix/posix_dir.c index 8f1a590..40b64b4 100644 --- a/code/posix/posix_dir.c +++ b/code/posix/posix_dir.c @@ -1,17 +1,9 @@ // posix_dir.c - chdir, getcwd, opendir, readdir, closedir #include +#include #include "posix.h" #include "posix_path.h" -extern uint32_t SysGetCurrentPid(void); -extern uint32_t SysSetCwd(const char* path); -extern uint32_t SysGetCwd(char* buf, size_t size); -extern uint32_t SysOpenDir(const char* path, uint32_t* handleOut); -extern uint32_t SysReadDir(uint32_t handle, void* entryOut); -extern void SysCloseHandle(uint32_t handle); -extern void* SysAlloc(size_t); -extern void SysFree(void*, size_t); - // KDIR_ENTRY layout mirrors io_file.h - duplicated here to avoid // pulling kernel headers into posix.dll #define KDIR_NAME_MAX 260 @@ -23,7 +15,7 @@ typedef struct { #define KATTR_DIR 0x10 -int chdir(const char* path) { +__declspec(dllexport) int chdir(const char* path) { char native[512]; PosixTranslatePath(path, native, sizeof(native)); uint32_t s = SysSetCwd(native); @@ -31,7 +23,7 @@ int chdir(const char* path) { return 0; } -char* getcwd(char* buf, size_t size) { +__declspec(dllexport) char* getcwd(char* buf, size_t size) { if (!buf || size == 0) { errno = EINVAL; return (char*)0; } char native[512]; uint32_t s = SysGetCwd(native, sizeof(native)); @@ -40,7 +32,7 @@ char* getcwd(char* buf, size_t size) { return buf; } -DIR* opendir(const char* path) { +__declspec(dllexport) DIR* opendir(const char* path) { char native[512]; PosixTranslatePath(path, native, sizeof(native)); uint32_t kh; @@ -55,7 +47,7 @@ DIR* opendir(const char* path) { return d; } -struct dirent* readdir(DIR* d) { +__declspec(dllexport) struct dirent* readdir(DIR* d) { if (!d) { errno = EBADF; return (struct dirent*)0; } KDIR_ENTRY ke; uint32_t s = SysReadDir(d->KHandle, &ke); @@ -70,7 +62,7 @@ struct dirent* readdir(DIR* d) { return &d->Current; } -int closedir(DIR* d) { +__declspec(dllexport) int closedir(DIR* d) { if (!d) { errno = EBADF; return -1; } SysCloseHandle(d->KHandle); SysFree(d, sizeof(DIR)); diff --git a/code/posix/posix_fd.c b/code/posix/posix_fd.c index 0059dc0..d10367e 100644 --- a/code/posix/posix_fd.c +++ b/code/posix/posix_fd.c @@ -1,20 +1,13 @@ // posix_fd.c - file descriptor table #include "posix.h" #include "posix_fd.h" - -// yeah -#ifdef errno -#undef errno -#endif +#include int errno = 0; static FD_ENTRY gFdTable[FD_TABLE_PIDS][FD_MAX]; -// Syscall stub declarations (from syscall.dll) -extern uint32_t SysGetCurrentPid(void); - -void PosixFdInit(void) { +__declspec(dllexport) void PosixFdInit(void) { uint32_t pid = SysGetCurrentPid(); uint32_t slot = pid % FD_TABLE_PIDS; @@ -28,7 +21,7 @@ void PosixFdInit(void) { gFdTable[slot][2] = (FD_ENTRY){ FD_CONSOLE_W, 0, true }; } -int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle) { +__declspec(dllexport) int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle) { uint32_t slot = pid % FD_TABLE_PIDS; for (int fd = 3; fd < FD_MAX; fd++) { if (!gFdTable[slot][fd].Used) { @@ -40,7 +33,7 @@ int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle) { return -1; } -FD_ENTRY* FdGet(uint32_t pid, int fd) { +__declspec(dllexport) FD_ENTRY* FdGet(uint32_t pid, int fd) { if (fd < 0 || fd >= FD_MAX) { errno = EBADF; return (FD_ENTRY*)0; } uint32_t slot = pid % FD_TABLE_PIDS; FD_ENTRY* e = &gFdTable[slot][fd]; @@ -48,7 +41,7 @@ FD_ENTRY* FdGet(uint32_t pid, int fd) { return e; } -void FdFree(uint32_t pid, int fd) { +__declspec(dllexport) void FdFree(uint32_t pid, int fd) { if (fd < 0 || fd >= FD_MAX) return; uint32_t slot = pid % FD_TABLE_PIDS; gFdTable[slot][fd] = (FD_ENTRY){ FD_FREE, 0, false }; diff --git a/code/posix/posix_fd.h b/code/posix/posix_fd.h index 0310089..fdb82f2 100644 --- a/code/posix/posix_fd.h +++ b/code/posix/posix_fd.h @@ -22,13 +22,13 @@ typedef struct { #define FD_TABLE_PIDS 32 // circular, indexed by pid % FD_TABLE_PIDS // Called from _start before main(); sets up fds 0/1/2 for this process. -void PosixFdInit(void); +__declspec(dllexport) void PosixFdInit(void); // Allocate the lowest free fd >= 3. Returns -1 and sets errno=EMFILE on failure. -int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle); +__declspec(dllexport) int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle); // Look up an fd. Returns NULL and sets errno=EBADF if invalid. -FD_ENTRY* FdGet(uint32_t pid, int fd); +__declspec(dllexport) FD_ENTRY* FdGet(uint32_t pid, int fd); // Release an fd slot (does NOT close the kernel handle). -void FdFree(uint32_t pid, int fd); +__declspec(dllexport) void FdFree(uint32_t pid, int fd); diff --git a/code/posix/posix_io.c b/code/posix/posix_io.c index 84bd5c4..a91049e 100644 --- a/code/posix/posix_io.c +++ b/code/posix/posix_io.c @@ -1,23 +1,11 @@ // posix_io.c - open, read, write, close, lseek #include #include +#include #include "posix.h" #include "posix_fd.h" #include "posix_path.h" -extern uint32_t SysGetCurrentPid(void); -extern uint32_t SysOpenFile (const char* path, uint32_t* handleOut); -extern uint32_t SysCreateFile (const char* path, uint32_t* handleOut); -extern uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize); -extern uint32_t SysReadFile (uint32_t handle, void* buf, size_t size, size_t* bytesRead); -extern uint32_t SysWriteFile (uint32_t handle, const void* buf, size_t size, size_t* written); -extern uint32_t SysSeekFile (uint32_t handle, uint64_t offset); -extern uint64_t SysGetFilePos (uint32_t handle); -extern uint64_t SysGetFileSize(uint32_t handle); -extern void SysCloseHandle(uint32_t handle); -extern void SysWriteConsole(const char* buf, size_t len); -extern char SysKbReadChar(void); - // KSTATUS -> errno static int KsToErrno(uint32_t s) { switch (s) { @@ -34,7 +22,12 @@ static int KsToErrno(uint32_t s) { } } -int open(const char* path, int flags, ...) { +__declspec(dllexport) int ClearConsoleScreenBuffer(void* hConsole) { + SysScreenClear(0x00000000); + return 1; +} + +__declspec(dllexport) int open(const char* path, int flags, ...) { char native[512]; const char* p = PosixTranslatePath(path, native, sizeof(native)); @@ -72,7 +65,7 @@ int open(const char* path, int flags, ...) { return fd; } -ssize_t read(int fd, void* buf, size_t count) { +__declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) { if (count == 0) return 0; uint32_t pid = SysGetCurrentPid(); FD_ENTRY* e = FdGet(pid, fd); @@ -105,7 +98,7 @@ ssize_t read(int fd, void* buf, size_t count) { } } -ssize_t write(int fd, const void* buf, size_t count) { +__declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) { if (count == 0) return 0; uint32_t pid = SysGetCurrentPid(); FD_ENTRY* e = FdGet(pid, fd); @@ -130,7 +123,7 @@ ssize_t write(int fd, const void* buf, size_t count) { } } -int close(int fd) { +__declspec(dllexport) int close(int fd) { uint32_t pid = SysGetCurrentPid(); FD_ENTRY* e = FdGet(pid, fd); if (!e) return -1; @@ -140,7 +133,23 @@ int close(int fd) { return 0; } -off_t lseek(int fd, off_t offset, int whence) { +__declspec(dllexport) int unlink(const char* path) { + if (!path) { errno = EINVAL; return -1; } + + char native[512]; + const char* p = PosixTranslatePath(path, native, sizeof(native)); + + if (strcmp(p, SYNTH_DEVNULL) == 0 || strcmp(p, SYNTH_DEVZERO) == 0) { + errno = EINVAL; + return -1; + } + + uint32_t s = SysDeleteFile(p); + if (s != 0) { errno = KsToErrno(s); return -1; } + return 0; +} + +__declspec(dllexport) off_t lseek(int fd, off_t offset, int whence) { uint32_t pid = SysGetCurrentPid(); FD_ENTRY* e = FdGet(pid, fd); if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; } diff --git a/code/posix/posix_path.c b/code/posix/posix_path.c index 79db61a..974deb1 100644 --- a/code/posix/posix_path.c +++ b/code/posix/posix_path.c @@ -5,7 +5,7 @@ // The drive letter used as the POSIX filesystem root. // A future SYS_GET_BOOT_DRIVE could populate this; hardcoded for v1. -static const char gRootDrive = 'C'; +static const char gRootDrive = '0'; static void FlipSlashes(char* s) { for (; *s; s++) if (*s == '/') *s = '\\'; @@ -27,7 +27,7 @@ const char* PosixTranslatePath(const char* in, char* out, size_t outSize) { // outSize must fit "C:\" + rest out[0] = gRootDrive; out[1] = ':'; - out[2] = '\\'; + out[2] = '/'; size_t rest = strlen(in + 1); if (rest + 4 > outSize) rest = outSize - 4; memcpy(out + 3, in + 1, rest); diff --git a/code/posix/posix_proc.c b/code/posix/posix_proc.c index bb84c80..c3df33d 100644 --- a/code/posix/posix_proc.c +++ b/code/posix/posix_proc.c @@ -1,17 +1,10 @@ // posix_proc.c - posix_spawn, waitpid, getpid #include #include +#include #include "posix.h" #include "posix_path.h" -extern uint32_t SysGetCurrentPid(void); -extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut, - const char* cmdline); -extern uint32_t SysWaitForProcess(uint32_t handle); -extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut); -extern uint32_t SysGetProcessPid(uint32_t handle, uint32_t* pidOut); -extern void SysCloseHandle(uint32_t handle); - // pid -> proc handle table (user-mode side, for waitpid) #define PROC_TABLE_SIZE 32 typedef struct { @@ -69,11 +62,11 @@ static void BuildCmdline(char* const argv[], char* out, size_t outSize) { // POSIX API // --------------------------------------------------------------------------- -pid_t getpid(void) { +__declspec(dllexport) pid_t getpid(void) { return (pid_t)SysGetCurrentPid(); } -int posix_spawn(pid_t* pidOut, +__declspec(dllexport) int posix_spawn(pid_t* pidOut, const char* path, const posix_spawn_file_actions_t* file_actions, // ignored v1 const posix_spawnattr_t* attrp, // ignored v1 @@ -108,7 +101,7 @@ int posix_spawn(pid_t* pidOut, } // options: WNOHANG not supported in v1 (always blocks). -pid_t waitpid(pid_t pid, int* status, int options) { +__declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) { (void)options; uint32_t handle = ProcTableFind(pid); @@ -124,3 +117,7 @@ pid_t waitpid(pid_t pid, int* status, int options) { ProcTableRemove(pid); return pid; } + +__declspec(dllexport) void exit(int status) { + SysExitProcess(status); +} diff --git a/code/posix/posix_thread.c b/code/posix/posix_thread.c index ae578ec..61a4b1a 100644 --- a/code/posix/posix_thread.c +++ b/code/posix/posix_thread.c @@ -1,21 +1,8 @@ // posix_thread.c - pthread_create, pthread_join, pthread_exit, pthread_mutex_* #include +#include #include "posix.h" -extern uint32_t SysGetCurrentPid(void); -extern uint32_t SysGetCurrentTid(void); -extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg, - uint32_t priority, uint32_t* tidOut); -extern uint32_t SysWaitThread(uint32_t tid); -extern void SysExitThread(int code); -extern void* SysAlloc(size_t size); -extern void SysFree(void* ptr, size_t size); -extern uint32_t SysMutexCreate(uint32_t* handleOut); -extern uint32_t SysMutexAcquire(uint32_t handle); -extern uint32_t SysMutexTryAcquire(uint32_t handle); -extern uint32_t SysMutexRelease(uint32_t handle); -extern void SysCloseHandle(uint32_t handle); - // Thread return-value table (PID-keyed like the fd table) #define PTHREAD_TABLE_PIDS 32 #define PTHREAD_TABLE_SIZE 32 @@ -79,7 +66,7 @@ static void PthreadTrampoline(uintptr_t raw) { // pthread API // --------------------------------------------------------------------------- -int pthread_create(pthread_t* t, const pthread_attr_t* attr, +__declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* attr, void* (*fn)(void*), void* arg) { (void)attr; @@ -108,7 +95,7 @@ int pthread_create(pthread_t* t, const pthread_attr_t* attr, return 0; } -int pthread_join(pthread_t t, void** retval) { +__declspec(dllexport) int pthread_join(pthread_t t, void** retval) { uint32_t pid = SysGetCurrentPid(); uint32_t s = SysWaitThread(t.Tid); // STATUS_NOT_FOUND (0x22) means already dead - treat as success @@ -120,7 +107,7 @@ int pthread_join(pthread_t t, void** retval) { return 0; } -void pthread_exit(void* retval) { +__declspec(dllexport) void pthread_exit(void* retval) { uint32_t pid = SysGetCurrentPid(); uint32_t tid = SysGetCurrentTid(); PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid); @@ -129,7 +116,7 @@ void pthread_exit(void* retval) { while (1); // unreachable } -int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) { +__declspec(dllexport) int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) { (void)attr; uint32_t h; uint32_t s = SysMutexCreate(&h); @@ -138,23 +125,23 @@ int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) { return 0; } -int pthread_mutex_destroy(pthread_mutex_t* m) { +__declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) { SysCloseHandle(m->Handle); m->Handle = 0; return 0; } -int pthread_mutex_lock(pthread_mutex_t* m) { +__declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) { uint32_t s = SysMutexAcquire(m->Handle); return s == 0 ? 0 : EINVAL; } -int pthread_mutex_trylock(pthread_mutex_t* m) { +__declspec(dllexport) int pthread_mutex_trylock(pthread_mutex_t* m) { uint32_t s = SysMutexTryAcquire(m->Handle); return s == 0 ? 0 : EAGAIN; } -int pthread_mutex_unlock(pthread_mutex_t* m) { +__declspec(dllexport) int pthread_mutex_unlock(pthread_mutex_t* m) { uint32_t s = SysMutexRelease(m->Handle); return s == 0 ? 0 : EINVAL; } diff --git a/code/shell_native/shell_native.c b/code/shell_native/shell_native.c new file mode 100644 index 0000000..f244374 --- /dev/null +++ b/code/shell_native/shell_native.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Output helpers +// --------------------------------------------------------------------------- +static void print(const char* s) { + SysWriteConsole(s, strlen(s)); +} + +static void printUInt(uint32_t n) { + char buf[12]; + int i = 0; + if (n == 0) { print("0"); return; } + while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; } + char rev[12]; + int j = 0; + while (i > 0) rev[j++] = buf[--i]; + rev[j] = '\0'; + print(rev); +} + +static void printInt(int32_t n) { + if (n < 0) { + print("-"); + printUInt((uint32_t)-(n + 1) + 1); + } else { + printUInt((uint32_t)n); + } +} + +// --------------------------------------------------------------------------- +// Line editor +// --------------------------------------------------------------------------- +#define MAX_CMD 256 + +static void readLine(char* buf, int len) { + int pos = 0; + while (true) { + char c = SysKbReadChar(); + if (!c) { + SysYield(); + continue; + } + + if (c == '\r' || c == '\n') { + print("\n"); + break; + } + + if ((c == '\b' || c == 127) && pos > 0) { + pos--; + print("\b \b"); + continue; + } + + if (c < 0x20 || c == 127) + continue; + + if (pos < len - 1) { + buf[pos++] = c; + char echo[2] = { c, '\0' }; + print(echo); + } + } + buf[pos] = '\0'; +} + +static char* trim(char* s) { + while (*s == ' ') s++; + int len = (int)strlen(s); + while (len > 0 && s[len - 1] == ' ') len--; + s[len] = '\0'; + return s; +} + +// --------------------------------------------------------------------------- +// Built-in commands +// --------------------------------------------------------------------------- +static void cmd_help() { + print("Available commands:\n" + " help - Show this list\n" + " dir - List files\n" + " cd - Change directory\n" + " type - Display file contents\n" + " del - Delete a file\n" + " write - Create a file with text\n" + " exit - Close the shell\n"); +} + +static void cmd_write(const char* args) { + char filename[MAX_CMD]; + int i = 0; + while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) { + filename[i] = args[i]; + i++; + } + filename[i] = '\0'; + + if (args[i] == '\0' || args[i+1] == '\0') { + print("write: usage: write \n"); + return; + } + + uint32_t hFile; + if (SysCreateFile(filename, &hFile) != 0) { + print("write: could not create file\n"); + return; + } + + const char* text = args + i + 1; + size_t written; + SysWriteFile(hFile, text, strlen(text), &written); + SysCloseHandle(hFile); + print("File written successfully.\n"); +} + +static void cmd_dir(const char* path) { + uint32_t hDir; + + // Validate input + if (path == NULL || path[0] == '\0') { + path = "."; // Default to current directory if empty + } + + // SysOpenDir (Syscall 54) handles the const char* path directly + if (SysOpenDir(path, &hDir) != 0) { + SysDebugPrint("dir: failed to open directory\n"); + return; + } + + // Assuming KDIR_ENTRY is your structure for directory entries + KDIR_ENTRY ke; + + // SysReadDir (Syscall 55) populates the buffer with entry data + // It typically returns 0 on success and non-zero (or error) when done + while (SysReadDir(hDir, &ke) == 0) { + // We assume ke.Name is a null-terminated string within the struct + print(ke.Name); + print("\n"); + } + + // Always close the handle (Syscall 26) to prevent leaks + SysCloseHandle(hDir); +} + +static void cmd_type(const char* filename) { + uint32_t hFile; + if (SysOpenFile(filename, &hFile) != 0) { + print("type: error opening file\n"); + return; + } + + char buffer[512]; + size_t bytesRead; + while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) { + buffer[bytesRead] = '\0'; + print(buffer); + } + + SysCloseHandle(hFile); + print("\n"); +} + +// --------------------------------------------------------------------------- +// Run one command +// --------------------------------------------------------------------------- +static int runCommand(const char* cmd) { + if (!cmd || *cmd == '\0') return 0; + + if (strcmp(cmd, "exit") == 0) return 1; + if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } + if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } + if (strcmp(cmd, "dir") == 0) { + cmd_dir("."); + return 0; + } + if (strncmp(cmd, "dir ", 4) == 0) { + cmd_dir(cmd + 4); + return 0; + } + + if (strcmp(cmd, "clear") == 0) { + SysScreenClear(0x00000000); + return 0; + } + + if (strncmp(cmd, "cd ", 3) == 0) { + SysSetCwd(cmd + 3); + return 0; + } + + if (strncmp(cmd, "del ", 4) == 0) { + SysDeleteFile(cmd + 4); + return 0; + } + + if (strncmp(cmd, "type ", 5) == 0) { + cmd_type(cmd + 5); + return 0; + } + + if (strncmp(cmd, "write ", 6) == 0) { + cmd_write(cmd + 6); + return 0; + } + + // --- External Process Execution --- + uint32_t hProc; + if (SysCreateProcess(cmd, &hProc, cmd) == 0) { + SysWaitForProcess(hProc); + int exitCode = 0; + SysGetProcessExitCode(hProc, &exitCode); + SysCloseHandle(hProc); + while (SysKbPoll()) { + (void)SysKbReadChar(); + } + } else { + print("shell: launch failed\n"); + } + + return 0; +} + +// --------------------------------------------------------------------------- +// Entry point +// --------------------------------------------------------------------------- +int main(int argc, char** argv) { + char lineBuf[MAX_CMD]; + + print("shell: ready\n"); + + while (true) { + print("> "); + readLine(lineBuf, MAX_CMD); + if (runCommand(trim(lineBuf))) + break; + } + + print("shell: goodbye\n"); + SysExitProcess(0); + return 0; +} diff --git a/code/shell_posix/shell_posix.c b/code/shell_posix/shell_posix.c new file mode 100644 index 0000000..1b1f2e1 --- /dev/null +++ b/code/shell_posix/shell_posix.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include + +#define NAME_MAX 259 + +typedef struct dirent { + char d_name[NAME_MAX + 1]; + unsigned char d_type; // DT_REG or DT_DIR +} dirent; + +typedef struct DIR { + uint32_t KHandle; // kernel DIR_OBJECT handle + dirent Current; + bool Valid; +} DIR; + +extern int read(int fd, void *buf, size_t count); +extern int write(int fd, const void *buf, size_t count); +extern int open(const char *pathname, int flags, ...); +extern int close(int fd); +extern int chdir(const char *path); +extern DIR* opendir(const char* path); +extern dirent* readdir(DIR* d); +extern int closedir(DIR* d); +extern int unlink(const char *pathname); +extern int posix_spawn(int *pid, const char *path, const void *file_act, const void *attr, char *const argv[], char *const envp[]); +extern int waitpid(int pid, int *status, int options); +extern void exit(int status); + +extern void PosixFdInit(void); +extern int ClearConsoleScreenBuffer(void* hConsole); + +// --------------------------------------------------------------------------- +// Global handles +// --------------------------------------------------------------------------- +static int hIn = 0; // stdin +static int hOut = 1; // stdout + +// --------------------------------------------------------------------------- +// Output helpers +// --------------------------------------------------------------------------- +static void print(const char* s) { + write(hOut, s, strlen(s)); +} + +static void printUInt(uint32_t n) { + char buf[12]; + int i = 0; + if (n == 0) { print("0"); return; } + while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; } + char rev[12]; + int j = 0; + while (i > 0) rev[j++] = buf[--i]; + rev[j] = '\0'; + print(rev); +} + +static void printInt(int32_t n) { + if (n < 0) { print("-"); n = -n; } + printUInt((uint32_t)n); +} + +// --------------------------------------------------------------------------- +// Line editor +// --------------------------------------------------------------------------- +#define MAX_CMD 256 + +static void readLine(char* buf, int len) { + int pos = 0; + while (true) { + char c; + if (read(hIn, &c, 1) <= 0) break; + if (c == '\r' || c == '\n') { print("\n"); break; } + if ((c == '\b' || c == 127) && pos > 0) { + pos--; + print("\b \b"); + continue; + } + if (c < 0x20 || c == 127) continue; + if (pos < len - 1) { + buf[pos++] = c; + char echo[2] = { c, '\0' }; + print(echo); + } + } + buf[pos] = '\0'; +} + +static char* trim(char* s) { + while (*s == ' ') s++; + int len = (int)strlen(s); + while (len > 0 && s[len - 1] == ' ') len--; + s[len] = '\0'; + return s; +} + +// --------------------------------------------------------------------------- +// Built-in commands +// --------------------------------------------------------------------------- +static void cmd_help() { + print("Available commands:\n" + " help - Show this list\n" + " dir [path] - List directory contents\n" + " cd - Change directory\n" + " type - Display file contents\n" + " del - Delete a file\n" + " write - Create a file with text\n" + " exit - Close the shell\n"); +} + +static void cmd_write(const char* args) { + char filename[MAX_CMD]; + int i = 0; + while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) { + filename[i] = args[i]; + i++; + } + filename[i] = '\0'; + if (args[i] == '\0' || args[i+1] == '\0') return; + + // Open with O_WRONLY | O_CREAT | O_TRUNC (typical flags: 0x0001 | 0x0040 | 0x0200) + int fd = open(filename, 0x0241, 0644); + if (fd < 0) return; + write(fd, args + i + 1, strlen(args + i + 1)); + close(fd); +} + +static void cmd_dir(const char* path) { + // If no path is provided, default to current directory + const char* target = (path && *path != '\0') ? path : "."; + + DIR* d = opendir(target); + if (!d) { + print("dir: cannot access '"); + print(target); + print("'\n"); + return; + } + + dirent* entry; + while ((entry = readdir(d)) != NULL) { + // Filter out the current and parent directory markers + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + // Display type (Assuming 1=DIR, 2=REG based on common custom POSIX layouts) + if (entry->d_type == 1) { + print(" "); + } else { + print(" "); + } + + print(entry->d_name); + print("\n"); + } + + closedir(d); +} + +static void cmd_type(const char* filename) { + int fd = open(filename, 0); // O_RDONLY + if (fd < 0) return; + char buffer[512]; + int n; + while ((n = read(fd, buffer, 511)) > 0) { + buffer[n] = '\0'; + print(buffer); + } + close(fd); + print("\n"); +} + +// --------------------------------------------------------------------------- +// Run one command. +// --------------------------------------------------------------------------- +static int runCommand(const char* cmd) { + if (!cmd || *cmd == '\0') return 0; + + if (strcmp(cmd, "exit") == 0) return 1; + if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } + if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } + + if (strcmp(cmd, "dir") == 0) { + cmd_dir("."); + return 0; + } + if (strncmp(cmd, "dir ", 4) == 0) { + cmd_dir(cmd + 4); + return 0; + } + + if (strcmp(cmd, "clear") == 0) { + ClearConsoleScreenBuffer(0); + return 0; + } + + if (strncmp(cmd, "cd ", 3) == 0) { chdir(cmd + 3); return 0; } + if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return 0; } + if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; } + if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; } + + // --- External Process Execution (No Fork) --- + int pid; + char *argv[] = {(char*)cmd, NULL}; + if (posix_spawn(&pid, cmd, NULL, NULL, argv, NULL) == 0) { + int status; + waitpid(pid, &status, 0); + } else { + print("shell: error launching\n"); + } + return 0; +} + +int main(const char* args) { + PosixFdInit(); + char lineBuf[MAX_CMD]; + print("shell: ready\n"); + + while (true) { + print("> "); + readLine(lineBuf, MAX_CMD); + if (runCommand(trim(lineBuf))) break; + } + + print("shell: goodbye\n"); + + exit(0); +} diff --git a/code/shell/shell.c b/code/shell_win32/shell_win32.c similarity index 82% rename from code/shell/shell.c rename to code/shell_win32/shell_win32.c index de958c2..d084613 100644 --- a/code/shell/shell.c +++ b/code/shell_win32/shell_win32.c @@ -148,20 +148,51 @@ static void cmd_write(const char* args) { CloseHandle(hFile); } -static void cmd_dir() { +static void cmd_dir(const char* path) { WIN32_FIND_DATAA fd; - HANDLE hFind = FindFirstFileA("*", &fd); + char search_path[MAX_PATH]; + + size_t path_len = 0; + while (path[path_len] != '\0' && path_len < MAX_PATH) { + path_len++; + } + + // Ensure we have room for the path + "\*" + null terminator + if (path_len + 3 > MAX_PATH) { + print("dir: path too long\n"); + return; + } + + // Manual concatenation + memcpy(search_path, path, path_len); + + // Append the wildcard pattern + search_path[path_len] = '\\'; + search_path[path_len + 1] = '*'; + search_path[path_len + 2] = '\0'; + + HANDLE hFind = FindFirstFileA(search_path, &fd); if (hFind == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + return; + } print("dir: failed to read directory\n"); return; } do { + // Skip the "." and ".." entries to keep output clean + if (path_len > 0 && fd.cFileName[0] == '.') { + if (fd.cFileName[1] == '\0' || (fd.cFileName[1] == '.' && fd.cFileName[2] == '\0')) { + continue; + } + } + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - print(" "); + print(" "); } else { - print(" "); + print(" "); } print(fd.cFileName); print("\n"); @@ -207,13 +238,22 @@ static int runCommand(const char* cmd) { return 0; } + if (strcmp(cmd, "crash") == 0) { + *(int*)0 = 42; + return 0; + } + if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(hOut); return 0; } - if (strcmp(cmd, "dir") == 0) { - cmd_dir(); + if (strcmp(cmd, "dir") == 0) { + cmd_dir("."); + return 0; + } + if (strncmp(cmd, "dir ", 4) == 0) { + cmd_dir(cmd + 4); return 0; } diff --git a/code/syscall/syscall.h b/code/syscall/syscall.h new file mode 100644 index 0000000..d964017 --- /dev/null +++ b/code/syscall/syscall.h @@ -0,0 +1,110 @@ +#pragma once +// Syscall.h + +#include +#include +#include + +// --------------------------------------------------------------------------- +// Thread & Process Management (0-7) +// --------------------------------------------------------------------------- +void SysExitProcess(int exitCode); // 00 +void SysExitThread(int exitCode); // 01 +uint32_t SysCreateThread(uintptr_t entry, uintptr_t arg, uint32_t priority, uint32_t* tidOut); // 02 +uint32_t SysGetCurrentTid(void); // 03 +uint32_t SysGetCurrentPid(void); // 04 +uint32_t SysYield(void); // 05 +uint32_t SysSleep(uint64_t ms); // 06 +uint32_t SysSetThreadPriority(uint32_t priority); // 07 + +// --------------------------------------------------------------------------- +// Memory Management (8-10) +// --------------------------------------------------------------------------- +void* SysAlloc(size_t size); // 08 +uint32_t SysFree(void* ptr, size_t size); // 09 +void* SysRealloc(void* ptr, size_t newSize); // 10 + +// --------------------------------------------------------------------------- +// Synchronization Primitives (11-22) +// --------------------------------------------------------------------------- +uint32_t SysMutexCreate(uint32_t* handleOut); // 11 +uint32_t SysMutexAcquire(uint32_t handle); // 12 +uint32_t SysMutexTryAcquire(uint32_t handle); // 13 +uint32_t SysMutexRelease(uint32_t handle); // 14 + +uint32_t SysSemCreate(int32_t initial, int32_t max, uint32_t* handleOut); // 15 +uint32_t SysSemWait(uint32_t handle); // 16 +uint32_t SysSemTryWait(uint32_t handle); // 17 +uint32_t SysSemSignal(uint32_t handle); // 18 + +uint32_t SysEventCreate(uint32_t type, uint32_t initialState, uint32_t* handleOut); // 19 +uint32_t SysEventSignal(uint32_t handle); // 20 +uint32_t SysEventClear(uint32_t handle); // 21 +uint32_t SysEventWait(uint32_t handle); // 22 + +// --------------------------------------------------------------------------- +// Anonymous Queues & Unified Handle Path (23-26) +// --------------------------------------------------------------------------- +uint32_t SysQueueCreate(uint32_t* handleOut); // 23 +uint32_t SysQueueSend(uint32_t handle, const void* msg, bool blocking); // 24 +uint32_t SysQueueRecv(uint32_t handle, void* msg, bool blocking); // 25 +uint32_t SysCloseHandle(uint32_t handle); // 26 + +// --------------------------------------------------------------------------- +// File System (27-35) +// --------------------------------------------------------------------------- +uint32_t SysOpenFile(const char* path, uint32_t* handleOut); // 27 +uint32_t SysReadFile(uint32_t handle, void* buf, size_t size, size_t* bytesRead); // 28 +uint32_t SysWriteFile(uint32_t handle, const void* buf, size_t size, size_t* written); // 29 +uint32_t SysCreateFile(const char* path, uint32_t* handleOut); // 30 +uint32_t SysDeleteFile(const char* path); // 31 +uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize); // 32 +uint64_t SysGetFilePos(uint32_t handle); // 33 +uint32_t SysSeekFile(uint32_t handle, uint64_t offset); // 34 +uint64_t SysGetFileSize(uint32_t handle); // 35 + +// --------------------------------------------------------------------------- +// Graphics & Input (36-47) +// --------------------------------------------------------------------------- +uint32_t SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels); // 36 +uint32_t SysScreenClear(uint32_t color); // 37 +uint32_t SysScreenGetSize(int* widthOut, int* heightOut); // 38 +uint32_t SysScreenPutChar(char c, int x, int y, uint32_t fg, uint32_t bg); // 39 + +uint64_t SysKbPoll(void); // 40 +uint64_t SysKbIsKeyDown(uint8_t scancode, bool extended); // 41 +char SysKbReadChar(void); // 42 +uint8_t SysKbReadScancode(void); // 43 +uint32_t SysKbGetModifiers(void); // 44 + +uint64_t SysMousePoll(void); // 45 +uint64_t SysMouseRead(int* dx, int* dy, uint8_t* buttons); // 46 +void SysMouseGetPos(int* x, int* y); // 47 + +// --------------------------------------------------------------------------- +// System Info & Process Management (48-53) +// --------------------------------------------------------------------------- +uint64_t SysGetTick(void); // 48 +uint32_t SysDebugPrint(const char* str); // 49 +uint32_t SysCreateProcess(const char* path, uint32_t* handleOut, const char* cmdline); // 50 +uint32_t SysWaitForProcess(uint32_t handle); // 51 +uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut); // 52 +uint32_t SysWriteConsole(const char* buf, size_t len); // 53 + +// --------------------------------------------------------------------------- +// Directories & Environment (54-57) +// --------------------------------------------------------------------------- +uint32_t SysOpenDir(const char* path, uint32_t* handleOut); // 54 +uint32_t SysReadDir(uint32_t handle, void* dirEntryBuffer); // 55 +uint32_t SysSetCwd(const char* path); // 56 +uint32_t SysGetCwd(char* buf, size_t size); // 57 + +// --------------------------------------------------------------------------- +// Process/Thread Helpers & Named Queues (58-63) +// --------------------------------------------------------------------------- +uint32_t SysWaitThread(uint32_t tid); // 58 +uint32_t SysGetProcessPid(uint32_t handle, uint32_t* pidOut); // 59 +uint32_t SysPublishQueue(const char* name, uint32_t handle); // 60 +uint32_t SysOpenNamedQueue(const char* name, uint32_t* handleOut); // 61 +uint32_t SysQueueDuplicate(uint32_t handle, uint32_t targetPid, uint32_t* handleOut); // 62 +uint32_t SysNamedQueueOwnerPid(const char* name, uint32_t* pidOut); // 63 diff --git a/code/video/video.c b/code/video/video.c index 365feee..d436d94 100644 --- a/code/video/video.c +++ b/code/video/video.c @@ -13,12 +13,16 @@ int main(const char* cmdline) { WriteConsoleA(h, "\n", 1, 0, 0); void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0); - if (!f) { + if (!f || f == (void*)(unsigned long long)0xFFFFFFFF) { // fuckass WriteConsoleA(h, "video: failed to open video.bk2\n", 32, 0, 0); ExitProcess(1); } unsigned long s = GetFileSize(f, 0); + if (!s || s == 0xFFFFFFFF) { + WriteConsoleA(h, "video: GetFileSize failed!\n", 27, 0, 0); + ExitProcess(1); + } void* buf = HeapAlloc(0, 0, s); if (!buf) { WriteConsoleA(h, "video: HeapAlloc failed\n", 24, 0, 0); diff --git a/code/win32/gdi32.c b/code/win32/gdi32.c index 5c26415..2e0d17c 100644 --- a/code/win32/gdi32.c +++ b/code/win32/gdi32.c @@ -1,6 +1,7 @@ // gdi32.c #include #include +#include #if defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86) #include @@ -8,14 +9,6 @@ #include #endif -// --------------------------------------------------------------------------- -// Syscall stubs (from syscall.dll) -// --------------------------------------------------------------------------- -extern void SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels); -extern void* SysAlloc(size_t size); -extern void SysFree(void* ptr, size_t size); - - // --------------------------------------------------------------------------- // Win32 Base Types // --------------------------------------------------------------------------- diff --git a/code/win32/kernel32.c b/code/win32/kernel32.c index 825223e..7d6ccb8 100644 --- a/code/win32/kernel32.c +++ b/code/win32/kernel32.c @@ -24,63 +24,7 @@ #include #include #include - -// --------------------------------------------------------------------------- -// Syscall stubs (from syscall.dll, assembled from syscall_stubs.asm) -// --------------------------------------------------------------------------- - -// Memory -extern void* SysAlloc(size_t size); -extern void SysFree(void* ptr, size_t size); -extern void* SysRealloc(void* ptr, size_t newSize); - -// Process & Thread -extern void SysExitProcess(int exitCode); -extern void SysExitThread(int exitCode); -extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut, const char* cmdline); -extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg, uint32_t priority, uint32_t* tidOut); -extern uint32_t SysGetCurrentPid(void); -extern uint32_t SysGetCurrentTid(void); -extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut); -extern uint32_t SysWaitForProcess(uint32_t handle); - -// File I/O -extern void SysCloseHandle(uint32_t handle); -extern uint32_t SysCreateFile(const char* path, uint32_t* handleOut); -extern uint32_t SysDeleteFile(const char* path); -extern uint64_t SysGetFilePos(uint32_t handle); -extern uint64_t SysGetFileSize(uint32_t handle); -extern uint32_t SysOpenFile(const char* path, uint32_t* handleOut); -extern uint32_t SysReadFile(uint32_t handle, void* buf, size_t size, size_t* bytesReadOut); -extern uint32_t SysSeekFile(uint32_t handle, uint64_t offset); -extern uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize); -extern uint32_t SysWriteFile(uint32_t handle, const void* buf, size_t size, size_t* bytesWrittenOut); - -// Console & directory & CWD stubs -extern uint32_t SysGetCwd(char* buf, size_t size); -extern uint32_t SysOpenDir(const char* dirPath, uint32_t* handleOut); -extern uint32_t SysReadDir(uint32_t handle, void* entryOut); -extern void SysScreenClear(uint32_t color); -extern uint32_t SysSetCwd(const char* path); -extern void SysWriteConsole(const char* buf, size_t len); - -// Keyboard syscalls -extern uint32_t SysKbGetModifiers(void); -extern uint64_t SysKbIsKeyDown(uint8_t scancode, bool extended); -extern uint32_t SysKbPoll(void); -extern char SysKbReadChar(void); -extern unsigned char SysKbReadScancode(void); - -// Mouse syscalls -extern void SysMouseGetPos(int* x, int* y); -extern uint32_t SysMousePoll(void); -extern uint32_t SysMouseRead(int* dx, int* dy, unsigned char* buttons); - -// Misc -extern void SysDebugPrint(const char* str); -extern void SysYield(void); -extern void SysSleep(uint32_t ms); -extern uint64_t SysGetTick(void); +#include // --------------------------------------------------------------------------- // Types @@ -143,6 +87,36 @@ typedef struct _WIN32_FIND_DATAA { static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; } static inline uint32_t HANDLE_TO_KH(HANDLE h) { return (uint32_t)(uintptr_t)h; } +// Path translator +static void TranslatePath(const char* input, char* output) { + if (input && input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) { + // Translate "C:\" -> "0:/" (assuming 'C' maps to '0', etc.) + // Map drive letter to index: 'C' or 'c' -> '0' + char drive = input[0]; + if (drive >= 'a') drive -= ('a' - 'A'); // To uppercase + + output[0] = (char)('0' + (drive - 'C')); + output[1] = ':'; + output[2] = '/'; + + // Copy the rest, swapping backslashes to forward slashes + const char* src = input + 3; + char* dst = output + 3; + while (*src) { + *dst = (*src == '\\') ? '/' : *src; + src++; dst++; + } + *dst = '\0'; + } else { + // Fallback for relative paths: just swap slashes + while (*input) { + *output = (*input == '\\') ? '/' : *input; + input++; output++; + } + *output = '\0'; + } +} + // --------------------------------------------------------------------------- // GetLastError / SetLastError (single-threaded stub) // --------------------------------------------------------------------------- @@ -342,24 +316,28 @@ __declspec(dllexport) DWORD GetOEMCP(void) { return 437; } // DOS Latin US // CreateFileA - opens or creates a file. // Supported modes: -// OPEN_EXISTING + GENERIC_READ -> open existing, read-only -// OPEN_EXISTING + GENERIC_WRITE -> open existing, read-write +// OPEN_EXISTING + GENERIC_READ -> open existing, read-only +// OPEN_EXISTING + GENERIC_WRITE -> open existing, read-write // OPEN_EXISTING + GENERIC_READ|GENERIC_WRITE -> open existing, read-write // CREATE_ALWAYS + GENERIC_WRITE -> create/truncate, write // OPEN_ALWAYS + GENERIC_WRITE -> create if absent, write // TRUNCATE_EXISTING + GENERIC_WRITE -> open & truncate to zero __declspec(dllexport) HANDLE CreateFileA(const char* name, - DWORD access, DWORD share, - void* sa, DWORD disposition, - DWORD flags, void* templ) { + DWORD access, DWORD share, + void* sa, DWORD disposition, + DWORD flags, void* templ) { (void)share; (void)sa; (void)flags; (void)templ; + // Translate the path from Windows format to custom 0:/ format + char translatedName[260]; + TranslatePath(name, translatedName); + int wantWrite = (access & GENERIC_WRITE) != 0; if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) { // Open the file via the read path (it must already exist). uint32_t kh = 0; - uint32_t s = SysOpenFile(name, &kh); + uint32_t s = SysOpenFile(translatedName, &kh); if (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; } if (disposition == TRUNCATE_EXISTING && wantWrite) { @@ -378,10 +356,10 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name, // Create-or-replace: try to create; if the file already exists, // open it and truncate it to zero instead. uint32_t kh = 0; - uint32_t s = SysCreateFile(name, &kh); + uint32_t s = SysCreateFile(translatedName, &kh); if (s != 0) { // File may already exist - open and truncate. - s = SysOpenFile(name, &kh); + s = SysOpenFile(translatedName, &kh); if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; } SysTruncateFile(kh, 0); } @@ -391,8 +369,8 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name, if (disposition == OPEN_ALWAYS && wantWrite) { // Open if exists, create if not. uint32_t kh = 0; - if (SysOpenFile(name, &kh) != 0) { - if (SysCreateFile(name, &kh) != 0) { + if (SysOpenFile(translatedName, &kh) != 0) { + if (SysCreateFile(translatedName, &kh) != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; } @@ -404,7 +382,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name, // Fail if the file already exists (we can't check atomically, // but SysCreateFile returns NAME_COLLISION for existing files). uint32_t kh = 0; - uint32_t s = SysCreateFile(name, &kh); + uint32_t s = SysCreateFile(translatedName, &kh); if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS return KH_TO_HANDLE(kh); } @@ -417,7 +395,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name, // GetFileSize - fills optional high-DWORD pointer, returns low DWORD. // Returns 0xFFFFFFFF on error, matching Win32 behaviour. __declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) { - if (!hFile) { gLastError = 6; return 0xFFFFFFFFUL; } + if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; } uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile)); if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32); return (DWORD)(sz & 0xFFFFFFFFULL); @@ -427,7 +405,7 @@ __declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) { __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead, DWORD* bytesRead, void* overlapped) { (void)overlapped; - if (!hFile || !buf) { gLastError = 6; return FALSE; } + if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) { gLastError = 6; return FALSE; } size_t got = 0; uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got); if (bytesRead) *bytesRead = (DWORD)got; @@ -452,7 +430,7 @@ __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrit // Returns new position low DWORD, or 0xFFFFFFFF on error. __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist, long* distHigh, DWORD method) { - if (!hFile) { gLastError = 6; return 0xFFFFFFFFUL; } + if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; } uint32_t kh = HANDLE_TO_KH(hFile); uint64_t offset = 0; @@ -481,7 +459,7 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist, // SetEndOfFile - truncate or extend the file to the current seek position. __declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) { - if (!hFile) { gLastError = 6; return FALSE; } + if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; } uint32_t kh = HANDLE_TO_KH(hFile); uint64_t pos = SysGetFilePos(kh); if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; } @@ -493,8 +471,12 @@ __declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) { // DeleteFileA - delete a file by name; no open handle required. __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) { if (!lpFileName) { gLastError = 87; return FALSE; } - uint32_t s = SysDeleteFile(lpFileName); - if (s != 0) { gLastError = 2; return FALSE; } // ERROR_FILE_NOT_FOUND + + char translatedPath[MAX_PATH]; + TranslatePath(lpFileName, translatedPath); + + uint32_t s = SysDeleteFile(translatedPath); + if (s != 0) { gLastError = 2; return FALSE; } return TRUE; } @@ -712,9 +694,12 @@ __declspec(dllexport) BOOL CreateProcessA( // Build the cmdline: prefer lpCommandLine if given, otherwise fall back to path const char* cmdline = lpCommandLine ? lpCommandLine : targetPath; + char translatedPath[MAX_PATH]; + TranslatePath(targetPath, translatedPath); + uint32_t kh = 0; // Call the syscall.dll stub - uint32_t s = SysCreateProcess(targetPath, &kh, cmdline); + uint32_t s = SysCreateProcess(translatedPath, &kh, cmdline); if (s != 0 || kh == 0) { gLastError = 2; // ERROR_FILE_NOT_FOUND return FALSE; @@ -956,10 +941,14 @@ __declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName, return INVALID_HANDLE_VALUE; } + // Translate the path + char translatedInput[MAX_PATH]; + TranslatePath(lpFileName, translatedInput); + // Split path into directory and wildcard pattern char dir[MAX_PATH]; char pat[MAX_PATH]; - SplitFindPath(lpFileName, dir, sizeof(dir), pat, sizeof(pat)); + SplitFindPath(translatedInput, dir, sizeof(dir), pat, sizeof(pat)); // Open the kernel directory iterator uint32_t kh = 0; @@ -1040,7 +1029,11 @@ __declspec(dllexport) BOOL FindClose(HANDLE hFindFile) { __declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) { if (!lpPathName) { gLastError = 87; return FALSE; } - uint32_t s = SysSetCwd(lpPathName); + + char translatedPath[MAX_PATH]; + TranslatePath(lpPathName, translatedPath); + + uint32_t s = SysSetCwd(translatedPath); if (s != 0) { gLastError = 2; return FALSE; } return TRUE; } diff --git a/code/win32/ole32.c b/code/win32/ole32.c index c86d62a..c1be4ef 100644 --- a/code/win32/ole32.c +++ b/code/win32/ole32.c @@ -2,6 +2,7 @@ // No COM infrastructure at this stage; all functions fail gracefully. #include #include +#include // --------------------------------------------------------------------------- // Types @@ -37,8 +38,6 @@ typedef struct { }; } PROPVARIANT; -extern void SysFree(void* ptr, size_t size); - // --------------------------------------------------------------------------- // COM initialisation // --------------------------------------------------------------------------- diff --git a/code/win32/user32.c b/code/win32/user32.c index 8bf0c4b..af2d4fd 100644 --- a/code/win32/user32.c +++ b/code/win32/user32.c @@ -1,9 +1,7 @@ // user32.c - Win32 User Interface #include #include - -extern void SysYield(void); -extern void SysDebugPrint(const char* str); +#include typedef void* HWND; typedef void* HDC; diff --git a/code/win32/winmm.c b/code/win32/winmm.c index e21fc4e..0c9ae4b 100644 --- a/code/win32/winmm.c +++ b/code/win32/winmm.c @@ -2,6 +2,7 @@ // Stubs only; no audio hardware access at this stage. #include #include +#include // --------------------------------------------------------------------------- // Types @@ -51,10 +52,6 @@ typedef struct { // Timer // --------------------------------------------------------------------------- -// timeGetTime is the lightweight alternative to GetTickCount; both return -// milliseconds since system start. We forward to the kernel tick counter. -extern uint64_t SysGetTick(void); - __declspec(dllexport) unsigned long timeGetTime(void) { return (unsigned long)SysGetTick(); } diff --git a/readme.md b/readme.md index 34474c4..bc8ae69 100644 --- a/readme.md +++ b/readme.md @@ -114,7 +114,7 @@ HAL: _S5_ SLP_TYP_A=0x00 SLP_TYP_B=0x00 HAL: LAPIC=00000000fee00000 IOAPIC=00000000fec00000 HPET=00000000fed00000 HAL: lapicPhys=0xFEE00000, lapicVirt=0xFFFF8800FEE00000, hpetPhys=0xFED00000 HAL: Attempting to calibrate APIC timer via HPET... -HAL: APIC timer: 62990 ticks/ms +HAL: APIC timer: 62981 ticks/ms HAL: initialisation complete. KernelMain: Handing over to scheduler... SystemInitThread: Thread started. Igniting the system... @@ -146,25 +146,51 @@ Found runtime driver: usb.sys Test runtime driver SystemInitThread: Loading system DLLs... Memory Status: - -> Free (PMM): 151.80 MB + -> Free (PMM): 151.71 MB -> Kernel Pool: 4.00 MB - -> User Heap: 0.47 MB + -> User Heap: 0.55 MB LaunchUserProcess: IoOpenFile: 0 LaunchUserProcess: IoGetFileSize: 5120 -LaunchUserProcess: MmAllocateUserMemory: 0x7A020 +LaunchUserProcess: MmAllocateUserMemory: 0x8E020 LaunchUserProcess: IoReadFile: 0 LaunchUserProcess: IoCloseFile Created process (PID 2) with command line: init.exe -LaunchUserProcess: PE Image mapped! Base 0x7c020, Entry 0x7D600 +LaunchUserProcess: PE Image mapped! Base 0x90020, Entry 0x91600 LaunchUserProcess: Thread created and queued! [init] starting [init] waiting shell: ready -shell.exe -> clear -> -> -> +> help +Available commands: + help - Show this list + dir - List files + cd - Change directory + type - Display file contents + del - Delete a file + write - Create a file with text + exit - Close the shell +> dir +System Volume Information +drivers +efi +colonel.exe +hal.dll +syscall.dll +win32.dll +posix.dll +init.exe +shell_win32.exe +shell_posix.exe +shell_native.exe +doom.exe +video.exe +boot.ini +bink2w64.dll +video.bk2 +doom1.wad +> shell_win32.exe +shell: ready +shell_win32.exe > doom.exe doom: starting getenv: Unknown variable DOOMWADDIR @@ -203,13 +229,51 @@ HU_Init: Setting up heads up display. ST_Init: Init status bar. doom: entering game loop I_ShutdownSound: NOT finishing pending sounds -> clear +> shell_posix.exe +shell: ready +> help +Available commands: + help - Show this list + dir [path] - List directory contents + cd - Change directory + type - Display file contents + del - Delete a file + write - Create a file with text + exit - Close the shell +> dir + System Volume Information + drivers + efi + colonel.exe + hal.dll + syscall.dll + win32.dll + posix.dll + init.exe + shell_win32.exe + shell_posix.exe + shell_native.exe + doom.exe + video.exe + boot.ini + bink2w64.dll + video.bk2 + doom1.wad + .doo > video.exe video: starting video.exe video: video.bk2 loaded shell: goodbye -> +> exit +shell: goodbye +> exit +shell: goodbye +> exit +shell: goodbye +[init] done +ProcessWatcherThread: Process "init.exe" (PID 2) exited with code 0. +ProcessWatcherThread: Shutting down. ```