Files
nt-lite/code/kernel/ke_syscall.c
T

3021 lines
124 KiB
C

// 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 <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <kernel/ke_thread.h>
#include <kernel/ke_cpu.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_process.h>
#include <kernel/ke_mutex.h>
#include <kernel/ke_semaphore.h>
#include <kernel/ke_event.h>
#include <kernel/ke_apc.h>
#include <kernel/ke_queue.h>
#include <kernel/ke_debug.h>
#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 <kernel/mm_virt.h>
#include <kernel/ldr_image.h>
#include <kernel/syscall_nr.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/io_file.h>
#include <kernel/net_socket.h>
#include <kernel/ke_trap.h>
#include <kernel/ke_input.h>
#include <kernel/ke_display.h>
#include <kernel/ke_time.h>
#include <ntlite/syscall.h>
// ---------------------------------------------------------------------------
// 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, &regionBase, &regionSize, &oldFlags, &type, &regionFlags);
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, &regionFlags);
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_CREATE_MUTEX (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_ACQUIRE_MUTEX (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_TRY_ACQUIRE_MUTEX (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_RELEASE_MUTEX (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_CREATE_SEM (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_WAIT_SEM (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_TRY_WAIT_SEM (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_SIGNAL_SEM (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_CREATE_EVENT (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_SIGNAL_EVENT (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_CLEAR_EVENT (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_WAIT_EVENT 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_CREATE_QUEUE (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_SEND_QUEUE (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_RECV_QUEUE (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_BLIT_SCREEN (66) a0=x a1=y a2=w a3=h a4=*pixels
// ---------------------------------------------------------------------------
static uint64_t SyscallBlitScreen(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a5;
if (!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))
s = KeDisplayBlit((int)a0, (int)a1, w, h, kpixels);
MmFreePool(kpixels);
return s;
}
// ---------------------------------------------------------------------------
// SYS_CLEAR_SCREEN (67) a0=color
// ---------------------------------------------------------------------------
static uint64_t SyscallClearScreen(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 KeDisplayClear((uint32_t)a0);
}
// ---------------------------------------------------------------------------
// SYS_GET_SCREEN_SIZE (68) a0=*widthOut a1=*heightOut
// ---------------------------------------------------------------------------
static uint64_t SyscallGetScreenSize(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;
int w = 0, h = 0;
KSTATUS gs = KeDisplayGetSize(&w, &h);
if (!KSUCCESS(gs)) return gs;
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_PUT_SCREEN_CHAR (65) a0=c a1=x a2=y a3=fg a4=bg
// ---------------------------------------------------------------------------
static uint64_t SyscallPutScreenChar(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a5;
return KeDisplayPutChar((char)a0, (int)a1, (int)a2,
(uint32_t)a3, (uint32_t)a4);
}
// ---------------------------------------------------------------------------
// SYS_GET_CONSOLE_INFO (61) a0=*SYS_CONSOLE_INFO
// ---------------------------------------------------------------------------
static uint64_t SyscallGetConsoleInfo(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;
KE_CONSOLE_INFO ki;
KSTATUS s = KeDisplayGetConsoleInfo(&ki);
if (!KSUCCESS(s)) return s;
SYS_CONSOLE_INFO out;
memset(&out, 0, sizeof(out));
out.Columns = ki.Columns;
out.Rows = ki.Rows;
out.CursorX = ki.CursorX;
out.CursorY = ki.CursorY;
out.Attribute = ki.Attribute;
return CopyToUser((void*)(uintptr_t)a0, &out, sizeof(out));
}
// ---------------------------------------------------------------------------
// SYS_SET_CONSOLE_CURSOR (62) a0=column a1=row
// ---------------------------------------------------------------------------
static uint64_t SyscallSetConsoleCursor(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;
int col = (int)(int32_t)a0;
int row = (int)(int32_t)a1;
if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER;
return KeDisplaySetConsoleCursor(col, row);
}
// ---------------------------------------------------------------------------
// SYS_FILL_CONSOLE_CHARACTER (63) a0=char a1=column a2=row a3=count a4=*writtenOut
// ---------------------------------------------------------------------------
static uint64_t SyscallFillConsoleCharacter(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a5;
int col = (int)(int32_t)a1;
int row = (int)(int32_t)a2;
if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER;
size_t written = 0;
KSTATUS s = KeDisplayFillConsoleChars((char)a0, col, row, (size_t)a3, &written);
if (a4) {
KSTATUS cs = KiCopySizeToUser(a4, written);
if (KSUCCESS(s) && !KSUCCESS(cs)) s = cs;
}
return s;
}
// ---------------------------------------------------------------------------
// SYS_FILL_CONSOLE_ATTRIBUTE (64) a0=attribute a1=column a2=row a3=count a4=*writtenOut
// ---------------------------------------------------------------------------
static uint64_t SyscallFillConsoleAttribute(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a5;
int col = (int)(int32_t)a1;
int row = (int)(int32_t)a2;
if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER;
size_t written = 0;
KSTATUS s = KeDisplayFillConsoleAttrs((uint16_t)a0, col, row, (size_t)a3, &written);
if (a4) {
KSTATUS cs = KiCopySizeToUser(a4, written);
if (KSUCCESS(s) && !KSUCCESS(cs)) s = cs;
}
return s;
}
// ---------------------------------------------------------------------------
// SYS_POLL_KEYBOARD (70) -> 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_IS_KEY_DOWN (71) 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_READ_KEYBOARD_CHAR (72) -> 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_READ_KEY (73) -> 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_GET_KEY_MODIFIERS (74) -> 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_POLL_MOUSE (75) -> 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_READ_MOUSE (76) 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_GET_MOUSE_POS (77) 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 (78) -> 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_GET_SYSTEM_TIME a0=*SYS_SYSTEM_TIME
// ---------------------------------------------------------------------------
static uint64_t SyscallGetSystemTime(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_SYSTEM_TIME t;
KSTATUS s = KeQuerySystemTime(&t);
if (!KSUCCESS(s)) return s;
return CopyToUser((void*)(uintptr_t)a0, &t, sizeof(t));
}
// ---------------------------------------------------------------------------
// SYS_PRINT_DEBUG (79) 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_PRINT_DEBUG_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 (60) a0=*buf a1=len
// Writes user console text in white; distinct from green SYS_PRINT_DEBUG.
// ---------------------------------------------------------------------------
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_DUPLICATE_QUEUE (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_GET_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_DUPLICATE_QUEUE.
// ---------------------------------------------------------------------------
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_CREATE_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_SOCKET 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_SOCKET 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_SOCKET 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_SOCKET 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_SOCKET 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_SOCKET 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_SOCKET a0=socketHandle a1=SYS_SHUTDOWN_SOCKET_*
// ---------------------------------------------------------------------------
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_SOCKET 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_SOCKET 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 SyscallQueryDisplayInfo(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;
KE_DISPLAY_INFO ki;
KSTATUS s = KeDisplayQueryInfo(&ki);
if (!KSUCCESS(s)) return s;
SYS_DISPLAY_INFO out;
memset(&out, 0, sizeof(out));
memcpy(out.PrimaryName, ki.PrimaryName, sizeof(out.PrimaryName));
out.Mode = ki.Mode;
out.Caps = ki.Caps;
out.DeviceCount = ki.DeviceCount;
out.ManagerStats = ki.ManagerStats;
out.DeviceStats = ki.DeviceStats;
return CopyToUser((void*)(uintptr_t)a0, &out, sizeof(out));
}
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_ALLOC_VM */ SyscallVmAlloc,
/* 18 SYS_FREE_VM */ SyscallVmFree,
/* 19 SYS_PROTECT_VM */ SyscallVmProtect,
/* 20 SYS_QUERY_VM */ SyscallVmQuery,
/* 21 SYS_CLOSE_HANDLE */ SyscallCloseHandle,
/* 22 SYS_CREATE_MUTEX */ SyscallMutexCreate,
/* 23 SYS_ACQUIRE_MUTEX */ SyscallMutexAcquire,
/* 24 SYS_TRY_ACQUIRE_MUTEX */ SyscallMutexTryAcquire,
/* 25 SYS_RELEASE_MUTEX */ SyscallMutexRelease,
/* 26 SYS_CREATE_SEM */ SyscallSemCreate,
/* 27 SYS_WAIT_SEM */ SyscallSemWait,
/* 28 SYS_TRY_WAIT_SEM */ SyscallSemTryWait,
/* 29 SYS_SIGNAL_SEM */ SyscallSemSignal,
/* 30 SYS_CREATE_EVENT */ SyscallEventCreate,
/* 31 SYS_SIGNAL_EVENT */ SyscallEventSignal,
/* 32 SYS_CLEAR_EVENT */ SyscallEventClear,
/* 33 SYS_WAIT_EVENT */ SyscallEventWait,
/* 34 SYS_CREATE_QUEUE */ SyscallQueueCreate,
/* 35 SYS_SEND_QUEUE */ SyscallQueueSend,
/* 36 SYS_RECV_QUEUE */ SyscallQueueRecv,
/* 37 SYS_PUBLISH_QUEUE */ SyscallPublishQueue,
/* 38 SYS_OPEN_NAMED_QUEUE */ SyscallOpenNamedQueue,
/* 39 SYS_DUPLICATE_QUEUE */ SyscallQueueDuplicate,
/* 40 SYS_GET_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_GET_CONSOLE_INFO */ SyscallGetConsoleInfo,
/* 62 SYS_SET_CONSOLE_CURSOR */ SyscallSetConsoleCursor,
/* 63 SYS_FILL_CONSOLE_CHARACTER */ SyscallFillConsoleCharacter,
/* 64 SYS_FILL_CONSOLE_ATTRIBUTE */ SyscallFillConsoleAttribute,
/* 65 SYS_PUT_SCREEN_CHAR */ SyscallPutScreenChar,
/* 66 SYS_BLIT_SCREEN */ SyscallBlitScreen,
/* 67 SYS_CLEAR_SCREEN */ SyscallClearScreen,
/* 68 SYS_GET_SCREEN_SIZE */ SyscallGetScreenSize,
/* 69 SYS_QUERY_DISPLAY_INFO */ SyscallQueryDisplayInfo,
/* 70 SYS_POLL_KEYBOARD */ SyscallKbPoll,
/* 71 SYS_IS_KEY_DOWN */ SyscallKbIsKeyDown,
/* 72 SYS_READ_KEYBOARD_CHAR */ SyscallKbReadChar,
/* 73 SYS_READ_KEY */ SyscallKbReadKey,
/* 74 SYS_GET_KEY_MODIFIERS */ SyscallKbGetModifiers,
/* 75 SYS_POLL_MOUSE */ SyscallMousePoll,
/* 76 SYS_READ_MOUSE */ SyscallMouseRead,
/* 77 SYS_GET_MOUSE_POS */ SyscallMouseGetPos,
/* 78 SYS_GET_TICK */ SyscallGetTick,
/* 79 SYS_PRINT_DEBUG */ SyscallDebugPrint,
/* 80 SYS_QUERY_TRAP_FRAME */ SyscallQueryTrapFrame,
/* 81 SYS_CREATE_SOCKET */ SyscallSocket,
/* 82 SYS_BIND_SOCKET */ SyscallBind,
/* 83 SYS_LISTEN_SOCKET */ SyscallListen,
/* 84 SYS_ACCEPT_SOCKET */ SyscallAccept,
/* 85 SYS_CONNECT_SOCKET */ SyscallConnect,
/* 86 SYS_SEND_SOCKET */ SyscallSend,
/* 87 SYS_RECV_SOCKET */ SyscallRecv,
/* 88 SYS_SHUTDOWN_SOCKET */ SyscallShutdown,
/* 89 SYS_SEND_TO_SOCKET */ SyscallSendTo,
/* 90 SYS_RECV_FROM_SOCKET */ SyscallRecvFrom,
/* 91 SYS_RESOLVE_HOST */ SyscallResolveHost,
/* 92 SYS_SET_SOCKET_OPTION */ SyscallSetSocketOption,
/* 93 SYS_GET_SOCKET_OPTION */ SyscallGetSocketOption,
/* 94 SYS_POLL */ SyscallPoll,
/* 95 SYS_GET_MEMORY_STATUS */ SyscallGetMemoryStatus,
/* 96 SYS_QUERY_KERNEL_STATS */ SyscallQueryKernelStats,
/* 97 SYS_GET_SYSTEM_TIME */ SyscallGetSystemTime
};
// ---------------------------------------------------------------------------
// 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;
}