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.
|
||||
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
||||
|
||||
//! Rumpk Build System
|
||||
//! Rumpk Build System — Unified Pipeline
|
||||
//!
|
||||
//! Orchestrates L0 (Zig) and L1 (Nim) compilation.
|
||||
//! Builds the Hardware Abstraction Layer as a static library.
|
||||
//! 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 {
|
||||
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)
|
||||
// =========================================================
|
||||
|
||||
// NOTE(Build): Zig 0.15.x API - using addLibrary with static linkage
|
||||
const hal_mod = b.createModule(.{
|
||||
.root_source_file = b.path("hal/abi.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
// Freestanding kernel - no libc, no red zone, no stack checks
|
||||
hal_mod.red_zone = false;
|
||||
hal_mod.stack_check = false;
|
||||
hal_mod.code_model = .medany;
|
||||
applyKernelSettings(hal_mod, config.code_model);
|
||||
|
||||
const hal = b.addLibrary(.{
|
||||
.name = "rumpk_hal",
|
||||
|
|
@ -39,15 +119,6 @@ pub fn build(b: *std.Build) void {
|
|||
|
||||
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)
|
||||
// =========================================================
|
||||
|
|
@ -57,38 +128,230 @@ pub fn build(b: *std.Build) void {
|
|||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
boot_mod.red_zone = false;
|
||||
boot_mod.stack_check = false;
|
||||
boot_mod.code_model = .medany;
|
||||
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"), // Fake root, we add objects later
|
||||
.root_source_file = b.path("hal/abi.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
kernel_mod.red_zone = false;
|
||||
kernel_mod.stack_check = false;
|
||||
kernel_mod.code_model = .medany;
|
||||
applyKernelSettings(kernel_mod, config.code_model);
|
||||
|
||||
const kernel = b.addExecutable(.{
|
||||
.name = "rumpk.elf",
|
||||
.root_module = kernel_mod,
|
||||
});
|
||||
|
||||
kernel.setLinkerScript(b.path("boot/linker.ld"));
|
||||
kernel.setLinkerScript(b.path(config.linker_script));
|
||||
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| {
|
||||
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)
|
||||
kernel.addObjectFile(b.path("build/switch.o")); // cpu_switch_to
|
||||
kernel.addObjectFile(b.path("build/sys_arch.o")); // sys_now, nexus_lwip_panic
|
||||
kernel.addObjectFile(b.path("build/libc_shim.o"));
|
||||
kernel.addObjectFile(b.path("build/clib.o"));
|
||||
kernel.addObjectFile(b.path("build/liblwip.a"));
|
||||
kernel.addObjectFile(b.path("build/initrd.o"));
|
||||
// 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
|
||||
// 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"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.target = b.graph.host,
|
||||
});
|
||||
|
||||
const hal_tests = b.addTest(.{
|
||||
.root_module = test_mod,
|
||||
.root_module = hal_test_mod,
|
||||
});
|
||||
|
||||
const run_tests = b.addRunArtifact(hal_tests);
|
||||
const test_step = b.step("test", "Run Rumpk HAL tests");
|
||||
test_step.dependOn(&run_tests.step);
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 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
|
||||
|
|
|
|||
|
|
@ -4,10 +4,22 @@
|
|||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef struct _FILE FILE;
|
||||
|
||||
extern FILE *stdin;
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
|
||||
int printf(const char *format, ...);
|
||||
int fprintf(FILE *stream, const char *format, ...);
|
||||
int sprintf(char *str, 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);
|
||||
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 remove(const char *pathname);
|
||||
|
||||
|
|
|
|||
130
run.sh
130
run.sh
|
|
@ -1,22 +1,122 @@
|
|||
#!/bin/bash
|
||||
# Rumpk QEMU Boot Script
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# 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)"
|
||||
KERNEL="$RUMPK_DIR/zig-out/bin/rumpk.elf"
|
||||
MODE="${1:-interactive}"
|
||||
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
|
||||
echo "ERROR: Kernel not found at $KERNEL"
|
||||
echo "Run ./build.sh first"
|
||||
echo "ERROR: No kernel binary found. Run: ./build_nim.sh && zig build -Dtarget=riscv64-freestanding-none -Dcpu=sifive_u54"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🚀 Booting Rumpk..."
|
||||
echo " Kernel: $KERNEL"
|
||||
echo ""
|
||||
|
||||
qemu-system-riscv64 \
|
||||
-M virt \
|
||||
-cpu max \
|
||||
-m 512M \
|
||||
-nographic \
|
||||
case "$ARCH" in
|
||||
riscv64)
|
||||
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
|
||||
|
||||
if [ "$MODE" = "test" ]; then
|
||||
echo "=== Rumpk Boot Test ($ARCH, ${TIMEOUT}s timeout) ==="
|
||||
|
||||
LOGFILE=$(mktemp /tmp/rumpk-boot-XXXXXX.log)
|
||||
trap "rm -f $LOGFILE" EXIT
|
||||
|
||||
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 \
|
||||
$BUILD_DIR/subject_entry.o \
|
||||
$BUILD_DIR/stubs_user.o \
|
||||
$BUILD_DIR/libc_shim.o \
|
||||
stubs_mksh.o \
|
||||
$OBJS \
|
||||
|
|
|
|||
|
|
@ -13,17 +13,78 @@
|
|||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#include <termios.h>
|
||||
|
||||
// Globals
|
||||
char **environ = NULL;
|
||||
|
||||
extern void console_write(const void* p, size_t len);
|
||||
#include <stdarg.h>
|
||||
|
||||
extern long 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);
|
||||
}
|
||||
|
||||
// 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
|
||||
int fstat(int fd, 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 sigaddset(sigset_t *set, int signum) { 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 kill(pid_t pid, int sig) { 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 pipe(int pipefd[2]) { return -1; }
|
||||
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 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 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; }
|
||||
pid_t tcgetpgrp(int fd) { return 1; }
|
||||
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; }
|
||||
|
||||
void longjmp(jmp_buf env, int val) { while(1); }
|
||||
int setjmp(jmp_buf env) { return 0; }
|
||||
int setjmp(jmp_buf env) {
|
||||
__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