feat: userspace overhaul, Intel GPU driver, and more
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
#include <Libraries/String.hpp>
|
#include <Libraries/String.hpp>
|
||||||
#include <Drivers/PS2/Keyboard.hpp>
|
#include <Drivers/PS2/Keyboard.hpp>
|
||||||
#include <Net/Icmp.hpp>
|
#include <Net/Icmp.hpp>
|
||||||
|
#include <Net/Dns.hpp>
|
||||||
#include <Net/Socket.hpp>
|
#include <Net/Socket.hpp>
|
||||||
#include <Net/ByteOrder.hpp>
|
#include <Net/ByteOrder.hpp>
|
||||||
#include <Net/NetConfig.hpp>
|
#include <Net/NetConfig.hpp>
|
||||||
@@ -209,11 +210,18 @@ namespace Zenith {
|
|||||||
* Graphics::Cursor::GetFramebufferPitch();
|
* Graphics::Cursor::GetFramebufferPitch();
|
||||||
uint64_t numPages = (fbSize + 0xFFF) / 0x1000;
|
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
|
// Map at a fixed user VA
|
||||||
constexpr uint64_t userVa = 0x50000000ULL;
|
constexpr uint64_t userVa = 0x50000000ULL;
|
||||||
|
|
||||||
for (uint64_t i = 0; i < numPages; i++) {
|
for (uint64_t i = 0; i < numPages; i++) {
|
||||||
Memory::VMM::Paging::MapUserIn(
|
Memory::VMM::Paging::MapUserInWC(
|
||||||
proc->pml4Phys,
|
proc->pml4Phys,
|
||||||
fbPhys + i * 0x1000,
|
fbPhys + i * 0x1000,
|
||||||
userVa + i * 0x1000
|
userVa + i * 0x1000
|
||||||
@@ -335,6 +343,7 @@ namespace Zenith {
|
|||||||
}
|
}
|
||||||
out->_pad[0] = 0;
|
out->_pad[0] = 0;
|
||||||
out->_pad[1] = 0;
|
out->_pad[1] = 0;
|
||||||
|
out->dnsServer = Net::GetDnsServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Sys_SetNetCfg(const NetCfg* in) {
|
static int Sys_SetNetCfg(const NetCfg* in) {
|
||||||
@@ -342,6 +351,7 @@ namespace Zenith {
|
|||||||
Net::SetIpAddress(in->ipAddress);
|
Net::SetIpAddress(in->ipAddress);
|
||||||
Net::SetSubnetMask(in->subnetMask);
|
Net::SetSubnetMask(in->subnetMask);
|
||||||
Net::SetGateway(in->gateway);
|
Net::SetGateway(in->gateway);
|
||||||
|
Net::SetDnsServer(in->dnsServer);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,6 +382,53 @@ namespace Zenith {
|
|||||||
return Fs::Vfs::VfsCreate(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;
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Dispatch ----
|
// ---- Dispatch ----
|
||||||
|
|
||||||
extern "C" int64_t SyscallDispatch(SyscallFrame* frame) {
|
extern "C" int64_t SyscallDispatch(SyscallFrame* frame) {
|
||||||
@@ -486,6 +543,12 @@ namespace Zenith {
|
|||||||
frame->arg3, frame->arg4);
|
frame->arg3, frame->arg4);
|
||||||
case SYS_FCREATE:
|
case SYS_FCREATE:
|
||||||
return (int64_t)Sys_FCreate((const char*)frame->arg1);
|
return (int64_t)Sys_FCreate((const char*)frame->arg1);
|
||||||
|
case SYS_TERMSCALE:
|
||||||
|
return Sys_TermScale(frame->arg1, frame->arg2);
|
||||||
|
case SYS_RESOLVE:
|
||||||
|
return Sys_Resolve((const char*)frame->arg1);
|
||||||
|
case SYS_GETRANDOM:
|
||||||
|
return Sys_GetRandom((uint8_t*)frame->arg1, frame->arg2);
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -512,7 +575,7 @@ namespace Zenith {
|
|||||||
Hal::WriteMSR(Hal::IA32_FMASK, 0x200);
|
Hal::WriteMSR(Hal::IA32_FMASK, 0x200);
|
||||||
|
|
||||||
Kt::KernelLogStream(Kt::OK, "Syscall") << "SYSCALL/SYSRET initialized (LSTAR="
|
Kt::KernelLogStream(Kt::OK, "Syscall") << "SYSCALL/SYSRET initialized (LSTAR="
|
||||||
<< kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", 43 syscalls)";
|
<< kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", 46 syscalls)";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ namespace Zenith {
|
|||||||
static constexpr uint64_t SYS_RECVFROM = 40;
|
static constexpr uint64_t SYS_RECVFROM = 40;
|
||||||
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;
|
||||||
|
static constexpr uint64_t SYS_TERMSCALE = 43;
|
||||||
|
static constexpr uint64_t SYS_RESOLVE = 44;
|
||||||
|
static constexpr uint64_t SYS_GETRANDOM = 45;
|
||||||
|
|
||||||
static constexpr int SOCK_TCP = 1;
|
static constexpr int SOCK_TCP = 1;
|
||||||
static constexpr int SOCK_UDP = 2;
|
static constexpr int SOCK_UDP = 2;
|
||||||
@@ -88,6 +91,7 @@ namespace Zenith {
|
|||||||
uint32_t gateway; // network byte order
|
uint32_t gateway; // network byte order
|
||||||
uint8_t macAddress[6];
|
uint8_t macAddress[6];
|
||||||
uint8_t _pad[2];
|
uint8_t _pad[2];
|
||||||
|
uint32_t dnsServer; // network byte order
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyEvent {
|
struct KeyEvent {
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ void Panic(const char *meditationString, System::PanicFrame* frame) {
|
|||||||
if (frame->InterruptVector == 0xE) {
|
if (frame->InterruptVector == 0xE) {
|
||||||
auto pf_frame = (System::PageFaultPanicFrame*)frame;
|
auto pf_frame = (System::PageFaultPanicFrame*)frame;
|
||||||
frame = (System::PanicFrame*)&pf_frame->IP;
|
frame = (System::PanicFrame*)&pf_frame->IP;
|
||||||
|
|
||||||
|
// CR2 holds the faulting virtual address for page faults
|
||||||
|
uint64_t cr2;
|
||||||
|
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
PrintBoxedHex(kerr, "Faulting Address (CR2)", cr2, boxWidth);
|
||||||
|
|
||||||
PrintBoxedLine(kerr, "Page Fault Error:", boxWidth, true);
|
PrintBoxedLine(kerr, "Page Fault Error:", boxWidth, true);
|
||||||
PrintBoxedDec(kerr, "Present", pf_frame->PageFaultError.Present, boxWidth);
|
PrintBoxedDec(kerr, "Present", pf_frame->PageFaultError.Present, boxWidth);
|
||||||
PrintBoxedDec(kerr, "Write", pf_frame->PageFaultError.Write, boxWidth);
|
PrintBoxedDec(kerr, "Write", pf_frame->PageFaultError.Write, boxWidth);
|
||||||
|
|||||||
@@ -0,0 +1,564 @@
|
|||||||
|
/*
|
||||||
|
* IntelGPU.cpp
|
||||||
|
* Intel integrated graphics (i915) modesetting driver
|
||||||
|
* Scans PCI for Intel display controllers, maps MMIO, initializes GTT,
|
||||||
|
* and sets up a framebuffer using the firmware's existing display timings.
|
||||||
|
* Copyright (c) 2025 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IntelGPU.hpp"
|
||||||
|
#include <Pci/Pci.hpp>
|
||||||
|
#include <Terminal/Terminal.hpp>
|
||||||
|
#include <CppLib/Stream.hpp>
|
||||||
|
#include <Memory/HHDM.hpp>
|
||||||
|
#include <Memory/Paging.hpp>
|
||||||
|
#include <Memory/PageFrameAllocator.hpp>
|
||||||
|
#include <Io/IoPort.hpp>
|
||||||
|
#include <Graphics/Cursor.hpp>
|
||||||
|
|
||||||
|
using namespace Kt;
|
||||||
|
|
||||||
|
namespace Drivers::Graphics::IntelGPU {
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Driver state
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static bool g_initialized = false;
|
||||||
|
|
||||||
|
static GpuInfo g_gpuInfo = {};
|
||||||
|
|
||||||
|
static volatile uint8_t* g_mmioBase = nullptr;
|
||||||
|
|
||||||
|
// GTT state
|
||||||
|
static volatile void* g_gttBase = nullptr; // Virtual address of GTT entries
|
||||||
|
static uint64_t g_gttEntryCount = 0; // Number of GTT entries
|
||||||
|
static uint64_t g_scratchPagePhys = 0; // Physical address of scratch page
|
||||||
|
static uint8_t g_gpuGen = 0; // Cached generation number
|
||||||
|
|
||||||
|
// Framebuffer state
|
||||||
|
static uint32_t* g_fbBase = nullptr; // HHDM virtual address of first FB page
|
||||||
|
static uint64_t g_fbPhysBase = 0; // Physical address of first FB page
|
||||||
|
static uint64_t g_fbWidth = 0;
|
||||||
|
static uint64_t g_fbHeight = 0;
|
||||||
|
static uint64_t g_fbPitch = 0; // Stride in bytes
|
||||||
|
static uint64_t g_fbSize = 0; // Total framebuffer size in bytes
|
||||||
|
static uint64_t g_fbGttOffset = 0; // GTT offset where FB starts (in bytes)
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Register access helpers
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static void WriteReg(uint32_t reg, uint32_t val) {
|
||||||
|
*(volatile uint32_t*)(g_mmioBase + reg) = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t ReadReg(uint32_t reg) {
|
||||||
|
return *(volatile uint32_t*)(g_mmioBase + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// PCI Detection
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static bool DetectGpu() {
|
||||||
|
auto& devices = Pci::GetDevices();
|
||||||
|
const Pci::PciDevice* found = nullptr;
|
||||||
|
const DeviceInfo* matchedInfo = nullptr;
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < devices.size(); i++) {
|
||||||
|
if (devices[i].VendorId != VendorIntel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (devices[i].ClassCode != ClassDisplay) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found an Intel display controller; try to match device ID
|
||||||
|
found = &devices[i];
|
||||||
|
|
||||||
|
for (int j = 0; j < SupportedDeviceCount; j++) {
|
||||||
|
if (devices[i].DeviceId == SupportedDevices[j].deviceId) {
|
||||||
|
matchedInfo = &SupportedDevices[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop at first Intel display controller
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found == nullptr) {
|
||||||
|
KernelLogStream(WARNING, "IntelGPU") << "No Intel display controller found";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_gpuInfo.pciBus = found->Bus;
|
||||||
|
g_gpuInfo.pciDevice = found->Device;
|
||||||
|
g_gpuInfo.pciFunction = found->Function;
|
||||||
|
g_gpuInfo.deviceId = found->DeviceId;
|
||||||
|
|
||||||
|
if (matchedInfo != nullptr) {
|
||||||
|
g_gpuInfo.gen = matchedInfo->gen;
|
||||||
|
g_gpuInfo.name = matchedInfo->name;
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "Found " << matchedInfo->name
|
||||||
|
<< " (device " << base::hex << (uint64_t)found->DeviceId << ")"
|
||||||
|
<< " at PCI " << (uint64_t)found->Bus << ":"
|
||||||
|
<< (uint64_t)found->Device << "." << (uint64_t)found->Function;
|
||||||
|
} else {
|
||||||
|
// Unknown device ID - accept generically but warn
|
||||||
|
g_gpuInfo.gen = 7; // Assume gen 7 as a safe default
|
||||||
|
g_gpuInfo.name = "Unknown Intel GPU";
|
||||||
|
|
||||||
|
KernelLogStream(WARNING, "IntelGPU") << "Unknown Intel display controller "
|
||||||
|
<< "(device " << base::hex << (uint64_t)found->DeviceId << ")"
|
||||||
|
<< " at PCI " << (uint64_t)found->Bus << ":"
|
||||||
|
<< (uint64_t)found->Device << "." << (uint64_t)found->Function
|
||||||
|
<< " - attempting generic initialization";
|
||||||
|
}
|
||||||
|
|
||||||
|
g_gpuGen = g_gpuInfo.gen;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// BAR0 MMIO Mapping
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static bool MapMmio() {
|
||||||
|
uint8_t bus = g_gpuInfo.pciBus;
|
||||||
|
uint8_t dev = g_gpuInfo.pciDevice;
|
||||||
|
uint8_t func = g_gpuInfo.pciFunction;
|
||||||
|
|
||||||
|
// Read BAR0
|
||||||
|
uint32_t bar0Low = Pci::LegacyRead32(bus, dev, func, (uint8_t)PCI_REG_BAR0);
|
||||||
|
uint64_t mmioPhys = bar0Low & 0xFFFFFFF0u; // Mask off type/prefetchable bits
|
||||||
|
|
||||||
|
// Check for 64-bit BAR (bit 2 of BAR type field)
|
||||||
|
if (bar0Low & 0x04) {
|
||||||
|
uint32_t bar0High = Pci::LegacyRead32(bus, dev, func, (uint8_t)(PCI_REG_BAR0 + 4));
|
||||||
|
mmioPhys |= ((uint64_t)bar0High << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_gpuInfo.mmioPhys = mmioPhys;
|
||||||
|
g_gpuInfo.mmioSize = 0x200000; // Map 2MB of MMIO space
|
||||||
|
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "BAR0 physical: " << base::hex << mmioPhys;
|
||||||
|
|
||||||
|
// Map 2MB of MMIO space (0x200 pages)
|
||||||
|
for (uint64_t offset = 0; offset < 0x200000; offset += 0x1000) {
|
||||||
|
Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys);
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "MMIO mapped at virtual " << base::hex << (uint64_t)g_mmioBase;
|
||||||
|
|
||||||
|
// Enable memory space and bus master in PCI command register
|
||||||
|
uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, (uint8_t)PCI_REG_COMMAND);
|
||||||
|
pciCmd |= PCI_CMD_MEM_SPACE | PCI_CMD_BUS_MASTER;
|
||||||
|
Pci::LegacyWrite16(bus, dev, func, (uint8_t)PCI_REG_COMMAND, pciCmd);
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "PCI memory space and bus mastering enabled";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// VGA Disable
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static void DisableVga() {
|
||||||
|
// Check if VGA plane is already disabled (bit 31 of VGACNTRL).
|
||||||
|
// On modern Intel iGPUs with eDP/LVDS panels, the firmware typically
|
||||||
|
// disables VGA when it sets up GOP. Skip if already done.
|
||||||
|
uint32_t vgaCtrl = ReadReg(VGACNTRL);
|
||||||
|
if (vgaCtrl & VGACNTRL_DISABLE) {
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "VGA plane already disabled by firmware";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Disable VGA screen via the VGA sequencer I/O ports
|
||||||
|
// SR01 bit 5 must be set BEFORE disabling the VGA plane register
|
||||||
|
Io::Out8(0x01, 0x3C4);
|
||||||
|
uint8_t sr01 = Io::In8(0x3C5);
|
||||||
|
sr01 |= (1 << 5);
|
||||||
|
Io::Out8(0x01, 0x3C4);
|
||||||
|
Io::Out8(sr01, 0x3C5);
|
||||||
|
|
||||||
|
// Step 2: Set bit 31 of VGACNTRL MMIO register to disable VGA display plane
|
||||||
|
vgaCtrl |= VGACNTRL_DISABLE;
|
||||||
|
WriteReg(VGACNTRL, vgaCtrl);
|
||||||
|
|
||||||
|
// Step 3: Read back to flush the write
|
||||||
|
(void)ReadReg(VGACNTRL);
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "VGA plane disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Read Current Display State
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static bool ReadDisplayState() {
|
||||||
|
// Read pipe A configuration
|
||||||
|
uint32_t pipeConf = ReadReg(PIPEACONF);
|
||||||
|
bool pipeEnabled = (pipeConf & PIPECONF_ENABLE) != 0;
|
||||||
|
|
||||||
|
// Read plane A control
|
||||||
|
uint32_t dspaCntr = ReadReg(DSPACNTR);
|
||||||
|
bool planeEnabled = (dspaCntr & DISP_ENABLE) != 0;
|
||||||
|
|
||||||
|
// Read current surface address and stride
|
||||||
|
uint32_t dspaSurf = ReadReg(DSPASURF);
|
||||||
|
uint32_t dspaStride = ReadReg(DSPASTRIDE);
|
||||||
|
|
||||||
|
// Read timing registers
|
||||||
|
uint32_t htotal = ReadReg(HTOTAL_A);
|
||||||
|
uint32_t vtotal = ReadReg(VTOTAL_A);
|
||||||
|
uint32_t pipeSrc = ReadReg(PIPEASRC);
|
||||||
|
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "Pipe A: "
|
||||||
|
<< (pipeEnabled ? "ENABLED" : "DISABLED")
|
||||||
|
<< ", Plane A: " << (planeEnabled ? "ENABLED" : "DISABLED");
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "DSPASURF: " << base::hex << (uint64_t)dspaSurf
|
||||||
|
<< ", DSPASTRIDE: " << (uint64_t)dspaStride;
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "HTOTAL_A: " << base::hex << (uint64_t)htotal
|
||||||
|
<< ", VTOTAL_A: " << (uint64_t)vtotal
|
||||||
|
<< ", PIPEASRC: " << (uint64_t)pipeSrc;
|
||||||
|
|
||||||
|
// Extract resolution from PIPEASRC (preferred) or timing registers
|
||||||
|
if (pipeSrc != 0) {
|
||||||
|
// PIPEASRC: bits [31:16] = horizontal size - 1, bits [15:0] = vertical size - 1
|
||||||
|
g_fbWidth = ((pipeSrc >> 16) & 0xFFFF) + 1;
|
||||||
|
g_fbHeight = (pipeSrc & 0xFFFF) + 1;
|
||||||
|
} else if (pipeEnabled) {
|
||||||
|
// Fallback to timing registers
|
||||||
|
g_fbWidth = (htotal & 0xFFF) + 1;
|
||||||
|
g_fbHeight = (vtotal & 0xFFF) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read stride from hardware.
|
||||||
|
// On Gen 9+ (Skylake and later), PLANE_STRIDE (same offset as DSPASTRIDE)
|
||||||
|
// stores the stride in 64-byte units, not bytes. Detect this by checking
|
||||||
|
// whether the raw value is too small to be a byte stride.
|
||||||
|
if (dspaStride != 0) {
|
||||||
|
g_fbPitch = dspaStride;
|
||||||
|
if (g_fbWidth > 0 && g_fbPitch < g_fbWidth * 4) {
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "DSPASTRIDE=" << base::dec << g_fbPitch
|
||||||
|
<< " is in 64-byte units (Gen 9+), converting to bytes";
|
||||||
|
g_fbPitch *= 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we still don't have valid dimensions, fall back to the firmware framebuffer
|
||||||
|
if (g_fbWidth == 0 || g_fbHeight == 0 || g_fbPitch == 0) {
|
||||||
|
g_fbWidth = ::Graphics::Cursor::GetFramebufferWidth();
|
||||||
|
g_fbHeight = ::Graphics::Cursor::GetFramebufferHeight();
|
||||||
|
g_fbPitch = ::Graphics::Cursor::GetFramebufferPitch();
|
||||||
|
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "Using firmware framebuffer dimensions: "
|
||||||
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
||||||
|
<< " pitch=" << g_fbPitch;
|
||||||
|
} else {
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "Detected resolution: "
|
||||||
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
||||||
|
<< " pitch=" << g_fbPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_fbWidth == 0 || g_fbHeight == 0) {
|
||||||
|
KernelLogStream(ERROR, "IntelGPU") << "Could not determine display resolution";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure pitch is at least width * 4 (BGRX8888)
|
||||||
|
if (g_fbPitch == 0) {
|
||||||
|
g_fbPitch = g_fbWidth * 4;
|
||||||
|
KernelLogStream(WARNING, "IntelGPU") << "Stride not available, assuming "
|
||||||
|
<< base::dec << g_fbPitch << " bytes";
|
||||||
|
}
|
||||||
|
|
||||||
|
g_fbSize = g_fbHeight * g_fbPitch;
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "Display state: "
|
||||||
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
||||||
|
<< ", stride=" << g_fbPitch
|
||||||
|
<< ", FB size=" << g_fbSize << " bytes";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// GTT Initialization
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static bool InitializeGtt() {
|
||||||
|
uint8_t bus = g_gpuInfo.pciBus;
|
||||||
|
uint8_t dev = g_gpuInfo.pciDevice;
|
||||||
|
uint8_t func = g_gpuInfo.pciFunction;
|
||||||
|
|
||||||
|
// Read GMCH_CTL to determine GTT size
|
||||||
|
uint16_t gmchCtl = Pci::LegacyRead16(bus, dev, func, (uint8_t)PCI_REG_GMCH_CTL);
|
||||||
|
uint8_t gttSizeBits = (gmchCtl >> 8) & 0x3;
|
||||||
|
|
||||||
|
uint64_t gttSizeBytes = 0;
|
||||||
|
|
||||||
|
if (g_gpuGen >= 8) {
|
||||||
|
// Gen 8+ has a different encoding for GTT size
|
||||||
|
switch (gttSizeBits) {
|
||||||
|
case 0: gttSizeBytes = 0; break; // No GTT
|
||||||
|
case 1: gttSizeBytes = 2 * 1024 * 1024; break; // 2MB
|
||||||
|
case 2: gttSizeBytes = 4 * 1024 * 1024; break; // 4MB
|
||||||
|
case 3: gttSizeBytes = 8 * 1024 * 1024; break; // 8MB
|
||||||
|
default: gttSizeBytes = 2 * 1024 * 1024; break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Gen 5-7
|
||||||
|
switch (gttSizeBits) {
|
||||||
|
case 0: gttSizeBytes = 0; break; // No GTT
|
||||||
|
case 1: gttSizeBytes = 1024 * 1024; break; // 1MB
|
||||||
|
case 2: gttSizeBytes = 2 * 1024 * 1024; break; // 2MB
|
||||||
|
case 3: gttSizeBytes = 2 * 1024 * 1024; break; // Depends on gen, default 2MB
|
||||||
|
default: gttSizeBytes = 1024 * 1024; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gttSizeBytes == 0) {
|
||||||
|
// If hardware reports no GTT, assume 1MB as a safe fallback
|
||||||
|
gttSizeBytes = 1024 * 1024;
|
||||||
|
KernelLogStream(WARNING, "IntelGPU") << "GMCH_CTL reports no GTT, assuming 1MB";
|
||||||
|
}
|
||||||
|
|
||||||
|
g_gpuInfo.gttSize = gttSizeBytes;
|
||||||
|
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "GMCH_CTL: " << base::hex << (uint64_t)gmchCtl
|
||||||
|
<< ", GTT size: " << base::dec << (gttSizeBytes / 1024) << " KB";
|
||||||
|
|
||||||
|
// The GTT entries reside at BAR0 + 2MB (offset 0x200000)
|
||||||
|
// This is correct for most Intel generations
|
||||||
|
uint64_t gttPhys = g_gpuInfo.mmioPhys + 0x200000;
|
||||||
|
|
||||||
|
// Map the GTT region (it may overlap with already-mapped MMIO, but we map
|
||||||
|
// additional pages beyond the initial 2MB MMIO mapping)
|
||||||
|
uint64_t gttMapSize = gttSizeBytes;
|
||||||
|
// Ensure we map at least up to the GTT region end
|
||||||
|
for (uint64_t offset = 0; offset < gttMapSize; offset += 0x1000) {
|
||||||
|
Memory::VMM::g_paging->MapMMIO(gttPhys + offset, Memory::HHDM(gttPhys + offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_gttBase = (volatile void*)Memory::HHDM(gttPhys);
|
||||||
|
|
||||||
|
// Calculate number of GTT entries
|
||||||
|
if (g_gpuGen >= 8) {
|
||||||
|
// 64-bit PTEs
|
||||||
|
g_gttEntryCount = gttSizeBytes / sizeof(uint64_t);
|
||||||
|
} else {
|
||||||
|
// 32-bit PTEs
|
||||||
|
g_gttEntryCount = gttSizeBytes / sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "GTT at physical " << base::hex << gttPhys
|
||||||
|
<< ", " << base::dec << g_gttEntryCount << " entries"
|
||||||
|
<< (g_gpuGen >= 8 ? " (64-bit PTEs)" : " (32-bit PTEs)");
|
||||||
|
|
||||||
|
// Allocate a scratch page (zeroed) for future use
|
||||||
|
void* scratchPageVirt = Memory::g_pfa->AllocateZeroed();
|
||||||
|
g_scratchPagePhys = Memory::SubHHDM(scratchPageVirt);
|
||||||
|
|
||||||
|
// Do NOT clear the entire GTT here. The firmware has active GTT mappings
|
||||||
|
// that the display engine is currently scanning out from. Clearing them
|
||||||
|
// would cause the display to go black (or worse) before we remap.
|
||||||
|
// Instead, we only write the entries we need in SetupFramebuffer().
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "GTT ready: " << base::dec << g_gttEntryCount
|
||||||
|
<< " entries, scratch page at " << base::hex << g_scratchPagePhys;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Framebuffer Allocation and Setup
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static bool SetupFramebuffer() {
|
||||||
|
// Map the firmware framebuffer's contiguous physical pages through our GTT.
|
||||||
|
// This keeps the same physical memory that the firmware set up (contiguous
|
||||||
|
// pages, already HHDM-mapped), so both kernel and userspace access continue
|
||||||
|
// to work via the original virtual/physical addresses. No copy is needed.
|
||||||
|
uint32_t* fwFb = ::Graphics::Cursor::GetFramebufferBase();
|
||||||
|
if (fwFb == nullptr) {
|
||||||
|
KernelLogStream(ERROR, "IntelGPU") << "No firmware framebuffer available";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fwFbPhys = Memory::SubHHDM((uint64_t)fwFb);
|
||||||
|
uint64_t pageCount = (g_fbSize + 0xFFF) / 0x1000;
|
||||||
|
|
||||||
|
if (pageCount > g_gttEntryCount) {
|
||||||
|
KernelLogStream(ERROR, "IntelGPU") << "Framebuffer requires " << base::dec << pageCount
|
||||||
|
<< " pages but GTT only has " << g_gttEntryCount << " entries";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "Mapping " << base::dec << pageCount
|
||||||
|
<< " firmware FB pages through GTT (phys base " << base::hex << fwFbPhys << ")";
|
||||||
|
|
||||||
|
// Program GTT entries to point to the firmware FB's contiguous physical pages
|
||||||
|
if (g_gpuGen >= 8) {
|
||||||
|
volatile uint64_t* gtt64 = (volatile uint64_t*)g_gttBase;
|
||||||
|
for (uint64_t i = 0; i < pageCount; i++) {
|
||||||
|
gtt64[i] = MakeGttPte64(fwFbPhys + i * 0x1000);
|
||||||
|
}
|
||||||
|
// Flush GTT writes
|
||||||
|
(void)gtt64[pageCount - 1];
|
||||||
|
} else {
|
||||||
|
volatile uint32_t* gtt32 = (volatile uint32_t*)g_gttBase;
|
||||||
|
for (uint64_t i = 0; i < pageCount; i++) {
|
||||||
|
gtt32[i] = MakeGttPte32(fwFbPhys + i * 0x1000);
|
||||||
|
}
|
||||||
|
// Flush GTT writes
|
||||||
|
(void)gtt32[pageCount - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep using the same framebuffer memory
|
||||||
|
g_fbBase = fwFb;
|
||||||
|
g_fbPhysBase = fwFbPhys;
|
||||||
|
g_fbGttOffset = 0; // Starting at GTT entry 0 => offset 0
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "Framebuffer mapped through GTT: " << base::dec << pageCount
|
||||||
|
<< " pages, phys=" << base::hex << fwFbPhys;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ProgramDisplayPlane() {
|
||||||
|
// Preserve the firmware's DSPACNTR value entirely. The firmware already
|
||||||
|
// configured the correct pixel format, pipe assignment, and tiling mode.
|
||||||
|
// We only need to point DSPASURF to our GTT-mapped framebuffer.
|
||||||
|
uint32_t dspaCntr = ReadReg(DSPACNTR);
|
||||||
|
uint32_t fmtBits = (dspaCntr & DISP_FORMAT_MASK) >> DISP_FORMAT_SHIFT;
|
||||||
|
uint32_t oldDspaSurf = ReadReg(DSPASURF);
|
||||||
|
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "Preserving firmware DSPACNTR: " << base::hex
|
||||||
|
<< (uint64_t)dspaCntr << " (format=" << (uint64_t)fmtBits << ")";
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "Firmware DSPASURF was: " << base::hex
|
||||||
|
<< (uint64_t)oldDspaSurf;
|
||||||
|
|
||||||
|
// Ensure the plane is enabled (it should already be)
|
||||||
|
if (!(dspaCntr & DISP_ENABLE)) {
|
||||||
|
dspaCntr |= DISP_ENABLE;
|
||||||
|
WriteReg(DSPACNTR, dspaCntr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do NOT write DSPASTRIDE — the firmware already set the correct value
|
||||||
|
// in the hardware's native format (bytes on Gen <9, 64-byte units on Gen 9+).
|
||||||
|
// Writing our byte-converted g_fbPitch would corrupt it on Gen 9+.
|
||||||
|
|
||||||
|
// Write the GTT base offset to DSPASURF - this triggers the plane update
|
||||||
|
// Since we mapped at GTT entry 0, the offset is 0
|
||||||
|
uint32_t surfAddr = (uint32_t)g_fbGttOffset;
|
||||||
|
WriteReg(DSPASURF, surfAddr);
|
||||||
|
|
||||||
|
// Read back to flush
|
||||||
|
(void)ReadReg(DSPASURF);
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "Display plane A: DSPASURF="
|
||||||
|
<< base::hex << (uint64_t)surfAddr
|
||||||
|
<< " (was " << (uint64_t)oldDspaSurf << ")"
|
||||||
|
<< ", stride=" << base::dec << g_fbPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Public API
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
void Initialize() {
|
||||||
|
KernelLogStream(INFO, "IntelGPU") << "Scanning for Intel integrated graphics...";
|
||||||
|
|
||||||
|
// Step 1: Detect GPU on PCI bus
|
||||||
|
if (!DetectGpu()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Map BAR0 MMIO region
|
||||||
|
if (!MapMmio()) {
|
||||||
|
KernelLogStream(ERROR, "IntelGPU") << "Failed to map MMIO region";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Disable VGA plane
|
||||||
|
DisableVga();
|
||||||
|
|
||||||
|
// Step 4: Read current display state from firmware
|
||||||
|
if (!ReadDisplayState()) {
|
||||||
|
KernelLogStream(ERROR, "IntelGPU") << "Failed to read display state";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Initialize GTT
|
||||||
|
if (!InitializeGtt()) {
|
||||||
|
KernelLogStream(ERROR, "IntelGPU") << "Failed to initialize GTT";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: Map firmware framebuffer pages through GTT
|
||||||
|
if (!SetupFramebuffer()) {
|
||||||
|
KernelLogStream(ERROR, "IntelGPU") << "Failed to set up framebuffer";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 7: Program the display plane to use our GTT-mapped framebuffer
|
||||||
|
ProgramDisplayPlane();
|
||||||
|
|
||||||
|
g_initialized = true;
|
||||||
|
|
||||||
|
// Diagnostic: compare GPU-detected values with firmware/Limine values
|
||||||
|
uint64_t fwWidth = ::Graphics::Cursor::GetFramebufferWidth();
|
||||||
|
uint64_t fwHeight = ::Graphics::Cursor::GetFramebufferHeight();
|
||||||
|
uint64_t fwPitch = ::Graphics::Cursor::GetFramebufferPitch();
|
||||||
|
|
||||||
|
if (g_fbWidth != fwWidth || g_fbHeight != fwHeight || g_fbPitch != fwPitch) {
|
||||||
|
KernelLogStream(WARNING, "IntelGPU") << "GPU dimensions differ from firmware!";
|
||||||
|
KernelLogStream(WARNING, "IntelGPU") << " GPU: "
|
||||||
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
||||||
|
<< " pitch=" << g_fbPitch;
|
||||||
|
KernelLogStream(WARNING, "IntelGPU") << " Firmware: "
|
||||||
|
<< base::dec << fwWidth << "x" << fwHeight
|
||||||
|
<< " pitch=" << fwPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelLogStream(OK, "IntelGPU") << "Initialization complete: "
|
||||||
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
||||||
|
<< " @ " << base::hex << (uint64_t)g_fbBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInitialized() {
|
||||||
|
return g_initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GpuInfo* GetGpuInfo() {
|
||||||
|
return &g_gpuInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t* GetFramebufferBase() {
|
||||||
|
return g_fbBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetFramebufferPhysBase() {
|
||||||
|
return g_fbPhysBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetWidth() {
|
||||||
|
return g_fbWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetHeight() {
|
||||||
|
return g_fbHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetPitch() {
|
||||||
|
return g_fbPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,420 @@
|
|||||||
|
/*
|
||||||
|
* IntelGPU.hpp
|
||||||
|
* Intel integrated graphics (i915) modesetting driver
|
||||||
|
* Supports Gen 5 (Ironlake) through Gen 12 (Tiger Lake / Alder Lake)
|
||||||
|
* Copyright (c) 2025 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Drivers::Graphics::IntelGPU {
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// PCI identification
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
static constexpr uint16_t VendorIntel = 0x8086;
|
||||||
|
static constexpr uint8_t ClassDisplay = 0x03;
|
||||||
|
static constexpr uint8_t SubclassVGA = 0x00;
|
||||||
|
|
||||||
|
// PCI config space offsets
|
||||||
|
static constexpr uint16_t PCI_REG_BAR0 = 0x10;
|
||||||
|
static constexpr uint16_t PCI_REG_BAR2 = 0x18;
|
||||||
|
static constexpr uint16_t PCI_REG_COMMAND = 0x04;
|
||||||
|
static constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1);
|
||||||
|
static constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2);
|
||||||
|
|
||||||
|
// Graphics control register (PCI config offset 0x50 on SNB+)
|
||||||
|
static constexpr uint16_t PCI_REG_GMCH_CTL = 0x50;
|
||||||
|
|
||||||
|
// Supported Intel GPU device IDs (representative subset)
|
||||||
|
struct DeviceInfo {
|
||||||
|
uint16_t deviceId;
|
||||||
|
uint8_t gen; // Intel graphics generation (5-12)
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr DeviceInfo SupportedDevices[] = {
|
||||||
|
// Gen 5 - Ironlake
|
||||||
|
{0x0042, 5, "Ironlake Desktop"},
|
||||||
|
{0x0046, 5, "Ironlake Mobile"},
|
||||||
|
|
||||||
|
// Gen 6 - Sandy Bridge
|
||||||
|
{0x0102, 6, "Sandy Bridge GT1 Desktop"},
|
||||||
|
{0x0112, 6, "Sandy Bridge GT2 Desktop"},
|
||||||
|
{0x0122, 6, "Sandy Bridge GT2 Desktop"},
|
||||||
|
{0x0106, 6, "Sandy Bridge GT1 Mobile"},
|
||||||
|
{0x0116, 6, "Sandy Bridge GT2 Mobile"},
|
||||||
|
{0x0126, 6, "Sandy Bridge GT2 Mobile"},
|
||||||
|
{0x010A, 6, "Sandy Bridge GT1 Server"},
|
||||||
|
|
||||||
|
// Gen 7 - Ivy Bridge
|
||||||
|
{0x0152, 7, "Ivy Bridge GT1 Desktop"},
|
||||||
|
{0x0162, 7, "Ivy Bridge GT2 Desktop"},
|
||||||
|
{0x0156, 7, "Ivy Bridge GT1 Mobile"},
|
||||||
|
{0x0166, 7, "Ivy Bridge GT2 Mobile"},
|
||||||
|
{0x015A, 7, "Ivy Bridge GT1 Server"},
|
||||||
|
{0x016A, 7, "Ivy Bridge GT2 Server"},
|
||||||
|
|
||||||
|
// Gen 7.5 - Haswell
|
||||||
|
{0x0402, 7, "Haswell GT1 Desktop"},
|
||||||
|
{0x0412, 7, "Haswell GT2 Desktop"},
|
||||||
|
{0x0422, 7, "Haswell GT3 Desktop"},
|
||||||
|
{0x0406, 7, "Haswell GT1 Mobile"},
|
||||||
|
{0x0416, 7, "Haswell GT2 Mobile"},
|
||||||
|
{0x0426, 7, "Haswell GT3 Mobile"},
|
||||||
|
{0x0A06, 7, "Haswell ULT GT1"},
|
||||||
|
{0x0A16, 7, "Haswell ULT GT2"},
|
||||||
|
{0x0A26, 7, "Haswell ULT GT3"},
|
||||||
|
{0x0D12, 7, "Haswell CRW GT2"},
|
||||||
|
{0x0D22, 7, "Haswell CRW GT3"},
|
||||||
|
|
||||||
|
// Gen 8 - Broadwell
|
||||||
|
{0x1602, 8, "Broadwell GT1"},
|
||||||
|
{0x1612, 8, "Broadwell GT2"},
|
||||||
|
{0x1616, 8, "Broadwell GT2 Mobile"},
|
||||||
|
{0x1622, 8, "Broadwell GT3"},
|
||||||
|
{0x1626, 8, "Broadwell GT3 Mobile"},
|
||||||
|
{0x162A, 8, "Broadwell GT3 Server"},
|
||||||
|
|
||||||
|
// Gen 9 - Skylake
|
||||||
|
{0x1902, 9, "Skylake GT1 Desktop"},
|
||||||
|
{0x1906, 9, "Skylake GT1 Mobile"},
|
||||||
|
{0x1912, 9, "Skylake GT2 Desktop"},
|
||||||
|
{0x1916, 9, "Skylake GT2 Mobile"},
|
||||||
|
{0x191E, 9, "Skylake GT2 Mobile"},
|
||||||
|
{0x1926, 9, "Skylake GT3 Mobile"},
|
||||||
|
{0x1932, 9, "Skylake GT4 Desktop"},
|
||||||
|
|
||||||
|
// Gen 9.5 - Kaby Lake / Coffee Lake
|
||||||
|
{0x5902, 9, "Kaby Lake GT1 Desktop"},
|
||||||
|
{0x5912, 9, "Kaby Lake GT2 Desktop"},
|
||||||
|
{0x5916, 9, "Kaby Lake GT2 Mobile"},
|
||||||
|
{0x5926, 9, "Kaby Lake GT3 Mobile"},
|
||||||
|
{0x3E90, 9, "Coffee Lake GT1 Desktop"},
|
||||||
|
{0x3E92, 9, "Coffee Lake GT2 Desktop"},
|
||||||
|
{0x3EA0, 9, "Coffee Lake GT3"},
|
||||||
|
{0x3E91, 9, "Coffee Lake GT2 Desktop"},
|
||||||
|
{0x3E98, 9, "Coffee Lake GT2 Desktop"},
|
||||||
|
{0x9B41, 9, "Comet Lake GT2"},
|
||||||
|
{0x9BA5, 9, "Comet Lake GT2 Mobile"},
|
||||||
|
|
||||||
|
// Gen 11 - Ice Lake
|
||||||
|
{0x8A52, 11, "Ice Lake GT2"},
|
||||||
|
{0x8A56, 11, "Ice Lake GT2 Mobile"},
|
||||||
|
{0x8A5A, 11, "Ice Lake GT1.5"},
|
||||||
|
{0x8A5C, 11, "Ice Lake GT1"},
|
||||||
|
|
||||||
|
// Gen 12 - Tiger Lake
|
||||||
|
{0x9A49, 12, "Tiger Lake GT2"},
|
||||||
|
{0x9A78, 12, "Tiger Lake GT2"},
|
||||||
|
{0x9A40, 12, "Tiger Lake GT2"},
|
||||||
|
|
||||||
|
// Gen 12 - Alder Lake
|
||||||
|
{0x4626, 12, "Alder Lake GT2"},
|
||||||
|
{0x4680, 12, "Alder Lake-S GT1"},
|
||||||
|
{0x4692, 12, "Alder Lake-S GT1"},
|
||||||
|
{0x46A6, 12, "Alder Lake-P GT2"},
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr int SupportedDeviceCount = sizeof(SupportedDevices) / sizeof(SupportedDevices[0]);
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// MMIO register offsets (relative to BAR0)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
// --- VGA control ---
|
||||||
|
static constexpr uint32_t VGACNTRL = 0x71400;
|
||||||
|
static constexpr uint32_t VGACNTRL_DISABLE = (1u << 31);
|
||||||
|
|
||||||
|
// --- DPLL (Display PLL) ---
|
||||||
|
static constexpr uint32_t DPLL_A = 0x06014;
|
||||||
|
static constexpr uint32_t DPLL_B = 0x06018;
|
||||||
|
static constexpr uint32_t FPA0 = 0x06040;
|
||||||
|
static constexpr uint32_t FPA1 = 0x06044;
|
||||||
|
static constexpr uint32_t FPB0 = 0x06048;
|
||||||
|
static constexpr uint32_t FPB1 = 0x0604C;
|
||||||
|
|
||||||
|
// DPLL control bits
|
||||||
|
static constexpr uint32_t DPLL_VCO_ENABLE = (1u << 31);
|
||||||
|
static constexpr uint32_t DPLL_VGA_MODE_DIS = (1u << 28);
|
||||||
|
static constexpr uint32_t DPLL_MODE_DAC_SDVO = (1u << 26);
|
||||||
|
static constexpr uint32_t DPLL_MODE_LVDS = (2u << 26);
|
||||||
|
|
||||||
|
// FP register fields
|
||||||
|
static constexpr uint32_t FP_N_DIV_SHIFT = 16;
|
||||||
|
static constexpr uint32_t FP_N_DIV_MASK = 0x3F0000;
|
||||||
|
static constexpr uint32_t FP_M1_DIV_SHIFT = 8;
|
||||||
|
static constexpr uint32_t FP_M1_DIV_MASK = 0x003F00;
|
||||||
|
static constexpr uint32_t FP_M2_DIV_SHIFT = 0;
|
||||||
|
static constexpr uint32_t FP_M2_DIV_MASK = 0x00003F;
|
||||||
|
|
||||||
|
// --- Display timing registers (Pipe A) ---
|
||||||
|
static constexpr uint32_t HTOTAL_A = 0x60000;
|
||||||
|
static constexpr uint32_t HBLANK_A = 0x60004;
|
||||||
|
static constexpr uint32_t HSYNC_A = 0x60008;
|
||||||
|
static constexpr uint32_t VTOTAL_A = 0x6000C;
|
||||||
|
static constexpr uint32_t VBLANK_A = 0x60010;
|
||||||
|
static constexpr uint32_t VSYNC_A = 0x60014;
|
||||||
|
static constexpr uint32_t PIPEASRC = 0x6001C;
|
||||||
|
|
||||||
|
// --- Display timing registers (Pipe B) ---
|
||||||
|
static constexpr uint32_t HTOTAL_B = 0x61000;
|
||||||
|
static constexpr uint32_t HBLANK_B = 0x61004;
|
||||||
|
static constexpr uint32_t HSYNC_B = 0x61008;
|
||||||
|
static constexpr uint32_t VTOTAL_B = 0x6100C;
|
||||||
|
static constexpr uint32_t VBLANK_B = 0x61010;
|
||||||
|
static constexpr uint32_t VSYNC_B = 0x61014;
|
||||||
|
static constexpr uint32_t PIPEBSRC = 0x6101C;
|
||||||
|
|
||||||
|
// --- Pipe configuration ---
|
||||||
|
static constexpr uint32_t PIPEACONF = 0x70008;
|
||||||
|
static constexpr uint32_t PIPEBCONF = 0x71008;
|
||||||
|
|
||||||
|
static constexpr uint32_t PIPECONF_ENABLE = (1u << 31);
|
||||||
|
static constexpr uint32_t PIPECONF_STATE = (1u << 30);
|
||||||
|
static constexpr uint32_t PIPECONF_8BPC = (0u << 5);
|
||||||
|
static constexpr uint32_t PIPECONF_10BPC = (1u << 5);
|
||||||
|
static constexpr uint32_t PIPECONF_6BPC = (2u << 5);
|
||||||
|
static constexpr uint32_t PIPECONF_12BPC = (3u << 5);
|
||||||
|
|
||||||
|
// --- Display plane control (Plane A, pre-Skylake i9xx-style) ---
|
||||||
|
static constexpr uint32_t DSPACNTR = 0x70180;
|
||||||
|
static constexpr uint32_t DSPALINOFF = 0x70184;
|
||||||
|
static constexpr uint32_t DSPASTRIDE = 0x70188;
|
||||||
|
static constexpr uint32_t DSPAPOS = 0x7018C;
|
||||||
|
static constexpr uint32_t DSPASIZE = 0x70190;
|
||||||
|
static constexpr uint32_t DSPASURF = 0x7019C;
|
||||||
|
static constexpr uint32_t DSPATILEOFF = 0x701A4;
|
||||||
|
|
||||||
|
// --- Display plane control (Plane B) ---
|
||||||
|
static constexpr uint32_t DSPBCNTR = 0x71180;
|
||||||
|
static constexpr uint32_t DSPBLINOFF = 0x71184;
|
||||||
|
static constexpr uint32_t DSPBSTRIDE = 0x71188;
|
||||||
|
static constexpr uint32_t DSPBPOS = 0x7118C;
|
||||||
|
static constexpr uint32_t DSPBSIZE = 0x71190;
|
||||||
|
static constexpr uint32_t DSPBSURF = 0x7119C;
|
||||||
|
static constexpr uint32_t DSPBTILEOFF = 0x711A4;
|
||||||
|
|
||||||
|
// DSPCNTR bits
|
||||||
|
static constexpr uint32_t DISP_ENABLE = (1u << 31);
|
||||||
|
static constexpr uint32_t DISP_GAMMA_ENABLE = (1u << 30);
|
||||||
|
static constexpr uint32_t DISP_FORMAT_SHIFT = 26;
|
||||||
|
static constexpr uint32_t DISP_FORMAT_MASK = (0xFu << 26);
|
||||||
|
static constexpr uint32_t DISP_FORMAT_BGRX8888 = (0x6u << 26); // 32bpp BGRX (no alpha)
|
||||||
|
static constexpr uint32_t DISP_FORMAT_BGRA8888 = (0x7u << 26); // 32bpp BGRA (with alpha)
|
||||||
|
static constexpr uint32_t DISP_FORMAT_RGBX8888 = (0xEu << 26); // 32bpp RGBX
|
||||||
|
static constexpr uint32_t DISP_FORMAT_BGR565 = (0x5u << 26); // 16bpp BGR 5:6:5
|
||||||
|
static constexpr uint32_t DISP_FORMAT_BGRX1010102 = (0xAu << 26);
|
||||||
|
static constexpr uint32_t DISP_PIPE_B_SELECT = (1u << 24);
|
||||||
|
static constexpr uint32_t DISP_TILED = (1u << 10);
|
||||||
|
|
||||||
|
// --- Cursor plane (Pipe A) ---
|
||||||
|
static constexpr uint32_t CURACNTR = 0x70080;
|
||||||
|
static constexpr uint32_t CURABASE = 0x70084;
|
||||||
|
static constexpr uint32_t CURAPOS = 0x70088;
|
||||||
|
|
||||||
|
// --- Output connectors ---
|
||||||
|
static constexpr uint32_t ADPA = 0x61100; // Analog Display Port (VGA/CRT)
|
||||||
|
static constexpr uint32_t DVOB = 0x61140; // DVO-B
|
||||||
|
static constexpr uint32_t DVOC = 0x61160; // DVO-C
|
||||||
|
static constexpr uint32_t LVDS = 0x61180; // LVDS panel
|
||||||
|
static constexpr uint32_t DP_B = 0x64100; // DisplayPort B
|
||||||
|
static constexpr uint32_t DP_C = 0x64200; // DisplayPort C
|
||||||
|
static constexpr uint32_t DP_D = 0x64300; // DisplayPort D
|
||||||
|
static constexpr uint32_t HDMI_B = 0x61140; // HDMI-B (same as DVOB on some gens)
|
||||||
|
static constexpr uint32_t HDMI_C = 0x61160; // HDMI-C (same as DVOC on some gens)
|
||||||
|
|
||||||
|
// ADPA bits
|
||||||
|
static constexpr uint32_t ADPA_DAC_ENABLE = (1u << 31);
|
||||||
|
static constexpr uint32_t ADPA_PIPE_B_SELECT = (1u << 30);
|
||||||
|
static constexpr uint32_t ADPA_HSYNC_ACTIVE_LOW = (1u << 3);
|
||||||
|
static constexpr uint32_t ADPA_VSYNC_ACTIVE_LOW = (1u << 4);
|
||||||
|
|
||||||
|
// LVDS bits
|
||||||
|
static constexpr uint32_t LVDS_PORT_ENABLE = (1u << 31);
|
||||||
|
static constexpr uint32_t LVDS_PIPE_B_SELECT = (1u << 30);
|
||||||
|
|
||||||
|
// --- GMBUS (I2C for EDID) ---
|
||||||
|
static constexpr uint32_t GMBUS0 = 0x5100; // Clock/port select
|
||||||
|
static constexpr uint32_t GMBUS1 = 0x5104; // Command/status
|
||||||
|
static constexpr uint32_t GMBUS2 = 0x5108; // Status
|
||||||
|
static constexpr uint32_t GMBUS3 = 0x510C; // Data buffer
|
||||||
|
static constexpr uint32_t GMBUS4 = 0x5110; // Interrupt mask
|
||||||
|
static constexpr uint32_t GMBUS5 = 0x5120; // 2-byte index
|
||||||
|
|
||||||
|
// --- Hardware status page ---
|
||||||
|
static constexpr uint32_t HWS_PGA = 0x02080;
|
||||||
|
|
||||||
|
// --- Fence registers (tiling) ---
|
||||||
|
static constexpr uint32_t FENCE_REG_BASE = 0x02000; // Gen 2-3
|
||||||
|
static constexpr uint32_t FENCE_REG_965_BASE = 0x03000; // Gen 4+
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// GTT (Graphics Translation Table)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
// GTT PTE format for Gen 6/7 (Sandy Bridge through Haswell) - 32-bit entries
|
||||||
|
// Bits [31:12] = physical page address [31:12]
|
||||||
|
// Bits [10:4] = physical page address [38:32]
|
||||||
|
// Bits [3:1] = cacheability
|
||||||
|
// Bit [0] = valid
|
||||||
|
static constexpr uint32_t GTT_PTE_VALID = (1u << 0);
|
||||||
|
static constexpr uint32_t GTT_PTE_WB_LLC = (3u << 1); // Write-back LLC cache
|
||||||
|
static constexpr uint32_t GTT_PTE_UNCACHED = (0u << 1);
|
||||||
|
|
||||||
|
// Gen 8+ uses 64-bit GTT PTEs
|
||||||
|
static constexpr uint64_t GTT_PTE64_VALID = (1ULL << 0);
|
||||||
|
|
||||||
|
// Helper: Build a Gen 6/7 GTT PTE from a physical address
|
||||||
|
static inline uint32_t MakeGttPte32(uint64_t physAddr) {
|
||||||
|
uint32_t pte = (uint32_t)(physAddr & 0xFFFFF000u); // bits [31:12]
|
||||||
|
pte |= (uint32_t)((physAddr >> 28) & 0x7F0u); // bits [38:32] -> PTE [10:4]
|
||||||
|
pte |= GTT_PTE_VALID;
|
||||||
|
return pte;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: Build a Gen 8+ GTT PTE from a physical address
|
||||||
|
static inline uint64_t MakeGttPte64(uint64_t physAddr) {
|
||||||
|
return (physAddr & ~0xFFFULL) | GTT_PTE64_VALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// DPLL clock calculation structures
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
struct DpllParams {
|
||||||
|
uint32_t n;
|
||||||
|
uint32_t m1;
|
||||||
|
uint32_t m2;
|
||||||
|
uint32_t p1;
|
||||||
|
uint32_t p2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// DPLL parameter limits (Gen 6 / Sandy Bridge)
|
||||||
|
struct DpllLimits {
|
||||||
|
uint32_t nMin, nMax;
|
||||||
|
uint32_t m1Min, m1Max;
|
||||||
|
uint32_t m2Min, m2Max;
|
||||||
|
uint32_t p1Min, p1Max;
|
||||||
|
uint32_t p2Slow, p2Fast; // P2 values for slow/fast pixel clocks
|
||||||
|
uint32_t p2Threshold; // kHz threshold between slow/fast P2
|
||||||
|
uint32_t vcoMin, vcoMax; // VCO range in kHz
|
||||||
|
uint32_t refClock; // Reference clock in kHz
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sandy Bridge / Ivy Bridge DAC/SDVO limits
|
||||||
|
static constexpr DpllLimits SNB_DAC_LIMITS = {
|
||||||
|
.nMin = 1, .nMax = 5,
|
||||||
|
.m1Min = 12, .m1Max = 22,
|
||||||
|
.m2Min = 5, .m2Max = 9,
|
||||||
|
.p1Min = 1, .p1Max = 8,
|
||||||
|
.p2Slow = 10, .p2Fast = 5,
|
||||||
|
.p2Threshold = 225000,
|
||||||
|
.vcoMin = 1750000, .vcoMax = 3500000,
|
||||||
|
.refClock = 120000,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sandy Bridge / Ivy Bridge LVDS limits
|
||||||
|
static constexpr DpllLimits SNB_LVDS_LIMITS = {
|
||||||
|
.nMin = 1, .nMax = 3,
|
||||||
|
.m1Min = 12, .m1Max = 22,
|
||||||
|
.m2Min = 5, .m2Max = 9,
|
||||||
|
.p1Min = 1, .p1Max = 8,
|
||||||
|
.p2Slow = 14, .p2Fast = 7,
|
||||||
|
.p2Threshold = 225000,
|
||||||
|
.vcoMin = 1750000, .vcoMax = 3500000,
|
||||||
|
.refClock = 120000,
|
||||||
|
};
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Display mode timing
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
struct DisplayMode {
|
||||||
|
uint32_t hdisplay; // Horizontal active pixels
|
||||||
|
uint32_t hsyncStart;
|
||||||
|
uint32_t hsyncEnd;
|
||||||
|
uint32_t htotal;
|
||||||
|
uint32_t vdisplay; // Vertical active lines
|
||||||
|
uint32_t vsyncStart;
|
||||||
|
uint32_t vsyncEnd;
|
||||||
|
uint32_t vtotal;
|
||||||
|
uint32_t pixelClock; // in kHz
|
||||||
|
bool hsyncPositive;
|
||||||
|
bool vsyncPositive;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Common mode timings (CVT/DMT standard)
|
||||||
|
static constexpr DisplayMode MODE_1920x1080_60 = {
|
||||||
|
.hdisplay = 1920, .hsyncStart = 2008, .hsyncEnd = 2052, .htotal = 2200,
|
||||||
|
.vdisplay = 1080, .vsyncStart = 1084, .vsyncEnd = 1089, .vtotal = 1125,
|
||||||
|
.pixelClock = 148500,
|
||||||
|
.hsyncPositive = true, .vsyncPositive = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr DisplayMode MODE_1280x720_60 = {
|
||||||
|
.hdisplay = 1280, .hsyncStart = 1390, .hsyncEnd = 1430, .htotal = 1650,
|
||||||
|
.vdisplay = 720, .vsyncStart = 725, .vsyncEnd = 730, .vtotal = 750,
|
||||||
|
.pixelClock = 74250,
|
||||||
|
.hsyncPositive = true, .vsyncPositive = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr DisplayMode MODE_1024x768_60 = {
|
||||||
|
.hdisplay = 1024, .hsyncStart = 1048, .hsyncEnd = 1184, .htotal = 1344,
|
||||||
|
.vdisplay = 768, .vsyncStart = 771, .vsyncEnd = 777, .vtotal = 806,
|
||||||
|
.pixelClock = 65000,
|
||||||
|
.hsyncPositive = false, .vsyncPositive = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr DisplayMode MODE_800x600_60 = {
|
||||||
|
.hdisplay = 800, .hsyncStart = 840, .hsyncEnd = 968, .htotal = 1056,
|
||||||
|
.vdisplay = 600, .vsyncStart = 601, .vsyncEnd = 605, .vtotal = 628,
|
||||||
|
.pixelClock = 40000,
|
||||||
|
.hsyncPositive = true, .vsyncPositive = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Detected GPU information
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
struct GpuInfo {
|
||||||
|
uint16_t deviceId;
|
||||||
|
uint8_t gen;
|
||||||
|
const char* name;
|
||||||
|
uint8_t pciBus;
|
||||||
|
uint8_t pciDevice;
|
||||||
|
uint8_t pciFunction;
|
||||||
|
uint64_t mmioPhys; // BAR0 physical address
|
||||||
|
uint64_t mmioSize; // BAR0 region size
|
||||||
|
uint64_t gmadrPhys; // BAR2 physical (aperture)
|
||||||
|
uint64_t gttSize; // GTT size in bytes
|
||||||
|
uint32_t stolenMb; // Stolen memory in MB
|
||||||
|
};
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Public API
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
// Initialize the Intel GPU driver (scans PCI, maps MMIO, sets up GTT + FB)
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
// Check if an Intel GPU was found and initialized
|
||||||
|
bool IsInitialized();
|
||||||
|
|
||||||
|
// Get detected GPU information
|
||||||
|
const GpuInfo* GetGpuInfo();
|
||||||
|
|
||||||
|
// Framebuffer access (returns HHDM virtual address)
|
||||||
|
uint32_t* GetFramebufferBase();
|
||||||
|
|
||||||
|
// Framebuffer physical base (for userspace mapping)
|
||||||
|
uint64_t GetFramebufferPhysBase();
|
||||||
|
|
||||||
|
// Framebuffer dimensions
|
||||||
|
uint64_t GetWidth();
|
||||||
|
uint64_t GetHeight();
|
||||||
|
uint64_t GetPitch();
|
||||||
|
|
||||||
|
};
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "Cursor.hpp"
|
#include "Cursor.hpp"
|
||||||
#include <Terminal/Terminal.hpp>
|
#include <Terminal/Terminal.hpp>
|
||||||
|
#include <Memory/HHDM.hpp>
|
||||||
|
|
||||||
namespace Graphics::Cursor {
|
namespace Graphics::Cursor {
|
||||||
|
|
||||||
@@ -30,4 +31,17 @@ namespace Graphics::Cursor {
|
|||||||
uint64_t GetFramebufferHeight() { return g_FbHeight; }
|
uint64_t GetFramebufferHeight() { return g_FbHeight; }
|
||||||
uint64_t GetFramebufferPitch() { return g_FbPitch; }
|
uint64_t GetFramebufferPitch() { return g_FbPitch; }
|
||||||
|
|
||||||
|
void SetFramebuffer(uint32_t* base, uint64_t width, uint64_t height, uint64_t pitch) {
|
||||||
|
g_FbBase = base;
|
||||||
|
g_FbWidth = width;
|
||||||
|
g_FbHeight = height;
|
||||||
|
g_FbPitch = pitch;
|
||||||
|
Kt::KernelLogStream(Kt::OK, "Graphics") << "Framebuffer switched ("
|
||||||
|
<< (uint64_t)g_FbWidth << "x" << (uint64_t)g_FbHeight << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetFramebufferPhysBase() {
|
||||||
|
return Memory::SubHHDM((uint64_t)g_FbBase);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,4 +17,7 @@ namespace Graphics::Cursor {
|
|||||||
uint64_t GetFramebufferHeight();
|
uint64_t GetFramebufferHeight();
|
||||||
uint64_t GetFramebufferPitch();
|
uint64_t GetFramebufferPitch();
|
||||||
|
|
||||||
|
void SetFramebuffer(uint32_t* base, uint64_t width, uint64_t height, uint64_t pitch);
|
||||||
|
uint64_t GetFramebufferPhysBase();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,5 +26,34 @@ namespace Hal {
|
|||||||
static constexpr uint32_t IA32_STAR = 0xC0000081;
|
static constexpr uint32_t IA32_STAR = 0xC0000081;
|
||||||
static constexpr uint32_t IA32_LSTAR = 0xC0000082;
|
static constexpr uint32_t IA32_LSTAR = 0xC0000082;
|
||||||
static constexpr uint32_t IA32_FMASK = 0xC0000084;
|
static constexpr uint32_t IA32_FMASK = 0xC0000084;
|
||||||
|
static constexpr uint32_t IA32_PAT = 0x00000277;
|
||||||
|
|
||||||
|
// PAT memory type encodings
|
||||||
|
static constexpr uint8_t PAT_UC = 0x00; // Uncacheable
|
||||||
|
static constexpr uint8_t PAT_WC = 0x01; // Write Combining
|
||||||
|
static constexpr uint8_t PAT_WT = 0x04; // Write Through
|
||||||
|
static constexpr uint8_t PAT_WP = 0x05; // Write Protect
|
||||||
|
static constexpr uint8_t PAT_WB = 0x06; // Write Back
|
||||||
|
static constexpr uint8_t PAT_UCM = 0x07; // UC- (UC minus)
|
||||||
|
|
||||||
|
// Program PAT so entry 1 = WC (default is WT).
|
||||||
|
// PAT index is selected by PTE bits: PAT(bit7) | PCD(bit4) | PWT(bit3)
|
||||||
|
// Entry 0 (000) = WB — normal memory (unchanged)
|
||||||
|
// Entry 1 (001) = WC — framebuffers (was WT)
|
||||||
|
// Entry 2 (010) = UC- — (unchanged)
|
||||||
|
// Entry 3 (011) = UC — MMIO registers (unchanged)
|
||||||
|
// Entries 4-7: unchanged from defaults
|
||||||
|
inline void InitializePAT() {
|
||||||
|
uint64_t pat =
|
||||||
|
((uint64_t)PAT_WB << 0) | // Entry 0: WB
|
||||||
|
((uint64_t)PAT_WC << 8) | // Entry 1: WC (was WT)
|
||||||
|
((uint64_t)PAT_UCM << 16) | // Entry 2: UC-
|
||||||
|
((uint64_t)PAT_UC << 24) | // Entry 3: UC
|
||||||
|
((uint64_t)PAT_WB << 32) | // Entry 4: WB
|
||||||
|
((uint64_t)PAT_WT << 40) | // Entry 5: WT
|
||||||
|
((uint64_t)PAT_UCM << 48) | // Entry 6: UC-
|
||||||
|
((uint64_t)PAT_UC << 56); // Entry 7: UC
|
||||||
|
WriteMSR(IA32_PAT, pat);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-2
@@ -35,9 +35,11 @@
|
|||||||
#include <Drivers/PS2/Mouse.hpp>
|
#include <Drivers/PS2/Mouse.hpp>
|
||||||
#include <Drivers/Net/E1000.hpp>
|
#include <Drivers/Net/E1000.hpp>
|
||||||
#include <Drivers/Net/E1000E.hpp>
|
#include <Drivers/Net/E1000E.hpp>
|
||||||
|
#include <Drivers/Graphics/IntelGPU.hpp>
|
||||||
#include <Net/Net.hpp>
|
#include <Net/Net.hpp>
|
||||||
#include <CppLib/BoxUI.hpp>
|
#include <CppLib/BoxUI.hpp>
|
||||||
#include <Graphics/Cursor.hpp>
|
#include <Graphics/Cursor.hpp>
|
||||||
|
#include <Hal/MSR.hpp>
|
||||||
#include <Fs/Ramdisk.hpp>
|
#include <Fs/Ramdisk.hpp>
|
||||||
#include <Fs/Vfs.hpp>
|
#include <Fs/Vfs.hpp>
|
||||||
#include <Sched/Scheduler.hpp>
|
#include <Sched/Scheduler.hpp>
|
||||||
@@ -138,7 +140,38 @@ extern "C" void kmain() {
|
|||||||
Memory::VMM::g_paging = &g_paging;
|
Memory::VMM::g_paging = &g_paging;
|
||||||
g_paging.Init((uint64_t)&KernelStartSymbol, ((uint64_t)&KernelEndSymbol - (uint64_t)&KernelStartSymbol), memmap_request.response);
|
g_paging.Init((uint64_t)&KernelStartSymbol, ((uint64_t)&KernelEndSymbol - (uint64_t)&KernelStartSymbol), memmap_request.response);
|
||||||
|
|
||||||
|
// Reprogram PAT so entry 1 = Write-Combining (default is Write-Through).
|
||||||
|
// Must be done after paging init and before any WC mappings.
|
||||||
|
Hal::InitializePAT();
|
||||||
|
Kt::KernelLogStream(OK, "Hal") << "PAT reprogrammed (entry 1 = WC)";
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Initialize Cursor early so we can WC-map the framebuffer before
|
||||||
|
// the bulk of boot logging begins (ACPI, PCI, drivers, etc.)
|
||||||
|
Graphics::Cursor::Initialize(framebuffer);
|
||||||
|
|
||||||
|
#if defined (__x86_64__)
|
||||||
|
// Map framebuffer as Write-Combining immediately for faster screen writes.
|
||||||
|
// All subsequent log output benefits from WC burst transfers.
|
||||||
|
{
|
||||||
|
uint64_t fbPhys = Graphics::Cursor::GetFramebufferPhysBase();
|
||||||
|
uint64_t fbSize = Graphics::Cursor::GetFramebufferHeight()
|
||||||
|
* Graphics::Cursor::GetFramebufferPitch();
|
||||||
|
uint64_t numPages = (fbSize + 0xFFF) / 0x1000;
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < numPages; i++) {
|
||||||
|
uint64_t phys = fbPhys + i * 0x1000;
|
||||||
|
g_paging.MapWC(phys, Memory::HHDM(phys));
|
||||||
|
}
|
||||||
|
|
||||||
|
asm volatile("mov %%cr3, %%rax; mov %%rax, %%cr3" ::: "rax", "memory");
|
||||||
|
|
||||||
|
Kt::KernelLogStream(OK, "Graphics") << "Framebuffer mapped as Write-Combining ("
|
||||||
|
<< kcp::dec << numPages << " pages)";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Hal::ACPI g_acpi((Hal::ACPI::XSDP*)Memory::HHDM(rsdp_request.response->address));
|
Hal::ACPI g_acpi((Hal::ACPI::XSDP*)Memory::HHDM(rsdp_request.response->address));
|
||||||
|
|
||||||
#if defined (__x86_64__)
|
#if defined (__x86_64__)
|
||||||
@@ -147,6 +180,18 @@ extern "C" void kmain() {
|
|||||||
|
|
||||||
Pci::Initialize(g_acpi.GetXSDT());
|
Pci::Initialize(g_acpi.GetXSDT());
|
||||||
|
|
||||||
|
// Intel GPU driver — initialize right after PCI so the native
|
||||||
|
// driver takes over early and all subsequent logs use it.
|
||||||
|
Drivers::Graphics::IntelGPU::Initialize();
|
||||||
|
if (Drivers::Graphics::IntelGPU::IsInitialized()) {
|
||||||
|
Graphics::Cursor::SetFramebuffer(
|
||||||
|
Drivers::Graphics::IntelGPU::GetFramebufferBase(),
|
||||||
|
Drivers::Graphics::IntelGPU::GetWidth(),
|
||||||
|
Drivers::Graphics::IntelGPU::GetHeight(),
|
||||||
|
Drivers::Graphics::IntelGPU::GetPitch()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Timekeeping::ApicTimerInitialize();
|
Timekeeping::ApicTimerInitialize();
|
||||||
|
|
||||||
Drivers::PS2::Initialize();
|
Drivers::PS2::Initialize();
|
||||||
@@ -199,8 +244,6 @@ extern "C" void kmain() {
|
|||||||
};
|
};
|
||||||
Fs::Vfs::RegisterDrive(0, &ramdiskDriver);
|
Fs::Vfs::RegisterDrive(0, &ramdiskDriver);
|
||||||
|
|
||||||
Graphics::Cursor::Initialize(framebuffer);
|
|
||||||
|
|
||||||
Hal::LoadTSS();
|
Hal::LoadTSS();
|
||||||
Zenith::InitializeSyscalls();
|
Zenith::InitializeSyscalls();
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,26 @@ namespace Memory::VMM {
|
|||||||
pageEntry->Address = physicalAddress >> 12;
|
pageEntry->Address = physicalAddress >> 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Paging::MapWC(std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
|
||||||
|
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
|
||||||
|
Panic("Value that isn't page-aligned passed as address to Paging::MapWC!", nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualAddress virtualAddressObj(virtualAddress);
|
||||||
|
|
||||||
|
auto PML3 = HandleLevel(virtualAddressObj, PML4, 4);
|
||||||
|
auto PML2 = HandleLevel(virtualAddressObj, PML3, 3);
|
||||||
|
auto PML1 = HandleLevel(virtualAddressObj, PML2, 2);
|
||||||
|
|
||||||
|
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&PML1->entries[virtualAddressObj.GetPageIndex()]);
|
||||||
|
|
||||||
|
pageEntry->Present = true;
|
||||||
|
pageEntry->Writable = true;
|
||||||
|
pageEntry->WriteThrough = true; // PWT=1, PCD=0 → PAT entry 1 = WC
|
||||||
|
|
||||||
|
pageEntry->Address = physicalAddress >> 12;
|
||||||
|
}
|
||||||
|
|
||||||
void Paging::MapMMIO(std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
|
void Paging::MapMMIO(std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
|
||||||
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
|
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
|
||||||
Panic("Value that isn't page-aligned passed as address to Paging::MapMMIO!", nullptr);
|
Panic("Value that isn't page-aligned passed as address to Paging::MapMMIO!", nullptr);
|
||||||
@@ -185,6 +205,41 @@ namespace Memory::VMM {
|
|||||||
pageEntry->Address = physicalAddress >> 12;
|
pageEntry->Address = physicalAddress >> 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Paging::MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
|
||||||
|
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
|
||||||
|
Panic("Non-aligned address in Paging::MapUserInWC!", nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualAddress va(virtualAddress);
|
||||||
|
|
||||||
|
auto walkLevel = [](PageTable* table, uint64_t index) -> PageTable* {
|
||||||
|
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[index]);
|
||||||
|
if (!entry->Present) {
|
||||||
|
entry->Present = true;
|
||||||
|
entry->Writable = true;
|
||||||
|
entry->Supervisor = 1;
|
||||||
|
uint64_t newPhys = Memory::SubHHDM((uint64_t)Memory::g_pfa->AllocateZeroed());
|
||||||
|
entry->Address = newPhys >> 12;
|
||||||
|
return (PageTable*)newPhys;
|
||||||
|
} else {
|
||||||
|
entry->Supervisor = 1;
|
||||||
|
return (PageTable*)(entry->Address << 12);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PageTable* pml4 = (PageTable*)pml4Phys;
|
||||||
|
auto pml3 = walkLevel(pml4, va.GetL4Index());
|
||||||
|
auto pml2 = walkLevel(pml3, va.GetL3Index());
|
||||||
|
auto pml1 = walkLevel(pml2, va.GetL2Index());
|
||||||
|
|
||||||
|
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
|
||||||
|
pageEntry->Present = true;
|
||||||
|
pageEntry->Writable = true;
|
||||||
|
pageEntry->Supervisor = 1;
|
||||||
|
pageEntry->WriteThrough = true; // PWT=1, PCD=0 → PAT entry 1 = WC
|
||||||
|
pageEntry->Address = physicalAddress >> 12;
|
||||||
|
}
|
||||||
|
|
||||||
std::uint64_t Paging::GetPhysAddr(std::uint64_t pml4, std::uint64_t virtualAddress, bool use40BitL1) {
|
std::uint64_t Paging::GetPhysAddr(std::uint64_t pml4, std::uint64_t virtualAddress, bool use40BitL1) {
|
||||||
VirtualAddress virtualAddressObj(virtualAddress);
|
VirtualAddress virtualAddressObj(virtualAddress);
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ public:
|
|||||||
void Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize, limine_memmap_response* memMap);
|
void Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize, limine_memmap_response* memMap);
|
||||||
void Map(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
void Map(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
void MapMMIO(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
void MapMMIO(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
|
void MapWC(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
void MapUser(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
void MapUser(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
static std::uint64_t GetPhysAddr(std::uint64_t PML4, std::uint64_t virtualAddress, bool use40BitL1 = false);
|
static std::uint64_t GetPhysAddr(std::uint64_t PML4, std::uint64_t virtualAddress, bool use40BitL1 = false);
|
||||||
std::uint64_t GetPhysAddr(std::uint64_t virtualAddress);
|
std::uint64_t GetPhysAddr(std::uint64_t virtualAddress);
|
||||||
@@ -104,6 +105,9 @@ public:
|
|||||||
|
|
||||||
// Map a page into an arbitrary PML4 (specified by physical address) with User bit set.
|
// Map a page into an arbitrary PML4 (specified by physical address) with User bit set.
|
||||||
static void MapUserIn(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
static void MapUserIn(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
|
|
||||||
|
// Map a page into an arbitrary PML4 with User + Write-Combining attributes.
|
||||||
|
static void MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Paging* g_paging;
|
extern Paging* g_paging;
|
||||||
|
|||||||
@@ -0,0 +1,375 @@
|
|||||||
|
/*
|
||||||
|
* Dns.cpp
|
||||||
|
* DNS resolver (kernel-level, RFC 1035)
|
||||||
|
* Copyright (c) 2025-2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Dns.hpp"
|
||||||
|
#include <Net/Udp.hpp>
|
||||||
|
#include <Net/ByteOrder.hpp>
|
||||||
|
#include <Net/NetConfig.hpp>
|
||||||
|
#include <Libraries/Memory.hpp>
|
||||||
|
#include <Libraries/String.hpp>
|
||||||
|
#include <Timekeeping/ApicTimer.hpp>
|
||||||
|
#include <Sched/Scheduler.hpp>
|
||||||
|
#include <Terminal/Terminal.hpp>
|
||||||
|
|
||||||
|
namespace Net::Dns {
|
||||||
|
|
||||||
|
// ---- DNS packet constants ----
|
||||||
|
|
||||||
|
static constexpr uint16_t DNS_PORT = 53;
|
||||||
|
static constexpr uint16_t DNS_FLAGS_RD = 0x0100; // Recursion Desired
|
||||||
|
static constexpr uint16_t DNS_QTYPE_A = 1;
|
||||||
|
static constexpr uint16_t DNS_QCLASS_IN = 1;
|
||||||
|
|
||||||
|
// ---- Simple cache ----
|
||||||
|
|
||||||
|
static constexpr int CACHE_SIZE = 8;
|
||||||
|
|
||||||
|
struct CacheEntry {
|
||||||
|
char hostname[128];
|
||||||
|
uint32_t ip;
|
||||||
|
uint32_t ttl; // TTL in seconds
|
||||||
|
uint64_t timestamp; // ms when cached
|
||||||
|
bool valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
static CacheEntry g_cache[CACHE_SIZE] = {};
|
||||||
|
|
||||||
|
static bool streq(const char* a, const char* b) {
|
||||||
|
while (*a && *b) {
|
||||||
|
if (*a != *b) return false;
|
||||||
|
a++; b++;
|
||||||
|
}
|
||||||
|
return *a == *b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t CacheLookup(const char* hostname) {
|
||||||
|
uint64_t now = Timekeeping::GetMilliseconds();
|
||||||
|
for (int i = 0; i < CACHE_SIZE; i++) {
|
||||||
|
if (!g_cache[i].valid) continue;
|
||||||
|
if (!streq(g_cache[i].hostname, hostname)) continue;
|
||||||
|
// Check TTL
|
||||||
|
uint64_t elapsed = (now - g_cache[i].timestamp) / 1000;
|
||||||
|
if (elapsed < g_cache[i].ttl) {
|
||||||
|
return g_cache[i].ip;
|
||||||
|
}
|
||||||
|
// Expired
|
||||||
|
g_cache[i].valid = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CacheStore(const char* hostname, uint32_t ip, uint32_t ttl) {
|
||||||
|
if (ttl == 0) ttl = 60; // Minimum 60s TTL
|
||||||
|
|
||||||
|
// Find free or oldest slot
|
||||||
|
int slot = 0;
|
||||||
|
uint64_t oldestTime = ~0ULL;
|
||||||
|
for (int i = 0; i < CACHE_SIZE; i++) {
|
||||||
|
if (!g_cache[i].valid) { slot = i; break; }
|
||||||
|
if (g_cache[i].timestamp < oldestTime) {
|
||||||
|
oldestTime = g_cache[i].timestamp;
|
||||||
|
slot = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheEntry& e = g_cache[slot];
|
||||||
|
// Copy hostname
|
||||||
|
int len = 0;
|
||||||
|
while (hostname[len] && len < 126) { e.hostname[len] = hostname[len]; len++; }
|
||||||
|
e.hostname[len] = '\0';
|
||||||
|
e.ip = ip;
|
||||||
|
e.ttl = ttl;
|
||||||
|
e.timestamp = Timekeeping::GetMilliseconds();
|
||||||
|
e.valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- DNS query building ----
|
||||||
|
|
||||||
|
// Encode a hostname as DNS labels: "example.com" -> "\x07example\x03com\x00"
|
||||||
|
// Returns number of bytes written, or 0 on error.
|
||||||
|
static int EncodeName(const char* hostname, uint8_t* out, int maxLen) {
|
||||||
|
int outPos = 0;
|
||||||
|
const char* p = hostname;
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
// Find the next dot or end
|
||||||
|
const char* dot = p;
|
||||||
|
while (*dot && *dot != '.') dot++;
|
||||||
|
int labelLen = (int)(dot - p);
|
||||||
|
|
||||||
|
if (labelLen == 0 || labelLen > 63) return 0;
|
||||||
|
if (outPos + 1 + labelLen >= maxLen) return 0;
|
||||||
|
|
||||||
|
out[outPos++] = (uint8_t)labelLen;
|
||||||
|
for (int i = 0; i < labelLen; i++) {
|
||||||
|
out[outPos++] = (uint8_t)p[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
p = dot;
|
||||||
|
if (*p == '.') p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outPos >= maxLen) return 0;
|
||||||
|
out[outPos++] = 0; // Root label terminator
|
||||||
|
return outPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a DNS query packet. Returns total packet length, or 0 on error.
|
||||||
|
static int BuildQuery(uint16_t id, const char* hostname, uint8_t* packet, int maxLen) {
|
||||||
|
if (maxLen < 12) return 0;
|
||||||
|
|
||||||
|
// Header (12 bytes)
|
||||||
|
packet[0] = (uint8_t)(id >> 8);
|
||||||
|
packet[1] = (uint8_t)(id & 0xFF);
|
||||||
|
packet[2] = (uint8_t)(DNS_FLAGS_RD >> 8); // Flags high: RD=1
|
||||||
|
packet[3] = (uint8_t)(DNS_FLAGS_RD & 0xFF); // Flags low
|
||||||
|
packet[4] = 0; packet[5] = 1; // QDCOUNT = 1
|
||||||
|
packet[6] = 0; packet[7] = 0; // ANCOUNT = 0
|
||||||
|
packet[8] = 0; packet[9] = 0; // NSCOUNT = 0
|
||||||
|
packet[10] = 0; packet[11] = 0; // ARCOUNT = 0
|
||||||
|
|
||||||
|
// Question section
|
||||||
|
int nameLen = EncodeName(hostname, packet + 12, maxLen - 12 - 4);
|
||||||
|
if (nameLen == 0) return 0;
|
||||||
|
|
||||||
|
int pos = 12 + nameLen;
|
||||||
|
if (pos + 4 > maxLen) return 0;
|
||||||
|
|
||||||
|
// QTYPE = A (1)
|
||||||
|
packet[pos++] = 0;
|
||||||
|
packet[pos++] = DNS_QTYPE_A;
|
||||||
|
// QCLASS = IN (1)
|
||||||
|
packet[pos++] = 0;
|
||||||
|
packet[pos++] = DNS_QCLASS_IN;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- DNS response parsing ----
|
||||||
|
|
||||||
|
// Skip over a DNS name in the packet (handles compression pointers).
|
||||||
|
// Returns the new offset, or -1 on error.
|
||||||
|
static int SkipName(const uint8_t* packet, int packetLen, int offset) {
|
||||||
|
int maxJumps = 32; // prevent infinite loops
|
||||||
|
bool jumped = false;
|
||||||
|
int returnOffset = -1;
|
||||||
|
|
||||||
|
while (offset < packetLen && maxJumps > 0) {
|
||||||
|
uint8_t len = packet[offset];
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
// End of name
|
||||||
|
offset++;
|
||||||
|
return jumped ? returnOffset : offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((len & 0xC0) == 0xC0) {
|
||||||
|
// Compression pointer
|
||||||
|
if (offset + 1 >= packetLen) return -1;
|
||||||
|
if (!jumped) returnOffset = offset + 2;
|
||||||
|
offset = ((len & 0x3F) << 8) | packet[offset + 1];
|
||||||
|
jumped = true;
|
||||||
|
maxJumps--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular label
|
||||||
|
offset += 1 + len;
|
||||||
|
maxJumps--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DnsAnswer {
|
||||||
|
uint32_t ip;
|
||||||
|
uint32_t ttl;
|
||||||
|
bool found;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse a DNS response and extract the first A record.
|
||||||
|
static DnsAnswer ParseResponse(uint16_t expectedId, const uint8_t* packet, int packetLen) {
|
||||||
|
DnsAnswer result = {0, 0, false};
|
||||||
|
|
||||||
|
if (packetLen < 12) return result;
|
||||||
|
|
||||||
|
// Check ID
|
||||||
|
uint16_t id = ((uint16_t)packet[0] << 8) | packet[1];
|
||||||
|
if (id != expectedId) return result;
|
||||||
|
|
||||||
|
// Check QR bit (must be response)
|
||||||
|
if (!(packet[2] & 0x80)) return result;
|
||||||
|
|
||||||
|
// Check RCODE (must be 0 = no error)
|
||||||
|
uint8_t rcode = packet[3] & 0x0F;
|
||||||
|
if (rcode != 0) return result;
|
||||||
|
|
||||||
|
uint16_t qdcount = ((uint16_t)packet[4] << 8) | packet[5];
|
||||||
|
uint16_t ancount = ((uint16_t)packet[6] << 8) | packet[7];
|
||||||
|
|
||||||
|
// Skip question section
|
||||||
|
int offset = 12;
|
||||||
|
for (uint16_t i = 0; i < qdcount; i++) {
|
||||||
|
offset = SkipName(packet, packetLen, offset);
|
||||||
|
if (offset < 0) return result;
|
||||||
|
offset += 4; // QTYPE + QCLASS
|
||||||
|
if (offset > packetLen) return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse answers
|
||||||
|
for (uint16_t i = 0; i < ancount; i++) {
|
||||||
|
offset = SkipName(packet, packetLen, offset);
|
||||||
|
if (offset < 0 || offset + 10 > packetLen) return result;
|
||||||
|
|
||||||
|
uint16_t atype = ((uint16_t)packet[offset] << 8) | packet[offset + 1];
|
||||||
|
// uint16_t aclass = ((uint16_t)packet[offset + 2] << 8) | packet[offset + 3];
|
||||||
|
uint32_t attl = ((uint32_t)packet[offset + 4] << 24) |
|
||||||
|
((uint32_t)packet[offset + 5] << 16) |
|
||||||
|
((uint32_t)packet[offset + 6] << 8) |
|
||||||
|
((uint32_t)packet[offset + 7]);
|
||||||
|
uint16_t rdlen = ((uint16_t)packet[offset + 8] << 8) | packet[offset + 9];
|
||||||
|
offset += 10;
|
||||||
|
|
||||||
|
if (offset + rdlen > packetLen) return result;
|
||||||
|
|
||||||
|
if (atype == DNS_QTYPE_A && rdlen == 4) {
|
||||||
|
// A record: 4-byte IPv4 address (already in network byte order)
|
||||||
|
result.ip = ((uint32_t)packet[offset])
|
||||||
|
| ((uint32_t)packet[offset + 1] << 8)
|
||||||
|
| ((uint32_t)packet[offset + 2] << 16)
|
||||||
|
| ((uint32_t)packet[offset + 3] << 24);
|
||||||
|
result.ttl = attl;
|
||||||
|
result.found = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += rdlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Resolve state (shared with UDP callback) ----
|
||||||
|
|
||||||
|
static volatile bool g_gotResponse = false;
|
||||||
|
static volatile uint16_t g_currentId = 0;
|
||||||
|
static uint8_t g_responseBuffer[512];
|
||||||
|
static volatile int g_responseLen = 0;
|
||||||
|
|
||||||
|
static void DnsRecvCallback(uint32_t srcIp, uint16_t srcPort,
|
||||||
|
uint16_t dstPort,
|
||||||
|
const uint8_t* data, uint16_t length) {
|
||||||
|
(void)srcIp;
|
||||||
|
(void)srcPort;
|
||||||
|
(void)dstPort;
|
||||||
|
|
||||||
|
if (g_gotResponse) return; // Already got a response
|
||||||
|
if (length > sizeof(g_responseBuffer)) length = sizeof(g_responseBuffer);
|
||||||
|
|
||||||
|
memcpy(g_responseBuffer, data, length);
|
||||||
|
g_responseLen = length;
|
||||||
|
g_gotResponse = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Simple PRNG for transaction IDs ----
|
||||||
|
|
||||||
|
static uint16_t g_nextId = 0x4E53; // "NS"
|
||||||
|
|
||||||
|
static uint16_t NextId() {
|
||||||
|
g_nextId = g_nextId * 25173 + 13849;
|
||||||
|
return g_nextId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Check if string is already an IP address ----
|
||||||
|
|
||||||
|
static bool IsIpAddress(const char* s) {
|
||||||
|
int dotCount = 0;
|
||||||
|
bool hasDigit = false;
|
||||||
|
for (int i = 0; s[i]; i++) {
|
||||||
|
if (s[i] >= '0' && s[i] <= '9') {
|
||||||
|
hasDigit = true;
|
||||||
|
} else if (s[i] == '.') {
|
||||||
|
if (!hasDigit) return false;
|
||||||
|
dotCount++;
|
||||||
|
hasDigit = false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasDigit && dotCount == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Public API ----
|
||||||
|
|
||||||
|
uint32_t Resolve(const char* hostname, uint32_t timeoutMs) {
|
||||||
|
if (hostname == nullptr || hostname[0] == '\0') return 0;
|
||||||
|
|
||||||
|
// Don't try to resolve IP addresses
|
||||||
|
if (IsIpAddress(hostname)) return 0;
|
||||||
|
|
||||||
|
// Check cache first
|
||||||
|
uint32_t cached = CacheLookup(hostname);
|
||||||
|
if (cached != 0) return cached;
|
||||||
|
|
||||||
|
// Check DNS server is configured
|
||||||
|
uint32_t dnsServer = Net::GetDnsServer();
|
||||||
|
if (dnsServer == 0) return 0;
|
||||||
|
|
||||||
|
// Pick a local port for receiving the response (ephemeral range)
|
||||||
|
uint16_t localPort = 10000 + (NextId() % 50000);
|
||||||
|
uint16_t txId = NextId();
|
||||||
|
|
||||||
|
// Build DNS query
|
||||||
|
uint8_t queryPacket[512];
|
||||||
|
int queryLen = BuildQuery(txId, hostname, queryPacket, sizeof(queryPacket));
|
||||||
|
if (queryLen == 0) return 0;
|
||||||
|
|
||||||
|
// Reset response state
|
||||||
|
g_gotResponse = false;
|
||||||
|
g_responseLen = 0;
|
||||||
|
g_currentId = txId;
|
||||||
|
|
||||||
|
// Bind our receive port
|
||||||
|
if (!Net::Udp::Bind(localPort, DnsRecvCallback)) {
|
||||||
|
// Port might be in use, try another
|
||||||
|
localPort = 10000 + (NextId() % 50000);
|
||||||
|
if (!Net::Udp::Bind(localPort, DnsRecvCallback)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the query to DNS server port 53
|
||||||
|
bool sent = Net::Udp::Send(dnsServer, localPort, DNS_PORT, queryPacket, (uint16_t)queryLen);
|
||||||
|
if (!sent) {
|
||||||
|
Net::Udp::Unbind(localPort);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for response with timeout
|
||||||
|
uint64_t start = Timekeeping::GetMilliseconds();
|
||||||
|
while (!g_gotResponse) {
|
||||||
|
if (Timekeeping::GetMilliseconds() - start >= timeoutMs) {
|
||||||
|
Net::Udp::Unbind(localPort);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Sched::Schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unbind the port
|
||||||
|
Net::Udp::Unbind(localPort);
|
||||||
|
|
||||||
|
// Parse the response
|
||||||
|
DnsAnswer answer = ParseResponse(txId, g_responseBuffer, g_responseLen);
|
||||||
|
if (!answer.found) return 0;
|
||||||
|
|
||||||
|
// Cache the result
|
||||||
|
CacheStore(hostname, answer.ip, answer.ttl);
|
||||||
|
|
||||||
|
return answer.ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Dns.hpp
|
||||||
|
* DNS resolver (kernel-level)
|
||||||
|
* Copyright (c) 2025-2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Net::Dns {
|
||||||
|
|
||||||
|
// Resolve a hostname to an IPv4 address.
|
||||||
|
// Returns the IP in network byte order, or 0 on failure.
|
||||||
|
uint32_t Resolve(const char* hostname, uint32_t timeoutMs = 5000);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ namespace Net {
|
|||||||
static uint32_t g_ipAddress = Ipv4Addr(10, 0, 68, 99);
|
static uint32_t g_ipAddress = Ipv4Addr(10, 0, 68, 99);
|
||||||
static uint32_t g_subnetMask = Ipv4Addr(255, 255, 255, 0);
|
static uint32_t g_subnetMask = Ipv4Addr(255, 255, 255, 0);
|
||||||
static uint32_t g_gateway = Ipv4Addr(10, 0, 68, 1);
|
static uint32_t g_gateway = Ipv4Addr(10, 0, 68, 1);
|
||||||
|
static uint32_t g_dnsServer = Ipv4Addr(10, 0, 68, 1);
|
||||||
|
|
||||||
uint32_t GetIpAddress() { return g_ipAddress; }
|
uint32_t GetIpAddress() { return g_ipAddress; }
|
||||||
void SetIpAddress(uint32_t ip) { g_ipAddress = ip; }
|
void SetIpAddress(uint32_t ip) { g_ipAddress = ip; }
|
||||||
@@ -22,6 +23,9 @@ namespace Net {
|
|||||||
uint32_t GetGateway() { return g_gateway; }
|
uint32_t GetGateway() { return g_gateway; }
|
||||||
void SetGateway(uint32_t gw) { g_gateway = gw; }
|
void SetGateway(uint32_t gw) { g_gateway = gw; }
|
||||||
|
|
||||||
|
uint32_t GetDnsServer() { return g_dnsServer; }
|
||||||
|
void SetDnsServer(uint32_t dns) { g_dnsServer = dns; }
|
||||||
|
|
||||||
bool IsLocalSubnet(uint32_t destIp) {
|
bool IsLocalSubnet(uint32_t destIp) {
|
||||||
return (destIp & g_subnetMask) == (g_ipAddress & g_subnetMask);
|
return (destIp & g_subnetMask) == (g_ipAddress & g_subnetMask);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ namespace Net {
|
|||||||
uint32_t GetGateway();
|
uint32_t GetGateway();
|
||||||
void SetGateway(uint32_t gw);
|
void SetGateway(uint32_t gw);
|
||||||
|
|
||||||
|
// Get/set the DNS server (network byte order)
|
||||||
|
uint32_t GetDnsServer();
|
||||||
|
void SetDnsServer(uint32_t dns);
|
||||||
|
|
||||||
// Check if a destination IP is on the local subnet
|
// Check if a destination IP is on the local subnet
|
||||||
bool IsLocalSubnet(uint32_t destIp);
|
bool IsLocalSubnet(uint32_t destIp);
|
||||||
|
|
||||||
|
|||||||
@@ -255,9 +255,6 @@ namespace Net::Tcp {
|
|||||||
|
|
||||||
// Send ACK
|
// Send ACK
|
||||||
SendSegment(conn, FLAG_ACK, nullptr, 0);
|
SendSegment(conn, FLAG_ACK, nullptr, 0);
|
||||||
|
|
||||||
KernelLogStream(INFO, "Net") << "TCP connection established to port "
|
|
||||||
<< base::dec << (uint64_t)conn->RemotePort;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5,16 +5,131 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Terminal.hpp"
|
#include "Terminal.hpp"
|
||||||
|
|
||||||
|
#define FLANTERM_IN_FLANTERM
|
||||||
#include "../Libraries/flanterm/src/flanterm_backends/fb.h"
|
#include "../Libraries/flanterm/src/flanterm_backends/fb.h"
|
||||||
#include "../Libraries/flanterm/src/flanterm.h"
|
#include "../Libraries/flanterm/src/flanterm.h"
|
||||||
|
|
||||||
#include "../Libraries/String.hpp"
|
#include "../Libraries/String.hpp"
|
||||||
|
#include "../Libraries/Memory.hpp"
|
||||||
#include <CppLib/CString.hpp>
|
#include <CppLib/CString.hpp>
|
||||||
|
|
||||||
namespace Kt {
|
namespace Kt {
|
||||||
flanterm_context *ctx;
|
flanterm_context *ctx;
|
||||||
std::size_t g_terminal_width = 0;
|
std::size_t g_terminal_width = 0;
|
||||||
|
|
||||||
|
// Maximum grid cells allocated at init (scale 1,1). Used to validate
|
||||||
|
// that a requested scale does not exceed the original buffer capacity.
|
||||||
|
static std::size_t g_max_grid_cells = 0;
|
||||||
|
|
||||||
|
// Custom plot_char that works for any font_scale_x/y >= 1.
|
||||||
|
// This is the same algorithm as flanterm's plot_char_scaled_uncanvas
|
||||||
|
// but lives outside fb.c so we can install it after rescaling.
|
||||||
|
static void plot_char_universal(struct flanterm_context *_ctx,
|
||||||
|
struct flanterm_fb_char *c,
|
||||||
|
size_t x, size_t y) {
|
||||||
|
struct flanterm_fb_context *fbctx = (struct flanterm_fb_context *)_ctx;
|
||||||
|
|
||||||
|
if (x >= _ctx->cols || y >= _ctx->rows) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t default_bg = fbctx->default_bg;
|
||||||
|
uint32_t bg = c->bg == 0xffffffff ? default_bg : c->bg;
|
||||||
|
uint32_t fg = c->fg == 0xffffffff ? fbctx->default_fg : c->fg;
|
||||||
|
|
||||||
|
x = fbctx->offset_x + x * fbctx->glyph_width;
|
||||||
|
y = fbctx->offset_y + y * fbctx->glyph_height;
|
||||||
|
|
||||||
|
bool *glyph = &fbctx->font_bool[c->c * fbctx->font_height * fbctx->font_width];
|
||||||
|
|
||||||
|
// Only ROTATE_0 is used in ZenithOS
|
||||||
|
volatile uint32_t *dest = fbctx->framebuffer + x + y * (fbctx->pitch / 4);
|
||||||
|
size_t stride = fbctx->pitch / 4;
|
||||||
|
|
||||||
|
for (size_t gy = 0; gy < fbctx->glyph_height; gy++) {
|
||||||
|
size_t fy = gy / fbctx->font_scale_y;
|
||||||
|
volatile uint32_t *fb_line = dest;
|
||||||
|
bool *glyph_pointer = glyph + (fy * fbctx->font_width);
|
||||||
|
for (size_t fx = 0; fx < fbctx->font_width; fx++) {
|
||||||
|
for (size_t i = 0; i < fbctx->font_scale_x; i++) {
|
||||||
|
*fb_line = *glyph_pointer ? fg : bg;
|
||||||
|
fb_line++;
|
||||||
|
}
|
||||||
|
glyph_pointer++;
|
||||||
|
}
|
||||||
|
dest += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rescale(std::size_t scale_x, std::size_t scale_y) {
|
||||||
|
if (scale_x == 0) scale_x = 1;
|
||||||
|
if (scale_y == 0) scale_y = 1;
|
||||||
|
|
||||||
|
struct flanterm_fb_context *fbctx = (struct flanterm_fb_context *)ctx;
|
||||||
|
|
||||||
|
// Calculate new dimensions
|
||||||
|
size_t new_glyph_w = fbctx->font_width * scale_x;
|
||||||
|
size_t new_glyph_h = fbctx->font_height * scale_y;
|
||||||
|
size_t new_cols = fbctx->width / new_glyph_w;
|
||||||
|
size_t new_rows = fbctx->height / new_glyph_h;
|
||||||
|
|
||||||
|
if (new_cols == 0 || new_rows == 0) return;
|
||||||
|
|
||||||
|
// Ensure the new grid fits within original buffer allocation
|
||||||
|
if (new_cols * new_rows > g_max_grid_cells) return;
|
||||||
|
|
||||||
|
// Update scale and glyph dimensions
|
||||||
|
fbctx->font_scale_x = scale_x;
|
||||||
|
fbctx->font_scale_y = scale_y;
|
||||||
|
fbctx->glyph_width = new_glyph_w;
|
||||||
|
fbctx->glyph_height = new_glyph_h;
|
||||||
|
|
||||||
|
// Update terminal grid dimensions
|
||||||
|
ctx->cols = new_cols;
|
||||||
|
ctx->rows = new_rows;
|
||||||
|
|
||||||
|
// Center the text area
|
||||||
|
fbctx->offset_x = (fbctx->width % new_glyph_w) / 2;
|
||||||
|
fbctx->offset_y = (fbctx->height % new_glyph_h) / 2;
|
||||||
|
|
||||||
|
// Install our universal plot_char
|
||||||
|
fbctx->plot_char = plot_char_universal;
|
||||||
|
|
||||||
|
// Reinitialize grid data (reuse existing buffers)
|
||||||
|
for (size_t i = 0; i < new_rows * new_cols; i++) {
|
||||||
|
fbctx->grid[i].c = ' ';
|
||||||
|
fbctx->grid[i].fg = fbctx->text_fg;
|
||||||
|
fbctx->grid[i].bg = fbctx->text_bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
fbctx->queue_i = 0;
|
||||||
|
memset(fbctx->queue, 0, new_rows * new_cols * sizeof(struct flanterm_fb_queue_item));
|
||||||
|
memset(fbctx->map, 0, new_rows * new_cols * sizeof(struct flanterm_fb_queue_item *));
|
||||||
|
|
||||||
|
// Clear the framebuffer
|
||||||
|
for (size_t y = 0; y < fbctx->height; y++) {
|
||||||
|
volatile uint32_t *row = fbctx->framebuffer + y * (fbctx->pitch / 4);
|
||||||
|
for (size_t x = 0; x < fbctx->width; x++) {
|
||||||
|
row[x] = fbctx->default_bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset terminal state and refresh
|
||||||
|
flanterm_context_reinit(ctx);
|
||||||
|
flanterm_full_refresh(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetFontScaleX() {
|
||||||
|
struct flanterm_fb_context *fbctx = (struct flanterm_fb_context *)ctx;
|
||||||
|
return fbctx->font_scale_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetFontScaleY() {
|
||||||
|
struct flanterm_fb_context *fbctx = (struct flanterm_fb_context *)ctx;
|
||||||
|
return fbctx->font_scale_y;
|
||||||
|
}
|
||||||
|
|
||||||
void UpdatePanelBar(CString panelText) {
|
void UpdatePanelBar(CString panelText) {
|
||||||
kout << "\033[s";
|
kout << "\033[s";
|
||||||
kout << "\033[H";
|
kout << "\033[H";
|
||||||
@@ -48,13 +163,20 @@ namespace Kt {
|
|||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, 0, 0, 1,
|
NULL, 0, 0, 1,
|
||||||
0, 0,
|
1, 1,
|
||||||
0,
|
0,
|
||||||
FLANTERM_FB_ROTATE_0
|
FLANTERM_FB_ROTATE_0
|
||||||
);
|
);
|
||||||
|
|
||||||
g_terminal_width = width;
|
g_terminal_width = width;
|
||||||
|
|
||||||
|
// Store max grid cells for rescale buffer bounds checking
|
||||||
|
g_max_grid_cells = ctx->cols * ctx->rows;
|
||||||
|
|
||||||
|
// Install our universal plot_char so rescaling works at any scale
|
||||||
|
struct flanterm_fb_context *fbctx = (struct flanterm_fb_context *)ctx;
|
||||||
|
fbctx->plot_char = plot_char_universal;
|
||||||
|
|
||||||
UpdatePanelBar("Initializing...");
|
UpdatePanelBar("Initializing...");
|
||||||
kout << "\n\n\n";
|
kout << "\n\n\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ namespace Kt
|
|||||||
|
|
||||||
void UpdatePanelBar(const char* panelText);
|
void UpdatePanelBar(const char* panelText);
|
||||||
|
|
||||||
|
void Rescale(std::size_t font_scale_x, std::size_t font_scale_y);
|
||||||
|
std::size_t GetFontScaleX();
|
||||||
|
std::size_t GetFontScaleY();
|
||||||
|
|
||||||
inline base base_custom(int custom)
|
inline base base_custom(int custom)
|
||||||
{
|
{
|
||||||
return (base)custom;
|
return (base)custom;
|
||||||
|
|||||||
+32
-5
@@ -56,9 +56,9 @@ BINDIR := bin
|
|||||||
# Discover all programs (each subdirectory under src/ is a program).
|
# Discover all programs (each subdirectory under src/ is a program).
|
||||||
PROGRAMS := $(notdir $(wildcard src/*))
|
PROGRAMS := $(notdir $(wildcard src/*))
|
||||||
|
|
||||||
# Games are built separately (doom has its own Makefile).
|
# Programs with custom Makefiles (built separately).
|
||||||
GAMES := doom
|
CUSTOM_BUILDS := doom fetch wiki
|
||||||
SYSTEM_PROGRAMS := $(filter-out $(GAMES),$(PROGRAMS))
|
SYSTEM_PROGRAMS := $(filter-out $(CUSTOM_BUILDS),$(PROGRAMS))
|
||||||
|
|
||||||
# Build targets: system programs go to bin/os/, games are handled separately.
|
# Build targets: system programs go to bin/os/, games are handled separately.
|
||||||
TARGETS := $(addprefix $(BINDIR)/os/,$(addsuffix .elf,$(SYSTEM_PROGRAMS)))
|
TARGETS := $(addprefix $(BINDIR)/os/,$(addsuffix .elf,$(SYSTEM_PROGRAMS)))
|
||||||
@@ -76,12 +76,31 @@ WWWDIR := www
|
|||||||
WWWSRC := $(wildcard $(WWWDIR)/*.*)
|
WWWSRC := $(wildcard $(WWWDIR)/*.*)
|
||||||
WWWDST := $(patsubst $(WWWDIR)/%,$(BINDIR)/www/%,$(WWWSRC))
|
WWWDST := $(patsubst $(WWWDIR)/%,$(BINDIR)/www/%,$(WWWSRC))
|
||||||
|
|
||||||
|
# CA certificate bundle.
|
||||||
|
CA_CERTS := $(BINDIR)/etc/ca-certificates.crt
|
||||||
|
|
||||||
# Home directory placeholder.
|
# Home directory placeholder.
|
||||||
HOMEKEEP := $(BINDIR)/home/.keep
|
HOMEKEEP := $(BINDIR)/home/.keep
|
||||||
|
|
||||||
.PHONY: all clean doom
|
.PHONY: all clean doom fetch wiki bearssl libc
|
||||||
|
|
||||||
all: $(TARGETS) doom $(GAME_DATA) $(MANDST) $(WWWDST) $(HOMEKEEP)
|
all: bearssl libc $(TARGETS) fetch wiki doom $(GAME_DATA) $(MANDST) $(WWWDST) $(CA_CERTS) $(HOMEKEEP)
|
||||||
|
|
||||||
|
# Build BearSSL static library.
|
||||||
|
bearssl:
|
||||||
|
$(MAKE) -C lib/bearssl
|
||||||
|
|
||||||
|
# Build shared libc static library.
|
||||||
|
libc:
|
||||||
|
$(MAKE) -C lib/libc
|
||||||
|
|
||||||
|
# Build fetch via its own Makefile (depends on bearssl and libc).
|
||||||
|
fetch: bearssl libc
|
||||||
|
$(MAKE) -C src/fetch
|
||||||
|
|
||||||
|
# Build wiki via its own Makefile (depends on bearssl and libc).
|
||||||
|
wiki: bearssl libc
|
||||||
|
$(MAKE) -C src/wiki
|
||||||
|
|
||||||
# Build doom via its own Makefile.
|
# Build doom via its own Makefile.
|
||||||
doom:
|
doom:
|
||||||
@@ -102,6 +121,11 @@ $(BINDIR)/www/%: $(WWWDIR)/%
|
|||||||
mkdir -p $(BINDIR)/www
|
mkdir -p $(BINDIR)/www
|
||||||
cp $< $@
|
cp $< $@
|
||||||
|
|
||||||
|
# Copy CA certificate bundle into bin/etc/.
|
||||||
|
$(CA_CERTS): data/ca-certificates.crt
|
||||||
|
mkdir -p $(BINDIR)/etc
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
# Create empty home directory with a keep file.
|
# Create empty home directory with a keep file.
|
||||||
$(HOMEKEEP):
|
$(HOMEKEEP):
|
||||||
mkdir -p $(BINDIR)/home
|
mkdir -p $(BINDIR)/home
|
||||||
@@ -115,4 +139,7 @@ $(BINDIR)/os/%.elf: src/%/main.cpp link.ld GNUmakefile
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BINDIR) obj
|
rm -rf $(BINDIR) obj
|
||||||
|
$(MAKE) -C lib/bearssl clean
|
||||||
|
$(MAKE) -C lib/libc clean
|
||||||
|
$(MAKE) -C src/fetch clean
|
||||||
$(MAKE) -C src/doom clean
|
$(MAKE) -C src/doom clean
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,9 @@ namespace Zenith {
|
|||||||
static constexpr uint64_t SYS_RECVFROM = 40;
|
static constexpr uint64_t SYS_RECVFROM = 40;
|
||||||
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;
|
||||||
|
static constexpr uint64_t SYS_TERMSCALE = 43;
|
||||||
|
static constexpr uint64_t SYS_RESOLVE = 44;
|
||||||
|
static constexpr uint64_t SYS_GETRANDOM = 45;
|
||||||
|
|
||||||
static constexpr int SOCK_TCP = 1;
|
static constexpr int SOCK_TCP = 1;
|
||||||
static constexpr int SOCK_UDP = 2;
|
static constexpr int SOCK_UDP = 2;
|
||||||
@@ -64,6 +67,7 @@ namespace Zenith {
|
|||||||
uint32_t gateway; // network byte order
|
uint32_t gateway; // network byte order
|
||||||
uint8_t macAddress[6];
|
uint8_t macAddress[6];
|
||||||
uint8_t _pad[2];
|
uint8_t _pad[2];
|
||||||
|
uint32_t dnsServer; // network byte order
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DateTime {
|
struct DateTime {
|
||||||
|
|||||||
@@ -165,6 +165,11 @@ namespace zenith {
|
|||||||
return (int32_t)syscall2(Zenith::SYS_PING, (uint64_t)ip, (uint64_t)timeoutMs);
|
return (int32_t)syscall2(Zenith::SYS_PING, (uint64_t)ip, (uint64_t)timeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DNS resolve: returns IP in network byte order, or 0 on failure
|
||||||
|
inline uint32_t resolve(const char* hostname) {
|
||||||
|
return (uint32_t)syscall1(Zenith::SYS_RESOLVE, (uint64_t)hostname);
|
||||||
|
}
|
||||||
|
|
||||||
// Network configuration
|
// Network configuration
|
||||||
inline void get_netcfg(Zenith::NetCfg* out) { syscall1(Zenith::SYS_GETNETCFG, (uint64_t)out); }
|
inline void get_netcfg(Zenith::NetCfg* out) { syscall1(Zenith::SYS_GETNETCFG, (uint64_t)out); }
|
||||||
inline int set_netcfg(const Zenith::NetCfg* cfg) { return (int)syscall1(Zenith::SYS_SETNETCFG, (uint64_t)cfg); }
|
inline int set_netcfg(const Zenith::NetCfg* cfg) { return (int)syscall1(Zenith::SYS_SETNETCFG, (uint64_t)cfg); }
|
||||||
@@ -222,9 +227,24 @@ namespace zenith {
|
|||||||
if (rows) *rows = (int)(r >> 32);
|
if (rows) *rows = (int)(r >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void termscale(int scale_x, int scale_y) {
|
||||||
|
syscall2(Zenith::SYS_TERMSCALE, (uint64_t)scale_x, (uint64_t)scale_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void get_termscale(int* scale_x, int* scale_y) {
|
||||||
|
uint64_t r = (uint64_t)syscall2(Zenith::SYS_TERMSCALE, 0, 0);
|
||||||
|
if (scale_x) *scale_x = (int)(r & 0xFFFFFFFF);
|
||||||
|
if (scale_y) *scale_y = (int)(r >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
// Timekeeping (wall-clock)
|
// Timekeeping (wall-clock)
|
||||||
inline void gettime(Zenith::DateTime* out) { syscall1(Zenith::SYS_GETTIME, (uint64_t)out); }
|
inline void gettime(Zenith::DateTime* out) { syscall1(Zenith::SYS_GETTIME, (uint64_t)out); }
|
||||||
|
|
||||||
|
// Random number generation
|
||||||
|
inline int64_t getrandom(void* buf, uint32_t len) {
|
||||||
|
return syscall2(Zenith::SYS_GETRANDOM, (uint64_t)buf, (uint64_t)len);
|
||||||
|
}
|
||||||
|
|
||||||
// Power management
|
// Power management
|
||||||
[[noreturn]] inline void reset() {
|
[[noreturn]] inline void reset() {
|
||||||
syscall0(Zenith::SYS_RESET);
|
syscall0(Zenith::SYS_RESET);
|
||||||
|
|||||||
Submodule
+1
Submodule programs/lib/bearssl added at 3d9be2f60b
@@ -0,0 +1,64 @@
|
|||||||
|
# Makefile for shared libc static library on ZenithOS
|
||||||
|
# Copyright (c) 2025-2026 Daniel Hammer
|
||||||
|
|
||||||
|
MAKEFLAGS += -rR
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
|
# ---- Toolchain ----
|
||||||
|
|
||||||
|
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
|
||||||
|
ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
|
||||||
|
CC := $(TOOLCHAIN_PREFIX)gcc
|
||||||
|
AR := $(TOOLCHAIN_PREFIX)ar
|
||||||
|
else
|
||||||
|
CC := gcc
|
||||||
|
AR := ar
|
||||||
|
endif
|
||||||
|
|
||||||
|
# ---- Paths ----
|
||||||
|
|
||||||
|
LIBC_INC := ../../include/libc
|
||||||
|
OBJDIR := obj
|
||||||
|
|
||||||
|
# ---- Compiler flags ----
|
||||||
|
|
||||||
|
CFLAGS := \
|
||||||
|
-std=gnu11 \
|
||||||
|
-g -O2 -pipe \
|
||||||
|
-Wall \
|
||||||
|
-Wno-unused-parameter \
|
||||||
|
-nostdinc \
|
||||||
|
-ffreestanding \
|
||||||
|
-fno-stack-protector \
|
||||||
|
-fno-stack-check \
|
||||||
|
-fno-PIC \
|
||||||
|
-ffunction-sections \
|
||||||
|
-fdata-sections \
|
||||||
|
-m64 \
|
||||||
|
-march=x86-64 \
|
||||||
|
-mno-80387 \
|
||||||
|
-mno-mmx \
|
||||||
|
-mno-sse \
|
||||||
|
-mno-sse2 \
|
||||||
|
-mno-red-zone \
|
||||||
|
-mcmodel=small \
|
||||||
|
-isystem $(LIBC_INC) \
|
||||||
|
-isystem $(shell $(CC) -print-file-name=include)
|
||||||
|
|
||||||
|
# ---- Target ----
|
||||||
|
|
||||||
|
TARGET := liblibc.a
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJDIR)/libc.o
|
||||||
|
$(AR) rcs $@ $^
|
||||||
|
|
||||||
|
$(OBJDIR)/libc.o: libc.c Makefile
|
||||||
|
@mkdir -p $(OBJDIR)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJDIR) $(TARGET)
|
||||||
@@ -0,0 +1,662 @@
|
|||||||
|
/*
|
||||||
|
* libc.c
|
||||||
|
* Minimal C standard library for ZenithOS userspace programs
|
||||||
|
* Based on the proven libc from the DOOM port.
|
||||||
|
* Copyright (c) 2025-2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
Raw syscall wrappers (C versions matching kernel ABI)
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
static inline long _zos_syscall0(long nr) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile("syscall" : "=a"(ret) : "a"(nr)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long _zos_syscall1(long nr, long a1) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile(
|
||||||
|
"mov %[a1], %%rdi\n\t"
|
||||||
|
"syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(nr), [a1] "r"(a1)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long _zos_syscall4(long nr, long a1, long a2, long a3, long a4) {
|
||||||
|
long ret;
|
||||||
|
__asm__ volatile(
|
||||||
|
"mov %[a1], %%rdi\n\t"
|
||||||
|
"mov %[a2], %%rsi\n\t"
|
||||||
|
"mov %[a3], %%rdx\n\t"
|
||||||
|
"mov %[a4], %%r10\n\t"
|
||||||
|
"syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2), [a3] "r"(a3), [a4] "r"(a4)
|
||||||
|
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Syscall numbers */
|
||||||
|
#define SYS_EXIT 0
|
||||||
|
#define SYS_PRINT 4
|
||||||
|
#define SYS_PUTCHAR 5
|
||||||
|
#define SYS_ALLOC 11
|
||||||
|
#define SYS_FREE 12
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
errno
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
int errno = 0;
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
string.h functions
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
void *memcpy(void *dest, const void *src, size_t n) {
|
||||||
|
unsigned char *d = (unsigned char *)dest;
|
||||||
|
const unsigned char *s = (const unsigned char *)src;
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
d[i] = s[i];
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memset(void *s, int c, size_t n) {
|
||||||
|
unsigned char *p = (unsigned char *)s;
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
p[i] = (unsigned char)c;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memmove(void *dest, const void *src, size_t n) {
|
||||||
|
unsigned char *d = (unsigned char *)dest;
|
||||||
|
const unsigned char *s = (const unsigned char *)src;
|
||||||
|
if (s < d && d < s + n) {
|
||||||
|
for (size_t i = n; i > 0; i--)
|
||||||
|
d[i - 1] = s[i - 1];
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
d[i] = s[i];
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
int memcmp(const void *s1, const void *s2, size_t n) {
|
||||||
|
const unsigned char *a = (const unsigned char *)s1;
|
||||||
|
const unsigned char *b = (const unsigned char *)s2;
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
if (a[i] != b[i])
|
||||||
|
return a[i] < b[i] ? -1 : 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlen(const char *s) {
|
||||||
|
size_t len = 0;
|
||||||
|
while (s[len]) len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strcmp(const char *a, const char *b) {
|
||||||
|
while (*a && *a == *b) { a++; b++; }
|
||||||
|
return (unsigned char)*a - (unsigned char)*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strncmp(const char *a, const char *b, size_t n) {
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
if (a[i] != b[i] || a[i] == 0)
|
||||||
|
return (unsigned char)a[i] - (unsigned char)b[i];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strcpy(char *dest, const char *src) {
|
||||||
|
char *d = dest;
|
||||||
|
while ((*d++ = *src++));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strncpy(char *dest, const char *src, size_t n) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < n && src[i]; i++)
|
||||||
|
dest[i] = src[i];
|
||||||
|
for (; i < n; i++)
|
||||||
|
dest[i] = '\0';
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strcat(char *dest, const char *src) {
|
||||||
|
char *d = dest + strlen(dest);
|
||||||
|
while ((*d++ = *src++));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strncat(char *dest, const char *src, size_t n) {
|
||||||
|
char *d = dest + strlen(dest);
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < n && src[i]; i++)
|
||||||
|
d[i] = src[i];
|
||||||
|
d[i] = '\0';
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strchr(const char *s, int c) {
|
||||||
|
while (*s) {
|
||||||
|
if (*s == (char)c) return (char *)s;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return (c == 0) ? (char *)s : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strrchr(const char *s, int c) {
|
||||||
|
const char *last = NULL;
|
||||||
|
while (*s) {
|
||||||
|
if (*s == (char)c) last = s;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
if (c == 0) return (char *)s;
|
||||||
|
return (char *)last;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strcasecmp(const char *a, const char *b) {
|
||||||
|
while (*a && ((*a >= 'A' && *a <= 'Z') ? *a + 32 : *a) ==
|
||||||
|
((*b >= 'A' && *b <= 'Z') ? *b + 32 : *b)) { a++; b++; }
|
||||||
|
int ca = (*a >= 'A' && *a <= 'Z') ? *a + 32 : *a;
|
||||||
|
int cb = (*b >= 'A' && *b <= 'Z') ? *b + 32 : *b;
|
||||||
|
return ca - cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strncasecmp(const char *a, const char *b, size_t n) {
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
int ca = (a[i] >= 'A' && a[i] <= 'Z') ? a[i] + 32 : a[i];
|
||||||
|
int cb = (b[i] >= 'A' && b[i] <= 'Z') ? b[i] + 32 : b[i];
|
||||||
|
if (ca != cb || ca == 0) return ca - cb;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strstr(const char *haystack, const char *needle) {
|
||||||
|
if (!*needle) return (char *)haystack;
|
||||||
|
size_t nlen = strlen(needle);
|
||||||
|
while (*haystack) {
|
||||||
|
if (strncmp(haystack, needle, nlen) == 0)
|
||||||
|
return (char *)haystack;
|
||||||
|
haystack++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forward declaration for strdup */
|
||||||
|
void *malloc(size_t size);
|
||||||
|
|
||||||
|
char *strdup(const char *s) {
|
||||||
|
size_t len = strlen(s) + 1;
|
||||||
|
char *d = (char *)malloc(len);
|
||||||
|
if (d) memcpy(d, s, len);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
ctype.h functions
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
int isalpha(int c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); }
|
||||||
|
int isdigit(int c) { return c >= '0' && c <= '9'; }
|
||||||
|
int isalnum(int c) { return isalpha(c) || isdigit(c); }
|
||||||
|
int isspace(int c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; }
|
||||||
|
int isupper(int c) { return c >= 'A' && c <= 'Z'; }
|
||||||
|
int islower(int c) { return c >= 'a' && c <= 'z'; }
|
||||||
|
int isprint(int c) { return c >= 0x20 && c <= 0x7E; }
|
||||||
|
int ispunct(int c) { return isprint(c) && !isalnum(c) && c != ' '; }
|
||||||
|
int isxdigit(int c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); }
|
||||||
|
int iscntrl(int c) { return (c >= 0 && c < 0x20) || c == 0x7F; }
|
||||||
|
int isgraph(int c) { return c > 0x20 && c <= 0x7E; }
|
||||||
|
int toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 32 : c; }
|
||||||
|
int tolower(int c) { return (c >= 'A' && c <= 'Z') ? c + 32 : c; }
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
Heap allocator (free-list, backed by SYS_ALLOC)
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
#define HEAP_MAGIC 0x5A484541ULL /* "ZHEA" */
|
||||||
|
|
||||||
|
struct HeapHeader {
|
||||||
|
uint64_t magic;
|
||||||
|
uint64_t size;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct FreeNode {
|
||||||
|
uint64_t size;
|
||||||
|
struct FreeNode *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct FreeNode g_heapHead = { 0, NULL };
|
||||||
|
static int g_heapInit = 0;
|
||||||
|
|
||||||
|
static void heap_insert_free(void *ptr, uint64_t size) {
|
||||||
|
struct FreeNode *node = (struct FreeNode *)ptr;
|
||||||
|
node->size = size;
|
||||||
|
node->next = g_heapHead.next;
|
||||||
|
g_heapHead.next = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void heap_grow(uint64_t bytes) {
|
||||||
|
uint64_t pages = (bytes + 0xFFF) / 0x1000;
|
||||||
|
if (pages < 4) pages = 4;
|
||||||
|
void *mem = (void *)_zos_syscall1(SYS_ALLOC, (long)(pages * 0x1000));
|
||||||
|
if (mem != NULL)
|
||||||
|
heap_insert_free(mem, pages * 0x1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *malloc(size_t size) {
|
||||||
|
if (!g_heapInit) {
|
||||||
|
heap_grow(16 * 0x1000);
|
||||||
|
g_heapInit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t needed = size + sizeof(struct HeapHeader);
|
||||||
|
needed = (needed + 15) & ~15ULL;
|
||||||
|
|
||||||
|
struct FreeNode *prev = &g_heapHead;
|
||||||
|
struct FreeNode *cur = g_heapHead.next;
|
||||||
|
|
||||||
|
while (cur != NULL) {
|
||||||
|
if (cur->size >= needed) {
|
||||||
|
uint64_t blockSize = cur->size;
|
||||||
|
prev->next = cur->next;
|
||||||
|
|
||||||
|
if (blockSize > needed + sizeof(struct FreeNode) + 16) {
|
||||||
|
void *rest = (void *)((uint8_t *)cur + needed);
|
||||||
|
heap_insert_free(rest, blockSize - needed);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HeapHeader *hdr = (struct HeapHeader *)cur;
|
||||||
|
hdr->magic = HEAP_MAGIC;
|
||||||
|
hdr->size = size;
|
||||||
|
return (void *)((uint8_t *)hdr + sizeof(struct HeapHeader));
|
||||||
|
}
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_grow(needed);
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void *ptr) {
|
||||||
|
if (ptr == NULL) return;
|
||||||
|
|
||||||
|
struct HeapHeader *hdr = (struct HeapHeader *)((uint8_t *)ptr - sizeof(struct HeapHeader));
|
||||||
|
uint64_t blockSize = hdr->size + sizeof(struct HeapHeader);
|
||||||
|
blockSize = (blockSize + 15) & ~15ULL;
|
||||||
|
heap_insert_free((void *)hdr, blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *calloc(size_t nmemb, size_t size) {
|
||||||
|
size_t total = nmemb * size;
|
||||||
|
void *p = malloc(total);
|
||||||
|
if (p) memset(p, 0, total);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *realloc(void *ptr, size_t size) {
|
||||||
|
if (ptr == NULL) return malloc(size);
|
||||||
|
if (size == 0) { free(ptr); return NULL; }
|
||||||
|
|
||||||
|
struct HeapHeader *hdr = (struct HeapHeader *)((uint8_t *)ptr - sizeof(struct HeapHeader));
|
||||||
|
uint64_t old = hdr->size;
|
||||||
|
|
||||||
|
void *newp = malloc(size);
|
||||||
|
if (newp == NULL) return NULL;
|
||||||
|
|
||||||
|
size_t copySize = old < size ? old : size;
|
||||||
|
memcpy(newp, ptr, copySize);
|
||||||
|
free(ptr);
|
||||||
|
return newp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
stdlib.h functions
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
int abs(int x) { return x < 0 ? -x : x; }
|
||||||
|
long labs(long x) { return x < 0 ? -x : x; }
|
||||||
|
|
||||||
|
int atoi(const char *s) {
|
||||||
|
int neg = 0, val = 0;
|
||||||
|
while (isspace((unsigned char)*s)) s++;
|
||||||
|
if (*s == '-') { neg = 1; s++; }
|
||||||
|
else if (*s == '+') { s++; }
|
||||||
|
while (isdigit((unsigned char)*s)) {
|
||||||
|
val = val * 10 + (*s - '0');
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return neg ? -val : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
long strtol(const char *nptr, char **endptr, int base) {
|
||||||
|
const char *s = nptr;
|
||||||
|
long val = 0;
|
||||||
|
int neg = 0;
|
||||||
|
|
||||||
|
while (isspace((unsigned char)*s)) s++;
|
||||||
|
if (*s == '-') { neg = 1; s++; }
|
||||||
|
else if (*s == '+') { s++; }
|
||||||
|
|
||||||
|
if (base == 0) {
|
||||||
|
if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) { base = 16; s += 2; }
|
||||||
|
else if (*s == '0') { base = 8; s++; }
|
||||||
|
else base = 10;
|
||||||
|
} else if (base == 16 && *s == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
||||||
|
s += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*s) {
|
||||||
|
int digit;
|
||||||
|
if (*s >= '0' && *s <= '9') digit = *s - '0';
|
||||||
|
else if (*s >= 'a' && *s <= 'z') digit = *s - 'a' + 10;
|
||||||
|
else if (*s >= 'A' && *s <= 'Z') digit = *s - 'A' + 10;
|
||||||
|
else break;
|
||||||
|
if (digit >= base) break;
|
||||||
|
val = val * base + digit;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endptr) *endptr = (char *)s;
|
||||||
|
return neg ? -val : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long strtoul(const char *nptr, char **endptr, int base) {
|
||||||
|
return (unsigned long)strtol(nptr, endptr, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getenv(const char *name) {
|
||||||
|
(void)name;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit(int status) {
|
||||||
|
_zos_syscall1(SYS_EXIT, (long)status);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void abort(void) {
|
||||||
|
_zos_syscall1(SYS_PRINT, (long)"abort() called\n");
|
||||||
|
_zos_syscall1(SYS_EXIT, 1);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
int system(const char *command) {
|
||||||
|
(void)command;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
printf family — vsnprintf core
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
struct _pf_state {
|
||||||
|
char *buf;
|
||||||
|
size_t pos;
|
||||||
|
size_t max;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void _pf_putc(struct _pf_state *st, char c) {
|
||||||
|
if (st->pos < st->max)
|
||||||
|
st->buf[st->pos] = c;
|
||||||
|
st->pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pf_puts(struct _pf_state *st, const char *s) {
|
||||||
|
while (*s) _pf_putc(st, *s++);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pf_putnum(struct _pf_state *st, unsigned long val, int base, int upper, int width, char pad, int neg, int precision) {
|
||||||
|
char tmp[24];
|
||||||
|
int i = 0;
|
||||||
|
const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||||
|
|
||||||
|
if (val == 0) {
|
||||||
|
tmp[i++] = '0';
|
||||||
|
} else {
|
||||||
|
while (val > 0) {
|
||||||
|
tmp[i++] = digits[val % base];
|
||||||
|
val /= base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int digitCount = i;
|
||||||
|
int precPad = 0;
|
||||||
|
if (precision > digitCount)
|
||||||
|
precPad = precision - digitCount;
|
||||||
|
|
||||||
|
int total = (neg ? 1 : 0) + precPad + digitCount;
|
||||||
|
|
||||||
|
if (neg && pad == '0' && precision < 0) {
|
||||||
|
_pf_putc(st, '-');
|
||||||
|
}
|
||||||
|
if (precision < 0 && pad == '0') {
|
||||||
|
for (int w = total; w < width; w++)
|
||||||
|
_pf_putc(st, '0');
|
||||||
|
} else {
|
||||||
|
for (int w = total; w < width; w++)
|
||||||
|
_pf_putc(st, ' ');
|
||||||
|
}
|
||||||
|
if (neg && !(pad == '0' && precision < 0)) {
|
||||||
|
_pf_putc(st, '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int p = 0; p < precPad; p++)
|
||||||
|
_pf_putc(st, '0');
|
||||||
|
|
||||||
|
while (i > 0)
|
||||||
|
_pf_putc(st, tmp[--i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) {
|
||||||
|
struct _pf_state st;
|
||||||
|
st.buf = buf;
|
||||||
|
st.pos = 0;
|
||||||
|
st.max = size > 0 ? size - 1 : 0;
|
||||||
|
|
||||||
|
while (*fmt) {
|
||||||
|
if (*fmt != '%') {
|
||||||
|
_pf_putc(&st, *fmt++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fmt++;
|
||||||
|
|
||||||
|
char pad = ' ';
|
||||||
|
int left_align = 0;
|
||||||
|
while (*fmt == '0' || *fmt == '-' || *fmt == '+' || *fmt == ' ') {
|
||||||
|
if (*fmt == '0') pad = '0';
|
||||||
|
if (*fmt == '-') { left_align = 1; pad = ' '; }
|
||||||
|
fmt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
if (*fmt == '*') {
|
||||||
|
width = va_arg(ap, int);
|
||||||
|
fmt++;
|
||||||
|
} else {
|
||||||
|
while (*fmt >= '0' && *fmt <= '9') {
|
||||||
|
width = width * 10 + (*fmt - '0');
|
||||||
|
fmt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int precision = -1;
|
||||||
|
if (*fmt == '.') {
|
||||||
|
fmt++;
|
||||||
|
precision = 0;
|
||||||
|
if (*fmt == '*') {
|
||||||
|
precision = va_arg(ap, int);
|
||||||
|
fmt++;
|
||||||
|
} else {
|
||||||
|
while (*fmt >= '0' && *fmt <= '9') {
|
||||||
|
precision = precision * 10 + (*fmt - '0');
|
||||||
|
fmt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_long = 0;
|
||||||
|
if (*fmt == 'l') { is_long = 1; fmt++; if (*fmt == 'l') { is_long = 2; fmt++; } }
|
||||||
|
else if (*fmt == 'h') { fmt++; if (*fmt == 'h') fmt++; }
|
||||||
|
else if (*fmt == 'z') { is_long = 1; fmt++; }
|
||||||
|
|
||||||
|
switch (*fmt) {
|
||||||
|
case 'd': case 'i': {
|
||||||
|
long val;
|
||||||
|
if (is_long >= 1) val = va_arg(ap, long);
|
||||||
|
else val = va_arg(ap, int);
|
||||||
|
int neg = 0;
|
||||||
|
unsigned long uval;
|
||||||
|
if (val < 0) { neg = 1; uval = (unsigned long)(-val); }
|
||||||
|
else uval = (unsigned long)val;
|
||||||
|
if (left_align) {
|
||||||
|
size_t before = st.pos;
|
||||||
|
_pf_putnum(&st, uval, 10, 0, 0, pad, neg, precision);
|
||||||
|
size_t len = st.pos - before;
|
||||||
|
for (size_t w = len; (int)w < width; w++) _pf_putc(&st, ' ');
|
||||||
|
} else {
|
||||||
|
_pf_putnum(&st, uval, 10, 0, width, pad, neg, precision);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u': {
|
||||||
|
unsigned long val;
|
||||||
|
if (is_long >= 1) val = va_arg(ap, unsigned long);
|
||||||
|
else val = va_arg(ap, unsigned int);
|
||||||
|
if (left_align) {
|
||||||
|
size_t before = st.pos;
|
||||||
|
_pf_putnum(&st, val, 10, 0, 0, ' ', 0, precision);
|
||||||
|
size_t len = st.pos - before;
|
||||||
|
for (size_t w = len; (int)w < width; w++) _pf_putc(&st, ' ');
|
||||||
|
} else {
|
||||||
|
_pf_putnum(&st, val, 10, 0, width, pad, 0, precision);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x': case 'X': {
|
||||||
|
unsigned long val;
|
||||||
|
if (is_long >= 1) val = va_arg(ap, unsigned long);
|
||||||
|
else val = va_arg(ap, unsigned int);
|
||||||
|
int upper = (*fmt == 'X');
|
||||||
|
if (left_align) {
|
||||||
|
size_t before = st.pos;
|
||||||
|
_pf_putnum(&st, val, 16, upper, 0, pad, 0, precision);
|
||||||
|
size_t len = st.pos - before;
|
||||||
|
for (size_t w = len; (int)w < width; w++) _pf_putc(&st, ' ');
|
||||||
|
} else {
|
||||||
|
_pf_putnum(&st, val, 16, upper, width, pad, 0, precision);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p': {
|
||||||
|
void *val = va_arg(ap, void *);
|
||||||
|
_pf_puts(&st, "0x");
|
||||||
|
_pf_putnum(&st, (unsigned long)val, 16, 0, 0, '0', 0, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 's': {
|
||||||
|
const char *s = va_arg(ap, const char *);
|
||||||
|
if (s == NULL) s = "(null)";
|
||||||
|
int slen = (int)strlen(s);
|
||||||
|
if (precision >= 0 && precision < slen) slen = precision;
|
||||||
|
if (!left_align) {
|
||||||
|
for (int w = slen; w < width; w++) _pf_putc(&st, ' ');
|
||||||
|
}
|
||||||
|
for (int i = 0; i < slen; i++) _pf_putc(&st, s[i]);
|
||||||
|
if (left_align) {
|
||||||
|
for (int w = slen; w < width; w++) _pf_putc(&st, ' ');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c': {
|
||||||
|
char c = (char)va_arg(ap, int);
|
||||||
|
_pf_putc(&st, c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '%':
|
||||||
|
_pf_putc(&st, '%');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_pf_putc(&st, '%');
|
||||||
|
_pf_putc(&st, *fmt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*fmt) fmt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
|
if (st.pos < size)
|
||||||
|
st.buf[st.pos] = '\0';
|
||||||
|
else
|
||||||
|
st.buf[size - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)st.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int snprintf(char *buf, size_t size, const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
int ret = vsnprintf(buf, size, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sprintf(char *buf, const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
int ret = vsnprintf(buf, (size_t)-1, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char _printbuf[4096];
|
||||||
|
|
||||||
|
int printf(const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
int ret = vsnprintf(_printbuf, sizeof(_printbuf), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
_zos_syscall1(SYS_PRINT, (long)_printbuf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int puts(const char *s) {
|
||||||
|
_zos_syscall1(SYS_PRINT, (long)s);
|
||||||
|
_zos_syscall1(SYS_PUTCHAR, (long)'\n');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int putchar(int c) {
|
||||||
|
_zos_syscall1(SYS_PUTCHAR, (long)c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
assert.h support
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
void __assert_fail(const char *expr, const char *file, int line, const char *func) {
|
||||||
|
_zos_syscall1(SYS_PRINT, (long)"Assertion failed: ");
|
||||||
|
_zos_syscall1(SYS_PRINT, (long)expr);
|
||||||
|
_zos_syscall1(SYS_PRINT, (long)" at ");
|
||||||
|
_zos_syscall1(SYS_PRINT, (long)file);
|
||||||
|
_zos_syscall1(SYS_PRINT, (long)"\n");
|
||||||
|
(void)line; (void)func;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,62 @@
|
|||||||
|
.TH FETCH 1
|
||||||
|
.SH NAME
|
||||||
|
fetch - HTTP/HTTPS client for ZenithOS
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
fetch [-v] <url>
|
||||||
|
fetch [-v] <host> <port> [path]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
fetch performs an HTTP/1.0 GET request and prints the response
|
||||||
|
body to the terminal. Supports both plain HTTP and HTTPS (TLS 1.2)
|
||||||
|
connections. By default only the body is printed.
|
||||||
|
|
||||||
|
In URL mode, the scheme (http:// or https://) determines whether
|
||||||
|
TLS is used. The port defaults to 80 for HTTP and 443 for HTTPS.
|
||||||
|
|
||||||
|
In legacy mode, the host and port are specified as separate
|
||||||
|
arguments and the connection is always plain HTTP.
|
||||||
|
|
||||||
|
The host may be an IP address or a hostname. Hostnames are
|
||||||
|
resolved via the configured DNS server.
|
||||||
|
|
||||||
|
If no path is given, "/" is used.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.B -v
|
||||||
|
Verbose mode. Print connection info, trust anchor count, TLS
|
||||||
|
handshake progress, and the HTTP status/size header before
|
||||||
|
the body.
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
fetch https://icanhazip.com
|
||||||
|
Print your public IP address over HTTPS.
|
||||||
|
|
||||||
|
fetch http://icanhazip.com
|
||||||
|
Same, but over plain HTTP.
|
||||||
|
|
||||||
|
fetch -v https://example.com
|
||||||
|
Fetch a page with verbose output showing:
|
||||||
|
Connecting to example.com:443 (HTTPS)...
|
||||||
|
Loaded 128 trust anchors
|
||||||
|
TLS handshake...
|
||||||
|
TLS connection established
|
||||||
|
GET /
|
||||||
|
HTTP 200 OK (1256 bytes)
|
||||||
|
|
||||||
|
fetch 10.0.68.1 80 /
|
||||||
|
Fetch from a local server by IP (legacy syntax).
|
||||||
|
|
||||||
|
.SH TLS SUPPORT
|
||||||
|
HTTPS connections use BearSSL for TLS 1.2. Server certificates
|
||||||
|
are validated against the system CA bundle at
|
||||||
|
0:/etc/ca-certificates.crt.
|
||||||
|
|
||||||
|
Entropy for the TLS handshake is provided by RDTSC-seeded
|
||||||
|
random data via the SYS_GETRANDOM syscall.
|
||||||
|
|
||||||
|
.SH KEYBOARD
|
||||||
|
Ctrl+Q Abort the request
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
ping(1), nslookup(1), tcpconnect(1), shell(1), syscalls(2)
|
||||||
+10
-1
@@ -67,8 +67,17 @@
|
|||||||
}
|
}
|
||||||
zenith::close(h);
|
zenith::close(h);
|
||||||
|
|
||||||
|
.SH WRITING FILES
|
||||||
|
Files can be created and written on the ramdisk:
|
||||||
|
|
||||||
|
.BI int zenith::fcreate(const char* path);
|
||||||
|
.BI int zenith::fwrite(int handle, const uint8_t* buf, uint64_t offset, uint64_t size);
|
||||||
|
|
||||||
|
fcreate creates a new file and returns a handle. fwrite writes
|
||||||
|
bytes at the given offset. Changes persist only until reboot --
|
||||||
|
the ramdisk is reloaded from the USTAR archive on each boot.
|
||||||
|
|
||||||
.SH NOTES
|
.SH NOTES
|
||||||
The filesystem is read-only. There are no write or create calls.
|
|
||||||
All files live on the ramdisk which is loaded at boot from a
|
All files live on the ramdisk which is loaded at boot from a
|
||||||
USTAR tar archive.
|
USTAR tar archive.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
.TH FONTSCALE 1
|
||||||
|
.SH NAME
|
||||||
|
fontscale - get or set terminal font scale
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
fontscale
|
||||||
|
fontscale <n>
|
||||||
|
fontscale <x> <y>
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Controls the terminal font scale factor. The Flanterm terminal
|
||||||
|
emulator renders text at a configurable scale multiplier.
|
||||||
|
Increasing the scale makes text larger, which is useful on
|
||||||
|
high-resolution displays or real hardware where text may be
|
||||||
|
too small to read comfortably.
|
||||||
|
|
||||||
|
With no arguments, prints the current scale factor and terminal
|
||||||
|
dimensions.
|
||||||
|
|
||||||
|
With one argument, sets both the horizontal and vertical scale
|
||||||
|
to the same value.
|
||||||
|
|
||||||
|
With two arguments, sets asymmetric horizontal and vertical
|
||||||
|
scale factors independently.
|
||||||
|
|
||||||
|
Valid scale values are 1 through 8. After rescaling, the screen
|
||||||
|
is cleared.
|
||||||
|
|
||||||
|
.SH OUTPUT
|
||||||
|
fontscale
|
||||||
|
Scale: 1x1 (160 cols x 50 rows)
|
||||||
|
|
||||||
|
fontscale 2
|
||||||
|
Scale set to 2x2 (80 cols x 25 rows)
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
fontscale Show current scale and dimensions
|
||||||
|
fontscale 2 Double the font size
|
||||||
|
fontscale 3 2 3x horizontal, 2x vertical
|
||||||
|
fontscale 1 Reset to default size
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
shell(1), syscalls(2)
|
||||||
@@ -53,6 +53,11 @@
|
|||||||
shell(1) Shell commands reference
|
shell(1) Shell commands reference
|
||||||
init(1) Init system
|
init(1) Init system
|
||||||
dhcp(1) DHCP client
|
dhcp(1) DHCP client
|
||||||
|
fetch(1) HTTP client
|
||||||
|
ping(1) ICMP ping
|
||||||
|
nslookup(1) DNS lookup
|
||||||
|
fontscale(1) Terminal font scaling
|
||||||
|
edit(1) Text editor
|
||||||
man(1) The man command itself
|
man(1) The man command itself
|
||||||
legal(7) Copyright and legal information
|
legal(7) Copyright and legal information
|
||||||
syscalls(2) Overview of all syscalls
|
syscalls(2) Overview of all syscalls
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
.TH NSLOOKUP 1
|
||||||
|
.SH NAME
|
||||||
|
nslookup - DNS hostname lookup
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
nslookup <hostname>
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Resolves a hostname to an IPv4 address using the configured
|
||||||
|
DNS server and prints the result.
|
||||||
|
|
||||||
|
The kernel DNS resolver sends a UDP query to port 53 of the
|
||||||
|
configured DNS server and waits up to 5 seconds for a reply.
|
||||||
|
Results are cached in an 8-entry kernel cache with TTL support.
|
||||||
|
|
||||||
|
.SH OUTPUT
|
||||||
|
Server: 10.0.68.1
|
||||||
|
Name: example.com
|
||||||
|
Address: 93.184.216.34
|
||||||
|
Time: 3ms
|
||||||
|
|
||||||
|
If the lookup fails:
|
||||||
|
|
||||||
|
Could not resolve: badhost.invalid
|
||||||
|
|
||||||
|
.SH DNS CONFIGURATION
|
||||||
|
The DNS server address is obtained automatically via DHCP.
|
||||||
|
It can also be viewed and set with ifconfig. The default
|
||||||
|
is 10.0.68.1 (QEMU user-mode networking).
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
nslookup google.com
|
||||||
|
nslookup icanhazip.com
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
ping(1), fetch(1), dhcp(1), ifconfig(1), syscalls(2)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
.TH PING 1
|
||||||
|
.SH NAME
|
||||||
|
ping - send ICMP echo requests
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
ping <host>
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Sends 4 ICMP echo requests to the specified host and prints
|
||||||
|
the round-trip time for each reply.
|
||||||
|
|
||||||
|
The host may be an IP address or a hostname. Hostnames are
|
||||||
|
resolved via the configured DNS server.
|
||||||
|
|
||||||
|
Each request has a 3-second timeout. Requests are sent at
|
||||||
|
1-second intervals.
|
||||||
|
|
||||||
|
.SH OUTPUT
|
||||||
|
PING example.com (93.184.216.34)
|
||||||
|
Reply from 93.184.216.34: time=12ms
|
||||||
|
Reply from 93.184.216.34: time=11ms
|
||||||
|
Reply from 93.184.216.34: time=13ms
|
||||||
|
Reply from 93.184.216.34: time=11ms
|
||||||
|
|
||||||
|
If a reply is not received within the timeout:
|
||||||
|
|
||||||
|
Request timed out
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
ping 10.0.68.1
|
||||||
|
Ping the gateway by IP address.
|
||||||
|
|
||||||
|
ping google.com
|
||||||
|
Ping by hostname (requires DNS).
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
nslookup(1), ifconfig(1), shell(1), syscalls(2)
|
||||||
@@ -61,18 +61,24 @@
|
|||||||
date Show current date and time
|
date Show current date and time
|
||||||
uptime Show system uptime
|
uptime Show system uptime
|
||||||
clear Clear the screen and framebuffer
|
clear Clear the screen and framebuffer
|
||||||
|
fontscale [n] Get or set terminal font scale
|
||||||
reset Reboot the system
|
reset Reboot the system
|
||||||
shutdown Shut down the system
|
shutdown Shut down the system
|
||||||
|
|
||||||
.SS Network commands (0:/os/)
|
.SS Network commands (0:/os/)
|
||||||
ping <ip> Send ICMP echo requests
|
ping <host> Send ICMP echo requests
|
||||||
|
nslookup <host> DNS lookup
|
||||||
ifconfig Show/set network configuration
|
ifconfig Show/set network configuration
|
||||||
tcpconnect <ip> <port> Interactive TCP client
|
tcpconnect <host> <port> Interactive TCP client
|
||||||
irc IRC client
|
irc IRC client
|
||||||
dhcp DHCP client
|
dhcp DHCP client
|
||||||
fetch HTTP client
|
fetch HTTP/HTTPS client (TLS 1.2)
|
||||||
|
wiki Wikipedia article viewer
|
||||||
httpd HTTP server
|
httpd HTTP server
|
||||||
|
|
||||||
|
Network commands accept both IP addresses and hostnames.
|
||||||
|
Hostnames are resolved via the configured DNS server.
|
||||||
|
|
||||||
.SS Games (0:/games/)
|
.SS Games (0:/games/)
|
||||||
doom DOOM
|
doom DOOM
|
||||||
|
|
||||||
|
|||||||
+37
-3
@@ -3,7 +3,7 @@
|
|||||||
syscalls - overview of ZenithOS system calls
|
syscalls - overview of ZenithOS system calls
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
ZenithOS provides 41 system calls (numbers 0-40) for userspace
|
ZenithOS provides 46 system calls (numbers 0-45) for userspace
|
||||||
programs. Syscalls use the x86-64 SYSCALL instruction with the
|
programs. Syscalls use the x86-64 SYSCALL instruction with the
|
||||||
following register convention:
|
following register convention:
|
||||||
|
|
||||||
@@ -121,13 +121,22 @@
|
|||||||
int32_t zenith::ping(uint32_t ip, uint32_t timeoutMs);
|
int32_t zenith::ping(uint32_t ip, uint32_t timeoutMs);
|
||||||
|
|
||||||
.B SYS_GETNETCFG (37)
|
.B SYS_GETNETCFG (37)
|
||||||
Get the current network configuration (IP, mask, gateway, MAC).
|
Get the current network configuration (IP, mask, gateway, MAC,
|
||||||
|
DNS server).
|
||||||
void zenith::get_netcfg(Zenith::NetCfg* out);
|
void zenith::get_netcfg(Zenith::NetCfg* out);
|
||||||
|
|
||||||
.B SYS_SETNETCFG (38)
|
.B SYS_SETNETCFG (38)
|
||||||
Set the network configuration (IP, mask, gateway).
|
Set the network configuration (IP, mask, gateway, DNS server).
|
||||||
int zenith::set_netcfg(const Zenith::NetCfg* cfg);
|
int zenith::set_netcfg(const Zenith::NetCfg* cfg);
|
||||||
|
|
||||||
|
.B SYS_RESOLVE (44)
|
||||||
|
Resolve a hostname to an IPv4 address via DNS. Sends a UDP
|
||||||
|
query to the configured DNS server and waits up to 5 seconds
|
||||||
|
for a reply. Returns the IP in network byte order, or 0 on
|
||||||
|
failure. IP address strings (e.g. "10.0.0.1") are detected
|
||||||
|
and returned directly without a DNS query.
|
||||||
|
uint32_t zenith::resolve(const char* hostname);
|
||||||
|
|
||||||
.SH SOCKETS
|
.SH SOCKETS
|
||||||
.B SYS_SOCKET (29)
|
.B SYS_SOCKET (29)
|
||||||
Create a socket. type=SOCK_TCP (1) or SOCK_UDP (2).
|
Create a socket. type=SOCK_TCP (1) or SOCK_UDP (2).
|
||||||
@@ -174,6 +183,17 @@
|
|||||||
int zenith::recvfrom(int fd, void* buf, uint32_t maxLen,
|
int zenith::recvfrom(int fd, void* buf, uint32_t maxLen,
|
||||||
uint32_t* srcIp, uint16_t* srcPort);
|
uint32_t* srcIp, uint16_t* srcPort);
|
||||||
|
|
||||||
|
.SH FILE WRITE
|
||||||
|
.B SYS_FWRITE (41)
|
||||||
|
Write bytes to a file at a given offset.
|
||||||
|
int zenith::fwrite(int handle, const uint8_t* buf,
|
||||||
|
uint64_t offset, uint64_t size);
|
||||||
|
|
||||||
|
.B SYS_FCREATE (42)
|
||||||
|
Create a new file on the ramdisk. Returns a handle or negative
|
||||||
|
on error.
|
||||||
|
int zenith::fcreate(const char* path);
|
||||||
|
|
||||||
.SH FRAMEBUFFER
|
.SH FRAMEBUFFER
|
||||||
.B SYS_FBINFO (21)
|
.B SYS_FBINFO (21)
|
||||||
Get framebuffer dimensions and format.
|
Get framebuffer dimensions and format.
|
||||||
@@ -188,11 +208,25 @@
|
|||||||
Get terminal dimensions (columns and rows).
|
Get terminal dimensions (columns and rows).
|
||||||
void zenith::termsize(int* cols, int* rows);
|
void zenith::termsize(int* cols, int* rows);
|
||||||
|
|
||||||
|
.B SYS_TERMSCALE (43)
|
||||||
|
Get or set the terminal font scale factor. When scale_x is 0,
|
||||||
|
returns the current scale as (scale_y << 32 | scale_x). When
|
||||||
|
scale_x is non-zero, sets the font scale and returns the new
|
||||||
|
terminal dimensions as (rows << 32 | cols).
|
||||||
|
void zenith::termscale(int scale_x, int scale_y);
|
||||||
|
void zenith::get_termscale(int* scale_x, int* scale_y);
|
||||||
|
|
||||||
.SH ARGUMENTS
|
.SH ARGUMENTS
|
||||||
.B SYS_GETARGS (25)
|
.B SYS_GETARGS (25)
|
||||||
Get the argument string passed to this process at spawn time.
|
Get the argument string passed to this process at spawn time.
|
||||||
int zenith::getargs(char* buf, uint64_t maxLen);
|
int zenith::getargs(char* buf, uint64_t maxLen);
|
||||||
|
|
||||||
|
.SH RANDOM
|
||||||
|
.B SYS_GETRANDOM (45)
|
||||||
|
Fill a buffer with random bytes using RDTSC-seeded entropy.
|
||||||
|
Returns the number of bytes written.
|
||||||
|
int64_t zenith::getrandom(void* buf, uint32_t len);
|
||||||
|
|
||||||
.SH POWER MANAGEMENT
|
.SH POWER MANAGEMENT
|
||||||
.B SYS_RESET (26)
|
.B SYS_RESET (26)
|
||||||
Reboot the system.
|
Reboot the system.
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
.TH TLS-ERRORS 5
|
||||||
|
.SH NAME
|
||||||
|
tls-errors - BearSSL TLS and X.509 error codes
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
ZenithOS uses BearSSL for TLS 1.2 connections. When a TLS
|
||||||
|
operation fails, an integer error code is reported. This page
|
||||||
|
lists all possible error codes.
|
||||||
|
|
||||||
|
.SH SSL/TLS ENGINE ERRORS
|
||||||
|
|
||||||
|
.B 0 BR_ERR_OK
|
||||||
|
No error.
|
||||||
|
|
||||||
|
.B 1 BR_ERR_BAD_PARAM
|
||||||
|
Caller-provided parameter is incorrect.
|
||||||
|
|
||||||
|
.B 2 BR_ERR_BAD_STATE
|
||||||
|
Operation cannot be applied in the current engine state.
|
||||||
|
|
||||||
|
.B 3 BR_ERR_UNSUPPORTED_VERSION
|
||||||
|
Incoming protocol or record version is unsupported.
|
||||||
|
|
||||||
|
.B 4 BR_ERR_BAD_VERSION
|
||||||
|
Incoming record version does not match the expected version.
|
||||||
|
|
||||||
|
.B 5 BR_ERR_BAD_LENGTH
|
||||||
|
Incoming record length is invalid.
|
||||||
|
|
||||||
|
.B 6 BR_ERR_TOO_LARGE
|
||||||
|
Incoming record is too large, or buffer is too small for the
|
||||||
|
handshake message to send.
|
||||||
|
|
||||||
|
.B 7 BR_ERR_BAD_MAC
|
||||||
|
Decryption found invalid padding, or the record MAC is
|
||||||
|
not correct.
|
||||||
|
|
||||||
|
.B 8 BR_ERR_NO_RANDOM
|
||||||
|
No initial entropy was provided and none could be obtained
|
||||||
|
from the OS.
|
||||||
|
|
||||||
|
.B 9 BR_ERR_UNKNOWN_TYPE
|
||||||
|
Incoming record type is unknown.
|
||||||
|
|
||||||
|
.B 10 BR_ERR_UNEXPECTED
|
||||||
|
Incoming record or message has wrong type for the current
|
||||||
|
engine state.
|
||||||
|
|
||||||
|
.B 12 BR_ERR_BAD_CCS
|
||||||
|
ChangeCipherSpec message from the peer has invalid contents.
|
||||||
|
|
||||||
|
.B 13 BR_ERR_BAD_ALERT
|
||||||
|
Alert message from the peer has invalid contents (odd length).
|
||||||
|
|
||||||
|
.B 14 BR_ERR_BAD_HANDSHAKE
|
||||||
|
Incoming handshake message decoding failed.
|
||||||
|
|
||||||
|
.B 15 BR_ERR_OVERSIZED_ID
|
||||||
|
ServerHello contains a session ID larger than 32 bytes.
|
||||||
|
|
||||||
|
.B 16 BR_ERR_BAD_CIPHER_SUITE
|
||||||
|
Server wants to use a cipher suite that we did not advertise,
|
||||||
|
or we tried to advertise a cipher suite that we do not support.
|
||||||
|
|
||||||
|
.B 17 BR_ERR_BAD_COMPRESSION
|
||||||
|
Server wants to use a compression method that we did not
|
||||||
|
advertise.
|
||||||
|
|
||||||
|
.B 18 BR_ERR_BAD_FRAGLEN
|
||||||
|
Server's max fragment length does not match client's.
|
||||||
|
|
||||||
|
.B 19 BR_ERR_BAD_SECRENEG
|
||||||
|
Secure renegotiation failed.
|
||||||
|
|
||||||
|
.B 20 BR_ERR_EXTRA_EXTENSION
|
||||||
|
Server sent an extension type that we did not announce, or
|
||||||
|
used the same extension type more than once in ServerHello.
|
||||||
|
|
||||||
|
.B 21 BR_ERR_BAD_SNI
|
||||||
|
Invalid Server Name Indication contents (when used by the
|
||||||
|
server, this extension shall be empty).
|
||||||
|
|
||||||
|
.B 22 BR_ERR_BAD_HELLO_DONE
|
||||||
|
Invalid ServerHelloDone from the server (length is not 0).
|
||||||
|
|
||||||
|
.B 23 BR_ERR_LIMIT_EXCEEDED
|
||||||
|
Internal limit exceeded (e.g. server's public key is too
|
||||||
|
large).
|
||||||
|
|
||||||
|
.B 24 BR_ERR_BAD_FINISHED
|
||||||
|
Finished message from peer does not match the expected value.
|
||||||
|
|
||||||
|
.B 25 BR_ERR_RESUME_MISMATCH
|
||||||
|
Session resumption attempted with a different version or
|
||||||
|
cipher suite.
|
||||||
|
|
||||||
|
.B 26 BR_ERR_INVALID_ALGORITHM
|
||||||
|
Unsupported or invalid algorithm (ECDHE curve, signature
|
||||||
|
algorithm, hash function).
|
||||||
|
|
||||||
|
.B 27 BR_ERR_BAD_SIGNATURE
|
||||||
|
Invalid signature on ServerKeyExchange or CertificateVerify.
|
||||||
|
|
||||||
|
.B 28 BR_ERR_WRONG_KEY_USAGE
|
||||||
|
Peer's public key does not have the proper type or is not
|
||||||
|
allowed for the requested operation.
|
||||||
|
|
||||||
|
.B 29 BR_ERR_NO_CLIENT_AUTH
|
||||||
|
Client did not send a certificate upon request, or the client
|
||||||
|
certificate could not be validated.
|
||||||
|
|
||||||
|
.B 31 BR_ERR_IO
|
||||||
|
I/O error or premature close on the underlying transport.
|
||||||
|
|
||||||
|
.SH X.509 CERTIFICATE ERRORS
|
||||||
|
|
||||||
|
.B 32 BR_ERR_X509_OK
|
||||||
|
X.509 validation was successful (not an error).
|
||||||
|
|
||||||
|
.B 33 BR_ERR_X509_INVALID_VALUE
|
||||||
|
Invalid value in an ASN.1 structure.
|
||||||
|
|
||||||
|
.B 34 BR_ERR_X509_TRUNCATED
|
||||||
|
Truncated certificate.
|
||||||
|
|
||||||
|
.B 35 BR_ERR_X509_EMPTY_CHAIN
|
||||||
|
Empty certificate chain (no certificate at all).
|
||||||
|
|
||||||
|
.B 36 BR_ERR_X509_INNER_TRUNC
|
||||||
|
Inner element extends beyond outer element size.
|
||||||
|
|
||||||
|
.B 37 BR_ERR_X509_BAD_TAG_CLASS
|
||||||
|
Unsupported tag class (application or private).
|
||||||
|
|
||||||
|
.B 38 BR_ERR_X509_BAD_TAG_VALUE
|
||||||
|
Unsupported tag value.
|
||||||
|
|
||||||
|
.B 39 BR_ERR_X509_INDEFINITE_LENGTH
|
||||||
|
Indefinite length encoding found.
|
||||||
|
|
||||||
|
.B 40 BR_ERR_X509_EXTRA_ELEMENT
|
||||||
|
Extraneous element in certificate.
|
||||||
|
|
||||||
|
.B 41 BR_ERR_X509_UNEXPECTED
|
||||||
|
Unexpected element in certificate.
|
||||||
|
|
||||||
|
.B 42 BR_ERR_X509_NOT_CONSTRUCTED
|
||||||
|
Expected constructed element, but found primitive.
|
||||||
|
|
||||||
|
.B 43 BR_ERR_X509_NOT_PRIMITIVE
|
||||||
|
Expected primitive element, but found constructed.
|
||||||
|
|
||||||
|
.B 44 BR_ERR_X509_PARTIAL_BYTE
|
||||||
|
BIT STRING length is not a multiple of 8.
|
||||||
|
|
||||||
|
.B 45 BR_ERR_X509_BAD_BOOLEAN
|
||||||
|
BOOLEAN value has invalid length.
|
||||||
|
|
||||||
|
.B 46 BR_ERR_X509_OVERFLOW
|
||||||
|
Value is off-limits (overflow).
|
||||||
|
|
||||||
|
.B 47 BR_ERR_X509_BAD_DN
|
||||||
|
Invalid distinguished name.
|
||||||
|
|
||||||
|
.B 48 BR_ERR_X509_BAD_TIME
|
||||||
|
Invalid date/time representation in certificate.
|
||||||
|
|
||||||
|
.B 49 BR_ERR_X509_UNSUPPORTED
|
||||||
|
Certificate contains unsupported features that cannot be
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
.B 50 BR_ERR_X509_LIMIT_EXCEEDED
|
||||||
|
Key or signature size exceeds internal limits.
|
||||||
|
|
||||||
|
.B 51 BR_ERR_X509_WRONG_KEY_TYPE
|
||||||
|
Key type does not match that which was expected.
|
||||||
|
|
||||||
|
.B 52 BR_ERR_X509_BAD_SIGNATURE
|
||||||
|
Signature is invalid.
|
||||||
|
|
||||||
|
.B 53 BR_ERR_X509_TIME_UNKNOWN
|
||||||
|
Validation time is unknown (no time was set).
|
||||||
|
|
||||||
|
.B 54 BR_ERR_X509_EXPIRED
|
||||||
|
Certificate is expired or not yet valid.
|
||||||
|
|
||||||
|
.B 55 BR_ERR_X509_DN_MISMATCH
|
||||||
|
Issuer/subject DN mismatch in the chain.
|
||||||
|
|
||||||
|
.B 56 BR_ERR_X509_BAD_SERVER_NAME
|
||||||
|
Expected server name was not found in the chain.
|
||||||
|
|
||||||
|
.B 57 BR_ERR_X509_CRITICAL_EXTENSION
|
||||||
|
Unknown critical extension in certificate.
|
||||||
|
|
||||||
|
.B 58 BR_ERR_X509_NOT_CA
|
||||||
|
Not a CA, or path length constraint violation.
|
||||||
|
|
||||||
|
.B 59 BR_ERR_X509_FORBIDDEN_KEY_USAGE
|
||||||
|
Key Usage extension prohibits the intended usage.
|
||||||
|
|
||||||
|
.B 60 BR_ERR_X509_WEAK_PUBLIC_KEY
|
||||||
|
Public key found in certificate is too small.
|
||||||
|
|
||||||
|
.B 62 BR_ERR_X509_NOT_TRUSTED
|
||||||
|
Chain could not be linked to a trust anchor.
|
||||||
|
|
||||||
|
.SH FATAL ALERTS
|
||||||
|
When a fatal alert is received from the peer, the error code
|
||||||
|
is 256 + the TLS alert value. When a fatal alert is sent to
|
||||||
|
the peer, the error code is 512 + the TLS alert value.
|
||||||
|
|
||||||
|
Common alert values:
|
||||||
|
0 close_notify
|
||||||
|
10 unexpected_message
|
||||||
|
20 bad_record_mac
|
||||||
|
40 handshake_failure
|
||||||
|
42 bad_certificate
|
||||||
|
43 unsupported_certificate
|
||||||
|
44 certificate_revoked
|
||||||
|
45 certificate_expired
|
||||||
|
46 certificate_unknown
|
||||||
|
47 illegal_parameter
|
||||||
|
48 unknown_ca
|
||||||
|
50 decode_error
|
||||||
|
51 decrypt_error
|
||||||
|
70 protocol_version
|
||||||
|
71 insufficient_security
|
||||||
|
80 internal_error
|
||||||
|
86 unrecognized_name
|
||||||
|
112 no_application_protocol
|
||||||
|
|
||||||
|
For example, error 296 means a handshake_failure alert was
|
||||||
|
received (256 + 40 = 296).
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
fetch(1), syscalls(2)
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
.TH WIKI 1
|
||||||
|
.SH NAME
|
||||||
|
wiki - Wikipedia article viewer for ZenithOS
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
wiki <title>
|
||||||
|
wiki -f <title>
|
||||||
|
wiki -s <query>
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
wiki fetches and displays Wikipedia articles in the terminal.
|
||||||
|
It connects to en.wikipedia.org over HTTPS (TLS 1.2) and
|
||||||
|
uses the Wikipedia REST and Action APIs to retrieve article
|
||||||
|
content as plain text.
|
||||||
|
|
||||||
|
Articles are displayed in a fullscreen interactive pager with
|
||||||
|
color-coded headings and word-wrapped text. Multi-word titles
|
||||||
|
are accepted as separate arguments and joined automatically.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.B -f
|
||||||
|
Full article mode. Display the complete article text instead
|
||||||
|
of just the summary. Section headings are color-coded.
|
||||||
|
|
||||||
|
.B -s
|
||||||
|
Search mode. Search Wikipedia for articles matching the
|
||||||
|
query and display a numbered list of up to 10 results.
|
||||||
|
Press a number key to view that article's summary.
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
wiki Linux
|
||||||
|
Show a summary of the Linux article.
|
||||||
|
|
||||||
|
wiki -f C programming language
|
||||||
|
Show the full text of the C programming language article.
|
||||||
|
|
||||||
|
wiki -s operating system
|
||||||
|
Search for articles related to "operating system".
|
||||||
|
|
||||||
|
.SH TLS SUPPORT
|
||||||
|
Connections use BearSSL for TLS 1.2. Server certificates
|
||||||
|
are validated against the system CA bundle at
|
||||||
|
0:/etc/ca-certificates.crt.
|
||||||
|
|
||||||
|
.SH KEYBOARD
|
||||||
|
|
||||||
|
.SS Article pager
|
||||||
|
j / Down Scroll down one line
|
||||||
|
k / Up Scroll up one line
|
||||||
|
Space / PgDn Scroll down one page
|
||||||
|
b / PgUp Scroll up one page
|
||||||
|
g / Home Jump to top
|
||||||
|
G / End Jump to bottom
|
||||||
|
q Quit pager
|
||||||
|
|
||||||
|
.SS Search results
|
||||||
|
1-9, 0 View article (0 = result 10)
|
||||||
|
q Quit search
|
||||||
|
|
||||||
|
.SS General
|
||||||
|
Ctrl+Q Abort during network request
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
fetch(1), ping(1), nslookup(1), shell(1)
|
||||||
Binary file not shown.
Binary file not shown.
@@ -463,6 +463,7 @@ extern "C" void _start() {
|
|||||||
newCfg.ipAddress = offer.offeredIp;
|
newCfg.ipAddress = offer.offeredIp;
|
||||||
newCfg.subnetMask = offer.subnetMask;
|
newCfg.subnetMask = offer.subnetMask;
|
||||||
newCfg.gateway = offer.router;
|
newCfg.gateway = offer.router;
|
||||||
|
newCfg.dnsServer = offer.dns;
|
||||||
zenith::set_netcfg(&newCfg);
|
zenith::set_netcfg(&newCfg);
|
||||||
|
|
||||||
// 9. Print results
|
// 9. Print results
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Makefile for fetch (HTTP/HTTPS client) on ZenithOS
|
||||||
|
# Copyright (c) 2025-2026 Daniel Hammer
|
||||||
|
|
||||||
|
MAKEFLAGS += -rR
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
|
# ---- Toolchain ----
|
||||||
|
|
||||||
|
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
|
||||||
|
ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
|
||||||
|
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||||
|
else
|
||||||
|
CXX := g++
|
||||||
|
endif
|
||||||
|
|
||||||
|
# ---- Paths ----
|
||||||
|
|
||||||
|
BEARSSL := ../../lib/bearssl
|
||||||
|
LIBC_LIB := ../../lib/libc
|
||||||
|
LIBC_INC := ../../include/libc
|
||||||
|
PROG_INC := ../../include
|
||||||
|
LINK_LD := ../../link.ld
|
||||||
|
BINDIR := ../../bin
|
||||||
|
OBJDIR := obj
|
||||||
|
|
||||||
|
# ---- Compiler flags ----
|
||||||
|
|
||||||
|
CXXFLAGS := \
|
||||||
|
-std=gnu++20 \
|
||||||
|
-g -O2 -pipe \
|
||||||
|
-Wall \
|
||||||
|
-Wextra \
|
||||||
|
-Wno-unused-parameter \
|
||||||
|
-nostdinc \
|
||||||
|
-ffreestanding \
|
||||||
|
-fno-stack-protector \
|
||||||
|
-fno-stack-check \
|
||||||
|
-fno-PIC \
|
||||||
|
-fno-rtti \
|
||||||
|
-fno-exceptions \
|
||||||
|
-ffunction-sections \
|
||||||
|
-fdata-sections \
|
||||||
|
-m64 \
|
||||||
|
-march=x86-64 \
|
||||||
|
-mno-80387 \
|
||||||
|
-mno-mmx \
|
||||||
|
-mno-sse \
|
||||||
|
-mno-sse2 \
|
||||||
|
-mno-red-zone \
|
||||||
|
-mcmodel=small \
|
||||||
|
-I $(PROG_INC) \
|
||||||
|
-isystem $(LIBC_INC) \
|
||||||
|
-I $(BEARSSL)/inc \
|
||||||
|
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
|
||||||
|
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
|
||||||
|
|
||||||
|
# ---- Linker flags ----
|
||||||
|
|
||||||
|
LDFLAGS := \
|
||||||
|
-nostdlib \
|
||||||
|
-static \
|
||||||
|
-Wl,--build-id=none \
|
||||||
|
-Wl,--gc-sections \
|
||||||
|
-Wl,-m,elf_x86_64 \
|
||||||
|
-z max-page-size=0x1000 \
|
||||||
|
-T $(LINK_LD)
|
||||||
|
|
||||||
|
# ---- Libraries ----
|
||||||
|
|
||||||
|
LIBS := $(BEARSSL)/libbearssl.a $(LIBC_LIB)/liblibc.a
|
||||||
|
|
||||||
|
# ---- Target ----
|
||||||
|
|
||||||
|
TARGET := $(BINDIR)/os/fetch.elf
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJDIR)/main.o $(LIBS) $(LINK_LD) Makefile
|
||||||
|
mkdir -p $(BINDIR)/os
|
||||||
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJDIR)/main.o $(LIBS) -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/main.o: main.cpp Makefile
|
||||||
|
mkdir -p $(OBJDIR)
|
||||||
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJDIR) $(TARGET)
|
||||||
+705
-219
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* main.cpp
|
||||||
|
* fontscale - Change terminal font scale
|
||||||
|
* Copyright (c) 2025-2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zenith/syscall.h>
|
||||||
|
#include <zenith/string.h>
|
||||||
|
|
||||||
|
static int atoi(const char* s) {
|
||||||
|
int n = 0;
|
||||||
|
while (*s >= '0' && *s <= '9') {
|
||||||
|
n = n * 10 + (*s - '0');
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_int(int n) {
|
||||||
|
char buf[16];
|
||||||
|
int i = 0;
|
||||||
|
if (n == 0) {
|
||||||
|
zenith::putchar('0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (n > 0 && i < 15) {
|
||||||
|
buf[i++] = '0' + (n % 10);
|
||||||
|
n /= 10;
|
||||||
|
}
|
||||||
|
while (i > 0) zenith::putchar(buf[--i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void _start() {
|
||||||
|
char args[128];
|
||||||
|
int len = zenith::getargs(args, sizeof(args));
|
||||||
|
|
||||||
|
if (len <= 0 || args[0] == '\0') {
|
||||||
|
// No args: show current scale
|
||||||
|
int sx, sy;
|
||||||
|
zenith::get_termscale(&sx, &sy);
|
||||||
|
int cols, rows;
|
||||||
|
zenith::termsize(&cols, &rows);
|
||||||
|
|
||||||
|
zenith::print("Font scale: ");
|
||||||
|
print_int(sx);
|
||||||
|
zenith::print("x");
|
||||||
|
print_int(sy);
|
||||||
|
zenith::print(" Terminal: ");
|
||||||
|
print_int(cols);
|
||||||
|
zenith::print("x");
|
||||||
|
print_int(rows);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
zenith::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse arguments
|
||||||
|
const char* p = zenith::skip_spaces(args);
|
||||||
|
int scale_x = atoi(p);
|
||||||
|
|
||||||
|
// Skip past first number to find optional second
|
||||||
|
while (*p >= '0' && *p <= '9') p++;
|
||||||
|
p = zenith::skip_spaces(p);
|
||||||
|
int scale_y = (*p >= '1' && *p <= '8') ? atoi(p) : scale_x;
|
||||||
|
|
||||||
|
if (scale_x < 1 || scale_x > 8 || scale_y < 1 || scale_y > 8) {
|
||||||
|
zenith::print("fontscale: scale must be 1-8\n");
|
||||||
|
zenith::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
zenith::termscale(scale_x, scale_y);
|
||||||
|
|
||||||
|
// Clear and show result
|
||||||
|
zenith::print("\033[2J\033[H");
|
||||||
|
|
||||||
|
int cols, rows;
|
||||||
|
zenith::termsize(&cols, &rows);
|
||||||
|
zenith::print("Font scale set to ");
|
||||||
|
print_int(scale_x);
|
||||||
|
zenith::print("x");
|
||||||
|
print_int(scale_y);
|
||||||
|
zenith::print(" (");
|
||||||
|
print_int(cols);
|
||||||
|
zenith::print("x");
|
||||||
|
print_int(rows);
|
||||||
|
zenith::print(")\n");
|
||||||
|
|
||||||
|
zenith::exit(0);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* main.cpp
|
* main.cpp
|
||||||
* HTTP/1.0 server for ZenithOS
|
* HTTP/1.0 server for ZenithOS
|
||||||
* Usage: run httpd.elf [port] (default: 80)
|
* Usage: httpd [port] (default: 80)
|
||||||
* Serves a built-in index page and files from the VFS
|
* Serves a built-in index page and files from the VFS
|
||||||
* Copyright (c) 2025-2026 Daniel Hammer
|
* Copyright (c) 2025-2026 Daniel Hammer
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ extern "C" void _start() {
|
|||||||
zenith::print(" Gateway: ");
|
zenith::print(" Gateway: ");
|
||||||
print_ip(cfg.gateway);
|
print_ip(cfg.gateway);
|
||||||
zenith::putchar('\n');
|
zenith::putchar('\n');
|
||||||
|
zenith::print(" DNS Server: ");
|
||||||
|
print_ip(cfg.dnsServer);
|
||||||
|
zenith::putchar('\n');
|
||||||
zenith::exit(0);
|
zenith::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-12
@@ -836,23 +836,26 @@ extern "C" void _start() {
|
|||||||
const char* arg = skip_spaces(argbuf);
|
const char* arg = skip_spaces(argbuf);
|
||||||
|
|
||||||
if (*arg == '\0') {
|
if (*arg == '\0') {
|
||||||
zenith::print("Usage: irc.elf <server_ip> <port> <nickname> [#channel]\n");
|
zenith::print("Usage: irc <server> <port> <nickname> [#channel]\n");
|
||||||
zenith::print("Example: run irc.elf 10.0.68.1 6667 ZenithUser #general\n");
|
zenith::print("Example: irc irc.libera.chat 6667 ZenithUser #general\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse IP
|
// Parse host (IP or hostname)
|
||||||
char ipStr[32];
|
char hostStr[128];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (arg[i] && arg[i] != ' ' && i < 31) { ipStr[i] = arg[i]; i++; }
|
while (arg[i] && arg[i] != ' ' && i < 127) { hostStr[i] = arg[i]; i++; }
|
||||||
ipStr[i] = '\0';
|
hostStr[i] = '\0';
|
||||||
arg = skip_spaces(arg + i);
|
arg = skip_spaces(arg + i);
|
||||||
|
|
||||||
if (!parse_ip(ipStr, &irc.serverIp)) {
|
if (!parse_ip(hostStr, &irc.serverIp)) {
|
||||||
zenith::print("Invalid IP address: ");
|
irc.serverIp = zenith::resolve(hostStr);
|
||||||
zenith::print(ipStr);
|
if (irc.serverIp == 0) {
|
||||||
zenith::putchar('\n');
|
zenith::print("Could not resolve: ");
|
||||||
return;
|
zenith::print(hostStr);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse port
|
// Parse port
|
||||||
@@ -910,7 +913,7 @@ extern "C" void _start() {
|
|||||||
|
|
||||||
// Initial draw
|
// Initial draw
|
||||||
msg_add("\033[1m*** ZenithOS IRC Client\033[0m");
|
msg_add("\033[1m*** ZenithOS IRC Client\033[0m");
|
||||||
msg_add_fmt("*** Connecting to %s:%d as %s...", ipStr, (int)irc.serverPort, irc.nick);
|
msg_add_fmt("*** Connecting to %s:%d as %s...", hostStr, (int)irc.serverPort, irc.nick);
|
||||||
ui_render();
|
ui_render();
|
||||||
|
|
||||||
// Create socket and connect
|
// Create socket and connect
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* main.cpp
|
||||||
|
* DNS lookup utility for ZenithOS
|
||||||
|
* Usage: nslookup <hostname>
|
||||||
|
* Copyright (c) 2025-2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zenith/syscall.h>
|
||||||
|
#include <zenith/string.h>
|
||||||
|
|
||||||
|
using zenith::skip_spaces;
|
||||||
|
|
||||||
|
static void print_ip(uint32_t ip) {
|
||||||
|
auto print_int = [](uint32_t n) {
|
||||||
|
if (n == 0) { zenith::putchar('0'); return; }
|
||||||
|
char buf[4];
|
||||||
|
int i = 0;
|
||||||
|
while (n > 0) { buf[i++] = '0' + (n % 10); n /= 10; }
|
||||||
|
for (int j = i - 1; j >= 0; j--) zenith::putchar(buf[j]);
|
||||||
|
};
|
||||||
|
|
||||||
|
print_int(ip & 0xFF);
|
||||||
|
zenith::putchar('.');
|
||||||
|
print_int((ip >> 8) & 0xFF);
|
||||||
|
zenith::putchar('.');
|
||||||
|
print_int((ip >> 16) & 0xFF);
|
||||||
|
zenith::putchar('.');
|
||||||
|
print_int((ip >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void _start() {
|
||||||
|
char args[256];
|
||||||
|
int len = zenith::getargs(args, sizeof(args));
|
||||||
|
|
||||||
|
const char* hostname = skip_spaces(args);
|
||||||
|
if (len <= 0 || hostname[0] == '\0') {
|
||||||
|
zenith::print("Usage: nslookup <hostname>\n");
|
||||||
|
zenith::print("Example: nslookup example.com\n");
|
||||||
|
zenith::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show DNS server
|
||||||
|
Zenith::NetCfg cfg;
|
||||||
|
zenith::get_netcfg(&cfg);
|
||||||
|
|
||||||
|
zenith::print("Server: ");
|
||||||
|
print_ip(cfg.dnsServer);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
|
||||||
|
zenith::print("Querying ");
|
||||||
|
zenith::print(hostname);
|
||||||
|
zenith::print("...\n");
|
||||||
|
|
||||||
|
uint64_t start = zenith::get_milliseconds();
|
||||||
|
uint32_t ip = zenith::resolve(hostname);
|
||||||
|
uint64_t elapsed = zenith::get_milliseconds() - start;
|
||||||
|
|
||||||
|
if (ip == 0) {
|
||||||
|
zenith::print("Error: could not resolve ");
|
||||||
|
zenith::print(hostname);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
zenith::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
zenith::print("Name: ");
|
||||||
|
zenith::print(hostname);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
zenith::print("Address: ");
|
||||||
|
print_ip(ip);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
|
||||||
|
// Print timing
|
||||||
|
zenith::print("Time: ");
|
||||||
|
char buf[20];
|
||||||
|
int i = 0;
|
||||||
|
uint64_t ms = elapsed;
|
||||||
|
if (ms == 0) { buf[i++] = '0'; }
|
||||||
|
else { while (ms > 0) { buf[i++] = '0' + (ms % 10); ms /= 10; } }
|
||||||
|
for (int j = i - 1; j >= 0; j--) zenith::putchar(buf[j]);
|
||||||
|
zenith::print(" ms\n");
|
||||||
|
|
||||||
|
zenith::exit(0);
|
||||||
|
}
|
||||||
@@ -65,21 +65,26 @@ extern "C" void _start() {
|
|||||||
int len = zenith::getargs(args, sizeof(args));
|
int len = zenith::getargs(args, sizeof(args));
|
||||||
|
|
||||||
if (len <= 0 || args[0] == '\0') {
|
if (len <= 0 || args[0] == '\0') {
|
||||||
zenith::print("Usage: ping <ip address>\n");
|
zenith::print("Usage: ping <host>\n");
|
||||||
zenith::exit(1);
|
zenith::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ip;
|
uint32_t ip;
|
||||||
if (!parse_ip(args, &ip)) {
|
if (!parse_ip(args, &ip)) {
|
||||||
zenith::print("Invalid IP address: ");
|
ip = zenith::resolve(args);
|
||||||
zenith::print(args);
|
if (ip == 0) {
|
||||||
zenith::putchar('\n');
|
zenith::print("Could not resolve: ");
|
||||||
zenith::exit(1);
|
zenith::print(args);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
zenith::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
zenith::print("PING ");
|
zenith::print("PING ");
|
||||||
|
zenith::print(args);
|
||||||
|
zenith::print(" (");
|
||||||
print_ip(ip);
|
print_ip(ip);
|
||||||
zenith::putchar('\n');
|
zenith::print(")\n");
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
int32_t rtt = zenith::ping(ip, 3000);
|
int32_t rtt = zenith::ping(ip, 3000);
|
||||||
|
|||||||
@@ -118,11 +118,13 @@ static void cmd_help() {
|
|||||||
zenith::print(" date Show current date and time\n");
|
zenith::print(" date Show current date and time\n");
|
||||||
zenith::print(" uptime Show uptime\n");
|
zenith::print(" uptime Show uptime\n");
|
||||||
zenith::print(" clear Clear the screen\n");
|
zenith::print(" clear Clear the screen\n");
|
||||||
|
zenith::print(" fontscale [n] Set terminal font scale (1-8)\n");
|
||||||
zenith::print(" reset Reboot the system\n");
|
zenith::print(" reset Reboot the system\n");
|
||||||
zenith::print(" shutdown Shut down the system\n");
|
zenith::print(" shutdown Shut down the system\n");
|
||||||
zenith::print("\n");
|
zenith::print("\n");
|
||||||
zenith::print("Network commands:\n");
|
zenith::print("Network commands:\n");
|
||||||
zenith::print(" ping <ip> Send ICMP echo requests\n");
|
zenith::print(" ping <ip> Send ICMP echo requests\n");
|
||||||
|
zenith::print(" nslookup DNS lookup\n");
|
||||||
zenith::print(" ifconfig Show/set network configuration\n");
|
zenith::print(" ifconfig Show/set network configuration\n");
|
||||||
zenith::print(" tcpconnect Connect to a TCP server\n");
|
zenith::print(" tcpconnect Connect to a TCP server\n");
|
||||||
zenith::print(" irc IRC client\n");
|
zenith::print(" irc IRC client\n");
|
||||||
|
|||||||
@@ -81,25 +81,28 @@ extern "C" void _start() {
|
|||||||
int len = zenith::getargs(args, sizeof(args));
|
int len = zenith::getargs(args, sizeof(args));
|
||||||
|
|
||||||
if (len <= 0 || args[0] == '\0') {
|
if (len <= 0 || args[0] == '\0') {
|
||||||
zenith::print("Usage: tcpconnect <ip> <port>\n");
|
zenith::print("Usage: tcpconnect <host> <port>\n");
|
||||||
zenith::exit(1);
|
zenith::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse IP address (up to first space)
|
// Parse host (IP or hostname)
|
||||||
char ipStr[32];
|
char hostStr[128];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (args[i] && args[i] != ' ' && i < 31) {
|
while (args[i] && args[i] != ' ' && i < 127) {
|
||||||
ipStr[i] = args[i];
|
hostStr[i] = args[i];
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
ipStr[i] = '\0';
|
hostStr[i] = '\0';
|
||||||
|
|
||||||
uint32_t ip;
|
uint32_t ip;
|
||||||
if (!parse_ip(ipStr, &ip)) {
|
if (!parse_ip(hostStr, &ip)) {
|
||||||
zenith::print("Invalid IP address: ");
|
ip = zenith::resolve(hostStr);
|
||||||
zenith::print(ipStr);
|
if (ip == 0) {
|
||||||
zenith::putchar('\n');
|
zenith::print("Could not resolve: ");
|
||||||
zenith::exit(1);
|
zenith::print(hostStr);
|
||||||
|
zenith::putchar('\n');
|
||||||
|
zenith::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse port
|
// Parse port
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Makefile for wiki (Wikipedia client) on ZenithOS
|
||||||
|
# Copyright (c) 2025-2026 Daniel Hammer
|
||||||
|
|
||||||
|
MAKEFLAGS += -rR
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
|
# ---- Toolchain ----
|
||||||
|
|
||||||
|
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
|
||||||
|
ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
|
||||||
|
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||||
|
else
|
||||||
|
CXX := g++
|
||||||
|
endif
|
||||||
|
|
||||||
|
# ---- Paths ----
|
||||||
|
|
||||||
|
BEARSSL := ../../lib/bearssl
|
||||||
|
LIBC_LIB := ../../lib/libc
|
||||||
|
LIBC_INC := ../../include/libc
|
||||||
|
PROG_INC := ../../include
|
||||||
|
LINK_LD := ../../link.ld
|
||||||
|
BINDIR := ../../bin
|
||||||
|
OBJDIR := obj
|
||||||
|
|
||||||
|
# ---- Compiler flags ----
|
||||||
|
|
||||||
|
CXXFLAGS := \
|
||||||
|
-std=gnu++20 \
|
||||||
|
-g -O2 -pipe \
|
||||||
|
-Wall \
|
||||||
|
-Wextra \
|
||||||
|
-Wno-unused-parameter \
|
||||||
|
-nostdinc \
|
||||||
|
-ffreestanding \
|
||||||
|
-fno-stack-protector \
|
||||||
|
-fno-stack-check \
|
||||||
|
-fno-PIC \
|
||||||
|
-fno-rtti \
|
||||||
|
-fno-exceptions \
|
||||||
|
-ffunction-sections \
|
||||||
|
-fdata-sections \
|
||||||
|
-m64 \
|
||||||
|
-march=x86-64 \
|
||||||
|
-mno-80387 \
|
||||||
|
-mno-mmx \
|
||||||
|
-mno-sse \
|
||||||
|
-mno-sse2 \
|
||||||
|
-mno-red-zone \
|
||||||
|
-mcmodel=small \
|
||||||
|
-I $(PROG_INC) \
|
||||||
|
-isystem $(LIBC_INC) \
|
||||||
|
-I $(BEARSSL)/inc \
|
||||||
|
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
|
||||||
|
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
|
||||||
|
|
||||||
|
# ---- Linker flags ----
|
||||||
|
|
||||||
|
LDFLAGS := \
|
||||||
|
-nostdlib \
|
||||||
|
-static \
|
||||||
|
-Wl,--build-id=none \
|
||||||
|
-Wl,--gc-sections \
|
||||||
|
-Wl,-m,elf_x86_64 \
|
||||||
|
-z max-page-size=0x1000 \
|
||||||
|
-T $(LINK_LD)
|
||||||
|
|
||||||
|
# ---- Libraries ----
|
||||||
|
|
||||||
|
LIBS := $(BEARSSL)/libbearssl.a $(LIBC_LIB)/liblibc.a
|
||||||
|
|
||||||
|
# ---- Target ----
|
||||||
|
|
||||||
|
TARGET := $(BINDIR)/os/wiki.elf
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJDIR)/main.o $(LIBS) $(LINK_LD) Makefile
|
||||||
|
mkdir -p $(BINDIR)/os
|
||||||
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJDIR)/main.o $(LIBS) -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/main.o: main.cpp Makefile
|
||||||
|
mkdir -p $(OBJDIR)
|
||||||
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJDIR) $(TARGET)
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user