feat: implement new IPC layer
This commit is contained in:
+106
-28
@@ -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;
|
||||||
}
|
|
||||||
|
|
||||||
// SPSC ring buffer helpers. On x86 TSO, atomic-width aligned
|
|
||||||
// loads/stores are naturally atomic. The compiler barrier ensures
|
|
||||||
// the data write is visible before the head update.
|
|
||||||
static void RingWrite(uint8_t* buf, volatile uint32_t& head, uint32_t /*tail*/, uint32_t size, uint8_t byte) {
|
|
||||||
uint32_t h = head;
|
|
||||||
buf[h] = byte;
|
|
||||||
asm volatile("" ::: "memory"); // compiler barrier
|
|
||||||
head = (h + 1) % size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int RingRead(uint8_t* buf, volatile uint32_t& head, volatile uint32_t& tail, uint32_t size, uint8_t* out, int maxLen) {
|
|
||||||
int count = 0;
|
|
||||||
uint32_t t = tail;
|
|
||||||
uint32_t h = head;
|
|
||||||
while (t != h && count < maxLen) {
|
|
||||||
out[count++] = buf[t];
|
|
||||||
t = (t + 1) % size;
|
|
||||||
}
|
}
|
||||||
asm volatile("" ::: "memory"); // compiler barrier
|
return (proc->parentPid >= 0) ? Sched::GetProcessByPid(proc->parentPid) : nullptr;
|
||||||
tail = t;
|
}
|
||||||
return count;
|
|
||||||
|
static bool ResolveProcessHandle(Sched::Process* proc, int handle, Ipc::HandleType expectedType,
|
||||||
|
Ipc::Object*& outObject, uint32_t* outRights = nullptr) {
|
||||||
|
if (proc == nullptr || handle < 0) return false;
|
||||||
|
|
||||||
|
int slot = Ipc::SlotForPid(proc->pid);
|
||||||
|
if (slot < 0) return false;
|
||||||
|
|
||||||
|
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 Ipc::Stream* GetRedirOutStream(Sched::Process* proc) {
|
||||||
|
proc = GetRedirTarget(proc);
|
||||||
|
if (proc == nullptr) return nullptr;
|
||||||
|
|
||||||
|
Ipc::Object* object = nullptr;
|
||||||
|
if (!ResolveProcessHandle(proc, proc->ioOutHandle, Ipc::HandleType::Stream, object)) return nullptr;
|
||||||
|
return (Ipc::Stream*)object;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Ipc::Stream* GetRedirInStream(Sched::Process* proc) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
+63
-26
@@ -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,53 +17,92 @@ 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;
|
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
|
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
|
||||||
|
|||||||
@@ -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
@@ -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,15 +25,18 @@ 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;
|
||||||
|
if (rc < 0) {
|
||||||
|
memset(outEvent, 0, sizeof(KeyEvent));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Sched::BlockOnObject(mailbox, 0);
|
||||||
}
|
}
|
||||||
*outEvent = target->keyBuf[target->keyTail];
|
|
||||||
target->keyTail = (target->keyTail + 1) % 64;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto k = Drivers::PS2::Keyboard::GetKey();
|
auto k = Drivers::PS2::Keyboard::GetKey();
|
||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+164
-294
@@ -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;
|
static int UnmapRetiredSnapshotsLocked(int windowId, int callerPid, uint64_t callerPml4) {
|
||||||
for (int i = 0; i < numPages; i++) {
|
int result = -1;
|
||||||
batch.physPages[i] = physPages[i];
|
for (int i = 0; i < MaxRetiredSnapshots; i++) {
|
||||||
physPages[i] = 0;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
return outVa;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
slot.desktopVa = userVa;
|
|
||||||
slot.desktopPid = callerPid;
|
|
||||||
heapNext += (uint64_t)slot.pixelNumPages * 0x1000;
|
|
||||||
|
|
||||||
return userVa;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal: send event without acquiring lock (caller must hold wsLock)
|
int Unmap(int windowId, int callerPid, uint64_t callerPml4) {
|
||||||
static int SendEventLocked(int windowId, const Montauk::WinEvent* event) {
|
WsGuard guard;
|
||||||
if (windowId < 0 || windowId >= MaxWindows) return -1;
|
if (windowId < 0 || windowId >= MaxWindows) {
|
||||||
|
return UnmapRetiredSnapshotsLocked(windowId, callerPid, callerPml4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = UnmapRetiredSnapshotsLocked(windowId, callerPid, callerPml4);
|
||||||
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
|
return -1;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* snapPage = Memory::g_pfa->AllocateZeroed();
|
|
||||||
if (snapPage == nullptr) return -1;
|
|
||||||
slot.snapshotPhysPages[i] = Memory::SubHHDM((uint64_t)snapPage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ipc::RetainSurface(newLive);
|
||||||
|
Ipc::RetainSurface(newSnapshot);
|
||||||
|
|
||||||
|
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,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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
+64
-67
@@ -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();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTable[globalHandle].inUse = true;
|
|
||||||
handleTable[globalHandle].driveNumber = drive;
|
|
||||||
handleTable[globalHandle].localHandle = localHandle;
|
|
||||||
|
|
||||||
vfsLock.Release();
|
vfsLock.Release();
|
||||||
return globalHandle;
|
if (localHandle < 0) return -1;
|
||||||
|
|
||||||
|
outFile.driveNumber = drive;
|
||||||
|
outFile.localHandle = localHandle;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VfsDelete(const char* path) {
|
int VfsDelete(const char* path) {
|
||||||
|
|||||||
+13
-7
@@ -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
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -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*/) {
|
||||||
if (!ValidFd(fd, pid)) return -1;
|
return Ipc::SocketConnectHandle(fd, ip, 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 Bind(int fd, uint16_t port, int /*pid*/) {
|
||||||
if (!ValidFd(fd, pid)) return -1;
|
return Ipc::SocketBindHandle(fd, port);
|
||||||
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 Listen(int fd, int pid) {
|
int Listen(int fd, int /*pid*/) {
|
||||||
if (!ValidFd(fd, pid)) return -1;
|
return Ipc::SocketListenHandle(fd);
|
||||||
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 Accept(int fd, int /*pid*/) {
|
||||||
if (!ValidFd(fd, pid)) return -1;
|
return Ipc::SocketAcceptHandle(fd);
|
||||||
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) {
|
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].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) {
|
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;
|
|
||||||
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
|
|
||||||
UdpDgramHeader hdr;
|
|
||||||
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
|
|
||||||
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) {
|
void Close(int fd, int /*pid*/) {
|
||||||
if (!ValidFd(fd, pid)) return;
|
Ipc::CloseHandle(fd);
|
||||||
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) {
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
+142
-76
@@ -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.outBuf = nullptr;
|
proc.waitingForPid = -1;
|
||||||
}
|
proc.sleepUntilTick = 0;
|
||||||
if (proc.inBuf) {
|
proc.waitingOnObject = nullptr;
|
||||||
Memory::g_pfa->Free(proc.inBuf);
|
proc.redirected = false;
|
||||||
proc.inBuf = nullptr;
|
proc.parentPid = -1;
|
||||||
}
|
proc.outBuf = nullptr;
|
||||||
|
proc.outHead = 0;
|
||||||
|
proc.outTail = 0;
|
||||||
|
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;
|
||||||
if (next >= 0) {
|
processTable[i].state = ProcessState::Ready;
|
||||||
cpu->currentSlot = next;
|
readyCount++;
|
||||||
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 {
|
|
||||||
cpu->currentSlot = -1;
|
|
||||||
|
|
||||||
SchedContextSwitch(&processTable[slot].savedRsp, cpu->idleSavedRsp,
|
|
||||||
GetKernelCR3(), processTable[slot].fpuState, nullptr);
|
|
||||||
schedLock.Release();
|
|
||||||
}
|
}
|
||||||
|
schedLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsAlive(int pid) {
|
bool IsAlive(int pid) {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
idleCount++;
|
uint32_t signals = montauk::wait_handle(
|
||||||
if (idleCount > 500) break; // Timeout
|
clientFd,
|
||||||
montauk::yield();
|
Montauk::IPC_SIGNAL_READABLE | Montauk::IPC_SIGNAL_PEER_CLOSED,
|
||||||
|
20
|
||||||
|
);
|
||||||
|
if (signals == 0) {
|
||||||
|
idleCount++;
|
||||||
|
if (idleCount > 500) break;
|
||||||
|
} else if (signals & Montauk::IPC_SIGNAL_PEER_CLOSED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reqBuf[reqLen] = '\0';
|
reqBuf[reqLen] = '\0';
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user