// 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. KiFileDestroy] // -> 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 #ifndef PROCESS_LAUNCH_DEBUG #define PROCESS_LAUNCH_DEBUG 0 #endif #if PROCESS_LAUNCH_DEBUG #define ProcDbg(...) DbgPrint(__VA_ARGS__) #else #define ProcDbg(...) ((void)0) #endif #include #include #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // HAL pointer - set once at boot by KeSyscallSetHal() // --------------------------------------------------------------------------- static const HAL_API* gHal; typedef struct SYS_IO_STATUS_BLOCK_K { volatile uint32_t Status; volatile uint64_t Information; } SYS_IO_STATUS_BLOCK_K; typedef void (*SYS_USER_APC_ROUTINE_K)(void* normalContext, SYS_IO_STATUS_BLOCK_K* iosb, uint64_t information); typedef struct SYS_ASYNC_FILE_REQUEST_K { uint32_t FileHandle; uint32_t EventHandle; uint32_t Flags; uint64_t Offset; void* Buffer; size_t Size; SYS_IO_STATUS_BLOCK_K* Iosb; SYS_USER_APC_ROUTINE_K ApcRoutine; void* ApcContext; uint32_t* RequestHandleOut; // optional cancellation/query handle output } SYS_ASYNC_FILE_REQUEST_K; typedef struct SYS_USER_ASYNC_APC_CONTEXT { KAPC KernelApc; THREAD* TargetThread; SYS_USER_APC_ROUTINE_K UserRoutine; void* UserContext; } SYS_USER_ASYNC_APC_CONTEXT; void KeSyscallInit(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* KiHandleToObject(uint32_t handle, OBJECT_TYPE expected, uint32_t requiredRights) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NULL; OBJECT* obj = KeLookupHandleWithRights(proc, handle, requiredRights); if (!obj) return NULL; if (obj->Type != expected) { ObDereferenceObject(obj); 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 KiAllocHandleForCurrentWithRights(OBJECT* obj, uint32_t rights, uint32_t* handleOut) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) { ObDereferenceObject(obj); return STATUS_FAILURE; } KSTATUS s = KeAllocateHandleWithRights(proc, obj, rights, handleOut); // Most syscall create/open paths receive a fresh creator reference from the // subsystem. The handle table takes its own reference; this helper always // drops the creator reference so the object is owned only by the returned // handle (and any subsystem-internal refs). ObDereferenceObject(obj); return s; } static KSTATUS KiAllocHandleForCurrent(OBJECT* obj, uint32_t* handleOut) { return KiAllocHandleForCurrentWithRights(obj, HANDLE_RIGHT_ALL, handleOut); } #define SYSCALL_STRING_MAX 1024 #define SYSCALL_TEXT_MAX_BYTES (64ULL * 1024ULL) #define SYSCALL_IO_MAX_BYTES (16ULL * 1024ULL * 1024ULL) #define SYSCALL_BLIT_MAX_BYTES (16ULL * 1024ULL * 1024ULL) static KSTATUS KiCopyUserRequest(uint64_t arg, void* dst, size_t size) { if (!arg || !dst || size == 0) return STATUS_INVALID_PARAMETER; return CopyFromUser(dst, (const void*)(uintptr_t)arg, size); } static KSTATUS KiImportUserStringView(SYS_STRING_VIEW userView, char* dst, size_t dstSize, SYS_STRING_VIEW* out, bool nulTerminate) { if (!out) return STATUS_INVALID_PARAMETER; *out = SysSvFromParts((const char*)0, 0); if (userView.Length == 0) { if (nulTerminate) { if (!dst || dstSize == 0) return STATUS_BUFFER_TOO_SMALL; dst[0] = '\0'; } return STATUS_SUCCESS; } if (!userView.Data || !dst) return STATUS_INVALID_PARAMETER; if (userView.Length > SYSCALL_STRING_MAX) return STATUS_BUFFER_TOO_SMALL; size_t needed = userView.Length + (nulTerminate ? 1 : 0); if (needed < userView.Length || needed > dstSize) return STATUS_BUFFER_TOO_SMALL; KSTATUS s = CopyFromUser(dst, userView.Data, userView.Length); if (!KSUCCESS(s)) return s; if (nulTerminate) dst[userView.Length] = '\0'; *out = SysSvFromParts(dst, userView.Length); return STATUS_SUCCESS; } static KSTATUS KiImportUserTextView(SYS_STRING_VIEW userView, char* dst, size_t dstSize, SYS_STRING_VIEW* out, bool nulTerminate) { KSTATUS s = KiImportUserStringView(userView, dst, dstSize, out, nulTerminate); if (!KSUCCESS(s)) return s; if (out && SysSvContainsByte(*out, '\0')) return STATUS_INVALID_PARAMETER; return STATUS_SUCCESS; } static KSTATUS KiCopyHandleToUser(uint64_t arg, uint32_t value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } static KSTATUS KiCopyU32ToUser(uint64_t arg, uint32_t value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } static KSTATUS KiCopySizeToUser(uint64_t arg, size_t value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } static KSTATUS KiCopyIntToUser(uint64_t arg, int value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } static int KiMulSize(size_t a, size_t b, size_t* out) { if (!out) return 0; if (a && b > ((size_t)-1) / a) return 0; *out = a * b; return 1; } // =========================================================================== // 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 (!a0) return STATUS_INVALID_PARAMETER; if (a2 >= PriorityCount) return STATUS_INVALID_PARAMETER; KSTATUS s = MmProbeUserRead((const void*)(uintptr_t)a0, 1); if (!KSUCCESS(s)) return s; if (a3) { s = MmProbeUserWrite((void*)(uintptr_t)a3, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; } THREAD* newt = NULL; s = KeCreateThread(proc, (uintptr_t)a0, (uintptr_t)a1, (THREAD_PRIORITY)a2, &newt); if (!KSUCCESS(s)) return s; if (a3) return KiCopyU32ToUser(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 * 1000u); while (gHal->Timer.QueryTicks() < deadline) KeYield(); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_SET_USER_APC_DISPATCHER a0 = dispatcher entry in syscall.dll/user image // --------------------------------------------------------------------------- static uint64_t SyscallSetUserApcDispatcher(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; KSTATUS s = MmProbeUserRead((const void*)(uintptr_t)a0, 1); if (!KSUCCESS(s)) return s; return KeSetUserApcDispatcher((void*)(uintptr_t)a0); } // --------------------------------------------------------------------------- // SYS_APC_RETURN a0 = SYS_USER_APC_FRAME* on the user stack // --------------------------------------------------------------------------- static uint64_t SyscallApcReturn(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; return KeUserApcReturn((const SYS_USER_APC_FRAME_K*)(uintptr_t)a0); } // --------------------------------------------------------------------------- // 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_VM_* page-granular user virtual memory // --------------------------------------------------------------------------- static KSTATUS KiVmProtectToPageFlags(uint32_t protect, uint64_t* flagsOut) { if (!flagsOut) return STATUS_INVALID_PARAMETER; switch (protect) { case SYS_VM_NOACCESS: *flagsOut = 0; return STATUS_SUCCESS; case SYS_VM_READ: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_NX; return STATUS_SUCCESS; case SYS_VM_READWRITE: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_WRITE | PAGE_NX; return STATUS_SUCCESS; case SYS_VM_EXECUTE: case SYS_VM_EXECUTE_READ: *flagsOut = PAGE_PRESENT | PAGE_USER; return STATUS_SUCCESS; case SYS_VM_EXECUTE_READWRITE: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_WRITE; return STATUS_SUCCESS; default: return STATUS_INVALID_PARAMETER; } } static uint32_t KiPageFlagsToVmProtect(uint64_t flags) { if (!(flags & PAGE_PRESENT)) return SYS_VM_NOACCESS; bool writable = (flags & PAGE_WRITE) != 0; bool executable = (flags & PAGE_NX) == 0; if (executable && writable) return SYS_VM_EXECUTE_READWRITE; if (executable) return SYS_VM_EXECUTE_READ; if (writable) return SYS_VM_READWRITE; return SYS_VM_READ; } static uint64_t SyscallVmAlloc(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; void* preferredBase = (void*)(uintptr_t)a0; size_t pageCount = (size_t)a1; uint32_t protect = (uint32_t)a2; uint32_t flags = (uint32_t)a3; void** baseOut = (void**)(uintptr_t)a4; if (!pageCount || !baseOut) return STATUS_INVALID_PARAMETER; if (pageCount > ((size_t)-1) / PAGE_SIZE) return STATUS_NO_MEMORY; if (preferredBase || (flags & SYS_VM_FLAG_FIXED)) return STATUS_NOT_SUPPORTED; uint64_t pageFlags = 0; KSTATUS s = KiVmProtectToPageFlags(protect, &pageFlags); if (!KSUCCESS(s)) return s; PROCESS* process = KeGetCurrentProcess(); if (!process) return STATUS_FAILURE; void* base = (void*)0; s = MmCreateUserRegion(process, pageCount * PAGE_SIZE, PAGE_SIZE, pageFlags ? pageFlags : (PAGE_PRESENT | PAGE_WRITE | PAGE_USER | PAGE_NX), USER_REGION_ZERO, UserRegionGeneric, &base); if (!KSUCCESS(s)) return s; if (protect == SYS_VM_NOACCESS) { s = MmProtectUserRegion(process, base, pageCount * PAGE_SIZE, 0); if (!KSUCCESS(s)) { MmDestroyUserRegion(process, base); return s; } } s = CopyToUser(baseOut, &base, sizeof(base)); if (!KSUCCESS(s)) { MmDestroyUserRegion(process, base); return s; } return STATUS_SUCCESS; } static uint64_t SyscallVmFree(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* process = KeGetCurrentProcess(); if (!process || !a0) return STATUS_INVALID_PARAMETER; return MmDestroyUserRegion(process, (void*)(uintptr_t)a0); } static uint64_t SyscallVmProtect(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a0 || !a1) return STATUS_INVALID_PARAMETER; if ((size_t)a1 > ((size_t)-1) / PAGE_SIZE) return STATUS_NO_MEMORY; uint64_t pageFlags = 0; KSTATUS s = KiVmProtectToPageFlags((uint32_t)a2, &pageFlags); if (!KSUCCESS(s)) return s; PROCESS* process = KeGetCurrentProcess(); if (!process) return STATUS_FAILURE; if (a3) { void* regionBase = (void*)0; size_t regionSize = 0; uint64_t oldFlags = 0; USER_REGION_TYPE type = UserRegionGeneric; uint32_t regionFlags = 0; s = MmQueryUserRegion(process, (void*)(uintptr_t)a0, ®ionBase, ®ionSize, &oldFlags, &type, ®ionFlags); if (!KSUCCESS(s)) return s; uint32_t oldProtect = KiPageFlagsToVmProtect(oldFlags); s = CopyToUser((void*)(uintptr_t)a3, &oldProtect, sizeof(oldProtect)); if (!KSUCCESS(s)) return s; } return MmProtectUserRegion(process, (void*)(uintptr_t)a0, (size_t)a1 * PAGE_SIZE, pageFlags); } static uint64_t SyscallVmQuery(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; PROCESS* process = KeGetCurrentProcess(); if (!process) return STATUS_FAILURE; void* base = (void*)0; size_t size = 0; uint64_t pageFlags = 0; USER_REGION_TYPE type = UserRegionGeneric; uint32_t regionFlags = 0; KSTATUS s = MmQueryUserRegion(process, (void*)(uintptr_t)a0, &base, &size, &pageFlags, &type, ®ionFlags); if (!KSUCCESS(s)) return s; SYS_VM_REGION_INFO info; info.Base = base; info.PageCount = size / PAGE_SIZE; info.Protect = KiPageFlagsToVmProtect(pageFlags); info.Flags = regionFlags; info.Type = (uint32_t)type; info.OwnerPid = process->ProcessId; return CopyToUser((void*)(uintptr_t)a1, &info, sizeof(info)); } // --------------------------------------------------------------------------- // 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 = KeMutexCreate(&mx); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&mx->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h); // takes ownership of the ref if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a0, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // --------------------------------------------------------------------------- // 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*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_WAIT); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_WAIT); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_SIGNAL); 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 = KiAllocHandleForCurrentWithRights(&sem->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a2, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // --------------------------------------------------------------------------- // 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*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_WAIT); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_WAIT); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_SIGNAL); 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 = KiAllocHandleForCurrentWithRights(&ev->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a2, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // --------------------------------------------------------------------------- // 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*)KiHandleToObject((uint32_t)a0, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL); if (!ev) return STATUS_NOT_FOUND; KeClearEvent(ev); ObDereferenceObject(&ev->Header); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_EVENT_WAIT a0 = handle, a1 = alertable // --------------------------------------------------------------------------- static uint64_t SyscallEventWait(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; EVENT* ev = (EVENT*)KiHandleToObject((uint32_t)a0, ObjectTypeEvent, HANDLE_RIGHT_WAIT); if (!ev) return STATUS_NOT_FOUND; uint8_t alertable = a1 ? 1 : 0; KSTATUS s = KeWaitForEvent(ev, alertable); if (alertable) { THREAD* thread = KeGetCurrentThread(); if (thread && thread->UserApcPending) thread->UserApcDeliverable = 1; } 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 = KeQueueCreate(&q); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&q->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_DUPLICATE | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a0, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // --------------------------------------------------------------------------- // 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_MESSAGE msg; KSTATUS s = CopyFromUser(&msg, (const void*)(uintptr_t)a1, sizeof(msg)); if (!KSUCCESS(s)) return s; QUEUE* q = (QUEUE*)KiHandleToObject((uint32_t)a0, ObjectTypeQueue, HANDLE_RIGHT_WRITE); if (!q) return STATUS_NOT_FOUND; s = KeSendMessage(q, &msg, (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*)KiHandleToObject((uint32_t)a0, ObjectTypeQueue, HANDLE_RIGHT_READ); if (!q) return STATUS_NOT_FOUND; QUEUE_MESSAGE msg; KSTATUS s = KeReceiveMessage(q, &msg, (bool)a2); ObDereferenceObject(&q->Header); if (!KSUCCESS(s)) return s; return CopyToUser((void*)(uintptr_t)a1, &msg, sizeof(msg)); } // --------------------------------------------------------------------------- // 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 (KiFileDestroy, 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PATH_HANDLE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = NULL; s = IoOpenFile(path, &file); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&file->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // --------------------------------------------------------------------------- // 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; size_t size = (size_t)a2; if (size && !a1) return STATUS_INVALID_PARAMETER; if (size > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (size == 0) { if (a3) return KiCopySizeToUser(a3, 0); return STATUS_SUCCESS; } KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, size); if (!KSUCCESS(s)) return s; void* kbuf = MmAllocatePool(size); if (!kbuf) return STATUS_NO_MEMORY; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_READ); if (!file) { MmFreePool(kbuf); return STATUS_NOT_FOUND; } size_t got = 0; s = IoReadFile(file, kbuf, size, &got); ObDereferenceObject(&file->Header); if (KSUCCESS(s)) s = CopyToUser((void*)(uintptr_t)a1, kbuf, got); MmFreePool(kbuf); if (KSUCCESS(s) && a3) s = KiCopySizeToUser(a3, got); 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; size_t size = (size_t)a2; if (size && !a1) return STATUS_INVALID_PARAMETER; if (size > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (size == 0) { if (a3) return KiCopySizeToUser(a3, 0); return STATUS_SUCCESS; } void* kbuf = MmAllocatePool(size); if (!kbuf) return STATUS_NO_MEMORY; KSTATUS s = CopyFromUser(kbuf, (const void*)(uintptr_t)a1, size); if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; } FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_WRITE); if (!file) { MmFreePool(kbuf); return STATUS_NOT_FOUND; } size_t written = 0; s = IoWriteFile(file, kbuf, size, &written); ObDereferenceObject(&file->Header); MmFreePool(kbuf); if (a3) { KSTATUS cs = KiCopySizeToUser(a3, written); if (KSUCCESS(s)) s = cs; } return s; } static void SyscallUserAsyncKernelRoutine(KAPC* apc, void** normalRoutine, void** normalContext, void** systemArgument1, void** systemArgument2) { (void)normalRoutine; (void)normalContext; (void)systemArgument1; (void)systemArgument2; SYS_USER_ASYNC_APC_CONTEXT* ctx = CONTAINING_RECORD(apc, SYS_USER_ASYNC_APC_CONTEXT, KernelApc); DbgPrint("USER/APC async: kernel routine ctx=0x%016llX iosb=0x%016llX status=%u info=%llu irql=%u\n", (uint64_t)ctx, (uint64_t)(uintptr_t)apc->SystemArgument1, (uint32_t)(uintptr_t)apc->SystemArgument2, apc->SystemArgument1 ? ((SYS_IO_STATUS_BLOCK_K*)apc->SystemArgument1)->Information : 0, KeGetCurrentIrql()); } static void SyscallUserAsyncApcContextFree(SYS_USER_ASYNC_APC_CONTEXT* ctx) { if (!ctx) return; if (ctx->TargetThread) { ObDereferenceObject(&ctx->TargetThread->Header); ctx->TargetThread = (THREAD*)0; } MmFreePool(ctx); } static void SyscallUserAsyncApcRundown(KAPC* apc) { if (!apc) return; SYS_USER_ASYNC_APC_CONTEXT* ctx = CONTAINING_RECORD(apc, SYS_USER_ASYNC_APC_CONTEXT, KernelApc); SyscallUserAsyncApcContextFree(ctx); } static void SyscallUserAsyncNormalRoutine(void* normalContext, void* systemArgument1, void* systemArgument2) { SYS_USER_ASYNC_APC_CONTEXT* ctx = (SYS_USER_ASYNC_APC_CONTEXT*)normalContext; SYS_IO_STATUS_BLOCK_K* iosb = (SYS_IO_STATUS_BLOCK_K*)systemArgument1; uint64_t information = iosb ? iosb->Information : 0; (void)systemArgument2; if (ctx && ctx->UserRoutine) { KSTATUS s = KeQueueUserApc(ctx->TargetThread, (void*)ctx->UserRoutine, ctx->UserContext, iosb, (void*)(uintptr_t)information); if (!KSUCCESS(s)) { DbgPrint("USER/APC async: KeQueueUserApc failed status=%u\n", s); } } SyscallUserAsyncApcContextFree(ctx); } static uint64_t SyscallAsyncFileCommon(uint64_t a0, bool write) { if (!a0) return STATUS_INVALID_PARAMETER; SYS_ASYNC_FILE_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a0, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags) return STATUS_INVALID_PARAMETER; if (!req.Iosb) return STATUS_INVALID_PARAMETER; if (!req.Buffer && req.Size) return STATUS_INVALID_PARAMETER; if (req.Size > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; s = MmProbeUserWrite(req.Iosb, sizeof(*req.Iosb)); if (!KSUCCESS(s)) return s; if (req.Size) { s = write ? MmProbeUserRead(req.Buffer, req.Size) : MmProbeUserWrite(req.Buffer, req.Size); if (!KSUCCESS(s)) return s; } if (req.RequestHandleOut) { s = MmProbeUserWrite(req.RequestHandleOut, sizeof(*req.RequestHandleOut)); if (!KSUCCESS(s)) return s; } FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, write ? HANDLE_RIGHT_WRITE : HANDLE_RIGHT_READ); if (!file) return STATUS_NOT_FOUND; EVENT* event = (EVENT*)0; if (req.EventHandle) { event = (EVENT*)KiHandleToObject(req.EventHandle, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL); if (!event) { ObDereferenceObject(&file->Header); return STATUS_NOT_FOUND; } } KAPC* completionApc = (KAPC*)0; SYS_USER_ASYNC_APC_CONTEXT* apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)0; if (req.ApcRoutine) { THREAD* thread = KeGetCurrentThread(); if (!thread || !thread->UserApcDispatcher) { if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return STATUS_INVALID_PARAMETER; } apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)MmAllocatePool(sizeof(SYS_USER_ASYNC_APC_CONTEXT)); if (!apcCtx) { if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return STATUS_NO_MEMORY; } memset(apcCtx, 0, sizeof(*apcCtx)); ObReferenceObject(&thread->Header); apcCtx->TargetThread = thread; apcCtx->UserRoutine = req.ApcRoutine; apcCtx->UserContext = req.ApcContext; KeInitializeApc(&apcCtx->KernelApc, thread, SyscallUserAsyncKernelRoutine, SyscallUserAsyncNormalRoutine, apcCtx); KeSetApcRundownRoutine(&apcCtx->KernelApc, SyscallUserAsyncApcRundown); completionApc = &apcCtx->KernelApc; } IO_ASYNC_FILE_REQUEST_OBJECT* asyncReq = (IO_ASYNC_FILE_REQUEST_OBJECT*)0; if (write) { s = IoWriteFileAsyncKernel(file, req.Buffer, req.Size, req.Offset, (IO_STATUS_BLOCK*)req.Iosb, event, completionApc, &asyncReq); } else { s = IoReadFileAsyncKernel(file, req.Buffer, req.Size, req.Offset, (IO_STATUS_BLOCK*)req.Iosb, event, completionApc, &asyncReq); } if (s == STATUS_PENDING && req.RequestHandleOut && asyncReq) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) { (void)IoCancelAsyncFileRequest(asyncReq); ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return STATUS_FAILURE; } uint32_t requestHandle = 0; KSTATUS hs = KeAllocateHandleWithRights(proc, IoAsyncFileRequestObject(asyncReq), HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE, &requestHandle); if (!KSUCCESS(hs)) { (void)IoCancelAsyncFileRequest(asyncReq); ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return hs; } KSTATUS cs = CopyToUser(req.RequestHandleOut, &requestHandle, sizeof(requestHandle)); if (!KSUCCESS(cs)) { (void)IoCancelAsyncFileRequest(asyncReq); KeCloseHandle(proc, requestHandle); ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return cs; } } else if (req.RequestHandleOut) { uint32_t zeroHandle = 0; KSTATUS cs = CopyToUser(req.RequestHandleOut, &zeroHandle, sizeof(zeroHandle)); if (!KSUCCESS(cs)) { if (asyncReq) ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); if (!KSUCCESS(s) && s != STATUS_PENDING && apcCtx) SyscallUserAsyncApcContextFree(apcCtx); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return cs; } } if (asyncReq) ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); // drop creator reference if (!KSUCCESS(s) && s != STATUS_PENDING && apcCtx) SyscallUserAsyncApcContextFree(apcCtx); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return s; } // --------------------------------------------------------------------------- // SYS_READ_FILE_ASYNC (43) a0=*SYS_ASYNC_FILE_REQUEST // --------------------------------------------------------------------------- static uint64_t SyscallReadFileAsync(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; return SyscallAsyncFileCommon(a0, false); } // --------------------------------------------------------------------------- // SYS_WRITE_FILE_ASYNC (44) a0=*SYS_ASYNC_FILE_REQUEST // --------------------------------------------------------------------------- static uint64_t SyscallWriteFileAsync(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; return SyscallAsyncFileCommon(a0, true); } // --------------------------------------------------------------------------- // SYS_CANCEL_IO a0 = async request handle // --------------------------------------------------------------------------- static uint64_t SyscallCancelIo(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; IO_ASYNC_FILE_REQUEST_OBJECT* req = (IO_ASYNC_FILE_REQUEST_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeAsyncIo, HANDLE_RIGHT_TERMINATE); if (!req) return STATUS_INVALID_HANDLE; KSTATUS s = IoCancelAsyncFileRequest(req); ObDereferenceObject(IoAsyncFileRequestObject(req)); 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PATH_HANDLE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = NULL; s = IoCreateFile(path, &file); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&file->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // --------------------------------------------------------------------------- // 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; SYS_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; return IoDeleteFile(path); } // --------------------------------------------------------------------------- // 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*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_WRITE); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY); if (!file) return (uint64_t)-1; uintptr_t flags = KeAcquireSpinlock(&file->Lock); uint64_t pos = file->Offset; KeReleaseSpinlock(&file->Lock, flags); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY); 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; int w = (int)a2; int h = (int)a3; if (w <= 0 || h <= 0) return STATUS_INVALID_PARAMETER; size_t pixels = 0; size_t bytes = 0; if (!KiMulSize((size_t)w, (size_t)h, &pixels) || !KiMulSize(pixels, sizeof(uint32_t), &bytes) || bytes > SYSCALL_BLIT_MAX_BYTES) return STATUS_INVALID_PARAMETER; uint32_t* kpixels = (uint32_t*)MmAllocatePool(bytes); if (!kpixels) return STATUS_NO_MEMORY; KSTATUS s = CopyFromUser(kpixels, (const void*)(uintptr_t)a4, bytes); if (KSUCCESS(s)) gHal->Screen.Blit((int)a0, (int)a1, w, h, kpixels); MmFreePool(kpixels); return s; } // --------------------------------------------------------------------------- // 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) { KSTATUS s = KiCopyIntToUser(a0, w); if (!KSUCCESS(s)) return s; } if (a1) { KSTATUS s = KiCopyIntToUser(a1, h); if (!KSUCCESS(s)) return s; } 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 (64) -> next INPUT_KEY or INPUT_KEY_NONE // --------------------------------------------------------------------------- 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; return (uint64_t)KeInputKbPoll(); } // --------------------------------------------------------------------------- // SYS_KB_IS_KEY_DOWN (65) a0=INPUT_KEY -> 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; return KeInputIsKeyDown((INPUT_KEY)a0) ? 1u : 0u; } // --------------------------------------------------------------------------- // SYS_KB_READ_CHAR (66) -> 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; while (!KeInputKbPoll()) KeYield(); return (uint64_t)(uint8_t)KeInputReadChar(); } // --------------------------------------------------------------------------- // SYS_KB_READ_KEY (67) -> INPUT_KEY (blocks, yields while waiting) // --------------------------------------------------------------------------- static uint64_t SyscallKbReadKey(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; while (!KeInputKbPoll()) KeYield(); return (uint64_t)KeInputReadKey(); } // --------------------------------------------------------------------------- // SYS_KB_GET_MODIFIERS (68) -> uint32_t INPUT_MOD_* flags // --------------------------------------------------------------------------- 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; return (uint64_t)KeInputGetModifiers(); } // --------------------------------------------------------------------------- // SYS_MOUSE_POLL (69) -> 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; return KeInputMousePoll() ? 1u : 0u; } // --------------------------------------------------------------------------- // SYS_MOUSE_READ (70) 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 (!a0 || !a1 || !a2) return 0; int dx = 0, dy = 0; uint8_t buttons = 0; if (!KeInputMouseRead(&dx, &dy, &buttons)) return 0; if (!KSUCCESS(CopyToUser((void*)(uintptr_t)a0, &dx, sizeof(dx)))) return 0; if (!KSUCCESS(CopyToUser((void*)(uintptr_t)a1, &dy, sizeof(dy)))) return 0; if (!KSUCCESS(CopyToUser((void*)(uintptr_t)a2, &buttons, sizeof(buttons)))) return 0; return 1u; } // --------------------------------------------------------------------------- // SYS_MOUSE_GET_POS (71) 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 (!a0 || !a1) return STATUS_INVALID_PARAMETER; int x = 0, y = 0; KeInputMouseGetPosition(&x, &y); KSTATUS s = KiCopyIntToUser(a0, x); if (!KSUCCESS(s)) return s; return KiCopyIntToUser(a1, y); } // --------------------------------------------------------------------------- // SYS_GET_TICK (48) -> uint64_t microseconds // --------------------------------------------------------------------------- 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; SYS_DEBUG_PRINT_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Text.Length == 0) return STATUS_SUCCESS; if (!req.Text.Data) return STATUS_INVALID_PARAMETER; if (req.Text.Length > SYSCALL_TEXT_MAX_BYTES) return STATUS_BUFFER_TOO_SMALL; char chunk[512]; size_t offset = 0; while (offset < req.Text.Length) { size_t todo = req.Text.Length - offset; if (todo > sizeof(chunk)) todo = sizeof(chunk); s = CopyFromUser(chunk, req.Text.Data + offset, todo); if (!KSUCCESS(s)) return s; DbgPrint("%v", SysSvFromParts(chunk, todo)); offset += todo; } return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // SYS_QUERY_TRAP_FRAME a0=*SYS_TRAP_FRAME_INFO // --------------------------------------------------------------------------- static uint64_t SyscallQueryTrapFrame(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; uint32_t userSize = 0; KSTATUS s = CopyFromUser(&userSize, (const void*)(uintptr_t)a0, sizeof(userSize)); if (!KSUCCESS(s)) return s; SYS_TRAP_FRAME_INFO_K info; memset(&info, 0, sizeof(info)); info.Size = userSize; s = KeQueryCurrentTrapFrame(&info); size_t copySize = (s == STATUS_BUFFER_TOO_SMALL) ? sizeof(info.Size) : sizeof(info); KSTATUS cs = CopyToUser((void*)(uintptr_t)a0, &info, copySize); if (!KSUCCESS(cs)) return cs; return s; } // --------------------------------------------------------------------------- // 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_CREATE_PROCESS_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; char pathBuf[SYSCALL_STRING_MAX]; char cmdlineBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; SYS_STRING_VIEW cmdlineView; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; s = KiImportUserTextView(req.CommandLine, cmdlineBuf, sizeof(cmdlineBuf), &cmdlineView, true); if (!KSUCCESS(s)) return s; s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; // Read the executable from disk. FILE_OBJECT* f; ProcDbg("CreateProcess: path='%v' cmd='%v' callerPid=%u\n", path, cmdlineView, KeGetCurrentProcess() ? KeGetCurrentProcess()->ProcessId : 0); s = IoOpenFile(path, &f); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: IoOpenFile failed st=%d\n", s); return s; } uint64_t size = IoGetFileSize(f); ProcDbg("CreateProcess: fileSize=%llu\n", size); void* mem = MmAllocatePool(size); if (!mem) { IoCloseFile(f); ProcDbg("CreateProcess: raw image pool alloc failed size=%llu\n", size); return STATUS_NO_MEMORY; } size_t got; s = IoReadFile(f, mem, size, &got); IoCloseFile(f); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: IoReadFile failed st=%d got=%llu\n", s, (unsigned long long)got); MmFreePool(mem); return s; } // Create the process object. PROCESS* proc; s = KeCreateProcess(pathBuf, &proc); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: KeCreateProcess failed st=%d\n", s); MmFreePool(mem); return s; } ProcDbg("CreateProcess: created pid=%u name=%s\n", proc->ProcessId, proc->Name); // Inherit CWD from parent PROCESS* parent = KeGetCurrentProcess(); if (parent) { uintptr_t flags = KeAcquireSpinlock(&parent->CwdLock); size_t cwdLen = parent->CurrentDirectoryLength; if (cwdLen >= sizeof(proc->CurrentDirectory)) cwdLen = sizeof(proc->CurrentDirectory) - 1; memcpy(proc->CurrentDirectory, parent->CurrentDirectory, cwdLen); proc->CurrentDirectory[cwdLen] = '\0'; proc->CurrentDirectoryLength = cwdLen; proc->CurrentDrive = parent->CurrentDrive; KeReleaseSpinlock(&parent->CwdLock, flags); } s = KeSetupUserProcessEnvironment(proc, path, cmdlineView, SysSvFromParts(proc->CurrentDirectory, proc->CurrentDirectoryLength)); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: env setup failed st=%d\n", s); MmFreePool(mem); ObDereferenceObject(&proc->Header); return s; } // Map the PE image into memory. uintptr_t imageBase = 0; s = LdrLoadImageForProcess(proc, mem, size, &imageBase); MmFreePool(mem); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: LdrLoadImage failed st=%d\n", s); ObDereferenceObject(&proc->Header); return s; } ProcDbg("CreateProcess: image mapped pid=%u base=%016llx size=%llx\n", proc->ProcessId, (unsigned long long)imageBase, (unsigned long long)LdrGetMappedImageSize(proc, imageBase)); s = LdrResolveImportsForProcess(proc, imageBase); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: resolve imports failed st=%d pid=%u image=%016llx\n", s, proc->ProcessId, (unsigned long long)imageBase); MmDebugDumpUserRegions(proc); ObDereferenceObject(&proc->Header); return s; } ProcDbg("CreateProcess: imports resolved pid=%u modules=%u\n", proc->ProcessId, proc->ModuleCount); s = LdrProtectImageForProcess(proc, imageBase, true); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: protect image failed st=%d pid=%u\n", s, proc->ProcessId); MmDebugDumpUserRegions(proc); ObDereferenceObject(&proc->Header); return s; } proc->ImageBase = imageBase; proc->ImageSize = LdrGetMappedImageSize(proc, imageBase); PROCESS* caller = KeGetCurrentProcess(); if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; } uint32_t handle = 0; s = KeAllocateHandleWithRights(caller, &proc->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE, &handle); if (!KSUCCESS(s)) { proc->ImageBase = 0; proc->ImageSize = 0; ObDereferenceObject(&proc->Header); return s; } THREAD* thread; uintptr_t entryPoint = LdrGetEntryPoint(imageBase); s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: KeCreateThread failed st=%d pid=%u entry=%016llx\n", s, proc->ProcessId, (unsigned long long)entryPoint); MmDebugDumpUserRegions(proc); KeCloseHandle(caller, handle); ObDereferenceObject(&proc->Header); return s; } ProcDbg("CreateProcess: thread created pid=%u tid=%llu entry=%016llx handle=%u\n", proc->ProcessId, (unsigned long long)thread->ThreadId, (unsigned long long)entryPoint, handle); s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle); if (!KSUCCESS(s)) { KeTerminateProcess(proc, -1); KeCloseHandle(caller, handle); ObDereferenceObject(&proc->Header); return s; } // Drop the creator's local reference. The returned handle and live thread // references now own the process lifetime. ObDereferenceObject(&proc->Header); 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*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_WAIT); if (!proc) return STATUS_INVALID_HANDLE; KSTATUS s = KeWaitForEvent(proc->ExitEvent, 0); 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; if (!a1) return STATUS_INVALID_PARAMETER; PROCESS* proc = (PROCESS*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_QUERY); if (!proc) return STATUS_INVALID_HANDLE; int exitCode = 0; KSTATUS s = KeGetProcessExitCode(proc, &exitCode); ObDereferenceObject(&proc->Header); if (!KSUCCESS(s)) return s; return KiCopyIntToUser(a1, exitCode); } // --------------------------------------------------------------------------- // 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_WRITE_CONSOLE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Text.Length == 0) return STATUS_SUCCESS; if (!req.Text.Data) return STATUS_INVALID_PARAMETER; if (req.Text.Length > SYSCALL_TEXT_MAX_BYTES) return STATUS_BUFFER_TOO_SMALL; char chunk[512]; size_t offset = 0; while (offset < req.Text.Length) { size_t todo = req.Text.Length - offset; if (todo > sizeof(chunk)) todo = sizeof(chunk); s = CopyFromUser(chunk, req.Text.Data + offset, todo); if (!KSUCCESS(s)) return s; KePrintConsole(chunk, todo); offset += todo; } 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PATH_HANDLE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; char dirPathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW dirPath; s = KiImportUserTextView(req.Path, dirPathBuf, sizeof(dirPathBuf), &dirPath, true); if (!KSUCCESS(s)) return s; s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; DIR_OBJECT* dir = NULL; s = IoOpenDir(dirPath, &dir); if (!KSUCCESS(s)) return s; uint32_t handle = 0; s = KiAllocHandleForCurrentWithRights(&dir->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_QUERY, &handle); if (!KSUCCESS(s)) return s; // KiAllocHandleForCurrentWithRights consumed IoOpenDir's creator reference; // the returned process handle now owns the directory object. s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); return s; } return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // 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; if (!a1) return STATUS_INVALID_PARAMETER; DIR_OBJECT* dir = (DIR_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeDir, HANDLE_RIGHT_READ); if (!dir) return STATUS_INVALID_PARAMETER; KDIR_ENTRY entry; KSTATUS s = IoReadDir(dir, &entry); ObDereferenceObject(&dir->Header); if (!KSUCCESS(s)) return s; return CopyToUser((void*)(uintptr_t)a1, &entry, sizeof(entry)); } // --------------------------------------------------------------------------- // 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; SYS_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_GET_CWD_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Buffer && req.Capacity) return STATUS_INVALID_PARAMETER; char cwd[SYSCALL_STRING_MAX]; s = IoGetCwd(cwd, sizeof(cwd)); if (!KSUCCESS(s)) return s; size_t len = strlen(cwd); if (req.LengthOut) { s = CopyToUser(req.LengthOut, &len, sizeof(len)); if (!KSUCCESS(s)) return s; } if (len > req.Capacity) return STATUS_BUFFER_TOO_SMALL; if (len) return CopyToUser(req.Buffer, cwd, len); return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // 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, 0); 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; if (!a1) return STATUS_INVALID_PARAMETER; PROCESS* proc = (PROCESS*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_QUERY); if (!proc) return STATUS_INVALID_HANDLE; uint32_t pid = proc->ProcessId; ObDereferenceObject(&proc->Header); return KiCopyU32ToUser(a1, pid); } // =========================================================================== // 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]; size_t NameLength; 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; static int KiNamedQueueNameEquals(const NAMED_QUEUE_ENTRY* entry, SYS_STRING_VIEW name) { if (!entry || !entry->Queue) return 0; return SysSvEqual(SysSvFromParts(entry->Name, entry->NameLength), name); } // --------------------------------------------------------------------------- // 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PUBLISH_QUEUE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char name[NAMED_QUEUE_NAME_MAX]; SYS_STRING_VIEW nameView; s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); if (!KSUCCESS(s)) return s; if (nameView.Length == 0) return STATUS_INVALID_PARAMETER; QUEUE* q = (QUEUE*)KiHandleToObject(req.QueueHandle, ObjectTypeQueue, HANDLE_RIGHT_DUPLICATE); if (!q) return STATUS_INVALID_HANDLE; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { KeReleaseSpinlock(&gNamedQueueLock, flags); ObDereferenceObject(&q->Header); return STATUS_ALREADY_EXISTS; } } for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (!gNamedQueues[i].Queue) { memcpy(gNamedQueues[i].Name, name, nameView.Length); gNamedQueues[i].NameLength = nameView.Length; gNamedQueues[i].Queue = q; gNamedQueues[i].PublisherPid = KeGetCurrentProcess()->ProcessId; KeReleaseSpinlock(&gNamedQueueLock, flags); return STATUS_SUCCESS; } } KeReleaseSpinlock(&gNamedQueueLock, flags); ObDereferenceObject(&q->Header); 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_OPEN_NAMED_QUEUE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; char name[NAMED_QUEUE_NAME_MAX]; SYS_STRING_VIEW nameView; s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); if (!KSUCCESS(s)) return s; if (nameView.Length == 0) return STATUS_INVALID_PARAMETER; s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); QUEUE* q = NULL; for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { q = gNamedQueues[i].Queue; ObReferenceObject(&q->Header); break; } } KeReleaseSpinlock(&gNamedQueueLock, flags); if (!q) return STATUS_NOT_FOUND; uint32_t handle = 0; s = KiAllocHandleForCurrentWithRights(&q->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_DUPLICATE | HANDLE_RIGHT_QUERY, &handle); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); return s; } return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // 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*)KiHandleToObject((uint32_t)a0, ObjectTypeQueue, HANDLE_RIGHT_DUPLICATE); 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 = KeAllocateHandleWithRights(target, &q->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_DUPLICATE | HANDLE_RIGHT_QUERY, &newHandle); if (KSUCCESS(s)) { KSTATUS cs = KiCopyHandleToUser(a2, newHandle); if (!KSUCCESS(cs)) { KeCloseHandle(target, newHandle); s = cs; } } ObDereferenceObject(&target->Header); ObDereferenceObject(&q->Header); return s; } // --------------------------------------------------------------------------- // 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_NAMED_QUEUE_OWNER_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.PidOut) return STATUS_INVALID_PARAMETER; char name[NAMED_QUEUE_NAME_MAX]; SYS_STRING_VIEW nameView; s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); if (!KSUCCESS(s)) return s; if (nameView.Length == 0) return STATUS_INVALID_PARAMETER; uint32_t pid = 0; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { pid = gNamedQueues[i].PublisherPid; KeReleaseSpinlock(&gNamedQueueLock, flags); return KiCopyU32ToUser((uint64_t)(uintptr_t)req.PidOut, pid); } } 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) ; } KSTATUS s = KeTerminateProcess(proc, exitCode); ObDereferenceObject(&proc->Header); return s; } // --------------------------------------------------------------------------- // SYS_GET_PROCESS_LIST (6) a0=*SYS_PROCESS_INFO[] 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; size_t maxCount = (size_t)a1; if (maxCount && !a0) return STATUS_INVALID_PARAMETER; if (a2) { KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a2, sizeof(size_t)); if (!KSUCCESS(s)) return s; } size_t total = KeCopyProcessInfo(NULL, 0); size_t toCopy = total < maxCount ? total : maxCount; if (toCopy) { size_t bytes = 0; if (!KiMulSize(toCopy, sizeof(SYS_PROCESS_INFO), &bytes)) return STATUS_INVALID_PARAMETER; SYS_PROCESS_INFO* tmp = (SYS_PROCESS_INFO*)MmAllocatePool(bytes); if (!tmp) return STATUS_NO_MEMORY; size_t copied = KeCopyProcessInfo(tmp, toCopy); if (copied < toCopy) toCopy = copied; KSTATUS s = CopyToUser((void*)(uintptr_t)a0, tmp, toCopy * sizeof(SYS_PROCESS_INFO)); MmFreePool(tmp); if (!KSUCCESS(s)) return s; } if (a2) return KiCopySizeToUser(a2, total); 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)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_RENAME_FILE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char oldPathBuf[SYSCALL_STRING_MAX]; char newPathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW oldPath; SYS_STRING_VIEW newPath; s = KiImportUserTextView(req.OldPath, oldPathBuf, sizeof(oldPathBuf), &oldPath, true); if (!KSUCCESS(s)) return s; s = KiImportUserTextView(req.NewPath, newPathBuf, sizeof(newPathBuf), &newPath, true); if (!KSUCCESS(s)) return s; if (oldPath.Length == 0 || newPath.Length == 0) 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; SYS_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) 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; SYS_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; return IoRemoveDir(path); } typedef struct SYS_SOCKET_SEND_TO_REQUEST_K { const void* Buffer; size_t Length; uint32_t Flags; const SYS_SOCKET_ADDRESS_IPV4_K* RemoteAddress; size_t RemoteAddressSize; size_t* SentOut; } SYS_SOCKET_SEND_TO_REQUEST_K; typedef struct SYS_SOCKET_RECV_FROM_REQUEST_K { void* Buffer; size_t Length; uint32_t Flags; SYS_SOCKET_ADDRESS_IPV4_K* RemoteAddressOut; size_t* RemoteAddressSizeInOut; size_t* ReceivedOut; } SYS_SOCKET_RECV_FROM_REQUEST_K; typedef struct SYS_RESOLVE_HOST_REQUEST_K { SYS_STRING_VIEW Name; uint32_t Family; uint32_t Flags; SYS_SOCKET_ADDRESS_IPV4_K* AddressOut; size_t* AddressSizeInOut; } SYS_RESOLVE_HOST_REQUEST_K; typedef struct SYS_SOCKET_OPTION_REQUEST_K { uint32_t Option; void* Value; size_t ValueSize; size_t* ActualSizeOut; } SYS_SOCKET_OPTION_REQUEST_K; typedef struct SYS_MEMORY_STATUS_K { uint64_t FreePhysicalBytes; uint64_t KernelPoolBytes; uint64_t UserHeapBytes; } SYS_MEMORY_STATUS_K; #define SYS_POLL_MAX_FDS 64u // --------------------------------------------------------------------------- // SYS_SOCKET a0=domain a1=type a2=protocol a3=*handleOut // --------------------------------------------------------------------------- static uint64_t SyscallSocket(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a3) return STATUS_INVALID_PARAMETER; SOCKET_OBJECT* socket = NULL; KSTATUS s = NetSocketCreate((uint32_t)a0, (uint32_t)a1, (uint32_t)a2, &socket); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&socket->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; // KiAllocHandleForCurrentWithRights consumed NetSocketCreate's creator // reference; the returned process handle now owns the socket object. s = KiCopyHandleToUser(a3, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // --------------------------------------------------------------------------- // SYS_BIND a0=socketHandle a1=*SYS_SOCKET_ADDRESS_IPV4 a2=addressSize // --------------------------------------------------------------------------- static uint64_t SyscallBind(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; if ((size_t)a2 < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) return STATUS_BUFFER_TOO_SMALL; SYS_SOCKET_ADDRESS_IPV4_K address; KSTATUS s = CopyFromUser(&address, (const void*)(uintptr_t)a1, sizeof(address)); if (!KSUCCESS(s)) return s; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; s = NetSocketBind(socket, &address, (size_t)a2); ObDereferenceObject(&socket->Header); return s; } // --------------------------------------------------------------------------- // SYS_LISTEN a0=socketHandle a1=backlog // --------------------------------------------------------------------------- static uint64_t SyscallListen(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; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; KSTATUS s = NetSocketListen(socket, (uint32_t)a1); ObDereferenceObject(&socket->Header); return s; } // --------------------------------------------------------------------------- // SYS_ACCEPT a0=listenSocket a1=*remoteAddressOut(optional) // a2=*remoteAddressSizeInOut(optional) a3=*acceptedHandleOut // --------------------------------------------------------------------------- static uint64_t SyscallAccept(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a3) return STATUS_INVALID_PARAMETER; bool wantRemote = (a1 != 0); size_t remoteCapacity = 0; if (wantRemote) { if (!a2) return STATUS_INVALID_PARAMETER; KSTATUS cs = CopyFromUser(&remoteCapacity, (const void*)(uintptr_t)a2, sizeof(remoteCapacity)); if (!KSUCCESS(cs)) return cs; if (remoteCapacity < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) { size_t required = sizeof(SYS_SOCKET_ADDRESS_IPV4_K); (void)CopyToUser((void*)(uintptr_t)a2, &required, sizeof(required)); return STATUS_BUFFER_TOO_SMALL; } cs = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(SYS_SOCKET_ADDRESS_IPV4_K)); if (!KSUCCESS(cs)) return cs; } SOCKET_OBJECT* listener = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!listener) return STATUS_NOT_FOUND; SOCKET_OBJECT* accepted = NULL; SYS_SOCKET_ADDRESS_IPV4_K remote; KSTATUS s = NetSocketAccept(listener, &accepted, wantRemote ? &remote : NULL); ObDereferenceObject(&listener->Header); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&accepted->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; // KiAllocHandleForCurrentWithRights consumed the accept creator reference; // the returned process handle owns the accepted socket from this point. if (wantRemote) { size_t actual = sizeof(SYS_SOCKET_ADDRESS_IPV4_K); s = CopyToUser((void*)(uintptr_t)a1, &remote, sizeof(remote)); if (KSUCCESS(s)) s = CopyToUser((void*)(uintptr_t)a2, &actual, sizeof(actual)); } if (KSUCCESS(s)) s = KiCopyHandleToUser(a3, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // --------------------------------------------------------------------------- // SYS_CONNECT a0=socketHandle a1=*SYS_SOCKET_ADDRESS_IPV4 a2=addressSize // --------------------------------------------------------------------------- static uint64_t SyscallConnect(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; if ((size_t)a2 < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) return STATUS_BUFFER_TOO_SMALL; SYS_SOCKET_ADDRESS_IPV4_K address; KSTATUS s = CopyFromUser(&address, (const void*)(uintptr_t)a1, sizeof(address)); if (!KSUCCESS(s)) return s; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; s = NetSocketConnect(socket, &address, (size_t)a2); ObDereferenceObject(&socket->Header); return s; } // --------------------------------------------------------------------------- // SYS_SEND a0=socketHandle a1=*buffer a2=length a3=flags a4=*sentOut(optional) // --------------------------------------------------------------------------- static uint64_t SyscallSend(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; size_t length = (size_t)a2; if (length && !a1) return STATUS_INVALID_PARAMETER; if (length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (length == 0) { if (a4) return KiCopySizeToUser(a4, 0); return STATUS_SUCCESS; } void* kbuf = MmAllocatePool(length); if (!kbuf) return STATUS_NO_MEMORY; KSTATUS s = CopyFromUser(kbuf, (const void*)(uintptr_t)a1, length); if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; } SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { MmFreePool(kbuf); return STATUS_NOT_FOUND; } size_t sent = 0; s = NetSocketSend(socket, kbuf, length, (uint32_t)a3, &sent); ObDereferenceObject(&socket->Header); MmFreePool(kbuf); if (a4) { KSTATUS cs = KiCopySizeToUser(a4, sent); if (KSUCCESS(s)) s = cs; } return s; } // --------------------------------------------------------------------------- // SYS_RECV a0=socketHandle a1=*buffer a2=length a3=flags a4=*receivedOut(optional) // --------------------------------------------------------------------------- static uint64_t SyscallRecv(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; size_t length = (size_t)a2; if (length && !a1) return STATUS_INVALID_PARAMETER; if (length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (length == 0) { if (a4) return KiCopySizeToUser(a4, 0); return STATUS_SUCCESS; } KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, length); if (!KSUCCESS(s)) return s; void* kbuf = MmAllocatePool(length); if (!kbuf) return STATUS_NO_MEMORY; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { MmFreePool(kbuf); return STATUS_NOT_FOUND; } size_t received = 0; s = NetSocketReceive(socket, kbuf, length, (uint32_t)a3, &received); ObDereferenceObject(&socket->Header); if (KSUCCESS(s) && received) s = CopyToUser((void*)(uintptr_t)a1, kbuf, received); MmFreePool(kbuf); if (KSUCCESS(s) && a4) s = KiCopySizeToUser(a4, received); return s; } // --------------------------------------------------------------------------- // SYS_SHUTDOWN a0=socketHandle a1=SYS_SHUTDOWN_* // --------------------------------------------------------------------------- static uint64_t SyscallShutdown(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; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; KSTATUS s = NetSocketShutdown(socket, (uint32_t)a1); ObDereferenceObject(&socket->Header); return s; } // --------------------------------------------------------------------------- // SYS_SEND_TO a0=socketHandle a1=*SYS_SOCKET_SEND_TO_REQUEST // --------------------------------------------------------------------------- static uint64_t SyscallSendTo(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 (!a1) return STATUS_INVALID_PARAMETER; SYS_SOCKET_SEND_TO_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Length && !req.Buffer) return STATUS_INVALID_PARAMETER; if (!req.RemoteAddress) return STATUS_INVALID_PARAMETER; if (req.RemoteAddressSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) return STATUS_BUFFER_TOO_SMALL; if (req.Length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; SYS_SOCKET_ADDRESS_IPV4_K remote; s = CopyFromUser(&remote, req.RemoteAddress, sizeof(remote)); if (!KSUCCESS(s)) return s; void* kbuf = NULL; if (req.Length) { kbuf = MmAllocatePool(req.Length); if (!kbuf) return STATUS_NO_MEMORY; s = CopyFromUser(kbuf, req.Buffer, req.Length); if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; } } SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { if (kbuf) MmFreePool(kbuf); return STATUS_NOT_FOUND; } size_t sent = 0; s = NetSocketSendTo(socket, kbuf, req.Length, req.Flags, &remote, req.RemoteAddressSize, &sent); ObDereferenceObject(&socket->Header); if (kbuf) MmFreePool(kbuf); if (req.SentOut) { KSTATUS cs = CopyToUser(req.SentOut, &sent, sizeof(sent)); if (KSUCCESS(s)) s = cs; } return s; } // --------------------------------------------------------------------------- // SYS_RECV_FROM a0=socketHandle a1=*SYS_SOCKET_RECV_FROM_REQUEST // --------------------------------------------------------------------------- static uint64_t SyscallRecvFrom(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 (!a1) return STATUS_INVALID_PARAMETER; SYS_SOCKET_RECV_FROM_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Length && !req.Buffer) return STATUS_INVALID_PARAMETER; if (req.Length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; bool wantRemote = (req.RemoteAddressOut != NULL); size_t remoteCapacity = 0; if (wantRemote) { if (!req.RemoteAddressSizeInOut) return STATUS_INVALID_PARAMETER; s = CopyFromUser(&remoteCapacity, req.RemoteAddressSizeInOut, sizeof(remoteCapacity)); if (!KSUCCESS(s)) return s; if (remoteCapacity < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) { size_t required = sizeof(SYS_SOCKET_ADDRESS_IPV4_K); (void)CopyToUser(req.RemoteAddressSizeInOut, &required, sizeof(required)); return STATUS_BUFFER_TOO_SMALL; } s = MmProbeUserWrite(req.RemoteAddressOut, sizeof(SYS_SOCKET_ADDRESS_IPV4_K)); if (!KSUCCESS(s)) return s; } if (req.Length) { s = MmProbeUserWrite(req.Buffer, req.Length); if (!KSUCCESS(s)) return s; } void* kbuf = NULL; if (req.Length) { kbuf = MmAllocatePool(req.Length); if (!kbuf) return STATUS_NO_MEMORY; } SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { if (kbuf) MmFreePool(kbuf); return STATUS_NOT_FOUND; } SYS_SOCKET_ADDRESS_IPV4_K remote; memset(&remote, 0, sizeof(remote)); size_t received = 0; s = NetSocketReceiveFrom(socket, kbuf, req.Length, req.Flags, wantRemote ? &remote : NULL, &received); ObDereferenceObject(&socket->Header); if (KSUCCESS(s) && received) s = CopyToUser(req.Buffer, kbuf, received); if (kbuf) MmFreePool(kbuf); if (KSUCCESS(s) && wantRemote) { size_t actual = sizeof(SYS_SOCKET_ADDRESS_IPV4_K); s = CopyToUser(req.RemoteAddressOut, &remote, sizeof(remote)); if (KSUCCESS(s)) s = CopyToUser(req.RemoteAddressSizeInOut, &actual, sizeof(actual)); } if (KSUCCESS(s) && req.ReceivedOut) s = CopyToUser(req.ReceivedOut, &received, sizeof(received)); return s; } // --------------------------------------------------------------------------- // SYS_RESOLVE_HOST a0=*SYS_RESOLVE_HOST_REQUEST // --------------------------------------------------------------------------- static uint64_t SyscallResolveHost(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; SYS_RESOLVE_HOST_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a0, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Name.Data || req.Name.Length == 0 || req.Name.Length > SYS_RESOLVE_HOST_NAME_MAX) return STATUS_INVALID_PARAMETER; if (req.Family != SYS_AF_INET) return STATUS_NOT_SUPPORTED; if (req.Flags != 0) return STATUS_NOT_SUPPORTED; if (!req.AddressOut || !req.AddressSizeInOut) return STATUS_INVALID_PARAMETER; size_t capacity = 0; s = CopyFromUser(&capacity, req.AddressSizeInOut, sizeof(capacity)); if (!KSUCCESS(s)) return s; if (capacity < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) { size_t required = sizeof(SYS_SOCKET_ADDRESS_IPV4_K); (void)CopyToUser(req.AddressSizeInOut, &required, sizeof(required)); return STATUS_BUFFER_TOO_SMALL; } char name[SYS_RESOLVE_HOST_NAME_MAX + 1]; SYS_STRING_VIEW nameView; s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, true); if (!KSUCCESS(s)) return s; SYS_SOCKET_ADDRESS_IPV4_K address; s = NetResolveHostIpv4(name, &address); if (!KSUCCESS(s)) return s; size_t actual = sizeof(SYS_SOCKET_ADDRESS_IPV4_K); s = CopyToUser(req.AddressOut, &address, sizeof(address)); if (!KSUCCESS(s)) return s; return CopyToUser(req.AddressSizeInOut, &actual, sizeof(actual)); } // --------------------------------------------------------------------------- // SYS_SET_SOCKET_OPTION a0=socketHandle a1=*SYS_SOCKET_OPTION_REQUEST // --------------------------------------------------------------------------- static uint64_t SyscallSetSocketOption(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 (!a1) return STATUS_INVALID_PARAMETER; SYS_SOCKET_OPTION_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Value) return STATUS_INVALID_PARAMETER; uint8_t value[sizeof(uint64_t)]; if (req.ValueSize > sizeof(value)) return STATUS_BUFFER_TOO_SMALL; s = CopyFromUser(value, req.Value, req.ValueSize); if (!KSUCCESS(s)) return s; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; s = NetSocketSetOption(socket, req.Option, value, req.ValueSize); ObDereferenceObject(&socket->Header); return s; } // --------------------------------------------------------------------------- // SYS_GET_SOCKET_OPTION a0=socketHandle a1=*SYS_SOCKET_OPTION_REQUEST // --------------------------------------------------------------------------- static uint64_t SyscallGetSocketOption(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 (!a1) return STATUS_INVALID_PARAMETER; SYS_SOCKET_OPTION_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Value) return STATUS_INVALID_PARAMETER; uint8_t value[sizeof(uint64_t)]; if (req.ValueSize > sizeof(value)) return STATUS_BUFFER_TOO_SMALL; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; size_t actual = 0; s = NetSocketGetOption(socket, req.Option, value, req.ValueSize, &actual); ObDereferenceObject(&socket->Header); if (!KSUCCESS(s)) { if (s == STATUS_BUFFER_TOO_SMALL && req.ActualSizeOut) (void)CopyToUser(req.ActualSizeOut, &actual, sizeof(actual)); return s; } s = CopyToUser(req.Value, value, actual); if (KSUCCESS(s) && req.ActualSizeOut) s = CopyToUser(req.ActualSizeOut, &actual, sizeof(actual)); return s; } static int KiPollDeadlineExpired(uint64_t start, uint32_t timeoutMs) { if (timeoutMs == 0) return 1; if (timeoutMs == SYS_POLL_TIMEOUT_INFINITE) return 0; if (!gHal) return 1; return (gHal->Timer.QueryTicks() - start) >= ((uint64_t)timeoutMs * 1000u); } // --------------------------------------------------------------------------- // SYS_POLL a0=*SYS_POLLFD array a1=count a2=timeoutMs a3=*readyOut(optional) // --------------------------------------------------------------------------- static uint64_t SyscallPoll(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; size_t count = (size_t)a1; if (count == 0 || count > SYS_POLL_MAX_FDS) return STATUS_INVALID_PARAMETER; size_t bytes = count * sizeof(SYS_POLLFD_K); SYS_POLLFD_K* fds = (SYS_POLLFD_K*)MmAllocatePool(bytes); if (!fds) return STATUS_NO_MEMORY; KSTATUS s = CopyFromUser(fds, (const void*)(uintptr_t)a0, bytes); if (!KSUCCESS(s)) { MmFreePool(fds); return s; } uint32_t timeoutMs = (uint32_t)a2; uint64_t start = gHal ? gHal->Timer.QueryTicks() : 0; size_t ready = 0; for (;;) { ready = 0; for (size_t i = 0; i < count; i++) { fds[i].ReturnedEvents = 0; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject(fds[i].Handle, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { fds[i].ReturnedEvents = SYS_POLL_INVALID; ready++; continue; } uint32_t revents = 0; s = NetSocketPoll(socket, fds[i].Events, &revents); ObDereferenceObject(&socket->Header); if (!KSUCCESS(s)) { fds[i].ReturnedEvents = SYS_POLL_ERROR; ready++; continue; } fds[i].ReturnedEvents = revents; if (revents != 0) ready++; } if (ready != 0 || KiPollDeadlineExpired(start, timeoutMs)) break; KeYield(); } s = CopyToUser((void*)(uintptr_t)a0, fds, bytes); if (KSUCCESS(s) && a3) s = KiCopySizeToUser(a3, ready); MmFreePool(fds); return s; } // --------------------------------------------------------------------------- // SYS_GET_MEMORY_STATUS a0=*SYS_MEMORY_STATUS // --------------------------------------------------------------------------- static uint64_t SyscallGetMemoryStatus(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; SYS_MEMORY_STATUS_K status; status.FreePhysicalBytes = MmFreeFrameCount() * PAGE_SIZE; status.KernelPoolBytes = MmGetKernelPoolUsage(); status.UserHeapBytes = MmGetUserHeapUsage(); return CopyToUser((void*)(uintptr_t)a0, &status, sizeof(status)); } // --------------------------------------------------------------------------- // SYS_QUERY_KERNEL_STATS a0=classId a1=buffer a2=bufferSize a3=*requiredSizeOut // --------------------------------------------------------------------------- static KSTATUS KiReturnKernelStats(uint64_t bufferArg, size_t bufferSize, uint64_t requiredArg, const void* src, size_t size) { if (requiredArg) { KSTATUS s = KiCopySizeToUser(requiredArg, size); if (!KSUCCESS(s)) return s; } if (!bufferArg) return STATUS_SUCCESS; if (bufferSize < size) return STATUS_BUFFER_TOO_SMALL; return CopyToUser((void*)(uintptr_t)bufferArg, src, size); } static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; switch ((uint32_t)a0) { case SYS_KERNEL_STATS_SCHEDULER: { KE_SCHED_STATS ks; SYS_SCHEDULER_STATS us; KeGetSchedulerStats(&ks); memset(&us, 0, sizeof(us)); us.Ticks = ks.Ticks; us.ContextSwitches = ks.ContextSwitches; us.KeepCurrentDecisions = ks.KeepCurrentDecisions; us.IdleSelections = ks.IdleSelections; us.Demotions = ks.Demotions; us.AgingPromotions = ks.AgingPromotions; us.QuantumExpirations = ks.QuantumExpirations; us.PriorityPreemptions = ks.PriorityPreemptions; us.VoluntaryBlocks = ks.VoluntaryBlocks; us.CpuBurstDemotions = ks.CpuBurstDemotions; us.SmtSiblingDeferrals = ks.SmtSiblingDeferrals; us.SmtPrimaryPokes = ks.SmtPrimaryPokes; return KiReturnKernelStats(a1, (size_t)a2, a3, &us, sizeof(us)); } case SYS_KERNEL_STATS_PMM: { MM_PHYSICAL_STATS km; SYS_PMM_STATS um; MmQueryPhysicalStats(&km); memset(&um, 0, sizeof(um)); um.TotalFrames = km.TotalFrames; um.FreeFrames = km.FreeFrames; um.UsedFrames = km.UsedFrames; um.LargestFreeRun = km.LargestFreeRun; um.FreeRunCount = km.FreeRunCount; for (size_t i = 0; i < 8; i++) um.FreeRunHistogram[i] = km.FreeRunHistogram[i]; return KiReturnKernelStats(a1, (size_t)a2, a3, &um, sizeof(um)); } case SYS_KERNEL_STATS_CPU: { SYS_CPU_STATS stats; memset(&stats, 0, sizeof(stats)); stats.CpuCount = gCpuCount; uint32_t n = gCpuCount; if (n > SYS_KERNEL_STATS_MAX_CPUS) n = SYS_KERNEL_STATS_MAX_CPUS; for (uint32_t i = 0; i < n; i++) { KCPU* cpu = &gCpus[i]; SYS_CPU_STAT* out = &stats.Cpu[i]; out->CpuIndex = cpu->CpuIndex; out->LapicId = cpu->LapicId; if (cpu->Present) out->Flags |= SYS_CPU_FLAG_PRESENT; if (cpu->Started) out->Flags |= SYS_CPU_FLAG_STARTED; if (cpu->Parked) out->Flags |= SYS_CPU_FLAG_PARKED; if (cpu->Online) out->Flags |= SYS_CPU_FLAG_ONLINE; if (cpu->IsBsp) out->Flags |= SYS_CPU_FLAG_BSP; if (cpu->IsPrimaryThread) out->Flags |= SYS_CPU_FLAG_PRIMARY; out->CurrentTid = cpu->CurrentThread ? cpu->CurrentThread->ThreadId : 0; out->SchedulerTicks = cpu->SchedulerTicks; out->ContextSwitches = cpu->ContextSwitches; } return KiReturnKernelStats(a1, (size_t)a2, a3, &stats, sizeof(stats)); } default: return STATUS_INVALID_PARAMETER; } } // =========================================================================== // 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_PROCESS */ SyscallCreateProcess, /* 03 SYS_WAIT_FOR_PROCESS */ SyscallWaitForProcess, /* 04 SYS_GET_PROCESS_EXIT_CODE */ SyscallGetProcessExitCode, /* 05 SYS_KILL_PROCESS */ SyscallKillProcess, /* 06 SYS_GET_PROCESS_LIST */ SyscallGetProcessList, /* 07 SYS_GET_PROCESS_PID */ SyscallGetProcessPid, /* 08 SYS_CREATE_THREAD */ SyscallCreateThread, /* 09 SYS_WAIT_THREAD */ SyscallWaitThread, /* 10 SYS_GET_CURRENT_TID */ SyscallGetCurrentTid, /* 11 SYS_GET_CURRENT_PID */ SyscallGetCurrentPid, /* 12 SYS_SET_THREAD_PRIORITY */ SyscallSetThreadPriority, /* 13 SYS_YIELD */ SyscallYield, /* 14 SYS_SLEEP */ SyscallSleep, /* 15 SYS_SET_USER_APC_DISPATCHER */ SyscallSetUserApcDispatcher, /* 16 SYS_APC_RETURN */ SyscallApcReturn, /* 17 SYS_VM_ALLOC */ SyscallVmAlloc, /* 18 SYS_VM_FREE */ SyscallVmFree, /* 19 SYS_VM_PROTECT */ SyscallVmProtect, /* 20 SYS_VM_QUERY */ SyscallVmQuery, /* 21 SYS_CLOSE_HANDLE */ SyscallCloseHandle, /* 22 SYS_MUTEX_CREATE */ SyscallMutexCreate, /* 23 SYS_MUTEX_ACQUIRE */ SyscallMutexAcquire, /* 24 SYS_MUTEX_TRY_ACQUIRE */ SyscallMutexTryAcquire, /* 25 SYS_MUTEX_RELEASE */ SyscallMutexRelease, /* 26 SYS_SEM_CREATE */ SyscallSemCreate, /* 27 SYS_SEM_WAIT */ SyscallSemWait, /* 28 SYS_SEM_TRY_WAIT */ SyscallSemTryWait, /* 29 SYS_SEM_SIGNAL */ SyscallSemSignal, /* 30 SYS_EVENT_CREATE */ SyscallEventCreate, /* 31 SYS_EVENT_SIGNAL */ SyscallEventSignal, /* 32 SYS_EVENT_CLEAR */ SyscallEventClear, /* 33 SYS_EVENT_WAIT */ SyscallEventWait, /* 34 SYS_QUEUE_CREATE */ SyscallQueueCreate, /* 35 SYS_QUEUE_SEND */ SyscallQueueSend, /* 36 SYS_QUEUE_RECV */ SyscallQueueRecv, /* 37 SYS_PUBLISH_QUEUE */ SyscallPublishQueue, /* 38 SYS_OPEN_NAMED_QUEUE */ SyscallOpenNamedQueue, /* 39 SYS_QUEUE_DUPLICATE */ SyscallQueueDuplicate, /* 40 SYS_NAMED_QUEUE_OWNER_PID */ SyscallNamedQueueOwnerPid, /* 41 SYS_OPEN_FILE */ SyscallOpenFile, /* 42 SYS_CREATE_FILE */ SyscallCreateFile, /* 43 SYS_READ_FILE */ SyscallReadFile, /* 44 SYS_WRITE_FILE */ SyscallWriteFile, /* 45 SYS_READ_FILE_ASYNC */ SyscallReadFileAsync, /* 46 SYS_WRITE_FILE_ASYNC */ SyscallWriteFileAsync, /* 47 SYS_CANCEL_IO */ SyscallCancelIo, /* 48 SYS_SEEK_FILE */ SyscallSeekFile, /* 49 SYS_GET_FILE_POS */ SyscallGetFilePos, /* 50 SYS_GET_FILE_SIZE */ SyscallGetFileSize, /* 51 SYS_TRUNCATE_FILE */ SyscallTruncateFile, /* 52 SYS_DELETE_FILE */ SyscallDeleteFile, /* 53 SYS_RENAME_FILE */ SyscallRenameFile, /* 54 SYS_OPEN_DIR */ SyscallOpenDir, /* 55 SYS_READ_DIR */ SyscallReadDir, /* 56 SYS_CREATE_DIR */ SyscallCreateDir, /* 57 SYS_REMOVE_DIR */ SyscallRemoveDir, /* 58 SYS_SET_CWD */ SyscallSetCwd, /* 59 SYS_GET_CWD */ SyscallGetCwd, /* 60 SYS_WRITE_CONSOLE */ SyscallWriteConsole, /* 61 SYS_SCREEN_BLIT */ SyscallScreenBlit, /* 62 SYS_SCREEN_CLEAR */ SyscallScreenClear, /* 63 SYS_SCREEN_GET_SIZE */ SyscallScreenGetSize, /* 64 SYS_SCREEN_PUT_CHAR */ SyscallScreenPutChar, /* 65 SYS_KB_POLL */ SyscallKbPoll, /* 66 SYS_KB_IS_KEY_DOWN */ SyscallKbIsKeyDown, /* 67 SYS_KB_READ_CHAR */ SyscallKbReadChar, /* 68 SYS_KB_READ_KEY */ SyscallKbReadKey, /* 69 SYS_KB_GET_MODIFIERS */ SyscallKbGetModifiers, /* 70 SYS_MOUSE_POLL */ SyscallMousePoll, /* 71 SYS_MOUSE_READ */ SyscallMouseRead, /* 72 SYS_MOUSE_GET_POS */ SyscallMouseGetPos, /* 73 SYS_GET_TICK */ SyscallGetTick, /* 74 SYS_DEBUG_PRINT */ SyscallDebugPrint, /* 75 SYS_QUERY_TRAP_FRAME */ SyscallQueryTrapFrame, /* 76 SYS_SOCKET */ SyscallSocket, /* 77 SYS_BIND */ SyscallBind, /* 78 SYS_LISTEN */ SyscallListen, /* 79 SYS_ACCEPT */ SyscallAccept, /* 80 SYS_CONNECT */ SyscallConnect, /* 81 SYS_SEND */ SyscallSend, /* 82 SYS_RECV */ SyscallRecv, /* 83 SYS_SHUTDOWN */ SyscallShutdown, /* 84 SYS_SEND_TO */ SyscallSendTo, /* 85 SYS_RECV_FROM */ SyscallRecvFrom, /* 86 SYS_RESOLVE_HOST */ SyscallResolveHost, /* 87 SYS_SET_SOCKET_OPTION */ SyscallSetSocketOption, /* 88 SYS_GET_SOCKET_OPTION */ SyscallGetSocketOption, /* 89 SYS_POLL */ SyscallPoll, /* 90 SYS_GET_MEMORY_STATUS */ SyscallGetMemoryStatus, /* 91 SYS_QUERY_KERNEL_STATS */ SyscallQueryKernelStats }; // --------------------------------------------------------------------------- // Entry point - called from ke_syscall.asm after argument re-ordering // --------------------------------------------------------------------------- uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { KiValidateSyscallTrapFrame(nr, a0, a1, a2, a3, a4, a5); THREAD* self = KeGetCurrentThread(); PROCESS* owner = self ? self->Owner : (PROCESS*)0; if (owner && KeIsProcessTerminating(owner) && nr != SYS_EXIT_PROCESS && nr != SYS_EXIT_THREAD) { int exitCode = owner->ExitCode; KeExitThread(self, exitCode); while (true) ; } if (nr >= SYS_MAX || !gSyscallTable[nr]) return STATUS_NOT_SUPPORTED; uint64_t result = gSyscallTable[nr](a0, a1, a2, a3, a4, a5); // A process may be terminated while one of its threads is already inside a // syscall. Do not allow that thread to return to userspace after the owner // has been marked terminating. owner = self ? self->Owner : (PROCESS*)0; if (owner && KeIsProcessTerminating(owner) && nr != SYS_EXIT_PROCESS && nr != SYS_EXIT_THREAD) { int exitCode = owner->ExitCode; KeExitThread(self, exitCode); while (true) ; } return result; }