feat: unify system status codes
This commit is contained in:
1 parent
93894b8fe5
commit
8a3ffb2be1
46 files changed
+1499
-489
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 251
|
||||
#define MONTAUK_BUILD_NUMBER 254
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -19,18 +19,18 @@ namespace montauk::abi {
|
||||
const char* path, const char* args,
|
||||
const SpawnCapabilities* capabilities) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return InvalidArgument;
|
||||
|
||||
int parentSlot = Ipc::CurrentSlot();
|
||||
int childPid = Sched::Spawn(resolved, args, false, nullptr, 0,
|
||||
capabilities);
|
||||
if (childPid < 0) return -1;
|
||||
if (childPid < 0) return childPid;
|
||||
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
int childSlot = Ipc::SlotForPid(childPid);
|
||||
if (child == nullptr || childSlot < 0 || parentSlot < 0) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
return GenericFailure;
|
||||
}
|
||||
|
||||
Ipc::Stream* outStream = Ipc::CreateStream(RedirOutputStreamCapacity);
|
||||
@@ -41,7 +41,7 @@ namespace montauk::abi {
|
||||
if (inStream != nullptr) Ipc::ReleaseStream(inStream, false, false);
|
||||
if (keyMailbox != nullptr) Ipc::ReleaseMailbox(keyMailbox, false, false);
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
return OutOfMemory;
|
||||
}
|
||||
|
||||
int parentOutHandle = Ipc::InstallHandleForSlot(
|
||||
@@ -71,7 +71,7 @@ namespace montauk::abi {
|
||||
if (child->ioInHandle < 0) Ipc::ReleaseStream(inStream, false, false);
|
||||
if (child->ioKeyHandle < 0) Ipc::ReleaseMailbox(keyMailbox, false, false);
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
return GenericFailure;
|
||||
}
|
||||
|
||||
child->redirected = true;
|
||||
@@ -80,7 +80,7 @@ namespace montauk::abi {
|
||||
child->termRows = 25;
|
||||
if (Sched::StartProcess(childPid) < 0) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
return GenericFailure;
|
||||
}
|
||||
|
||||
return childPid;
|
||||
@@ -93,11 +93,11 @@ namespace montauk::abi {
|
||||
static int Sys_SpawnRedirCaps(const char* path, const char* args,
|
||||
const SpawnCapabilities* requested) {
|
||||
auto* parent = Sched::GetCurrentProcessPtr();
|
||||
if (parent == nullptr || requested == nullptr) return -1;
|
||||
if (parent == nullptr || requested == nullptr) return InvalidArgument;
|
||||
|
||||
SpawnCapabilities copy = *requested;
|
||||
if (!ValidCapabilityDelegation(copy, parent->delegableCaps)) {
|
||||
return SYS_ERR_PERMISSION;
|
||||
return CapabilityDenied;
|
||||
}
|
||||
return Sys_SpawnRedirInternal(path, args, ©);
|
||||
}
|
||||
@@ -105,51 +105,51 @@ namespace montauk::abi {
|
||||
static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
|
||||
return SYS_ERR_PERMISSION;
|
||||
return PermissionDenied;
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Stream* stream = GetRedirOutStream(child, snapshot);
|
||||
if (!child->redirected || stream == nullptr) return -1;
|
||||
if (!child->redirected || stream == nullptr) return InvalidArgument;
|
||||
return Ipc::StreamRead(stream, (uint8_t*)buf, maxLen, true);
|
||||
}
|
||||
|
||||
static int Sys_ChildIoWrite(int childPid, const char* data, int len) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
|
||||
return SYS_ERR_PERMISSION;
|
||||
return PermissionDenied;
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Stream* stream = GetRedirInStream(child, snapshot);
|
||||
if (!child->redirected || stream == nullptr) return -1;
|
||||
if (!child->redirected || stream == nullptr) return InvalidArgument;
|
||||
return WriteAllToStream(stream, (const uint8_t*)data, len);
|
||||
}
|
||||
|
||||
static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) {
|
||||
if (key == nullptr) return -1;
|
||||
if (key == nullptr) return InvalidArgument;
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
|
||||
return SYS_ERR_PERMISSION;
|
||||
return PermissionDenied;
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(child, snapshot);
|
||||
if (!child->redirected || mailbox == nullptr) return -1;
|
||||
if (!child->redirected || mailbox == nullptr) return InvalidArgument;
|
||||
|
||||
for (;;) {
|
||||
uint64_t observedWake = Sched::ObserveObjectWake(mailbox);
|
||||
int rc = Ipc::MailboxSend(mailbox, 0, key, sizeof(KeyEvent));
|
||||
if (rc < 0) return -1;
|
||||
if (rc < 0) return GenericFailure;
|
||||
if (rc == 0) {
|
||||
Sched::BlockOnObjectSince(mailbox, 0, observedWake);
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
return Success;
|
||||
}
|
||||
}
|
||||
|
||||
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
|
||||
return SYS_ERR_PERMISSION;
|
||||
if (!child->redirected) return -1;
|
||||
return PermissionDenied;
|
||||
if (!child->redirected) return InvalidArgument;
|
||||
child->termCols = cols;
|
||||
child->termRows = rows;
|
||||
return 0;
|
||||
return Success;
|
||||
}
|
||||
};
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 ----
|
||||
|
||||
@@ -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
@@ -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
@@ -29,7 +29,7 @@ namespace montauk::abi {
|
||||
// Fill a user buffer with partition info. Returns the number of entries written.
|
||||
static int Sys_PartList(PartInfo* buf, int maxCount) {
|
||||
if (buf == nullptr || maxCount <= 0) return 0;
|
||||
if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(PartInfo), true)) return -1;
|
||||
if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(PartInfo), true)) return BadPointer;
|
||||
|
||||
int total = Drivers::Storage::Gpt::GetPartitionCount();
|
||||
int count = total < maxCount ? total : maxCount;
|
||||
@@ -58,10 +58,10 @@ namespace montauk::abi {
|
||||
}
|
||||
|
||||
// Fill a user buffer with one entry per mounted volume. Returns the number
|
||||
// of entries written, or -1 for a bad buffer.
|
||||
// of entries written, or BadPointer for a bad buffer.
|
||||
static int Sys_VolInfo(VolInfo* buf, int maxCount) {
|
||||
if (buf == nullptr || maxCount <= 0) return 0;
|
||||
if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(VolInfo), true)) return -1;
|
||||
if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(VolInfo), true)) return BadPointer;
|
||||
|
||||
int drives[Fs::Vfs::MaxDrives];
|
||||
int n = Fs::Vfs::VfsDriveList(drives, Fs::Vfs::MaxDrives);
|
||||
@@ -99,47 +99,47 @@ namespace montauk::abi {
|
||||
return n;
|
||||
}
|
||||
|
||||
// Read sectors from a block device. Returns bytes read, or -1 on error.
|
||||
// Read sectors from a block device. Returns bytes read, or a status code.
|
||||
static int64_t Sys_DiskRead(int blockDev, uint64_t lba, uint32_t count, void* buffer) {
|
||||
if (buffer == nullptr || count == 0) return -1;
|
||||
if (count > 128) return -1; // same limit as AHCI
|
||||
if (buffer == nullptr || count == 0) return InvalidArgument;
|
||||
if (count > 128) return InvalidArgument; // same limit as AHCI
|
||||
|
||||
auto* dev = Drivers::Storage::GetBlockDevice(blockDev);
|
||||
if (!dev) return -1;
|
||||
if (!dev) return NoDevice;
|
||||
|
||||
if (lba >= dev->SectorCount || count > dev->SectorCount - lba) return -1;
|
||||
if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, true)) return -1;
|
||||
if (lba >= dev->SectorCount || count > dev->SectorCount - lba) return InvalidArgument;
|
||||
if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, true)) return BadPointer;
|
||||
|
||||
if (!dev->ReadSectors(dev->Ctx, lba, count, buffer)) return -1;
|
||||
if (!dev->ReadSectors(dev->Ctx, lba, count, buffer)) return IoFailure;
|
||||
|
||||
return (int64_t)(count * dev->SectorSize);
|
||||
}
|
||||
|
||||
// Write sectors to a block device. Returns bytes written, or -1 on error.
|
||||
// Write sectors to a block device. Returns bytes written, or a status code.
|
||||
static int64_t Sys_DiskWrite(int blockDev, uint64_t lba, uint32_t count, const void* buffer) {
|
||||
if (buffer == nullptr || count == 0) return -1;
|
||||
if (count > 128) return -1;
|
||||
if (buffer == nullptr || count == 0) return InvalidArgument;
|
||||
if (count > 128) return InvalidArgument;
|
||||
|
||||
auto* dev = Drivers::Storage::GetBlockDevice(blockDev);
|
||||
if (!dev) return -1;
|
||||
if (!dev) return NoDevice;
|
||||
|
||||
if (lba >= dev->SectorCount || count > dev->SectorCount - lba) return -1;
|
||||
if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, false)) return -1;
|
||||
if (lba >= dev->SectorCount || count > dev->SectorCount - lba) return InvalidArgument;
|
||||
if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, false)) return BadPointer;
|
||||
|
||||
if (!dev->WriteSectors(dev->Ctx, lba, count, buffer)) return -1;
|
||||
if (!dev->WriteSectors(dev->Ctx, lba, count, buffer)) return IoFailure;
|
||||
|
||||
return (int64_t)(count * dev->SectorSize);
|
||||
}
|
||||
|
||||
// Initialize a new GPT on a block device. Returns 0 on success, -1 on error.
|
||||
// Initialize a new GPT on a block device. Returns 0 on success, GenericFailure on error.
|
||||
static int64_t Sys_GptInit(int blockDev) {
|
||||
Fs::FsProbe::UnmountPartitionsForBlockDevice(blockDev);
|
||||
return (int64_t)Drivers::Storage::Gpt::InitializeGpt(blockDev);
|
||||
}
|
||||
|
||||
// Add a partition to an existing GPT. Returns partition index on success, -1 on error.
|
||||
// Add a partition to an existing GPT. Returns partition index on success, GenericFailure on error.
|
||||
static int64_t Sys_GptAdd(const GptAddParams* params) {
|
||||
if (params == nullptr) return -1;
|
||||
if (params == nullptr) return InvalidArgument;
|
||||
|
||||
Drivers::Storage::Gpt::Guid typeGuid;
|
||||
__builtin_memcpy(&typeGuid, ¶ms->typeGuid, sizeof(typeGuid));
|
||||
@@ -149,7 +149,7 @@ namespace montauk::abi {
|
||||
typeGuid, params->name);
|
||||
}
|
||||
|
||||
// Mount a partition as a VFS drive. Returns 0 on success, -1 on error.
|
||||
// Mount a partition as a VFS drive. Returns 0 on success, GenericFailure on error.
|
||||
static int64_t Sys_FsMount(int partIndex, int driveNum) {
|
||||
return (int64_t)Fs::FsProbe::MountPartition(partIndex, driveNum);
|
||||
}
|
||||
@@ -163,12 +163,12 @@ namespace montauk::abi {
|
||||
return (int64_t)Fs::FsProbe::SyncAndUnmountAll();
|
||||
}
|
||||
|
||||
// Format a partition with a filesystem. Returns 0 on success, -1 on error.
|
||||
// Format a partition with a filesystem. Returns 0 on success, GenericFailure on error.
|
||||
static int64_t Sys_FsFormat(const FsFormatParams* params) {
|
||||
if (params == nullptr) return -1;
|
||||
if (params == nullptr) return InvalidArgument;
|
||||
|
||||
auto* part = Drivers::Storage::Gpt::GetPartition(params->partIndex);
|
||||
if (!part) return -1;
|
||||
if (!part) return InvalidArgument;
|
||||
|
||||
switch (params->fsType) {
|
||||
case FS_TYPE_FAT32:
|
||||
@@ -180,7 +180,7 @@ namespace montauk::abi {
|
||||
part->BlockDevIndex, part->StartLba, part->SectorCount,
|
||||
params->label[0] ? params->label : nullptr);
|
||||
default:
|
||||
return -1; // unknown filesystem type
|
||||
return UnsupportedOp; // unknown filesystem type
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+182
-180
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <Api/Syscall.hpp>
|
||||
#include <montauk/statuscodes.h>
|
||||
|
||||
namespace montauk {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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)"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
Reference in new issue
Block a user