feat: unify system status codes

This commit is contained in:
daniel committed 2026-09-30 09:43:18 +02:00
1 parent 93894b8fe5
commit 8a3ffb2be1
46 files changed
+1499 -489

No files matched your search

+2 -2
View File
@@ -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) {
+14 -14
View File
@@ -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;
}
}
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 251
#define MONTAUK_BUILD_NUMBER 254
+18 -16
View File
@@ -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;
}
}
+4 -4
View File
@@ -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;
}
};
+32 -22
View File
@@ -20,6 +20,8 @@
#include <Fs/ProtectedPaths.hpp>
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);
}
+10 -5
View File
@@ -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) {
+9 -9
View File
@@ -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
+20 -20
View File
@@ -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, &copy);
}
@@ -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;
}
};
+6 -6
View File
@@ -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) {
+4 -4
View File
@@ -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.
+3 -3
View File
@@ -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 ----
+6 -6
View File
@@ -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;
}
};
+30 -30
View File
@@ -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;
}
};
+25 -25
View File
@@ -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, &params->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
}
}
+182 -180
View File
@@ -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<SysInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<SysInfo>(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<KeyEvent>(frame->arg1)) return -1;
if (!UserMemory::Writable<KeyEvent>(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<SpawnCapabilities>(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<SpawnCapabilities>(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<FbInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<FbInfo>(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<DisplayInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<DisplayInfo>(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<FileStat>(frame->arg2)) return -1;
if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return BadPointer;
if (!UserMemory::Writable<FileStat>(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<DateTime>(frame->arg1)) return -1;
if (!UserMemory::Writable<DateTime>(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<NetCfg>(frame->arg1)) return -1;
if (!UserMemory::Writable<NetCfg>(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<NetCfg>(frame->arg1)) return -1;
if (!Sched::Consent::Require(CAP_NETWORK_ADMIN)) return CapabilityDenied;
if (!UserMemory::Readable<NetCfg>(frame->arg1)) return BadPointer;
return (int64_t)Sys_SetNetCfg((const NetCfg*)frame->arg1);
case SYS_NETSTATUS:
if (!UserMemory::Writable<NetStatus>(frame->arg1)) return -1;
if (!UserMemory::Writable<NetStatus>(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<MouseState>(frame->arg1)) return -1;
if (!UserMemory::Writable<MouseState>(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<SpawnCapabilities>(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<SpawnCapabilities>(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<KeyEvent>(frame->arg2)) return -1;
if (!UserMemory::Readable<KeyEvent>(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<WinCreateResult>(frame->arg4)) return -1;
if (!UserMemory::String(frame->arg1, kMaxWindowTitleBytes)) return BadPointer;
if (!UserMemory::Writable<WinCreateResult>(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<WinEvent>(frame->arg2)) return -1;
if (!UserMemory::Writable<WinEvent>(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<WinEvent>(frame->arg2)) return -1;
if (!UserMemory::Readable<WinEvent>(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<DiskInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<DiskInfo>(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<MemStats>(frame->arg1)) return -1;
if (!UserMemory::Writable<MemStats>(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<GptAddParams>(frame->arg1)) return -1;
if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return CapabilityDenied;
if (!UserMemory::Readable<GptAddParams>(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<FsFormatParams>(frame->arg1)) return -1;
if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return CapabilityDenied;
if (!UserMemory::Readable<FsFormatParams>(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<PowerInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<PowerInfo>(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<BatteryInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<BatteryInfo>(frame->arg1)) return BadPointer;
return Sys_BatteryInfo((BatteryInfo*)frame->arg1);
case SYS_GETCAPS:
if (!UserMemory::Writable<SpawnCapabilities>(frame->arg1)) return -1;
if (!UserMemory::Writable<SpawnCapabilities>(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<ConsentRequest>(frame->arg1)) return -1;
if (!UserMemory::Writable<ConsentRequest>(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<int>(frame->arg2)) return -1;
if (frame->arg2 != 0 && !UserMemory::Writable<int>(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<BtAdapterInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<BtAdapterInfo>(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<int>(frame->arg1) || !UserMemory::Writable<int>(frame->arg2)) return -1;
if (!UserMemory::Writable<int>(frame->arg1) || !UserMemory::Writable<int>(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<int>(frame->arg1) || !UserMemory::Writable<int>(frame->arg2)) return -1;
if (!UserMemory::Writable<int>(frame->arg1) || !UserMemory::Writable<int>(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<uint16_t>(frame->arg4)) return -1;
if (frame->arg2 != 0 && !UserMemory::Writable<uint32_t>(frame->arg2)) return -1;
if (!UserMemory::Writable<uint16_t>(frame->arg4)) return BadPointer;
if (frame->arg2 != 0 && !UserMemory::Writable<uint32_t>(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<int>(frame->arg5)) return -1;
if (frame->arg5 != 0 && !UserMemory::Writable<int>(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<IpcWaitResult>(frame->arg2)) return -1;
if (frame->arg2 != 0 && !UserMemory::Writable<IpcWaitResult>(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<CrashReportInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<CrashReportInfo>(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<ClipboardInfo>(frame->arg1)) return -1;
if (!UserMemory::Writable<ClipboardInfo>(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<uint32_t>(frame->arg3)) return -1;
if (frame->arg4 != 0 && !UserMemory::Writable<uint64_t>(frame->arg4)) return -1;
if (frame->arg2 > 0 && !UserMemory::Range(frame->arg1, frame->arg2, true)) return BadPointer;
if (!UserMemory::Writable<uint32_t>(frame->arg3)) return BadPointer;
if (frame->arg4 != 0 && !UserMemory::Writable<uint64_t>(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;
}
}
+4
View File
@@ -7,6 +7,7 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <Common/StatusCodes.hpp>
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
+2 -2
View File
@@ -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;
}
};
+42 -37
View File
@@ -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() {
+7 -7
View File
@@ -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);
}
+28
View File
@@ -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
};
+6 -5
View File
@@ -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);
}
+7 -7
View File
@@ -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) {
+4
View File
@@ -7,6 +7,7 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <montauk/statuscodes.h>
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
+21
View File
@@ -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)
+35
View File
@@ -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
+1
View File
@@ -6,6 +6,7 @@
#pragma once
#include <Api/Syscall.hpp>
#include <montauk/statuscodes.h>
namespace montauk {
+6 -4
View File
@@ -15,6 +15,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include "../../include/montauk/statuscodes.h"
#include <math.h>
#include <string.h>
#include <time.h>
@@ -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;
}
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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");
+2 -2
View File
@@ -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;
}
+92 -18
View File
@@ -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"(?<!^)(?=[A-Z])", "_", name).upper()
def check_status(c_text):
"""Print drift between the kernel enum and its two mirrors; return count."""
canon = parse_status(STATUS_HDR)
problems = 0
mirror = parse_status(STATUS_MIRROR)
if mirror != canon:
problems += 1
print("montauk-syscalls: DRIFT between Common/StatusCodes.hpp and "
"montauk/statuscodes.h", file=sys.stderr)
print(" kernel: %s" % canon, file=sys.stderr)
print(" mirror: %s" % mirror, file=sys.stderr)
want = {status_macro(n): v for n, v in canon}
have = {n: int(v) for n, v in STATUS_DEF_RE.findall(c_text)}
if want != have:
problems += 1
print("montauk-syscalls: DRIFT between Common/StatusCodes.hpp and "
"MTK_STATUS_* in libc/montauk.h", file=sys.stderr)
for n in sorted(set(want) | set(have)):
if want.get(n) != have.get(n):
print(" MTK_STATUS_%-22s kernel=%s montauk.h=%s"
% (n, want.get(n), have.get(n)), file=sys.stderr)
return problems
def diff(abi, defs):
"""Return (missing, mismatched, extra) describing drift of defs vs abi."""
missing = sorted(n for n in abi if n not in defs)
@@ -74,10 +126,17 @@ def diff(abi, defs):
def cmd_check():
abi = parse_abi()
defs = parse_defines(read(C_HDR))
c_text = read(C_HDR)
defs = parse_defines(c_text)
missing, mismatched, extra = diff(abi, defs)
status_problems = check_status(c_text)
if not (missing or mismatched or extra):
print("montauk-syscalls: OK (%d syscalls in sync)" % len(abi))
if status_problems:
print("\n Run 'make gen-syscalls' to regenerate MTK_STATUS_*; "
"montauk/statuscodes.h is fixed by hand.", file=sys.stderr)
return 1
print("montauk-syscalls: OK (%d syscalls, %d status codes in sync)"
% (len(abi), len(parse_status(STATUS_HDR))))
return 0
print("montauk-syscalls: DRIFT between Api/Syscall.hpp and libc/montauk.h",
@@ -115,18 +174,33 @@ def render_block(abi):
return "\n".join(lines)
def render_status_block(codes):
width = max(len("MTK_STATUS_" + status_macro(n)) for n, _ in codes) + 1
lines = [STATUS_BEGIN_MARKER,
" Generated from Common/StatusCodes.hpp by scripts/montauk-syscalls.py.",
" Do not edit by hand; run 'make gen-syscalls' to refresh. */"]
for name, value in codes:
macro = "MTK_STATUS_" + status_macro(name)
lines.append("#define %-*s(%d)" % (width, macro, value))
lines.append(STATUS_END_MARKER)
return "\n".join(lines)
def replace_block(text, begin, end, block):
if begin not in text or end not in text:
sys.exit("montauk-syscalls: markers not found in %s; add a "
"%s ... %s block first" % (C_HDR, begin, end))
start = text.index(begin)
stop = text.index(end) + len(end)
return text[:start] + block + text[stop:]
def cmd_gen():
abi = parse_abi()
text = read(C_HDR)
block = render_block(abi)
if BEGIN_MARKER in text and END_MARKER in text:
start = text.index(BEGIN_MARKER)
end = text.index(END_MARKER) + len(END_MARKER)
new = text[:start] + block + text[end:]
else:
sys.exit("montauk-syscalls: markers not found in %s; add a "
"%s ... %s block first" % (C_HDR, BEGIN_MARKER, END_MARKER))
new = replace_block(text, BEGIN_MARKER, END_MARKER, render_block(abi))
new = replace_block(new, STATUS_BEGIN_MARKER, STATUS_END_MARKER,
render_status_block(parse_status(STATUS_HDR)))
if new == text:
print("montauk-syscalls: montauk.h already up to date (%d syscalls)"
+23
View File
@@ -2,8 +2,31 @@
All syscalls are available through `#include <montauk/syscall.h>` 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)
+168
View File
@@ -7,6 +7,7 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <montauk/statuscodes.h>
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;
+10
View File
@@ -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
+101 -40
View File
@@ -10,6 +10,7 @@
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <Api/Syscall.hpp>
#include <montauk/capabilities.h>
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;
+4 -4
View File
@@ -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;
}
+36 -6
View File
@@ -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);
}
/* ====================================================================
+1
View File
@@ -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
}
@@ -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 <montauk/syscall.h>
#include <montauk/toml.h>
#include <montauk/config.h>
#include <montauk/heap.h>
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.<id>." 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
+209
View File
@@ -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 <montauk/syscall.h>
#include <montauk/string.h>
#include <montauk/toml.h>
#include <montauk/config.h>
#include <montauk/capabilities.h>
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 "<App> 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.<id>.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
@@ -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
+60 -7
View File
@@ -6,6 +6,7 @@
#pragma once
#include <Api/Syscall.hpp>
#include <montauk/statuscodes.h>
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.
+10
View File
@@ -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,
Binary file not shown.