Files
nt-lite/code/kernel/ke_syscall.c
T
2026-04-30 23:52:46 +02:00

378 lines
16 KiB
C

// ke_syscall.c - syscall dispatch
//
// KeSyscallEntry (MASM64) pushes all arguments onto the kernel stack and
// calls KeSyscallDispatch. We read them from the stack frame.
//#include "ke_syscall.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 "syscall_nr.h"
#include "hal_api.h"
#include "io_file.h"
#include <stdint.h>
#include <stdbool.h>
// hal pointer set at boot
static const HAL_API* gHal;
void KeSyscallSetHal(const HAL_API* hal) { gHal = hal; }
// ---------------------------------------------------------------------------
// Individual syscall handlers
// ---------------------------------------------------------------------------
static uint64_t SyscallExitThread(uint64_t code, 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;
KeExitThread(KeCurrentThread(), (int)code);
return STATUS_SUCCESS;
}
static uint64_t SyscallExitProcess(uint64_t code, 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 && t->Owner) KeExitProcess(t->Owner, (int)code);
KeExitThread(t, (int)code);
return STATUS_SUCCESS;
}
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;
}
static uint64_t SyscallSleep(uint64_t ms, 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;
// Simple busy-wait sleep via HAL timer.
if (gHal) {
uint64_t start = gHal->Timer.QueryTicks();
while (gHal->Timer.QueryTicks() - start < ms) KeYield();
}
return STATUS_SUCCESS;
}
static uint64_t SyscallAlloc(uint64_t size, 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 (uint64_t)MmAllocateUserMemory((size_t)size);
}
static uint64_t SyscallFree(uint64_t ptr, uint64_t size, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
MmFreeUserMemory((void *)ptr, (size_t)size);
return STATUS_SUCCESS;
}
static uint64_t SyscallCreateThread(uint64_t entry, uint64_t arg,
uint64_t priority, uint64_t a3,
uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
THREAD* cur = KeCurrentThread();
if (!cur || !cur->Owner) return (uint64_t)STATUS_FAILURE;
THREAD* newThread;
KSTATUS s = KeCreateThread(cur->Owner, entry, arg,
(THREAD_PRIORITY)priority, &newThread);
if (!KSUCCESS(s)) return (uint64_t)s;
uint32_t handle;
KeAllocateHandle(cur->Owner, &newThread->Header, &handle);
return (uint64_t)handle;
}
static uint64_t SyscallCloseHandle(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
KeCloseHandle(cur->Owner, (uint32_t)handle);
return STATUS_SUCCESS;
}
static uint64_t SyscallDbgPrint(uint64_t strPtr, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
const char* s = (const char *)(uintptr_t)strPtr;
DbgPrint(s);
return STATUS_SUCCESS;
}
static uint64_t SyscallMutexCreate(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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return 0;
MUTEX* m; if (!KSUCCESS(MutexCreate(&m))) return 0;
uint32_t h; KeAllocateHandle(cur->Owner, &m->Header, &h);
ObDereferenceObject(&m->Header); // handle holds the ref
return h;
}
static uint64_t SyscallMutexAcquire(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj || obj->Type != ObjectTypeMutex) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; }
KSTATUS s = KeAcquireMutex((MUTEX *)obj);
ObDereferenceObject(obj);
return s;
}
static uint64_t SyscallMutexRelease(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj || obj->Type != ObjectTypeMutex) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; }
KeReleaseMutex((MUTEX *)obj);
ObDereferenceObject(obj);
return STATUS_SUCCESS;
}
static uint64_t SyscallSemCreate(uint64_t initial, uint64_t max, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
THREAD* cur = KeCurrentThread();
if (!cur || !cur->Owner) return 0;
SEMAPHORE* s; if (!KSUCCESS(SemaphoreCreate((int32_t)initial,(int32_t)max,&s))) return 0;
uint32_t h; KeAllocateHandle(cur->Owner, &s->Header, &h);
ObDereferenceObject(&s->Header);
return h;
}
static uint64_t SyscallSemWait(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj || obj->Type != ObjectTypeSemaphore) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; }
KSTATUS s = KeWaitSemaphore((SEMAPHORE *)obj);
ObDereferenceObject(obj);
return s;
}
static uint64_t SyscallSemSignal(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj || obj->Type != ObjectTypeSemaphore) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; }
KeSignalSemaphore((SEMAPHORE *)obj);
ObDereferenceObject(obj);
return STATUS_SUCCESS;
}
static uint64_t SyscallEventCreate(uint64_t manualReset, uint64_t initial,
uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
THREAD* cur = KeCurrentThread();
if (!cur || !cur->Owner) return 0;
EVENT* e;
EVENT_TYPE t = manualReset ? EventManualReset : EventAutoReset;
if (!KSUCCESS(KeEventCreate(t, (uint32_t)initial, &e))) return 0;
uint32_t h; KeAllocateHandle(cur->Owner, &e->Header, &h);
ObDereferenceObject(&e->Header);
return h;
}
static uint64_t SyscallEventSet(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj || obj->Type != ObjectTypeEvent) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; }
KeSignalEvent((EVENT *)obj);
ObDereferenceObject(obj);
return STATUS_SUCCESS;
}
static uint64_t SyscallEventWait(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj || obj->Type != ObjectTypeEvent) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; }
KSTATUS s = KeWaitForEvent((EVENT *)obj);
ObDereferenceObject(obj);
return s;
}
static uint64_t SyscallQueueCreate(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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return 0;
QUEUE* q; if (!KSUCCESS(QueueCreate(&q))) return 0;
uint32_t h; KeAllocateHandle(cur->Owner, &q->Header, &h);
ObDereferenceObject(&q->Header);
return h;
}
static uint64_t SyscallQueueSend(uint64_t handle, uint64_t msgPtr,
uint64_t blocking, uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
THREAD* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj || obj->Type != ObjectTypeQueue) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; }
KSTATUS s = KeSendMessage((QUEUE *)obj, (const QUEUE_MESSAGE *)msgPtr, (bool)blocking);
ObDereferenceObject(obj);
return s;
}
static uint64_t SyscallQueueRecv(uint64_t handle, uint64_t msgPtr,
uint64_t blocking, uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
THREAD* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj || obj->Type != ObjectTypeQueue) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; }
KSTATUS s = KeReceiveMessage((QUEUE *)obj, (QUEUE_MESSAGE *)msgPtr, (bool)blocking);
ObDereferenceObject(obj);
return s;
}
static uint64_t SyscallScreenBlit(uint64_t x, uint64_t y, uint64_t w,
uint64_t h, uint64_t pixels, uint64_t a5) {
(void)a5;
if (gHal) {
gHal->Screen.Blit((int)x, (int)y, (int)w, (int)h, (const uint32_t *)pixels);
return STATUS_SUCCESS;
}
DbgPrint("!! SyscallScreenBlit: what?\n");
return STATUS_FAILURE;
}
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;
}
static uint64_t SyscallOpen(uint64_t pathPtr, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return 0;
FILE_OBJECT* f;
KSTATUS s = IoOpenFile((const char *)pathPtr, &f);
if (!KSUCCESS(s)) return 0;
uint32_t handle;
s = KeAllocateHandle(cur->Owner, &f->Header, &handle);
ObDereferenceObject(&f->Header); // handle holds the ref now
if (!KSUCCESS(s)) return 0;
return (uint64_t)handle;
}
static uint64_t SyscallRead(uint64_t handle, uint64_t bufPtr, uint64_t size,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
THREAD* cur = KeCurrentThread();
if (!cur || !cur->Owner) return 0;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj) return 0;
// FILE_OBJECT reuses ObjectTypeQueue slot - check it holds a Fs pointer.
FILE_OBJECT* f = (FILE_OBJECT *)obj;
size_t bytesRead = 0;
IoReadFile(f, (void *)bufPtr, (size_t)size, &bytesRead);
ObDereferenceObject(obj);
return (uint64_t)bytesRead;
}
static uint64_t SyscallFClose(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
KeCloseHandle(cur->Owner, (uint32_t)handle);
return STATUS_SUCCESS;
}
static uint64_t SyscallFSeek(uint64_t handle, uint64_t offset, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
THREAD* cur = KeCurrentThread();
if (!cur || !cur->Owner) return STATUS_FAILURE;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj) return STATUS_NOT_FOUND;
KSTATUS s = IoSeekFile((FILE_OBJECT *)obj, offset);
ObDereferenceObject(obj);
return s;
}
static uint64_t SyscallFSize(uint64_t handle, 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* cur = KeCurrentThread();
if (!cur || !cur->Owner) return 0;
OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle);
if (!obj) return 0;
uint64_t sz = IoGetFileSize((FILE_OBJECT *)obj);
ObDereferenceObject(obj);
return sz;
}
// ---------------------------------------------------------------------------
// Dispatch table
// ---------------------------------------------------------------------------
typedef uint64_t (*SYSCALL_FN)(uint64_t, uint64_t, uint64_t,
uint64_t, uint64_t, uint64_t);
static const SYSCALL_FN gSyscallTable[SYS_MAX] = {
[SYS_EXIT_THREAD] = SyscallExitThread,
[SYS_EXIT_PROCESS] = SyscallExitProcess,
[SYS_SPAWN] = (SYSCALL_FN)0, // TODO: process spawn
[SYS_SLEEP] = SyscallSleep,
[SYS_YIELD] = SyscallYield,
[SYS_ALLOC] = SyscallAlloc,
[SYS_FREE] = SyscallFree,
[SYS_CREATE_THREAD]= SyscallCreateThread,
[SYS_OPEN_HANDLE] = (SYSCALL_FN)0, // TODO
[SYS_CLOSE_HANDLE] = SyscallCloseHandle,
[SYS_DBG_PRINT] = SyscallDbgPrint,
[SYS_MUTEX_CREATE] = SyscallMutexCreate,
[SYS_MUTEX_ACQUIRE]= SyscallMutexAcquire,
[SYS_MUTEX_RELEASE]= SyscallMutexRelease,
[SYS_SEM_CREATE] = SyscallSemCreate,
[SYS_SEM_WAIT] = SyscallSemWait,
[SYS_SEM_SIGNAL] = SyscallSemSignal,
[SYS_EVENT_CREATE] = SyscallEventCreate,
[SYS_EVENT_SET] = SyscallEventSet,
[SYS_EVENT_WAIT] = SyscallEventWait,
[SYS_QUEUE_CREATE] = SyscallQueueCreate,
[SYS_QUEUE_SEND] = SyscallQueueSend,
[SYS_QUEUE_RECV] = SyscallQueueRecv,
[SYS_SCREEN_BLIT] = SyscallScreenBlit,
[SYS_GET_TICK] = SyscallGetTick,
[SYS_OPEN] = SyscallOpen,
[SYS_READ] = SyscallRead,
[SYS_FCLOSE] = SyscallFClose,
[SYS_FSEEK] = SyscallFSeek,
[SYS_FSIZE] = SyscallFSize,
};
// Called from ke_syscall.asm - args already on the stack in order.
uint64_t 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 (uint64_t)STATUS_NOT_SUPPORTED;
return gSyscallTable[nr](a0, a1, a2, a3, a4, a5);
}