feat: Sockets, user-mode networking, IRC client application
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
#include <Libraries/String.hpp>
|
#include <Libraries/String.hpp>
|
||||||
#include <Drivers/PS2/Keyboard.hpp>
|
#include <Drivers/PS2/Keyboard.hpp>
|
||||||
#include <Net/Icmp.hpp>
|
#include <Net/Icmp.hpp>
|
||||||
|
#include <Net/Socket.hpp>
|
||||||
#include <Net/ByteOrder.hpp>
|
#include <Net/ByteOrder.hpp>
|
||||||
#include <Hal/MSR.hpp>
|
#include <Hal/MSR.hpp>
|
||||||
#include <Hal/GDT.hpp>
|
#include <Hal/GDT.hpp>
|
||||||
@@ -268,6 +269,40 @@ namespace Zenith {
|
|||||||
out->Second = dt.Second;
|
out->Second = dt.Second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Socket syscalls ----
|
||||||
|
|
||||||
|
static int Sys_Socket(int type) {
|
||||||
|
return Net::Socket::Create(type, Sched::GetCurrentPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Sys_Connect(int fd, uint32_t ip, uint16_t port) {
|
||||||
|
return Net::Socket::Connect(fd, ip, port, Sched::GetCurrentPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Sys_Bind(int fd, uint16_t port) {
|
||||||
|
return Net::Socket::Bind(fd, port, Sched::GetCurrentPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Sys_Listen(int fd) {
|
||||||
|
return Net::Socket::Listen(fd, Sched::GetCurrentPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Sys_Accept(int fd) {
|
||||||
|
return Net::Socket::Accept(fd, Sched::GetCurrentPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Sys_Send(int fd, const uint8_t* data, uint32_t len) {
|
||||||
|
return Net::Socket::Send(fd, data, len, Sched::GetCurrentPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Sys_Recv(int fd, uint8_t* buf, uint32_t maxLen) {
|
||||||
|
return Net::Socket::Recv(fd, buf, maxLen, Sched::GetCurrentPid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Sys_CloseSock(int fd) {
|
||||||
|
Net::Socket::Close(fd, Sched::GetCurrentPid());
|
||||||
|
}
|
||||||
|
|
||||||
static void Sys_Reset() {
|
static void Sys_Reset() {
|
||||||
/*
|
/*
|
||||||
Triple fault for now; TODO: implement UEFI runtime function for clean reboot.
|
Triple fault for now; TODO: implement UEFI runtime function for clean reboot.
|
||||||
@@ -364,6 +399,23 @@ namespace Zenith {
|
|||||||
case SYS_GETTIME:
|
case SYS_GETTIME:
|
||||||
Sys_GetTime((DateTime*)frame->arg1);
|
Sys_GetTime((DateTime*)frame->arg1);
|
||||||
return 0;
|
return 0;
|
||||||
|
case SYS_SOCKET:
|
||||||
|
return (int64_t)Sys_Socket((int)frame->arg1);
|
||||||
|
case SYS_CONNECT:
|
||||||
|
return (int64_t)Sys_Connect((int)frame->arg1, (uint32_t)frame->arg2, (uint16_t)frame->arg3);
|
||||||
|
case SYS_BIND:
|
||||||
|
return (int64_t)Sys_Bind((int)frame->arg1, (uint16_t)frame->arg2);
|
||||||
|
case SYS_LISTEN:
|
||||||
|
return (int64_t)Sys_Listen((int)frame->arg1);
|
||||||
|
case SYS_ACCEPT:
|
||||||
|
return (int64_t)Sys_Accept((int)frame->arg1);
|
||||||
|
case SYS_SEND:
|
||||||
|
return (int64_t)Sys_Send((int)frame->arg1, (const uint8_t*)frame->arg2, (uint32_t)frame->arg3);
|
||||||
|
case SYS_RECV:
|
||||||
|
return (int64_t)Sys_Recv((int)frame->arg1, (uint8_t*)frame->arg2, (uint32_t)frame->arg3);
|
||||||
|
case SYS_CLOSESOCK:
|
||||||
|
Sys_CloseSock((int)frame->arg1);
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -390,7 +442,7 @@ namespace Zenith {
|
|||||||
Hal::WriteMSR(Hal::IA32_FMASK, 0x200);
|
Hal::WriteMSR(Hal::IA32_FMASK, 0x200);
|
||||||
|
|
||||||
Kt::KernelLogStream(Kt::OK, "Syscall") << "SYSCALL/SYSRET initialized (LSTAR="
|
Kt::KernelLogStream(Kt::OK, "Syscall") << "SYSCALL/SYSRET initialized (LSTAR="
|
||||||
<< kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", 29 syscalls)";
|
<< kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", 37 syscalls)";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,16 @@ namespace Zenith {
|
|||||||
static constexpr uint64_t SYS_RESET = 26;
|
static constexpr uint64_t SYS_RESET = 26;
|
||||||
static constexpr uint64_t SYS_SHUTDOWN = 27;
|
static constexpr uint64_t SYS_SHUTDOWN = 27;
|
||||||
static constexpr uint64_t SYS_GETTIME = 28;
|
static constexpr uint64_t SYS_GETTIME = 28;
|
||||||
|
static constexpr uint64_t SYS_SOCKET = 29;
|
||||||
|
static constexpr uint64_t SYS_CONNECT = 30;
|
||||||
|
static constexpr uint64_t SYS_BIND = 31;
|
||||||
|
static constexpr uint64_t SYS_LISTEN = 32;
|
||||||
|
static constexpr uint64_t SYS_ACCEPT = 33;
|
||||||
|
static constexpr uint64_t SYS_SEND = 34;
|
||||||
|
static constexpr uint64_t SYS_RECV = 35;
|
||||||
|
static constexpr uint64_t SYS_CLOSESOCK = 36;
|
||||||
|
|
||||||
|
static constexpr int SOCK_TCP = 1;
|
||||||
|
|
||||||
struct DateTime {
|
struct DateTime {
|
||||||
uint16_t Year;
|
uint16_t Year;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <Net/Icmp.hpp>
|
#include <Net/Icmp.hpp>
|
||||||
#include <Net/Udp.hpp>
|
#include <Net/Udp.hpp>
|
||||||
#include <Net/Tcp.hpp>
|
#include <Net/Tcp.hpp>
|
||||||
|
#include <Net/Socket.hpp>
|
||||||
#include <Net/NetConfig.hpp>
|
#include <Net/NetConfig.hpp>
|
||||||
#include <Drivers/Net/E1000.hpp>
|
#include <Drivers/Net/E1000.hpp>
|
||||||
#include <Terminal/Terminal.hpp>
|
#include <Terminal/Terminal.hpp>
|
||||||
@@ -33,6 +34,7 @@ namespace Net {
|
|||||||
Icmp::Initialize();
|
Icmp::Initialize();
|
||||||
Udp::Initialize();
|
Udp::Initialize();
|
||||||
Tcp::Initialize();
|
Tcp::Initialize();
|
||||||
|
Socket::Initialize();
|
||||||
|
|
||||||
// Hook E1000 RX to our Ethernet dispatcher
|
// Hook E1000 RX to our Ethernet dispatcher
|
||||||
Drivers::Net::E1000::SetRxCallback(Ethernet::OnFrameReceived);
|
Drivers::Net::E1000::SetRxCallback(Ethernet::OnFrameReceived);
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Socket.cpp
|
||||||
|
* Socket descriptor table wrapping kernel TCP layer
|
||||||
|
* Copyright (c) 2025 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Socket.hpp"
|
||||||
|
#include <Net/Tcp.hpp>
|
||||||
|
#include <Net/NetConfig.hpp>
|
||||||
|
#include <Terminal/Terminal.hpp>
|
||||||
|
#include <CppLib/Stream.hpp>
|
||||||
|
|
||||||
|
using namespace Kt;
|
||||||
|
|
||||||
|
namespace Net::Socket {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize() {
|
||||||
|
for (int i = 0; i < MAX_SOCKETS; i++) {
|
||||||
|
g_sockets[i].Active = false;
|
||||||
|
}
|
||||||
|
KernelLogStream(OK, "Net") << "Socket table initialized";
|
||||||
|
}
|
||||||
|
|
||||||
|
int Create(int type, int pid) {
|
||||||
|
if (type != SOCK_TCP) return -1;
|
||||||
|
|
||||||
|
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].LocalPort = 0;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Connect(int fd, uint32_t ip, uint16_t port, int pid) {
|
||||||
|
if (!ValidFd(fd, pid)) 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) {
|
||||||
|
if (!ValidFd(fd, pid)) return -1;
|
||||||
|
g_sockets[fd].LocalPort = port;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Listen(int fd, int pid) {
|
||||||
|
if (!ValidFd(fd, pid)) 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) {
|
||||||
|
if (!ValidFd(fd, pid)) 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].LocalPort = g_sockets[fd].LocalPort;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No free socket slot — close the accepted connection
|
||||||
|
Tcp::Close(clientConn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Send(int fd, const uint8_t* data, uint32_t len, int pid) {
|
||||||
|
if (!ValidFd(fd, pid)) return -1;
|
||||||
|
if (g_sockets[fd].TcpConn == nullptr) return -1;
|
||||||
|
return Tcp::Send(g_sockets[fd].TcpConn, data, (uint16_t)len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Recv(int fd, uint8_t* buf, uint32_t maxLen, int pid) {
|
||||||
|
if (!ValidFd(fd, pid)) return -1;
|
||||||
|
if (g_sockets[fd].TcpConn == nullptr) return -1;
|
||||||
|
return Tcp::ReceiveNonBlocking(g_sockets[fd].TcpConn, buf, (uint16_t)maxLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close(int fd, int pid) {
|
||||||
|
if (!ValidFd(fd, pid)) return;
|
||||||
|
if (g_sockets[fd].TcpConn != nullptr) {
|
||||||
|
Tcp::Close(g_sockets[fd].TcpConn);
|
||||||
|
g_sockets[fd].TcpConn = nullptr;
|
||||||
|
}
|
||||||
|
g_sockets[fd].Active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CleanupProcess(int pid) {
|
||||||
|
for (int i = 0; i < MAX_SOCKETS; i++) {
|
||||||
|
if (g_sockets[i].Active && g_sockets[i].OwnerPid == pid) {
|
||||||
|
if (g_sockets[i].TcpConn != nullptr) {
|
||||||
|
Tcp::Close(g_sockets[i].TcpConn);
|
||||||
|
g_sockets[i].TcpConn = nullptr;
|
||||||
|
}
|
||||||
|
g_sockets[i].Active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Socket.hpp
|
||||||
|
* Socket descriptor table for userspace TCP access
|
||||||
|
* Copyright (c) 2025 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <Net/Tcp.hpp>
|
||||||
|
|
||||||
|
namespace Net::Socket {
|
||||||
|
|
||||||
|
static constexpr int SOCK_TCP = 1;
|
||||||
|
static constexpr int SOCK_UDP = 2;
|
||||||
|
static constexpr int MAX_SOCKETS = 64;
|
||||||
|
|
||||||
|
struct SocketEntry {
|
||||||
|
bool Active;
|
||||||
|
int Type;
|
||||||
|
int OwnerPid;
|
||||||
|
Tcp::Connection* TcpConn;
|
||||||
|
uint16_t LocalPort;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
// Create a socket of the given type. Returns fd or -1.
|
||||||
|
int Create(int type, int pid);
|
||||||
|
|
||||||
|
// Connect socket fd to remote ip:port. Returns 0 or -1.
|
||||||
|
int Connect(int fd, uint32_t ip, uint16_t port, int pid);
|
||||||
|
|
||||||
|
// Bind socket fd to a local port. Returns 0 or -1.
|
||||||
|
int Bind(int fd, uint16_t port, int pid);
|
||||||
|
|
||||||
|
// Start listening on a bound socket. Returns 0 or -1.
|
||||||
|
int Listen(int fd, int pid);
|
||||||
|
|
||||||
|
// Accept an incoming connection. Returns new fd or -1.
|
||||||
|
int Accept(int fd, int pid);
|
||||||
|
|
||||||
|
// Send data on a connected socket. Returns bytes sent or -1.
|
||||||
|
int Send(int fd, const uint8_t* data, uint32_t len, int pid);
|
||||||
|
|
||||||
|
// Receive data from a connected socket. Returns bytes received, 0 on close, or -1.
|
||||||
|
int Recv(int fd, uint8_t* buf, uint32_t maxLen, int pid);
|
||||||
|
|
||||||
|
// Close a socket.
|
||||||
|
void Close(int fd, int pid);
|
||||||
|
|
||||||
|
// Close all sockets owned by a process (called on process exit).
|
||||||
|
void CleanupProcess(int pid);
|
||||||
|
|
||||||
|
}
|
||||||
+65
-6
@@ -523,9 +523,11 @@ namespace Net::Tcp {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 1: Send data segments with interrupts disabled (lock-safe)
|
||||||
|
uint64_t flags;
|
||||||
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
||||||
conn->Lock.Acquire();
|
conn->Lock.Acquire();
|
||||||
|
|
||||||
// Send data in segments up to MSS (we use a simple 1460 byte MSS)
|
|
||||||
constexpr uint16_t MSS = 1460;
|
constexpr uint16_t MSS = 1460;
|
||||||
uint16_t sent = 0;
|
uint16_t sent = 0;
|
||||||
|
|
||||||
@@ -538,6 +540,7 @@ namespace Net::Tcp {
|
|||||||
bool ok = SendSegment(conn, FLAG_ACK | FLAG_PSH, data + sent, segLen);
|
bool ok = SendSegment(conn, FLAG_ACK | FLAG_PSH, data + sent, segLen);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
conn->Lock.Release();
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
return sent > 0 ? sent : -1;
|
return sent > 0 ? sent : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,30 +557,36 @@ namespace Net::Tcp {
|
|||||||
sent += segLen;
|
sent += segLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple wait for ACK with retransmission
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
|
|
||||||
|
// Phase 2: Wait for ACK without holding the lock (interrupts enabled)
|
||||||
uint64_t startTime = Timekeeping::GetMilliseconds();
|
uint64_t startTime = Timekeeping::GetMilliseconds();
|
||||||
while (conn->SendUnack != conn->SendNext) {
|
while (conn->SendUnack != conn->SendNext) {
|
||||||
uint64_t now = Timekeeping::GetMilliseconds();
|
uint64_t now = Timekeeping::GetMilliseconds();
|
||||||
if ((now - startTime) > (RETRANSMIT_TIMEOUT_MS * MAX_RETRANSMITS)) {
|
if ((now - startTime) > (RETRANSMIT_TIMEOUT_MS * MAX_RETRANSMITS)) {
|
||||||
break; // Give up
|
break;
|
||||||
}
|
}
|
||||||
if ((now - conn->RetransmitTime) > RETRANSMIT_TIMEOUT_MS && conn->RetransmitLen > 0) {
|
if ((now - conn->RetransmitTime) > RETRANSMIT_TIMEOUT_MS && conn->RetransmitLen > 0) {
|
||||||
conn->RetransmitCount++;
|
conn->RetransmitCount++;
|
||||||
if (conn->RetransmitCount > MAX_RETRANSMITS) {
|
if (conn->RetransmitCount > MAX_RETRANSMITS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Retransmit: rewind SendNext temporarily
|
// Retransmit with brief interrupt-disabled window
|
||||||
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
||||||
|
conn->Lock.Acquire();
|
||||||
uint32_t savedNext = conn->SendNext;
|
uint32_t savedNext = conn->SendNext;
|
||||||
conn->SendNext = conn->SendUnack;
|
conn->SendNext = conn->SendUnack;
|
||||||
SendSegment(conn, FLAG_ACK | FLAG_PSH,
|
SendSegment(conn, FLAG_ACK | FLAG_PSH,
|
||||||
conn->RetransmitBuffer, conn->RetransmitLen);
|
conn->RetransmitBuffer, conn->RetransmitLen);
|
||||||
conn->SendNext = savedNext;
|
conn->SendNext = savedNext;
|
||||||
conn->RetransmitTime = now;
|
conn->RetransmitTime = Timekeeping::GetMilliseconds();
|
||||||
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
}
|
}
|
||||||
Timekeeping::Sleep(10);
|
Timekeeping::Sleep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->Lock.Release();
|
|
||||||
return sent;
|
return sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,6 +597,8 @@ namespace Net::Tcp {
|
|||||||
|
|
||||||
// Block until data is available or connection is closing
|
// Block until data is available or connection is closing
|
||||||
while (true) {
|
while (true) {
|
||||||
|
uint64_t flags;
|
||||||
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
||||||
conn->Lock.Acquire();
|
conn->Lock.Acquire();
|
||||||
|
|
||||||
if (conn->RecvCount > 0) {
|
if (conn->RecvCount > 0) {
|
||||||
@@ -603,6 +614,7 @@ namespace Net::Tcp {
|
|||||||
conn->RecvCount -= toRead;
|
conn->RecvCount -= toRead;
|
||||||
|
|
||||||
conn->Lock.Release();
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
return toRead;
|
return toRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,19 +622,62 @@ namespace Net::Tcp {
|
|||||||
conn->CurrentState == State::Closed ||
|
conn->CurrentState == State::Closed ||
|
||||||
conn->CurrentState == State::TimeWait) {
|
conn->CurrentState == State::TimeWait) {
|
||||||
conn->Lock.Release();
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
return 0; // Connection closed
|
return 0; // Connection closed
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->Lock.Release();
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
Timekeeping::Sleep(10);
|
Timekeeping::Sleep(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ReceiveNonBlocking(Connection* conn, uint8_t* buffer, uint16_t bufferSize) {
|
||||||
|
if (conn == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable interrupts while holding the lock to prevent deadlock
|
||||||
|
// with OnPacketReceived (called from the network interrupt handler)
|
||||||
|
uint64_t flags;
|
||||||
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
||||||
|
|
||||||
|
conn->Lock.Acquire();
|
||||||
|
|
||||||
|
int result;
|
||||||
|
if (conn->RecvCount > 0) {
|
||||||
|
uint16_t toRead = conn->RecvCount;
|
||||||
|
if (toRead > bufferSize) {
|
||||||
|
toRead = bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < toRead; i++) {
|
||||||
|
buffer[i] = conn->RecvBuffer[conn->RecvHead];
|
||||||
|
conn->RecvHead = (conn->RecvHead + 1) % RECV_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
conn->RecvCount -= toRead;
|
||||||
|
result = toRead;
|
||||||
|
} else if (conn->CurrentState == State::CloseWait ||
|
||||||
|
conn->CurrentState == State::Closed ||
|
||||||
|
conn->CurrentState == State::TimeWait) {
|
||||||
|
result = -1; // Connection closed
|
||||||
|
} else {
|
||||||
|
result = 0; // No data available
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void Close(Connection* conn) {
|
void Close(Connection* conn) {
|
||||||
if (conn == nullptr) {
|
if (conn == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t flags;
|
||||||
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
||||||
conn->Lock.Acquire();
|
conn->Lock.Acquire();
|
||||||
|
|
||||||
switch (conn->CurrentState) {
|
switch (conn->CurrentState) {
|
||||||
@@ -631,6 +686,7 @@ namespace Net::Tcp {
|
|||||||
SendSegment(conn, FLAG_FIN | FLAG_ACK, nullptr, 0);
|
SendSegment(conn, FLAG_FIN | FLAG_ACK, nullptr, 0);
|
||||||
conn->SendNext++;
|
conn->SendNext++;
|
||||||
conn->Lock.Release();
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
|
|
||||||
// Wait for close to complete
|
// Wait for close to complete
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
@@ -649,6 +705,7 @@ namespace Net::Tcp {
|
|||||||
SendSegment(conn, FLAG_FIN | FLAG_ACK, nullptr, 0);
|
SendSegment(conn, FLAG_FIN | FLAG_ACK, nullptr, 0);
|
||||||
conn->SendNext++;
|
conn->SendNext++;
|
||||||
conn->Lock.Release();
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
|
|
||||||
// Wait for final ACK
|
// Wait for final ACK
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
@@ -666,11 +723,13 @@ namespace Net::Tcp {
|
|||||||
conn->CurrentState = State::Closed;
|
conn->CurrentState = State::Closed;
|
||||||
conn->Active = false;
|
conn->Active = false;
|
||||||
conn->Lock.Release();
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
conn->Lock.Release();
|
conn->Lock.Release();
|
||||||
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
|
||||||
conn->Active = false;
|
conn->Active = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ namespace Net::Tcp {
|
|||||||
// Blocks until data is available or connection is closed.
|
// Blocks until data is available or connection is closed.
|
||||||
int Receive(Connection* conn, uint8_t* buffer, uint16_t bufferSize);
|
int Receive(Connection* conn, uint8_t* buffer, uint16_t bufferSize);
|
||||||
|
|
||||||
|
// Non-blocking receive. Returns bytes read, 0 if no data available, or -1 on closed/error.
|
||||||
|
int ReceiveNonBlocking(Connection* conn, uint8_t* buffer, uint16_t bufferSize);
|
||||||
|
|
||||||
// Close a TCP connection gracefully
|
// Close a TCP connection gracefully
|
||||||
void Close(Connection* conn);
|
void Close(Connection* conn);
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -40,6 +40,16 @@ namespace Zenith {
|
|||||||
static constexpr uint64_t SYS_RESET = 26;
|
static constexpr uint64_t SYS_RESET = 26;
|
||||||
static constexpr uint64_t SYS_SHUTDOWN = 27;
|
static constexpr uint64_t SYS_SHUTDOWN = 27;
|
||||||
static constexpr uint64_t SYS_GETTIME = 28;
|
static constexpr uint64_t SYS_GETTIME = 28;
|
||||||
|
static constexpr uint64_t SYS_SOCKET = 29;
|
||||||
|
static constexpr uint64_t SYS_CONNECT = 30;
|
||||||
|
static constexpr uint64_t SYS_BIND = 31;
|
||||||
|
static constexpr uint64_t SYS_LISTEN = 32;
|
||||||
|
static constexpr uint64_t SYS_ACCEPT = 33;
|
||||||
|
static constexpr uint64_t SYS_SEND = 34;
|
||||||
|
static constexpr uint64_t SYS_RECV = 35;
|
||||||
|
static constexpr uint64_t SYS_CLOSESOCK = 36;
|
||||||
|
|
||||||
|
static constexpr int SOCK_TCP = 1;
|
||||||
|
|
||||||
struct DateTime {
|
struct DateTime {
|
||||||
uint16_t Year;
|
uint16_t Year;
|
||||||
|
|||||||
@@ -157,6 +157,32 @@ namespace zenith {
|
|||||||
return (int32_t)syscall2(Zenith::SYS_PING, (uint64_t)ip, (uint64_t)timeoutMs);
|
return (int32_t)syscall2(Zenith::SYS_PING, (uint64_t)ip, (uint64_t)timeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sockets
|
||||||
|
inline int socket(int type) {
|
||||||
|
return (int)syscall1(Zenith::SYS_SOCKET, (uint64_t)type);
|
||||||
|
}
|
||||||
|
inline int connect(int fd, uint32_t ip, uint16_t port) {
|
||||||
|
return (int)syscall3(Zenith::SYS_CONNECT, (uint64_t)fd, (uint64_t)ip, (uint64_t)port);
|
||||||
|
}
|
||||||
|
inline int bind(int fd, uint16_t port) {
|
||||||
|
return (int)syscall2(Zenith::SYS_BIND, (uint64_t)fd, (uint64_t)port);
|
||||||
|
}
|
||||||
|
inline int listen(int fd) {
|
||||||
|
return (int)syscall1(Zenith::SYS_LISTEN, (uint64_t)fd);
|
||||||
|
}
|
||||||
|
inline int accept(int fd) {
|
||||||
|
return (int)syscall1(Zenith::SYS_ACCEPT, (uint64_t)fd);
|
||||||
|
}
|
||||||
|
inline int send(int fd, const void* data, uint32_t len) {
|
||||||
|
return (int)syscall3(Zenith::SYS_SEND, (uint64_t)fd, (uint64_t)data, (uint64_t)len);
|
||||||
|
}
|
||||||
|
inline int recv(int fd, void* buf, uint32_t maxLen) {
|
||||||
|
return (int)syscall3(Zenith::SYS_RECV, (uint64_t)fd, (uint64_t)buf, (uint64_t)maxLen);
|
||||||
|
}
|
||||||
|
inline int closesocket(int fd) {
|
||||||
|
return (int)syscall1(Zenith::SYS_CLOSESOCK, (uint64_t)fd);
|
||||||
|
}
|
||||||
|
|
||||||
// Process management
|
// Process management
|
||||||
inline void waitpid(int pid) { syscall1(Zenith::SYS_WAITPID, (uint64_t)pid); }
|
inline void waitpid(int pid) { syscall1(Zenith::SYS_WAITPID, (uint64_t)pid); }
|
||||||
|
|
||||||
|
|||||||
+35
-1
@@ -3,7 +3,7 @@
|
|||||||
syscalls - overview of ZenithOS system calls
|
syscalls - overview of ZenithOS system calls
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
ZenithOS provides 29 system calls (numbers 0-28) for userspace
|
ZenithOS provides 37 system calls (numbers 0-36) for userspace
|
||||||
programs. Syscalls use the x86-64 SYSCALL instruction with the
|
programs. Syscalls use the x86-64 SYSCALL instruction with the
|
||||||
following register convention:
|
following register convention:
|
||||||
|
|
||||||
@@ -120,6 +120,40 @@
|
|||||||
Send an ICMP echo request and wait for reply.
|
Send an ICMP echo request and wait for reply.
|
||||||
int32_t zenith::ping(uint32_t ip, uint32_t timeoutMs);
|
int32_t zenith::ping(uint32_t ip, uint32_t timeoutMs);
|
||||||
|
|
||||||
|
.B SYS_SOCKET (29)
|
||||||
|
Create a socket. type=SOCK_TCP (1). Returns fd or -1.
|
||||||
|
int zenith::socket(int type);
|
||||||
|
|
||||||
|
.B SYS_CONNECT (30)
|
||||||
|
Connect a TCP socket to a remote host.
|
||||||
|
int zenith::connect(int fd, uint32_t ip, uint16_t port);
|
||||||
|
|
||||||
|
.B SYS_BIND (31)
|
||||||
|
Bind a socket to a local port for listening.
|
||||||
|
int zenith::bind(int fd, uint16_t port);
|
||||||
|
|
||||||
|
.B SYS_LISTEN (32)
|
||||||
|
Start listening for incoming TCP connections.
|
||||||
|
int zenith::listen(int fd);
|
||||||
|
|
||||||
|
.B SYS_ACCEPT (33)
|
||||||
|
Accept an incoming connection on a listening socket.
|
||||||
|
Returns a new socket fd for the client connection.
|
||||||
|
int zenith::accept(int fd);
|
||||||
|
|
||||||
|
.B SYS_SEND (34)
|
||||||
|
Send data on a connected socket. Returns bytes sent.
|
||||||
|
int zenith::send(int fd, const void* data, uint32_t len);
|
||||||
|
|
||||||
|
.B SYS_RECV (35)
|
||||||
|
Receive data from a connected socket. Returns bytes
|
||||||
|
received, 0 on connection close, or -1 on error.
|
||||||
|
int zenith::recv(int fd, void* buf, uint32_t maxLen);
|
||||||
|
|
||||||
|
.B SYS_CLOSESOCK (36)
|
||||||
|
Close a socket and release its resources.
|
||||||
|
int zenith::closesocket(int fd);
|
||||||
|
|
||||||
.SH FRAMEBUFFER
|
.SH FRAMEBUFFER
|
||||||
.B SYS_FBINFO (21)
|
.B SYS_FBINFO (21)
|
||||||
Get framebuffer dimensions and format.
|
Get framebuffer dimensions and format.
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
+153
-6
@@ -94,6 +94,7 @@ static void cmd_help() {
|
|||||||
zenith::print(" cat <file> Display file contents\n");
|
zenith::print(" cat <file> Display file contents\n");
|
||||||
zenith::print(" run <file> Spawn a new process from an ELF file\n");
|
zenith::print(" run <file> Spawn a new process from an ELF file\n");
|
||||||
zenith::print(" ping <ip> Send ICMP echo requests\n");
|
zenith::print(" ping <ip> Send ICMP echo requests\n");
|
||||||
|
zenith::print(" tcpconnect <ip> <port> Connect to a TCP server\n");
|
||||||
zenith::print(" date Show current date and time\n");
|
zenith::print(" date Show current date and time\n");
|
||||||
zenith::print(" uptime Show uptime in milliseconds\n");
|
zenith::print(" uptime Show uptime in milliseconds\n");
|
||||||
zenith::print(" clear Clear the screen\n");
|
zenith::print(" clear Clear the screen\n");
|
||||||
@@ -297,20 +298,162 @@ static void cmd_ping(const char* arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmd_run(const char* arg) {
|
static bool parse_uint16(const char* s, uint16_t* out) {
|
||||||
|
uint32_t val = 0;
|
||||||
|
if (*s == '\0') return false;
|
||||||
|
while (*s) {
|
||||||
|
if (*s < '0' || *s > '9') return false;
|
||||||
|
val = val * 10 + (*s - '0');
|
||||||
|
if (val > 65535) return false;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
*out = (uint16_t)val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmd_tcpconnect(const char* arg) {
|
||||||
arg = skip_spaces(arg);
|
arg = skip_spaces(arg);
|
||||||
if (*arg == '\0') {
|
if (*arg == '\0') {
|
||||||
zenith::print("Usage: run <filename>\n");
|
zenith::print("Usage: tcpconnect <ip> <port>\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char path[128];
|
// Parse IP address (up to first space)
|
||||||
resolve_path(arg, path, sizeof(path));
|
char ipStr[32];
|
||||||
|
int i = 0;
|
||||||
|
while (arg[i] && arg[i] != ' ' && i < 31) {
|
||||||
|
ipStr[i] = arg[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
ipStr[i] = '\0';
|
||||||
|
|
||||||
int pid = zenith::spawn(path);
|
uint32_t ip;
|
||||||
|
if (!parse_ip(ipStr, &ip)) {
|
||||||
|
zenith::print("Invalid IP address: ");
|
||||||
|
zenith::print(ipStr);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse port
|
||||||
|
const char* portStr = skip_spaces(arg + i);
|
||||||
|
if (*portStr == '\0') {
|
||||||
|
zenith::print("Usage: tcpconnect <ip> <port>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16_t port;
|
||||||
|
if (!parse_uint16(portStr, &port)) {
|
||||||
|
zenith::print("Invalid port: ");
|
||||||
|
zenith::print(portStr);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create socket
|
||||||
|
int fd = zenith::socket(Zenith::SOCK_TCP);
|
||||||
|
if (fd < 0) {
|
||||||
|
zenith::print("Error: failed to create socket\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
zenith::print("Connecting to ");
|
||||||
|
print_ip(ip);
|
||||||
|
zenith::putchar(':');
|
||||||
|
print_int(port);
|
||||||
|
zenith::print("...\n");
|
||||||
|
|
||||||
|
if (zenith::connect(fd, ip, port) < 0) {
|
||||||
|
zenith::print("Error: connection failed\n");
|
||||||
|
zenith::closesocket(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
zenith::print("Connected! Type to send, Ctrl+Q to disconnect.\n");
|
||||||
|
|
||||||
|
// Interactive send/receive loop
|
||||||
|
char sendBuf[256];
|
||||||
|
int sendPos = 0;
|
||||||
|
uint8_t recvBuf[512];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Poll for received data (non-blocking)
|
||||||
|
int r = zenith::recv(fd, recvBuf, sizeof(recvBuf) - 1);
|
||||||
|
if (r < 0) {
|
||||||
|
zenith::print("\nConnection closed by remote.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r > 0) {
|
||||||
|
recvBuf[r] = '\0';
|
||||||
|
zenith::print((const char*)recvBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll keyboard
|
||||||
|
if (zenith::is_key_available()) {
|
||||||
|
Zenith::KeyEvent ev;
|
||||||
|
zenith::getkey(&ev);
|
||||||
|
|
||||||
|
if (!ev.pressed) continue;
|
||||||
|
|
||||||
|
// Ctrl+Q to quit
|
||||||
|
if (ev.ctrl && (ev.ascii == 'q' || ev.ascii == 'Q')) {
|
||||||
|
zenith::print("\nDisconnecting...\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.ascii == '\n') {
|
||||||
|
sendBuf[sendPos++] = '\n';
|
||||||
|
zenith::putchar('\n');
|
||||||
|
zenith::send(fd, sendBuf, sendPos);
|
||||||
|
sendPos = 0;
|
||||||
|
} else if (ev.ascii == '\b') {
|
||||||
|
if (sendPos > 0) {
|
||||||
|
sendPos--;
|
||||||
|
zenith::putchar('\b');
|
||||||
|
zenith::putchar(' ');
|
||||||
|
zenith::putchar('\b');
|
||||||
|
}
|
||||||
|
} else if (ev.ascii >= ' ' && sendPos < 254) {
|
||||||
|
sendBuf[sendPos++] = ev.ascii;
|
||||||
|
zenith::putchar(ev.ascii);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No key and no data — yield to avoid busy-spinning
|
||||||
|
zenith::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zenith::closesocket(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmd_run(const char* arg) {
|
||||||
|
arg = skip_spaces(arg);
|
||||||
|
if (*arg == '\0') {
|
||||||
|
zenith::print("Usage: run <filename> [args...]\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split filename from arguments at first space
|
||||||
|
char filename[128];
|
||||||
|
int i = 0;
|
||||||
|
while (arg[i] && arg[i] != ' ' && i < 127) {
|
||||||
|
filename[i] = arg[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
filename[i] = '\0';
|
||||||
|
|
||||||
|
const char* args = nullptr;
|
||||||
|
if (arg[i] == ' ') {
|
||||||
|
args = skip_spaces(arg + i);
|
||||||
|
if (*args == '\0') args = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[128];
|
||||||
|
resolve_path(filename, path, sizeof(path));
|
||||||
|
|
||||||
|
int pid = zenith::spawn(path, args);
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
zenith::print("Error: failed to spawn '");
|
zenith::print("Error: failed to spawn '");
|
||||||
zenith::print(arg);
|
zenith::print(filename);
|
||||||
zenith::print("'\n");
|
zenith::print("'\n");
|
||||||
} else {
|
} else {
|
||||||
zenith::waitpid(pid);
|
zenith::waitpid(pid);
|
||||||
@@ -461,6 +604,10 @@ static void process_command(const char* line) {
|
|||||||
cmd_ping(line + 5);
|
cmd_ping(line + 5);
|
||||||
} else if (streq(line, "ping")) {
|
} else if (streq(line, "ping")) {
|
||||||
cmd_ping("");
|
cmd_ping("");
|
||||||
|
} else if (starts_with(line, "tcpconnect ")) {
|
||||||
|
cmd_tcpconnect(line + 11);
|
||||||
|
} else if (streq(line, "tcpconnect")) {
|
||||||
|
cmd_tcpconnect("");
|
||||||
} else if (streq(line, "date")) {
|
} else if (streq(line, "date")) {
|
||||||
cmd_date();
|
cmd_date();
|
||||||
} else if (streq(line, "uptime")) {
|
} else if (streq(line, "uptime")) {
|
||||||
|
|||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user