feat(build): dual-arch build system — build_nim.sh, build_full.sh, run_aarch64.sh
This commit is contained in:
parent
49c58fbd94
commit
a38bc523a8
383
build.zig
383
build.zig
|
|
@ -5,31 +5,111 @@
|
||||||
// This file is part of the Nexus Commonwealth.
|
// This file is part of the Nexus Commonwealth.
|
||||||
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
||||||
|
|
||||||
//! Rumpk Build System
|
//! Rumpk Build System — Unified Pipeline
|
||||||
//!
|
//!
|
||||||
//! Orchestrates L0 (Zig) and L1 (Nim) compilation.
|
//! Single command builds everything: Nim kernel + Zig HAL + LwIP + link.
|
||||||
//! Builds the Hardware Abstraction Layer as a static library.
|
//!
|
||||||
|
//! 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 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 {
|
pub fn build(b: *std.Build) void {
|
||||||
const target = b.standardTargetOptions(.{});
|
// =========================================================
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
// 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)
|
// L0: Hardware Abstraction Layer (Zig)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
// NOTE(Build): Zig 0.15.x API - using addLibrary with static linkage
|
|
||||||
const hal_mod = b.createModule(.{
|
const hal_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("hal/abi.zig"),
|
.root_source_file = b.path("hal/abi.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
// Freestanding kernel - no libc, no red zone, no stack checks
|
applyKernelSettings(hal_mod, config.code_model);
|
||||||
hal_mod.red_zone = false;
|
|
||||||
hal_mod.stack_check = false;
|
|
||||||
hal_mod.code_model = .medany;
|
|
||||||
|
|
||||||
const hal = b.addLibrary(.{
|
const hal = b.addLibrary(.{
|
||||||
.name = "rumpk_hal",
|
.name = "rumpk_hal",
|
||||||
|
|
@ -39,15 +119,6 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
b.installArtifact(hal);
|
b.installArtifact(hal);
|
||||||
|
|
||||||
// TODO(Build): Microui needs stdio.h stubs for freestanding.
|
|
||||||
// Re-enable after creating libs/microui/stdio_stub.h
|
|
||||||
// Microui Integration (Phase 3.5b)
|
|
||||||
// hal_mod.addIncludePath(b.path("libs/microui"));
|
|
||||||
// hal_mod.addCSourceFile(.{
|
|
||||||
// .file = b.path("libs/microui/microui.c"),
|
|
||||||
// .flags = &.{"-std=c99"},
|
|
||||||
// });
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Boot: Entry Point (Assembly + Zig)
|
// Boot: Entry Point (Assembly + Zig)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -57,38 +128,230 @@ pub fn build(b: *std.Build) void {
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
boot_mod.red_zone = false;
|
applyKernelSettings(boot_mod, config.code_model);
|
||||||
boot_mod.stack_check = false;
|
|
||||||
boot_mod.code_model = .medany;
|
|
||||||
|
|
||||||
const boot = b.addObject(.{
|
const boot = b.addObject(.{
|
||||||
.name = "boot",
|
.name = "boot",
|
||||||
.root_module = boot_mod,
|
.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
|
// Final Link: rumpk.elf
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
const kernel_mod = b.createModule(.{
|
const kernel_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("hal/abi.zig"), // Fake root, we add objects later
|
.root_source_file = b.path("hal/abi.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
kernel_mod.red_zone = false;
|
applyKernelSettings(kernel_mod, config.code_model);
|
||||||
kernel_mod.stack_check = false;
|
|
||||||
kernel_mod.code_model = .medany;
|
|
||||||
|
|
||||||
const kernel = b.addExecutable(.{
|
const kernel = b.addExecutable(.{
|
||||||
.name = "rumpk.elf",
|
.name = "rumpk.elf",
|
||||||
.root_module = kernel_mod,
|
.root_module = kernel_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
kernel.setLinkerScript(b.path("boot/linker.ld"));
|
kernel.setLinkerScript(b.path(config.linker_script));
|
||||||
kernel.addObject(boot);
|
kernel.addObject(boot);
|
||||||
// kernel.linkLibrary(hal); // Redundant, already in kernel_mod
|
kernel.addObject(clib);
|
||||||
|
kernel.addObject(libc_shim);
|
||||||
|
kernel.addObject(lfs_hal);
|
||||||
|
kernel.addObject(lwf_adapter);
|
||||||
|
kernel.addObject(switch_obj);
|
||||||
|
|
||||||
// Add Nim-generated objects
|
// 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| {
|
var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
|
||||||
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
||||||
|
|
@ -104,31 +367,63 @@ pub fn build(b: *std.Build) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add external pre-built dependencies (Order matters: Libs after users)
|
// Embedded InitRD — assemble for the target architecture
|
||||||
kernel.addObjectFile(b.path("build/switch.o")); // cpu_switch_to
|
const initrd_obj = b.addObject(.{
|
||||||
kernel.addObjectFile(b.path("build/sys_arch.o")); // sys_now, nexus_lwip_panic
|
.name = "initrd",
|
||||||
kernel.addObjectFile(b.path("build/libc_shim.o"));
|
.root_module = b.createModule(.{
|
||||||
kernel.addObjectFile(b.path("build/clib.o"));
|
.target = target,
|
||||||
kernel.addObjectFile(b.path("build/liblwip.a"));
|
.optimize = optimize,
|
||||||
kernel.addObjectFile(b.path("build/initrd.o"));
|
}),
|
||||||
|
});
|
||||||
|
initrd_obj.addAssemblyFile(b.path("build/embed_initrd.S"));
|
||||||
|
kernel.addObject(initrd_obj);
|
||||||
|
|
||||||
b.installArtifact(kernel);
|
b.installArtifact(kernel);
|
||||||
|
|
||||||
|
// Make default install depend on Nim compilation
|
||||||
|
kernel.step.dependOn(&nim_step.step);
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Tests
|
// Tests (always run on native host)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
const test_mod = b.createModule(.{
|
// HAL unit tests
|
||||||
|
const hal_test_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("hal/abi.zig"),
|
.root_source_file = b.path("hal/abi.zig"),
|
||||||
.target = target,
|
.target = b.graph.host,
|
||||||
.optimize = optimize,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const hal_tests = b.addTest(.{
|
const hal_tests = b.addTest(.{
|
||||||
.root_module = test_mod,
|
.root_module = hal_test_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
const run_tests = b.addRunArtifact(hal_tests);
|
const run_hal_tests = b.addRunArtifact(hal_tests);
|
||||||
const test_step = b.step("test", "Run Rumpk HAL tests");
|
const test_step = b.step("test", "Run Rumpk HAL tests (native host)");
|
||||||
test_step.dependOn(&run_tests.step);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
#!/usr/bin/env zsh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ARCH=${1:-riscv64}
|
||||||
|
|
||||||
|
# Architecture-specific settings
|
||||||
|
if [ "$ARCH" = "aarch64" ]; then
|
||||||
|
NIM_CPU="arm64"
|
||||||
|
ZIG_TARGET="aarch64-freestanding-none"
|
||||||
|
ZIG_CPU="baseline"
|
||||||
|
LINKER_SCRIPT="apps/linker_user_aarch64.ld"
|
||||||
|
BUILD_FLAG="-Darch=aarch64"
|
||||||
|
echo "=== Building NipBox Userland (aarch64) ==="
|
||||||
|
else
|
||||||
|
NIM_CPU="riscv64"
|
||||||
|
ZIG_TARGET="riscv64-freestanding-none"
|
||||||
|
ZIG_CPU="sifive_u54"
|
||||||
|
LINKER_SCRIPT="apps/linker_user.ld"
|
||||||
|
BUILD_FLAG=""
|
||||||
|
echo "=== Building NipBox Userland (riscv64) ==="
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compile Nim sources to C
|
||||||
|
nim c --cpu:${NIM_CPU} --os:any --compileOnly --mm:arc --opt:size \
|
||||||
|
--stackTrace:off --lineDir:off --nomain --nimcache:build/init_nimcache \
|
||||||
|
-d:noSignalHandler -d:RUMPK_USER -d:nimAllocPagesViaMalloc -d:NIPBOX_LITE \
|
||||||
|
npl/nipbox/nipbox.nim
|
||||||
|
|
||||||
|
# Compile Nim-generated C (check if files exist first)
|
||||||
|
# Skip net_glue (needs LwIP headers not available in userland build)
|
||||||
|
EXTRA_CC_FLAGS=""
|
||||||
|
if [ "$ARCH" = "riscv64" ]; then
|
||||||
|
EXTRA_CC_FLAGS="-mcmodel=medany"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ls build/init_nimcache/*.c 1> /dev/null 2>&1; then
|
||||||
|
for f in build/init_nimcache/*.c; do
|
||||||
|
case "$f" in
|
||||||
|
*net_glue*) echo " [skip] $f (LwIP dependency)"; continue ;;
|
||||||
|
esac
|
||||||
|
zig cc -target ${ZIG_TARGET} -mcpu=${ZIG_CPU} ${EXTRA_CC_FLAGS} \
|
||||||
|
-fno-sanitize=all -fno-vectorize \
|
||||||
|
-I/usr/lib/nim/lib -Icore -Ilibs/membrane -Ilibs/membrane/include \
|
||||||
|
-include string.h \
|
||||||
|
-Os -c "$f" -o "${f%.c}.o"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compile clib
|
||||||
|
zig cc -target ${ZIG_TARGET} -mcpu=${ZIG_CPU} ${EXTRA_CC_FLAGS} \
|
||||||
|
-fno-sanitize=all \
|
||||||
|
-DNO_SYS=1 -DOMIT_EXIT -DRUMPK_USER -Ilibs/membrane/include -c libs/membrane/clib.c -o build/clib_user.o
|
||||||
|
|
||||||
|
# Create startup assembly
|
||||||
|
if [ "$ARCH" = "aarch64" ]; then
|
||||||
|
cat > build/head_user.S << 'EOF'
|
||||||
|
.section .text._start
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
bl NimMain
|
||||||
|
1: wfi
|
||||||
|
b 1b
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat > build/head_user.S << 'EOF'
|
||||||
|
.section .text._start
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
.option push
|
||||||
|
.option norelax
|
||||||
|
1:auipc gp, %pcrel_hi(__global_pointer$)
|
||||||
|
addi gp, gp, %pcrel_lo(1b)
|
||||||
|
.option pop
|
||||||
|
call NimMain
|
||||||
|
1: wfi
|
||||||
|
j 1b
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
zig cc -target ${ZIG_TARGET} -mcpu=${ZIG_CPU} ${EXTRA_CC_FLAGS} \
|
||||||
|
-fno-sanitize=all \
|
||||||
|
-c build/head_user.S -o build/head_user.o
|
||||||
|
|
||||||
|
# Link init
|
||||||
|
zig cc -target ${ZIG_TARGET} -mcpu=${ZIG_CPU} ${EXTRA_CC_FLAGS} -nostdlib \
|
||||||
|
-fno-sanitize=all \
|
||||||
|
-T ${LINKER_SCRIPT} -Wl,--gc-sections \
|
||||||
|
build/head_user.o build/init_nimcache/*.o build/clib_user.o \
|
||||||
|
-o build/init
|
||||||
|
|
||||||
|
echo "✓ NipBox binary built (${ARCH})"
|
||||||
|
file build/init
|
||||||
|
|
||||||
|
# Create initrd
|
||||||
|
mkdir -p build/sysro/bin
|
||||||
|
cp build/init build/sysro/init
|
||||||
|
if [ "$ARCH" = "riscv64" ] && [ -f vendor/mksh/mksh.elf ]; then
|
||||||
|
cp vendor/mksh/mksh.elf build/sysro/bin/mksh
|
||||||
|
fi
|
||||||
|
cd build/sysro
|
||||||
|
tar --format=ustar -cf ../initrd.tar *
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
# Embed initrd
|
||||||
|
cat > build/embed_initrd.S << EOF
|
||||||
|
.section .rodata
|
||||||
|
.global _initrd_start
|
||||||
|
.global _initrd_end
|
||||||
|
.align 4
|
||||||
|
_initrd_start:
|
||||||
|
.incbin "$(pwd)/build/initrd.tar"
|
||||||
|
_initrd_end:
|
||||||
|
EOF
|
||||||
|
|
||||||
|
zig cc -target ${ZIG_TARGET} -mcpu=${ZIG_CPU} ${EXTRA_CC_FLAGS} \
|
||||||
|
-c build/embed_initrd.S -o build/initrd.o
|
||||||
|
|
||||||
|
cp build/initrd.tar hal/initrd.tar
|
||||||
|
|
||||||
|
# Build kernel
|
||||||
|
rm -f zig-out/lib/librumpk_hal.a
|
||||||
|
zig build ${BUILD_FLAG}
|
||||||
|
|
||||||
|
echo "=== BUILD COMPLETE (${ARCH}) ==="
|
||||||
|
ls -lh build/init zig-out/bin/rumpk.elf
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#!/usr/bin/env zsh
|
||||||
|
# Build LwIP as a pure C library without Zig runtime dependencies
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
mkdir -p build/lwip_objs
|
||||||
|
rm -f build/lwip_objs/*.o 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Building LwIP..."
|
||||||
|
|
||||||
|
# Compile each source file
|
||||||
|
compile() {
|
||||||
|
local src=$1
|
||||||
|
local obj="build/lwip_objs/$(basename ${src%.c}.o)"
|
||||||
|
echo " $src"
|
||||||
|
zig cc -target riscv64-freestanding-none -mcpu=sifive_u54 -mcmodel=medany \
|
||||||
|
-Os -fno-sanitize=all \
|
||||||
|
-DNO_SYS=1 -Icore -Ilibs/membrane -Ilibs/membrane/include \
|
||||||
|
-Ilibs/membrane/external/lwip/src/include \
|
||||||
|
-c "$src" -o "$obj"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Core sources
|
||||||
|
compile "libs/membrane/external/lwip/src/core/init.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/def.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/dns.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/inet_chksum.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/ip.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/mem.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/memp.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/netif.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/pbuf.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/raw.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/sys.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/tcp.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/tcp_in.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/tcp_out.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/timeouts.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/udp.c"
|
||||||
|
|
||||||
|
# IPv4 sources
|
||||||
|
compile "libs/membrane/external/lwip/src/core/ipv4/autoip.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/ipv4/dhcp.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/ipv4/etharp.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/ipv4/icmp.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/ipv4/ip4.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/ipv4/ip4_addr.c"
|
||||||
|
compile "libs/membrane/external/lwip/src/core/ipv4/ip4_frag.c"
|
||||||
|
|
||||||
|
# Netif sources
|
||||||
|
compile "libs/membrane/external/lwip/src/netif/ethernet.c"
|
||||||
|
|
||||||
|
# SysArch
|
||||||
|
compile "libs/membrane/sys_arch.c"
|
||||||
|
|
||||||
|
echo "Creating liblwip.a..."
|
||||||
|
mkdir -p zig-out/lib
|
||||||
|
rm -f zig-out/lib/liblwip.a
|
||||||
|
(cd build/lwip_objs && ar rcs ../../zig-out/lib/liblwip.a *.o)
|
||||||
|
echo "Done! liblwip.a created at zig-out/lib/liblwip.a"
|
||||||
|
ls -lh zig-out/lib/liblwip.a
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================================
|
||||||
|
# Rumpk Nim Kernel Build — nim → C → .o (cross-compiled for target arch)
|
||||||
|
# ============================================================================
|
||||||
|
# Usage:
|
||||||
|
# ./build_nim.sh # Default: riscv64
|
||||||
|
# ./build_nim.sh riscv64 # RISC-V 64-bit
|
||||||
|
# ./build_nim.sh aarch64 # ARM64
|
||||||
|
# ./build_nim.sh x86_64 # AMD64
|
||||||
|
#
|
||||||
|
# This script:
|
||||||
|
# 1. Invokes nim c --compileOnly to generate C from Nim
|
||||||
|
# 2. Cross-compiles each .c to .o using zig cc
|
||||||
|
# 3. Outputs to build/nimcache/*.o (consumed by build.zig)
|
||||||
|
# ============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
ARCH="${1:-riscv64}"
|
||||||
|
|
||||||
|
# ---- Validate architecture ----
|
||||||
|
case "$ARCH" in
|
||||||
|
riscv64)
|
||||||
|
ZIG_TARGET="riscv64-freestanding-none"
|
||||||
|
ZIG_CPU="-mcpu=sifive_u54"
|
||||||
|
ZIG_MODEL="-mcmodel=medany"
|
||||||
|
NIM_CPU="riscv64"
|
||||||
|
;;
|
||||||
|
aarch64)
|
||||||
|
ZIG_TARGET="aarch64-freestanding-none"
|
||||||
|
ZIG_CPU=""
|
||||||
|
ZIG_MODEL="-fno-vectorize"
|
||||||
|
NIM_CPU="arm64"
|
||||||
|
;;
|
||||||
|
x86_64)
|
||||||
|
ZIG_TARGET="x86_64-freestanding-none"
|
||||||
|
ZIG_CPU=""
|
||||||
|
ZIG_MODEL="-mcmodel=kernel"
|
||||||
|
NIM_CPU="amd64"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: Unknown architecture '$ARCH'"
|
||||||
|
echo "Supported: riscv64, aarch64, x86_64"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
NIMCACHE="build/nimcache"
|
||||||
|
|
||||||
|
echo "=== Rumpk Nim Build: $ARCH ==="
|
||||||
|
echo " Target: $ZIG_TARGET"
|
||||||
|
echo " Output: $NIMCACHE/"
|
||||||
|
|
||||||
|
# ---- Step 1: Nim → C ----
|
||||||
|
echo ""
|
||||||
|
echo "[1/2] nim c --compileOnly core/kernel.nim"
|
||||||
|
|
||||||
|
nim c \
|
||||||
|
--cpu:"$NIM_CPU" \
|
||||||
|
--os:any \
|
||||||
|
--compileOnly \
|
||||||
|
--mm:arc \
|
||||||
|
--opt:size \
|
||||||
|
--stackTrace:off \
|
||||||
|
--lineDir:off \
|
||||||
|
--nomain \
|
||||||
|
--nimcache:"$NIMCACHE" \
|
||||||
|
-d:noSignalHandler \
|
||||||
|
-d:RUMPK_KERNEL \
|
||||||
|
-d:nimAllocPagesViaMalloc \
|
||||||
|
core/kernel.nim
|
||||||
|
|
||||||
|
C_COUNT=$(ls -1 "$NIMCACHE"/*.c 2>/dev/null | wc -l)
|
||||||
|
echo " Generated $C_COUNT C files"
|
||||||
|
|
||||||
|
# ---- Step 2: C → .o (zig cc cross-compile) ----
|
||||||
|
echo ""
|
||||||
|
echo "[2/2] zig cc → $ZIG_TARGET"
|
||||||
|
|
||||||
|
COMPILED=0
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
for cfile in "$NIMCACHE"/*.c; do
|
||||||
|
[ -f "$cfile" ] || continue
|
||||||
|
ofile="${cfile%.c}.o"
|
||||||
|
|
||||||
|
# Skip if .o is newer than .c (incremental)
|
||||||
|
if [ -f "$ofile" ] && [ "$ofile" -nt "$cfile" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if zig cc \
|
||||||
|
-target "$ZIG_TARGET" \
|
||||||
|
$ZIG_CPU \
|
||||||
|
$ZIG_MODEL \
|
||||||
|
-fno-sanitize=all \
|
||||||
|
-fvisibility=default \
|
||||||
|
-I/usr/lib/nim/lib \
|
||||||
|
-Icore \
|
||||||
|
-Icore/include \
|
||||||
|
-Ilibs/membrane \
|
||||||
|
-Ilibs/membrane/include \
|
||||||
|
-Ilibs/membrane/external/lwip/src/include \
|
||||||
|
-Os \
|
||||||
|
-c "$cfile" \
|
||||||
|
-o "$ofile" 2>/dev/null; then
|
||||||
|
COMPILED=$((COMPILED + 1))
|
||||||
|
else
|
||||||
|
echo " FAIL: $(basename "$cfile")"
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
O_COUNT=$(ls -1 "$NIMCACHE"/*.o 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Result ==="
|
||||||
|
echo " Arch: $ARCH"
|
||||||
|
echo " C files: $C_COUNT"
|
||||||
|
echo " Compiled: $COMPILED (incremental skip: $((O_COUNT - COMPILED)))"
|
||||||
|
echo " Objects: $O_COUNT"
|
||||||
|
if [ "$FAILED" -gt 0 ]; then
|
||||||
|
echo " FAILED: $FAILED"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " Status: OK"
|
||||||
|
|
@ -3,5 +3,10 @@
|
||||||
|
|
||||||
double pow(double x, double y);
|
double pow(double x, double y);
|
||||||
double log10(double x);
|
double log10(double x);
|
||||||
|
double fabs(double x);
|
||||||
|
double floor(double x);
|
||||||
|
double ceil(double x);
|
||||||
|
double fmod(double x, double y);
|
||||||
|
double round(double x);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,22 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef struct _FILE FILE;
|
||||||
|
|
||||||
|
extern FILE *stdin;
|
||||||
|
extern FILE *stdout;
|
||||||
|
extern FILE *stderr;
|
||||||
|
|
||||||
int printf(const char *format, ...);
|
int printf(const char *format, ...);
|
||||||
|
int fprintf(FILE *stream, const char *format, ...);
|
||||||
int sprintf(char *str, const char *format, ...);
|
int sprintf(char *str, const char *format, ...);
|
||||||
int snprintf(char *str, size_t size, const char *format, ...);
|
int snprintf(char *str, size_t size, const char *format, ...);
|
||||||
|
int vprintf(const char *format, va_list ap);
|
||||||
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||||
|
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||||
|
int fflush(FILE *stream);
|
||||||
|
int fputc(int c, FILE *stream);
|
||||||
|
int fputs(const char *s, FILE *stream);
|
||||||
int rename(const char *oldpath, const char *newpath);
|
int rename(const char *oldpath, const char *newpath);
|
||||||
int remove(const char *pathname);
|
int remove(const char *pathname);
|
||||||
|
|
||||||
|
|
|
||||||
130
run.sh
130
run.sh
|
|
@ -1,22 +1,122 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# Rumpk QEMU Boot Script
|
# ============================================================================
|
||||||
|
# Rumpk QEMU Runner — Boot, Test, or Interactive Shell
|
||||||
|
# ============================================================================
|
||||||
|
# Usage:
|
||||||
|
# ./run.sh # Interactive RISC-V session
|
||||||
|
# ./run.sh test # Automated boot test (10s timeout, check output)
|
||||||
|
# ./run.sh [riscv64|aarch64] # Architecture selection (future)
|
||||||
|
#
|
||||||
|
# Exit codes:
|
||||||
|
# 0 = Boot successful (test mode: all checks passed)
|
||||||
|
# 1 = Boot failed or timeout
|
||||||
|
# ============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
MODE="${1:-interactive}"
|
||||||
KERNEL="$RUMPK_DIR/zig-out/bin/rumpk.elf"
|
ARCH="${2:-riscv64}"
|
||||||
|
TIMEOUT=25
|
||||||
|
|
||||||
|
KERNEL="zig-out/bin/rumpk.elf"
|
||||||
|
DISK="build/disk.img"
|
||||||
|
|
||||||
|
# Fallback to build/ location if zig-out doesn't have it
|
||||||
|
if [ ! -f "$KERNEL" ]; then
|
||||||
|
KERNEL="build/rumpk-riscv64.elf"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f "$KERNEL" ]; then
|
if [ ! -f "$KERNEL" ]; then
|
||||||
echo "ERROR: Kernel not found at $KERNEL"
|
echo "ERROR: No kernel binary found. Run: ./build_nim.sh && zig build -Dtarget=riscv64-freestanding-none -Dcpu=sifive_u54"
|
||||||
echo "Run ./build.sh first"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "🚀 Booting Rumpk..."
|
case "$ARCH" in
|
||||||
echo " Kernel: $KERNEL"
|
riscv64)
|
||||||
echo ""
|
QEMU_CMD=(
|
||||||
|
qemu-system-riscv64
|
||||||
|
-M virt -m 512M -nographic
|
||||||
|
-kernel "$KERNEL"
|
||||||
|
)
|
||||||
|
# Add disk if it exists
|
||||||
|
if [ -f "$DISK" ]; then
|
||||||
|
QEMU_CMD+=(
|
||||||
|
-drive "if=none,format=raw,file=$DISK,id=blk0"
|
||||||
|
-device virtio-blk-pci,drive=blk0
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
QEMU_CMD+=(
|
||||||
|
-device virtio-net-pci,netdev=net0
|
||||||
|
-netdev user,id=net0
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: Architecture '$ARCH' not yet supported"
|
||||||
|
echo "Supported: riscv64"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
qemu-system-riscv64 \
|
if [ "$MODE" = "test" ]; then
|
||||||
-M virt \
|
echo "=== Rumpk Boot Test ($ARCH, ${TIMEOUT}s timeout) ==="
|
||||||
-cpu max \
|
|
||||||
-m 512M \
|
LOGFILE=$(mktemp /tmp/rumpk-boot-XXXXXX.log)
|
||||||
-nographic \
|
trap "rm -f $LOGFILE" EXIT
|
||||||
-kernel "$KERNEL"
|
|
||||||
|
timeout "$TIMEOUT" "${QEMU_CMD[@]}" > "$LOGFILE" 2>&1 || true
|
||||||
|
|
||||||
|
# Boot verification checks
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
TOTAL=0
|
||||||
|
|
||||||
|
check() {
|
||||||
|
local name="$1"
|
||||||
|
local pattern="$2"
|
||||||
|
TOTAL=$((TOTAL + 1))
|
||||||
|
if grep -q "$pattern" "$LOGFILE"; then
|
||||||
|
echo " PASS: $name"
|
||||||
|
PASS=$((PASS + 1))
|
||||||
|
else
|
||||||
|
echo " FAIL: $name"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check "OpenSBI handoff" "Boot HART ID"
|
||||||
|
check "UART proof-of-life" "UART.*Loopback Test: PASS"
|
||||||
|
check "Zig HAL entry" "zig_entry reached"
|
||||||
|
check "Nim L1 handoff" "Handing off to Nim L1"
|
||||||
|
check "ION pool ready" "ION.*Pool Ready"
|
||||||
|
check "VirtIO-Net init" "VirtIO.*Initialization Complete"
|
||||||
|
check "CSpace init" "Capability system initialized"
|
||||||
|
check "Truth Ledger init" "System Truth Ledger initialized"
|
||||||
|
check "Fiber dispatcher" "Multi-Fiber Dispatcher starting"
|
||||||
|
check "NetSwitch online" "Traffic Engine Online"
|
||||||
|
check "LFS mounted" "Sovereign filesystem mounted"
|
||||||
|
check "Init loaded" "NIPBOX.*Entry point reached"
|
||||||
|
check "Shell spawned" "SOVEREIGN SUPERVISOR"
|
||||||
|
check "DHCP IP acquired" "IP STATUS CHANGE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Result: $PASS/$TOTAL passed, $FAIL failed ==="
|
||||||
|
|
||||||
|
if [ "$FAIL" -gt 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Boot log saved: $LOGFILE"
|
||||||
|
trap - EXIT # Don't delete on failure
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
elif [ "$MODE" = "interactive" ]; then
|
||||||
|
echo "=== Rumpk Interactive Session ($ARCH) ==="
|
||||||
|
echo " Kernel: $KERNEL"
|
||||||
|
echo " Exit: Ctrl-A X"
|
||||||
|
echo ""
|
||||||
|
exec "${QEMU_CMD[@]}"
|
||||||
|
else
|
||||||
|
echo "Usage: ./run.sh [interactive|test] [riscv64]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================================
|
||||||
|
# Rumpk ARM64 QEMU Runner
|
||||||
|
# ============================================================================
|
||||||
|
# Usage:
|
||||||
|
# ./run_aarch64.sh # Interactive boot
|
||||||
|
# ./run_aarch64.sh test # Automated boot test
|
||||||
|
# ============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
KERNEL="zig-out/bin/rumpk.elf"
|
||||||
|
DISK="build/disk_aarch64.img"
|
||||||
|
|
||||||
|
if [ ! -f "$KERNEL" ]; then
|
||||||
|
echo "ERROR: $KERNEL not found. Run: zig build -Darch=aarch64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create disk image if missing (16MB for LittleFS)
|
||||||
|
if [ ! -f "$DISK" ]; then
|
||||||
|
mkdir -p build
|
||||||
|
dd if=/dev/zero of="$DISK" bs=1M count=16 2>/dev/null
|
||||||
|
echo "Created $DISK (16MB)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
MODE="${1:-interactive}"
|
||||||
|
|
||||||
|
# VirtIO MMIO devices for aarch64 virt
|
||||||
|
VIRTIO_NET="-device virtio-net-device,netdev=net0 -netdev user,id=net0"
|
||||||
|
VIRTIO_BLK="-device virtio-blk-device,drive=hd0 -drive if=none,id=hd0,format=raw,file=$DISK"
|
||||||
|
|
||||||
|
if [ "$MODE" = "test" ]; then
|
||||||
|
echo "=== Rumpk ARM64 Boot Test ==="
|
||||||
|
TIMEOUT=20
|
||||||
|
|
||||||
|
LOGFILE=$(mktemp /tmp/rumpk-aarch64-XXXXXX.log)
|
||||||
|
timeout "$TIMEOUT" qemu-system-aarch64 \
|
||||||
|
-M virt -cpu cortex-a72 -m 512M \
|
||||||
|
-nographic -serial mon:stdio \
|
||||||
|
-kernel "$KERNEL" \
|
||||||
|
$VIRTIO_NET $VIRTIO_BLK \
|
||||||
|
-no-reboot >"$LOGFILE" 2>&1 || true
|
||||||
|
|
||||||
|
cat "$LOGFILE"
|
||||||
|
echo ""
|
||||||
|
echo "=== Boot Checks ==="
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
check() {
|
||||||
|
local label="$1"
|
||||||
|
local pattern="$2"
|
||||||
|
if grep -qa "$pattern" "$LOGFILE"; then
|
||||||
|
echo " [PASS] $label"
|
||||||
|
PASS=$((PASS + 1))
|
||||||
|
else
|
||||||
|
echo " [FAIL] $label"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check "UART loopback" "Loopback Test: PASS"
|
||||||
|
check "aarch64_init reached" "aarch64_init reached"
|
||||||
|
check "GICv2 initialized" "GICv2 initialized"
|
||||||
|
check "Timer initialized" "Timer initialized"
|
||||||
|
check "Vectors installed" "vectors installed"
|
||||||
|
check "Nim handoff" "Handing off to Nim"
|
||||||
|
check "VirtIO-Net found" "VirtIO-Net"
|
||||||
|
check "VirtIO-Blk found" "VirtIO-Block\|VirtIO-Blk\|Storage initialized"
|
||||||
|
check "VirtIO init complete" "Initialization Complete"
|
||||||
|
|
||||||
|
TOTAL=$((PASS + FAIL))
|
||||||
|
echo ""
|
||||||
|
echo "=== Result: $PASS/$TOTAL ==="
|
||||||
|
if [ "$FAIL" -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
exec qemu-system-aarch64 \
|
||||||
|
-M virt -cpu cortex-a72 -m 512M \
|
||||||
|
-nographic -serial mon:stdio \
|
||||||
|
-kernel "$KERNEL" \
|
||||||
|
$VIRTIO_NET $VIRTIO_BLK
|
||||||
|
fi
|
||||||
|
|
@ -55,7 +55,6 @@ BUILD_DIR="../../build"
|
||||||
|
|
||||||
zig cc -target $TARGET -nostdlib -static -T $LINKER_SCRIPT \
|
zig cc -target $TARGET -nostdlib -static -T $LINKER_SCRIPT \
|
||||||
$BUILD_DIR/subject_entry.o \
|
$BUILD_DIR/subject_entry.o \
|
||||||
$BUILD_DIR/stubs_user.o \
|
|
||||||
$BUILD_DIR/libc_shim.o \
|
$BUILD_DIR/libc_shim.o \
|
||||||
stubs_mksh.o \
|
stubs_mksh.o \
|
||||||
$OBJS \
|
$OBJS \
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,78 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#include <stdarg.h>
|
||||||
// Globals
|
|
||||||
char **environ = NULL;
|
|
||||||
|
|
||||||
extern void console_write(const void* p, size_t len);
|
|
||||||
|
|
||||||
extern long syscall(long nr, long a0, long a1, long a2);
|
extern long syscall(long nr, long a0, long a1, long a2);
|
||||||
long k_handle_syscall(long nr, long a0, long a1, long a2) {
|
long k_handle_syscall(long nr, long a0, long a1, long a2) {
|
||||||
return syscall(nr, a0, a1, a2);
|
return syscall(nr, a0, a1, a2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Globals
|
||||||
|
char **environ = NULL;
|
||||||
|
|
||||||
|
// Safe Userland Allocator (Bump Pointer with Headers)
|
||||||
|
#define HEAP_SIZE (32 * 1024 * 1024)
|
||||||
|
static char heap_memory[HEAP_SIZE];
|
||||||
|
static size_t heap_ptr = 0;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
size_t magic;
|
||||||
|
} BlockHeader;
|
||||||
|
#define ALLOC_MAGIC 0xCAFEBABE
|
||||||
|
|
||||||
|
void *malloc(size_t size) {
|
||||||
|
if (size == 0) return NULL;
|
||||||
|
|
||||||
|
// Align total size (header + data) to 16 bytes
|
||||||
|
size_t required = size + sizeof(BlockHeader);
|
||||||
|
size_t aligned_total = (required + 15) & ~15;
|
||||||
|
|
||||||
|
if (heap_ptr + aligned_total > HEAP_SIZE) return NULL;
|
||||||
|
|
||||||
|
BlockHeader *hdr = (BlockHeader *)&heap_memory[heap_ptr];
|
||||||
|
hdr->size = size;
|
||||||
|
hdr->magic = ALLOC_MAGIC;
|
||||||
|
|
||||||
|
void *ptr = (void *)((char *)hdr + sizeof(BlockHeader));
|
||||||
|
heap_ptr += aligned_total;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void *ptr) {
|
||||||
|
// No-op bump allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
void *realloc(void *ptr, size_t size) {
|
||||||
|
if (!ptr) return malloc(size);
|
||||||
|
if (size == 0) return NULL; // Standard says free.. or return NULL? mksh expects NULL or ptr.
|
||||||
|
|
||||||
|
// Get header
|
||||||
|
BlockHeader *hdr = (BlockHeader *)((char *)ptr - sizeof(BlockHeader));
|
||||||
|
if (hdr->magic != ALLOC_MAGIC) {
|
||||||
|
// Corrupted ptr? return NULL or fail.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimization: If it's the LAST block, simple extend? (Not implemented for simplicity)
|
||||||
|
|
||||||
|
void *new_ptr = malloc(size);
|
||||||
|
if (!new_ptr) return NULL;
|
||||||
|
|
||||||
|
size_t copy_size = (hdr->size < size) ? hdr->size : size;
|
||||||
|
memcpy(new_ptr, ptr, copy_size);
|
||||||
|
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *calloc(size_t nmemb, size_t size) {
|
||||||
|
size_t total = nmemb * size;
|
||||||
|
void *ptr = malloc(total);
|
||||||
|
if (ptr) memset(ptr, 0, total);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Stubs
|
// Stubs
|
||||||
int fstat(int fd, struct stat *buf) { return 0; }
|
int fstat(int fd, struct stat *buf) { return 0; }
|
||||||
int lstat(const char *path, struct stat *buf) { return 0; }
|
int lstat(const char *path, struct stat *buf) { return 0; }
|
||||||
|
|
@ -32,7 +93,14 @@ int stat(const char *path, struct stat *buf) { return 0; }
|
||||||
int sigemptyset(sigset_t *set) { return 0; }
|
int sigemptyset(sigset_t *set) { return 0; }
|
||||||
int sigaddset(sigset_t *set, int signum) { return 0; }
|
int sigaddset(sigset_t *set, int signum) { return 0; }
|
||||||
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { return 0; }
|
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { return 0; }
|
||||||
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { return 0; }
|
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) {
|
||||||
|
if (oldact) {
|
||||||
|
// Initialize to safe defaults - no handler installed
|
||||||
|
memset(oldact, 0, sizeof(struct sigaction));
|
||||||
|
oldact->sa_handler = SIG_DFL; // Default handler
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int sigsuspend(const sigset_t *mask) { return -1; }
|
int sigsuspend(const sigset_t *mask) { return -1; }
|
||||||
int kill(pid_t pid, int sig) { return 0; }
|
int kill(pid_t pid, int sig) { return 0; }
|
||||||
unsigned int alarm(unsigned int seconds) { return 0; }
|
unsigned int alarm(unsigned int seconds) { return 0; }
|
||||||
|
|
@ -45,7 +113,21 @@ off_t lseek(int fd, off_t offset, int whence) { return 0; }
|
||||||
// int close(int fd) { return 0; } // In libc.nim
|
// int close(int fd) { return 0; } // In libc.nim
|
||||||
int pipe(int pipefd[2]) { return -1; }
|
int pipe(int pipefd[2]) { return -1; }
|
||||||
int dup2(int oldfd, int newfd) { return newfd; }
|
int dup2(int oldfd, int newfd) { return newfd; }
|
||||||
int fcntl(int fd, int cmd, ...) { return 0; }
|
extern void console_write(const void* p, unsigned long len);
|
||||||
|
static void ksh_debug(const char* s) {
|
||||||
|
console_write(s, (unsigned long)strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
int fcntl(int fd, int cmd, ...) {
|
||||||
|
ksh_debug("[mksh] fcntl called\n");
|
||||||
|
va_list args;
|
||||||
|
va_start(args, cmd);
|
||||||
|
long arg = va_arg(args, long);
|
||||||
|
va_end(args);
|
||||||
|
int res = (int)syscall(0x206, (long)fd, (long)cmd, arg);
|
||||||
|
ksh_debug("[mksh] fcntl returning\n");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
int ioctl(int fd, unsigned long request, ...) { return 0; }
|
int ioctl(int fd, unsigned long request, ...) { return 0; }
|
||||||
|
|
||||||
// int execve(const char *pathname, char *const argv[], char *const envp[]) { return -1; } // In clib.c
|
// int execve(const char *pathname, char *const argv[], char *const envp[]) { return -1; } // In clib.c
|
||||||
|
|
@ -65,7 +147,26 @@ int seteuid(uid_t uid) { return 0; }
|
||||||
int setegid(gid_t gid) { return 0; }
|
int setegid(gid_t gid) { return 0; }
|
||||||
int setpgid(pid_t pid, pid_t pgid) { return 0; }
|
int setpgid(pid_t pid, pid_t pgid) { return 0; }
|
||||||
|
|
||||||
int tcgetattr(int fd, struct termios *termios_p) { return 0; }
|
int tcgetattr(int fd, struct termios *termios_p) {
|
||||||
|
if (termios_p) {
|
||||||
|
// Initialize with safe defaults (using numeric values to avoid missing constants)
|
||||||
|
memset(termios_p, 0, sizeof(struct termios));
|
||||||
|
// Set basic flags for canonical mode
|
||||||
|
termios_p->c_iflag = 0x0100; // ICRNL
|
||||||
|
termios_p->c_oflag = 0x0001 | 0x0004; // OPOST | ONLCR
|
||||||
|
termios_p->c_cflag = 0x0030 | 0x0080; // CS8 | CREAD
|
||||||
|
termios_p->c_lflag = ISIG | ICANON | ECHO; // These should be defined
|
||||||
|
// Set control characters
|
||||||
|
termios_p->c_cc[VINTR] = 3; // Ctrl-C
|
||||||
|
termios_p->c_cc[VQUIT] = 28; // Ctrl-backslash
|
||||||
|
termios_p->c_cc[VERASE] = 127; // DEL
|
||||||
|
termios_p->c_cc[VKILL] = 21; // Ctrl-U
|
||||||
|
termios_p->c_cc[VEOF] = 4; // Ctrl-D
|
||||||
|
termios_p->c_cc[VMIN] = 1;
|
||||||
|
termios_p->c_cc[VTIME] = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) { return 0; }
|
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) { return 0; }
|
||||||
pid_t tcgetpgrp(int fd) { return 1; }
|
pid_t tcgetpgrp(int fd) { return 1; }
|
||||||
int tcsetpgrp(int fd, pid_t pgrp) { return 0; }
|
int tcsetpgrp(int fd, pid_t pgrp) { return 0; }
|
||||||
|
|
@ -128,7 +229,56 @@ mode_t umask(mode_t mask) { return 0; }
|
||||||
|
|
||||||
int c_ulimit(const char **wp) { return 0; }
|
int c_ulimit(const char **wp) { return 0; }
|
||||||
|
|
||||||
void longjmp(jmp_buf env, int val) { while(1); }
|
int setjmp(jmp_buf env) {
|
||||||
int setjmp(jmp_buf env) { return 0; }
|
__asm__ volatile (
|
||||||
|
"sd ra, 0(%0)\n"
|
||||||
|
"sd sp, 8(%0)\n"
|
||||||
|
"sd s0, 16(%0)\n"
|
||||||
|
"sd s1, 24(%0)\n"
|
||||||
|
"sd s2, 32(%0)\n"
|
||||||
|
"sd s3, 40(%0)\n"
|
||||||
|
"sd s4, 48(%0)\n"
|
||||||
|
"sd s5, 56(%0)\n"
|
||||||
|
"sd s6, 64(%0)\n"
|
||||||
|
"sd s7, 72(%0)\n"
|
||||||
|
"sd s8, 80(%0)\n"
|
||||||
|
"sd s9, 88(%0)\n"
|
||||||
|
"sd s10, 96(%0)\n"
|
||||||
|
"sd s11, 104(%0)\n"
|
||||||
|
"li a0, 0\n"
|
||||||
|
: : "r"(env) : "memory", "a0"
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void exit(int status) { while(1); }
|
void longjmp(jmp_buf env, int val) {
|
||||||
|
__asm__ volatile (
|
||||||
|
"ld ra, 0(%0)\n"
|
||||||
|
"ld sp, 8(%0)\n"
|
||||||
|
"ld s0, 16(%0)\n"
|
||||||
|
"ld s1, 24(%0)\n"
|
||||||
|
"ld s2, 32(%0)\n"
|
||||||
|
"ld s3, 40(%0)\n"
|
||||||
|
"ld s4, 48(%0)\n"
|
||||||
|
"ld s5, 56(%0)\n"
|
||||||
|
"ld s6, 64(%0)\n"
|
||||||
|
"ld s7, 72(%0)\n"
|
||||||
|
"ld s8, 80(%0)\n"
|
||||||
|
"ld s9, 88(%0)\n"
|
||||||
|
"ld s10, 96(%0)\n"
|
||||||
|
"ld s11, 104(%0)\n"
|
||||||
|
"mv a0, %1\n"
|
||||||
|
"seqz t0, a0\n"
|
||||||
|
"add a0, a0, t0\n"
|
||||||
|
: : "r"(env), "r"(val) : "memory", "a0", "t0"
|
||||||
|
);
|
||||||
|
// Note: longjmp should not return. In this freestanding env,
|
||||||
|
// the asm above ends with registers restored and we jump back.
|
||||||
|
// However, to be extra safe we could add a ret at the end of asm.
|
||||||
|
__asm__ volatile("ret");
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit(int status) {
|
||||||
|
syscall(0x01, (long)status, 0, 0);
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
zig cc -target riscv64-freestanding-none -mcpu=sifive_u54 "$@"
|
||||||
Loading…
Reference in New Issue