diff --git a/LICENSE b/LICENSE index 83411e2..06b06a7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,2 +1,23 @@ -Copyright (c) 2025-2026 Daniel Hammer, et al. - (including contributors to other projects, i.e. The Limine Bootloader. Please refer to any other project's own license.) \ No newline at end of file +Copyright (c) 2025-2026 Daniel Hammer, et al. All rights reserved. + (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. \ No newline at end of file diff --git a/kernel/src/Api/Common.hpp b/kernel/src/Api/Common.hpp new file mode 100644 index 0000000..5e64347 --- /dev/null +++ b/kernel/src/Api/Common.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +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; + } +} \ No newline at end of file diff --git a/kernel/src/Api/Device.hpp b/kernel/src/Api/Device.hpp new file mode 100644 index 0000000..15af17d --- /dev/null +++ b/kernel/src/Api/Device.hpp @@ -0,0 +1,152 @@ +/* + * Device.hpp + * SYS_DEVLIST syscall + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + } +}; diff --git a/kernel/src/Api/Filesystem.hpp b/kernel/src/Api/Filesystem.hpp new file mode 100644 index 0000000..1f42db6 --- /dev/null +++ b/kernel/src/Api/Filesystem.hpp @@ -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 +#include +#include +#include +#include +#include + +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); + } +}; \ No newline at end of file diff --git a/kernel/src/Api/Graphics.hpp b/kernel/src/Api/Graphics.hpp new file mode 100644 index 0000000..8b41ccb --- /dev/null +++ b/kernel/src/Api/Graphics.hpp @@ -0,0 +1,86 @@ +/* + * Graphics.hpp + * SYS_FBINFO, SYS_FBMAP, SYS_TERMSIZE, SYS_TERMSCALE syscalls + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include +#include +#include +#include +#include + +#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)); + } +}; diff --git a/kernel/src/Api/Heap.hpp b/kernel/src/Api/Heap.hpp new file mode 100644 index 0000000..ec355a4 --- /dev/null +++ b/kernel/src/Api/Heap.hpp @@ -0,0 +1,39 @@ +/* + * Heap.hpp + * SYS_ALLOC, SYS_FREE syscalls + * Copyright (c) 2026 Daniel Hammer +*/ + +#include +#include +#include +#include +#include + +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) + } +}; \ No newline at end of file diff --git a/kernel/src/Api/Info.hpp b/kernel/src/Api/Info.hpp new file mode 100644 index 0000000..862052f --- /dev/null +++ b/kernel/src/Api/Info.hpp @@ -0,0 +1,28 @@ +/* + * Info.hpp + * SYS_GETINFO syscall + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include + +#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; + } +}; diff --git a/kernel/src/Api/IoRedir.hpp b/kernel/src/Api/IoRedir.hpp new file mode 100644 index 0000000..960d6e9 --- /dev/null +++ b/kernel/src/Api/IoRedir.hpp @@ -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 +#include + +#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; + } +}; diff --git a/kernel/src/Api/Keyboard.hpp b/kernel/src/Api/Keyboard.hpp new file mode 100644 index 0000000..a32eb00 --- /dev/null +++ b/kernel/src/Api/Keyboard.hpp @@ -0,0 +1,63 @@ +/* + * Keyboard.hpp + * SYS_ISKEYAVAILABLE, SYS_GETKEY, SYS_GETCHAR syscalls + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include + +#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(); + } +}; \ No newline at end of file diff --git a/kernel/src/Api/MemInfo.hpp b/kernel/src/Api/MemInfo.hpp new file mode 100644 index 0000000..5bb79ed --- /dev/null +++ b/kernel/src/Api/MemInfo.hpp @@ -0,0 +1,18 @@ +/* + * MemInfo.hpp + * SYS_MEMSTATS syscall + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include + +#include "Syscall.hpp" + +namespace Zenith { + + static void Sys_MemStats(MemStats* out) { + if (out == nullptr) return; + Memory::g_pfa->GetStats(out); + } +}; diff --git a/kernel/src/Api/Mouse.hpp b/kernel/src/Api/Mouse.hpp new file mode 100644 index 0000000..2d263bd --- /dev/null +++ b/kernel/src/Api/Mouse.hpp @@ -0,0 +1,26 @@ +/* + * Mouse.hpp + * SYS_MOUSESTATE, SYS_SETMOUSEBOUNDS syscalls + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include + +#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); + } +}; diff --git a/kernel/src/Api/Net.hpp b/kernel/src/Api/Net.hpp new file mode 100644 index 0000000..1495b00 --- /dev/null +++ b/kernel/src/Api/Net.hpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + } +}; diff --git a/kernel/src/Api/Power.hpp b/kernel/src/Api/Power.hpp new file mode 100644 index 0000000..2f4d104 --- /dev/null +++ b/kernel/src/Api/Power.hpp @@ -0,0 +1,28 @@ +/* + * Power.hpp + * SYS_RESET, SYS_SHUTDOWN syscalls + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include + +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(); + } +}; diff --git a/kernel/src/Api/Process.hpp b/kernel/src/Api/Process.hpp new file mode 100644 index 0000000..f595fba --- /dev/null +++ b/kernel/src/Api/Process.hpp @@ -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 +#include + +#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; + } +}; \ No newline at end of file diff --git a/kernel/src/Api/Random.hpp b/kernel/src/Api/Random.hpp new file mode 100644 index 0000000..17c6f16 --- /dev/null +++ b/kernel/src/Api/Random.hpp @@ -0,0 +1,39 @@ +/* + * Random.hpp + * SYS_GETRANDOM syscall + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include + +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; + } +}; diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index d58f337..29e9b6f 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -5,860 +5,36 @@ */ #include "Syscall.hpp" -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include "../Libraries/flanterm/src/flanterm.h" -#include "WinServer.hpp" -#include -#include -#include -#include -#include + +/* For common functions used by multiple syscall implementations*/ +#include "Common.hpp" + +/* Syscall impl. includes */ +#include "Process.hpp" // SYS_EXIT, SYS_YIELD, SYS_SLEEP_MS, SYS_GETPID, SYS_WAITPID, SYS_SPAWN, SYS_GETARGS, SYS_PROCLIST, SYS_KILL +#include "Terminal.hpp" // SYS_PRINT, SYS_PUTCHAR +#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 extern "C" void SyscallEntry(); 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 ---- extern "C" int64_t SyscallDispatch(SyscallFrame* frame) { diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index 894db3c..febafce 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -11,35 +11,69 @@ namespace Zenith { // Syscall numbers + + /* Process.hpp */ static constexpr uint64_t SYS_EXIT = 0; static constexpr uint64_t SYS_YIELD = 1; static constexpr uint64_t SYS_SLEEP_MS = 2; static constexpr uint64_t SYS_GETPID = 3; + + /* Terminal.hpp */ 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_READ = 7; static constexpr uint64_t SYS_GETSIZE = 8; static constexpr uint64_t SYS_CLOSE = 9; static constexpr uint64_t SYS_READDIR = 10; + + /* Heap.hpp */ static constexpr uint64_t SYS_ALLOC = 11; static constexpr uint64_t SYS_FREE = 12; + + /* Time.hpp */ static constexpr uint64_t SYS_GETTICKS = 13; static constexpr uint64_t SYS_GETMILLISECONDS = 14; + + /* Info.hpp */ static constexpr uint64_t SYS_GETINFO = 15; + + /* Keyboard.hpp */ static constexpr uint64_t SYS_ISKEYAVAILABLE = 16; static constexpr uint64_t SYS_GETKEY = 17; static constexpr uint64_t SYS_GETCHAR = 18; + + + /* Net.hpp */ static constexpr uint64_t SYS_PING = 19; + + + /* Process.hpp */ static constexpr uint64_t SYS_SPAWN = 20; + + /* Graphics.hpp */ static constexpr uint64_t SYS_FBINFO = 21; static constexpr uint64_t SYS_FBMAP = 22; + + /* Process.hpp */ static constexpr uint64_t SYS_WAITPID = 23; + + /* Graphics.hpp */ static constexpr uint64_t SYS_TERMSIZE = 24; + + /* Process.hpp */ static constexpr uint64_t SYS_GETARGS = 25; + + /* Power.hpp */ static constexpr uint64_t SYS_RESET = 26; static constexpr uint64_t SYS_SHUTDOWN = 27; + + /* Time.hpp */ static constexpr uint64_t SYS_GETTIME = 28; + + /* Net.hpp */ static constexpr uint64_t SYS_SOCKET = 29; static constexpr uint64_t SYS_CONNECT = 30; static constexpr uint64_t SYS_BIND = 31; @@ -48,25 +82,38 @@ namespace Zenith { static constexpr uint64_t SYS_SEND = 34; static constexpr uint64_t SYS_RECV = 35; static constexpr uint64_t SYS_CLOSESOCK = 36; - static constexpr uint64_t SYS_GETNETCFG = 37; - static constexpr uint64_t SYS_SETNETCFG = 38; - static constexpr uint64_t SYS_SENDTO = 39; - static constexpr uint64_t SYS_RECVFROM = 40; - static constexpr uint64_t SYS_FWRITE = 41; - static constexpr uint64_t SYS_FCREATE = 42; - static constexpr uint64_t SYS_TERMSCALE = 43; - static constexpr uint64_t SYS_RESOLVE = 44; - static constexpr uint64_t SYS_GETRANDOM = 45; + static constexpr uint64_t SYS_GETNETCFG = 37; + static constexpr uint64_t SYS_SETNETCFG = 38; + static constexpr uint64_t SYS_SENDTO = 39; + static constexpr uint64_t SYS_RECVFROM = 40; + + /* Filesystem.hpp */ + static constexpr uint64_t SYS_FWRITE = 41; + static constexpr uint64_t SYS_FCREATE = 42; + + /* Graphics.hpp */ + static constexpr uint64_t SYS_TERMSCALE = 43; + + /* Net.hpp */ + static constexpr uint64_t SYS_RESOLVE = 44; + + /* Random.hpp */ + static constexpr uint64_t SYS_GETRANDOM = 45; + static constexpr uint64_t SYS_KLOG = 46; - static constexpr uint64_t SYS_MOUSESTATE = 47; - static constexpr uint64_t SYS_SETMOUSEBOUNDS = 48; - static constexpr uint64_t SYS_SPAWN_REDIR = 49; - static constexpr uint64_t SYS_CHILDIO_READ = 50; - static constexpr uint64_t SYS_CHILDIO_WRITE = 51; - static constexpr uint64_t SYS_CHILDIO_WRITEKEY = 52; + + /* Mouse.hpp */ + static constexpr uint64_t SYS_MOUSESTATE = 47; + static constexpr uint64_t SYS_SETMOUSEBOUNDS = 48; + + /* IoRedir.hpp */ + static constexpr uint64_t SYS_SPAWN_REDIR = 49; + static constexpr uint64_t SYS_CHILDIO_READ = 50; + static constexpr uint64_t SYS_CHILDIO_WRITE = 51; + static constexpr uint64_t SYS_CHILDIO_WRITEKEY = 52; 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_WINDESTROY = 55; static constexpr uint64_t SYS_WINPRESENT = 56; @@ -74,16 +121,18 @@ namespace Zenith { static constexpr uint64_t SYS_WINENUM = 58; static constexpr uint64_t SYS_WINMAP = 59; static constexpr uint64_t SYS_WINSENDEVENT = 60; - static constexpr uint64_t SYS_WINRESIZE = 64; - static constexpr uint64_t SYS_WINSETSCALE = 65; - static constexpr uint64_t SYS_WINGETSCALE = 66; + static constexpr uint64_t SYS_WINRESIZE = 64; + static constexpr uint64_t SYS_WINSETSCALE = 65; + static constexpr uint64_t SYS_WINGETSCALE = 66; - // Process management syscalls + /* Process.hpp */ static constexpr uint64_t SYS_PROCLIST = 61; static constexpr uint64_t SYS_KILL = 62; + + /* Device.hpp */ static constexpr uint64_t SYS_DEVLIST = 63; - // Kernel introspection syscalls + /* MemInfo.hpp */ static constexpr uint64_t SYS_MEMSTATS = 67; static constexpr int SOCK_TCP = 1; diff --git a/kernel/src/Api/Terminal.hpp b/kernel/src/Api/Terminal.hpp new file mode 100644 index 0000000..31d089a --- /dev/null +++ b/kernel/src/Api/Terminal.hpp @@ -0,0 +1,40 @@ +/* + * Terminal.hpp + * SYS_PRINT, SYS_PUTCHAR syscalls + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include + +#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); + } +}; \ No newline at end of file diff --git a/kernel/src/Api/Time.hpp b/kernel/src/Api/Time.hpp new file mode 100644 index 0000000..d288271 --- /dev/null +++ b/kernel/src/Api/Time.hpp @@ -0,0 +1,32 @@ +/* + * Time.hpp + * SYS_GETTICKS, SYS_GETMILLISECONDS, SYS_GETTIME syscalls + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include + +#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; + } +}; \ No newline at end of file diff --git a/kernel/src/Api/Window.hpp b/kernel/src/Api/Window.hpp new file mode 100644 index 0000000..17fca53 --- /dev/null +++ b/kernel/src/Api/Window.hpp @@ -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 + +#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(); + } +}; diff --git a/kernel/src/Sched/Scheduler.hpp b/kernel/src/Sched/Scheduler.hpp index 218b870..11d0edf 100644 --- a/kernel/src/Sched/Scheduler.hpp +++ b/kernel/src/Sched/Scheduler.hpp @@ -10,7 +10,7 @@ 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 StackSize = StackPages * 0x1000; static constexpr uint64_t UserStackPages = 4; // 16 KiB user stack diff --git a/programs/man/legal.7 b/programs/man/legal.7 index 4e2484e..c203ac3 100644 --- a/programs/man/legal.7 +++ b/programs/man/legal.7 @@ -3,5 +3,26 @@ ZenithOS legal/copyright information .SH DESCRIPTION - Copyright (c) 2025-2026 Daniel Hammer, et al. - (including contributors to other projects, i.e. The Limine Bootloader. Please refer to any other project's own license.) \ No newline at end of file + Copyright (c) 2025-2026 Daniel Hammer, et al. All rights reserved. + (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. \ No newline at end of file diff --git a/ramdisk.tar b/ramdisk.tar index 12fbd13..5879d08 100644 Binary files a/ramdisk.tar and b/ramdisk.tar differ