// ke_syscall.c - system call dispatcher // // KeSyscallEntry (ke_syscall.asm) handles the INT 0x40 ring-3 -> ring-0 // transition, re-orders the arguments to match MS x64, and calls // KeSyscallDispatch with: // // nr = original RAX (syscall number) // a0 .. a5 = RCX, RDX, R8, R9, R10, R11 (user arguments) // // Every handler receives the same (a0..a5) signature even if it uses fewer // arguments; unused parameters are silently ignored. // // Return value (RAX) is a KSTATUS code *or* an arbitrary 64-bit datum // (pointer, size, tick count, etc.) depending on the call; conventions are // documented in syscall_nr.h. // // --------------------------------------------------------------------------- // Unified handle design // --------------------------------------------------------------------------- // All kernel objects (FILE_OBJECT, MUTEX, SEMAPHORE, EVENT, QUEUE) are // reference-counted OBJECTs stored in the per-process handle table. // SYS_CLOSE_HANDLE is the single close path for every type: // // KeCloseHandle(proc, handle) // -> ObDereferenceObject(obj) // -> obj->Destroy(obj) [type-specific cleanup, e.g. FileDestroy] // -> MmFreePool(obj) // // There is no separate SyscallCloseFile. Removing that stub was the right // call: the object model already handled it; the dedicated syscall was only // adding a footgun where callers could bypass the unified refcount path. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // HAL pointer - set once at boot by KeSyscallSetHal() // --------------------------------------------------------------------------- static const HAL_API* gHal; void KeSyscallSetHal(const HAL_API* hal) { gHal = hal; } // --------------------------------------------------------------------------- // Helper: look up a handle and type-check it in one call. // Bumps the refcount on success; caller must ObDereferenceObject when done. // Returns NULL if the handle is invalid or the type does not match. // --------------------------------------------------------------------------- static OBJECT* HandleToObject(uint32_t handle, OBJECT_TYPE expected) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NULL; OBJECT* obj = KeLookupHandle(proc, handle); // bumps refcount if (!obj) return NULL; if (obj->Type != expected) { ObDereferenceObject(obj); // undo the bump return NULL; } return obj; } // --------------------------------------------------------------------------- // Helper: allocate a handle in the current process's table for 'obj'. // Takes one reference on behalf of the handle slot. // On failure, drops the reference it would have stored. // --------------------------------------------------------------------------- static KSTATUS AllocHandleForCurrent(OBJECT* obj, uint32_t* handleOut) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; KSTATUS s = KeAllocateHandle(proc, obj, handleOut); if (!KSUCCESS(s)) ObDereferenceObject(obj); return s; } // =========================================================================== // Individual syscall handlers // =========================================================================== // --------------------------------------------------------------------------- // SYS_EXIT_PROCESS (0) a0 = exitCode // --------------------------------------------------------------------------- static uint64_t SyscallExitProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; THREAD* t = KeGetCurrentThread(); PROCESS* proc = t ? t->Owner : NULL; if (proc) KeExitProcess(proc, (int)a0); if (t) KeExitThread(t, (int)a0); while (true) ; // unreachable - KeExitThread does not return } // --------------------------------------------------------------------------- // SYS_EXIT_THREAD (1) a0 = exitCode // --------------------------------------------------------------------------- static uint64_t SyscallExitThread(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; THREAD* t = KeGetCurrentThread(); if (t) KeExitThread(t, (int)a0); while (true); } // --------------------------------------------------------------------------- // SYS_CREATE_THREAD (2) a0=entry a1=arg a2=priority a3=*tidOut // --------------------------------------------------------------------------- static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; if (a2 >= PriorityCount) return STATUS_INVALID_PARAMETER; THREAD* newt = NULL; KSTATUS s = KeCreateThread(proc, (uintptr_t)a0, (uintptr_t)a1, (THREAD_PRIORITY)a2, &newt); if (!KSUCCESS(s)) return s; if (a3) *(uint32_t*)(uintptr_t)a3 = newt->ThreadId; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_GET_CURRENT_TID (3) // --------------------------------------------------------------------------- static uint64_t SyscallGetCurrentTid(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; THREAD* t = KeGetCurrentThread(); return t ? t->ThreadId : 0; } // --------------------------------------------------------------------------- // SYS_GET_CURRENT_PID (4) // --------------------------------------------------------------------------- static uint64_t SyscallGetCurrentPid(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* p = KeGetCurrentProcess(); return p ? p->ProcessId : 0; } // --------------------------------------------------------------------------- // SYS_YIELD (5) // --------------------------------------------------------------------------- static uint64_t SyscallYield(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; KeYield(); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_SLEEP (6) a0 = milliseconds // --------------------------------------------------------------------------- static uint64_t SyscallSleep(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return STATUS_FAILURE; uint64_t deadline = gHal->Timer.QueryTicks() + (uint64_t)a0; while (gHal->Timer.QueryTicks() < deadline) KeYield(); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_SET_THREAD_PRIORITY (7) a0 = priority // --------------------------------------------------------------------------- static uint64_t SyscallSetThreadPriority(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (a0 >= PriorityCount) return STATUS_INVALID_PARAMETER; THREAD* t = KeGetCurrentThread(); if (!t) return STATUS_FAILURE; t->Priority = (THREAD_PRIORITY)a0; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_ALLOC (8) a0 = size -> void* // --------------------------------------------------------------------------- static uint64_t SyscallAlloc(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return 0; return (uint64_t)(uintptr_t)MmAllocateUserMemory((size_t)a0); } // --------------------------------------------------------------------------- // SYS_FREE (9) a0 = ptr, a1 = size // --------------------------------------------------------------------------- static uint64_t SyscallFree(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (a0) MmFreeUserMemory((void*)(uintptr_t)a0, (size_t)a1); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_REALLOC (10) a0 = ptr, a1 = newSize -> void* // --------------------------------------------------------------------------- static uint64_t SyscallRealloc(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; return (uint64_t)(uintptr_t)MmReallocateUserMemory( (void*)(uintptr_t)a0, (size_t)a1); } // --------------------------------------------------------------------------- // SYS_MUTEX_CREATE (11) a0 = *handleOut // --------------------------------------------------------------------------- static uint64_t SyscallMutexCreate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; MUTEX* mx = NULL; KSTATUS s = MutexCreate(&mx); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = AllocHandleForCurrent(&mx->Header, &h); // takes ownership of the ref if (!KSUCCESS(s)) return s; *(uint32_t*)(uintptr_t)a0 = h; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_MUTEX_ACQUIRE (12) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallMutexAcquire(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex); if (!mx) return STATUS_NOT_FOUND; KSTATUS s = KeAcquireMutex(mx); ObDereferenceObject(&mx->Header); return s; } // --------------------------------------------------------------------------- // SYS_MUTEX_TRY_ACQUIRE (13) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallMutexTryAcquire(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex); if (!mx) return STATUS_NOT_FOUND; KSTATUS s = KeTryAcquireMutex(mx); ObDereferenceObject(&mx->Header); return s; } // --------------------------------------------------------------------------- // SYS_MUTEX_RELEASE (14) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallMutexRelease(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex); if (!mx) return STATUS_NOT_FOUND; KeReleaseMutex(mx); ObDereferenceObject(&mx->Header); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_SEM_CREATE (15) a0=initial a1=max a2=*handleOut // --------------------------------------------------------------------------- static uint64_t SyscallSemCreate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a2) return STATUS_INVALID_PARAMETER; SEMAPHORE* sem = NULL; KSTATUS s = KeSemaphoreCreate((int32_t)a0, (int32_t)a1, &sem); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = AllocHandleForCurrent(&sem->Header, &h); if (!KSUCCESS(s)) return s; *(uint32_t*)(uintptr_t)a2 = h; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_SEM_WAIT (16) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallSemWait(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore); if (!sem) return STATUS_NOT_FOUND; KSTATUS s = KeWaitSemaphore(sem); ObDereferenceObject(&sem->Header); return s; } // --------------------------------------------------------------------------- // SYS_SEM_TRY_WAIT (17) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallSemTryWait(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore); if (!sem) return STATUS_NOT_FOUND; KSTATUS s = KeTrySemaphore(sem); ObDereferenceObject(&sem->Header); return s; } // --------------------------------------------------------------------------- // SYS_SEM_SIGNAL (18) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallSemSignal(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore); if (!sem) return STATUS_NOT_FOUND; KeSignalSemaphore(sem); ObDereferenceObject(&sem->Header); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_EVENT_CREATE (19) a0=type a1=initialState a2=*handleOut // --------------------------------------------------------------------------- static uint64_t SyscallEventCreate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a2 || a0 > EventManualReset) return STATUS_INVALID_PARAMETER; EVENT* ev = NULL; KSTATUS s = KeEventCreate((EVENT_TYPE)a0, (uint32_t)a1, &ev); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = AllocHandleForCurrent(&ev->Header, &h); if (!KSUCCESS(s)) return s; *(uint32_t*)(uintptr_t)a2 = h; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_EVENT_SIGNAL (20) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallEventSignal(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent); if (!ev) return STATUS_NOT_FOUND; KeSignalEvent(ev); ObDereferenceObject(&ev->Header); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_EVENT_CLEAR (21) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallEventClear(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent); if (!ev) return STATUS_NOT_FOUND; KeClearEvent(ev); ObDereferenceObject(&ev->Header); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_EVENT_WAIT (22) a0 = handle // --------------------------------------------------------------------------- static uint64_t SyscallEventWait(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent); if (!ev) return STATUS_NOT_FOUND; KSTATUS s = KeWaitForEvent(ev); ObDereferenceObject(&ev->Header); return s; } // --------------------------------------------------------------------------- // SYS_QUEUE_CREATE (23) a0 = *handleOut // --------------------------------------------------------------------------- static uint64_t SyscallQueueCreate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; QUEUE* q = NULL; KSTATUS s = QueueCreate(&q); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = AllocHandleForCurrent(&q->Header, &h); if (!KSUCCESS(s)) return s; *(uint32_t*)(uintptr_t)a0 = h; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_QUEUE_SEND (24) a0=handle a1=*msg a2=blocking // --------------------------------------------------------------------------- static uint64_t SyscallQueueSend(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; QUEUE* q = (QUEUE*)HandleToObject((uint32_t)a0, ObjectTypeQueue); if (!q) return STATUS_NOT_FOUND; KSTATUS s = KeSendMessage(q, (const QUEUE_MESSAGE*)(uintptr_t)a1, (bool)a2); ObDereferenceObject(&q->Header); return s; } // --------------------------------------------------------------------------- // SYS_QUEUE_RECV (25) a0=handle a1=*msg a2=blocking // --------------------------------------------------------------------------- static uint64_t SyscallQueueRecv(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; QUEUE* q = (QUEUE*)HandleToObject((uint32_t)a0, ObjectTypeQueue); if (!q) return STATUS_NOT_FOUND; KSTATUS s = KeReceiveMessage(q, (QUEUE_MESSAGE*)(uintptr_t)a1, (bool)a2); ObDereferenceObject(&q->Header); return s; } // --------------------------------------------------------------------------- // SYS_CLOSE_HANDLE (26) a0 = handle // // The one and only close path. KeCloseHandle drops the handle-table reference // on 'obj'. If that was the last reference, ObDereferenceObject runs the // type-specific Destroy callback (FileDestroy, etc.) and frees the pool memory. // No per-type close syscall needed or wanted. // --------------------------------------------------------------------------- static uint64_t SyscallCloseHandle(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; KeCloseHandle(proc, (uint32_t)a0); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_OPEN_FILE (27) a0=*path a1=*handleOut // --------------------------------------------------------------------------- static uint64_t SyscallOpenFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a0 || !a1) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = NULL; KSTATUS s = IoOpenFile((const char*)(uintptr_t)a0, &file); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = AllocHandleForCurrent(&file->Header, &h); if (!KSUCCESS(s)) { // AllocHandleForCurrent already dropped the ref on failure; // the refcount is now 0, so ObDereferenceObject will run FileDestroy. ObDereferenceObject(&file->Header); return s; } *(uint32_t*)(uintptr_t)a1 = h; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_READ_FILE (28) a0=handle a1=buf a2=size a3=*bytesReadOut // --------------------------------------------------------------------------- static uint64_t SyscallReadFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile); if (!file) return STATUS_NOT_FOUND; size_t got = 0; KSTATUS s = IoReadFile(file, (void*)(uintptr_t)a1, (size_t)a2, &got); if (a3) *(size_t*)(uintptr_t)a3 = got; ObDereferenceObject(&file->Header); return s; } // --------------------------------------------------------------------------- // SYS_WRITE_FILE (29) a0=handle a1=buf a2=size a3=*bytesWrittenOut // --------------------------------------------------------------------------- static uint64_t SyscallWriteFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile); if (!file) return STATUS_NOT_FOUND; size_t written = 0; KSTATUS s = IoWriteFile(file, (const void*)(uintptr_t)a1, (size_t)a2, &written); if (a3) *(size_t*)(uintptr_t)a3 = written; ObDereferenceObject(&file->Header); return s; } // --------------------------------------------------------------------------- // SYS_CREATE_FILE (30) a0=*path a1=*handleOut // --------------------------------------------------------------------------- static uint64_t SyscallCreateFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a0 || !a1) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = NULL; KSTATUS s = IoCreateFile((const char*)(uintptr_t)a0, &file); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = AllocHandleForCurrent(&file->Header, &h); if (!KSUCCESS(s)) { ObDereferenceObject(&file->Header); return s; } *(uint32_t*)(uintptr_t)a1 = h; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_DELETE_FILE (31) a0=*path // --------------------------------------------------------------------------- static uint64_t SyscallDeleteFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; return IoDeleteFile((const char*)(uintptr_t)a0); } // --------------------------------------------------------------------------- // SYS_TRUNCATE_FILE (32) a0=handle a1=newSize // --------------------------------------------------------------------------- static uint64_t SyscallTruncateFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile); if (!file) return STATUS_NOT_FOUND; KSTATUS s = IoTruncateFile(file, (uint64_t)a1); ObDereferenceObject(&file->Header); return s; } // --------------------------------------------------------------------------- // SYS_GET_FILE_POS (33) a0=handle -> current seek offset // --------------------------------------------------------------------------- static uint64_t SyscallGetFilePos(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile); if (!file) return (uint64_t)-1; uint64_t pos = file->Offset; ObDereferenceObject(&file->Header); return pos; } // --------------------------------------------------------------------------- // SYS_SEEK_FILE (34) a0=handle a1=offset // --------------------------------------------------------------------------- static uint64_t SyscallSeekFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile); if (!file) return STATUS_NOT_FOUND; KSTATUS s = IoSeekFile(file, (uint64_t)a1); ObDereferenceObject(&file->Header); return s; } // --------------------------------------------------------------------------- // SYS_GET_FILE_SIZE (35) a0=handle -> uint64_t // --------------------------------------------------------------------------- static uint64_t SyscallGetFileSize(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile); if (!file) return 0; uint64_t sz = IoGetFileSize(file); ObDereferenceObject(&file->Header); return sz; } // --------------------------------------------------------------------------- // SYS_SCREEN_BLIT (36) a0=x a1=y a2=w a3=h a4=*pixels // --------------------------------------------------------------------------- static uint64_t SyscallScreenBlit(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; if (!gHal || !a4) return STATUS_FAILURE; gHal->Screen.Blit((int)a0, (int)a1, (int)a2, (int)a3, (const uint32_t*)(uintptr_t)a4); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_SCREEN_CLEAR (37) a0=color // --------------------------------------------------------------------------- static uint64_t SyscallScreenClear(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return STATUS_FAILURE; gHal->Screen.Clear((uint32_t)a0); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_SCREEN_GET_SIZE (38) a0=*widthOut a1=*heightOut // --------------------------------------------------------------------------- static uint64_t SyscallScreenGetSize(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return STATUS_FAILURE; int w = 0, h = 0; gHal->Screen.GetSize(&w, &h); if (a0) *(int*)(uintptr_t)a0 = w; if (a1) *(int*)(uintptr_t)a1 = h; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_SCREEN_PUT_CHAR (39) a0=c a1=x a2=y a3=fg a4=bg // --------------------------------------------------------------------------- static uint64_t SyscallScreenPutChar(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; if (!gHal) return STATUS_FAILURE; gHal->Screen.PutChar((char)a0, (int)a1, (int)a2, (uint32_t)a3, (uint32_t)a4); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_KB_POLL (40) -> 1 or 0 // --------------------------------------------------------------------------- static uint64_t SyscallKbPoll(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return 0; return gHal->Keyboard.Poll(); } // --------------------------------------------------------------------------- // SYS_KB_IS_KEY_DOWN (41) a0=scancode a1=extended -> 1 or 0 // --------------------------------------------------------------------------- static uint64_t SyscallKbIsKeyDown(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; // Check gHal and ensure the HAL struct actually has the new function pointer if (!gHal || !gHal->Keyboard.IsKeyDown) return 0; return gHal->Keyboard.IsKeyDown((uint8_t)a0, (bool)a1) ? 1u : 0u; } // --------------------------------------------------------------------------- // SYS_KB_READ_CHAR (42) -> char (blocks, yields while waiting) // --------------------------------------------------------------------------- static uint64_t SyscallKbReadChar(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return 0; while (!gHal->Keyboard.Poll()) KeYield(); return (uint64_t)(uint8_t)gHal->Keyboard.ReadChar(); } // --------------------------------------------------------------------------- // SYS_KB_READ_SCANCODE (43) -> uint8_t (blocks, yields while waiting) // --------------------------------------------------------------------------- static uint64_t SyscallKbReadScancode(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return 0; while (!gHal->Keyboard.Poll()) KeYield(); return (uint64_t)gHal->Keyboard.ReadScancode(); } // --------------------------------------------------------------------------- // SYS_KB_GET_MODIFIERS (44) -> uint32_t // --------------------------------------------------------------------------- static uint64_t SyscallKbGetModifiers(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return 0; return (uint64_t)gHal->Keyboard.GetModifiers(); } // --------------------------------------------------------------------------- // SYS_MOUSE_POLL (45) -> 1 or 0 // --------------------------------------------------------------------------- static uint64_t SyscallMousePoll(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return 0; return gHal->Mouse.Poll() ? 1u : 0u; } // --------------------------------------------------------------------------- // SYS_MOUSE_READ (46) a0=*dx a1=*dy a2=*buttons -> 1 or 0 // --------------------------------------------------------------------------- static uint64_t SyscallMouseRead(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!gHal) return 0; return gHal->Mouse.Read((int*)(uintptr_t)a0, (int*)(uintptr_t)a1, (uint8_t*)(uintptr_t)a2) ? 1u : 0u; } // --------------------------------------------------------------------------- // SYS_MOUSE_GET_POS (47) a0=*x a1=*y // --------------------------------------------------------------------------- static uint64_t SyscallMouseGetPos(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal) return 0; gHal->Mouse.GetPosition((int*)(uintptr_t)a0, (int*)(uintptr_t)a1); return 0; } // --------------------------------------------------------------------------- // SYS_GET_TICK (48) -> uint64_t milliseconds // --------------------------------------------------------------------------- static uint64_t SyscallGetTick(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; return gHal ? gHal->Timer.QueryTicks() : 0; } // --------------------------------------------------------------------------- // SYS_DEBUG_PRINT (49) a0=*str // --------------------------------------------------------------------------- static uint64_t SyscallDebugPrint(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (a0) DbgPrint("%s", (const char*)(uintptr_t)a0); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_CREATE_PROCESS (50) a0=*path a1=*handleOut a2=*cmdline (optional) // --------------------------------------------------------------------------- static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; const char* path = (const char*)(uintptr_t)a0; uint32_t* handleOut = (uint32_t*)(uintptr_t)a1; const char* cmdline = (const char*)(uintptr_t)a2; // NULL = no args if (!path || !handleOut) return STATUS_INVALID_PARAMETER; // Read the executable from disk. FILE_OBJECT* f; KSTATUS s = IoOpenFile(path, &f); if (!KSUCCESS(s)) return s; uint64_t size = IoGetFileSize(f); void* mem = MmAllocateUserMemory(size); if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; } size_t got; s = IoReadFile(f, mem, size, &got); IoCloseFile(f); if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; } // Create the process object. PROCESS* proc; 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]) strncpy(proc->Cmdline, cmdline, sizeof(proc->Cmdline) - 1); else strncpy(proc->Cmdline, path, sizeof(proc->Cmdline) - 1); proc->Cmdline[sizeof(proc->Cmdline) - 1] = '\0'; // proc->Cmdline is kernel pool - CPL=3 code can't read it. // 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) { 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; s = LdrLoadImage(mem, size, &imageBase, true); 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)) { 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(); if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; } uint32_t handle; s = KeAllocateHandle(caller, &proc->Header, &handle); if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; } *handleOut = handle; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_WAIT_FOR_PROCESS (51) a0=handle // Blocks until the target process signals its ExitEvent. // --------------------------------------------------------------------------- static uint64_t SyscallWaitForProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* proc = (PROCESS*)HandleToObject((uint32_t)a0, ObjectTypeProcess); if (!proc) return STATUS_INVALID_HANDLE; KSTATUS s = KeWaitForEvent(proc->ExitEvent); ObDereferenceObject(&proc->Header); return s; } // --------------------------------------------------------------------------- // SYS_GET_PROCESS_EXIT_CODE (52) a0=handle a1=*exitCodeOut // --------------------------------------------------------------------------- static uint64_t SyscallGetProcessExitCode(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* proc = (PROCESS*)HandleToObject((uint32_t)a0, ObjectTypeProcess); if (!proc) return STATUS_INVALID_HANDLE; int* exitCodeOut = (int*)(uintptr_t)a1; KSTATUS s = KeGetProcessExitCode(proc, exitCodeOut); ObDereferenceObject(&proc->Header); return s; } // --------------------------------------------------------------------------- // SYS_WRITE_CONSOLE (53) a0=*buf a1=len // Writes user console text in white; distinct from green SYS_DEBUG_PRINT. // --------------------------------------------------------------------------- static uint64_t SyscallWriteConsole(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; const char* buf = (const char*)(uintptr_t)a0; size_t len = (size_t)a1; if (buf && len) KePrintConsole(buf, len); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_OPEN_DIR (54) a0=*dirPath a1=*handleOut // --------------------------------------------------------------------------- static uint64_t SyscallOpenDir(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; const char* dirPath = (const char*)(uintptr_t)a0; uint32_t* handleOut = (uint32_t*)(uintptr_t)a1; if (!handleOut) return STATUS_INVALID_PARAMETER; DIR_OBJECT* dir = NULL; KSTATUS s = IoOpenDir(dirPath, &dir); if (!KSUCCESS(s)) return s; // Put the object in the handle table and get a valid index (1-255) s = KeAllocateHandle(KeGetCurrentProcess(), &dir->Header, handleOut); // Drop the reference KeAllocateHandle took, so that when user space // calls CloseHandle, the refcount drops to 0 and the object is destroyed. ObDereferenceObject(&dir->Header); return s; } // --------------------------------------------------------------------------- // SYS_READ_DIR (55) a0=handle a1=*KDIR_ENTRY // Returns STATUS_NO_MORE_FILES when the directory is exhausted. // --------------------------------------------------------------------------- static uint64_t SyscallReadDir(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; // a0 = handle from user mode // a1 = KDIR_ENTRY* buffer // Look up a DIR_OBJECT using ObjectTypeDir (not ObjectTypeFile) DIR_OBJECT* dir = (DIR_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeDir); if (!dir) return STATUS_INVALID_PARAMETER; KSTATUS s = IoReadDir(dir, (void*)(uintptr_t)a1); ObDereferenceObject(&dir->Header); // Drop the ref taken by HandleToObject return s; } // --------------------------------------------------------------------------- // SYS_SET_CWD (56) a0=*path // --------------------------------------------------------------------------- static uint64_t SyscallSetCwd(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; const char* path = (const char*)(uintptr_t)a0; if (!path) return STATUS_INVALID_PARAMETER; return IoSetCwd(path); } // --------------------------------------------------------------------------- // SYS_GET_CWD (57) a0=*buf a1=size // --------------------------------------------------------------------------- static uint64_t SyscallGetCwd(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; char* buf = (char*)(uintptr_t)a0; size_t size = (size_t)a1; if (!buf || size == 0) return STATUS_INVALID_PARAMETER; return IoGetCwd(buf, size); } // --------------------------------------------------------------------------- // SYS_WAIT_THREAD (58) a0=tid // Blocks the caller until the thread with the given TID exits. // Searches the current process's thread list; cross-process join is not // supported in v1 (POSIX pthread_join is always intra-process). // Returns STATUS_NOT_FOUND if the TID is unknown (already reaped or never // existed) - treat the same as a successful join at the POSIX layer. // --------------------------------------------------------------------------- static uint64_t SyscallWaitThread(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; uint32_t targetTid = (uint32_t)a0; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; // Walk the thread list under the spinlock to find the target, then bump // its ExitEvent refcount so the event survives even if the thread exits // and is destroyed while we wait. EVENT* exitEvent = (EVENT*)0; uintptr_t flags = KeAcquireSpinlock(&proc->HandleLock); LIST_ENTRY* entry = proc->ThreadList.Fwd; while (entry != &proc->ThreadList) { THREAD* t = CONTAINING_RECORD(entry, THREAD, ProcessLink); if (t->ThreadId == targetTid && t->ExitEvent) { exitEvent = t->ExitEvent; ObReferenceObject(&exitEvent->Header); // keep event alive break; } entry = entry->Fwd; } KeReleaseSpinlock(&proc->HandleLock, flags); if (!exitEvent) { // TID not found - either already exited and reaped, or invalid. // Treat as success at the POSIX layer (join on an already-dead thread). return STATUS_NOT_FOUND; } KSTATUS s = KeWaitForEvent(exitEvent); ObDereferenceObject(&exitEvent->Header); return s; } // --------------------------------------------------------------------------- // SYS_GET_PROCESS_PID (59) a0=procHandle a1=*pidOut // Returns the numeric PID of a process identified by handle. // Used by posix_spawn to map the kernel handle back to a pid_t for waitpid. // --------------------------------------------------------------------------- static uint64_t SyscallGetProcessPid(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* proc = (PROCESS*)HandleToObject((uint32_t)a0, ObjectTypeProcess); uint32_t* pidOut = (uint32_t*)(uintptr_t)a1; if (!proc) return STATUS_INVALID_HANDLE; if (!pidOut) { ObDereferenceObject(&proc->Header); return STATUS_INVALID_PARAMETER; } *pidOut = proc->ProcessId; ObDereferenceObject(&proc->Header); return STATUS_SUCCESS; } // =========================================================================== // Named Queue Registry // // A flat, fixed-size table of { name -> QUEUE* } pairs protected by a // spinlock. The registry holds one reference to each published queue for // the lifetime of the entry, so the queue object cannot be destroyed while // it is registered even if every process closes its own handle to it. // // Limits: NAMED_QUEUE_MAX names, each up to NAMED_QUEUE_NAME_MAX bytes. // These are generous for the intended usage (a handful of system servers). // =========================================================================== #define NAMED_QUEUE_MAX 16 #define NAMED_QUEUE_NAME_MAX 64 typedef struct { char Name[NAMED_QUEUE_NAME_MAX]; QUEUE* Queue; // NULL = slot free; non-NULL = occupied uint32_t PublisherPid; // ProcessId of whoever called SysPublishQueue } NAMED_QUEUE_ENTRY; static NAMED_QUEUE_ENTRY gNamedQueues[NAMED_QUEUE_MAX]; static SPINLOCK gNamedQueueLock; // --------------------------------------------------------------------------- // SYS_PUBLISH_QUEUE (60) a0=*name a1=queueHandle // // Registers the queue identified by 'queueHandle' under 'name'. // Fails with STATUS_ALREADY_EXISTS if the name is already taken. // The registry bumps the queue's refcount so it outlives the handle. // --------------------------------------------------------------------------- static uint64_t SyscallPublishQueue(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; const char* name = (const char*)(uintptr_t)a0; if (!name || !name[0]) return STATUS_INVALID_PARAMETER; // Resolve the handle to a QUEUE object. HandleToObject bumps the // refcount by one; we keep that ref in the registry slot. QUEUE* q = (QUEUE*)HandleToObject((uint32_t)a1, ObjectTypeQueue); if (!q) return STATUS_INVALID_HANDLE; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); // Reject duplicate names. for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (gNamedQueues[i].Queue && strncmp(gNamedQueues[i].Name, name, NAMED_QUEUE_NAME_MAX) == 0) { KeReleaseSpinlock(&gNamedQueueLock, flags); ObDereferenceObject(&q->Header); // drop the ref we just took return STATUS_ALREADY_EXISTS; } } // Find a free slot. for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (!gNamedQueues[i].Queue) { strncpy(gNamedQueues[i].Name, name, NAMED_QUEUE_NAME_MAX - 1); gNamedQueues[i].Name[NAMED_QUEUE_NAME_MAX - 1] = '\0'; gNamedQueues[i].Queue = q; // ref transferred to registry gNamedQueues[i].PublisherPid = KeGetCurrentProcess()->ProcessId; KeReleaseSpinlock(&gNamedQueueLock, flags); return STATUS_SUCCESS; } } KeReleaseSpinlock(&gNamedQueueLock, flags); ObDereferenceObject(&q->Header); // no room - drop the ref return STATUS_NO_MEMORY; } // --------------------------------------------------------------------------- // SYS_OPEN_NAMED_QUEUE (61) a0=*name a1=*handleOut // // Looks up 'name' in the registry and allocates a handle to the queue in the // calling process's handle table. Returns STATUS_NOT_FOUND if no match. // Callers that race against the publisher should retry on NOT_FOUND. // --------------------------------------------------------------------------- static uint64_t SyscallOpenNamedQueue(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; const char* name = (const char*)(uintptr_t)a0; uint32_t* handleOut = (uint32_t*)(uintptr_t)a1; if (!name || !name[0] || !handleOut) return STATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); QUEUE* q = NULL; for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (gNamedQueues[i].Queue && strncmp(gNamedQueues[i].Name, name, NAMED_QUEUE_NAME_MAX) == 0) { q = gNamedQueues[i].Queue; ObReferenceObject(&q->Header); // one ref for the new handle break; } } KeReleaseSpinlock(&gNamedQueueLock, flags); if (!q) return STATUS_NOT_FOUND; // AllocHandleForCurrent consumes the ref we just bumped. // On failure it calls ObDereferenceObject internally. return AllocHandleForCurrent(&q->Header, handleOut); } // --------------------------------------------------------------------------- // SYS_QUEUE_DUPLICATE (62) a0=handle a1=targetPid a2=*handleOut // // Looks up the QUEUE behind 'handle' in the CALLER's table, then allocates // a new handle for the same object in the TARGET process's table. // Both processes share the same underlying QUEUE (refcount bumped). // The caller's own handle is unchanged. // --------------------------------------------------------------------------- static uint64_t SyscallQueueDuplicate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a2) return STATUS_INVALID_PARAMETER; QUEUE* q = (QUEUE*)HandleToObject((uint32_t)a0, ObjectTypeQueue); if (!q) return STATUS_NOT_FOUND; PROCESS* target = KeGetProcessById((uint32_t)a1); if (!target) { ObDereferenceObject(&q->Header); return STATUS_NOT_FOUND; } uint32_t newHandle = 0; KSTATUS s = KeAllocateHandle(target, &q->Header, &newHandle); ObDereferenceObject(&target->Header); ObDereferenceObject(&q->Header); if (!KSUCCESS(s)) return s; *(uint32_t*)(uintptr_t)a2 = newHandle; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_NAMED_QUEUE_OWNER_PID (63) a0=*name a1=*pidOut // // Returns the ProcessId of whoever published the named queue. // Lets a client get the publisher's PID so it can duplicate one of its // own handles into that process via SYS_QUEUE_DUPLICATE. // --------------------------------------------------------------------------- static uint64_t SyscallNamedQueueOwnerPid(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; const char* name = (const char*)(uintptr_t)a0; uint32_t* pidOut = (uint32_t*)(uintptr_t)a1; if (!name || !name[0] || !pidOut) return STATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (gNamedQueues[i].Queue && strncmp(gNamedQueues[i].Name, name, NAMED_QUEUE_NAME_MAX) == 0) { *pidOut = gNamedQueues[i].PublisherPid; KeReleaseSpinlock(&gNamedQueueLock, flags); return STATUS_SUCCESS; } } KeReleaseSpinlock(&gNamedQueueLock, flags); return STATUS_NOT_FOUND; } static uint64_t SyscallKillProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* proc = KeGetProcessById((uint32_t)a0); if (!proc) return STATUS_NOT_FOUND; THREAD* self = KeGetCurrentThread(); int exitCode = (int)a1; if (self && self->Owner == proc) { KeExitProcess(proc, exitCode); ObDereferenceObject(&proc->Header); KeExitThread(self, exitCode); while (true) ; } KeExitProcess(proc, exitCode); ObDereferenceObject(&proc->Header); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_GET_PROCESS_LIST (65) a0=*pidsOut a1=maxCount a2=*countOut // --------------------------------------------------------------------------- static uint64_t SyscallGetProcessList(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; uint32_t* pidsOut = (uint32_t*)(uintptr_t)a0; size_t maxCount = (size_t)a1; size_t* countOut = (size_t*)(uintptr_t)a2; size_t count = KeCopyProcessIds(pidsOut, maxCount); if (countOut) *countOut = count; return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_RENAME_FILE (66) a0=*oldPath a1=*newPath // --------------------------------------------------------------------------- static uint64_t SyscallRenameFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; const char* oldPath = (const char*)(uintptr_t)a0; const char* newPath = (const char*)(uintptr_t)a1; if (!oldPath || !newPath) return STATUS_INVALID_PARAMETER; return IoRenameFile(oldPath, newPath); } // --------------------------------------------------------------------------- // SYS_CREATE_DIR (67) a0=*path // --------------------------------------------------------------------------- static uint64_t SyscallCreateDir(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; const char* path = (const char*)(uintptr_t)a0; if (!path) return STATUS_INVALID_PARAMETER; return IoCreateDir(path); } // --------------------------------------------------------------------------- // SYS_REMOVE_DIR (68) a0=*path // --------------------------------------------------------------------------- static uint64_t SyscallRemoveDir(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; const char* path = (const char*)(uintptr_t)a0; if (!path) return STATUS_INVALID_PARAMETER; return IoRemoveDir(path); } // =========================================================================== // Dispatch table - index must match SYS_* in syscall_nr.h exactly. // =========================================================================== typedef uint64_t (*SYSCALL_HANDLER)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t); static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = { /* 00 SYS_EXIT_PROCESS */ SyscallExitProcess, /* 01 SYS_EXIT_THREAD */ SyscallExitThread, /* 02 SYS_CREATE_THREAD */ SyscallCreateThread, /* 03 SYS_GET_CURRENT_TID */ SyscallGetCurrentTid, /* 04 SYS_GET_CURRENT_PID */ SyscallGetCurrentPid, /* 05 SYS_YIELD */ SyscallYield, /* 06 SYS_SLEEP */ SyscallSleep, /* 07 SYS_SET_THREAD_PRIORITY */ SyscallSetThreadPriority, /* 08 SYS_ALLOC */ SyscallAlloc, /* 09 SYS_FREE */ SyscallFree, /* 10 SYS_REALLOC */ SyscallRealloc, /* 11 SYS_MUTEX_CREATE */ SyscallMutexCreate, /* 12 SYS_MUTEX_ACQUIRE */ SyscallMutexAcquire, /* 13 SYS_MUTEX_TRY_ACQUIRE */ SyscallMutexTryAcquire, /* 14 SYS_MUTEX_RELEASE */ SyscallMutexRelease, /* 15 SYS_SEM_CREATE */ SyscallSemCreate, /* 16 SYS_SEM_WAIT */ SyscallSemWait, /* 17 SYS_SEM_TRY_WAIT */ SyscallSemTryWait, /* 18 SYS_SEM_SIGNAL */ SyscallSemSignal, /* 19 SYS_EVENT_CREATE */ SyscallEventCreate, /* 20 SYS_EVENT_SIGNAL */ SyscallEventSignal, /* 21 SYS_EVENT_CLEAR */ SyscallEventClear, /* 22 SYS_EVENT_WAIT */ SyscallEventWait, /* 23 SYS_QUEUE_CREATE */ SyscallQueueCreate, /* 24 SYS_QUEUE_SEND */ SyscallQueueSend, /* 25 SYS_QUEUE_RECV */ SyscallQueueRecv, /* 26 SYS_CLOSE_HANDLE */ SyscallCloseHandle, /* 27 SYS_OPEN_FILE */ SyscallOpenFile, /* 28 SYS_READ_FILE */ SyscallReadFile, /* 29 SYS_WRITE_FILE */ SyscallWriteFile, /* 30 SYS_CREATE_FILE */ SyscallCreateFile, /* 31 SYS_DELETE_FILE */ SyscallDeleteFile, /* 32 SYS_TRUNCATE_FILE */ SyscallTruncateFile, /* 33 SYS_GET_FILE_POS */ SyscallGetFilePos, /* 34 SYS_SEEK_FILE */ SyscallSeekFile, /* 35 SYS_GET_FILE_SIZE */ SyscallGetFileSize, /* 36 SYS_SCREEN_BLIT */ SyscallScreenBlit, /* 37 SYS_SCREEN_CLEAR */ SyscallScreenClear, /* 38 SYS_SCREEN_GET_SIZE */ SyscallScreenGetSize, /* 39 SYS_SCREEN_PUT_CHAR */ SyscallScreenPutChar, /* 40 SYS_KB_POLL */ SyscallKbPoll, /* 41 SYS_KB_IS_KEY_DOWN */ SyscallKbIsKeyDown, /* 42 SYS_KB_READ_CHAR */ SyscallKbReadChar, /* 43 SYS_KB_READ_SCANCODE */ SyscallKbReadScancode, /* 44 SYS_KB_GET_MODIFIERS */ SyscallKbGetModifiers, /* 45 SYS_MOUSE_POLL */ SyscallMousePoll, /* 46 SYS_MOUSE_READ */ SyscallMouseRead, /* 47 SYS_MOUSE_GET_POS */ SyscallMouseGetPos, /* 48 SYS_GET_TICK */ SyscallGetTick, /* 49 SYS_DEBUG_PRINT */ SyscallDebugPrint, /* 50 SYS_CREATE_PROCESS */ SyscallCreateProcess, /* 51 SYS_WAIT_FOR_PROCESS */ SyscallWaitForProcess, /* 52 SYS_GET_PROCESS_EXIT_CODE */ SyscallGetProcessExitCode, /* 53 SYS_WRITE_CONSOLE */ SyscallWriteConsole, /* 54 SYS_OPEN_DIR */ SyscallOpenDir, /* 55 SYS_READ_DIR */ SyscallReadDir, /* 56 SYS_SET_CWD */ SyscallSetCwd, /* 57 SYS_GET_CWD */ SyscallGetCwd, /* 58 SYS_WAIT_THREAD */ SyscallWaitThread, /* 59 SYS_GET_PROCESS_PID */ SyscallGetProcessPid, /* 60 SYS_PUBLISH_QUEUE */ SyscallPublishQueue, /* 61 SYS_OPEN_NAMED_QUEUE */ SyscallOpenNamedQueue, /* 62 SYS_QUEUE_DUPLICATE */ SyscallQueueDuplicate, /* 63 SYS_NAMED_QUEUE_OWNER_PID */ SyscallNamedQueueOwnerPid, /* 64 SYS_KILL_PROCESS */ SyscallKillProcess, /* 65 SYS_GET_PROCESS_LIST */ SyscallGetProcessList, /* 66 SYS_RENAME_FILE */ SyscallRenameFile, /* 67 SYS_CREATE_DIR */ SyscallCreateDir, /* 68 SYS_REMOVE_DIR */ SyscallRemoveDir, }; // --------------------------------------------------------------------------- // Entry point - called from ke_syscall.asm after argument re-ordering // --------------------------------------------------------------------------- KSTATUS KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { if (nr >= SYS_MAX || !gSyscallTable[nr]) return STATUS_NOT_SUPPORTED; return (KSTATUS)gSyscallTable[nr](a0, a1, a2, a3, a4, a5); }