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
|
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() =
|
proc main() =
|
||||||
# 1. Pledge Sovereignty
|
# 1. Pledge Sovereignty
|
||||||
discard pledge(0xFFFFFFFFFFFFFFFF'u64) # PLEDGE_ALL
|
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║ SOVEREIGN INIT (NexInit v1.0) ║\x1b[0m\n"))
|
||||||
print(cstring("\x1b[1;35m╚═══════════════════════════════════════╝\x1b[0m\n\n"))
|
print(cstring("\x1b[1;35m╚═══════════════════════════════════════╝\x1b[0m\n\n"))
|
||||||
|
|
||||||
print(cstring("[INIT] PHASE_42_VERIFY: Membrane Network Stack...\\n"))
|
print(cstring("[INIT] Initializing Membrane Network Stack...\n"))
|
||||||
# DISABLED: Network stack requires LwIP
|
membrane_init()
|
||||||
# membrane_init()
|
|
||||||
|
|
||||||
# proc glue_get_ip(): uint32 {.importc: "glue_get_ip", cdecl.}
|
proc glue_get_ip(): uint32 {.importc: "glue_get_ip", cdecl.}
|
||||||
|
|
||||||
# # --- DHCP PHASE ---
|
# --- DHCP PHASE ---
|
||||||
# print(cstring("[INIT] Waiting for DHCP IP Address...\n"))
|
print(cstring("[INIT] Waiting for DHCP IP Address...\n"))
|
||||||
# var ip: uint32 = 0
|
var ip: uint32 = 0
|
||||||
# for i in 0 ..< 600: # 60 seconds
|
for i in 0 ..< 600: # 60 seconds
|
||||||
# pump_membrane_stack()
|
pump_membrane_stack()
|
||||||
# ip = glue_get_ip()
|
ip = glue_get_ip()
|
||||||
# if ip != 0: break
|
if ip != 0: break
|
||||||
# discard syscall(0x65, 100000000'u64) # 100ms
|
discard syscall(0x65, 100000000'u64) # 100ms
|
||||||
|
|
||||||
# if ip == 0:
|
if ip == 0:
|
||||||
# print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n"))
|
print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n"))
|
||||||
# else:
|
else:
|
||||||
# print(cstring("[INIT] Network ONLINE (10.0.2.15)\n"))
|
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
|
|
||||||
|
|
||||||
|
# --- 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 ---
|
# --- SHELL PHASE ---
|
||||||
proc spawn_fiber(path: cstring): int =
|
proc spawn_fiber(path: cstring): int =
|
||||||
return int(syscall(0x300, cast[uint64](path), 0, 0))
|
return int(syscall(0x300, cast[uint64](path), 0, 0))
|
||||||
|
|
||||||
print(cstring("[INIT] Spawning mksh...\n"))
|
print(cstring("[INIT] Spawning mksh...\n"))
|
||||||
discard spawn_fiber(cstring("/bin/mksh"))
|
discard spawn_fiber(cstring("/bin/mksh"))
|
||||||
|
|
||||||
# --- SUPERVISOR PHASE ---
|
# --- SUPERVISOR PHASE ---
|
||||||
print(cstring("[INIT] Entering Supervisor Loop...\n"))
|
print(cstring("[INIT] Entering Supervisor Loop...\n"))
|
||||||
var loop_count = 0
|
var loop_count = 0
|
||||||
while true:
|
while true:
|
||||||
# pump_membrane_stack() # DISABLED: Requires LwIP
|
pump_membrane_stack()
|
||||||
loop_count += 1
|
loop_count += 1
|
||||||
if loop_count mod 0x100000 == 0: # Every ~1M iterations
|
if loop_count mod 100 == 0:
|
||||||
discard syscall(0x65, 1000000000'u64) # 1s yield
|
print(cstring("[INIT] Heartbeat\n"))
|
||||||
discard syscall(0x65, 100000000'u64) # 100ms
|
discard syscall(0x65, 100000000'u64) # 100ms
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,6 @@ SECTIONS
|
||||||
*(.data.*)
|
*(.data.*)
|
||||||
} > RAM
|
} > RAM
|
||||||
|
|
||||||
.nexus.manifest : {
|
|
||||||
KEEP(*(.nexus.manifest))
|
|
||||||
} > RAM
|
|
||||||
|
|
||||||
.bss : {
|
.bss : {
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
__bss_start = .;
|
__bss_start = .;
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,9 @@ _start:
|
||||||
2:
|
2:
|
||||||
fence rw, rw
|
fence rw, rw
|
||||||
|
|
||||||
# Valid Args from Stack (Linux ABI)
|
# Arguments (argc, argv) are already in a0, a1 from Kernel
|
||||||
ld a0, 0(sp) # argc
|
# sp is already pointing to argc from Kernel
|
||||||
addi a1, sp, 8 # argv
|
|
||||||
|
|
||||||
# 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 main
|
||||||
|
|
||||||
# Call exit(result)
|
# 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
|
//! Rumpk Boot Header
|
||||||
//!
|
//!
|
||||||
//! Architecture-dispatched entry point for bare-metal boot.
|
//! Defines the Multiboot2 header for GRUB/QEMU and the bare-metal entry point.
|
||||||
//! Handles BSS clearing and stack initialization before jumping to HAL init.
|
//! Handles BSS clearing and stack initialization before jumping to the Nim kernel.
|
||||||
//!
|
//!
|
||||||
//! SAFETY: Executed in the earliest boot stage with no environment initialized.
|
//! SAFETY: Executed in the earliest boot stage with no environment initialized.
|
||||||
|
|
||||||
const std = @import("std");
|
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;
|
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 riscv_init() noreturn;
|
||||||
extern fn aarch64_init() void; // Returns void (calls rumpk_halt internally)
|
|
||||||
|
|
||||||
// =========================================================
|
|
||||||
// Entry Point (Architecture Dispatched)
|
|
||||||
// =========================================================
|
|
||||||
|
|
||||||
// 1MB Kernel Stack
|
// 1MB Kernel Stack
|
||||||
const STACK_SIZE = 0x100000;
|
const STACK_SIZE = 0x100000;
|
||||||
export var kernel_stack: [STACK_SIZE]u8 align(16) linksection(".bss.stack") = undefined;
|
export var kernel_stack: [STACK_SIZE]u8 align(16) linksection(".bss.stack") = undefined;
|
||||||
|
|
||||||
export fn _start() callconv(.naked) noreturn {
|
export fn _start() callconv(.naked) noreturn {
|
||||||
switch (builtin.cpu.arch) {
|
// Clear BSS, set up stack, then jump to RISC-V Init
|
||||||
.riscv64 => {
|
asm volatile (
|
||||||
asm volatile (
|
\\ // Set up stack
|
||||||
\\ // Set up stack
|
\\ la sp, kernel_stack
|
||||||
\\ la sp, kernel_stack
|
\\ li t0, %[stack_size]
|
||||||
\\ li t0, %[stack_size]
|
\\ add sp, sp, t0
|
||||||
\\ add sp, sp, t0
|
\\
|
||||||
\\
|
\\ // Clear BSS
|
||||||
\\ // Clear BSS
|
\\ la t0, __bss_start
|
||||||
\\ la t0, __bss_start
|
\\ la t1, __bss_end
|
||||||
\\ la t1, __bss_end
|
\\1:
|
||||||
\\1:
|
\\ bge t0, t1, 2f
|
||||||
\\ bge t0, t1, 2f
|
\\ sd zero, (t0)
|
||||||
\\ sd zero, (t0)
|
\\ addi t0, t0, 8
|
||||||
\\ addi t0, t0, 8
|
\\ j 1b
|
||||||
\\ j 1b
|
\\2:
|
||||||
\\2:
|
\\ // Jump to HAL Init
|
||||||
\\ // Jump to RISC-V HAL Init
|
\\ call riscv_init
|
||||||
\\ call riscv_init
|
\\
|
||||||
\\
|
\\ // Should never return
|
||||||
\\ // Should never return
|
\\ wfi
|
||||||
\\ wfi
|
\\ j 2b
|
||||||
\\ j 2b
|
:
|
||||||
:
|
: [stack_size] "i" (STACK_SIZE),
|
||||||
: [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;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
383
build.zig
383
build.zig
|
|
@ -5,111 +5,31 @@
|
||||||
// This file is part of the Nexus Commonwealth.
|
// This file is part of the Nexus Commonwealth.
|
||||||
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
||||||
|
|
||||||
//! Rumpk Build System — Unified Pipeline
|
//! Rumpk Build System
|
||||||
//!
|
//!
|
||||||
//! Single command builds everything: Nim kernel + Zig HAL + LwIP + link.
|
//! Orchestrates L0 (Zig) and L1 (Nim) compilation.
|
||||||
//!
|
//! Builds the Hardware Abstraction Layer as a static library.
|
||||||
//! Usage:
|
|
||||||
//! zig build # Build kernel (default: riscv64)
|
|
||||||
//! zig build -Darch=aarch64 # ARM64 target
|
|
||||||
//! zig build -Darch=x86_64 # AMD64 target
|
|
||||||
//! zig build nim # Recompile Nim kernel sources only
|
|
||||||
//! zig build test-lwf # Run LWF tests (native host)
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const Arch = enum { riscv64, aarch64, x86_64 };
|
|
||||||
|
|
||||||
/// Per-architecture build configuration.
|
|
||||||
const ArchConfig = struct {
|
|
||||||
cpu_arch: std.Target.Cpu.Arch,
|
|
||||||
code_model: std.builtin.CodeModel,
|
|
||||||
switch_asm: []const u8,
|
|
||||||
linker_script: []const u8,
|
|
||||||
/// Arch name for build_nim.sh
|
|
||||||
nim_arch: []const u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn archConfig(arch: Arch) ArchConfig {
|
|
||||||
return switch (arch) {
|
|
||||||
.riscv64 => .{
|
|
||||||
.cpu_arch = .riscv64,
|
|
||||||
.code_model = .medany,
|
|
||||||
.switch_asm = "hal/arch/riscv64/switch.S",
|
|
||||||
.linker_script = "boot/linker.ld",
|
|
||||||
.nim_arch = "riscv64",
|
|
||||||
},
|
|
||||||
.aarch64 => .{
|
|
||||||
.cpu_arch = .aarch64,
|
|
||||||
.code_model = .default,
|
|
||||||
.switch_asm = "hal/arch/aarch64/switch.S",
|
|
||||||
.linker_script = "boot/linker_aarch64.ld",
|
|
||||||
.nim_arch = "aarch64",
|
|
||||||
},
|
|
||||||
.x86_64 => .{
|
|
||||||
.cpu_arch = .x86_64,
|
|
||||||
.code_model = .kernel,
|
|
||||||
.switch_asm = "hal/arch/x86_64/switch.S",
|
|
||||||
.linker_script = "boot/linker_x86_64.ld",
|
|
||||||
.nim_arch = "x86_64",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply freestanding kernel settings to a module.
|
|
||||||
fn applyKernelSettings(mod: *std.Build.Module, code_model: std.builtin.CodeModel) void {
|
|
||||||
mod.red_zone = false;
|
|
||||||
mod.stack_check = false;
|
|
||||||
mod.code_model = code_model;
|
|
||||||
mod.sanitize_thread = false;
|
|
||||||
mod.single_threaded = true;
|
|
||||||
mod.strip = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
// =========================================================
|
const target = b.standardTargetOptions(.{});
|
||||||
// Target Selection: -Darch=riscv64|aarch64|x86_64
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
// =========================================================
|
|
||||||
|
|
||||||
const arch = b.option(Arch, "arch", "Target architecture (default: riscv64)") orelse .riscv64;
|
|
||||||
const config = archConfig(arch);
|
|
||||||
|
|
||||||
var query: std.Target.Query = .{
|
|
||||||
.cpu_arch = config.cpu_arch,
|
|
||||||
.os_tag = .freestanding,
|
|
||||||
.abi = .none,
|
|
||||||
};
|
|
||||||
// Disable NEON/FP in kernel code generation to avoid unaligned SIMD stores
|
|
||||||
if (config.cpu_arch == .aarch64) {
|
|
||||||
query.cpu_features_sub = std.Target.aarch64.featureSet(&.{.neon});
|
|
||||||
}
|
|
||||||
const target = b.resolveTargetQuery(query);
|
|
||||||
|
|
||||||
// HEPHAESTUS DECREE: Force ReleaseSmall. A kernel runs naked.
|
|
||||||
const optimize: std.builtin.OptimizeMode = .ReleaseSmall;
|
|
||||||
|
|
||||||
// =========================================================
|
|
||||||
// Step: Nim Kernel Compilation (nim -> C -> .o)
|
|
||||||
// =========================================================
|
|
||||||
// Calls build_nim.sh which handles:
|
|
||||||
// 1. nim c --compileOnly -> generates C
|
|
||||||
// 2. zig cc -> cross-compiles C to .o
|
|
||||||
// Incremental: only recompiles changed files.
|
|
||||||
|
|
||||||
const nim_step = b.addSystemCommand(&.{ "./build_nim.sh", config.nim_arch });
|
|
||||||
const nim_build_step = b.step("nim", "Recompile Nim kernel sources (nim -> C -> .o)");
|
|
||||||
nim_build_step.dependOn(&nim_step.step);
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// L0: Hardware Abstraction Layer (Zig)
|
// L0: Hardware Abstraction Layer (Zig)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
|
// NOTE(Build): Zig 0.15.x API - using addLibrary with static linkage
|
||||||
const hal_mod = b.createModule(.{
|
const hal_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("hal/abi.zig"),
|
.root_source_file = b.path("hal/abi.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
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(.{
|
const hal = b.addLibrary(.{
|
||||||
.name = "rumpk_hal",
|
.name = "rumpk_hal",
|
||||||
|
|
@ -119,6 +39,15 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
b.installArtifact(hal);
|
b.installArtifact(hal);
|
||||||
|
|
||||||
|
// TODO(Build): Microui needs stdio.h stubs for freestanding.
|
||||||
|
// Re-enable after creating libs/microui/stdio_stub.h
|
||||||
|
// Microui Integration (Phase 3.5b)
|
||||||
|
// hal_mod.addIncludePath(b.path("libs/microui"));
|
||||||
|
// hal_mod.addCSourceFile(.{
|
||||||
|
// .file = b.path("libs/microui/microui.c"),
|
||||||
|
// .flags = &.{"-std=c99"},
|
||||||
|
// });
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Boot: Entry Point (Assembly + Zig)
|
// Boot: Entry Point (Assembly + Zig)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -128,230 +57,38 @@ pub fn build(b: *std.Build) void {
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.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(.{
|
const boot = b.addObject(.{
|
||||||
.name = "boot",
|
.name = "boot",
|
||||||
.root_module = boot_mod,
|
.root_module = boot_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
// =========================================================
|
|
||||||
// LwIP: Lightweight TCP/IP Stack
|
|
||||||
// =========================================================
|
|
||||||
// Compiled as C objects with freestanding flags.
|
|
||||||
// ubsan disabled per-file to avoid runtime dependency.
|
|
||||||
|
|
||||||
const lwip_base = "libs/membrane/external/lwip/src/";
|
|
||||||
const lwip_srcs = [_][]const u8{
|
|
||||||
// Core
|
|
||||||
lwip_base ++ "core/init.c",
|
|
||||||
lwip_base ++ "core/def.c",
|
|
||||||
lwip_base ++ "core/dns.c",
|
|
||||||
lwip_base ++ "core/inet_chksum.c",
|
|
||||||
lwip_base ++ "core/ip.c",
|
|
||||||
lwip_base ++ "core/mem.c",
|
|
||||||
lwip_base ++ "core/memp.c",
|
|
||||||
lwip_base ++ "core/netif.c",
|
|
||||||
lwip_base ++ "core/pbuf.c",
|
|
||||||
lwip_base ++ "core/raw.c",
|
|
||||||
lwip_base ++ "core/sys.c",
|
|
||||||
lwip_base ++ "core/tcp.c",
|
|
||||||
lwip_base ++ "core/tcp_in.c",
|
|
||||||
lwip_base ++ "core/tcp_out.c",
|
|
||||||
lwip_base ++ "core/timeouts.c",
|
|
||||||
lwip_base ++ "core/udp.c",
|
|
||||||
// IPv4
|
|
||||||
lwip_base ++ "core/ipv4/autoip.c",
|
|
||||||
lwip_base ++ "core/ipv4/dhcp.c",
|
|
||||||
lwip_base ++ "core/ipv4/etharp.c",
|
|
||||||
lwip_base ++ "core/ipv4/icmp.c",
|
|
||||||
lwip_base ++ "core/ipv4/ip4.c",
|
|
||||||
lwip_base ++ "core/ipv4/ip4_addr.c",
|
|
||||||
lwip_base ++ "core/ipv4/ip4_frag.c",
|
|
||||||
// Netif
|
|
||||||
lwip_base ++ "netif/ethernet.c",
|
|
||||||
// SysArch (Rumpk integration)
|
|
||||||
"libs/membrane/sys_arch.c",
|
|
||||||
};
|
|
||||||
|
|
||||||
const lwip_flags: []const []const u8 = switch (arch) {
|
|
||||||
.riscv64 => &.{
|
|
||||||
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
||||||
"-DNO_SYS=1", "-mcmodel=medany", "-Icore",
|
|
||||||
"-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
|
||||||
},
|
|
||||||
.aarch64 => &.{
|
|
||||||
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
||||||
"-DNO_SYS=1", "-Icore", "-Ilibs/membrane",
|
|
||||||
"-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
|
||||||
},
|
|
||||||
.x86_64 => &.{
|
|
||||||
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
||||||
"-DNO_SYS=1", "-mcmodel=kernel", "-Icore",
|
|
||||||
"-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const liblwip = b.addLibrary(.{
|
|
||||||
.name = "lwip",
|
|
||||||
.root_module = b.createModule(.{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
}),
|
|
||||||
.linkage = .static,
|
|
||||||
});
|
|
||||||
liblwip.root_module.sanitize_thread = false;
|
|
||||||
liblwip.root_module.red_zone = false;
|
|
||||||
liblwip.root_module.single_threaded = true;
|
|
||||||
|
|
||||||
for (lwip_srcs) |src| {
|
|
||||||
liblwip.addCSourceFile(.{
|
|
||||||
.file = b.path(src),
|
|
||||||
.flags = lwip_flags,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
b.installArtifact(liblwip);
|
|
||||||
|
|
||||||
// =========================================================
|
|
||||||
// LittleFS: Sovereign Filesystem (Persistent Storage)
|
|
||||||
// =========================================================
|
|
||||||
// Compiled as C object with freestanding flags.
|
|
||||||
// Uses LFS_CONFIG=lfs_rumpk.h to replace lfs_util.h entirely,
|
|
||||||
// avoiding stdio/assert deps. malloc/free provided by clib.c.
|
|
||||||
|
|
||||||
const lfs_flags: []const []const u8 = switch (arch) {
|
|
||||||
.riscv64 => &.{
|
|
||||||
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
||||||
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=medany",
|
|
||||||
},
|
|
||||||
.aarch64 => &.{
|
|
||||||
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
||||||
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs",
|
|
||||||
},
|
|
||||||
.x86_64 => &.{
|
|
||||||
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
|
||||||
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=kernel",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const lfs_obj = b.addObject(.{
|
|
||||||
.name = "littlefs",
|
|
||||||
.root_module = b.createModule(.{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
lfs_obj.root_module.sanitize_thread = false;
|
|
||||||
lfs_obj.root_module.red_zone = false;
|
|
||||||
lfs_obj.root_module.single_threaded = true;
|
|
||||||
|
|
||||||
lfs_obj.addCSourceFile(.{
|
|
||||||
.file = b.path("vendor/littlefs/lfs.c"),
|
|
||||||
.flags = lfs_flags,
|
|
||||||
});
|
|
||||||
|
|
||||||
// =========================================================
|
|
||||||
// Dependencies (CLib, LibC Shim, LWF Adapter, Switch)
|
|
||||||
// =========================================================
|
|
||||||
|
|
||||||
// 1. CLib (Minimal C stdlib)
|
|
||||||
const clib = b.addObject(.{
|
|
||||||
.name = "clib",
|
|
||||||
.root_module = b.createModule(.{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
clib.addCSourceFile(.{
|
|
||||||
.file = b.path("libs/membrane/clib.c"),
|
|
||||||
.flags = switch (arch) {
|
|
||||||
.riscv64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=medany" },
|
|
||||||
.aarch64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1" },
|
|
||||||
.x86_64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=kernel" },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. LibC Shim (Zig)
|
|
||||||
const libc_shim_mod = b.createModule(.{
|
|
||||||
.root_source_file = b.path("libs/membrane/libc_shim.zig"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
applyKernelSettings(libc_shim_mod, config.code_model);
|
|
||||||
|
|
||||||
const libc_shim = b.addObject(.{
|
|
||||||
.name = "libc_shim",
|
|
||||||
.root_module = libc_shim_mod,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 3. LittleFS HAL (VirtIO-Block ↔ LittleFS glue)
|
|
||||||
const lfs_hal_mod = b.createModule(.{
|
|
||||||
.root_source_file = b.path("hal/littlefs_hal.zig"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
applyKernelSettings(lfs_hal_mod, config.code_model);
|
|
||||||
|
|
||||||
const lfs_hal = b.addObject(.{
|
|
||||||
.name = "lfs_hal",
|
|
||||||
.root_module = lfs_hal_mod,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 4. LWF Adapter (Project LibWeb — Libertaria Wire Frame)
|
|
||||||
const lwf_adapter_mod = b.createModule(.{
|
|
||||||
.root_source_file = b.path("libs/libertaria/lwf_adapter.zig"),
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
applyKernelSettings(lwf_adapter_mod, config.code_model);
|
|
||||||
|
|
||||||
const lwf_adapter = b.addObject(.{
|
|
||||||
.name = "lwf_adapter",
|
|
||||||
.root_module = lwf_adapter_mod,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 4. Context Switch (Architecture-specific Assembly)
|
|
||||||
const switch_obj = b.addObject(.{
|
|
||||||
.name = "switch",
|
|
||||||
.root_module = b.createModule(.{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
switch_obj.addAssemblyFile(b.path(config.switch_asm));
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Final Link: rumpk.elf
|
// Final Link: rumpk.elf
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
const kernel_mod = b.createModule(.{
|
const kernel_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("hal/abi.zig"),
|
.root_source_file = b.path("hal/abi.zig"), // Fake root, we add objects later
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.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(.{
|
const kernel = b.addExecutable(.{
|
||||||
.name = "rumpk.elf",
|
.name = "rumpk.elf",
|
||||||
.root_module = kernel_mod,
|
.root_module = kernel_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
kernel.setLinkerScript(b.path(config.linker_script));
|
kernel.setLinkerScript(b.path("boot/linker.ld"));
|
||||||
kernel.addObject(boot);
|
kernel.addObject(boot);
|
||||||
kernel.addObject(clib);
|
// kernel.linkLibrary(hal); // Redundant, already in kernel_mod
|
||||||
kernel.addObject(libc_shim);
|
|
||||||
kernel.addObject(lfs_hal);
|
|
||||||
kernel.addObject(lwf_adapter);
|
|
||||||
kernel.addObject(switch_obj);
|
|
||||||
|
|
||||||
// LwIP linked into kernel — Membrane/NetSwitch drives DHCP/TCP/ICMP.
|
// Add Nim-generated objects
|
||||||
kernel.linkLibrary(liblwip);
|
|
||||||
|
|
||||||
// LittleFS IS linked into kernel — provides /nexus persistent storage.
|
|
||||||
kernel.addObject(lfs_obj);
|
|
||||||
|
|
||||||
// Nim-generated objects from build/nimcache/
|
|
||||||
{
|
{
|
||||||
var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
|
var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
|
||||||
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
||||||
|
|
@ -367,63 +104,31 @@ pub fn build(b: *std.Build) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Embedded InitRD — assemble for the target architecture
|
// Add external pre-built dependencies (Order matters: Libs after users)
|
||||||
const initrd_obj = b.addObject(.{
|
kernel.addObjectFile(b.path("build/switch.o")); // cpu_switch_to
|
||||||
.name = "initrd",
|
kernel.addObjectFile(b.path("build/sys_arch.o")); // sys_now, nexus_lwip_panic
|
||||||
.root_module = b.createModule(.{
|
kernel.addObjectFile(b.path("build/libc_shim.o"));
|
||||||
.target = target,
|
kernel.addObjectFile(b.path("build/clib.o"));
|
||||||
.optimize = optimize,
|
kernel.addObjectFile(b.path("build/liblwip.a"));
|
||||||
}),
|
kernel.addObjectFile(b.path("build/initrd.o"));
|
||||||
});
|
|
||||||
initrd_obj.addAssemblyFile(b.path("build/embed_initrd.S"));
|
|
||||||
kernel.addObject(initrd_obj);
|
|
||||||
|
|
||||||
b.installArtifact(kernel);
|
b.installArtifact(kernel);
|
||||||
|
|
||||||
// Make default install depend on Nim compilation
|
|
||||||
kernel.step.dependOn(&nim_step.step);
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Tests (always run on native host)
|
// Tests
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
// HAL unit tests
|
const test_mod = b.createModule(.{
|
||||||
const hal_test_mod = b.createModule(.{
|
|
||||||
.root_source_file = b.path("hal/abi.zig"),
|
.root_source_file = b.path("hal/abi.zig"),
|
||||||
.target = b.graph.host,
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
const hal_tests = b.addTest(.{
|
const hal_tests = b.addTest(.{
|
||||||
.root_module = hal_test_mod,
|
.root_module = test_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
const run_hal_tests = b.addRunArtifact(hal_tests);
|
const run_tests = b.addRunArtifact(hal_tests);
|
||||||
const test_step = b.step("test", "Run Rumpk HAL tests (native host)");
|
const test_step = b.step("test", "Run Rumpk HAL tests");
|
||||||
test_step.dependOn(&run_hal_tests.step);
|
test_step.dependOn(&run_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);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ proc cspace_grant_cap*(
|
||||||
proc cspace_lookup*(fiber_id: uint64, slot: uint): pointer {.importc, cdecl.}
|
proc cspace_lookup*(fiber_id: uint64, slot: uint): pointer {.importc, cdecl.}
|
||||||
proc cspace_revoke*(fiber_id: uint64, slot: uint) {.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_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)
|
## Capability Types (Mirror from cspace.zig)
|
||||||
type
|
type
|
||||||
|
|
@ -81,10 +80,10 @@ proc fiber_grant_memory*(
|
||||||
end_addr
|
end_addr
|
||||||
)
|
)
|
||||||
|
|
||||||
proc fiber_check_channel_access*(fiber_id: uint64, channel_id: uint64, write: bool): bool =
|
proc fiber_check_channel_access*(fiber_id: uint64, slot: uint, write: bool): bool =
|
||||||
## Check if fiber has Channel capability for given channel_id
|
## Check if fiber has channel access via capability
|
||||||
let perm = if write: PERM_WRITE else: PERM_READ
|
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) =
|
proc fiber_revoke_capability*(fiber_id: uint64, slot: uint) =
|
||||||
## Revoke a capability from a fiber
|
## Revoke a capability from a fiber
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||||
# Rumpk Phase 10: Multitasking & Context Switching
|
# Rumpk Phase 10: Multitasking & Context Switching
|
||||||
#
|
#
|
||||||
# Responsibilities:
|
# Responsibilities:
|
||||||
# - Define the Fiber abstraction (Hardware Context + Stack)
|
# - Define the Fiber abstraction (Hardware Context + Stack)
|
||||||
# - Abstract the ISA-specific context switch mechanism
|
# - Abstract the ISA-specific context switch mechanism
|
||||||
|
|
@ -24,10 +24,6 @@ when defined(riscv64):
|
||||||
const ARCH_NAME* = "riscv64"
|
const ARCH_NAME* = "riscv64"
|
||||||
const CONTEXT_SIZE* = 128
|
const CONTEXT_SIZE* = 128
|
||||||
const RET_ADDR_INDEX* = 0 # Offset in stack for RA
|
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):
|
elif defined(amd64) or defined(x86_64):
|
||||||
const ARCH_NAME* = "amd64"
|
const ARCH_NAME* = "amd64"
|
||||||
const CONTEXT_SIZE* = 64
|
const CONTEXT_SIZE* = 64
|
||||||
|
|
@ -116,7 +112,7 @@ const STACK_SIZE* = 4096
|
||||||
# Fiber State
|
# Fiber State
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
var main_fiber*: FiberObject
|
var main_fiber: FiberObject
|
||||||
var current_fiber* {.global.}: Fiber = addr main_fiber
|
var current_fiber* {.global.}: Fiber = addr main_fiber
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
@ -139,9 +135,6 @@ proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
|
||||||
when defined(riscv64):
|
when defined(riscv64):
|
||||||
while true:
|
while true:
|
||||||
{.emit: "asm volatile(\"wfi\");".}
|
{.emit: "asm volatile(\"wfi\");".}
|
||||||
elif defined(arm64):
|
|
||||||
while true:
|
|
||||||
{.emit: "asm volatile(\"wfe\");".}
|
|
||||||
else:
|
else:
|
||||||
while true: discard
|
while true: discard
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@
|
||||||
## Freestanding implementation (No OS module dependencies).
|
## Freestanding implementation (No OS module dependencies).
|
||||||
## Uses fixed-size arrays for descriptors to ensure deterministic latency.
|
## Uses fixed-size arrays for descriptors to ensure deterministic latency.
|
||||||
|
|
||||||
import tar, sfs, lfs_bridge
|
import tar, sfs
|
||||||
|
|
||||||
type
|
type
|
||||||
VFSMode = enum
|
VFSMode = enum
|
||||||
MODE_TAR, MODE_SFS, MODE_RAM, MODE_TTY, MODE_LFS
|
MODE_TAR, MODE_SFS, MODE_RAM, MODE_TTY
|
||||||
|
|
||||||
MountPoint = object
|
MountPoint = object
|
||||||
prefix: array[32, char]
|
prefix: array[32, char]
|
||||||
|
|
@ -25,7 +25,6 @@ type
|
||||||
offset: uint64
|
offset: uint64
|
||||||
mode: VFSMode
|
mode: VFSMode
|
||||||
active: bool
|
active: bool
|
||||||
lfs_handle: int32 ## LFS file handle (-1 = not open)
|
|
||||||
|
|
||||||
const MAX_MOUNTS = 8
|
const MAX_MOUNTS = 8
|
||||||
const MAX_FDS = 32
|
const MAX_FDS = 32
|
||||||
|
|
@ -65,16 +64,9 @@ proc vfs_add_mount(prefix: cstring, mode: VFSMode) =
|
||||||
mnt_table[mnt_count].mode = mode
|
mnt_table[mnt_count].mode = mode
|
||||||
mnt_count += 1
|
mnt_count += 1
|
||||||
|
|
||||||
proc kprintln(s: cstring) {.importc, cdecl.}
|
|
||||||
|
|
||||||
proc vfs_mount_init*() =
|
proc vfs_mount_init*() =
|
||||||
# Mount LittleFS for /nexus (persistent sovereign storage)
|
# Restore the SPEC-502 baseline
|
||||||
if lfs_bridge.lfs_mount_fs():
|
vfs_add_mount("/nexus", MODE_SFS)
|
||||||
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)
|
|
||||||
vfs_add_mount("/sysro", MODE_TAR)
|
vfs_add_mount("/sysro", MODE_TAR)
|
||||||
vfs_add_mount("/state", MODE_RAM)
|
vfs_add_mount("/state", MODE_RAM)
|
||||||
vfs_add_mount("/dev/tty", MODE_TTY)
|
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.} =
|
proc ion_vfs_open*(path: cstring, flags: int32): int32 {.exportc, cdecl.} =
|
||||||
let (mode, prefix_len) = resolve_path(path)
|
let (mode, prefix_len) = resolve_path(path)
|
||||||
|
|
||||||
# Delegate internal open
|
# Delegate internal open
|
||||||
let sub_path = cast[cstring](cast[uint64](path) + uint64(prefix_len))
|
let sub_path = cast[cstring](cast[uint64](path) + uint64(prefix_len))
|
||||||
var internal_fd: int32 = -1
|
var internal_fd: int32 = -1
|
||||||
|
|
||||||
# Map VFS flags to LFS flags for MODE_LFS
|
|
||||||
var lfs_h: int32 = -1
|
|
||||||
|
|
||||||
case mode:
|
case mode:
|
||||||
of MODE_TAR, MODE_RAM: internal_fd = tar.vfs_open(sub_path, flags)
|
of MODE_TAR, MODE_RAM: internal_fd = tar.vfs_open(sub_path, flags)
|
||||||
of MODE_SFS: internal_fd = 0 # Shim
|
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
|
of MODE_TTY: internal_fd = 1 # Shim
|
||||||
|
|
||||||
if internal_fd >= 0:
|
if internal_fd >= 0:
|
||||||
for i in 0..<MAX_FDS:
|
for i in 0..<MAX_FDS:
|
||||||
if not fd_table[i].active:
|
if not fd_table[i].active:
|
||||||
fd_table[i].active = true
|
fd_table[i].active = true
|
||||||
fd_table[i].mode = mode
|
fd_table[i].mode = mode
|
||||||
fd_table[i].offset = 0
|
fd_table[i].offset = 0
|
||||||
fd_table[i].lfs_handle = lfs_h
|
|
||||||
let p = cast[ptr UncheckedArray[char]](sub_path)
|
let p = cast[ptr UncheckedArray[char]](sub_path)
|
||||||
var j = 0
|
var j = 0
|
||||||
while p[j] != '\0' and j < 63:
|
while p[j] != '\0' and j < 63:
|
||||||
|
|
@ -128,15 +106,13 @@ proc ion_vfs_open*(path: cstring, flags: int32): int32 {.exportc, cdecl.} =
|
||||||
j += 1
|
j += 1
|
||||||
fd_table[i].path[j] = '\0'
|
fd_table[i].path[j] = '\0'
|
||||||
return int32(i + 3) # FDs start at 3
|
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
|
return -1
|
||||||
|
|
||||||
proc ion_vfs_read*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
proc ion_vfs_read*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
||||||
let idx = int(fd - 3)
|
let idx = int(fd - 3)
|
||||||
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||||
let fh = addr fd_table[idx]
|
let fh = addr fd_table[idx]
|
||||||
|
|
||||||
case fh.mode:
|
case fh.mode:
|
||||||
of MODE_TTY: return -2
|
of MODE_TTY: return -2
|
||||||
of MODE_TAR, MODE_RAM:
|
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
|
fh.offset += actual
|
||||||
return int64(actual)
|
return int64(actual)
|
||||||
return 0
|
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.} =
|
proc ion_vfs_write*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
||||||
let idx = int(fd - 3)
|
let idx = int(fd - 3)
|
||||||
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||||
let fh = addr fd_table[idx]
|
let fh = addr fd_table[idx]
|
||||||
|
|
||||||
case fh.mode:
|
case fh.mode:
|
||||||
of MODE_TTY: return -2
|
of MODE_TTY: return -2
|
||||||
of MODE_TAR, MODE_RAM:
|
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])
|
let path = cast[cstring](addr fh.path[0])
|
||||||
sfs.sfs_write_file(path, buf, int(count))
|
sfs.sfs_write_file(path, buf, int(count))
|
||||||
return int64(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.} =
|
proc ion_vfs_close*(fd: int32): int32 {.exportc, cdecl.} =
|
||||||
let idx = int(fd - 3)
|
let idx = int(fd - 3)
|
||||||
if idx >= 0 and idx < MAX_FDS:
|
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
|
fd_table[idx].active = false
|
||||||
return 0
|
return 0
|
||||||
return -1
|
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.} =
|
proc ion_vfs_list*(buf: pointer, max_len: uint64): int64 {.exportc, cdecl.} =
|
||||||
# Hardcoded baseline for now to avoid string/os dependency
|
# Hardcoded baseline for now to avoid string/os dependency
|
||||||
let msg = "/nexus\n/sysro\n/state\n"
|
let msg = "/nexus\n/sysro\n/state\n"
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,5 @@
|
||||||
|
|
||||||
double pow(double x, double y);
|
double pow(double x, double y);
|
||||||
double log10(double x);
|
double log10(double x);
|
||||||
double fabs(double x);
|
|
||||||
double floor(double x);
|
|
||||||
double ceil(double x);
|
|
||||||
double fmod(double x, double y);
|
|
||||||
double round(double x);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,10 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
typedef struct _FILE FILE;
|
|
||||||
|
|
||||||
extern FILE *stdin;
|
|
||||||
extern FILE *stdout;
|
|
||||||
extern FILE *stderr;
|
|
||||||
|
|
||||||
int printf(const char *format, ...);
|
int printf(const char *format, ...);
|
||||||
int fprintf(FILE *stream, const char *format, ...);
|
|
||||||
int sprintf(char *str, const char *format, ...);
|
int sprintf(char *str, const char *format, ...);
|
||||||
int snprintf(char *str, size_t size, const char *format, ...);
|
int snprintf(char *str, size_t size, const char *format, ...);
|
||||||
int vprintf(const char *format, va_list ap);
|
|
||||||
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||||
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
|
|
||||||
int fflush(FILE *stream);
|
|
||||||
int fputc(int c, FILE *stream);
|
|
||||||
int fputs(const char *s, FILE *stream);
|
|
||||||
int rename(const char *oldpath, const char *newpath);
|
int rename(const char *oldpath, const char *newpath);
|
||||||
int remove(const char *pathname);
|
int remove(const char *pathname);
|
||||||
|
|
||||||
|
|
|
||||||
39
core/ion.nim
39
core/ion.nim
|
|
@ -37,7 +37,7 @@ type
|
||||||
CMD_GET_GPU_STATUS = 0x102
|
CMD_GET_GPU_STATUS = 0x102
|
||||||
CMD_FS_OPEN = 0x200
|
CMD_FS_OPEN = 0x200
|
||||||
CMD_FS_READ = 0x201
|
CMD_FS_READ = 0x201
|
||||||
CMD_FS_READDIR = 0x202
|
CMD_FS_READDIR = 0x202
|
||||||
CMD_FS_WRITE = 0x203
|
CMD_FS_WRITE = 0x203
|
||||||
CMD_FS_MOUNT = 0x204
|
CMD_FS_MOUNT = 0x204
|
||||||
CMD_ION_FREE = 0x300
|
CMD_ION_FREE = 0x300
|
||||||
|
|
@ -79,7 +79,7 @@ type
|
||||||
|
|
||||||
SysTable* = object
|
SysTable* = object
|
||||||
magic*: uint32 # 0x4E585553
|
magic*: uint32 # 0x4E585553
|
||||||
reserved*: uint32
|
reserved*: uint32
|
||||||
s_rx*: ptr HAL_Ring[IonPacket]
|
s_rx*: ptr HAL_Ring[IonPacket]
|
||||||
s_tx*: ptr HAL_Ring[IonPacket]
|
s_tx*: ptr HAL_Ring[IonPacket]
|
||||||
s_event*: 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_list*: proc(buf: pointer, max_len: uint64): int64 {.cdecl.}
|
||||||
fn_vfs_write*: proc(fd: int32, buf: pointer, count: 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_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_log*: pointer
|
||||||
fn_pledge*: proc(promises: uint64): int32 {.cdecl.}
|
fn_pledge*: proc(promises: uint64): int32 {.cdecl.}
|
||||||
|
|
||||||
# Framebuffer
|
# Framebuffer
|
||||||
fb_addr*: uint64
|
fb_addr*: uint64
|
||||||
fb_width*: uint32
|
fb_width*: uint32
|
||||||
|
|
@ -117,18 +115,14 @@ type
|
||||||
# Phase 36.3: Shared ION (16 bytes)
|
# Phase 36.3: Shared ION (16 bytes)
|
||||||
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
||||||
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
||||||
|
|
||||||
# Phase 36.4: I/O Multiplexing (8 bytes)
|
# Phase 36.4: I/O Multiplexing (8 bytes)
|
||||||
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
||||||
|
|
||||||
# Phase 36.5: Network Hardware Info (8 bytes)
|
# Phase 36.5: Network Hardware Info (8 bytes)
|
||||||
net_mac*: array[6, byte]
|
net_mac*: array[6, byte]
|
||||||
reserved_mac*: array[2, 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
|
include invariant
|
||||||
|
|
||||||
# --- Sovereign Logic ---
|
# --- Sovereign Logic ---
|
||||||
|
|
@ -173,12 +167,6 @@ var net_rx_hal: HAL_Ring[IonPacket]
|
||||||
var net_tx_hal: HAL_Ring[IonPacket]
|
var net_tx_hal: HAL_Ring[IonPacket]
|
||||||
var netswitch_rx_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.} =
|
proc ion_init_input*() {.exportc, cdecl.} =
|
||||||
guest_input_hal.head = 0
|
guest_input_hal.head = 0
|
||||||
guest_input_hal.tail = 0
|
guest_input_hal.tail = 0
|
||||||
|
|
@ -193,7 +181,7 @@ proc ion_init_network*() {.exportc, cdecl.} =
|
||||||
net_rx_hal.tail = 0
|
net_rx_hal.tail = 0
|
||||||
net_rx_hal.mask = 255
|
net_rx_hal.mask = 255
|
||||||
chan_net_rx.ring = addr net_rx_hal
|
chan_net_rx.ring = addr net_rx_hal
|
||||||
|
|
||||||
net_tx_hal.head = 0
|
net_tx_hal.head = 0
|
||||||
net_tx_hal.tail = 0
|
net_tx_hal.tail = 0
|
||||||
net_tx_hal.mask = 255
|
net_tx_hal.mask = 255
|
||||||
|
|
@ -203,18 +191,7 @@ proc ion_init_network*() {.exportc, cdecl.} =
|
||||||
netswitch_rx_hal.tail = 0
|
netswitch_rx_hal.tail = 0
|
||||||
netswitch_rx_hal.mask = 255
|
netswitch_rx_hal.mask = 255
|
||||||
chan_netswitch_rx.ring = addr netswitch_rx_hal
|
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
|
# Initialize user slab
|
||||||
ion_user_slab_init()
|
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(IonPacket) == 24, "IonPacket size mismatch!")
|
||||||
static: doAssert(sizeof(CmdPacket) == 32, "CmdPacket 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
|
import ../ring
|
||||||
|
|
||||||
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
||||||
|
proc dbg(s: string) =
|
||||||
var NEWLINE_BUF: array[2, char] = ['\n', '\0']
|
console_write(unsafeAddr s[0], csize_t(s.len))
|
||||||
|
var nl = "\n"
|
||||||
proc dbg(s: cstring) {.inline.} =
|
console_write(unsafeAddr nl[0], csize_t(1))
|
||||||
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))
|
|
||||||
|
|
||||||
const
|
const
|
||||||
SLAB_SIZE* = 2048 # Max Packet Size (Ethernet Frame + Headroom)
|
SLAB_SIZE* = 2048 # Max Packet Size (Ethernet Frame + Headroom)
|
||||||
POOL_COUNT* = 1024 # Number of packets in the pool (2MB total RAM)
|
POOL_COUNT* = 1024 # Number of packets in the pool (2MB total RAM)
|
||||||
POOL_ALIGN* = 4096 # VirtIO/Page Alignment
|
POOL_ALIGN* = 4096 # VirtIO/Page Alignment
|
||||||
|
|
||||||
SYSTABLE_BASE = when defined(arm64): 0x50000000'u64
|
SYSTABLE_BASE = 0x83000000'u64
|
||||||
else: 0x83000000'u64
|
|
||||||
USER_SLAB_OFFSET = 0x10000'u64 # Offset within SYSTABLE
|
USER_SLAB_OFFSET = 0x10000'u64 # Offset within SYSTABLE
|
||||||
USER_SLAB_BASE* = SYSTABLE_BASE + USER_SLAB_OFFSET # 0x83010000
|
USER_SLAB_BASE* = SYSTABLE_BASE + USER_SLAB_OFFSET # 0x83010000
|
||||||
USER_SLAB_COUNT = 512 # 512 packets to cover RX Ring (256) + TX
|
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...")
|
dbg("[ION] Initializing Pool...")
|
||||||
|
|
||||||
# 1. Get the VIRTUAL address of the static buffer
|
# 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])
|
let virt_addr = cast[uint64](addr global_pool.buffer[0])
|
||||||
|
|
||||||
# 2. Translate to PHYSICAL (Identity Mapped for Phase 7)
|
# 2. Translate to PHYSICAL (Identity Mapped for Phase 7)
|
||||||
dbg("[ION] Step 2: Setting base phys...")
|
|
||||||
global_pool.base_phys = virt_addr
|
global_pool.base_phys = virt_addr
|
||||||
|
|
||||||
# Tracing for Phase 37
|
# Tracing for Phase 37
|
||||||
|
|
@ -73,16 +64,12 @@ proc ion_pool_init*() {.exportc.} =
|
||||||
kprint_hex(global_pool.base_phys)
|
kprint_hex(global_pool.base_phys)
|
||||||
dbg("")
|
dbg("")
|
||||||
|
|
||||||
dbg("[ION] Step 3: Ring Init (free_ring)...")
|
dbg("[ION] Ring Init...")
|
||||||
dbg(" 3a: About to call init()")
|
|
||||||
global_pool.free_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()
|
global_tx_ring.init()
|
||||||
dbg(" 4a: tx_ring init complete")
|
|
||||||
|
|
||||||
# Fill the free ring with all indices [0..1023]
|
# Fill the free ring with all indices [0..1023]
|
||||||
dbg("[ION] Step 5: Filling Slabs...")
|
dbg("[ION] Filling Slabs...")
|
||||||
var count = 0
|
var count = 0
|
||||||
for i in 0 ..< POOL_COUNT:
|
for i in 0 ..< POOL_COUNT:
|
||||||
if global_pool.free_ring.push(uint16(i)):
|
if global_pool.free_ring.push(uint16(i)):
|
||||||
|
|
@ -90,8 +77,6 @@ proc ion_pool_init*() {.exportc.} =
|
||||||
|
|
||||||
dbg("[ION] Pool Ready.")
|
dbg("[ION] Pool Ready.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc ion_alloc*(): IonPacket {.exportc.} =
|
proc ion_alloc*(): IonPacket {.exportc.} =
|
||||||
## O(1) Allocation. Returns an empty packet struct.
|
## O(1) Allocation. Returns an empty packet struct.
|
||||||
## If OOM, returns packet with data = nil
|
## 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.} =
|
proc ion_alloc_shared*(out_id: ptr uint16): uint64 {.exportc, cdecl.} =
|
||||||
## Allocate a buffer from the user-visible slab (Kernel Side, Shared Bitmap)
|
## Allocate a buffer from the user-visible slab (Kernel Side, Shared Bitmap)
|
||||||
let bitmap = cast[ptr array[64, byte]](USER_BITMAP_ADDR)
|
let bitmap = cast[ptr array[64, byte]](USER_BITMAP_ADDR)
|
||||||
|
|
||||||
for byteIdx in 0 ..< 64:
|
for byteIdx in 0 ..< 64:
|
||||||
if bitmap[byteIdx] != 0xFF:
|
if bitmap[byteIdx] != 0xFF:
|
||||||
for bitIdx in 0 ..< 8:
|
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
|
bitmap[byteIdx] = bitmap[byteIdx] or mask
|
||||||
let idx = byteIdx * 8 + bitIdx
|
let idx = byteIdx * 8 + bitIdx
|
||||||
if idx >= USER_SLAB_COUNT: return 0
|
if idx >= USER_SLAB_COUNT: return 0
|
||||||
|
|
||||||
out_id[] = uint16(idx) or 0x8000
|
out_id[] = uint16(idx) or 0x8000
|
||||||
return USER_SLAB_BASE + uint64(idx) * USER_PKT_SIZE
|
return USER_SLAB_BASE + uint64(idx) * USER_PKT_SIZE
|
||||||
return 0
|
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
|
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) =
|
proc kexec*(path: string) =
|
||||||
let entry = kload(path)
|
let entry = kload(path)
|
||||||
if entry != 0:
|
if entry != 0:
|
||||||
|
|
|
||||||
|
|
@ -37,48 +37,8 @@ type
|
||||||
p_memsz*: uint64
|
p_memsz*: uint64
|
||||||
p_align*: 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
|
const
|
||||||
PT_LOAD* = 1
|
PT_LOAD* = 1
|
||||||
PT_NOTE* = 4
|
|
||||||
PF_X* = 1
|
PF_X* = 1
|
||||||
PF_W* = 2
|
PF_W* = 2
|
||||||
PF_R* = 4
|
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
|
# core/rumpk/core/netswitch.nim
|
||||||
# Phase 36.2: The Traffic Cop (Network Membrane Layer 2 Switch)
|
# Phase 36.2: The Traffic Cop (Network Membrane Layer 2 Switch)
|
||||||
#
|
#
|
||||||
# Responsibilities:
|
# Responsibilities:
|
||||||
# - Poll VirtIO-Net hardware for incoming packets
|
# - Poll VirtIO-Net hardware for incoming packets
|
||||||
# - Route RX packets to s_net_rx ring (Kernel -> Userland)
|
# - 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 kprint_hex(v: uint64) {.importc, cdecl.}
|
||||||
proc get_now_ns(): uint64 {.importc: "rumpk_timer_now_ns", 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)
|
# Membrane Infrastructure (LwIP Glue)
|
||||||
proc membrane_init*() {.importc, cdecl.}
|
proc membrane_init*() {.importc, cdecl.}
|
||||||
proc pump_membrane_stack*() {.importc, cdecl.}
|
proc pump_membrane_stack*() {.importc, cdecl.}
|
||||||
|
|
@ -78,39 +73,19 @@ proc netswitch_process_packet(pkt: IonPacket): bool =
|
||||||
|
|
||||||
case etype:
|
case etype:
|
||||||
of 0x0800, 0x0806, 0x86DD: # IPv4, ARP, IPv6
|
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
|
# Route to Legacy/LwIP Membrane
|
||||||
if not chan_net_rx.send(pkt):
|
if not chan_net_rx.send(pkt):
|
||||||
# Ring full (Backpressure)
|
# Ring full (Backpressure)
|
||||||
ion_free(pkt)
|
ion_free(pkt)
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
of 0x88B5: # Sovereign UTCP (SPEC-700)
|
of 0x88B5: # Sovereign UTCP (SPEC-700)
|
||||||
# TODO: Route to dedicated UTCP channel
|
# TODO: Route to dedicated UTCP channel
|
||||||
# kprintln("[NetSwitch] UTCP Sovereign Packet Identified")
|
# kprintln("[NetSwitch] UTCP Sovereign Packet Identified")
|
||||||
ion_free(pkt)
|
ion_free(pkt)
|
||||||
return true # Handled (dropped)
|
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:
|
else:
|
||||||
# Drop unknown EtherTypes (Security/Sovereignty)
|
# Drop unknown EtherTypes (Security/Sovereignty)
|
||||||
ion_free(pkt)
|
ion_free(pkt)
|
||||||
|
|
@ -118,30 +93,30 @@ proc netswitch_process_packet(pkt: IonPacket): bool =
|
||||||
|
|
||||||
proc fiber_netswitch_entry*() {.cdecl.} =
|
proc fiber_netswitch_entry*() {.cdecl.} =
|
||||||
kprintln("[NetSwitch] Fiber Entry - The Traffic Cop is ON DUTY")
|
kprintln("[NetSwitch] Fiber Entry - The Traffic Cop is ON DUTY")
|
||||||
|
|
||||||
var rx_activity: bool = false
|
var rx_activity: bool = false
|
||||||
var tx_activity: bool = false
|
var tx_activity: bool = false
|
||||||
var rx_count: uint64 = 0
|
var rx_count: uint64 = 0
|
||||||
var tx_count: uint64 = 0
|
var tx_count: uint64 = 0
|
||||||
var last_stat_print: uint64 = 0
|
var last_stat_print: uint64 = 0
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
rx_activity = false
|
rx_activity = false
|
||||||
tx_activity = false
|
tx_activity = false
|
||||||
|
|
||||||
# 1. Drive the hardware poll (fills chan_netswitch_rx)
|
# 1. Drive the hardware poll (fills chan_netswitch_rx)
|
||||||
virtio_net_poll()
|
virtio_net_poll()
|
||||||
|
|
||||||
# [Cleaned] Driven by Userland now
|
# [Cleaned] Driven by Userland now
|
||||||
|
|
||||||
# 2. Consume from the Driver -> Switch internal ring
|
# 2. Consume from the Driver -> Switch internal ring
|
||||||
var raw_pkt: IonPacket
|
var raw_pkt: IonPacket
|
||||||
while chan_netswitch_rx.recv(raw_pkt):
|
while chan_netswitch_rx.recv(raw_pkt):
|
||||||
if netswitch_process_packet(raw_pkt):
|
if netswitch_process_packet(raw_pkt):
|
||||||
rx_activity = true
|
rx_activity = true
|
||||||
inc rx_count
|
inc rx_count
|
||||||
|
|
||||||
# 3. TX PATH: Userland -> Hardware (Legacy/LwIP)
|
# 3. TX PATH: Userland -> Hardware
|
||||||
var tx_pkt: IonPacket
|
var tx_pkt: IonPacket
|
||||||
while chan_net_tx.recv(tx_pkt):
|
while chan_net_tx.recv(tx_pkt):
|
||||||
if tx_pkt.data != nil and tx_pkt.len > 0:
|
if tx_pkt.data != nil and tx_pkt.len > 0:
|
||||||
|
|
@ -149,16 +124,7 @@ proc fiber_netswitch_entry*() {.cdecl.} =
|
||||||
inc tx_count
|
inc tx_count
|
||||||
ion_free(tx_pkt)
|
ion_free(tx_pkt)
|
||||||
tx_activity = true
|
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
|
# 4. Periodically print stats
|
||||||
let now = get_now_ns()
|
let now = get_now_ns()
|
||||||
if now - last_stat_print > 5000000000'u64: # 5 seconds
|
if now - last_stat_print > 5000000000'u64: # 5 seconds
|
||||||
|
|
|
||||||
|
|
@ -161,24 +161,6 @@ proc emit_access_denied*(
|
||||||
0, 0
|
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
|
## Initialization
|
||||||
proc init_stl_subsystem*() =
|
proc init_stl_subsystem*() =
|
||||||
## Initialize the STL subsystem (call from kmain)
|
## 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] I/O & Channels: "); kprint_hex(uint64(stats.io_events)); kprintln("")
|
||||||
kprint("[STL] Memory: "); kprint_hex(uint64(stats.mem_events)); kprintln("")
|
kprint("[STL] Memory: "); kprint_hex(uint64(stats.mem_events)); kprintln("")
|
||||||
kprint("[STL] Security/Policy: "); kprint_hex(uint64(stats.security_events)); kprintln("")
|
kprint("[STL] Security/Policy: "); kprint_hex(uint64(stats.security_events)); kprintln("")
|
||||||
|
|
||||||
# Demonstrate Causal Graph for the last event
|
# Demonstrate Causal Graph for the last event
|
||||||
if stats.total_events > 0:
|
if stats.total_events > 0:
|
||||||
let last_id = uint64(stats.total_events - 1)
|
let last_id = uint64(stats.total_events - 1)
|
||||||
var lineage: LineageResult
|
var lineage: LineageResult
|
||||||
stl_trace_lineage(last_id, lineage)
|
stl_trace_lineage(last_id, lineage)
|
||||||
|
|
||||||
kprintln("\n[STL] Causal Graph Audit:");
|
kprintln("\n[STL] Causal Graph Audit:");
|
||||||
kprint("[STL] Target: "); kprint_hex(last_id); kprintln("")
|
kprint("[STL] Target: "); kprint_hex(last_id); kprintln("")
|
||||||
|
|
||||||
for i in 0..<lineage.count:
|
for i in 0..<lineage.count:
|
||||||
let eid = lineage.event_ids[i]
|
let eid = lineage.event_ids[i]
|
||||||
let ev_ptr = stl_lookup(eid)
|
let ev_ptr = stl_lookup(eid)
|
||||||
|
|
||||||
if i > 0: kprintln(" |")
|
if i > 0: kprintln(" |")
|
||||||
|
|
||||||
kprint(" +-- ["); kprint_hex(eid); kprint("] ")
|
kprint(" +-- ["); kprint_hex(eid); kprint("] ")
|
||||||
|
|
||||||
if ev_ptr != nil:
|
if ev_ptr != nil:
|
||||||
# Kind is at offset 0 (2 bytes)
|
# Kind is at offset 0 (2 bytes)
|
||||||
let kind_val = cast[ptr uint16](ev_ptr)[]
|
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):
|
if ring_push(pty.slave_to_master, pty.stm_head, pty.stm_tail, b):
|
||||||
written += 1
|
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
|
# Also render to FB terminal
|
||||||
term_putc(char(b))
|
term_putc(char(b))
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
# Render frame after batch write
|
# Render frame after batch write
|
||||||
if written > 0:
|
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.
|
# 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
|
# 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.
|
# 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.
|
# 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!).
|
# 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.
|
# Or sched.nim is just a helper module that kernel.nim uses.
|
||||||
|
|
||||||
# Let's define the Strategy here.
|
# Let's define the Strategy here.
|
||||||
|
|
||||||
# To avoid circular imports, kernel.nim will likely INCLUDE sched.nim or sched.nim
|
# To avoid circular imports, kernel.nim will likely INCLUDE sched.nim or sched.nim
|
||||||
# will act on a passed context.
|
# will act on a passed context.
|
||||||
# BUT, SPEC-102 implies sched.nim *is* the logic.
|
# 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).
|
# 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 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)
|
# Forward declaration for channel data check (provided by kernel/channels)
|
||||||
proc fiber_can_run_on_channels*(id: uint64, mask: uint64): bool {.importc, cdecl.}
|
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 =
|
proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
let now = sched_get_now_ns()
|
let now = sched_get_now_ns()
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Phase 1: PHOTON (Hard Real-Time / Hardware Driven)
|
# Phase 1: PHOTON (Hard Real-Time / Hardware Driven)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
var run_photon = false
|
var run_photon = false
|
||||||
for i in 0..<fibers.len:
|
for f in fibers:
|
||||||
let idx = (photon_idx + i) mod fibers.len
|
|
||||||
let f = fibers[idx]
|
|
||||||
if f != nil and f.getSpectrum() == Spectrum.Photon:
|
if f != nil and f.getSpectrum() == Spectrum.Photon:
|
||||||
if is_runnable(f, now):
|
if is_runnable(f, now):
|
||||||
if f != current_fiber:
|
if f != current_fiber:
|
||||||
photon_idx = (idx + 1) mod fibers.len
|
switch(f); return true
|
||||||
let t0 = sched_get_now_ns()
|
|
||||||
switch(f)
|
|
||||||
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
|
||||||
return true
|
|
||||||
else:
|
else:
|
||||||
run_photon = true
|
run_photon = true
|
||||||
if run_photon: return 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)
|
# Phase 2: MATTER (Interactive / Latency Sensitive)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
var run_matter = false
|
var run_matter = false
|
||||||
for i in 0..<fibers.len:
|
for f in fibers:
|
||||||
let idx = (matter_idx + i) mod fibers.len
|
|
||||||
let f = fibers[idx]
|
|
||||||
if f != nil and f.getSpectrum() == Spectrum.Matter:
|
if f != nil and f.getSpectrum() == Spectrum.Matter:
|
||||||
if is_runnable(f, now):
|
if is_runnable(f, now):
|
||||||
if f != current_fiber:
|
if f != current_fiber:
|
||||||
matter_idx = (idx + 1) mod fibers.len
|
switch(f); return true
|
||||||
let t0 = sched_get_now_ns()
|
|
||||||
switch(f)
|
|
||||||
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
|
||||||
return true
|
|
||||||
else:
|
else:
|
||||||
run_matter = true
|
run_matter = true
|
||||||
if run_matter: return true
|
if run_matter: return true
|
||||||
|
|
@ -113,17 +89,11 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
# Phase 3: GRAVITY (Throughput / Background)
|
# Phase 3: GRAVITY (Throughput / Background)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
var run_gravity = false
|
var run_gravity = false
|
||||||
for i in 0..<fibers.len:
|
for f in fibers:
|
||||||
let idx = (gravity_idx + i) mod fibers.len
|
|
||||||
let f = fibers[idx]
|
|
||||||
if f != nil and f.getSpectrum() == Spectrum.Gravity:
|
if f != nil and f.getSpectrum() == Spectrum.Gravity:
|
||||||
if is_runnable(f, now):
|
if is_runnable(f, now):
|
||||||
if f != current_fiber:
|
if f != current_fiber:
|
||||||
gravity_idx = (idx + 1) mod fibers.len
|
switch(f); return true
|
||||||
let t0 = sched_get_now_ns()
|
|
||||||
switch(f)
|
|
||||||
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
|
||||||
return true
|
|
||||||
else:
|
else:
|
||||||
run_gravity = true
|
run_gravity = true
|
||||||
if run_gravity: return true
|
if run_gravity: return true
|
||||||
|
|
@ -131,20 +101,15 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Phase 4: VOID (Scavenger)
|
# Phase 4: VOID (Scavenger)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
for i in 0..<fibers.len:
|
for f in fibers:
|
||||||
let idx = (void_idx + i) mod fibers.len
|
|
||||||
let f = fibers[idx]
|
|
||||||
if f != nil and f.getSpectrum() == Spectrum.Void:
|
if f != nil and f.getSpectrum() == Spectrum.Void:
|
||||||
if is_runnable(f, now):
|
if is_runnable(f, now):
|
||||||
if f != current_fiber:
|
if f != current_fiber:
|
||||||
void_idx = (idx + 1) mod fibers.len
|
|
||||||
let t0 = sched_get_now_ns()
|
|
||||||
switch(f)
|
switch(f)
|
||||||
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
|
||||||
return true
|
return true
|
||||||
else:
|
else:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# THE SILENCE
|
# 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 != nil and f.sleep_until > now:
|
||||||
if f.sleep_until < min_wakeup:
|
if f.sleep_until < min_wakeup:
|
||||||
min_wakeup = f.sleep_until
|
min_wakeup = f.sleep_until
|
||||||
|
|
||||||
return min_wakeup
|
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)
|
# THE RATCHET (Post-Execution Analysis)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
|
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
||||||
|
|
||||||
proc sched_log(msg: string) =
|
proc sched_log(msg: string) =
|
||||||
if msg.len > 0:
|
if msg.len > 0:
|
||||||
console_write(unsafeAddr msg[0], csize_t(msg.len))
|
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
|
## Analyze the burst duration of a fiber after it yields/switches
|
||||||
## Implements the 3-strike rule for budget violations
|
## Implements the 3-strike rule for budget violations
|
||||||
if f == nil: return
|
if f == nil: return
|
||||||
|
|
||||||
f.last_burst_ns = burst_ns
|
f.last_burst_ns = burst_ns
|
||||||
|
|
||||||
# Update moving average (exponential: 75% old, 25% new)
|
# Update moving average (exponential: 75% old, 25% new)
|
||||||
if f.avg_burst == 0:
|
if f.avg_burst == 0:
|
||||||
f.avg_burst = burst_ns
|
f.avg_burst = burst_ns
|
||||||
else:
|
else:
|
||||||
f.avg_burst = (f.avg_burst * 3 + burst_ns) div 4
|
f.avg_burst = (f.avg_burst * 3 + burst_ns) div 4
|
||||||
|
|
||||||
# Budget enforcement
|
# Budget enforcement
|
||||||
if f.budget_ns > 0 and burst_ns > f.budget_ns:
|
if f.budget_ns > 0 and burst_ns > f.budget_ns:
|
||||||
f.violations += 1
|
f.violations += 1
|
||||||
discard emit_policy_violation(f.id, f.budget_ns, burst_ns, f.violations, 0)
|
console_write(cstring("[Ratchet] Violation: fiber exceeded budget\n"), 42)
|
||||||
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)
|
|
||||||
|
|
||||||
if f.violations >= 3:
|
if f.violations >= 3:
|
||||||
sched_demote(f)
|
sched_demote(f)
|
||||||
f.violations = 0 # Reset after demotion
|
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");
|
// =========================================================
|
||||||
|
// Exports for Nim FFI
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
export fn rumpk_console_write(ptr: [*]const u8, len: usize) void {
|
export fn rumpk_console_write(ptr: [*]const u8, len: usize) void {
|
||||||
hal.console_write(ptr, len);
|
hal.console_write(ptr, len);
|
||||||
|
|
@ -118,27 +113,17 @@ pub const cspace_check_perm = cspace.cspace_check_perm;
|
||||||
pub const surface = @import("surface.zig");
|
pub const surface = @import("surface.zig");
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
// Force analysis — architecture-independent modules
|
// Force analysis
|
||||||
_ = @import("stubs.zig");
|
_ = @import("stubs.zig");
|
||||||
|
_ = @import("mm.zig");
|
||||||
_ = @import("channel.zig");
|
_ = @import("channel.zig");
|
||||||
_ = @import("uart.zig");
|
_ = @import("uart.zig");
|
||||||
|
_ = @import("virtio_block.zig");
|
||||||
|
_ = @import("virtio_net.zig");
|
||||||
|
_ = @import("virtio_pci.zig");
|
||||||
_ = @import("ontology.zig");
|
_ = @import("ontology.zig");
|
||||||
|
_ = @import("entry_riscv.zig");
|
||||||
_ = @import("cspace.zig");
|
_ = @import("cspace.zig");
|
||||||
_ = @import("surface.zig");
|
_ = @import("surface.zig");
|
||||||
_ = @import("initrd.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.
|
//! SAFETY: All operations use atomic loads/stores with proper memory fences.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
|
||||||
|
|
||||||
pub const IonPacket = extern struct {
|
pub const IonPacket = extern struct {
|
||||||
data: u64,
|
data: u64,
|
||||||
|
|
@ -42,8 +41,8 @@ pub fn Ring(comptime T: type) type {
|
||||||
|
|
||||||
// INVARIANT 1: The Handle Barrier
|
// INVARIANT 1: The Handle Barrier
|
||||||
fn validate_ring_ptr(ptr: u64) void {
|
fn validate_ring_ptr(ptr: u64) void {
|
||||||
const min_valid: u64 = if (builtin.cpu.arch == .aarch64) 0x4000_0000 else 0x8000_0000;
|
// 0x8000_0000 is kernel base, 0x8300_0000 is ION base.
|
||||||
if (ptr < min_valid) {
|
if (ptr < 0x8000_0000) {
|
||||||
@panic("HAL: Invariant Violation - Invalid Ring Pointer");
|
@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
|
// Ensure we see data written by producer before reading it
|
||||||
switch (builtin.cpu.arch) {
|
asm volatile ("fence r, rw" ::: .{ .memory = true });
|
||||||
.riscv64 => asm volatile ("fence r, rw" ::: .{ .memory = true }),
|
|
||||||
.aarch64 => asm volatile ("dmb ld" ::: .{ .memory = true }),
|
|
||||||
else => @compileError("unsupported arch"),
|
|
||||||
}
|
|
||||||
|
|
||||||
out_pkt.* = ring.data[tail & ring.mask];
|
out_pkt.* = ring.data[tail & ring.mask];
|
||||||
const next = (tail + 1) & 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);
|
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
|
// Unit tests
|
||||||
test "Capability creation and validation" {
|
test "Capability creation and validation" {
|
||||||
const cap = Capability{
|
const cap = Capability{
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ export fn rss_trap_handler(frame: *TrapFrame) void {
|
||||||
const irq = PLIC_CLAIM.*;
|
const irq = PLIC_CLAIM.*;
|
||||||
|
|
||||||
if (irq == 10) { // UART0 is IRQ 10 on Virt machine
|
if (irq == 10) { // UART0 is IRQ 10 on Virt machine
|
||||||
uart.print("[IRQ] 10\n");
|
// uart.print("[IRQ] 10\n");
|
||||||
uart_input.poll_input();
|
uart_input.poll_input();
|
||||||
} else if (irq >= 32 and irq <= 35) {
|
} else if (irq >= 32 and irq <= 35) {
|
||||||
virtio_net.virtio_net_poll();
|
virtio_net.virtio_net_poll();
|
||||||
|
|
@ -447,13 +447,6 @@ export fn hal_io_init() void {
|
||||||
virtio_block.init();
|
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 {
|
export fn rumpk_halt() noreturn {
|
||||||
uart.print("[Rumpk RISC-V] Halting.\n");
|
uart.print("[Rumpk RISC-V] Halting.\n");
|
||||||
while (true) {
|
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, 0x40000000, 0x40000000, 0x10000000, PTE_R | PTE_W); // PCIe MMIO
|
||||||
try map_range(root, 0x20000000, 0x20000000, 0x10000, PTE_R | PTE_W); // PTY Slave
|
try map_range(root, 0x20000000, 0x20000000, 0x10000, PTE_R | PTE_W); // PTY Slave
|
||||||
|
|
||||||
// 4. Overlap stack with user access (Optional)
|
// 4. Overlap stack with user access
|
||||||
if (stack_base != 0) {
|
try map_range(root, stack_base, stack_base, stack_size, PTE_R | PTE_W | PTE_U);
|
||||||
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)
|
// 5. Shared SysTable & Rings & User Slab (0x83000000) - Map 256KB (64 pages; covers up to 0x40000)
|
||||||
var j: u64 = 0;
|
var j: u64 = 0;
|
||||||
|
|
|
||||||
|
|
@ -194,12 +194,10 @@ pub export fn stl_init() void {
|
||||||
stl_initialized = true;
|
stl_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sovereign timer — canonical time source for all kernel timestamps
|
/// Get current timestamp (placeholder - will be replaced by HAL timer)
|
||||||
extern fn rumpk_timer_now_ns() u64;
|
|
||||||
|
|
||||||
/// Get current timestamp in nanoseconds since boot
|
|
||||||
fn get_timestamp_ns() u64 {
|
fn get_timestamp_ns() u64 {
|
||||||
return rumpk_timer_now_ns();
|
// TODO: Integrate with HAL timer
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emit event to STL (C ABI)
|
/// Emit event to STL (C ABI)
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,6 @@
|
||||||
|
|
||||||
const uart = @import("uart.zig");
|
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)
|
// Heap Stubs (Bump Allocator with Block Headers)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -140,9 +137,13 @@ export fn calloc(nmemb: usize, size: usize) ?*anyopaque {
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
export fn get_ticks() u32 {
|
export fn get_ticks() u32 {
|
||||||
// Delegate to sovereign timer — single source of truth for all time
|
var time_val: u64 = 0;
|
||||||
const ns = rumpk_timer_now_ns();
|
asm volatile ("rdtime %[ret]"
|
||||||
return @truncate(ns / 1_000_000); // ns → ms
|
: [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 {
|
// 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;
|
extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize;
|
||||||
|
|
||||||
// export fn exit(code: c_int) noreturn {
|
export fn exit(code: c_int) noreturn {
|
||||||
// _ = code;
|
_ = code;
|
||||||
// while (true) asm volatile ("wfi");
|
while (true) asm volatile ("wfi");
|
||||||
// }
|
}
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Atomic Stubs (To resolve linker errors with libcompiler_rt)
|
// Atomic Stubs (To resolve linker errors with libcompiler_rt)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
//! DECISION(Alloc): Bump allocator chosen for simplicity and determinism.
|
//! DECISION(Alloc): Bump allocator chosen for simplicity and determinism.
|
||||||
//! Memory is never reclaimed; system reboots to reset.
|
//! 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)
|
// Heap Stubs (Bump Allocator with Block Headers)
|
||||||
|
|
@ -27,13 +27,11 @@ var heap_idx: usize = 0;
|
||||||
var heap_init_done: bool = false;
|
var heap_init_done: bool = false;
|
||||||
|
|
||||||
export fn debug_print(s: [*]const u8, len: usize) void {
|
export fn debug_print(s: [*]const u8, len: usize) void {
|
||||||
_ = s;
|
uart.print(s[0..len]);
|
||||||
_ = len;
|
|
||||||
// TODO: Use syscall for userland debug printing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn kprint_hex(value: u64) void {
|
export fn kprint_hex(value: u64) void {
|
||||||
_ = value;
|
uart.print_hex(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header structure (64 bytes aligned to match LwIP MEM_ALIGNMENT)
|
// 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_init_done) {
|
||||||
if (heap_idx != 0) {
|
if (heap_idx != 0) {
|
||||||
// uart.print("[Alloc] WARNING: BSS NOT ZEROED! heap_idx: ");
|
uart.print("[Alloc] WARNING: BSS NOT ZEROED! heap_idx: ");
|
||||||
// uart.print_hex(heap_idx);
|
uart.print_hex(heap_idx);
|
||||||
// uart.print("\n");
|
uart.print("\n");
|
||||||
heap_idx = 0;
|
heap_idx = 0;
|
||||||
}
|
}
|
||||||
heap_init_done = true;
|
heap_init_done = true;
|
||||||
|
|
@ -60,11 +58,11 @@ export fn malloc(size: usize) ?*anyopaque {
|
||||||
const aligned_idx = (heap_idx + align_mask) & ~align_mask;
|
const aligned_idx = (heap_idx + align_mask) & ~align_mask;
|
||||||
|
|
||||||
if (aligned_idx + total_needed > heap.len) {
|
if (aligned_idx + total_needed > heap.len) {
|
||||||
// uart.print("[Alloc] OOM! Size: ");
|
uart.print("[Alloc] OOM! Size: ");
|
||||||
// uart.print_hex(size);
|
uart.print_hex(size);
|
||||||
// uart.print(" Used: ");
|
uart.print(" Used: ");
|
||||||
// uart.print_hex(heap_idx);
|
uart.print_hex(heap_idx);
|
||||||
// uart.print("\n");
|
uart.print("\n");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,29 +137,3 @@ export fn calloc(nmemb: usize, size: usize) ?*anyopaque {
|
||||||
export fn get_ticks() u32 {
|
export fn get_ticks() u32 {
|
||||||
return 0; // TODO: Implement real timer
|
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
|
// 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 {
|
pub fn init() void {
|
||||||
switch (builtin.cpu.arch) {
|
switch (builtin.cpu.arch) {
|
||||||
.riscv64 => init_riscv(),
|
.riscv64 => init_riscv(),
|
||||||
.aarch64 => init_aarch64(),
|
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -117,78 +107,6 @@ pub fn init_riscv() void {
|
||||||
// uart_input.poll_input(); // We cannot call this here safely without dep
|
// 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 {
|
fn write_char_arm64(c: u8) void {
|
||||||
const dr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_DR);
|
const dr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_DR);
|
||||||
const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR);
|
const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR);
|
||||||
|
|
@ -234,13 +152,6 @@ pub fn read_direct() ?u8 {
|
||||||
return thr.*;
|
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 => {},
|
else => {},
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -252,11 +163,6 @@ pub fn get_lsr() u8 {
|
||||||
const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
|
const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
|
||||||
return 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,
|
else => return 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ pub fn poll_input() void {
|
||||||
// Only Kernel uses this
|
// Only Kernel uses this
|
||||||
const Kernel = struct {
|
const Kernel = struct {
|
||||||
extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void;
|
extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void;
|
||||||
extern fn kprint(s: [*]const u8) void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (builtin.cpu.arch) {
|
switch (builtin.cpu.arch) {
|
||||||
|
|
@ -35,9 +34,6 @@ pub fn poll_input() void {
|
||||||
const byte = thr.*;
|
const byte = thr.*;
|
||||||
const byte_arr = [1]u8{byte};
|
const byte_arr = [1]u8{byte};
|
||||||
|
|
||||||
// DEBUG: Trace hardware read
|
|
||||||
Kernel.kprint("[HW Read]\n");
|
|
||||||
|
|
||||||
// Forward to Kernel Input Channel
|
// Forward to Kernel Input Channel
|
||||||
Kernel.ion_push_stdin(&byte_arr, 1);
|
Kernel.ion_push_stdin(&byte_arr, 1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,8 @@
|
||||||
//! the request. Uses bounce-buffers to guarantee alignment.
|
//! the request. Uses bounce-buffers to guarantee alignment.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
|
||||||
const uart = @import("uart.zig");
|
const uart = @import("uart.zig");
|
||||||
|
const pci = @import("virtio_pci.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");
|
|
||||||
|
|
||||||
// External C/Zig stubs
|
// External C/Zig stubs
|
||||||
extern fn malloc(size: usize) ?*anyopaque;
|
extern fn malloc(size: usize) ?*anyopaque;
|
||||||
|
|
@ -52,43 +46,13 @@ pub fn init() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const VirtioBlkDriver = struct {
|
pub const VirtioBlkDriver = struct {
|
||||||
transport: transport_mod.VirtioTransport,
|
transport: pci.VirtioTransport,
|
||||||
v_desc: [*]volatile VirtioDesc,
|
v_desc: [*]volatile VirtioDesc,
|
||||||
v_avail: *volatile VirtioAvail,
|
v_avail: *volatile VirtioAvail,
|
||||||
v_used: *volatile VirtioUsed,
|
v_used: *volatile VirtioUsed,
|
||||||
queue_size: u16,
|
queue_size: u16,
|
||||||
|
|
||||||
pub fn probe() ?VirtioBlkDriver {
|
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 PCI_ECAM_BASE: usize = 0x30000000;
|
||||||
const bus: u8 = 0;
|
const bus: u8 = 0;
|
||||||
const func: u8 = 0;
|
const func: u8 = 0;
|
||||||
|
|
@ -105,7 +69,7 @@ pub const VirtioBlkDriver = struct {
|
||||||
uart.print_hex(i);
|
uart.print_hex(i);
|
||||||
uart.print(".0\n");
|
uart.print(".0\n");
|
||||||
var self = VirtioBlkDriver{
|
var self = VirtioBlkDriver{
|
||||||
.transport = transport_mod.VirtioTransport.init(addr),
|
.transport = pci.VirtioTransport.init(addr),
|
||||||
.v_desc = undefined,
|
.v_desc = undefined,
|
||||||
.v_avail = undefined,
|
.v_avail = undefined,
|
||||||
.v_used = undefined,
|
.v_used = undefined,
|
||||||
|
|
@ -123,56 +87,29 @@ pub const VirtioBlkDriver = struct {
|
||||||
if (!self.transport.probe()) return false;
|
if (!self.transport.probe()) return false;
|
||||||
|
|
||||||
self.transport.reset();
|
self.transport.reset();
|
||||||
self.transport.add_status(1); // ACKNOWLEDGE
|
self.transport.add_status(1);
|
||||||
self.transport.add_status(2); // DRIVER
|
self.transport.add_status(2);
|
||||||
|
|
||||||
// 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.select_queue(0);
|
self.transport.select_queue(0);
|
||||||
const max_count = self.transport.get_queue_size();
|
const 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;
|
|
||||||
|
|
||||||
// [Desc] [Avail] [Used] (Simplified layout)
|
// [Desc] [Avail] [Used] (Simplified layout)
|
||||||
const total = (count * 16) + (6 + count * 2) + 4096 + (6 + count * 8);
|
const total = (count * 16) + (6 + count * 2) + 4096 + (6 + count * 8);
|
||||||
const raw_ptr = malloc(total + 4096) orelse return false;
|
const raw_ptr = malloc(total + 4096) orelse return false;
|
||||||
const aligned = (@intFromPtr(raw_ptr) + 4095) & ~@as(usize, 4095);
|
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_desc = @ptrFromInt(aligned);
|
||||||
self.v_avail = @ptrFromInt(aligned + (count * 16));
|
self.v_avail = @ptrFromInt(aligned + (count * 16));
|
||||||
self.v_used = @ptrFromInt(aligned + (count * 16) + (6 + count * 2) + 4096);
|
self.v_used = @ptrFromInt(aligned + (count * 16) + (6 + count * 2) + 4096);
|
||||||
self.queue_size = count;
|
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) {
|
if (self.transport.is_modern) {
|
||||||
self.transport.setup_modern_queue(aligned, aligned + (count * 16), @intFromPtr(self.v_used));
|
self.transport.setup_modern_queue(aligned, aligned + (count * 16), @intFromPtr(self.v_used));
|
||||||
} else {
|
} else {
|
||||||
self.transport.set_queue_size(count);
|
|
||||||
self.transport.setup_legacy_queue(@intCast(aligned >> 12));
|
self.transport.setup_legacy_queue(@intCast(aligned >> 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.transport.add_status(4); // DRIVER_OK
|
self.transport.add_status(4);
|
||||||
global_blk = self.*;
|
global_blk = self.*;
|
||||||
|
|
||||||
uart.print("[VirtIO-Blk] Device Ready. Queue Size: ");
|
uart.print("[VirtIO-Blk] Device Ready. Queue Size: ");
|
||||||
|
|
@ -214,26 +151,15 @@ pub const VirtioBlkDriver = struct {
|
||||||
// Submit to Avail Ring
|
// Submit to Avail Ring
|
||||||
const ring = @as([*]volatile u16, @ptrFromInt(@intFromPtr(self.v_avail) + 4));
|
const ring = @as([*]volatile u16, @ptrFromInt(@intFromPtr(self.v_avail) + 4));
|
||||||
ring[self.v_avail.idx % self.queue_size] = 0; // Head of chain
|
ring[self.v_avail.idx % self.queue_size] = 0; // Head of chain
|
||||||
const expected_used = self.v_used.idx +% 1;
|
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||||
transport_mod.io_barrier();
|
|
||||||
self.v_avail.idx +%= 1;
|
self.v_avail.idx +%= 1;
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||||
|
|
||||||
self.transport.notify(0);
|
self.transport.notify(0);
|
||||||
|
|
||||||
// Wait for device (Polling — wait until used ring advances)
|
// Wait for device (Polling)
|
||||||
var timeout: usize = 0;
|
while (self.v_used.idx == 0) {
|
||||||
while (self.v_used.idx != expected_used) {
|
asm volatile ("nop");
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != 0) return error.DiskError;
|
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));
|
const ring = @as([*]volatile u16, @ptrFromInt(@intFromPtr(self.v_avail) + 4));
|
||||||
ring[self.v_avail.idx % self.queue_size] = 3;
|
ring[self.v_avail.idx % self.queue_size] = 3;
|
||||||
const expected_used = self.v_used.idx +% 1;
|
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||||
transport_mod.io_barrier();
|
|
||||||
self.v_avail.idx +%= 1;
|
self.v_avail.idx +%= 1;
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||||
|
|
||||||
self.transport.notify(0);
|
self.transport.notify(0);
|
||||||
|
|
||||||
// Wait for device (Polling — wait until used ring advances)
|
while (status == 0xFF) {
|
||||||
var timeout: usize = 0;
|
asm volatile ("nop");
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != 0) return error.DiskError;
|
if (status != 0) return error.DiskError;
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,8 @@
|
||||||
//! to ensure correct synchronization with the virtual device.
|
//! to ensure correct synchronization with the virtual device.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
|
||||||
const uart = @import("uart.zig");
|
const uart = @import("uart.zig");
|
||||||
|
const pci = @import("virtio_pci.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");
|
|
||||||
|
|
||||||
// VirtIO Feature Bits
|
// VirtIO Feature Bits
|
||||||
const VIRTIO_F_VERSION_1 = 32;
|
const VIRTIO_F_VERSION_1 = 32;
|
||||||
|
|
@ -123,24 +117,47 @@ pub export fn rumpk_net_init() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const VirtioNetDriver = struct {
|
pub const VirtioNetDriver = struct {
|
||||||
transport: transport_mod.VirtioTransport,
|
transport: pci.VirtioTransport,
|
||||||
irq: u32,
|
irq: u32,
|
||||||
rx_queue: ?*Virtqueue = null,
|
rx_queue: ?*Virtqueue = null,
|
||||||
tx_queue: ?*Virtqueue = null,
|
tx_queue: ?*Virtqueue = null,
|
||||||
|
|
||||||
pub fn get_mac(self: *VirtioNetDriver, out: [*]u8) void {
|
pub fn get_mac(self: *VirtioNetDriver, out: [*]u8) void {
|
||||||
uart.print("[VirtIO-Net] Reading MAC from device config...\n");
|
uart.print("[VirtIO-Net] Reading MAC from device_cfg...\n");
|
||||||
for (0..6) |i| {
|
if (self.transport.is_modern) {
|
||||||
out[i] = self.transport.get_device_config_byte(i);
|
// Use device_cfg directly - this is the VirtIO-Net specific config
|
||||||
uart.print_hex8(out[i]);
|
if (self.transport.device_cfg) |cfg| {
|
||||||
if (i < 5) uart.print(":");
|
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 {
|
pub fn init(base: usize, irq_num: u32) VirtioNetDriver {
|
||||||
return .{
|
return .{
|
||||||
.transport = transport_mod.VirtioTransport.init(base),
|
.transport = pci.VirtioTransport.init(base),
|
||||||
.irq = irq_num,
|
.irq = irq_num,
|
||||||
.rx_queue = null,
|
.rx_queue = null,
|
||||||
.tx_queue = null,
|
.tx_queue = null,
|
||||||
|
|
@ -148,32 +165,6 @@ pub const VirtioNetDriver = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn probe() ?VirtioNetDriver {
|
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");
|
uart.print("[VirtIO] Probing PCI for networking device...\n");
|
||||||
const PCI_ECAM_BASE: usize = 0x30000000;
|
const PCI_ECAM_BASE: usize = 0x30000000;
|
||||||
const bus: u8 = 0;
|
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_ACKNOWLEDGE);
|
||||||
self.transport.add_status(VIRTIO_CONFIG_S_DRIVER);
|
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");
|
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("[VirtIO] Device Features: ");
|
||||||
uart.print_hex(dev_features);
|
uart.print_hex(f_low);
|
||||||
|
uart.print(" ");
|
||||||
|
uart.print_hex(f_high);
|
||||||
uart.print("\n");
|
uart.print("\n");
|
||||||
|
|
||||||
// Accept VERSION_1 (Modern) and MAC
|
// Accept VERSION_1 (Modern) and MAC
|
||||||
const accept: u64 = (1 << VIRTIO_NET_F_MAC) |
|
const accept_low: u32 = (1 << VIRTIO_NET_F_MAC);
|
||||||
(@as(u64, 1) << VIRTIO_F_VERSION_1);
|
const accept_high: u32 = (1 << (VIRTIO_F_VERSION_1 - 32));
|
||||||
self.transport.set_driver_features(accept);
|
|
||||||
transport_mod.io_barrier();
|
|
||||||
|
|
||||||
|
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);
|
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) {
|
if ((self.transport.get_status() & VIRTIO_CONFIG_S_FEATURES_OK) == 0) {
|
||||||
uart.print("[VirtIO] Feature negotiation failed!\n");
|
uart.print("[VirtIO] Feature negotiation failed!\n");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -246,15 +267,10 @@ pub const VirtioNetDriver = struct {
|
||||||
}
|
}
|
||||||
// 5. Setup RX Queue (0)
|
// 5. Setup RX Queue (0)
|
||||||
self.transport.select_queue(0);
|
self.transport.select_queue(0);
|
||||||
const rx_max = self.transport.get_queue_size();
|
const rx_count = 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;
|
|
||||||
uart.print("[VirtIO] RX Queue Size: ");
|
uart.print("[VirtIO] RX Queue Size: ");
|
||||||
uart.print_hex(rx_count);
|
uart.print_hex(rx_count);
|
||||||
uart.print(" (max: ");
|
uart.print("\n");
|
||||||
uart.print_hex(rx_max);
|
|
||||||
uart.print(")\n");
|
|
||||||
|
|
||||||
if (rx_count == 0 or rx_count == 0xFFFF) {
|
if (rx_count == 0 or rx_count == 0xFFFF) {
|
||||||
uart.print("[VirtIO] Invalid RX Queue Size. Aborting.\n");
|
uart.print("[VirtIO] Invalid RX Queue Size. Aborting.\n");
|
||||||
|
|
@ -272,13 +288,10 @@ pub const VirtioNetDriver = struct {
|
||||||
|
|
||||||
// 6. Setup TX Queue (1)
|
// 6. Setup TX Queue (1)
|
||||||
self.transport.select_queue(1);
|
self.transport.select_queue(1);
|
||||||
const tx_max = self.transport.get_queue_size();
|
const tx_count = self.transport.get_queue_size();
|
||||||
const tx_count = if (tx_max > MAX_QUEUE) MAX_QUEUE else tx_max;
|
|
||||||
uart.print("[VirtIO] TX Queue Size: ");
|
uart.print("[VirtIO] TX Queue Size: ");
|
||||||
uart.print_hex(tx_count);
|
uart.print_hex(tx_count);
|
||||||
uart.print(" (max: ");
|
uart.print("\n");
|
||||||
uart.print_hex(tx_max);
|
|
||||||
uart.print(")\n");
|
|
||||||
|
|
||||||
if (tx_count == 0 or tx_count == 0xFFFF) {
|
if (tx_count == 0 or tx_count == 0xFFFF) {
|
||||||
uart.print("[VirtIO] Invalid TX Queue Size. Aborting.\n");
|
uart.print("[VirtIO] Invalid TX Queue Size. Aborting.\n");
|
||||||
|
|
@ -379,11 +392,11 @@ pub const VirtioNetDriver = struct {
|
||||||
q_ptr.avail.flags = 0;
|
q_ptr.avail.flags = 0;
|
||||||
q_ptr.used.flags = 0;
|
q_ptr.used.flags = 0;
|
||||||
|
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||||
|
|
||||||
if (is_rx) {
|
if (is_rx) {
|
||||||
q_ptr.avail.idx = count;
|
q_ptr.avail.idx = count;
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence w, w" ::: .{ .memory = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const phys_addr = aligned_addr;
|
const phys_addr = aligned_addr;
|
||||||
|
|
@ -391,7 +404,6 @@ pub const VirtioNetDriver = struct {
|
||||||
if (self.transport.is_modern) {
|
if (self.transport.is_modern) {
|
||||||
self.transport.setup_modern_queue(phys_addr, phys_addr + desc_size, phys_addr + used_offset);
|
self.transport.setup_modern_queue(phys_addr, phys_addr + desc_size, phys_addr + used_offset);
|
||||||
} else {
|
} else {
|
||||||
self.transport.set_queue_size(count);
|
|
||||||
const pfn = @as(u32, @intCast(phys_addr >> 12));
|
const pfn = @as(u32, @intCast(phys_addr >> 12));
|
||||||
self.transport.setup_legacy_queue(pfn);
|
self.transport.setup_legacy_queue(pfn);
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +413,7 @@ pub const VirtioNetDriver = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rx_poll(self: *VirtioNetDriver, q: *Virtqueue) void {
|
pub fn rx_poll(self: *VirtioNetDriver, q: *Virtqueue) void {
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
|
||||||
const used = q.used;
|
const used = q.used;
|
||||||
const hw_idx = used.idx;
|
const hw_idx = used.idx;
|
||||||
|
|
@ -461,7 +473,7 @@ pub const VirtioNetDriver = struct {
|
||||||
q.desc[desc_idx].addr = new_phys;
|
q.desc[desc_idx].addr = new_phys;
|
||||||
q.ids[desc_idx] = new_id;
|
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);
|
avail_ring[q.avail.idx % q.num] = @intCast(desc_idx);
|
||||||
q.avail.idx +%= 1;
|
q.avail.idx +%= 1;
|
||||||
|
|
@ -474,14 +486,14 @@ pub const VirtioNetDriver = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replenished) {
|
if (replenished) {
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
self.transport.notify(0);
|
self.transport.notify(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tx_poll(self: *VirtioNetDriver, q: *Virtqueue) void {
|
pub fn tx_poll(self: *VirtioNetDriver, q: *Virtqueue) void {
|
||||||
_ = self;
|
_ = self;
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
const used = q.used;
|
const used = q.used;
|
||||||
const used_idx = used.idx;
|
const used_idx = used.idx;
|
||||||
const used_ring = get_used_ring(used);
|
const used_ring = get_used_ring(used);
|
||||||
|
|
@ -516,11 +528,11 @@ pub const VirtioNetDriver = struct {
|
||||||
|
|
||||||
q.ids[idx] = slab_id;
|
q.ids[idx] = slab_id;
|
||||||
|
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
avail_ring[idx] = @intCast(idx);
|
avail_ring[idx] = @intCast(idx);
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
q.avail.idx +%= 1;
|
q.avail.idx +%= 1;
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
|
||||||
self.transport.notify(1);
|
self.transport.notify(1);
|
||||||
uart.print("[VirtIO TX-Slab] Sent ");
|
uart.print("[VirtIO TX-Slab] Sent ");
|
||||||
|
|
@ -567,11 +579,11 @@ pub const VirtioNetDriver = struct {
|
||||||
desc.len = @intCast(header_len + copy_len);
|
desc.len = @intCast(header_len + copy_len);
|
||||||
desc.flags = 0;
|
desc.flags = 0;
|
||||||
|
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
avail_ring[idx] = @intCast(desc_idx);
|
avail_ring[idx] = @intCast(desc_idx);
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
q.avail.idx +%= 1;
|
q.avail.idx +%= 1;
|
||||||
transport_mod.io_barrier();
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
|
||||||
self.transport.notify(1);
|
self.transport.notify(1);
|
||||||
uart.print("[VirtIO TX] Queued & Notified Len=");
|
uart.print("[VirtIO TX] Queued & Notified Len=");
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
//! Dynamically assigns BARs (Base Address Registers) if unassigned by firmware.
|
//! Dynamically assigns BARs (Base Address Registers) if unassigned by firmware.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
|
||||||
const uart = @import("uart.zig");
|
const uart = @import("uart.zig");
|
||||||
|
|
||||||
// PCI Config Offsets
|
// 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 {
|
pub fn setup_legacy_queue(self: *VirtioTransport, pfn: u32) void {
|
||||||
// Only for legacy
|
// Only for legacy
|
||||||
@as(*volatile u32, @ptrFromInt(self.legacy_bar + 0x08)).* = pfn;
|
@as(*volatile u32, @ptrFromInt(self.legacy_bar + 0x08)).* = pfn;
|
||||||
|
|
@ -357,65 +345,8 @@ pub const VirtioTransport = struct {
|
||||||
notify_ptr.* = queue_idx;
|
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
|
// Modern Config Structure Layout
|
||||||
pub const VirtioPciCommonCfg = extern struct {
|
pub const VirtioPciCommonCfg = extern struct {
|
||||||
device_feature_select: u32,
|
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 <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdarg.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
|
// Types needed for stubs
|
||||||
typedef int32_t pid_t;
|
typedef int32_t pid_t;
|
||||||
typedef int32_t uid_t;
|
typedef int32_t uid_t;
|
||||||
|
|
@ -28,45 +15,12 @@ struct stat {
|
||||||
|
|
||||||
int errno = 0;
|
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
|
||||||
// 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)
|
// LwIP Panic Handler (for Membrane stack)
|
||||||
extern void console_write(const void* p, size_t len);
|
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; }
|
int atoi(const char* nptr) { return 0; }
|
||||||
|
|
||||||
double strtod(const char* nptr, char** endptr) {
|
double strtod(const char* nptr, char** endptr) {
|
||||||
const char *p = nptr;
|
if (endptr) *endptr = (char*)nptr;
|
||||||
double result = 0.0;
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double pow(double x, double y) { 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
|
#ifdef RUMPK_KERNEL
|
||||||
extern long k_handle_syscall(long nr, long a0, long a1, long a2);
|
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
|
#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) {
|
long syscall(long nr, long a0, long a1, long a2) {
|
||||||
#ifdef RUMPK_KERNEL
|
#ifdef RUMPK_KERNEL
|
||||||
return k_handle_syscall(nr, a0, a1, a2);
|
return k_handle_syscall(nr, a0, a1, a2);
|
||||||
|
|
@ -335,7 +222,11 @@ int printf(const char *format, ...) {
|
||||||
return res;
|
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
|
// System stubs
|
||||||
extern void nexus_yield(void);
|
extern void nexus_yield(void);
|
||||||
|
|
@ -379,16 +270,6 @@ int memcmp(const void *s1, const void *s2, size_t n) {
|
||||||
return 0;
|
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) {
|
void *memmove(void *dest, const void *src, size_t n) {
|
||||||
char *d = (char *)dest;
|
char *d = (char *)dest;
|
||||||
const char *s = (const char *)src;
|
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
|
#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) {
|
void console_write(const void* p, size_t len) {
|
||||||
hal_console_write(p, len);
|
hal_console_write(p, len);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
// User Mode: Syscall
|
||||||
void console_write(const void* p, size_t len) {
|
void console_write(const void* p, size_t len) {
|
||||||
write(1, p, len);
|
write(1, p, len);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,9 @@
|
||||||
|
|
||||||
import ../../core/ion
|
import ../../core/ion
|
||||||
|
|
||||||
const SYS_TABLE_ADDR = when defined(arm64): 0x50000000'u64
|
const SYS_TABLE_ADDR = 0x83000000'u64
|
||||||
else: 0x83000000'u64
|
|
||||||
|
|
||||||
const
|
const
|
||||||
GAP = 10 # Pixels between windows
|
GAP = 10 # Pixels between windows
|
||||||
FOCUS_COLOR = 0xFF00FFFF'u32 # Cyan border for focused window
|
FOCUS_COLOR = 0xFF00FFFF'u32 # Cyan border for focused window
|
||||||
BG_COLOR = 0xFF101020'u32 # Dark Blue background
|
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 ix >= 0 and ix < fb_w:
|
||||||
if y >= 0 and y < fb_h: fb[y * fb_w + ix] = color
|
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
|
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:
|
for iy in y ..< y + h:
|
||||||
if iy >= 0 and iy < fb_h:
|
if iy >= 0 and iy < fb_h:
|
||||||
if x >= 0 and x < fb_w: fb[iy * fb_w + x] = color
|
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
|
let screen_x = s.x - c.view_x
|
||||||
if screen_x + s.width < 0 or screen_x >= int(sys.fb_width):
|
if screen_x + s.width < 0 or screen_x >= int(sys.fb_width):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
let screen_y = (int(sys.fb_height) - s.height) div 2
|
let screen_y = (int(sys.fb_height) - s.height) div 2
|
||||||
blit_surface(s, screen_x, screen_y)
|
blit_surface(s, screen_x, screen_y)
|
||||||
|
|
||||||
if s.focused:
|
if s.focused:
|
||||||
draw_border(screen_x, screen_y, s.width, s.height, FOCUS_COLOR)
|
draw_border(screen_x, screen_y, s.width, s.height, FOCUS_COLOR)
|
||||||
|
|
||||||
proc create_surface*(w, h: int): int32 =
|
proc create_surface*(w, h: int): int32 =
|
||||||
let id = hal_surface_alloc(uint32(w), uint32(h))
|
let id = hal_surface_alloc(uint32(w), uint32(h))
|
||||||
if id < 0: return -1
|
if id < 0: return -1
|
||||||
|
|
||||||
let p = hal_surface_get_ptr(id)
|
let p = hal_surface_get_ptr(id)
|
||||||
if p == nil: return -1
|
if p == nil: return -1
|
||||||
|
|
||||||
|
|
@ -128,11 +127,11 @@ proc create_surface*(w, h: int): int32 =
|
||||||
s.width = w
|
s.width = w
|
||||||
s.height = h
|
s.height = h
|
||||||
s.dirty = true
|
s.dirty = true
|
||||||
|
|
||||||
c.surfaces.add(s)
|
c.surfaces.add(s)
|
||||||
if c.surfaces.len == 1:
|
if c.surfaces.len == 1:
|
||||||
c.focused_idx = 0
|
c.focused_idx = 0
|
||||||
|
|
||||||
return id
|
return id
|
||||||
|
|
||||||
proc compositor_step*() =
|
proc compositor_step*() =
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,4 @@
|
||||||
|
|
||||||
#include "lfs_config.h"
|
#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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
//! All memory accesses to the SysTable are through volatile pointers.
|
//! All memory accesses to the SysTable are through volatile pointers.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
|
||||||
|
|
||||||
// --- Protocol Definitions (Match core/ion.nim) ---
|
// --- Protocol Definitions (Match core/ion.nim) ---
|
||||||
|
|
||||||
|
|
@ -95,11 +94,7 @@ pub fn RingBuffer(comptime T: type) type {
|
||||||
self.data[head & mask] = item;
|
self.data[head & mask] = item;
|
||||||
|
|
||||||
// Commit
|
// Commit
|
||||||
switch (builtin.cpu.arch) {
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
.riscv64 => asm volatile ("fence" ::: .{ .memory = true }),
|
|
||||||
.aarch64 => asm volatile ("dmb ish" ::: .{ .memory = true }),
|
|
||||||
else => @compileError("unsupported arch"),
|
|
||||||
}
|
|
||||||
self.head = (head + 1) & mask;
|
self.head = (head + 1) & mask;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -120,8 +115,6 @@ pub const SysTable = extern struct {
|
||||||
fn_vfs_list: u64,
|
fn_vfs_list: u64,
|
||||||
fn_vfs_write: u64,
|
fn_vfs_write: u64,
|
||||||
fn_vfs_close: u64,
|
fn_vfs_close: u64,
|
||||||
fn_vfs_dup: u64,
|
|
||||||
fn_vfs_dup2: u64,
|
|
||||||
fn_log: u64,
|
fn_log: u64,
|
||||||
fn_pledge: u64,
|
fn_pledge: u64,
|
||||||
// Framebuffer
|
// Framebuffer
|
||||||
|
|
@ -134,7 +127,6 @@ pub const SysTable = extern struct {
|
||||||
// Crypto
|
// Crypto
|
||||||
fn_siphash: u64,
|
fn_siphash: u64,
|
||||||
fn_ed25519_verify: u64,
|
fn_ed25519_verify: u64,
|
||||||
fn_blake3: u64,
|
|
||||||
// Network
|
// Network
|
||||||
s_net_rx: *RingBuffer(IonPacket),
|
s_net_rx: *RingBuffer(IonPacket),
|
||||||
s_net_tx: *RingBuffer(IonPacket),
|
s_net_tx: *RingBuffer(IonPacket),
|
||||||
|
|
@ -142,27 +134,16 @@ pub const SysTable = extern struct {
|
||||||
// Phase 36.3: Shared ION (16 bytes)
|
// Phase 36.3: Shared ION (16 bytes)
|
||||||
fn_ion_alloc: u64,
|
fn_ion_alloc: u64,
|
||||||
fn_ion_free: 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 {
|
comptime {
|
||||||
if (@sizeOf(IonPacket) != 24) @compileError("IonPacket size mismatch!");
|
if (@sizeOf(IonPacket) != 24) @compileError("IonPacket size mismatch!");
|
||||||
if (@sizeOf(SysTable) != 240) {
|
if (@sizeOf(SysTable) != 184) {
|
||||||
@compileError("SysTable size mismatch! Expected 240 (LibWeb LWF channels added)");
|
@compileError("SysTable size mismatch! Expected 184");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SYSTABLE_ADDR: usize = if (builtin.cpu.arch == .aarch64) 0x50000000 else 0x83000000;
|
const SYSTABLE_ADDR: usize = 0x83000000;
|
||||||
|
|
||||||
// --- API ---
|
// --- API ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,7 @@
|
||||||
# Instead, locally declare only the types we need for userspace
|
# Instead, locally declare only the types we need for userspace
|
||||||
import ../../core/ring
|
import ../../core/ring
|
||||||
|
|
||||||
const SYS_TABLE_ADDR* = when defined(arm64): 0x50000000'u64
|
const SYS_TABLE_ADDR* = 0x83000000'u64
|
||||||
else: 0x83000000'u64
|
|
||||||
|
|
||||||
# Local type declarations (must match core/ion.nim definitions)
|
# Local type declarations (must match core/ion.nim definitions)
|
||||||
type
|
type
|
||||||
|
|
@ -71,8 +70,6 @@ type
|
||||||
fn_vfs_list*: proc(buf: pointer, max_len: uint64): int64 {.cdecl.}
|
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_write*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.}
|
||||||
fn_vfs_close*: proc(fd: int32): int32 {.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_log*: pointer
|
||||||
fn_pledge*: proc(promises: uint64): int32 {.cdecl.}
|
fn_pledge*: proc(promises: uint64): int32 {.cdecl.}
|
||||||
fb_addr*: uint64
|
fb_addr*: uint64
|
||||||
|
|
@ -92,20 +89,16 @@ type
|
||||||
# Phase 36.3: Shared ION (16 bytes)
|
# Phase 36.3: Shared ION (16 bytes)
|
||||||
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
||||||
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
||||||
|
|
||||||
# Phase 36.4: I/O Multiplexing (8 bytes)
|
# Phase 36.4: I/O Multiplexing (8 bytes)
|
||||||
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
||||||
|
|
||||||
# Phase 36.5: Network Hardware Info (8 bytes)
|
# Phase 36.5: Network Hardware Info (8 bytes)
|
||||||
net_mac*: array[6, byte]
|
net_mac*: array[6, byte]
|
||||||
reserved_mac*: array[2, 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:
|
static:
|
||||||
doAssert sizeof(SysTable) == 240
|
doAssert sizeof(SysTable) == 208
|
||||||
|
|
||||||
var membrane_rx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
|
var membrane_rx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
|
||||||
var membrane_tx_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"
|
var err = "[ION-Client] ERROR: Invalid SysTable Magic!\n"
|
||||||
console_write(addr err[0], uint(err.len))
|
console_write(addr err[0], uint(err.len))
|
||||||
return
|
return
|
||||||
|
|
||||||
membrane_rx_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_rx)
|
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_tx_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_tx)
|
||||||
membrane_cmd_ring_ptr = cast[ptr RingBuffer[CmdPacket, 256]](sys.s_cmd)
|
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
|
# Phase 36.2: Network rings
|
||||||
membrane_net_rx_ptr = cast[ptr HAL_Ring_Input](sys.s_net_rx)
|
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)
|
membrane_net_tx_ptr = cast[ptr HAL_Ring_Input](sys.s_net_tx)
|
||||||
|
|
||||||
var ok = "[ION-Client] Rings Mapped.\n"
|
var ok = "[ION-Client] Rings Mapped.\n"
|
||||||
console_write(addr ok[0], uint(ok.len))
|
console_write(addr ok[0], uint(ok.len))
|
||||||
else:
|
else:
|
||||||
|
|
@ -151,10 +144,10 @@ proc ion_user_init*() {.exportc.} =
|
||||||
# Pure shared-memory slab allocator - NO kernel function calls!
|
# Pure shared-memory slab allocator - NO kernel function calls!
|
||||||
|
|
||||||
const
|
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_SLAB_COUNT = 512 # Number of packet slots
|
||||||
USER_PKT_SIZE = 2048 # Size of each packet buffer
|
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)
|
# Get pointer to shared bitmap (512 bits = 64 bytes for 512 slots)
|
||||||
proc get_user_bitmap(): ptr array[64, byte] =
|
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.} =
|
proc ion_user_alloc*(out_pkt: ptr IonPacket): bool {.exportc.} =
|
||||||
## Allocate packet from shared slab - pure userland, no kernel call
|
## Allocate packet from shared slab - pure userland, no kernel call
|
||||||
let bitmap = get_user_bitmap()
|
let bitmap = get_user_bitmap()
|
||||||
|
|
||||||
# Find first free slot
|
# Find first free slot
|
||||||
for byteIdx in 0 ..< 64:
|
for byteIdx in 0 ..< 64:
|
||||||
if bitmap[byteIdx] != 0xFF: # At least one bit free
|
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:
|
if (bitmap[byteIdx] and mask) == 0:
|
||||||
# Found free slot - mark as used
|
# Found free slot - mark as used
|
||||||
bitmap[byteIdx] = bitmap[byteIdx] or mask
|
bitmap[byteIdx] = bitmap[byteIdx] or mask
|
||||||
|
|
||||||
let addr_val = USER_SLAB_BASE + uint64(slotIdx) * USER_PKT_SIZE
|
let addr_val = USER_SLAB_BASE + uint64(slotIdx) * USER_PKT_SIZE
|
||||||
out_pkt.id = uint16(slotIdx) or 0x8000
|
out_pkt.id = uint16(slotIdx) or 0x8000
|
||||||
out_pkt.phys = addr_val
|
out_pkt.phys = addr_val
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,13 @@
|
||||||
# (C) 2026 Markus Maiwald
|
# (C) 2026 Markus Maiwald
|
||||||
|
|
||||||
import ion_client
|
import ion_client
|
||||||
when defined(RUMPK_KERNEL):
|
import net_glue
|
||||||
import net_glue
|
|
||||||
export net_glue
|
|
||||||
|
|
||||||
# memcpy removed to avoid C header conflict
|
# memcpy removed to avoid C header conflict
|
||||||
|
|
||||||
# --- SHARED CONSTANTS & TYPES ---
|
# --- SHARED CONSTANTS & TYPES ---
|
||||||
|
|
||||||
const
|
const
|
||||||
MAX_SOCKS = 32
|
MAX_SOCKS = 32
|
||||||
FD_OFFSET = 3
|
FD_OFFSET = 3
|
||||||
# Syscalls
|
# Syscalls
|
||||||
|
|
@ -52,37 +50,39 @@ proc syscall*(nr: int, a0: uint64 = 0, a1: uint64 = 0, a2: uint64 = 0): int =
|
||||||
let v0 = a0
|
let v0 = a0
|
||||||
let v1 = a1
|
let v1 = a1
|
||||||
let v2 = a2
|
let v2 = a2
|
||||||
when defined(arm64):
|
{.emit: """
|
||||||
{.emit: """
|
register unsigned long a7 __asm__("a7") = `n`;
|
||||||
register unsigned long x8_ __asm__("x8") = `n`;
|
register unsigned long a0_ __asm__("a0") = `v0`;
|
||||||
register unsigned long x0_ __asm__("x0") = `v0`;
|
register unsigned long a1_ __asm__("a1") = `v1`;
|
||||||
register unsigned long x1_ __asm__("x1") = `v1`;
|
register unsigned long a2_ __asm__("a2") = `v2`;
|
||||||
register unsigned long x2_ __asm__("x2") = `v2`;
|
__asm__ volatile("ecall" : "+r"(a0_) : "r"(a7), "r"(a1_), "r"(a2_) : "memory");
|
||||||
__asm__ volatile("svc #0" : "+r"(x0_) : "r"(x8_), "r"(x1_), "r"(x2_) : "memory");
|
`res` = (int)a0_;
|
||||||
`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_;
|
|
||||||
""".}
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
when defined(RUMPK_KERNEL):
|
when defined(RUMPK_KERNEL):
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# KERNEL IMPLEMENTATION
|
# KERNEL IMPLEMENTATION
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# SockState, NexusSock, membrane_init, pump_membrane_stack,
|
|
||||||
# glue_connect/bind/listen/accept_peek/setup_socket/write/read/close,
|
type
|
||||||
# glue_resolve_start/check — all provided by net_glue (imported above)
|
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_sockets: array[MAX_SOCKS, NexusSock]
|
||||||
var g_sock_used: array[MAX_SOCKS, bool]
|
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.}
|
proc rumpk_yield_internal() {.importc, cdecl.}
|
||||||
|
|
||||||
{.emit: """
|
{.emit: """
|
||||||
|
|
@ -90,6 +90,17 @@ when defined(RUMPK_KERNEL):
|
||||||
extern void trigger_http_test(void);
|
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
|
const
|
||||||
MAX_FILES = 16
|
MAX_FILES = 16
|
||||||
FILE_FD_START = 100
|
FILE_FD_START = 100
|
||||||
|
|
@ -113,7 +124,7 @@ when defined(RUMPK_KERNEL):
|
||||||
g_sockets[i].rx_len = 0
|
g_sockets[i].rx_len = 0
|
||||||
g_sockets[i].accepted_pcb = nil
|
g_sockets[i].accepted_pcb = nil
|
||||||
g_sockets[i].accepted_pending = 0
|
g_sockets[i].accepted_pending = 0
|
||||||
|
|
||||||
g_fd_table[fd].kind = FD_SOCKET
|
g_fd_table[fd].kind = FD_SOCKET
|
||||||
return i
|
return i
|
||||||
return -1
|
return -1
|
||||||
|
|
@ -129,7 +140,7 @@ when defined(RUMPK_KERNEL):
|
||||||
if fd < 0 or fd >= 256: return false
|
if fd < 0 or fd >= 256: return false
|
||||||
return g_fd_table[fd].kind == FD_SOCKET
|
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()
|
let idx = alloc_sock()
|
||||||
if idx < 0: return -1
|
if idx < 0: return -1
|
||||||
return idx + FD_OFFSET
|
return idx + FD_OFFSET
|
||||||
|
|
@ -172,7 +183,7 @@ when defined(RUMPK_KERNEL):
|
||||||
if sock.state == ESTABLISHED: return 0
|
if sock.state == ESTABLISHED: return 0
|
||||||
return -1
|
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)
|
let sock = get_sock(fd)
|
||||||
if sock == nil: return -1
|
if sock == nil: return -1
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -180,13 +191,13 @@ when defined(RUMPK_KERNEL):
|
||||||
let n = glue_read(sock, buf, int(count))
|
let n = glue_read(sock, buf, int(count))
|
||||||
if n > 0: return n
|
if n > 0: return n
|
||||||
if sock.state == CLOSED: return 0
|
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)
|
let sock = get_sock(fd)
|
||||||
if sock == nil: return -1
|
if sock == nil: return -1
|
||||||
let res = glue_write(sock, buf, int(count))
|
let res = glue_write(sock, buf, int(count))
|
||||||
pump_membrane_stack()
|
pump_membrane_stack()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
proc libc_impl_close_socket*(fd: int): int {.exportc: "libc_impl_close_socket", cdecl.} =
|
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`);" .}
|
# {.emit: "printf(\"[Membrane] libc_impl_getaddrinfo(node=%s, res_ptr=%p)\\n\", `node`, `res`);" .}
|
||||||
let status = glue_resolve_start(node)
|
let status = glue_resolve_start(node)
|
||||||
var resolved = false
|
var resolved = false
|
||||||
|
|
||||||
if status == 0:
|
if status == 0:
|
||||||
# Cached / Done
|
# Cached / Done
|
||||||
var ip_tmp: uint32
|
var ip_tmp: uint32
|
||||||
|
|
@ -219,16 +230,16 @@ when defined(RUMPK_KERNEL):
|
||||||
break
|
break
|
||||||
if glue_resolve_check(addr ip) == -1:
|
if glue_resolve_check(addr ip) == -1:
|
||||||
break
|
break
|
||||||
rumpk_yield_internal()
|
rumpk_yield_internal()
|
||||||
|
|
||||||
if not resolved: return -1 # EAI_FAIL
|
if not resolved: return -1 # EAI_FAIL
|
||||||
|
|
||||||
# 2. Allocate AddrInfo struct (using User Allocator? No, Kernel Allocator)
|
# 2. Allocate AddrInfo struct (using User Allocator? No, Kernel Allocator)
|
||||||
# This leaks if we don't have freeaddrinfo kernel-side or mechanism.
|
# This leaks if we don't have freeaddrinfo kernel-side or mechanism.
|
||||||
|
|
||||||
var ai = create(AddrInfo)
|
var ai = create(AddrInfo)
|
||||||
var sa = create(SockAddr)
|
var sa = create(SockAddr)
|
||||||
|
|
||||||
ai.ai_family = 2 # AF_INET
|
ai.ai_family = 2 # AF_INET
|
||||||
ai.ai_socktype = 1 # SOCK_STREAM
|
ai.ai_socktype = 1 # SOCK_STREAM
|
||||||
ai.ai_protocol = 6 # IPPROTO_TCP
|
ai.ai_protocol = 6 # IPPROTO_TCP
|
||||||
|
|
@ -236,7 +247,7 @@ when defined(RUMPK_KERNEL):
|
||||||
ai.ai_addr = sa
|
ai.ai_addr = sa
|
||||||
ai.ai_canonname = nil
|
ai.ai_canonname = nil
|
||||||
ai.ai_next = nil
|
ai.ai_next = nil
|
||||||
|
|
||||||
sa.sa_family = 2 # AF_INET
|
sa.sa_family = 2 # AF_INET
|
||||||
# Port 0 (Service not implemented yet)
|
# Port 0 (Service not implemented yet)
|
||||||
# IP
|
# IP
|
||||||
|
|
@ -254,16 +265,16 @@ when defined(RUMPK_KERNEL):
|
||||||
|
|
||||||
struct my_sockaddr_in *sin = (struct my_sockaddr_in *)`sa`;
|
struct my_sockaddr_in *sin = (struct my_sockaddr_in *)`sa`;
|
||||||
sin->sin_addr.s_addr = `ip`;
|
sin->sin_addr.s_addr = `ip`;
|
||||||
sin->sin_port = 0;
|
sin->sin_port = 0;
|
||||||
sin->sin_family = 2; // AF_INET
|
sin->sin_family = 2; // AF_INET
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
if res != nil:
|
if res != nil:
|
||||||
res[] = ai
|
res[] = ai
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc libc_impl_freeaddrinfo*(res: ptr AddrInfo) {.exportc: "libc_impl_freeaddrinfo", cdecl.} =
|
proc libc_impl_freeaddrinfo*(res: ptr AddrInfo) {.exportc: "libc_impl_freeaddrinfo", cdecl.} =
|
||||||
if res != nil:
|
if res != nil:
|
||||||
if res.ai_addr != nil: dealloc(res.ai_addr)
|
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.} =
|
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: return int(syscall(0x203, 0, cast[uint64](buf), count))
|
||||||
|
|
||||||
if fd >= 0 and fd < 256:
|
if fd >= 0 and fd < 256:
|
||||||
if g_fd_table[fd].kind == FD_FILE:
|
if g_fd_table[fd].kind == FD_FILE:
|
||||||
let path = cast[cstring](addr g_fd_table[fd].path[0])
|
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 open*(path: cstring, flags: int = 0): int {.importc: "open", cdecl.}
|
||||||
proc close*(fd: int): int {.importc: "close", cdecl.}
|
proc close*(fd: int): int {.importc: "close", cdecl.}
|
||||||
proc execv*(path: cstring, argv: pointer): int {.importc: "execv", cdecl.}
|
proc execv*(path: cstring, argv: pointer): int {.importc: "execv", cdecl.}
|
||||||
|
|
||||||
# Manual strlen to avoid C header conflicts
|
# Manual strlen to avoid C header conflicts
|
||||||
proc libc_strlen(s: cstring): uint64 =
|
proc libc_strlen(s: cstring): uint64 =
|
||||||
if s == nil: return 0
|
if s == nil: return 0
|
||||||
|
|
@ -353,17 +364,15 @@ else:
|
||||||
|
|
||||||
proc exit*(status: int) {.exportc, cdecl.} =
|
proc exit*(status: int) {.exportc, cdecl.} =
|
||||||
discard syscall(0x01, uint64(status))
|
discard syscall(0x01, uint64(status))
|
||||||
while true: discard
|
while true: discard
|
||||||
|
|
||||||
proc yield_fiber*() {.exportc: "yield", cdecl.} =
|
proc yield_fiber*() {.exportc: "yield", cdecl.} =
|
||||||
discard syscall(0x100, 0)
|
discard syscall(0x100, 0)
|
||||||
|
|
||||||
proc pump_membrane_stack*() =
|
proc pump_membrane_stack*() {.importc, cdecl.}
|
||||||
## No-op in userland — kernel drives LwIP stack
|
proc membrane_init*() {.importc, cdecl.}
|
||||||
yield_fiber()
|
|
||||||
# proc membrane_init*() {.importc, cdecl.}
|
|
||||||
proc ion_user_wait_multi*(mask: uint64): int32 {.importc, cdecl.}
|
proc ion_user_wait_multi*(mask: uint64): int32 {.importc, cdecl.}
|
||||||
|
|
||||||
proc pledge*(promises: uint64): int {.exportc, cdecl.} =
|
proc pledge*(promises: uint64): int {.exportc, cdecl.} =
|
||||||
return int(syscall(0x101, promises))
|
return int(syscall(0x101, promises))
|
||||||
|
|
||||||
|
|
@ -378,13 +387,13 @@ else:
|
||||||
|
|
||||||
proc upgrade*(id: int, path: cstring): int {.exportc, cdecl.} =
|
proc upgrade*(id: int, path: cstring): int {.exportc, cdecl.} =
|
||||||
# Deprecated: Use kexec directly
|
# Deprecated: Use kexec directly
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc get_vfs_listing*(): seq[string] =
|
proc get_vfs_listing*(): seq[string] =
|
||||||
var buf: array[4096, char]
|
var buf: array[4096, char]
|
||||||
let n = readdir(addr buf[0], 4096)
|
let n = readdir(addr buf[0], 4096)
|
||||||
if n <= 0: return @[]
|
if n <= 0: return @[]
|
||||||
|
|
||||||
result = @[]
|
result = @[]
|
||||||
var current = ""
|
var current = ""
|
||||||
for i in 0..<n:
|
for i in 0..<n:
|
||||||
|
|
@ -406,7 +415,7 @@ else:
|
||||||
proc sys_surface_get_ptr*(surf_id: int): pointer {.exportc, cdecl.} =
|
proc sys_surface_get_ptr*(surf_id: int): pointer {.exportc, cdecl.} =
|
||||||
return cast[pointer](syscall(0x302, uint64(surf_id)))
|
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)))
|
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.} =
|
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.} =
|
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)))
|
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))
|
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))
|
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.} =
|
proc getaddrinfo*(node: cstring, service: cstring, hints: ptr AddrInfo, res: ptr ptr AddrInfo): int {.exportc, cdecl.} =
|
||||||
# Syscall 0x905
|
# 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.} =
|
proc freeaddrinfo*(res: ptr AddrInfo) {.exportc, cdecl.} =
|
||||||
# No-op for now (Kernel allocated statically/leak for MVP)
|
# No-op for now (Kernel allocated statically/leak for MVP)
|
||||||
# Or implement Syscall 0x906 if needed.
|
# 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
|
## Generate cryptographically strong random number for TCP ISN
|
||||||
## Implementation: SipHash-2-4(MonolithKey, Time || CycleCount)
|
## Implementation: SipHash-2-4(MonolithKey, Time || CycleCount)
|
||||||
## Per SPEC-805: Hash Strategy
|
## Per SPEC-805: Hash Strategy
|
||||||
|
|
||||||
# TODO: Optimize to avoid overhead if called frequently
|
# TODO: Optimize to avoid overhead if called frequently
|
||||||
let time_ns = syscall_get_time_ns()
|
let time_ns = syscall_get_time_ns()
|
||||||
|
|
||||||
# Temporary simple mix
|
# Temporary simple mix
|
||||||
return uint32(time_ns xor (time_ns shr 32))
|
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!
|
# 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 console_write(s: pointer, len: csize_t) {.importc: "console_write", cdecl.}
|
||||||
|
|
||||||
proc glue_print(s: string) =
|
proc glue_print(s: string) =
|
||||||
console_write(unsafeAddr s[0], csize_t(s.len))
|
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
|
# LwIP Imports
|
||||||
{.passC: "-Icore/rumpk/vendor/lwip/src/include".}
|
{.passC: "-Icore/rumpk/vendor/lwip/src/include".}
|
||||||
{.passC: "-Icore/rumpk/libs/membrane/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);
|
memset((char *)p->payload + sizeof(struct icmp_echo_hdr), 'A', 32);
|
||||||
|
|
||||||
iecho->chksum = inet_chksum(iecho, p->len);
|
iecho->chksum = inet_chksum(iecho, p->len);
|
||||||
|
|
||||||
raw_sendto(ping_pcb, p, addr);
|
raw_sendto(ping_pcb, p, addr);
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
}
|
}
|
||||||
|
|
@ -124,8 +114,6 @@ type
|
||||||
state*: SockState
|
state*: SockState
|
||||||
rx_buf*: array[4096, byte]
|
rx_buf*: array[4096, byte]
|
||||||
rx_len*: int
|
rx_len*: int
|
||||||
accepted_pcb*: pointer
|
|
||||||
accepted_pending*: int32
|
|
||||||
|
|
||||||
|
|
||||||
# Forward declarations for LwIP callbacks
|
# 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`;
|
struct pbuf *curr = (struct pbuf *)`p`;
|
||||||
while (curr != NULL) {
|
while (curr != NULL) {
|
||||||
if (`offset` + curr->len > 2000) break;
|
if (`offset` + curr->len > 2000) break;
|
||||||
|
|
||||||
// Copy Ethernet frame directly (includes header)
|
// Copy Ethernet frame directly (includes header)
|
||||||
memcpy((void*)((uintptr_t)`pkt`.data + `offset`), curr->payload, curr->len);
|
memcpy((void*)((uintptr_t)`pkt`.data + `offset`), curr->payload, curr->len);
|
||||||
`offset` += curr->len;
|
`offset` += curr->len;
|
||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
# Zero out VirtIO-net header (first 12 bytes - Modern with MRG_RXBUF)
|
# Zero out VirtIO-net header (first 12 bytes - Modern with MRG_RXBUF)
|
||||||
{.emit: """
|
{.emit: """
|
||||||
memset((void*)`pkt`.data, 0, 12);
|
memset((void*)`pkt`.data, 0, 12);
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
pkt.len = uint16(offset) # Total: 12 (VirtIO) + Ethernet frame
|
pkt.len = uint16(offset) # Total: 12 (VirtIO) + Ethernet frame
|
||||||
|
|
||||||
if not ion_net_tx(pkt):
|
if not ion_net_tx(pkt):
|
||||||
ion_user_free(pkt)
|
ion_user_free(pkt)
|
||||||
return -1 # ERR_IF
|
return -1 # ERR_IF
|
||||||
|
|
||||||
return 0 # ERR_OK
|
return 0 # ERR_OK
|
||||||
|
|
||||||
proc ion_netif_init(netif: pointer): int32 {.exportc, cdecl.} =
|
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->mtu = 1500;
|
||||||
ni->hwaddr_len = 6;
|
ni->hwaddr_len = 6;
|
||||||
ni->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_LINK_UP;
|
ni->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_LINK_UP;
|
||||||
|
|
||||||
// Set MAC from SysTable
|
// Set MAC from SysTable
|
||||||
ni->hwaddr[0] = `mac`[0];
|
ni->hwaddr[0] = `mac`[0];
|
||||||
ni->hwaddr[1] = `mac`[1];
|
ni->hwaddr[1] = `mac`[1];
|
||||||
ni->hwaddr[2] = `mac`[2];
|
ni->hwaddr[2] = `mac`[2];
|
||||||
ni->hwaddr[3] = `mac`[3];
|
ni->hwaddr[3] = `mac`[3];
|
||||||
ni->hwaddr[4] = `mac`[4];
|
ni->hwaddr[4] = `mac`[4];
|
||||||
ni->hwaddr[5] = `mac`[5];
|
ni->hwaddr[5] = `mac`[5];
|
||||||
""".}
|
""".}
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -204,51 +192,67 @@ proc membrane_init*() {.exportc, cdecl.} =
|
||||||
last_dhcp_coarse = now
|
last_dhcp_coarse = now
|
||||||
last_dns_tmr = now
|
last_dns_tmr = now
|
||||||
|
|
||||||
glue_print("[Membrane] Initialization Starting...\n")
|
glue_print("[Membrane] Initialization...\n")
|
||||||
ion_user_init()
|
ion_user_init()
|
||||||
|
|
||||||
# 1. LwIP Stack Init
|
# 1. LwIP Stack Init
|
||||||
glue_print("[Membrane] Calling lwip_init()...\n")
|
glue_print("[Membrane] Calling lwip_init()...\n")
|
||||||
lwip_init()
|
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)
|
# Set Fallback DNS (10.0.2.3 - QEMU Default)
|
||||||
{.emit: """
|
{.emit: """
|
||||||
static ip_addr_t dns_server;
|
static ip_addr_t dns_server;
|
||||||
IP4_ADDR(ip_2_ip4(&dns_server), 10, 0, 2, 3);
|
IP4_ADDR(ip_2_ip4(&dns_server), 10, 0, 2, 3);
|
||||||
dns_setserver(0, &dns_server);
|
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
|
||||||
|
|
||||||
# 2. Setup Netif with DHCP
|
|
||||||
{.emit: """
|
{.emit: """
|
||||||
static struct netif ni_static;
|
static struct netif ni_static;
|
||||||
ip4_addr_t ip, mask, gw;
|
ip4_addr_t ip, mask, gw;
|
||||||
|
|
||||||
// Start with zeros — DHCP will assign
|
// Use Static IP to stabilize test environment
|
||||||
IP4_ADDR(&ip, 0, 0, 0, 0);
|
IP4_ADDR(&ip, 10, 0, 2, 15);
|
||||||
IP4_ADDR(&mask, 0, 0, 0, 0);
|
IP4_ADDR(&mask, 255, 255, 255, 0);
|
||||||
IP4_ADDR(&gw, 0, 0, 0, 0);
|
IP4_ADDR(&gw, 10, 0, 2, 2);
|
||||||
|
|
||||||
struct netif *res = netif_add(&ni_static, &ip, &mask, &gw, NULL,
|
struct netif *res = netif_add(&ni_static, &ip, &mask, &gw, NULL, (netif_init_fn)ion_netif_init, (netif_input_fn)ethernet_input);
|
||||||
(netif_init_fn)ion_netif_init,
|
printf("[Membrane] netif_add returned: 0x%x\n", (unsigned int)res);
|
||||||
(netif_input_fn)ethernet_input);
|
|
||||||
|
netif_set_default(&ni_static);
|
||||||
if (res == NULL) {
|
netif_set_up(&ni_static);
|
||||||
printf("[Membrane] CRITICAL: netif_add FAILED!\n");
|
|
||||||
} else {
|
printf("[Membrane] netif_default: 0x%x | netif_list: 0x%x\n", (unsigned int)netif_default, (unsigned int)netif_list);
|
||||||
netif_set_default(&ni_static);
|
|
||||||
netif_set_up(&ni_static);
|
// dhcp_start(&ni_static); // Bypassing DHCP
|
||||||
dhcp_start(&ni_static);
|
|
||||||
printf("[Membrane] DHCP started on io0\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
`g_netif` = &ni_static;
|
`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.} =
|
proc glue_get_ip*(): uint32 {.exportc, cdecl.} =
|
||||||
## Returns current IP address in host byte order
|
## Returns current IP address in host byte order
|
||||||
|
|
@ -271,7 +275,7 @@ proc glue_print_hex(v: uint64) =
|
||||||
|
|
||||||
proc pump_membrane_stack*() {.exportc, cdecl.} =
|
proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
## The Pulse of the Membrane. Call frequently to handle timers and RX.
|
## The Pulse of the Membrane. Call frequently to handle timers and RX.
|
||||||
|
|
||||||
pump_iterations += 1
|
pump_iterations += 1
|
||||||
let now = sys_now()
|
let now = sys_now()
|
||||||
|
|
||||||
|
|
@ -283,7 +287,7 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
glue_print_hex(uint64(ip_addr))
|
glue_print_hex(uint64(ip_addr))
|
||||||
glue_print("\n")
|
glue_print("\n")
|
||||||
last_notified_ip = ip_addr
|
last_notified_ip = ip_addr
|
||||||
|
|
||||||
# Phase 40: Fast Trigger for Helios Probe
|
# Phase 40: Fast Trigger for Helios Probe
|
||||||
glue_print("[Membrane] IP Found. Triggering Helios Probe...\n")
|
glue_print("[Membrane] IP Found. Triggering Helios Probe...\n")
|
||||||
{.emit: "trigger_http_test();" .}
|
{.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):
|
if (now - last_tcp_tmr >= 250) or (pump_iterations mod 25 == 0):
|
||||||
tcp_tmr()
|
tcp_tmr()
|
||||||
last_tcp_tmr = now
|
last_tcp_tmr = now
|
||||||
|
|
||||||
# ARP Timer (5s)
|
# ARP Timer (5s)
|
||||||
if (now - last_arp_tmr >= 5000) or (pump_iterations mod 500 == 0):
|
if (now - last_arp_tmr >= 5000) or (pump_iterations mod 500 == 0):
|
||||||
etharp_tmr()
|
etharp_tmr()
|
||||||
last_arp_tmr = now
|
last_arp_tmr = now
|
||||||
|
|
||||||
# DHCP Timers
|
# DHCP Timers
|
||||||
if (now - last_dhcp_fine >= 500) or (pump_iterations mod 50 == 0):
|
if (now - last_dhcp_fine >= 500) or (pump_iterations mod 50 == 0):
|
||||||
dhcp_fine_tmr()
|
dhcp_fine_tmr()
|
||||||
last_dhcp_fine = now
|
last_dhcp_fine = now
|
||||||
|
|
||||||
if (now - last_dhcp_coarse >= 60000) or (pump_iterations mod 6000 == 0):
|
if (now - last_dhcp_coarse >= 60000) or (pump_iterations mod 6000 == 0):
|
||||||
dhcp_coarse_tmr()
|
dhcp_coarse_tmr()
|
||||||
last_dhcp_coarse = now
|
last_dhcp_coarse = now
|
||||||
|
|
@ -323,14 +327,14 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
# Phase 37a: ICMP Ping Verification
|
# Phase 37a: ICMP Ping Verification
|
||||||
if now - last_ping_time > 1000:
|
if now - last_ping_time > 1000:
|
||||||
last_ping_time = now
|
last_ping_time = now
|
||||||
|
|
||||||
if ip_addr != 0:
|
if ip_addr != 0:
|
||||||
glue_print("[Membrane] TESTING EXTERNAL REACHABILITY: PING 142.250.185.78...\n")
|
glue_print("[Membrane] TESTING EXTERNAL REACHABILITY: PING 142.250.185.78...\n")
|
||||||
{.emit: """
|
{.emit: """
|
||||||
ip_addr_t target;
|
ip_addr_t target;
|
||||||
IP4_ADDR(&target, 142, 250, 185, 78);
|
IP4_ADDR(&target, 142, 250, 185, 78);
|
||||||
ping_send(&target);
|
ping_send(&target);
|
||||||
|
|
||||||
// Trigger the Helios TCP Probe
|
// Trigger the Helios TCP Probe
|
||||||
trigger_http_test();
|
trigger_http_test();
|
||||||
""".}
|
""".}
|
||||||
|
|
@ -381,7 +385,7 @@ proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
int state; // 0=CLOSED, 1=LISTEN, 2=CONNECTING, 3=ESTABLISHED
|
int state; // 0=CLOSED, 1=LISTEN, 2=CONNECTING, 3=ESTABLISHED
|
||||||
unsigned char rx_buf[4096];
|
unsigned char rx_buf[4096];
|
||||||
NI rx_len;
|
NI rx_len;
|
||||||
|
|
||||||
// Server Fields
|
// Server Fields
|
||||||
void* accepted_pcb;
|
void* accepted_pcb;
|
||||||
int accepted_pending;
|
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`;
|
NexusSock_C *s = (NexusSock_C *)`arg`;
|
||||||
s->state = 3; // ESTABLISHED
|
s->state = 3; // ESTABLISHED
|
||||||
""".}
|
""".}
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
proc ion_tcp_sent(arg: pointer, pcb: pointer, len: uint16): int8 {.exportc, cdecl.} =
|
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.} =
|
proc ion_tcp_recv(arg: pointer, pcb: pointer, p: pointer, err: int8): int8 {.exportc, cdecl.} =
|
||||||
if p == nil:
|
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
|
s->state = 0; // CLOSED
|
||||||
""".}
|
""".}
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Append data to rx_buf
|
# Append data to rx_buf
|
||||||
{.emit: """
|
{.emit: """
|
||||||
struct pbuf *curr = (struct pbuf *)`p`;
|
struct pbuf *curr = (struct pbuf *)`p`;
|
||||||
NexusSock_C *s = (NexusSock_C *)`arg`;
|
NexusSock_C *s = (NexusSock_C *)`arg`;
|
||||||
|
|
||||||
if (curr != NULL) {
|
if (curr != NULL) {
|
||||||
struct pbuf *q;
|
struct pbuf *q;
|
||||||
for (q = curr; q != NULL; q = q->next) {
|
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) {
|
if (listener->accepted_pending == 0) {
|
||||||
listener->accepted_pcb = `new_pcb`;
|
listener->accepted_pcb = `new_pcb`;
|
||||||
listener->accepted_pending = 1;
|
listener->accepted_pending = 1;
|
||||||
|
|
||||||
// Increase reference count? No, LwIP gives us ownership.
|
// Increase reference count? No, LwIP gives us ownership.
|
||||||
// Important: We must not set callbacks yet?
|
// Important: We must not set callbacks yet?
|
||||||
// LwIP doc: "When a new connection arrives, the accept callback function is called.
|
// LwIP doc: "When a new connection arrives, the accept callback function is called.
|
||||||
// The new pcb is passed as a parameter."
|
// The new pcb is passed as a parameter."
|
||||||
|
|
||||||
// We'll set callbacks later when libc performs the accept() syscall.
|
// We'll set callbacks later when libc performs the accept() syscall.
|
||||||
} else {
|
} else {
|
||||||
// Backlog full, reject?
|
// 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 -1; // ERR_ABRT
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
proc glue_setup_socket*(sock: ptr NexusSock, pcb_ptr: pointer) {.exportc, cdecl.} =
|
proc glue_setup_socket*(sock: ptr NexusSock, pcb_ptr: pointer) {.exportc, cdecl.} =
|
||||||
# Wire LwIP callbacks to a NexusSock
|
# 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`;
|
NexusSock_C *ns = (NexusSock_C *)`sock`;
|
||||||
struct tcp_pcb *pcb = (struct tcp_pcb *)`pcb_ptr`;
|
struct tcp_pcb *pcb = (struct tcp_pcb *)`pcb_ptr`;
|
||||||
ns->pcb = pcb;
|
ns->pcb = pcb;
|
||||||
|
|
||||||
tcp_arg(pcb, ns);
|
tcp_arg(pcb, ns);
|
||||||
tcp_recv(pcb, (tcp_recv_fn)ion_tcp_recv);
|
tcp_recv(pcb, (tcp_recv_fn)ion_tcp_recv);
|
||||||
tcp_sent(pcb, (tcp_sent_fn)ion_tcp_sent);
|
tcp_sent(pcb, (tcp_sent_fn)ion_tcp_sent);
|
||||||
// tcp_poll(pcb, ...);
|
// tcp_poll(pcb, ...);
|
||||||
|
|
@ -472,13 +476,13 @@ proc glue_connect*(sock: ptr NexusSock, ip: uint32, port: uint16): int {.exportc
|
||||||
{.emit: """
|
{.emit: """
|
||||||
struct tcp_pcb *pcb = tcp_new();
|
struct tcp_pcb *pcb = tcp_new();
|
||||||
if (pcb == NULL) return -1;
|
if (pcb == NULL) return -1;
|
||||||
|
|
||||||
// Wire up
|
// Wire up
|
||||||
glue_setup_socket(`sock`, pcb);
|
glue_setup_socket(`sock`, pcb);
|
||||||
|
|
||||||
ip4_addr_t remote_ip;
|
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);
|
tcp_connect(pcb, &remote_ip, `port`, (tcp_connected_fn)ion_tcp_connected);
|
||||||
""".}
|
""".}
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -488,18 +492,18 @@ proc glue_bind*(sock: ptr NexusSock, port: uint16): int {.exportc, cdecl.} =
|
||||||
{.emit: """
|
{.emit: """
|
||||||
struct tcp_pcb *pcb = tcp_new();
|
struct tcp_pcb *pcb = tcp_new();
|
||||||
if (pcb == NULL) return -1;
|
if (pcb == NULL) return -1;
|
||||||
|
|
||||||
// Bind to ANY
|
// Bind to ANY
|
||||||
if (tcp_bind(pcb, IP_ADDR_ANY, `port`) != ERR_OK) {
|
if (tcp_bind(pcb, IP_ADDR_ANY, `port`) != ERR_OK) {
|
||||||
memp_free(MEMP_TCP_PCB, pcb);
|
memp_free(MEMP_TCP_PCB, pcb);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update sock
|
// Update sock
|
||||||
// glue_setup_socket checks validity, but here we just need to store PCB
|
// 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.
|
// Actually we need tcp_arg for accept callback.
|
||||||
|
|
||||||
NexusSock_C *ns = (NexusSock_C *)`sock`;
|
NexusSock_C *ns = (NexusSock_C *)`sock`;
|
||||||
ns->pcb = pcb;
|
ns->pcb = pcb;
|
||||||
tcp_arg(pcb, ns);
|
tcp_arg(pcb, ns);
|
||||||
|
|
@ -511,7 +515,7 @@ proc glue_listen*(sock: ptr NexusSock): int {.exportc, cdecl.} =
|
||||||
NexusSock_C *ns = (NexusSock_C *)`sock`;
|
NexusSock_C *ns = (NexusSock_C *)`sock`;
|
||||||
struct tcp_pcb *lpcb = tcp_listen((struct tcp_pcb *)ns->pcb);
|
struct tcp_pcb *lpcb = tcp_listen((struct tcp_pcb *)ns->pcb);
|
||||||
if (lpcb == NULL) return -1;
|
if (lpcb == NULL) return -1;
|
||||||
|
|
||||||
ns->pcb = lpcb; // Update to listening PCB
|
ns->pcb = lpcb; // Update to listening PCB
|
||||||
tcp_accept(lpcb, (tcp_accept_fn)ion_tcp_accept);
|
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
|
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: """
|
{.emit: """
|
||||||
struct tcp_pcb *pcb = (struct tcp_pcb *)`sock`->pcb;
|
struct tcp_pcb *pcb = (struct tcp_pcb *)`sock`->pcb;
|
||||||
if (pcb == NULL) return -1;
|
if (pcb == NULL) return -1;
|
||||||
|
|
||||||
tcp_write(pcb, `buf`, `len`, TCP_WRITE_FLAG_COPY);
|
tcp_write(pcb, `buf`, `len`, TCP_WRITE_FLAG_COPY);
|
||||||
tcp_output(pcb);
|
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.} =
|
proc glue_read*(sock: ptr NexusSock, buf: pointer, len: int): int {.exportc, cdecl.} =
|
||||||
if sock.rx_len == 0: return 0
|
if sock.rx_len == 0: return 0
|
||||||
|
|
||||||
var to_read = len
|
var to_read = len
|
||||||
if to_read > sock.rx_len: to_read = sock.rx_len
|
if to_read > sock.rx_len: to_read = sock.rx_len
|
||||||
|
|
||||||
copyMem(buf, addr sock.rx_buf[0], uint64(to_read))
|
copyMem(buf, addr sock.rx_buf[0], uint64(to_read))
|
||||||
|
|
||||||
# Shift buffer (Ring buffer would be better, but this is MVP)
|
# Shift buffer (Ring buffer would be better, but this is MVP)
|
||||||
var remaining = sock.rx_len - to_read
|
var remaining = sock.rx_len - to_read
|
||||||
if remaining > 0:
|
if remaining > 0:
|
||||||
copyMem(addr sock.rx_buf[0], addr sock.rx_buf[to_read], uint64(remaining))
|
copyMem(addr sock.rx_buf[0], addr sock.rx_buf[to_read], uint64(remaining))
|
||||||
|
|
||||||
sock.rx_len = remaining
|
sock.rx_len = remaining
|
||||||
return to_read
|
return to_read
|
||||||
|
|
||||||
|
|
@ -596,7 +600,7 @@ int glue_dns_check_init(void) {
|
||||||
int glue_resolve_start(char* hostname) {
|
int glue_resolve_start(char* hostname) {
|
||||||
// BYPASS: Mock DNS to unblock Userland
|
// BYPASS: Mock DNS to unblock Userland
|
||||||
// printf("[Membrane] DNS MOCK: Resolving '%s' -> 10.0.2.2\n", hostname);
|
// printf("[Membrane] DNS MOCK: Resolving '%s' -> 10.0.2.2\n", hostname);
|
||||||
|
|
||||||
ip_addr_t ip;
|
ip_addr_t ip;
|
||||||
IP4_ADDR(ip_2_ip4(&ip), 10, 0, 2, 2); // Gateway
|
IP4_ADDR(ip_2_ip4(&ip), 10, 0, 2, 2); // Gateway
|
||||||
g_dns_ip = ip;
|
g_dns_ip = ip;
|
||||||
|
|
@ -649,17 +653,18 @@ void trigger_http_test(void) {
|
||||||
|
|
||||||
ip_addr_t google_ip;
|
ip_addr_t google_ip;
|
||||||
IP4_ADDR(ip_2_ip4(&google_ip), 142, 250, 185, 78);
|
IP4_ADDR(ip_2_ip4(&google_ip), 142, 250, 185, 78);
|
||||||
|
|
||||||
struct tcp_pcb *pcb = tcp_new();
|
struct tcp_pcb *pcb = tcp_new();
|
||||||
if (!pcb) {
|
if (!pcb) {
|
||||||
printf("[Membrane] HELIOS Error: Failed to create TCP PCB\n");
|
printf("[Membrane] HELIOS Error: Failed to create TCP PCB\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_arg(pcb, NULL);
|
tcp_arg(pcb, NULL);
|
||||||
tcp_recv(pcb, tcp_recv_callback);
|
tcp_recv(pcb, tcp_recv_callback);
|
||||||
|
|
||||||
printf("[Membrane] HELIOS: INITIATING TCP CONNECTION to 142.250.185.78:80...\n");
|
printf("[Membrane] HELIOS: INITIATING TCP CONNECTION to 142.250.185.78:80...\n");
|
||||||
tcp_connect(pcb, &google_ip, 80, tcp_connected_callback);
|
tcp_connect(pcb, &google_ip, 80, tcp_connected_callback);
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,49 +12,9 @@ import strutils, parseutils, tables, sequtils, json
|
||||||
import kdl
|
import kdl
|
||||||
import ../../libs/membrane/libc as lb
|
import ../../libs/membrane/libc as lb
|
||||||
import ../../libs/membrane/libc_net as lnet
|
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
|
import editor
|
||||||
when not defined(NIPBOX_LITE):
|
import ../../libs/membrane/term # Phase 26: Visual Cortex
|
||||||
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
|
|
||||||
};
|
|
||||||
""".}
|
|
||||||
|
|
||||||
# Phase 30: Pledge Constants
|
# Phase 30: Pledge Constants
|
||||||
const
|
const
|
||||||
|
|
@ -261,7 +221,7 @@ proc cmd_cp*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577
|
# 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:
|
if fd_dest < 0:
|
||||||
print("cp: cannot create '" & dest & "'\n")
|
print("cp: cannot create '" & dest & "'\n")
|
||||||
discard lb.close(fd_src)
|
discard lb.close(fd_src)
|
||||||
|
|
@ -287,11 +247,11 @@ proc cmd_mv*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len < 2:
|
if args.len < 2:
|
||||||
print("Usage: mv <source> <dest>\n")
|
print("Usage: mv <source> <dest>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Step 1: Copy
|
# Step 1: Copy
|
||||||
print("[mv] Copying...\n")
|
print("[mv] Copying...\n")
|
||||||
discard cmd_cp(args, input)
|
discard cmd_cp(args, input)
|
||||||
|
|
||||||
# Step 2: Unlink (Not yet supported by Kernel)
|
# Step 2: Unlink (Not yet supported by Kernel)
|
||||||
print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n")
|
print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
@ -300,7 +260,7 @@ proc cmd_touch*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len < 1:
|
if args.len < 1:
|
||||||
print("Usage: touch <filename>\n")
|
print("Usage: touch <filename>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC
|
let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC
|
||||||
if fd >= 0:
|
if fd >= 0:
|
||||||
discard lb.close(fd)
|
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 =
|
proc cmd_write*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
## write <filename> <content>
|
## write <filename> <content>
|
||||||
## Uses USERLAND SFS (Block Valve architecture)
|
## Uses USERLAND SFS (Block Valve architecture)
|
||||||
when not defined(NIPBOX_LITE):
|
if args.len < 2:
|
||||||
if args.len < 2:
|
print("Usage: write <filename> <content>\n")
|
||||||
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")
|
|
||||||
return @[]
|
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:
|
else:
|
||||||
print("[nipbox] 'write' requires SFS (not available in lite mode)\n")
|
print("Error: Could not write to " & filename & "\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
proc cmd_read*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_read*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
## read <filename>
|
## read <filename>
|
||||||
## Uses USERLAND SFS (Block Valve architecture)
|
## Uses USERLAND SFS (Block Valve architecture)
|
||||||
when not defined(NIPBOX_LITE):
|
if args.len == 0:
|
||||||
if args.len == 0:
|
print("Usage: read <filename>\n")
|
||||||
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")
|
|
||||||
return @[]
|
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:
|
else:
|
||||||
print("[nipbox] 'read' requires SFS (not available in lite mode)\n")
|
print("Error: Could not open " & filename & "\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len == 0:
|
if args.len == 0:
|
||||||
|
|
@ -530,223 +482,239 @@ proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
return @[node]
|
return @[node]
|
||||||
|
|
||||||
proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
when not defined(NIPBOX_LITE):
|
if args.len < 2:
|
||||||
if args.len < 2:
|
print("Usage: http.download <ip:port/path> <outfile>\n")
|
||||||
print("Usage: http.download <ip:port/path> <outfile>\n")
|
return @[]
|
||||||
return @[]
|
|
||||||
|
|
||||||
let url_arg = args[0]
|
let url_arg = args[0]
|
||||||
let outfile = args[1]
|
let outfile = args[1]
|
||||||
|
|
||||||
var host_part = url_arg
|
var host_part = url_arg
|
||||||
var path_str = "/"
|
var path_str = "/"
|
||||||
|
|
||||||
let slash_pos = url_arg.find('/')
|
let slash_pos = url_arg.find('/')
|
||||||
if slash_pos != -1:
|
if slash_pos != -1:
|
||||||
host_part = url_arg[0..<slash_pos]
|
host_part = url_arg[0..<slash_pos]
|
||||||
path_str = url_arg[slash_pos..^1]
|
path_str = url_arg[slash_pos..^1]
|
||||||
|
|
||||||
let parts = host_part.split(':')
|
let parts = host_part.split(':')
|
||||||
if parts.len != 2:
|
if parts.len != 2:
|
||||||
print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
|
print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let ip_str = parts[0]
|
let ip_str = parts[0]
|
||||||
let port = uint16(parseInt(parts[1]))
|
let port = uint16(parseInt(parts[1]))
|
||||||
|
|
||||||
# Parse IP
|
# Parse IP
|
||||||
var ip_val: uint32 = 0
|
var ip_val: uint32 = 0
|
||||||
try:
|
try:
|
||||||
let p = ip_str.split('.')
|
let p = ip_str.split('.')
|
||||||
ip_val = (uint32(parseInt(p[0])) and 0xFF) or
|
ip_val = (uint32(parseInt(p[0])) and 0xFF) or
|
||||||
((uint32(parseInt(p[1])) and 0xFF) shl 8) or
|
((uint32(parseInt(p[1])) and 0xFF) shl 8) or
|
||||||
((uint32(parseInt(p[2])) and 0xFF) shl 16) or
|
((uint32(parseInt(p[2])) and 0xFF) shl 16) or
|
||||||
((uint32(parseInt(p[3])) and 0xFF) shl 24)
|
((uint32(parseInt(p[3])) and 0xFF) shl 24)
|
||||||
except:
|
except:
|
||||||
print("Error: Invalid IP\n")
|
print("Error: Invalid IP\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("[Download] Connecting to " & host_part & "...\n")
|
print("[Download] Connecting to " & host_part & "...\n")
|
||||||
let fd = lb.socket(2, 1, 0)
|
let fd = lb.socket(2, 1, 0)
|
||||||
if fd < 0: return @[]
|
if fd < 0: return @[]
|
||||||
|
|
||||||
# SockAddr setup
|
# SockAddr setup
|
||||||
var addr_buf: array[16, byte]
|
var addr_buf: array[16, byte]
|
||||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||||
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
||||||
|
|
||||||
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
||||||
print("Error: Connection Failed.\n")
|
print("Error: Connection Failed.\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Request
|
# Request
|
||||||
let req = "GET " & path_str & " HTTP/1.0\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n"
|
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:
|
if lb.send(cint(fd), cast[pointer](unsafeAddr req[0]), csize_t(req.len), 0) <= 0:
|
||||||
print("Error: Send Failed.\n")
|
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)
|
|
||||||
discard lb.close(cint(fd))
|
discard lb.close(cint(fd))
|
||||||
print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n")
|
|
||||||
return @[]
|
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 @[]
|
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
|
# Phase 37: HTTP Verification Tool
|
||||||
proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if args.len < 1:
|
if args.len < 1:
|
||||||
print("Usage: http.test <host>\n")
|
print("Usage: http <host>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let host = args[0]
|
let host = args[0]
|
||||||
print("Dialing " & host & ":80...\n")
|
print("Dialing " & host & ":80...\n")
|
||||||
|
|
||||||
let fd = lnet.net_dial_tcp(host, 80)
|
let fd = lnet.net_dial_tcp(host, 80)
|
||||||
if fd < 0:
|
if fd < 0:
|
||||||
print("Connection Failed! Error: " & $fd & "\n")
|
print("Connection Failed! Error: " & $fd & "\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("Connected! Sending GET request...\n")
|
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")
|
discard lnet.net_send(fd, "GET / HTTP/1.0\r\nHost: " & host & "\r\nConnection: close\r\n\r\n")
|
||||||
|
|
||||||
print("Waiting for response...\n")
|
print("Waiting for response...\n")
|
||||||
|
|
||||||
|
# Simple read loop
|
||||||
var total = 0
|
var total = 0
|
||||||
while true:
|
while true:
|
||||||
|
# lb.pump_membrane_stack()
|
||||||
let resp = lnet.net_recv(fd, 512)
|
let resp = lnet.net_recv(fd, 512)
|
||||||
if resp.len > 0:
|
if resp.len > 0:
|
||||||
print(resp)
|
print(resp)
|
||||||
total += resp.len
|
total += resp.len
|
||||||
else:
|
else:
|
||||||
|
# End of stream or empty poll
|
||||||
break
|
break
|
||||||
|
|
||||||
print("\n[HTTP] Closed. Total bytes: " & $total & "\n")
|
print("\n[HTTP] Closed. Total bytes: " & $total & "\n")
|
||||||
lnet.net_close(fd)
|
lnet.net_close(fd)
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData =
|
proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
print("[Server] Starting Nexus Web/1.0...\n")
|
print("[Server] Starting Nexus Web/1.0...\n")
|
||||||
|
|
||||||
let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80
|
let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80
|
||||||
|
|
||||||
let s = lb.socket(2, 1, 0)
|
let s = lb.socket(2, 1, 0)
|
||||||
if s < 0:
|
if s < 0:
|
||||||
print("Error: Socket creation failed.\n")
|
print("Error: Socket creation failed.\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Bind 0.0.0.0:port
|
# Bind 0.0.0.0:port
|
||||||
var addr_buf: array[16, byte]
|
var addr_buf: array[16, byte]
|
||||||
addr_buf[0] = 2 # AF_INET
|
addr_buf[0] = 2 # AF_INET
|
||||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||||
# IP 0.0.0.0 is default 0s
|
# IP 0.0.0.0 is default 0s
|
||||||
|
|
||||||
if lb.bind_socket(s, addr addr_buf[0], 16) < 0:
|
if lb.bind_socket(s, addr addr_buf[0], 16) < 0:
|
||||||
print("Error: Bind failed.\n")
|
print("Error: Bind failed.\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
if lb.listen(s, 1) < 0:
|
if lb.listen(s, 1) < 0:
|
||||||
print("Error: Listen failed.\n")
|
print("Error: Listen failed.\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("[Server] Listening on port " & $port & "...\n")
|
print("[Server] Listening on port " & $port & "...\n")
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
# Accept blocks and pumps stack
|
# Accept blocks and pumps stack
|
||||||
let client = lb.accept(s, nil, nil)
|
let client = lb.accept(s, nil, nil)
|
||||||
if client < 0:
|
if client < 0:
|
||||||
print("Error: Accept failed.\n")
|
print("Error: Accept failed.\n")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print("[Server] Client Connected (FD " & $client & ")\n")
|
print("[Server] Client Connected (FD " & $client & ")\n")
|
||||||
|
|
||||||
var buf: array[1024, char]
|
var buf: array[1024, char]
|
||||||
let n = lb.recv(client, addr buf[0], 1024, 0)
|
let n = lb.recv(client, addr buf[0], 1024, 0)
|
||||||
|
|
||||||
if n > 0:
|
if n > 0:
|
||||||
var req = ""
|
var req = ""
|
||||||
for i in 0..<n: req.add(buf[i])
|
for i in 0..<n: req.add(buf[i])
|
||||||
print("[Server] Request:\n" & req & "\n")
|
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"
|
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.send(client, unsafeAddr resp[0], uint64(resp.len), 0)
|
||||||
|
|
||||||
discard lb.close(client)
|
discard lb.close(client)
|
||||||
print("[Server] Client Closed.\n")
|
print("[Server] Client Closed.\n")
|
||||||
|
|
||||||
# Just handle one for testing? No, loop forever.
|
# Just handle one for testing? No, loop forever.
|
||||||
# But how to exit? Ctrl-C not implemented in NipBox yet?
|
# But how to exit? Ctrl-C not implemented in NipBox yet?
|
||||||
# We'll just run forever for MVP.
|
# We'll just run forever for MVP.
|
||||||
|
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData =
|
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 =
|
proc cmd_help*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
when defined(NIPBOX_LITE):
|
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
|
||||||
print("NipBox " & NIPBOX_VERSION & " [LITE] (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")
|
||||||
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")
|
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# --- DISPATCHER ---
|
# --- DISPATCHER ---
|
||||||
|
|
@ -844,16 +802,16 @@ proc dispatch_command(name: string, args: seq[string],
|
||||||
if args.len < 1:
|
if args.len < 1:
|
||||||
print("Usage: http <ip>\n")
|
print("Usage: http <ip>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let host = args[0]
|
let host = args[0]
|
||||||
print("[NipBox] Dialing " & host & ":80...\n")
|
print("[NipBox] Dialing " & host & ":80...\n")
|
||||||
|
|
||||||
# Use libc.socket/connect (Phase 38 Shim)
|
# Use libc.socket/connect (Phase 38 Shim)
|
||||||
let fd = lb.socket(2, 1, 0)
|
let fd = lb.socket(2, 1, 0)
|
||||||
if fd < 0:
|
if fd < 0:
|
||||||
print("Socket Error\n")
|
print("Socket Error\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Parse IP (Quick hack for 10.0.2.2)
|
# Parse IP (Quick hack for 10.0.2.2)
|
||||||
# We need proper parsing but let's assume raw IP for MVP
|
# We need proper parsing but let's assume raw IP for MVP
|
||||||
var ip_val: uint32 = 0
|
var ip_val: uint32 = 0
|
||||||
|
|
@ -866,28 +824,28 @@ proc dispatch_command(name: string, args: seq[string],
|
||||||
except:
|
except:
|
||||||
print("Error: Invalid IP format (use A.B.C.D)\n")
|
print("Error: Invalid IP format (use A.B.C.D)\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
# Construct SockAddrIn (Layout must match libc.connect hack)
|
# Construct SockAddrIn (Layout must match libc.connect hack)
|
||||||
var addr_buf: array[16, byte]
|
var addr_buf: array[16, byte]
|
||||||
# Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE?
|
# Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE?
|
||||||
# 80 = 0x0050. LE in mem: 50 00.
|
# 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.
|
# connect() shim expects us to pass port as uint16.
|
||||||
# But the shim casts addr_ptr+2 to uint16*.
|
# But the shim casts addr_ptr+2 to uint16*.
|
||||||
# If we write 80 there, it reads 80.
|
# If we write 80 there, it reads 80.
|
||||||
|
|
||||||
let port: uint16 = 80
|
let port: uint16 = 80
|
||||||
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
copyMem(addr addr_buf[2], unsafeAddr port, 2)
|
||||||
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
|
||||||
|
|
||||||
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
if lb.connect(fd, addr addr_buf[0], 16) < 0:
|
||||||
print("Connect Failed\n")
|
print("Connect Failed\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
print("[NipBox] Connected! Sending Payload...\n")
|
print("[NipBox] Connected! Sending Payload...\n")
|
||||||
let req = "GET / HTTP/1.0\r\n\r\n"
|
let req = "GET / HTTP/1.0\r\n\r\n"
|
||||||
discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0)
|
discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0)
|
||||||
|
|
||||||
print("[NipBox] Waiting for Data...\n")
|
print("[NipBox] Waiting for Data...\n")
|
||||||
var buf: array[1024, char]
|
var buf: array[1024, char]
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -898,7 +856,7 @@ proc dispatch_command(name: string, args: seq[string],
|
||||||
print(s)
|
print(s)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
print("\n[NipBox] Done.\n")
|
print("\n[NipBox] Done.\n")
|
||||||
return @[]
|
return @[]
|
||||||
of "http.test": return cmd_http_test(args, input)
|
of "http.test": return cmd_http_test(args, input)
|
||||||
|
|
@ -1109,7 +1067,7 @@ proc run_script(path: string) =
|
||||||
proc nipbox_main*() =
|
proc nipbox_main*() =
|
||||||
# DIAGNOSTIC: Very first thing - prove we're executing
|
# DIAGNOSTIC: Very first thing - prove we're executing
|
||||||
print("[NIPBOX] Entry point reached!\n")
|
print("[NIPBOX] Entry point reached!\n")
|
||||||
|
|
||||||
# Phase 30: Pledge Safety
|
# Phase 30: Pledge Safety
|
||||||
# NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones
|
# 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
|
# For now, we PLEDGE_ALL because the shell needs to explore
|
||||||
|
|
@ -1129,7 +1087,7 @@ proc nipbox_main*() =
|
||||||
# Phase 38: Boot Script
|
# Phase 38: Boot Script
|
||||||
run_script("/init.nsh")
|
run_script("/init.nsh")
|
||||||
print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n")
|
print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n")
|
||||||
|
|
||||||
|
|
||||||
print("\x1b[1;33mroot@nexus:# \x1b[0m")
|
print("\x1b[1;33mroot@nexus:# \x1b[0m")
|
||||||
var inputBuffer: string = ""
|
var inputBuffer: string = ""
|
||||||
|
|
|
||||||
130
run.sh
130
run.sh
|
|
@ -1,122 +1,22 @@
|
||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
# ============================================================================
|
# Rumpk QEMU Boot Script
|
||||||
# Rumpk QEMU Runner — Boot, Test, or Interactive Shell
|
|
||||||
# ============================================================================
|
|
||||||
# Usage:
|
|
||||||
# ./run.sh # Interactive RISC-V session
|
|
||||||
# ./run.sh test # Automated boot test (10s timeout, check output)
|
|
||||||
# ./run.sh [riscv64|aarch64] # Architecture selection (future)
|
|
||||||
#
|
|
||||||
# Exit codes:
|
|
||||||
# 0 = Boot successful (test mode: all checks passed)
|
|
||||||
# 1 = Boot failed or timeout
|
|
||||||
# ============================================================================
|
|
||||||
set -euo pipefail
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
|
|
||||||
MODE="${1:-interactive}"
|
RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
ARCH="${2:-riscv64}"
|
KERNEL="$RUMPK_DIR/zig-out/bin/rumpk.elf"
|
||||||
TIMEOUT=25
|
|
||||||
|
|
||||||
KERNEL="zig-out/bin/rumpk.elf"
|
|
||||||
DISK="build/disk.img"
|
|
||||||
|
|
||||||
# Fallback to build/ location if zig-out doesn't have it
|
|
||||||
if [ ! -f "$KERNEL" ]; then
|
|
||||||
KERNEL="build/rumpk-riscv64.elf"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$KERNEL" ]; then
|
if [ ! -f "$KERNEL" ]; then
|
||||||
echo "ERROR: 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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$ARCH" in
|
echo "🚀 Booting Rumpk..."
|
||||||
riscv64)
|
echo " Kernel: $KERNEL"
|
||||||
QEMU_CMD=(
|
echo ""
|
||||||
qemu-system-riscv64
|
|
||||||
-M virt -m 512M -nographic
|
|
||||||
-kernel "$KERNEL"
|
|
||||||
)
|
|
||||||
# Add disk if it exists
|
|
||||||
if [ -f "$DISK" ]; then
|
|
||||||
QEMU_CMD+=(
|
|
||||||
-drive "if=none,format=raw,file=$DISK,id=blk0"
|
|
||||||
-device virtio-blk-pci,drive=blk0
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
QEMU_CMD+=(
|
|
||||||
-device virtio-net-pci,netdev=net0
|
|
||||||
-netdev user,id=net0
|
|
||||||
)
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "ERROR: Architecture '$ARCH' not yet supported"
|
|
||||||
echo "Supported: riscv64"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ "$MODE" = "test" ]; then
|
qemu-system-riscv64 \
|
||||||
echo "=== Rumpk Boot Test ($ARCH, ${TIMEOUT}s timeout) ==="
|
-M virt \
|
||||||
|
-cpu max \
|
||||||
LOGFILE=$(mktemp /tmp/rumpk-boot-XXXXXX.log)
|
-m 512M \
|
||||||
trap "rm -f $LOGFILE" EXIT
|
-nographic \
|
||||||
|
-kernel "$KERNEL"
|
||||||
timeout "$TIMEOUT" "${QEMU_CMD[@]}" > "$LOGFILE" 2>&1 || true
|
|
||||||
|
|
||||||
# Boot verification checks
|
|
||||||
PASS=0
|
|
||||||
FAIL=0
|
|
||||||
TOTAL=0
|
|
||||||
|
|
||||||
check() {
|
|
||||||
local name="$1"
|
|
||||||
local pattern="$2"
|
|
||||||
TOTAL=$((TOTAL + 1))
|
|
||||||
if grep -q "$pattern" "$LOGFILE"; then
|
|
||||||
echo " PASS: $name"
|
|
||||||
PASS=$((PASS + 1))
|
|
||||||
else
|
|
||||||
echo " FAIL: $name"
|
|
||||||
FAIL=$((FAIL + 1))
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check "OpenSBI handoff" "Boot HART ID"
|
|
||||||
check "UART proof-of-life" "UART.*Loopback Test: PASS"
|
|
||||||
check "Zig HAL entry" "zig_entry reached"
|
|
||||||
check "Nim L1 handoff" "Handing off to Nim L1"
|
|
||||||
check "ION pool ready" "ION.*Pool Ready"
|
|
||||||
check "VirtIO-Net init" "VirtIO.*Initialization Complete"
|
|
||||||
check "CSpace init" "Capability system initialized"
|
|
||||||
check "Truth Ledger init" "System Truth Ledger initialized"
|
|
||||||
check "Fiber dispatcher" "Multi-Fiber Dispatcher starting"
|
|
||||||
check "NetSwitch online" "Traffic Engine Online"
|
|
||||||
check "LFS mounted" "Sovereign filesystem mounted"
|
|
||||||
check "Init loaded" "NIPBOX.*Entry point reached"
|
|
||||||
check "Shell spawned" "SOVEREIGN SUPERVISOR"
|
|
||||||
check "DHCP IP acquired" "IP STATUS CHANGE"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "=== Result: $PASS/$TOTAL passed, $FAIL failed ==="
|
|
||||||
|
|
||||||
if [ "$FAIL" -gt 0 ]; then
|
|
||||||
echo ""
|
|
||||||
echo "Boot log saved: $LOGFILE"
|
|
||||||
trap - EXIT # Don't delete on failure
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
|
|
||||||
elif [ "$MODE" = "interactive" ]; then
|
|
||||||
echo "=== Rumpk Interactive Session ($ARCH) ==="
|
|
||||||
echo " Kernel: $KERNEL"
|
|
||||||
echo " Exit: Ctrl-A X"
|
|
||||||
echo ""
|
|
||||||
exec "${QEMU_CMD[@]}"
|
|
||||||
else
|
|
||||||
echo "Usage: ./run.sh [interactive|test] [riscv64]"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ BUILD_DIR="../../build"
|
||||||
|
|
||||||
zig cc -target $TARGET -nostdlib -static -T $LINKER_SCRIPT \
|
zig cc -target $TARGET -nostdlib -static -T $LINKER_SCRIPT \
|
||||||
$BUILD_DIR/subject_entry.o \
|
$BUILD_DIR/subject_entry.o \
|
||||||
|
$BUILD_DIR/stubs_user.o \
|
||||||
$BUILD_DIR/libc_shim.o \
|
$BUILD_DIR/libc_shim.o \
|
||||||
stubs_mksh.o \
|
stubs_mksh.o \
|
||||||
$OBJS \
|
$OBJS \
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -13,76 +13,15 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <termios.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
|
// Globals
|
||||||
char **environ = NULL;
|
char **environ = NULL;
|
||||||
|
|
||||||
// Safe Userland Allocator (Bump Pointer with Headers)
|
extern void console_write(const void* p, size_t len);
|
||||||
#define HEAP_SIZE (32 * 1024 * 1024)
|
|
||||||
static char heap_memory[HEAP_SIZE];
|
|
||||||
static size_t heap_ptr = 0;
|
|
||||||
|
|
||||||
typedef struct {
|
extern long syscall(long nr, long a0, long a1, long a2);
|
||||||
size_t size;
|
long k_handle_syscall(long nr, long a0, long a1, long a2) {
|
||||||
size_t magic;
|
return syscall(nr, a0, a1, a2);
|
||||||
} BlockHeader;
|
|
||||||
#define ALLOC_MAGIC 0xCAFEBABE
|
|
||||||
|
|
||||||
void *malloc(size_t size) {
|
|
||||||
if (size == 0) return NULL;
|
|
||||||
|
|
||||||
// Align total size (header + data) to 16 bytes
|
|
||||||
size_t required = size + sizeof(BlockHeader);
|
|
||||||
size_t aligned_total = (required + 15) & ~15;
|
|
||||||
|
|
||||||
if (heap_ptr + aligned_total > HEAP_SIZE) return NULL;
|
|
||||||
|
|
||||||
BlockHeader *hdr = (BlockHeader *)&heap_memory[heap_ptr];
|
|
||||||
hdr->size = size;
|
|
||||||
hdr->magic = ALLOC_MAGIC;
|
|
||||||
|
|
||||||
void *ptr = (void *)((char *)hdr + sizeof(BlockHeader));
|
|
||||||
heap_ptr += aligned_total;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(void *ptr) {
|
|
||||||
// No-op bump allocator
|
|
||||||
}
|
|
||||||
|
|
||||||
void *realloc(void *ptr, size_t size) {
|
|
||||||
if (!ptr) return malloc(size);
|
|
||||||
if (size == 0) return NULL; // Standard says free.. or return NULL? mksh expects NULL or ptr.
|
|
||||||
|
|
||||||
// Get header
|
|
||||||
BlockHeader *hdr = (BlockHeader *)((char *)ptr - sizeof(BlockHeader));
|
|
||||||
if (hdr->magic != ALLOC_MAGIC) {
|
|
||||||
// Corrupted ptr? return NULL or fail.
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimization: If it's the LAST block, simple extend? (Not implemented for simplicity)
|
|
||||||
|
|
||||||
void *new_ptr = malloc(size);
|
|
||||||
if (!new_ptr) return NULL;
|
|
||||||
|
|
||||||
size_t copy_size = (hdr->size < size) ? hdr->size : size;
|
|
||||||
memcpy(new_ptr, ptr, copy_size);
|
|
||||||
|
|
||||||
return new_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *calloc(size_t nmemb, size_t size) {
|
|
||||||
size_t total = nmemb * size;
|
|
||||||
void *ptr = malloc(total);
|
|
||||||
if (ptr) memset(ptr, 0, total);
|
|
||||||
return ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stubs
|
// Stubs
|
||||||
|
|
@ -93,14 +32,7 @@ int stat(const char *path, struct stat *buf) { return 0; }
|
||||||
int sigemptyset(sigset_t *set) { return 0; }
|
int sigemptyset(sigset_t *set) { return 0; }
|
||||||
int sigaddset(sigset_t *set, int signum) { return 0; }
|
int sigaddset(sigset_t *set, int signum) { return 0; }
|
||||||
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { return 0; }
|
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { return 0; }
|
||||||
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) {
|
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { return 0; }
|
||||||
if (oldact) {
|
|
||||||
// Initialize to safe defaults - no handler installed
|
|
||||||
memset(oldact, 0, sizeof(struct sigaction));
|
|
||||||
oldact->sa_handler = SIG_DFL; // Default handler
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int sigsuspend(const sigset_t *mask) { return -1; }
|
int sigsuspend(const sigset_t *mask) { return -1; }
|
||||||
int kill(pid_t pid, int sig) { return 0; }
|
int kill(pid_t pid, int sig) { return 0; }
|
||||||
unsigned int alarm(unsigned int seconds) { return 0; }
|
unsigned int alarm(unsigned int seconds) { return 0; }
|
||||||
|
|
@ -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 close(int fd) { return 0; } // In libc.nim
|
||||||
int pipe(int pipefd[2]) { return -1; }
|
int pipe(int pipefd[2]) { return -1; }
|
||||||
int dup2(int oldfd, int newfd) { return newfd; }
|
int dup2(int oldfd, int newfd) { return newfd; }
|
||||||
extern void console_write(const void* p, unsigned long len);
|
int fcntl(int fd, int cmd, ...) { return 0; }
|
||||||
static void ksh_debug(const char* s) {
|
|
||||||
console_write(s, (unsigned long)strlen(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
int fcntl(int fd, int cmd, ...) {
|
|
||||||
ksh_debug("[mksh] fcntl called\n");
|
|
||||||
va_list args;
|
|
||||||
va_start(args, cmd);
|
|
||||||
long arg = va_arg(args, long);
|
|
||||||
va_end(args);
|
|
||||||
int res = (int)syscall(0x206, (long)fd, (long)cmd, arg);
|
|
||||||
ksh_debug("[mksh] fcntl returning\n");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
int ioctl(int fd, unsigned long request, ...) { return 0; }
|
int ioctl(int fd, unsigned long request, ...) { return 0; }
|
||||||
|
|
||||||
// int execve(const char *pathname, char *const argv[], char *const envp[]) { return -1; } // In clib.c
|
// int execve(const char *pathname, char *const argv[], char *const envp[]) { return -1; } // In clib.c
|
||||||
|
|
@ -147,26 +65,7 @@ int seteuid(uid_t uid) { return 0; }
|
||||||
int setegid(gid_t gid) { return 0; }
|
int setegid(gid_t gid) { return 0; }
|
||||||
int setpgid(pid_t pid, pid_t pgid) { return 0; }
|
int setpgid(pid_t pid, pid_t pgid) { return 0; }
|
||||||
|
|
||||||
int tcgetattr(int fd, struct termios *termios_p) {
|
int tcgetattr(int fd, struct termios *termios_p) { return 0; }
|
||||||
if (termios_p) {
|
|
||||||
// Initialize with safe defaults (using numeric values to avoid missing constants)
|
|
||||||
memset(termios_p, 0, sizeof(struct termios));
|
|
||||||
// Set basic flags for canonical mode
|
|
||||||
termios_p->c_iflag = 0x0100; // ICRNL
|
|
||||||
termios_p->c_oflag = 0x0001 | 0x0004; // OPOST | ONLCR
|
|
||||||
termios_p->c_cflag = 0x0030 | 0x0080; // CS8 | CREAD
|
|
||||||
termios_p->c_lflag = ISIG | ICANON | ECHO; // These should be defined
|
|
||||||
// Set control characters
|
|
||||||
termios_p->c_cc[VINTR] = 3; // Ctrl-C
|
|
||||||
termios_p->c_cc[VQUIT] = 28; // Ctrl-backslash
|
|
||||||
termios_p->c_cc[VERASE] = 127; // DEL
|
|
||||||
termios_p->c_cc[VKILL] = 21; // Ctrl-U
|
|
||||||
termios_p->c_cc[VEOF] = 4; // Ctrl-D
|
|
||||||
termios_p->c_cc[VMIN] = 1;
|
|
||||||
termios_p->c_cc[VTIME] = 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) { return 0; }
|
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) { return 0; }
|
||||||
pid_t tcgetpgrp(int fd) { return 1; }
|
pid_t tcgetpgrp(int fd) { return 1; }
|
||||||
int tcsetpgrp(int fd, pid_t pgrp) { return 0; }
|
int tcsetpgrp(int fd, pid_t pgrp) { return 0; }
|
||||||
|
|
@ -229,56 +128,7 @@ mode_t umask(mode_t mask) { return 0; }
|
||||||
|
|
||||||
int c_ulimit(const char **wp) { return 0; }
|
int c_ulimit(const char **wp) { return 0; }
|
||||||
|
|
||||||
int setjmp(jmp_buf env) {
|
void longjmp(jmp_buf env, int val) { while(1); }
|
||||||
__asm__ volatile (
|
int setjmp(jmp_buf env) { return 0; }
|
||||||
"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) {
|
void exit(int status) { while(1); }
|
||||||
__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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue