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

767 lines
35 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. FileDestroy]
// -> MmFreePool(obj)
//
// There is no separate SyscallCloseFile. Removing that stub was the right
// call: the object model already handled it; the dedicated syscall was only
// adding a footgun where callers could bypass the unified refcount path.
#include <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_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>
// ---------------------------------------------------------------------------
// HAL pointer - set once at boot by KeSyscallSetHal()
// ---------------------------------------------------------------------------
static const HAL_API* gHal;
void KeSyscallSetHal(const HAL_API* hal) { gHal = hal; }
// ---------------------------------------------------------------------------
// Helper: get the current process, or NULL if unavailable.
// ---------------------------------------------------------------------------
static inline PROCESS* CurrentProcess(void) {
THREAD* t = KeCurrentThread();
return t ? t->Owner : NULL;
}
// ---------------------------------------------------------------------------
// Helper: look up a handle and type-check it in one call.
// Bumps the refcount on success; caller must ObDereferenceObject when done.
// Returns NULL if the handle is invalid or the type does not match.
// ---------------------------------------------------------------------------
static OBJECT* HandleToObject(uint32_t handle, OBJECT_TYPE expected) {
PROCESS* proc = CurrentProcess();
if (!proc) return NULL;
OBJECT* obj = KeLookupHandle(proc, handle); // bumps refcount
if (!obj) return NULL;
if (obj->Type != expected) {
ObDereferenceObject(obj); // undo the bump
return NULL;
}
return obj;
}
// ---------------------------------------------------------------------------
// Helper: allocate a handle in the current process's table for 'obj'.
// Takes one reference on behalf of the handle slot.
// On failure, drops the reference it would have stored.
// ---------------------------------------------------------------------------
static KSTATUS AllocHandleForCurrent(OBJECT* obj, uint32_t* handleOut) {
PROCESS* proc = CurrentProcess();
if (!proc) return STATUS_FAILURE;
KSTATUS s = KeAllocateHandle(proc, obj, handleOut);
if (!KSUCCESS(s)) ObDereferenceObject(obj);
return s;
}
// ===========================================================================
// Individual syscall handlers
// ===========================================================================
// ---------------------------------------------------------------------------
// SYS_EXIT_PROCESS (0) a0 = exitCode
// ---------------------------------------------------------------------------
static uint64_t SyscallExitProcess(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
THREAD* t = KeCurrentThread();
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 (true) 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 = KeCurrentThread();
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 = CurrentProcess();
if (!proc) return STATUS_FAILURE;
if (a2 >= PriorityCount) return STATUS_INVALID_PARAMETER;
THREAD* newt = NULL;
KSTATUS s = KeCreateThread(proc, (uintptr_t)a0, (uintptr_t)a1,
(THREAD_PRIORITY)a2, &newt);
if (!KSUCCESS(s)) return s;
if (a3) *(uint32_t*)(uintptr_t)a3 = newt->ThreadId;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_GET_CURRENT_TID (3)
// ---------------------------------------------------------------------------
static uint64_t SyscallGetCurrentTid(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
THREAD* t = KeCurrentThread();
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 = CurrentProcess();
return p ? p->ProcessId : 0;
}
// ---------------------------------------------------------------------------
// SYS_YIELD (5)
// ---------------------------------------------------------------------------
static uint64_t SyscallYield(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
KeYield();
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_SLEEP (6) a0 = milliseconds
// ---------------------------------------------------------------------------
static uint64_t SyscallSleep(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (!gHal) return STATUS_FAILURE;
uint64_t deadline = gHal->Timer.QueryTicks() + (uint64_t)a0;
while (gHal->Timer.QueryTicks() < deadline)
KeYield();
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_SET_THREAD_PRIORITY (7) a0 = priority
// ---------------------------------------------------------------------------
static uint64_t SyscallSetThreadPriority(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (a0 >= PriorityCount) return STATUS_INVALID_PARAMETER;
THREAD* t = KeCurrentThread();
if (!t) return STATUS_FAILURE;
t->Priority = (THREAD_PRIORITY)a0;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_ALLOC (8) a0 = size -> void*
// ---------------------------------------------------------------------------
static uint64_t SyscallAlloc(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (!a0) return 0;
return (uint64_t)(uintptr_t)MmAllocateUserMemory((size_t)a0);
}
// ---------------------------------------------------------------------------
// SYS_FREE (9) a0 = ptr, a1 = size
// ---------------------------------------------------------------------------
static uint64_t SyscallFree(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
if (a0) MmFreeUserMemory((void*)(uintptr_t)a0, (size_t)a1);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_REALLOC (10) a0 = ptr, a1 = newSize -> void*
// ---------------------------------------------------------------------------
static uint64_t SyscallRealloc(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
return (uint64_t)(uintptr_t)MmReallocateUserMemory(
(void*)(uintptr_t)a0, (size_t)a1);
}
// ---------------------------------------------------------------------------
// SYS_MUTEX_CREATE (11) a0 = *handleOut
// ---------------------------------------------------------------------------
static uint64_t SyscallMutexCreate(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (!a0) return STATUS_INVALID_PARAMETER;
MUTEX* mx = NULL;
KSTATUS s = MutexCreate(&mx);
if (!KSUCCESS(s)) return s;
uint32_t h = 0;
s = AllocHandleForCurrent(&mx->Header, &h); // takes ownership of the ref
if (!KSUCCESS(s)) return s;
*(uint32_t*)(uintptr_t)a0 = h;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_MUTEX_ACQUIRE (12) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallMutexAcquire(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex);
if (!mx) return STATUS_NOT_FOUND;
KSTATUS s = KeAcquireMutex(mx);
ObDereferenceObject(&mx->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_MUTEX_TRY_ACQUIRE (13) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallMutexTryAcquire(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex);
if (!mx) return STATUS_NOT_FOUND;
KSTATUS s = KeTryAcquireMutex(mx);
ObDereferenceObject(&mx->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_MUTEX_RELEASE (14) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallMutexRelease(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex);
if (!mx) return STATUS_NOT_FOUND;
KeReleaseMutex(mx);
ObDereferenceObject(&mx->Header);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_SEM_CREATE (15) a0=initial a1=max a2=*handleOut
// ---------------------------------------------------------------------------
static uint64_t SyscallSemCreate(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
if (!a2) return STATUS_INVALID_PARAMETER;
SEMAPHORE* sem = NULL;
KSTATUS s = KeSemaphoreCreate((int32_t)a0, (int32_t)a1, &sem);
if (!KSUCCESS(s)) return s;
uint32_t h = 0;
s = AllocHandleForCurrent(&sem->Header, &h);
if (!KSUCCESS(s)) return s;
*(uint32_t*)(uintptr_t)a2 = h;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_SEM_WAIT (16) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallSemWait(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore);
if (!sem) return STATUS_NOT_FOUND;
KSTATUS s = KeWaitSemaphore(sem);
ObDereferenceObject(&sem->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_SEM_TRY_WAIT (17) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallSemTryWait(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore);
if (!sem) return STATUS_NOT_FOUND;
KSTATUS s = KeTrySemaphore(sem);
ObDereferenceObject(&sem->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_SEM_SIGNAL (18) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallSemSignal(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore);
if (!sem) return STATUS_NOT_FOUND;
KeSignalSemaphore(sem);
ObDereferenceObject(&sem->Header);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_EVENT_CREATE (19) a0=type a1=initialState a2=*handleOut
// ---------------------------------------------------------------------------
static uint64_t SyscallEventCreate(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
if (!a2 || a0 > EventManualReset) return STATUS_INVALID_PARAMETER;
EVENT* ev = NULL;
KSTATUS s = KeEventCreate((EVENT_TYPE)a0, (uint32_t)a1, &ev);
if (!KSUCCESS(s)) return s;
uint32_t h = 0;
s = AllocHandleForCurrent(&ev->Header, &h);
if (!KSUCCESS(s)) return s;
*(uint32_t*)(uintptr_t)a2 = h;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_EVENT_SIGNAL (20) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallEventSignal(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent);
if (!ev) return STATUS_NOT_FOUND;
KeSignalEvent(ev);
ObDereferenceObject(&ev->Header);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_EVENT_CLEAR (21) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallEventClear(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent);
if (!ev) return STATUS_NOT_FOUND;
KeClearEvent(ev);
ObDereferenceObject(&ev->Header);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_EVENT_WAIT (22) a0 = handle
// ---------------------------------------------------------------------------
static uint64_t SyscallEventWait(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent);
if (!ev) return STATUS_NOT_FOUND;
KSTATUS s = KeWaitForEvent(ev);
ObDereferenceObject(&ev->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_QUEUE_CREATE (23) a0 = *handleOut
// ---------------------------------------------------------------------------
static uint64_t SyscallQueueCreate(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (!a0) return STATUS_INVALID_PARAMETER;
QUEUE* q = NULL;
KSTATUS s = QueueCreate(&q);
if (!KSUCCESS(s)) return s;
uint32_t h = 0;
s = AllocHandleForCurrent(&q->Header, &h);
if (!KSUCCESS(s)) return s;
*(uint32_t*)(uintptr_t)a0 = h;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_QUEUE_SEND (24) a0=handle a1=*msg a2=blocking
// ---------------------------------------------------------------------------
static uint64_t SyscallQueueSend(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
if (!a1) return STATUS_INVALID_PARAMETER;
QUEUE* q = (QUEUE*)HandleToObject((uint32_t)a0, ObjectTypeQueue);
if (!q) return STATUS_NOT_FOUND;
KSTATUS s = KeSendMessage(q, (const QUEUE_MESSAGE*)(uintptr_t)a1, (bool)a2);
ObDereferenceObject(&q->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_QUEUE_RECV (25) a0=handle a1=*msg a2=blocking
// ---------------------------------------------------------------------------
static uint64_t SyscallQueueRecv(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
if (!a1) return STATUS_INVALID_PARAMETER;
QUEUE* q = (QUEUE*)HandleToObject((uint32_t)a0, ObjectTypeQueue);
if (!q) return STATUS_NOT_FOUND;
KSTATUS s = KeReceiveMessage(q, (QUEUE_MESSAGE*)(uintptr_t)a1, (bool)a2);
ObDereferenceObject(&q->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_CLOSE_HANDLE (26) a0 = handle
//
// The one and only close path. KeCloseHandle drops the handle-table reference
// on 'obj'. If that was the last reference, ObDereferenceObject runs the
// type-specific Destroy callback (FileDestroy, etc.) and frees the pool memory.
// No per-type close syscall needed or wanted.
// ---------------------------------------------------------------------------
static uint64_t SyscallCloseHandle(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
PROCESS* proc = CurrentProcess();
if (!proc) return STATUS_FAILURE;
KeCloseHandle(proc, (uint32_t)a0);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_OPEN_FILE (27) a0=*path a1=*handleOut
// ---------------------------------------------------------------------------
static uint64_t SyscallOpenFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
if (!a0 || !a1) return STATUS_INVALID_PARAMETER;
FILE_OBJECT* file = NULL;
KSTATUS s = IoOpenFile((const char*)(uintptr_t)a0, &file);
if (!KSUCCESS(s)) return s;
uint32_t h = 0;
s = AllocHandleForCurrent(&file->Header, &h);
if (!KSUCCESS(s)) {
// AllocHandleForCurrent already dropped the ref on failure;
// the refcount is now 0, so ObDereferenceObject will run FileDestroy.
ObDereferenceObject(&file->Header);
return s;
}
*(uint32_t*)(uintptr_t)a1 = h;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_READ_FILE (28) a0=handle a1=buf a2=size a3=*bytesReadOut
// ---------------------------------------------------------------------------
static uint64_t SyscallReadFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a4; (void)a5;
if (!a1) return STATUS_INVALID_PARAMETER;
FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile);
if (!file) return STATUS_NOT_FOUND;
size_t got = 0;
KSTATUS s = IoReadFile(file, (void*)(uintptr_t)a1, (size_t)a2, &got);
if (a3) *(size_t*)(uintptr_t)a3 = got;
ObDereferenceObject(&file->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_SEEK_FILE (29) a0=handle a1=offset
// ---------------------------------------------------------------------------
static uint64_t SyscallSeekFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile);
if (!file) return STATUS_NOT_FOUND;
KSTATUS s = IoSeekFile(file, (uint64_t)a1);
ObDereferenceObject(&file->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_GET_FILE_SIZE (30) a0=handle -> uint64_t
// ---------------------------------------------------------------------------
static uint64_t SyscallGetFileSize(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile);
if (!file) return 0;
uint64_t sz = IoGetFileSize(file);
ObDereferenceObject(&file->Header);
return sz;
}
// ---------------------------------------------------------------------------
// SYS_SCREEN_BLIT (31) a0=x a1=y a2=w a3=h a4=*pixels
// ---------------------------------------------------------------------------
static uint64_t SyscallScreenBlit(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a5;
if (!gHal || !a4) return STATUS_FAILURE;
gHal->Screen.Blit((int)a0, (int)a1, (int)a2, (int)a3,
(const uint32_t*)(uintptr_t)a4);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_SCREEN_CLEAR (32) 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 (33) a0=*widthOut a1=*heightOut
// ---------------------------------------------------------------------------
static uint64_t SyscallScreenGetSize(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
if (!gHal) return STATUS_FAILURE;
int w = 0, h = 0;
gHal->Screen.GetSize(&w, &h);
if (a0) *(int*)(uintptr_t)a0 = w;
if (a1) *(int*)(uintptr_t)a1 = h;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_SCREEN_PUT_CHAR (34) 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 (35) -> 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() ? 1u : 0u;
}
// ---------------------------------------------------------------------------
// SYS_KB_READ_CHAR (36) -> 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 (37) -> 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_GET_TICK (38) -> uint64_t milliseconds
// ---------------------------------------------------------------------------
static uint64_t SyscallGetTick(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
return gHal ? gHal->Timer.QueryTicks() : 0;
}
// ---------------------------------------------------------------------------
// SYS_DEBUG_PRINT (39) a0=*str
// ---------------------------------------------------------------------------
static uint64_t SyscallDebugPrint(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (a0) DbgPrint("%s", (const char*)(uintptr_t)a0);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_CREATE_PROCESS (40) a0=*path a1=*handleOut
// ---------------------------------------------------------------------------
static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
const char* path = (const char*)(uintptr_t)a0;
uint32_t* handleOut = (uint32_t*)(uintptr_t)a1;
if (!path || !handleOut) return STATUS_INVALID_PARAMETER;
// Read the executable from disk.
FILE_OBJECT* f;
KSTATUS s = IoOpenFile(path, &f);
if (!KSUCCESS(s)) return s;
uint64_t size = IoGetFileSize(f);
void* mem = MmAllocateUserMemory(size);
if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; }
size_t got;
s = IoReadFile(f, mem, size, &got);
IoCloseFile(f);
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
// Create the process object.
PROCESS* proc;
s = KeCreateProcess(path, &proc);
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
// Map the PE image into memory.
uintptr_t imageBase = 0;
s = LdrLoadImage(mem, size, &imageBase, true);
MmFreeUserMemory(mem, size);
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
// Spin up the initial thread at the PE entry point.
THREAD* thread;
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread);
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
// Give the caller a handle to the new process so they can wait on it.
PROCESS* caller = CurrentProcess();
if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; }
uint32_t handle;
s = KeAllocateHandle(caller, &proc->Header, &handle);
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
*handleOut = handle;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_WAIT_FOR_PROCESS (41) a0=handle
// Blocks until the target process signals its ExitEvent.
// ---------------------------------------------------------------------------
static uint64_t SyscallWaitForProcess(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
PROCESS* proc = (PROCESS*)HandleToObject((uint32_t)a0, ObjectTypeProcess);
if (!proc) return STATUS_INVALID_HANDLE;
KSTATUS s = KeWaitForEvent(proc->ExitEvent);
ObDereferenceObject(&proc->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_GET_PROCESS_EXIT_CODE (42) a0=handle a1=*exitCodeOut
// ---------------------------------------------------------------------------
static uint64_t SyscallGetProcessExitCode(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
PROCESS* proc = (PROCESS*)HandleToObject((uint32_t)a0, ObjectTypeProcess);
if (!proc) return STATUS_INVALID_HANDLE;
int* exitCodeOut = (int*)(uintptr_t)a1;
KSTATUS s = KeGetProcessExitCode(proc, exitCodeOut);
ObDereferenceObject(&proc->Header);
return s;
}
// ===========================================================================
// Dispatch table - index must match SYS_* in syscall_nr.h exactly.
// ===========================================================================
typedef uint64_t (*SYSCALL_HANDLER)(uint64_t, uint64_t, uint64_t,
uint64_t, uint64_t, uint64_t);
static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
/* 00 SYS_EXIT_PROCESS */ SyscallExitProcess,
/* 01 SYS_EXIT_THREAD */ SyscallExitThread,
/* 02 SYS_CREATE_THREAD */ SyscallCreateThread,
/* 03 SYS_GET_CURRENT_TID */ SyscallGetCurrentTid,
/* 04 SYS_GET_CURRENT_PID */ SyscallGetCurrentPid,
/* 05 SYS_YIELD */ SyscallYield,
/* 06 SYS_SLEEP */ SyscallSleep,
/* 07 SYS_SET_THREAD_PRIORITY */ SyscallSetThreadPriority,
/* 08 SYS_ALLOC */ SyscallAlloc,
/* 09 SYS_FREE */ SyscallFree,
/* 10 SYS_REALLOC */ SyscallRealloc,
/* 11 SYS_MUTEX_CREATE */ SyscallMutexCreate,
/* 12 SYS_MUTEX_ACQUIRE */ SyscallMutexAcquire,
/* 13 SYS_MUTEX_TRY_ACQUIRE */ SyscallMutexTryAcquire,
/* 14 SYS_MUTEX_RELEASE */ SyscallMutexRelease,
/* 15 SYS_SEM_CREATE */ SyscallSemCreate,
/* 16 SYS_SEM_WAIT */ SyscallSemWait,
/* 17 SYS_SEM_TRY_WAIT */ SyscallSemTryWait,
/* 18 SYS_SEM_SIGNAL */ SyscallSemSignal,
/* 19 SYS_EVENT_CREATE */ SyscallEventCreate,
/* 20 SYS_EVENT_SIGNAL */ SyscallEventSignal,
/* 21 SYS_EVENT_CLEAR */ SyscallEventClear,
/* 22 SYS_EVENT_WAIT */ SyscallEventWait,
/* 23 SYS_QUEUE_CREATE */ SyscallQueueCreate,
/* 24 SYS_QUEUE_SEND */ SyscallQueueSend,
/* 25 SYS_QUEUE_RECV */ SyscallQueueRecv,
/* 26 SYS_CLOSE_HANDLE */ SyscallCloseHandle,
/* 27 SYS_OPEN_FILE */ SyscallOpenFile,
/* 28 SYS_READ_FILE */ SyscallReadFile,
/* 29 SYS_SEEK_FILE */ SyscallSeekFile,
/* 30 SYS_GET_FILE_SIZE */ SyscallGetFileSize,
/* 31 SYS_SCREEN_BLIT */ SyscallScreenBlit,
/* 32 SYS_SCREEN_CLEAR */ SyscallScreenClear,
/* 33 SYS_SCREEN_GET_SIZE */ SyscallScreenGetSize,
/* 34 SYS_SCREEN_PUT_CHAR */ SyscallScreenPutChar,
/* 35 SYS_KB_POLL */ SyscallKbPoll,
/* 36 SYS_KB_READ_CHAR */ SyscallKbReadChar,
/* 37 SYS_KB_READ_SCANCODE */ SyscallKbReadScancode,
/* 38 SYS_GET_TICK */ SyscallGetTick,
/* 39 SYS_DEBUG_PRINT */ SyscallDebugPrint,
/* 40 SYS_CREATE_PROCESS */ SyscallCreateProcess,
/* 41 SYS_WAIT_FOR_PROCESS */ SyscallWaitForProcess,
/* 42 SYS_GET_PROCESS_EXIT_CODE */ SyscallGetProcessExitCode,
};
// ---------------------------------------------------------------------------
// Entry point - called from ke_syscall.asm after argument re-ordering
// ---------------------------------------------------------------------------
KSTATUS KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1,
uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) {
if (nr >= SYS_MAX || !gSyscallTable[nr]) return STATUS_NOT_SUPPORTED;
return (KSTATUS)gSyscallTable[nr](a0, a1, a2, a3, a4, a5);
}