2682 lines
110 KiB
C
2682 lines
110 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 <ke_thread.h>
|
|
#include <ke_sched.h>
|
|
#include <ke_process.h>
|
|
#include <ke_mutex.h>
|
|
#include <ke_semaphore.h>
|
|
#include <ke_event.h>
|
|
#include <ke_apc.h>
|
|
#include <ke_queue.h>
|
|
#include <ke_debug.h>
|
|
#include <mm_virt.h>
|
|
#include <ldr_image.h>
|
|
#include <syscall_nr.h>
|
|
#include <hal_api.h>
|
|
#include <io_file.h>
|
|
#include <net_socket.h>
|
|
#include <ke_trap.h>
|
|
#include <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;
|
|
uint32_t Reserved;
|
|
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_ALLOC (8) a0 = size -> void*
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallAlloc(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a0) return 0;
|
|
return (uint64_t)(uintptr_t)MmAllocateUserMemory((size_t)a0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_FREE a0 = ptr
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallFree(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) MmFreeUserMemory((void*)(uintptr_t)a0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_REALLOC (10) a0 = ptr, a1 = newSize -> void*
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallRealloc(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
return (uint64_t)(uintptr_t)MmReallocateUserMemory(
|
|
(void*)(uintptr_t)a0, (size_t)a1);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_MUTEX_CREATE (11) a0 = *handleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallMutexCreate(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a0) return STATUS_INVALID_PARAMETER;
|
|
MUTEX* mx = NULL;
|
|
KSTATUS s = KeMutexCreate(&mx);
|
|
if (!KSUCCESS(s)) return s;
|
|
uint32_t h = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&mx->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h); // takes ownership of the ref
|
|
if (!KSUCCESS(s)) return s;
|
|
s = KiCopyHandleToUser(a0, h);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_MUTEX_ACQUIRE (12) a0 = handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallMutexAcquire(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
MUTEX* mx = (MUTEX*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_WAIT);
|
|
if (!mx) return STATUS_NOT_FOUND;
|
|
KSTATUS s = KeAcquireMutex(mx);
|
|
ObDereferenceObject(&mx->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_MUTEX_TRY_ACQUIRE (13) a0 = handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallMutexTryAcquire(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
MUTEX* mx = (MUTEX*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_WAIT);
|
|
if (!mx) return STATUS_NOT_FOUND;
|
|
KSTATUS s = KeTryAcquireMutex(mx);
|
|
ObDereferenceObject(&mx->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_MUTEX_RELEASE (14) a0 = handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallMutexRelease(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
MUTEX* mx = (MUTEX*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_SIGNAL);
|
|
if (!mx) return STATUS_NOT_FOUND;
|
|
KeReleaseMutex(mx);
|
|
ObDereferenceObject(&mx->Header);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SEM_CREATE (15) a0=initial a1=max a2=*handleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSemCreate(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
if (!a2) return STATUS_INVALID_PARAMETER;
|
|
SEMAPHORE* sem = NULL;
|
|
KSTATUS s = KeSemaphoreCreate((int32_t)a0, (int32_t)a1, &sem);
|
|
if (!KSUCCESS(s)) return s;
|
|
uint32_t h = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&sem->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h);
|
|
if (!KSUCCESS(s)) return s;
|
|
s = KiCopyHandleToUser(a2, h);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SEM_WAIT (16) a0 = handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSemWait(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
SEMAPHORE* sem = (SEMAPHORE*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_WAIT);
|
|
if (!sem) return STATUS_NOT_FOUND;
|
|
KSTATUS s = KeWaitSemaphore(sem);
|
|
ObDereferenceObject(&sem->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SEM_TRY_WAIT (17) a0 = handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSemTryWait(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
SEMAPHORE* sem = (SEMAPHORE*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_WAIT);
|
|
if (!sem) return STATUS_NOT_FOUND;
|
|
KSTATUS s = KeTrySemaphore(sem);
|
|
ObDereferenceObject(&sem->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SEM_SIGNAL (18) a0 = handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSemSignal(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
SEMAPHORE* sem = (SEMAPHORE*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_SIGNAL);
|
|
if (!sem) return STATUS_NOT_FOUND;
|
|
KeSignalSemaphore(sem);
|
|
ObDereferenceObject(&sem->Header);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_EVENT_CREATE (19) a0=type a1=initialState a2=*handleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallEventCreate(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
if (!a2 || a0 > EventManualReset) return STATUS_INVALID_PARAMETER;
|
|
EVENT* ev = NULL;
|
|
KSTATUS s = KeEventCreate((EVENT_TYPE)a0, (uint32_t)a1, &ev);
|
|
if (!KSUCCESS(s)) return s;
|
|
uint32_t h = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&ev->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h);
|
|
if (!KSUCCESS(s)) return s;
|
|
s = KiCopyHandleToUser(a2, h);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_EVENT_SIGNAL (20) a0 = handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallEventSignal(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
EVENT* ev = (EVENT*)KiHandleToObject((uint32_t)a0, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL);
|
|
if (!ev) return STATUS_NOT_FOUND;
|
|
KeSignalEvent(ev);
|
|
ObDereferenceObject(&ev->Header);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_EVENT_CLEAR (21) a0 = handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallEventClear(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
EVENT* ev = (EVENT*)KiHandleToObject((uint32_t)a0, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL);
|
|
if (!ev) return STATUS_NOT_FOUND;
|
|
KeClearEvent(ev);
|
|
ObDereferenceObject(&ev->Header);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_EVENT_WAIT a0 = handle, a1 = alertable
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallEventWait(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
EVENT* ev = (EVENT*)KiHandleToObject((uint32_t)a0, ObjectTypeEvent, HANDLE_RIGHT_WAIT);
|
|
if (!ev) return STATUS_NOT_FOUND;
|
|
uint8_t alertable = a1 ? 1 : 0;
|
|
KSTATUS s = KeWaitForEvent(ev, alertable);
|
|
if (alertable) {
|
|
THREAD* thread = KeGetCurrentThread();
|
|
if (thread && thread->UserApcPending)
|
|
thread->UserApcDeliverable = 1;
|
|
}
|
|
ObDereferenceObject(&ev->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_QUEUE_CREATE (23) a0 = *handleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallQueueCreate(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a0) return STATUS_INVALID_PARAMETER;
|
|
QUEUE* q = NULL;
|
|
KSTATUS s = KeQueueCreate(&q);
|
|
if (!KSUCCESS(s)) return s;
|
|
uint32_t h = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&q->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_DUPLICATE | HANDLE_RIGHT_QUERY, &h);
|
|
if (!KSUCCESS(s)) return s;
|
|
s = KiCopyHandleToUser(a0, h);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_QUEUE_SEND (24) a0=handle a1=*msg a2=blocking
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallQueueSend(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
|
|
QUEUE_MESSAGE msg;
|
|
KSTATUS s = CopyFromUser(&msg, (const void*)(uintptr_t)a1, sizeof(msg));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
QUEUE* q = (QUEUE*)KiHandleToObject((uint32_t)a0, ObjectTypeQueue, HANDLE_RIGHT_WRITE);
|
|
if (!q) return STATUS_NOT_FOUND;
|
|
s = KeSendMessage(q, &msg, (bool)a2);
|
|
ObDereferenceObject(&q->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_QUEUE_RECV (25) a0=handle a1=*msg a2=blocking
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallQueueRecv(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
|
|
QUEUE* q = (QUEUE*)KiHandleToObject((uint32_t)a0, ObjectTypeQueue, HANDLE_RIGHT_READ);
|
|
if (!q) return STATUS_NOT_FOUND;
|
|
|
|
QUEUE_MESSAGE msg;
|
|
KSTATUS s = KeReceiveMessage(q, &msg, (bool)a2);
|
|
ObDereferenceObject(&q->Header);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
return CopyToUser((void*)(uintptr_t)a1, &msg, sizeof(msg));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_CLOSE_HANDLE (26) a0 = handle
|
|
//
|
|
// The one and only close path. KeCloseHandle drops the handle-table reference
|
|
// on 'obj'. If that was the last reference, ObDereferenceObject runs the
|
|
// type-specific Destroy callback (KiFileDestroy, etc.) and frees the pool memory.
|
|
// No per-type close syscall needed or wanted.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallCloseHandle(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (!proc) return STATUS_FAILURE;
|
|
KeCloseHandle(proc, (uint32_t)a0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_OPEN_FILE (27) a0=*path a1=*handleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallOpenFile(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_PATH_HANDLE_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (!req.HandleOut) return STATUS_INVALID_PARAMETER;
|
|
|
|
char pathBuf[SYSCALL_STRING_MAX];
|
|
SYS_STRING_VIEW path;
|
|
s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true);
|
|
if (!KSUCCESS(s)) return s;
|
|
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
FILE_OBJECT* file = NULL;
|
|
s = IoOpenFile(path, &file);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
uint32_t h = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&file->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, h);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_READ_FILE (28) a0=handle a1=buf a2=size a3=*bytesReadOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallReadFile(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a4; (void)a5;
|
|
size_t size = (size_t)a2;
|
|
if (size && !a1) return STATUS_INVALID_PARAMETER;
|
|
if (size > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (size == 0) {
|
|
if (a3) return KiCopySizeToUser(a3, 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, size);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
void* kbuf = MmAllocatePool(size);
|
|
if (!kbuf) return STATUS_NO_MEMORY;
|
|
|
|
FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_READ);
|
|
if (!file) { MmFreePool(kbuf); return STATUS_NOT_FOUND; }
|
|
|
|
size_t got = 0;
|
|
s = IoReadFile(file, kbuf, size, &got);
|
|
ObDereferenceObject(&file->Header);
|
|
|
|
if (KSUCCESS(s))
|
|
s = CopyToUser((void*)(uintptr_t)a1, kbuf, got);
|
|
MmFreePool(kbuf);
|
|
|
|
if (KSUCCESS(s) && a3)
|
|
s = KiCopySizeToUser(a3, got);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_WRITE_FILE (29) a0=handle a1=buf a2=size a3=*bytesWrittenOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallWriteFile(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a4; (void)a5;
|
|
size_t size = (size_t)a2;
|
|
if (size && !a1) return STATUS_INVALID_PARAMETER;
|
|
if (size > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (size == 0) {
|
|
if (a3) return KiCopySizeToUser(a3, 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
void* kbuf = MmAllocatePool(size);
|
|
if (!kbuf) return STATUS_NO_MEMORY;
|
|
|
|
KSTATUS s = CopyFromUser(kbuf, (const void*)(uintptr_t)a1, size);
|
|
if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; }
|
|
|
|
FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_WRITE);
|
|
if (!file) { MmFreePool(kbuf); return STATUS_NOT_FOUND; }
|
|
|
|
size_t written = 0;
|
|
s = IoWriteFile(file, kbuf, size, &written);
|
|
ObDereferenceObject(&file->Header);
|
|
MmFreePool(kbuf);
|
|
|
|
if (a3) {
|
|
KSTATUS cs = KiCopySizeToUser(a3, written);
|
|
if (KSUCCESS(s)) s = cs;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static void SyscallUserAsyncKernelRoutine(KAPC* apc,
|
|
void** normalRoutine,
|
|
void** normalContext,
|
|
void** systemArgument1,
|
|
void** systemArgument2) {
|
|
(void)normalRoutine;
|
|
(void)normalContext;
|
|
(void)systemArgument1;
|
|
(void)systemArgument2;
|
|
|
|
SYS_USER_ASYNC_APC_CONTEXT* ctx = CONTAINING_RECORD(apc, SYS_USER_ASYNC_APC_CONTEXT, KernelApc);
|
|
DbgPrint("USER/APC async: kernel routine ctx=0x%llX iosb=0x%llX 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 || req.Reserved) return STATUS_INVALID_PARAMETER;
|
|
if (!req.Iosb) return STATUS_INVALID_PARAMETER;
|
|
if (!req.Buffer && req.Size) return STATUS_INVALID_PARAMETER;
|
|
if (req.Size > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER;
|
|
|
|
s = MmProbeUserWrite(req.Iosb, sizeof(*req.Iosb));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
if (req.Size) {
|
|
s = write ? MmProbeUserRead(req.Buffer, req.Size)
|
|
: MmProbeUserWrite(req.Buffer, req.Size);
|
|
if (!KSUCCESS(s)) return s;
|
|
}
|
|
|
|
if (req.RequestHandleOut) {
|
|
s = MmProbeUserWrite(req.RequestHandleOut, sizeof(*req.RequestHandleOut));
|
|
if (!KSUCCESS(s)) return s;
|
|
}
|
|
|
|
FILE_OBJECT* file =
|
|
(FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, write ? HANDLE_RIGHT_WRITE : HANDLE_RIGHT_READ);
|
|
if (!file) return STATUS_NOT_FOUND;
|
|
|
|
EVENT* event = (EVENT*)0;
|
|
if (req.EventHandle) {
|
|
event = (EVENT*)KiHandleToObject(req.EventHandle, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL);
|
|
if (!event) {
|
|
ObDereferenceObject(&file->Header);
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
KAPC* completionApc = (KAPC*)0;
|
|
SYS_USER_ASYNC_APC_CONTEXT* apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)0;
|
|
if (req.ApcRoutine) {
|
|
THREAD* thread = KeGetCurrentThread();
|
|
if (!thread || !thread->UserApcDispatcher) {
|
|
if (event) ObDereferenceObject(&event->Header);
|
|
ObDereferenceObject(&file->Header);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)MmAllocatePool(sizeof(SYS_USER_ASYNC_APC_CONTEXT));
|
|
if (!apcCtx) {
|
|
if (event) ObDereferenceObject(&event->Header);
|
|
ObDereferenceObject(&file->Header);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
memset(apcCtx, 0, sizeof(*apcCtx));
|
|
ObReferenceObject(&thread->Header);
|
|
apcCtx->TargetThread = thread;
|
|
apcCtx->UserRoutine = req.ApcRoutine;
|
|
apcCtx->UserContext = req.ApcContext;
|
|
KeInitializeApc(&apcCtx->KernelApc,
|
|
thread,
|
|
SyscallUserAsyncKernelRoutine,
|
|
SyscallUserAsyncNormalRoutine,
|
|
apcCtx);
|
|
KeSetApcRundownRoutine(&apcCtx->KernelApc, SyscallUserAsyncApcRundown);
|
|
completionApc = &apcCtx->KernelApc;
|
|
}
|
|
|
|
IO_ASYNC_FILE_REQUEST_OBJECT* asyncReq = (IO_ASYNC_FILE_REQUEST_OBJECT*)0;
|
|
if (write) {
|
|
s = IoWriteFileAsyncKernel(file, req.Buffer, req.Size, req.Offset,
|
|
(IO_STATUS_BLOCK*)req.Iosb, event,
|
|
completionApc,
|
|
&asyncReq);
|
|
} else {
|
|
s = IoReadFileAsyncKernel(file, req.Buffer, req.Size, req.Offset,
|
|
(IO_STATUS_BLOCK*)req.Iosb, event,
|
|
completionApc,
|
|
&asyncReq);
|
|
}
|
|
|
|
if (s == STATUS_PENDING && req.RequestHandleOut && asyncReq) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (!proc) {
|
|
(void)IoCancelAsyncFileRequest(asyncReq);
|
|
ObDereferenceObject(IoAsyncFileRequestObject(asyncReq));
|
|
if (event) ObDereferenceObject(&event->Header);
|
|
ObDereferenceObject(&file->Header);
|
|
return STATUS_FAILURE;
|
|
}
|
|
|
|
uint32_t requestHandle = 0;
|
|
KSTATUS hs = KeAllocateHandleWithRights(proc, IoAsyncFileRequestObject(asyncReq),
|
|
HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE,
|
|
&requestHandle);
|
|
if (!KSUCCESS(hs)) {
|
|
(void)IoCancelAsyncFileRequest(asyncReq);
|
|
ObDereferenceObject(IoAsyncFileRequestObject(asyncReq));
|
|
if (event) ObDereferenceObject(&event->Header);
|
|
ObDereferenceObject(&file->Header);
|
|
return hs;
|
|
}
|
|
|
|
KSTATUS cs = CopyToUser(req.RequestHandleOut, &requestHandle, sizeof(requestHandle));
|
|
if (!KSUCCESS(cs)) {
|
|
(void)IoCancelAsyncFileRequest(asyncReq);
|
|
KeCloseHandle(proc, requestHandle);
|
|
ObDereferenceObject(IoAsyncFileRequestObject(asyncReq));
|
|
if (event) ObDereferenceObject(&event->Header);
|
|
ObDereferenceObject(&file->Header);
|
|
return cs;
|
|
}
|
|
} else if (req.RequestHandleOut) {
|
|
uint32_t zeroHandle = 0;
|
|
KSTATUS cs = CopyToUser(req.RequestHandleOut, &zeroHandle, sizeof(zeroHandle));
|
|
if (!KSUCCESS(cs)) {
|
|
if (asyncReq) ObDereferenceObject(IoAsyncFileRequestObject(asyncReq));
|
|
if (!KSUCCESS(s) && s != STATUS_PENDING && apcCtx)
|
|
SyscallUserAsyncApcContextFree(apcCtx);
|
|
if (event) ObDereferenceObject(&event->Header);
|
|
ObDereferenceObject(&file->Header);
|
|
return cs;
|
|
}
|
|
}
|
|
|
|
if (asyncReq)
|
|
ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); // drop creator reference
|
|
|
|
if (!KSUCCESS(s) && s != STATUS_PENDING && apcCtx)
|
|
SyscallUserAsyncApcContextFree(apcCtx);
|
|
|
|
if (event) ObDereferenceObject(&event->Header);
|
|
ObDereferenceObject(&file->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_READ_FILE_ASYNC (43) a0=*SYS_ASYNC_FILE_REQUEST
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallReadFileAsync(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
return SyscallAsyncFileCommon(a0, false);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_WRITE_FILE_ASYNC (44) a0=*SYS_ASYNC_FILE_REQUEST
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallWriteFileAsync(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
return SyscallAsyncFileCommon(a0, true);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_CANCEL_IO a0 = async request handle
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallCancelIo(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
IO_ASYNC_FILE_REQUEST_OBJECT* req =
|
|
(IO_ASYNC_FILE_REQUEST_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeAsyncIo, HANDLE_RIGHT_TERMINATE);
|
|
if (!req) return STATUS_INVALID_HANDLE;
|
|
|
|
KSTATUS s = IoCancelAsyncFileRequest(req);
|
|
ObDereferenceObject(IoAsyncFileRequestObject(req));
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_CREATE_FILE (30) a0=*path a1=*handleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallCreateFile(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_PATH_HANDLE_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (!req.HandleOut) return STATUS_INVALID_PARAMETER;
|
|
|
|
char pathBuf[SYSCALL_STRING_MAX];
|
|
SYS_STRING_VIEW path;
|
|
s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true);
|
|
if (!KSUCCESS(s)) return s;
|
|
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
FILE_OBJECT* file = NULL;
|
|
s = IoCreateFile(path, &file);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
uint32_t h = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&file->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, h);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_DELETE_FILE (31) a0=*path
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallDeleteFile(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_PATH_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
char pathBuf[SYSCALL_STRING_MAX];
|
|
SYS_STRING_VIEW path;
|
|
s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true);
|
|
if (!KSUCCESS(s)) return s;
|
|
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
return IoDeleteFile(path);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_TRUNCATE_FILE (32) a0=handle a1=newSize
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallTruncateFile(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_WRITE);
|
|
if (!file) return STATUS_NOT_FOUND;
|
|
KSTATUS s = IoTruncateFile(file, (uint64_t)a1);
|
|
ObDereferenceObject(&file->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_GET_FILE_POS (33) a0=handle -> current seek offset
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallGetFilePos(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY);
|
|
if (!file) return (uint64_t)-1;
|
|
uintptr_t flags = KeAcquireSpinlock(&file->Lock);
|
|
uint64_t pos = file->Offset;
|
|
KeReleaseSpinlock(&file->Lock, flags);
|
|
ObDereferenceObject(&file->Header);
|
|
return pos;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SEEK_FILE (34) a0=handle a1=offset
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSeekFile(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY);
|
|
if (!file) return STATUS_NOT_FOUND;
|
|
KSTATUS s = IoSeekFile(file, (uint64_t)a1);
|
|
ObDereferenceObject(&file->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_GET_FILE_SIZE (35) a0=handle -> uint64_t
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallGetFileSize(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY);
|
|
if (!file) return 0;
|
|
uint64_t sz = IoGetFileSize(file);
|
|
ObDereferenceObject(&file->Header);
|
|
return sz;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SCREEN_BLIT (36) a0=x a1=y a2=w a3=h a4=*pixels
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallScreenBlit(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a5;
|
|
if (!gHal || !a4) return STATUS_FAILURE;
|
|
|
|
int w = (int)a2;
|
|
int h = (int)a3;
|
|
if (w <= 0 || h <= 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
size_t pixels = 0;
|
|
size_t bytes = 0;
|
|
if (!KiMulSize((size_t)w, (size_t)h, &pixels) ||
|
|
!KiMulSize(pixels, sizeof(uint32_t), &bytes) ||
|
|
bytes > SYSCALL_BLIT_MAX_BYTES)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t* kpixels = (uint32_t*)MmAllocatePool(bytes);
|
|
if (!kpixels) return STATUS_NO_MEMORY;
|
|
|
|
KSTATUS s = CopyFromUser(kpixels, (const void*)(uintptr_t)a4, bytes);
|
|
if (KSUCCESS(s))
|
|
gHal->Screen.Blit((int)a0, (int)a1, w, h, kpixels);
|
|
|
|
MmFreePool(kpixels);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SCREEN_CLEAR (37) a0=color
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallScreenClear(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!gHal) return STATUS_FAILURE;
|
|
gHal->Screen.Clear((uint32_t)a0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SCREEN_GET_SIZE (38) a0=*widthOut a1=*heightOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallScreenGetSize(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!gHal) return STATUS_FAILURE;
|
|
int w = 0, h = 0;
|
|
gHal->Screen.GetSize(&w, &h);
|
|
if (a0) {
|
|
KSTATUS s = KiCopyIntToUser(a0, w);
|
|
if (!KSUCCESS(s)) return s;
|
|
}
|
|
if (a1) {
|
|
KSTATUS s = KiCopyIntToUser(a1, h);
|
|
if (!KSUCCESS(s)) return s;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SCREEN_PUT_CHAR (39) a0=c a1=x a2=y a3=fg a4=bg
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallScreenPutChar(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a5;
|
|
if (!gHal) return STATUS_FAILURE;
|
|
gHal->Screen.PutChar((char)a0, (int)a1, (int)a2,
|
|
(uint32_t)a3, (uint32_t)a4);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_KB_POLL (40) -> 1 or 0
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallKbPoll(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!gHal) return 0;
|
|
return gHal->Keyboard.Poll();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_KB_IS_KEY_DOWN (41) a0=scancode a1=extended -> 1 or 0
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallKbIsKeyDown(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
// Check gHal and ensure the HAL struct actually has the new function pointer
|
|
if (!gHal || !gHal->Keyboard.IsKeyDown) return 0;
|
|
|
|
return gHal->Keyboard.IsKeyDown((uint8_t)a0, (bool)a1) ? 1u : 0u;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_KB_READ_CHAR (42) -> char (blocks, yields while waiting)
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallKbReadChar(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!gHal) return 0;
|
|
while (!gHal->Keyboard.Poll()) KeYield();
|
|
return (uint64_t)(uint8_t)gHal->Keyboard.ReadChar();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_KB_READ_SCANCODE (43) -> uint8_t (blocks, yields while waiting)
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallKbReadScancode(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!gHal) return 0;
|
|
while (!gHal->Keyboard.Poll()) KeYield();
|
|
return (uint64_t)gHal->Keyboard.ReadScancode();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_KB_GET_MODIFIERS (44) -> uint32_t
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallKbGetModifiers(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!gHal) return 0;
|
|
return (uint64_t)gHal->Keyboard.GetModifiers();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_MOUSE_POLL (45) -> 1 or 0
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallMousePoll(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!gHal) return 0;
|
|
return gHal->Mouse.Poll() ? 1u : 0u;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_MOUSE_READ (46) a0=*dx a1=*dy a2=*buttons -> 1 or 0
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallMouseRead(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
if (!gHal || !a0 || !a1 || !a2) return 0;
|
|
|
|
int dx = 0, dy = 0;
|
|
uint8_t buttons = 0;
|
|
if (!gHal->Mouse.Read(&dx, &dy, &buttons)) return 0;
|
|
|
|
if (!KSUCCESS(CopyToUser((void*)(uintptr_t)a0, &dx, sizeof(dx)))) return 0;
|
|
if (!KSUCCESS(CopyToUser((void*)(uintptr_t)a1, &dy, sizeof(dy)))) return 0;
|
|
if (!KSUCCESS(CopyToUser((void*)(uintptr_t)a2, &buttons, sizeof(buttons)))) return 0;
|
|
return 1u;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_MOUSE_GET_POS (47) a0=*x a1=*y
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallMouseGetPos(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!gHal || !a0 || !a1) return STATUS_INVALID_PARAMETER;
|
|
|
|
int x = 0, y = 0;
|
|
gHal->Mouse.GetPosition(&x, &y);
|
|
KSTATUS s = KiCopyIntToUser(a0, x);
|
|
if (!KSUCCESS(s)) return s;
|
|
return KiCopyIntToUser(a1, y);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_GET_TICK (48) -> uint64_t microseconds
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallGetTick(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
return gHal ? gHal->Timer.QueryTicks() : 0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_DEBUG_PRINT (49) a0=*str
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallDebugPrint(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_DEBUG_PRINT_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (req.Text.Length == 0) return STATUS_SUCCESS;
|
|
if (!req.Text.Data) return STATUS_INVALID_PARAMETER;
|
|
if (req.Text.Length > SYSCALL_TEXT_MAX_BYTES) return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
char chunk[512];
|
|
size_t offset = 0;
|
|
while (offset < req.Text.Length) {
|
|
size_t todo = req.Text.Length - offset;
|
|
if (todo > sizeof(chunk)) todo = sizeof(chunk);
|
|
s = CopyFromUser(chunk, req.Text.Data + offset, todo);
|
|
if (!KSUCCESS(s)) return s;
|
|
DbgPrint("%v", SysSvFromParts(chunk, todo));
|
|
offset += todo;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_QUERY_TRAP_FRAME a0=*SYS_TRAP_FRAME_INFO
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallQueryTrapFrame(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a0) return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t userSize = 0;
|
|
KSTATUS s = CopyFromUser(&userSize, (const void*)(uintptr_t)a0, sizeof(userSize));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
SYS_TRAP_FRAME_INFO_K info;
|
|
memset(&info, 0, sizeof(info));
|
|
info.Size = userSize;
|
|
s = KeQueryCurrentTrapFrame(&info);
|
|
|
|
size_t copySize = (s == STATUS_BUFFER_TOO_SMALL) ? sizeof(info.Size) : sizeof(info);
|
|
KSTATUS cs = CopyToUser((void*)(uintptr_t)a0, &info, copySize);
|
|
if (!KSUCCESS(cs)) return cs;
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_CREATE_PROCESS (50) a0=*path a1=*handleOut a2=*cmdline (optional)
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_CREATE_PROCESS_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (!req.HandleOut) return STATUS_INVALID_PARAMETER;
|
|
|
|
char pathBuf[SYSCALL_STRING_MAX];
|
|
char cmdlineBuf[SYSCALL_STRING_MAX];
|
|
SYS_STRING_VIEW path;
|
|
SYS_STRING_VIEW cmdlineView;
|
|
|
|
s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true);
|
|
if (!KSUCCESS(s)) return s;
|
|
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
s = KiImportUserTextView(req.CommandLine, cmdlineBuf, sizeof(cmdlineBuf), &cmdlineView, true);
|
|
if (!KSUCCESS(s)) return s;
|
|
s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
// Read the executable from disk.
|
|
FILE_OBJECT* f;
|
|
s = IoOpenFile(path, &f);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
uint64_t size = IoGetFileSize(f);
|
|
void* mem = MmAllocatePool(size);
|
|
if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; }
|
|
|
|
size_t got;
|
|
s = IoReadFile(f, mem, size, &got);
|
|
IoCloseFile(f);
|
|
if (!KSUCCESS(s)) { MmFreePool(mem); return s; }
|
|
|
|
// Create the process object.
|
|
PROCESS* proc;
|
|
s = KeCreateProcess(pathBuf, &proc);
|
|
if (!KSUCCESS(s)) { MmFreePool(mem); return s; }
|
|
|
|
// 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)) { MmFreePool(mem); ObDereferenceObject(&proc->Header); return s; }
|
|
|
|
// Map the PE image into memory.
|
|
uintptr_t imageBase = 0;
|
|
s = LdrLoadImage(mem, size, &imageBase, true);
|
|
MmFreePool(mem);
|
|
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
|
|
|
|
s = LdrResolveImportsForProcess(proc, imageBase);
|
|
if (!KSUCCESS(s)) {
|
|
MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
|
|
ObDereferenceObject(&proc->Header);
|
|
return s;
|
|
}
|
|
|
|
s = LdrProtectImage(imageBase, true);
|
|
if (!KSUCCESS(s)) {
|
|
MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
|
|
ObDereferenceObject(&proc->Header);
|
|
return s;
|
|
}
|
|
|
|
proc->ImageBase = imageBase;
|
|
proc->ImageSize = LdrGetImageSize(imageBase);
|
|
|
|
PROCESS* caller = KeGetCurrentProcess();
|
|
if (!caller) {
|
|
MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
|
|
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;
|
|
MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
|
|
ObDereferenceObject(&proc->Header);
|
|
return s;
|
|
}
|
|
|
|
THREAD* thread;
|
|
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
|
s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread);
|
|
if (!KSUCCESS(s)) {
|
|
KeCloseHandle(caller, handle);
|
|
ObDereferenceObject(&proc->Header);
|
|
return s;
|
|
}
|
|
|
|
s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle);
|
|
if (!KSUCCESS(s)) {
|
|
KeTerminateProcess(proc, -1);
|
|
KeCloseHandle(caller, handle);
|
|
ObDereferenceObject(&proc->Header);
|
|
return s;
|
|
}
|
|
|
|
// Drop the creator's local reference. The returned handle and live thread
|
|
// references now own the process lifetime.
|
|
ObDereferenceObject(&proc->Header);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_WAIT_FOR_PROCESS (51) a0=handle
|
|
// Blocks until the target process signals its ExitEvent.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallWaitForProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
PROCESS* proc = (PROCESS*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_WAIT);
|
|
if (!proc) return STATUS_INVALID_HANDLE;
|
|
|
|
KSTATUS s = KeWaitForEvent(proc->ExitEvent, 0);
|
|
ObDereferenceObject(&proc->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_GET_PROCESS_EXIT_CODE (52) a0=handle a1=*exitCodeOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallGetProcessExitCode(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
|
|
PROCESS* proc = (PROCESS*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_QUERY);
|
|
if (!proc) return STATUS_INVALID_HANDLE;
|
|
|
|
int exitCode = 0;
|
|
KSTATUS s = KeGetProcessExitCode(proc, &exitCode);
|
|
ObDereferenceObject(&proc->Header);
|
|
if (!KSUCCESS(s)) return s;
|
|
return KiCopyIntToUser(a1, exitCode);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_WRITE_CONSOLE (53) a0=*buf a1=len
|
|
// Writes user console text in white; distinct from green SYS_DEBUG_PRINT.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallWriteConsole(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_WRITE_CONSOLE_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (req.Text.Length == 0) return STATUS_SUCCESS;
|
|
if (!req.Text.Data) return STATUS_INVALID_PARAMETER;
|
|
if (req.Text.Length > SYSCALL_TEXT_MAX_BYTES) return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
char chunk[512];
|
|
size_t offset = 0;
|
|
while (offset < req.Text.Length) {
|
|
size_t todo = req.Text.Length - offset;
|
|
if (todo > sizeof(chunk)) todo = sizeof(chunk);
|
|
s = CopyFromUser(chunk, req.Text.Data + offset, todo);
|
|
if (!KSUCCESS(s)) return s;
|
|
KePrintConsole(chunk, todo);
|
|
offset += todo;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_OPEN_DIR (54) a0=*dirPath a1=*handleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallOpenDir(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_PATH_HANDLE_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (!req.HandleOut) return STATUS_INVALID_PARAMETER;
|
|
|
|
char dirPathBuf[SYSCALL_STRING_MAX];
|
|
SYS_STRING_VIEW dirPath;
|
|
s = KiImportUserTextView(req.Path, dirPathBuf, sizeof(dirPathBuf), &dirPath, true);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
DIR_OBJECT* dir = NULL;
|
|
s = IoOpenDir(dirPath, &dir);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
uint32_t handle = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&dir->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_QUERY, &handle);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
// KiAllocHandleForCurrentWithRights consumed IoOpenDir's creator reference;
|
|
// the returned process handle now owns the directory object.
|
|
|
|
s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, handle);
|
|
return s;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_READ_DIR (55) a0=handle a1=*KDIR_ENTRY
|
|
// Returns STATUS_NO_MORE_FILES when the directory is exhausted.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallReadDir(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
|
|
DIR_OBJECT* dir = (DIR_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeDir, HANDLE_RIGHT_READ);
|
|
if (!dir) return STATUS_INVALID_PARAMETER;
|
|
|
|
KDIR_ENTRY entry;
|
|
KSTATUS s = IoReadDir(dir, &entry);
|
|
ObDereferenceObject(&dir->Header);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
return CopyToUser((void*)(uintptr_t)a1, &entry, sizeof(entry));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SET_CWD (56) a0=*path
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSetCwd(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_PATH_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
char pathBuf[SYSCALL_STRING_MAX];
|
|
SYS_STRING_VIEW path;
|
|
s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true);
|
|
if (!KSUCCESS(s)) return s;
|
|
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
return IoSetCwd(path);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_GET_CWD (57) a0=*buf a1=size
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallGetCwd(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_GET_CWD_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (!req.Buffer && req.Capacity) return STATUS_INVALID_PARAMETER;
|
|
|
|
char cwd[SYSCALL_STRING_MAX];
|
|
s = IoGetCwd(cwd, sizeof(cwd));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
size_t len = strlen(cwd);
|
|
if (req.LengthOut) {
|
|
s = CopyToUser(req.LengthOut, &len, sizeof(len));
|
|
if (!KSUCCESS(s)) return s;
|
|
}
|
|
if (len > req.Capacity) return STATUS_BUFFER_TOO_SMALL;
|
|
if (len) return CopyToUser(req.Buffer, cwd, len);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_WAIT_THREAD (58) a0=tid
|
|
// Blocks the caller until the thread with the given TID exits.
|
|
// Searches the current process's thread list; cross-process join is not
|
|
// supported in v1 (POSIX pthread_join is always intra-process).
|
|
// Returns STATUS_NOT_FOUND if the TID is unknown (already reaped or never
|
|
// existed) - treat the same as a successful join at the POSIX layer.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallWaitThread(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
uint32_t targetTid = (uint32_t)a0;
|
|
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (!proc) return STATUS_FAILURE;
|
|
|
|
// Walk the thread list under the spinlock to find the target, then bump
|
|
// its ExitEvent refcount so the event survives even if the thread exits
|
|
// and is destroyed while we wait.
|
|
EVENT* exitEvent = (EVENT*)0;
|
|
uintptr_t flags = KeAcquireSpinlock(&proc->HandleLock);
|
|
LIST_ENTRY* entry = proc->ThreadList.Fwd;
|
|
while (entry != &proc->ThreadList) {
|
|
THREAD* t = CONTAINING_RECORD(entry, THREAD, ProcessLink);
|
|
if (t->ThreadId == targetTid && t->ExitEvent) {
|
|
exitEvent = t->ExitEvent;
|
|
ObReferenceObject(&exitEvent->Header); // keep event alive
|
|
break;
|
|
}
|
|
entry = entry->Fwd;
|
|
}
|
|
KeReleaseSpinlock(&proc->HandleLock, flags);
|
|
|
|
if (!exitEvent) {
|
|
// TID not found - either already exited and reaped, or invalid.
|
|
// Treat as success at the POSIX layer (join on an already-dead thread).
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
KSTATUS s = KeWaitForEvent(exitEvent, 0);
|
|
ObDereferenceObject(&exitEvent->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_GET_PROCESS_PID (59) a0=procHandle a1=*pidOut
|
|
// Returns the numeric PID of a process identified by handle.
|
|
// Used by posix_spawn to map the kernel handle back to a pid_t for waitpid.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallGetProcessPid(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
|
|
PROCESS* proc = (PROCESS*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_QUERY);
|
|
if (!proc) return STATUS_INVALID_HANDLE;
|
|
|
|
uint32_t pid = proc->ProcessId;
|
|
ObDereferenceObject(&proc->Header);
|
|
return KiCopyU32ToUser(a1, pid);
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Named Queue Registry
|
|
//
|
|
// A flat, fixed-size table of { name -> QUEUE* } pairs protected by a
|
|
// spinlock. The registry holds one reference to each published queue for
|
|
// the lifetime of the entry, so the queue object cannot be destroyed while
|
|
// it is registered even if every process closes its own handle to it.
|
|
//
|
|
// Limits: NAMED_QUEUE_MAX names, each up to NAMED_QUEUE_NAME_MAX bytes.
|
|
// These are generous for the intended usage (a handful of system servers).
|
|
// ===========================================================================
|
|
|
|
#define NAMED_QUEUE_MAX 16
|
|
#define NAMED_QUEUE_NAME_MAX 64
|
|
|
|
typedef struct {
|
|
char Name[NAMED_QUEUE_NAME_MAX];
|
|
size_t NameLength;
|
|
QUEUE* Queue; // NULL = slot free; non-NULL = occupied
|
|
uint32_t PublisherPid; // ProcessId of whoever called SysPublishQueue
|
|
} NAMED_QUEUE_ENTRY;
|
|
|
|
static NAMED_QUEUE_ENTRY gNamedQueues[NAMED_QUEUE_MAX];
|
|
static SPINLOCK gNamedQueueLock;
|
|
|
|
static int KiNamedQueueNameEquals(const NAMED_QUEUE_ENTRY* entry,
|
|
SYS_STRING_VIEW name) {
|
|
if (!entry || !entry->Queue) return 0;
|
|
return SysSvEqual(SysSvFromParts(entry->Name, entry->NameLength), name);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_PUBLISH_QUEUE (60) a0=*name a1=queueHandle
|
|
//
|
|
// Registers the queue identified by 'queueHandle' under 'name'.
|
|
// Fails with STATUS_ALREADY_EXISTS if the name is already taken.
|
|
// The registry bumps the queue's refcount so it outlives the handle.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallPublishQueue(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_PUBLISH_QUEUE_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
char name[NAMED_QUEUE_NAME_MAX];
|
|
SYS_STRING_VIEW nameView;
|
|
s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false);
|
|
if (!KSUCCESS(s)) return s;
|
|
if (nameView.Length == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
QUEUE* q = (QUEUE*)KiHandleToObject(req.QueueHandle, ObjectTypeQueue, HANDLE_RIGHT_DUPLICATE);
|
|
if (!q) return STATUS_INVALID_HANDLE;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock);
|
|
|
|
for (int i = 0; i < NAMED_QUEUE_MAX; i++) {
|
|
if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) {
|
|
KeReleaseSpinlock(&gNamedQueueLock, flags);
|
|
ObDereferenceObject(&q->Header);
|
|
return STATUS_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < NAMED_QUEUE_MAX; i++) {
|
|
if (!gNamedQueues[i].Queue) {
|
|
memcpy(gNamedQueues[i].Name, name, nameView.Length);
|
|
gNamedQueues[i].NameLength = nameView.Length;
|
|
gNamedQueues[i].Queue = q;
|
|
gNamedQueues[i].PublisherPid = KeGetCurrentProcess()->ProcessId;
|
|
KeReleaseSpinlock(&gNamedQueueLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinlock(&gNamedQueueLock, flags);
|
|
ObDereferenceObject(&q->Header);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_OPEN_NAMED_QUEUE (61) a0=*name a1=*handleOut
|
|
//
|
|
// Looks up 'name' in the registry and allocates a handle to the queue in the
|
|
// calling process's handle table. Returns STATUS_NOT_FOUND if no match.
|
|
// Callers that race against the publisher should retry on NOT_FOUND.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallOpenNamedQueue(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_OPEN_NAMED_QUEUE_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (!req.HandleOut) return STATUS_INVALID_PARAMETER;
|
|
|
|
char name[NAMED_QUEUE_NAME_MAX];
|
|
SYS_STRING_VIEW nameView;
|
|
s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false);
|
|
if (!KSUCCESS(s)) return s;
|
|
if (nameView.Length == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock);
|
|
|
|
QUEUE* q = NULL;
|
|
for (int i = 0; i < NAMED_QUEUE_MAX; i++) {
|
|
if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) {
|
|
q = gNamedQueues[i].Queue;
|
|
ObReferenceObject(&q->Header);
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinlock(&gNamedQueueLock, flags);
|
|
|
|
if (!q) return STATUS_NOT_FOUND;
|
|
|
|
uint32_t handle = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&q->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_DUPLICATE | HANDLE_RIGHT_QUERY, &handle);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, handle);
|
|
return s;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_QUEUE_DUPLICATE (62) a0=handle a1=targetPid a2=*handleOut
|
|
//
|
|
// Looks up the QUEUE behind 'handle' in the CALLER's table, then allocates
|
|
// a new handle for the same object in the TARGET process's table.
|
|
// Both processes share the same underlying QUEUE (refcount bumped).
|
|
// The caller's own handle is unchanged.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallQueueDuplicate(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
if (!a2) return STATUS_INVALID_PARAMETER;
|
|
|
|
QUEUE* q = (QUEUE*)KiHandleToObject((uint32_t)a0, ObjectTypeQueue, HANDLE_RIGHT_DUPLICATE);
|
|
if (!q) return STATUS_NOT_FOUND;
|
|
|
|
PROCESS* target = KeGetProcessById((uint32_t)a1);
|
|
if (!target) {
|
|
ObDereferenceObject(&q->Header);
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
uint32_t newHandle = 0;
|
|
KSTATUS s = KeAllocateHandleWithRights(target, &q->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_DUPLICATE | HANDLE_RIGHT_QUERY, &newHandle);
|
|
if (KSUCCESS(s)) {
|
|
KSTATUS cs = KiCopyHandleToUser(a2, newHandle);
|
|
if (!KSUCCESS(cs)) {
|
|
KeCloseHandle(target, newHandle);
|
|
s = cs;
|
|
}
|
|
}
|
|
|
|
ObDereferenceObject(&target->Header);
|
|
ObDereferenceObject(&q->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_NAMED_QUEUE_OWNER_PID (63) a0=*name a1=*pidOut
|
|
//
|
|
// Returns the ProcessId of whoever published the named queue.
|
|
// Lets a client get the publisher's PID so it can duplicate one of its
|
|
// own handles into that process via SYS_QUEUE_DUPLICATE.
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallNamedQueueOwnerPid(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SYS_NAMED_QUEUE_OWNER_REQUEST req;
|
|
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (!req.PidOut) return STATUS_INVALID_PARAMETER;
|
|
|
|
char name[NAMED_QUEUE_NAME_MAX];
|
|
SYS_STRING_VIEW nameView;
|
|
s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false);
|
|
if (!KSUCCESS(s)) return s;
|
|
if (nameView.Length == 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t pid = 0;
|
|
uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock);
|
|
for (int i = 0; i < NAMED_QUEUE_MAX; i++) {
|
|
if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) {
|
|
pid = gNamedQueues[i].PublisherPid;
|
|
KeReleaseSpinlock(&gNamedQueueLock, flags);
|
|
return KiCopyU32ToUser((uint64_t)(uintptr_t)req.PidOut, pid);
|
|
}
|
|
}
|
|
KeReleaseSpinlock(&gNamedQueueLock, flags);
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
static uint64_t SyscallKillProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
PROCESS* proc = KeGetProcessById((uint32_t)a0);
|
|
if (!proc) return STATUS_NOT_FOUND;
|
|
THREAD* self = KeGetCurrentThread();
|
|
int exitCode = (int)a1;
|
|
if (self && self->Owner == proc) {
|
|
KeExitProcess(proc, exitCode);
|
|
ObDereferenceObject(&proc->Header);
|
|
KeExitThread(self, exitCode);
|
|
while (true) ;
|
|
}
|
|
KSTATUS s = KeTerminateProcess(proc, exitCode);
|
|
ObDereferenceObject(&proc->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_GET_PROCESS_LIST (65) a0=*pidsOut a1=maxCount a2=*countOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallGetProcessList(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
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 = KeCopyProcessIds(NULL, 0);
|
|
size_t toCopy = total < maxCount ? total : maxCount;
|
|
|
|
if (toCopy) {
|
|
size_t bytes = 0;
|
|
if (!KiMulSize(toCopy, sizeof(uint32_t), &bytes)) return STATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t* tmp = (uint32_t*)MmAllocatePool(bytes);
|
|
if (!tmp) return STATUS_NO_MEMORY;
|
|
|
|
size_t copied = KeCopyProcessIds(tmp, toCopy);
|
|
if (copied < toCopy) toCopy = copied;
|
|
|
|
KSTATUS s = CopyToUser((void*)(uintptr_t)a0, tmp, toCopy * sizeof(uint32_t));
|
|
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;
|
|
uint32_t Reserved;
|
|
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;
|
|
uint32_t Reserved;
|
|
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;
|
|
uint32_t Reserved;
|
|
void* Value;
|
|
size_t ValueSize;
|
|
size_t* ActualSizeOut;
|
|
} SYS_SOCKET_OPTION_REQUEST_K;
|
|
|
|
typedef struct SYS_MEMORY_STATUS_K {
|
|
uint64_t FreePhysicalBytes;
|
|
uint64_t KernelPoolBytes;
|
|
uint64_t UserHeapBytes;
|
|
} SYS_MEMORY_STATUS_K;
|
|
|
|
#define SYS_POLL_MAX_FDS 64u
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SOCKET a0=domain a1=type a2=protocol a3=*handleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSocket(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a4; (void)a5;
|
|
if (!a3) return STATUS_INVALID_PARAMETER;
|
|
|
|
SOCKET_OBJECT* socket = NULL;
|
|
KSTATUS s = NetSocketCreate((uint32_t)a0, (uint32_t)a1, (uint32_t)a2, &socket);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
uint32_t h = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&socket->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
// KiAllocHandleForCurrentWithRights consumed NetSocketCreate's creator
|
|
// reference; the returned process handle now owns the socket object.
|
|
|
|
s = KiCopyHandleToUser(a3, h);
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_BIND a0=socketHandle a1=*SYS_SOCKET_ADDRESS_IPV4 a2=addressSize
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallBind(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
if ((size_t)a2 < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
SYS_SOCKET_ADDRESS_IPV4_K address;
|
|
KSTATUS s = CopyFromUser(&address, (const void*)(uintptr_t)a1, sizeof(address));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
|
|
if (!socket) return STATUS_NOT_FOUND;
|
|
|
|
s = NetSocketBind(socket, &address, (size_t)a2);
|
|
ObDereferenceObject(&socket->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_LISTEN a0=socketHandle a1=backlog
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallListen(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
|
|
if (!socket) return STATUS_NOT_FOUND;
|
|
|
|
KSTATUS s = NetSocketListen(socket, (uint32_t)a1);
|
|
ObDereferenceObject(&socket->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_ACCEPT a0=listenSocket a1=*remoteAddressOut(optional)
|
|
// a2=*remoteAddressSizeInOut(optional) a3=*acceptedHandleOut
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallAccept(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a4; (void)a5;
|
|
if (!a3) return STATUS_INVALID_PARAMETER;
|
|
|
|
bool wantRemote = (a1 != 0);
|
|
size_t remoteCapacity = 0;
|
|
if (wantRemote) {
|
|
if (!a2) return STATUS_INVALID_PARAMETER;
|
|
KSTATUS cs = CopyFromUser(&remoteCapacity, (const void*)(uintptr_t)a2, sizeof(remoteCapacity));
|
|
if (!KSUCCESS(cs)) return cs;
|
|
if (remoteCapacity < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) {
|
|
size_t required = sizeof(SYS_SOCKET_ADDRESS_IPV4_K);
|
|
(void)CopyToUser((void*)(uintptr_t)a2, &required, sizeof(required));
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
cs = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(SYS_SOCKET_ADDRESS_IPV4_K));
|
|
if (!KSUCCESS(cs)) return cs;
|
|
}
|
|
|
|
SOCKET_OBJECT* listener = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
|
|
if (!listener) return STATUS_NOT_FOUND;
|
|
|
|
SOCKET_OBJECT* accepted = NULL;
|
|
SYS_SOCKET_ADDRESS_IPV4_K remote;
|
|
KSTATUS s = NetSocketAccept(listener, &accepted, wantRemote ? &remote : NULL);
|
|
ObDereferenceObject(&listener->Header);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
uint32_t h = 0;
|
|
s = KiAllocHandleForCurrentWithRights(&accepted->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
// KiAllocHandleForCurrentWithRights consumed the accept creator reference;
|
|
// the returned process handle owns the accepted socket from this point.
|
|
|
|
if (wantRemote) {
|
|
size_t actual = sizeof(SYS_SOCKET_ADDRESS_IPV4_K);
|
|
s = CopyToUser((void*)(uintptr_t)a1, &remote, sizeof(remote));
|
|
if (KSUCCESS(s))
|
|
s = CopyToUser((void*)(uintptr_t)a2, &actual, sizeof(actual));
|
|
}
|
|
|
|
if (KSUCCESS(s))
|
|
s = KiCopyHandleToUser(a3, h);
|
|
|
|
if (!KSUCCESS(s)) {
|
|
PROCESS* proc = KeGetCurrentProcess();
|
|
if (proc) KeCloseHandle(proc, h);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_CONNECT a0=socketHandle a1=*SYS_SOCKET_ADDRESS_IPV4 a2=addressSize
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallConnect(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
if ((size_t)a2 < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
SYS_SOCKET_ADDRESS_IPV4_K address;
|
|
KSTATUS s = CopyFromUser(&address, (const void*)(uintptr_t)a1, sizeof(address));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
|
|
if (!socket) return STATUS_NOT_FOUND;
|
|
|
|
s = NetSocketConnect(socket, &address, (size_t)a2);
|
|
ObDereferenceObject(&socket->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SEND a0=socketHandle a1=*buffer a2=length a3=flags a4=*sentOut(optional)
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSend(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a5;
|
|
size_t length = (size_t)a2;
|
|
if (length && !a1) return STATUS_INVALID_PARAMETER;
|
|
if (length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (length == 0) {
|
|
if (a4) return KiCopySizeToUser(a4, 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
void* kbuf = MmAllocatePool(length);
|
|
if (!kbuf) return STATUS_NO_MEMORY;
|
|
|
|
KSTATUS s = CopyFromUser(kbuf, (const void*)(uintptr_t)a1, length);
|
|
if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; }
|
|
|
|
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
|
|
if (!socket) { MmFreePool(kbuf); return STATUS_NOT_FOUND; }
|
|
|
|
size_t sent = 0;
|
|
s = NetSocketSend(socket, kbuf, length, (uint32_t)a3, &sent);
|
|
ObDereferenceObject(&socket->Header);
|
|
MmFreePool(kbuf);
|
|
|
|
if (a4) {
|
|
KSTATUS cs = KiCopySizeToUser(a4, sent);
|
|
if (KSUCCESS(s)) s = cs;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_RECV a0=socketHandle a1=*buffer a2=length a3=flags a4=*receivedOut(optional)
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallRecv(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a5;
|
|
size_t length = (size_t)a2;
|
|
if (length && !a1) return STATUS_INVALID_PARAMETER;
|
|
if (length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (length == 0) {
|
|
if (a4) return KiCopySizeToUser(a4, 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, length);
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
void* kbuf = MmAllocatePool(length);
|
|
if (!kbuf) return STATUS_NO_MEMORY;
|
|
|
|
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
|
|
if (!socket) { MmFreePool(kbuf); return STATUS_NOT_FOUND; }
|
|
|
|
size_t received = 0;
|
|
s = NetSocketReceive(socket, kbuf, length, (uint32_t)a3, &received);
|
|
ObDereferenceObject(&socket->Header);
|
|
|
|
if (KSUCCESS(s) && received)
|
|
s = CopyToUser((void*)(uintptr_t)a1, kbuf, received);
|
|
MmFreePool(kbuf);
|
|
|
|
if (KSUCCESS(s) && a4)
|
|
s = KiCopySizeToUser(a4, received);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SHUTDOWN a0=socketHandle a1=SYS_SHUTDOWN_*
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallShutdown(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
|
|
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
|
|
if (!socket) return STATUS_NOT_FOUND;
|
|
|
|
KSTATUS s = NetSocketShutdown(socket, (uint32_t)a1);
|
|
ObDereferenceObject(&socket->Header);
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_SEND_TO a0=socketHandle a1=*SYS_SOCKET_SEND_TO_REQUEST
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallSendTo(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
|
|
SYS_SOCKET_SEND_TO_REQUEST_K req;
|
|
KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (req.Reserved != 0) return STATUS_INVALID_PARAMETER;
|
|
if (req.Length && !req.Buffer) return STATUS_INVALID_PARAMETER;
|
|
if (!req.RemoteAddress) return STATUS_INVALID_PARAMETER;
|
|
if (req.RemoteAddressSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) return STATUS_BUFFER_TOO_SMALL;
|
|
if (req.Length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER;
|
|
|
|
SYS_SOCKET_ADDRESS_IPV4_K remote;
|
|
s = CopyFromUser(&remote, req.RemoteAddress, sizeof(remote));
|
|
if (!KSUCCESS(s)) return s;
|
|
|
|
void* kbuf = NULL;
|
|
if (req.Length) {
|
|
kbuf = MmAllocatePool(req.Length);
|
|
if (!kbuf) return STATUS_NO_MEMORY;
|
|
s = CopyFromUser(kbuf, req.Buffer, req.Length);
|
|
if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; }
|
|
}
|
|
|
|
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
|
|
if (!socket) { if (kbuf) MmFreePool(kbuf); return STATUS_NOT_FOUND; }
|
|
|
|
size_t sent = 0;
|
|
s = NetSocketSendTo(socket, kbuf, req.Length, req.Flags, &remote, req.RemoteAddressSize, &sent);
|
|
ObDereferenceObject(&socket->Header);
|
|
if (kbuf) MmFreePool(kbuf);
|
|
|
|
if (req.SentOut) {
|
|
KSTATUS cs = CopyToUser(req.SentOut, &sent, sizeof(sent));
|
|
if (KSUCCESS(s)) s = cs;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SYS_RECV_FROM a0=socketHandle a1=*SYS_SOCKET_RECV_FROM_REQUEST
|
|
// ---------------------------------------------------------------------------
|
|
static uint64_t SyscallRecvFrom(uint64_t a0, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
|
(void)a2; (void)a3; (void)a4; (void)a5;
|
|
if (!a1) return STATUS_INVALID_PARAMETER;
|
|
|
|
SYS_SOCKET_RECV_FROM_REQUEST_K req;
|
|
KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req));
|
|
if (!KSUCCESS(s)) return s;
|
|
if (req.Reserved != 0) return STATUS_INVALID_PARAMETER;
|
|
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));
|
|
}
|
|
|
|
// ===========================================================================
|
|
// 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 */ SyscallAlloc,
|
|
/* 18 SYS_FREE */ SyscallFree,
|
|
/* 19 SYS_REALLOC */ SyscallRealloc,
|
|
/* 20 SYS_CLOSE_HANDLE */ SyscallCloseHandle,
|
|
/* 21 SYS_MUTEX_CREATE */ SyscallMutexCreate,
|
|
/* 22 SYS_MUTEX_ACQUIRE */ SyscallMutexAcquire,
|
|
/* 23 SYS_MUTEX_TRY_ACQUIRE */ SyscallMutexTryAcquire,
|
|
/* 24 SYS_MUTEX_RELEASE */ SyscallMutexRelease,
|
|
/* 25 SYS_SEM_CREATE */ SyscallSemCreate,
|
|
/* 26 SYS_SEM_WAIT */ SyscallSemWait,
|
|
/* 27 SYS_SEM_TRY_WAIT */ SyscallSemTryWait,
|
|
/* 28 SYS_SEM_SIGNAL */ SyscallSemSignal,
|
|
/* 29 SYS_EVENT_CREATE */ SyscallEventCreate,
|
|
/* 30 SYS_EVENT_SIGNAL */ SyscallEventSignal,
|
|
/* 31 SYS_EVENT_CLEAR */ SyscallEventClear,
|
|
/* 32 SYS_EVENT_WAIT */ SyscallEventWait,
|
|
/* 33 SYS_QUEUE_CREATE */ SyscallQueueCreate,
|
|
/* 34 SYS_QUEUE_SEND */ SyscallQueueSend,
|
|
/* 35 SYS_QUEUE_RECV */ SyscallQueueRecv,
|
|
/* 36 SYS_PUBLISH_QUEUE */ SyscallPublishQueue,
|
|
/* 37 SYS_OPEN_NAMED_QUEUE */ SyscallOpenNamedQueue,
|
|
/* 38 SYS_QUEUE_DUPLICATE */ SyscallQueueDuplicate,
|
|
/* 39 SYS_NAMED_QUEUE_OWNER_PID */ SyscallNamedQueueOwnerPid,
|
|
/* 40 SYS_OPEN_FILE */ SyscallOpenFile,
|
|
/* 41 SYS_CREATE_FILE */ SyscallCreateFile,
|
|
/* 42 SYS_READ_FILE */ SyscallReadFile,
|
|
/* 43 SYS_WRITE_FILE */ SyscallWriteFile,
|
|
/* 44 SYS_READ_FILE_ASYNC */ SyscallReadFileAsync,
|
|
/* 45 SYS_WRITE_FILE_ASYNC */ SyscallWriteFileAsync,
|
|
/* 46 SYS_CANCEL_IO */ SyscallCancelIo,
|
|
/* 47 SYS_SEEK_FILE */ SyscallSeekFile,
|
|
/* 48 SYS_GET_FILE_POS */ SyscallGetFilePos,
|
|
/* 49 SYS_GET_FILE_SIZE */ SyscallGetFileSize,
|
|
/* 50 SYS_TRUNCATE_FILE */ SyscallTruncateFile,
|
|
/* 51 SYS_DELETE_FILE */ SyscallDeleteFile,
|
|
/* 52 SYS_RENAME_FILE */ SyscallRenameFile,
|
|
/* 53 SYS_OPEN_DIR */ SyscallOpenDir,
|
|
/* 54 SYS_READ_DIR */ SyscallReadDir,
|
|
/* 55 SYS_CREATE_DIR */ SyscallCreateDir,
|
|
/* 56 SYS_REMOVE_DIR */ SyscallRemoveDir,
|
|
/* 57 SYS_SET_CWD */ SyscallSetCwd,
|
|
/* 58 SYS_GET_CWD */ SyscallGetCwd,
|
|
/* 59 SYS_WRITE_CONSOLE */ SyscallWriteConsole,
|
|
/* 60 SYS_SCREEN_BLIT */ SyscallScreenBlit,
|
|
/* 61 SYS_SCREEN_CLEAR */ SyscallScreenClear,
|
|
/* 62 SYS_SCREEN_GET_SIZE */ SyscallScreenGetSize,
|
|
/* 63 SYS_SCREEN_PUT_CHAR */ SyscallScreenPutChar,
|
|
/* 64 SYS_KB_POLL */ SyscallKbPoll,
|
|
/* 65 SYS_KB_IS_KEY_DOWN */ SyscallKbIsKeyDown,
|
|
/* 66 SYS_KB_READ_CHAR */ SyscallKbReadChar,
|
|
/* 67 SYS_KB_READ_SCANCODE */ SyscallKbReadScancode,
|
|
/* 68 SYS_KB_GET_MODIFIERS */ SyscallKbGetModifiers,
|
|
/* 69 SYS_MOUSE_POLL */ SyscallMousePoll,
|
|
/* 70 SYS_MOUSE_READ */ SyscallMouseRead,
|
|
/* 71 SYS_MOUSE_GET_POS */ SyscallMouseGetPos,
|
|
/* 72 SYS_GET_TICK */ SyscallGetTick,
|
|
/* 73 SYS_DEBUG_PRINT */ SyscallDebugPrint,
|
|
/* 74 SYS_QUERY_TRAP_FRAME */ SyscallQueryTrapFrame,
|
|
/* 75 SYS_SOCKET */ SyscallSocket,
|
|
/* 76 SYS_BIND */ SyscallBind,
|
|
/* 77 SYS_LISTEN */ SyscallListen,
|
|
/* 78 SYS_ACCEPT */ SyscallAccept,
|
|
/* 79 SYS_CONNECT */ SyscallConnect,
|
|
/* 80 SYS_SEND */ SyscallSend,
|
|
/* 81 SYS_RECV */ SyscallRecv,
|
|
/* 82 SYS_SHUTDOWN */ SyscallShutdown,
|
|
/* 83 SYS_SEND_TO */ SyscallSendTo,
|
|
/* 84 SYS_RECV_FROM */ SyscallRecvFrom,
|
|
/* 85 SYS_RESOLVE_HOST */ SyscallResolveHost,
|
|
/* 86 SYS_SET_SOCKET_OPTION */ SyscallSetSocketOption,
|
|
/* 87 SYS_GET_SOCKET_OPTION */ SyscallGetSocketOption,
|
|
/* 88 SYS_POLL */ SyscallPoll,
|
|
/* 89 SYS_GET_MEMORY_STATUS */ SyscallGetMemoryStatus
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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;
|
|
return gSyscallTable[nr](a0, a1, a2, a3, a4, a5);
|
|
}
|