/* * Syscall.cpp * SYSCALL/SYSRET setup and number-based dispatch * Copyright (c) 2025 Daniel Hammer */ #include "Syscall.hpp" #include #include #include /* For common functions used by multiple syscall implementations*/ #include "Common.hpp" #include /* Syscall impl. includes */ #include "Process.hpp" // SYS_EXIT, SYS_YIELD, SYS_SLEEP_MS, SYS_GETPID, SYS_WAITPID, SYS_SPAWN, SYS_GETARGS, SYS_PROCLIST, SYS_KILL #include "Terminal.hpp" // SYS_PRINT, SYS_PUTCHAR #include "Filesystem.hpp" // SYS_OPEN, SYS_READ, SYS_GETSIZE, SYS_CLOSE, SYS_READDIR, SYS_FWRITE, SYS_FCREATE #include "Heap.hpp" // SYS_ALLOC, SYS_FREE #include "Time.hpp" // SYS_GETTICKS, SYS_GETMILLISECONDS, SYS_GETTIME #include "Keyboard.hpp" // SYS_ISKEYAVAILABLE, SYS_GETKEY, SYS_GETCHAR #include "Info.hpp" // SYS_GETINFO #include "Graphics.hpp" // SYS_FBINFO, SYS_FBMAP, SYS_TERMSIZE, SYS_TERMSCALE #include "Net.hpp" // SYS_PING, SYS_SOCKET, SYS_CONNECT, SYS_BIND, SYS_LISTEN, SYS_ACCEPT, SYS_SEND, SYS_RECV, SYS_CLOSESOCK, SYS_SENDTO, SYS_RECVFROM, SYS_GETNETCFG, SYS_SETNETCFG, SYS_RESOLVE, SYS_NETSTATUS #include "Power.hpp" // SYS_RESET, SYS_SHUTDOWN, SYS_SUSPEND #include "Mouse.hpp" // SYS_MOUSESTATE, SYS_SETMOUSEBOUNDS #include "IoRedir.hpp" // SYS_SPAWN_REDIR, SYS_CHILDIO_READ, SYS_CHILDIO_WRITE, SYS_CHILDIO_WRITEKEY, SYS_CHILDIO_SETTERMSZ #include "Random.hpp" // SYS_GETRANDOM #include "MemInfo.hpp" // SYS_MEMSTATS #include "Device.hpp" // SYS_DEVLIST, SYS_DISKINFO #include "Input.hpp" // SYS_INPUT_WAIT #include "Storage.hpp" // SYS_PARTLIST, SYS_VOLINFO, SYS_DISKREAD, SYS_DISKWRITE #include "Window.hpp" // SYS_WINCREATE, SYS_WINDESTROY, SYS_WINPRESENT, SYS_WINPOLL, SYS_WINENUM, SYS_WINMAP, SYS_WINSENDEVENT, SYS_WINRESIZE, SYS_WINSETCURSOR, SYS_WINSETFLAGS, SYS_WINSETSCALE, SYS_WINGETSCALE #include "Audio.hpp" // SYS_AUDIOOPEN, SYS_AUDIOCLOSE, SYS_AUDIOWRITE, SYS_AUDIOCTL #include "BluetoothSyscall.hpp" // SYS_BTSCAN, SYS_BTCONNECT, SYS_BTDISCONNECT, SYS_BTLIST, SYS_BTINFO #include "Usb.hpp" // generic process-owned USB interface access #include "WifiSyscall.hpp" // SYS_WIFI_SCAN, SYS_WIFI_INFO, SYS_WIFI_CONNECT, SYS_WIFI_DISCONNECT #include "IpcSyscall.hpp" // SYS_DUPHANDLE, SYS_WAIT_HANDLE, SYS_STREAM_CREATE, SYS_STREAM_READ, SYS_STREAM_WRITE, SYS_MAILBOX_CREATE, SYS_MAILBOX_SEND, SYS_MAILBOX_RECV, SYS_WAITSET_CREATE, SYS_WAITSET_ADD, SYS_WAITSET_REMOVE, SYS_WAITSET_WAIT, SYS_PROC_OPEN, SYS_SURFACE_CREATE, SYS_SURFACE_MAP, SYS_SURFACE_RESIZE #include "LibSyscall.hpp" // SYS_LOAD_LIB, SYS_UNLOAD_LIB, SYS_DLSYM #include "CrashReportSyscall.hpp" // SYS_CRASH_REPORT #include "Clipboard.hpp" // SYS_CLIPBOARD_* #include "UserMemory.hpp" // Assembly entry point extern "C" void SyscallEntry(); namespace montauk::abi { static constexpr uint64_t kMaxPrintableStringBytes = 4096; static constexpr uint64_t kMaxPathBytes = 256; // Large enough for compiler driver command lines (cc1/ld invocations). static constexpr uint64_t kMaxArgsBytes = 4096; static constexpr uint64_t kMaxWindowTitleBytes = 256; static constexpr uint64_t kMaxHostnameBytes = 256; static constexpr uint64_t kMaxUserNameBytes = 32; static constexpr uint64_t kMaxUserspaceLogEntryBytes = 1024; // ---- Dispatch ---- extern "C" int64_t SyscallDispatch(SyscallFrame* frame) { switch (frame->syscall_nr) { case SYS_EXIT: { Sys_Exit((int)frame->arg1); return Success; } case SYS_YIELD: Sys_Yield(); return Success; case SYS_SLEEP_MS: Sys_SleepMs(frame->arg1); return Success; case SYS_GETPID: return (int64_t)Sys_GetPid(); case SYS_PRINT: if (!UserMemory::String(frame->arg1, kMaxPrintableStringBytes)) return BadPointer; Sys_Print((const char*)frame->arg1); return Success; case SYS_PUTCHAR: Sys_Putchar((char)frame->arg1); return Success; case SYS_OPEN: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; return (int64_t)Sys_Open((const char*)frame->arg1); case SYS_READ: if (!UserMemory::Range(frame->arg2, frame->arg4, true)) return BadPointer; return (int64_t)Sys_Read((int)frame->arg1, (uint8_t*)frame->arg2, frame->arg3, frame->arg4); case SYS_GETSIZE: return (int64_t)Sys_GetSize((int)frame->arg1); case SYS_CLOSE: Sys_Close((int)frame->arg1); return Success; case SYS_READDIR: if ((int64_t)frame->arg3 < 0) return InvalidArgument; if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (!UserMemory::Range(frame->arg2, (uint64_t)((frame->arg3 > 256) ? 256 : frame->arg3) * sizeof(const char*), true)) { return BadPointer; } return (int64_t)Sys_ReadDir((const char*)frame->arg1, (const char**)frame->arg2, (int)frame->arg3); case SYS_READDIR_AT: if ((int64_t)frame->arg3 < 0) return InvalidArgument; if ((int64_t)frame->arg4 < 0) return InvalidArgument; if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (!UserMemory::Range(frame->arg2, (uint64_t)((frame->arg3 > 256) ? 256 : frame->arg3) * sizeof(const char*), true)) { return BadPointer; } return (int64_t)Sys_ReadDirAt((const char*)frame->arg1, (const char**)frame->arg2, (int)frame->arg3, (int)frame->arg4); case SYS_ALLOC: return (int64_t)Sys_Alloc(frame->arg1); case SYS_ALLOC_EAGER: return (int64_t)Sys_AllocEager(frame->arg1); case SYS_FREE: Sys_Free(frame->arg1); return Success; case SYS_MMAP_ANON: return (int64_t)Sys_MapAnonymous(frame->arg1, frame->arg2); case SYS_MUNMAP: return Sys_Unmap(frame->arg1, frame->arg2); case SYS_MPROTECT: return Sys_Protect(frame->arg1, frame->arg2, frame->arg3); case SYS_GETTICKS: return (int64_t)Sys_GetTicks(); case SYS_GETMILLISECONDS: return (int64_t)Sys_GetMilliseconds(); case SYS_GETINFO: if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_GetInfo((SysInfo*)frame->arg1); return Success; case SYS_ISKEYAVAILABLE: return (int64_t)Sys_IsKeyAvailable(); case SYS_GETKEY: if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_GetKey((KeyEventV1*)frame->arg1); return Success; case SYS_GETCHAR: return (int64_t)Sys_GetChar(); case SYS_GETCHAR_NB: return (int64_t)Sys_GetCharNb(); case SYS_PING: return (int64_t)Sys_Ping((uint32_t)frame->arg1, (uint32_t)frame->arg2); case SYS_SPAWN: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return BadPointer; return (int64_t)Sys_Spawn((const char*)frame->arg1, UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr); case SYS_SPAWN_CAPS: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return BadPointer; if (frame->arg3 != 0 && !UserMemory::String(frame->arg3, 32)) return BadPointer; if (!UserMemory::Readable(frame->arg4)) return BadPointer; return (int64_t)Sys_SpawnCaps( (const char*)frame->arg1, frame->arg2 ? (const char*)frame->arg2 : nullptr, frame->arg3 ? (const char*)frame->arg3 : nullptr, (const SpawnCapabilities*)frame->arg4); case SYS_SPAWN_ENV: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return BadPointer; if (frame->arg4 == 0 || frame->arg4 > Sched::Process::EnvironmentBytes) return InvalidArgument; if (frame->arg3 == 0 || !UserMemory::Range(frame->arg3, frame->arg4, false)) return BadPointer; if (((const char*)frame->arg3)[frame->arg4 - 1] != '\0') return InvalidArgument; return (int64_t)Sys_Spawn((const char*)frame->arg1, UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr, (const char*)frame->arg3, (uint32_t)frame->arg4); case SYS_WAITPID: return Sys_WaitPid((int)frame->arg1); case SYS_GETENVIRON: if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer; return Sys_GetEnviron((char*)frame->arg1, frame->arg2); case SYS_SETENVIRON: if (frame->arg2 == 0 || frame->arg2 > Sched::Process::EnvironmentBytes) return InvalidArgument; if (!UserMemory::Range(frame->arg1, frame->arg2, false)) return BadPointer; return Sys_SetEnviron((const char*)frame->arg1, frame->arg2); case SYS_FBINFO: if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_FbInfo((FbInfo*)frame->arg1); return Success; case SYS_FBMAP: return (int64_t)Sys_FbMap(); case SYS_FBFLIP: return Sys_FbFlip(frame->arg1, frame->arg2); case SYS_DISPLAYINFO: if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sys_DisplayInfo((DisplayInfo*)frame->arg1); case SYS_DISPLAYMODES: // The Intel driver publishes at most 32 modes. Bounding this // here also keeps the userspace range calculation overflow-free. if ((int64_t)frame->arg2 < 0 || frame->arg2 > 32) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(DisplayModeInfo), true)) return BadPointer; return Sys_DisplayModes((DisplayModeInfo*)frame->arg1, (int)frame->arg2); case SYS_DISPLAYSETMODE: if (!Sched::Consent::Require(CAP_DISPLAY_ADMIN)) return CapabilityDenied; return Sys_DisplaySetMode((int)frame->arg1); case SYS_DISPLAYBRIGHTNESS: if ((int64_t)frame->arg1 >= 0 && !Sched::Consent::Require(CAP_BRIGHTNESS)) return CapabilityDenied; return Sys_DisplayBrightness((int)frame->arg1); case SYS_GETEXECPATH: if (!UserMemory::Range(frame->arg2 ? frame->arg1 : frame->arg1, frame->arg2, true)) return BadPointer; return Sys_GetExecPath((char*)frame->arg1, frame->arg2); case SYS_STAT: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (!UserMemory::Writable(frame->arg2)) return BadPointer; return (int64_t)Sys_Stat((const char*)frame->arg1, (FileStat*)frame->arg2); case SYS_UTIME: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; return (int64_t)Sys_Utime((const char*)frame->arg1, (int64_t)frame->arg2, (int64_t)frame->arg3, frame->arg4 != 0); case SYS_TERMSIZE: return (int64_t)Sys_TermSize(); case SYS_GETARGS: if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer; return (int64_t)Sys_GetArgs((char*)frame->arg1, frame->arg2); case SYS_RESET: if (!Sched::HasCapability(CAP_POWER_CONTROL)) return CapabilityDenied; Sys_Reset(); return Success; case SYS_SHUTDOWN: if (!Sched::HasCapability(CAP_POWER_CONTROL)) return CapabilityDenied; Sys_Shutdown(); return Success; case SYS_POWER_REQUEST: if (frame->arg1 != POWER_REQ_QUERY && frame->arg1 != POWER_REQ_SHUTDOWN && frame->arg1 != POWER_REQ_REBOOT && frame->arg1 != POWER_REQ_PEEK) return InvalidArgument; if (frame->arg1 == POWER_REQ_QUERY) { if (!Sched::HasCapability(CAP_POWER_CONTROL)) return CapabilityDenied; } else if (!Sched::HasCapability(CAP_POWER_REQUEST)) { return CapabilityDenied; } return Sys_PowerRequest((int)frame->arg1); case SYS_GETTIME: if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_GetTime((DateTime*)frame->arg1); return Success; case SYS_SETUNIXTIME: if (!Sched::Consent::Require(CAP_SET_TIME)) return CapabilityDenied; return Sys_SetUnixTime((int64_t)frame->arg1); case SYS_SOCKET: return (int64_t)Sys_Socket((int)frame->arg1); case SYS_CONNECT: return (int64_t)Sys_Connect((int)frame->arg1, (uint32_t)frame->arg2, (uint16_t)frame->arg3); case SYS_BIND: return (int64_t)Sys_Bind((int)frame->arg1, (uint16_t)frame->arg2); case SYS_LISTEN: return (int64_t)Sys_Listen((int)frame->arg1); case SYS_ACCEPT: return (int64_t)Sys_Accept((int)frame->arg1); case SYS_SEND: if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return BadPointer; return (int64_t)Sys_Send((int)frame->arg1, (const uint8_t*)frame->arg2, (uint32_t)frame->arg3); case SYS_RECV: if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return BadPointer; return (int64_t)Sys_Recv((int)frame->arg1, (uint8_t*)frame->arg2, (uint32_t)frame->arg3); case SYS_CLOSESOCK: Sys_CloseSock((int)frame->arg1); return Success; case SYS_GETNETCFG: if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_GetNetCfg((NetCfg*)frame->arg1); return Success; case SYS_SETNETCFG: if (!Sched::Consent::Require(CAP_NETWORK_ADMIN)) return CapabilityDenied; if (!UserMemory::Readable(frame->arg1)) return BadPointer; return (int64_t)Sys_SetNetCfg((const NetCfg*)frame->arg1); case SYS_NETSTATUS: if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_NetStatus((NetStatus*)frame->arg1); return Success; case SYS_SENDTO: if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return BadPointer; return (int64_t)Sys_SendTo((int)frame->arg1, (const uint8_t*)frame->arg2, (uint32_t)frame->arg3, (uint32_t)frame->arg4, (uint16_t)frame->arg5); case SYS_RECVFROM: if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return BadPointer; if (!UserMemory::OptionalRange(frame->arg4, sizeof(uint32_t), true)) return BadPointer; if (!UserMemory::OptionalRange(frame->arg5, sizeof(uint16_t), true)) return BadPointer; return (int64_t)Sys_RecvFrom((int)frame->arg1, (uint8_t*)frame->arg2, (uint32_t)frame->arg3, UserMemory::IsUserPtr(frame->arg4) ? (uint32_t*)frame->arg4 : nullptr, UserMemory::IsUserPtr(frame->arg5) ? (uint16_t*)frame->arg5 : nullptr); case SYS_FWRITE: if (!UserMemory::Range(frame->arg2, frame->arg4, false)) return BadPointer; return (int64_t)Sys_FWrite((int)frame->arg1, (const uint8_t*)frame->arg2, frame->arg3, frame->arg4); case SYS_FCREATE: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; return (int64_t)Sys_FCreate((const char*)frame->arg1); case SYS_FDELETE: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; return (int64_t)Sys_FDelete((const char*)frame->arg1); case SYS_FMKDIR: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; return (int64_t)Sys_FMkdir((const char*)frame->arg1); case SYS_FRENAME: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (!UserMemory::String(frame->arg2, kMaxPathBytes)) return BadPointer; return (int64_t)Sys_FRename((const char*)frame->arg1, (const char*)frame->arg2); case SYS_DRIVELIST: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(int), true)) return BadPointer; return (int64_t)Sys_DriveList((int*)frame->arg1, (int)frame->arg2); case SYS_DRIVELABEL: if ((int64_t)frame->arg3 <= 0) return InvalidArgument; if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return BadPointer; return (int64_t)Sys_DriveLabel((int)frame->arg1, (char*)frame->arg2, (int)frame->arg3); case SYS_DRIVEKIND: return (int64_t)Sys_DriveKind((int)frame->arg1); case SYS_TERMSCALE: return Sys_TermScale(frame->arg1, frame->arg2); case SYS_RESOLVE: if (!UserMemory::String(frame->arg1, kMaxHostnameBytes)) return BadPointer; return Sys_Resolve((const char*)frame->arg1); case SYS_GETRANDOM: if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer; return Sys_GetRandom((uint8_t*)frame->arg1, frame->arg2); case SYS_LOG: if (!Sched::Consent::Require(CAP_LOG_READ)) return CapabilityDenied; if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer; return Kt::ReadKernelLogBuffer((char*)frame->arg1, frame->arg2); case SYS_MOUSESTATE: if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_MouseState((MouseState*)frame->arg1); return Success; case SYS_SETMOUSEBOUNDS: Sys_SetMouseBounds((int32_t)frame->arg1, (int32_t)frame->arg2); return Success; case SYS_SPAWN_REDIR: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return BadPointer; return (int64_t)Sys_SpawnRedir((const char*)frame->arg1, UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr); case SYS_SPAWN_REDIR_CAPS: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return BadPointer; if (!UserMemory::Readable(frame->arg3)) return BadPointer; return (int64_t)Sys_SpawnRedirCaps( (const char*)frame->arg1, frame->arg2 ? (const char*)frame->arg2 : nullptr, (const SpawnCapabilities*)frame->arg3); case SYS_CHILDIO_READ: if ((int64_t)frame->arg3 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, true)) return BadPointer; return (int64_t)Sys_ChildIoRead((int)frame->arg1, (char*)frame->arg2, (int)frame->arg3); case SYS_CHILDIO_WRITE: if ((int64_t)frame->arg3 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, false)) return BadPointer; return (int64_t)Sys_ChildIoWrite((int)frame->arg1, (const char*)frame->arg2, (int)frame->arg3); case SYS_CHILDIO_WRITEKEY: if (!UserMemory::Readable(frame->arg2)) return BadPointer; return (int64_t)Sys_ChildIoWriteKey((int)frame->arg1, (const KeyEventV1*)frame->arg2); case SYS_CHILDIO_SETTERMSZ: return (int64_t)Sys_ChildIoSetTermsz((int)frame->arg1, (int)frame->arg2, (int)frame->arg3); case SYS_WINCREATE: if (!UserMemory::String(frame->arg1, kMaxWindowTitleBytes)) return BadPointer; if (!UserMemory::Writable(frame->arg4)) return BadPointer; return (int64_t)Sys_WinCreate((const char*)frame->arg1, (int)frame->arg2, (int)frame->arg3, (WinCreateResult*)frame->arg4); case SYS_WINDESTROY: return (int64_t)Sys_WinDestroy((int)frame->arg1); case SYS_WINPRESENT: return (int64_t)Sys_WinPresent((int)frame->arg1); case SYS_WINPOLL: if (!UserMemory::Writable(frame->arg2)) return BadPointer; return (int64_t)Sys_WinPoll((int)frame->arg1, (WinEvent*)frame->arg2); case SYS_WINENUM: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WinInfo), true)) return BadPointer; return (int64_t)Sys_WinEnum((WinInfo*)frame->arg1, (int)frame->arg2); case SYS_WINMAP: return (int64_t)Sys_WinMap((int)frame->arg1); case SYS_WINUNMAP: return (int64_t)Sys_WinUnmap((int)frame->arg1); case SYS_WINSENDEVENT: if (!UserMemory::Readable(frame->arg2)) return BadPointer; return (int64_t)Sys_WinSendEvent((int)frame->arg1, (const WinEvent*)frame->arg2); case SYS_WINRESIZE: return (int64_t)Sys_WinResize((int)frame->arg1, (int)frame->arg2, (int)frame->arg3); case SYS_PROCLIST: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(ProcInfo), true)) return BadPointer; return (int64_t)Sys_ProcList((ProcInfo*)frame->arg1, (int)frame->arg2); case SYS_KILL: return (int64_t)Sys_Kill((int)frame->arg1); case SYS_SETSESSION: return (int64_t)Sys_SetSession(); case SYS_KILLSESSION: if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) return CapabilityDenied; return (int64_t)Sys_KillSession((int)frame->arg1); case SYS_DEVLIST: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(DevInfo), true)) return BadPointer; return (int64_t)Sys_DevList((DevInfo*)frame->arg1, (int)frame->arg2); case SYS_DISKINFO: if (!UserMemory::Writable(frame->arg1)) return BadPointer; return (int64_t)Sys_DiskInfo((DiskInfo*)frame->arg1, (int)frame->arg2); case SYS_WINSETSCALE: return (int64_t)Sys_WinSetScale((int)frame->arg1); case SYS_WINGETSCALE: return (int64_t)Sys_WinGetScale(); case SYS_WINSETCURSOR: return (int64_t)Sys_WinSetCursor((int)frame->arg1, (int)frame->arg2); case SYS_WINSETFLAGS: return (int64_t)Sys_WinSetFlags((int)frame->arg1, (uint32_t)frame->arg2); case SYS_MEMSTATS: if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_MemStats((MemStats*)frame->arg1); return Success; case SYS_PARTLIST: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(PartInfo), true)) return BadPointer; return (int64_t)Sys_PartList((PartInfo*)frame->arg1, (int)frame->arg2); case SYS_VOLINFO: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(VolInfo), true)) return BadPointer; return (int64_t)Sys_VolInfo((VolInfo*)frame->arg1, (int)frame->arg2); case SYS_DISKREAD: if (!Sched::HasCapability(CAP_RAW_STORAGE)) return CapabilityDenied; return (int64_t)Sys_DiskRead((int)frame->arg1, frame->arg2, (uint32_t)frame->arg3, (void*)frame->arg4); case SYS_DISKWRITE: if (!Sched::HasCapability(CAP_RAW_STORAGE)) return CapabilityDenied; return (int64_t)Sys_DiskWrite((int)frame->arg1, frame->arg2, (uint32_t)frame->arg3, (const void*)frame->arg4); case SYS_GPTINIT: if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return CapabilityDenied; return (int64_t)Sys_GptInit((int)frame->arg1); case SYS_GPTADD: if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return CapabilityDenied; if (!UserMemory::Readable(frame->arg1)) return BadPointer; return (int64_t)Sys_GptAdd((const GptAddParams*)frame->arg1); case SYS_FSMOUNT: if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return CapabilityDenied; return (int64_t)Sys_FsMount((int)frame->arg1, (int)frame->arg2); case SYS_FS_SYNC: if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return CapabilityDenied; return Sys_FsSync(); case SYS_FSFORMAT: if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return CapabilityDenied; if (!UserMemory::Readable(frame->arg1)) return BadPointer; return (int64_t)Sys_FsFormat((const FsFormatParams*)frame->arg1); case SYS_AUDIOOPEN: return Sys_AudioOpen((uint32_t)frame->arg1, (uint8_t)frame->arg2, (uint8_t)frame->arg3); case SYS_AUDIOCLOSE: return Sys_AudioClose((int)frame->arg1); case SYS_AUDIOWRITE: if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return BadPointer; return Sys_AudioWrite((int)frame->arg1, (const uint8_t*)frame->arg2, (uint32_t)frame->arg3); case SYS_AUDIOCTL: return Sys_AudioCtl((int)frame->arg1, (int)frame->arg2, (int)frame->arg3); case SYS_AUDIOLIST: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(AudioStreamInfo), true)) return BadPointer; return Sys_AudioList((AudioStreamInfo*)frame->arg1, (int)frame->arg2); case SYS_AUDIOWAIT: return Sys_AudioWait(frame->arg1, frame->arg2); case SYS_USB_LIST: { if ((int64_t)frame->arg2 < 0) return USB_ERR_INVALID; uint64_t maxCount = frame->arg2 > 16 ? 16 : frame->arg2; if (!UserMemory::Range(frame->arg1, maxCount * sizeof(UsbInterfaceInfo), true)) return USB_ERR_INVALID; return Sys_UsbList((UsbInterfaceInfo*)frame->arg1, (int)maxCount); } case SYS_USB_CLAIM: if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION; if (frame->arg1 == 0 || frame->arg1 > 16 || frame->arg2 > 255) return USB_ERR_INVALID; return Sys_UsbClaim((uint8_t)frame->arg1, (uint8_t)frame->arg2); case SYS_USB_CLOSE: return Sys_UsbClose((int)frame->arg1); case SYS_USB_CONTROL: { if (!UserMemory::Readable(frame->arg2)) return USB_ERR_INVALID; UsbControlRequest request = *(const UsbControlRequest*)frame->arg2; if (frame->arg4 > 4096 || frame->arg4 != request.length) return USB_ERR_INVALID; bool deviceToHost = (request.requestType & 0x80) != 0; if (frame->arg4 != 0 && !UserMemory::Range(frame->arg3, frame->arg4, deviceToHost)) return USB_ERR_INVALID; return Sys_UsbControl((int)frame->arg1, &request, (void*)frame->arg3, (uint32_t)frame->arg4); } case SYS_USB_BULK_IN_START: if (frame->arg2 > 0xffffffffULL || frame->arg3 > 0xffffffffULL) return USB_ERR_INVALID; return Sys_UsbBulkInStart((int)frame->arg1, (uint32_t)frame->arg2, (uint32_t)frame->arg3); case SYS_USB_BULK_IN_STOP: return Sys_UsbBulkInStop((int)frame->arg1); case SYS_USB_BULK_IN_READ: if (frame->arg3 > 0xffffffffULL) return USB_ERR_INVALID; if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return USB_ERR_INVALID; return Sys_UsbBulkInRead((int)frame->arg1, (uint8_t*)frame->arg2, (uint32_t)frame->arg3); case SYS_POWERINFO: if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sys_PowerInfo((PowerInfo*)frame->arg1); case SYS_BATTERYINFO: // Read-only power telemetry, ungated like SYS_POWERINFO: the // panel indicator has to work in an unprivileged session, which // holds no device capability to spend on it. if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sys_BatteryInfo((BatteryInfo*)frame->arg1); case SYS_GETCAPS: if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sys_GetCaps((SpawnCapabilities*)frame->arg1); case SYS_CONSENT_REGISTER: return Sched::Consent::Register(); case SYS_CONSENT_NEXT: if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sched::Consent::Next((ConsentRequest*)frame->arg1); case SYS_CONSENT_REPLY: return Sched::Consent::Reply(frame->arg1, (int)frame->arg2); case SYS_GETKEY_EX: if (frame->arg2 < sizeof(KeyEventV1) || frame->arg2 > 4096) return InvalidArgument; if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer; return Sys_GetKeyEx((void*)frame->arg1, frame->arg2); case SYS_CHILDIO_WRITEKEY_EX: if (frame->arg3 < sizeof(KeyEventV1) || frame->arg3 > 4096) return InvalidArgument; if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return BadPointer; return (int64_t)Sys_ChildIoWriteKeyEx((int)frame->arg1, (const void*)frame->arg2, frame->arg3); case SYS_THREAD_SPAWN: return Sys_ThreadSpawn(frame->arg1, frame->arg2, frame->arg3); case SYS_THREAD_EXIT: Sys_ThreadExit((int)frame->arg1); return Success; case SYS_THREAD_JOIN: if (frame->arg2 != 0 && !UserMemory::Writable(frame->arg2)) return BadPointer; return Sys_ThreadJoin((int)frame->arg1, (int*)frame->arg2); case SYS_THREAD_SELF: return Sys_ThreadSelf(); case SYS_FUTEX_WAIT: // Validation demand-commits the page so the scheduler can // read the word with its lock held without faulting. if (!UserMemory::Readable(frame->arg1)) return BadPointer; return Sys_FutexWait(frame->arg1, (uint32_t)frame->arg2, frame->arg3); case SYS_FUTEX_WAKE: return Sys_FutexWake(frame->arg1, (uint32_t)frame->arg2); case SYS_BTSCAN: if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return CapabilityDenied; if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtScanResult), true)) return BadPointer; return Sys_BtScan((BtScanResult*)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3); case SYS_BTCONNECT: if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return CapabilityDenied; if (!UserMemory::Range(frame->arg1, 6, false)) return BadPointer; return Sys_BtConnect((const uint8_t*)frame->arg1); case SYS_BTDISCONNECT: if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return CapabilityDenied; if (!UserMemory::Range(frame->arg1, 6, false)) return BadPointer; return Sys_BtDisconnect((const uint8_t*)frame->arg1); case SYS_BTSETADDR: if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return CapabilityDenied; if (!UserMemory::Range(frame->arg1, 6, false)) return BadPointer; return Sys_BtSetAddr((const uint8_t*)frame->arg1); case SYS_BTBONDS: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtBondInfo), true)) return BadPointer; return Sys_BtBonds((BtBondInfo*)frame->arg1, (int)frame->arg2); case SYS_BTFORGET: if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return CapabilityDenied; if (!UserMemory::Range(frame->arg1, 6, false)) return BadPointer; return Sys_BtForget((const uint8_t*)frame->arg1); case SYS_BTLIST: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtDevInfo), true)) return BadPointer; return Sys_BtList((BtDevInfo*)frame->arg1, (int)frame->arg2); case SYS_BTINFO: if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sys_BtInfo((BtAdapterInfo*)frame->arg1); case SYS_WIFI_SCAN: if (!Sched::Consent::Require(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION; if ((int64_t)frame->arg2 < 0) return -1; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WifiNetwork), true)) return -1; return Sys_WifiScan((WifiNetwork*)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3); case SYS_WIFI_INFO: if (!UserMemory::Writable(frame->arg1)) return -1; return Sys_WifiInfo((WifiInfo*)frame->arg1); case SYS_WIFI_CONNECT: if (!Sched::Consent::Require(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION; if (!UserMemory::String(frame->arg1, 64)) return -1; if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, 128)) return -1; return Sys_WifiConnect((const char*)frame->arg1, (const char*)frame->arg2); case SYS_WIFI_DISCONNECT: if (!Sched::Consent::Require(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION; return Sys_WifiDisconnect(); case SYS_WIFI_SCAN_START: if (!Sched::Consent::Require(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION; return Sys_WifiScanStart((uint32_t)frame->arg1); case SYS_WIFI_RESULTS: if ((int64_t)frame->arg2 < 0) return -1; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WifiNetwork), true)) return -1; return Sys_WifiResults((WifiNetwork*)frame->arg1, (int)frame->arg2); case SYS_WIFI_CONNECT_ASYNC: if (!Sched::Consent::Require(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION; if (!UserMemory::String(frame->arg1, 64)) return -1; if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, 128)) return -1; return Sys_WifiConnectAsync((const char*)frame->arg1, (const char*)frame->arg2); case SYS_NETIFS: if ((int64_t)frame->arg2 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(NetIfInfo), true)) return BadPointer; return Sys_NetIfs((NetIfInfo*)frame->arg1, (int)frame->arg2); case SYS_SUSPEND: if (!Sched::HasCapability(CAP_SUSPEND)) return CapabilityDenied; return Sys_Suspend(); case SYS_SETTZ: if (!Sched::Consent::Require(CAP_SET_TIME)) return CapabilityDenied; return Sys_SetTZ((int32_t)frame->arg1); case SYS_GETTZ: return Sys_GetTZ(); case SYS_SETUSER: if (!Sched::HasCapability(CAP_USER_ADMIN)) return CapabilityDenied; if (!UserMemory::String(frame->arg2, kMaxUserNameBytes)) return BadPointer; return Sys_SetUser((int)frame->arg1, (const char*)frame->arg2); case SYS_GETUSER: if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer; return Sys_GetUser((char*)frame->arg1, frame->arg2); case SYS_GETCWD: if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer; return Sys_GetCwd((char*)frame->arg1, frame->arg2); case SYS_CHDIR: if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer; return Sys_Chdir((const char*)frame->arg1); case SYS_DUPHANDLE: return Sys_DupHandle((int)frame->arg1); case SYS_WAIT_HANDLE: return (int64_t)Sys_WaitHandle((int)frame->arg1, (uint32_t)frame->arg2, frame->arg3); case SYS_STREAM_CREATE: if (!UserMemory::Writable(frame->arg1) || !UserMemory::Writable(frame->arg2)) return BadPointer; return Sys_StreamCreate((int*)frame->arg1, (int*)frame->arg2, (uint32_t)frame->arg3); case SYS_STREAM_READ: if ((int64_t)frame->arg3 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, true)) return BadPointer; return Sys_StreamRead((int)frame->arg1, (uint8_t*)frame->arg2, (int)frame->arg3); case SYS_STREAM_WRITE: if ((int64_t)frame->arg3 < 0) return InvalidArgument; if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, false)) return BadPointer; return Sys_StreamWrite((int)frame->arg1, (const uint8_t*)frame->arg2, (int)frame->arg3); case SYS_MAILBOX_CREATE: if (!UserMemory::Writable(frame->arg1) || !UserMemory::Writable(frame->arg2)) return BadPointer; return Sys_MailboxCreate((int*)frame->arg1, (int*)frame->arg2); case SYS_MAILBOX_SEND: if (frame->arg4 > 0 && !UserMemory::Range(frame->arg3, frame->arg4, false)) return BadPointer; return Sys_MailboxSend((int)frame->arg1, (uint32_t)frame->arg2, (const void*)frame->arg3, (uint16_t)frame->arg4, (int)frame->arg5); case SYS_MAILBOX_RECV: if (!UserMemory::Writable(frame->arg4)) return BadPointer; if (frame->arg2 != 0 && !UserMemory::Writable(frame->arg2)) return BadPointer; if (frame->arg3 != 0) { uint16_t maxCopy = *(uint16_t*)frame->arg4; if (!UserMemory::Range(frame->arg3, maxCopy, true)) return BadPointer; } if (frame->arg5 != 0 && !UserMemory::Writable(frame->arg5)) return BadPointer; return Sys_MailboxRecv((int)frame->arg1, UserMemory::IsUserPtr(frame->arg2) ? (uint32_t*)frame->arg2 : nullptr, UserMemory::IsUserPtr(frame->arg3) ? (void*)frame->arg3 : nullptr, (uint16_t*)frame->arg4, UserMemory::IsUserPtr(frame->arg5) ? (int*)frame->arg5 : nullptr); case SYS_WAITSET_CREATE: return Sys_WaitsetCreate(); case SYS_WAITSET_ADD: return Sys_WaitsetAdd((int)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3); case SYS_WAITSET_REMOVE: return Sys_WaitsetRemove((int)frame->arg1, (int)frame->arg2); case SYS_WAITSET_WAIT: if (frame->arg2 != 0 && !UserMemory::Writable(frame->arg2)) return BadPointer; return Sys_WaitsetWait((int)frame->arg1, UserMemory::IsUserPtr(frame->arg2) ? (IpcWaitResult*)frame->arg2 : nullptr, frame->arg3); case SYS_PROC_OPEN: return Sys_ProcOpen((int)frame->arg1); case SYS_SURFACE_CREATE: return Sys_SurfaceCreate(frame->arg1); case SYS_SURFACE_MAP: return (int64_t)Sys_SurfaceMap((int)frame->arg1); case SYS_SURFACE_RESIZE: return Sys_SurfaceResize((int)frame->arg1, frame->arg2); case SYS_LOAD_LIB: if (!UserMemory::String(frame->arg1, 128)) return 0; return (int64_t)Sys_LoadLib((const char*)frame->arg1); case SYS_UNLOAD_LIB: return Sys_UnloadLib(frame->arg1); case SYS_DLSYM: return (int64_t)Sys_DLSym(frame->arg1, frame->arg2); case SYS_GETLIBBASE: return (int64_t)Sys_GetLibBase(frame->arg1); case SYS_CRASH_REPORT: if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sys_CrashReport((CrashReportInfo*)frame->arg1); case SYS_CLIPBOARD_SET_TEXT: if (frame->arg2 > 0 && !UserMemory::Range(frame->arg1, frame->arg2, false)) return BadPointer; return Sys_ClipboardSetText((const char*)frame->arg1, (uint32_t)frame->arg2); case SYS_CLIPBOARD_GET_INFO: if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sys_ClipboardGetInfo((ClipboardInfo*)frame->arg1); case SYS_CLIPBOARD_GET_TEXT: if (frame->arg2 > 0 && !UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer; if (!UserMemory::Writable(frame->arg3)) return BadPointer; if (frame->arg4 != 0 && !UserMemory::Writable(frame->arg4)) return BadPointer; return Sys_ClipboardGetText(UserMemory::IsUserPtr(frame->arg1) ? (char*)frame->arg1 : nullptr, (uint32_t)frame->arg2, (uint32_t*)frame->arg3, UserMemory::IsUserPtr(frame->arg4) ? (uint64_t*)frame->arg4 : nullptr); case SYS_CLIPBOARD_CLEAR: return Sys_ClipboardClear(); case SYS_INPUT_WAIT: return (int64_t)Sys_InputWait(frame->arg1, frame->arg2); case SYS_LOG_WRITE: { if (!UserMemory::String(frame->arg1, kMaxUserspaceLogEntryBytes)) return BadPointer; auto* process = Sched::GetCurrentProcessPtr(); const char* username = process != nullptr && process->user[0] != '\0' ? process->user : "unknown"; const char* imageName = process != nullptr && process->name[0] != '\0' ? process->name : "unknown"; Kt::UserspaceLogStream(imageName, username) << (const char*)frame->arg1; return Success; } case SYS_TERMINAL_ATTACHED: return Sys_TerminalAttached(); default: return BadInvocation; } } // ---- SYSCALL MSR initialization ---- void InitializeSyscalls() { // Enable SYSCALL/SYSRET and no-execute page permissions in EFER. uint64_t efer = Hal::ReadMSR(Hal::IA32_EFER); efer |= 1 | (1ULL << 11); // SCE | NXE Hal::WriteMSR(Hal::IA32_EFER, efer); // STAR: kernel CS in [47:32], sysret base in [63:48] // SYSCALL: CS=0x08, SS=0x10 // SYSRET: CS=0x10+16=0x20|RPL3=0x23, SS=0x10+8=0x18|RPL3=0x1B uint64_t star = (0x0010ULL << 48) | (0x0008ULL << 32); Hal::WriteMSR(Hal::IA32_STAR, star); // LSTAR: SYSCALL entry point Hal::WriteMSR(Hal::IA32_LSTAR, (uint64_t)SyscallEntry); // FMASK: mask IF on SYSCALL entry (bit 9 = IF) Hal::WriteMSR(Hal::IA32_FMASK, 0x200); Kt::KernelLogStream(Kt::OK, "Syscall") << "SYSCALL/SYSRET initialized (LSTAR=" << kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", " << (SYS_SPAWN_REDIR_CAPS + 1) << " syscall slots)"; } }