diff --git a/kernel/src/Api/Audio.hpp b/kernel/src/Api/Audio.hpp index bf495bf..179b8bd 100644 --- a/kernel/src/Api/Audio.hpp +++ b/kernel/src/Api/Audio.hpp @@ -36,12 +36,12 @@ namespace montauk::abi { bitsPerSample, pid, name); } - return -1; + return NoDevice; } static int64_t Sys_AudioClose(int handle) { Drivers::Audio::Mixer::Close(handle); - return 0; + return Success; } static int64_t Sys_AudioWrite(int handle, const uint8_t* data, uint32_t size) { diff --git a/kernel/src/Api/BluetoothSyscall.hpp b/kernel/src/Api/BluetoothSyscall.hpp index bd7a385..5bd2403 100644 --- a/kernel/src/Api/BluetoothSyscall.hpp +++ b/kernel/src/Api/BluetoothSyscall.hpp @@ -18,8 +18,8 @@ namespace montauk::abi { static int64_t Sys_BtScan(BtScanResult* buf, int maxCount, uint32_t timeoutMs) { - if (!buf || maxCount <= 0) return -1; - if (!Drivers::USB::Bluetooth::IsInitialized()) return -1; + if (!buf || maxCount <= 0) return InvalidArgument; + if (!Drivers::USB::Bluetooth::IsInitialized()) return NoDevice; // Use a stack buffer for kernel-side results (max 16) Drivers::USB::Bluetooth::Hci::InquiryDevice tmpBuf[16]; @@ -46,15 +46,15 @@ namespace montauk::abi { } static int64_t Sys_BtConnect(const uint8_t* bdAddr) { - if (!bdAddr) return -1; - if (!Drivers::USB::Bluetooth::IsInitialized()) return -1; + if (!bdAddr) return InvalidArgument; + if (!Drivers::USB::Bluetooth::IsInitialized()) return NoDevice; return (int64_t)Drivers::USB::Bluetooth::Connect(bdAddr); } static int64_t Sys_BtDisconnect(const uint8_t* bdAddr) { - if (!bdAddr) return -1; - if (!Drivers::USB::Bluetooth::IsInitialized()) return -1; + if (!bdAddr) return InvalidArgument; + if (!Drivers::USB::Bluetooth::IsInitialized()) return NoDevice; Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec << (uint64_t)Sched::GetCurrentPid() @@ -63,11 +63,11 @@ namespace montauk::abi { } static int64_t Sys_BtSetAddr(const uint8_t* bdAddr) { - if (!bdAddr) return -1; - if (!Drivers::USB::Bluetooth::IsInitialized()) return -1; + if (!bdAddr) return InvalidArgument; + if (!Drivers::USB::Bluetooth::IsInitialized()) return NoDevice; // bdAddr is a 6-byte buffer, addr[0] = least-significant octet. - return Drivers::USB::Bluetooth::SetAddress(bdAddr) ? 0 : -1; + return Drivers::USB::Bluetooth::SetAddress(bdAddr) ? Success : GenericFailure; } static int64_t Sys_BtBonds(BtBondInfo* buf, int maxCount) { @@ -90,8 +90,8 @@ namespace montauk::abi { } static int64_t Sys_BtForget(const uint8_t* bdAddr) { - if (!bdAddr) return -1; - if (!Drivers::USB::Bluetooth::IsInitialized()) return -1; + if (!bdAddr) return InvalidArgument; + if (!Drivers::USB::Bluetooth::IsInitialized()) return NoDevice; return (int64_t)Drivers::USB::Bluetooth::ForgetDevice(bdAddr); } @@ -119,13 +119,13 @@ namespace montauk::abi { } static int64_t Sys_BtInfo(BtAdapterInfo* buf) { - if (!buf) return -1; + if (!buf) return InvalidArgument; memset(buf, 0, sizeof(BtAdapterInfo)); if (!Drivers::USB::Bluetooth::IsInitialized()) { buf->initialized = 0; - return -1; + return NoDevice; } buf->initialized = 1; @@ -138,7 +138,7 @@ namespace montauk::abi { for (; i < 63 && name[i]; i++) buf->name[i] = name[i]; buf->name[i] = '\0'; - return 0; + return Success; } } diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index 2fb68a4..377d126 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 251 +#define MONTAUK_BUILD_NUMBER 254 diff --git a/kernel/src/Api/Clipboard.hpp b/kernel/src/Api/Clipboard.hpp index 3f959bd..24b53e0 100644 --- a/kernel/src/Api/Clipboard.hpp +++ b/kernel/src/Api/Clipboard.hpp @@ -90,22 +90,24 @@ namespace montauk::abi { static int Sys_ClipboardSetText(const char* data, uint32_t len) { if (len == 0) return Sys_ClipboardClear(); - if (data == nullptr || len > CLIPBOARD_MAX_TEXT_BYTES || Memory::g_heap == nullptr) - return -1; + if (data == nullptr || len > CLIPBOARD_MAX_TEXT_BYTES) + return InvalidArgument; + if (Memory::g_heap == nullptr) + return OutOfMemory; for (uint32_t i = 0; i < len; i++) { if (data[i] == '\0') - return -1; + return InvalidArgument; } const char* currentUser = ClipboardCurrentUser(); - if (currentUser == nullptr) return -1; + if (currentUser == nullptr) return GenericFailure; char user[32]; ClipboardCopyUser(user, currentUser); char* newText = (char*)Memory::g_heap->Request(len + 1); - if (newText == nullptr) return -1; + if (newText == nullptr) return OutOfMemory; for (uint32_t i = 0; i < len; i++) newText[i] = data[i]; newText[len] = '\0'; @@ -129,18 +131,18 @@ namespace montauk::abi { if (oldText != nullptr) Memory::g_heap->Free(oldText); - return 0; + return Success; } static int Sys_ClipboardGetInfo(ClipboardInfo* out) { - if (out == nullptr) return -1; + if (out == nullptr) return InvalidArgument; out->textBytes = 0; out->_pad = 0; out->serial = 0; const char* currentUser = ClipboardCurrentUser(); - if (currentUser == nullptr) return -1; + if (currentUser == nullptr) return GenericFailure; char user[32]; ClipboardCopyUser(user, currentUser); @@ -152,17 +154,17 @@ namespace montauk::abi { out->serial = slot->serial; } g_clipboardLock.Release(); - return 0; + return Success; } static int Sys_ClipboardGetText(char* buffer, uint32_t bufferLen, uint32_t* outLen, uint64_t* outSerial) { - if (outLen == nullptr) return -1; + if (outLen == nullptr) return InvalidArgument; *outLen = 0; if (outSerial != nullptr) *outSerial = 0; const char* currentUser = ClipboardCurrentUser(); - if (currentUser == nullptr) return -1; + if (currentUser == nullptr) return GenericFailure; char user[32]; ClipboardCopyUser(user, currentUser); @@ -173,25 +175,25 @@ namespace montauk::abi { if (slot != nullptr && outSerial != nullptr) *outSerial = slot->serial; g_clipboardLock.Release(); - return 0; + return Success; } *outLen = slot->len; if (outSerial != nullptr) *outSerial = slot->serial; if (buffer == nullptr || bufferLen < slot->len) { g_clipboardLock.Release(); - return -1; + return BufferTooSmall; } for (uint32_t i = 0; i < slot->len; i++) buffer[i] = slot->text[i]; g_clipboardLock.Release(); - return 0; + return Success; } static int Sys_ClipboardClear() { const char* currentUser = ClipboardCurrentUser(); - if (currentUser == nullptr) return -1; + if (currentUser == nullptr) return GenericFailure; char user[32]; ClipboardCopyUser(user, currentUser); @@ -214,7 +216,7 @@ namespace montauk::abi { if (oldText != nullptr) Memory::g_heap->Free(oldText); - return 0; + return Success; } } diff --git a/kernel/src/Api/Device.hpp b/kernel/src/Api/Device.hpp index fb28000..0ad9906 100644 --- a/kernel/src/Api/Device.hpp +++ b/kernel/src/Api/Device.hpp @@ -225,10 +225,10 @@ namespace montauk::abi { } static int Sys_DiskInfo(DiskInfo* buf, int blockDevIndex) { - if (buf == nullptr) return -1; + if (buf == nullptr) return InvalidArgument; auto* bdev = Drivers::Storage::GetBlockDevice(blockDevIndex); - if (!bdev) return -1; + if (!bdev) return NoDevice; // Zero the struct first so SATA-specific fields default to 0 for (unsigned i = 0; i < sizeof(DiskInfo); i++) @@ -303,8 +303,8 @@ namespace montauk::abi { } // If type is still 0, this block device is not recognized - if (buf->type == 0) return -1; + if (buf->type == 0) return UnsupportedOp; - return 0; + return Success; } }; diff --git a/kernel/src/Api/Filesystem.hpp b/kernel/src/Api/Filesystem.hpp index 3311a08..fb69bcf 100644 --- a/kernel/src/Api/Filesystem.hpp +++ b/kernel/src/Api/Filesystem.hpp @@ -20,6 +20,8 @@ #include namespace montauk::abi { + // Writes refused here return SystemIntegrityError: the path is protected + // by Fs/ProtectedPaths, not merely gated on an ordinary capability. static bool CanModifyFilePath(const char* resolved) { uint64_t required = Fs::RequiredFileWriteCapability(resolved); return required == 0 || Sched::HasCapability(required); @@ -27,7 +29,7 @@ namespace montauk::abi { static int Sys_Open(const char* path) { char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; return Ipc::OpenFileHandle(resolved); } @@ -53,8 +55,8 @@ namespace montauk::abi { static int Sys_ReadDirAt(const char* path, const char** outNames, int maxEntries, int startIndex) { char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; - if (startIndex < 0) return -1; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; + if (startIndex < 0) return InvalidArgument; // Get entries from VFS into a kernel-local array const char* kernelNames[256]; @@ -65,7 +67,7 @@ namespace montauk::abi { // Allocate a user-accessible page for string data via process heap auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr) return -1; + if (proc == nullptr) return GenericFailure; // Use a rotating ring of scratch pages below the heap instead of // extending the heap high-water mark on every call. This keeps repeated @@ -80,11 +82,11 @@ namespace montauk::abi { if (physAddr == 0) { void* page = Memory::g_pfa->AllocateZeroed(); - if (page == nullptr) return -1; + if (page == nullptr) return OutOfMemory; physAddr = Memory::SubHHDM((uint64_t)page); if (!Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa)) { Memory::g_pfa->Free(page); - return -1; + return OutOfMemory; } pageBuf = (uint8_t*)page; } else { @@ -113,25 +115,25 @@ namespace montauk::abi { static int Sys_FCreate(const char* path) { char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; - if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; + if (!CanModifyFilePath(resolved)) return SystemIntegrityError; return Ipc::CreateFileHandle(resolved); } static int Sys_FDelete(const char* path) { char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; - if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; + if (!CanModifyFilePath(resolved)) return SystemIntegrityError; return Fs::Vfs::VfsDelete(resolved); } static int Sys_Stat(const char* path, FileStat* out) { - if (out == nullptr) return -1; + if (out == nullptr) return InvalidArgument; char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; Fs::Vfs::StatInfo info; - if (Fs::Vfs::VfsStat(resolved, info) != 0) return -1; + if (Fs::Vfs::VfsStat(resolved, info) != 0) return NoSuchPath; out->size = info.size; out->mtime = info.mtime; @@ -139,38 +141,46 @@ namespace montauk::abi { out->atime = info.atime; out->mode = info.mode; out->isDir = info.isDir ? 1u : 0u; - return 0; + return Success; } static int Sys_Utime(const char* path, int64_t atime, int64_t mtime, bool useCurrent) { char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; // Timestamps are file state like any other: a protected path must not // be mutable through a side door that skips the write check. - if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION; + if (!CanModifyFilePath(resolved)) return SystemIntegrityError; if (useCurrent) { int64_t now = Timekeeping::GetUnixTimestamp(); atime = now; mtime = now; } - return Fs::Vfs::VfsSetTimes(resolved, atime, mtime); + // VfsSetTimes has its own codes: -1 not found, -2 unsupported by the + // filesystem, -3 timestamp out of range, -4 I/O error. + switch (Fs::Vfs::VfsSetTimes(resolved, atime, mtime)) { + case 0: return Success; + case -2: return UnsupportedOp; + case -3: return InvalidArgument; + case -4: return IoFailure; + default: return NoSuchPath; + } } static int Sys_FMkdir(const char* path) { char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; - if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; + if (!CanModifyFilePath(resolved)) return SystemIntegrityError; return Fs::Vfs::VfsMkdir(resolved); } static int Sys_FRename(const char* oldPath, const char* newPath) { char resolvedOld[256]; char resolvedNew[256]; - if (!ResolveProcessPath(oldPath, resolvedOld, sizeof(resolvedOld))) return -1; - if (!ResolveProcessPath(newPath, resolvedNew, sizeof(resolvedNew))) return -1; + if (!ResolveProcessPath(oldPath, resolvedOld, sizeof(resolvedOld))) return InvalidArgument; + if (!ResolveProcessPath(newPath, resolvedNew, sizeof(resolvedNew))) return InvalidArgument; if (!CanModifyFilePath(resolvedOld) || !CanModifyFilePath(resolvedNew)) - return SYS_ERR_PERMISSION; + return SystemIntegrityError; return Fs::Vfs::VfsRename(resolvedOld, resolvedNew); } diff --git a/kernel/src/Api/Graphics.hpp b/kernel/src/Api/Graphics.hpp index 4a04cee..fb3304b 100644 --- a/kernel/src/Api/Graphics.hpp +++ b/kernel/src/Api/Graphics.hpp @@ -118,18 +118,18 @@ namespace montauk::abi { if ((int64_t)index == -1) { return GPU::FlipSupported() ? 1 : 0; } - if (!GPU::FlipSupported()) return -1; + if (!GPU::FlipSupported()) return UnsupportedOp; auto* proc = Sched::GetCurrentProcessPtr(); if ((int64_t)index == -2) { - return proc != nullptr ? GPU::AcquireFlip(proc->pid) : -1; + return proc != nullptr ? GPU::AcquireFlip(proc->pid) : GenericFailure; } - if (proc == nullptr) return -1; + if (proc == nullptr) return GenericFailure; return GPU::Flip((int)index, (flags & 1) != 0, proc->pid); } static int64_t Sys_DisplayInfo(DisplayInfo* out) { - return Drivers::Graphics::IntelGPU::GetDisplayInfo(out) ? 0 : -1; + return Drivers::Graphics::IntelGPU::GetDisplayInfo(out) ? Success : NoDevice; } static int64_t Sys_DisplayModes(DisplayModeInfo* out, int maxCount) { @@ -137,7 +137,12 @@ namespace montauk::abi { } static int64_t Sys_DisplaySetMode(int modeIndex) { - return Drivers::Graphics::IntelGPU::SetDisplayMode(modeIndex); + // The driver answers -2 for a mode that would need link retraining + // and -3 for a modeset that failed and was rolled back. + int64_t rc = Drivers::Graphics::IntelGPU::SetDisplayMode(modeIndex); + if (rc == -2) return UnsupportedOp; + if (rc == -3) return IoFailure; + return rc; } static int64_t Sys_DisplayBrightness(int percent) { diff --git a/kernel/src/Api/Heap.hpp b/kernel/src/Api/Heap.hpp index a4e149f..4d8ae64 100644 --- a/kernel/src/Api/Heap.hpp +++ b/kernel/src/Api/Heap.hpp @@ -211,9 +211,9 @@ namespace montauk::abi { auto* proc = Sched::GetCurrentProcessPtr(); int slot = GetCurrentSlot(); if (proc == nullptr || slot < 0 || size == 0 || (addr & 0xFFFULL) != 0 || - size > UINT64_MAX - 0xFFFULL) return -1; + size > UINT64_MAX - 0xFFFULL) return InvalidArgument; size = (size + 0xFFFULL) & ~0xFFFULL; - if (addr > UINT64_MAX - size) return -1; + if (addr > UINT64_MAX - size) return InvalidArgument; g_heapLocks[slot].Acquire(); uint64_t firstPage = 0; @@ -229,7 +229,7 @@ namespace montauk::abi { } link = &area->next; } - if (*link == nullptr) { g_heapLocks[slot].Release(); return -1; } + if (*link == nullptr) { g_heapLocks[slot].Release(); return InvalidArgument; } HeapAlloc* area = *link; uint64_t oldPages = area->numPages; @@ -262,7 +262,7 @@ namespace montauk::abi { g_heapLocks[slot].Acquire(); Sched::g_allocatedPages[slot] -= resident; g_heapLocks[slot].Release(); - return 0; + return Success; } inline int64_t Sys_Protect(uint64_t addr, uint64_t size, uint64_t prot) { @@ -272,9 +272,9 @@ namespace montauk::abi { size > UINT64_MAX - 0xFFFULL || (prot & ~(VmProtRead | VmProtWrite | VmProtExec)) != 0 || (prot & VmProtRead) == 0 || - ((prot & VmProtWrite) != 0 && (prot & VmProtExec) != 0)) return -1; + ((prot & VmProtWrite) != 0 && (prot & VmProtExec) != 0)) return InvalidArgument; size = (size + 0xFFFULL) & ~0xFFFULL; - if (addr > UINT64_MAX - size) return -1; + if (addr > UINT64_MAX - size) return InvalidArgument; g_heapLocks[slot].Acquire(); uint64_t pages = size / 0x1000ULL; @@ -293,7 +293,7 @@ namespace montauk::abi { break; } } - if (!covered) { g_heapLocks[slot].Release(); return -1; } + if (!covered) { g_heapLocks[slot].Release(); return InvalidArgument; } } uint64_t end = addr + size; @@ -343,12 +343,12 @@ namespace montauk::abi { addr + i * 0x1000ULL, writable, executable)) { g_heapLocks[slot].Release(); - return -1; + return GenericFailure; } } g_heapLocks[slot].Release(); Memory::ShootdownUserRange(proc->pml4Phys, addr, (uint32_t)pages); - return 0; + return Success; } // Resolve a non-present access inside an anonymous VMA. This is used for diff --git a/kernel/src/Api/IoRedir.hpp b/kernel/src/Api/IoRedir.hpp index b8d1726..6974e95 100644 --- a/kernel/src/Api/IoRedir.hpp +++ b/kernel/src/Api/IoRedir.hpp @@ -19,18 +19,18 @@ namespace montauk::abi { const char* path, const char* args, const SpawnCapabilities* capabilities) { char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; int parentSlot = Ipc::CurrentSlot(); int childPid = Sched::Spawn(resolved, args, false, nullptr, 0, capabilities); - if (childPid < 0) return -1; + if (childPid < 0) return childPid; auto* child = Sched::GetProcessByPid(childPid); int childSlot = Ipc::SlotForPid(childPid); if (child == nullptr || childSlot < 0 || parentSlot < 0) { Sched::KillProcess(childPid); - return -1; + return GenericFailure; } Ipc::Stream* outStream = Ipc::CreateStream(RedirOutputStreamCapacity); @@ -41,7 +41,7 @@ namespace montauk::abi { if (inStream != nullptr) Ipc::ReleaseStream(inStream, false, false); if (keyMailbox != nullptr) Ipc::ReleaseMailbox(keyMailbox, false, false); Sched::KillProcess(childPid); - return -1; + return OutOfMemory; } int parentOutHandle = Ipc::InstallHandleForSlot( @@ -71,7 +71,7 @@ namespace montauk::abi { if (child->ioInHandle < 0) Ipc::ReleaseStream(inStream, false, false); if (child->ioKeyHandle < 0) Ipc::ReleaseMailbox(keyMailbox, false, false); Sched::KillProcess(childPid); - return -1; + return GenericFailure; } child->redirected = true; @@ -80,7 +80,7 @@ namespace montauk::abi { child->termRows = 25; if (Sched::StartProcess(childPid) < 0) { Sched::KillProcess(childPid); - return -1; + return GenericFailure; } return childPid; @@ -93,11 +93,11 @@ namespace montauk::abi { static int Sys_SpawnRedirCaps(const char* path, const char* args, const SpawnCapabilities* requested) { auto* parent = Sched::GetCurrentProcessPtr(); - if (parent == nullptr || requested == nullptr) return -1; + if (parent == nullptr || requested == nullptr) return InvalidArgument; SpawnCapabilities copy = *requested; if (!ValidCapabilityDelegation(copy, parent->delegableCaps)) { - return SYS_ERR_PERMISSION; + return CapabilityDenied; } return Sys_SpawnRedirInternal(path, args, ©); } @@ -105,51 +105,51 @@ namespace montauk::abi { static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) { auto* child = Sched::GetProcessByPid(childPid); if (child == nullptr || child->parentPid != Sched::GetCurrentPid()) - return SYS_ERR_PERMISSION; + return PermissionDenied; Ipc::HandleSnapshot snapshot; Ipc::Stream* stream = GetRedirOutStream(child, snapshot); - if (!child->redirected || stream == nullptr) return -1; + if (!child->redirected || stream == nullptr) return InvalidArgument; return Ipc::StreamRead(stream, (uint8_t*)buf, maxLen, true); } static int Sys_ChildIoWrite(int childPid, const char* data, int len) { auto* child = Sched::GetProcessByPid(childPid); if (child == nullptr || child->parentPid != Sched::GetCurrentPid()) - return SYS_ERR_PERMISSION; + return PermissionDenied; Ipc::HandleSnapshot snapshot; Ipc::Stream* stream = GetRedirInStream(child, snapshot); - if (!child->redirected || stream == nullptr) return -1; + if (!child->redirected || stream == nullptr) return InvalidArgument; return WriteAllToStream(stream, (const uint8_t*)data, len); } static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) { - if (key == nullptr) return -1; + if (key == nullptr) return InvalidArgument; auto* child = Sched::GetProcessByPid(childPid); if (child == nullptr || child->parentPid != Sched::GetCurrentPid()) - return SYS_ERR_PERMISSION; + return PermissionDenied; Ipc::HandleSnapshot snapshot; Ipc::Mailbox* mailbox = GetRedirKeyMailbox(child, snapshot); - if (!child->redirected || mailbox == nullptr) return -1; + if (!child->redirected || mailbox == nullptr) return InvalidArgument; for (;;) { uint64_t observedWake = Sched::ObserveObjectWake(mailbox); int rc = Ipc::MailboxSend(mailbox, 0, key, sizeof(KeyEvent)); - if (rc < 0) return -1; + if (rc < 0) return GenericFailure; if (rc == 0) { Sched::BlockOnObjectSince(mailbox, 0, observedWake); continue; } - return 0; + return Success; } } static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) { auto* child = Sched::GetProcessByPid(childPid); if (child == nullptr || child->parentPid != Sched::GetCurrentPid()) - return SYS_ERR_PERMISSION; - if (!child->redirected) return -1; + return PermissionDenied; + if (!child->redirected) return InvalidArgument; child->termCols = cols; child->termRows = rows; - return 0; + return Success; } }; diff --git a/kernel/src/Api/IpcSyscall.hpp b/kernel/src/Api/IpcSyscall.hpp index 2f2df34..1ba579b 100644 --- a/kernel/src/Api/IpcSyscall.hpp +++ b/kernel/src/Api/IpcSyscall.hpp @@ -20,15 +20,15 @@ namespace montauk::abi { } static int Sys_StreamCreate(int* outReadHandle, int* outWriteHandle, uint32_t capacity) { - if (outReadHandle == nullptr || outWriteHandle == nullptr) return -1; + if (outReadHandle == nullptr || outWriteHandle == nullptr) return InvalidArgument; int readHandle = -1; int writeHandle = -1; - if (Ipc::CreateStreamHandlePair(capacity, readHandle, writeHandle) < 0) return -1; + if (Ipc::CreateStreamHandlePair(capacity, readHandle, writeHandle) < 0) return GenericFailure; *outReadHandle = readHandle; *outWriteHandle = writeHandle; - return 0; + return Success; } static int Sys_StreamRead(int handle, uint8_t* buffer, int maxLen) { @@ -40,15 +40,15 @@ namespace montauk::abi { } static int Sys_MailboxCreate(int* outSendHandle, int* outRecvHandle) { - if (outSendHandle == nullptr || outRecvHandle == nullptr) return -1; + if (outSendHandle == nullptr || outRecvHandle == nullptr) return InvalidArgument; int sendHandle = -1; int recvHandle = -1; - if (Ipc::CreateMailboxHandlePair(sendHandle, recvHandle) < 0) return -1; + if (Ipc::CreateMailboxHandlePair(sendHandle, recvHandle) < 0) return GenericFailure; *outSendHandle = sendHandle; *outRecvHandle = recvHandle; - return 0; + return Success; } static int Sys_MailboxSend(int handle, uint32_t msgType, const void* data, uint16_t len, int attachHandle) { diff --git a/kernel/src/Api/LibSyscall.hpp b/kernel/src/Api/LibSyscall.hpp index 5341235..19d4853 100644 --- a/kernel/src/Api/LibSyscall.hpp +++ b/kernel/src/Api/LibSyscall.hpp @@ -115,15 +115,15 @@ namespace montauk::abi { // Decrements refcount; actually unmaps when refcount reaches 0. inline int Sys_UnloadLib(uint64_t handle) { int slot = GetCurrentSlot(); - if (slot < 0) return -1; + if (slot < 0) return GenericFailure; int libSlot = (int)handle - 1; - if (libSlot < 0 || libSlot >= MaxLibsPerProcess) return -1; + if (libSlot < 0 || libSlot >= MaxLibsPerProcess) return InvalidArgument; g_libLocks[slot].Acquire(); if (!g_libTable[slot][libSlot].inUse) { g_libLocks[slot].Release(); - return -1; + return InvalidArgument; } g_libTable[slot][libSlot].refcount--; @@ -144,7 +144,7 @@ namespace montauk::abi { } g_libLocks[slot].Release(); - return 0; + return Success; } // Resolve a symbol in a loaded library. diff --git a/kernel/src/Api/Net.hpp b/kernel/src/Api/Net.hpp index 9924175..37a3bc6 100644 --- a/kernel/src/Api/Net.hpp +++ b/kernel/src/Api/Net.hpp @@ -164,7 +164,7 @@ namespace montauk::abi { // wired and wireless status apart: the IP configuration is global, so the // only way to say which interface owns it is `active`. static int Sys_NetIfs(NetIfInfo* out, int maxCount) { - if (out == nullptr || maxCount <= 0) return -1; + if (out == nullptr || maxCount <= 0) return InvalidArgument; const auto* active = ::Net::NetIf::Active(); int count = ::Net::NetIf::Count(); @@ -193,12 +193,12 @@ namespace montauk::abi { } static int Sys_SetNetCfg(const NetCfg* in) { - if (in == nullptr) return -1; + if (in == nullptr) return InvalidArgument; Net::SetIpAddress(in->ipAddress); Net::SetSubnetMask(in->subnetMask); Net::SetGateway(in->gateway); Net::SetDnsServer(in->dnsServer); - return 0; + return Success; } // ---- DNS resolve ---- diff --git a/kernel/src/Api/Power.hpp b/kernel/src/Api/Power.hpp index e347716..a9995d9 100644 --- a/kernel/src/Api/Power.hpp +++ b/kernel/src/Api/Power.hpp @@ -49,7 +49,7 @@ namespace montauk::abi { Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec << (uint64_t)Sched::GetCurrentPid() << " posted graceful power request " << (uint64_t)action; - return 0; + return Success; } static void Sys_Reset() { @@ -88,7 +88,7 @@ namespace montauk::abi { static int64_t Sys_Suspend() { if (!Hal::AcpiSleep::IsS3Available()) { - return -1; // S3 not supported + return UnsupportedOp; // S3 not supported } return (int64_t)Hal::AcpiSleep::Suspend(); } @@ -98,7 +98,7 @@ namespace montauk::abi { static int64_t Sys_PowerInfo(PowerInfo* out) { Hal::CpuPower::Snapshot s{}; if (!Hal::CpuPower::GetSnapshot(s)) { - return -1; + return NoDevice; } out->hwpActive = s.HwpActive ? 1 : 0; @@ -113,7 +113,7 @@ namespace montauk::abi { out->maxMHz = s.MaxMHz; out->effMHz = s.EffMHz; out->apIdleHint = Hal::CpuIdle::GetApIdleHint(); - return 0; + return Success; } // SYS_BATTERYINFO. Fills a battery snapshot. Returns -1 on a machine with @@ -122,7 +122,7 @@ namespace montauk::abi { static int64_t Sys_BatteryInfo(BatteryInfo* out) { Hal::Battery::Status s{}; if (!Hal::Battery::GetStatus(s)) { - return -1; + return NoDevice; } out->present = s.Present ? 1 : 0; @@ -139,6 +139,6 @@ namespace montauk::abi { out->presentRate = s.PresentRate; out->voltageMv = s.VoltageMv; out->minutesRemaining = s.MinutesRemaining; - return 0; + return Success; } }; diff --git a/kernel/src/Api/Process.hpp b/kernel/src/Api/Process.hpp index 26eeaae..d9602ab 100644 --- a/kernel/src/Api/Process.hpp +++ b/kernel/src/Api/Process.hpp @@ -49,13 +49,13 @@ namespace montauk::abi { // stream, whether or not it also carries a capability grant. The child is // created suspended (startReady == false) so its first instruction cannot // run before the channels exist, and is started here once they do. - // Returns childPid, or kills the child and returns -1 on failure. + // Returns childPid, or kills the child and returns GenericFailure. static int InheritRedirection(int childPid, Sched::Process* parent, int parentSlot) { auto* child = Sched::GetProcessByPid(childPid); int childSlot = Ipc::SlotForPid(childPid); if (child == nullptr || childSlot < 0 || parentSlot < 0) { Sched::KillProcess(childPid); - return -1; + return GenericFailure; } child->ioOutHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioOutHandle, childSlot); @@ -65,7 +65,7 @@ namespace montauk::abi { if (child->ioOutHandle < 0 || child->ioInHandle < 0 || child->ioKeyHandle < 0 || !ConfigureRedirWaitsetForSlot(childSlot, child)) { Sched::KillProcess(childPid); - return -1; + return GenericFailure; } child->redirected = true; @@ -74,7 +74,7 @@ namespace montauk::abi { child->termRows = parent->termRows; if (Sched::StartProcess(childPid) < 0) { Sched::KillProcess(childPid); - return -1; + return GenericFailure; } return childPid; @@ -83,7 +83,7 @@ namespace montauk::abi { static int Sys_Spawn(const char* path, const char* args, const char* environment = nullptr, uint32_t environmentLength = 0) { char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; auto* parent = Sched::GetCurrentProcessPtr(); int parentSlot = Ipc::CurrentSlot(); @@ -101,7 +101,7 @@ namespace montauk::abi { static int Sys_SpawnCaps(const char* path, const char* args, const char* user, const SpawnCapabilities* requested) { auto* parent = Sched::GetCurrentProcessPtr(); - if (parent == nullptr || requested == nullptr) return -1; + if (parent == nullptr || requested == nullptr) return InvalidArgument; // Snapshot all security-sensitive userspace inputs before evaluating // them. This prevents another thread from changing a mask or owner @@ -111,7 +111,7 @@ namespace montauk::abi { const char* userOverride = nullptr; if (user != nullptr) { if (!Sched::HasCapability(CAP_USER_ADMIN)) - return SYS_ERR_PERMISSION; + return CapabilityDenied; int i = 0; for (; i < 31 && user[i]; i++) childUser[i] = user[i]; childUser[i] = '\0'; @@ -122,11 +122,11 @@ namespace montauk::abi { // possessing a capability is insufficient to pass it: the parent must // also hold it in its delegable set. if (!ValidCapabilityDelegation(copy, parent->delegableCaps)) { - return SYS_ERR_PERMISSION; + return CapabilityDenied; } char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; int parentSlot = Ipc::CurrentSlot(); bool inheritRedirection = parent->redirected; @@ -143,16 +143,16 @@ namespace montauk::abi { // The calling process's own capability masks. static int Sys_GetCaps(SpawnCapabilities* out) { auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr || out == nullptr) return -1; + if (proc == nullptr || out == nullptr) return InvalidArgument; out->effective = proc->effectiveCaps; out->delegable = proc->delegableCaps; - return 0; + return Success; } // Copy the absolute path this process was spawned from (argv[0]). static int Sys_GetExecPath(char* buf, uint64_t maxLen) { auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr || buf == nullptr || maxLen == 0) return -1; + if (proc == nullptr || buf == nullptr || maxLen == 0) return InvalidArgument; int i = 0; for (; i < (int)maxLen - 1 && proc->name[i]; i++) { buf[i] = proc->name[i]; @@ -163,7 +163,7 @@ namespace montauk::abi { static int Sys_GetArgs(char* buf, uint64_t maxLen) { auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr || buf == nullptr || maxLen == 0) return -1; + if (proc == nullptr || buf == nullptr || maxLen == 0) return InvalidArgument; int i = 0; for (; i < (int)maxLen - 1 && proc->args[i]; i++) { buf[i] = proc->args[i]; @@ -174,8 +174,8 @@ namespace montauk::abi { static int Sys_GetEnviron(char* buf, uint64_t maxLen) { auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr || buf == nullptr || maxLen == 0) return -1; - if (maxLen < proc->environmentLength) return -2; + if (proc == nullptr || buf == nullptr || maxLen == 0) return InvalidArgument; + if (maxLen < proc->environmentLength) return BufferTooSmall; for (uint32_t i = 0; i < proc->environmentLength; i++) buf[i] = proc->environment[i]; return (int)proc->environmentLength; @@ -184,10 +184,10 @@ namespace montauk::abi { static int Sys_SetEnviron(const char* buf, uint64_t len) { auto* proc = Sched::GetCurrentProcessPtr(); if (proc == nullptr || buf == nullptr || len == 0 || - len > Sched::Process::EnvironmentBytes || buf[len - 1] != '\0') return -1; + len > Sched::Process::EnvironmentBytes || buf[len - 1] != '\0') return InvalidArgument; for (uint64_t i = 0; i < len; i++) proc->environment[i] = buf[i]; proc->environmentLength = (uint32_t)len; - return 0; + return Success; } static int Sys_ProcList(ProcInfo* buf, int maxCount) { @@ -233,7 +233,7 @@ namespace montauk::abi { } ancestor = target->parentPid; } - if (!descendant) return SYS_ERR_PERMISSION; + if (!descendant) return PermissionDenied; } return Sched::KillProcess(pid); } @@ -247,19 +247,19 @@ namespace montauk::abi { } static int Sys_SetUser(int pid, const char* name) { - if (name == nullptr) return -1; + if (name == nullptr) return InvalidArgument; auto* target = Sched::GetProcessByPid(pid); - if (target == nullptr) return -1; + if (target == nullptr) return InvalidArgument; int i = 0; for (; i < 31 && name[i]; i++) target->user[i] = name[i]; target->user[i] = '\0'; - return 0; + return Success; } static int Sys_GetUser(char* buf, uint64_t maxLen) { auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr || buf == nullptr || maxLen == 0) return -1; + if (proc == nullptr || buf == nullptr || maxLen == 0) return InvalidArgument; int i = 0; for (; i < (int)maxLen - 1 && proc->user[i]; i++) buf[i] = proc->user[i]; @@ -269,15 +269,15 @@ namespace montauk::abi { static int Sys_GetCwd(char* buf, uint64_t maxLen) { auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr || buf == nullptr || maxLen == 0) return -1; + if (proc == nullptr || buf == nullptr || maxLen == 0) return InvalidArgument; const char* cwd = proc->cwd[0] ? proc->cwd : "0:/"; uint64_t cwdLen = 0; while (cwd[cwdLen] != '\0') cwdLen++; - // Reserve -1 for non-size failures so libc can distinguish them - // from a buffer that can be retried with a larger allocation. - if (maxLen <= cwdLen) return -2; + // BufferTooSmall tells libc the call can be retried with a larger + // allocation (ERANGE), unlike the other failures. + if (maxLen <= cwdLen) return BufferTooSmall; for (uint64_t i = 0; i <= cwdLen; i++) buf[i] = cwd[i]; return (int)cwdLen; @@ -304,10 +304,10 @@ namespace montauk::abi { static int Sys_Chdir(const char* path) { auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr || path == nullptr) return -1; + if (proc == nullptr || path == nullptr) return InvalidArgument; char resolved[256]; - if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; + if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument; // ReadDir doubles as the directory-existence probe: it fails // for nonexistent paths and for regular files (directories are @@ -315,12 +315,12 @@ namespace montauk::abi { // would reject every valid target). { const char* entries[1]; - if (Fs::Vfs::VfsReadDir(resolved, entries, 1) < 0) return -1; + if (Fs::Vfs::VfsReadDir(resolved, entries, 1) < 0) return NoSuchPath; } int i = 0; for (; i < 255 && resolved[i]; i++) proc->cwd[i] = resolved[i]; proc->cwd[i] = '\0'; - return 0; + return Success; } }; diff --git a/kernel/src/Api/Storage.hpp b/kernel/src/Api/Storage.hpp index 9cdc89c..792d364 100644 --- a/kernel/src/Api/Storage.hpp +++ b/kernel/src/Api/Storage.hpp @@ -29,7 +29,7 @@ namespace montauk::abi { // Fill a user buffer with partition info. Returns the number of entries written. static int Sys_PartList(PartInfo* buf, int maxCount) { if (buf == nullptr || maxCount <= 0) return 0; - if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(PartInfo), true)) return -1; + if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(PartInfo), true)) return BadPointer; int total = Drivers::Storage::Gpt::GetPartitionCount(); int count = total < maxCount ? total : maxCount; @@ -58,10 +58,10 @@ namespace montauk::abi { } // Fill a user buffer with one entry per mounted volume. Returns the number - // of entries written, or -1 for a bad buffer. + // of entries written, or BadPointer for a bad buffer. static int Sys_VolInfo(VolInfo* buf, int maxCount) { if (buf == nullptr || maxCount <= 0) return 0; - if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(VolInfo), true)) return -1; + if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(VolInfo), true)) return BadPointer; int drives[Fs::Vfs::MaxDrives]; int n = Fs::Vfs::VfsDriveList(drives, Fs::Vfs::MaxDrives); @@ -99,47 +99,47 @@ namespace montauk::abi { return n; } - // Read sectors from a block device. Returns bytes read, or -1 on error. + // Read sectors from a block device. Returns bytes read, or a status code. static int64_t Sys_DiskRead(int blockDev, uint64_t lba, uint32_t count, void* buffer) { - if (buffer == nullptr || count == 0) return -1; - if (count > 128) return -1; // same limit as AHCI + if (buffer == nullptr || count == 0) return InvalidArgument; + if (count > 128) return InvalidArgument; // same limit as AHCI auto* dev = Drivers::Storage::GetBlockDevice(blockDev); - if (!dev) return -1; + if (!dev) return NoDevice; - if (lba >= dev->SectorCount || count > dev->SectorCount - lba) return -1; - if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, true)) return -1; + if (lba >= dev->SectorCount || count > dev->SectorCount - lba) return InvalidArgument; + if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, true)) return BadPointer; - if (!dev->ReadSectors(dev->Ctx, lba, count, buffer)) return -1; + if (!dev->ReadSectors(dev->Ctx, lba, count, buffer)) return IoFailure; return (int64_t)(count * dev->SectorSize); } - // Write sectors to a block device. Returns bytes written, or -1 on error. + // Write sectors to a block device. Returns bytes written, or a status code. static int64_t Sys_DiskWrite(int blockDev, uint64_t lba, uint32_t count, const void* buffer) { - if (buffer == nullptr || count == 0) return -1; - if (count > 128) return -1; + if (buffer == nullptr || count == 0) return InvalidArgument; + if (count > 128) return InvalidArgument; auto* dev = Drivers::Storage::GetBlockDevice(blockDev); - if (!dev) return -1; + if (!dev) return NoDevice; - if (lba >= dev->SectorCount || count > dev->SectorCount - lba) return -1; - if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, false)) return -1; + if (lba >= dev->SectorCount || count > dev->SectorCount - lba) return InvalidArgument; + if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, false)) return BadPointer; - if (!dev->WriteSectors(dev->Ctx, lba, count, buffer)) return -1; + if (!dev->WriteSectors(dev->Ctx, lba, count, buffer)) return IoFailure; return (int64_t)(count * dev->SectorSize); } - // Initialize a new GPT on a block device. Returns 0 on success, -1 on error. + // Initialize a new GPT on a block device. Returns 0 on success, GenericFailure on error. static int64_t Sys_GptInit(int blockDev) { Fs::FsProbe::UnmountPartitionsForBlockDevice(blockDev); return (int64_t)Drivers::Storage::Gpt::InitializeGpt(blockDev); } - // Add a partition to an existing GPT. Returns partition index on success, -1 on error. + // Add a partition to an existing GPT. Returns partition index on success, GenericFailure on error. static int64_t Sys_GptAdd(const GptAddParams* params) { - if (params == nullptr) return -1; + if (params == nullptr) return InvalidArgument; Drivers::Storage::Gpt::Guid typeGuid; __builtin_memcpy(&typeGuid, ¶ms->typeGuid, sizeof(typeGuid)); @@ -149,7 +149,7 @@ namespace montauk::abi { typeGuid, params->name); } - // Mount a partition as a VFS drive. Returns 0 on success, -1 on error. + // Mount a partition as a VFS drive. Returns 0 on success, GenericFailure on error. static int64_t Sys_FsMount(int partIndex, int driveNum) { return (int64_t)Fs::FsProbe::MountPartition(partIndex, driveNum); } @@ -163,12 +163,12 @@ namespace montauk::abi { return (int64_t)Fs::FsProbe::SyncAndUnmountAll(); } - // Format a partition with a filesystem. Returns 0 on success, -1 on error. + // Format a partition with a filesystem. Returns 0 on success, GenericFailure on error. static int64_t Sys_FsFormat(const FsFormatParams* params) { - if (params == nullptr) return -1; + if (params == nullptr) return InvalidArgument; auto* part = Drivers::Storage::Gpt::GetPartition(params->partIndex); - if (!part) return -1; + if (!part) return InvalidArgument; switch (params->fsType) { case FS_TYPE_FAT32: @@ -180,7 +180,7 @@ namespace montauk::abi { part->BlockDevIndex, part->StartLba, part->SectorCount, params->label[0] ? params->label : nullptr); default: - return -1; // unknown filesystem type + return UnsupportedOp; // unknown filesystem type } } diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index 30c93bc..314bb42 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -62,54 +62,54 @@ namespace montauk::abi { switch (frame->syscall_nr) { case SYS_EXIT: { Sys_Exit((int)frame->arg1); - return 0; + return Success; } case SYS_YIELD: Sys_Yield(); - return 0; + return Success; case SYS_SLEEP_MS: Sys_SleepMs(frame->arg1); - return 0; + return Success; case SYS_GETPID: return (int64_t)Sys_GetPid(); case SYS_PRINT: - if (!UserMemory::String(frame->arg1, kMaxPrintableStringBytes)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPrintableStringBytes)) return BadPointer; Sys_Print((const char*)frame->arg1); - return 0; + return Success; case SYS_PUTCHAR: Sys_Putchar((char)frame->arg1); - return 0; + return Success; case SYS_OPEN: - if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; + 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 -1; + 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 0; + return Success; case SYS_READDIR: - if ((int64_t)frame->arg3 < 0) return -1; - if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; + 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 -1; + 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 -1; - if ((int64_t)frame->arg4 < 0) return -1; - if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; + 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 -1; + return BadPointer; } return (int64_t)Sys_ReadDirAt((const char*)frame->arg1, (const char**)frame->arg2, @@ -121,7 +121,7 @@ namespace montauk::abi { return (int64_t)Sys_AllocEager(frame->arg1); case SYS_FREE: Sys_Free(frame->arg1); - return 0; + return Success; case SYS_MMAP_ANON: return (int64_t)Sys_MapAnonymous(frame->arg1, frame->arg2); case SYS_MUNMAP: @@ -133,15 +133,15 @@ namespace montauk::abi { case SYS_GETMILLISECONDS: return (int64_t)Sys_GetMilliseconds(); case SYS_GETINFO: - if (!UserMemory::Writable(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_GetInfo((SysInfo*)frame->arg1); - return 0; + return Success; case SYS_ISKEYAVAILABLE: return (int64_t)Sys_IsKeyAvailable(); case SYS_GETKEY: - if (!UserMemory::Writable(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_GetKey((KeyEvent*)frame->arg1); - return 0; + return Success; case SYS_GETCHAR: return (int64_t)Sys_GetChar(); case SYS_GETCHAR_NB: @@ -149,73 +149,75 @@ namespace montauk::abi { 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 -1; - if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; + 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 -1; - if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; - if (frame->arg3 != 0 && !UserMemory::String(frame->arg3, 32)) return -1; - if (!UserMemory::Readable(frame->arg4)) return -1; + 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 -1; - if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; - if (frame->arg3 == 0 || frame->arg4 == 0 || - frame->arg4 > Sched::Process::EnvironmentBytes || - !UserMemory::Range(frame->arg3, frame->arg4, false) || - ((const char*)frame->arg3)[frame->arg4 - 1] != '\0') return -1; + 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 -1; + 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 || - !UserMemory::Range(frame->arg1, frame->arg2, false)) return -1; + 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 -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_FbInfo((FbInfo*)frame->arg1); - return 0; + 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 -1; + 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 -1; + if ((int64_t)frame->arg2 < 0 || frame->arg2 > 32) return InvalidArgument; if (!UserMemory::Range(frame->arg1, - (uint64_t)frame->arg2 * sizeof(DisplayModeInfo), true)) return -1; + (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 SYS_ERR_PERMISSION; + 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_DISPLAY_ADMIN)) return SYS_ERR_PERMISSION; + !Sched::Consent::Require(CAP_DISPLAY_ADMIN)) return CapabilityDenied; return Sys_DisplayBrightness((int)frame->arg1); case SYS_GETEXECPATH: - if (!UserMemory::Range(frame->arg2 ? frame->arg1 : frame->arg1, frame->arg2, true)) return -1; + 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 -1; - if (!UserMemory::Writable(frame->arg2)) return -1; + 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 -1; + 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, @@ -223,33 +225,33 @@ namespace montauk::abi { case SYS_TERMSIZE: return (int64_t)Sys_TermSize(); case SYS_GETARGS: - if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; + 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 SYS_ERR_PERMISSION; + if (!Sched::HasCapability(CAP_POWER_CONTROL)) return CapabilityDenied; Sys_Reset(); - return 0; + return Success; case SYS_SHUTDOWN: - if (!Sched::HasCapability(CAP_POWER_CONTROL)) return SYS_ERR_PERMISSION; + if (!Sched::HasCapability(CAP_POWER_CONTROL)) return CapabilityDenied; Sys_Shutdown(); - return 0; + 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 -1; + frame->arg1 != POWER_REQ_PEEK) return InvalidArgument; if (frame->arg1 == POWER_REQ_QUERY) { - if (!Sched::HasCapability(CAP_POWER_CONTROL)) return SYS_ERR_PERMISSION; + if (!Sched::HasCapability(CAP_POWER_CONTROL)) return CapabilityDenied; } else if (!Sched::HasCapability(CAP_POWER_REQUEST)) { - return SYS_ERR_PERMISSION; + return CapabilityDenied; } return Sys_PowerRequest((int)frame->arg1); case SYS_GETTIME: - if (!UserMemory::Writable(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_GetTime((DateTime*)frame->arg1); - return 0; + return Success; case SYS_SETUNIXTIME: - if (!Sched::Consent::Require(CAP_SET_TIME)) return SYS_ERR_PERMISSION; + 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); @@ -262,114 +264,114 @@ namespace montauk::abi { case SYS_ACCEPT: return (int64_t)Sys_Accept((int)frame->arg1); case SYS_SEND: - if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return -1; + 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 -1; + 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 0; + return Success; case SYS_GETNETCFG: - if (!UserMemory::Writable(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_GetNetCfg((NetCfg*)frame->arg1); - return 0; + return Success; case SYS_SETNETCFG: - if (!Sched::Consent::Require(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION; - if (!UserMemory::Readable(frame->arg1)) return -1; + 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 -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_NetStatus((NetStatus*)frame->arg1); - return 0; + return Success; case SYS_SENDTO: - if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return -1; + 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 -1; - if (!UserMemory::OptionalRange(frame->arg4, sizeof(uint32_t), true)) return -1; - if (!UserMemory::OptionalRange(frame->arg5, sizeof(uint16_t), true)) return -1; + 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 -1; + 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 -1; + 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 -1; + 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 -1; + 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 -1; - if (!UserMemory::String(frame->arg2, kMaxPathBytes)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(int), true)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return -1; + 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 -1; + 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 -1; + 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 SYS_ERR_PERMISSION; - if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; + 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 -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_MouseState((MouseState*)frame->arg1); - return 0; + return Success; case SYS_SETMOUSEBOUNDS: Sys_SetMouseBounds((int32_t)frame->arg1, (int32_t)frame->arg2); - return 0; + return Success; case SYS_SPAWN_REDIR: - if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; - if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; + 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 -1; - if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; - if (!UserMemory::Readable(frame->arg3)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, true)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, false)) return -1; + 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 -1; + if (!UserMemory::Readable(frame->arg2)) return BadPointer; return (int64_t)Sys_ChildIoWriteKey((int)frame->arg1, (const KeyEvent*)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 -1; - if (!UserMemory::Writable(frame->arg4)) return -1; + 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: @@ -377,38 +379,38 @@ namespace montauk::abi { case SYS_WINPRESENT: return (int64_t)Sys_WinPresent((int)frame->arg1); case SYS_WINPOLL: - if (!UserMemory::Writable(frame->arg2)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WinInfo), true)) return -1; + 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 -1; + 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(ProcInfo), true)) return -1; + 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 SYS_ERR_PERMISSION; + 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(DevInfo), true)) return -1; + 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 -1; + 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); @@ -419,54 +421,54 @@ namespace montauk::abi { case SYS_WINSETFLAGS: return (int64_t)Sys_WinSetFlags((int)frame->arg1, (uint32_t)frame->arg2); case SYS_MEMSTATS: - if (!UserMemory::Writable(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; Sys_MemStats((MemStats*)frame->arg1); - return 0; + return Success; case SYS_PARTLIST: - if ((int64_t)frame->arg2 < 0) return -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(PartInfo), true)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(VolInfo), true)) return -1; + 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 SYS_ERR_PERMISSION; + 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 SYS_ERR_PERMISSION; + 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 SYS_ERR_PERMISSION; + 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 SYS_ERR_PERMISSION; - if (!UserMemory::Readable(frame->arg1)) return -1; + 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 SYS_ERR_PERMISSION; + 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 SYS_ERR_PERMISSION; + if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return CapabilityDenied; return Sys_FsSync(); case SYS_FSFORMAT: - if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION; - if (!UserMemory::Readable(frame->arg1)) return -1; + 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 -1; + 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(AudioStreamInfo), true)) return -1; + 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); @@ -507,21 +509,21 @@ namespace montauk::abi { return Sys_UsbBulkInRead((int)frame->arg1, (uint8_t*)frame->arg2, (uint32_t)frame->arg3); case SYS_POWERINFO: - if (!UserMemory::Writable(frame->arg1)) return -1; + 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 -1; + if (!UserMemory::Writable(frame->arg1)) return BadPointer; return Sys_BatteryInfo((BatteryInfo*)frame->arg1); case SYS_GETCAPS: - if (!UserMemory::Writable(frame->arg1)) return -1; + 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 -1; + 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); @@ -529,43 +531,43 @@ namespace montauk::abi { return Sys_ThreadSpawn(frame->arg1, frame->arg2, frame->arg3); case SYS_THREAD_EXIT: Sys_ThreadExit((int)frame->arg1); - return 0; + return Success; case SYS_THREAD_JOIN: - if (frame->arg2 != 0 && !UserMemory::Writable(frame->arg2)) return -1; + 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_BTSCAN: - if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION; - if ((int64_t)frame->arg2 < 0) return -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtScanResult), true)) return -1; + 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 SYS_ERR_PERMISSION; - if (!UserMemory::Range(frame->arg1, 6, false)) return -1; + 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 SYS_ERR_PERMISSION; - if (!UserMemory::Range(frame->arg1, 6, false)) return -1; + 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 SYS_ERR_PERMISSION; - if (!UserMemory::Range(frame->arg1, 6, false)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtBondInfo), true)) return -1; + 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 SYS_ERR_PERMISSION; - if (!UserMemory::Range(frame->arg1, 6, false)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtDevInfo), true)) return -1; + 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 -1; + 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; @@ -596,61 +598,61 @@ namespace montauk::abi { 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 -1; - if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(NetIfInfo), true)) return -1; + 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 SYS_ERR_PERMISSION; + if (!Sched::HasCapability(CAP_SUSPEND)) return CapabilityDenied; return Sys_Suspend(); case SYS_SETTZ: - if (!Sched::Consent::Require(CAP_SET_TIME)) return SYS_ERR_PERMISSION; + 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 SYS_ERR_PERMISSION; - if (!UserMemory::String(frame->arg2, kMaxUserNameBytes)) return -1; + 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 -1; + 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 -1; + 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 -1; + 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 -1; + 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 -1; - if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, true)) return -1; + 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 -1; - if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, false)) return -1; + 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 -1; + 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 -1; + 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 -1; - if (frame->arg2 != 0 && !UserMemory::Writable(frame->arg2)) return -1; + 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 -1; + if (!UserMemory::Range(frame->arg3, maxCopy, true)) return BadPointer; } - if (frame->arg5 != 0 && !UserMemory::Writable(frame->arg5)) return -1; + 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, @@ -663,7 +665,7 @@ namespace montauk::abi { 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 -1; + 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); @@ -685,18 +687,18 @@ namespace montauk::abi { case SYS_GETLIBBASE: return (int64_t)Sys_GetLibBase(frame->arg1); case SYS_CRASH_REPORT: - if (!UserMemory::Writable(frame->arg1)) return -1; + 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 -1; + 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 -1; + 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 -1; - if (!UserMemory::Writable(frame->arg3)) return -1; - if (frame->arg4 != 0 && !UserMemory::Writable(frame->arg4)) return -1; + 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, @@ -706,7 +708,7 @@ namespace montauk::abi { case SYS_INPUT_WAIT: return (int64_t)Sys_InputWait(frame->arg1, frame->arg2); case SYS_LOG_WRITE: { - if (!UserMemory::String(frame->arg1, kMaxUserspaceLogEntryBytes)) return -1; + if (!UserMemory::String(frame->arg1, kMaxUserspaceLogEntryBytes)) return BadPointer; auto* process = Sched::GetCurrentProcessPtr(); const char* username = process != nullptr && process->user[0] != '\0' @@ -719,12 +721,12 @@ namespace montauk::abi { Kt::UserspaceLogStream(imageName, username) << (const char*)frame->arg1; - return 0; + return Success; } case SYS_TERMINAL_ATTACHED: return Sys_TerminalAttached(); default: - return -1; + return BadInvocation; } } diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index 0bc423d..001d2e2 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -7,6 +7,7 @@ #pragma once #include #include +#include namespace montauk::abi { @@ -399,6 +400,9 @@ namespace montauk::abi { static_assert((CAP_ADMIN_SESSION & CAP_COMPOSITOR) == 0, "a session must not delegate the compositor's authority; " "login grants it to the desktop directly"); + // Capability denial for the Wi-Fi and USB syscalls only, which keep their + // own WIFI_ERR_* / USB_ERR_* spaces; everything else returns + // CapabilityDenied from StatusCodes. static constexpr int SYS_ERR_PERMISSION = -13; // A process holds two independent sets: what it may use (effective) and diff --git a/kernel/src/Api/Time.hpp b/kernel/src/Api/Time.hpp index 1205526..880d272 100644 --- a/kernel/src/Api/Time.hpp +++ b/kernel/src/Api/Time.hpp @@ -32,7 +32,7 @@ namespace montauk::abi { static int64_t Sys_SetTZ(int32_t offsetMinutes) { Timekeeping::SetTZOffset(offsetMinutes); - return 0; + return Success; } static int64_t Sys_GetTZ() { @@ -40,6 +40,6 @@ namespace montauk::abi { } static int64_t Sys_SetUnixTime(int64_t unixSeconds) { - return Timekeeping::SetUnixTimestamp(unixSeconds) ? 0 : -1; + return Timekeeping::SetUnixTimestamp(unixSeconds) ? Success : InvalidArgument; } }; diff --git a/kernel/src/Api/WinServer.cpp b/kernel/src/Api/WinServer.cpp index cefd4a5..64e6929 100644 --- a/kernel/src/Api/WinServer.cpp +++ b/kernel/src/Api/WinServer.cpp @@ -55,13 +55,13 @@ namespace WinServer { } static int UnmapRetiredSnapshotsLocked(int windowId, int callerPid, uint64_t callerPml4) { - int result = -1; + int result = InvalidArgument; for (int i = 0; i < MaxRetiredSnapshots; i++) { if (!g_retiredSnapshots[i].used || g_retiredSnapshots[i].windowId != windowId) continue; if (g_retiredSnapshots[i].surface != nullptr) { if (Ipc::UnmapSurfaceForPid(g_retiredSnapshots[i].surface, callerPid, callerPml4) == 0) { - result = 0; + result = Success; } Ipc::ReleaseSurface(g_retiredSnapshots[i].surface); } @@ -95,21 +95,22 @@ namespace WinServer { } static int SendEventLocked(int windowId, const montauk::abi::WinEvent* event) { - if (windowId < 0 || windowId >= MaxWindows || event == nullptr) return -1; + if (windowId < 0 || windowId >= MaxWindows || event == nullptr) return InvalidArgument; WindowSlot& slot = g_slots[windowId]; - if (!slot.used || slot.eventMailbox == nullptr) return -1; + if (!slot.used || slot.eventMailbox == nullptr) return InvalidArgument; int rc = (event->type == 1) ? Ipc::MailboxSendCoalescedMouse(slot.eventMailbox, 0, event, sizeof(*event)) : Ipc::MailboxSend(slot.eventMailbox, 0, event, sizeof(*event)); - return rc > 0 ? 0 : -1; + if (rc < 0) return GenericFailure; + return rc > 0 ? Success : Busy; } int Create(int ownerPid, uint64_t ownerPml4, const char* title, int w, int h, uint64_t& heapNext, uint64_t& outVa) { WsGuard guard; - if (w <= 0 || h <= 0 || w > 16384 || h > 16384) return -1; + if (w <= 0 || h <= 0 || w > 16384 || h > 16384) return InvalidArgument; int slotIdx = -1; for (int i = 0; i < MaxWindows; i++) { @@ -118,7 +119,7 @@ namespace WinServer { break; } } - if (slotIdx < 0) return -1; + if (slotIdx < 0) return GenericFailure; uint64_t bufSize = (uint64_t)w * (uint64_t)h * 4ULL; Ipc::Surface* live = Ipc::CreateSurface(bufSize); @@ -128,7 +129,7 @@ namespace WinServer { if (live != nullptr) Ipc::ReleaseSurface(live); if (snapshot != nullptr) Ipc::ReleaseSurface(snapshot); if (mailbox != nullptr) Ipc::ReleaseMailbox(mailbox, false, false); - return -1; + return OutOfMemory; } Ipc::RetainSurface(live); @@ -146,7 +147,7 @@ namespace WinServer { Ipc::ReleaseMailbox(mailbox, true, true); Ipc::ReleaseSurface(snapshot); Ipc::ReleaseSurface(live); - return -1; + return GenericFailure; } WindowSlot& slot = g_slots[slotIdx]; @@ -176,40 +177,41 @@ namespace WinServer { int Destroy(int windowId, int callerPid) { WsGuard guard; - if (windowId < 0 || windowId >= MaxWindows) return -1; + if (windowId < 0 || windowId >= MaxWindows) return InvalidArgument; WindowSlot& slot = g_slots[windowId]; - if (!slot.used || slot.ownerPid != callerPid) return -1; + if (!slot.used) return InvalidArgument; + if (slot.ownerPid != callerPid) return PermissionDenied; ReleaseSlotResourcesLocked(slot, true); - return 0; + return Success; } int Present(int windowId, int callerPid) { WsGuard guard; - if (windowId < 0 || windowId >= MaxWindows) return -1; + if (windowId < 0 || windowId >= MaxWindows) return InvalidArgument; WindowSlot& slot = g_slots[windowId]; - if (!slot.used || slot.ownerPid != callerPid || - slot.liveSurface == nullptr || slot.snapshotSurface == nullptr) { - return -1; - } + if (!slot.used || slot.liveSurface == nullptr || slot.snapshotSurface == nullptr) + return InvalidArgument; + if (slot.ownerPid != callerPid) return PermissionDenied; - if (Ipc::CopySurface(slot.snapshotSurface, slot.liveSurface) != 0) return -1; + if (Ipc::CopySurface(slot.snapshotSurface, slot.liveSurface) != 0) return GenericFailure; slot.dirty = true; - return 0; + return Success; } int Poll(int windowId, int callerPid, montauk::abi::WinEvent* outEvent) { WsGuard guard; - if (windowId < 0 || windowId >= MaxWindows || outEvent == nullptr) return -1; + if (windowId < 0 || windowId >= MaxWindows || outEvent == nullptr) return InvalidArgument; WindowSlot& slot = g_slots[windowId]; - if (!slot.used || slot.ownerPid != callerPid || slot.eventMailbox == nullptr) return -1; + if (!slot.used || slot.eventMailbox == nullptr) return InvalidArgument; + if (slot.ownerPid != callerPid) return PermissionDenied; uint16_t len = sizeof(*outEvent); int rc = Ipc::MailboxRecv(slot.eventMailbox, nullptr, outEvent, &len, true); - if (rc < 0) return -1; + if (rc < 0) return GenericFailure; return rc > 0 ? 1 : 0; } @@ -259,7 +261,7 @@ namespace WinServer { if (!slot.used || slot.snapshotSurface == nullptr) return result; int current = Ipc::UnmapSurfaceForPid(slot.snapshotSurface, callerPid, callerPml4); - if (current == 0) return 0; + if (current == 0) return Success; return result; } @@ -271,14 +273,15 @@ namespace WinServer { int Resize(int windowId, int callerPid, uint64_t ownerPml4, int newW, int newH, uint64_t& heapNext, uint64_t& outVa) { WsGuard guard; - if (windowId < 0 || windowId >= MaxWindows) return -1; + if (windowId < 0 || windowId >= MaxWindows) return InvalidArgument; WindowSlot& slot = g_slots[windowId]; - if (!slot.used || slot.ownerPid != callerPid) return -1; - if (newW <= 0 || newH <= 0 || newW > 16384 || newH > 16384) return -1; + if (!slot.used) return InvalidArgument; + if (slot.ownerPid != callerPid) return PermissionDenied; + if (newW <= 0 || newH <= 0 || newW > 16384 || newH > 16384) return InvalidArgument; if (newW == slot.width && newH == slot.height) { outVa = slot.ownerVa; - return 0; + return Success; } uint64_t bufSize = (uint64_t)newW * (uint64_t)newH * 4ULL; @@ -287,7 +290,7 @@ namespace WinServer { if (newLive == nullptr || newSnapshot == nullptr) { if (newLive != nullptr) Ipc::ReleaseSurface(newLive); if (newSnapshot != nullptr) Ipc::ReleaseSurface(newSnapshot); - return -1; + return OutOfMemory; } Ipc::RetainSurface(newLive); @@ -297,7 +300,7 @@ namespace WinServer { if (Ipc::MapSurfaceForPid(newLive, callerPid, ownerPml4, heapNext, newOwnerVa, false) != 0) { Ipc::ReleaseSurface(newSnapshot); Ipc::ReleaseSurface(newLive); - return -1; + return GenericFailure; } if (slot.liveSurface != nullptr) { @@ -327,27 +330,29 @@ namespace WinServer { slot.dirty = true; outVa = newOwnerVa; - return 0; + return Success; } int SetCursor(int windowId, int callerPid, int cursor) { WsGuard guard; - if (windowId < 0 || windowId >= MaxWindows) return -1; + if (windowId < 0 || windowId >= MaxWindows) return InvalidArgument; WindowSlot& slot = g_slots[windowId]; - if (!slot.used || slot.ownerPid != callerPid) return -1; + if (!slot.used) return InvalidArgument; + if (slot.ownerPid != callerPid) return PermissionDenied; slot.cursor = (uint8_t)cursor; - return 0; + return Success; } int SetFlags(int windowId, int callerPid, uint32_t flags) { WsGuard guard; - if (windowId < 0 || windowId >= MaxWindows) return -1; + if (windowId < 0 || windowId >= MaxWindows) return InvalidArgument; WindowSlot& slot = g_slots[windowId]; - if (!slot.used || slot.ownerPid != callerPid) return -1; + if (!slot.used) return InvalidArgument; + if (slot.ownerPid != callerPid) return PermissionDenied; slot.flags = flags & montauk::abi::WIN_FLAG_FULLSCREEN; - return 0; + return Success; } int SetScale(int scale) { @@ -364,7 +369,7 @@ namespace WinServer { SendEventLocked(i, &ev); } } - return 0; + return Success; } int GetScale() { diff --git a/kernel/src/Api/Window.hpp b/kernel/src/Api/Window.hpp index 9b7d83e..66be3cf 100644 --- a/kernel/src/Api/Window.hpp +++ b/kernel/src/Api/Window.hpp @@ -15,16 +15,16 @@ namespace montauk::abi { static int Sys_WinCreate(const char* title, int w, int h, WinCreateResult* result) { - if (result == nullptr || title == nullptr) return -1; + if (result == nullptr || title == nullptr) return InvalidArgument; auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr) return -1; + if (proc == nullptr) return GenericFailure; uint64_t outVa = 0; int id = WinServer::Create(proc->pid, proc->pml4Phys, title, w, h, proc->heapNext, outVa); result->id = id; result->pixelVa = (id >= 0) ? outVa : 0; - return id >= 0 ? 0 : -1; + return id >= 0 ? Success : id; } static int Sys_WinDestroy(int windowId) { @@ -36,7 +36,7 @@ namespace montauk::abi { } static int Sys_WinPoll(int windowId, WinEvent* outEvent) { - if (outEvent == nullptr) return -1; + if (outEvent == nullptr) return InvalidArgument; return WinServer::Poll(windowId, Sched::GetCurrentPid(), outEvent); } @@ -58,13 +58,13 @@ namespace montauk::abi { static int Sys_WinUnmap(int windowId) { auto* proc = Sched::GetCurrentProcessPtr(); - if (proc == nullptr) return -1; + if (proc == nullptr) return GenericFailure; return WinServer::Unmap(windowId, proc->pid, proc->pml4Phys); } static int Sys_WinSendEvent(int windowId, const WinEvent* event) { - if (!Sched::HasCapability(CAP_COMPOSITOR)) return SYS_ERR_PERMISSION; - if (event == nullptr) return -1; + if (!Sched::HasCapability(CAP_COMPOSITOR)) return CapabilityDenied; + if (event == nullptr) return InvalidArgument; return WinServer::SendEvent(windowId, event); } diff --git a/kernel/src/Common/StatusCodes.hpp b/kernel/src/Common/StatusCodes.hpp new file mode 100644 index 0000000..5cf43e8 --- /dev/null +++ b/kernel/src/Common/StatusCodes.hpp @@ -0,0 +1,28 @@ +/* + * StatusCodes.hpp + * MontaukOS system status codes. + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +// Syscall return status. Mirrored for userspace in +// programs/include/montauk/statuscodes.h and, as MTK_STATUS_*, in +// programs/include/libc/montauk.h; scripts/montauk-syscalls.py checks all three. +enum StatusCodes { + Success = 0, + GenericFailure = -1, + PermissionDenied = -2, + CapabilityDenied = -3, + SystemIntegrityError = -4, + BadPointer = -5, + InvalidArgument = -6, + UnsupportedOp = -7, + NoDevice = -8, + IoFailure = -9, + BadInvocation = -10, + NoSuchPath = -11, + Busy = -12, + BufferTooSmall = -13, + OutOfMemory = -14 +}; diff --git a/kernel/src/Ipc/Ipc.cpp b/kernel/src/Ipc/Ipc.cpp index 7112fca..e7eb252 100644 --- a/kernel/src/Ipc/Ipc.cpp +++ b/kernel/src/Ipc/Ipc.cpp @@ -1153,7 +1153,7 @@ namespace Ipc { uint64_t writeCapability = Fs::RequiredFileWriteCapability(path); if (create && writeCapability != 0 && !Sched::HasCapability(writeCapability)) { - return montauk::abi::SYS_ERR_PERMISSION; + return SystemIntegrityError; } Fs::Vfs::BackendFile backend = {-1, -1, 0}; @@ -1207,15 +1207,16 @@ namespace Ipc { int FileWriteHandle(int handle, const uint8_t* buffer, uint64_t offset, uint64_t size) { if (buffer == nullptr) return -1; - if (size > 0 && !montauk::abi::UserMemory::Range((uint64_t)buffer, size, false)) return -1; + if (size > 0 && !montauk::abi::UserMemory::Range((uint64_t)buffer, size, false)) return BadPointer; HandleSnapshot snapshot; - if (!snapshot.Capture(CurrentSlot(), handle)) return -1; - if (snapshot.type != HandleType::File || (snapshot.rights & RightWrite) == 0) return -1; + if (!snapshot.Capture(CurrentSlot(), handle)) return InvalidArgument; + if (snapshot.type != HandleType::File) return InvalidArgument; + if ((snapshot.rights & RightWrite) == 0) return PermissionDenied; File* file = (File*)snapshot.object; if (file->writeCapability != 0 && !Sched::HasCapability(file->writeCapability)) { - return montauk::abi::SYS_ERR_PERMISSION; + return SystemIntegrityError; } return Fs::Vfs::WriteBackendFile(file->backend, buffer, offset, size); } diff --git a/kernel/src/Sched/Consent.cpp b/kernel/src/Sched/Consent.cpp index 7c2b238..3cc3669 100644 --- a/kernel/src/Sched/Consent.cpp +++ b/kernel/src/Sched/Consent.cpp @@ -161,25 +161,25 @@ namespace Sched::Consent { } int Register() { - if (!HasCapability(CAP_COMPOSITOR)) return SYS_ERR_PERMISSION; + if (!HasCapability(CAP_COMPOSITOR)) return CapabilityDenied; int pid = GetCurrentPid(); g_lock.Acquire(); // OnProcessExit clears a dead agent, so a live one is always current. if (g_agentPid >= 0 && g_agentPid != pid) { g_lock.Release(); - return -1; + return Busy; } g_agentPid = pid; g_lock.Release(); Kt::KernelLogStream(Kt::INFO, "Consent") << "Agent registered: pid " << (uint64_t)pid; - return 0; + return Success; } int Next(ConsentRequest* out) { int agent = GetCurrentPid(); - if (agent < 0 || agent != g_agentPid) return SYS_ERR_PERMISSION; + if (agent < 0 || agent != g_agentPid) return PermissionDenied; ConsentRequest local; for (;;) { @@ -226,7 +226,7 @@ namespace Sched::Consent { int Reply(uint64_t id, int decision) { int agent = GetCurrentPid(); - if (agent < 0 || agent != g_agentPid) return SYS_ERR_PERMISSION; + if (agent < 0 || agent != g_agentPid) return PermissionDenied; Request* request = nullptr; int pid = -1; @@ -253,13 +253,13 @@ namespace Sched::Consent { } g_lock.Release(); - if (request == nullptr) return -1; + if (request == nullptr) return InvalidArgument; WakeObjectWaiters(request); Kt::KernelLogStream(Kt::INFO, "Consent") << "pid " << (uint64_t)pid << " " << NameOf(capability) << ": " << (granted ? "allowed" : (decision == CONSENT_ALLOW ? "allow refused (no longer valid)" : "denied")); - return 0; + return Success; } void OnProcessExit(int pid) { diff --git a/programs/include/Api/Syscall.hpp b/programs/include/Api/Syscall.hpp index 1790f04..5807e67 100644 --- a/programs/include/Api/Syscall.hpp +++ b/programs/include/Api/Syscall.hpp @@ -7,6 +7,7 @@ #pragma once #include #include +#include namespace montauk::abi { @@ -312,6 +313,9 @@ namespace montauk::abi { static_assert((CAP_ADMIN_SESSION & CAP_COMPOSITOR) == 0, "a session must not delegate the compositor's authority; " "login grants it to the desktop directly"); + // Capability denial for the Wi-Fi and USB syscalls only, which keep their + // own WIFI_ERR_* / USB_ERR_* spaces; everything else returns + // CapabilityDenied from StatusCodes. static constexpr int SYS_ERR_PERMISSION = -13; // A process holds two independent sets: what it may use (effective) and diff --git a/programs/include/libc/montauk.h b/programs/include/libc/montauk.h index fc947f4..d08f457 100644 --- a/programs/include/libc/montauk.h +++ b/programs/include/libc/montauk.h @@ -219,6 +219,27 @@ extern "C" { #define MTK_SYS_CONSENT_REPLY 193 /* @SYSCALLS-END */ +/* Syscall status codes (montauk/statuscodes.h for C++). */ +/* @STATUS-BEGIN + Generated from Common/StatusCodes.hpp by scripts/montauk-syscalls.py. + Do not edit by hand; run 'make gen-syscalls' to refresh. */ +#define MTK_STATUS_SUCCESS (0) +#define MTK_STATUS_GENERIC_FAILURE (-1) +#define MTK_STATUS_PERMISSION_DENIED (-2) +#define MTK_STATUS_CAPABILITY_DENIED (-3) +#define MTK_STATUS_SYSTEM_INTEGRITY_ERROR (-4) +#define MTK_STATUS_BAD_POINTER (-5) +#define MTK_STATUS_INVALID_ARGUMENT (-6) +#define MTK_STATUS_UNSUPPORTED_OP (-7) +#define MTK_STATUS_NO_DEVICE (-8) +#define MTK_STATUS_IO_FAILURE (-9) +#define MTK_STATUS_BAD_INVOCATION (-10) +#define MTK_STATUS_NO_SUCH_PATH (-11) +#define MTK_STATUS_BUSY (-12) +#define MTK_STATUS_BUFFER_TOO_SMALL (-13) +#define MTK_STATUS_OUT_OF_MEMORY (-14) +/* @STATUS-END */ + #define MTK_SOCK_TCP 1 #define MTK_SOCK_UDP 2 #define MTK_IPC_SIGNAL_READABLE (1u << 0) diff --git a/programs/include/montauk/statuscodes.h b/programs/include/montauk/statuscodes.h new file mode 100644 index 0000000..be65416 --- /dev/null +++ b/programs/include/montauk/statuscodes.h @@ -0,0 +1,35 @@ +/* + * statuscodes.h + * MontaukOS status codes + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +// Userspace mirror of kernel/src/Common/StatusCodes.hpp. Also usable from C +// (the libc includes it), where the enumerators are unqualified. +#ifdef __cplusplus +namespace montauk { +#endif + +enum StatusCodes { + Success = 0, + GenericFailure = -1, + PermissionDenied = -2, + CapabilityDenied = -3, + SystemIntegrityError = -4, + BadPointer = -5, + InvalidArgument = -6, + UnsupportedOp = -7, + NoDevice = -8, + IoFailure = -9, + BadInvocation = -10, + NoSuchPath = -11, + Busy = -12, + BufferTooSmall = -13, + OutOfMemory = -14 +}; + +#ifdef __cplusplus +} +#endif diff --git a/programs/include/montauk/syscall.h b/programs/include/montauk/syscall.h index 468e0bd..5c9c188 100644 --- a/programs/include/montauk/syscall.h +++ b/programs/include/montauk/syscall.h @@ -6,6 +6,7 @@ #pragma once #include +#include namespace montauk { diff --git a/programs/lib/libc/libc.c b/programs/lib/libc/libc.c index b1c45da..52b63d8 100644 --- a/programs/lib/libc/libc.c +++ b/programs/lib/libc/libc.c @@ -15,6 +15,7 @@ #include #include #include +#include "../../include/montauk/statuscodes.h" #include #include #include @@ -2476,7 +2477,7 @@ char *getcwd(char *buf, size_t size) { } int result = (int)_mtk_syscall2(SYS_GETCWD, (long)buf, (long)size); if (result < 0) { - errno = result == -2 ? ERANGE : EIO; + errno = result == BufferTooSmall ? ERANGE : EIO; return NULL; } return buf; @@ -3566,9 +3567,10 @@ int utime(const char *path, const struct utimbuf *times) { times != NULL ? (long)times->modtime : 0, times == NULL); if (result == 0) return 0; - if (result == -2) errno = ENOTSUP; - else if (result == -3) errno = EOVERFLOW; - else if (result == -4) errno = EIO; + if (result == UnsupportedOp) errno = ENOTSUP; + else if (result == InvalidArgument) errno = EOVERFLOW; + else if (result == IoFailure) errno = EIO; + else if (result == SystemIntegrityError) errno = EPERM; else errno = ENOENT; return -1; } diff --git a/programs/src/display/main.cpp b/programs/src/display/main.cpp index c7517cd..503a134 100644 --- a/programs/src/display/main.cpp +++ b/programs/src/display/main.cpp @@ -500,7 +500,7 @@ static void apply_changes() { if (g_driver_available && g_selected_mode != g_info.currentMode) { int result = montauk::display_set_mode(g_selected_mode); if (result < 0) { - set_status(result == -2 + set_status(result == montauk::UnsupportedOp ? "Mode needs unsupported display-link training" : "Mode change failed; previous mode restored"); refresh_state(true); diff --git a/programs/src/login/login_shutdown.cpp b/programs/src/login/login_shutdown.cpp index 0897014..bf0dcdd 100644 --- a/programs/src/login/login_shutdown.cpp +++ b/programs/src/login/login_shutdown.cpp @@ -262,7 +262,7 @@ void perform_graceful_shutdown(LoginState* ls, int action) { // A successful power-control syscall never returns. If it does return, // keep the trusted supervisor alive and make the authorization failure // visible instead of falling through an unreachable-code assumption. - show_stage(ls, heading, rc == montauk::abi::SYS_ERR_PERMISSION + show_stage(ls, heading, rc == montauk::CapabilityDenied ? "Power control permission denied." : "Power control failed."); for (;;) montauk::sleep_ms(1000); diff --git a/programs/src/reset/main.cpp b/programs/src/reset/main.cpp index 0601529..9f0bde7 100644 --- a/programs/src/reset/main.cpp +++ b/programs/src/reset/main.cpp @@ -9,7 +9,7 @@ extern "C" void _start() { montauk::print("Rebooting...\n"); int rc = montauk::reset(); - if (rc == montauk::abi::SYS_ERR_PERMISSION) + if (rc == montauk::CapabilityDenied) montauk::print("reset: permission denied\n"); else montauk::print("reset: reboot failed\n"); diff --git a/programs/src/shell/main.cpp b/programs/src/shell/main.cpp index fff8605..9da0029 100644 --- a/programs/src/shell/main.cpp +++ b/programs/src/shell/main.cpp @@ -194,7 +194,7 @@ static int process_command(const char* line) { : montauk::abi::POWER_REQ_SHUTDOWN; int rc = montauk::power_request(action); if (rc == 0) montauk::exit(0); - montauk::print(rc == montauk::abi::SYS_ERR_PERMISSION + montauk::print(rc == montauk::CapabilityDenied ? "power: permission denied\n" : "power: request failed\n"); return 1; @@ -202,7 +202,7 @@ static int process_command(const char* line) { if (streq(cmd, "suspend")) { int rc = montauk::suspend(); - if (rc == montauk::abi::SYS_ERR_PERMISSION) + if (rc == montauk::CapabilityDenied) montauk::print("suspend: permission denied\n"); return rc == 0 ? 0 : 1; } diff --git a/scripts/montauk-syscalls.py b/scripts/montauk-syscalls.py index a5f28bc..043e09d 100755 --- a/scripts/montauk-syscalls.py +++ b/scripts/montauk-syscalls.py @@ -1,9 +1,12 @@ #!/usr/bin/env python3 -# montauk-syscalls.py - Keep the TCC C API header's syscall numbers in sync -# with the canonical syscall ABI. +# montauk-syscalls.py - Keep the TCC C API header's syscall numbers and status +# codes in sync with the canonical syscall ABI. # # Source of truth : programs/include/Api/Syscall.hpp (montauk::abi::SYS_*) -# Generated block : programs/include/libc/montauk.h (#define MTK_SYS_*) +# kernel/src/Common/StatusCodes.hpp (enum StatusCodes) +# Mirrors : programs/include/montauk/statuscodes.h (must match exactly) +# Generated blocks: programs/include/libc/montauk.h (#define MTK_SYS_*, +# #define MTK_STATUS_*) # # The libc/montauk.h header is a hand-maintained, TCC-compatible C mirror of the # syscall surface. Its typed wrapper functions stay hand-written (their argument @@ -13,10 +16,13 @@ # # Usage: # montauk-syscalls.py check Verify every SYS_* has a matching MTK_SYS_* with -# the same number. Exits non-zero on any drift. -# Run from the build so stale headers fail loudly. -# montauk-syscalls.py gen Rewrite the MTK_SYS_* block in montauk.h from the -# canonical enum (numbers only; wrappers untouched). +# the same number, and that both status code +# mirrors match the kernel enum. Exits non-zero on +# any drift. Run from the build so stale headers +# fail loudly. +# montauk-syscalls.py gen Rewrite the MTK_SYS_* and MTK_STATUS_* blocks in +# montauk.h (numbers only; wrappers untouched). +# statuscodes.h is not generated; fix it by hand. import os import re @@ -26,9 +32,13 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) REPO_ROOT = os.path.dirname(SCRIPT_DIR) ABI_HDR = os.path.join(REPO_ROOT, "programs/include/Api/Syscall.hpp") C_HDR = os.path.join(REPO_ROOT, "programs/include/libc/montauk.h") +STATUS_HDR = os.path.join(REPO_ROOT, "kernel/src/Common/StatusCodes.hpp") +STATUS_MIRROR = os.path.join(REPO_ROOT, "programs/include/montauk/statuscodes.h") BEGIN_MARKER = "/* @SYSCALLS-BEGIN" END_MARKER = "/* @SYSCALLS-END */" +STATUS_BEGIN_MARKER = "/* @STATUS-BEGIN" +STATUS_END_MARKER = "/* @STATUS-END */" # Canonical SYS_* names intentionally NOT exposed in the C API. Empty by default; # add a name here (without the SYS_ prefix) to suppress a kernel-internal syscall. @@ -38,6 +48,11 @@ EXCLUDE = set() ABI_RE = re.compile(r"constexpr\s+\w+\s+SYS_([A-Z0-9_]+)\s*=\s*(\d+)\s*;") # #define MTK_SYS_FOO 42 DEF_RE = re.compile(r"#define\s+MTK_SYS_([A-Z0-9_]+)\s+(\d+)\b") +# enum StatusCodes { Name = -3, ... }; +ENUM_RE = re.compile(r"enum\s+StatusCodes\s*\{([^}]*)\}", re.S) +ENUM_ENTRY_RE = re.compile(r"([A-Za-z_]\w*)\s*=\s*(-?\d+)") +# #define MTK_STATUS_FOO (-3) +STATUS_DEF_RE = re.compile(r"#define\s+MTK_STATUS_([A-Z0-9_]+)\s+\(?(-?\d+)\)?") def read(path): @@ -62,6 +77,43 @@ def parse_defines(text): return {name: int(num) for name, num in DEF_RE.findall(text)} +def parse_status(path): + """Return [(Name, value)] from the StatusCodes enum in path, in order.""" + m = ENUM_RE.search(read(path)) + if not m: + sys.exit("montauk-syscalls: no enum StatusCodes found in %s" % path) + return [(n, int(v)) for n, v in ENUM_ENTRY_RE.findall(m.group(1))] + + +def status_macro(name): + """GenericFailure -> GENERIC_FAILURE.""" + return re.sub(r"(?` in the `montauk` namespace. The syscall layer provides `syscall0()` through `syscall6()` primitives using AMD64 calling conventions. All raw syscalls return `int64_t`. +## Status Codes + +| Code | Value | Meaning | +|------|-------|---------| +| `Success` | 0 | The call succeeded | +| `GenericFailure` | -1 | Unspecified failure | +| `PermissionDenied` | -2 | The object belongs to another process or the handle lacks the right | +| `CapabilityDenied` | -3 | The caller lacks (or was refused consent for) a required capability | +| `SystemIntegrityError` | -4 | The path is protected (ProtectedPaths) and the caller may not modify it | +| `BadPointer` | -5 | A user pointer or buffer range is invalid | +| `InvalidArgument` | -6 | An argument, id, or handle is invalid | +| `UnsupportedOp` | -7 | The operation is not supported here | +| `NoDevice` | -8 | The required device is absent or not initialized | +| `IoFailure` | -9 | Device I/O failed | +| `BadInvocation` | -10 | Unknown syscall number | +| `NoSuchPath` | -11 | The path does not exist | +| `Busy` | -12 | The resource is in use or its queue is full | +| `BufferTooSmall` | -13 | The output buffer is too small; retry with a larger one | +| `OutOfMemory` | -14 | Memory could not be allocated | + +The Wi-Fi (`WIFI_ERR_*`) and USB (`USB_ERR_*`) syscalls keep their own error codes. + ## Table of Contents +- [Status Codes](#status-codes) - [Process Management](#process-management) - [File System](#file-system) - [Memory](#memory) diff --git a/template/sysroot/include/Api/Syscall.hpp b/template/sysroot/include/Api/Syscall.hpp index 0814b5c..5807e67 100644 --- a/template/sysroot/include/Api/Syscall.hpp +++ b/template/sysroot/include/Api/Syscall.hpp @@ -7,6 +7,7 @@ #pragma once #include #include +#include namespace montauk::abi { @@ -250,6 +251,122 @@ namespace montauk::abi { static constexpr uint64_t SYS_LOG_WRITE = 176; // (logMessage) -> 0 static constexpr uint64_t SYS_TERMINAL_ATTACHED = 177; // () -> 1 when connected to a userspace terminal + static constexpr uint64_t SYS_SPAWN_CAPS = 185; + static constexpr uint64_t SYS_SPAWN_REDIR_CAPS = 186; + // As SYS_ALLOC, but commits every page up front instead of faulting them + // in one at a time. For buffers the caller is about to touch in full. + static constexpr uint64_t SYS_ALLOC_EAGER = 187; // (bytes) -> va, 0 on failure + + /* Battery.hpp -- ACPI battery status */ + static constexpr uint64_t SYS_BATTERYINFO = 188; // (BatteryInfo*) -> 0, -1 no battery device + + // The caller's own capability masks. + static constexpr uint64_t SYS_GETCAPS = 189; // (SpawnCapabilities*) -> 0, -1 bad pointer + + /* Storage.hpp */ + static constexpr uint64_t SYS_VOLINFO = 190; // (VolInfo*, maxCount) -> count, -1 bad pointer + + /* Consent.hpp */ + static constexpr uint64_t SYS_CONSENT_REGISTER = 191; // () -> 0, -13 lacks CAP_COMPOSITOR, -1 another agent is live + static constexpr uint64_t SYS_CONSENT_NEXT = 192; // (ConsentRequest*) -> 1 delivered, 0 none queued + static constexpr uint64_t SYS_CONSENT_REPLY = 193; // (requestId, CONSENT_ALLOW/DENY) -> 0, -1 stale id + + /* Kernel-owned process capabilities. User identities may namespace + per-user resources, but never participate in authorization decisions. */ + static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0; + static constexpr uint64_t CAP_POWER_REQUEST = 1ULL << 1; + static constexpr uint64_t CAP_POWER_CONTROL = 1ULL << 2; + static constexpr uint64_t CAP_SUSPEND = 1ULL << 3; + static constexpr uint64_t CAP_STORAGE_ADMIN = 1ULL << 4; + static constexpr uint64_t CAP_RAW_STORAGE = 1ULL << 5; + static constexpr uint64_t CAP_NETWORK_ADMIN = 1ULL << 6; + static constexpr uint64_t CAP_SET_TIME = 1ULL << 7; + static constexpr uint64_t CAP_USER_ADMIN = 1ULL << 8; + static constexpr uint64_t CAP_DISPLAY_ADMIN = 1ULL << 9; + static constexpr uint64_t CAP_DEVICE_ADMIN = 1ULL << 10; + static constexpr uint64_t CAP_LOG_READ = 1ULL << 11; + /* Write to the program images the system boots and runs (0:/os, + 0:/apps). Deliberately separate from CAP_STORAGE_ADMIN: grants are + keyed on binary path, so writing an image is equivalent to acquiring + whatever that image is granted at its next launch. Formatting a data + volume must not carry that authority with it. */ + static constexpr uint64_t CAP_SYSTEM_IMAGE = 1ULL << 12; + /* Map other processes' windows and deliver input events to them + (SYS_WINMAP, SYS_WINSENDEVENT): the desktop compositor's authority. + Anything holding it can read every window's pixels and click on any + window, including a consent prompt it is itself waiting on, so it is + never part of a session mask, never part of "all", and never + delegated by the compositor to what it launches. */ + static constexpr uint64_t CAP_COMPOSITOR = 1ULL << 13; + static constexpr uint64_t CAP_ALL = (1ULL << 14) - 1; + static constexpr uint64_t CAP_STANDARD_SESSION = CAP_POWER_REQUEST | CAP_SUSPEND; + static constexpr uint64_t CAP_ADMIN_SESSION = + CAP_STANDARD_SESSION | CAP_PROCESS_ADMIN | CAP_STORAGE_ADMIN | + CAP_RAW_STORAGE | CAP_NETWORK_ADMIN | CAP_SET_TIME | CAP_USER_ADMIN | + CAP_DISPLAY_ADMIN | CAP_DEVICE_ADMIN | CAP_LOG_READ; + static_assert((CAP_STANDARD_SESSION & ~CAP_ADMIN_SESSION) == 0); + static_assert((CAP_ADMIN_SESSION & CAP_POWER_CONTROL) == 0, + "final power control belongs only to the session supervisor"); + static_assert((CAP_ADMIN_SESSION & CAP_SYSTEM_IMAGE) == 0, + "an admin session must not imply authority to rewrite the " + "programs it launches; grant CAP_SYSTEM_IMAGE per binary"); + static_assert((CAP_ADMIN_SESSION & CAP_COMPOSITOR) == 0, + "a session must not delegate the compositor's authority; " + "login grants it to the desktop directly"); + // Capability denial for the Wi-Fi and USB syscalls only, which keep their + // own WIFI_ERR_* / USB_ERR_* spaces; everything else returns + // CapabilityDenied from StatusCodes. + static constexpr int SYS_ERR_PERMISSION = -13; + + // A process holds two independent sets: what it may use (effective) and + // what it may pass to children (delegable). A launcher can delegate what + // it never uses itself, so neither set bounds the other. + struct SpawnCapabilities { + uint64_t effective; + uint64_t delegable; + }; + + // Both of the child's sets must come out of the parent's delegable set. + constexpr bool ValidCapabilityDelegation(const SpawnCapabilities& child, + uint64_t parentDelegable) { + return ((child.effective | child.delegable) & ~CAP_ALL) == 0 && + (child.effective & ~parentDelegable) == 0 && + (child.delegable & ~parentDelegable) == 0; + } + static_assert(ValidCapabilityDelegation( + {CAP_NETWORK_ADMIN, 0}, CAP_NETWORK_ADMIN)); + static_assert(ValidCapabilityDelegation( + {0, CAP_NETWORK_ADMIN}, CAP_NETWORK_ADMIN)); + static_assert(!ValidCapabilityDelegation( + {CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN}, 0)); + static_assert(!ValidCapabilityDelegation( + {CAP_NETWORK_ADMIN | CAP_SET_TIME, 0}, CAP_NETWORK_ADMIN)); + static_assert(!ValidCapabilityDelegation( + {0, CAP_NETWORK_ADMIN | CAP_SET_TIME}, CAP_NETWORK_ADMIN)); + + /* Capabilities a user can grant at first use through a consent prompt. + A consent grant never exceeds what the process's parent could have + delegated to it at spawn (its consent ceiling), is never delegable + onwards, and is issued only by the session's consent agent -- so it is + deferred delegation, not amplification. */ + static constexpr uint64_t CAP_CONSENT_ELIGIBLE = + CAP_STORAGE_ADMIN | CAP_NETWORK_ADMIN | CAP_SET_TIME | + CAP_DISPLAY_ADMIN | CAP_LOG_READ; + static_assert((CAP_CONSENT_ELIGIBLE & ~CAP_ADMIN_SESSION) == 0, + "consent can only hand out authority an admin session holds"); + + static constexpr int CONSENT_DENY = 0; + static constexpr int CONSENT_ALLOW = 1; + // A request left unanswered this long is denied (and not remembered). + static constexpr uint64_t CONSENT_TIMEOUT_MS = 120000; + + struct ConsentRequest { + uint64_t id; + uint64_t capability; // exactly one CAP_* bit + int32_t pid; + uint32_t imageProtected; // 1 when writing the image needs CAP_SYSTEM_IMAGE + char imagePath[256]; + }; static constexpr int USB_ERR_INVALID = -1; @@ -264,10 +381,17 @@ namespace montauk::abi { // Graceful power-off request actions (SYS_POWER_REQUEST). The desktop posts // a pending action and exits; login.elf reads it, runs the shutdown stages, // then issues the matching SYS_SHUTDOWN / SYS_RESET. + // + // A request can also be posted from inside the session -- the shell's + // shutdown builtin does. login only looks at it once the session leader + // exits, so the leader has to notice and stand down: POWER_REQ_PEEK is the + // non-destructive read it polls with. Only login consumes (QUERY), so a + // leader that peeks cannot swallow the request it is meant to act on. enum PowerRequestAction : int { POWER_REQ_QUERY = 0, // read-and-clear the pending action POWER_REQ_SHUTDOWN = 1, POWER_REQ_REBOOT = 2, + POWER_REQ_PEEK = 3, // read the pending action without clearing it }; static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024; @@ -530,6 +654,28 @@ namespace montauk::abi { char typeName[24]; // human-readable type name }; + // One mounted volume (returned by SYS_VOLINFO). A volume is a single + // mounted filesystem, which is what a drive number names: one physical + // disk with three partitions occupies three drive numbers. + // + // A volume with no backing partition -- the boot ramdisk -- reports + // blockDev and partIndex as -1 and leaves the geometry fields zero. + struct VolInfo { + int32_t drive; // VFS drive number ("N:/") + int32_t blockDev; // backing block device index, -1 if none + int32_t partIndex; // global GPT partition index, -1 if none + uint32_t kind; // BlockDeviceKind: 0=none/ramdisk, 1=SATA, 2=SATAPI, 3=NVMe, 4=USB MSC + uint64_t startLba; // partition start LBA, 0 if none + uint64_t sectorCount; // partition length in sectors, 0 if none + uint32_t sectorSize; // logical sector size in bytes, 0 if none + uint32_t _pad0; + char label[36]; // filesystem volume label, "" if unset + char fsType[12]; // "ext2", "FAT32", "ramdisk", "" if unknown + char partName[72]; // GPT partition name, "" if none + char model[41]; // backing disk model, "" if none + char _pad1[3]; + }; + struct GptAddParams { int32_t blockDev; uint32_t _pad0; @@ -729,6 +875,8 @@ namespace montauk::abi { char name[64]; uint64_t heapUsed; // Distance from UserHeapBase to high-water mark uint64_t cpuTimeMs; // accumulated scheduler runtime + uint64_t effectiveCaps; + uint64_t delegableCaps; }; struct MemStats { @@ -738,6 +886,26 @@ namespace montauk::abi { uint64_t pageSize; }; + // Battery status (returned by SYS_BATTERYINFO). A machine with no battery + // device fails the call outright; `present` distinguishes a laptop with the + // pack removed from one that is running on it. + struct BatteryInfo { + uint8_t present; // a battery is installed + uint8_t charging; + uint8_t discharging; + uint8_t critical; + uint8_t acOnline; // AC adapter connected + uint8_t hasAcAdapter; // an AC adapter device exists at all + uint8_t percent; // 0..100, 0 when capacity is unknown + uint8_t powerUnitMilliAmps; // capacities are mAh/mA, not mWh/mW + uint32_t remainingCapacity; + uint32_t fullCapacity; // last full charge capacity + uint32_t designCapacity; + uint32_t presentRate; // mW or mA, 0 when unknown + uint32_t voltageMv; + int32_t minutesRemaining; // -1 when not estimable (e.g. charging) + }; + // Crash report (filled by kernel on process fault, returned via SYS_CRASH_REPORT) struct CrashReportInfo { int pid; diff --git a/template/sysroot/include/gui/desktop.hpp b/template/sysroot/include/gui/desktop.hpp index 0783747..54b948c 100644 --- a/template/sysroot/include/gui/desktop.hpp +++ b/template/sysroot/include/gui/desktop.hpp @@ -217,6 +217,16 @@ struct DesktopState { uint64_t wifi_status_time; + // Battery indicator. Absent entirely on a machine with no ACPI battery + // device, which is how a desktop keeps a clean panel. + SvgIcon icon_battery; + char battery_icon_name[64]; // which glyph icon_battery holds + bool battery_present; // a battery device exists + bool battery_popup_open; + Rect battery_icon_rect; + montauk::abi::BatteryInfo battery_info; + uint64_t battery_last_poll; + bool vol_popup_open; Rect vol_icon_rect; int vol_level; // 0-100 diff --git a/template/sysroot/include/gui/terminal.hpp b/template/sysroot/include/gui/terminal.hpp index 70f41aa..6534bcc 100644 --- a/template/sysroot/include/gui/terminal.hpp +++ b/template/sysroot/include/gui/terminal.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace gui { @@ -59,32 +60,47 @@ struct TerminalState { int csi_current_param; }; +// ==== Palette ==== +// +// The palette is a mutable global rather than a set of constants because the +// terminal lets the user switch themes at runtime. Cells store resolved colors, +// so a theme switch also has to rewrite the cells that were painted with the +// outgoing palette -- see terminal_remap_palette below. + +struct TermPalette { + Color bg; + Color fg; + Color cursor; + Color ansi[16]; +}; + +inline constexpr TermPalette TERM_PALETTE_DEFAULT = { + colors::TERM_BG, + colors::TERM_FG, + colors::TERM_FG, + { + Color::from_hex(0x000000), Color::from_hex(0xCC0000), + Color::from_hex(0x4E9A06), Color::from_hex(0xC4A000), + Color::from_hex(0x3465A4), Color::from_hex(0x75507B), + Color::from_hex(0x06989A), Color::from_hex(0xD3D7CF), + Color::from_hex(0x555753), Color::from_hex(0xEF2929), + Color::from_hex(0x8AE234), Color::from_hex(0xFCE94F), + Color::from_hex(0x729FCF), Color::from_hex(0xAD7FA8), + Color::from_hex(0x34E2E2), Color::from_hex(0xEEEEEC), + } +}; + +inline TermPalette g_term_palette = TERM_PALETTE_DEFAULT; + // Standard ANSI color palette as ARGB pixels static inline Color term_ansi_color(int idx) { - switch (idx) { - case 0: return Color::from_hex(0x000000); - case 1: return Color::from_hex(0xCC0000); - case 2: return Color::from_hex(0x4E9A06); - case 3: return Color::from_hex(0xC4A000); - case 4: return Color::from_hex(0x3465A4); - case 5: return Color::from_hex(0x75507B); - case 6: return Color::from_hex(0x06989A); - case 7: return Color::from_hex(0xD3D7CF); - case 8: return Color::from_hex(0x555753); - case 9: return Color::from_hex(0xEF2929); - case 10: return Color::from_hex(0x8AE234); - case 11: return Color::from_hex(0xFCE94F); - case 12: return Color::from_hex(0x729FCF); - case 13: return Color::from_hex(0xAD7FA8); - case 14: return Color::from_hex(0x34E2E2); - case 15: return Color::from_hex(0xEEEEEC); - default: return colors::TERM_FG; - } + if (idx < 0 || idx > 15) return g_term_palette.fg; + return g_term_palette.ansi[idx]; } // ANSI 256-color palette (0-15 = standard, 16-231 = RGB cube, 232-255 = grayscale) static inline Color term_ansi_256_color(int idx) { - if (idx < 0) return colors::TERM_FG; + if (idx < 0) return g_term_palette.fg; if (idx <= 15) return term_ansi_color(idx); if (idx <= 231) { // 6x6x6 RGB cube: idx 16 = (0,0,0), idx 231 = (5,5,5) @@ -118,6 +134,47 @@ static inline void terminal_invalidate_render_cache(TerminalState* t) { t->render_cursor_visible = false; } +// Translate one already-resolved cell color from an outgoing palette to the +// incoming one. Colors that are not palette entries -- 256-color cube and +// grayscale ramp values -- are left exactly as the program asked for them. +static inline Color term_remap_color(Color c, const TermPalette& from, + const TermPalette& to) { + if (term_color_equal(c, from.bg)) return to.bg; + if (term_color_equal(c, from.fg)) return to.fg; + for (int i = 0; i < 16; i++) { + if (term_color_equal(c, from.ansi[i])) return to.ansi[i]; + } + return c; +} + +// Repaint an existing terminal in a new palette. Cells carry resolved colors, +// so switching themes without this leaves all scrollback -- and the shell's +// colored prompt -- in the old theme's colors. +static inline void terminal_remap_palette(TerminalState* t, + const TermPalette& from, + const TermPalette& to) { + if (!t || !t->cells) return; + + int total = (t->rows + t->max_scrollback) * t->cols; + for (int i = 0; i < total; i++) { + t->cells[i].fg = term_remap_color(t->cells[i].fg, from, to); + t->cells[i].bg = term_remap_color(t->cells[i].bg, from, to); + } + + if (t->alt_cells) { + int screen = t->rows * t->cols; + for (int i = 0; i < screen; i++) { + t->alt_cells[i].fg = term_remap_color(t->alt_cells[i].fg, from, to); + t->alt_cells[i].bg = term_remap_color(t->alt_cells[i].bg, from, to); + } + } + + t->current_fg = term_remap_color(t->current_fg, from, to); + t->current_bg = term_remap_color(t->current_bg, from, to); + terminal_invalidate_render_cache(t); + t->dirty = true; +} + static inline Color terminal_erase_bg(TerminalState* t) { return t->current_bg; } @@ -162,8 +219,8 @@ static inline void terminal_init_cells(TerminalState* t, int cols, int rows, int t->scrollback_lines = 0; t->max_scrollback = max_sb; t->view_offset = 0; - t->current_fg = colors::TERM_FG; - t->current_bg = colors::TERM_BG; + t->current_fg = g_term_palette.fg; + t->current_bg = g_term_palette.bg; t->cursor_visible = false; t->alt_screen_active = false; t->reverse_video = false; @@ -201,10 +258,10 @@ static inline void terminal_init_cells(TerminalState* t, int cols, int rows, int return; } for (int i = 0; i < total_cells; i++) { - t->cells[i] = {' ', colors::TERM_FG, colors::TERM_BG}; + t->cells[i] = {' ', g_term_palette.fg, g_term_palette.bg}; } for (int i = 0; i < screen_cells; i++) { - t->alt_cells[i] = {' ', colors::TERM_FG, colors::TERM_BG}; + t->alt_cells[i] = {' ', g_term_palette.fg, g_term_palette.bg}; } } @@ -212,7 +269,8 @@ static inline void terminal_init(TerminalState* t, int cols, int rows) { terminal_init_cells(t, cols, rows, TERM_MAX_SCROLLBACK); t->cursor_visible = true; - t->child_pid = montauk::spawn_redir("0:/os/shell.elf"); + static constexpr const char* kShellPath = "0:/os/shell.elf"; + t->child_pid = montauk::caps::launch(kShellPath, {.redirected = true}); if (t->child_pid > 0) montauk::childio_settermsz(t->child_pid, cols, rows); } @@ -245,7 +303,7 @@ static inline void terminal_enter_alt_screen(TerminalState* t) { TermCell* screen = term_screen_row(t, 0); for (int i = 0; i < total; i++) { t->alt_cells[i] = screen[i]; - screen[i] = {' ', colors::TERM_FG, colors::TERM_BG}; + screen[i] = {' ', g_term_palette.fg, g_term_palette.bg}; } t->view_offset = 0; t->cursor_x = 0; @@ -424,8 +482,8 @@ static inline void terminal_process_csi(TerminalState* t, char cmd) { } if (code == 0) { - t->current_fg = colors::TERM_FG; - t->current_bg = colors::TERM_BG; + t->current_fg = g_term_palette.fg; + t->current_bg = g_term_palette.bg; t->reverse_video = false; } else if (code == 1) { // Bold: map to bright version of current color @@ -473,15 +531,15 @@ static inline void terminal_process_csi(TerminalState* t, char cmd) { } else if (code >= 100 && code <= 107) { t->current_bg = term_ansi_color(code - 100 + 8); } else if (code == 39) { - t->current_fg = colors::TERM_FG; + t->current_fg = g_term_palette.fg; } else if (code == 49) { - t->current_bg = colors::TERM_BG; + t->current_bg = g_term_palette.bg; } } if (t->csi_param_count == 0) { // ESC[m with no params = reset - t->current_fg = colors::TERM_FG; - t->current_bg = colors::TERM_BG; + t->current_fg = g_term_palette.fg; + t->current_bg = g_term_palette.bg; t->reverse_video = false; } break; @@ -532,8 +590,8 @@ static inline void terminal_feed(TerminalState* t, const char* data, int len) { for (int j = 0; j < 8; j++) t->csi_params[j] = 0; } else if (ch == 'c') { // Reset terminal - t->current_fg = colors::TERM_FG; - t->current_bg = colors::TERM_BG; + t->current_fg = g_term_palette.fg; + t->current_bg = g_term_palette.bg; t->cursor_x = 0; t->cursor_y = 0; t->parse_state = TerminalState::STATE_NORMAL; @@ -627,18 +685,21 @@ static inline void terminal_draw_cursor_pixels(const TermCell& cell, uint32_t* pixels, int pw, int ph, int px, int py, int cell_w, int cell_h, bool use_ttf, GlyphCache* gc) { + // The block cursor takes its color from the palette and punches the glyph + // out in the background color, so it stays legible on light themes too -- + // a hardcoded white block disappears on a white background. terminal_fill_pixel_rect(pixels, pw, ph, px, py, cell_w, cell_h, - colors::WHITE.to_pixel()); + g_term_palette.cursor.to_pixel()); if (cell.ch <= 32 && cell.ch >= 0) return; if (use_ttf) { int baseline = py + gc->ascent; fonts::mono->draw_char_to_buffer(pixels, pw, ph, - px, baseline, (unsigned char)cell.ch, colors::BLACK, gc); + px, baseline, (unsigned char)cell.ch, g_term_palette.bg, gc); } else { terminal_draw_bitmap_char(pixels, pw, ph, px, py, (unsigned char)cell.ch, - colors::BLACK.to_pixel()); + g_term_palette.bg.to_pixel()); } } @@ -700,7 +761,7 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i t->render_base_row != base_row || t->render_term_cols != t->cols || t->render_term_rows != t->rows; - uint32_t bg_px = colors::TERM_BG.to_pixel(); + uint32_t bg_px = g_term_palette.bg.to_pixel(); if (full_redraw) { int row_bytes = pw * sizeof(uint32_t); for (int i = 0; i < pw; i++) pixels[i] = bg_px; @@ -779,9 +840,9 @@ static inline void terminal_resize(TerminalState* t, int new_cols, int new_rows) // Clear new buffers for (int i = 0; i < new_total; i++) - new_cells[i] = {' ', colors::TERM_FG, colors::TERM_BG}; + new_cells[i] = {' ', g_term_palette.fg, g_term_palette.bg}; for (int i = 0; i < new_rows * new_cols; i++) - new_alt[i] = {' ', colors::TERM_FG, colors::TERM_BG}; + new_alt[i] = {' ', g_term_palette.fg, g_term_palette.bg}; // Copy content: scrollback + visible screen int old_content = t->scrollback_lines + t->rows; diff --git a/template/sysroot/include/gui/truetype.hpp b/template/sysroot/include/gui/truetype.hpp index edf3593..f7d877e 100644 --- a/template/sysroot/include/gui/truetype.hpp +++ b/template/sysroot/include/gui/truetype.hpp @@ -483,10 +483,10 @@ namespace fonts { return f; }; - system_font = load("0:/fonts/Roboto-Medium.ttf"); - system_bold = load("0:/fonts/Roboto-Bold.ttf"); - mono = load("0:/fonts/JetBrainsMono-Regular.ttf"); - mono_bold = load("0:/fonts/JetBrainsMono-Bold.ttf"); + system_font = load("0:/os/fonts/Roboto-Medium.ttf"); + system_bold = load("0:/os/fonts/Roboto-Bold.ttf"); + mono = load("0:/os/fonts/JetBrainsMono-Regular.ttf"); + mono_bold = load("0:/os/fonts/JetBrainsMono-Bold.ttf"); return system_font != nullptr; } diff --git a/template/sysroot/include/libc/montauk.h b/template/sysroot/include/libc/montauk.h index dd49f4c..d08f457 100644 --- a/template/sysroot/include/libc/montauk.h +++ b/template/sysroot/include/libc/montauk.h @@ -208,8 +208,38 @@ extern "C" { #define MTK_SYS_USB_BULK_IN_START 182 #define MTK_SYS_USB_BULK_IN_STOP 183 #define MTK_SYS_USB_BULK_IN_READ 184 +#define MTK_SYS_SPAWN_CAPS 185 +#define MTK_SYS_SPAWN_REDIR_CAPS 186 +#define MTK_SYS_ALLOC_EAGER 187 +#define MTK_SYS_BATTERYINFO 188 +#define MTK_SYS_GETCAPS 189 +#define MTK_SYS_VOLINFO 190 +#define MTK_SYS_CONSENT_REGISTER 191 +#define MTK_SYS_CONSENT_NEXT 192 +#define MTK_SYS_CONSENT_REPLY 193 /* @SYSCALLS-END */ +/* Syscall status codes (montauk/statuscodes.h for C++). */ +/* @STATUS-BEGIN + Generated from Common/StatusCodes.hpp by scripts/montauk-syscalls.py. + Do not edit by hand; run 'make gen-syscalls' to refresh. */ +#define MTK_STATUS_SUCCESS (0) +#define MTK_STATUS_GENERIC_FAILURE (-1) +#define MTK_STATUS_PERMISSION_DENIED (-2) +#define MTK_STATUS_CAPABILITY_DENIED (-3) +#define MTK_STATUS_SYSTEM_INTEGRITY_ERROR (-4) +#define MTK_STATUS_BAD_POINTER (-5) +#define MTK_STATUS_INVALID_ARGUMENT (-6) +#define MTK_STATUS_UNSUPPORTED_OP (-7) +#define MTK_STATUS_NO_DEVICE (-8) +#define MTK_STATUS_IO_FAILURE (-9) +#define MTK_STATUS_BAD_INVOCATION (-10) +#define MTK_STATUS_NO_SUCH_PATH (-11) +#define MTK_STATUS_BUSY (-12) +#define MTK_STATUS_BUFFER_TOO_SMALL (-13) +#define MTK_STATUS_OUT_OF_MEMORY (-14) +/* @STATUS-END */ + #define MTK_SOCK_TCP 1 #define MTK_SOCK_UDP 2 #define MTK_IPC_SIGNAL_READABLE (1u << 0) @@ -683,8 +713,8 @@ static inline int mtk_set_unix_time(int64_t unix_seconds) { return (int)_mtk_syscall1(MTK_SYS_SETUNIXTIME, (long)unix_seconds); } -static inline void mtk_settz(int offset_minutes) { - _mtk_syscall1(MTK_SYS_SETTZ, (long)offset_minutes); +static inline int mtk_settz(int offset_minutes) { + return (int)_mtk_syscall1(MTK_SYS_SETTZ, (long)offset_minutes); } static inline int mtk_gettz(void) { @@ -883,12 +913,12 @@ static inline int mtk_audio_ctl(int handle, int cmd, int value) { Power management ==================================================================== */ -static inline void mtk_reset(void) { - _mtk_syscall0(MTK_SYS_RESET); +static inline int mtk_reset(void) { + return (int)_mtk_syscall0(MTK_SYS_RESET); } -static inline void mtk_shutdown(void) { - _mtk_syscall0(MTK_SYS_SHUTDOWN); +static inline int mtk_shutdown(void) { + return (int)_mtk_syscall0(MTK_SYS_SHUTDOWN); } /* ==================================================================== diff --git a/template/sysroot/include/libc/wchar.h b/template/sysroot/include/libc/wchar.h index cd90739..45c8baf 100644 --- a/template/sysroot/include/libc/wchar.h +++ b/template/sysroot/include/libc/wchar.h @@ -21,6 +21,7 @@ typedef struct { /* Byte-oriented C locale only: one byte, one character. */ size_t mbstowcs(wchar_t *dst, const char *src, size_t n); int mblen(const char *s, size_t n); +size_t mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps); #ifdef __cplusplus } diff --git a/template/sysroot/include/montauk/capabilities.h b/template/sysroot/include/montauk/capabilities.h new file mode 100644 index 0000000..bbf0595 --- /dev/null +++ b/template/sysroot/include/montauk/capabilities.h @@ -0,0 +1,216 @@ +/* + * capabilities.h + * Shared reader for the capability grant table (0:/config/capabilities.toml) + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include +#include +#include + +namespace montauk { +namespace caps { + + inline constexpr const char* GRANT_CONFIG = "capabilities"; + inline constexpr const char* GRANT_PREFIX = "grant."; + + struct CapName { + const char* name; + uint64_t bit; + }; + + // Names as they appear in the config file. Kept in the same order as the + // CAP_* bit definitions in Api/Syscall.hpp. + inline constexpr CapName NAMES[] = { + {"process_admin", montauk::abi::CAP_PROCESS_ADMIN}, + {"power_request", montauk::abi::CAP_POWER_REQUEST}, + {"power_control", montauk::abi::CAP_POWER_CONTROL}, + {"suspend", montauk::abi::CAP_SUSPEND}, + {"storage_admin", montauk::abi::CAP_STORAGE_ADMIN}, + {"raw_storage", montauk::abi::CAP_RAW_STORAGE}, + {"network_admin", montauk::abi::CAP_NETWORK_ADMIN}, + {"set_time", montauk::abi::CAP_SET_TIME}, + {"user_admin", montauk::abi::CAP_USER_ADMIN}, + {"display_admin", montauk::abi::CAP_DISPLAY_ADMIN}, + {"device_admin", montauk::abi::CAP_DEVICE_ADMIN}, + {"log_read", montauk::abi::CAP_LOG_READ}, + {"system_image", montauk::abi::CAP_SYSTEM_IMAGE}, + {"compositor", montauk::abi::CAP_COMPOSITOR}, + }; + + // Never implied by "all": each must be named explicitly in a grant. + inline constexpr uint64_t EXPLICIT_ONLY = + montauk::abi::CAP_SYSTEM_IMAGE | montauk::abi::CAP_COMPOSITOR; + + inline uint64_t bit_for_name(const char* name) { + if (name == nullptr || name[0] == '\0') return 0; + // "all" means "everything this launcher may pass on". + if (montauk::streq(name, "all")) + return montauk::abi::CAP_ALL & ~EXPLICIT_ONLY; + for (const auto& entry : NAMES) { + if (montauk::streq(entry.name, name)) return entry.bit; + } + + return 0; + } + + // True when a grant for `path` can apply: the program is installed under + // 0:/os or 0:/apps, which cannot be rewritten without system_image. The + // kernel enforces this (Fs::IsSystemImage) by starting any other program + // without capabilities; this copy of the rule is only for display. + inline bool is_installed_path(const char* path) { + if (path == nullptr) return false; + const char* p = path; + uint64_t drive = 0; + if (*p < '0' || *p > '9') return false; + while (*p >= '0' && *p <= '9') drive = drive * 10 + (uint64_t)(*p++ - '0'); + if (*p != ':' || drive != 0) return false; + p++; + static const char* const roots[] = {"/os", "/apps"}; + for (const char* root : roots) { + int i = 0; + while (root[i]) { + char c = p[i]; + if (c >= 'A' && c <= 'Z') c = (char)(c + ('a' - 'A')); + if (c != root[i]) break; + i++; + } + if (root[i] == '\0' && (p[i] == '\0' || p[i] == '/')) return true; + } + return false; + } + + // Read an array-of-strings key into a capability mask. + inline uint64_t mask_from_key(const montauk::toml::Doc& doc, const char* key) { + montauk::toml::Value* arr = doc.get_array(key); + if (arr == nullptr) return 0; + + uint64_t mask = 0; + for (int i = 0; i < arr->array.count; i++) { + montauk::toml::Value* item = arr->array.items[i]; + if (item == nullptr || item->type != montauk::toml::Type::String) continue; + mask |= bit_for_name(item->str); + } + return mask; + } + + // Append `suffix` to the "grant.." stem of `path_key`. + // Returns false if the key is not of that shape or does not fit. + inline bool build_sibling_key(const char* path_key, const char* suffix, + char* out, int outSz) { + int prefixLen = 0; + for (; GRANT_PREFIX[prefixLen]; prefixLen++) { + if (path_key[prefixLen] != GRANT_PREFIX[prefixLen]) return false; + } + + // Copy through the final '.' so "grant.foo.path" yields "grant.foo.". + int lastDot = -1; + for (int i = 0; path_key[i]; i++) { + if (path_key[i] == '.') lastDot = i; + } + if (lastDot < prefixLen) return false; + + int n = 0; + for (; n <= lastDot && n < outSz - 1; n++) out[n] = path_key[n]; + for (int i = 0; suffix[i] && n < outSz - 1; i++) out[n++] = suffix[i]; + out[n] = '\0'; + return true; + } + + // Look up the grant declared for `binary_path`. Returns false when the + // path has no entry, which is the common case and means "no authority". + inline bool lookup(const char* binary_path, + montauk::abi::SpawnCapabilities& out) { + out = {0, 0}; + if (binary_path == nullptr || binary_path[0] == '\0') return false; + + montauk::toml::Doc doc = montauk::config::load(GRANT_CONFIG); + + bool found = false; + for (int i = 0; i < doc.entries.count && !found; i++) { + montauk::toml::Value* entry = doc.entries.items[i]; + if (entry == nullptr || entry->key == nullptr) continue; + if (entry->type != montauk::toml::Type::String) continue; + + char sibling[128]; + if (!build_sibling_key(entry->key, "path", sibling, sizeof(sibling))) continue; + if (!montauk::streq(sibling, entry->key)) continue; + if (!montauk::streq(entry->str, binary_path)) continue; + + build_sibling_key(entry->key, "effective", sibling, sizeof(sibling)); + uint64_t effective = mask_from_key(doc, sibling); + build_sibling_key(entry->key, "delegable", sibling, sizeof(sibling)); + uint64_t delegable = mask_from_key(doc, sibling); + + out.effective = effective; + out.delegable = delegable; + found = true; + } + + doc.destroy(); + return found; + } + + // The calling process's own capability masks. + inline bool self(montauk::abi::SpawnCapabilities& out) { + out = {0, 0}; + return montauk::getcaps(&out) == 0; + } + + inline uint64_t self_delegable() { + montauk::abi::SpawnCapabilities mine; + return self(mine) ? mine.delegable : 0; + } + + // Build a spawn request for `binary_path`, clamped to what the caller may + // actually delegate. + inline montauk::abi::SpawnCapabilities for_binary(const char* binary_path, + uint64_t caller_delegable) { + montauk::abi::SpawnCapabilities caps{0, 0}; + + if (caller_delegable == 0) return caps; + + if (!lookup(binary_path, caps)) return caps; + + caps.effective &= caller_delegable; + caps.delegable &= caller_delegable; + return caps; + } + + // The calling process's delegable set, cached. + inline uint64_t my_delegable() { + static uint64_t cached = 0; + static bool loaded = false; + if (!loaded) { + cached = self_delegable(); + loaded = true; + } + return cached; + } + + struct LaunchOptions { + const char* args = nullptr; + // Owner identity for the child. Requires CAP_USER_ADMIN and is + // reserved for session creators (init, login); + const char* user = nullptr; + // True for a console host that reads and writes the child's I/O + // through the childio calls rather than sharing its own. + bool redirected = false; + }; + + // Unified launch helper. + inline int launch(const char* path, const LaunchOptions& opt = {}) { + if (path == nullptr || path[0] == '\0') return -1; + + if (opt.redirected && opt.user != nullptr) return -1; + + montauk::abi::SpawnCapabilities caps = for_binary(path, my_delegable()); + return opt.redirected + ? spawn_redir_with_caps(path, opt.args, caps) + : spawn_with_caps(path, opt.args, opt.user, caps); + } + +} // namespace caps +} // namespace montauk diff --git a/template/sysroot/include/montauk/consent.h b/template/sysroot/include/montauk/consent.h new file mode 100644 index 0000000..93abc45 --- /dev/null +++ b/template/sysroot/include/montauk/consent.h @@ -0,0 +1,209 @@ +/* + * consent.h + * First-use consent: prompt wording and the stored-decision table + * (0:/config/consent.toml). + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include +#include +#include +#include + +namespace montauk { +namespace consent { + + inline constexpr const char* STORE_CONFIG = "consent"; + inline constexpr const char* STORE_PREFIX = "decision."; + + struct CapText { + uint64_t bit; + const char* action; // completes " wants to ..." + const char* icon; // shown in the prompt + }; + + inline constexpr CapText TEXTS[] = { + {montauk::abi::CAP_STORAGE_ADMIN, "format, partition and mount disks", + "0:/os/icons/drive-harddisk.svg"}, + {montauk::abi::CAP_NETWORK_ADMIN, "change network settings and join Wi-Fi networks", + "0:/os/icons/network-wired.svg"}, + {montauk::abi::CAP_SET_TIME, "change the system clock and time zone", + "0:/os/icons/preferences-system-time.svg"}, + {montauk::abi::CAP_DISPLAY_ADMIN, "change the screen resolution and brightness", + "0:/os/icons/preferences-desktop-display.svg"}, + {montauk::abi::CAP_LOG_READ, "read the system log, which can reveal what other apps and devices are doing", + "0:/os/icons/utilities-log-viewer.svg"}, + }; + + // Fallback for a capability without its own icon. + inline constexpr const char* DEFAULT_ICON = "0:/os/icons/lock.svg"; + + inline const char* action_for(uint64_t bit) { + for (const auto& t : TEXTS) { + if (t.bit == bit) return t.action; + } + return "use a protected system capability"; + } + + inline const char* icon_for(uint64_t bit) { + for (const auto& t : TEXTS) { + if (t.bit == bit) return t.icon; + } + return DEFAULT_ICON; + } + + inline const char* name_for(uint64_t bit) { + for (const auto& n : montauk::caps::NAMES) { + if (n.bit == bit) return n.name; + } + return ""; + } + + // One stored decision, as read from the table. Only programs installed + // under 0:/os or 0:/apps are ever stored: those paths cannot be rewritten + // without CAP_SYSTEM_IMAGE, so the path alone identifies the program. + // The kernel reports this as ConsentRequest::imageProtected. + struct Decision { + char id[16]; + char path[256]; + uint64_t capability; + bool allow; + }; + + namespace detail { + inline void key(char* out, int cap, const char* id, const char* field) { + int n = 0; + for (const char* p = STORE_PREFIX; *p && n < cap - 1; p++) out[n++] = *p; + for (const char* p = id; *p && n < cap - 1; p++) out[n++] = *p; + if (n < cap - 1) out[n++] = '.'; + for (const char* p = field; *p && n < cap - 1; p++) out[n++] = *p; + out[n] = '\0'; + } + + // Ids of "decision..path" entries, in file order. + inline int collect_ids(const montauk::toml::Doc& doc, char (*ids)[16], int max) { + int count = 0; + int prefixLen = montauk::slen(STORE_PREFIX); + for (int i = 0; i < doc.entries.count && count < max; i++) { + montauk::toml::Value* v = doc.entries.items[i]; + if (v == nullptr || v->key == nullptr) continue; + if (!montauk::starts_with(v->key, STORE_PREFIX)) continue; + const char* rest = v->key + prefixLen; + int dot = 0; + while (rest[dot] && rest[dot] != '.') dot++; + if (rest[dot] != '.' || !montauk::streq(rest + dot + 1, "path")) continue; + if (dot <= 0 || dot >= 16) continue; + montauk::memcpy(ids[count], rest, dot); + ids[count][dot] = '\0'; + count++; + } + return count; + } + + inline bool read(const montauk::toml::Doc& doc, const char* id, Decision& out) { + char k[64]; + montauk::memset(&out, 0, sizeof(out)); + montauk::strncpy(out.id, id, sizeof(out.id)); + key(k, sizeof(k), id, "path"); + montauk::strncpy(out.path, doc.get_string(k, ""), sizeof(out.path)); + key(k, sizeof(k), id, "capability"); + out.capability = montauk::caps::bit_for_name(doc.get_string(k, "")); + key(k, sizeof(k), id, "decision"); + out.allow = montauk::streq(doc.get_string(k, ""), "allow"); + // "all" or an unknown name is never a valid stored decision. + return out.path[0] && + (out.capability & montauk::abi::CAP_CONSENT_ELIGIBLE) != 0 && + (out.capability & (out.capability - 1)) == 0; + } + + inline void remove_id(montauk::toml::Doc* doc, const char* id) { + static const char* fields[] = {"path", "capability", "decision"}; + char k[64]; + for (const char* f : fields) { + key(k, sizeof(k), id, f); + montauk::config::unset(doc, k); + } + } + } // namespace detail + + // Read up to `max` stored decisions. Returns the count. + inline int load_all(Decision* out, int max) { + montauk::toml::Doc doc = montauk::config::load(STORE_CONFIG); + char ids[128][16]; + int idCount = detail::collect_ids(doc, ids, 128); + int count = 0; + for (int i = 0; i < idCount && count < max; i++) { + if (detail::read(doc, ids[i], out[count])) count++; + } + doc.destroy(); + return count; + } + + // Stored answer for (path, capability): 1 allow, 0 deny, -1 none. + inline int lookup(const char* path, uint64_t capability) { + montauk::toml::Doc doc = montauk::config::load(STORE_CONFIG); + char ids[128][16]; + int idCount = detail::collect_ids(doc, ids, 128); + int result = -1; + for (int i = 0; i < idCount && result < 0; i++) { + Decision d; + if (!detail::read(doc, ids[i], d)) continue; + if (d.capability == capability && montauk::streq(d.path, path)) { + result = d.allow ? 1 : 0; + } + } + doc.destroy(); + return result; + } + + // Store an answer, replacing any earlier one for the same path and + // capability. Returns 0 on success, negative when the table cannot be + // written. + inline int record(const char* path, uint64_t capability, bool allow) { + montauk::toml::Doc doc = montauk::config::load(STORE_CONFIG); + char ids[128][16]; + int idCount = detail::collect_ids(doc, ids, 128); + int nextId = 1; + for (int i = 0; i < idCount; i++) { + Decision d; + bool valid = detail::read(doc, ids[i], d); + if (valid && d.capability == capability && montauk::streq(d.path, path)) + detail::remove_id(&doc, ids[i]); + int n = 0; + for (const char* p = ids[i]; *p >= '0' && *p <= '9'; p++) n = n * 10 + (*p - '0'); + if (n >= nextId) nextId = n + 1; + } + + char id[16]; + int len = 0; + char rev[16]; + for (int n = nextId; n > 0 && len < 15; n /= 10) rev[len++] = (char)('0' + n % 10); + for (int i = 0; i < len; i++) id[i] = rev[len - 1 - i]; + id[len] = '\0'; + + char k[64]; + detail::key(k, sizeof(k), id, "path"); + montauk::config::set_string(&doc, k, path); + detail::key(k, sizeof(k), id, "capability"); + montauk::config::set_string(&doc, k, name_for(capability)); + detail::key(k, sizeof(k), id, "decision"); + montauk::config::set_string(&doc, k, allow ? "allow" : "deny"); + + int rc = montauk::config::save(STORE_CONFIG, &doc); + doc.destroy(); + return rc; + } + + // Drop one stored decision by id; the program is asked again next time. + inline int forget(const char* id) { + montauk::toml::Doc doc = montauk::config::load(STORE_CONFIG); + detail::remove_id(&doc, id); + int rc = montauk::config::save(STORE_CONFIG, &doc); + doc.destroy(); + return rc; + } + +} // namespace consent +} // namespace montauk diff --git a/template/sysroot/include/montauk/statuscodes.h b/template/sysroot/include/montauk/statuscodes.h new file mode 100644 index 0000000..be65416 --- /dev/null +++ b/template/sysroot/include/montauk/statuscodes.h @@ -0,0 +1,35 @@ +/* + * statuscodes.h + * MontaukOS status codes + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +// Userspace mirror of kernel/src/Common/StatusCodes.hpp. Also usable from C +// (the libc includes it), where the enumerators are unqualified. +#ifdef __cplusplus +namespace montauk { +#endif + +enum StatusCodes { + Success = 0, + GenericFailure = -1, + PermissionDenied = -2, + CapabilityDenied = -3, + SystemIntegrityError = -4, + BadPointer = -5, + InvalidArgument = -6, + UnsupportedOp = -7, + NoDevice = -8, + IoFailure = -9, + BadInvocation = -10, + NoSuchPath = -11, + Busy = -12, + BufferTooSmall = -13, + OutOfMemory = -14 +}; + +#ifdef __cplusplus +} +#endif diff --git a/template/sysroot/include/montauk/syscall.h b/template/sysroot/include/montauk/syscall.h index e963fa3..5c9c188 100644 --- a/template/sysroot/include/montauk/syscall.h +++ b/template/sysroot/include/montauk/syscall.h @@ -6,6 +6,7 @@ #pragma once #include +#include namespace montauk { @@ -120,6 +121,28 @@ namespace montauk { inline int spawn(const char* path, const char* args = nullptr) { return (int)syscall2(montauk::abi::SYS_SPAWN, (uint64_t)path, (uint64_t)args); } + inline int spawn_with_caps(const char* path, const char* args, + const char* user, + const montauk::abi::SpawnCapabilities& capabilities) { + return (int)syscall4(montauk::abi::SYS_SPAWN_CAPS, (uint64_t)path, + (uint64_t)args, (uint64_t)user, + (uint64_t)&capabilities); + } + // The calling process's own capability masks. Returns 0 on success. + inline int getcaps(montauk::abi::SpawnCapabilities* out) { + return (int)syscall1(montauk::abi::SYS_GETCAPS, (uint64_t)out); + } + + // Consent agent. + inline int consent_register() { + return (int)syscall0(montauk::abi::SYS_CONSENT_REGISTER); + } + inline int consent_next(montauk::abi::ConsentRequest* out) { + return (int)syscall1(montauk::abi::SYS_CONSENT_NEXT, (uint64_t)out); + } + inline int consent_reply(uint64_t id, int decision) { + return (int)syscall2(montauk::abi::SYS_CONSENT_REPLY, id, (uint64_t)decision); + } inline int chdir(const char* path) { return (int)syscall1(montauk::abi::SYS_CHDIR, (uint64_t)path); } @@ -184,6 +207,10 @@ namespace montauk { // Memory inline void* alloc(uint64_t size) { return (void*)syscall1(montauk::abi::SYS_ALLOC, size); } + // As alloc(), but the kernel commits the whole range immediately. Use it + // for a buffer that is about to be written end to end: the lazy path costs + // one page fault, one mutex acquire and one VMA walk per 4 KiB. + inline void* alloc_eager(uint64_t size) { return (void*)syscall1(montauk::abi::SYS_ALLOC_EAGER, size); } inline void free(void* ptr) { syscall1(montauk::abi::SYS_FREE, (uint64_t)ptr); } // Timekeeping @@ -391,7 +418,10 @@ namespace montauk { } // Timezone offset (total minutes from UTC) - inline void settz(int offset_minutes) { syscall1(montauk::abi::SYS_SETTZ, (uint64_t)(int64_t)offset_minutes); } + inline int settz(int offset_minutes) { + return (int)syscall1(montauk::abi::SYS_SETTZ, + (uint64_t)(int64_t)offset_minutes); + } inline int gettz() { return (int)syscall0(montauk::abi::SYS_GETTZ); } // Random number generation @@ -400,14 +430,12 @@ namespace montauk { } // Power management - [[noreturn]] inline void reset() { - syscall0(montauk::abi::SYS_RESET); - __builtin_unreachable(); + inline int reset() { + return (int)syscall0(montauk::abi::SYS_RESET); } - [[noreturn]] inline void shutdown() { - syscall0(montauk::abi::SYS_SHUTDOWN); - __builtin_unreachable(); + inline int shutdown() { + return (int)syscall0(montauk::abi::SYS_SHUTDOWN); } inline int suspend() { @@ -423,6 +451,13 @@ namespace montauk { return (int)syscall1(montauk::abi::SYS_POWER_REQUEST, (uint64_t)(int64_t)action); } + // Non-destructive read of the pending request, for a session leader that + // must stand down when something inside its session (the shell's shutdown + // builtin, say) asked for power-off. Returns POWER_REQ_QUERY when idle. + inline int power_request_pending() { + return power_request(montauk::abi::POWER_REQ_PEEK); + } + // Mouse inline void mouse_state(montauk::abi::MouseState* out) { syscall1(montauk::abi::SYS_MOUSESTATE, (uint64_t)out); } inline void set_mouse_bounds(int32_t maxX, int32_t maxY) { @@ -447,6 +482,13 @@ namespace montauk { inline int spawn_redir(const char* path, const char* args = nullptr) { return (int)syscall2(montauk::abi::SYS_SPAWN_REDIR, (uint64_t)path, (uint64_t)args); } + inline int spawn_redir_with_caps( + const char* path, const char* args, + const montauk::abi::SpawnCapabilities& capabilities) { + return (int)syscall3(montauk::abi::SYS_SPAWN_REDIR_CAPS, + (uint64_t)path, (uint64_t)args, + (uint64_t)&capabilities); + } inline int childio_read(int childPid, char* buf, int maxLen) { return (int)syscall3(montauk::abi::SYS_CHILDIO_READ, (uint64_t)childPid, (uint64_t)buf, (uint64_t)maxLen); } @@ -484,6 +526,10 @@ namespace montauk { inline int partlist(montauk::abi::PartInfo* buf, int max) { return (int)syscall2(montauk::abi::SYS_PARTLIST, (uint64_t)buf, (uint64_t)max); } + // One entry per mounted volume (one filesystem, i.e. one drive number). + inline int volinfo(montauk::abi::VolInfo* buf, int max) { + return (int)syscall2(montauk::abi::SYS_VOLINFO, (uint64_t)buf, (uint64_t)max); + } // Raw block device I/O (driver-agnostic) inline int64_t disk_read(int blockDev, uint64_t lba, uint32_t sectorCount, void* buf) { @@ -613,6 +659,13 @@ namespace montauk { return (int)syscall1(montauk::abi::SYS_BTINFO, (uint64_t)buf); } + // ACPI battery status. Returns 0 on success, or -1 on a machine with no + // battery device at all -- which is the signal not to draw an indicator. + // A laptop with the pack removed still returns 0, with present == 0. + inline int battery_info(montauk::abi::BatteryInfo* out) { + return (int)syscall1(montauk::abi::SYS_BATTERYINFO, (uint64_t)out); + } + // Wi-Fi. scan() runs a full channel sweep and blocks until it finishes or // timeoutMs elapses, then fills buf with the networks seen; it returns the // number of entries written, or -1 when no adapter is ready. diff --git a/template/sysroot/include/montauk/user.h b/template/sysroot/include/montauk/user.h index 85dbae5..efff1e0 100644 --- a/template/sysroot/include/montauk/user.h +++ b/template/sysroot/include/montauk/user.h @@ -219,6 +219,16 @@ namespace user { return false; } + inline bool is_admin(const char* username) { + UserInfo users[MAX_USERS]; + int count = load_users(users, MAX_USERS); + for (int i = 0; i < count; i++) { + if (montauk::streq(users[i].username, username)) + return montauk::streq(users[i].role, "admin"); + } + return false; + } + // ---- User management ---- inline bool create_user(const char* username, const char* display_name, diff --git a/template/sysroot/lib/liblibc.a b/template/sysroot/lib/liblibc.a index f69128e..17fd946 100644 Binary files a/template/sysroot/lib/liblibc.a and b/template/sysroot/lib/liblibc.a differ