430 lines
14 KiB
Zig
430 lines
14 KiB
Zig
// SPDX-License-Identifier: LCL-1.0
|
|
// Copyright (c) 2026 Markus Maiwald
|
|
// Stewardship: Self Sovereign Society Foundation
|
|
//
|
|
// This file is part of the Nexus Commonwealth.
|
|
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
|
|
|
//! Rumpk Build System — Unified Pipeline
|
|
//!
|
|
//! Single command builds everything: Nim kernel + Zig HAL + LwIP + link.
|
|
//!
|
|
//! Usage:
|
|
//! zig build # Build kernel (default: riscv64)
|
|
//! zig build -Darch=aarch64 # ARM64 target
|
|
//! zig build -Darch=x86_64 # AMD64 target
|
|
//! zig build nim # Recompile Nim kernel sources only
|
|
//! zig build test-lwf # Run LWF tests (native host)
|
|
|
|
const std = @import("std");
|
|
|
|
const Arch = enum { riscv64, aarch64, x86_64 };
|
|
|
|
/// Per-architecture build configuration.
|
|
const ArchConfig = struct {
|
|
cpu_arch: std.Target.Cpu.Arch,
|
|
code_model: std.builtin.CodeModel,
|
|
switch_asm: []const u8,
|
|
linker_script: []const u8,
|
|
/// Arch name for build_nim.sh
|
|
nim_arch: []const u8,
|
|
};
|
|
|
|
fn archConfig(arch: Arch) ArchConfig {
|
|
return switch (arch) {
|
|
.riscv64 => .{
|
|
.cpu_arch = .riscv64,
|
|
.code_model = .medany,
|
|
.switch_asm = "hal/arch/riscv64/switch.S",
|
|
.linker_script = "boot/linker.ld",
|
|
.nim_arch = "riscv64",
|
|
},
|
|
.aarch64 => .{
|
|
.cpu_arch = .aarch64,
|
|
.code_model = .default,
|
|
.switch_asm = "hal/arch/aarch64/switch.S",
|
|
.linker_script = "boot/linker_aarch64.ld",
|
|
.nim_arch = "aarch64",
|
|
},
|
|
.x86_64 => .{
|
|
.cpu_arch = .x86_64,
|
|
.code_model = .kernel,
|
|
.switch_asm = "hal/arch/x86_64/switch.S",
|
|
.linker_script = "boot/linker_x86_64.ld",
|
|
.nim_arch = "x86_64",
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Apply freestanding kernel settings to a module.
|
|
fn applyKernelSettings(mod: *std.Build.Module, code_model: std.builtin.CodeModel) void {
|
|
mod.red_zone = false;
|
|
mod.stack_check = false;
|
|
mod.code_model = code_model;
|
|
mod.sanitize_thread = false;
|
|
mod.single_threaded = true;
|
|
mod.strip = true;
|
|
}
|
|
|
|
pub fn build(b: *std.Build) void {
|
|
// =========================================================
|
|
// Target Selection: -Darch=riscv64|aarch64|x86_64
|
|
// =========================================================
|
|
|
|
const arch = b.option(Arch, "arch", "Target architecture (default: riscv64)") orelse .riscv64;
|
|
const config = archConfig(arch);
|
|
|
|
var query: std.Target.Query = .{
|
|
.cpu_arch = config.cpu_arch,
|
|
.os_tag = .freestanding,
|
|
.abi = .none,
|
|
};
|
|
// Disable NEON/FP in kernel code generation to avoid unaligned SIMD stores
|
|
if (config.cpu_arch == .aarch64) {
|
|
query.cpu_features_sub = std.Target.aarch64.featureSet(&.{.neon});
|
|
}
|
|
const target = b.resolveTargetQuery(query);
|
|
|
|
// HEPHAESTUS DECREE: Force ReleaseSmall. A kernel runs naked.
|
|
const optimize: std.builtin.OptimizeMode = .ReleaseSmall;
|
|
|
|
// =========================================================
|
|
// Step: Nim Kernel Compilation (nim -> C -> .o)
|
|
// =========================================================
|
|
// Calls build_nim.sh which handles:
|
|
// 1. nim c --compileOnly -> generates C
|
|
// 2. zig cc -> cross-compiles C to .o
|
|
// Incremental: only recompiles changed files.
|
|
|
|
const nim_step = b.addSystemCommand(&.{ "./build_nim.sh", config.nim_arch });
|
|
const nim_build_step = b.step("nim", "Recompile Nim kernel sources (nim -> C -> .o)");
|
|
nim_build_step.dependOn(&nim_step.step);
|
|
|
|
// =========================================================
|
|
// L0: Hardware Abstraction Layer (Zig)
|
|
// =========================================================
|
|
|
|
const hal_mod = b.createModule(.{
|
|
.root_source_file = b.path("hal/abi.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
applyKernelSettings(hal_mod, config.code_model);
|
|
|
|
const hal = b.addLibrary(.{
|
|
.name = "rumpk_hal",
|
|
.root_module = hal_mod,
|
|
.linkage = .static,
|
|
});
|
|
|
|
b.installArtifact(hal);
|
|
|
|
// =========================================================
|
|
// Boot: Entry Point (Assembly + Zig)
|
|
// =========================================================
|
|
|
|
const boot_mod = b.createModule(.{
|
|
.root_source_file = b.path("boot/header.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
applyKernelSettings(boot_mod, config.code_model);
|
|
|
|
const boot = b.addObject(.{
|
|
.name = "boot",
|
|
.root_module = boot_mod,
|
|
});
|
|
|
|
// =========================================================
|
|
// LwIP: Lightweight TCP/IP Stack
|
|
// =========================================================
|
|
// Compiled as C objects with freestanding flags.
|
|
// ubsan disabled per-file to avoid runtime dependency.
|
|
|
|
const lwip_base = "libs/membrane/external/lwip/src/";
|
|
const lwip_srcs = [_][]const u8{
|
|
// Core
|
|
lwip_base ++ "core/init.c",
|
|
lwip_base ++ "core/def.c",
|
|
lwip_base ++ "core/dns.c",
|
|
lwip_base ++ "core/inet_chksum.c",
|
|
lwip_base ++ "core/ip.c",
|
|
lwip_base ++ "core/mem.c",
|
|
lwip_base ++ "core/memp.c",
|
|
lwip_base ++ "core/netif.c",
|
|
lwip_base ++ "core/pbuf.c",
|
|
lwip_base ++ "core/raw.c",
|
|
lwip_base ++ "core/sys.c",
|
|
lwip_base ++ "core/tcp.c",
|
|
lwip_base ++ "core/tcp_in.c",
|
|
lwip_base ++ "core/tcp_out.c",
|
|
lwip_base ++ "core/timeouts.c",
|
|
lwip_base ++ "core/udp.c",
|
|
// IPv4
|
|
lwip_base ++ "core/ipv4/autoip.c",
|
|
lwip_base ++ "core/ipv4/dhcp.c",
|
|
lwip_base ++ "core/ipv4/etharp.c",
|
|
lwip_base ++ "core/ipv4/icmp.c",
|
|
lwip_base ++ "core/ipv4/ip4.c",
|
|
lwip_base ++ "core/ipv4/ip4_addr.c",
|
|
lwip_base ++ "core/ipv4/ip4_frag.c",
|
|
// Netif
|
|
lwip_base ++ "netif/ethernet.c",
|
|
// SysArch (Rumpk integration)
|
|
"libs/membrane/sys_arch.c",
|
|
};
|
|
|
|
const lwip_flags: []const []const u8 = switch (arch) {
|
|
.riscv64 => &.{
|
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
"-DNO_SYS=1", "-mcmodel=medany", "-Icore",
|
|
"-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
|
},
|
|
.aarch64 => &.{
|
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
"-DNO_SYS=1", "-Icore", "-Ilibs/membrane",
|
|
"-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
|
},
|
|
.x86_64 => &.{
|
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
"-DNO_SYS=1", "-mcmodel=kernel", "-Icore",
|
|
"-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
|
},
|
|
};
|
|
|
|
const liblwip = b.addLibrary(.{
|
|
.name = "lwip",
|
|
.root_module = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
}),
|
|
.linkage = .static,
|
|
});
|
|
liblwip.root_module.sanitize_thread = false;
|
|
liblwip.root_module.red_zone = false;
|
|
liblwip.root_module.single_threaded = true;
|
|
|
|
for (lwip_srcs) |src| {
|
|
liblwip.addCSourceFile(.{
|
|
.file = b.path(src),
|
|
.flags = lwip_flags,
|
|
});
|
|
}
|
|
|
|
b.installArtifact(liblwip);
|
|
|
|
// =========================================================
|
|
// LittleFS: Sovereign Filesystem (Persistent Storage)
|
|
// =========================================================
|
|
// Compiled as C object with freestanding flags.
|
|
// Uses LFS_CONFIG=lfs_rumpk.h to replace lfs_util.h entirely,
|
|
// avoiding stdio/assert deps. malloc/free provided by clib.c.
|
|
|
|
const lfs_flags: []const []const u8 = switch (arch) {
|
|
.riscv64 => &.{
|
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=medany",
|
|
},
|
|
.aarch64 => &.{
|
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs",
|
|
},
|
|
.x86_64 => &.{
|
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=kernel",
|
|
},
|
|
};
|
|
|
|
const lfs_obj = b.addObject(.{
|
|
.name = "littlefs",
|
|
.root_module = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
lfs_obj.root_module.sanitize_thread = false;
|
|
lfs_obj.root_module.red_zone = false;
|
|
lfs_obj.root_module.single_threaded = true;
|
|
|
|
lfs_obj.addCSourceFile(.{
|
|
.file = b.path("vendor/littlefs/lfs.c"),
|
|
.flags = lfs_flags,
|
|
});
|
|
|
|
// =========================================================
|
|
// Dependencies (CLib, LibC Shim, LWF Adapter, Switch)
|
|
// =========================================================
|
|
|
|
// 1. CLib (Minimal C stdlib)
|
|
const clib = b.addObject(.{
|
|
.name = "clib",
|
|
.root_module = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
clib.addCSourceFile(.{
|
|
.file = b.path("libs/membrane/clib.c"),
|
|
.flags = switch (arch) {
|
|
.riscv64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=medany" },
|
|
.aarch64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1" },
|
|
.x86_64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=kernel" },
|
|
},
|
|
});
|
|
|
|
// 2. LibC Shim (Zig)
|
|
const libc_shim_mod = b.createModule(.{
|
|
.root_source_file = b.path("libs/membrane/libc_shim.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
applyKernelSettings(libc_shim_mod, config.code_model);
|
|
|
|
const libc_shim = b.addObject(.{
|
|
.name = "libc_shim",
|
|
.root_module = libc_shim_mod,
|
|
});
|
|
|
|
// 3. LittleFS HAL (VirtIO-Block ↔ LittleFS glue)
|
|
const lfs_hal_mod = b.createModule(.{
|
|
.root_source_file = b.path("hal/littlefs_hal.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
applyKernelSettings(lfs_hal_mod, config.code_model);
|
|
|
|
const lfs_hal = b.addObject(.{
|
|
.name = "lfs_hal",
|
|
.root_module = lfs_hal_mod,
|
|
});
|
|
|
|
// 4. LWF Adapter (Project LibWeb — Libertaria Wire Frame)
|
|
const lwf_adapter_mod = b.createModule(.{
|
|
.root_source_file = b.path("libs/libertaria/lwf_adapter.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
applyKernelSettings(lwf_adapter_mod, config.code_model);
|
|
|
|
const lwf_adapter = b.addObject(.{
|
|
.name = "lwf_adapter",
|
|
.root_module = lwf_adapter_mod,
|
|
});
|
|
|
|
// 4. Context Switch (Architecture-specific Assembly)
|
|
const switch_obj = b.addObject(.{
|
|
.name = "switch",
|
|
.root_module = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
switch_obj.addAssemblyFile(b.path(config.switch_asm));
|
|
|
|
// =========================================================
|
|
// Final Link: rumpk.elf
|
|
// =========================================================
|
|
|
|
const kernel_mod = b.createModule(.{
|
|
.root_source_file = b.path("hal/abi.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
applyKernelSettings(kernel_mod, config.code_model);
|
|
|
|
const kernel = b.addExecutable(.{
|
|
.name = "rumpk.elf",
|
|
.root_module = kernel_mod,
|
|
});
|
|
|
|
kernel.setLinkerScript(b.path(config.linker_script));
|
|
kernel.addObject(boot);
|
|
kernel.addObject(clib);
|
|
kernel.addObject(libc_shim);
|
|
kernel.addObject(lfs_hal);
|
|
kernel.addObject(lwf_adapter);
|
|
kernel.addObject(switch_obj);
|
|
|
|
// LwIP linked into kernel — Membrane/NetSwitch drives DHCP/TCP/ICMP.
|
|
kernel.linkLibrary(liblwip);
|
|
|
|
// LittleFS IS linked into kernel — provides /nexus persistent storage.
|
|
kernel.addObject(lfs_obj);
|
|
|
|
// Nim-generated objects from build/nimcache/
|
|
{
|
|
var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
|
|
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
|
return;
|
|
};
|
|
defer nimcache_dir.close();
|
|
var it = nimcache_dir.iterate();
|
|
while (it.next() catch null) |entry| {
|
|
if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".o")) {
|
|
const path = b.fmt("build/nimcache/{s}", .{entry.name});
|
|
kernel.addObjectFile(b.path(path));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Embedded InitRD — assemble for the target architecture
|
|
const initrd_obj = b.addObject(.{
|
|
.name = "initrd",
|
|
.root_module = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
initrd_obj.addAssemblyFile(b.path("build/embed_initrd.S"));
|
|
kernel.addObject(initrd_obj);
|
|
|
|
b.installArtifact(kernel);
|
|
|
|
// Make default install depend on Nim compilation
|
|
kernel.step.dependOn(&nim_step.step);
|
|
|
|
// =========================================================
|
|
// Tests (always run on native host)
|
|
// =========================================================
|
|
|
|
// HAL unit tests
|
|
const hal_test_mod = b.createModule(.{
|
|
.root_source_file = b.path("hal/abi.zig"),
|
|
.target = b.graph.host,
|
|
});
|
|
|
|
const hal_tests = b.addTest(.{
|
|
.root_module = hal_test_mod,
|
|
});
|
|
|
|
const run_hal_tests = b.addRunArtifact(hal_tests);
|
|
const test_step = b.step("test", "Run Rumpk HAL tests (native host)");
|
|
test_step.dependOn(&run_hal_tests.step);
|
|
|
|
// LWF Adapter + Membrane tests (Project LibWeb)
|
|
const lwf_test_mod = b.createModule(.{
|
|
.root_source_file = b.path("libs/libertaria/lwf_adapter.zig"),
|
|
.target = b.graph.host,
|
|
});
|
|
|
|
const lwf_tests = b.addTest(.{
|
|
.root_module = lwf_test_mod,
|
|
});
|
|
|
|
const run_lwf_tests = b.addRunArtifact(lwf_tests);
|
|
const lwf_test_step = b.step("test-lwf", "Run LWF Adapter tests (Project LibWeb)");
|
|
lwf_test_step.dependOn(&run_lwf_tests.step);
|
|
|
|
const lwf_membrane_mod = b.createModule(.{
|
|
.root_source_file = b.path("libs/libertaria/lwf_membrane.zig"),
|
|
.target = b.graph.host,
|
|
});
|
|
|
|
const lwf_membrane_tests = b.addTest(.{
|
|
.root_module = lwf_membrane_mod,
|
|
});
|
|
|
|
const run_lwf_membrane_tests = b.addRunArtifact(lwf_membrane_tests);
|
|
lwf_test_step.dependOn(&run_lwf_membrane_tests.step);
|
|
}
|