feat: split syscalls into files, change max process ceiling

This commit is contained in:
2026-02-22 17:23:35 +01:00
parent 99f231acd2
commit 114aa37ef8
23 changed files with 1181 additions and 872 deletions
+23 -2
View File
@@ -1,2 +1,23 @@
Copyright (c) 2025-2026 Daniel Hammer, et al. Copyright (c) 2025-2026 Daniel Hammer, et al. All rights reserved.
(including contributors to other projects, i.e. The Limine Bootloader. Please refer to any other project's own license.) (includes contributors to other projects, i.e. The Limine Bootloader. Please refer to any other project's own license.)
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
== License for the Limine C++ template (certain portions derive therefrom) ==
Copyright (C) 2023-2026 Mintsuki and contributors.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <Sched/Scheduler.hpp>
namespace Zenith {
// 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) {
if (!proc || !proc->redirected) return nullptr;
if (proc->outBuf) return proc; // owns buffers
return Sched::GetProcessByPid(proc->parentPid);
}
static void RingWrite(uint8_t* buf, uint32_t& head, uint32_t /*tail*/, uint32_t size, uint8_t byte) {
buf[head] = byte;
head = (head + 1) % size;
}
static int RingRead(uint8_t* buf, uint32_t& head, uint32_t& tail, uint32_t size, uint8_t* out, int maxLen) {
int count = 0;
while (tail != head && count < maxLen) {
out[count++] = buf[tail];
tail = (tail + 1) % size;
}
return count;
}
}
+152
View File
@@ -0,0 +1,152 @@
/*
* Device.hpp
* SYS_DEVLIST syscall
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <Hal/Apic/ApicInit.hpp>
#include <Drivers/PS2/PS2Controller.hpp>
#include <Drivers/USB/Xhci.hpp>
#include <Drivers/Net/E1000.hpp>
#include <Drivers/Net/E1000E.hpp>
#include <Drivers/Graphics/IntelGPU.hpp>
#include <Pci/Pci.hpp>
#include "Syscall.hpp"
namespace Zenith {
static void dl_strcpy(char* dst, const char* src, int max) {
int i = 0;
for (; i < max - 1 && src[i]; i++) dst[i] = src[i];
dst[i] = '\0';
}
static int dl_append(char* dst, int pos, const char* src, int max) {
for (int i = 0; src[i] && pos < max - 1; i++) dst[pos++] = src[i];
dst[pos] = '\0';
return pos;
}
static int dl_append_hex(char* dst, int pos, unsigned val, int digits, int max) {
const char* hex = "0123456789abcdef";
char tmp[8];
for (int i = digits - 1; i >= 0; i--) { tmp[i] = hex[val & 0xF]; val >>= 4; }
for (int i = 0; i < digits && pos < max - 1; i++) dst[pos++] = tmp[i];
dst[pos] = '\0';
return pos;
}
static int dl_append_dec(char* dst, int pos, int val, int max) {
if (val == 0) { if (pos < max - 1) dst[pos++] = '0'; dst[pos] = '\0'; return pos; }
char tmp[12]; int i = 0;
while (val > 0) { tmp[i++] = '0' + (val % 10); val /= 10; }
while (i > 0 && pos < max - 1) dst[pos++] = tmp[--i];
dst[pos] = '\0';
return pos;
}
static int Sys_DevList(DevInfo* buf, int maxCount) {
if (buf == nullptr || maxCount <= 0) return 0;
int count = 0;
auto add = [&](uint8_t cat, const char* name, const char* detail) {
if (count >= maxCount) return;
buf[count].category = cat;
buf[count]._pad[0] = 0; buf[count]._pad[1] = 0; buf[count]._pad[2] = 0;
dl_strcpy(buf[count].name, name, 48);
dl_strcpy(buf[count].detail, detail, 48);
count++;
};
// CPU cores (category 0)
int cpuCount = Hal::GetDetectedCpuCount();
if (cpuCount > 0) {
char detail[48];
int p = 0;
p = dl_append(detail, p, "x86_64, ", 48);
p = dl_append_dec(detail, p, cpuCount, 48);
p = dl_append(detail, p, " core(s)", 48);
add(0, "Processor", detail);
}
// Interrupt controllers (category 1)
add(1, "Local APIC", "");
add(1, "I/O APIC", "");
// Timer (category 2)
add(2, "LAPIC Timer", "Local APIC periodic timer");
// PS/2 Input (category 3)
add(3, "PS/2 Keyboard", "IRQ 1");
if (Drivers::PS2::IsDualChannel()) {
add(3, "PS/2 Mouse", "IRQ 12");
}
// USB devices (category 4)
if (Drivers::USB::Xhci::IsInitialized()) {
for (uint8_t slot = 1; slot <= Drivers::USB::Xhci::MAX_SLOTS && count < maxCount; slot++) {
auto* dev = Drivers::USB::Xhci::GetDevice(slot);
if (!dev || !dev->Active) continue;
const char* devName = "USB Device";
if (dev->InterfaceClass == 3) {
if (dev->InterfaceProtocol == 1) devName = "USB HID Keyboard";
else if (dev->InterfaceProtocol == 2) devName = "USB HID Mouse";
else devName = "USB HID Device";
} else if (dev->InterfaceClass == 8) {
devName = "USB Mass Storage";
} else if (dev->InterfaceClass == 9) {
devName = "USB Hub";
}
char detail[48];
int p = 0;
p = dl_append(detail, p, "Port ", 48);
p = dl_append_dec(detail, p, dev->PortId, 48);
p = dl_append(detail, p, ", VID:", 48);
p = dl_append_hex(detail, p, dev->VendorId, 4, 48);
p = dl_append(detail, p, " PID:", 48);
p = dl_append_hex(detail, p, dev->ProductId, 4, 48);
add(4, devName, detail);
}
}
// Network (category 5)
if (Drivers::Net::E1000::IsInitialized()) {
add(5, "Intel E1000", "Gigabit Ethernet (82540EM)");
}
if (Drivers::Net::E1000E::IsInitialized()) {
add(5, "Intel E1000E", "Gigabit Ethernet (82574L)");
}
// Display (category 6)
if (Drivers::Graphics::IntelGPU::IsInitialized()) {
auto* gpu = Drivers::Graphics::IntelGPU::GetGpuInfo();
if (gpu) {
add(6, gpu->name, "Intel Integrated Graphics");
}
}
// PCI devices (category 7)
auto& pciDevs = Pci::GetDevices();
for (int i = 0; i < (int)pciDevs.size() && count < maxCount; i++) {
auto& d = pciDevs[i];
const char* className = Pci::GetClassName(d.ClassCode, d.SubClass);
char detail[48];
int p = 0;
p = dl_append_hex(detail, p, d.Bus, 2, 48);
p = dl_append(detail, p, ":", 48);
p = dl_append_hex(detail, p, d.Device, 2, 48);
p = dl_append(detail, p, ".", 48);
p = dl_append_dec(detail, p, d.Function, 48);
p = dl_append(detail, p, " ", 48);
p = dl_append_hex(detail, p, d.VendorId, 4, 48);
p = dl_append(detail, p, ":", 48);
p = dl_append_hex(detail, p, d.DeviceId, 4, 48);
add(7, className, detail);
}
return count;
}
};
+75
View File
@@ -0,0 +1,75 @@
/*
* Filesystem.hpp
* SYS_OPEN, SYS_READ, SYS_GETSIZE, SYS_CLOSE, SYS_READDIR,
* SYS_FWRITE, SYS_FCREATE syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Fs/Vfs.hpp>
#include <Sched/Scheduler.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include <Memory/HHDM.hpp>
#include <Memory/Paging.hpp>
#include <Libraries/Memory.hpp>
namespace Zenith {
static int Sys_Open(const char* path) {
return Fs::Vfs::VfsOpen(path);
}
static int Sys_Read(int handle, uint8_t* buffer, uint64_t offset, uint64_t size) {
return Fs::Vfs::VfsRead(handle, buffer, offset, size);
}
static uint64_t Sys_GetSize(int handle) {
return Fs::Vfs::VfsGetSize(handle);
}
static void Sys_Close(int handle) {
Fs::Vfs::VfsClose(handle);
}
static int Sys_ReadDir(const char* path, const char** outNames, int maxEntries) {
// Get entries from VFS into a kernel-local array
const char* kernelNames[64];
int max = maxEntries;
if (max > 64) max = 64;
int count = Fs::Vfs::VfsReadDir(path, kernelNames, max);
if (count <= 0) return count;
// Allocate a user-accessible page for string data via process heap
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return -1;
void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) return -1;
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
uint64_t userVa = proc->heapNext;
proc->heapNext += 0x1000;
Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa);
// Copy strings into the user page and write pointers to outNames
uint64_t offset = 0;
uint8_t* pageBuf = (uint8_t*)Memory::HHDM(physAddr);
int copied = 0;
for (int i = 0; i < count; i++) {
int len = Lib::strlen(kernelNames[i]) + 1;
if (offset + len > 0x1000) break;
memcpy(pageBuf + offset, kernelNames[i], len);
outNames[i] = (const char*)(userVa + offset);
offset += len;
copied++;
}
return copied;
}
static int Sys_FWrite(int handle, const uint8_t* data, uint64_t offset, uint64_t size) {
return Fs::Vfs::VfsWrite(handle, data, offset, size);
}
static int Sys_FCreate(const char* path) {
return Fs::Vfs::VfsCreate(path);
}
};
+86
View File
@@ -0,0 +1,86 @@
/*
* Graphics.hpp
* SYS_FBINFO, SYS_FBMAP, SYS_TERMSIZE, SYS_TERMSCALE syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <Sched/Scheduler.hpp>
#include <Memory/Paging.hpp>
#include <Memory/HHDM.hpp>
#include <Graphics/Cursor.hpp>
#include <Terminal/Terminal.hpp>
#include "Syscall.hpp"
#include "Common.hpp"
#include "../Libraries/flanterm/src/flanterm.h"
namespace Zenith {
static void Sys_FbInfo(FbInfo* out) {
if (out == nullptr) return;
out->width = Graphics::Cursor::GetFramebufferWidth();
out->height = Graphics::Cursor::GetFramebufferHeight();
out->pitch = Graphics::Cursor::GetFramebufferPitch();
out->bpp = 32;
out->userAddr = 0;
}
static uint64_t Sys_FbMap() {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
uint32_t* fbBase = Graphics::Cursor::GetFramebufferBase();
if (fbBase == nullptr) return 0;
uint64_t fbPhys = Memory::SubHHDM((uint64_t)fbBase);
uint64_t fbSize = Graphics::Cursor::GetFramebufferHeight()
* Graphics::Cursor::GetFramebufferPitch();
uint64_t numPages = (fbSize + 0xFFF) / 0x1000;
Kt::KernelLogStream(Kt::INFO, "FbMap") << "fbPhys=" << kcp::hex << fbPhys
<< " size=" << kcp::dec << fbSize
<< " pages=" << numPages
<< " (" << Graphics::Cursor::GetFramebufferWidth()
<< "x" << Graphics::Cursor::GetFramebufferHeight()
<< " pitch=" << Graphics::Cursor::GetFramebufferPitch() << ")";
// Map at a fixed user VA
constexpr uint64_t userVa = 0x50000000ULL;
for (uint64_t i = 0; i < numPages; i++) {
Memory::VMM::Paging::MapUserInWC(
proc->pml4Phys,
fbPhys + i * 0x1000,
userVa + i * 0x1000
);
}
return userVa;
}
static uint64_t Sys_TermSize() {
// If the process is redirected to a GUI terminal, return those dimensions
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target && target->termCols > 0 && target->termRows > 0) {
return ((uint64_t)target->termRows << 32) | ((uint64_t)target->termCols & 0xFFFFFFFF);
}
}
size_t cols = 0, rows = 0;
flanterm_get_dimensions(Kt::ctx, &cols, &rows);
return (rows << 32) | (cols & 0xFFFFFFFF);
}
static int64_t Sys_TermScale(uint64_t scale_x, uint64_t scale_y) {
if (scale_x == 0) {
return (int64_t)((Kt::GetFontScaleY() << 32) | (Kt::GetFontScaleX() & 0xFFFFFFFF));
}
Kt::Rescale((size_t)scale_x, (size_t)scale_y);
size_t cols = 0, rows = 0;
flanterm_get_dimensions(Kt::ctx, &cols, &rows);
return (int64_t)((rows << 32) | (cols & 0xFFFFFFFF));
}
};
+39
View File
@@ -0,0 +1,39 @@
/*
* Heap.hpp
* SYS_ALLOC, SYS_FREE syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#include <cstdint>
#include <Sched/Scheduler.hpp>
#include <Memory/Paging.hpp>
#include <Memory/HHDM.hpp>
#include <Memory/PageFrameAllocator.hpp>
namespace Zenith {
static uint64_t Sys_Alloc(uint64_t size) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
// Round up to page boundary
size = (size + 0xFFF) & ~0xFFFULL;
if (size == 0) size = 0x1000;
uint64_t userVa = proc->heapNext;
uint64_t numPages = size / 0x1000;
for (uint64_t i = 0; i < numPages; i++) {
void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) return 0;
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa + i * 0x1000);
}
proc->heapNext += size;
return userVa;
}
static void Sys_Free(uint64_t) {
// TODO No-op for now (pages leak)
}
};
+28
View File
@@ -0,0 +1,28 @@
/*
* Info.hpp
* SYS_GETINFO syscall
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Sched/Scheduler.hpp>
#include "Syscall.hpp"
namespace Zenith {
static void Sys_GetInfo(SysInfo* outInfo) {
if (outInfo == nullptr) return;
// Copy strings into fixed-size arrays (user-accessible)
const char* name = "ZenithOS";
const char* ver = "0.1.0";
for (int i = 0; name[i]; i++) outInfo->osName[i] = name[i];
outInfo->osName[8] = '\0';
for (int i = 0; ver[i]; i++) outInfo->osVersion[i] = ver[i];
outInfo->osVersion[5] = '\0';
outInfo->apiVersion = 2;
outInfo->maxProcesses = Sched::MaxProcesses;
}
};
+74
View File
@@ -0,0 +1,74 @@
/*
* IoRedir.hpp
* SYS_SPAWN_REDIR, SYS_CHILDIO_READ, SYS_CHILDIO_WRITE,
* SYS_CHILDIO_WRITEKEY, SYS_CHILDIO_SETTERMSZ syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Sched/Scheduler.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include "Syscall.hpp"
#include "Common.hpp"
namespace Zenith {
static int Sys_SpawnRedir(const char* path, const char* args) {
int childPid = Sched::Spawn(path, args);
if (childPid < 0) return -1;
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr) return -1;
// Allocate ring buffers
void* outPage = Memory::g_pfa->AllocateZeroed();
void* inPage = Memory::g_pfa->AllocateZeroed();
if (!outPage || !inPage) 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->parentPid = Sched::GetCurrentPid();
return childPid;
}
static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected || !child->outBuf) return -1;
return RingRead(child->outBuf, child->outHead, child->outTail, Sched::Process::IoBufSize, (uint8_t*)buf, maxLen);
}
static int Sys_ChildIoWrite(int childPid, const char* data, int len) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected || !child->inBuf) return -1;
for (int i = 0; i < len; i++) {
RingWrite(child->inBuf, child->inHead, child->inTail, Sched::Process::IoBufSize, (uint8_t)data[i]);
}
return len;
}
static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) {
if (key == nullptr) return -1;
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected) return -1;
child->keyBuf[child->keyHead] = *key;
child->keyHead = (child->keyHead + 1) % 64;
return 0;
}
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected) return -1;
child->termCols = cols;
child->termRows = rows;
return 0;
}
};
+63
View File
@@ -0,0 +1,63 @@
/*
* Keyboard.hpp
* SYS_ISKEYAVAILABLE, SYS_GETKEY, SYS_GETCHAR syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Sched/Scheduler.hpp>
#include <Drivers/PS2/Keyboard.hpp>
#include "Common.hpp"
namespace Zenith {
static bool Sys_IsKeyAvailable() {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target) return target->keyHead != target->keyTail;
}
return Drivers::PS2::Keyboard::IsKeyAvailable();
}
static void Sys_GetKey(KeyEvent* outEvent) {
if (outEvent == nullptr) return;
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target) {
// Wait for key in target's keyBuf ring
while (target->keyHead == target->keyTail) {
Sched::Schedule();
}
*outEvent = target->keyBuf[target->keyTail];
target->keyTail = (target->keyTail + 1) % 64;
return;
}
}
auto k = Drivers::PS2::Keyboard::GetKey();
outEvent->scancode = k.Scancode;
outEvent->ascii = k.Ascii;
outEvent->pressed = k.Pressed;
outEvent->shift = k.Shift;
outEvent->ctrl = k.Ctrl;
outEvent->alt = k.Alt;
}
static char Sys_GetChar() {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target && target->inBuf) {
// Wait for data in target's inBuf ring
while (target->inTail == target->inHead) {
Sched::Schedule(); // yield until parent writes
}
uint8_t c = target->inBuf[target->inTail];
target->inTail = (target->inTail + 1) % Sched::Process::IoBufSize;
return (char)c;
}
}
return Drivers::PS2::Keyboard::GetChar();
}
};
+18
View File
@@ -0,0 +1,18 @@
/*
* MemInfo.hpp
* SYS_MEMSTATS syscall
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Memory/PageFrameAllocator.hpp>
#include "Syscall.hpp"
namespace Zenith {
static void Sys_MemStats(MemStats* out) {
if (out == nullptr) return;
Memory::g_pfa->GetStats(out);
}
};
+26
View File
@@ -0,0 +1,26 @@
/*
* Mouse.hpp
* SYS_MOUSESTATE, SYS_SETMOUSEBOUNDS syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Drivers/PS2/Mouse.hpp>
#include "Syscall.hpp"
namespace Zenith {
static void Sys_MouseState(MouseState* out) {
if (out == nullptr) return;
auto state = Drivers::PS2::Mouse::GetMouseState();
out->x = state.X;
out->y = state.Y;
out->scrollDelta = state.ScrollDelta;
out->buttons = state.Buttons;
}
static void Sys_SetMouseBounds(int32_t maxX, int32_t maxY) {
Drivers::PS2::Mouse::SetBounds(maxX, maxY);
}
};
+125
View File
@@ -0,0 +1,125 @@
/*
* Net.hpp
* Networking syscalls: SYS_PING, SYS_SOCKET, SYS_CONNECT, SYS_BIND,
* SYS_LISTEN, SYS_ACCEPT, SYS_SEND, SYS_RECV, SYS_CLOSESOCK,
* SYS_SENDTO, SYS_RECVFROM, SYS_GETNETCFG, SYS_SETNETCFG, SYS_RESOLVE
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <Sched/Scheduler.hpp>
#include <Timekeeping/ApicTimer.hpp>
#include <Net/Icmp.hpp>
#include <Net/Dns.hpp>
#include <Net/Socket.hpp>
#include <Net/NetConfig.hpp>
#include <Drivers/Net/E1000.hpp>
#include <Drivers/Net/E1000E.hpp>
#include "Syscall.hpp"
namespace Zenith {
static uint16_t g_pingSeq = 0;
static constexpr uint16_t PING_ID = 0x2E01; // "ZE"
static int32_t Sys_Ping(uint32_t ipAddr, uint32_t timeoutMs) {
uint16_t seq = g_pingSeq++;
Net::Icmp::ResetReply();
Net::Icmp::SendEchoRequest(ipAddr, PING_ID, seq);
uint64_t start = Timekeeping::GetMilliseconds();
while (!Net::Icmp::HasReply(PING_ID, seq)) {
if (Timekeeping::GetMilliseconds() - start >= timeoutMs) {
return -1;
}
Sched::Schedule();
}
return (int32_t)(Timekeeping::GetMilliseconds() - start);
}
// ---- 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 int Sys_SendTo(int fd, const uint8_t* data, uint32_t len,
uint32_t destIp, uint16_t destPort) {
return Net::Socket::SendTo(fd, data, len, destIp, destPort, Sched::GetCurrentPid());
}
static int Sys_RecvFrom(int fd, uint8_t* buf, uint32_t maxLen,
uint32_t* srcIp, uint16_t* srcPort) {
return Net::Socket::RecvFrom(fd, buf, maxLen, srcIp, srcPort, Sched::GetCurrentPid());
}
static void Sys_GetNetCfg(NetCfg* out) {
if (out == nullptr) return;
out->ipAddress = Net::GetIpAddress();
out->subnetMask = Net::GetSubnetMask();
out->gateway = Net::GetGateway();
const uint8_t* mac = nullptr;
if (Drivers::Net::E1000::IsInitialized()) {
mac = Drivers::Net::E1000::GetMacAddress();
} else if (Drivers::Net::E1000E::IsInitialized()) {
mac = Drivers::Net::E1000E::GetMacAddress();
}
if (mac) {
for (int i = 0; i < 6; i++) out->macAddress[i] = mac[i];
} else {
for (int i = 0; i < 6; i++) out->macAddress[i] = 0;
}
out->_pad[0] = 0;
out->_pad[1] = 0;
out->dnsServer = Net::GetDnsServer();
}
static int Sys_SetNetCfg(const NetCfg* in) {
if (in == nullptr) return -1;
Net::SetIpAddress(in->ipAddress);
Net::SetSubnetMask(in->subnetMask);
Net::SetGateway(in->gateway);
Net::SetDnsServer(in->dnsServer);
return 0;
}
// ---- DNS resolve ----
static int64_t Sys_Resolve(const char* hostname) {
uint32_t ip = Net::Dns::Resolve(hostname);
return (int64_t)ip;
}
};
+28
View File
@@ -0,0 +1,28 @@
/*
* Power.hpp
* SYS_RESET, SYS_SHUTDOWN syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
namespace Zenith {
static void Sys_Reset() {
/*
Triple fault for now; TODO: implement UEFI runtime function for clean reboot.
We implement the triple fault by loading a null IDT into the IDT register,
and then immediately triggering an interrupt.
This technique should pretty much work across the board but it's of course
better to use the UEFI runtime API as it has a method for this purpose,
along with shutdown.
*/
struct [[gnu::packed]] { uint16_t limit; uint64_t base; } nullIdt = {0, 0};
asm volatile("lidt %0; int $0x03" :: "m"(nullIdt));
__builtin_unreachable();
}
};
+114
View File
@@ -0,0 +1,114 @@
/*
* Process.hpp
* SYS_EXIT, SYS_YIELD, SYS_SLEEP_MS, SYS_GETPID,
* SYS_WAITPID, SYS_SPAWN, SYS_GETARGS, SYS_PROCLIST, SYS_KILL syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Sched/Scheduler.hpp>
#include <Timekeeping/ApicTimer.hpp>
#include "Syscall.hpp"
#include "WinServer.hpp"
namespace Zenith {
static void Sys_Exit(int exitCode) {
(void)exitCode;
Sched::ExitProcess();
}
static void Sys_Yield() {
Sched::Schedule();
}
static void Sys_SleepMs(uint64_t ms) {
Timekeeping::Sleep(ms);
}
static int Sys_GetPid() {
return Sched::GetCurrentPid();
}
static void Sys_WaitPid(int pid) {
while (Sched::IsAlive(pid)) {
Sched::Schedule(); // yield until the process exits
}
}
static int Sys_Spawn(const char* path, const char* args) {
auto* parent = Sched::GetCurrentProcessPtr();
int childPid = Sched::Spawn(path, args);
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) {
auto* child = Sched::GetProcessByPid(childPid);
if (child) {
child->redirected = true;
// Point to the buffer owner: if parent owns buffers, target parent;
// if parent itself inherited, follow the chain.
child->parentPid = parent->outBuf ? parent->pid : parent->parentPid;
}
}
return childPid;
}
static int Sys_GetArgs(char* buf, uint64_t maxLen) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr || buf == nullptr || maxLen == 0) return -1;
int i = 0;
for (; i < (int)maxLen - 1 && proc->args[i]; i++) {
buf[i] = proc->args[i];
}
buf[i] = '\0';
return i;
}
static int Sys_ProcList(ProcInfo* buf, int maxCount) {
if (buf == nullptr || maxCount <= 0) return 0;
int count = 0;
for (int i = 0; i < Sched::MaxProcesses && count < maxCount; i++) {
auto* proc = Sched::GetProcessSlot(i);
if (!proc || proc->state == Sched::ProcessState::Free) continue;
buf[count].pid = (int32_t)proc->pid;
buf[count].parentPid = (int32_t)proc->parentPid;
buf[count].state = (uint8_t)proc->state;
buf[count]._pad[0] = 0;
buf[count]._pad[1] = 0;
buf[count]._pad[2] = 0;
{
int j = 0;
for (; j < 63 && proc->name[j]; j++)
buf[count].name[j] = proc->name[j];
buf[count].name[j] = '\0';
}
buf[count].heapUsed = (proc->heapNext > Sched::UserHeapBase)
? proc->heapNext - Sched::UserHeapBase : 0;
count++;
}
return count;
}
static int Sys_Kill(int pid) {
// Refuse to kill PID 0 (init)
if (pid == 0) return -1;
// Refuse to kill the caller's own process
if (pid == Sched::GetCurrentPid()) return -1;
auto* proc = Sched::GetProcessByPid(pid);
if (!proc) return -1;
// Clean up any windows owned by this process
WinServer::CleanupProcess(pid);
proc->state = Sched::ProcessState::Terminated;
return 0;
}
};
+39
View File
@@ -0,0 +1,39 @@
/*
* Random.hpp
* SYS_GETRANDOM syscall
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
namespace Zenith {
// ---- Random number generation ----
// Uses RDTSC mixed with xorshift64* PRNG for entropy.
// RDRAND is intentionally avoided: some firmware disables the RDRAND
// hardware unit while CPUID still advertises support (bit 30 of ECX),
// causing #UD on real hardware. RDTSC-based entropy is sufficient for
// seeding BearSSL's PRNG for TLS session keys.
static int64_t Sys_GetRandom(uint8_t* buf, uint64_t len) {
uint64_t tsc;
asm volatile("rdtsc; shl $32, %%rdx; or %%rdx, %%rax" : "=a"(tsc) :: "rdx");
uint64_t state = tsc;
for (uint64_t i = 0; i < len; i += 8) {
asm volatile("rdtsc; shl $32, %%rdx; or %%rdx, %%rax" : "=a"(tsc) :: "rdx");
state ^= tsc;
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
uint64_t val = state * 0x2545F4914F6CDD1DULL;
uint64_t remaining = len - i;
uint64_t toCopy = remaining < 8 ? remaining : 8;
for (uint64_t j = 0; j < toCopy; j++)
buf[i + j] = (uint8_t)(val >> (j * 8));
}
return (int64_t)len;
}
};
+21 -845
View File
@@ -5,860 +5,36 @@
*/ */
#include "Syscall.hpp" #include "Syscall.hpp"
#include <Timekeeping/Time.hpp>
#include <Terminal/Terminal.hpp> #include <Terminal/Terminal.hpp>
#include <Fs/Vfs.hpp>
#include <Memory/Heap.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include <Memory/Paging.hpp>
#include <Memory/HHDM.hpp>
#include <Timekeeping/ApicTimer.hpp>
#include <Sched/Scheduler.hpp>
#include <Libraries/Memory.hpp>
#include <Libraries/String.hpp>
#include <Drivers/PS2/Keyboard.hpp>
#include <Drivers/PS2/Mouse.hpp>
#include <Net/Icmp.hpp>
#include <Net/Dns.hpp>
#include <Net/Socket.hpp>
#include <Net/ByteOrder.hpp>
#include <Net/NetConfig.hpp>
#include <Drivers/Net/E1000.hpp>
#include <Drivers/Net/E1000E.hpp>
#include <Hal/MSR.hpp> #include <Hal/MSR.hpp>
#include <Hal/GDT.hpp> #include <Hal/GDT.hpp>
#include <Graphics/Cursor.hpp>
#include "../Libraries/flanterm/src/flanterm.h" /* For common functions used by multiple syscall implementations*/
#include "WinServer.hpp" #include "Common.hpp"
#include <Pci/Pci.hpp>
#include <Drivers/USB/Xhci.hpp> /* Syscall impl. includes */
#include <Drivers/Graphics/IntelGPU.hpp> #include "Process.hpp" // SYS_EXIT, SYS_YIELD, SYS_SLEEP_MS, SYS_GETPID, SYS_WAITPID, SYS_SPAWN, SYS_GETARGS, SYS_PROCLIST, SYS_KILL
#include <Drivers/PS2/PS2Controller.hpp> #include "Terminal.hpp" // SYS_PRINT, SYS_PUTCHAR
#include <Hal/Apic/ApicInit.hpp> #include "Filesystem.hpp" // SYS_OPEN, SYS_READ, SYS_GETSIZE, SYS_CLOSE, SYS_READDIR, SYS_FWRITE, SYS_FCREATE
#include "Heap.hpp" // SYS_ALLOC, SYS_FREE
#include "Time.hpp" // SYS_GETTICKS, SYS_GETMILLISECONDS, SYS_GETTIME
#include "Keyboard.hpp" // SYS_ISKEYAVAILABLE, SYS_GETKEY, SYS_GETCHAR
#include "Info.hpp" // SYS_GETINFO
#include "Graphics.hpp" // SYS_FBINFO, SYS_FBMAP, SYS_TERMSIZE, SYS_TERMSCALE
#include "Net.hpp" // SYS_PING, SYS_SOCKET, SYS_CONNECT, SYS_BIND, SYS_LISTEN, SYS_ACCEPT, SYS_SEND, SYS_RECV, SYS_CLOSESOCK, SYS_SENDTO, SYS_RECVFROM, SYS_GETNETCFG, SYS_SETNETCFG, SYS_RESOLVE
#include "Power.hpp" // SYS_RESET, SYS_SHUTDOWN
#include "Mouse.hpp" // SYS_MOUSESTATE, SYS_SETMOUSEBOUNDS
#include "IoRedir.hpp" // SYS_SPAWN_REDIR, SYS_CHILDIO_READ, SYS_CHILDIO_WRITE, SYS_CHILDIO_WRITEKEY, SYS_CHILDIO_SETTERMSZ
#include "Random.hpp" // SYS_GETRANDOM
#include "MemInfo.hpp" // SYS_MEMSTATS
#include "Device.hpp" // SYS_DEVLIST
#include "Window.hpp" // SYS_WINCREATE, SYS_WINDESTROY, SYS_WINPRESENT, SYS_WINPOLL, SYS_WINENUM, SYS_WINMAP, SYS_WINSENDEVENT, SYS_WINRESIZE, SYS_WINSETSCALE, SYS_WINGETSCALE
// Assembly entry point // Assembly entry point
extern "C" void SyscallEntry(); extern "C" void SyscallEntry();
namespace Zenith { namespace Zenith {
// ---- Syscall implementations ----
static void Sys_Exit(int exitCode) {
(void)exitCode;
Sched::ExitProcess();
}
static void Sys_Yield() {
Sched::Schedule();
}
static void Sys_SleepMs(uint64_t ms) {
Timekeeping::Sleep(ms);
}
static int Sys_GetPid() {
return Sched::GetCurrentPid();
}
static void RingWrite(uint8_t* buf, uint32_t& head, uint32_t /*tail*/, uint32_t size, uint8_t byte) {
buf[head] = byte;
head = (head + 1) % size;
}
static int RingRead(uint8_t* buf, uint32_t& head, uint32_t& tail, uint32_t size, uint8_t* out, int maxLen) {
int count = 0;
while (tail != head && count < maxLen) {
out[count++] = buf[tail];
tail = (tail + 1) % size;
}
return count;
}
// 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) {
if (!proc || !proc->redirected) return nullptr;
if (proc->outBuf) return proc; // owns buffers
return Sched::GetProcessByPid(proc->parentPid);
}
static void Sys_Print(const char* text) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target && target->outBuf) {
for (int i = 0; text[i]; i++) {
RingWrite(target->outBuf, target->outHead, target->outTail, Sched::Process::IoBufSize, (uint8_t)text[i]);
}
return;
}
}
Kt::Print(text);
}
static void Sys_Putchar(char c) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target && target->outBuf) {
RingWrite(target->outBuf, target->outHead, target->outTail, Sched::Process::IoBufSize, (uint8_t)c);
return;
}
}
Kt::Putchar(c);
}
static int Sys_Open(const char* path) {
return Fs::Vfs::VfsOpen(path);
}
static int Sys_Read(int handle, uint8_t* buffer, uint64_t offset, uint64_t size) {
return Fs::Vfs::VfsRead(handle, buffer, offset, size);
}
static uint64_t Sys_GetSize(int handle) {
return Fs::Vfs::VfsGetSize(handle);
}
static void Sys_Close(int handle) {
Fs::Vfs::VfsClose(handle);
}
static int Sys_ReadDir(const char* path, const char** outNames, int maxEntries) {
// Get entries from VFS into a kernel-local array
const char* kernelNames[64];
int max = maxEntries;
if (max > 64) max = 64;
int count = Fs::Vfs::VfsReadDir(path, kernelNames, max);
if (count <= 0) return count;
// Allocate a user-accessible page for string data via process heap
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return -1;
void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) return -1;
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
uint64_t userVa = proc->heapNext;
proc->heapNext += 0x1000;
Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa);
// Copy strings into the user page and write pointers to outNames
uint64_t offset = 0;
uint8_t* pageBuf = (uint8_t*)Memory::HHDM(physAddr);
int copied = 0;
for (int i = 0; i < count; i++) {
int len = Lib::strlen(kernelNames[i]) + 1;
if (offset + len > 0x1000) break;
memcpy(pageBuf + offset, kernelNames[i], len);
outNames[i] = (const char*)(userVa + offset);
offset += len;
copied++;
}
return copied;
}
static uint64_t Sys_Alloc(uint64_t size) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
// Round up to page boundary
size = (size + 0xFFF) & ~0xFFFULL;
if (size == 0) size = 0x1000;
uint64_t userVa = proc->heapNext;
uint64_t numPages = size / 0x1000;
for (uint64_t i = 0; i < numPages; i++) {
void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) return 0;
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa + i * 0x1000);
}
proc->heapNext += size;
return userVa;
}
static void Sys_Free(uint64_t) {
// No-op for now (pages leak). Proper freeing can come later.
}
static uint64_t Sys_GetTicks() {
return Timekeeping::GetTicks();
}
static uint64_t Sys_GetMilliseconds() {
return Timekeeping::GetMilliseconds();
}
static void Sys_GetInfo(SysInfo* outInfo) {
if (outInfo == nullptr) return;
// Copy strings into fixed-size arrays (user-accessible)
const char* name = "ZenithOS";
const char* ver = "0.1.0";
for (int i = 0; name[i]; i++) outInfo->osName[i] = name[i];
outInfo->osName[8] = '\0';
for (int i = 0; ver[i]; i++) outInfo->osVersion[i] = ver[i];
outInfo->osVersion[5] = '\0';
outInfo->apiVersion = 2;
outInfo->maxProcesses = Sched::MaxProcesses;
}
static bool Sys_IsKeyAvailable() {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target) return target->keyHead != target->keyTail;
}
return Drivers::PS2::Keyboard::IsKeyAvailable();
}
static void Sys_GetKey(KeyEvent* outEvent) {
if (outEvent == nullptr) return;
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target) {
// Wait for key in target's keyBuf ring
while (target->keyHead == target->keyTail) {
Sched::Schedule();
}
*outEvent = target->keyBuf[target->keyTail];
target->keyTail = (target->keyTail + 1) % 64;
return;
}
}
auto k = Drivers::PS2::Keyboard::GetKey();
outEvent->scancode = k.Scancode;
outEvent->ascii = k.Ascii;
outEvent->pressed = k.Pressed;
outEvent->shift = k.Shift;
outEvent->ctrl = k.Ctrl;
outEvent->alt = k.Alt;
}
static char Sys_GetChar() {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target && target->inBuf) {
// Wait for data in target's inBuf ring
while (target->inTail == target->inHead) {
Sched::Schedule(); // yield until parent writes
}
uint8_t c = target->inBuf[target->inTail];
target->inTail = (target->inTail + 1) % Sched::Process::IoBufSize;
return (char)c;
}
}
return Drivers::PS2::Keyboard::GetChar();
}
static uint16_t g_pingSeq = 0;
static constexpr uint16_t PING_ID = 0x2E01; // "ZE"
static void Sys_FbInfo(FbInfo* out) {
if (out == nullptr) return;
out->width = Graphics::Cursor::GetFramebufferWidth();
out->height = Graphics::Cursor::GetFramebufferHeight();
out->pitch = Graphics::Cursor::GetFramebufferPitch();
out->bpp = 32;
out->userAddr = 0;
}
static void Sys_WaitPid(int pid) {
while (Sched::IsAlive(pid)) {
Sched::Schedule(); // yield until the process exits
}
}
static uint64_t Sys_FbMap() {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
uint32_t* fbBase = Graphics::Cursor::GetFramebufferBase();
if (fbBase == nullptr) return 0;
uint64_t fbPhys = Memory::SubHHDM((uint64_t)fbBase);
uint64_t fbSize = Graphics::Cursor::GetFramebufferHeight()
* Graphics::Cursor::GetFramebufferPitch();
uint64_t numPages = (fbSize + 0xFFF) / 0x1000;
Kt::KernelLogStream(Kt::INFO, "FbMap") << "fbPhys=" << kcp::hex << fbPhys
<< " size=" << kcp::dec << fbSize
<< " pages=" << numPages
<< " (" << Graphics::Cursor::GetFramebufferWidth()
<< "x" << Graphics::Cursor::GetFramebufferHeight()
<< " pitch=" << Graphics::Cursor::GetFramebufferPitch() << ")";
// Map at a fixed user VA
constexpr uint64_t userVa = 0x50000000ULL;
for (uint64_t i = 0; i < numPages; i++) {
Memory::VMM::Paging::MapUserInWC(
proc->pml4Phys,
fbPhys + i * 0x1000,
userVa + i * 0x1000
);
}
return userVa;
}
static int32_t Sys_Ping(uint32_t ipAddr, uint32_t timeoutMs) {
uint16_t seq = g_pingSeq++;
Net::Icmp::ResetReply();
Net::Icmp::SendEchoRequest(ipAddr, PING_ID, seq);
uint64_t start = Timekeeping::GetMilliseconds();
while (!Net::Icmp::HasReply(PING_ID, seq)) {
if (Timekeeping::GetMilliseconds() - start >= timeoutMs) {
return -1;
}
Sched::Schedule();
}
return (int32_t)(Timekeeping::GetMilliseconds() - start);
}
static int Sys_Spawn(const char* path, const char* args) {
auto* parent = Sched::GetCurrentProcessPtr();
int childPid = Sched::Spawn(path, args);
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) {
auto* child = Sched::GetProcessByPid(childPid);
if (child) {
child->redirected = true;
// Point to the buffer owner: if parent owns buffers, target parent;
// if parent itself inherited, follow the chain.
child->parentPid = parent->outBuf ? parent->pid : parent->parentPid;
}
}
return childPid;
}
static int Sys_GetArgs(char* buf, uint64_t maxLen) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr || buf == nullptr || maxLen == 0) return -1;
int i = 0;
for (; i < (int)maxLen - 1 && proc->args[i]; i++) {
buf[i] = proc->args[i];
}
buf[i] = '\0';
return i;
}
static uint64_t Sys_TermSize() {
// If the process is redirected to a GUI terminal, return those dimensions
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target && target->termCols > 0 && target->termRows > 0) {
return ((uint64_t)target->termRows << 32) | ((uint64_t)target->termCols & 0xFFFFFFFF);
}
}
size_t cols = 0, rows = 0;
flanterm_get_dimensions(Kt::ctx, &cols, &rows);
return (rows << 32) | (cols & 0xFFFFFFFF);
}
static void Sys_GetTime(DateTime* out) {
if (out == nullptr) return;
Timekeeping::DateTime dt = Timekeeping::GetDateTime();
out->Year = dt.Year;
out->Month = dt.Month;
out->Day = dt.Day;
out->Hour = dt.Hour;
out->Minute = dt.Minute;
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 int Sys_SendTo(int fd, const uint8_t* data, uint32_t len,
uint32_t destIp, uint16_t destPort) {
return Net::Socket::SendTo(fd, data, len, destIp, destPort, Sched::GetCurrentPid());
}
static int Sys_RecvFrom(int fd, uint8_t* buf, uint32_t maxLen,
uint32_t* srcIp, uint16_t* srcPort) {
return Net::Socket::RecvFrom(fd, buf, maxLen, srcIp, srcPort, Sched::GetCurrentPid());
}
static void Sys_GetNetCfg(NetCfg* out) {
if (out == nullptr) return;
out->ipAddress = Net::GetIpAddress();
out->subnetMask = Net::GetSubnetMask();
out->gateway = Net::GetGateway();
const uint8_t* mac = nullptr;
if (Drivers::Net::E1000::IsInitialized()) {
mac = Drivers::Net::E1000::GetMacAddress();
} else if (Drivers::Net::E1000E::IsInitialized()) {
mac = Drivers::Net::E1000E::GetMacAddress();
}
if (mac) {
for (int i = 0; i < 6; i++) out->macAddress[i] = mac[i];
} else {
for (int i = 0; i < 6; i++) out->macAddress[i] = 0;
}
out->_pad[0] = 0;
out->_pad[1] = 0;
out->dnsServer = Net::GetDnsServer();
}
static int Sys_SetNetCfg(const NetCfg* in) {
if (in == nullptr) return -1;
Net::SetIpAddress(in->ipAddress);
Net::SetSubnetMask(in->subnetMask);
Net::SetGateway(in->gateway);
Net::SetDnsServer(in->dnsServer);
return 0;
}
static void Sys_Reset() {
/*
Triple fault for now; TODO: implement UEFI runtime function for clean reboot.
We implement the triple fault by loading a null IDT into the IDT register,
and then immediately triggering an interrupt.
This technique should pretty much work across the board but it's of course
better to use the UEFI runtime API as it has a method for this purpose,
along with shutdown.
*/
struct [[gnu::packed]] { uint16_t limit; uint64_t base; } nullIdt = {0, 0};
asm volatile("lidt %0; int $0x03" :: "m"(nullIdt));
__builtin_unreachable();
}
// ---- File write/create ----
static int Sys_FWrite(int handle, const uint8_t* data, uint64_t offset, uint64_t size) {
return Fs::Vfs::VfsWrite(handle, data, offset, size);
}
static int Sys_FCreate(const char* path) {
return Fs::Vfs::VfsCreate(path);
}
// ---- Terminal scaling ----
static int64_t Sys_TermScale(uint64_t scale_x, uint64_t scale_y) {
if (scale_x == 0) {
return (int64_t)((Kt::GetFontScaleY() << 32) | (Kt::GetFontScaleX() & 0xFFFFFFFF));
}
Kt::Rescale((size_t)scale_x, (size_t)scale_y);
size_t cols = 0, rows = 0;
flanterm_get_dimensions(Kt::ctx, &cols, &rows);
return (int64_t)((rows << 32) | (cols & 0xFFFFFFFF));
}
// ---- DNS resolve ----
static int64_t Sys_Resolve(const char* hostname) {
uint32_t ip = Net::Dns::Resolve(hostname);
return (int64_t)ip;
}
// ---- Random number generation ----
// Uses RDTSC mixed with xorshift64* PRNG for entropy.
// RDRAND is intentionally avoided: some firmware disables the RDRAND
// hardware unit while CPUID still advertises support (bit 30 of ECX),
// causing #UD on real hardware. RDTSC-based entropy is sufficient for
// seeding BearSSL's PRNG for TLS session keys.
static int64_t Sys_GetRandom(uint8_t* buf, uint64_t len) {
uint64_t tsc;
asm volatile("rdtsc; shl $32, %%rdx; or %%rdx, %%rax" : "=a"(tsc) :: "rdx");
uint64_t state = tsc;
for (uint64_t i = 0; i < len; i += 8) {
asm volatile("rdtsc; shl $32, %%rdx; or %%rdx, %%rax" : "=a"(tsc) :: "rdx");
state ^= tsc;
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
uint64_t val = state * 0x2545F4914F6CDD1DULL;
uint64_t remaining = len - i;
uint64_t toCopy = remaining < 8 ? remaining : 8;
for (uint64_t j = 0; j < toCopy; j++)
buf[i + j] = (uint8_t)(val >> (j * 8));
}
return (int64_t)len;
}
// ---- Mouse syscalls ----
static void Sys_MouseState(MouseState* out) {
if (out == nullptr) return;
auto state = Drivers::PS2::Mouse::GetMouseState();
out->x = state.X;
out->y = state.Y;
out->scrollDelta = state.ScrollDelta;
out->buttons = state.Buttons;
}
static void Sys_SetMouseBounds(int32_t maxX, int32_t maxY) {
Drivers::PS2::Mouse::SetBounds(maxX, maxY);
}
// ---- I/O redirection syscalls ----
static int Sys_SpawnRedir(const char* path, const char* args) {
int childPid = Sched::Spawn(path, args);
if (childPid < 0) return -1;
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr) return -1;
// Allocate ring buffers
void* outPage = Memory::g_pfa->AllocateZeroed();
void* inPage = Memory::g_pfa->AllocateZeroed();
if (!outPage || !inPage) 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->parentPid = Sched::GetCurrentPid();
return childPid;
}
static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected || !child->outBuf) return -1;
return RingRead(child->outBuf, child->outHead, child->outTail, Sched::Process::IoBufSize, (uint8_t*)buf, maxLen);
}
static int Sys_ChildIoWrite(int childPid, const char* data, int len) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected || !child->inBuf) return -1;
for (int i = 0; i < len; i++) {
RingWrite(child->inBuf, child->inHead, child->inTail, Sched::Process::IoBufSize, (uint8_t)data[i]);
}
return len;
}
static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) {
if (key == nullptr) return -1;
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected) return -1;
child->keyBuf[child->keyHead] = *key;
child->keyHead = (child->keyHead + 1) % 64;
return 0;
}
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected) return -1;
child->termCols = cols;
child->termRows = rows;
return 0;
}
// ---- Process listing / kill ----
static int Sys_ProcList(ProcInfo* buf, int maxCount) {
if (buf == nullptr || maxCount <= 0) return 0;
int count = 0;
for (int i = 0; i < Sched::MaxProcesses && count < maxCount; i++) {
auto* proc = Sched::GetProcessSlot(i);
if (!proc || proc->state == Sched::ProcessState::Free) continue;
buf[count].pid = (int32_t)proc->pid;
buf[count].parentPid = (int32_t)proc->parentPid;
buf[count].state = (uint8_t)proc->state;
buf[count]._pad[0] = 0;
buf[count]._pad[1] = 0;
buf[count]._pad[2] = 0;
{
int j = 0;
for (; j < 63 && proc->name[j]; j++)
buf[count].name[j] = proc->name[j];
buf[count].name[j] = '\0';
}
buf[count].heapUsed = (proc->heapNext > Sched::UserHeapBase)
? proc->heapNext - Sched::UserHeapBase : 0;
count++;
}
return count;
}
static int Sys_Kill(int pid) {
// Refuse to kill PID 0 (init)
if (pid == 0) return -1;
// Refuse to kill the caller's own process
if (pid == Sched::GetCurrentPid()) return -1;
auto* proc = Sched::GetProcessByPid(pid);
if (!proc) return -1;
// Clean up any windows owned by this process
WinServer::CleanupProcess(pid);
proc->state = Sched::ProcessState::Terminated;
return 0;
}
// ---- Device list ----
static void dl_strcpy(char* dst, const char* src, int max) {
int i = 0;
for (; i < max - 1 && src[i]; i++) dst[i] = src[i];
dst[i] = '\0';
}
static int dl_append(char* dst, int pos, const char* src, int max) {
for (int i = 0; src[i] && pos < max - 1; i++) dst[pos++] = src[i];
dst[pos] = '\0';
return pos;
}
static int dl_append_hex(char* dst, int pos, unsigned val, int digits, int max) {
const char* hex = "0123456789abcdef";
char tmp[8];
for (int i = digits - 1; i >= 0; i--) { tmp[i] = hex[val & 0xF]; val >>= 4; }
for (int i = 0; i < digits && pos < max - 1; i++) dst[pos++] = tmp[i];
dst[pos] = '\0';
return pos;
}
static int dl_append_dec(char* dst, int pos, int val, int max) {
if (val == 0) { if (pos < max - 1) dst[pos++] = '0'; dst[pos] = '\0'; return pos; }
char tmp[12]; int i = 0;
while (val > 0) { tmp[i++] = '0' + (val % 10); val /= 10; }
while (i > 0 && pos < max - 1) dst[pos++] = tmp[--i];
dst[pos] = '\0';
return pos;
}
static int Sys_DevList(DevInfo* buf, int maxCount) {
if (buf == nullptr || maxCount <= 0) return 0;
int count = 0;
auto add = [&](uint8_t cat, const char* name, const char* detail) {
if (count >= maxCount) return;
buf[count].category = cat;
buf[count]._pad[0] = 0; buf[count]._pad[1] = 0; buf[count]._pad[2] = 0;
dl_strcpy(buf[count].name, name, 48);
dl_strcpy(buf[count].detail, detail, 48);
count++;
};
// CPU cores (category 0)
int cpuCount = Hal::GetDetectedCpuCount();
if (cpuCount > 0) {
char detail[48];
int p = 0;
p = dl_append(detail, p, "x86_64, ", 48);
p = dl_append_dec(detail, p, cpuCount, 48);
p = dl_append(detail, p, " core(s)", 48);
add(0, "Processor", detail);
}
// Interrupt controllers (category 1)
add(1, "Local APIC", "Per-CPU interrupt controller");
add(1, "I/O APIC", "System interrupt router");
// Timer (category 2)
add(2, "LAPIC Timer", "Local APIC periodic timer");
// PS/2 Input (category 3)
add(3, "PS/2 Keyboard", "IRQ 1, scan code set 1");
if (Drivers::PS2::IsDualChannel()) {
add(3, "PS/2 Mouse", "IRQ 12, dual-channel 8042");
}
// USB devices (category 4)
if (Drivers::USB::Xhci::IsInitialized()) {
for (uint8_t slot = 1; slot <= Drivers::USB::Xhci::MAX_SLOTS && count < maxCount; slot++) {
auto* dev = Drivers::USB::Xhci::GetDevice(slot);
if (!dev || !dev->Active) continue;
const char* devName = "USB Device";
if (dev->InterfaceClass == 3) {
if (dev->InterfaceProtocol == 1) devName = "USB HID Keyboard";
else if (dev->InterfaceProtocol == 2) devName = "USB HID Mouse";
else devName = "USB HID Device";
} else if (dev->InterfaceClass == 8) {
devName = "USB Mass Storage";
} else if (dev->InterfaceClass == 9) {
devName = "USB Hub";
}
char detail[48];
int p = 0;
p = dl_append(detail, p, "Port ", 48);
p = dl_append_dec(detail, p, dev->PortId, 48);
p = dl_append(detail, p, ", VID:", 48);
p = dl_append_hex(detail, p, dev->VendorId, 4, 48);
p = dl_append(detail, p, " PID:", 48);
p = dl_append_hex(detail, p, dev->ProductId, 4, 48);
add(4, devName, detail);
}
}
// Network (category 5)
if (Drivers::Net::E1000::IsInitialized()) {
add(5, "Intel E1000", "Gigabit Ethernet (82540EM)");
}
if (Drivers::Net::E1000E::IsInitialized()) {
add(5, "Intel E1000E", "Gigabit Ethernet (82574L)");
}
// Display (category 6)
if (Drivers::Graphics::IntelGPU::IsInitialized()) {
auto* gpu = Drivers::Graphics::IntelGPU::GetGpuInfo();
if (gpu) {
add(6, gpu->name, "Intel Integrated Graphics");
}
}
// PCI devices (category 7)
auto& pciDevs = Pci::GetDevices();
for (int i = 0; i < (int)pciDevs.size() && count < maxCount; i++) {
auto& d = pciDevs[i];
const char* className = Pci::GetClassName(d.ClassCode, d.SubClass);
char detail[48];
int p = 0;
p = dl_append_hex(detail, p, d.Bus, 2, 48);
p = dl_append(detail, p, ":", 48);
p = dl_append_hex(detail, p, d.Device, 2, 48);
p = dl_append(detail, p, ".", 48);
p = dl_append_dec(detail, p, d.Function, 48);
p = dl_append(detail, p, " ", 48);
p = dl_append_hex(detail, p, d.VendorId, 4, 48);
p = dl_append(detail, p, ":", 48);
p = dl_append_hex(detail, p, d.DeviceId, 4, 48);
add(7, className, detail);
}
return count;
}
// ---- Kernel introspection syscalls ----
static void Sys_MemStats(MemStats* out) {
if (out == nullptr) return;
Memory::g_pfa->GetStats(out);
}
// ---- Window scale syscalls ----
static int Sys_WinSetScale(int scale) {
return WinServer::SetScale(scale);
}
static int Sys_WinGetScale() {
return WinServer::GetScale();
}
// ---- Window server syscalls ----
static int Sys_WinCreate(const char* title, int w, int h, WinCreateResult* result) {
if (result == nullptr || title == nullptr) return -1;
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return -1;
uint64_t outVa = 0;
int id = WinServer::Create(proc->pid, proc->pml4Phys, title, w, h,
proc->heapNext, outVa);
result->id = id;
result->pixelVa = (id >= 0) ? outVa : 0;
return id >= 0 ? 0 : -1;
}
static int Sys_WinDestroy(int windowId) {
return WinServer::Destroy(windowId, Sched::GetCurrentPid());
}
static uint64_t Sys_WinPresent(int windowId) {
return WinServer::Present(windowId, Sched::GetCurrentPid());
}
static int Sys_WinPoll(int windowId, WinEvent* outEvent) {
if (outEvent == nullptr) return -1;
return WinServer::Poll(windowId, Sched::GetCurrentPid(), outEvent);
}
static int Sys_WinEnum(WinInfo* outArray, int maxCount) {
if (outArray == nullptr || maxCount <= 0) return 0;
return WinServer::Enumerate(outArray, maxCount);
}
static uint64_t Sys_WinMap(int windowId) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
return WinServer::Map(windowId, proc->pid, proc->pml4Phys, proc->heapNext);
}
static int Sys_WinSendEvent(int windowId, const WinEvent* event) {
if (event == nullptr) return -1;
return WinServer::SendEvent(windowId, event);
}
static uint64_t Sys_WinResize(int windowId, int newW, int newH) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
uint64_t outVa = 0;
int r = WinServer::Resize(windowId, proc->pid, proc->pml4Phys, newW, newH,
proc->heapNext, outVa);
return (r == 0) ? outVa : 0;
}
// ---- Dispatch ---- // ---- Dispatch ----
extern "C" int64_t SyscallDispatch(SyscallFrame* frame) { extern "C" int64_t SyscallDispatch(SyscallFrame* frame) {
+52 -3
View File
@@ -11,35 +11,69 @@
namespace Zenith { namespace Zenith {
// Syscall numbers // Syscall numbers
/* Process.hpp */
static constexpr uint64_t SYS_EXIT = 0; static constexpr uint64_t SYS_EXIT = 0;
static constexpr uint64_t SYS_YIELD = 1; static constexpr uint64_t SYS_YIELD = 1;
static constexpr uint64_t SYS_SLEEP_MS = 2; static constexpr uint64_t SYS_SLEEP_MS = 2;
static constexpr uint64_t SYS_GETPID = 3; static constexpr uint64_t SYS_GETPID = 3;
/* Terminal.hpp */
static constexpr uint64_t SYS_PRINT = 4; static constexpr uint64_t SYS_PRINT = 4;
static constexpr uint64_t SYS_PUTCHAR = 5; static constexpr uint64_t SYS_PUTCHAR = 5;
/* Filesystem.hpp */
static constexpr uint64_t SYS_OPEN = 6; static constexpr uint64_t SYS_OPEN = 6;
static constexpr uint64_t SYS_READ = 7; static constexpr uint64_t SYS_READ = 7;
static constexpr uint64_t SYS_GETSIZE = 8; static constexpr uint64_t SYS_GETSIZE = 8;
static constexpr uint64_t SYS_CLOSE = 9; static constexpr uint64_t SYS_CLOSE = 9;
static constexpr uint64_t SYS_READDIR = 10; static constexpr uint64_t SYS_READDIR = 10;
/* Heap.hpp */
static constexpr uint64_t SYS_ALLOC = 11; static constexpr uint64_t SYS_ALLOC = 11;
static constexpr uint64_t SYS_FREE = 12; static constexpr uint64_t SYS_FREE = 12;
/* Time.hpp */
static constexpr uint64_t SYS_GETTICKS = 13; static constexpr uint64_t SYS_GETTICKS = 13;
static constexpr uint64_t SYS_GETMILLISECONDS = 14; static constexpr uint64_t SYS_GETMILLISECONDS = 14;
/* Info.hpp */
static constexpr uint64_t SYS_GETINFO = 15; static constexpr uint64_t SYS_GETINFO = 15;
/* Keyboard.hpp */
static constexpr uint64_t SYS_ISKEYAVAILABLE = 16; static constexpr uint64_t SYS_ISKEYAVAILABLE = 16;
static constexpr uint64_t SYS_GETKEY = 17; static constexpr uint64_t SYS_GETKEY = 17;
static constexpr uint64_t SYS_GETCHAR = 18; static constexpr uint64_t SYS_GETCHAR = 18;
/* Net.hpp */
static constexpr uint64_t SYS_PING = 19; static constexpr uint64_t SYS_PING = 19;
/* Process.hpp */
static constexpr uint64_t SYS_SPAWN = 20; static constexpr uint64_t SYS_SPAWN = 20;
/* Graphics.hpp */
static constexpr uint64_t SYS_FBINFO = 21; static constexpr uint64_t SYS_FBINFO = 21;
static constexpr uint64_t SYS_FBMAP = 22; static constexpr uint64_t SYS_FBMAP = 22;
/* Process.hpp */
static constexpr uint64_t SYS_WAITPID = 23; static constexpr uint64_t SYS_WAITPID = 23;
/* Graphics.hpp */
static constexpr uint64_t SYS_TERMSIZE = 24; static constexpr uint64_t SYS_TERMSIZE = 24;
/* Process.hpp */
static constexpr uint64_t SYS_GETARGS = 25; static constexpr uint64_t SYS_GETARGS = 25;
/* Power.hpp */
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;
/* Time.hpp */
static constexpr uint64_t SYS_GETTIME = 28; static constexpr uint64_t SYS_GETTIME = 28;
/* Net.hpp */
static constexpr uint64_t SYS_SOCKET = 29; static constexpr uint64_t SYS_SOCKET = 29;
static constexpr uint64_t SYS_CONNECT = 30; static constexpr uint64_t SYS_CONNECT = 30;
static constexpr uint64_t SYS_BIND = 31; static constexpr uint64_t SYS_BIND = 31;
@@ -52,21 +86,34 @@ namespace Zenith {
static constexpr uint64_t SYS_SETNETCFG = 38; static constexpr uint64_t SYS_SETNETCFG = 38;
static constexpr uint64_t SYS_SENDTO = 39; static constexpr uint64_t SYS_SENDTO = 39;
static constexpr uint64_t SYS_RECVFROM = 40; static constexpr uint64_t SYS_RECVFROM = 40;
/* Filesystem.hpp */
static constexpr uint64_t SYS_FWRITE = 41; static constexpr uint64_t SYS_FWRITE = 41;
static constexpr uint64_t SYS_FCREATE = 42; static constexpr uint64_t SYS_FCREATE = 42;
/* Graphics.hpp */
static constexpr uint64_t SYS_TERMSCALE = 43; static constexpr uint64_t SYS_TERMSCALE = 43;
/* Net.hpp */
static constexpr uint64_t SYS_RESOLVE = 44; static constexpr uint64_t SYS_RESOLVE = 44;
/* Random.hpp */
static constexpr uint64_t SYS_GETRANDOM = 45; static constexpr uint64_t SYS_GETRANDOM = 45;
static constexpr uint64_t SYS_KLOG = 46; static constexpr uint64_t SYS_KLOG = 46;
/* Mouse.hpp */
static constexpr uint64_t SYS_MOUSESTATE = 47; static constexpr uint64_t SYS_MOUSESTATE = 47;
static constexpr uint64_t SYS_SETMOUSEBOUNDS = 48; static constexpr uint64_t SYS_SETMOUSEBOUNDS = 48;
/* IoRedir.hpp */
static constexpr uint64_t SYS_SPAWN_REDIR = 49; static constexpr uint64_t SYS_SPAWN_REDIR = 49;
static constexpr uint64_t SYS_CHILDIO_READ = 50; static constexpr uint64_t SYS_CHILDIO_READ = 50;
static constexpr uint64_t SYS_CHILDIO_WRITE = 51; static constexpr uint64_t SYS_CHILDIO_WRITE = 51;
static constexpr uint64_t SYS_CHILDIO_WRITEKEY = 52; static constexpr uint64_t SYS_CHILDIO_WRITEKEY = 52;
static constexpr uint64_t SYS_CHILDIO_SETTERMSZ = 53; static constexpr uint64_t SYS_CHILDIO_SETTERMSZ = 53;
// Window server syscalls /* Window.hpp */
static constexpr uint64_t SYS_WINCREATE = 54; static constexpr uint64_t SYS_WINCREATE = 54;
static constexpr uint64_t SYS_WINDESTROY = 55; static constexpr uint64_t SYS_WINDESTROY = 55;
static constexpr uint64_t SYS_WINPRESENT = 56; static constexpr uint64_t SYS_WINPRESENT = 56;
@@ -78,12 +125,14 @@ namespace Zenith {
static constexpr uint64_t SYS_WINSETSCALE = 65; static constexpr uint64_t SYS_WINSETSCALE = 65;
static constexpr uint64_t SYS_WINGETSCALE = 66; static constexpr uint64_t SYS_WINGETSCALE = 66;
// Process management syscalls /* Process.hpp */
static constexpr uint64_t SYS_PROCLIST = 61; static constexpr uint64_t SYS_PROCLIST = 61;
static constexpr uint64_t SYS_KILL = 62; static constexpr uint64_t SYS_KILL = 62;
/* Device.hpp */
static constexpr uint64_t SYS_DEVLIST = 63; static constexpr uint64_t SYS_DEVLIST = 63;
// Kernel introspection syscalls /* MemInfo.hpp */
static constexpr uint64_t SYS_MEMSTATS = 67; static constexpr uint64_t SYS_MEMSTATS = 67;
static constexpr int SOCK_TCP = 1; static constexpr int SOCK_TCP = 1;
+40
View File
@@ -0,0 +1,40 @@
/*
* Terminal.hpp
* SYS_PRINT, SYS_PUTCHAR syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Sched/Scheduler.hpp>
#include <Terminal/Terminal.hpp>
#include "Common.hpp"
namespace Zenith {
static void Sys_Print(const char* text) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target && target->outBuf) {
for (int i = 0; text[i]; i++) {
RingWrite(target->outBuf, target->outHead, target->outTail, Sched::Process::IoBufSize, (uint8_t)text[i]);
}
return;
}
}
Kt::Print(text);
}
static void Sys_Putchar(char c) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc && proc->redirected) {
auto* target = GetRedirTarget(proc);
if (target && target->outBuf) {
RingWrite(target->outBuf, target->outHead, target->outTail, Sched::Process::IoBufSize, (uint8_t)c);
return;
}
}
Kt::Putchar(c);
}
};
+32
View File
@@ -0,0 +1,32 @@
/*
* Time.hpp
* SYS_GETTICKS, SYS_GETMILLISECONDS, SYS_GETTIME syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Timekeeping/ApicTimer.hpp>
#include <Timekeeping/Time.hpp>
#include "Syscall.hpp"
namespace Zenith {
static uint64_t Sys_GetTicks() {
return Timekeeping::GetTicks();
}
static uint64_t Sys_GetMilliseconds() {
return Timekeeping::GetMilliseconds();
}
static void Sys_GetTime(DateTime* out) {
if (out == nullptr) return;
Timekeeping::DateTime dt = Timekeeping::GetDateTime();
out->Year = dt.Year;
out->Month = dt.Month;
out->Day = dt.Day;
out->Hour = dt.Hour;
out->Minute = dt.Minute;
out->Second = dt.Second;
}
};
+75
View File
@@ -0,0 +1,75 @@
/*
* Window.hpp
* SYS_WINCREATE, SYS_WINDESTROY, SYS_WINPRESENT, SYS_WINPOLL,
* SYS_WINENUM, SYS_WINMAP, SYS_WINSENDEVENT, SYS_WINRESIZE,
* SYS_WINSETSCALE, SYS_WINGETSCALE syscalls
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Sched/Scheduler.hpp>
#include "Syscall.hpp"
#include "WinServer.hpp"
namespace Zenith {
static int Sys_WinCreate(const char* title, int w, int h, WinCreateResult* result) {
if (result == nullptr || title == nullptr) return -1;
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return -1;
uint64_t outVa = 0;
int id = WinServer::Create(proc->pid, proc->pml4Phys, title, w, h,
proc->heapNext, outVa);
result->id = id;
result->pixelVa = (id >= 0) ? outVa : 0;
return id >= 0 ? 0 : -1;
}
static int Sys_WinDestroy(int windowId) {
return WinServer::Destroy(windowId, Sched::GetCurrentPid());
}
static uint64_t Sys_WinPresent(int windowId) {
return WinServer::Present(windowId, Sched::GetCurrentPid());
}
static int Sys_WinPoll(int windowId, WinEvent* outEvent) {
if (outEvent == nullptr) return -1;
return WinServer::Poll(windowId, Sched::GetCurrentPid(), outEvent);
}
static int Sys_WinEnum(WinInfo* outArray, int maxCount) {
if (outArray == nullptr || maxCount <= 0) return 0;
return WinServer::Enumerate(outArray, maxCount);
}
static uint64_t Sys_WinMap(int windowId) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
return WinServer::Map(windowId, proc->pid, proc->pml4Phys, proc->heapNext);
}
static int Sys_WinSendEvent(int windowId, const WinEvent* event) {
if (event == nullptr) return -1;
return WinServer::SendEvent(windowId, event);
}
static uint64_t Sys_WinResize(int windowId, int newW, int newH) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0;
uint64_t outVa = 0;
int r = WinServer::Resize(windowId, proc->pid, proc->pml4Phys, newW, newH,
proc->heapNext, outVa);
return (r == 0) ? outVa : 0;
}
static int Sys_WinSetScale(int scale) {
return WinServer::SetScale(scale);
}
static int Sys_WinGetScale() {
return WinServer::GetScale();
}
};
+1 -1
View File
@@ -10,7 +10,7 @@
namespace Sched { namespace Sched {
static constexpr int MaxProcesses = 16; static constexpr int MaxProcesses = 256;
static constexpr uint64_t StackPages = 4; // 16 KiB kernel stack per process static constexpr uint64_t StackPages = 4; // 16 KiB kernel stack per process
static constexpr uint64_t StackSize = StackPages * 0x1000; static constexpr uint64_t StackSize = StackPages * 0x1000;
static constexpr uint64_t UserStackPages = 4; // 16 KiB user stack static constexpr uint64_t UserStackPages = 4; // 16 KiB user stack
+23 -2
View File
@@ -3,5 +3,26 @@
ZenithOS legal/copyright information ZenithOS legal/copyright information
.SH DESCRIPTION .SH DESCRIPTION
Copyright (c) 2025-2026 Daniel Hammer, et al. Copyright (c) 2025-2026 Daniel Hammer, et al. All rights reserved.
(including contributors to other projects, i.e. The Limine Bootloader. Please refer to any other project's own license.) (includes contributors to other projects, i.e. The Limine Bootloader. Please refer to any other project's own license.)
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
== License for the Limine C++ template (certain portions derive therefrom) ==
Copyright (C) 2023-2026 Mintsuki and contributors.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
BIN
View File
Binary file not shown.