feat: implement new IPC layer

This commit is contained in:
2026-04-01 13:11:03 +02:00
parent 6276f8b162
commit 346977a0f9
34 changed files with 3210 additions and 897 deletions
+102 -24
View File
@@ -1,37 +1,115 @@
#pragma once #pragma once
#include <Sched/Scheduler.hpp> #include <Sched/Scheduler.hpp>
#include <Ipc/Ipc.hpp>
namespace Montauk { namespace Montauk {
// Find the process that owns the I/O ring buffers for a redirected process.
// If proc owns buffers itself (spawned via spawn_redir), returns proc.
// If proc inherited redirection (spawned via spawn from a redirected parent),
// follows parentPid to find the buffer owner.
static Sched::Process* GetRedirTarget(Sched::Process* proc) { static Sched::Process* GetRedirTarget(Sched::Process* proc) {
if (!proc || !proc->redirected) return nullptr; if (!proc || !proc->redirected) return nullptr;
if (proc->outBuf) return proc; // owns buffers if (proc->ioOutHandle >= 0 || proc->ioInHandle >= 0 || proc->ioKeyHandle >= 0) {
return Sched::GetProcessByPid(proc->parentPid); return proc;
}
return (proc->parentPid >= 0) ? Sched::GetProcessByPid(proc->parentPid) : nullptr;
} }
// SPSC ring buffer helpers. On x86 TSO, atomic-width aligned static bool ResolveProcessHandle(Sched::Process* proc, int handle, Ipc::HandleType expectedType,
// loads/stores are naturally atomic. The compiler barrier ensures Ipc::Object*& outObject, uint32_t* outRights = nullptr) {
// the data write is visible before the head update. if (proc == nullptr || handle < 0) return false;
static void RingWrite(uint8_t* buf, volatile uint32_t& head, uint32_t /*tail*/, uint32_t size, uint8_t byte) {
uint32_t h = head; int slot = Ipc::SlotForPid(proc->pid);
buf[h] = byte; if (slot < 0) return false;
asm volatile("" ::: "memory"); // compiler barrier
head = (h + 1) % size; Ipc::HandleType type = Ipc::HandleType::None;
Ipc::Object* object = nullptr;
uint32_t rights = 0;
if (!Ipc::SnapshotHandleForSlot(slot, handle, type, object, rights)) return false;
if (type != expectedType || object == nullptr) return false;
outObject = object;
if (outRights != nullptr) *outRights = rights;
return true;
} }
static int RingRead(uint8_t* buf, volatile uint32_t& head, volatile uint32_t& tail, uint32_t size, uint8_t* out, int maxLen) { static Ipc::Stream* GetRedirOutStream(Sched::Process* proc) {
int count = 0; proc = GetRedirTarget(proc);
uint32_t t = tail; if (proc == nullptr) return nullptr;
uint32_t h = head;
while (t != h && count < maxLen) { Ipc::Object* object = nullptr;
out[count++] = buf[t]; if (!ResolveProcessHandle(proc, proc->ioOutHandle, Ipc::HandleType::Stream, object)) return nullptr;
t = (t + 1) % size; return (Ipc::Stream*)object;
} }
asm volatile("" ::: "memory"); // compiler barrier
tail = t; static Ipc::Stream* GetRedirInStream(Sched::Process* proc) {
return count; proc = GetRedirTarget(proc);
if (proc == nullptr) return nullptr;
Ipc::Object* object = nullptr;
if (!ResolveProcessHandle(proc, proc->ioInHandle, Ipc::HandleType::Stream, object)) return nullptr;
return (Ipc::Stream*)object;
}
static Ipc::Mailbox* GetRedirKeyMailbox(Sched::Process* proc) {
proc = GetRedirTarget(proc);
if (proc == nullptr) return nullptr;
Ipc::Object* object = nullptr;
if (!ResolveProcessHandle(proc, proc->ioKeyHandle, Ipc::HandleType::Mailbox, object)) return nullptr;
return (Ipc::Mailbox*)object;
}
static int DuplicateHandleBetweenSlots(int srcSlot, int handle, int dstSlot) {
if (srcSlot < 0 || dstSlot < 0 || handle < 0) return -1;
Ipc::HandleType type = Ipc::HandleType::None;
Ipc::Object* object = nullptr;
uint32_t rights = 0;
if (!Ipc::SnapshotHandleForSlot(srcSlot, handle, type, object, rights)) return -1;
if ((rights & Ipc::RightDup) == 0) return -1;
return Ipc::InstallHandleForSlot(dstSlot, object, type, rights);
}
static bool ConfigureRedirWaitsetForSlot(int slot, Sched::Process* proc) {
if (slot < 0 || proc == nullptr) return false;
int waitset = Ipc::CreateWaitsetHandleForSlot(slot);
if (waitset < 0) return false;
if (proc->ioInHandle >= 0) {
if (Ipc::WaitsetAddHandleForSlot(slot, waitset, proc->ioInHandle,
Ipc::SignalReadable | Ipc::SignalPeerClosed) < 0) {
Ipc::CloseHandleForSlot(slot, waitset);
return false;
}
}
if (proc->ioKeyHandle >= 0) {
if (Ipc::WaitsetAddHandleForSlot(slot, waitset, proc->ioKeyHandle,
Ipc::SignalReadable | Ipc::SignalPeerClosed) < 0) {
Ipc::CloseHandleForSlot(slot, waitset);
return false;
}
}
proc->ioWaitsetHandle = waitset;
return true;
}
static int WriteAllToStream(Ipc::Stream* stream, const uint8_t* data, int len) {
if (stream == nullptr || data == nullptr || len <= 0) return -1;
int total = 0;
while (total < len) {
int written = Ipc::StreamWrite(stream, data + total, len - total, false);
if (written < 0) {
return (total > 0) ? total : -1;
}
if (written == 0) {
Sched::BlockOnObject(stream, 0);
continue;
}
total += written;
}
return total;
} }
} }
+7 -6
View File
@@ -12,25 +12,26 @@
#include <Memory/HHDM.hpp> #include <Memory/HHDM.hpp>
#include <Memory/Paging.hpp> #include <Memory/Paging.hpp>
#include <Libraries/Memory.hpp> #include <Libraries/Memory.hpp>
#include <Ipc/Ipc.hpp>
#include "Path.hpp" #include "Path.hpp"
namespace Montauk { namespace Montauk {
static int Sys_Open(const char* path) { static int Sys_Open(const char* path) {
char resolved[256]; char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
return Fs::Vfs::VfsOpen(resolved); return Ipc::OpenFileHandle(resolved);
} }
static int Sys_Read(int handle, uint8_t* buffer, uint64_t offset, uint64_t size) { static int Sys_Read(int handle, uint8_t* buffer, uint64_t offset, uint64_t size) {
return Fs::Vfs::VfsRead(handle, buffer, offset, size); return Ipc::FileReadHandle(handle, buffer, offset, size);
} }
static uint64_t Sys_GetSize(int handle) { static uint64_t Sys_GetSize(int handle) {
return Fs::Vfs::VfsGetSize(handle); return Ipc::FileGetSizeHandle(handle);
} }
static void Sys_Close(int handle) { static void Sys_Close(int handle) {
Fs::Vfs::VfsClose(handle); Ipc::CloseHandle(handle);
} }
static int Sys_ReadDir(const char* path, const char** outNames, int maxEntries) { static int Sys_ReadDir(const char* path, const char** outNames, int maxEntries) {
@@ -89,13 +90,13 @@ namespace Montauk {
} }
static int Sys_FWrite(int handle, const uint8_t* data, uint64_t offset, uint64_t size) { static int Sys_FWrite(int handle, const uint8_t* data, uint64_t offset, uint64_t size) {
return Fs::Vfs::VfsWrite(handle, data, offset, size); return Ipc::FileWriteHandle(handle, data, offset, size);
} }
static int Sys_FCreate(const char* path) { static int Sys_FCreate(const char* path) {
char resolved[256]; char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
return Fs::Vfs::VfsCreate(resolved); return Ipc::CreateFileHandle(resolved);
} }
static int Sys_FDelete(const char* path) { static int Sys_FDelete(const char* path) {
+3
View File
@@ -66,6 +66,9 @@ namespace Montauk {
// If the process is redirected to a GUI terminal, return those dimensions // If the process is redirected to a GUI terminal, return those dimensions
auto* proc = Sched::GetCurrentProcessPtr(); auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) { if (proc && proc->redirected) {
if (proc->termCols > 0 && proc->termRows > 0) {
return ((uint64_t)proc->termRows << 32) | ((uint64_t)proc->termCols & 0xFFFFFFFF);
}
auto* target = GetRedirTarget(proc); auto* target = GetRedirTarget(proc);
if (target && target->termCols > 0 && target->termRows > 0) { if (target && target->termCols > 0 && target->termRows > 0) {
return ((uint64_t)target->termRows << 32) | ((uint64_t)target->termCols & 0xFFFFFFFF); return ((uint64_t)target->termRows << 32) | ((uint64_t)target->termCols & 0xFFFFFFFF);
+62 -25
View File
@@ -7,8 +7,6 @@
#pragma once #pragma once
#include <Sched/Scheduler.hpp> #include <Sched/Scheduler.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include "Syscall.hpp" #include "Syscall.hpp"
#include "Common.hpp" #include "Common.hpp"
#include "Path.hpp" #include "Path.hpp"
@@ -19,54 +17,93 @@ namespace Montauk {
char resolved[256]; char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
int parentSlot = Ipc::CurrentSlot();
int childPid = Sched::Spawn(resolved, args); int childPid = Sched::Spawn(resolved, args);
if (childPid < 0) return -1; if (childPid < 0) return -1;
auto* child = Sched::GetProcessByPid(childPid); auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr) return -1; int childSlot = Ipc::SlotForPid(childPid);
if (child == nullptr || childSlot < 0 || parentSlot < 0) return -1;
// Allocate ring buffers Ipc::Stream* outStream = Ipc::CreateStream();
void* outPage = Memory::g_pfa->AllocateZeroed(); Ipc::Stream* inStream = Ipc::CreateStream();
void* inPage = Memory::g_pfa->AllocateZeroed(); Ipc::Mailbox* keyMailbox = Ipc::CreateMailbox();
if (!outPage || !inPage) return -1; if (outStream == nullptr || inStream == nullptr || keyMailbox == nullptr) {
if (outStream != nullptr) Ipc::ReleaseStream(outStream, false, false);
if (inStream != nullptr) Ipc::ReleaseStream(inStream, false, false);
if (keyMailbox != nullptr) Ipc::ReleaseMailbox(keyMailbox, false, false);
Sched::KillProcess(childPid);
return -1;
}
int parentOutHandle = Ipc::InstallHandleForSlot(
parentSlot, (Ipc::Object*)outStream, Ipc::HandleType::Stream, Ipc::RightRead);
int parentInHandle = Ipc::InstallHandleForSlot(
parentSlot, (Ipc::Object*)inStream, Ipc::HandleType::Stream, Ipc::RightWrite);
int parentKeyHandle = Ipc::InstallHandleForSlot(
parentSlot, (Ipc::Object*)keyMailbox, Ipc::HandleType::Mailbox, Ipc::RightSend);
child->ioOutHandle = Ipc::InstallHandleForSlot(
childSlot, (Ipc::Object*)outStream, Ipc::HandleType::Stream,
Ipc::RightWrite | Ipc::RightWait | Ipc::RightDup);
child->ioInHandle = Ipc::InstallHandleForSlot(
childSlot, (Ipc::Object*)inStream, Ipc::HandleType::Stream,
Ipc::RightRead | Ipc::RightWait | Ipc::RightDup);
child->ioKeyHandle = Ipc::InstallHandleForSlot(
childSlot, (Ipc::Object*)keyMailbox, Ipc::HandleType::Mailbox,
Ipc::RightRecv | Ipc::RightWait | Ipc::RightDup);
if (parentOutHandle < 0 || parentInHandle < 0 || parentKeyHandle < 0 ||
child->ioOutHandle < 0 || child->ioInHandle < 0 || child->ioKeyHandle < 0 ||
!ConfigureRedirWaitsetForSlot(childSlot, child)) {
if (parentOutHandle >= 0) Ipc::CloseHandleForSlot(parentSlot, parentOutHandle);
if (parentInHandle >= 0) Ipc::CloseHandleForSlot(parentSlot, parentInHandle);
if (parentKeyHandle >= 0) Ipc::CloseHandleForSlot(parentSlot, parentKeyHandle);
if (child->ioOutHandle < 0) Ipc::ReleaseStream(outStream, false, false);
if (child->ioInHandle < 0) Ipc::ReleaseStream(inStream, false, false);
if (child->ioKeyHandle < 0) Ipc::ReleaseMailbox(keyMailbox, false, false);
Sched::KillProcess(childPid);
return -1;
}
child->outBuf = (uint8_t*)outPage;
child->inBuf = (uint8_t*)inPage;
child->outHead = 0;
child->outTail = 0;
child->inHead = 0;
child->inTail = 0;
child->keyHead = 0;
child->keyTail = 0;
child->redirected = true; child->redirected = true;
child->parentPid = Sched::GetCurrentPid(); child->parentPid = Sched::GetCurrentPid();
child->termCols = 0;
child->termRows = 0;
return childPid; return childPid;
} }
static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) { static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) {
auto* child = Sched::GetProcessByPid(childPid); auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected || !child->outBuf) return -1; Ipc::Stream* stream = GetRedirOutStream(child);
return RingRead(child->outBuf, child->outHead, child->outTail, Sched::Process::IoBufSize, (uint8_t*)buf, maxLen); if (child == nullptr || !child->redirected || stream == nullptr) return -1;
return Ipc::StreamRead(stream, (uint8_t*)buf, maxLen, true);
} }
static int Sys_ChildIoWrite(int childPid, const char* data, int len) { static int Sys_ChildIoWrite(int childPid, const char* data, int len) {
auto* child = Sched::GetProcessByPid(childPid); auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected || !child->inBuf) return -1; Ipc::Stream* stream = GetRedirInStream(child);
for (int i = 0; i < len; i++) { if (child == nullptr || !child->redirected || stream == nullptr) return -1;
RingWrite(child->inBuf, child->inHead, child->inTail, Sched::Process::IoBufSize, (uint8_t)data[i]); return WriteAllToStream(stream, (const uint8_t*)data, len);
}
return len;
} }
static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) { static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) {
if (key == nullptr) return -1; if (key == nullptr) return -1;
auto* child = Sched::GetProcessByPid(childPid); auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected) return -1; Ipc::Mailbox* mailbox = GetRedirKeyMailbox(child);
child->keyBuf[child->keyHead] = *key; if (child == nullptr || !child->redirected || mailbox == nullptr) return -1;
child->keyHead = (child->keyHead + 1) % 64;
for (;;) {
int rc = Ipc::MailboxSend(mailbox, 0, key, sizeof(KeyEvent));
if (rc < 0) return -1;
if (rc == 0) {
Sched::BlockOnObject(mailbox, 0);
continue;
}
return 0; return 0;
} }
}
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) { static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
auto* child = Sched::GetProcessByPid(childPid); auto* child = Sched::GetProcessByPid(childPid);
+101
View File
@@ -0,0 +1,101 @@
/*
* IpcSyscall.hpp
* Generic IPC handle, stream, mailbox, waitset, process, and surface syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Ipc/Ipc.hpp>
#include <Sched/Scheduler.hpp>
#include "Syscall.hpp"
namespace Montauk {
static int Sys_DupHandle(int handle) {
return Ipc::DupHandle(handle);
}
static uint32_t Sys_WaitHandle(int handle, uint32_t wantedSignals, uint64_t timeoutMs) {
return Ipc::WaitOnHandle(handle, wantedSignals, timeoutMs);
}
static int Sys_StreamCreate(int* outReadHandle, int* outWriteHandle, uint32_t capacity) {
if (outReadHandle == nullptr || outWriteHandle == nullptr) return -1;
int readHandle = -1;
int writeHandle = -1;
if (Ipc::CreateStreamHandlePair(capacity, readHandle, writeHandle) < 0) return -1;
*outReadHandle = readHandle;
*outWriteHandle = writeHandle;
return 0;
}
static int Sys_StreamRead(int handle, uint8_t* buffer, int maxLen) {
return Ipc::StreamReadHandle(handle, buffer, maxLen);
}
static int Sys_StreamWrite(int handle, const uint8_t* data, int len) {
return Ipc::StreamWriteHandle(handle, data, len);
}
static int Sys_MailboxCreate(int* outSendHandle, int* outRecvHandle) {
if (outSendHandle == nullptr || outRecvHandle == nullptr) return -1;
int sendHandle = -1;
int recvHandle = -1;
if (Ipc::CreateMailboxHandlePair(sendHandle, recvHandle) < 0) return -1;
*outSendHandle = sendHandle;
*outRecvHandle = recvHandle;
return 0;
}
static int Sys_MailboxSend(int handle, uint32_t msgType, const void* data, uint16_t len, int attachHandle) {
return Ipc::MailboxSendHandle(handle, msgType, data, len, attachHandle);
}
static int Sys_MailboxRecv(int handle, uint32_t* outMsgType, void* data,
uint16_t* inOutLen, int* outAttachHandle) {
return Ipc::MailboxRecvHandle(handle, outMsgType, data, inOutLen, outAttachHandle);
}
static int Sys_WaitsetCreate() {
return Ipc::CreateWaitsetHandleForCurrent();
}
static int Sys_WaitsetAdd(int waitsetHandle, int targetHandle, uint32_t signals) {
return Ipc::WaitsetAddHandle(waitsetHandle, targetHandle, signals);
}
static int Sys_WaitsetRemove(int waitsetHandle, int index) {
return Ipc::WaitsetRemoveIndex(waitsetHandle, index);
}
static int Sys_WaitsetWait(int waitsetHandle, IpcWaitResult* outReady, uint64_t timeoutMs) {
Ipc::WaitsetReady ready = {-1, 0};
int result = Ipc::WaitsetWaitHandle(waitsetHandle, outReady ? &ready : nullptr, timeoutMs);
if (result > 0 && outReady != nullptr) {
outReady->index = ready.index;
outReady->signals = ready.signals;
}
return result;
}
static int Sys_ProcOpen(int pid) {
return Ipc::OpenProcessHandle(pid);
}
static int Sys_SurfaceCreate(uint64_t byteSize) {
return Ipc::CreateSurfaceHandle(byteSize);
}
static uint64_t Sys_SurfaceMap(int handle) {
return Ipc::MapSurfaceHandle(handle);
}
static int Sys_SurfaceResize(int handle, uint64_t newSize) {
return Ipc::ResizeSurfaceHandle(handle, newSize);
}
}
+43 -23
View File
@@ -7,6 +7,7 @@
#pragma once #pragma once
#include <Sched/Scheduler.hpp> #include <Sched/Scheduler.hpp>
#include <Drivers/PS2/Keyboard.hpp> #include <Drivers/PS2/Keyboard.hpp>
#include <Libraries/Memory.hpp>
#include "Common.hpp" #include "Common.hpp"
@@ -14,8 +15,8 @@ namespace Montauk {
static bool Sys_IsKeyAvailable() { static bool Sys_IsKeyAvailable() {
auto* proc = Sched::GetCurrentProcessPtr(); auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) { if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc); Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
if (target) return target->keyHead != target->keyTail; if (mailbox != nullptr) return Ipc::MailboxHasMessage(mailbox);
} }
return Drivers::PS2::Keyboard::IsKeyAvailable(); return Drivers::PS2::Keyboard::IsKeyAvailable();
} }
@@ -24,16 +25,19 @@ namespace Montauk {
if (outEvent == nullptr) return; if (outEvent == nullptr) return;
auto* proc = Sched::GetCurrentProcessPtr(); auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) { if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc); Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
if (target) { if (mailbox != nullptr) {
// Wait for key in target's keyBuf ring for (;;) {
while (target->keyHead == target->keyTail) { uint16_t len = sizeof(KeyEvent);
Sched::Schedule(); int rc = Ipc::MailboxRecv(mailbox, nullptr, outEvent, &len, true);
} if (rc > 0) return;
*outEvent = target->keyBuf[target->keyTail]; if (rc < 0) {
target->keyTail = (target->keyTail + 1) % 64; memset(outEvent, 0, sizeof(KeyEvent));
return; return;
} }
Sched::BlockOnObject(mailbox, 0);
}
}
} }
auto k = Drivers::PS2::Keyboard::GetKey(); auto k = Drivers::PS2::Keyboard::GetKey();
outEvent->scancode = k.Scancode; outEvent->scancode = k.Scancode;
@@ -47,24 +51,40 @@ namespace Montauk {
static char Sys_GetChar() { static char Sys_GetChar() {
auto* proc = Sched::GetCurrentProcessPtr(); auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) { if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc); Ipc::Stream* input = GetRedirInStream(proc);
if (target) { Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
while (true) { if (input != nullptr || mailbox != nullptr) {
if (target->inBuf && target->inTail != target->inHead) { for (;;) {
uint8_t c = target->inBuf[target->inTail]; if (input != nullptr) {
target->inTail = (target->inTail + 1) % Sched::Process::IoBufSize; uint8_t c = 0;
return (char)c; int rc = Ipc::StreamRead(input, &c, 1, true);
if (rc > 0) return (char)c;
if (rc < 0 && mailbox == nullptr) return 0;
} }
if (target->keyTail != target->keyHead) { if (mailbox != nullptr) {
auto ev = target->keyBuf[target->keyTail]; KeyEvent ev{};
target->keyTail = (target->keyTail + 1) % 64; uint16_t len = sizeof(ev);
if (ev.pressed && ev.ascii != 0) { int rc = Ipc::MailboxRecv(mailbox, nullptr, &ev, &len, true);
return ev.ascii; if (rc > 0) {
if (ev.pressed && ev.ascii != 0) return ev.ascii;
continue;
} }
if (rc < 0 && input == nullptr) return 0;
} }
Sched::Schedule(); if (proc->ioWaitsetHandle >= 0) {
Ipc::WaitsetReady ready{};
if (Ipc::WaitsetWaitHandle(proc->ioWaitsetHandle, &ready, ~0ULL) < 0) {
return 0;
}
} else if (input != nullptr) {
Sched::BlockOnObject(input, 0);
} else if (mailbox != nullptr) {
Sched::BlockOnObject(mailbox, 0);
} else {
return 0;
}
} }
} }
} }
+24 -13
View File
@@ -14,6 +14,7 @@
#include <Fs/Vfs.hpp> #include <Fs/Vfs.hpp>
#include "Syscall.hpp" #include "Syscall.hpp"
#include "Common.hpp"
#include "WinServer.hpp" #include "WinServer.hpp"
#include "Path.hpp" #include "Path.hpp"
@@ -44,22 +45,32 @@ namespace Montauk {
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
auto* parent = Sched::GetCurrentProcessPtr(); auto* parent = Sched::GetCurrentProcessPtr();
int parentSlot = Ipc::CurrentSlot();
int childPid = Sched::Spawn(resolved, args); int childPid = Sched::Spawn(resolved, args);
if (childPid < 0) return childPid; if (childPid < 0) return childPid;
// Inherit I/O redirection: if the parent is redirected, the child
// is marked redirected too. It stores a parentPid pointing to the
// process that owns the actual ring buffers (the one spawned via
// spawn_redir). The child does NOT get its own buffers — Sys_Print
// et al. look up the buffer owner at write time.
if (parent && parent->redirected) { if (parent && parent->redirected) {
auto* child = Sched::GetProcessByPid(childPid); auto* child = Sched::GetProcessByPid(childPid);
if (child) { int childSlot = Ipc::SlotForPid(childPid);
child->redirected = true; if (child == nullptr || childSlot < 0 || parentSlot < 0) {
// Point to the buffer owner: if parent owns buffers, target parent; Sched::KillProcess(childPid);
// if parent itself inherited, follow the chain. return -1;
child->parentPid = parent->outBuf ? parent->pid : parent->parentPid;
} }
child->ioOutHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioOutHandle, childSlot);
child->ioInHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioInHandle, childSlot);
child->ioKeyHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioKeyHandle, childSlot);
if (child->ioOutHandle < 0 || child->ioInHandle < 0 || child->ioKeyHandle < 0 ||
!ConfigureRedirWaitsetForSlot(childSlot, child)) {
Sched::KillProcess(childPid);
return -1;
}
child->redirected = true;
child->parentPid = parent->pid;
child->termCols = parent->termCols;
child->termRows = parent->termRows;
} }
return childPid; return childPid;
@@ -157,9 +168,9 @@ namespace Montauk {
const char* entries[1]; const char* entries[1];
if (Fs::Vfs::VfsReadDir(resolved, entries, 1) < 0) return -1; if (Fs::Vfs::VfsReadDir(resolved, entries, 1) < 0) return -1;
} else { } else {
int handle = Fs::Vfs::VfsOpen(resolved); Fs::Vfs::BackendFile file = {-1, -1};
if (handle < 0) return -1; if (Fs::Vfs::OpenBackendFile(resolved, file) < 0) return -1;
Fs::Vfs::VfsClose(handle); Fs::Vfs::CloseBackendFile(file);
} }
int i = 0; int i = 0;
+53
View File
@@ -32,6 +32,7 @@
#include "Window.hpp" // SYS_WINCREATE, SYS_WINDESTROY, SYS_WINPRESENT, SYS_WINPOLL, SYS_WINENUM, SYS_WINMAP, SYS_WINSENDEVENT, SYS_WINRESIZE, SYS_WINSETSCALE, SYS_WINGETSCALE #include "Window.hpp" // SYS_WINCREATE, SYS_WINDESTROY, SYS_WINPRESENT, SYS_WINPOLL, SYS_WINENUM, SYS_WINMAP, SYS_WINSENDEVENT, SYS_WINRESIZE, SYS_WINSETSCALE, SYS_WINGETSCALE
#include "Audio.hpp" // SYS_AUDIOOPEN, SYS_AUDIOCLOSE, SYS_AUDIOWRITE, SYS_AUDIOCTL #include "Audio.hpp" // SYS_AUDIOOPEN, SYS_AUDIOCLOSE, SYS_AUDIOWRITE, SYS_AUDIOCTL
#include "BluetoothSyscall.hpp" // SYS_BTSCAN, SYS_BTCONNECT, SYS_BTDISCONNECT, SYS_BTLIST, SYS_BTINFO #include "BluetoothSyscall.hpp" // SYS_BTSCAN, SYS_BTCONNECT, SYS_BTDISCONNECT, SYS_BTLIST, SYS_BTINFO
#include "IpcSyscall.hpp" // SYS_DUPHANDLE, SYS_WAIT_HANDLE, SYS_STREAM_CREATE, SYS_STREAM_READ, SYS_STREAM_WRITE, SYS_MAILBOX_CREATE, SYS_MAILBOX_SEND, SYS_MAILBOX_RECV, SYS_WAITSET_CREATE, SYS_WAITSET_ADD, SYS_WAITSET_REMOVE, SYS_WAITSET_WAIT, SYS_PROC_OPEN, SYS_SURFACE_CREATE, SYS_SURFACE_MAP, SYS_SURFACE_RESIZE
// Assembly entry point // Assembly entry point
extern "C" void SyscallEntry(); extern "C" void SyscallEntry();
@@ -256,6 +257,8 @@ namespace Montauk {
return (int64_t)Sys_WinEnum((WinInfo*)frame->arg1, (int)frame->arg2); return (int64_t)Sys_WinEnum((WinInfo*)frame->arg1, (int)frame->arg2);
case SYS_WINMAP: case SYS_WINMAP:
return (int64_t)Sys_WinMap((int)frame->arg1); return (int64_t)Sys_WinMap((int)frame->arg1);
case SYS_WINUNMAP:
return (int64_t)Sys_WinUnmap((int)frame->arg1);
case SYS_WINSENDEVENT: case SYS_WINSENDEVENT:
if (!ValidUserPtr(frame->arg2)) return -1; if (!ValidUserPtr(frame->arg2)) return -1;
return (int64_t)Sys_WinSendEvent((int)frame->arg1, (const WinEvent*)frame->arg2); return (int64_t)Sys_WinSendEvent((int)frame->arg1, (const WinEvent*)frame->arg2);
@@ -353,6 +356,56 @@ namespace Montauk {
case SYS_CHDIR: case SYS_CHDIR:
if (!ValidUserPtr(frame->arg1)) return -1; if (!ValidUserPtr(frame->arg1)) return -1;
return Sys_Chdir((const char*)frame->arg1); 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 (!ValidUserPtr(frame->arg1) || !ValidUserPtr(frame->arg2)) return -1;
return Sys_StreamCreate((int*)frame->arg1, (int*)frame->arg2, (uint32_t)frame->arg3);
case SYS_STREAM_READ:
if (!ValidUserPtr(frame->arg2)) return -1;
return Sys_StreamRead((int)frame->arg1, (uint8_t*)frame->arg2, (int)frame->arg3);
case SYS_STREAM_WRITE:
if (!ValidUserPtr(frame->arg2)) return -1;
return Sys_StreamWrite((int)frame->arg1, (const uint8_t*)frame->arg2, (int)frame->arg3);
case SYS_MAILBOX_CREATE:
if (!ValidUserPtr(frame->arg1) || !ValidUserPtr(frame->arg2)) return -1;
return Sys_MailboxCreate((int*)frame->arg1, (int*)frame->arg2);
case SYS_MAILBOX_SEND:
if (frame->arg3 != 0 && !ValidUserPtr(frame->arg3)) return -1;
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 (frame->arg2 != 0 && !IsUserPtr(frame->arg2)) return -1;
if (frame->arg3 != 0 && !IsUserPtr(frame->arg3)) return -1;
if (!ValidUserPtr(frame->arg4)) return -1;
if (frame->arg5 != 0 && !IsUserPtr(frame->arg5)) return -1;
return Sys_MailboxRecv((int)frame->arg1,
IsUserPtr(frame->arg2) ? (uint32_t*)frame->arg2 : nullptr,
IsUserPtr(frame->arg3) ? (void*)frame->arg3 : nullptr,
(uint16_t*)frame->arg4,
IsUserPtr(frame->arg5) ? (int*)frame->arg5 : nullptr);
case SYS_WAITSET_CREATE:
return Sys_WaitsetCreate();
case SYS_WAITSET_ADD:
return Sys_WaitsetAdd((int)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3);
case SYS_WAITSET_REMOVE:
return Sys_WaitsetRemove((int)frame->arg1, (int)frame->arg2);
case SYS_WAITSET_WAIT:
if (frame->arg2 != 0 && !ValidUserPtr(frame->arg2)) return -1;
return Sys_WaitsetWait((int)frame->arg1,
IsUserPtr(frame->arg2) ? (IpcWaitResult*)frame->arg2 : nullptr,
frame->arg3);
case SYS_PROC_OPEN:
return Sys_ProcOpen((int)frame->arg1);
case SYS_SURFACE_CREATE:
return Sys_SurfaceCreate(frame->arg1);
case SYS_SURFACE_MAP:
return (int64_t)Sys_SurfaceMap((int)frame->arg1);
case SYS_SURFACE_RESIZE:
return Sys_SurfaceResize((int)frame->arg1, frame->arg2);
default: default:
return -1; return -1;
} }
+30
View File
@@ -123,6 +123,7 @@ namespace Montauk {
static constexpr uint64_t SYS_WINPOLL = 57; static constexpr uint64_t SYS_WINPOLL = 57;
static constexpr uint64_t SYS_WINENUM = 58; static constexpr uint64_t SYS_WINENUM = 58;
static constexpr uint64_t SYS_WINMAP = 59; static constexpr uint64_t SYS_WINMAP = 59;
static constexpr uint64_t SYS_WINUNMAP = 97;
static constexpr uint64_t SYS_WINSENDEVENT = 60; static constexpr uint64_t SYS_WINSENDEVENT = 60;
static constexpr uint64_t SYS_WINRESIZE = 64; static constexpr uint64_t SYS_WINRESIZE = 64;
static constexpr uint64_t SYS_WINSETSCALE = 65; static constexpr uint64_t SYS_WINSETSCALE = 65;
@@ -187,6 +188,30 @@ namespace Montauk {
static constexpr uint64_t SYS_GETCWD = 95; static constexpr uint64_t SYS_GETCWD = 95;
static constexpr uint64_t SYS_CHDIR = 96; static constexpr uint64_t SYS_CHDIR = 96;
/* IpcSyscall.hpp */
static constexpr uint64_t SYS_DUPHANDLE = 98;
static constexpr uint64_t SYS_WAIT_HANDLE = 99;
static constexpr uint64_t SYS_STREAM_CREATE = 100;
static constexpr uint64_t SYS_STREAM_READ = 101;
static constexpr uint64_t SYS_STREAM_WRITE = 102;
static constexpr uint64_t SYS_MAILBOX_CREATE = 103;
static constexpr uint64_t SYS_MAILBOX_SEND = 104;
static constexpr uint64_t SYS_MAILBOX_RECV = 105;
static constexpr uint64_t SYS_WAITSET_CREATE = 106;
static constexpr uint64_t SYS_WAITSET_ADD = 107;
static constexpr uint64_t SYS_WAITSET_REMOVE = 108;
static constexpr uint64_t SYS_WAITSET_WAIT = 109;
static constexpr uint64_t SYS_PROC_OPEN = 110;
static constexpr uint64_t SYS_SURFACE_CREATE = 111;
static constexpr uint64_t SYS_SURFACE_MAP = 112;
static constexpr uint64_t SYS_SURFACE_RESIZE = 113;
static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0;
static constexpr uint32_t IPC_SIGNAL_WRITABLE = 1u << 1;
static constexpr uint32_t IPC_SIGNAL_PEER_CLOSED = 1u << 2;
static constexpr uint32_t IPC_SIGNAL_EXITED = 1u << 3;
static constexpr uint32_t IPC_SIGNAL_READY = 1u << 4;
static constexpr int SOCK_TCP = 1; static constexpr int SOCK_TCP = 1;
static constexpr int SOCK_UDP = 2; static constexpr int SOCK_UDP = 2;
@@ -239,6 +264,11 @@ namespace Montauk {
uint8_t buttons; uint8_t buttons;
}; };
struct IpcWaitResult {
int32_t index;
uint32_t signals;
};
// Window server shared types // Window server shared types
struct WinEvent { struct WinEvent {
uint8_t type; // 0=key, 1=mouse, 2=resize, 3=close, 4=scale uint8_t type; // 0=key, 1=mouse, 2=resize, 3=close, 4=scale
+10 -7
View File
@@ -15,10 +15,12 @@ namespace Montauk {
static void Sys_Print(const char* text) { static void Sys_Print(const char* text) {
auto* proc = Sched::GetCurrentProcessPtr(); auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) { if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc); Ipc::Stream* stream = GetRedirOutStream(proc);
if (target && target->outBuf) { if (stream != nullptr) {
for (int i = 0; text[i]; i++) { int len = 0;
RingWrite(target->outBuf, target->outHead, target->outTail, Sched::Process::IoBufSize, (uint8_t)text[i]); while (text[len]) len++;
if (len > 0) {
WriteAllToStream(stream, (const uint8_t*)text, len);
} }
return; return;
} }
@@ -31,9 +33,10 @@ namespace Montauk {
static void Sys_Putchar(char c) { static void Sys_Putchar(char c) {
auto* proc = Sched::GetCurrentProcessPtr(); auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) { if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc); Ipc::Stream* stream = GetRedirOutStream(proc);
if (target && target->outBuf) { if (stream != nullptr) {
RingWrite(target->outBuf, target->outHead, target->outTail, Sched::Process::IoBufSize, (uint8_t)c); uint8_t byte = (uint8_t)c;
WriteAllToStream(stream, &byte, 1);
return; return;
} }
} }
+158 -288
View File
@@ -5,118 +5,104 @@
*/ */
#include "WinServer.hpp" #include "WinServer.hpp"
#include <Memory/PageFrameAllocator.hpp>
#include <Memory/Paging.hpp>
#include <Memory/HHDM.hpp>
#include <Libraries/Memory.hpp> #include <Libraries/Memory.hpp>
#include <Terminal/Terminal.hpp> #include <Terminal/Terminal.hpp>
#include <CppLib/Spinlock.hpp> #include <CppLib/Spinlock.hpp>
#include <Sched/Scheduler.hpp> #include <Sched/Scheduler.hpp>
#include <Timekeeping/ApicTimer.hpp>
namespace WinServer { namespace WinServer {
static WindowSlot g_slots[MaxWindows]; static WindowSlot g_slots[MaxWindows];
static int g_uiScale = 1; static int g_uiScale = 1;
static kcp::Mutex wsLock; static kcp::Mutex wsLock;
static constexpr uint64_t RetireGraceMs = 2000; static constexpr int MaxRetiredSnapshots = MaxWindows * 4;
static constexpr int MaxRetiredBatches = MaxWindows * 4;
struct RetiredBatch { struct RetiredSnapshot {
bool used; bool used;
int numPages; int windowId;
uint64_t freeAfterMs; Ipc::Surface* surface;
uint64_t physPages[MaxPixelPages];
}; };
static RetiredBatch g_retired[MaxRetiredBatches]; static RetiredSnapshot g_retiredSnapshots[MaxRetiredSnapshots];
// RAII lock guard for WinServer operations
struct WsGuard { struct WsGuard {
WsGuard() { wsLock.Acquire(); } WsGuard() { wsLock.Acquire(); }
~WsGuard() { wsLock.Release(); } ~WsGuard() { wsLock.Release(); }
}; };
static void FreePageBatchLocked(uint64_t* physPages, int numPages) { static void ResetSlotLocked(WindowSlot& slot) {
for (int i = 0; i < numPages; i++) { memset(&slot, 0, sizeof(WindowSlot));
if (physPages[i] != 0) {
Memory::g_pfa->Free((void*)Memory::HHDM(physPages[i]));
physPages[i] = 0;
}
}
} }
static void ProcessRetiredLocked() { static void RetireSnapshotLocked(int windowId, Ipc::Surface* surface) {
uint64_t now = Timekeeping::GetMilliseconds(); if (surface == nullptr) return;
for (int i = 0; i < MaxRetiredBatches; i++) {
if (!g_retired[i].used) continue;
if (now < g_retired[i].freeAfterMs) continue;
FreePageBatchLocked(g_retired[i].physPages, g_retired[i].numPages);
g_retired[i].used = false;
g_retired[i].numPages = 0;
g_retired[i].freeAfterMs = 0;
}
}
static void RetirePageBatchLocked(uint64_t* physPages, int numPages) { for (int i = 0; i < MaxRetiredSnapshots; i++) {
if (numPages <= 0) return; if (g_retiredSnapshots[i].used) continue;
g_retiredSnapshots[i].used = true;
int retireIdx = -1; g_retiredSnapshots[i].windowId = windowId;
for (int i = 0; i < MaxRetiredBatches; i++) { g_retiredSnapshots[i].surface = surface;
if (!g_retired[i].used) {
retireIdx = i;
break;
}
}
if (retireIdx < 0) {
ProcessRetiredLocked();
for (int i = 0; i < MaxRetiredBatches; i++) {
if (!g_retired[i].used) {
retireIdx = i;
break;
}
}
}
if (retireIdx < 0) {
uint64_t oldest = ~0ULL;
for (int i = 0; i < MaxRetiredBatches; i++) {
if (g_retired[i].freeAfterMs < oldest) {
oldest = g_retired[i].freeAfterMs;
retireIdx = i;
}
}
if (retireIdx >= 0) {
Kt::KernelLogStream(Kt::ERROR, "WinServer")
<< "Retire queue full, forcing early free of stale window pages";
FreePageBatchLocked(g_retired[retireIdx].physPages, g_retired[retireIdx].numPages);
g_retired[retireIdx].used = false;
g_retired[retireIdx].numPages = 0;
g_retired[retireIdx].freeAfterMs = 0;
}
}
if (retireIdx < 0) {
FreePageBatchLocked(physPages, numPages);
return; return;
} }
RetiredBatch& batch = g_retired[retireIdx]; Ipc::ReleaseSurface(surface);
batch.used = true;
batch.numPages = numPages;
batch.freeAfterMs = Timekeeping::GetMilliseconds() + RetireGraceMs;
for (int i = 0; i < numPages; i++) {
batch.physPages[i] = physPages[i];
physPages[i] = 0;
} }
static int UnmapRetiredSnapshotsLocked(int windowId, int callerPid, uint64_t callerPml4) {
int result = -1;
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;
}
Ipc::ReleaseSurface(g_retiredSnapshots[i].surface);
}
g_retiredSnapshots[i].used = false;
g_retiredSnapshots[i].windowId = -1;
g_retiredSnapshots[i].surface = nullptr;
}
return result;
}
static void ReleaseSlotResourcesLocked(WindowSlot& slot, bool unmapOwner) {
if (unmapOwner && slot.liveSurface != nullptr) {
auto* owner = Sched::GetProcessByPid(slot.ownerPid);
if (owner != nullptr) {
Ipc::UnmapSurfaceForPid(slot.liveSurface, slot.ownerPid, owner->pml4Phys);
}
}
if (slot.eventMailbox != nullptr) {
Ipc::ReleaseMailbox(slot.eventMailbox, true, true);
}
if (slot.liveSurface != nullptr) {
Ipc::ReleaseSurface(slot.liveSurface);
}
if (slot.snapshotSurface != nullptr) {
RetireSnapshotLocked((int)(&slot - g_slots), slot.snapshotSurface);
}
ResetSlotLocked(slot);
}
static int SendEventLocked(int windowId, const Montauk::WinEvent* event) {
if (windowId < 0 || windowId >= MaxWindows || event == nullptr) return -1;
WindowSlot& slot = g_slots[windowId];
if (!slot.used || slot.eventMailbox == nullptr) return -1;
int rc = Ipc::MailboxSend(slot.eventMailbox, 0, event, sizeof(*event));
return rc > 0 ? 0 : -1;
} }
int Create(int ownerPid, uint64_t ownerPml4, const char* title, int w, int h, int Create(int ownerPid, uint64_t ownerPml4, const char* title, int w, int h,
uint64_t& heapNext, uint64_t& outVa) { uint64_t& heapNext, uint64_t& outVa) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
// Find a free slot if (w <= 0 || h <= 0 || w > 16384 || h > 16384) return -1;
int slotIdx = -1; int slotIdx = -1;
for (int i = 0; i < MaxWindows; i++) { for (int i = 0; i < MaxWindows; i++) {
if (!g_slots[i].used) { if (!g_slots[i].used) {
@@ -126,26 +112,42 @@ namespace WinServer {
} }
if (slotIdx < 0) return -1; if (slotIdx < 0) return -1;
// Validate dimensions (cap at 16384 to prevent integer overflow in w*h*4) uint64_t bufSize = (uint64_t)w * (uint64_t)h * 4ULL;
if (w <= 0 || h <= 0 || w > 16384 || h > 16384) return -1; Ipc::Surface* live = Ipc::CreateSurface(bufSize);
uint64_t bufSize = (uint64_t)w * h * 4; Ipc::Surface* snapshot = Ipc::CreateSurface(bufSize);
int numPages = (int)((bufSize + 0xFFF) / 0x1000); Ipc::Mailbox* mailbox = Ipc::CreateMailbox();
if (numPages > MaxPixelPages) return -1; if (live == nullptr || snapshot == nullptr || mailbox == nullptr) {
if (live != nullptr) Ipc::ReleaseSurface(live);
if (snapshot != nullptr) Ipc::ReleaseSurface(snapshot);
if (mailbox != nullptr) Ipc::ReleaseMailbox(mailbox, false, false);
return -1;
}
Ipc::RetainSurface(live);
Ipc::RetainSurface(snapshot);
Ipc::RetainMailbox(mailbox, true, true);
uint64_t userVa = 0;
if (Ipc::MapSurfaceForPid(live, ownerPid, ownerPml4, heapNext, userVa) != 0) {
Ipc::ReleaseMailbox(mailbox, true, true);
Ipc::ReleaseSurface(snapshot);
Ipc::ReleaseSurface(live);
return -1;
}
WindowSlot& slot = g_slots[slotIdx]; WindowSlot& slot = g_slots[slotIdx];
memset(&slot, 0, sizeof(WindowSlot)); ResetSlotLocked(slot);
slot.used = true; slot.used = true;
slot.ownerPid = ownerPid; slot.ownerPid = ownerPid;
slot.width = w; slot.width = w;
slot.height = h; slot.height = h;
slot.pixelNumPages = numPages; slot.liveSurface = live;
slot.eventHead = 0; slot.snapshotSurface = snapshot;
slot.eventTail = 0; slot.eventMailbox = mailbox;
slot.ownerVa = userVa;
slot.dirty = false; slot.dirty = false;
slot.desktopVa = 0; slot.cursor = 0;
slot.desktopPid = 0;
// Copy title
int tlen = 0; int tlen = 0;
while (title[tlen] && tlen < 63) { while (title[tlen] && tlen < 63) {
slot.title[tlen] = title[tlen]; slot.title[tlen] = title[tlen];
@@ -153,127 +155,55 @@ namespace WinServer {
} }
slot.title[tlen] = '\0'; slot.title[tlen] = '\0';
// Allocate live pixel pages (app renders here) and snapshot pages
// (compositor reads here). Present copies live -> snapshot.
uint64_t userVa = heapNext;
for (int i = 0; i < numPages; i++) {
void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) {
slot.used = false;
return -1;
}
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
slot.pixelPhysPages[i] = physAddr;
if (!Memory::VMM::Paging::MapUserIn(ownerPml4, physAddr, userVa + (uint64_t)i * 0x1000)) {
slot.used = false;
return -1;
}
// Allocate corresponding snapshot page
void* snapPage = Memory::g_pfa->AllocateZeroed();
if (snapPage == nullptr) {
slot.used = false;
return -1;
}
slot.snapshotPhysPages[i] = Memory::SubHHDM((uint64_t)snapPage);
}
slot.ownerVa = userVa;
heapNext += (uint64_t)numPages * 0x1000;
outVa = userVa; outVa = userVa;
Kt::KernelLogStream(Kt::OK, "WinServer") << "Created window " << slotIdx
<< " (" << w << "x" << h << ") for PID " << ownerPid;
return slotIdx; return slotIdx;
} }
int Destroy(int windowId, int callerPid) { int Destroy(int windowId, int callerPid) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
if (windowId < 0 || windowId >= MaxWindows) return -1; if (windowId < 0 || windowId >= MaxWindows) return -1;
WindowSlot& slot = g_slots[windowId]; WindowSlot& slot = g_slots[windowId];
if (!slot.used || slot.ownerPid != callerPid) return -1; if (!slot.used || slot.ownerPid != callerPid) return -1;
// Unmap LIVE pixel pages from the owner's address space so that ReleaseSlotResourcesLocked(slot, true);
// FreeUserHalf() won't double-free them when the process exits.
// Physical page reclamation is deferred below to avoid stale-TLB
// consumers reading freed memory.
{
auto* ownerProc = Sched::GetProcessByPid(slot.ownerPid);
if (ownerProc) {
for (int p = 0; p < slot.pixelNumPages; p++) {
Memory::VMM::Paging::UnmapUserIn(
ownerProc->pml4Phys,
slot.ownerVa + (uint64_t)p * 0x1000);
}
}
}
// Retire both the owner-facing live pages and the compositor-facing
// snapshot pages. The desktop can still hold stale mappings to the
// snapshot pages for a short time after Enumerate notices the window
// is gone, so freeing them immediately turns that stale mapping into
// a use-after-free.
RetirePageBatchLocked(slot.pixelPhysPages, slot.pixelNumPages);
RetirePageBatchLocked(slot.snapshotPhysPages, slot.pixelNumPages);
slot.used = false;
slot.pixelNumPages = 0;
slot.ownerVa = 0;
slot.desktopVa = 0;
slot.desktopPid = 0;
return 0; return 0;
} }
int Present(int windowId, int callerPid) { int Present(int windowId, int callerPid) {
// Validate ownership under the lock (brief) WsGuard guard;
wsLock.Acquire(); if (windowId < 0 || windowId >= MaxWindows) return -1;
ProcessRetiredLocked();
if (windowId < 0 || windowId >= MaxWindows) { wsLock.Release(); return -1; }
WindowSlot& slot = g_slots[windowId];
if (!slot.used || slot.ownerPid != callerPid) { wsLock.Release(); return -1; }
int numPages = slot.pixelNumPages;
wsLock.Release();
// Snapshot memcpy OUTSIDE the lock. This is safe because only the WindowSlot& slot = g_slots[windowId];
// owning process calls Present/Resize, and a process runs on one if (!slot.used || slot.ownerPid != callerPid ||
// CPU at a time. Holding wsLock during an 8MB copy would block slot.liveSurface == nullptr || slot.snapshotSurface == nullptr) {
// ALL other WinServer operations (Poll, Enumerate, SendEvent) return -1;
// across all CPUs, causing convoy stalls and lockups.
for (int i = 0; i < numPages; i++) {
void* src = (void*)Memory::HHDM(slot.pixelPhysPages[i]);
void* dst = (void*)Memory::HHDM(slot.snapshotPhysPages[i]);
memcpy(dst, src, 0x1000);
} }
// Set dirty under lock if (Ipc::CopySurface(slot.snapshotSurface, slot.liveSurface) != 0) return -1;
wsLock.Acquire();
slot.dirty = true; slot.dirty = true;
wsLock.Release();
return 0; return 0;
} }
int Poll(int windowId, int callerPid, Montauk::WinEvent* outEvent) { int Poll(int windowId, int callerPid, Montauk::WinEvent* outEvent) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked(); if (windowId < 0 || windowId >= MaxWindows || outEvent == nullptr) return -1;
if (windowId < 0 || windowId >= MaxWindows) return -1;
WindowSlot& slot = g_slots[windowId]; WindowSlot& slot = g_slots[windowId];
if (!slot.used || slot.ownerPid != callerPid) return -1; if (!slot.used || slot.ownerPid != callerPid || slot.eventMailbox == nullptr) return -1;
if (slot.eventHead == slot.eventTail) return 0; // no events uint16_t len = sizeof(*outEvent);
int rc = Ipc::MailboxRecv(slot.eventMailbox, nullptr, outEvent, &len, true);
*outEvent = slot.events[slot.eventTail]; if (rc < 0) return -1;
slot.eventTail = (slot.eventTail + 1) % MaxEvents; return rc > 0 ? 1 : 0;
return 1;
} }
int Enumerate(Montauk::WinInfo* outArray, int maxCount) { int Enumerate(Montauk::WinInfo* outArray, int maxCount) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
int count = 0; int count = 0;
for (int i = 0; i < MaxWindows && count < maxCount; i++) { for (int i = 0; i < MaxWindows && count < maxCount; i++) {
if (!g_slots[i].used) continue; if (!g_slots[i].used) continue;
Montauk::WinInfo& info = outArray[count]; Montauk::WinInfo& info = outArray[count];
info.id = i; info.id = i;
info.ownerPid = g_slots[i].ownerPid; info.ownerPid = g_slots[i].ownerPid;
@@ -282,7 +212,7 @@ namespace WinServer {
info.height = g_slots[i].height; info.height = g_slots[i].height;
info.dirty = g_slots[i].dirty ? 1 : 0; info.dirty = g_slots[i].dirty ? 1 : 0;
info.cursor = g_slots[i].cursor; info.cursor = g_slots[i].cursor;
g_slots[i].dirty = false; // clear dirty after read g_slots[i].dirty = false;
count++; count++;
} }
return count; return count;
@@ -290,59 +220,43 @@ namespace WinServer {
uint64_t Map(int windowId, int callerPid, uint64_t callerPml4, uint64_t& heapNext) { uint64_t Map(int windowId, int callerPid, uint64_t callerPml4, uint64_t& heapNext) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
if (windowId < 0 || windowId >= MaxWindows) return 0; if (windowId < 0 || windowId >= MaxWindows) return 0;
WindowSlot& slot = g_slots[windowId]; WindowSlot& slot = g_slots[windowId];
if (!slot.used) return 0; if (!slot.used || slot.snapshotSurface == nullptr) return 0;
// If already mapped into this process, return existing VA uint64_t outVa = 0;
if (slot.desktopPid == callerPid && slot.desktopVa != 0) { if (Ipc::MapSurfaceForPid(slot.snapshotSurface, callerPid, callerPml4, heapNext, outVa) != 0) {
return slot.desktopVa;
}
uint64_t userVa = heapNext;
// Map the SNAPSHOT pages (not live pages) so the compositor
// always reads a complete frame captured at Present time.
for (int i = 0; i < slot.pixelNumPages; i++) {
if (!Memory::VMM::Paging::MapUserIn(callerPml4, slot.snapshotPhysPages[i],
userVa + (uint64_t)i * 0x1000)) {
return 0; return 0;
} }
return outVa;
} }
slot.desktopVa = userVa; int Unmap(int windowId, int callerPid, uint64_t callerPml4) {
slot.desktopPid = callerPid; WsGuard guard;
heapNext += (uint64_t)slot.pixelNumPages * 0x1000; if (windowId < 0 || windowId >= MaxWindows) {
return UnmapRetiredSnapshotsLocked(windowId, callerPid, callerPml4);
return userVa;
} }
// Internal: send event without acquiring lock (caller must hold wsLock) int result = UnmapRetiredSnapshotsLocked(windowId, callerPid, callerPml4);
static int SendEventLocked(int windowId, const Montauk::WinEvent* event) {
if (windowId < 0 || windowId >= MaxWindows) return -1;
WindowSlot& slot = g_slots[windowId]; WindowSlot& slot = g_slots[windowId];
if (!slot.used) return -1; if (!slot.used || slot.snapshotSurface == nullptr) return result;
int nextHead = (slot.eventHead + 1) % MaxEvents; int current = Ipc::UnmapSurfaceForPid(slot.snapshotSurface, callerPid, callerPml4);
if (nextHead == slot.eventTail) return -1; // queue full, drop event if (current == 0) return 0;
return result;
slot.events[slot.eventHead] = *event;
slot.eventHead = nextHead;
return 0;
} }
int SendEvent(int windowId, const Montauk::WinEvent* event) { int SendEvent(int windowId, const Montauk::WinEvent* event) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
return SendEventLocked(windowId, event); return SendEventLocked(windowId, event);
} }
int Resize(int windowId, int callerPid, uint64_t ownerPml4, int newW, int newH, int Resize(int windowId, int callerPid, uint64_t ownerPml4, int newW, int newH,
uint64_t& heapNext, uint64_t& outVa) { uint64_t& heapNext, uint64_t& outVa) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
if (windowId < 0 || windowId >= MaxWindows) return -1; if (windowId < 0 || windowId >= MaxWindows) return -1;
WindowSlot& slot = g_slots[windowId]; WindowSlot& slot = g_slots[windowId];
if (!slot.used || slot.ownerPid != callerPid) return -1; if (!slot.used || slot.ownerPid != callerPid) return -1;
if (newW <= 0 || newH <= 0 || newW > 16384 || newH > 16384) return -1; if (newW <= 0 || newH <= 0 || newW > 16384 || newH > 16384) return -1;
@@ -351,56 +265,48 @@ namespace WinServer {
return 0; return 0;
} }
uint64_t bufSize = (uint64_t)newW * newH * 4; uint64_t bufSize = (uint64_t)newW * (uint64_t)newH * 4ULL;
int numPages = (int)((bufSize + 0xFFF) / 0x1000); Ipc::Surface* newLive = Ipc::CreateSurface(bufSize);
if (numPages > MaxPixelPages) return -1; Ipc::Surface* newSnapshot = Ipc::CreateSurface(bufSize);
if (newLive == nullptr || newSnapshot == nullptr) {
// Unmap old LIVE pixel pages from the owner's address space. Actual if (newLive != nullptr) Ipc::ReleaseSurface(newLive);
// physical page reclamation is deferred below for both live and if (newSnapshot != nullptr) Ipc::ReleaseSurface(newSnapshot);
// snapshot pages so stale desktop mappings cannot turn into
// use-after-free reads during remap.
int oldNumPages = slot.pixelNumPages;
for (int i = 0; i < oldNumPages; i++) {
Memory::VMM::Paging::UnmapUserIn(
ownerPml4, slot.ownerVa + (uint64_t)i * 0x1000);
}
RetirePageBatchLocked(slot.pixelPhysPages, oldNumPages);
RetirePageBatchLocked(slot.snapshotPhysPages, oldNumPages);
// Allocate new live + snapshot pages and map live into owner's address space
uint64_t userVa = heapNext;
for (int i = 0; i < numPages; i++) {
void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) return -1;
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
slot.pixelPhysPages[i] = physAddr;
if (!Memory::VMM::Paging::MapUserIn(ownerPml4, physAddr, userVa + (uint64_t)i * 0x1000)) {
return -1; return -1;
} }
void* snapPage = Memory::g_pfa->AllocateZeroed(); Ipc::RetainSurface(newLive);
if (snapPage == nullptr) return -1; Ipc::RetainSurface(newSnapshot);
slot.snapshotPhysPages[i] = Memory::SubHHDM((uint64_t)snapPage);
uint64_t newOwnerVa = 0;
if (Ipc::MapSurfaceForPid(newLive, callerPid, ownerPml4, heapNext, newOwnerVa) != 0) {
Ipc::ReleaseSurface(newSnapshot);
Ipc::ReleaseSurface(newLive);
return -1;
} }
if (slot.liveSurface != nullptr) {
Ipc::UnmapSurfaceForPid(slot.liveSurface, callerPid, ownerPml4);
Ipc::ReleaseSurface(slot.liveSurface);
}
if (slot.snapshotSurface != nullptr) {
RetireSnapshotLocked(windowId, slot.snapshotSurface);
}
slot.liveSurface = newLive;
slot.snapshotSurface = newSnapshot;
slot.width = newW; slot.width = newW;
slot.height = newH; slot.height = newH;
slot.pixelNumPages = numPages; slot.ownerVa = newOwnerVa;
slot.ownerVa = userVa; slot.dirty = true;
heapNext += (uint64_t)numPages * 0x1000;
// Invalidate desktop mapping so it re-maps on next enumerate outVa = newOwnerVa;
slot.desktopVa = 0;
slot.desktopPid = 0;
outVa = userVa;
return 0; return 0;
} }
int SetCursor(int windowId, int callerPid, int cursor) { int SetCursor(int windowId, int callerPid, int cursor) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
if (windowId < 0 || windowId >= MaxWindows) return -1; if (windowId < 0 || windowId >= MaxWindows) return -1;
WindowSlot& slot = g_slots[windowId]; WindowSlot& slot = g_slots[windowId];
if (!slot.used || slot.ownerPid != callerPid) return -1; if (!slot.used || slot.ownerPid != callerPid) return -1;
slot.cursor = (uint8_t)cursor; slot.cursor = (uint8_t)cursor;
@@ -409,14 +315,11 @@ namespace WinServer {
int SetScale(int scale) { int SetScale(int scale) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
if (scale < 0) scale = 0; if (scale < 0) scale = 0;
if (scale > 2) scale = 2; if (scale > 2) scale = 2;
g_uiScale = scale; g_uiScale = scale;
// Broadcast scale event to all active windows Montauk::WinEvent ev{};
Montauk::WinEvent ev;
memset(&ev, 0, sizeof(ev));
ev.type = 4; ev.type = 4;
ev.scale.scale = scale; ev.scale.scale = scale;
for (int i = 0; i < MaxWindows; i++) { for (int i = 0; i < MaxWindows; i++) {
@@ -433,42 +336,9 @@ namespace WinServer {
void CleanupProcess(int pid) { void CleanupProcess(int pid) {
WsGuard guard; WsGuard guard;
ProcessRetiredLocked();
for (int i = 0; i < MaxWindows; i++) { for (int i = 0; i < MaxWindows; i++) {
if (g_slots[i].used && g_slots[i].ownerPid == pid) { if (!g_slots[i].used || g_slots[i].ownerPid != pid) continue;
Kt::KernelLogStream(Kt::INFO, "WinServer") << "Cleaning up window " ReleaseSlotResourcesLocked(g_slots[i], false);
<< i << " for exited PID " << pid;
// Do NOT free live pixel pages here -- they are still mapped
// in the owner's page tables and FreeUserHalf() (called right
// after CleanupProcess) will free them.
// Snapshot pages can still be mapped into the desktop, so
// retire them instead of freeing them immediately.
RetirePageBatchLocked(g_slots[i].snapshotPhysPages, g_slots[i].pixelNumPages);
g_slots[i].used = false;
g_slots[i].pixelNumPages = 0;
g_slots[i].ownerVa = 0;
g_slots[i].desktopVa = 0;
g_slots[i].desktopPid = 0;
}
// If this process had windows mapped INTO it (was the desktop viewer),
// unmap those pixel pages so FreeUserHalf() won't free pages owned
// by other processes.
if (g_slots[i].used && g_slots[i].desktopPid == pid) {
auto* proc = Sched::GetProcessByPid(pid);
if (proc) {
for (int p = 0; p < g_slots[i].pixelNumPages; p++) {
Memory::VMM::Paging::UnmapUserIn(
proc->pml4Phys,
g_slots[i].desktopVa + (uint64_t)p * 0x1000);
}
}
g_slots[i].desktopVa = 0;
g_slots[i].desktopPid = 0;
}
} }
} }
+7 -11
View File
@@ -7,28 +7,23 @@
#pragma once #pragma once
#include "Syscall.hpp" #include "Syscall.hpp"
#include <cstdint> #include <cstdint>
#include <Ipc/Ipc.hpp>
namespace WinServer { namespace WinServer {
static constexpr int MaxWindows = 8; static constexpr int MaxWindows = 8;
static constexpr int MaxEvents = 64;
static constexpr int MaxPixelPages = 8192; // up to 3840x2160 @ 32bpp = 32MB
struct WindowSlot { struct WindowSlot {
bool used; bool used;
int ownerPid; int ownerPid;
char title[64]; char title[64];
int width, height; int width, height;
uint64_t pixelPhysPages[MaxPixelPages]; // live buffer (app writes here) Ipc::Surface* liveSurface; // app writes here
uint64_t snapshotPhysPages[MaxPixelPages]; // snapshot (compositor reads here) Ipc::Surface* snapshotSurface; // compositor/viewers read here
int pixelNumPages; Ipc::Mailbox* eventMailbox; // input/control events for the app
uint64_t ownerVa; // VA in owner's address space uint64_t ownerVa; // mapped VA of liveSurface in owner
uint64_t desktopVa; // VA in desktop's address space (0 = not yet mapped)
int desktopPid; // PID of the process that mapped it
Montauk::WinEvent events[MaxEvents];
int eventHead, eventTail;
bool dirty; bool dirty;
uint8_t cursor; // cursor style requested by app (0=arrow, 1=resize_h, 2=resize_v) uint8_t cursor; // 0=arrow, 1=resize_h, 2=resize_v
}; };
int Create(int ownerPid, uint64_t ownerPml4, const char* title, int w, int h, int Create(int ownerPid, uint64_t ownerPml4, const char* title, int w, int h,
@@ -38,6 +33,7 @@ namespace WinServer {
int Poll(int windowId, int callerPid, Montauk::WinEvent* outEvent); int Poll(int windowId, int callerPid, Montauk::WinEvent* outEvent);
int Enumerate(Montauk::WinInfo* outArray, int maxCount); int Enumerate(Montauk::WinInfo* outArray, int maxCount);
uint64_t Map(int windowId, int callerPid, uint64_t callerPml4, uint64_t& heapNext); uint64_t Map(int windowId, int callerPid, uint64_t callerPml4, uint64_t& heapNext);
int Unmap(int windowId, int callerPid, uint64_t callerPml4);
int SendEvent(int windowId, const Montauk::WinEvent* event); int SendEvent(int windowId, const Montauk::WinEvent* event);
int Resize(int windowId, int callerPid, uint64_t ownerPml4, int newW, int newH, int Resize(int windowId, int callerPid, uint64_t ownerPml4, int newW, int newH,
uint64_t& heapNext, uint64_t& outVa); uint64_t& heapNext, uint64_t& outVa);
+6
View File
@@ -51,6 +51,12 @@ namespace Montauk {
return WinServer::Map(windowId, proc->pid, proc->pml4Phys, proc->heapNext); return WinServer::Map(windowId, proc->pid, proc->pml4Phys, proc->heapNext);
} }
static int Sys_WinUnmap(int windowId) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return -1;
return WinServer::Unmap(windowId, proc->pid, proc->pml4Phys);
}
static int Sys_WinSendEvent(int windowId, const WinEvent* event) { static int Sys_WinSendEvent(int windowId, const WinEvent* event) {
if (event == nullptr) return -1; if (event == nullptr) return -1;
return WinServer::SendEvent(windowId, event); return WinServer::SendEvent(windowId, event);
+63 -66
View File
@@ -10,14 +10,7 @@
namespace Fs::Vfs { namespace Fs::Vfs {
struct HandleEntry {
bool inUse;
int driveNumber;
int localHandle;
};
static FsDriver* driveTable[MaxDrives]; static FsDriver* driveTable[MaxDrives];
static HandleEntry handleTable[MaxHandles];
// Protects handle table and driver dispatch from concurrent CPU access. // Protects handle table and driver dispatch from concurrent CPU access.
// Uses Mutex (not Spinlock) so interrupts stay enabled while held -- // Uses Mutex (not Spinlock) so interrupts stay enabled while held --
@@ -49,22 +42,12 @@ namespace Fs::Vfs {
return true; return true;
} }
static int AllocHandle() {
for (int i = 0; i < MaxHandles; i++) {
if (!handleTable[i].inUse) return i;
}
return -1;
}
void Initialize() { void Initialize() {
for (int i = 0; i < MaxDrives; i++) { for (int i = 0; i < MaxDrives; i++) {
driveTable[i] = nullptr; driveTable[i] = nullptr;
} }
for (int i = 0; i < MaxHandles; i++) {
handleTable[i].inUse = false;
}
Kt::KernelLogStream(Kt::OK, "VFS") << "Initialized (" << MaxDrives << " drives, " << MaxHandles << " handles)"; Kt::KernelLogStream(Kt::OK, "VFS") << "Initialized (" << MaxDrives << " drives)";
} }
int RegisterDrive(int driveNumber, FsDriver* driver) { int RegisterDrive(int driveNumber, FsDriver* driver) {
@@ -76,7 +59,10 @@ namespace Fs::Vfs {
return 0; return 0;
} }
int VfsOpen(const char* path) { int OpenBackendFile(const char* path, BackendFile& outFile) {
outFile.driveNumber = -1;
outFile.localHandle = -1;
int drive; int drive;
const char* localPath; const char* localPath;
@@ -84,67 +70,86 @@ namespace Fs::Vfs {
if (drive < 0 || drive >= MaxDrives || driveTable[drive] == nullptr) return -1; if (drive < 0 || drive >= MaxDrives || driveTable[drive] == nullptr) return -1;
vfsLock.Acquire(); vfsLock.Acquire();
int localHandle = driveTable[drive]->Open(localPath); int localHandle = driveTable[drive]->Open(localPath);
if (localHandle < 0) { vfsLock.Release(); return -1; } vfsLock.Release();
if (localHandle < 0) return -1;
int globalHandle = AllocHandle(); outFile.driveNumber = drive;
if (globalHandle < 0) { outFile.localHandle = localHandle;
driveTable[drive]->Close(localHandle); return 0;
}
int ReadBackendFile(const BackendFile& file, uint8_t* buffer, uint64_t offset, uint64_t size) {
vfsLock.Acquire();
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || driveTable[file.driveNumber] == nullptr ||
file.localHandle < 0) {
vfsLock.Release(); vfsLock.Release();
return -1; return -1;
} }
handleTable[globalHandle].inUse = true; int result = driveTable[file.driveNumber]->Read(file.localHandle, buffer, offset, size);
handleTable[globalHandle].driveNumber = drive;
handleTable[globalHandle].localHandle = localHandle;
vfsLock.Release();
return globalHandle;
}
int VfsRead(int handle, uint8_t* buffer, uint64_t offset, uint64_t size) {
vfsLock.Acquire();
if (handle < 0 || handle >= MaxHandles || !handleTable[handle].inUse) { vfsLock.Release(); return -1; }
HandleEntry& entry = handleTable[handle];
int result = driveTable[entry.driveNumber]->Read(entry.localHandle, buffer, offset, size);
vfsLock.Release(); vfsLock.Release();
return result; return result;
} }
uint64_t VfsGetSize(int handle) { uint64_t GetBackendFileSize(const BackendFile& file) {
vfsLock.Acquire(); vfsLock.Acquire();
if (handle < 0 || handle >= MaxHandles || !handleTable[handle].inUse) { vfsLock.Release(); return 0; } if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || driveTable[file.driveNumber] == nullptr ||
file.localHandle < 0) {
vfsLock.Release();
return 0;
}
HandleEntry& entry = handleTable[handle]; uint64_t result = driveTable[file.driveNumber]->GetSize(file.localHandle);
uint64_t result = driveTable[entry.driveNumber]->GetSize(entry.localHandle);
vfsLock.Release(); vfsLock.Release();
return result; return result;
} }
void VfsClose(int handle) { bool BackendFileCanWrite(const BackendFile& file) {
vfsLock.Acquire(); vfsLock.Acquire();
if (handle < 0 || handle >= MaxHandles || !handleTable[handle].inUse) { vfsLock.Release(); return; } bool canWrite = file.driveNumber >= 0 && file.driveNumber < MaxDrives &&
driveTable[file.driveNumber] != nullptr &&
HandleEntry& entry = handleTable[handle]; file.localHandle >= 0 &&
driveTable[entry.driveNumber]->Close(entry.localHandle); driveTable[file.driveNumber]->Write != nullptr;
entry.inUse = false;
vfsLock.Release(); vfsLock.Release();
return canWrite;
} }
int VfsWrite(int handle, const uint8_t* buffer, uint64_t offset, uint64_t size) { void CloseBackendFile(BackendFile& file) {
vfsLock.Acquire(); vfsLock.Acquire();
if (handle < 0 || handle >= MaxHandles || !handleTable[handle].inUse) { vfsLock.Release(); return -1; } if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || driveTable[file.driveNumber] == nullptr ||
file.localHandle < 0) {
vfsLock.Release();
file.driveNumber = -1;
file.localHandle = -1;
return;
}
HandleEntry& entry = handleTable[handle]; driveTable[file.driveNumber]->Close(file.localHandle);
if (driveTable[entry.driveNumber]->Write == nullptr) { vfsLock.Release(); return -1; } vfsLock.Release();
int result = driveTable[entry.driveNumber]->Write(entry.localHandle, buffer, offset, size);
file.driveNumber = -1;
file.localHandle = -1;
}
int WriteBackendFile(const BackendFile& file, const uint8_t* buffer, uint64_t offset, uint64_t size) {
vfsLock.Acquire();
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || driveTable[file.driveNumber] == nullptr ||
file.localHandle < 0) {
vfsLock.Release();
return -1;
}
if (driveTable[file.driveNumber]->Write == nullptr) { vfsLock.Release(); return -1; }
int result = driveTable[file.driveNumber]->Write(file.localHandle, buffer, offset, size);
vfsLock.Release(); vfsLock.Release();
return result; return result;
} }
int VfsCreate(const char* path) { int CreateBackendFile(const char* path, BackendFile& outFile) {
outFile.driveNumber = -1;
outFile.localHandle = -1;
int drive; int drive;
const char* localPath; const char* localPath;
@@ -155,20 +160,12 @@ namespace Fs::Vfs {
vfsLock.Acquire(); vfsLock.Acquire();
int localHandle = driveTable[drive]->Create(localPath); int localHandle = driveTable[drive]->Create(localPath);
if (localHandle < 0) { vfsLock.Release(); return -1; }
int globalHandle = AllocHandle();
if (globalHandle < 0) {
vfsLock.Release(); vfsLock.Release();
return -1; if (localHandle < 0) return -1;
}
handleTable[globalHandle].inUse = true; outFile.driveNumber = drive;
handleTable[globalHandle].driveNumber = drive; outFile.localHandle = localHandle;
handleTable[globalHandle].localHandle = localHandle; return 0;
vfsLock.Release();
return globalHandle;
} }
int VfsDelete(const char* path) { int VfsDelete(const char* path) {
+13 -7
View File
@@ -11,7 +11,11 @@
namespace Fs::Vfs { namespace Fs::Vfs {
static constexpr int MaxDrives = 16; static constexpr int MaxDrives = 16;
static constexpr int MaxHandles = 64;
struct BackendFile {
int driveNumber;
int localHandle;
};
struct FsDriver { struct FsDriver {
int (*Open)(const char* path); int (*Open)(const char* path);
@@ -29,13 +33,15 @@ namespace Fs::Vfs {
void Initialize(); void Initialize();
int RegisterDrive(int driveNumber, FsDriver* driver); int RegisterDrive(int driveNumber, FsDriver* driver);
int VfsOpen(const char* path); int OpenBackendFile(const char* path, BackendFile& outFile);
int VfsRead(int handle, uint8_t* buffer, uint64_t offset, uint64_t size); int CreateBackendFile(const char* path, BackendFile& outFile);
int VfsWrite(int handle, const uint8_t* buffer, uint64_t offset, uint64_t size); int ReadBackendFile(const BackendFile& file, uint8_t* buffer, uint64_t offset, uint64_t size);
int VfsCreate(const char* path); int WriteBackendFile(const BackendFile& file, const uint8_t* buffer, uint64_t offset, uint64_t size);
uint64_t GetBackendFileSize(const BackendFile& file);
bool BackendFileCanWrite(const BackendFile& file);
void CloseBackendFile(BackendFile& file);
int VfsDelete(const char* path); int VfsDelete(const char* path);
uint64_t VfsGetSize(int handle);
void VfsClose(int handle);
int VfsReadDir(const char* path, const char** outNames, int maxEntries); int VfsReadDir(const char* path, const char** outNames, int maxEntries);
int VfsMkdir(const char* path); int VfsMkdir(const char* path);
int VfsRename(const char* oldPath, const char* newPath); int VfsRename(const char* oldPath, const char* newPath);
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
#pragma once
#include <cstdint>
namespace Net::Tcp { struct Connection; }
namespace Ipc {
static constexpr int MaxHandlesPerProcess = 128;
static constexpr int MaxSurfaceMapsPerProcess = 64;
static constexpr uint32_t DefaultStreamCapacity = 4096;
enum class HandleType : uint8_t {
None = 0,
Stream,
Mailbox,
File,
Socket,
Surface,
Process,
Waitset
};
enum Rights : uint32_t {
RightWait = 1u << 0,
RightRead = 1u << 1,
RightWrite = 1u << 2,
RightSend = 1u << 3,
RightRecv = 1u << 4,
RightMap = 1u << 5,
RightManage = 1u << 6,
RightDup = 1u << 7
};
enum Signals : uint32_t {
SignalNone = 0,
SignalReadable = 1u << 0,
SignalWritable = 1u << 1,
SignalPeerClosed = 1u << 2,
SignalExited = 1u << 3,
SignalReady = 1u << 4
};
struct Object;
struct Stream;
struct Mailbox;
struct File;
struct Socket;
struct Surface;
struct ProcessObject;
struct Waitset;
struct HandleEntry {
bool used;
uint32_t rights;
HandleType type;
Object* object;
};
struct WaitsetReady {
int32_t index;
uint32_t signals;
};
void Initialize();
int CurrentSlot();
int SlotForPid(int pid);
int InstallHandleForSlot(int slot, Object* object, HandleType type, uint32_t rights);
int CloseHandleForSlot(int slot, int handle);
int CloseHandle(int handle);
int DupHandle(int handle);
bool SnapshotHandleForSlot(int slot, int handle, HandleType& type, Object*& object, uint32_t& rights);
uint32_t GetHandleSignalsForSlot(int slot, int handle);
Stream* CreateStream(uint32_t capacity = DefaultStreamCapacity);
int CreateStreamHandlePairForSlot(int slot, uint32_t capacity, int& outReadHandle, int& outWriteHandle);
int CreateStreamHandlePair(uint32_t capacity, int& outReadHandle, int& outWriteHandle);
void RetainStream(Stream* stream, bool readSide, bool writeSide);
void ReleaseStream(Stream* stream, bool readSide, bool writeSide);
int StreamRead(Stream* stream, uint8_t* out, int maxLen, bool nonBlocking);
int StreamWrite(Stream* stream, const uint8_t* data, int len, bool nonBlocking);
int StreamReadHandle(int handle, uint8_t* out, int maxLen);
int StreamWriteHandle(int handle, const uint8_t* data, int len);
bool StreamHasData(Stream* stream);
Mailbox* CreateMailbox();
int CreateMailboxHandlePairForSlot(int slot, int& outSendHandle, int& outRecvHandle);
int CreateMailboxHandlePair(int& outSendHandle, int& outRecvHandle);
void RetainMailbox(Mailbox* mailbox, bool sender, bool receiver);
void ReleaseMailbox(Mailbox* mailbox, bool sender, bool receiver);
int MailboxSend(Mailbox* mailbox, uint32_t msgType, const void* data, uint16_t len);
int MailboxRecv(Mailbox* mailbox, uint32_t* msgType, void* data, uint16_t* inOutLen, bool nonBlocking);
int MailboxSendHandle(int handle, uint32_t msgType, const void* data, uint16_t len, int attachHandle);
int MailboxRecvHandle(int handle, uint32_t* msgType, void* data, uint16_t* inOutLen, int* outAttachHandle);
bool MailboxHasMessage(Mailbox* mailbox);
int OpenFileHandleForSlot(int slot, const char* path, bool create);
int OpenFileHandle(const char* path);
int CreateFileHandle(const char* path);
int FileReadHandle(int handle, uint8_t* buffer, uint64_t offset, uint64_t size);
int FileWriteHandle(int handle, const uint8_t* buffer, uint64_t offset, uint64_t size);
uint64_t FileGetSizeHandle(int handle);
int CreateSocketHandleForSlot(int slot, int type);
int CreateSocketHandle(int type);
int SocketConnectHandle(int handle, uint32_t ip, uint16_t port);
int SocketBindHandle(int handle, uint16_t port);
int SocketListenHandle(int handle);
int SocketAcceptHandle(int handle);
int SocketSendHandle(int handle, const uint8_t* data, uint32_t len);
int SocketRecvHandle(int handle, uint8_t* buffer, uint32_t maxLen);
int SocketSendToHandle(int handle, const uint8_t* data, uint32_t len, uint32_t destIp, uint16_t destPort);
int SocketRecvFromHandle(int handle, uint8_t* buffer, uint32_t maxLen, uint32_t* srcIp, uint16_t* srcPort);
void NotifyTcpConnectionChanged(Net::Tcp::Connection* connection);
Surface* CreateSurface(uint64_t byteSize);
int CreateSurfaceHandle(uint64_t byteSize);
void RetainSurface(Surface* surface);
void ReleaseSurface(Surface* surface);
uint64_t GetSurfaceSize(const Surface* surface);
int ResizeSurface(Surface* surface, uint64_t newSize);
int CopySurface(Surface* dst, Surface* src);
uint64_t MapSurfaceHandle(int handle);
int ResizeSurfaceHandle(int handle, uint64_t newSize);
int MapSurfaceForPid(Surface* surface, int pid, uint64_t pml4Phys, uint64_t& heapNext, uint64_t& outVa);
int UnmapSurfaceForPid(Surface* surface, int pid, uint64_t pml4Phys);
ProcessObject* GetProcessObject(int pid);
int OpenProcessHandle(int pid);
void ProcessStartedInSlot(int slot, int pid);
void ProcessExitedInSlot(int slot, int pid);
bool ProcessHasExited(ProcessObject* process);
uint32_t WaitOnHandle(int handle, uint32_t wantedSignals, uint64_t timeoutMs);
int CreateWaitsetHandleForSlot(int slot);
int CreateWaitsetHandleForCurrent();
int WaitsetAddHandleForSlot(int slot, int waitsetHandle, int targetHandle, uint32_t signals);
int WaitsetAddHandle(int waitsetHandle, int targetHandle, uint32_t signals);
int WaitsetRemoveIndexForSlot(int slot, int waitsetHandle, int index);
int WaitsetRemoveIndex(int waitsetHandle, int index);
int WaitsetWaitHandle(int waitsetHandle, WaitsetReady* outReady, uint64_t timeoutMs);
void NotifyObjectChanged(Object* object);
void CleanupProcessSlot(int slot, int pid, uint64_t pml4Phys);
}
+2
View File
@@ -38,6 +38,7 @@
#include <Fs/Ext2.hpp> #include <Fs/Ext2.hpp>
#include <Fs/FsProbe.hpp> #include <Fs/FsProbe.hpp>
#include <Sched/Scheduler.hpp> #include <Sched/Scheduler.hpp>
#include <Ipc/Ipc.hpp>
#include <Api/Syscall.hpp> #include <Api/Syscall.hpp>
#include <Hal/SmpBoot.hpp> #include <Hal/SmpBoot.hpp>
using namespace Kt; using namespace Kt;
@@ -227,6 +228,7 @@ extern "C" void kmain() {
Montauk::InitializeSyscalls(); Montauk::InitializeSyscalls();
Sched::Initialize(); Sched::Initialize();
Ipc::Initialize();
// Boot Application Processors (all subsystems ready, APs can schedule) // Boot Application Processors (all subsystems ready, APs can schedule)
Smp::BootAPs(); Smp::BootAPs();
+22 -286
View File
@@ -5,324 +5,60 @@
*/ */
#include "Socket.hpp" #include "Socket.hpp"
#include <Net/Tcp.hpp> #include <Ipc/Ipc.hpp>
#include <Net/Udp.hpp>
#include <Net/NetConfig.hpp>
#include <Terminal/Terminal.hpp> #include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
#include <Libraries/Memory.hpp>
using namespace Kt; using namespace Kt;
namespace Net::Socket { namespace Net::Socket {
// ---- UDP socket state ----
static constexpr uint32_t UDP_RING_SIZE = 4096;
static constexpr int MAX_UDP_SOCKETS = 16;
struct UdpDgramHeader {
uint32_t SrcIp;
uint16_t SrcPort;
uint16_t DataLen;
};
struct UdpSocketState {
uint8_t Ring[UDP_RING_SIZE];
uint32_t Head;
uint32_t Tail;
uint32_t Count;
uint16_t LocalPort;
bool Active;
};
static UdpSocketState g_udpSockets[MAX_UDP_SOCKETS] = {};
static SocketEntry g_sockets[MAX_SOCKETS] = {};
static uint16_t g_nextEphemeralPort = 49152;
static uint16_t AllocEphemeralPort() {
uint16_t port = g_nextEphemeralPort++;
if (g_nextEphemeralPort == 0) g_nextEphemeralPort = 49152;
return port;
}
static bool ValidFd(int fd, int pid) {
if (fd < 0 || fd >= MAX_SOCKETS) return false;
if (!g_sockets[fd].Active) return false;
if (g_sockets[fd].OwnerPid != pid) return false;
return true;
}
static UdpSocketState* AllocUdpState() {
for (int i = 0; i < MAX_UDP_SOCKETS; i++) {
if (!g_udpSockets[i].Active) {
g_udpSockets[i].Active = true;
g_udpSockets[i].Head = 0;
g_udpSockets[i].Tail = 0;
g_udpSockets[i].Count = 0;
g_udpSockets[i].LocalPort = 0;
return &g_udpSockets[i];
}
}
return nullptr;
}
static void FreeUdpState(UdpSocketState* state) {
if (state) {
if (state->LocalPort != 0) {
Udp::Unbind(state->LocalPort);
}
state->Active = false;
}
}
static void UdpSocketDispatcher(uint32_t srcIp, uint16_t srcPort,
uint16_t dstPort,
const uint8_t* data, uint16_t length) {
for (int i = 0; i < MAX_UDP_SOCKETS; i++) {
if (g_udpSockets[i].Active && g_udpSockets[i].LocalPort == dstPort) {
UdpSocketState* st = &g_udpSockets[i];
uint32_t needed = sizeof(UdpDgramHeader) + length;
if (st->Count + needed > UDP_RING_SIZE) {
return; // drop if buffer full
}
// Enqueue header
UdpDgramHeader hdr;
hdr.SrcIp = srcIp;
hdr.SrcPort = srcPort;
hdr.DataLen = length;
const uint8_t* hdrBytes = (const uint8_t*)&hdr;
for (uint32_t j = 0; j < sizeof(UdpDgramHeader); j++) {
st->Ring[st->Tail] = hdrBytes[j];
st->Tail = (st->Tail + 1) % UDP_RING_SIZE;
}
// Enqueue payload
for (uint16_t j = 0; j < length; j++) {
st->Ring[st->Tail] = data[j];
st->Tail = (st->Tail + 1) % UDP_RING_SIZE;
}
st->Count += needed;
return;
}
}
}
// ---- Public API ----
void Initialize() { void Initialize() {
for (int i = 0; i < MAX_SOCKETS; i++) { KernelLogStream(OK, "Net") << "Socket handles initialized";
g_sockets[i].Active = false;
}
for (int i = 0; i < MAX_UDP_SOCKETS; i++) {
g_udpSockets[i].Active = false;
}
KernelLogStream(OK, "Net") << "Socket table initialized";
} }
int Create(int type, int pid) { int Create(int type, int pid) {
if (type != SOCK_TCP && type != SOCK_UDP) return -1; return Ipc::CreateSocketHandleForSlot(Ipc::SlotForPid(pid), type);
for (int i = 0; i < MAX_SOCKETS; i++) {
if (!g_sockets[i].Active) {
g_sockets[i].Active = true;
g_sockets[i].Type = type;
g_sockets[i].OwnerPid = pid;
g_sockets[i].TcpConn = nullptr;
g_sockets[i].UdpState = nullptr;
g_sockets[i].LocalPort = 0;
if (type == SOCK_UDP) {
UdpSocketState* us = AllocUdpState();
if (!us) {
g_sockets[i].Active = false;
return -1;
}
g_sockets[i].UdpState = us;
} }
return i; int Connect(int fd, uint32_t ip, uint16_t port, int /*pid*/) {
} return Ipc::SocketConnectHandle(fd, ip, port);
}
return -1;
} }
int Connect(int fd, uint32_t ip, uint16_t port, int pid) { int Bind(int fd, uint16_t port, int /*pid*/) {
if (!ValidFd(fd, pid)) return -1; return Ipc::SocketBindHandle(fd, port);
if (g_sockets[fd].Type != SOCK_TCP) return -1;
if (g_sockets[fd].TcpConn != nullptr) return -1;
uint16_t srcPort = AllocEphemeralPort();
g_sockets[fd].LocalPort = srcPort;
Tcp::Connection* conn = Tcp::Connect(ip, port, srcPort);
if (conn == nullptr) return -1;
g_sockets[fd].TcpConn = conn;
return 0;
} }
int Bind(int fd, uint16_t port, int pid) { int Listen(int fd, int /*pid*/) {
if (!ValidFd(fd, pid)) return -1; return Ipc::SocketListenHandle(fd);
g_sockets[fd].LocalPort = port;
if (g_sockets[fd].Type == SOCK_UDP) {
UdpSocketState* us = g_sockets[fd].UdpState;
if (!us) return -1;
us->LocalPort = port;
if (!Udp::Bind(port, UdpSocketDispatcher)) return -1;
} }
return 0; int Accept(int fd, int /*pid*/) {
return Ipc::SocketAcceptHandle(fd);
} }
int Listen(int fd, int pid) { int Send(int fd, const uint8_t* data, uint32_t len, int /*pid*/) {
if (!ValidFd(fd, pid)) return -1; return Ipc::SocketSendHandle(fd, data, len);
if (g_sockets[fd].Type != SOCK_TCP) return -1;
if (g_sockets[fd].LocalPort == 0) return -1;
if (g_sockets[fd].TcpConn != nullptr) return -1;
Tcp::Connection* conn = Tcp::Listen(g_sockets[fd].LocalPort);
if (conn == nullptr) return -1;
g_sockets[fd].TcpConn = conn;
return 0;
} }
int Accept(int fd, int pid) { int Recv(int fd, uint8_t* buf, uint32_t maxLen, int /*pid*/) {
if (!ValidFd(fd, pid)) return -1; return Ipc::SocketRecvHandle(fd, buf, maxLen);
if (g_sockets[fd].Type != SOCK_TCP) return -1;
if (g_sockets[fd].TcpConn == nullptr) return -1;
Tcp::Connection* clientConn = Tcp::Accept(g_sockets[fd].TcpConn);
if (clientConn == nullptr) return -1;
// Allocate a new socket entry for the accepted connection
for (int i = 0; i < MAX_SOCKETS; i++) {
if (!g_sockets[i].Active) {
g_sockets[i].Active = true;
g_sockets[i].Type = SOCK_TCP;
g_sockets[i].OwnerPid = pid;
g_sockets[i].TcpConn = clientConn;
g_sockets[i].UdpState = nullptr;
g_sockets[i].LocalPort = g_sockets[fd].LocalPort;
return i;
}
}
// No free socket slot — close the accepted connection
Tcp::Close(clientConn);
return -1;
}
int Send(int fd, const uint8_t* data, uint32_t len, int pid) {
if (!ValidFd(fd, pid)) return -1;
if (g_sockets[fd].Type != SOCK_TCP) return -1;
if (g_sockets[fd].TcpConn == nullptr) return -1;
return Tcp::Send(g_sockets[fd].TcpConn, data, (uint16_t)len);
}
int Recv(int fd, uint8_t* buf, uint32_t maxLen, int pid) {
if (!ValidFd(fd, pid)) return -1;
if (g_sockets[fd].Type != SOCK_TCP) return -1;
if (g_sockets[fd].TcpConn == nullptr) return -1;
return Tcp::ReceiveNonBlocking(g_sockets[fd].TcpConn, buf, (uint16_t)maxLen);
} }
int SendTo(int fd, const uint8_t* data, uint32_t len, int SendTo(int fd, const uint8_t* data, uint32_t len,
uint32_t destIp, uint16_t destPort, int pid) { uint32_t destIp, uint16_t destPort, int /*pid*/) {
if (!ValidFd(fd, pid)) return -1; return Ipc::SocketSendToHandle(fd, data, len, destIp, destPort);
if (g_sockets[fd].Type != SOCK_UDP) return -1;
UdpSocketState* us = g_sockets[fd].UdpState;
if (!us) return -1;
// Auto-bind ephemeral port if not already bound
if (us->LocalPort == 0) {
uint16_t ep = AllocEphemeralPort();
us->LocalPort = ep;
g_sockets[fd].LocalPort = ep;
if (!Udp::Bind(ep, UdpSocketDispatcher)) return -1;
}
if (!Udp::Send(destIp, us->LocalPort, destPort, data, (uint16_t)len)) {
return -1;
}
return (int)len;
} }
int RecvFrom(int fd, uint8_t* buf, uint32_t maxLen, int RecvFrom(int fd, uint8_t* buf, uint32_t maxLen,
uint32_t* srcIp, uint16_t* srcPort, int pid) { uint32_t* srcIp, uint16_t* srcPort, int /*pid*/) {
if (!ValidFd(fd, pid)) return -1; return Ipc::SocketRecvFromHandle(fd, buf, maxLen, srcIp, srcPort);
if (g_sockets[fd].Type != SOCK_UDP) return -1;
UdpSocketState* us = g_sockets[fd].UdpState;
if (!us) return -1;
if (us->Count < sizeof(UdpDgramHeader)) {
return -1; // no data available
} }
// Dequeue header void Close(int fd, int /*pid*/) {
UdpDgramHeader hdr; Ipc::CloseHandle(fd);
uint8_t* hdrBytes = (uint8_t*)&hdr;
for (uint32_t j = 0; j < sizeof(UdpDgramHeader); j++) {
hdrBytes[j] = us->Ring[us->Head];
us->Head = (us->Head + 1) % UDP_RING_SIZE;
}
us->Count -= sizeof(UdpDgramHeader);
// Dequeue payload
uint16_t copyLen = hdr.DataLen;
if (copyLen > maxLen) copyLen = (uint16_t)maxLen;
for (uint16_t j = 0; j < copyLen; j++) {
buf[j] = us->Ring[us->Head];
us->Head = (us->Head + 1) % UDP_RING_SIZE;
} }
// Skip remaining data if buffer was too small void CleanupProcess(int /*pid*/) {
for (uint16_t j = copyLen; j < hdr.DataLen; j++) {
us->Head = (us->Head + 1) % UDP_RING_SIZE;
}
us->Count -= hdr.DataLen;
if (srcIp) *srcIp = hdr.SrcIp;
if (srcPort) *srcPort = hdr.SrcPort;
return (int)copyLen;
}
void Close(int fd, int pid) {
if (!ValidFd(fd, pid)) return;
if (g_sockets[fd].TcpConn != nullptr) {
Tcp::Close(g_sockets[fd].TcpConn);
g_sockets[fd].TcpConn = nullptr;
}
if (g_sockets[fd].UdpState != nullptr) {
FreeUdpState(g_sockets[fd].UdpState);
g_sockets[fd].UdpState = nullptr;
}
g_sockets[fd].Active = false;
}
void CleanupProcess(int pid) {
for (int i = 0; i < MAX_SOCKETS; i++) {
if (g_sockets[i].Active && g_sockets[i].OwnerPid == pid) {
if (g_sockets[i].TcpConn != nullptr) {
Tcp::Close(g_sockets[i].TcpConn);
g_sockets[i].TcpConn = nullptr;
}
if (g_sockets[i].UdpState != nullptr) {
FreeUdpState(g_sockets[i].UdpState);
g_sockets[i].UdpState = nullptr;
}
g_sockets[i].Active = false;
}
}
} }
} }
+5 -18
View File
@@ -6,40 +6,27 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <Net/Tcp.hpp>
namespace Net::Socket { namespace Net::Socket {
static constexpr int SOCK_TCP = 1; static constexpr int SOCK_TCP = 1;
static constexpr int SOCK_UDP = 2; static constexpr int SOCK_UDP = 2;
static constexpr int MAX_SOCKETS = 64;
struct UdpSocketState;
struct SocketEntry {
bool Active;
int Type;
int OwnerPid;
Tcp::Connection* TcpConn;
UdpSocketState* UdpState;
uint16_t LocalPort;
};
void Initialize(); void Initialize();
// Create a socket of the given type. Returns fd or -1. // Create a socket of the given type. Returns an IPC handle or -1.
int Create(int type, int pid); int Create(int type, int pid);
// Connect socket fd to remote ip:port. Returns 0 or -1. // Connect socket handle to remote ip:port. Returns 0 or -1.
int Connect(int fd, uint32_t ip, uint16_t port, int pid); int Connect(int fd, uint32_t ip, uint16_t port, int pid);
// Bind socket fd to a local port. Returns 0 or -1. // Bind socket handle to a local port. Returns 0 or -1.
int Bind(int fd, uint16_t port, int pid); int Bind(int fd, uint16_t port, int pid);
// Start listening on a bound socket. Returns 0 or -1. // Start listening on a bound socket. Returns 0 or -1.
int Listen(int fd, int pid); int Listen(int fd, int pid);
// Accept an incoming connection. Returns new fd or -1. // Accept an incoming connection. Returns a new IPC handle or -1.
int Accept(int fd, int pid); int Accept(int fd, int pid);
// Send data on a connected socket. Returns bytes sent or -1. // Send data on a connected socket. Returns bytes sent or -1.
@@ -59,7 +46,7 @@ namespace Net::Socket {
// Close a socket. // Close a socket.
void Close(int fd, int pid); void Close(int fd, int pid);
// Close all sockets owned by a process (called on process exit). // Per-process cleanup is handled by IPC handle teardown.
void CleanupProcess(int pid); void CleanupProcess(int pid);
} }
+95 -10
View File
@@ -8,11 +8,13 @@
#include <Net/Ipv4.hpp> #include <Net/Ipv4.hpp>
#include <Net/ByteOrder.hpp> #include <Net/ByteOrder.hpp>
#include <Net/NetConfig.hpp> #include <Net/NetConfig.hpp>
#include <Ipc/Ipc.hpp>
#include <Libraries/Memory.hpp> #include <Libraries/Memory.hpp>
#include <Terminal/Terminal.hpp> #include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp> #include <CppLib/Stream.hpp>
#include <CppLib/Spinlock.hpp> #include <CppLib/Spinlock.hpp>
#include <Timekeeping/ApicTimer.hpp> #include <Timekeeping/ApicTimer.hpp>
#include <Sched/Scheduler.hpp>
using namespace Kt; using namespace Kt;
@@ -216,6 +218,8 @@ namespace Net::Tcp {
listener->PendingRemotePort = srcPort; listener->PendingRemotePort = srcPort;
listener->PendingSeq = seqNum; listener->PendingSeq = seqNum;
listener->Lock.Release(); listener->Lock.Release();
Sched::WakeObjectWaiters(listener);
Ipc::NotifyTcpConnectionChanged(listener);
return; return;
} }
} }
@@ -235,12 +239,15 @@ namespace Net::Tcp {
} }
conn->Lock.Acquire(); conn->Lock.Acquire();
bool notify = false;
// RST handling // RST handling
if (flags & FLAG_RST) { if (flags & FLAG_RST) {
conn->CurrentState = State::Closed; conn->CurrentState = State::Closed;
conn->Active = false; conn->Active = false;
conn->Lock.Release(); conn->Lock.Release();
Sched::WakeObjectWaiters(conn);
Ipc::NotifyTcpConnectionChanged(conn);
return; return;
} }
@@ -255,6 +262,7 @@ namespace Net::Tcp {
// Send ACK // Send ACK
SendSegment(conn, FLAG_ACK, nullptr, 0); SendSegment(conn, FLAG_ACK, nullptr, 0);
notify = true;
} }
} }
break; break;
@@ -266,6 +274,7 @@ namespace Net::Tcp {
if (ackNum == conn->SendNext) { if (ackNum == conn->SendNext) {
conn->SendUnack = ackNum; conn->SendUnack = ackNum;
conn->CurrentState = State::Established; conn->CurrentState = State::Established;
notify = true;
} }
} }
break; break;
@@ -275,6 +284,7 @@ namespace Net::Tcp {
// Handle incoming data // Handle incoming data
if (flags & FLAG_ACK) { if (flags & FLAG_ACK) {
conn->SendUnack = ackNum; conn->SendUnack = ackNum;
notify = true;
} }
if (payloadLen > 0 && seqNum == conn->RecvNext) { if (payloadLen > 0 && seqNum == conn->RecvNext) {
@@ -283,6 +293,7 @@ namespace Net::Tcp {
// Send ACK // Send ACK
SendSegment(conn, FLAG_ACK, nullptr, 0); SendSegment(conn, FLAG_ACK, nullptr, 0);
notify = true;
} }
if (flags & FLAG_FIN) { if (flags & FLAG_FIN) {
@@ -291,6 +302,7 @@ namespace Net::Tcp {
// Send ACK for the FIN // Send ACK for the FIN
SendSegment(conn, FLAG_ACK, nullptr, 0); SendSegment(conn, FLAG_ACK, nullptr, 0);
notify = true;
} }
break; break;
} }
@@ -302,13 +314,16 @@ namespace Net::Tcp {
conn->RecvNext = seqNum + 1; conn->RecvNext = seqNum + 1;
conn->CurrentState = State::TimeWait; conn->CurrentState = State::TimeWait;
SendSegment(conn, FLAG_ACK, nullptr, 0); SendSegment(conn, FLAG_ACK, nullptr, 0);
notify = true;
} else { } else {
conn->CurrentState = State::FinWait2; conn->CurrentState = State::FinWait2;
notify = true;
} }
} else if (flags & FLAG_FIN) { } else if (flags & FLAG_FIN) {
conn->RecvNext = seqNum + 1; conn->RecvNext = seqNum + 1;
conn->CurrentState = State::TimeWait; conn->CurrentState = State::TimeWait;
SendSegment(conn, FLAG_ACK, nullptr, 0); SendSegment(conn, FLAG_ACK, nullptr, 0);
notify = true;
} }
break; break;
} }
@@ -318,6 +333,7 @@ namespace Net::Tcp {
conn->RecvNext = seqNum + 1; conn->RecvNext = seqNum + 1;
conn->CurrentState = State::TimeWait; conn->CurrentState = State::TimeWait;
SendSegment(conn, FLAG_ACK, nullptr, 0); SendSegment(conn, FLAG_ACK, nullptr, 0);
notify = true;
} }
break; break;
} }
@@ -326,6 +342,7 @@ namespace Net::Tcp {
if (flags & FLAG_ACK) { if (flags & FLAG_ACK) {
conn->CurrentState = State::Closed; conn->CurrentState = State::Closed;
conn->Active = false; conn->Active = false;
notify = true;
} }
break; break;
} }
@@ -340,6 +357,10 @@ namespace Net::Tcp {
} }
conn->Lock.Release(); conn->Lock.Release();
if (notify) {
Sched::WakeObjectWaiters(conn);
Ipc::NotifyTcpConnectionChanged(conn);
}
} }
Connection* Listen(uint16_t port) { Connection* Listen(uint16_t port) {
@@ -421,19 +442,25 @@ namespace Net::Tcp {
} }
// Wait for ACK to complete the handshake // Wait for ACK to complete the handshake
for (int i = 0; i < 100; i++) { uint64_t deadline = Timekeeping::GetMilliseconds() + 5000;
while (Timekeeping::GetMilliseconds() < deadline) {
if (conn->CurrentState == State::Established) { if (conn->CurrentState == State::Established) {
return conn; return conn;
} }
Timekeeping::Sleep(50); uint64_t now = Timekeeping::GetMilliseconds();
uint64_t waitMs = (deadline > now) ? (deadline - now) : 0;
if (waitMs == 0) break;
Sched::BlockOnObject(conn, waitMs);
} }
// Timed out waiting for ACK // Timed out waiting for ACK
conn->Active = false; conn->Active = false;
Sched::WakeObjectWaiters(conn);
Ipc::NotifyTcpConnectionChanged(conn);
return nullptr; return nullptr;
} }
listener->Lock.Release(); listener->Lock.Release();
Timekeeping::Sleep(10); Sched::BlockOnObject(listener, 0);
} }
} }
@@ -480,11 +507,15 @@ namespace Net::Tcp {
// Wait for SYN-ACK // Wait for SYN-ACK
for (int attempt = 0; attempt < MAX_RETRANSMITS; attempt++) { for (int attempt = 0; attempt < MAX_RETRANSMITS; attempt++) {
for (int i = 0; i < 20; i++) { uint64_t deadline = Timekeeping::GetMilliseconds() + 1000;
while (Timekeeping::GetMilliseconds() < deadline) {
if (conn->CurrentState == State::Established) { if (conn->CurrentState == State::Established) {
return conn; return conn;
} }
Timekeeping::Sleep(50); uint64_t now = Timekeeping::GetMilliseconds();
uint64_t waitMs = (deadline > now) ? (deadline - now) : 0;
if (waitMs == 0) break;
Sched::BlockOnObject(conn, waitMs);
} }
if (conn->CurrentState == State::SynSent) { if (conn->CurrentState == State::SynSent) {
@@ -512,6 +543,8 @@ namespace Net::Tcp {
// Failed to connect // Failed to connect
conn->Active = false; conn->Active = false;
Sched::WakeObjectWaiters(conn);
Ipc::NotifyTcpConnectionChanged(conn);
return nullptr; return nullptr;
} }
@@ -580,8 +613,15 @@ namespace Net::Tcp {
conn->RetransmitTime = Timekeeping::GetMilliseconds(); conn->RetransmitTime = Timekeeping::GetMilliseconds();
conn->Lock.Release(); conn->Lock.Release();
asm volatile("push %0; popfq" :: "r"(flags) : "memory"); asm volatile("push %0; popfq" :: "r"(flags) : "memory");
continue;
} }
Timekeeping::Sleep(10);
uint64_t waitMs = 10;
if (conn->RetransmitLen > 0 && now <= conn->RetransmitTime + RETRANSMIT_TIMEOUT_MS) {
waitMs = (conn->RetransmitTime + RETRANSMIT_TIMEOUT_MS) - now;
if (waitMs == 0) waitMs = 1;
}
Sched::BlockOnObject(conn, waitMs);
} }
return sent; return sent;
@@ -625,7 +665,7 @@ namespace Net::Tcp {
conn->Lock.Release(); conn->Lock.Release();
asm volatile("push %0; popfq" :: "r"(flags) : "memory"); asm volatile("push %0; popfq" :: "r"(flags) : "memory");
Timekeeping::Sleep(10); Sched::BlockOnObject(conn, 0);
} }
} }
@@ -691,9 +731,11 @@ namespace Net::Tcp {
conn->CurrentState == State::Closed) { conn->CurrentState == State::Closed) {
break; break;
} }
Timekeeping::Sleep(50); Sched::BlockOnObject(conn, 50);
} }
conn->Active = false; conn->Active = false;
Sched::WakeObjectWaiters(conn);
Ipc::NotifyTcpConnectionChanged(conn);
return; return;
} }
@@ -709,9 +751,11 @@ namespace Net::Tcp {
if (conn->CurrentState == State::Closed) { if (conn->CurrentState == State::Closed) {
break; break;
} }
Timekeeping::Sleep(50); Sched::BlockOnObject(conn, 50);
} }
conn->Active = false; conn->Active = false;
Sched::WakeObjectWaiters(conn);
Ipc::NotifyTcpConnectionChanged(conn);
return; return;
} }
@@ -721,6 +765,8 @@ namespace Net::Tcp {
conn->Active = false; conn->Active = false;
conn->Lock.Release(); conn->Lock.Release();
asm volatile("push %0; popfq" :: "r"(flags) : "memory"); asm volatile("push %0; popfq" :: "r"(flags) : "memory");
Sched::WakeObjectWaiters(conn);
Ipc::NotifyTcpConnectionChanged(conn);
return; return;
} }
@@ -728,6 +774,8 @@ namespace Net::Tcp {
conn->Lock.Release(); conn->Lock.Release();
asm volatile("push %0; popfq" :: "r"(flags) : "memory"); asm volatile("push %0; popfq" :: "r"(flags) : "memory");
conn->Active = false; conn->Active = false;
Sched::WakeObjectWaiters(conn);
Ipc::NotifyTcpConnectionChanged(conn);
return; return;
} }
} }
@@ -736,7 +784,44 @@ namespace Net::Tcp {
if (conn == nullptr) { if (conn == nullptr) {
return State::Closed; return State::Closed;
} }
return conn->CurrentState; conn->Lock.Acquire();
State state = conn->CurrentState;
conn->Lock.Release();
return state;
}
bool HasPendingAccept(Connection* conn) {
if (conn == nullptr) return false;
conn->Lock.Acquire();
bool pending = conn->PendingAccept;
conn->Lock.Release();
return pending;
}
bool HasReceiveData(Connection* conn) {
if (conn == nullptr) return false;
conn->Lock.Acquire();
bool hasData = conn->RecvCount > 0;
conn->Lock.Release();
return hasData;
}
bool CanSend(Connection* conn) {
if (conn == nullptr) return false;
conn->Lock.Acquire();
bool writable = conn->CurrentState == State::Established;
conn->Lock.Release();
return writable;
}
bool IsClosedForIo(Connection* conn) {
if (conn == nullptr) return true;
conn->Lock.Acquire();
bool closed = conn->CurrentState == State::CloseWait ||
conn->CurrentState == State::Closed ||
conn->CurrentState == State::TimeWait;
conn->Lock.Release();
return closed;
} }
} }
+6
View File
@@ -80,4 +80,10 @@ namespace Net::Tcp {
// Get the state of a connection // Get the state of a connection
State GetState(Connection* conn); State GetState(Connection* conn);
// Readiness helpers used by the IPC layer.
bool HasPendingAccept(Connection* conn);
bool HasReceiveData(Connection* conn);
bool CanSend(Connection* conn);
bool IsClosedForIo(Connection* conn);
} }
+7 -7
View File
@@ -52,15 +52,15 @@ namespace Sched {
} }
uint64_t ElfLoad(const char* vfsPath, uint64_t pml4Phys) { uint64_t ElfLoad(const char* vfsPath, uint64_t pml4Phys) {
int handle = Fs::Vfs::VfsOpen(vfsPath); Fs::Vfs::BackendFile file = {-1, -1};
if (handle < 0) { if (Fs::Vfs::OpenBackendFile(vfsPath, file) < 0) {
return 0; return 0;
} }
uint64_t fileSize = Fs::Vfs::VfsGetSize(handle); uint64_t fileSize = Fs::Vfs::GetBackendFileSize(file);
if (fileSize < sizeof(Elf64Header)) { if (fileSize < sizeof(Elf64Header)) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "File too small (" << fileSize << " bytes)"; Kt::KernelLogStream(Kt::ERROR, "ELF") << "File too small (" << fileSize << " bytes)";
Fs::Vfs::VfsClose(handle); Fs::Vfs::CloseBackendFile(file);
return 0; return 0;
} }
@@ -68,12 +68,12 @@ namespace Sched {
uint8_t* fileData = (uint8_t*)Memory::g_heap->Request(fileSize); uint8_t* fileData = (uint8_t*)Memory::g_heap->Request(fileSize);
if (fileData == nullptr) { if (fileData == nullptr) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to allocate " << fileSize << " bytes for file"; Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to allocate " << fileSize << " bytes for file";
Fs::Vfs::VfsClose(handle); Fs::Vfs::CloseBackendFile(file);
return 0; return 0;
} }
Fs::Vfs::VfsRead(handle, fileData, 0, fileSize); Fs::Vfs::ReadBackendFile(file, fileData, 0, fileSize);
Fs::Vfs::VfsClose(handle); Fs::Vfs::CloseBackendFile(file);
// Prevent the optimizer from reordering the VfsRead store past the // Prevent the optimizer from reordering the VfsRead store past the
// header validation reads that follow. // header validation reads that follow.
+137 -71
View File
@@ -18,6 +18,7 @@
#include <Hal/SmpBoot.hpp> #include <Hal/SmpBoot.hpp>
#include <Timekeeping/ApicTimer.hpp> #include <Timekeeping/ApicTimer.hpp>
#include <Api/WinServer.hpp> #include <Api/WinServer.hpp>
#include <Ipc/Ipc.hpp>
// Assembly: context switch with CR3 and FPU state parameters // Assembly: context switch with CR3 and FPU state parameters
extern "C" void SchedContextSwitch(uint64_t* oldRsp, uint64_t newRsp, uint64_t newCR3, extern "C" void SchedContextSwitch(uint64_t* oldRsp, uint64_t newRsp, uint64_t newCR3,
@@ -47,6 +48,45 @@ namespace Sched {
return (uint64_t)Memory::VMM::g_paging->PML4; return (uint64_t)Memory::VMM::g_paging->PML4;
} }
static void SwitchAwayFromBlockedCurrentLocked() {
auto* cpu = Smp::GetCurrentCpuData();
int slot = cpu->currentSlot;
if (slot < 0) {
schedLock.Release();
return;
}
int next = -1;
for (int i = 0; i < MaxProcesses; i++) {
if (processTable[i].state == ProcessState::Ready) {
next = i;
break;
}
}
if (next >= 0) {
cpu->currentSlot = next;
processTable[next].state = ProcessState::Running;
readyCount--;
processTable[next].runningOnCpu = cpu->cpuIndex;
processTable[next].sliceRemaining = TimeSliceMs;
cpu->kernelRsp = processTable[next].kernelStackTop;
cpu->tss->rsp0 = processTable[next].kernelStackTop;
SchedContextSwitch(&processTable[slot].savedRsp, processTable[next].savedRsp,
processTable[next].pml4Phys,
processTable[slot].fpuState, processTable[next].fpuState);
schedLock.Release();
return;
}
cpu->currentSlot = -1;
SchedContextSwitch(&processTable[slot].savedRsp, cpu->idleSavedRsp,
GetKernelCR3(), processTable[slot].fpuState, nullptr);
schedLock.Release();
}
// Startup function for newly spawned processes. // Startup function for newly spawned processes.
// SchedContextSwitch "returns" here on first schedule. // SchedContextSwitch "returns" here on first schedule.
// The schedLock is held (acquired by the switching-from CPU's Schedule). // The schedLock is held (acquired by the switching-from CPU's Schedule).
@@ -97,6 +137,7 @@ namespace Sched {
processTable[i].killPending = false; processTable[i].killPending = false;
processTable[i].waitingForPid = -1; processTable[i].waitingForPid = -1;
processTable[i].sleepUntilTick = 0; processTable[i].sleepUntilTick = 0;
processTable[i].waitingOnObject = nullptr;
processTable[i].redirected = false; processTable[i].redirected = false;
processTable[i].parentPid = -1; processTable[i].parentPid = -1;
processTable[i].outBuf = nullptr; processTable[i].outBuf = nullptr;
@@ -109,6 +150,10 @@ namespace Sched {
processTable[i].keyTail = 0; processTable[i].keyTail = 0;
processTable[i].termCols = 0; processTable[i].termCols = 0;
processTable[i].termRows = 0; processTable[i].termRows = 0;
processTable[i].ioOutHandle = -1;
processTable[i].ioInHandle = -1;
processTable[i].ioKeyHandle = -1;
processTable[i].ioWaitsetHandle = -1;
} }
nextPid = 0; nextPid = 0;
@@ -276,6 +321,7 @@ namespace Sched {
proc.killPending = false; proc.killPending = false;
proc.waitingForPid = -1; proc.waitingForPid = -1;
proc.sleepUntilTick = 0; proc.sleepUntilTick = 0;
proc.waitingOnObject = nullptr;
// Copy arguments string into process // Copy arguments string into process
proc.args[0] = '\0'; proc.args[0] = '\0';
@@ -333,12 +379,18 @@ namespace Sched {
proc.keyTail = 0; proc.keyTail = 0;
proc.termCols = 0; proc.termCols = 0;
proc.termRows = 0; proc.termRows = 0;
proc.ioOutHandle = -1;
proc.ioInHandle = -1;
proc.ioKeyHandle = -1;
proc.ioWaitsetHandle = -1;
// Initialize FPU state: zero out, then set default FCW and MXCSR // Initialize FPU state: zero out, then set default FCW and MXCSR
memset(proc.fpuState, 0, 512); memset(proc.fpuState, 0, 512);
*(uint16_t*)&proc.fpuState[0] = 0x037F; // FCW: default x87 control word *(uint16_t*)&proc.fpuState[0] = 0x037F; // FCW: default x87 control word
*(uint32_t*)&proc.fpuState[24] = 0x1F80; // MXCSR: default SSE control/status *(uint32_t*)&proc.fpuState[24] = 0x1F80; // MXCSR: default SSE control/status
Ipc::ProcessStartedInSlot(slot, proc.pid);
int resultPid = proc.pid; int resultPid = proc.pid;
schedLock.Release(); schedLock.Release();
@@ -486,6 +538,8 @@ namespace Sched {
processTable[i].sleepUntilTick != 0 && processTable[i].sleepUntilTick != 0 &&
now >= processTable[i].sleepUntilTick) { now >= processTable[i].sleepUntilTick) {
processTable[i].sleepUntilTick = 0; processTable[i].sleepUntilTick = 0;
processTable[i].waitingForPid = -1;
processTable[i].waitingOnObject = nullptr;
processTable[i].state = ProcessState::Ready; processTable[i].state = ProcessState::Ready;
readyCount++; readyCount++;
} }
@@ -554,15 +608,30 @@ namespace Sched {
// Clean up any windows owned by this process // Clean up any windows owned by this process
WinServer::CleanupProcess(exitingPid); WinServer::CleanupProcess(exitingPid);
// Free I/O redirect buffers // Release process-scoped IPC handles/mappings before tearing down the address space.
if (proc.outBuf) { Ipc::CleanupProcessSlot(slot, exitingPid, proc.pml4Phys);
Memory::g_pfa->Free(proc.outBuf);
proc.waitingForPid = -1;
proc.sleepUntilTick = 0;
proc.waitingOnObject = nullptr;
proc.redirected = false;
proc.parentPid = -1;
proc.outBuf = nullptr; proc.outBuf = nullptr;
} proc.outHead = 0;
if (proc.inBuf) { proc.outTail = 0;
Memory::g_pfa->Free(proc.inBuf);
proc.inBuf = nullptr; proc.inBuf = nullptr;
} proc.inHead = 0;
proc.inTail = 0;
proc.keyHead = 0;
proc.keyTail = 0;
proc.termCols = 0;
proc.termRows = 0;
proc.ioOutHandle = -1;
proc.ioInHandle = -1;
proc.ioKeyHandle = -1;
proc.ioWaitsetHandle = -1;
Ipc::ProcessExitedInSlot(slot, exitingPid);
// Free all user-space physical pages and page table structures // Free all user-space physical pages and page table structures
Memory::VMM::Paging::FreeUserHalf(proc.pml4Phys); Memory::VMM::Paging::FreeUserHalf(proc.pml4Phys);
@@ -579,6 +648,8 @@ namespace Sched {
processTable[i].state = ProcessState::Ready; processTable[i].state = ProcessState::Ready;
readyCount++; readyCount++;
processTable[i].waitingForPid = -1; processTable[i].waitingForPid = -1;
processTable[i].waitingOnObject = nullptr;
processTable[i].sleepUntilTick = 0;
} }
} }
@@ -664,6 +735,9 @@ namespace Sched {
readyCount--; readyCount--;
proc.state = ProcessState::Terminated; proc.state = ProcessState::Terminated;
proc.killPending = false; proc.killPending = false;
proc.waitingForPid = -1;
proc.sleepUntilTick = 0;
proc.waitingOnObject = nullptr;
// Wake any processes blocked on this PID // Wake any processes blocked on this PID
for (int i = 0; i < MaxProcesses; i++) { for (int i = 0; i < MaxProcesses; i++) {
@@ -672,6 +746,8 @@ namespace Sched {
processTable[i].state = ProcessState::Ready; processTable[i].state = ProcessState::Ready;
readyCount++; readyCount++;
processTable[i].waitingForPid = -1; processTable[i].waitingForPid = -1;
processTable[i].waitingOnObject = nullptr;
processTable[i].sleepUntilTick = 0;
} }
} }
@@ -679,15 +755,26 @@ namespace Sched {
// Safe to clean up resources now -- process is not running anywhere. // Safe to clean up resources now -- process is not running anywhere.
WinServer::CleanupProcess(killedPid); WinServer::CleanupProcess(killedPid);
Ipc::CleanupProcessSlot(slot, killedPid, proc.pml4Phys);
if (proc.outBuf) { proc.redirected = false;
Memory::g_pfa->Free(proc.outBuf); proc.parentPid = -1;
proc.outBuf = nullptr; proc.outBuf = nullptr;
} proc.outHead = 0;
if (proc.inBuf) { proc.outTail = 0;
Memory::g_pfa->Free(proc.inBuf);
proc.inBuf = nullptr; proc.inBuf = nullptr;
} proc.inHead = 0;
proc.inTail = 0;
proc.keyHead = 0;
proc.keyTail = 0;
proc.termCols = 0;
proc.termRows = 0;
proc.ioOutHandle = -1;
proc.ioInHandle = -1;
proc.ioKeyHandle = -1;
proc.ioWaitsetHandle = -1;
Ipc::ProcessExitedInSlot(slot, killedPid);
Memory::VMM::Paging::FreeUserHalf(proc.pml4Phys); Memory::VMM::Paging::FreeUserHalf(proc.pml4Phys);
@@ -727,39 +814,10 @@ namespace Sched {
// ExitProcess will wake us when the target terminates. // ExitProcess will wake us when the target terminates.
processTable[slot].state = ProcessState::Blocked; processTable[slot].state = ProcessState::Blocked;
processTable[slot].waitingForPid = pid; processTable[slot].waitingForPid = pid;
processTable[slot].waitingOnObject = nullptr;
processTable[slot].sleepUntilTick = 0;
processTable[slot].runningOnCpu = -1; processTable[slot].runningOnCpu = -1;
SwitchAwayFromBlockedCurrentLocked();
// Find next ready process to switch to
int next = -1;
for (int i = 0; i < MaxProcesses; i++) {
if (processTable[i].state == ProcessState::Ready) {
next = i;
break;
}
}
if (next >= 0) {
cpu->currentSlot = next;
processTable[next].state = ProcessState::Running;
readyCount--;
processTable[next].runningOnCpu = cpu->cpuIndex;
processTable[next].sliceRemaining = TimeSliceMs;
cpu->kernelRsp = processTable[next].kernelStackTop;
cpu->tss->rsp0 = processTable[next].kernelStackTop;
SchedContextSwitch(&processTable[slot].savedRsp, processTable[next].savedRsp,
processTable[next].pml4Phys,
processTable[slot].fpuState, processTable[next].fpuState);
schedLock.Release();
} else {
// No ready process -- go idle
cpu->currentSlot = -1;
SchedContextSwitch(&processTable[slot].savedRsp, cpu->idleSavedRsp,
GetKernelCR3(), processTable[slot].fpuState, nullptr);
schedLock.Release();
}
} }
void BlockForSleep(uint64_t ms) { void BlockForSleep(uint64_t ms) {
@@ -772,38 +830,46 @@ namespace Sched {
schedLock.Acquire(); schedLock.Acquire();
processTable[slot].state = ProcessState::Blocked; processTable[slot].state = ProcessState::Blocked;
processTable[slot].waitingForPid = -1;
processTable[slot].waitingOnObject = nullptr;
processTable[slot].sleepUntilTick = Timekeeping::GetTicks() + ms; processTable[slot].sleepUntilTick = Timekeeping::GetTicks() + ms;
processTable[slot].runningOnCpu = -1; processTable[slot].runningOnCpu = -1;
SwitchAwayFromBlockedCurrentLocked();
}
int next = -1; void BlockOnObject(void* object, uint64_t timeoutMs) {
if (object == nullptr) return;
auto* cpu = Smp::GetCurrentCpuData();
int slot = cpu->currentSlot;
if (slot < 0) return;
schedLock.Acquire();
processTable[slot].state = ProcessState::Blocked;
processTable[slot].waitingForPid = -1;
processTable[slot].waitingOnObject = object;
processTable[slot].sleepUntilTick = (timeoutMs > 0)
? (Timekeeping::GetTicks() + timeoutMs)
: 0;
processTable[slot].runningOnCpu = -1;
SwitchAwayFromBlockedCurrentLocked();
}
void WakeObjectWaiters(void* object) {
if (object == nullptr) return;
schedLock.Acquire();
for (int i = 0; i < MaxProcesses; i++) { for (int i = 0; i < MaxProcesses; i++) {
if (processTable[i].state == ProcessState::Ready) { if (processTable[i].state != ProcessState::Blocked) continue;
next = i; if (processTable[i].waitingOnObject != object) continue;
break;
processTable[i].waitingOnObject = nullptr;
processTable[i].sleepUntilTick = 0;
processTable[i].waitingForPid = -1;
processTable[i].state = ProcessState::Ready;
readyCount++;
} }
}
if (next >= 0) {
cpu->currentSlot = next;
processTable[next].state = ProcessState::Running;
readyCount--;
processTable[next].runningOnCpu = cpu->cpuIndex;
processTable[next].sliceRemaining = TimeSliceMs;
cpu->kernelRsp = processTable[next].kernelStackTop;
cpu->tss->rsp0 = processTable[next].kernelStackTop;
SchedContextSwitch(&processTable[slot].savedRsp, processTable[next].savedRsp,
processTable[next].pml4Phys,
processTable[slot].fpuState, processTable[next].fpuState);
schedLock.Release(); schedLock.Release();
} else {
cpu->currentSlot = -1;
SchedContextSwitch(&processTable[slot].savedRsp, cpu->idleSavedRsp,
GetKernelCR3(), processTable[slot].fpuState, nullptr);
schedLock.Release();
}
} }
bool IsAlive(int pid) { bool IsAlive(int pid) {
+15 -1
View File
@@ -35,7 +35,8 @@ namespace Sched {
int pid; int pid;
ProcessState state; ProcessState state;
int waitingForPid; // PID this process is blocked on (-1 if none) int waitingForPid; // PID this process is blocked on (-1 if none)
uint64_t sleepUntilTick; // Tick deadline for SYS_SLEEP_MS (0 = not sleeping) uint64_t sleepUntilTick; // Tick deadline for sleep/object wait timeout (0 = none)
void* waitingOnObject; // IPC/scheduler object this process is blocked on (nullptr if none)
char name[64]; char name[64];
uint64_t savedRsp; uint64_t savedRsp;
uint64_t stackBase; // Bottom of allocated kernel stack (lowest address) uint64_t stackBase; // Bottom of allocated kernel stack (lowest address)
@@ -71,6 +72,12 @@ namespace Sched {
int termCols = 0; int termCols = 0;
int termRows = 0; int termRows = 0;
// IPC-backed redirected terminal channels
int ioOutHandle = -1;
int ioInHandle = -1;
int ioKeyHandle = -1;
int ioWaitsetHandle = -1;
// FPU/SSE state (FXSAVE format, must be 16-byte aligned) // FPU/SSE state (FXSAVE format, must be 16-byte aligned)
uint8_t fpuState[512] __attribute__((aligned(16))); uint8_t fpuState[512] __attribute__((aligned(16)));
}; };
@@ -100,6 +107,13 @@ namespace Sched {
// Block the current process for the given number of milliseconds. // Block the current process for the given number of milliseconds.
void BlockForSleep(uint64_t ms); void BlockForSleep(uint64_t ms);
// Block the current process until the given object is signaled or timed out.
// timeoutMs == 0 means wait indefinitely.
void BlockOnObject(void* object, uint64_t timeoutMs = 0);
// Wake any processes blocked on the given object.
void WakeObjectWaiters(void* object);
// Kill a process by PID. If the process is running on another CPU, // Kill a process by PID. If the process is running on another CPU,
// sets a kill-pending flag checked on the next timer tick. // sets a kill-pending flag checked on the next timer tick.
// Returns 0 on success, -1 on failure. // Returns 0 on success, -1 on failure.
+28
View File
@@ -76,6 +76,7 @@ namespace Montauk {
static constexpr uint64_t SYS_WINPOLL = 57; static constexpr uint64_t SYS_WINPOLL = 57;
static constexpr uint64_t SYS_WINENUM = 58; static constexpr uint64_t SYS_WINENUM = 58;
static constexpr uint64_t SYS_WINMAP = 59; static constexpr uint64_t SYS_WINMAP = 59;
static constexpr uint64_t SYS_WINUNMAP = 97;
static constexpr uint64_t SYS_WINSENDEVENT = 60; static constexpr uint64_t SYS_WINSENDEVENT = 60;
static constexpr uint64_t SYS_WINRESIZE = 64; static constexpr uint64_t SYS_WINRESIZE = 64;
static constexpr uint64_t SYS_WINSETSCALE = 65; static constexpr uint64_t SYS_WINSETSCALE = 65;
@@ -126,6 +127,28 @@ namespace Montauk {
static constexpr uint64_t SYS_FRENAME = 94; static constexpr uint64_t SYS_FRENAME = 94;
static constexpr uint64_t SYS_GETCWD = 95; static constexpr uint64_t SYS_GETCWD = 95;
static constexpr uint64_t SYS_CHDIR = 96; static constexpr uint64_t SYS_CHDIR = 96;
static constexpr uint64_t SYS_DUPHANDLE = 98;
static constexpr uint64_t SYS_WAIT_HANDLE = 99;
static constexpr uint64_t SYS_STREAM_CREATE = 100;
static constexpr uint64_t SYS_STREAM_READ = 101;
static constexpr uint64_t SYS_STREAM_WRITE = 102;
static constexpr uint64_t SYS_MAILBOX_CREATE = 103;
static constexpr uint64_t SYS_MAILBOX_SEND = 104;
static constexpr uint64_t SYS_MAILBOX_RECV = 105;
static constexpr uint64_t SYS_WAITSET_CREATE = 106;
static constexpr uint64_t SYS_WAITSET_ADD = 107;
static constexpr uint64_t SYS_WAITSET_REMOVE = 108;
static constexpr uint64_t SYS_WAITSET_WAIT = 109;
static constexpr uint64_t SYS_PROC_OPEN = 110;
static constexpr uint64_t SYS_SURFACE_CREATE = 111;
static constexpr uint64_t SYS_SURFACE_MAP = 112;
static constexpr uint64_t SYS_SURFACE_RESIZE = 113;
static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0;
static constexpr uint32_t IPC_SIGNAL_WRITABLE = 1u << 1;
static constexpr uint32_t IPC_SIGNAL_PEER_CLOSED = 1u << 2;
static constexpr uint32_t IPC_SIGNAL_EXITED = 1u << 3;
static constexpr uint32_t IPC_SIGNAL_READY = 1u << 4;
// Audio control commands (for SYS_AUDIOCTL) // Audio control commands (for SYS_AUDIOCTL)
static constexpr int AUDIO_CTL_SET_VOLUME = 0; static constexpr int AUDIO_CTL_SET_VOLUME = 0;
@@ -188,6 +211,11 @@ namespace Montauk {
uint8_t buttons; uint8_t buttons;
}; };
struct IpcWaitResult {
int32_t index;
uint32_t signals;
};
// Window server shared types // Window server shared types
struct WinEvent { struct WinEvent {
uint8_t type; // 0=key, 1=mouse, 2=resize, 3=close, 4=scale uint8_t type; // 0=key, 1=mouse, 2=resize, 3=close, 4=scale
+107
View File
@@ -79,6 +79,7 @@ extern "C" {
#define MTK_SYS_WINPOLL 57 #define MTK_SYS_WINPOLL 57
#define MTK_SYS_WINENUM 58 #define MTK_SYS_WINENUM 58
#define MTK_SYS_WINMAP 59 #define MTK_SYS_WINMAP 59
#define MTK_SYS_WINUNMAP 97
#define MTK_SYS_WINSENDEVENT 60 #define MTK_SYS_WINSENDEVENT 60
#define MTK_SYS_PROCLIST 61 #define MTK_SYS_PROCLIST 61
#define MTK_SYS_KILL 62 #define MTK_SYS_KILL 62
@@ -99,9 +100,30 @@ extern "C" {
#define MTK_SYS_GETTZ 91 #define MTK_SYS_GETTZ 91
#define MTK_SYS_GETCWD 95 #define MTK_SYS_GETCWD 95
#define MTK_SYS_CHDIR 96 #define MTK_SYS_CHDIR 96
#define MTK_SYS_DUPHANDLE 98
#define MTK_SYS_WAIT_HANDLE 99
#define MTK_SYS_STREAM_CREATE 100
#define MTK_SYS_STREAM_READ 101
#define MTK_SYS_STREAM_WRITE 102
#define MTK_SYS_MAILBOX_CREATE 103
#define MTK_SYS_MAILBOX_SEND 104
#define MTK_SYS_MAILBOX_RECV 105
#define MTK_SYS_WAITSET_CREATE 106
#define MTK_SYS_WAITSET_ADD 107
#define MTK_SYS_WAITSET_REMOVE 108
#define MTK_SYS_WAITSET_WAIT 109
#define MTK_SYS_PROC_OPEN 110
#define MTK_SYS_SURFACE_CREATE 111
#define MTK_SYS_SURFACE_MAP 112
#define MTK_SYS_SURFACE_RESIZE 113
#define MTK_SOCK_TCP 1 #define MTK_SOCK_TCP 1
#define MTK_SOCK_UDP 2 #define MTK_SOCK_UDP 2
#define MTK_IPC_SIGNAL_READABLE (1u << 0)
#define MTK_IPC_SIGNAL_WRITABLE (1u << 1)
#define MTK_IPC_SIGNAL_PEER_CLOSED (1u << 2)
#define MTK_IPC_SIGNAL_EXITED (1u << 3)
#define MTK_IPC_SIGNAL_READY (1u << 4)
/* Window event types */ /* Window event types */
#define MTK_EVENT_KEY 0 #define MTK_EVENT_KEY 0
@@ -136,6 +158,11 @@ typedef struct {
uint8_t buttons; uint8_t buttons;
} mtk_mouse_state; } mtk_mouse_state;
typedef struct {
int32_t index;
uint32_t signals;
} mtk_ipc_wait_result;
typedef struct { typedef struct {
uint8_t type; uint8_t type;
uint8_t _pad[3]; uint8_t _pad[3];
@@ -396,6 +423,78 @@ static inline int mtk_readdir(const char *path, const char **names, int max) {
return (int)_mtk_syscall3(MTK_SYS_READDIR, (long)path, (long)names, (long)max); return (int)_mtk_syscall3(MTK_SYS_READDIR, (long)path, (long)names, (long)max);
} }
/* ====================================================================
Generic IPC
==================================================================== */
static inline int mtk_dup_handle(int handle) {
return (int)_mtk_syscall1(MTK_SYS_DUPHANDLE, (long)handle);
}
static inline uint32_t mtk_wait_handle(int handle, uint32_t wanted_signals, unsigned long timeout_ms) {
return (uint32_t)_mtk_syscall3(MTK_SYS_WAIT_HANDLE, (long)handle, (long)wanted_signals, (long)timeout_ms);
}
static inline int mtk_stream_create(int *out_read_handle, int *out_write_handle, unsigned long capacity) {
return (int)_mtk_syscall3(MTK_SYS_STREAM_CREATE, (long)out_read_handle, (long)out_write_handle, (long)capacity);
}
static inline int mtk_stream_read(int handle, void *buf, int max_len) {
return (int)_mtk_syscall3(MTK_SYS_STREAM_READ, (long)handle, (long)buf, (long)max_len);
}
static inline int mtk_stream_write(int handle, const void *data, int len) {
return (int)_mtk_syscall3(MTK_SYS_STREAM_WRITE, (long)handle, (long)data, (long)len);
}
static inline int mtk_mailbox_create(int *out_send_handle, int *out_recv_handle) {
return (int)_mtk_syscall2(MTK_SYS_MAILBOX_CREATE, (long)out_send_handle, (long)out_recv_handle);
}
static inline int mtk_mailbox_send(int handle, uint32_t msg_type, const void *data,
uint16_t len, int attach_handle) {
return (int)_mtk_syscall5(MTK_SYS_MAILBOX_SEND, (long)handle, (long)msg_type,
(long)data, (long)len, (long)attach_handle);
}
static inline int mtk_mailbox_recv(int handle, uint32_t *out_msg_type, void *data,
uint16_t *in_out_len, int *out_attach_handle) {
return (int)_mtk_syscall5(MTK_SYS_MAILBOX_RECV, (long)handle, (long)out_msg_type,
(long)data, (long)in_out_len, (long)out_attach_handle);
}
static inline int mtk_waitset_create(void) {
return (int)_mtk_syscall0(MTK_SYS_WAITSET_CREATE);
}
static inline int mtk_waitset_add(int waitset_handle, int target_handle, uint32_t signals) {
return (int)_mtk_syscall3(MTK_SYS_WAITSET_ADD, (long)waitset_handle, (long)target_handle, (long)signals);
}
static inline int mtk_waitset_remove(int waitset_handle, int index) {
return (int)_mtk_syscall2(MTK_SYS_WAITSET_REMOVE, (long)waitset_handle, (long)index);
}
static inline int mtk_waitset_wait(int waitset_handle, mtk_ipc_wait_result *out_ready, unsigned long timeout_ms) {
return (int)_mtk_syscall3(MTK_SYS_WAITSET_WAIT, (long)waitset_handle, (long)out_ready, (long)timeout_ms);
}
static inline int mtk_proc_open(int pid) {
return (int)_mtk_syscall1(MTK_SYS_PROC_OPEN, (long)pid);
}
static inline int mtk_surface_create(unsigned long byte_size) {
return (int)_mtk_syscall1(MTK_SYS_SURFACE_CREATE, (long)byte_size);
}
static inline void *mtk_surface_map(int handle) {
return (void *)_mtk_syscall1(MTK_SYS_SURFACE_MAP, (long)handle);
}
static inline int mtk_surface_resize(int handle, unsigned long new_size) {
return (int)_mtk_syscall2(MTK_SYS_SURFACE_RESIZE, (long)handle, (long)new_size);
}
/* ==================================================================== /* ====================================================================
Memory Memory
==================================================================== */ ==================================================================== */
@@ -478,6 +577,10 @@ static inline int mtk_win_poll(int id, mtk_win_event *event) {
return (int)_mtk_syscall2(MTK_SYS_WINPOLL, (long)id, (long)event); return (int)_mtk_syscall2(MTK_SYS_WINPOLL, (long)id, (long)event);
} }
static inline int mtk_win_unmap(int id) {
return (int)_mtk_syscall1(MTK_SYS_WINUNMAP, (long)id);
}
static inline unsigned long mtk_win_resize(int id, int w, int h) { static inline unsigned long mtk_win_resize(int id, int w, int h) {
return (unsigned long)_mtk_syscall3(MTK_SYS_WINRESIZE, (long)id, (long)w, (long)h); return (unsigned long)_mtk_syscall3(MTK_SYS_WINRESIZE, (long)id, (long)w, (long)h);
} }
@@ -486,6 +589,10 @@ static inline int mtk_win_enumerate(mtk_win_info *info, int max) {
return (int)_mtk_syscall2(MTK_SYS_WINENUM, (long)info, (long)max); return (int)_mtk_syscall2(MTK_SYS_WINENUM, (long)info, (long)max);
} }
static inline unsigned long mtk_win_map(int id) {
return (unsigned long)_mtk_syscall1(MTK_SYS_WINMAP, (long)id);
}
static inline int mtk_win_setcursor(int id, int cursor) { static inline int mtk_win_setcursor(int id, int cursor) {
return (int)_mtk_syscall2(MTK_SYS_WINSETCURSOR, (long)id, (long)cursor); return (int)_mtk_syscall2(MTK_SYS_WINSETCURSOR, (long)id, (long)cursor);
} }
+59
View File
@@ -223,6 +223,62 @@ namespace montauk {
(uint64_t)maxLen, (uint64_t)srcIp, (uint64_t)srcPort); (uint64_t)maxLen, (uint64_t)srcIp, (uint64_t)srcPort);
} }
// Generic IPC
inline int dup_handle(int handle) {
return (int)syscall1(Montauk::SYS_DUPHANDLE, (uint64_t)handle);
}
inline uint32_t wait_handle(int handle, uint32_t wantedSignals, uint64_t timeoutMs = ~0ULL) {
return (uint32_t)syscall3(Montauk::SYS_WAIT_HANDLE, (uint64_t)handle,
(uint64_t)wantedSignals, timeoutMs);
}
inline int stream_create(int* outReadHandle, int* outWriteHandle, uint32_t capacity = 0) {
return (int)syscall3(Montauk::SYS_STREAM_CREATE, (uint64_t)outReadHandle,
(uint64_t)outWriteHandle, (uint64_t)capacity);
}
inline int stream_read(int handle, void* buf, int maxLen) {
return (int)syscall3(Montauk::SYS_STREAM_READ, (uint64_t)handle, (uint64_t)buf, (uint64_t)maxLen);
}
inline int stream_write(int handle, const void* data, int len) {
return (int)syscall3(Montauk::SYS_STREAM_WRITE, (uint64_t)handle, (uint64_t)data, (uint64_t)len);
}
inline int mailbox_create(int* outSendHandle, int* outRecvHandle) {
return (int)syscall2(Montauk::SYS_MAILBOX_CREATE, (uint64_t)outSendHandle, (uint64_t)outRecvHandle);
}
inline int mailbox_send(int handle, uint32_t msgType, const void* data, uint16_t len, int attachHandle = -1) {
return (int)syscall5(Montauk::SYS_MAILBOX_SEND, (uint64_t)handle, (uint64_t)msgType,
(uint64_t)data, (uint64_t)len, (uint64_t)(int64_t)attachHandle);
}
inline int mailbox_recv(int handle, uint32_t* outMsgType, void* data,
uint16_t* inOutLen, int* outAttachHandle = nullptr) {
return (int)syscall5(Montauk::SYS_MAILBOX_RECV, (uint64_t)handle, (uint64_t)outMsgType,
(uint64_t)data, (uint64_t)inOutLen, (uint64_t)outAttachHandle);
}
inline int waitset_create() {
return (int)syscall0(Montauk::SYS_WAITSET_CREATE);
}
inline int waitset_add(int waitsetHandle, int targetHandle, uint32_t signals) {
return (int)syscall3(Montauk::SYS_WAITSET_ADD, (uint64_t)waitsetHandle,
(uint64_t)targetHandle, (uint64_t)signals);
}
inline int waitset_remove(int waitsetHandle, int index) {
return (int)syscall2(Montauk::SYS_WAITSET_REMOVE, (uint64_t)waitsetHandle, (uint64_t)index);
}
inline int waitset_wait(int waitsetHandle, Montauk::IpcWaitResult* outReady, uint64_t timeoutMs = ~0ULL) {
return (int)syscall3(Montauk::SYS_WAITSET_WAIT, (uint64_t)waitsetHandle, (uint64_t)outReady, timeoutMs);
}
inline int proc_open(int pid) {
return (int)syscall1(Montauk::SYS_PROC_OPEN, (uint64_t)pid);
}
inline int surface_create(uint64_t byteSize) {
return (int)syscall1(Montauk::SYS_SURFACE_CREATE, byteSize);
}
inline void* surface_map(int handle) {
return (void*)syscall1(Montauk::SYS_SURFACE_MAP, (uint64_t)handle);
}
inline int surface_resize(int handle, uint64_t newSize) {
return (int)syscall2(Montauk::SYS_SURFACE_RESIZE, (uint64_t)handle, newSize);
}
// Process management // Process management
inline void waitpid(int pid) { syscall1(Montauk::SYS_WAITPID, (uint64_t)pid); } inline void waitpid(int pid) { syscall1(Montauk::SYS_WAITPID, (uint64_t)pid); }
@@ -435,6 +491,9 @@ namespace montauk {
inline uint64_t win_map(int id) { inline uint64_t win_map(int id) {
return (uint64_t)syscall1(Montauk::SYS_WINMAP, (uint64_t)id); return (uint64_t)syscall1(Montauk::SYS_WINMAP, (uint64_t)id);
} }
inline int win_unmap(int id) {
return (int)syscall1(Montauk::SYS_WINUNMAP, (uint64_t)id);
}
inline int win_sendevent(int id, const Montauk::WinEvent* event) { inline int win_sendevent(int id, const Montauk::WinEvent* event) {
return (int)syscall2(Montauk::SYS_WINSENDEVENT, (uint64_t)id, (uint64_t)event); return (int)syscall2(Montauk::SYS_WINSENDEVENT, (uint64_t)id, (uint64_t)event);
} }
+19 -5
View File
@@ -390,11 +390,14 @@ bool desktop_poll_external_windows(DesktopState* ds) {
for (int i = 0; i < ds->window_count; i++) { for (int i = 0; i < ds->window_count; i++) {
if (ds->windows[i].external && ds->windows[i].ext_win_id == extId) { if (ds->windows[i].external && ds->windows[i].ext_win_id == extId) {
found = true; found = true;
bool remapRequested = false;
uint64_t currentVa = (uint64_t)(uintptr_t)ds->windows[i].content;
if (ds->windows[i].content_w != extWins[e].width || if (ds->windows[i].content_w != extWins[e].width ||
ds->windows[i].content_h != extWins[e].height) { ds->windows[i].content_h != extWins[e].height) {
ds->windows[i].content_w = extWins[e].width; ds->windows[i].content_w = extWins[e].width;
ds->windows[i].content_h = extWins[e].height; ds->windows[i].content_h = extWins[e].height;
ds->windows[i].dirty = true; ds->windows[i].dirty = true;
remapRequested = true;
if (ds->windows[i].state != WIN_MINIMIZED && ds->windows[i].state != WIN_CLOSED) { if (ds->windows[i].state != WIN_MINIMIZED && ds->windows[i].state != WIN_CLOSED) {
changed = true; changed = true;
} }
@@ -411,12 +414,20 @@ bool desktop_poll_external_windows(DesktopState* ds) {
changed = true; changed = true;
} }
ds->windows[i].ext_cursor = extWins[e].cursor; ds->windows[i].ext_cursor = extWins[e].cursor;
// Always verify mapping is current. If a window slot was
// recycled (app exited, new app got same slot), desktopVa if (remapRequested && currentVa != 0) {
// was zeroed and Map() returns a new VA. Detect this and montauk::win_unmap(extId);
// update the content pointer to avoid using a stale VA. ds->windows[i].content = nullptr;
currentVa = 0;
}
uint64_t va = montauk::win_map(extId); uint64_t va = montauk::win_map(extId);
if (va != 0 && va != (uint64_t)(uintptr_t)ds->windows[i].content) { if (!remapRequested && va != 0 && va != currentVa) {
montauk::win_unmap(extId);
va = montauk::win_map(extId);
}
if (va != 0 && va != currentVa) {
ds->windows[i].content = (uint32_t*)va; ds->windows[i].content = (uint32_t*)va;
ds->windows[i].content_w = extWins[e].width; ds->windows[i].content_w = extWins[e].width;
ds->windows[i].content_h = extWins[e].height; ds->windows[i].content_h = extWins[e].height;
@@ -508,6 +519,9 @@ bool desktop_poll_external_windows(DesktopState* ds) {
if (!stillExists) { if (!stillExists) {
// Window gone — remove without freeing content (shared memory) // Window gone — remove without freeing content (shared memory)
if (ds->windows[i].content != nullptr) {
montauk::win_unmap(extId);
}
ds->windows[i].content = nullptr; // prevent free ds->windows[i].content = nullptr; // prevent free
gui::desktop_close_window(ds, i); gui::desktop_close_window(ds, i);
changed = true; changed = true;
+6 -2
View File
@@ -397,7 +397,9 @@ extern "C" void _start() {
} }
} }
} }
montauk::yield(); uint64_t now = montauk::get_milliseconds();
if (now >= startMs + 10000) break;
montauk::wait_handle(fd, Montauk::IPC_SIGNAL_READABLE, startMs + 10000 - now);
} }
if (!gotOffer) { if (!gotOffer) {
@@ -447,7 +449,9 @@ extern "C" void _start() {
} }
} }
} }
montauk::yield(); uint64_t now = montauk::get_milliseconds();
if (now >= startMs + 10000) break;
montauk::wait_handle(fd, Montauk::IPC_SIGNAL_READABLE, startMs + 10000 - now);
} }
montauk::closesocket(fd); montauk::closesocket(fd);
+8 -2
View File
@@ -198,8 +198,14 @@ static int plain_http_exchange(int fd, const char* request, int reqLen,
if (r > 0) { respLen += r; deadline = montauk::get_milliseconds() + 15000; } if (r > 0) { respLen += r; deadline = montauk::get_milliseconds() + 15000; }
else if (r < 0) break; else if (r < 0) break;
else { else {
if (montauk::get_milliseconds() >= deadline) break; uint64_t now = montauk::get_milliseconds();
montauk::sleep_ms(1); if (now >= deadline) break;
uint32_t signals = montauk::wait_handle(
fd,
Montauk::IPC_SIGNAL_READABLE | Montauk::IPC_SIGNAL_PEER_CLOSED,
deadline - now
);
if (signals == 0 || signals == (uint32_t)-1) break;
} }
} }
return respLen; return respLen;
+10 -2
View File
@@ -386,9 +386,17 @@ static void handle_client(int clientFd) {
} else if (r == 0) { } else if (r == 0) {
break; // Connection closed break; // Connection closed
} else { } else {
uint32_t signals = montauk::wait_handle(
clientFd,
Montauk::IPC_SIGNAL_READABLE | Montauk::IPC_SIGNAL_PEER_CLOSED,
20
);
if (signals == 0) {
idleCount++; idleCount++;
if (idleCount > 500) break; // Timeout if (idleCount > 500) break;
montauk::yield(); } else if (signals & Montauk::IPC_SIGNAL_PEER_CLOSED) {
break;
}
} }
} }
reqBuf[reqLen] = '\0'; reqBuf[reqLen] = '\0';
+3 -1
View File
@@ -1028,7 +1028,9 @@ extern "C" void _start() {
} }
} else { } else {
if (!dirty) { if (!dirty) {
montauk::yield(); montauk::wait_handle(irc.fd,
Montauk::IPC_SIGNAL_READABLE | Montauk::IPC_SIGNAL_PEER_CLOSED,
10);
continue; continue;
} }
} }
+1 -2
View File
@@ -187,8 +187,7 @@ extern "C" void _start() {
montauk::putchar(ev.ascii); montauk::putchar(ev.ascii);
} }
} else { } else {
// No key and no data -- yield to avoid busy-spinning montauk::wait_handle(fd, Montauk::IPC_SIGNAL_READABLE | Montauk::IPC_SIGNAL_PEER_CLOSED, 10);
montauk::yield();
} }
} }