Compare commits
46 Commits
eaf000e5ec
...
8b14b16317
| Author | SHA1 | Date |
|---|---|---|
|
|
8b14b16317 | |
|
|
ad09e59f6e | |
|
|
f3629ce9ea | |
|
|
536dad4282 | |
|
|
72caf911b1 | |
|
|
5e476f76fa | |
|
|
5ff0ee6ea2 | |
|
|
af687b0d4a | |
|
|
8fcdf4e9cc | |
|
|
b09f05805f | |
|
|
fc9f2eff6b | |
|
|
f9d95c81b2 | |
|
|
334d4458fc | |
|
|
9eba865099 | |
|
|
c13d475147 | |
|
|
00fcda08e7 | |
|
|
2fa1c14e5b | |
|
|
0cfe5a27ae | |
|
|
c3bb72c888 | |
|
|
3423539036 | |
|
|
132e842cf4 | |
|
|
ef3c761a5a | |
|
|
1a411cd806 | |
|
|
990fb8f02d | |
|
|
69ad105885 | |
|
|
c57bb2a770 | |
|
|
c01dd213b1 | |
|
|
a0ac0ddb64 | |
|
|
ad8926e492 | |
|
|
34a2986522 | |
|
|
e3007c72ca | |
|
|
1cac56db5f | |
|
|
73620c43b1 | |
|
|
4eafafa4d1 | |
|
|
82e1b7c657 | |
|
|
ba57b1c54e | |
|
|
c557f4f4f9 | |
|
|
08159d7341 | |
|
|
6aa563effe | |
|
|
663ae649f8 | |
|
|
1751153763 | |
|
|
436c4504a4 | |
|
|
738869c04b | |
|
|
7f2ca0d38e | |
|
|
5416c8cd93 | |
|
|
33d08a2bf2 |
|
|
@ -9,62 +9,6 @@
|
|||
|
||||
import ../../libs/membrane/libc
|
||||
|
||||
# --- M4.4: BKDL Capability Manifest (SPEC-071) ---
|
||||
# Declares what capabilities this binary needs. The kernel reads this
|
||||
# from the .nexus.manifest ELF section during loading and grants only
|
||||
# what is declared here. No manifest = PLEDGE_STDIO only.
|
||||
#
|
||||
# Capabilities requested:
|
||||
# - Channel 0x1001 (console.output) WRITE
|
||||
# - Channel 0x2000 (VFS) READ
|
||||
# - Channel 0x0500 (NET_TX) WRITE
|
||||
# - Channel 0x0501 (NET_RX) READ
|
||||
|
||||
{.emit: """
|
||||
__attribute__((section(".nexus.manifest"), used))
|
||||
static const unsigned char nexus_manifest[166] = {
|
||||
/* BkdlHeader (118 bytes) */
|
||||
0x53, 0x55, 0x58, 0x4E, /* magic: "NXUS" (LE) */
|
||||
0x01, 0x00, /* version: 1 */
|
||||
0x00, 0x00, /* flags: 0 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* signature[0..63]: zeros */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* pubkey_hash[0..31]: zeros */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, /* cap_count: 4 */
|
||||
0x00, 0x00, 0x00, 0x00, /* blob_size: 0 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* entry_point: 0 */
|
||||
/* CapDescriptor[0]: console.output (0x1001) WRITE */
|
||||
0x02, /* cap_type: Channel */
|
||||
0x02, /* perms: PERM_WRITE */
|
||||
0x00, 0x00, /* reserved */
|
||||
0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* resource_id: 0x1001 (LE) */
|
||||
/* CapDescriptor[1]: VFS (0x2000) READ */
|
||||
0x02, /* cap_type: Channel */
|
||||
0x01, /* perms: PERM_READ */
|
||||
0x00, 0x00, /* reserved */
|
||||
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* resource_id: 0x2000 (LE) */
|
||||
/* CapDescriptor[2]: NET_TX (0x0500) WRITE */
|
||||
0x02, /* cap_type: Channel */
|
||||
0x02, /* perms: PERM_WRITE */
|
||||
0x00, 0x00, /* reserved */
|
||||
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* resource_id: 0x0500 (LE) */
|
||||
/* CapDescriptor[3]: NET_RX (0x0501) READ */
|
||||
0x02, /* cap_type: Channel */
|
||||
0x01, /* perms: PERM_READ */
|
||||
0x00, 0x00, /* reserved */
|
||||
0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* resource_id: 0x0501 (LE) */
|
||||
};
|
||||
""".}
|
||||
|
||||
proc main() =
|
||||
# 1. Pledge Sovereignty
|
||||
discard pledge(0xFFFFFFFFFFFFFFFF'u64) # PLEDGE_ALL
|
||||
|
|
@ -74,67 +18,60 @@ proc main() =
|
|||
print(cstring("\x1b[1;35m║ SOVEREIGN INIT (NexInit v1.0) ║\x1b[0m\n"))
|
||||
print(cstring("\x1b[1;35m╚═══════════════════════════════════════╝\x1b[0m\n\n"))
|
||||
|
||||
print(cstring("[INIT] PHASE_42_VERIFY: Membrane Network Stack...\\n"))
|
||||
# DISABLED: Network stack requires LwIP
|
||||
# membrane_init()
|
||||
print(cstring("[INIT] Initializing Membrane Network Stack...\n"))
|
||||
membrane_init()
|
||||
|
||||
# proc glue_get_ip(): uint32 {.importc: "glue_get_ip", cdecl.}
|
||||
proc glue_get_ip(): uint32 {.importc: "glue_get_ip", cdecl.}
|
||||
|
||||
# # --- DHCP PHASE ---
|
||||
# print(cstring("[INIT] Waiting for DHCP IP Address...\n"))
|
||||
# var ip: uint32 = 0
|
||||
# for i in 0 ..< 600: # 60 seconds
|
||||
# pump_membrane_stack()
|
||||
# ip = glue_get_ip()
|
||||
# if ip != 0: break
|
||||
# discard syscall(0x65, 100000000'u64) # 100ms
|
||||
|
||||
# if ip == 0:
|
||||
# print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n"))
|
||||
# else:
|
||||
# print(cstring("[INIT] Network ONLINE (10.0.2.15)\n"))
|
||||
|
||||
# # --- DNS PHASE ---
|
||||
# print(cstring("\n[TEST] ══════════════════════════════════════\n"))
|
||||
# print(cstring("[TEST] DNS Resolution: google.com\n"))
|
||||
# print(cstring("[TEST] ══════════════════════════════════════\n\n"))
|
||||
|
||||
# type
|
||||
# AddrInfo {.importc: "struct addrinfo", header: "<netdb.h>".} = object
|
||||
|
||||
# proc getaddrinfo(node: cstring, service: cstring, hints: pointer, res: ptr ptr AddrInfo): cint {.importc, header: "<netdb.h>".}
|
||||
# proc freeaddrinfo(res: ptr AddrInfo) {.importc, header: "<netdb.h>".}
|
||||
|
||||
# var res: ptr AddrInfo
|
||||
# for attempt in 1..5:
|
||||
# print(cstring("[TEST] Resolving google.com (Attempt "))
|
||||
# # (Simplified number printing not available, just loop)
|
||||
|
||||
# if getaddrinfo("google.com", nil, nil, addr res) == 0:
|
||||
# print(cstring(") -> SUCCESS!\n"))
|
||||
# freeaddrinfo(res)
|
||||
# break
|
||||
# else:
|
||||
# print(cstring(") -> FAILED. Waiting 5s...\n"))
|
||||
# for j in 1..50:
|
||||
# pump_membrane_stack()
|
||||
# discard syscall(0x65, 100000000'u64) # 100ms
|
||||
# --- DHCP PHASE ---
|
||||
print(cstring("[INIT] Waiting for DHCP IP Address...\n"))
|
||||
var ip: uint32 = 0
|
||||
for i in 0 ..< 600: # 60 seconds
|
||||
pump_membrane_stack()
|
||||
ip = glue_get_ip()
|
||||
if ip != 0: break
|
||||
discard syscall(0x65, 100000000'u64) # 100ms
|
||||
|
||||
if ip == 0:
|
||||
print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n"))
|
||||
else:
|
||||
print(cstring("[INIT] Network ONLINE (10.0.2.15)\n"))
|
||||
|
||||
# --- DNS PHASE ---
|
||||
print(cstring("\n[TEST] ══════════════════════════════════════\n"))
|
||||
print(cstring("[TEST] DNS Resolution: google.com\n"))
|
||||
print(cstring("[TEST] ══════════════════════════════════════\n\n"))
|
||||
|
||||
var res: ptr AddrInfo
|
||||
for attempt in 1..5:
|
||||
print(cstring("[TEST] Resolving google.com (Attempt "))
|
||||
# (Simplified number printing not available, just loop)
|
||||
|
||||
if getaddrinfo("google.com", nil, nil, addr res) == 0:
|
||||
print(cstring(") -> SUCCESS!\n"))
|
||||
freeaddrinfo(res)
|
||||
break
|
||||
else:
|
||||
print(cstring(") -> FAILED. Waiting 5s...\n"))
|
||||
for j in 1..50:
|
||||
pump_membrane_stack()
|
||||
discard syscall(0x65, 100000000'u64) # 100ms
|
||||
|
||||
# --- SHELL PHASE ---
|
||||
proc spawn_fiber(path: cstring): int =
|
||||
return int(syscall(0x300, cast[uint64](path), 0, 0))
|
||||
|
||||
|
||||
print(cstring("[INIT] Spawning mksh...\n"))
|
||||
discard spawn_fiber(cstring("/bin/mksh"))
|
||||
|
||||
|
||||
# --- SUPERVISOR PHASE ---
|
||||
print(cstring("[INIT] Entering Supervisor Loop...\n"))
|
||||
var loop_count = 0
|
||||
while true:
|
||||
# pump_membrane_stack() # DISABLED: Requires LwIP
|
||||
pump_membrane_stack()
|
||||
loop_count += 1
|
||||
if loop_count mod 0x100000 == 0: # Every ~1M iterations
|
||||
discard syscall(0x65, 1000000000'u64) # 1s yield
|
||||
if loop_count mod 100 == 0:
|
||||
print(cstring("[INIT] Heartbeat\n"))
|
||||
discard syscall(0x65, 100000000'u64) # 100ms
|
||||
|
||||
when isMainModule:
|
||||
|
|
|
|||
|
|
@ -33,10 +33,6 @@ SECTIONS
|
|||
*(.data.*)
|
||||
} > RAM
|
||||
|
||||
.nexus.manifest : {
|
||||
KEEP(*(.nexus.manifest))
|
||||
} > RAM
|
||||
|
||||
.bss : {
|
||||
. = ALIGN(8);
|
||||
__bss_start = .;
|
||||
|
|
|
|||
|
|
@ -11,15 +11,9 @@ _start:
|
|||
2:
|
||||
fence rw, rw
|
||||
|
||||
# Valid Args from Stack (Linux ABI)
|
||||
ld a0, 0(sp) # argc
|
||||
addi a1, sp, 8 # argv
|
||||
# Arguments (argc, argv) are already in a0, a1 from Kernel
|
||||
# sp is already pointing to argc from Kernel
|
||||
|
||||
# Calculate envp in a2: envp = argv + (argc + 1) * 8
|
||||
addi t0, a0, 1 # t0 = argc + 1
|
||||
slli t0, t0, 3 # t0 = (argc + 1) * 8
|
||||
add a2, a1, t0 # a2 = argv + offset
|
||||
|
||||
call main
|
||||
|
||||
# Call exit(result)
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
// Minimal test shell to verify the execution environment
|
||||
#include <stddef.h>
|
||||
|
||||
extern int write(int fd, const void *buf, size_t count);
|
||||
extern int read(int fd, void *buf, size_t count);
|
||||
|
||||
int main(int argc, char *argv[], char *envp[]) {
|
||||
const char *prompt = "shell> ";
|
||||
char buf[128];
|
||||
|
||||
while (1) {
|
||||
// Print prompt
|
||||
write(1, prompt, 7);
|
||||
|
||||
// Read command
|
||||
int n = read(0, buf, sizeof(buf) - 1);
|
||||
if (n <= 0) continue;
|
||||
|
||||
buf[n] = '\0';
|
||||
|
||||
// Echo back
|
||||
write(1, "You typed: ", 11);
|
||||
write(1, buf, n);
|
||||
|
||||
// Check for exit
|
||||
if (buf[0] == 'q' && buf[1] == '\n') {
|
||||
write(1, "Goodbye!\n", 9);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
112
boot/header.zig
112
boot/header.zig
|
|
@ -7,16 +7,15 @@
|
|||
|
||||
//! Rumpk Boot Header
|
||||
//!
|
||||
//! Architecture-dispatched entry point for bare-metal boot.
|
||||
//! Handles BSS clearing and stack initialization before jumping to HAL init.
|
||||
//! Defines the Multiboot2 header for GRUB/QEMU and the bare-metal entry point.
|
||||
//! Handles BSS clearing and stack initialization before jumping to the Nim kernel.
|
||||
//!
|
||||
//! SAFETY: Executed in the earliest boot stage with no environment initialized.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
// =========================================================
|
||||
// Multiboot2 Header (for GRUB/QEMU — x86 only)
|
||||
// Multiboot2 Header (for GRUB/QEMU)
|
||||
// =========================================================
|
||||
|
||||
const MULTIBOOT2_MAGIC: u32 = 0xe85250d6;
|
||||
|
|
@ -44,92 +43,39 @@ export const multiboot2_header linksection(".multiboot2") = Multiboot2Header{
|
|||
};
|
||||
|
||||
// =========================================================
|
||||
// Arch-Specific HAL Entry Points
|
||||
// Entry Point
|
||||
// =========================================================
|
||||
|
||||
extern fn riscv_init() noreturn;
|
||||
extern fn aarch64_init() void; // Returns void (calls rumpk_halt internally)
|
||||
|
||||
// =========================================================
|
||||
// Entry Point (Architecture Dispatched)
|
||||
// =========================================================
|
||||
|
||||
// 1MB Kernel Stack
|
||||
const STACK_SIZE = 0x100000;
|
||||
export var kernel_stack: [STACK_SIZE]u8 align(16) linksection(".bss.stack") = undefined;
|
||||
|
||||
export fn _start() callconv(.naked) noreturn {
|
||||
switch (builtin.cpu.arch) {
|
||||
.riscv64 => {
|
||||
asm volatile (
|
||||
\\ // Set up stack
|
||||
\\ la sp, kernel_stack
|
||||
\\ li t0, %[stack_size]
|
||||
\\ add sp, sp, t0
|
||||
\\
|
||||
\\ // Clear BSS
|
||||
\\ la t0, __bss_start
|
||||
\\ la t1, __bss_end
|
||||
\\1:
|
||||
\\ bge t0, t1, 2f
|
||||
\\ sd zero, (t0)
|
||||
\\ addi t0, t0, 8
|
||||
\\ j 1b
|
||||
\\2:
|
||||
\\ // Jump to RISC-V HAL Init
|
||||
\\ call riscv_init
|
||||
\\
|
||||
\\ // Should never return
|
||||
\\ wfi
|
||||
\\ j 2b
|
||||
:
|
||||
: [stack_size] "i" (STACK_SIZE),
|
||||
);
|
||||
},
|
||||
.aarch64 => {
|
||||
asm volatile (
|
||||
// Mask all exceptions
|
||||
\\ msr daifset, #0xf
|
||||
//
|
||||
// Enable FP/SIMD (CPACR_EL1.FPEN = 0b11)
|
||||
\\ mov x0, #(3 << 20)
|
||||
\\ msr cpacr_el1, x0
|
||||
\\ isb
|
||||
//
|
||||
// Disable alignment check (SCTLR_EL1.A = 0)
|
||||
\\ mrs x0, sctlr_el1
|
||||
\\ bic x0, x0, #(1 << 1)
|
||||
\\ msr sctlr_el1, x0
|
||||
\\ isb
|
||||
//
|
||||
// Set up stack
|
||||
\\ adrp x0, kernel_stack
|
||||
\\ add x0, x0, :lo12:kernel_stack
|
||||
\\ mov x1, #0x100000
|
||||
\\ add sp, x0, x1
|
||||
//
|
||||
// Clear BSS
|
||||
\\ adrp x0, __bss_start
|
||||
\\ add x0, x0, :lo12:__bss_start
|
||||
\\ adrp x1, __bss_end
|
||||
\\ add x1, x1, :lo12:__bss_end
|
||||
\\ 1: cmp x0, x1
|
||||
\\ b.ge 2f
|
||||
\\ str xzr, [x0], #8
|
||||
\\ b 1b
|
||||
\\ 2:
|
||||
//
|
||||
// Jump to ARM64 HAL Init
|
||||
\\ bl aarch64_init
|
||||
//
|
||||
// Should never return
|
||||
\\ 3: wfe
|
||||
\\ b 3b
|
||||
);
|
||||
},
|
||||
else => {
|
||||
// Unsupported architecture
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
// Clear BSS, set up stack, then jump to RISC-V Init
|
||||
asm volatile (
|
||||
\\ // Set up stack
|
||||
\\ la sp, kernel_stack
|
||||
\\ li t0, %[stack_size]
|
||||
\\ add sp, sp, t0
|
||||
\\
|
||||
\\ // Clear BSS
|
||||
\\ la t0, __bss_start
|
||||
\\ la t1, __bss_end
|
||||
\\1:
|
||||
\\ bge t0, t1, 2f
|
||||
\\ sd zero, (t0)
|
||||
\\ addi t0, t0, 8
|
||||
\\ j 1b
|
||||
\\2:
|
||||
\\ // Jump to HAL Init
|
||||
\\ call riscv_init
|
||||
\\
|
||||
\\ // Should never return
|
||||
\\ wfi
|
||||
\\ j 2b
|
||||
:
|
||||
: [stack_size] "i" (STACK_SIZE),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
383
build.zig
383
build.zig
|
|
@ -5,111 +5,31 @@
|
|||
// This file is part of the Nexus Commonwealth.
|
||||
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
||||
|
||||
//! Rumpk Build System — Unified Pipeline
|
||||
//! Rumpk Build System
|
||||
//!
|
||||
//! 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)
|
||||
//! Orchestrates L0 (Zig) and L1 (Nim) compilation.
|
||||
//! Builds the Hardware Abstraction Layer as a static library.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const Arch = enum { riscv64, aarch64, x86_64 };
|
||||
|
||||
/// Per-architecture build configuration.
|
||||
const ArchConfig = struct {
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
code_model: std.builtin.CodeModel,
|
||||
switch_asm: []const u8,
|
||||
linker_script: []const u8,
|
||||
/// Arch name for build_nim.sh
|
||||
nim_arch: []const u8,
|
||||
};
|
||||
|
||||
fn archConfig(arch: Arch) ArchConfig {
|
||||
return switch (arch) {
|
||||
.riscv64 => .{
|
||||
.cpu_arch = .riscv64,
|
||||
.code_model = .medany,
|
||||
.switch_asm = "hal/arch/riscv64/switch.S",
|
||||
.linker_script = "boot/linker.ld",
|
||||
.nim_arch = "riscv64",
|
||||
},
|
||||
.aarch64 => .{
|
||||
.cpu_arch = .aarch64,
|
||||
.code_model = .default,
|
||||
.switch_asm = "hal/arch/aarch64/switch.S",
|
||||
.linker_script = "boot/linker_aarch64.ld",
|
||||
.nim_arch = "aarch64",
|
||||
},
|
||||
.x86_64 => .{
|
||||
.cpu_arch = .x86_64,
|
||||
.code_model = .kernel,
|
||||
.switch_asm = "hal/arch/x86_64/switch.S",
|
||||
.linker_script = "boot/linker_x86_64.ld",
|
||||
.nim_arch = "x86_64",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Apply freestanding kernel settings to a module.
|
||||
fn applyKernelSettings(mod: *std.Build.Module, code_model: std.builtin.CodeModel) void {
|
||||
mod.red_zone = false;
|
||||
mod.stack_check = false;
|
||||
mod.code_model = code_model;
|
||||
mod.sanitize_thread = false;
|
||||
mod.single_threaded = true;
|
||||
mod.strip = true;
|
||||
}
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
// =========================================================
|
||||
// Target Selection: -Darch=riscv64|aarch64|x86_64
|
||||
// =========================================================
|
||||
|
||||
const arch = b.option(Arch, "arch", "Target architecture (default: riscv64)") orelse .riscv64;
|
||||
const config = archConfig(arch);
|
||||
|
||||
var query: std.Target.Query = .{
|
||||
.cpu_arch = config.cpu_arch,
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
};
|
||||
// Disable NEON/FP in kernel code generation to avoid unaligned SIMD stores
|
||||
if (config.cpu_arch == .aarch64) {
|
||||
query.cpu_features_sub = std.Target.aarch64.featureSet(&.{.neon});
|
||||
}
|
||||
const target = b.resolveTargetQuery(query);
|
||||
|
||||
// HEPHAESTUS DECREE: Force ReleaseSmall. A kernel runs naked.
|
||||
const optimize: std.builtin.OptimizeMode = .ReleaseSmall;
|
||||
|
||||
// =========================================================
|
||||
// Step: Nim Kernel Compilation (nim -> C -> .o)
|
||||
// =========================================================
|
||||
// Calls build_nim.sh which handles:
|
||||
// 1. nim c --compileOnly -> generates C
|
||||
// 2. zig cc -> cross-compiles C to .o
|
||||
// Incremental: only recompiles changed files.
|
||||
|
||||
const nim_step = b.addSystemCommand(&.{ "./build_nim.sh", config.nim_arch });
|
||||
const nim_build_step = b.step("nim", "Recompile Nim kernel sources (nim -> C -> .o)");
|
||||
nim_build_step.dependOn(&nim_step.step);
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
// =========================================================
|
||||
// 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,
|
||||
});
|
||||
applyKernelSettings(hal_mod, config.code_model);
|
||||
// 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;
|
||||
|
||||
const hal = b.addLibrary(.{
|
||||
.name = "rumpk_hal",
|
||||
|
|
@ -119,6 +39,15 @@ 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)
|
||||
// =========================================================
|
||||
|
|
@ -128,230 +57,38 @@ pub fn build(b: *std.Build) void {
|
|||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
applyKernelSettings(boot_mod, config.code_model);
|
||||
boot_mod.red_zone = false;
|
||||
boot_mod.stack_check = false;
|
||||
boot_mod.code_model = .medany;
|
||||
|
||||
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"),
|
||||
.root_source_file = b.path("hal/abi.zig"), // Fake root, we add objects later
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
applyKernelSettings(kernel_mod, config.code_model);
|
||||
kernel_mod.red_zone = false;
|
||||
kernel_mod.stack_check = false;
|
||||
kernel_mod.code_model = .medany;
|
||||
|
||||
const kernel = b.addExecutable(.{
|
||||
.name = "rumpk.elf",
|
||||
.root_module = kernel_mod,
|
||||
});
|
||||
|
||||
kernel.setLinkerScript(b.path(config.linker_script));
|
||||
kernel.setLinkerScript(b.path("boot/linker.ld"));
|
||||
kernel.addObject(boot);
|
||||
kernel.addObject(clib);
|
||||
kernel.addObject(libc_shim);
|
||||
kernel.addObject(lfs_hal);
|
||||
kernel.addObject(lwf_adapter);
|
||||
kernel.addObject(switch_obj);
|
||||
// kernel.linkLibrary(hal); // Redundant, already in kernel_mod
|
||||
|
||||
// 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/
|
||||
// Add Nim-generated objects
|
||||
{
|
||||
var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
|
||||
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
||||
|
|
@ -367,63 +104,31 @@ pub fn build(b: *std.Build) void {
|
|||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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"));
|
||||
|
||||
b.installArtifact(kernel);
|
||||
|
||||
// Make default install depend on Nim compilation
|
||||
kernel.step.dependOn(&nim_step.step);
|
||||
|
||||
// =========================================================
|
||||
// Tests (always run on native host)
|
||||
// Tests
|
||||
// =========================================================
|
||||
|
||||
// HAL unit tests
|
||||
const hal_test_mod = b.createModule(.{
|
||||
const test_mod = b.createModule(.{
|
||||
.root_source_file = b.path("hal/abi.zig"),
|
||||
.target = b.graph.host,
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const hal_tests = b.addTest(.{
|
||||
.root_module = hal_test_mod,
|
||||
.root_module = test_mod,
|
||||
});
|
||||
|
||||
const run_hal_tests = b.addRunArtifact(hal_tests);
|
||||
const test_step = b.step("test", "Run Rumpk HAL tests (native host)");
|
||||
test_step.dependOn(&run_hal_tests.step);
|
||||
|
||||
// LWF Adapter + Membrane tests (Project LibWeb)
|
||||
const lwf_test_mod = b.createModule(.{
|
||||
.root_source_file = b.path("libs/libertaria/lwf_adapter.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
|
||||
const lwf_tests = b.addTest(.{
|
||||
.root_module = lwf_test_mod,
|
||||
});
|
||||
|
||||
const run_lwf_tests = b.addRunArtifact(lwf_tests);
|
||||
const lwf_test_step = b.step("test-lwf", "Run LWF Adapter tests (Project LibWeb)");
|
||||
lwf_test_step.dependOn(&run_lwf_tests.step);
|
||||
|
||||
const lwf_membrane_mod = b.createModule(.{
|
||||
.root_source_file = b.path("libs/libertaria/lwf_membrane.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
|
||||
const lwf_membrane_tests = b.addTest(.{
|
||||
.root_module = lwf_membrane_mod,
|
||||
});
|
||||
|
||||
const run_lwf_membrane_tests = b.addRunArtifact(lwf_membrane_tests);
|
||||
lwf_test_step.dependOn(&run_lwf_membrane_tests.step);
|
||||
const run_tests = b.addRunArtifact(hal_tests);
|
||||
const test_step = b.step("test", "Run Rumpk HAL tests");
|
||||
test_step.dependOn(&run_tests.step);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ proc cspace_grant_cap*(
|
|||
proc cspace_lookup*(fiber_id: uint64, slot: uint): pointer {.importc, cdecl.}
|
||||
proc cspace_revoke*(fiber_id: uint64, slot: uint) {.importc, cdecl.}
|
||||
proc cspace_check_perm*(fiber_id: uint64, slot: uint, perm_bits: uint8): bool {.importc, cdecl.}
|
||||
proc cspace_check_channel*(fiber_id: uint64, channel_id: uint64, perm_bits: uint8): bool {.importc, cdecl.}
|
||||
|
||||
## Capability Types (Mirror from cspace.zig)
|
||||
type
|
||||
|
|
@ -81,10 +80,10 @@ proc fiber_grant_memory*(
|
|||
end_addr
|
||||
)
|
||||
|
||||
proc fiber_check_channel_access*(fiber_id: uint64, channel_id: uint64, write: bool): bool =
|
||||
## Check if fiber has Channel capability for given channel_id
|
||||
proc fiber_check_channel_access*(fiber_id: uint64, slot: uint, write: bool): bool =
|
||||
## Check if fiber has channel access via capability
|
||||
let perm = if write: PERM_WRITE else: PERM_READ
|
||||
return cspace_check_channel(fiber_id, channel_id, perm)
|
||||
return cspace_check_perm(fiber_id, slot, perm)
|
||||
|
||||
proc fiber_revoke_capability*(fiber_id: uint64, slot: uint) =
|
||||
## Revoke a capability from a fiber
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||
# Rumpk Phase 10: Multitasking & Context Switching
|
||||
#
|
||||
#
|
||||
# Responsibilities:
|
||||
# - Define the Fiber abstraction (Hardware Context + Stack)
|
||||
# - Abstract the ISA-specific context switch mechanism
|
||||
|
|
@ -24,10 +24,6 @@ when defined(riscv64):
|
|||
const ARCH_NAME* = "riscv64"
|
||||
const CONTEXT_SIZE* = 128
|
||||
const RET_ADDR_INDEX* = 0 # Offset in stack for RA
|
||||
elif defined(arm64):
|
||||
const ARCH_NAME* = "aarch64"
|
||||
const CONTEXT_SIZE* = 96 # 6 register pairs (x19-x30) * 16 bytes
|
||||
const RET_ADDR_INDEX* = 11 # x30 (LR) at [sp + 88] = index 11
|
||||
elif defined(amd64) or defined(x86_64):
|
||||
const ARCH_NAME* = "amd64"
|
||||
const CONTEXT_SIZE* = 64
|
||||
|
|
@ -116,7 +112,7 @@ const STACK_SIZE* = 4096
|
|||
# Fiber State
|
||||
# =========================================================
|
||||
|
||||
var main_fiber*: FiberObject
|
||||
var main_fiber: FiberObject
|
||||
var current_fiber* {.global.}: Fiber = addr main_fiber
|
||||
|
||||
# =========================================================
|
||||
|
|
@ -139,9 +135,6 @@ proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
|
|||
when defined(riscv64):
|
||||
while true:
|
||||
{.emit: "asm volatile(\"wfi\");".}
|
||||
elif defined(arm64):
|
||||
while true:
|
||||
{.emit: "asm volatile(\"wfe\");".}
|
||||
else:
|
||||
while true: discard
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@
|
|||
## Freestanding implementation (No OS module dependencies).
|
||||
## Uses fixed-size arrays for descriptors to ensure deterministic latency.
|
||||
|
||||
import tar, sfs, lfs_bridge
|
||||
import tar, sfs
|
||||
|
||||
type
|
||||
VFSMode = enum
|
||||
MODE_TAR, MODE_SFS, MODE_RAM, MODE_TTY, MODE_LFS
|
||||
MODE_TAR, MODE_SFS, MODE_RAM, MODE_TTY
|
||||
|
||||
MountPoint = object
|
||||
prefix: array[32, char]
|
||||
|
|
@ -25,7 +25,6 @@ type
|
|||
offset: uint64
|
||||
mode: VFSMode
|
||||
active: bool
|
||||
lfs_handle: int32 ## LFS file handle (-1 = not open)
|
||||
|
||||
const MAX_MOUNTS = 8
|
||||
const MAX_FDS = 32
|
||||
|
|
@ -65,16 +64,9 @@ proc vfs_add_mount(prefix: cstring, mode: VFSMode) =
|
|||
mnt_table[mnt_count].mode = mode
|
||||
mnt_count += 1
|
||||
|
||||
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||
|
||||
proc vfs_mount_init*() =
|
||||
# Mount LittleFS for /nexus (persistent sovereign storage)
|
||||
if lfs_bridge.lfs_mount_fs():
|
||||
vfs_add_mount("/nexus", MODE_LFS)
|
||||
else:
|
||||
# Fallback to SFS if LittleFS mount fails (no block device?)
|
||||
kprintln("[VFS] LFS mount failed, falling back to SFS for /nexus")
|
||||
vfs_add_mount("/nexus", MODE_SFS)
|
||||
# Restore the SPEC-502 baseline
|
||||
vfs_add_mount("/nexus", MODE_SFS)
|
||||
vfs_add_mount("/sysro", MODE_TAR)
|
||||
vfs_add_mount("/state", MODE_RAM)
|
||||
vfs_add_mount("/dev/tty", MODE_TTY)
|
||||
|
|
@ -91,36 +83,22 @@ proc resolve_path(path: cstring): (VFSMode, int) =
|
|||
|
||||
proc ion_vfs_open*(path: cstring, flags: int32): int32 {.exportc, cdecl.} =
|
||||
let (mode, prefix_len) = resolve_path(path)
|
||||
|
||||
|
||||
# Delegate internal open
|
||||
let sub_path = cast[cstring](cast[uint64](path) + uint64(prefix_len))
|
||||
var internal_fd: int32 = -1
|
||||
|
||||
# Map VFS flags to LFS flags for MODE_LFS
|
||||
var lfs_h: int32 = -1
|
||||
|
||||
|
||||
case mode:
|
||||
of MODE_TAR, MODE_RAM: internal_fd = tar.vfs_open(sub_path, flags)
|
||||
of MODE_SFS: internal_fd = 0 # Shim
|
||||
of MODE_LFS:
|
||||
# Convert POSIX-ish flags to LFS flags
|
||||
var lfs_flags = lfs_bridge.LFS_O_RDONLY
|
||||
if (flags and 3) == 1: lfs_flags = lfs_bridge.LFS_O_WRONLY
|
||||
elif (flags and 3) == 2: lfs_flags = lfs_bridge.LFS_O_RDWR
|
||||
if (flags and 0x40) != 0: lfs_flags = lfs_flags or lfs_bridge.LFS_O_CREAT # O_CREAT
|
||||
if (flags and 0x200) != 0: lfs_flags = lfs_flags or lfs_bridge.LFS_O_TRUNC # O_TRUNC
|
||||
if (flags and 0x400) != 0: lfs_flags = lfs_flags or lfs_bridge.LFS_O_APPEND # O_APPEND
|
||||
lfs_h = lfs_bridge.lfs_open_file(sub_path, lfs_flags)
|
||||
if lfs_h >= 0: internal_fd = 0
|
||||
of MODE_TTY: internal_fd = 1 # Shim
|
||||
|
||||
|
||||
if internal_fd >= 0:
|
||||
for i in 0..<MAX_FDS:
|
||||
if not fd_table[i].active:
|
||||
fd_table[i].active = true
|
||||
fd_table[i].mode = mode
|
||||
fd_table[i].offset = 0
|
||||
fd_table[i].lfs_handle = lfs_h
|
||||
let p = cast[ptr UncheckedArray[char]](sub_path)
|
||||
var j = 0
|
||||
while p[j] != '\0' and j < 63:
|
||||
|
|
@ -128,15 +106,13 @@ proc ion_vfs_open*(path: cstring, flags: int32): int32 {.exportc, cdecl.} =
|
|||
j += 1
|
||||
fd_table[i].path[j] = '\0'
|
||||
return int32(i + 3) # FDs start at 3
|
||||
# No free slot — close LFS handle if we opened one
|
||||
if lfs_h >= 0: discard lfs_bridge.lfs_close_file(lfs_h)
|
||||
return -1
|
||||
|
||||
proc ion_vfs_read*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
||||
let idx = int(fd - 3)
|
||||
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||
let fh = addr fd_table[idx]
|
||||
|
||||
|
||||
case fh.mode:
|
||||
of MODE_TTY: return -2
|
||||
of MODE_TAR, MODE_RAM:
|
||||
|
|
@ -156,17 +132,12 @@ proc ion_vfs_read*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cde
|
|||
fh.offset += actual
|
||||
return int64(actual)
|
||||
return 0
|
||||
of MODE_LFS:
|
||||
if fh.lfs_handle < 0: return -1
|
||||
let n = lfs_bridge.lfs_read_file(fh.lfs_handle, buf, uint32(count))
|
||||
if n > 0: fh.offset += uint64(n)
|
||||
return int64(n)
|
||||
|
||||
proc ion_vfs_write*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
||||
let idx = int(fd - 3)
|
||||
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||
let fh = addr fd_table[idx]
|
||||
|
||||
|
||||
case fh.mode:
|
||||
of MODE_TTY: return -2
|
||||
of MODE_TAR, MODE_RAM:
|
||||
|
|
@ -178,47 +149,14 @@ proc ion_vfs_write*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cd
|
|||
let path = cast[cstring](addr fh.path[0])
|
||||
sfs.sfs_write_file(path, buf, int(count))
|
||||
return int64(count)
|
||||
of MODE_LFS:
|
||||
if fh.lfs_handle < 0: return -1
|
||||
let n = lfs_bridge.lfs_write_file(fh.lfs_handle, buf, uint32(count))
|
||||
if n > 0: fh.offset += uint64(n)
|
||||
return int64(n)
|
||||
|
||||
proc ion_vfs_close*(fd: int32): int32 {.exportc, cdecl.} =
|
||||
let idx = int(fd - 3)
|
||||
if idx >= 0 and idx < MAX_FDS:
|
||||
if fd_table[idx].mode == MODE_LFS and fd_table[idx].lfs_handle >= 0:
|
||||
discard lfs_bridge.lfs_close_file(fd_table[idx].lfs_handle)
|
||||
fd_table[idx].lfs_handle = -1
|
||||
fd_table[idx].active = false
|
||||
return 0
|
||||
return -1
|
||||
|
||||
proc ion_vfs_dup*(fd: int32, min_fd: int32 = 0): int32 {.exportc, cdecl.} =
|
||||
let idx = int(fd - 3)
|
||||
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||
|
||||
# F_DUPFD needs to find first fd >= min_fd
|
||||
let start_idx = if min_fd > 3: int(min_fd - 3) else: 0
|
||||
|
||||
for i in start_idx..<MAX_FDS:
|
||||
if not fd_table[i].active:
|
||||
fd_table[i] = fd_table[idx]
|
||||
return int32(i + 3)
|
||||
return -1
|
||||
|
||||
proc ion_vfs_dup2*(old_fd: int32, new_fd: int32): int32 {.exportc, cdecl.} =
|
||||
let old_idx = int(old_fd - 3)
|
||||
let new_idx = int(new_fd - 3)
|
||||
if old_idx < 0 or old_idx >= MAX_FDS or not fd_table[old_idx].active: return -1
|
||||
if new_idx < 0 or new_idx >= MAX_FDS: return -1
|
||||
|
||||
if old_idx == new_idx: return new_fd
|
||||
|
||||
fd_table[new_idx] = fd_table[old_idx]
|
||||
fd_table[new_idx].active = true
|
||||
return new_fd
|
||||
|
||||
proc ion_vfs_list*(buf: pointer, max_len: uint64): int64 {.exportc, cdecl.} =
|
||||
# Hardcoded baseline for now to avoid string/os dependency
|
||||
let msg = "/nexus\n/sysro\n/state\n"
|
||||
|
|
|
|||
|
|
@ -3,10 +3,5 @@
|
|||
|
||||
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,22 +4,10 @@
|
|||
#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);
|
||||
|
||||
|
|
|
|||
39
core/ion.nim
39
core/ion.nim
|
|
@ -37,7 +37,7 @@ type
|
|||
CMD_GET_GPU_STATUS = 0x102
|
||||
CMD_FS_OPEN = 0x200
|
||||
CMD_FS_READ = 0x201
|
||||
CMD_FS_READDIR = 0x202
|
||||
CMD_FS_READDIR = 0x202
|
||||
CMD_FS_WRITE = 0x203
|
||||
CMD_FS_MOUNT = 0x204
|
||||
CMD_ION_FREE = 0x300
|
||||
|
|
@ -79,7 +79,7 @@ type
|
|||
|
||||
SysTable* = object
|
||||
magic*: uint32 # 0x4E585553
|
||||
reserved*: uint32
|
||||
reserved*: uint32
|
||||
s_rx*: ptr HAL_Ring[IonPacket]
|
||||
s_tx*: ptr HAL_Ring[IonPacket]
|
||||
s_event*: ptr HAL_Ring[IonPacket]
|
||||
|
|
@ -91,11 +91,9 @@ type
|
|||
fn_vfs_list*: proc(buf: pointer, max_len: uint64): int64 {.cdecl.}
|
||||
fn_vfs_write*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.}
|
||||
fn_vfs_close*: proc(fd: int32): int32 {.cdecl.}
|
||||
fn_vfs_dup*: proc(fd: int32, min_fd: int32): int32 {.cdecl.}
|
||||
fn_vfs_dup2*: proc(old_fd, new_fd: int32): int32 {.cdecl.}
|
||||
fn_log*: pointer
|
||||
fn_pledge*: proc(promises: uint64): int32 {.cdecl.}
|
||||
|
||||
|
||||
# Framebuffer
|
||||
fb_addr*: uint64
|
||||
fb_width*: uint32
|
||||
|
|
@ -117,18 +115,14 @@ type
|
|||
# Phase 36.3: Shared ION (16 bytes)
|
||||
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
||||
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
||||
|
||||
|
||||
# Phase 36.4: I/O Multiplexing (8 bytes)
|
||||
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
||||
|
||||
|
||||
# Phase 36.5: Network Hardware Info (8 bytes)
|
||||
net_mac*: array[6, byte]
|
||||
reserved_mac*: array[2, byte]
|
||||
|
||||
# Project LibWeb: LWF Sovereign Channel (16 bytes)
|
||||
s_lwf_rx*: ptr HAL_Ring[IonPacket] # Kernel Producer -> User Consumer (LWF frames)
|
||||
s_lwf_tx*: ptr HAL_Ring[IonPacket] # User Producer -> Kernel Consumer (LWF frames)
|
||||
|
||||
include invariant
|
||||
|
||||
# --- Sovereign Logic ---
|
||||
|
|
@ -173,12 +167,6 @@ var net_rx_hal: HAL_Ring[IonPacket]
|
|||
var net_tx_hal: HAL_Ring[IonPacket]
|
||||
var netswitch_rx_hal: HAL_Ring[IonPacket]
|
||||
|
||||
# Project LibWeb: LWF Sovereign Channels
|
||||
var chan_lwf_rx*: SovereignChannel[IonPacket] # Kernel -> User (LWF frames)
|
||||
var chan_lwf_tx*: SovereignChannel[IonPacket] # User -> Kernel (LWF frames)
|
||||
var lwf_rx_hal: HAL_Ring[IonPacket]
|
||||
var lwf_tx_hal: HAL_Ring[IonPacket]
|
||||
|
||||
proc ion_init_input*() {.exportc, cdecl.} =
|
||||
guest_input_hal.head = 0
|
||||
guest_input_hal.tail = 0
|
||||
|
|
@ -193,7 +181,7 @@ proc ion_init_network*() {.exportc, cdecl.} =
|
|||
net_rx_hal.tail = 0
|
||||
net_rx_hal.mask = 255
|
||||
chan_net_rx.ring = addr net_rx_hal
|
||||
|
||||
|
||||
net_tx_hal.head = 0
|
||||
net_tx_hal.tail = 0
|
||||
net_tx_hal.mask = 255
|
||||
|
|
@ -203,18 +191,7 @@ proc ion_init_network*() {.exportc, cdecl.} =
|
|||
netswitch_rx_hal.tail = 0
|
||||
netswitch_rx_hal.mask = 255
|
||||
chan_netswitch_rx.ring = addr netswitch_rx_hal
|
||||
|
||||
# Project LibWeb: LWF Rings
|
||||
lwf_rx_hal.head = 0
|
||||
lwf_rx_hal.tail = 0
|
||||
lwf_rx_hal.mask = 255
|
||||
chan_lwf_rx.ring = addr lwf_rx_hal
|
||||
|
||||
lwf_tx_hal.head = 0
|
||||
lwf_tx_hal.tail = 0
|
||||
lwf_tx_hal.mask = 255
|
||||
chan_lwf_tx.ring = addr lwf_tx_hal
|
||||
|
||||
|
||||
# Initialize user slab
|
||||
ion_user_slab_init()
|
||||
|
||||
|
|
@ -241,4 +218,4 @@ proc ion_user_free_systable*(id: uint16) {.exportc, cdecl.} =
|
|||
|
||||
static: doAssert(sizeof(IonPacket) == 24, "IonPacket size mismatch!")
|
||||
static: doAssert(sizeof(CmdPacket) == 32, "CmdPacket size mismatch!")
|
||||
static: doAssert(sizeof(SysTable) == 240, "SysTable size mismatch! (Expected 240 after LibWeb LWF channels)")
|
||||
static: doAssert(sizeof(SysTable) == 208, "SysTable size mismatch! (Expected 208 after MAC+pad)")
|
||||
|
|
|
|||
|
|
@ -13,24 +13,17 @@
|
|||
import ../ring
|
||||
|
||||
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
||||
|
||||
var NEWLINE_BUF: array[2, char] = ['\n', '\0']
|
||||
|
||||
proc dbg(s: cstring) {.inline.} =
|
||||
if s != nil:
|
||||
var i = 0
|
||||
let p = cast[ptr UncheckedArray[char]](s)
|
||||
while p[i] != '\0': inc i
|
||||
console_write(cast[pointer](s), csize_t(i))
|
||||
console_write(addr NEWLINE_BUF[0], csize_t(1))
|
||||
proc dbg(s: string) =
|
||||
console_write(unsafeAddr s[0], csize_t(s.len))
|
||||
var nl = "\n"
|
||||
console_write(unsafeAddr nl[0], csize_t(1))
|
||||
|
||||
const
|
||||
SLAB_SIZE* = 2048 # Max Packet Size (Ethernet Frame + Headroom)
|
||||
POOL_COUNT* = 1024 # Number of packets in the pool (2MB total RAM)
|
||||
POOL_ALIGN* = 4096 # VirtIO/Page Alignment
|
||||
|
||||
SYSTABLE_BASE = when defined(arm64): 0x50000000'u64
|
||||
else: 0x83000000'u64
|
||||
SYSTABLE_BASE = 0x83000000'u64
|
||||
USER_SLAB_OFFSET = 0x10000'u64 # Offset within SYSTABLE
|
||||
USER_SLAB_BASE* = SYSTABLE_BASE + USER_SLAB_OFFSET # 0x83010000
|
||||
USER_SLAB_COUNT = 512 # 512 packets to cover RX Ring (256) + TX
|
||||
|
|
@ -60,11 +53,9 @@ proc ion_pool_init*() {.exportc.} =
|
|||
dbg("[ION] Initializing Pool...")
|
||||
|
||||
# 1. Get the VIRTUAL address of the static buffer
|
||||
dbg("[ION] Step 1: Getting virt addr...")
|
||||
let virt_addr = cast[uint64](addr global_pool.buffer[0])
|
||||
|
||||
# 2. Translate to PHYSICAL (Identity Mapped for Phase 7)
|
||||
dbg("[ION] Step 2: Setting base phys...")
|
||||
global_pool.base_phys = virt_addr
|
||||
|
||||
# Tracing for Phase 37
|
||||
|
|
@ -73,16 +64,12 @@ proc ion_pool_init*() {.exportc.} =
|
|||
kprint_hex(global_pool.base_phys)
|
||||
dbg("")
|
||||
|
||||
dbg("[ION] Step 3: Ring Init (free_ring)...")
|
||||
dbg(" 3a: About to call init()")
|
||||
dbg("[ION] Ring Init...")
|
||||
global_pool.free_ring.init()
|
||||
dbg(" 3b: free_ring init complete")
|
||||
dbg("[ION] Step 4: Ring Init (tx_ring)...")
|
||||
global_tx_ring.init()
|
||||
dbg(" 4a: tx_ring init complete")
|
||||
|
||||
# Fill the free ring with all indices [0..1023]
|
||||
dbg("[ION] Step 5: Filling Slabs...")
|
||||
dbg("[ION] Filling Slabs...")
|
||||
var count = 0
|
||||
for i in 0 ..< POOL_COUNT:
|
||||
if global_pool.free_ring.push(uint16(i)):
|
||||
|
|
@ -90,8 +77,6 @@ proc ion_pool_init*() {.exportc.} =
|
|||
|
||||
dbg("[ION] Pool Ready.")
|
||||
|
||||
|
||||
|
||||
proc ion_alloc*(): IonPacket {.exportc.} =
|
||||
## O(1) Allocation. Returns an empty packet struct.
|
||||
## If OOM, returns packet with data = nil
|
||||
|
|
@ -212,7 +197,7 @@ proc ion_user_slab_init*() {.exportc.} =
|
|||
proc ion_alloc_shared*(out_id: ptr uint16): uint64 {.exportc, cdecl.} =
|
||||
## Allocate a buffer from the user-visible slab (Kernel Side, Shared Bitmap)
|
||||
let bitmap = cast[ptr array[64, byte]](USER_BITMAP_ADDR)
|
||||
|
||||
|
||||
for byteIdx in 0 ..< 64:
|
||||
if bitmap[byteIdx] != 0xFF:
|
||||
for bitIdx in 0 ..< 8:
|
||||
|
|
@ -222,7 +207,7 @@ proc ion_alloc_shared*(out_id: ptr uint16): uint64 {.exportc, cdecl.} =
|
|||
bitmap[byteIdx] = bitmap[byteIdx] or mask
|
||||
let idx = byteIdx * 8 + bitIdx
|
||||
if idx >= USER_SLAB_COUNT: return 0
|
||||
|
||||
|
||||
out_id[] = uint16(idx) or 0x8000
|
||||
return USER_SLAB_BASE + uint64(idx) * USER_PKT_SIZE
|
||||
return 0
|
||||
|
|
|
|||
749
core/kernel.nim
749
core/kernel.nim
File diff suppressed because it is too large
Load Diff
|
|
@ -67,76 +67,6 @@ proc kload*(path: string): uint64 =
|
|||
|
||||
return ehdr.e_entry
|
||||
|
||||
# --- M4.4: BKDL Manifest Extraction ---
|
||||
|
||||
proc streq_n(a: ptr UncheckedArray[byte], b: cstring, maxlen: int): bool =
|
||||
## Compare byte array against C string, bounded by maxlen
|
||||
var i = 0
|
||||
while i < maxlen:
|
||||
if b[i] == '\0':
|
||||
return true # b ended, all matched
|
||||
if a[i] != byte(b[i]):
|
||||
return false
|
||||
i += 1
|
||||
return false
|
||||
|
||||
proc kload_manifest*(file_content: openArray[byte]): ManifestResult =
|
||||
## Scan ELF section headers for .nexus.manifest containing BKDL data.
|
||||
## Returns header=nil if no manifest found.
|
||||
result.header = nil
|
||||
result.caps = nil
|
||||
result.count = 0
|
||||
|
||||
if file_content.len < int(sizeof(Elf64_Ehdr)):
|
||||
return
|
||||
|
||||
let ehdr = cast[ptr Elf64_Ehdr](unsafeAddr file_content[0])
|
||||
let base = cast[uint64](unsafeAddr file_content[0])
|
||||
let file_len = uint64(file_content.len)
|
||||
|
||||
# Validate section header table is within file
|
||||
if ehdr.e_shoff == 0 or ehdr.e_shnum == 0:
|
||||
return
|
||||
if ehdr.e_shoff + uint64(ehdr.e_shnum) * uint64(ehdr.e_shentsize) > file_len:
|
||||
return
|
||||
|
||||
# Get string table section (shstrtab)
|
||||
if ehdr.e_shstrndx >= ehdr.e_shnum:
|
||||
return
|
||||
let strtab_shdr = cast[ptr Elf64_Shdr](base + ehdr.e_shoff + uint64(ehdr.e_shstrndx) * uint64(ehdr.e_shentsize))
|
||||
if strtab_shdr.sh_offset + strtab_shdr.sh_size > file_len:
|
||||
return
|
||||
let strtab = cast[ptr UncheckedArray[byte]](base + strtab_shdr.sh_offset)
|
||||
|
||||
# Scan sections for .nexus.manifest
|
||||
let target = cstring(".nexus.manifest")
|
||||
for i in 0 ..< int(ehdr.e_shnum):
|
||||
let shdr = cast[ptr Elf64_Shdr](base + ehdr.e_shoff + uint64(i) * uint64(ehdr.e_shentsize))
|
||||
if shdr.sh_name < uint32(strtab_shdr.sh_size):
|
||||
let name_ptr = cast[ptr UncheckedArray[byte]](cast[uint64](strtab) + uint64(shdr.sh_name))
|
||||
let remaining = int(strtab_shdr.sh_size) - int(shdr.sh_name)
|
||||
if streq_n(name_ptr, target, remaining):
|
||||
# Found .nexus.manifest section
|
||||
if shdr.sh_offset + shdr.sh_size > file_len:
|
||||
return # Section data out of bounds
|
||||
if shdr.sh_size < uint64(sizeof(BkdlHeader)):
|
||||
return # Too small
|
||||
|
||||
let hdr = cast[ptr BkdlHeader](base + shdr.sh_offset)
|
||||
if hdr.magic != BKDL_MAGIC or hdr.version != BKDL_VERSION:
|
||||
kprintln("[Manifest] Invalid BKDL magic/version")
|
||||
return
|
||||
|
||||
let expected_size = uint64(sizeof(BkdlHeader)) + uint64(hdr.cap_count) * uint64(sizeof(CapDescriptor))
|
||||
if expected_size > shdr.sh_size:
|
||||
kprintln("[Manifest] BKDL cap_count exceeds section size")
|
||||
return
|
||||
|
||||
result.header = hdr
|
||||
result.caps = cast[ptr UncheckedArray[CapDescriptor]](base + shdr.sh_offset + uint64(sizeof(BkdlHeader)))
|
||||
result.count = int(hdr.cap_count)
|
||||
return
|
||||
|
||||
proc kexec*(path: string) =
|
||||
let entry = kload(path)
|
||||
if entry != 0:
|
||||
|
|
|
|||
|
|
@ -37,48 +37,8 @@ type
|
|||
p_memsz*: uint64
|
||||
p_align*: uint64
|
||||
|
||||
Elf64_Shdr* {.packed.} = object
|
||||
sh_name*: uint32
|
||||
sh_type*: uint32
|
||||
sh_flags*: uint64
|
||||
sh_addr*: uint64
|
||||
sh_offset*: uint64
|
||||
sh_size*: uint64
|
||||
sh_link*: uint32
|
||||
sh_info*: uint32
|
||||
sh_addralign*: uint64
|
||||
sh_entsize*: uint64
|
||||
|
||||
const
|
||||
PT_LOAD* = 1
|
||||
PT_NOTE* = 4
|
||||
PF_X* = 1
|
||||
PF_W* = 2
|
||||
PF_R* = 4
|
||||
|
||||
# SPEC-071: BKDL (Binary Manifest) types
|
||||
const
|
||||
BKDL_MAGIC* = 0x4E585553'u32 # "NXUS" (little-endian)
|
||||
BKDL_VERSION* = 1'u16
|
||||
|
||||
type
|
||||
BkdlHeader* {.packed.} = object
|
||||
magic*: uint32
|
||||
version*: uint16
|
||||
flags*: uint16
|
||||
signature*: array[64, uint8] # Ed25519 (unchecked in dev mode)
|
||||
pubkey_hash*: array[32, uint8] # SHA-256 of signing key
|
||||
cap_count*: uint16
|
||||
blob_size*: uint32
|
||||
entry_point*: uint64 # 0 = use ELF e_entry
|
||||
|
||||
CapDescriptor* {.packed.} = object
|
||||
cap_type*: uint8 # CapType enum value
|
||||
perms*: uint8 # Permission bitmask
|
||||
reserved*: uint16 # Alignment padding
|
||||
resource_id*: uint64 # SipHash of resource name
|
||||
|
||||
ManifestResult* = object
|
||||
header*: ptr BkdlHeader
|
||||
caps*: ptr UncheckedArray[CapDescriptor]
|
||||
count*: int
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
# core/rumpk/core/netswitch.nim
|
||||
# Phase 36.2: The Traffic Cop (Network Membrane Layer 2 Switch)
|
||||
#
|
||||
#
|
||||
# Responsibilities:
|
||||
# - Poll VirtIO-Net hardware for incoming packets
|
||||
# - Route RX packets to s_net_rx ring (Kernel -> Userland)
|
||||
|
|
@ -34,11 +34,6 @@ proc kprint(s: cstring) {.importc, cdecl.}
|
|||
proc kprint_hex(v: uint64) {.importc, cdecl.}
|
||||
proc get_now_ns(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}
|
||||
|
||||
# Project LibWeb: LWF Adapter (Zig FFI)
|
||||
proc lwf_validate(data: pointer, len: uint16): uint8 {.importc, cdecl.}
|
||||
|
||||
const ETHERTYPE_LWF = 0x4C57'u16 # "LW" — Libertaria Wire Frame
|
||||
|
||||
# Membrane Infrastructure (LwIP Glue)
|
||||
proc membrane_init*() {.importc, cdecl.}
|
||||
proc pump_membrane_stack*() {.importc, cdecl.}
|
||||
|
|
@ -78,39 +73,19 @@ proc netswitch_process_packet(pkt: IonPacket): bool =
|
|||
|
||||
case etype:
|
||||
of 0x0800, 0x0806, 0x86DD: # IPv4, ARP, IPv6
|
||||
# NOTE(LibWeb): IPv6 is the first-class citizen for sovereign mesh.
|
||||
# Most chapter nodes sit behind consumer NAT — IPv6 provides
|
||||
# end-to-end addressability without traversal hacks.
|
||||
# IPv4 is the fallback, not the default. Membrane/Transport
|
||||
# layer enforces preference order (IPv6 → IPv4).
|
||||
# Route to Legacy/LwIP Membrane
|
||||
if not chan_net_rx.send(pkt):
|
||||
# Ring full (Backpressure)
|
||||
ion_free(pkt)
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
of 0x88B5: # Sovereign UTCP (SPEC-700)
|
||||
# TODO: Route to dedicated UTCP channel
|
||||
# kprintln("[NetSwitch] UTCP Sovereign Packet Identified")
|
||||
ion_free(pkt)
|
||||
return true # Handled (dropped)
|
||||
|
||||
of ETHERTYPE_LWF: # Project LibWeb: Libertaria Wire Frame
|
||||
# Validate LWF magic before routing (Fast Drop)
|
||||
let lwf_data = cast[pointer](cast[uint64](pkt.data) + 14) # Skip Eth header
|
||||
let lwf_len = if pkt.len > 14: uint16(pkt.len - 14) else: 0'u16
|
||||
if lwf_len >= 88 and lwf_validate(lwf_data, lwf_len) == 1:
|
||||
# Valid LWF — route to dedicated LWF channel
|
||||
if not chan_lwf_rx.send(pkt):
|
||||
ion_free(pkt) # Backpressure
|
||||
return false
|
||||
return true
|
||||
else:
|
||||
# Invalid LWF frame — drop
|
||||
ion_free(pkt)
|
||||
return false
|
||||
|
||||
|
||||
else:
|
||||
# Drop unknown EtherTypes (Security/Sovereignty)
|
||||
ion_free(pkt)
|
||||
|
|
@ -118,30 +93,30 @@ proc netswitch_process_packet(pkt: IonPacket): bool =
|
|||
|
||||
proc fiber_netswitch_entry*() {.cdecl.} =
|
||||
kprintln("[NetSwitch] Fiber Entry - The Traffic Cop is ON DUTY")
|
||||
|
||||
|
||||
var rx_activity: bool = false
|
||||
var tx_activity: bool = false
|
||||
var rx_count: uint64 = 0
|
||||
var tx_count: uint64 = 0
|
||||
var last_stat_print: uint64 = 0
|
||||
|
||||
|
||||
while true:
|
||||
rx_activity = false
|
||||
tx_activity = false
|
||||
|
||||
|
||||
# 1. Drive the hardware poll (fills chan_netswitch_rx)
|
||||
virtio_net_poll()
|
||||
|
||||
|
||||
# [Cleaned] Driven by Userland now
|
||||
|
||||
|
||||
# 2. Consume from the Driver -> Switch internal ring
|
||||
var raw_pkt: IonPacket
|
||||
while chan_netswitch_rx.recv(raw_pkt):
|
||||
if netswitch_process_packet(raw_pkt):
|
||||
rx_activity = true
|
||||
inc rx_count
|
||||
|
||||
# 3. TX PATH: Userland -> Hardware (Legacy/LwIP)
|
||||
|
||||
# 3. TX PATH: Userland -> Hardware
|
||||
var tx_pkt: IonPacket
|
||||
while chan_net_tx.recv(tx_pkt):
|
||||
if tx_pkt.data != nil and tx_pkt.len > 0:
|
||||
|
|
@ -149,16 +124,7 @@ proc fiber_netswitch_entry*() {.cdecl.} =
|
|||
inc tx_count
|
||||
ion_free(tx_pkt)
|
||||
tx_activity = true
|
||||
|
||||
# 3b. TX PATH: LWF Egress (Project LibWeb)
|
||||
var lwf_pkt: IonPacket
|
||||
while chan_lwf_tx.recv(lwf_pkt):
|
||||
if lwf_pkt.data != nil and lwf_pkt.len > 0:
|
||||
virtio_net_send(cast[pointer](lwf_pkt.data), uint32(lwf_pkt.len))
|
||||
inc tx_count
|
||||
ion_free(lwf_pkt)
|
||||
tx_activity = true
|
||||
|
||||
|
||||
# 4. Periodically print stats
|
||||
let now = get_now_ns()
|
||||
if now - last_stat_print > 5000000000'u64: # 5 seconds
|
||||
|
|
|
|||
|
|
@ -161,24 +161,6 @@ proc emit_access_denied*(
|
|||
0, 0
|
||||
)
|
||||
|
||||
proc emit_policy_violation*(
|
||||
fiber_id: uint64,
|
||||
budget_ns: uint64,
|
||||
actual_ns: uint64,
|
||||
violation_count: uint32,
|
||||
cause_id: uint64 = 0
|
||||
): uint64 {.exportc, cdecl.} =
|
||||
## Emit budget violation event to STL (The Ratchet, M4.5)
|
||||
return stl_emit(
|
||||
uint16(EvPolicyViolation),
|
||||
fiber_id,
|
||||
budget_ns,
|
||||
cause_id,
|
||||
actual_ns,
|
||||
uint64(violation_count),
|
||||
0
|
||||
)
|
||||
|
||||
## Initialization
|
||||
proc init_stl_subsystem*() =
|
||||
## Initialize the STL subsystem (call from kmain)
|
||||
|
|
@ -198,24 +180,24 @@ proc stl_print_summary*() {.exportc, cdecl.} =
|
|||
kprint("[STL] I/O & Channels: "); kprint_hex(uint64(stats.io_events)); kprintln("")
|
||||
kprint("[STL] Memory: "); kprint_hex(uint64(stats.mem_events)); kprintln("")
|
||||
kprint("[STL] Security/Policy: "); kprint_hex(uint64(stats.security_events)); kprintln("")
|
||||
|
||||
|
||||
# Demonstrate Causal Graph for the last event
|
||||
if stats.total_events > 0:
|
||||
let last_id = uint64(stats.total_events - 1)
|
||||
let last_id = uint64(stats.total_events - 1)
|
||||
var lineage: LineageResult
|
||||
stl_trace_lineage(last_id, lineage)
|
||||
|
||||
|
||||
kprintln("\n[STL] Causal Graph Audit:");
|
||||
kprint("[STL] Target: "); kprint_hex(last_id); kprintln("")
|
||||
|
||||
|
||||
for i in 0..<lineage.count:
|
||||
let eid = lineage.event_ids[i]
|
||||
let ev_ptr = stl_lookup(eid)
|
||||
|
||||
|
||||
if i > 0: kprintln(" |")
|
||||
|
||||
|
||||
kprint(" +-- ["); kprint_hex(eid); kprint("] ")
|
||||
|
||||
|
||||
if ev_ptr != nil:
|
||||
# Kind is at offset 0 (2 bytes)
|
||||
let kind_val = cast[ptr uint16](ev_ptr)[]
|
||||
|
|
|
|||
|
|
@ -175,17 +175,10 @@ proc pty_write_slave*(fd: int, data: ptr byte, len: int): int {.exportc, cdecl.}
|
|||
if ring_push(pty.slave_to_master, pty.stm_head, pty.stm_tail, b):
|
||||
written += 1
|
||||
|
||||
# Mirror to UART console
|
||||
var c_buf: array[2, char]
|
||||
c_buf[0] = char(b)
|
||||
c_buf[1] = '\0'
|
||||
kprint(cast[cstring](addr c_buf[0]))
|
||||
|
||||
# Also render to FB terminal
|
||||
term_putc(char(b))
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
# Render frame after batch write
|
||||
if written > 0:
|
||||
|
|
|
|||
100
core/sched.nim
100
core/sched.nim
|
|
@ -22,38 +22,26 @@ import fiber
|
|||
# We need a centralized registry or a way to iterate.
|
||||
#
|
||||
# For the first pass, we can replicate the logic in kernel.nim which explicitly checks
|
||||
# the known fibers, but structured as the Spectrum loop.
|
||||
# the known fibers, but structured as the Spectrum loop.
|
||||
# Or we can make kernel.nim pass the fibers to us.
|
||||
#
|
||||
# Let's keep it simple and stateless in sched.nim if possible, or have it manage the queue.
|
||||
# Since kernel.nim holds the variables, sched.nim should probably define the *Strategy*
|
||||
# Since kernel.nim holds the variables, sched.nim should probably define the *Strategy*
|
||||
# and kernel.nim calls it, OR sched.nim should import kernel (circular!).
|
||||
#
|
||||
# Better: fiber.nim holds a linked list of fibers?
|
||||
# Better: fiber.nim holds a linked list of fibers?
|
||||
# Or sched.nim is just a helper module that kernel.nim uses.
|
||||
|
||||
# Let's define the Strategy here.
|
||||
|
||||
# To avoid circular imports, kernel.nim will likely INCLUDE sched.nim or sched.nim
|
||||
# will act on a passed context.
|
||||
# To avoid circular imports, kernel.nim will likely INCLUDE sched.nim or sched.nim
|
||||
# will act on a passed context.
|
||||
# BUT, SPEC-102 implies sched.nim *is* the logic.
|
||||
#
|
||||
# Let's define the Harmonic logic.
|
||||
# Let's define the Harmonic logic.
|
||||
# We need access to `current_fiber` (from fiber.nim) and `get_now_ns` (helper).
|
||||
|
||||
proc sched_get_now_ns*(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}
|
||||
proc console_write(p: pointer, len: csize_t) {.importc: "hal_console_write", cdecl.}
|
||||
proc uart_print_hex(v: uint64) {.importc: "uart_print_hex", cdecl.}
|
||||
proc uart_print_hex8(v: uint8) {.importc: "uart_print_hex8", cdecl.}
|
||||
|
||||
# M4.5: STL emission for budget violations (The Ratchet)
|
||||
proc emit_policy_violation*(fiber_id, budget_ns, actual_ns: uint64,
|
||||
violation_count: uint32, cause_id: uint64): uint64 {.importc, cdecl.}
|
||||
|
||||
# Forward declaration — implementation is in THE RATCHET section below
|
||||
proc sched_analyze_burst*(f: ptr FiberObject, burst_ns: uint64)
|
||||
|
||||
var photon_idx, matter_idx, gravity_idx, void_idx: int
|
||||
|
||||
# Forward declaration for channel data check (provided by kernel/channels)
|
||||
proc fiber_can_run_on_channels*(id: uint64, mask: uint64): bool {.importc, cdecl.}
|
||||
|
|
@ -70,22 +58,16 @@ proc is_runnable(f: ptr FiberObject, now: uint64): bool =
|
|||
|
||||
proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||
let now = sched_get_now_ns()
|
||||
|
||||
|
||||
# =========================================================
|
||||
# Phase 1: PHOTON (Hard Real-Time / Hardware Driven)
|
||||
# =========================================================
|
||||
var run_photon = false
|
||||
for i in 0..<fibers.len:
|
||||
let idx = (photon_idx + i) mod fibers.len
|
||||
let f = fibers[idx]
|
||||
for f in fibers:
|
||||
if f != nil and f.getSpectrum() == Spectrum.Photon:
|
||||
if is_runnable(f, now):
|
||||
if f != current_fiber:
|
||||
photon_idx = (idx + 1) mod fibers.len
|
||||
let t0 = sched_get_now_ns()
|
||||
switch(f)
|
||||
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
||||
return true
|
||||
switch(f); return true
|
||||
else:
|
||||
run_photon = true
|
||||
if run_photon: return true
|
||||
|
|
@ -94,17 +76,11 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
|||
# Phase 2: MATTER (Interactive / Latency Sensitive)
|
||||
# =========================================================
|
||||
var run_matter = false
|
||||
for i in 0..<fibers.len:
|
||||
let idx = (matter_idx + i) mod fibers.len
|
||||
let f = fibers[idx]
|
||||
for f in fibers:
|
||||
if f != nil and f.getSpectrum() == Spectrum.Matter:
|
||||
if is_runnable(f, now):
|
||||
if f != current_fiber:
|
||||
matter_idx = (idx + 1) mod fibers.len
|
||||
let t0 = sched_get_now_ns()
|
||||
switch(f)
|
||||
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
||||
return true
|
||||
switch(f); return true
|
||||
else:
|
||||
run_matter = true
|
||||
if run_matter: return true
|
||||
|
|
@ -113,17 +89,11 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
|||
# Phase 3: GRAVITY (Throughput / Background)
|
||||
# =========================================================
|
||||
var run_gravity = false
|
||||
for i in 0..<fibers.len:
|
||||
let idx = (gravity_idx + i) mod fibers.len
|
||||
let f = fibers[idx]
|
||||
for f in fibers:
|
||||
if f != nil and f.getSpectrum() == Spectrum.Gravity:
|
||||
if is_runnable(f, now):
|
||||
if f != current_fiber:
|
||||
gravity_idx = (idx + 1) mod fibers.len
|
||||
let t0 = sched_get_now_ns()
|
||||
switch(f)
|
||||
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
||||
return true
|
||||
switch(f); return true
|
||||
else:
|
||||
run_gravity = true
|
||||
if run_gravity: return true
|
||||
|
|
@ -131,20 +101,15 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
|||
# =========================================================
|
||||
# Phase 4: VOID (Scavenger)
|
||||
# =========================================================
|
||||
for i in 0..<fibers.len:
|
||||
let idx = (void_idx + i) mod fibers.len
|
||||
let f = fibers[idx]
|
||||
for f in fibers:
|
||||
if f != nil and f.getSpectrum() == Spectrum.Void:
|
||||
if is_runnable(f, now):
|
||||
if f != current_fiber:
|
||||
void_idx = (idx + 1) mod fibers.len
|
||||
let t0 = sched_get_now_ns()
|
||||
switch(f)
|
||||
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
||||
return true
|
||||
else:
|
||||
return true
|
||||
|
||||
|
||||
# =========================================================
|
||||
# THE SILENCE
|
||||
# =========================================================
|
||||
|
|
@ -159,25 +124,15 @@ proc sched_get_next_wakeup*(fibers: openArray[ptr FiberObject]): uint64 =
|
|||
if f != nil and f.sleep_until > now:
|
||||
if f.sleep_until < min_wakeup:
|
||||
min_wakeup = f.sleep_until
|
||||
|
||||
|
||||
return min_wakeup
|
||||
|
||||
# =========================================================
|
||||
# M4.5: Budget Defaults Per Spectrum Tier
|
||||
# =========================================================
|
||||
|
||||
proc default_budget_for_spectrum*(s: Spectrum): uint64 =
|
||||
## Return the default budget_ns for a given Spectrum tier
|
||||
case s
|
||||
of Spectrum.Photon: return 2_000_000'u64 # 2ms — hard real-time
|
||||
of Spectrum.Matter: return 10_000_000'u64 # 10ms — interactive
|
||||
of Spectrum.Gravity: return 50_000_000'u64 # 50ms — batch
|
||||
of Spectrum.Void: return 0'u64 # unlimited — scavenger
|
||||
|
||||
# =========================================================
|
||||
# THE RATCHET (Post-Execution Analysis)
|
||||
# =========================================================
|
||||
|
||||
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
||||
|
||||
proc sched_log(msg: string) =
|
||||
if msg.len > 0:
|
||||
console_write(unsafeAddr msg[0], csize_t(msg.len))
|
||||
|
|
@ -202,29 +157,20 @@ proc sched_analyze_burst*(f: ptr FiberObject, burst_ns: uint64) =
|
|||
## Analyze the burst duration of a fiber after it yields/switches
|
||||
## Implements the 3-strike rule for budget violations
|
||||
if f == nil: return
|
||||
|
||||
|
||||
f.last_burst_ns = burst_ns
|
||||
|
||||
|
||||
# Update moving average (exponential: 75% old, 25% new)
|
||||
if f.avg_burst == 0:
|
||||
f.avg_burst = burst_ns
|
||||
else:
|
||||
f.avg_burst = (f.avg_burst * 3 + burst_ns) div 4
|
||||
|
||||
|
||||
# Budget enforcement
|
||||
if f.budget_ns > 0 and burst_ns > f.budget_ns:
|
||||
f.violations += 1
|
||||
discard emit_policy_violation(f.id, f.budget_ns, burst_ns, f.violations, 0)
|
||||
console_write(cstring("[Ratchet] Budget violation fiber=0x"), 33)
|
||||
uart_print_hex(f.id)
|
||||
console_write(cstring(" burst="), 7)
|
||||
uart_print_hex(burst_ns)
|
||||
console_write(cstring(" budget="), 8)
|
||||
uart_print_hex(f.budget_ns)
|
||||
console_write(cstring(" strikes="), 9)
|
||||
uart_print_hex(uint64(f.violations))
|
||||
console_write(cstring("\n"), 1)
|
||||
|
||||
console_write(cstring("[Ratchet] Violation: fiber exceeded budget\n"), 42)
|
||||
|
||||
if f.violations >= 3:
|
||||
sched_demote(f)
|
||||
f.violations = 0 # Reset after demotion
|
||||
|
|
|
|||
33
hal/abi.zig
33
hal/abi.zig
|
|
@ -55,14 +55,9 @@ fn halt_impl() callconv(.c) noreturn {
|
|||
}
|
||||
}
|
||||
|
||||
const builtin = @import("builtin");
|
||||
|
||||
// Sovereign timer — canonical time source for the entire kernel
|
||||
extern fn rumpk_timer_now_ns() u64;
|
||||
|
||||
export fn hal_get_time_ns() u64 {
|
||||
return rumpk_timer_now_ns();
|
||||
}
|
||||
// =========================================================
|
||||
// Exports for Nim FFI
|
||||
// =========================================================
|
||||
|
||||
export fn rumpk_console_write(ptr: [*]const u8, len: usize) void {
|
||||
hal.console_write(ptr, len);
|
||||
|
|
@ -118,27 +113,17 @@ pub const cspace_check_perm = cspace.cspace_check_perm;
|
|||
pub const surface = @import("surface.zig");
|
||||
|
||||
comptime {
|
||||
// Force analysis — architecture-independent modules
|
||||
// Force analysis
|
||||
_ = @import("stubs.zig");
|
||||
_ = @import("mm.zig");
|
||||
_ = @import("channel.zig");
|
||||
_ = @import("uart.zig");
|
||||
_ = @import("virtio_block.zig");
|
||||
_ = @import("virtio_net.zig");
|
||||
_ = @import("virtio_pci.zig");
|
||||
_ = @import("ontology.zig");
|
||||
_ = @import("entry_riscv.zig");
|
||||
_ = @import("cspace.zig");
|
||||
_ = @import("surface.zig");
|
||||
_ = @import("initrd.zig");
|
||||
|
||||
// Architecture-specific modules
|
||||
if (builtin.cpu.arch == .riscv64) {
|
||||
_ = @import("mm.zig");
|
||||
_ = @import("virtio_block.zig");
|
||||
_ = @import("virtio_net.zig");
|
||||
_ = @import("virtio_pci.zig");
|
||||
_ = @import("entry_riscv.zig");
|
||||
} else if (builtin.cpu.arch == .aarch64) {
|
||||
_ = @import("entry_aarch64.zig");
|
||||
_ = @import("gic.zig");
|
||||
_ = @import("virtio_mmio.zig");
|
||||
_ = @import("virtio_block.zig");
|
||||
_ = @import("virtio_net.zig");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
//! SAFETY: All operations use atomic loads/stores with proper memory fences.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub const IonPacket = extern struct {
|
||||
data: u64,
|
||||
|
|
@ -42,8 +41,8 @@ pub fn Ring(comptime T: type) type {
|
|||
|
||||
// INVARIANT 1: The Handle Barrier
|
||||
fn validate_ring_ptr(ptr: u64) void {
|
||||
const min_valid: u64 = if (builtin.cpu.arch == .aarch64) 0x4000_0000 else 0x8000_0000;
|
||||
if (ptr < min_valid) {
|
||||
// 0x8000_0000 is kernel base, 0x8300_0000 is ION base.
|
||||
if (ptr < 0x8000_0000) {
|
||||
@panic("HAL: Invariant Violation - Invalid Ring Pointer");
|
||||
}
|
||||
}
|
||||
|
|
@ -73,11 +72,7 @@ fn popGeneric(comptime T: type, ring: *Ring(T), out_pkt: *T) bool {
|
|||
}
|
||||
|
||||
// Ensure we see data written by producer before reading it
|
||||
switch (builtin.cpu.arch) {
|
||||
.riscv64 => asm volatile ("fence r, rw" ::: .{ .memory = true }),
|
||||
.aarch64 => asm volatile ("dmb ld" ::: .{ .memory = true }),
|
||||
else => @compileError("unsupported arch"),
|
||||
}
|
||||
asm volatile ("fence r, rw" ::: .{ .memory = true });
|
||||
|
||||
out_pkt.* = ring.data[tail & ring.mask];
|
||||
const next = (tail + 1) & ring.mask;
|
||||
|
|
|
|||
|
|
@ -230,19 +230,6 @@ pub export fn cspace_check_perm(fiber_id: u64, slot: usize, perm_bits: u8) bool
|
|||
return cap.has_perm(perm);
|
||||
}
|
||||
|
||||
/// Check if fiber has Channel capability for given channel_id with required permission (C ABI)
|
||||
/// Scans all CSpace slots for a matching Channel capability by object_id.
|
||||
pub export fn cspace_check_channel(fiber_id: u64, channel_id: u64, perm_bits: u8) bool {
|
||||
const cs = cspace_get(fiber_id) orelse return false;
|
||||
const perm: CapPerms = @bitCast(perm_bits);
|
||||
for (&cs.slots) |*cap| {
|
||||
if (cap.cap_type == .Channel and cap.object_id == channel_id and cap.has_perm(perm)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unit tests
|
||||
test "Capability creation and validation" {
|
||||
const cap = Capability{
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ export fn rss_trap_handler(frame: *TrapFrame) void {
|
|||
const irq = PLIC_CLAIM.*;
|
||||
|
||||
if (irq == 10) { // UART0 is IRQ 10 on Virt machine
|
||||
uart.print("[IRQ] 10\n");
|
||||
// uart.print("[IRQ] 10\n");
|
||||
uart_input.poll_input();
|
||||
} else if (irq >= 32 and irq <= 35) {
|
||||
virtio_net.virtio_net_poll();
|
||||
|
|
@ -447,13 +447,6 @@ export fn hal_io_init() void {
|
|||
virtio_block.init();
|
||||
}
|
||||
|
||||
export fn hal_panic(msg: [*:0]const u8) callconv(.c) noreturn {
|
||||
uart.print("[HAL PANIC] ");
|
||||
uart.print(std.mem.span(msg));
|
||||
uart.print("\n");
|
||||
rumpk_halt();
|
||||
}
|
||||
|
||||
export fn rumpk_halt() noreturn {
|
||||
uart.print("[Rumpk RISC-V] Halting.\n");
|
||||
while (true) {
|
||||
|
|
|
|||
|
|
@ -205,10 +205,8 @@ pub fn create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64, phy
|
|||
try map_range(root, 0x40000000, 0x40000000, 0x10000000, PTE_R | PTE_W); // PCIe MMIO
|
||||
try map_range(root, 0x20000000, 0x20000000, 0x10000, PTE_R | PTE_W); // PTY Slave
|
||||
|
||||
// 4. Overlap stack with user access (Optional)
|
||||
if (stack_base != 0) {
|
||||
try map_range(root, stack_base, stack_base, stack_size, PTE_R | PTE_W | PTE_U);
|
||||
}
|
||||
// 4. Overlap stack with user access
|
||||
try map_range(root, stack_base, stack_base, stack_size, PTE_R | PTE_W | PTE_U);
|
||||
|
||||
// 5. Shared SysTable & Rings & User Slab (0x83000000) - Map 256KB (64 pages; covers up to 0x40000)
|
||||
var j: u64 = 0;
|
||||
|
|
|
|||
|
|
@ -194,12 +194,10 @@ pub export fn stl_init() void {
|
|||
stl_initialized = true;
|
||||
}
|
||||
|
||||
/// Sovereign timer — canonical time source for all kernel timestamps
|
||||
extern fn rumpk_timer_now_ns() u64;
|
||||
|
||||
/// Get current timestamp in nanoseconds since boot
|
||||
/// Get current timestamp (placeholder - will be replaced by HAL timer)
|
||||
fn get_timestamp_ns() u64 {
|
||||
return rumpk_timer_now_ns();
|
||||
// TODO: Integrate with HAL timer
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Emit event to STL (C ABI)
|
||||
|
|
|
|||
|
|
@ -15,9 +15,6 @@
|
|||
|
||||
const uart = @import("uart.zig");
|
||||
|
||||
// Sovereign timer — canonical time source for the entire kernel
|
||||
extern fn rumpk_timer_now_ns() u64;
|
||||
|
||||
// =========================================================
|
||||
// Heap Stubs (Bump Allocator with Block Headers)
|
||||
// =========================================================
|
||||
|
|
@ -140,9 +137,13 @@ export fn calloc(nmemb: usize, size: usize) ?*anyopaque {
|
|||
// =========================================================
|
||||
|
||||
export fn get_ticks() u32 {
|
||||
// Delegate to sovereign timer — single source of truth for all time
|
||||
const ns = rumpk_timer_now_ns();
|
||||
return @truncate(ns / 1_000_000); // ns → ms
|
||||
var time_val: u64 = 0;
|
||||
asm volatile ("rdtime %[ret]"
|
||||
: [ret] "=r" (time_val),
|
||||
);
|
||||
// QEMU 'virt' RISC-V timebase is 10MHz (10,000,000 Hz).
|
||||
// Convert to milliseconds: val / 10,000.
|
||||
return @truncate(time_val / 10000);
|
||||
}
|
||||
|
||||
// export fn rumpk_timer_set_ns(ns: u64) void {
|
||||
|
|
@ -159,10 +160,10 @@ export fn nexshell_main() void {
|
|||
}
|
||||
extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize;
|
||||
|
||||
// export fn exit(code: c_int) noreturn {
|
||||
// _ = code;
|
||||
// while (true) asm volatile ("wfi");
|
||||
// }
|
||||
export fn exit(code: c_int) noreturn {
|
||||
_ = code;
|
||||
while (true) asm volatile ("wfi");
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Atomic Stubs (To resolve linker errors with libcompiler_rt)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
//! DECISION(Alloc): Bump allocator chosen for simplicity and determinism.
|
||||
//! Memory is never reclaimed; system reboots to reset.
|
||||
|
||||
// const uart = @import("uart.zig");
|
||||
const uart = @import("uart.zig");
|
||||
|
||||
// =========================================================
|
||||
// Heap Stubs (Bump Allocator with Block Headers)
|
||||
|
|
@ -27,13 +27,11 @@ var heap_idx: usize = 0;
|
|||
var heap_init_done: bool = false;
|
||||
|
||||
export fn debug_print(s: [*]const u8, len: usize) void {
|
||||
_ = s;
|
||||
_ = len;
|
||||
// TODO: Use syscall for userland debug printing
|
||||
uart.print(s[0..len]);
|
||||
}
|
||||
|
||||
export fn kprint_hex(value: u64) void {
|
||||
_ = value;
|
||||
uart.print_hex(value);
|
||||
}
|
||||
|
||||
// Header structure (64 bytes aligned to match LwIP MEM_ALIGNMENT)
|
||||
|
|
@ -47,9 +45,9 @@ export fn malloc(size: usize) ?*anyopaque {
|
|||
|
||||
if (!heap_init_done) {
|
||||
if (heap_idx != 0) {
|
||||
// uart.print("[Alloc] WARNING: BSS NOT ZEROED! heap_idx: ");
|
||||
// uart.print_hex(heap_idx);
|
||||
// uart.print("\n");
|
||||
uart.print("[Alloc] WARNING: BSS NOT ZEROED! heap_idx: ");
|
||||
uart.print_hex(heap_idx);
|
||||
uart.print("\n");
|
||||
heap_idx = 0;
|
||||
}
|
||||
heap_init_done = true;
|
||||
|
|
@ -60,11 +58,11 @@ export fn malloc(size: usize) ?*anyopaque {
|
|||
const aligned_idx = (heap_idx + align_mask) & ~align_mask;
|
||||
|
||||
if (aligned_idx + total_needed > heap.len) {
|
||||
// uart.print("[Alloc] OOM! Size: ");
|
||||
// uart.print_hex(size);
|
||||
// uart.print(" Used: ");
|
||||
// uart.print_hex(heap_idx);
|
||||
// uart.print("\n");
|
||||
uart.print("[Alloc] OOM! Size: ");
|
||||
uart.print_hex(size);
|
||||
uart.print(" Used: ");
|
||||
uart.print_hex(heap_idx);
|
||||
uart.print("\n");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -139,29 +137,3 @@ export fn calloc(nmemb: usize, size: usize) ?*anyopaque {
|
|||
export fn get_ticks() u32 {
|
||||
return 0; // TODO: Implement real timer
|
||||
}
|
||||
|
||||
// export fn strlen(s: [*]const u8) usize {
|
||||
// var i: usize = 0;
|
||||
// while (s[i] != 0) : (i += 1) {}
|
||||
// return i;
|
||||
// }
|
||||
|
||||
// export fn fwrite(ptr: ?*anyopaque, size: usize, nmemb: usize, stream: ?*anyopaque) usize {
|
||||
// _ = ptr;
|
||||
// _ = size;
|
||||
// _ = nmemb;
|
||||
// _ = stream;
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// export fn fflush(stream: ?*anyopaque) c_int {
|
||||
// _ = stream;
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// export fn write(fd: c_int, buf: ?*anyopaque, count: usize) isize {
|
||||
// _ = fd;
|
||||
// _ = buf;
|
||||
// _ = count;
|
||||
// return 0;
|
||||
// }
|
||||
|
|
|
|||
94
hal/uart.zig
94
hal/uart.zig
|
|
@ -34,19 +34,9 @@ pub const NS16550A_LCR: usize = 0x03; // Line Control Register
|
|||
|
||||
// Input logic moved to uart_input.zig
|
||||
|
||||
// PL011 Additional Registers
|
||||
pub const PL011_IBRD: usize = 0x24; // Integer Baud Rate Divisor
|
||||
pub const PL011_FBRD: usize = 0x28; // Fractional Baud Rate Divisor
|
||||
pub const PL011_LCR_H: usize = 0x2C; // Line Control
|
||||
pub const PL011_CR: usize = 0x30; // Control
|
||||
pub const PL011_IMSC: usize = 0x38; // Interrupt Mask Set/Clear
|
||||
pub const PL011_ICR: usize = 0x44; // Interrupt Clear
|
||||
pub const PL011_RXFE: u32 = 1 << 4; // Receive FIFO Empty
|
||||
|
||||
pub fn init() void {
|
||||
switch (builtin.cpu.arch) {
|
||||
.riscv64 => init_riscv(),
|
||||
.aarch64 => init_aarch64(),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
|
@ -117,78 +107,6 @@ pub fn init_riscv() void {
|
|||
// uart_input.poll_input(); // We cannot call this here safely without dep
|
||||
}
|
||||
|
||||
pub fn init_aarch64() void {
|
||||
const base = PL011_BASE;
|
||||
|
||||
// 1. Disable UART during setup
|
||||
const cr: *volatile u32 = @ptrFromInt(base + PL011_CR);
|
||||
cr.* = 0;
|
||||
|
||||
// 2. Clear all pending interrupts
|
||||
const icr: *volatile u32 = @ptrFromInt(base + PL011_ICR);
|
||||
icr.* = 0x7FF;
|
||||
|
||||
// 3. Set baud rate (115200 @ 24MHz QEMU clock)
|
||||
// IBRD = 24000000 / (16 * 115200) = 13
|
||||
// FBRD = ((0.0208... * 64) + 0.5) = 1
|
||||
const ibrd: *volatile u32 = @ptrFromInt(base + PL011_IBRD);
|
||||
const fbrd: *volatile u32 = @ptrFromInt(base + PL011_FBRD);
|
||||
ibrd.* = 13;
|
||||
fbrd.* = 1;
|
||||
|
||||
// 4. Line Control: 8N1, FIFO enable
|
||||
const lcr_h: *volatile u32 = @ptrFromInt(base + PL011_LCR_H);
|
||||
lcr_h.* = (0x3 << 5) | (1 << 4); // WLEN=8bit, FEN=1
|
||||
|
||||
// 5. Enable receive interrupt
|
||||
const imsc: *volatile u32 = @ptrFromInt(base + PL011_IMSC);
|
||||
imsc.* = (1 << 4); // RXIM: Receive interrupt mask
|
||||
|
||||
// 6. Enable UART: TXE + RXE + UARTEN
|
||||
cr.* = (1 << 8) | (1 << 9) | (1 << 0); // TXE | RXE | UARTEN
|
||||
|
||||
// --- LOOPBACK TEST ---
|
||||
// PL011 has loopback via CR bit 7 (LBE)
|
||||
cr.* = cr.* | (1 << 7); // Enable loopback
|
||||
|
||||
// Write test byte
|
||||
const dr: *volatile u32 = @ptrFromInt(base + PL011_DR);
|
||||
const fr: *volatile u32 = @ptrFromInt(base + PL011_FR);
|
||||
|
||||
// Wait for TX not full
|
||||
while ((fr.* & PL011_TXFF) != 0) {}
|
||||
dr.* = 0xA5;
|
||||
|
||||
// Wait for RX not empty
|
||||
var timeout: usize = 1000000;
|
||||
while ((fr.* & PL011_RXFE) != 0 and timeout > 0) {
|
||||
timeout -= 1;
|
||||
}
|
||||
|
||||
var passed = false;
|
||||
var reason: []const u8 = "Timeout";
|
||||
|
||||
if ((fr.* & PL011_RXFE) == 0) {
|
||||
const val: u8 = @truncate(dr.*);
|
||||
if (val == 0xA5) {
|
||||
passed = true;
|
||||
} else {
|
||||
reason = "Data Mismatch";
|
||||
}
|
||||
}
|
||||
|
||||
// Disable loopback
|
||||
cr.* = cr.* & ~@as(u32, 1 << 7);
|
||||
|
||||
if (passed) {
|
||||
write_bytes("[UART] Loopback Test: PASS\n");
|
||||
} else {
|
||||
write_bytes("[UART] Loopback Test: FAIL (");
|
||||
write_bytes(reason);
|
||||
write_bytes(")\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn write_char_arm64(c: u8) void {
|
||||
const dr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_DR);
|
||||
const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR);
|
||||
|
|
@ -234,13 +152,6 @@ pub fn read_direct() ?u8 {
|
|||
return thr.*;
|
||||
}
|
||||
},
|
||||
.aarch64 => {
|
||||
const dr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_DR);
|
||||
const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR);
|
||||
if ((fr.* & PL011_RXFE) == 0) {
|
||||
return @truncate(dr.*);
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return null;
|
||||
|
|
@ -252,11 +163,6 @@ pub fn get_lsr() u8 {
|
|||
const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
|
||||
return lsr.*;
|
||||
},
|
||||
.aarch64 => {
|
||||
// Return PL011 flags register (low byte)
|
||||
const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR);
|
||||
return @truncate(fr.*);
|
||||
},
|
||||
else => return 0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ pub fn poll_input() void {
|
|||
// Only Kernel uses this
|
||||
const Kernel = struct {
|
||||
extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void;
|
||||
extern fn kprint(s: [*]const u8) void;
|
||||
};
|
||||
|
||||
switch (builtin.cpu.arch) {
|
||||
|
|
@ -35,9 +34,6 @@ pub fn poll_input() void {
|
|||
const byte = thr.*;
|
||||
const byte_arr = [1]u8{byte};
|
||||
|
||||
// DEBUG: Trace hardware read
|
||||
Kernel.kprint("[HW Read]\n");
|
||||
|
||||
// Forward to Kernel Input Channel
|
||||
Kernel.ion_push_stdin(&byte_arr, 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,14 +13,8 @@
|
|||
//! the request. Uses bounce-buffers to guarantee alignment.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const uart = @import("uart.zig");
|
||||
|
||||
// Comptime transport switch: PCI on RISC-V, MMIO on ARM64
|
||||
const transport_mod = if (builtin.cpu.arch == .aarch64)
|
||||
@import("virtio_mmio.zig")
|
||||
else
|
||||
@import("virtio_pci.zig");
|
||||
const pci = @import("virtio_pci.zig");
|
||||
|
||||
// External C/Zig stubs
|
||||
extern fn malloc(size: usize) ?*anyopaque;
|
||||
|
|
@ -52,43 +46,13 @@ pub fn init() void {
|
|||
}
|
||||
|
||||
pub const VirtioBlkDriver = struct {
|
||||
transport: transport_mod.VirtioTransport,
|
||||
transport: pci.VirtioTransport,
|
||||
v_desc: [*]volatile VirtioDesc,
|
||||
v_avail: *volatile VirtioAvail,
|
||||
v_used: *volatile VirtioUsed,
|
||||
queue_size: u16,
|
||||
|
||||
pub fn probe() ?VirtioBlkDriver {
|
||||
if (builtin.cpu.arch == .aarch64) {
|
||||
return probe_mmio();
|
||||
} else {
|
||||
return probe_pci();
|
||||
}
|
||||
}
|
||||
|
||||
fn probe_mmio() ?VirtioBlkDriver {
|
||||
const mmio = @import("virtio_mmio.zig");
|
||||
const base = mmio.find_device(2) orelse { // device_id=2 is block
|
||||
uart.print("[VirtIO] No VirtIO-Block MMIO device found\n");
|
||||
return null;
|
||||
};
|
||||
uart.print("[VirtIO] Found VirtIO-Block at MMIO 0x");
|
||||
uart.print_hex(base);
|
||||
uart.print("\n");
|
||||
var self = VirtioBlkDriver{
|
||||
.transport = transport_mod.VirtioTransport.init(base),
|
||||
.v_desc = undefined,
|
||||
.v_avail = undefined,
|
||||
.v_used = undefined,
|
||||
.queue_size = 0,
|
||||
};
|
||||
if (self.init_device()) {
|
||||
return self;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn probe_pci() ?VirtioBlkDriver {
|
||||
const PCI_ECAM_BASE: usize = 0x30000000;
|
||||
const bus: u8 = 0;
|
||||
const func: u8 = 0;
|
||||
|
|
@ -105,7 +69,7 @@ pub const VirtioBlkDriver = struct {
|
|||
uart.print_hex(i);
|
||||
uart.print(".0\n");
|
||||
var self = VirtioBlkDriver{
|
||||
.transport = transport_mod.VirtioTransport.init(addr),
|
||||
.transport = pci.VirtioTransport.init(addr),
|
||||
.v_desc = undefined,
|
||||
.v_avail = undefined,
|
||||
.v_used = undefined,
|
||||
|
|
@ -123,56 +87,29 @@ pub const VirtioBlkDriver = struct {
|
|||
if (!self.transport.probe()) return false;
|
||||
|
||||
self.transport.reset();
|
||||
self.transport.add_status(1); // ACKNOWLEDGE
|
||||
self.transport.add_status(2); // DRIVER
|
||||
|
||||
// Feature negotiation
|
||||
const dev_features = self.transport.get_device_features();
|
||||
_ = dev_features;
|
||||
// Accept no special features for block — just basic operation
|
||||
self.transport.set_driver_features(0);
|
||||
transport_mod.io_barrier();
|
||||
// FEATURES_OK only on modern (v2) transport
|
||||
if (self.transport.is_modern) {
|
||||
self.transport.add_status(8); // FEATURES_OK
|
||||
transport_mod.io_barrier();
|
||||
}
|
||||
self.transport.add_status(1);
|
||||
self.transport.add_status(2);
|
||||
|
||||
self.transport.select_queue(0);
|
||||
const max_count = self.transport.get_queue_size();
|
||||
// Cap queue size for memory efficiency
|
||||
const MAX_BLK_QUEUE: u16 = 128;
|
||||
const count = if (max_count > MAX_BLK_QUEUE) MAX_BLK_QUEUE else max_count;
|
||||
const count = self.transport.get_queue_size();
|
||||
|
||||
// [Desc] [Avail] [Used] (Simplified layout)
|
||||
const total = (count * 16) + (6 + count * 2) + 4096 + (6 + count * 8);
|
||||
const raw_ptr = malloc(total + 4096) orelse return false;
|
||||
const aligned = (@intFromPtr(raw_ptr) + 4095) & ~@as(usize, 4095);
|
||||
|
||||
// Zero out queue memory to ensure clean state
|
||||
const byte_ptr: [*]u8 = @ptrFromInt(aligned);
|
||||
for (0..total) |i| {
|
||||
byte_ptr[i] = 0;
|
||||
}
|
||||
|
||||
self.v_desc = @ptrFromInt(aligned);
|
||||
self.v_avail = @ptrFromInt(aligned + (count * 16));
|
||||
self.v_used = @ptrFromInt(aligned + (count * 16) + (6 + count * 2) + 4096);
|
||||
self.queue_size = count;
|
||||
|
||||
// Ensure avail/used rings start clean
|
||||
self.v_avail.flags = 0;
|
||||
self.v_avail.idx = 0;
|
||||
self.v_used.flags = 0;
|
||||
|
||||
if (self.transport.is_modern) {
|
||||
self.transport.setup_modern_queue(aligned, aligned + (count * 16), @intFromPtr(self.v_used));
|
||||
} else {
|
||||
self.transport.set_queue_size(count);
|
||||
self.transport.setup_legacy_queue(@intCast(aligned >> 12));
|
||||
}
|
||||
|
||||
self.transport.add_status(4); // DRIVER_OK
|
||||
self.transport.add_status(4);
|
||||
global_blk = self.*;
|
||||
|
||||
uart.print("[VirtIO-Blk] Device Ready. Queue Size: ");
|
||||
|
|
@ -214,26 +151,15 @@ pub const VirtioBlkDriver = struct {
|
|||
// Submit to Avail Ring
|
||||
const ring = @as([*]volatile u16, @ptrFromInt(@intFromPtr(self.v_avail) + 4));
|
||||
ring[self.v_avail.idx % self.queue_size] = 0; // Head of chain
|
||||
const expected_used = self.v_used.idx +% 1;
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||
self.v_avail.idx +%= 1;
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||
|
||||
self.transport.notify(0);
|
||||
|
||||
// Wait for device (Polling — wait until used ring advances)
|
||||
var timeout: usize = 0;
|
||||
while (self.v_used.idx != expected_used) {
|
||||
transport_mod.io_barrier();
|
||||
timeout += 1;
|
||||
if (timeout > 100_000_000) {
|
||||
uart.print("[VirtIO-Blk] READ TIMEOUT! used.idx=");
|
||||
uart.print_hex(self.v_used.idx);
|
||||
uart.print(" expected=");
|
||||
uart.print_hex(expected_used);
|
||||
uart.print("\n");
|
||||
return error.DiskError;
|
||||
}
|
||||
// Wait for device (Polling)
|
||||
while (self.v_used.idx == 0) {
|
||||
asm volatile ("nop");
|
||||
}
|
||||
|
||||
if (status != 0) return error.DiskError;
|
||||
|
|
@ -264,22 +190,14 @@ pub const VirtioBlkDriver = struct {
|
|||
|
||||
const ring = @as([*]volatile u16, @ptrFromInt(@intFromPtr(self.v_avail) + 4));
|
||||
ring[self.v_avail.idx % self.queue_size] = 3;
|
||||
const expected_used = self.v_used.idx +% 1;
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||
self.v_avail.idx +%= 1;
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||
|
||||
self.transport.notify(0);
|
||||
|
||||
// Wait for device (Polling — wait until used ring advances)
|
||||
var timeout: usize = 0;
|
||||
while (self.v_used.idx != expected_used) {
|
||||
transport_mod.io_barrier();
|
||||
timeout += 1;
|
||||
if (timeout > 100_000_000) {
|
||||
uart.print("[VirtIO-Blk] WRITE TIMEOUT!\n");
|
||||
return error.DiskError;
|
||||
}
|
||||
while (status == 0xFF) {
|
||||
asm volatile ("nop");
|
||||
}
|
||||
|
||||
if (status != 0) return error.DiskError;
|
||||
|
|
|
|||
|
|
@ -14,14 +14,8 @@
|
|||
//! to ensure correct synchronization with the virtual device.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const uart = @import("uart.zig");
|
||||
|
||||
// Comptime transport switch: PCI on RISC-V, MMIO on ARM64
|
||||
const transport_mod = if (builtin.cpu.arch == .aarch64)
|
||||
@import("virtio_mmio.zig")
|
||||
else
|
||||
@import("virtio_pci.zig");
|
||||
const pci = @import("virtio_pci.zig");
|
||||
|
||||
// VirtIO Feature Bits
|
||||
const VIRTIO_F_VERSION_1 = 32;
|
||||
|
|
@ -123,24 +117,47 @@ pub export fn rumpk_net_init() void {
|
|||
}
|
||||
|
||||
pub const VirtioNetDriver = struct {
|
||||
transport: transport_mod.VirtioTransport,
|
||||
transport: pci.VirtioTransport,
|
||||
irq: u32,
|
||||
rx_queue: ?*Virtqueue = null,
|
||||
tx_queue: ?*Virtqueue = null,
|
||||
|
||||
pub fn get_mac(self: *VirtioNetDriver, out: [*]u8) void {
|
||||
uart.print("[VirtIO-Net] Reading MAC from device config...\n");
|
||||
for (0..6) |i| {
|
||||
out[i] = self.transport.get_device_config_byte(i);
|
||||
uart.print_hex8(out[i]);
|
||||
if (i < 5) uart.print(":");
|
||||
uart.print("[VirtIO-Net] Reading MAC from device_cfg...\n");
|
||||
if (self.transport.is_modern) {
|
||||
// Use device_cfg directly - this is the VirtIO-Net specific config
|
||||
if (self.transport.device_cfg) |cfg| {
|
||||
const ptr: [*]volatile u8 = @ptrCast(cfg);
|
||||
uart.print(" DeviceCfg at: ");
|
||||
uart.print_hex(@intFromPtr(cfg));
|
||||
uart.print("\n MAC bytes: ");
|
||||
|
||||
for (0..6) |i| {
|
||||
out[i] = ptr[i];
|
||||
uart.print_hex8(ptr[i]);
|
||||
if (i < 5) uart.print(":");
|
||||
}
|
||||
uart.print("\n");
|
||||
} else {
|
||||
uart.print(" ERROR: device_cfg is null!\n");
|
||||
// Fallback to zeros
|
||||
for (0..6) |i| {
|
||||
out[i] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Legacy
|
||||
// Device Config starts at offset 20.
|
||||
const base = self.transport.legacy_bar + 20;
|
||||
for (0..6) |i| {
|
||||
out[i] = @as(*volatile u8, @ptrFromInt(base + i)).*;
|
||||
}
|
||||
}
|
||||
uart.print("\n");
|
||||
}
|
||||
|
||||
pub fn init(base: usize, irq_num: u32) VirtioNetDriver {
|
||||
return .{
|
||||
.transport = transport_mod.VirtioTransport.init(base),
|
||||
.transport = pci.VirtioTransport.init(base),
|
||||
.irq = irq_num,
|
||||
.rx_queue = null,
|
||||
.tx_queue = null,
|
||||
|
|
@ -148,32 +165,6 @@ pub const VirtioNetDriver = struct {
|
|||
}
|
||||
|
||||
pub fn probe() ?VirtioNetDriver {
|
||||
if (builtin.cpu.arch == .aarch64) {
|
||||
return probe_mmio();
|
||||
} else {
|
||||
return probe_pci();
|
||||
}
|
||||
}
|
||||
|
||||
fn probe_mmio() ?VirtioNetDriver {
|
||||
uart.print("[VirtIO] Probing MMIO for networking device...\n");
|
||||
const mmio = @import("virtio_mmio.zig");
|
||||
const base = mmio.find_device(1) orelse { // device_id=1 is net
|
||||
uart.print("[VirtIO] No VirtIO-Net MMIO device found\n");
|
||||
return null;
|
||||
};
|
||||
uart.print("[VirtIO] Found VirtIO-Net at MMIO 0x");
|
||||
uart.print_hex(base);
|
||||
uart.print("\n");
|
||||
const irq = mmio.slot_irq(base);
|
||||
var self = VirtioNetDriver.init(base, irq);
|
||||
if (self.init_device()) {
|
||||
return self;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn probe_pci() ?VirtioNetDriver {
|
||||
uart.print("[VirtIO] Probing PCI for networking device...\n");
|
||||
const PCI_ECAM_BASE: usize = 0x30000000;
|
||||
const bus: u8 = 0;
|
||||
|
|
@ -222,22 +213,52 @@ pub const VirtioNetDriver = struct {
|
|||
self.transport.add_status(VIRTIO_CONFIG_S_ACKNOWLEDGE);
|
||||
self.transport.add_status(VIRTIO_CONFIG_S_DRIVER);
|
||||
|
||||
// 4. Feature Negotiation (unified across PCI and MMIO)
|
||||
{
|
||||
// 4. Feature Negotiation
|
||||
if (self.transport.is_modern) {
|
||||
uart.print("[VirtIO] Starting feature negotiation...\n");
|
||||
const dev_features = self.transport.get_device_features();
|
||||
|
||||
if (self.transport.common_cfg == null) {
|
||||
uart.print("[VirtIO] ERROR: common_cfg is null!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const cfg = self.transport.common_cfg.?;
|
||||
uart.print("[VirtIO] common_cfg addr: ");
|
||||
uart.print_hex(@intFromPtr(cfg));
|
||||
uart.print("\n");
|
||||
|
||||
uart.print("[VirtIO] Reading device features...\n");
|
||||
// Read Device Features (Page 0)
|
||||
cfg.device_feature_select = 0;
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
const f_low = cfg.device_feature;
|
||||
|
||||
// Read Device Features (Page 1)
|
||||
cfg.device_feature_select = 1;
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
const f_high = cfg.device_feature;
|
||||
|
||||
uart.print("[VirtIO] Device Features: ");
|
||||
uart.print_hex(dev_features);
|
||||
uart.print_hex(f_low);
|
||||
uart.print(" ");
|
||||
uart.print_hex(f_high);
|
||||
uart.print("\n");
|
||||
|
||||
// Accept VERSION_1 (Modern) and MAC
|
||||
const accept: u64 = (1 << VIRTIO_NET_F_MAC) |
|
||||
(@as(u64, 1) << VIRTIO_F_VERSION_1);
|
||||
self.transport.set_driver_features(accept);
|
||||
transport_mod.io_barrier();
|
||||
const accept_low: u32 = (1 << VIRTIO_NET_F_MAC);
|
||||
const accept_high: u32 = (1 << (VIRTIO_F_VERSION_1 - 32));
|
||||
|
||||
uart.print("[VirtIO] Writing driver features...\n");
|
||||
cfg.driver_feature_select = 0;
|
||||
cfg.driver_feature = accept_low;
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
cfg.driver_feature_select = 1;
|
||||
cfg.driver_feature = accept_high;
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
|
||||
uart.print("[VirtIO] Checking feature negotiation...\n");
|
||||
self.transport.add_status(VIRTIO_CONFIG_S_FEATURES_OK);
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
if ((self.transport.get_status() & VIRTIO_CONFIG_S_FEATURES_OK) == 0) {
|
||||
uart.print("[VirtIO] Feature negotiation failed!\n");
|
||||
return false;
|
||||
|
|
@ -246,15 +267,10 @@ pub const VirtioNetDriver = struct {
|
|||
}
|
||||
// 5. Setup RX Queue (0)
|
||||
self.transport.select_queue(0);
|
||||
const rx_max = self.transport.get_queue_size();
|
||||
// Cap queue size to avoid ION pool exhaustion (MMIO v1 reports 1024)
|
||||
const MAX_QUEUE: u16 = 256;
|
||||
const rx_count = if (rx_max > MAX_QUEUE) MAX_QUEUE else rx_max;
|
||||
const rx_count = self.transport.get_queue_size();
|
||||
uart.print("[VirtIO] RX Queue Size: ");
|
||||
uart.print_hex(rx_count);
|
||||
uart.print(" (max: ");
|
||||
uart.print_hex(rx_max);
|
||||
uart.print(")\n");
|
||||
uart.print("\n");
|
||||
|
||||
if (rx_count == 0 or rx_count == 0xFFFF) {
|
||||
uart.print("[VirtIO] Invalid RX Queue Size. Aborting.\n");
|
||||
|
|
@ -272,13 +288,10 @@ pub const VirtioNetDriver = struct {
|
|||
|
||||
// 6. Setup TX Queue (1)
|
||||
self.transport.select_queue(1);
|
||||
const tx_max = self.transport.get_queue_size();
|
||||
const tx_count = if (tx_max > MAX_QUEUE) MAX_QUEUE else tx_max;
|
||||
const tx_count = self.transport.get_queue_size();
|
||||
uart.print("[VirtIO] TX Queue Size: ");
|
||||
uart.print_hex(tx_count);
|
||||
uart.print(" (max: ");
|
||||
uart.print_hex(tx_max);
|
||||
uart.print(")\n");
|
||||
uart.print("\n");
|
||||
|
||||
if (tx_count == 0 or tx_count == 0xFFFF) {
|
||||
uart.print("[VirtIO] Invalid TX Queue Size. Aborting.\n");
|
||||
|
|
@ -379,11 +392,11 @@ pub const VirtioNetDriver = struct {
|
|||
q_ptr.avail.flags = 0;
|
||||
q_ptr.used.flags = 0;
|
||||
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||
|
||||
if (is_rx) {
|
||||
q_ptr.avail.idx = count;
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||
}
|
||||
|
||||
const phys_addr = aligned_addr;
|
||||
|
|
@ -391,7 +404,6 @@ pub const VirtioNetDriver = struct {
|
|||
if (self.transport.is_modern) {
|
||||
self.transport.setup_modern_queue(phys_addr, phys_addr + desc_size, phys_addr + used_offset);
|
||||
} else {
|
||||
self.transport.set_queue_size(count);
|
||||
const pfn = @as(u32, @intCast(phys_addr >> 12));
|
||||
self.transport.setup_legacy_queue(pfn);
|
||||
}
|
||||
|
|
@ -401,7 +413,7 @@ pub const VirtioNetDriver = struct {
|
|||
}
|
||||
|
||||
pub fn rx_poll(self: *VirtioNetDriver, q: *Virtqueue) void {
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
|
||||
const used = q.used;
|
||||
const hw_idx = used.idx;
|
||||
|
|
@ -461,7 +473,7 @@ pub const VirtioNetDriver = struct {
|
|||
q.desc[desc_idx].addr = new_phys;
|
||||
q.ids[desc_idx] = new_id;
|
||||
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
|
||||
avail_ring[q.avail.idx % q.num] = @intCast(desc_idx);
|
||||
q.avail.idx +%= 1;
|
||||
|
|
@ -474,14 +486,14 @@ pub const VirtioNetDriver = struct {
|
|||
}
|
||||
|
||||
if (replenished) {
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
self.transport.notify(0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tx_poll(self: *VirtioNetDriver, q: *Virtqueue) void {
|
||||
_ = self;
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
const used = q.used;
|
||||
const used_idx = used.idx;
|
||||
const used_ring = get_used_ring(used);
|
||||
|
|
@ -516,11 +528,11 @@ pub const VirtioNetDriver = struct {
|
|||
|
||||
q.ids[idx] = slab_id;
|
||||
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
avail_ring[idx] = @intCast(idx);
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
q.avail.idx +%= 1;
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
|
||||
self.transport.notify(1);
|
||||
uart.print("[VirtIO TX-Slab] Sent ");
|
||||
|
|
@ -567,11 +579,11 @@ pub const VirtioNetDriver = struct {
|
|||
desc.len = @intCast(header_len + copy_len);
|
||||
desc.flags = 0;
|
||||
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
avail_ring[idx] = @intCast(desc_idx);
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
q.avail.idx +%= 1;
|
||||
transport_mod.io_barrier();
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
|
||||
self.transport.notify(1);
|
||||
uart.print("[VirtIO TX] Queued & Notified Len=");
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
//! Dynamically assigns BARs (Base Address Registers) if unassigned by firmware.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const uart = @import("uart.zig");
|
||||
|
||||
// PCI Config Offsets
|
||||
|
|
@ -317,17 +316,6 @@ pub const VirtioTransport = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_queue_size(self: *VirtioTransport, size: u16) void {
|
||||
// PCI legacy: queue size is read-only (device sets it)
|
||||
// Modern: could set via common_cfg.queue_size
|
||||
if (self.is_modern) {
|
||||
if (self.common_cfg) |cfg| {
|
||||
cfg.queue_size = size;
|
||||
}
|
||||
}
|
||||
// Legacy PCI: queue size is fixed by device, no register to write
|
||||
}
|
||||
|
||||
pub fn setup_legacy_queue(self: *VirtioTransport, pfn: u32) void {
|
||||
// Only for legacy
|
||||
@as(*volatile u32, @ptrFromInt(self.legacy_bar + 0x08)).* = pfn;
|
||||
|
|
@ -357,65 +345,8 @@ pub const VirtioTransport = struct {
|
|||
notify_ptr.* = queue_idx;
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Unified Accessor API (matches MMIO transport)
|
||||
// =========================================================
|
||||
|
||||
pub fn get_device_features(self: *VirtioTransport) u64 {
|
||||
if (self.is_modern) {
|
||||
const cfg = self.common_cfg.?;
|
||||
cfg.device_feature_select = 0;
|
||||
io_barrier();
|
||||
const low: u64 = cfg.device_feature;
|
||||
cfg.device_feature_select = 1;
|
||||
io_barrier();
|
||||
const high: u64 = cfg.device_feature;
|
||||
return (high << 32) | low;
|
||||
} else {
|
||||
// Legacy: features at offset 0x00 (32-bit only)
|
||||
return @as(*volatile u32, @ptrFromInt(self.legacy_bar + 0x00)).*;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_driver_features(self: *VirtioTransport, features: u64) void {
|
||||
if (self.is_modern) {
|
||||
const cfg = self.common_cfg.?;
|
||||
cfg.driver_feature_select = 0;
|
||||
cfg.driver_feature = @truncate(features);
|
||||
io_barrier();
|
||||
cfg.driver_feature_select = 1;
|
||||
cfg.driver_feature = @truncate(features >> 32);
|
||||
io_barrier();
|
||||
} else {
|
||||
// Legacy: guest features at offset 0x04 (32-bit only)
|
||||
@as(*volatile u32, @ptrFromInt(self.legacy_bar + 0x04)).* = @truncate(features);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_config_byte(self: *VirtioTransport, offset: usize) u8 {
|
||||
if (self.is_modern) {
|
||||
if (self.device_cfg) |cfg| {
|
||||
const ptr: [*]volatile u8 = @ptrCast(cfg);
|
||||
return ptr[offset];
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
// Legacy: device config starts at offset 20
|
||||
return @as(*volatile u8, @ptrFromInt(self.legacy_bar + 20 + offset)).*;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Arch-safe memory barrier for VirtIO I/O ordering.
|
||||
pub inline fn io_barrier() void {
|
||||
switch (builtin.cpu.arch) {
|
||||
.riscv64 => asm volatile ("fence" ::: .{ .memory = true }),
|
||||
.aarch64 => asm volatile ("dmb sy" ::: .{ .memory = true }),
|
||||
else => @compileError("unsupported arch"),
|
||||
}
|
||||
}
|
||||
|
||||
// Modern Config Structure Layout
|
||||
pub const VirtioPciCommonCfg = extern struct {
|
||||
device_feature_select: u32,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,7 @@
|
|||
// SPDX-License-Identifier: LSL-1.0
|
||||
// Copyright (c) 2026 Markus Maiwald
|
||||
// Stewardship: Self Sovereign Society Foundation
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
// Freestanding FILE stubs (Nim's system.nim references stderr)
|
||||
struct _FILE { int dummy; };
|
||||
typedef struct _FILE FILE;
|
||||
static FILE _stdin_placeholder;
|
||||
static FILE _stdout_placeholder;
|
||||
static FILE _stderr_placeholder;
|
||||
FILE *stdin = &_stdin_placeholder;
|
||||
FILE *stdout = &_stdout_placeholder;
|
||||
FILE *stderr = &_stderr_placeholder;
|
||||
|
||||
// Types needed for stubs
|
||||
typedef int32_t pid_t;
|
||||
typedef int32_t uid_t;
|
||||
|
|
@ -28,45 +15,12 @@ struct stat {
|
|||
|
||||
int errno = 0;
|
||||
|
||||
void* memset(void* s, int c, size_t n);
|
||||
|
||||
// Basic memory stubs (Bump Allocator) - USERLAND ONLY
|
||||
// Kernel gets malloc/free/calloc from stubs.zig
|
||||
#ifdef RUMPK_USER
|
||||
static uint8_t heap[4 * 1024 * 1024]; // 4MB Heap
|
||||
static size_t heap_idx = 0;
|
||||
|
||||
void* malloc(size_t size) {
|
||||
if (size == 0) return NULL;
|
||||
size = (size + 7) & ~7; // Align to 8 bytes
|
||||
if (heap_idx + size > sizeof(heap)) return NULL;
|
||||
void* ptr = &heap[heap_idx];
|
||||
heap_idx += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void free(void* ptr) {
|
||||
(void)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;
|
||||
}
|
||||
#endif // RUMPK_USER
|
||||
|
||||
// Forward declarations
|
||||
int write(int fd, const void *buf, size_t count);
|
||||
int read(int fd, void *buf, size_t count);
|
||||
int open(const char *pathname, int flags, ...);
|
||||
int close(int fd);
|
||||
|
||||
void* memset(void* s, int c, size_t n);
|
||||
|
||||
// Basic memory stubs
|
||||
// Basic memory stubs (Bump Allocator)
|
||||
extern void* malloc(size_t size);
|
||||
extern void free(void* ptr);
|
||||
|
||||
// Forward declare memset (defined below)
|
||||
void* memset(void* s, int c, size_t n);
|
||||
|
||||
// LwIP Panic Handler (for Membrane stack)
|
||||
extern void console_write(const void* p, size_t len);
|
||||
|
|
@ -90,41 +44,8 @@ int strncmp(const char *s1, const char *s2, size_t n) {
|
|||
int atoi(const char* nptr) { return 0; }
|
||||
|
||||
double strtod(const char* nptr, char** endptr) {
|
||||
const char *p = nptr;
|
||||
double result = 0.0;
|
||||
double sign = 1.0;
|
||||
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
|
||||
if (*p == '-') { sign = -1.0; p++; }
|
||||
else if (*p == '+') { p++; }
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
result = result * 10.0 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
if (*p == '.') {
|
||||
double frac = 0.1;
|
||||
p++;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
result += (*p - '0') * frac;
|
||||
frac *= 0.1;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
if (*p == 'e' || *p == 'E') {
|
||||
p++;
|
||||
int exp_sign = 1, exp_val = 0;
|
||||
if (*p == '-') { exp_sign = -1; p++; }
|
||||
else if (*p == '+') { p++; }
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
exp_val = exp_val * 10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
double m = 1.0;
|
||||
for (int i = 0; i < exp_val; i++)
|
||||
m = exp_sign > 0 ? m * 10.0 : m * 0.1;
|
||||
result *= m;
|
||||
}
|
||||
if (endptr) *endptr = (char*)p;
|
||||
return sign * result;
|
||||
if (endptr) *endptr = (char*)nptr;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double pow(double x, double y) { return 0.0; }
|
||||
|
|
@ -134,42 +55,8 @@ double log10(double x) { return 0.0; }
|
|||
|
||||
#ifdef RUMPK_KERNEL
|
||||
extern long k_handle_syscall(long nr, long a0, long a1, long a2);
|
||||
extern uint64_t hal_get_time_ns(void);
|
||||
extern void hal_console_write(const void* p, size_t len);
|
||||
extern void hal_panic(const char* msg);
|
||||
uint64_t syscall_get_time_ns(void) { return hal_get_time_ns(); }
|
||||
void syscall_panic(void) { while(1); }
|
||||
#endif
|
||||
|
||||
// Support for Nim system.nim
|
||||
#ifndef OMIT_EXIT
|
||||
void exit(int status) {
|
||||
#ifdef RUMPK_KERNEL
|
||||
while(1);
|
||||
#else
|
||||
syscall(1, (long)status, 0, 0); // SYS_EXIT
|
||||
while(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void abort(void) {
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t fwrite(const void* ptr, size_t size, size_t nmemb, void* stream) {
|
||||
#ifdef RUMPK_KERNEL
|
||||
hal_console_write(ptr, size * nmemb);
|
||||
#else
|
||||
write(1, ptr, size * nmemb); // Forward to stdout
|
||||
#endif
|
||||
return nmemb;
|
||||
}
|
||||
|
||||
int fflush(void* stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long syscall(long nr, long a0, long a1, long a2) {
|
||||
#ifdef RUMPK_KERNEL
|
||||
return k_handle_syscall(nr, a0, a1, a2);
|
||||
|
|
@ -335,7 +222,11 @@ int printf(const char *format, ...) {
|
|||
return res;
|
||||
}
|
||||
|
||||
// REDUNDANT REMOVED
|
||||
int fwrite(const void *ptr, size_t size, size_t nmemb, void *stream) {
|
||||
return write(1, ptr, size * nmemb);
|
||||
}
|
||||
|
||||
int fflush(void *stream) { return 0; }
|
||||
|
||||
// System stubs
|
||||
extern void nexus_yield(void);
|
||||
|
|
@ -379,16 +270,6 @@ int memcmp(const void *s1, const void *s2, size_t n) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef RUMPK_USER
|
||||
void *memchr(const void *s, int c, size_t n) {
|
||||
const unsigned char *p = (const unsigned char *)s;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (p[i] == (unsigned char)c) return (void *)(p + i);
|
||||
}
|
||||
return (void *)0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void *memmove(void *dest, const void *src, size_t n) {
|
||||
char *d = (char *)dest;
|
||||
const char *s = (const char *)src;
|
||||
|
|
@ -462,10 +343,13 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
|||
|
||||
|
||||
#ifdef RUMPK_KERNEL
|
||||
// Kernel Mode: Direct UART
|
||||
extern void hal_console_write(const char* ptr, size_t len);
|
||||
void console_write(const void* p, size_t len) {
|
||||
hal_console_write(p, len);
|
||||
}
|
||||
#else
|
||||
// User Mode: Syscall
|
||||
void console_write(const void* p, size_t len) {
|
||||
write(1, p, len);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,9 @@
|
|||
|
||||
import ../../core/ion
|
||||
|
||||
const SYS_TABLE_ADDR = when defined(arm64): 0x50000000'u64
|
||||
else: 0x83000000'u64
|
||||
const SYS_TABLE_ADDR = 0x83000000'u64
|
||||
|
||||
const
|
||||
const
|
||||
GAP = 10 # Pixels between windows
|
||||
FOCUS_COLOR = 0xFF00FFFF'u32 # Cyan border for focused window
|
||||
BG_COLOR = 0xFF101020'u32 # Dark Blue background
|
||||
|
|
@ -74,7 +73,7 @@ proc draw_border(x, y, w, h: int, color: uint32) =
|
|||
if ix >= 0 and ix < fb_w:
|
||||
if y >= 0 and y < fb_h: fb[y * fb_w + ix] = color
|
||||
if y + h - 1 >= 0 and y + h - 1 < fb_h: fb[(y + h - 1) * fb_w + ix] = color
|
||||
|
||||
|
||||
for iy in y ..< y + h:
|
||||
if iy >= 0 and iy < fb_h:
|
||||
if x >= 0 and x < fb_w: fb[iy * fb_w + x] = color
|
||||
|
|
@ -108,17 +107,17 @@ proc render_frame*(c: var Compositor) =
|
|||
let screen_x = s.x - c.view_x
|
||||
if screen_x + s.width < 0 or screen_x >= int(sys.fb_width):
|
||||
continue
|
||||
|
||||
|
||||
let screen_y = (int(sys.fb_height) - s.height) div 2
|
||||
blit_surface(s, screen_x, screen_y)
|
||||
|
||||
|
||||
if s.focused:
|
||||
draw_border(screen_x, screen_y, s.width, s.height, FOCUS_COLOR)
|
||||
|
||||
proc create_surface*(w, h: int): int32 =
|
||||
let id = hal_surface_alloc(uint32(w), uint32(h))
|
||||
if id < 0: return -1
|
||||
|
||||
|
||||
let p = hal_surface_get_ptr(id)
|
||||
if p == nil: return -1
|
||||
|
||||
|
|
@ -128,11 +127,11 @@ proc create_surface*(w, h: int): int32 =
|
|||
s.width = w
|
||||
s.height = h
|
||||
s.dirty = true
|
||||
|
||||
|
||||
c.surfaces.add(s)
|
||||
if c.surfaces.len == 1:
|
||||
c.focused_idx = 0
|
||||
|
||||
|
||||
return id
|
||||
|
||||
proc compositor_step*() =
|
||||
|
|
|
|||
|
|
@ -3,16 +3,4 @@
|
|||
|
||||
#include "lfs_config.h"
|
||||
|
||||
#ifndef _SIZE_T_DEFINED
|
||||
#define _SIZE_T_DEFINED
|
||||
typedef unsigned long size_t;
|
||||
#endif
|
||||
|
||||
void *memchr(const void *s, int c, size_t n);
|
||||
void *memcpy(void *dest, const void *src, size_t n);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
int memcmp(const void *s1, const void *s2, size_t n);
|
||||
void *memmove(void *dest, const void *src, size_t n);
|
||||
double strtod(const char *nptr, char **endptr);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
//! All memory accesses to the SysTable are through volatile pointers.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
// --- Protocol Definitions (Match core/ion.nim) ---
|
||||
|
||||
|
|
@ -95,11 +94,7 @@ pub fn RingBuffer(comptime T: type) type {
|
|||
self.data[head & mask] = item;
|
||||
|
||||
// Commit
|
||||
switch (builtin.cpu.arch) {
|
||||
.riscv64 => asm volatile ("fence" ::: .{ .memory = true }),
|
||||
.aarch64 => asm volatile ("dmb ish" ::: .{ .memory = true }),
|
||||
else => @compileError("unsupported arch"),
|
||||
}
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
self.head = (head + 1) & mask;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -120,8 +115,6 @@ pub const SysTable = extern struct {
|
|||
fn_vfs_list: u64,
|
||||
fn_vfs_write: u64,
|
||||
fn_vfs_close: u64,
|
||||
fn_vfs_dup: u64,
|
||||
fn_vfs_dup2: u64,
|
||||
fn_log: u64,
|
||||
fn_pledge: u64,
|
||||
// Framebuffer
|
||||
|
|
@ -134,7 +127,6 @@ pub const SysTable = extern struct {
|
|||
// Crypto
|
||||
fn_siphash: u64,
|
||||
fn_ed25519_verify: u64,
|
||||
fn_blake3: u64,
|
||||
// Network
|
||||
s_net_rx: *RingBuffer(IonPacket),
|
||||
s_net_tx: *RingBuffer(IonPacket),
|
||||
|
|
@ -142,27 +134,16 @@ pub const SysTable = extern struct {
|
|||
// Phase 36.3: Shared ION (16 bytes)
|
||||
fn_ion_alloc: u64,
|
||||
fn_ion_free: u64,
|
||||
|
||||
// Phase 36.4: Wait Multi
|
||||
fn_wait_multi: u64,
|
||||
|
||||
// Phase 36.5: HW Info
|
||||
net_mac: [6]u8,
|
||||
reserved_mac: [2]u8,
|
||||
|
||||
// Project LibWeb: LWF Sovereign Channel
|
||||
s_lwf_rx: *RingBuffer(IonPacket), // Kernel -> User (LWF frames)
|
||||
s_lwf_tx: *RingBuffer(IonPacket), // User -> Kernel (LWF frames)
|
||||
};
|
||||
|
||||
comptime {
|
||||
if (@sizeOf(IonPacket) != 24) @compileError("IonPacket size mismatch!");
|
||||
if (@sizeOf(SysTable) != 240) {
|
||||
@compileError("SysTable size mismatch! Expected 240 (LibWeb LWF channels added)");
|
||||
if (@sizeOf(SysTable) != 184) {
|
||||
@compileError("SysTable size mismatch! Expected 184");
|
||||
}
|
||||
}
|
||||
|
||||
const SYSTABLE_ADDR: usize = if (builtin.cpu.arch == .aarch64) 0x50000000 else 0x83000000;
|
||||
const SYSTABLE_ADDR: usize = 0x83000000;
|
||||
|
||||
// --- API ---
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@
|
|||
# Instead, locally declare only the types we need for userspace
|
||||
import ../../core/ring
|
||||
|
||||
const SYS_TABLE_ADDR* = when defined(arm64): 0x50000000'u64
|
||||
else: 0x83000000'u64
|
||||
const SYS_TABLE_ADDR* = 0x83000000'u64
|
||||
|
||||
# Local type declarations (must match core/ion.nim definitions)
|
||||
type
|
||||
|
|
@ -71,8 +70,6 @@ type
|
|||
fn_vfs_list*: proc(buf: pointer, max_len: uint64): int64 {.cdecl.}
|
||||
fn_vfs_write*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.}
|
||||
fn_vfs_close*: proc(fd: int32): int32 {.cdecl.}
|
||||
fn_vfs_dup*: proc(fd: int32, min_fd: int32): int32 {.cdecl.}
|
||||
fn_vfs_dup2*: proc(old_fd, new_fd: int32): int32 {.cdecl.}
|
||||
fn_log*: pointer
|
||||
fn_pledge*: proc(promises: uint64): int32 {.cdecl.}
|
||||
fb_addr*: uint64
|
||||
|
|
@ -92,20 +89,16 @@ type
|
|||
# Phase 36.3: Shared ION (16 bytes)
|
||||
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
||||
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
||||
|
||||
|
||||
# Phase 36.4: I/O Multiplexing (8 bytes)
|
||||
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
||||
|
||||
|
||||
# Phase 36.5: Network Hardware Info (8 bytes)
|
||||
net_mac*: array[6, byte]
|
||||
reserved_mac*: array[2, byte]
|
||||
|
||||
# Project LibWeb: LWF Sovereign Channel (16 bytes)
|
||||
s_lwf_rx*: pointer # Kernel -> User (LWF frames)
|
||||
s_lwf_tx*: pointer # User -> Kernel (LWF frames)
|
||||
|
||||
static:
|
||||
doAssert sizeof(SysTable) == 240
|
||||
doAssert sizeof(SysTable) == 208
|
||||
|
||||
var membrane_rx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
|
||||
var membrane_tx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
|
||||
|
|
@ -132,7 +125,7 @@ proc ion_user_init*() {.exportc.} =
|
|||
var err = "[ION-Client] ERROR: Invalid SysTable Magic!\n"
|
||||
console_write(addr err[0], uint(err.len))
|
||||
return
|
||||
|
||||
|
||||
membrane_rx_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_rx)
|
||||
membrane_tx_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_tx)
|
||||
membrane_cmd_ring_ptr = cast[ptr RingBuffer[CmdPacket, 256]](sys.s_cmd)
|
||||
|
|
@ -140,7 +133,7 @@ proc ion_user_init*() {.exportc.} =
|
|||
# Phase 36.2: Network rings
|
||||
membrane_net_rx_ptr = cast[ptr HAL_Ring_Input](sys.s_net_rx)
|
||||
membrane_net_tx_ptr = cast[ptr HAL_Ring_Input](sys.s_net_tx)
|
||||
|
||||
|
||||
var ok = "[ION-Client] Rings Mapped.\n"
|
||||
console_write(addr ok[0], uint(ok.len))
|
||||
else:
|
||||
|
|
@ -151,10 +144,10 @@ proc ion_user_init*() {.exportc.} =
|
|||
# Pure shared-memory slab allocator - NO kernel function calls!
|
||||
|
||||
const
|
||||
USER_SLAB_BASE = SYS_TABLE_ADDR + 0x10000'u64 # Start of user packet slab in SysTable region
|
||||
USER_SLAB_BASE = 0x83010000'u64 # Start of user packet slab in SysTable region
|
||||
USER_SLAB_COUNT = 512 # Number of packet slots
|
||||
USER_PKT_SIZE = 2048 # Size of each packet buffer
|
||||
USER_BITMAP_ADDR = SYS_TABLE_ADDR + 0x100'u64 # Bitmap stored in SysTable region (after SysTable struct)
|
||||
USER_BITMAP_ADDR = 0x83000100'u64 # Bitmap stored in SysTable region (after SysTable struct)
|
||||
|
||||
# Get pointer to shared bitmap (512 bits = 64 bytes for 512 slots)
|
||||
proc get_user_bitmap(): ptr array[64, byte] =
|
||||
|
|
@ -163,7 +156,7 @@ proc get_user_bitmap(): ptr array[64, byte] =
|
|||
proc ion_user_alloc*(out_pkt: ptr IonPacket): bool {.exportc.} =
|
||||
## Allocate packet from shared slab - pure userland, no kernel call
|
||||
let bitmap = get_user_bitmap()
|
||||
|
||||
|
||||
# Find first free slot
|
||||
for byteIdx in 0 ..< 64:
|
||||
if bitmap[byteIdx] != 0xFF: # At least one bit free
|
||||
|
|
@ -175,7 +168,7 @@ proc ion_user_alloc*(out_pkt: ptr IonPacket): bool {.exportc.} =
|
|||
if (bitmap[byteIdx] and mask) == 0:
|
||||
# Found free slot - mark as used
|
||||
bitmap[byteIdx] = bitmap[byteIdx] or mask
|
||||
|
||||
|
||||
let addr_val = USER_SLAB_BASE + uint64(slotIdx) * USER_PKT_SIZE
|
||||
out_pkt.id = uint16(slotIdx) or 0x8000
|
||||
out_pkt.phys = addr_val
|
||||
|
|
|
|||
|
|
@ -12,15 +12,13 @@
|
|||
# (C) 2026 Markus Maiwald
|
||||
|
||||
import ion_client
|
||||
when defined(RUMPK_KERNEL):
|
||||
import net_glue
|
||||
export net_glue
|
||||
import net_glue
|
||||
|
||||
# memcpy removed to avoid C header conflict
|
||||
|
||||
# --- SHARED CONSTANTS & TYPES ---
|
||||
|
||||
const
|
||||
const
|
||||
MAX_SOCKS = 32
|
||||
FD_OFFSET = 3
|
||||
# Syscalls
|
||||
|
|
@ -52,37 +50,39 @@ proc syscall*(nr: int, a0: uint64 = 0, a1: uint64 = 0, a2: uint64 = 0): int =
|
|||
let v0 = a0
|
||||
let v1 = a1
|
||||
let v2 = a2
|
||||
when defined(arm64):
|
||||
{.emit: """
|
||||
register unsigned long x8_ __asm__("x8") = `n`;
|
||||
register unsigned long x0_ __asm__("x0") = `v0`;
|
||||
register unsigned long x1_ __asm__("x1") = `v1`;
|
||||
register unsigned long x2_ __asm__("x2") = `v2`;
|
||||
__asm__ volatile("svc #0" : "+r"(x0_) : "r"(x8_), "r"(x1_), "r"(x2_) : "memory");
|
||||
`res` = (long)x0_;
|
||||
""".}
|
||||
else:
|
||||
{.emit: """
|
||||
register unsigned long a7 __asm__("a7") = `n`;
|
||||
register unsigned long a0_ __asm__("a0") = `v0`;
|
||||
register unsigned long a1_ __asm__("a1") = `v1`;
|
||||
register unsigned long a2_ __asm__("a2") = `v2`;
|
||||
__asm__ volatile("ecall" : "+r"(a0_) : "r"(a7), "r"(a1_), "r"(a2_) : "memory");
|
||||
`res` = (int)a0_;
|
||||
""".}
|
||||
{.emit: """
|
||||
register unsigned long a7 __asm__("a7") = `n`;
|
||||
register unsigned long a0_ __asm__("a0") = `v0`;
|
||||
register unsigned long a1_ __asm__("a1") = `v1`;
|
||||
register unsigned long a2_ __asm__("a2") = `v2`;
|
||||
__asm__ volatile("ecall" : "+r"(a0_) : "r"(a7), "r"(a1_), "r"(a2_) : "memory");
|
||||
`res` = (int)a0_;
|
||||
""".}
|
||||
return res
|
||||
|
||||
when defined(RUMPK_KERNEL):
|
||||
# =========================================================
|
||||
# KERNEL IMPLEMENTATION
|
||||
# =========================================================
|
||||
# SockState, NexusSock, membrane_init, pump_membrane_stack,
|
||||
# glue_connect/bind/listen/accept_peek/setup_socket/write/read/close,
|
||||
# glue_resolve_start/check — all provided by net_glue (imported above)
|
||||
|
||||
type
|
||||
SockState = enum
|
||||
CLOSED, LISTEN, CONNECTING, ESTABLISHED, FIN_WAIT
|
||||
|
||||
NexusSock = object
|
||||
fd: int
|
||||
pcb: pointer
|
||||
state: SockState
|
||||
rx_buf: array[4096, byte]
|
||||
rx_len: int
|
||||
accepted_pcb: pointer
|
||||
accepted_pending: int32
|
||||
|
||||
var g_sockets: array[MAX_SOCKS, NexusSock]
|
||||
var g_sock_used: array[MAX_SOCKS, bool]
|
||||
|
||||
proc membrane_init*() {.importc, cdecl.}
|
||||
proc pump_membrane_stack*() {.importc, cdecl.}
|
||||
proc rumpk_yield_internal() {.importc, cdecl.}
|
||||
|
||||
{.emit: """
|
||||
|
|
@ -90,6 +90,17 @@ when defined(RUMPK_KERNEL):
|
|||
extern void trigger_http_test(void);
|
||||
""".}
|
||||
|
||||
proc glue_connect(sock: ptr NexusSock, ip: uint32, port: uint16): int {.importc, cdecl.}
|
||||
proc glue_bind(sock: ptr NexusSock, port: uint16): int {.importc, cdecl.}
|
||||
proc glue_listen(sock: ptr NexusSock): int {.importc, cdecl.}
|
||||
proc glue_accept_peek(sock: ptr NexusSock): pointer {.importc, cdecl.}
|
||||
proc glue_setup_socket(sock: ptr NexusSock, pcb: pointer) {.importc, cdecl.}
|
||||
proc glue_write(sock: ptr NexusSock, buf: pointer, len: int): int {.importc, cdecl.}
|
||||
proc glue_read(sock: ptr NexusSock, buf: pointer, len: int): int {.importc, cdecl.}
|
||||
proc glue_close(sock: ptr NexusSock): int {.importc, cdecl.}
|
||||
proc glue_resolve_start(hostname: cstring): int {.importc, cdecl.}
|
||||
proc glue_resolve_check(ip_out: ptr uint32): int {.importc, cdecl.}
|
||||
|
||||
const
|
||||
MAX_FILES = 16
|
||||
FILE_FD_START = 100
|
||||
|
|
@ -113,7 +124,7 @@ when defined(RUMPK_KERNEL):
|
|||
g_sockets[i].rx_len = 0
|
||||
g_sockets[i].accepted_pcb = nil
|
||||
g_sockets[i].accepted_pending = 0
|
||||
|
||||
|
||||
g_fd_table[fd].kind = FD_SOCKET
|
||||
return i
|
||||
return -1
|
||||
|
|
@ -129,7 +140,7 @@ when defined(RUMPK_KERNEL):
|
|||
if fd < 0 or fd >= 256: return false
|
||||
return g_fd_table[fd].kind == FD_SOCKET
|
||||
|
||||
proc libc_impl_socket*(domain, sock_type, proto: int): int {.exportc: "libc_impl_socket", cdecl.} =
|
||||
proc libc_impl_socket*(domain, sock_type, proto: int): int {.exportc: "libc_impl_socket", cdecl.} =
|
||||
let idx = alloc_sock()
|
||||
if idx < 0: return -1
|
||||
return idx + FD_OFFSET
|
||||
|
|
@ -172,7 +183,7 @@ when defined(RUMPK_KERNEL):
|
|||
if sock.state == ESTABLISHED: return 0
|
||||
return -1
|
||||
|
||||
proc libc_impl_recv*(fd: int, buf: pointer, count: uint64): int {.exportc: "libc_impl_recv", cdecl.} =
|
||||
proc libc_impl_recv*(fd: int, buf: pointer, count: uint64): int {.exportc: "libc_impl_recv", cdecl.} =
|
||||
let sock = get_sock(fd)
|
||||
if sock == nil: return -1
|
||||
while true:
|
||||
|
|
@ -180,13 +191,13 @@ when defined(RUMPK_KERNEL):
|
|||
let n = glue_read(sock, buf, int(count))
|
||||
if n > 0: return n
|
||||
if sock.state == CLOSED: return 0
|
||||
rumpk_yield_internal()
|
||||
rumpk_yield_internal()
|
||||
|
||||
proc libc_impl_send*(fd: int, buf: pointer, count: uint64): int {.exportc: "libc_impl_send", cdecl.} =
|
||||
proc libc_impl_send*(fd: int, buf: pointer, count: uint64): int {.exportc: "libc_impl_send", cdecl.} =
|
||||
let sock = get_sock(fd)
|
||||
if sock == nil: return -1
|
||||
let res = glue_write(sock, buf, int(count))
|
||||
pump_membrane_stack()
|
||||
pump_membrane_stack()
|
||||
return res
|
||||
|
||||
proc libc_impl_close_socket*(fd: int): int {.exportc: "libc_impl_close_socket", cdecl.} =
|
||||
|
|
@ -203,7 +214,7 @@ when defined(RUMPK_KERNEL):
|
|||
# {.emit: "printf(\"[Membrane] libc_impl_getaddrinfo(node=%s, res_ptr=%p)\\n\", `node`, `res`);" .}
|
||||
let status = glue_resolve_start(node)
|
||||
var resolved = false
|
||||
|
||||
|
||||
if status == 0:
|
||||
# Cached / Done
|
||||
var ip_tmp: uint32
|
||||
|
|
@ -219,16 +230,16 @@ when defined(RUMPK_KERNEL):
|
|||
break
|
||||
if glue_resolve_check(addr ip) == -1:
|
||||
break
|
||||
rumpk_yield_internal()
|
||||
|
||||
rumpk_yield_internal()
|
||||
|
||||
if not resolved: return -1 # EAI_FAIL
|
||||
|
||||
|
||||
# 2. Allocate AddrInfo struct (using User Allocator? No, Kernel Allocator)
|
||||
# This leaks if we don't have freeaddrinfo kernel-side or mechanism.
|
||||
|
||||
|
||||
var ai = create(AddrInfo)
|
||||
var sa = create(SockAddr)
|
||||
|
||||
|
||||
ai.ai_family = 2 # AF_INET
|
||||
ai.ai_socktype = 1 # SOCK_STREAM
|
||||
ai.ai_protocol = 6 # IPPROTO_TCP
|
||||
|
|
@ -236,7 +247,7 @@ when defined(RUMPK_KERNEL):
|
|||
ai.ai_addr = sa
|
||||
ai.ai_canonname = nil
|
||||
ai.ai_next = nil
|
||||
|
||||
|
||||
sa.sa_family = 2 # AF_INET
|
||||
# Port 0 (Service not implemented yet)
|
||||
# IP
|
||||
|
|
@ -254,16 +265,16 @@ when defined(RUMPK_KERNEL):
|
|||
|
||||
struct my_sockaddr_in *sin = (struct my_sockaddr_in *)`sa`;
|
||||
sin->sin_addr.s_addr = `ip`;
|
||||
sin->sin_port = 0;
|
||||
sin->sin_port = 0;
|
||||
sin->sin_family = 2; // AF_INET
|
||||
""".}
|
||||
|
||||
|
||||
if res != nil:
|
||||
res[] = ai
|
||||
return 0
|
||||
return 0
|
||||
else:
|
||||
return -1
|
||||
|
||||
|
||||
proc libc_impl_freeaddrinfo*(res: ptr AddrInfo) {.exportc: "libc_impl_freeaddrinfo", cdecl.} =
|
||||
if res != nil:
|
||||
if res.ai_addr != nil: dealloc(res.ai_addr)
|
||||
|
|
@ -291,7 +302,7 @@ when defined(RUMPK_KERNEL):
|
|||
|
||||
proc libc_impl_read*(fd: int, buf: pointer, count: uint64): int {.exportc: "libc_impl_read", cdecl.} =
|
||||
if fd == 0: return int(syscall(0x203, 0, cast[uint64](buf), count))
|
||||
|
||||
|
||||
if fd >= 0 and fd < 256:
|
||||
if g_fd_table[fd].kind == FD_FILE:
|
||||
let path = cast[cstring](addr g_fd_table[fd].path[0])
|
||||
|
|
@ -330,7 +341,7 @@ else:
|
|||
proc open*(path: cstring, flags: int = 0): int {.importc: "open", cdecl.}
|
||||
proc close*(fd: int): int {.importc: "close", cdecl.}
|
||||
proc execv*(path: cstring, argv: pointer): int {.importc: "execv", cdecl.}
|
||||
|
||||
|
||||
# Manual strlen to avoid C header conflicts
|
||||
proc libc_strlen(s: cstring): uint64 =
|
||||
if s == nil: return 0
|
||||
|
|
@ -353,17 +364,15 @@ else:
|
|||
|
||||
proc exit*(status: int) {.exportc, cdecl.} =
|
||||
discard syscall(0x01, uint64(status))
|
||||
while true: discard
|
||||
|
||||
while true: discard
|
||||
|
||||
proc yield_fiber*() {.exportc: "yield", cdecl.} =
|
||||
discard syscall(0x100, 0)
|
||||
|
||||
proc pump_membrane_stack*() =
|
||||
## No-op in userland — kernel drives LwIP stack
|
||||
yield_fiber()
|
||||
# proc membrane_init*() {.importc, cdecl.}
|
||||
|
||||
proc pump_membrane_stack*() {.importc, cdecl.}
|
||||
proc membrane_init*() {.importc, cdecl.}
|
||||
proc ion_user_wait_multi*(mask: uint64): int32 {.importc, cdecl.}
|
||||
|
||||
|
||||
proc pledge*(promises: uint64): int {.exportc, cdecl.} =
|
||||
return int(syscall(0x101, promises))
|
||||
|
||||
|
|
@ -378,13 +387,13 @@ else:
|
|||
|
||||
proc upgrade*(id: int, path: cstring): int {.exportc, cdecl.} =
|
||||
# Deprecated: Use kexec directly
|
||||
return -1
|
||||
|
||||
return -1
|
||||
|
||||
proc get_vfs_listing*(): seq[string] =
|
||||
var buf: array[4096, char]
|
||||
let n = readdir(addr buf[0], 4096)
|
||||
if n <= 0: return @[]
|
||||
|
||||
|
||||
result = @[]
|
||||
var current = ""
|
||||
for i in 0..<n:
|
||||
|
|
@ -406,7 +415,7 @@ else:
|
|||
proc sys_surface_get_ptr*(surf_id: int): pointer {.exportc, cdecl.} =
|
||||
return cast[pointer](syscall(0x302, uint64(surf_id)))
|
||||
|
||||
proc socket*(domain, sock_type, protocol: int): int {.exportc, cdecl.} =
|
||||
proc socket*(domain, sock_type, protocol: int): int {.exportc, cdecl.} =
|
||||
return int(syscall(SYS_SOCK_SOCKET, uint64(domain), uint64(sock_type), uint64(protocol)))
|
||||
|
||||
proc bind_socket*(fd: int, addr_ptr: pointer, len: int): int {.exportc: "bind", cdecl.} =
|
||||
|
|
@ -421,16 +430,16 @@ else:
|
|||
proc accept*(fd: int, addr_ptr: pointer, len_ptr: pointer): int {.exportc, cdecl.} =
|
||||
return int(syscall(SYS_SOCK_ACCEPT, uint64(fd), cast[uint64](addr_ptr), cast[uint64](len_ptr)))
|
||||
|
||||
proc send*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} =
|
||||
proc send*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} =
|
||||
return int(syscall(0x204, uint64(fd), cast[uint64](buf), count))
|
||||
|
||||
proc recv*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} =
|
||||
proc recv*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} =
|
||||
return int(syscall(0x203, uint64(fd), cast[uint64](buf), count))
|
||||
|
||||
proc getaddrinfo*(node: cstring, service: cstring, hints: ptr AddrInfo, res: ptr ptr AddrInfo): int {.exportc, cdecl.} =
|
||||
# Syscall 0x905
|
||||
return int(syscall(SYS_SOCK_RESOLVE, cast[uint64](node), cast[uint64](service), cast[uint64](res)))
|
||||
|
||||
return int(syscall(SYS_SOCK_RESOLVE, cast[uint64](node), cast[uint64](service), cast[uint64](res)))
|
||||
|
||||
proc freeaddrinfo*(res: ptr AddrInfo) {.exportc, cdecl.} =
|
||||
# No-op for now (Kernel allocated statically/leak for MVP)
|
||||
# Or implement Syscall 0x906 if needed.
|
||||
|
|
@ -453,10 +462,10 @@ proc syscall_get_random*(): uint32 {.exportc, cdecl.} =
|
|||
## Generate cryptographically strong random number for TCP ISN
|
||||
## Implementation: SipHash-2-4(MonolithKey, Time || CycleCount)
|
||||
## Per SPEC-805: Hash Strategy
|
||||
|
||||
|
||||
# TODO: Optimize to avoid overhead if called frequently
|
||||
let time_ns = syscall_get_time_ns()
|
||||
|
||||
|
||||
# Temporary simple mix
|
||||
return uint32(time_ns xor (time_ns shr 32))
|
||||
|
||||
|
|
|
|||
|
|
@ -14,20 +14,10 @@ import ion_client
|
|||
# NOTE: Do NOT import ../../core/ion - it pulls in the KERNEL-ONLY 2MB memory pool!
|
||||
|
||||
proc console_write(s: pointer, len: csize_t) {.importc: "console_write", cdecl.}
|
||||
|
||||
|
||||
proc glue_print(s: string) =
|
||||
console_write(unsafeAddr s[0], csize_t(s.len))
|
||||
|
||||
# =========================================================
|
||||
# lwIP Syscall Bridge (kernel-native, no ecalls)
|
||||
# syscall_get_time_ns and syscall_panic provided by clib.c
|
||||
# =========================================================
|
||||
proc rumpk_timer_now_ns(): uint64 {.importc, cdecl.}
|
||||
|
||||
proc syscall_get_random*(): uint32 {.exportc, cdecl.} =
|
||||
let t = rumpk_timer_now_ns()
|
||||
return uint32(t xor (t shr 32))
|
||||
|
||||
# LwIP Imports
|
||||
{.passC: "-Icore/rumpk/vendor/lwip/src/include".}
|
||||
{.passC: "-Icore/rumpk/libs/membrane/include".}
|
||||
|
|
@ -106,7 +96,7 @@ void ping_send(const ip_addr_t *addr) {
|
|||
memset((char *)p->payload + sizeof(struct icmp_echo_hdr), 'A', 32);
|
||||
|
||||
iecho->chksum = inet_chksum(iecho, p->len);
|
||||
|
||||
|
||||
raw_sendto(ping_pcb, p, addr);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
|
@ -124,8 +114,6 @@ type
|
|||
state*: SockState
|
||||
rx_buf*: array[4096, byte]
|
||||
rx_len*: int
|
||||
accepted_pcb*: pointer
|
||||
accepted_pending*: int32
|
||||
|
||||
|
||||
# Forward declarations for LwIP callbacks
|
||||
|
|
@ -144,25 +132,25 @@ proc ion_linkoutput(netif: pointer, p: pointer): int32 {.exportc, cdecl.} =
|
|||
struct pbuf *curr = (struct pbuf *)`p`;
|
||||
while (curr != NULL) {
|
||||
if (`offset` + curr->len > 2000) break;
|
||||
|
||||
|
||||
// Copy Ethernet frame directly (includes header)
|
||||
memcpy((void*)((uintptr_t)`pkt`.data + `offset`), curr->payload, curr->len);
|
||||
`offset` += curr->len;
|
||||
curr = curr->next;
|
||||
}
|
||||
""".}
|
||||
|
||||
|
||||
# Zero out VirtIO-net header (first 12 bytes - Modern with MRG_RXBUF)
|
||||
{.emit: """
|
||||
memset((void*)`pkt`.data, 0, 12);
|
||||
""".}
|
||||
|
||||
|
||||
pkt.len = uint16(offset) # Total: 12 (VirtIO) + Ethernet frame
|
||||
|
||||
|
||||
if not ion_net_tx(pkt):
|
||||
ion_user_free(pkt)
|
||||
return -1 # ERR_IF
|
||||
|
||||
|
||||
return 0 # ERR_OK
|
||||
|
||||
proc ion_netif_init(netif: pointer): int32 {.exportc, cdecl.} =
|
||||
|
|
@ -177,13 +165,13 @@ proc ion_netif_init(netif: pointer): int32 {.exportc, cdecl.} =
|
|||
ni->mtu = 1500;
|
||||
ni->hwaddr_len = 6;
|
||||
ni->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_LINK_UP;
|
||||
|
||||
|
||||
// Set MAC from SysTable
|
||||
ni->hwaddr[0] = `mac`[0];
|
||||
ni->hwaddr[1] = `mac`[1];
|
||||
ni->hwaddr[0] = `mac`[0];
|
||||
ni->hwaddr[1] = `mac`[1];
|
||||
ni->hwaddr[2] = `mac`[2];
|
||||
ni->hwaddr[3] = `mac`[3];
|
||||
ni->hwaddr[4] = `mac`[4];
|
||||
ni->hwaddr[3] = `mac`[3];
|
||||
ni->hwaddr[4] = `mac`[4];
|
||||
ni->hwaddr[5] = `mac`[5];
|
||||
""".}
|
||||
return 0
|
||||
|
|
@ -204,51 +192,67 @@ proc membrane_init*() {.exportc, cdecl.} =
|
|||
last_dhcp_coarse = now
|
||||
last_dns_tmr = now
|
||||
|
||||
glue_print("[Membrane] Initialization Starting...\n")
|
||||
glue_print("[Membrane] Initialization...\n")
|
||||
ion_user_init()
|
||||
|
||||
|
||||
# 1. LwIP Stack Init
|
||||
glue_print("[Membrane] Calling lwip_init()...\n")
|
||||
lwip_init()
|
||||
|
||||
dns_init()
|
||||
# DIAGNOSTIC: Audit Memory Pools
|
||||
{.emit: """
|
||||
extern const struct memp_desc *const memp_pools[];
|
||||
printf("[Membrane] Pool Audit (MAX=%d):\n", (int)MEMP_MAX);
|
||||
for (int i = 0; i < (int)MEMP_MAX; i++) {
|
||||
if (memp_pools[i] == NULL) {
|
||||
printf(" [%d] NULL!\n", i);
|
||||
} else {
|
||||
printf(" [%d] OK\n", i);
|
||||
}
|
||||
}
|
||||
printf("[Membrane] Enum Lookup:\n");
|
||||
printf(" MEMP_UDP_PCB: %d\n", (int)MEMP_UDP_PCB);
|
||||
printf(" MEMP_TCP_PCB: %d\n", (int)MEMP_TCP_PCB);
|
||||
printf(" MEMP_PBUF: %d\n", (int)MEMP_PBUF);
|
||||
""".}
|
||||
|
||||
dns_init() # Initialize DNS resolver
|
||||
|
||||
# Set Fallback DNS (10.0.2.3 - QEMU Default)
|
||||
{.emit: """
|
||||
static ip_addr_t dns_server;
|
||||
IP4_ADDR(ip_2_ip4(&dns_server), 10, 0, 2, 3);
|
||||
dns_setserver(0, &dns_server);
|
||||
""".}
|
||||
|
||||
glue_print("[Membrane] DNS resolver configured with fallback 10.0.2.3\n")
|
||||
|
||||
glue_print("[Membrane] lwip_init() returned. DNS Initialized.\n")
|
||||
|
||||
glue_print("[Membrane] DNS configured (10.0.2.3)\n")
|
||||
|
||||
# 2. Setup Netif with DHCP
|
||||
# 2. Setup Netif
|
||||
{.emit: """
|
||||
static struct netif ni_static;
|
||||
ip4_addr_t ip, mask, gw;
|
||||
|
||||
// Start with zeros — DHCP will assign
|
||||
IP4_ADDR(&ip, 0, 0, 0, 0);
|
||||
IP4_ADDR(&mask, 0, 0, 0, 0);
|
||||
IP4_ADDR(&gw, 0, 0, 0, 0);
|
||||
|
||||
struct netif *res = netif_add(&ni_static, &ip, &mask, &gw, NULL,
|
||||
(netif_init_fn)ion_netif_init,
|
||||
(netif_input_fn)ethernet_input);
|
||||
|
||||
if (res == NULL) {
|
||||
printf("[Membrane] CRITICAL: netif_add FAILED!\n");
|
||||
} else {
|
||||
netif_set_default(&ni_static);
|
||||
netif_set_up(&ni_static);
|
||||
dhcp_start(&ni_static);
|
||||
printf("[Membrane] DHCP started on io0\n");
|
||||
}
|
||||
|
||||
// Use Static IP to stabilize test environment
|
||||
IP4_ADDR(&ip, 10, 0, 2, 15);
|
||||
IP4_ADDR(&mask, 255, 255, 255, 0);
|
||||
IP4_ADDR(&gw, 10, 0, 2, 2);
|
||||
|
||||
struct netif *res = netif_add(&ni_static, &ip, &mask, &gw, NULL, (netif_init_fn)ion_netif_init, (netif_input_fn)ethernet_input);
|
||||
printf("[Membrane] netif_add returned: 0x%x\n", (unsigned int)res);
|
||||
|
||||
netif_set_default(&ni_static);
|
||||
netif_set_up(&ni_static);
|
||||
|
||||
printf("[Membrane] netif_default: 0x%x | netif_list: 0x%x\n", (unsigned int)netif_default, (unsigned int)netif_list);
|
||||
|
||||
// dhcp_start(&ni_static); // Bypassing DHCP
|
||||
|
||||
`g_netif` = &ni_static;
|
||||
""".}
|
||||
|
||||
glue_print("[Membrane] Network Stack Operational (DHCP...)\n")
|
||||
glue_print("[Membrane] Network Stack Operational (Waiting for DHCP IP...)\n")
|
||||
|
||||
proc glue_get_ip*(): uint32 {.exportc, cdecl.} =
|
||||
## Returns current IP address in host byte order
|
||||
|
|
@ -271,7 +275,7 @@ proc glue_print_hex(v: uint64) =
|
|||
|
||||
proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||
## The Pulse of the Membrane. Call frequently to handle timers and RX.
|
||||
|
||||
|
||||
pump_iterations += 1
|
||||
let now = sys_now()
|
||||
|
||||
|
|
@ -283,7 +287,7 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
|||
glue_print_hex(uint64(ip_addr))
|
||||
glue_print("\n")
|
||||
last_notified_ip = ip_addr
|
||||
|
||||
|
||||
# Phase 40: Fast Trigger for Helios Probe
|
||||
glue_print("[Membrane] IP Found. Triggering Helios Probe...\n")
|
||||
{.emit: "trigger_http_test();" .}
|
||||
|
|
@ -300,17 +304,17 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
|||
if (now - last_tcp_tmr >= 250) or (pump_iterations mod 25 == 0):
|
||||
tcp_tmr()
|
||||
last_tcp_tmr = now
|
||||
|
||||
|
||||
# ARP Timer (5s)
|
||||
if (now - last_arp_tmr >= 5000) or (pump_iterations mod 500 == 0):
|
||||
etharp_tmr()
|
||||
last_arp_tmr = now
|
||||
|
||||
|
||||
# DHCP Timers
|
||||
if (now - last_dhcp_fine >= 500) or (pump_iterations mod 50 == 0):
|
||||
dhcp_fine_tmr()
|
||||
last_dhcp_fine = now
|
||||
|
||||
|
||||
if (now - last_dhcp_coarse >= 60000) or (pump_iterations mod 6000 == 0):
|
||||
dhcp_coarse_tmr()
|
||||
last_dhcp_coarse = now
|
||||
|
|
@ -323,14 +327,14 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
|||
# Phase 37a: ICMP Ping Verification
|
||||
if now - last_ping_time > 1000:
|
||||
last_ping_time = now
|
||||
|
||||
|
||||
if ip_addr != 0:
|
||||
glue_print("[Membrane] TESTING EXTERNAL REACHABILITY: PING 142.250.185.78...\n")
|
||||
{.emit: """
|
||||
ip_addr_t target;
|
||||
IP4_ADDR(&target, 142, 250, 185, 78);
|
||||
ping_send(&target);
|
||||
|
||||
|
||||
// Trigger the Helios TCP Probe
|
||||
trigger_http_test();
|
||||
""".}
|
||||
|
|
@ -381,7 +385,7 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
|||
int state; // 0=CLOSED, 1=LISTEN, 2=CONNECTING, 3=ESTABLISHED
|
||||
unsigned char rx_buf[4096];
|
||||
NI rx_len;
|
||||
|
||||
|
||||
// Server Fields
|
||||
void* accepted_pcb;
|
||||
int accepted_pending;
|
||||
|
|
@ -394,10 +398,10 @@ proc ion_tcp_connected(arg: pointer, pcb: pointer, err: int8): int8 {.exportc, c
|
|||
NexusSock_C *s = (NexusSock_C *)`arg`;
|
||||
s->state = 3; // ESTABLISHED
|
||||
""".}
|
||||
return 0
|
||||
return 0
|
||||
|
||||
proc ion_tcp_sent(arg: pointer, pcb: pointer, len: uint16): int8 {.exportc, cdecl.} =
|
||||
return 0
|
||||
return 0
|
||||
|
||||
proc ion_tcp_recv(arg: pointer, pcb: pointer, p: pointer, err: int8): int8 {.exportc, cdecl.} =
|
||||
if p == nil:
|
||||
|
|
@ -407,12 +411,12 @@ proc ion_tcp_recv(arg: pointer, pcb: pointer, p: pointer, err: int8): int8 {.exp
|
|||
s->state = 0; // CLOSED
|
||||
""".}
|
||||
return 0
|
||||
|
||||
|
||||
# Append data to rx_buf
|
||||
{.emit: """
|
||||
struct pbuf *curr = (struct pbuf *)`p`;
|
||||
NexusSock_C *s = (NexusSock_C *)`arg`;
|
||||
|
||||
|
||||
if (curr != NULL) {
|
||||
struct pbuf *q;
|
||||
for (q = curr; q != NULL; q = q->next) {
|
||||
|
|
@ -437,12 +441,12 @@ proc ion_tcp_accept(arg: pointer, new_pcb: pointer, err: int8): int8 {.exportc,
|
|||
if (listener->accepted_pending == 0) {
|
||||
listener->accepted_pcb = `new_pcb`;
|
||||
listener->accepted_pending = 1;
|
||||
|
||||
|
||||
// Increase reference count? No, LwIP gives us ownership.
|
||||
// Important: We must not set callbacks yet?
|
||||
// LwIP doc: "When a new connection arrives, the accept callback function is called.
|
||||
// Important: We must not set callbacks yet?
|
||||
// LwIP doc: "When a new connection arrives, the accept callback function is called.
|
||||
// The new pcb is passed as a parameter."
|
||||
|
||||
|
||||
// We'll set callbacks later when libc performs the accept() syscall.
|
||||
} else {
|
||||
// Backlog full, reject?
|
||||
|
|
@ -450,7 +454,7 @@ proc ion_tcp_accept(arg: pointer, new_pcb: pointer, err: int8): int8 {.exportc,
|
|||
return -1; // ERR_ABRT
|
||||
}
|
||||
""".}
|
||||
return 0
|
||||
return 0
|
||||
|
||||
proc glue_setup_socket*(sock: ptr NexusSock, pcb_ptr: pointer) {.exportc, cdecl.} =
|
||||
# Wire LwIP callbacks to a NexusSock
|
||||
|
|
@ -458,8 +462,8 @@ proc glue_setup_socket*(sock: ptr NexusSock, pcb_ptr: pointer) {.exportc, cdecl.
|
|||
NexusSock_C *ns = (NexusSock_C *)`sock`;
|
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)`pcb_ptr`;
|
||||
ns->pcb = pcb;
|
||||
|
||||
tcp_arg(pcb, ns);
|
||||
|
||||
tcp_arg(pcb, ns);
|
||||
tcp_recv(pcb, (tcp_recv_fn)ion_tcp_recv);
|
||||
tcp_sent(pcb, (tcp_sent_fn)ion_tcp_sent);
|
||||
// tcp_poll(pcb, ...);
|
||||
|
|
@ -472,13 +476,13 @@ proc glue_connect*(sock: ptr NexusSock, ip: uint32, port: uint16): int {.exportc
|
|||
{.emit: """
|
||||
struct tcp_pcb *pcb = tcp_new();
|
||||
if (pcb == NULL) return -1;
|
||||
|
||||
|
||||
// Wire up
|
||||
glue_setup_socket(`sock`, pcb);
|
||||
|
||||
|
||||
ip4_addr_t remote_ip;
|
||||
remote_ip.addr = `ip`;
|
||||
|
||||
remote_ip.addr = `ip`;
|
||||
|
||||
tcp_connect(pcb, &remote_ip, `port`, (tcp_connected_fn)ion_tcp_connected);
|
||||
""".}
|
||||
return 0
|
||||
|
|
@ -488,18 +492,18 @@ proc glue_bind*(sock: ptr NexusSock, port: uint16): int {.exportc, cdecl.} =
|
|||
{.emit: """
|
||||
struct tcp_pcb *pcb = tcp_new();
|
||||
if (pcb == NULL) return -1;
|
||||
|
||||
|
||||
// Bind to ANY
|
||||
if (tcp_bind(pcb, IP_ADDR_ANY, `port`) != ERR_OK) {
|
||||
memp_free(MEMP_TCP_PCB, pcb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Update sock
|
||||
// glue_setup_socket checks validity, but here we just need to store PCB
|
||||
// Because we are not connecting, we don't set recv/sent yet?
|
||||
// Because we are not connecting, we don't set recv/sent yet?
|
||||
// Actually we need tcp_arg for accept callback.
|
||||
|
||||
|
||||
NexusSock_C *ns = (NexusSock_C *)`sock`;
|
||||
ns->pcb = pcb;
|
||||
tcp_arg(pcb, ns);
|
||||
|
|
@ -511,7 +515,7 @@ proc glue_listen*(sock: ptr NexusSock): int {.exportc, cdecl.} =
|
|||
NexusSock_C *ns = (NexusSock_C *)`sock`;
|
||||
struct tcp_pcb *lpcb = tcp_listen((struct tcp_pcb *)ns->pcb);
|
||||
if (lpcb == NULL) return -1;
|
||||
|
||||
|
||||
ns->pcb = lpcb; // Update to listening PCB
|
||||
tcp_accept(lpcb, (tcp_accept_fn)ion_tcp_accept);
|
||||
""".}
|
||||
|
|
@ -530,11 +534,11 @@ proc glue_accept_peek*(sock: ptr NexusSock): pointer {.exportc, cdecl.} =
|
|||
""".}
|
||||
return p
|
||||
|
||||
proc glue_write*(sock: ptr NexusSock, buf: pointer, len: int): int {.exportc, cdecl.} =
|
||||
proc glue_write*(sock: ptr NexusSock, buf: pointer, len: int): int {.exportc, cdecl.} =
|
||||
{.emit: """
|
||||
struct tcp_pcb *pcb = (struct tcp_pcb *)`sock`->pcb;
|
||||
if (pcb == NULL) return -1;
|
||||
|
||||
|
||||
tcp_write(pcb, `buf`, `len`, TCP_WRITE_FLAG_COPY);
|
||||
tcp_output(pcb);
|
||||
""".}
|
||||
|
|
@ -542,17 +546,17 @@ proc glue_write*(sock: ptr NexusSock, buf: pointer, len: int): int {.exportc, cd
|
|||
|
||||
proc glue_read*(sock: ptr NexusSock, buf: pointer, len: int): int {.exportc, cdecl.} =
|
||||
if sock.rx_len == 0: return 0
|
||||
|
||||
|
||||
var to_read = len
|
||||
if to_read > sock.rx_len: to_read = sock.rx_len
|
||||
|
||||
|
||||
copyMem(buf, addr sock.rx_buf[0], uint64(to_read))
|
||||
|
||||
|
||||
# Shift buffer (Ring buffer would be better, but this is MVP)
|
||||
var remaining = sock.rx_len - to_read
|
||||
if remaining > 0:
|
||||
copyMem(addr sock.rx_buf[0], addr sock.rx_buf[to_read], uint64(remaining))
|
||||
|
||||
|
||||
sock.rx_len = remaining
|
||||
return to_read
|
||||
|
||||
|
|
@ -596,7 +600,7 @@ int glue_dns_check_init(void) {
|
|||
int glue_resolve_start(char* hostname) {
|
||||
// BYPASS: Mock DNS to unblock Userland
|
||||
// printf("[Membrane] DNS MOCK: Resolving '%s' -> 10.0.2.2\n", hostname);
|
||||
|
||||
|
||||
ip_addr_t ip;
|
||||
IP4_ADDR(ip_2_ip4(&ip), 10, 0, 2, 2); // Gateway
|
||||
g_dns_ip = ip;
|
||||
|
|
@ -649,17 +653,18 @@ void trigger_http_test(void) {
|
|||
|
||||
ip_addr_t google_ip;
|
||||
IP4_ADDR(ip_2_ip4(&google_ip), 142, 250, 185, 78);
|
||||
|
||||
|
||||
struct tcp_pcb *pcb = tcp_new();
|
||||
if (!pcb) {
|
||||
printf("[Membrane] HELIOS Error: Failed to create TCP PCB\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
tcp_arg(pcb, NULL);
|
||||
tcp_recv(pcb, tcp_recv_callback);
|
||||
|
||||
|
||||
printf("[Membrane] HELIOS: INITIATING TCP CONNECTION to 142.250.185.78:80...\n");
|
||||
tcp_connect(pcb, &google_ip, 80, tcp_connected_callback);
|
||||
}
|
||||
""".}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,49 +12,9 @@ import strutils, parseutils, tables, sequtils, json
|
|||
import kdl
|
||||
import ../../libs/membrane/libc as lb
|
||||
import ../../libs/membrane/libc_net as lnet
|
||||
when not defined(NIPBOX_LITE):
|
||||
import ../../libs/membrane/fs/sfs_user as sfs
|
||||
import ../../libs/membrane/fs/sfs_user as sfs
|
||||
import editor
|
||||
when not defined(NIPBOX_LITE):
|
||||
import ../../libs/membrane/term # Phase 26: Visual Cortex
|
||||
|
||||
# --- M4.4: BKDL Capability Manifest (SPEC-071) ---
|
||||
{.emit: """
|
||||
__attribute__((section(".nexus.manifest"), used))
|
||||
static const unsigned char nexus_manifest[166] = {
|
||||
/* BkdlHeader (118 bytes) */
|
||||
0x53, 0x55, 0x58, 0x4E, /* magic: "NXUS" (LE) */
|
||||
0x01, 0x00, /* version: 1 */
|
||||
0x00, 0x00, /* flags: 0 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* signature[0..63]: zeros */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* pubkey_hash[0..31]: zeros */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, /* cap_count: 4 */
|
||||
0x00, 0x00, 0x00, 0x00, /* blob_size: 0 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* entry_point: 0 */
|
||||
/* CapDescriptor[0]: console.output (0x1001) WRITE */
|
||||
0x02, 0x02, 0x00, 0x00,
|
||||
0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* CapDescriptor[1]: VFS (0x2000) READ */
|
||||
0x02, 0x01, 0x00, 0x00,
|
||||
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* CapDescriptor[2]: NET_TX (0x0500) WRITE */
|
||||
0x02, 0x02, 0x00, 0x00,
|
||||
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* CapDescriptor[3]: NET_RX (0x0501) READ */
|
||||
0x02, 0x01, 0x00, 0x00,
|
||||
0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
""".}
|
||||
import ../../libs/membrane/term # Phase 26: Visual Cortex
|
||||
|
||||
# Phase 30: Pledge Constants
|
||||
const
|
||||
|
|
@ -261,7 +221,7 @@ proc cmd_cp*(args: seq[string], input: PipelineData): PipelineData =
|
|||
return @[]
|
||||
|
||||
# O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577
|
||||
let fd_dest = lb.open(dest.cstring, 577)
|
||||
let fd_dest = lb.open(dest.cstring, 577)
|
||||
if fd_dest < 0:
|
||||
print("cp: cannot create '" & dest & "'\n")
|
||||
discard lb.close(fd_src)
|
||||
|
|
@ -287,11 +247,11 @@ proc cmd_mv*(args: seq[string], input: PipelineData): PipelineData =
|
|||
if args.len < 2:
|
||||
print("Usage: mv <source> <dest>\n")
|
||||
return @[]
|
||||
|
||||
|
||||
# Step 1: Copy
|
||||
print("[mv] Copying...\n")
|
||||
discard cmd_cp(args, input)
|
||||
|
||||
|
||||
# Step 2: Unlink (Not yet supported by Kernel)
|
||||
print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n")
|
||||
return @[]
|
||||
|
|
@ -300,7 +260,7 @@ proc cmd_touch*(args: seq[string], input: PipelineData): PipelineData =
|
|||
if args.len < 1:
|
||||
print("Usage: touch <filename>\n")
|
||||
return @[]
|
||||
|
||||
|
||||
let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC
|
||||
if fd >= 0:
|
||||
discard lb.close(fd)
|
||||
|
|
@ -331,53 +291,45 @@ proc cmd_cat*(args: seq[string], input: PipelineData): PipelineData =
|
|||
proc cmd_write*(args: seq[string], input: PipelineData): PipelineData =
|
||||
## write <filename> <content>
|
||||
## Uses USERLAND SFS (Block Valve architecture)
|
||||
when not defined(NIPBOX_LITE):
|
||||
if args.len < 2:
|
||||
print("Usage: write <filename> <content>\n")
|
||||
return @[]
|
||||
|
||||
let filename = args[0]
|
||||
let content = args[1..^1].join(" ")
|
||||
|
||||
# Mount userland FS if not already done
|
||||
if not sfs.sfs_is_mounted():
|
||||
discard sfs.sfs_mount()
|
||||
|
||||
let bytes_written = sfs.sfs_write(filename, cast[pointer](unsafeAddr content[0]), content.len)
|
||||
if bytes_written > 0:
|
||||
print("[Glass Vault] Written " & $bytes_written & " bytes to: " & filename & " (Userland SFS)\n")
|
||||
else:
|
||||
print("Error: Could not write to " & filename & "\n")
|
||||
if args.len < 2:
|
||||
print("Usage: write <filename> <content>\n")
|
||||
return @[]
|
||||
|
||||
let filename = args[0]
|
||||
let content = args[1..^1].join(" ")
|
||||
|
||||
# Mount userland FS if not already done
|
||||
if not sfs.sfs_is_mounted():
|
||||
discard sfs.sfs_mount()
|
||||
|
||||
let bytes_written = sfs.sfs_write(filename, cast[pointer](unsafeAddr content[0]), content.len)
|
||||
if bytes_written > 0:
|
||||
print("[Glass Vault] Written " & $bytes_written & " bytes to: " & filename & " (Userland SFS)\n")
|
||||
else:
|
||||
print("[nipbox] 'write' requires SFS (not available in lite mode)\n")
|
||||
return @[]
|
||||
print("Error: Could not write to " & filename & "\n")
|
||||
return @[]
|
||||
|
||||
proc cmd_read*(args: seq[string], input: PipelineData): PipelineData =
|
||||
## read <filename>
|
||||
## Uses USERLAND SFS (Block Valve architecture)
|
||||
when not defined(NIPBOX_LITE):
|
||||
if args.len == 0:
|
||||
print("Usage: read <filename>\n")
|
||||
return @[]
|
||||
|
||||
let filename = args[0]
|
||||
|
||||
# Mount userland FS if not already done
|
||||
if not sfs.sfs_is_mounted():
|
||||
discard sfs.sfs_mount()
|
||||
|
||||
var buf: array[4096, char]
|
||||
let bytes_read = sfs.sfs_read(filename, addr buf[0], 4096)
|
||||
if bytes_read > 0:
|
||||
discard lb.write(cint(1), addr buf[0], csize_t(bytes_read))
|
||||
print("\n[Glass Vault] Read " & $bytes_read & " bytes from: " & filename & " (Userland SFS)\n")
|
||||
else:
|
||||
print("Error: Could not open " & filename & "\n")
|
||||
if args.len == 0:
|
||||
print("Usage: read <filename>\n")
|
||||
return @[]
|
||||
|
||||
let filename = args[0]
|
||||
|
||||
# Mount userland FS if not already done
|
||||
if not sfs.sfs_is_mounted():
|
||||
discard sfs.sfs_mount()
|
||||
|
||||
var buf: array[4096, char]
|
||||
let bytes_read = sfs.sfs_read(filename, addr buf[0], 4096)
|
||||
if bytes_read > 0:
|
||||
discard lb.write(cint(1), addr buf[0], csize_t(bytes_read))
|
||||
print("\n[Glass Vault] Read " & $bytes_read & " bytes from: " & filename & " (Userland SFS)\n")
|
||||
else:
|
||||
print("[nipbox] 'read' requires SFS (not available in lite mode)\n")
|
||||
return @[]
|
||||
print("Error: Could not open " & filename & "\n")
|
||||
return @[]
|
||||
|
||||
proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData =
|
||||
if args.len == 0:
|
||||
|
|
@ -530,223 +482,239 @@ proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData =
|
|||
return @[node]
|
||||
|
||||
proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
||||
when not defined(NIPBOX_LITE):
|
||||
if args.len < 2:
|
||||
print("Usage: http.download <ip:port/path> <outfile>\n")
|
||||
return @[]
|
||||
if args.len < 2:
|
||||
print("Usage: http.download <ip:port/path> <outfile>\n")
|
||||
return @[]
|
||||
|
||||
let url_arg = args[0]
|
||||
let outfile = args[1]
|
||||
let url_arg = args[0]
|
||||
let outfile = args[1]
|
||||
|
||||
var host_part = url_arg
|
||||
var path_str = "/"
|
||||
var host_part = url_arg
|
||||
var path_str = "/"
|
||||
|
||||
let slash_pos = url_arg.find('/')
|
||||
if slash_pos != -1:
|
||||
host_part = url_arg[0..<slash_pos]
|
||||
path_str = url_arg[slash_pos..^1]
|
||||
let slash_pos = url_arg.find('/')
|
||||
if slash_pos != -1:
|
||||
host_part = url_arg[0..<slash_pos]
|
||||
path_str = url_arg[slash_pos..^1]
|
||||
|
||||
let parts = host_part.split(':')
|
||||
if parts.len != 2:
|
||||
print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
|
||||
return @[]
|
||||
let parts = host_part.split(':')
|
||||
if parts.len != 2:
|
||||
print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
|
||||
return @[]
|
||||
|
||||
let ip_str = parts[0]
|
||||
let port = uint16(parseInt(parts[1]))
|
||||
let ip_str = parts[0]
|
||||
let port = uint16(parseInt(parts[1]))
|
||||
|
||||
# Parse IP
|
||||
var ip_val: uint32 = 0
|
||||
try:
|
||||
let p = ip_str.split('.')
|
||||
ip_val = (uint32(parseInt(p[0])) and 0xFF) or
|
||||
((uint32(parseInt(p[1])) and 0xFF) shl 8) or
|
||||
((uint32(parseInt(p[2])) and 0xFF) shl 16) or
|
||||
((uint32(parseInt(p[3])) and 0xFF) shl 24)
|
||||
except:
|
||||
print("Error: Invalid IP\n")
|
||||
return @[]
|
||||
# Parse IP
|
||||
var ip_val: uint32 = 0
|
||||
try:
|
||||
let p = ip_str.split('.')
|
||||
ip_val = (uint32(parseInt(p[0])) and 0xFF) or
|
||||
((uint32(parseInt(p[1])) and 0xFF) shl 8) or
|
||||
((uint32(parseInt(p[2])) and 0xFF) shl 16) or
|
||||
((uint32(parseInt(p[3])) and 0xFF) shl 24)
|
||||
except:
|
||||
print("Error: Invalid IP\n")
|
||||
return @[]
|
||||
|
||||
print("[Download] Connecting to " & host_part & "...\n")
|
||||
let fd = lb.socket(2, 1, 0)
|
||||
if fd < 0: return @[]
|
||||
print("[Download] Connecting to " & host_part & "...\n")
|
||||
let fd = lb.socket(2, 1, 0)
|
||||
if fd < 0: return @[]
|
||||
|
||||
# SockAddr setup
|
||||
var addr_buf: array[16, byte]
|
||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
||||
# SockAddr setup
|
||||
var addr_buf: array[16, byte]
|
||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
||||
|
||||
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
||||
print("Error: Connection Failed.\n")
|
||||
return @[]
|
||||
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
||||
print("Error: Connection Failed.\n")
|
||||
return @[]
|
||||
|
||||
# Request
|
||||
let req = "GET " & path_str & " HTTP/1.0\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n"
|
||||
if lb.send(cint(fd), cast[pointer](unsafeAddr req[0]), csize_t(req.len), 0) <= 0:
|
||||
print("Error: Send Failed.\n")
|
||||
discard lb.close(cint(fd))
|
||||
return @[]
|
||||
|
||||
# Mount SFS if needed
|
||||
if not sfs.sfs_is_mounted():
|
||||
if not sfs.sfs_mount():
|
||||
print("Error: Could not mount SFS.\n")
|
||||
discard lb.close(cint(fd))
|
||||
return @[]
|
||||
|
||||
# Open SFS Stream
|
||||
let sfs_h = sfs.sfs_open_write(outfile)
|
||||
if sfs_h == nil:
|
||||
print("Error: Could not create file " & outfile & "\n")
|
||||
discard lb.close(cint(fd))
|
||||
return @[]
|
||||
|
||||
print("[Download] Downloading...\n")
|
||||
|
||||
var buf: array[4096, char]
|
||||
var header_acc = ""
|
||||
var header_parsed = false
|
||||
var total_bytes = 0
|
||||
var content_len = -1
|
||||
var timeout = 0
|
||||
|
||||
while timeout < 10000:
|
||||
let n = lb.recv(cint(fd), addr buf[0], 4096, 0)
|
||||
|
||||
if n > 0:
|
||||
timeout = 0
|
||||
if not header_parsed:
|
||||
for i in 0..<n: header_acc.add(buf[i])
|
||||
let sep = header_acc.find("\r\n\r\n")
|
||||
if sep != -1:
|
||||
header_parsed = true
|
||||
|
||||
let lower_head = header_acc.toLowerAscii()
|
||||
let cl_idx = lower_head.find("content-length:")
|
||||
if cl_idx != -1:
|
||||
let end_line = lower_head.find("\r\n", cl_idx)
|
||||
if end_line != -1:
|
||||
try:
|
||||
content_len = parseInt(lower_head[cl_idx+15..<end_line].strip())
|
||||
except:
|
||||
content_len = -1
|
||||
|
||||
let body_start = sep + 4
|
||||
if body_start < header_acc.len:
|
||||
let chunk = header_acc[body_start..^1]
|
||||
sfs.sfs_write_chunk(sfs_h, cast[pointer](unsafeAddr chunk[0]), chunk.len)
|
||||
total_bytes += chunk.len
|
||||
header_acc = ""
|
||||
else:
|
||||
if header_acc.len > 8192:
|
||||
print("Error: Headers too large.\n")
|
||||
break
|
||||
else:
|
||||
sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
|
||||
total_bytes += int(n)
|
||||
|
||||
if content_len > 0:
|
||||
if total_bytes mod 10240 < int(n): print(".")
|
||||
else:
|
||||
if total_bytes mod 10240 < int(n): print(".")
|
||||
|
||||
elif n == 0:
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
sfs.sfs_close_write(sfs_h)
|
||||
# Request
|
||||
let req = "GET " & path_str & " HTTP/1.0\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n"
|
||||
if lb.send(cint(fd), cast[pointer](unsafeAddr req[0]), csize_t(req.len), 0) <= 0:
|
||||
print("Error: Send Failed.\n")
|
||||
discard lb.close(cint(fd))
|
||||
print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n")
|
||||
return @[]
|
||||
else:
|
||||
print("[nipbox] 'http.download' requires SFS (not available in lite mode)\n")
|
||||
|
||||
# Mount SFS if needed
|
||||
if not sfs.sfs_is_mounted():
|
||||
if not sfs.sfs_mount():
|
||||
print("Error: Could not mount SFS.\n")
|
||||
discard lb.close(cint(fd))
|
||||
return @[]
|
||||
|
||||
# Open SFS Stream
|
||||
let sfs_h = sfs.sfs_open_write(outfile)
|
||||
if sfs_h == nil:
|
||||
print("Error: Could not create file " & outfile & "\n")
|
||||
discard lb.close(cint(fd))
|
||||
return @[]
|
||||
|
||||
print("[Download] Downloading...\n")
|
||||
|
||||
var buf: array[4096, char]
|
||||
var header_acc = ""
|
||||
var header_parsed = false
|
||||
var total_bytes = 0
|
||||
var content_len = -1
|
||||
var timeout = 0
|
||||
|
||||
while timeout < 10000:
|
||||
# Use libc shim which pumps stack
|
||||
let n = lb.recv(cint(fd), addr buf[0], 4096, 0)
|
||||
|
||||
if n > 0:
|
||||
timeout = 0
|
||||
if not header_parsed:
|
||||
for i in 0..<n: header_acc.add(buf[i])
|
||||
let sep = header_acc.find("\r\n\r\n")
|
||||
if sep != -1:
|
||||
header_parsed = true
|
||||
|
||||
# Try to find Content-Length
|
||||
# Quick hacky parse
|
||||
let lower_head = header_acc.toLowerAscii()
|
||||
let cl_idx = lower_head.find("content-length:")
|
||||
if cl_idx != -1:
|
||||
let end_line = lower_head.find("\r\n", cl_idx)
|
||||
if end_line != -1:
|
||||
try:
|
||||
content_len = parseInt(lower_head[cl_idx+15..<end_line].strip())
|
||||
except:
|
||||
content_len = -1
|
||||
|
||||
let body_start = sep + 4
|
||||
if body_start < header_acc.len:
|
||||
let chunk = header_acc[body_start..^1]
|
||||
sfs.sfs_write_chunk(sfs_h, cast[pointer](unsafeAddr chunk[0]), chunk.len)
|
||||
total_bytes += chunk.len
|
||||
header_acc = ""
|
||||
else:
|
||||
if header_acc.len > 8192:
|
||||
print("Error: Headers too large.\n")
|
||||
break
|
||||
else:
|
||||
# Stream directly to SFS
|
||||
sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
|
||||
total_bytes += int(n)
|
||||
|
||||
# Progress Bar
|
||||
if content_len > 0:
|
||||
# let pct = (total_bytes * 100) div content_len
|
||||
if total_bytes mod 10240 < int(n): print(".")
|
||||
else:
|
||||
if total_bytes mod 10240 < int(n): print(".")
|
||||
|
||||
elif n == 0:
|
||||
break
|
||||
else:
|
||||
timeout += 1
|
||||
# Busy wait / pump handled in recv?
|
||||
# Recv calls pump_membrane_stack loop
|
||||
# But if we return -1 (EAGAIN), we need to retry.
|
||||
# My libc.libc_recv returns 0 on closed?
|
||||
# Actually libc_recv in step 945 waits until data or closed.
|
||||
# So n==0 means closed.
|
||||
# Wait, libc.nim recv implementation:
|
||||
# while true: pump; if data return n; if closed return 0.
|
||||
# So it blocks until data.
|
||||
# Thus n > 0 always unless closed.
|
||||
break
|
||||
|
||||
sfs.sfs_close_write(sfs_h)
|
||||
discard lb.close(cint(fd))
|
||||
print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n")
|
||||
return @[]
|
||||
|
||||
# Phase 37: HTTP Verification Tool
|
||||
proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData =
|
||||
if args.len < 1:
|
||||
print("Usage: http.test <host>\n")
|
||||
print("Usage: http <host>\n")
|
||||
return @[]
|
||||
|
||||
|
||||
let host = args[0]
|
||||
print("Dialing " & host & ":80...\n")
|
||||
|
||||
|
||||
let fd = lnet.net_dial_tcp(host, 80)
|
||||
if fd < 0:
|
||||
print("Connection Failed! Error: " & $fd & "\n")
|
||||
return @[]
|
||||
|
||||
|
||||
print("Connected! Sending GET request...\n")
|
||||
discard lnet.net_send(fd, "GET / HTTP/1.0\r\nHost: " & host & "\r\nConnection: close\r\n\r\n")
|
||||
|
||||
print("Waiting for response...\n")
|
||||
|
||||
|
||||
# Simple read loop
|
||||
var total = 0
|
||||
while true:
|
||||
# lb.pump_membrane_stack()
|
||||
let resp = lnet.net_recv(fd, 512)
|
||||
if resp.len > 0:
|
||||
print(resp)
|
||||
total += resp.len
|
||||
else:
|
||||
# End of stream or empty poll
|
||||
break
|
||||
|
||||
|
||||
print("\n[HTTP] Closed. Total bytes: " & $total & "\n")
|
||||
lnet.net_close(fd)
|
||||
return @[]
|
||||
|
||||
proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData =
|
||||
print("[Server] Starting Nexus Web/1.0...\n")
|
||||
|
||||
|
||||
let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80
|
||||
|
||||
|
||||
let s = lb.socket(2, 1, 0)
|
||||
if s < 0:
|
||||
print("Error: Socket creation failed.\n")
|
||||
return @[]
|
||||
|
||||
|
||||
# Bind 0.0.0.0:port
|
||||
var addr_buf: array[16, byte]
|
||||
addr_buf[0] = 2 # AF_INET
|
||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||
# IP 0.0.0.0 is default 0s
|
||||
|
||||
|
||||
if lb.bind_socket(s, addr addr_buf[0], 16) < 0:
|
||||
print("Error: Bind failed.\n")
|
||||
return @[]
|
||||
|
||||
|
||||
if lb.listen(s, 1) < 0:
|
||||
print("Error: Listen failed.\n")
|
||||
return @[]
|
||||
|
||||
|
||||
print("[Server] Listening on port " & $port & "...\n")
|
||||
|
||||
|
||||
while true:
|
||||
# Accept blocks and pumps stack
|
||||
let client = lb.accept(s, nil, nil)
|
||||
if client < 0:
|
||||
print("Error: Accept failed.\n")
|
||||
continue
|
||||
|
||||
|
||||
print("[Server] Client Connected (FD " & $client & ")\n")
|
||||
|
||||
|
||||
var buf: array[1024, char]
|
||||
let n = lb.recv(client, addr buf[0], 1024, 0)
|
||||
|
||||
|
||||
if n > 0:
|
||||
var req = ""
|
||||
for i in 0..<n: req.add(buf[i])
|
||||
print("[Server] Request:\n" & req & "\n")
|
||||
|
||||
|
||||
let resp = "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello from Nexus Unikernel!\n"
|
||||
discard lb.send(client, unsafeAddr resp[0], uint64(resp.len), 0)
|
||||
|
||||
|
||||
discard lb.close(client)
|
||||
print("[Server] Client Closed.\n")
|
||||
|
||||
|
||||
# Just handle one for testing? No, loop forever.
|
||||
# But how to exit? Ctrl-C not implemented in NipBox yet?
|
||||
# We'll just run forever for MVP.
|
||||
|
||||
|
||||
return @[]
|
||||
|
||||
proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData =
|
||||
|
|
@ -806,18 +774,8 @@ proc cmd_set*(args: seq[string], input: PipelineData): PipelineData =
|
|||
|
||||
|
||||
proc cmd_help*(args: seq[string], input: PipelineData): PipelineData =
|
||||
when defined(NIPBOX_LITE):
|
||||
print("NipBox " & NIPBOX_VERSION & " [LITE] (Phase 34: Orbital Drop)\n")
|
||||
print("Commands: ls, cat, cp, mv, touch, edit, echo, where, http, http.get,\n")
|
||||
print(" http.test, http.serve, from_json, mount, matrix, crash,\n")
|
||||
print(" sys.upgrade, set, if, while, help, exit\n")
|
||||
print("Disabled: write, read, http.download (requires SFS)\n")
|
||||
else:
|
||||
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
|
||||
print("Commands: ls, cat, cp, mv, touch, edit, echo, where, write, read,\n")
|
||||
print(" http, http.get, http.download, http.test, http.serve,\n")
|
||||
print(" from_json, mount, matrix, crash, sys.upgrade,\n")
|
||||
print(" set, if, while, help, exit\n")
|
||||
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
|
||||
print("Commands: ls, cat, echo, where, http, http.get, http.download, from_json, mount, matrix, set, if, while, help, exit\n")
|
||||
return @[]
|
||||
|
||||
# --- DISPATCHER ---
|
||||
|
|
@ -844,16 +802,16 @@ proc dispatch_command(name: string, args: seq[string],
|
|||
if args.len < 1:
|
||||
print("Usage: http <ip>\n")
|
||||
return @[]
|
||||
|
||||
|
||||
let host = args[0]
|
||||
print("[NipBox] Dialing " & host & ":80...\n")
|
||||
|
||||
|
||||
# Use libc.socket/connect (Phase 38 Shim)
|
||||
let fd = lb.socket(2, 1, 0)
|
||||
if fd < 0:
|
||||
print("Socket Error\n")
|
||||
return @[]
|
||||
|
||||
|
||||
# Parse IP (Quick hack for 10.0.2.2)
|
||||
# We need proper parsing but let's assume raw IP for MVP
|
||||
var ip_val: uint32 = 0
|
||||
|
|
@ -866,28 +824,28 @@ proc dispatch_command(name: string, args: seq[string],
|
|||
except:
|
||||
print("Error: Invalid IP format (use A.B.C.D)\n")
|
||||
return @[]
|
||||
|
||||
|
||||
# Construct SockAddrIn (Layout must match libc.connect hack)
|
||||
var addr_buf: array[16, byte]
|
||||
# Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE?
|
||||
# 80 = 0x0050. LE in mem: 50 00.
|
||||
# LwIP wants host byte order or network?
|
||||
# LwIP wants host byte order or network?
|
||||
# connect() shim expects us to pass port as uint16.
|
||||
# But the shim casts addr_ptr+2 to uint16*.
|
||||
# If we write 80 there, it reads 80.
|
||||
|
||||
|
||||
let port: uint16 = 80
|
||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
||||
|
||||
|
||||
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
||||
print("Connect Failed\n")
|
||||
return @[]
|
||||
|
||||
|
||||
print("[NipBox] Connected! Sending Payload...\n")
|
||||
let req = "GET / HTTP/1.0\r\n\r\n"
|
||||
discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0)
|
||||
|
||||
|
||||
print("[NipBox] Waiting for Data...\n")
|
||||
var buf: array[1024, char]
|
||||
while true:
|
||||
|
|
@ -898,7 +856,7 @@ proc dispatch_command(name: string, args: seq[string],
|
|||
print(s)
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
print("\n[NipBox] Done.\n")
|
||||
return @[]
|
||||
of "http.test": return cmd_http_test(args, input)
|
||||
|
|
@ -1109,7 +1067,7 @@ proc run_script(path: string) =
|
|||
proc nipbox_main*() =
|
||||
# DIAGNOSTIC: Very first thing - prove we're executing
|
||||
print("[NIPBOX] Entry point reached!\n")
|
||||
|
||||
|
||||
# Phase 30: Pledge Safety
|
||||
# NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones
|
||||
# For now, we PLEDGE_ALL because the shell needs to explore
|
||||
|
|
@ -1129,7 +1087,7 @@ proc nipbox_main*() =
|
|||
# Phase 38: Boot Script
|
||||
run_script("/init.nsh")
|
||||
print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n")
|
||||
|
||||
|
||||
|
||||
print("\x1b[1;33mroot@nexus:# \x1b[0m")
|
||||
var inputBuffer: string = ""
|
||||
|
|
|
|||
130
run.sh
130
run.sh
|
|
@ -1,122 +1,22 @@
|
|||
#!/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")"
|
||||
#!/bin/bash
|
||||
# Rumpk QEMU Boot Script
|
||||
|
||||
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
|
||||
RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
KERNEL="$RUMPK_DIR/zig-out/bin/rumpk.elf"
|
||||
|
||||
if [ ! -f "$KERNEL" ]; then
|
||||
echo "ERROR: No kernel binary found. Run: ./build_nim.sh && zig build -Dtarget=riscv64-freestanding-none -Dcpu=sifive_u54"
|
||||
echo "ERROR: Kernel not found at $KERNEL"
|
||||
echo "Run ./build.sh first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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
|
||||
echo "🚀 Booting Rumpk..."
|
||||
echo " Kernel: $KERNEL"
|
||||
echo ""
|
||||
|
||||
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
|
||||
qemu-system-riscv64 \
|
||||
-M virt \
|
||||
-cpu max \
|
||||
-m 512M \
|
||||
-nographic \
|
||||
-kernel "$KERNEL"
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ 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 \
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -13,76 +13,15 @@
|
|||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#include <termios.h>
|
||||
#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;
|
||||
extern void console_write(const void* p, size_t len);
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
// Stubs
|
||||
|
|
@ -93,14 +32,7 @@ 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) {
|
||||
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 sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { 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; }
|
||||
|
|
@ -113,21 +45,7 @@ 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; }
|
||||
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 fcntl(int fd, int cmd, ...) { 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
|
||||
|
|
@ -147,26 +65,7 @@ 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) {
|
||||
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 tcgetattr(int fd, 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; }
|
||||
int tcsetpgrp(int fd, pid_t pgrp) { return 0; }
|
||||
|
|
@ -229,56 +128,7 @@ mode_t umask(mode_t mask) { return 0; }
|
|||
|
||||
int c_ulimit(const char **wp) { 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 longjmp(jmp_buf env, int val) { while(1); }
|
||||
int setjmp(jmp_buf env) { return 0; }
|
||||
|
||||
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);
|
||||
}
|
||||
void exit(int status) { while(1); }
|
||||
|
|
|
|||
Loading…
Reference in New Issue