Compare commits
52 Commits
8b14b16317
...
eaf000e5ec
| Author | SHA1 | Date |
|---|---|---|
|
|
eaf000e5ec | |
|
|
225d08908b | |
|
|
e3246e8509 | |
|
|
e8b58f375a | |
|
|
84c3345595 | |
|
|
a38bc523a8 | |
|
|
49c58fbd94 | |
|
|
8d64fe2180 | |
|
|
0c598ce0bd | |
|
|
8d4b581519 | |
|
|
011e0b699e | |
|
|
7207282236 | |
|
|
11db62ea8c | |
|
|
5c57341b81 | |
|
|
f5f9f0bf6d | |
|
|
1f164eca59 | |
|
|
6bc5804e48 | |
|
|
eaf753c70c | |
|
|
0949ea1187 | |
|
|
fd8e3beb84 | |
|
|
49dd5382b9 | |
|
|
b0e2dfa20e | |
|
|
eedf05fadf | |
|
|
b480f14bb5 | |
|
|
4c91aa7f14 | |
|
|
77b4cb55c7 | |
|
|
96ee0a0112 | |
|
|
068fc732a6 | |
|
|
a59a4cf9db | |
|
|
8b109652ab | |
|
|
3779197eb9 | |
|
|
bf427290f1 | |
|
|
72891287fb | |
|
|
4cec2d8c25 | |
|
|
6e78b7f458 | |
|
|
bd03bed91f | |
|
|
9f490297d2 | |
|
|
641847ba47 | |
|
|
1b4facd86b | |
|
|
4e0e9ed467 | |
|
|
ccaa10c509 | |
|
|
2e772051f8 | |
|
|
de6a7499fd | |
|
|
c6e569afe8 | |
|
|
3a907439fe | |
|
|
9733300d3d | |
|
|
4f1ad1f3be | |
|
|
64380de4a7 | |
|
|
e367dd8380 | |
|
|
c8a679b067 | |
|
|
5a607266a5 | |
|
|
2a1af03e28 |
|
|
@ -9,6 +9,62 @@
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -18,44 +74,51 @@ 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] Initializing Membrane Network Stack...\n"))
|
print(cstring("[INIT] PHASE_42_VERIFY: Membrane Network Stack...\\n"))
|
||||||
membrane_init()
|
# DISABLED: Network stack requires LwIP
|
||||||
|
# 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 ---
|
# # --- DNS PHASE ---
|
||||||
print(cstring("\n[TEST] ══════════════════════════════════════\n"))
|
# print(cstring("\n[TEST] ══════════════════════════════════════\n"))
|
||||||
print(cstring("[TEST] DNS Resolution: google.com\n"))
|
# print(cstring("[TEST] DNS Resolution: google.com\n"))
|
||||||
print(cstring("[TEST] ══════════════════════════════════════\n\n"))
|
# print(cstring("[TEST] ══════════════════════════════════════\n\n"))
|
||||||
|
|
||||||
var res: ptr AddrInfo
|
# type
|
||||||
for attempt in 1..5:
|
# AddrInfo {.importc: "struct addrinfo", header: "<netdb.h>".} = object
|
||||||
print(cstring("[TEST] Resolving google.com (Attempt "))
|
|
||||||
# (Simplified number printing not available, just loop)
|
|
||||||
|
|
||||||
if getaddrinfo("google.com", nil, nil, addr res) == 0:
|
# proc getaddrinfo(node: cstring, service: cstring, hints: pointer, res: ptr ptr AddrInfo): cint {.importc, header: "<netdb.h>".}
|
||||||
print(cstring(") -> SUCCESS!\n"))
|
# proc freeaddrinfo(res: ptr AddrInfo) {.importc, header: "<netdb.h>".}
|
||||||
freeaddrinfo(res)
|
|
||||||
break
|
# var res: ptr AddrInfo
|
||||||
else:
|
# for attempt in 1..5:
|
||||||
print(cstring(") -> FAILED. Waiting 5s...\n"))
|
# print(cstring("[TEST] Resolving google.com (Attempt "))
|
||||||
for j in 1..50:
|
# # (Simplified number printing not available, just loop)
|
||||||
pump_membrane_stack()
|
|
||||||
discard syscall(0x65, 100000000'u64) # 100ms
|
# 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 =
|
||||||
|
|
@ -68,10 +131,10 @@ proc main() =
|
||||||
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()
|
# pump_membrane_stack() # DISABLED: Requires LwIP
|
||||||
loop_count += 1
|
loop_count += 1
|
||||||
if loop_count mod 100 == 0:
|
if loop_count mod 0x100000 == 0: # Every ~1M iterations
|
||||||
print(cstring("[INIT] Heartbeat\n"))
|
discard syscall(0x65, 1000000000'u64) # 1s yield
|
||||||
discard syscall(0x65, 100000000'u64) # 100ms
|
discard syscall(0x65, 100000000'u64) # 100ms
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@ SECTIONS
|
||||||
*(.data.*)
|
*(.data.*)
|
||||||
} > RAM
|
} > RAM
|
||||||
|
|
||||||
|
.nexus.manifest : {
|
||||||
|
KEEP(*(.nexus.manifest))
|
||||||
|
} > RAM
|
||||||
|
|
||||||
.bss : {
|
.bss : {
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
__bss_start = .;
|
__bss_start = .;
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,14 @@ _start:
|
||||||
2:
|
2:
|
||||||
fence rw, rw
|
fence rw, rw
|
||||||
|
|
||||||
# Arguments (argc, argv) are already in a0, a1 from Kernel
|
# Valid Args from Stack (Linux ABI)
|
||||||
# sp is already pointing to argc from Kernel
|
ld a0, 0(sp) # argc
|
||||||
|
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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
@ -7,15 +7,16 @@
|
||||||
|
|
||||||
//! Rumpk Boot Header
|
//! Rumpk Boot Header
|
||||||
//!
|
//!
|
||||||
//! Defines the Multiboot2 header for GRUB/QEMU and the bare-metal entry point.
|
//! Architecture-dispatched entry point for bare-metal boot.
|
||||||
//! Handles BSS clearing and stack initialization before jumping to the Nim kernel.
|
//! Handles BSS clearing and stack initialization before jumping to HAL init.
|
||||||
//!
|
//!
|
||||||
//! 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)
|
// Multiboot2 Header (for GRUB/QEMU — x86 only)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
const MULTIBOOT2_MAGIC: u32 = 0xe85250d6;
|
const MULTIBOOT2_MAGIC: u32 = 0xe85250d6;
|
||||||
|
|
@ -43,17 +44,23 @@ export const multiboot2_header linksection(".multiboot2") = Multiboot2Header{
|
||||||
};
|
};
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Entry Point
|
// Arch-Specific HAL Entry Points
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
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 {
|
||||||
// Clear BSS, set up stack, then jump to RISC-V Init
|
switch (builtin.cpu.arch) {
|
||||||
|
.riscv64 => {
|
||||||
asm volatile (
|
asm volatile (
|
||||||
\\ // Set up stack
|
\\ // Set up stack
|
||||||
\\ la sp, kernel_stack
|
\\ la sp, kernel_stack
|
||||||
|
|
@ -69,7 +76,7 @@ export fn _start() callconv(.naked) noreturn {
|
||||||
\\ 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
|
||||||
|
|
@ -78,4 +85,51 @@ export fn _start() callconv(.naked) noreturn {
|
||||||
:
|
:
|
||||||
: [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,31 +5,111 @@
|
||||||
// This file is part of the Nexus Commonwealth.
|
// This file is part of the Nexus Commonwealth.
|
||||||
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
||||||
|
|
||||||
//! Rumpk Build System
|
//! Rumpk Build System — Unified Pipeline
|
||||||
//!
|
//!
|
||||||
//! Orchestrates L0 (Zig) and L1 (Nim) compilation.
|
//! Single command builds everything: Nim kernel + Zig HAL + LwIP + link.
|
||||||
//! Builds the Hardware Abstraction Layer as a static library.
|
//!
|
||||||
|
//! Usage:
|
||||||
|
//! zig build # Build kernel (default: riscv64)
|
||||||
|
//! zig build -Darch=aarch64 # ARM64 target
|
||||||
|
//! zig build -Darch=x86_64 # AMD64 target
|
||||||
|
//! zig build nim # Recompile Nim kernel sources only
|
||||||
|
//! zig build test-lwf # Run LWF tests (native host)
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Arch = enum { riscv64, aarch64, x86_64 };
|
||||||
|
|
||||||
|
/// Per-architecture build configuration.
|
||||||
|
const ArchConfig = struct {
|
||||||
|
cpu_arch: std.Target.Cpu.Arch,
|
||||||
|
code_model: std.builtin.CodeModel,
|
||||||
|
switch_asm: []const u8,
|
||||||
|
linker_script: []const u8,
|
||||||
|
/// Arch name for build_nim.sh
|
||||||
|
nim_arch: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn archConfig(arch: Arch) ArchConfig {
|
||||||
|
return switch (arch) {
|
||||||
|
.riscv64 => .{
|
||||||
|
.cpu_arch = .riscv64,
|
||||||
|
.code_model = .medany,
|
||||||
|
.switch_asm = "hal/arch/riscv64/switch.S",
|
||||||
|
.linker_script = "boot/linker.ld",
|
||||||
|
.nim_arch = "riscv64",
|
||||||
|
},
|
||||||
|
.aarch64 => .{
|
||||||
|
.cpu_arch = .aarch64,
|
||||||
|
.code_model = .default,
|
||||||
|
.switch_asm = "hal/arch/aarch64/switch.S",
|
||||||
|
.linker_script = "boot/linker_aarch64.ld",
|
||||||
|
.nim_arch = "aarch64",
|
||||||
|
},
|
||||||
|
.x86_64 => .{
|
||||||
|
.cpu_arch = .x86_64,
|
||||||
|
.code_model = .kernel,
|
||||||
|
.switch_asm = "hal/arch/x86_64/switch.S",
|
||||||
|
.linker_script = "boot/linker_x86_64.ld",
|
||||||
|
.nim_arch = "x86_64",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply freestanding kernel settings to a module.
|
||||||
|
fn applyKernelSettings(mod: *std.Build.Module, code_model: std.builtin.CodeModel) void {
|
||||||
|
mod.red_zone = false;
|
||||||
|
mod.stack_check = false;
|
||||||
|
mod.code_model = code_model;
|
||||||
|
mod.sanitize_thread = false;
|
||||||
|
mod.single_threaded = true;
|
||||||
|
mod.strip = true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
const target = b.standardTargetOptions(.{});
|
// =========================================================
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
// Target Selection: -Darch=riscv64|aarch64|x86_64
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
const arch = b.option(Arch, "arch", "Target architecture (default: riscv64)") orelse .riscv64;
|
||||||
|
const config = archConfig(arch);
|
||||||
|
|
||||||
|
var query: std.Target.Query = .{
|
||||||
|
.cpu_arch = config.cpu_arch,
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .none,
|
||||||
|
};
|
||||||
|
// Disable NEON/FP in kernel code generation to avoid unaligned SIMD stores
|
||||||
|
if (config.cpu_arch == .aarch64) {
|
||||||
|
query.cpu_features_sub = std.Target.aarch64.featureSet(&.{.neon});
|
||||||
|
}
|
||||||
|
const target = b.resolveTargetQuery(query);
|
||||||
|
|
||||||
|
// HEPHAESTUS DECREE: Force ReleaseSmall. A kernel runs naked.
|
||||||
|
const optimize: std.builtin.OptimizeMode = .ReleaseSmall;
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Step: Nim Kernel Compilation (nim -> C -> .o)
|
||||||
|
// =========================================================
|
||||||
|
// Calls build_nim.sh which handles:
|
||||||
|
// 1. nim c --compileOnly -> generates C
|
||||||
|
// 2. zig cc -> cross-compiles C to .o
|
||||||
|
// Incremental: only recompiles changed files.
|
||||||
|
|
||||||
|
const nim_step = b.addSystemCommand(&.{ "./build_nim.sh", config.nim_arch });
|
||||||
|
const nim_build_step = b.step("nim", "Recompile Nim kernel sources (nim -> C -> .o)");
|
||||||
|
nim_build_step.dependOn(&nim_step.step);
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// L0: Hardware Abstraction Layer (Zig)
|
// L0: Hardware Abstraction Layer (Zig)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
// NOTE(Build): Zig 0.15.x API - using addLibrary with static linkage
|
|
||||||
const hal_mod = b.createModule(.{
|
const hal_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("hal/abi.zig"),
|
.root_source_file = b.path("hal/abi.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
// Freestanding kernel - no libc, no red zone, no stack checks
|
applyKernelSettings(hal_mod, config.code_model);
|
||||||
hal_mod.red_zone = false;
|
|
||||||
hal_mod.stack_check = false;
|
|
||||||
hal_mod.code_model = .medany;
|
|
||||||
|
|
||||||
const hal = b.addLibrary(.{
|
const hal = b.addLibrary(.{
|
||||||
.name = "rumpk_hal",
|
.name = "rumpk_hal",
|
||||||
|
|
@ -39,15 +119,6 @@ pub fn build(b: *std.Build) void {
|
||||||
|
|
||||||
b.installArtifact(hal);
|
b.installArtifact(hal);
|
||||||
|
|
||||||
// TODO(Build): Microui needs stdio.h stubs for freestanding.
|
|
||||||
// Re-enable after creating libs/microui/stdio_stub.h
|
|
||||||
// Microui Integration (Phase 3.5b)
|
|
||||||
// hal_mod.addIncludePath(b.path("libs/microui"));
|
|
||||||
// hal_mod.addCSourceFile(.{
|
|
||||||
// .file = b.path("libs/microui/microui.c"),
|
|
||||||
// .flags = &.{"-std=c99"},
|
|
||||||
// });
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Boot: Entry Point (Assembly + Zig)
|
// Boot: Entry Point (Assembly + Zig)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -57,38 +128,230 @@ pub fn build(b: *std.Build) void {
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
boot_mod.red_zone = false;
|
applyKernelSettings(boot_mod, config.code_model);
|
||||||
boot_mod.stack_check = false;
|
|
||||||
boot_mod.code_model = .medany;
|
|
||||||
|
|
||||||
const boot = b.addObject(.{
|
const boot = b.addObject(.{
|
||||||
.name = "boot",
|
.name = "boot",
|
||||||
.root_module = boot_mod,
|
.root_module = boot_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// LwIP: Lightweight TCP/IP Stack
|
||||||
|
// =========================================================
|
||||||
|
// Compiled as C objects with freestanding flags.
|
||||||
|
// ubsan disabled per-file to avoid runtime dependency.
|
||||||
|
|
||||||
|
const lwip_base = "libs/membrane/external/lwip/src/";
|
||||||
|
const lwip_srcs = [_][]const u8{
|
||||||
|
// Core
|
||||||
|
lwip_base ++ "core/init.c",
|
||||||
|
lwip_base ++ "core/def.c",
|
||||||
|
lwip_base ++ "core/dns.c",
|
||||||
|
lwip_base ++ "core/inet_chksum.c",
|
||||||
|
lwip_base ++ "core/ip.c",
|
||||||
|
lwip_base ++ "core/mem.c",
|
||||||
|
lwip_base ++ "core/memp.c",
|
||||||
|
lwip_base ++ "core/netif.c",
|
||||||
|
lwip_base ++ "core/pbuf.c",
|
||||||
|
lwip_base ++ "core/raw.c",
|
||||||
|
lwip_base ++ "core/sys.c",
|
||||||
|
lwip_base ++ "core/tcp.c",
|
||||||
|
lwip_base ++ "core/tcp_in.c",
|
||||||
|
lwip_base ++ "core/tcp_out.c",
|
||||||
|
lwip_base ++ "core/timeouts.c",
|
||||||
|
lwip_base ++ "core/udp.c",
|
||||||
|
// IPv4
|
||||||
|
lwip_base ++ "core/ipv4/autoip.c",
|
||||||
|
lwip_base ++ "core/ipv4/dhcp.c",
|
||||||
|
lwip_base ++ "core/ipv4/etharp.c",
|
||||||
|
lwip_base ++ "core/ipv4/icmp.c",
|
||||||
|
lwip_base ++ "core/ipv4/ip4.c",
|
||||||
|
lwip_base ++ "core/ipv4/ip4_addr.c",
|
||||||
|
lwip_base ++ "core/ipv4/ip4_frag.c",
|
||||||
|
// Netif
|
||||||
|
lwip_base ++ "netif/ethernet.c",
|
||||||
|
// SysArch (Rumpk integration)
|
||||||
|
"libs/membrane/sys_arch.c",
|
||||||
|
};
|
||||||
|
|
||||||
|
const lwip_flags: []const []const u8 = switch (arch) {
|
||||||
|
.riscv64 => &.{
|
||||||
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
||||||
|
"-DNO_SYS=1", "-mcmodel=medany", "-Icore",
|
||||||
|
"-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
||||||
|
},
|
||||||
|
.aarch64 => &.{
|
||||||
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
||||||
|
"-DNO_SYS=1", "-Icore", "-Ilibs/membrane",
|
||||||
|
"-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
||||||
|
},
|
||||||
|
.x86_64 => &.{
|
||||||
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
||||||
|
"-DNO_SYS=1", "-mcmodel=kernel", "-Icore",
|
||||||
|
"-Ilibs/membrane", "-Ilibs/membrane/include", "-Ilibs/membrane/external/lwip/src/include",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const liblwip = b.addLibrary(.{
|
||||||
|
.name = "lwip",
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}),
|
||||||
|
.linkage = .static,
|
||||||
|
});
|
||||||
|
liblwip.root_module.sanitize_thread = false;
|
||||||
|
liblwip.root_module.red_zone = false;
|
||||||
|
liblwip.root_module.single_threaded = true;
|
||||||
|
|
||||||
|
for (lwip_srcs) |src| {
|
||||||
|
liblwip.addCSourceFile(.{
|
||||||
|
.file = b.path(src),
|
||||||
|
.flags = lwip_flags,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
b.installArtifact(liblwip);
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// LittleFS: Sovereign Filesystem (Persistent Storage)
|
||||||
|
// =========================================================
|
||||||
|
// Compiled as C object with freestanding flags.
|
||||||
|
// Uses LFS_CONFIG=lfs_rumpk.h to replace lfs_util.h entirely,
|
||||||
|
// avoiding stdio/assert deps. malloc/free provided by clib.c.
|
||||||
|
|
||||||
|
const lfs_flags: []const []const u8 = switch (arch) {
|
||||||
|
.riscv64 => &.{
|
||||||
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
||||||
|
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=medany",
|
||||||
|
},
|
||||||
|
.aarch64 => &.{
|
||||||
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
||||||
|
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs",
|
||||||
|
},
|
||||||
|
.x86_64 => &.{
|
||||||
|
"-Os", "-fno-sanitize=all", "-Wno-everything",
|
||||||
|
"-DLFS_CONFIG=lfs_rumpk.h", "-Ivendor/littlefs", "-mcmodel=kernel",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const lfs_obj = b.addObject(.{
|
||||||
|
.name = "littlefs",
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
lfs_obj.root_module.sanitize_thread = false;
|
||||||
|
lfs_obj.root_module.red_zone = false;
|
||||||
|
lfs_obj.root_module.single_threaded = true;
|
||||||
|
|
||||||
|
lfs_obj.addCSourceFile(.{
|
||||||
|
.file = b.path("vendor/littlefs/lfs.c"),
|
||||||
|
.flags = lfs_flags,
|
||||||
|
});
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Dependencies (CLib, LibC Shim, LWF Adapter, Switch)
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
// 1. CLib (Minimal C stdlib)
|
||||||
|
const clib = b.addObject(.{
|
||||||
|
.name = "clib",
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
clib.addCSourceFile(.{
|
||||||
|
.file = b.path("libs/membrane/clib.c"),
|
||||||
|
.flags = switch (arch) {
|
||||||
|
.riscv64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=medany" },
|
||||||
|
.aarch64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1" },
|
||||||
|
.x86_64 => &.{ "-Os", "-DNO_SYS=1", "-DRUMPK_KERNEL=1", "-mcmodel=kernel" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. LibC Shim (Zig)
|
||||||
|
const libc_shim_mod = b.createModule(.{
|
||||||
|
.root_source_file = b.path("libs/membrane/libc_shim.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
applyKernelSettings(libc_shim_mod, config.code_model);
|
||||||
|
|
||||||
|
const libc_shim = b.addObject(.{
|
||||||
|
.name = "libc_shim",
|
||||||
|
.root_module = libc_shim_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. LittleFS HAL (VirtIO-Block ↔ LittleFS glue)
|
||||||
|
const lfs_hal_mod = b.createModule(.{
|
||||||
|
.root_source_file = b.path("hal/littlefs_hal.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
applyKernelSettings(lfs_hal_mod, config.code_model);
|
||||||
|
|
||||||
|
const lfs_hal = b.addObject(.{
|
||||||
|
.name = "lfs_hal",
|
||||||
|
.root_module = lfs_hal_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. LWF Adapter (Project LibWeb — Libertaria Wire Frame)
|
||||||
|
const lwf_adapter_mod = b.createModule(.{
|
||||||
|
.root_source_file = b.path("libs/libertaria/lwf_adapter.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
applyKernelSettings(lwf_adapter_mod, config.code_model);
|
||||||
|
|
||||||
|
const lwf_adapter = b.addObject(.{
|
||||||
|
.name = "lwf_adapter",
|
||||||
|
.root_module = lwf_adapter_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. Context Switch (Architecture-specific Assembly)
|
||||||
|
const switch_obj = b.addObject(.{
|
||||||
|
.name = "switch",
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
switch_obj.addAssemblyFile(b.path(config.switch_asm));
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Final Link: rumpk.elf
|
// Final Link: rumpk.elf
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
const kernel_mod = b.createModule(.{
|
const kernel_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("hal/abi.zig"), // Fake root, we add objects later
|
.root_source_file = b.path("hal/abi.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
kernel_mod.red_zone = false;
|
applyKernelSettings(kernel_mod, config.code_model);
|
||||||
kernel_mod.stack_check = false;
|
|
||||||
kernel_mod.code_model = .medany;
|
|
||||||
|
|
||||||
const kernel = b.addExecutable(.{
|
const kernel = b.addExecutable(.{
|
||||||
.name = "rumpk.elf",
|
.name = "rumpk.elf",
|
||||||
.root_module = kernel_mod,
|
.root_module = kernel_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
kernel.setLinkerScript(b.path("boot/linker.ld"));
|
kernel.setLinkerScript(b.path(config.linker_script));
|
||||||
kernel.addObject(boot);
|
kernel.addObject(boot);
|
||||||
// kernel.linkLibrary(hal); // Redundant, already in kernel_mod
|
kernel.addObject(clib);
|
||||||
|
kernel.addObject(libc_shim);
|
||||||
|
kernel.addObject(lfs_hal);
|
||||||
|
kernel.addObject(lwf_adapter);
|
||||||
|
kernel.addObject(switch_obj);
|
||||||
|
|
||||||
// Add Nim-generated objects
|
// LwIP linked into kernel — Membrane/NetSwitch drives DHCP/TCP/ICMP.
|
||||||
|
kernel.linkLibrary(liblwip);
|
||||||
|
|
||||||
|
// LittleFS IS linked into kernel — provides /nexus persistent storage.
|
||||||
|
kernel.addObject(lfs_obj);
|
||||||
|
|
||||||
|
// Nim-generated objects from build/nimcache/
|
||||||
{
|
{
|
||||||
var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
|
var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
|
||||||
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
||||||
|
|
@ -104,31 +367,63 @@ pub fn build(b: *std.Build) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add external pre-built dependencies (Order matters: Libs after users)
|
// Embedded InitRD — assemble for the target architecture
|
||||||
kernel.addObjectFile(b.path("build/switch.o")); // cpu_switch_to
|
const initrd_obj = b.addObject(.{
|
||||||
kernel.addObjectFile(b.path("build/sys_arch.o")); // sys_now, nexus_lwip_panic
|
.name = "initrd",
|
||||||
kernel.addObjectFile(b.path("build/libc_shim.o"));
|
.root_module = b.createModule(.{
|
||||||
kernel.addObjectFile(b.path("build/clib.o"));
|
.target = target,
|
||||||
kernel.addObjectFile(b.path("build/liblwip.a"));
|
.optimize = optimize,
|
||||||
kernel.addObjectFile(b.path("build/initrd.o"));
|
}),
|
||||||
|
});
|
||||||
|
initrd_obj.addAssemblyFile(b.path("build/embed_initrd.S"));
|
||||||
|
kernel.addObject(initrd_obj);
|
||||||
|
|
||||||
b.installArtifact(kernel);
|
b.installArtifact(kernel);
|
||||||
|
|
||||||
|
// Make default install depend on Nim compilation
|
||||||
|
kernel.step.dependOn(&nim_step.step);
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Tests
|
// Tests (always run on native host)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
const test_mod = b.createModule(.{
|
// HAL unit tests
|
||||||
|
const hal_test_mod = b.createModule(.{
|
||||||
.root_source_file = b.path("hal/abi.zig"),
|
.root_source_file = b.path("hal/abi.zig"),
|
||||||
.target = target,
|
.target = b.graph.host,
|
||||||
.optimize = optimize,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const hal_tests = b.addTest(.{
|
const hal_tests = b.addTest(.{
|
||||||
.root_module = test_mod,
|
.root_module = hal_test_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
const run_tests = b.addRunArtifact(hal_tests);
|
const run_hal_tests = b.addRunArtifact(hal_tests);
|
||||||
const test_step = b.step("test", "Run Rumpk HAL tests");
|
const test_step = b.step("test", "Run Rumpk HAL tests (native host)");
|
||||||
test_step.dependOn(&run_tests.step);
|
test_step.dependOn(&run_hal_tests.step);
|
||||||
|
|
||||||
|
// LWF Adapter + Membrane tests (Project LibWeb)
|
||||||
|
const lwf_test_mod = b.createModule(.{
|
||||||
|
.root_source_file = b.path("libs/libertaria/lwf_adapter.zig"),
|
||||||
|
.target = b.graph.host,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lwf_tests = b.addTest(.{
|
||||||
|
.root_module = lwf_test_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_lwf_tests = b.addRunArtifact(lwf_tests);
|
||||||
|
const lwf_test_step = b.step("test-lwf", "Run LWF Adapter tests (Project LibWeb)");
|
||||||
|
lwf_test_step.dependOn(&run_lwf_tests.step);
|
||||||
|
|
||||||
|
const lwf_membrane_mod = b.createModule(.{
|
||||||
|
.root_source_file = b.path("libs/libertaria/lwf_membrane.zig"),
|
||||||
|
.target = b.graph.host,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lwf_membrane_tests = b.addTest(.{
|
||||||
|
.root_module = lwf_membrane_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_lwf_membrane_tests = b.addRunArtifact(lwf_membrane_tests);
|
||||||
|
lwf_test_step.dependOn(&run_lwf_membrane_tests.step);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ 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
|
||||||
|
|
@ -80,10 +81,10 @@ proc fiber_grant_memory*(
|
||||||
end_addr
|
end_addr
|
||||||
)
|
)
|
||||||
|
|
||||||
proc fiber_check_channel_access*(fiber_id: uint64, slot: uint, write: bool): bool =
|
proc fiber_check_channel_access*(fiber_id: uint64, channel_id: uint64, write: bool): bool =
|
||||||
## Check if fiber has channel access via capability
|
## Check if fiber has Channel capability for given channel_id
|
||||||
let perm = if write: PERM_WRITE else: PERM_READ
|
let perm = if write: PERM_WRITE else: PERM_READ
|
||||||
return cspace_check_perm(fiber_id, slot, perm)
|
return cspace_check_channel(fiber_id, channel_id, 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
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,10 @@ 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
|
||||||
|
|
@ -112,7 +116,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
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
@ -135,6 +139,9 @@ 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
|
import tar, sfs, lfs_bridge
|
||||||
|
|
||||||
type
|
type
|
||||||
VFSMode = enum
|
VFSMode = enum
|
||||||
MODE_TAR, MODE_SFS, MODE_RAM, MODE_TTY
|
MODE_TAR, MODE_SFS, MODE_RAM, MODE_TTY, MODE_LFS
|
||||||
|
|
||||||
MountPoint = object
|
MountPoint = object
|
||||||
prefix: array[32, char]
|
prefix: array[32, char]
|
||||||
|
|
@ -25,6 +25,7 @@ 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
|
||||||
|
|
@ -64,8 +65,15 @@ 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*() =
|
||||||
# Restore the SPEC-502 baseline
|
# Mount LittleFS for /nexus (persistent sovereign storage)
|
||||||
|
if lfs_bridge.lfs_mount_fs():
|
||||||
|
vfs_add_mount("/nexus", MODE_LFS)
|
||||||
|
else:
|
||||||
|
# Fallback to SFS if LittleFS mount fails (no block device?)
|
||||||
|
kprintln("[VFS] LFS mount failed, falling back to SFS for /nexus")
|
||||||
vfs_add_mount("/nexus", MODE_SFS)
|
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)
|
||||||
|
|
@ -88,9 +96,22 @@ proc ion_vfs_open*(path: cstring, flags: int32): int32 {.exportc, cdecl.} =
|
||||||
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:
|
||||||
|
|
@ -99,6 +120,7 @@ proc ion_vfs_open*(path: cstring, flags: int32): int32 {.exportc, cdecl.} =
|
||||||
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:
|
||||||
|
|
@ -106,6 +128,8 @@ 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.} =
|
||||||
|
|
@ -132,6 +156,11 @@ 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)
|
||||||
|
|
@ -149,14 +178,47 @@ 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,5 +3,10 @@
|
||||||
|
|
||||||
double pow(double x, double y);
|
double pow(double x, double y);
|
||||||
double log10(double x);
|
double log10(double x);
|
||||||
|
double fabs(double x);
|
||||||
|
double floor(double x);
|
||||||
|
double ceil(double x);
|
||||||
|
double fmod(double x, double y);
|
||||||
|
double round(double x);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,22 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef struct _FILE FILE;
|
||||||
|
|
||||||
|
extern FILE *stdin;
|
||||||
|
extern FILE *stdout;
|
||||||
|
extern FILE *stderr;
|
||||||
|
|
||||||
int printf(const char *format, ...);
|
int printf(const char *format, ...);
|
||||||
|
int fprintf(FILE *stream, const char *format, ...);
|
||||||
int sprintf(char *str, const char *format, ...);
|
int sprintf(char *str, const char *format, ...);
|
||||||
int snprintf(char *str, size_t size, const char *format, ...);
|
int snprintf(char *str, size_t size, const char *format, ...);
|
||||||
|
int vprintf(const char *format, va_list ap);
|
||||||
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||||
|
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||||
|
int fflush(FILE *stream);
|
||||||
|
int fputc(int c, FILE *stream);
|
||||||
|
int fputs(const char *s, FILE *stream);
|
||||||
int rename(const char *oldpath, const char *newpath);
|
int rename(const char *oldpath, const char *newpath);
|
||||||
int remove(const char *pathname);
|
int remove(const char *pathname);
|
||||||
|
|
||||||
|
|
|
||||||
25
core/ion.nim
25
core/ion.nim
|
|
@ -91,6 +91,8 @@ 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.}
|
||||||
|
|
||||||
|
|
@ -123,6 +125,10 @@ type
|
||||||
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 ---
|
||||||
|
|
@ -167,6 +173,12 @@ 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
|
||||||
|
|
@ -192,6 +204,17 @@ proc ion_init_network*() {.exportc, cdecl.} =
|
||||||
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()
|
||||||
|
|
||||||
|
|
@ -218,4 +241,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) == 208, "SysTable size mismatch! (Expected 208 after MAC+pad)")
|
static: doAssert(sizeof(SysTable) == 240, "SysTable size mismatch! (Expected 240 after LibWeb LWF channels)")
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,24 @@
|
||||||
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) =
|
|
||||||
console_write(unsafeAddr s[0], csize_t(s.len))
|
var NEWLINE_BUF: array[2, char] = ['\n', '\0']
|
||||||
var nl = "\n"
|
|
||||||
console_write(unsafeAddr nl[0], csize_t(1))
|
proc dbg(s: cstring) {.inline.} =
|
||||||
|
if s != nil:
|
||||||
|
var i = 0
|
||||||
|
let p = cast[ptr UncheckedArray[char]](s)
|
||||||
|
while p[i] != '\0': inc i
|
||||||
|
console_write(cast[pointer](s), csize_t(i))
|
||||||
|
console_write(addr NEWLINE_BUF[0], csize_t(1))
|
||||||
|
|
||||||
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 = 0x83000000'u64
|
SYSTABLE_BASE = when defined(arm64): 0x50000000'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
|
||||||
|
|
@ -53,9 +60,11 @@ 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
|
||||||
|
|
@ -64,12 +73,16 @@ proc ion_pool_init*() {.exportc.} =
|
||||||
kprint_hex(global_pool.base_phys)
|
kprint_hex(global_pool.base_phys)
|
||||||
dbg("")
|
dbg("")
|
||||||
|
|
||||||
dbg("[ION] Ring Init...")
|
dbg("[ION] Step 3: Ring Init (free_ring)...")
|
||||||
|
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] Filling Slabs...")
|
dbg("[ION] Step 5: 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)):
|
||||||
|
|
@ -77,6 +90,8 @@ 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
|
||||||
|
|
|
||||||
551
core/kernel.nim
551
core/kernel.nim
|
|
@ -12,11 +12,22 @@ import fiber, ion, sched, pty, cspace, ontology, fastpath, utcp
|
||||||
import fs/vfs, fs/tar
|
import fs/vfs, fs/tar
|
||||||
import loader/elf
|
import loader/elf
|
||||||
import ../libs/membrane/term
|
import ../libs/membrane/term
|
||||||
|
import ../libs/membrane/net_glue
|
||||||
|
|
||||||
const
|
const
|
||||||
MAX_WORKERS* = 8
|
MAX_WORKERS* = 8
|
||||||
MAX_FIBER_STACK* = 128 * 1024
|
MAX_FIBER_STACK* = 128 * 1024
|
||||||
SYSTABLE_BASE* = 0x83000000'u64
|
SYSTABLE_BASE* = when defined(arm64): 0x50000000'u64
|
||||||
|
else: 0x83000000'u64
|
||||||
|
# Cellular Memory Architecture (M3.3)
|
||||||
|
CELL0_BASE* = when defined(arm64): 0x48000000'u64
|
||||||
|
else: 0x88000000'u64
|
||||||
|
CELL1_BASE* = when defined(arm64): 0x4C000000'u64
|
||||||
|
else: 0x8C000000'u64
|
||||||
|
USER_VA_BASE* = when defined(arm64): 0x48000000'u64
|
||||||
|
else: 0x88000000'u64
|
||||||
|
USER_SP_FALLBACK* = when defined(arm64): 0x4BFFFFF0'u64
|
||||||
|
else: 0x8BFFFFF0'u64
|
||||||
|
|
||||||
# Export Nim Timer Handler for HAL (Zig calls this)
|
# Export Nim Timer Handler for HAL (Zig calls this)
|
||||||
proc rumpk_timer_handler() {.exportc, cdecl, used.} =
|
proc rumpk_timer_handler() {.exportc, cdecl, used.} =
|
||||||
|
|
@ -35,6 +46,7 @@ proc nexshell_main() {.importc, cdecl.}
|
||||||
proc console_poll() {.importc, cdecl.}
|
proc console_poll() {.importc, cdecl.}
|
||||||
proc ion_get_virt(id: uint16): uint64 {.importc, cdecl.}
|
proc ion_get_virt(id: uint16): uint64 {.importc, cdecl.}
|
||||||
|
|
||||||
|
|
||||||
# InitRD Symbols
|
# InitRD Symbols
|
||||||
var initrd_start {.importc: "_initrd_start" .}: byte
|
var initrd_start {.importc: "_initrd_start" .}: byte
|
||||||
var initrd_end {.importc: "_initrd_end" .}: byte
|
var initrd_end {.importc: "_initrd_end" .}: byte
|
||||||
|
|
@ -151,6 +163,7 @@ proc kload_phys(path: cstring, phys_offset: uint64): uint64 =
|
||||||
|
|
||||||
if phdr.p_type == PT_LOAD:
|
if phdr.p_type == PT_LOAD:
|
||||||
# Enable S-mode access to U-mode pages (SUM=1)
|
# Enable S-mode access to U-mode pages (SUM=1)
|
||||||
|
when defined(riscv64):
|
||||||
{.emit: """asm volatile ("li t1, 0x40000; csrs sstatus, t1" : : : "t1");""" .}
|
{.emit: """asm volatile ("li t1, 0x40000; csrs sstatus, t1" : : : "t1");""" .}
|
||||||
|
|
||||||
let dest = cast[ptr UncheckedArray[byte]](phdr.p_vaddr)
|
let dest = cast[ptr UncheckedArray[byte]](phdr.p_vaddr)
|
||||||
|
|
@ -168,11 +181,139 @@ proc kload_phys(path: cstring, phys_offset: uint64): uint64 =
|
||||||
# {.emit: """asm volatile ("li t1, 0x40000; csrc sstatus, t1" : : : "t1");""" .}
|
# {.emit: """asm volatile ("li t1, 0x40000; csrc sstatus, t1" : : : "t1");""" .}
|
||||||
|
|
||||||
# ⚡ ARCH-SYNC: Flush I-Cache after loading new code
|
# ⚡ ARCH-SYNC: Flush I-Cache after loading new code
|
||||||
|
when defined(riscv64):
|
||||||
{.emit: """asm volatile ("fence.i" : : : "memory");""" .}
|
{.emit: """asm volatile ("fence.i" : : : "memory");""" .}
|
||||||
|
elif defined(arm64):
|
||||||
|
{.emit: """asm volatile ("ic iallu; dsb ish; isb" : : : "memory");""" .}
|
||||||
|
|
||||||
discard ion_vfs_close(fd)
|
discard ion_vfs_close(fd)
|
||||||
return ehdr.e_entry
|
return ehdr.e_entry
|
||||||
|
|
||||||
|
# --- M4.4: MANIFEST-DRIVEN CAPABILITY LOADING ---
|
||||||
|
|
||||||
|
proc set_pledge*(f: var FiberObject, pledge: uint64) =
|
||||||
|
## Set fiber pledge mask, preserving Spectrum bits [63:62]
|
||||||
|
f.promises = (f.promises and 0xC000000000000000'u64) or (pledge and 0x3FFFFFFFFFFFFFFF'u64)
|
||||||
|
|
||||||
|
proc kload_manifest_tar(path: cstring): ManifestResult =
|
||||||
|
## Scan ELF section headers via TAR for .nexus.manifest containing BKDL data.
|
||||||
|
## Uses tar.vfs_read_at() for selective reads (same approach as kload_phys).
|
||||||
|
result.header = nil
|
||||||
|
result.caps = nil
|
||||||
|
result.count = 0
|
||||||
|
|
||||||
|
# Strip /sysro prefix for TAR paths (same logic as kload_phys)
|
||||||
|
var tar_path = path
|
||||||
|
if path[0] == '/':
|
||||||
|
tar_path = cast[cstring](cast[uint64](path) + 1)
|
||||||
|
if k_starts_with(path, "/sysro"):
|
||||||
|
tar_path = cast[cstring](cast[uint64](path) + 6)
|
||||||
|
if tar_path[0] == '/': tar_path = cast[cstring](cast[uint64](tar_path) + 1)
|
||||||
|
|
||||||
|
# 1. Read ELF header
|
||||||
|
var ehdr: Elf64_Ehdr
|
||||||
|
if tar.vfs_read_at(tar_path, addr ehdr, uint64(sizeof(ehdr)), 0) != int64(sizeof(ehdr)):
|
||||||
|
return
|
||||||
|
if ehdr.e_ident[0] != 0x7F or ehdr.e_ident[1] != 'E'.uint8:
|
||||||
|
return
|
||||||
|
if ehdr.e_shoff == 0 or ehdr.e_shnum == 0 or ehdr.e_shstrndx >= ehdr.e_shnum:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. Read shstrtab section header to get string table location
|
||||||
|
let strtab_offset = ehdr.e_shoff + uint64(ehdr.e_shstrndx) * uint64(ehdr.e_shentsize)
|
||||||
|
var strtab_shdr: Elf64_Shdr
|
||||||
|
if tar.vfs_read_at(tar_path, addr strtab_shdr, uint64(sizeof(Elf64_Shdr)), strtab_offset) != int64(sizeof(Elf64_Shdr)):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 3. Read the string table itself (cap at 512 bytes — section names are short)
|
||||||
|
var strtab_buf: array[512, byte]
|
||||||
|
let strtab_read_size = min(strtab_shdr.sh_size, 512'u64)
|
||||||
|
if tar.vfs_read_at(tar_path, addr strtab_buf[0], strtab_read_size, strtab_shdr.sh_offset) != int64(strtab_read_size):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 4. Scan section headers for .nexus.manifest
|
||||||
|
let target = cstring(".nexus.manifest")
|
||||||
|
for i in 0 ..< int(ehdr.e_shnum):
|
||||||
|
var shdr: Elf64_Shdr
|
||||||
|
let sh_offset = ehdr.e_shoff + uint64(i) * uint64(ehdr.e_shentsize)
|
||||||
|
if tar.vfs_read_at(tar_path, addr shdr, uint64(sizeof(Elf64_Shdr)), sh_offset) != int64(sizeof(Elf64_Shdr)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if shdr.sh_name < uint32(strtab_read_size):
|
||||||
|
|
||||||
|
# Compare section name against ".nexus.manifest"
|
||||||
|
var match = true
|
||||||
|
var j = 0
|
||||||
|
while j < 16: # len(".nexus.manifest") + null = 16
|
||||||
|
let ch = target[j]
|
||||||
|
let idx = int(shdr.sh_name) + j
|
||||||
|
if idx >= int(strtab_read_size):
|
||||||
|
match = false
|
||||||
|
break
|
||||||
|
if ch == '\0':
|
||||||
|
break # End of target string — all matched
|
||||||
|
if strtab_buf[idx] != byte(ch):
|
||||||
|
match = false
|
||||||
|
break
|
||||||
|
j += 1
|
||||||
|
|
||||||
|
if match and shdr.sh_size >= uint64(sizeof(BkdlHeader)):
|
||||||
|
# 5. Read the manifest section data into a static buffer
|
||||||
|
# Max manifest size: BkdlHeader (118) + 16 caps * CapDescriptor (12) = 310 bytes
|
||||||
|
var manifest_buf {.global.}: array[512, byte]
|
||||||
|
let read_size = min(shdr.sh_size, 512'u64)
|
||||||
|
if tar.vfs_read_at(tar_path, addr manifest_buf[0], read_size, shdr.sh_offset) != int64(read_size):
|
||||||
|
return
|
||||||
|
|
||||||
|
let hdr = cast[ptr BkdlHeader](addr manifest_buf[0])
|
||||||
|
if hdr.magic != BKDL_MAGIC or hdr.version != BKDL_VERSION:
|
||||||
|
kprintln("[Manifest] Invalid BKDL magic/version")
|
||||||
|
return
|
||||||
|
|
||||||
|
let expected = uint64(sizeof(BkdlHeader)) + uint64(hdr.cap_count) * uint64(sizeof(CapDescriptor))
|
||||||
|
if expected > read_size:
|
||||||
|
kprintln("[Manifest] BKDL cap_count exceeds section size")
|
||||||
|
return
|
||||||
|
|
||||||
|
kprint("[Manifest] WARNING: Signature unchecked (dev mode)")
|
||||||
|
kprintln("")
|
||||||
|
discard emit_access_denied(0, 0xB0D1, 0, 0) # STL: signature skip event
|
||||||
|
|
||||||
|
result.header = hdr
|
||||||
|
result.caps = cast[ptr UncheckedArray[CapDescriptor]](addr manifest_buf[sizeof(BkdlHeader)])
|
||||||
|
result.count = int(hdr.cap_count)
|
||||||
|
return
|
||||||
|
|
||||||
|
proc apply_manifest*(fiber_id: uint64, manifest: ManifestResult, pledge_out: var uint64) =
|
||||||
|
## Apply BKDL manifest capabilities to fiber's CSpace.
|
||||||
|
## Derives pledge mask from requested capability types.
|
||||||
|
var derived_pledge: uint64 = PLEDGE_STDIO # Always grant basic I/O
|
||||||
|
|
||||||
|
for i in 0 ..< manifest.count:
|
||||||
|
let cap = manifest.caps[i]
|
||||||
|
let slot = fiber_grant_channel(fiber_id, cap.resource_id, cap.perms)
|
||||||
|
if slot >= 0:
|
||||||
|
discard emit_capability_grant(fiber_id, cap.cap_type, cap.resource_id, uint8(slot), 0)
|
||||||
|
kprint("[Manifest] Granted cap "); kprint_hex(cap.resource_id)
|
||||||
|
kprint(" perms="); kprint_hex(uint64(cap.perms))
|
||||||
|
kprint(" to fiber "); kprint_hex(fiber_id); kprintln("")
|
||||||
|
else:
|
||||||
|
discard emit_access_denied(fiber_id, cap.resource_id, cap.perms, 0)
|
||||||
|
kprint("[Manifest] DENIED cap "); kprint_hex(cap.resource_id)
|
||||||
|
kprint(" for fiber "); kprint_hex(fiber_id); kprintln(" (CSpace full)")
|
||||||
|
|
||||||
|
# Derive pledge bits from capability types
|
||||||
|
if cap.resource_id == 0x2000: # VFS
|
||||||
|
derived_pledge = derived_pledge or PLEDGE_RPATH
|
||||||
|
if (cap.perms and PERM_WRITE) != 0:
|
||||||
|
derived_pledge = derived_pledge or PLEDGE_WPATH
|
||||||
|
elif cap.resource_id == 0x500 or cap.resource_id == 0x501: # NET_TX / NET_RX
|
||||||
|
derived_pledge = derived_pledge or PLEDGE_INET
|
||||||
|
elif cap.resource_id == 0x1000 or cap.resource_id == 0x1001: # Console
|
||||||
|
discard # PLEDGE_STDIO already set
|
||||||
|
|
||||||
|
pledge_out = derived_pledge
|
||||||
|
|
||||||
# --- FIBER ENTRIES ---
|
# --- FIBER ENTRIES ---
|
||||||
|
|
||||||
proc subject_fiber_entry() {.cdecl.} =
|
proc subject_fiber_entry() {.cdecl.} =
|
||||||
|
|
@ -185,13 +326,26 @@ proc subject_fiber_entry() {.cdecl.} =
|
||||||
# Load into Cellular Slot (phys_offset)
|
# Load into Cellular Slot (phys_offset)
|
||||||
let entry_addr = kload_phys(cast[cstring](addr subject_loading_path[0]), current_fiber.phys_offset)
|
let entry_addr = kload_phys(cast[cstring](addr subject_loading_path[0]), current_fiber.phys_offset)
|
||||||
|
|
||||||
|
# M4.4: Scan ELF for BKDL manifest and apply capabilities
|
||||||
|
let loading_path = cast[cstring](addr subject_loading_path[0])
|
||||||
|
let manifest = kload_manifest_tar(loading_path)
|
||||||
|
if manifest.header != nil:
|
||||||
|
kprint("[Manifest] Found BKDL manifest: "); kprint_hex(uint64(manifest.count)); kprintln(" capabilities")
|
||||||
|
var pledge: uint64 = 0
|
||||||
|
apply_manifest(current_fiber.id, manifest, pledge)
|
||||||
|
set_pledge(current_fiber[], pledge)
|
||||||
|
kprint("[Manifest] Pledge mask: "); kprint_hex(pledge); kprintln("")
|
||||||
|
else:
|
||||||
|
kprintln("[Manifest] No BKDL manifest — default policy (STDIO only)")
|
||||||
|
set_pledge(current_fiber[], PLEDGE_STDIO)
|
||||||
|
|
||||||
if entry_addr != 0:
|
if entry_addr != 0:
|
||||||
kprint("[Subject:"); kprint_hex(fid); kprint("] Entering Payload at: "); kprint_hex(entry_addr); kprintln("")
|
kprint("[Subject:"); kprint_hex(fid); kprint("] Entering Payload at: "); kprint_hex(entry_addr); kprintln("")
|
||||||
proc hal_enter_userland(entry, systable, sp: uint64) {.importc, cdecl.}
|
proc hal_enter_userland(entry, systable, sp: uint64) {.importc, cdecl.}
|
||||||
var sp = current_fiber.user_sp_init
|
var sp = current_fiber.user_sp_init
|
||||||
if sp == 0:
|
if sp == 0:
|
||||||
# Fallback (Legacy/Init) - Top of the 64MB Sentinel Cell
|
# Fallback (Legacy/Init) - Top of the 64MB Sentinel Cell
|
||||||
sp = 0x8BFFFFF0'u64
|
sp = USER_SP_FALLBACK
|
||||||
|
|
||||||
kprintln("╔════════════════════════════════════════════════════╗")
|
kprintln("╔════════════════════════════════════════════════════╗")
|
||||||
kprintln("║ PRE-FLIGHT: USERLAND TRANSITION ║")
|
kprintln("║ PRE-FLIGHT: USERLAND TRANSITION ║")
|
||||||
|
|
@ -232,51 +386,71 @@ proc compositor_fiber_entry() {.cdecl.} =
|
||||||
|
|
||||||
proc mm_create_worker_map(stack_base: uint64, stack_size: uint64, packet_addr: uint64, phys_base: uint64, region_size: uint64): uint64 {.importc, cdecl.}
|
proc mm_create_worker_map(stack_base: uint64, stack_size: uint64, packet_addr: uint64, phys_base: uint64, region_size: uint64): uint64 {.importc, cdecl.}
|
||||||
|
|
||||||
proc setup_mksh_stack(stack_base: pointer, stack_size: int): uint64 =
|
proc setup_cellular_stack(phys_base: uint64, slot_size: uint64, user_base: uint64): uint64 =
|
||||||
var sp = cast[uint64](stack_base) + cast[uint64](stack_size)
|
# Stack grows down from the top of the cell
|
||||||
sp = sp and not 15'u64 # Align 16
|
var phys_top = phys_base + slot_size
|
||||||
|
var user_sp = user_base + slot_size
|
||||||
|
|
||||||
# Term String
|
# Align 16
|
||||||
let term_str = "TERM=nexus\0"
|
phys_top = phys_top and not 15'u64
|
||||||
sp -= uint64(term_str.len)
|
user_sp = user_sp and not 15'u64
|
||||||
copyMem(cast[pointer](sp), unsafeAddr term_str[0], term_str.len)
|
|
||||||
let term_addr = sp
|
|
||||||
|
|
||||||
# Path String
|
# Helper to push data
|
||||||
let path_str = "/bin/mksh\0"
|
template push_str(s: string): uint64 =
|
||||||
sp -= uint64(path_str.len)
|
let s_val = s
|
||||||
copyMem(cast[pointer](sp), unsafeAddr path_str[0], path_str.len)
|
let l = uint64(s_val.len) + 1 # + null terminator
|
||||||
let path_addr = sp
|
phys_top -= l
|
||||||
|
user_sp -= l
|
||||||
|
# Zero the memory first (for null safety)
|
||||||
|
k_zero_mem(cast[pointer](phys_top), l)
|
||||||
|
copyMem(cast[pointer](phys_top), unsafeAddr s_val[0], s_val.len)
|
||||||
|
user_sp # Return USER address
|
||||||
|
|
||||||
sp = sp and not 15'u64 # Align 16
|
# Environment strings
|
||||||
|
let home_addr = push_str("HOME=/")
|
||||||
|
let term_addr = push_str("TERM=vt100")
|
||||||
|
let path_addr = push_str("/bin/mksh")
|
||||||
|
|
||||||
|
kprint("[Stack] HOME env at: "); kprint_hex(home_addr); kprint("\n")
|
||||||
|
kprint("[Stack] TERM env at: "); kprint_hex(term_addr); kprint("\n")
|
||||||
|
kprint("[Stack] argv[0] at: "); kprint_hex(path_addr); kprint("\n")
|
||||||
|
|
||||||
|
# Align 16 before pointer arrays
|
||||||
|
phys_top = phys_top and not 15'u64
|
||||||
|
user_sp = user_sp and not 15'u64
|
||||||
|
|
||||||
|
# Helper to push uint64
|
||||||
|
template push_u64(val: uint64) =
|
||||||
|
phys_top -= 8
|
||||||
|
user_sp -= 8
|
||||||
|
cast[ptr uint64](phys_top)[] = val
|
||||||
|
|
||||||
# Auxv (0, 0)
|
# Auxv (0, 0)
|
||||||
sp -= 16
|
push_u64(0)
|
||||||
cast[ptr uint64](sp)[] = 0
|
push_u64(0)
|
||||||
cast[ptr uint64](sp+8)[] = 0
|
|
||||||
|
|
||||||
# Envp (term, NULL)
|
# Envp (NULL, TERM, HOME) - Reverse order of push
|
||||||
sp -= 16
|
push_u64(0)
|
||||||
cast[ptr uint64](sp)[] = term_addr
|
push_u64(term_addr)
|
||||||
cast[ptr uint64](sp+8)[] = 0
|
push_u64(home_addr)
|
||||||
|
|
||||||
# Argv (path, NULL)
|
# Argv (NULL, path)
|
||||||
sp -= 16
|
push_u64(0)
|
||||||
cast[ptr uint64](sp)[] = path_addr
|
push_u64(path_addr)
|
||||||
cast[ptr uint64](sp+8)[] = 0
|
|
||||||
|
|
||||||
# Argc (1)
|
# Argc (1)
|
||||||
sp -= 8
|
push_u64(1)
|
||||||
cast[ptr uint64](sp)[] = 1
|
|
||||||
|
|
||||||
return sp
|
kprint("[Stack] Final SP: "); kprint_hex(user_sp); kprint("\n")
|
||||||
|
|
||||||
|
return user_sp
|
||||||
|
|
||||||
proc ion_fiber_entry() {.cdecl.} =
|
proc ion_fiber_entry() {.cdecl.} =
|
||||||
kprintln("[ION] Fiber Entry reached.")
|
kprintln("[ION] Fiber Entry reached.")
|
||||||
while true:
|
while true:
|
||||||
var pkt: CmdPacket
|
var pkt: CmdPacket
|
||||||
if chan_cmd.recv(pkt):
|
if chan_cmd.recv(pkt):
|
||||||
kprint("[ION] Received Packet Kind: "); kprint_hex(uint64(pkt.kind))
|
kprint("[ION] Received Packet Kind: "); kprint_hex(uint64(pkt.kind)); kprintln("")
|
||||||
case CmdType(pkt.kind):
|
case CmdType(pkt.kind):
|
||||||
of CMD_SYS_EXIT:
|
of CMD_SYS_EXIT:
|
||||||
kprintln("[ION] Restarting Subject...")
|
kprintln("[ION] Restarting Subject...")
|
||||||
|
|
@ -300,21 +474,31 @@ proc ion_fiber_entry() {.cdecl.} =
|
||||||
kprintln("[ION] Failed to allocate PTY for child!")
|
kprintln("[ION] Failed to allocate PTY for child!")
|
||||||
|
|
||||||
# Re-initialize fiber_child with the requested binary
|
# Re-initialize fiber_child with the requested binary
|
||||||
|
# Note: stack_child is used for KERNEL context switching, not user stack.
|
||||||
init_fiber(addr fiber_child, subject_fiber_entry, addr stack_child[0], sizeof(stack_child))
|
init_fiber(addr fiber_child, subject_fiber_entry, addr stack_child[0], sizeof(stack_child))
|
||||||
|
|
||||||
# Phase 40: Set PTY & Stack
|
|
||||||
fiber_child.pty_id = pid
|
|
||||||
fiber_child.user_sp_init = setup_mksh_stack(addr stack_child[0], sizeof(stack_child))
|
|
||||||
|
|
||||||
# 🏛️ CELLULAR ALLOCATION (SPEC-202 Rev 2)
|
# 🏛️ CELLULAR ALLOCATION (SPEC-202 Rev 2)
|
||||||
# Sentinel (Init) takes 0x88000000 - 0x8C000000 (64MB)
|
# Sentinel (Init) takes Cell 0, Mksh (First Child) takes Cell 1
|
||||||
# Mksh (First Child) starts in the 'Wild' at 0x8C000000
|
let cell_base = CELL1_BASE
|
||||||
let cell_base = 0x8C000000'u64
|
let user_base = USER_VA_BASE
|
||||||
|
let cell_size = 64 * 1024 * 1024'u64
|
||||||
|
|
||||||
|
# Phase 40: Set PTY & User Stack
|
||||||
|
fiber_child.pty_id = pid
|
||||||
fiber_child.phys_offset = cell_base
|
fiber_child.phys_offset = cell_base
|
||||||
|
|
||||||
# Allocate 64MB Slot for Mksh (Needs >32MB for BSS)
|
# Setup User Stack in CELLULAR Memory (User Top)
|
||||||
let cell_size = 64 * 1024 * 1024'u64
|
# We write to Physical Address (cell_base + size), but Mksh sees (user_base + size)
|
||||||
fiber_child.satp_value = mm_create_worker_map(cast[uint64](addr stack_child[0]), uint64(sizeof(stack_child)), SYSTABLE_BASE, cell_base, cell_size)
|
fiber_child.user_sp_init = setup_cellular_stack(cell_base, cell_size, user_base)
|
||||||
|
|
||||||
|
# Create Map avoiding Kernel Stack exposure
|
||||||
|
# We pass 0 as stack_base to skip mapping stack_child into user space.
|
||||||
|
fiber_child.satp_value = mm_create_worker_map(0, 0, SYSTABLE_BASE, cell_base, cell_size)
|
||||||
|
|
||||||
|
# M4.4: Child capabilities are applied in subject_fiber_entry via BKDL manifest.
|
||||||
|
# No hardcoded grants needed here — manifest drives CSpace + pledge.
|
||||||
|
# M4.5: Set budget based on child's Spectrum tier
|
||||||
|
fiber_child.budget_ns = default_budget_for_spectrum((addr fiber_child).getSpectrum())
|
||||||
kprintln("[ION] Child fiber spawned successfully")
|
kprintln("[ION] Child fiber spawned successfully")
|
||||||
else: discard
|
else: discard
|
||||||
else:
|
else:
|
||||||
|
|
@ -326,8 +510,12 @@ proc rumpk_yield_internal*() {.exportc, cdecl.} =
|
||||||
switch(active_fibers_arr[6])
|
switch(active_fibers_arr[6])
|
||||||
|
|
||||||
proc fiber_yield*() {.exportc, cdecl.} =
|
proc fiber_yield*() {.exportc, cdecl.} =
|
||||||
current_fiber.wants_yield = true
|
# Return to the dispatcher context (main_fiber)
|
||||||
rumpk_yield_internal()
|
# kprint("[Y"); kprint_hex(current_fiber.id); kprint("]")
|
||||||
|
switch(addr main_fiber)
|
||||||
|
# The `ld_internal()` part from the instruction was syntactically incorrect
|
||||||
|
# and likely a typo or incomplete instruction.
|
||||||
|
# Assuming the intent was to replace the previous yield mechanism with a direct switch.
|
||||||
|
|
||||||
proc fiber_netswitch_entry() {.cdecl.} =
|
proc fiber_netswitch_entry() {.cdecl.} =
|
||||||
kprintln("[NetSwitch] Traffic Engine Online")
|
kprintln("[NetSwitch] Traffic Engine Online")
|
||||||
|
|
@ -343,6 +531,10 @@ proc fiber_netswitch_entry() {.cdecl.} =
|
||||||
|
|
||||||
kprintln("[NetSwitch] Channels verified. Sovereignty confirmed.")
|
kprintln("[NetSwitch] Channels verified. Sovereignty confirmed.")
|
||||||
|
|
||||||
|
# Initialize LwIP Membrane (DHCP, Netif, DNS)
|
||||||
|
membrane_init()
|
||||||
|
kprintln("[NetSwitch] Membrane initialized — DHCP running")
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
var pkt: IonPacket
|
var pkt: IonPacket
|
||||||
|
|
||||||
|
|
@ -370,6 +562,9 @@ proc fiber_netswitch_entry() {.cdecl.} =
|
||||||
var res = ion_tx_push(pkt)
|
var res = ion_tx_push(pkt)
|
||||||
if not res: kprintln("[NetSwitch] Drop (TX Full)")
|
if not res: kprintln("[NetSwitch] Drop (TX Full)")
|
||||||
|
|
||||||
|
# Drive LwIP timers + RX ingestion (DHCP, ARP, TCP, ICMP)
|
||||||
|
pump_membrane_stack()
|
||||||
|
|
||||||
# Poll Network
|
# Poll Network
|
||||||
virtio_net_poll()
|
virtio_net_poll()
|
||||||
|
|
||||||
|
|
@ -378,7 +573,6 @@ proc fiber_netswitch_entry() {.cdecl.} =
|
||||||
|
|
||||||
# Prevent Starvation
|
# Prevent Starvation
|
||||||
fiber_sleep(10) # 10ms - allow DHCP state machine to execute
|
fiber_sleep(10) # 10ms - allow DHCP state machine to execute
|
||||||
# 10ms - allow DHCP state machine to execute
|
|
||||||
|
|
||||||
proc ion_ingress*(id: uint16, len: uint16, offset: uint16) {.exportc, cdecl.} =
|
proc ion_ingress*(id: uint16, len: uint16, offset: uint16) {.exportc, cdecl.} =
|
||||||
## Handle packet from Network Driver
|
## Handle packet from Network Driver
|
||||||
|
|
@ -397,15 +591,35 @@ proc ion_ingress*(id: uint16, len: uint16, offset: uint16) {.exportc, cdecl.} =
|
||||||
if not chan_netswitch_rx.send(pkt):
|
if not chan_netswitch_rx.send(pkt):
|
||||||
ion_free_raw(id)
|
ion_free_raw(id)
|
||||||
|
|
||||||
|
|
||||||
|
# --- SCHEDULER STATE ---
|
||||||
|
# --- SCHEDULER STATE ---
|
||||||
|
|
||||||
proc ion_push_stdin*(p: pointer, len: csize_t) {.exportc, cdecl.} =
|
proc ion_push_stdin*(p: pointer, len: csize_t) {.exportc, cdecl.} =
|
||||||
if chan_input.ring == nil: return
|
if chan_input.ring == nil: return
|
||||||
|
|
||||||
var pkt = ion_alloc()
|
var pkt = ion_alloc()
|
||||||
if pkt.data == nil: return
|
if pkt.data == nil:
|
||||||
let to_copy = if int(len) < 2048: int(len) else: 2048
|
# kprintln("[ION Push] CRITICAL: Slab allocation failed!")
|
||||||
copyMem(pkt.data, p, to_copy)
|
return
|
||||||
pkt.len = uint16(to_copy)
|
|
||||||
if fiber_subject.sleep_until == 0xFFFFFFFFFFFFFFFF'u64: fiber_subject.sleep_until = 0
|
pkt.data[0] = cast[ptr byte](p)[]
|
||||||
discard chan_input.send(pkt)
|
pkt.len = 1
|
||||||
|
|
||||||
|
var wake_count = 0
|
||||||
|
if chan_input.send(pkt):
|
||||||
|
# Wake up any fibers waiting for terminal input
|
||||||
|
for i in 0..<16:
|
||||||
|
let f = active_fibers_arr[i]
|
||||||
|
if f != nil and f.is_blocked and (f.blocked_on_mask and 0x1000) != 0:
|
||||||
|
f.is_blocked = false
|
||||||
|
f.blocked_on_mask = 0
|
||||||
|
f.sleep_until = 0 # Wake immediately
|
||||||
|
wake_count += 1
|
||||||
|
|
||||||
|
if wake_count > 0:
|
||||||
|
# kprint("[ION] Woke "); kprint_hex(uint64(wake_count)); kprintln(" fibers")
|
||||||
|
discard
|
||||||
|
|
||||||
proc k_check_deferred_yield*() {.exportc, cdecl.} =
|
proc k_check_deferred_yield*() {.exportc, cdecl.} =
|
||||||
if current_fiber != nil and current_fiber.wants_yield:
|
if current_fiber != nil and current_fiber.wants_yield:
|
||||||
|
|
@ -449,8 +663,14 @@ proc k_handle_exception*(scause, sepc, stval: uint) {.exportc, cdecl.} =
|
||||||
|
|
||||||
kprintln("")
|
kprintln("")
|
||||||
kprintln("[IMMUNE] System HALTING (Trap Loop Prevention).")
|
kprintln("[IMMUNE] System HALTING (Trap Loop Prevention).")
|
||||||
|
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:
|
||||||
|
while true: discard
|
||||||
|
|
||||||
proc k_get_current_satp*(): uint64 {.exportc, cdecl.} =
|
proc k_get_current_satp*(): uint64 {.exportc, cdecl.} =
|
||||||
if current_fiber != nil:
|
if current_fiber != nil:
|
||||||
|
|
@ -460,9 +680,40 @@ proc k_get_current_satp*(): uint64 {.exportc, cdecl.} =
|
||||||
proc wrapper_vfs_write(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} =
|
proc wrapper_vfs_write(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} =
|
||||||
return ion_vfs_write(fd, buf, count)
|
return ion_vfs_write(fd, buf, count)
|
||||||
|
|
||||||
|
# --- M4: PLEDGE ENFORCEMENT ---
|
||||||
|
|
||||||
|
proc check_pledge(nr: uint, f: Fiber): bool =
|
||||||
|
## Returns true if syscall is allowed by fiber's pledge mask
|
||||||
|
let pledges = f.promises and 0x3FFFFFFFFFFFFFFF'u64
|
||||||
|
if pledges == PLEDGE_ALL: return true # Unrestricted
|
||||||
|
|
||||||
|
case nr:
|
||||||
|
of 0x01, 0x65, 0x66, 0x100, 0x101, 0x102:
|
||||||
|
# exit, nanosleep, get_time, yield, pledge, wait_multi — always allowed
|
||||||
|
return true
|
||||||
|
of 0x203, 0x204: # READ, WRITE
|
||||||
|
return (pledges and PLEDGE_STDIO) != 0
|
||||||
|
of 0x200, 0x202: # OPEN, LIST
|
||||||
|
return (pledges and PLEDGE_RPATH) != 0
|
||||||
|
of 0x201, 0x205, 0x206, 0x207: # CLOSE, IOCTL, FCNTL, DUP2
|
||||||
|
return (pledges and PLEDGE_STDIO) != 0
|
||||||
|
of 0x600, 0x300: # EXECV, SPAWN_FIBER
|
||||||
|
return (pledges and PLEDGE_EXEC) != 0
|
||||||
|
of 0x905: # SYS_SOCK_RESOLVE
|
||||||
|
return (pledges and PLEDGE_INET) != 0
|
||||||
|
else:
|
||||||
|
return true # Unknown syscalls pass through
|
||||||
|
|
||||||
# --- SYSCALL HANDLER ---
|
# --- SYSCALL HANDLER ---
|
||||||
|
|
||||||
proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
|
proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
|
||||||
|
# M4: Pledge enforcement gate
|
||||||
|
if current_fiber != nil and not check_pledge(nr, current_fiber):
|
||||||
|
kprint("[DENIED] Fiber "); kprint_hex(current_fiber.id)
|
||||||
|
kprint(" pledge violation: syscall "); kprint_hex(uint64(nr)); kprintln("")
|
||||||
|
discard emit_access_denied(current_fiber.id, uint64(nr), 0, 0)
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u # -1 EPERM
|
||||||
|
|
||||||
if nr != 0x100 and nr != 0x205 and nr != 0x204 and nr != 0x203:
|
if nr != 0x100 and nr != 0x205 and nr != 0x204 and nr != 0x203:
|
||||||
kprint("[Syscall] NR: "); kprint_hex(uint64(nr)); kprintln("")
|
kprint("[Syscall] NR: "); kprint_hex(uint64(nr)); kprintln("")
|
||||||
|
|
||||||
|
|
@ -484,60 +735,113 @@ proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
|
||||||
of 0x100: # YIELD
|
of 0x100: # YIELD
|
||||||
fiber_yield()
|
fiber_yield()
|
||||||
return 0
|
return 0
|
||||||
|
of 0x101: # SYS_PLEDGE (OpenBSD semantics: can only narrow, never widen)
|
||||||
|
let current = current_fiber.promises and 0x3FFFFFFFFFFFFFFF'u64
|
||||||
|
let requested = a0 and 0x3FFFFFFFFFFFFFFF'u64
|
||||||
|
if (requested and (not current)) != 0:
|
||||||
|
kprint("[DENIED] Fiber "); kprint_hex(current_fiber.id); kprintln(" pledge widen attempt")
|
||||||
|
discard emit_access_denied(current_fiber.id, 0x101, 0, 0)
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u # -1 EPERM
|
||||||
|
current_fiber.promises = (current_fiber.promises and 0xC000000000000000'u64) or requested
|
||||||
|
return 0
|
||||||
of 0x102: # SYS_WAIT_MULTI (Silence Doctrine)
|
of 0x102: # SYS_WAIT_MULTI (Silence Doctrine)
|
||||||
current_fiber.blocked_on_mask = a0
|
current_fiber.blocked_on_mask = a0
|
||||||
current_fiber.is_blocked = true
|
current_fiber.is_blocked = true
|
||||||
fiber_yield()
|
fiber_yield()
|
||||||
return 0
|
return 0
|
||||||
of 0x200: # OPEN
|
of 0x200: # OPEN
|
||||||
# return uint(libc_impl.libc_impl_open(cast[cstring](a0), int(a1)))
|
# M4: Check VFS capability (channel 0x2000)
|
||||||
return 0
|
if current_fiber != nil and current_fiber.cspace_id < 16:
|
||||||
|
let write_mode = (a1 and 1) != 0 # O_WRONLY or O_RDWR
|
||||||
|
let needed_perm = if write_mode: PERM_WRITE else: PERM_READ
|
||||||
|
if not cspace_check_channel(current_fiber.cspace_id, 0x2000, needed_perm):
|
||||||
|
discard emit_access_denied(current_fiber.id, 0x2000, needed_perm, 0)
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u
|
||||||
|
let path = cast[cstring](a0)
|
||||||
|
let result = ion_vfs_open(path, int32(a1))
|
||||||
|
kprint("[OPEN] path="); kprint(path); kprint(" result="); kprint_hex(uint64(result)); kprint(" returning...\n")
|
||||||
|
let ret_val = uint(result)
|
||||||
|
kprint("[OPEN] About to return value: "); kprint_hex(ret_val); kprint("\n")
|
||||||
|
return ret_val
|
||||||
of 0x201: # CLOSE
|
of 0x201: # CLOSE
|
||||||
# return uint(libc_impl.libc_impl_close(int(a0)))
|
kprint("[CLOSE] fd="); kprint_hex(a0); kprint("\n")
|
||||||
return 0
|
return uint(ion_vfs_close(int32(a0)))
|
||||||
of 0x202: # LIST
|
of 0x202: # LIST
|
||||||
|
# M4: Check VFS read capability (channel 0x2000)
|
||||||
|
if current_fiber != nil and current_fiber.cspace_id < 16:
|
||||||
|
if not cspace_check_channel(current_fiber.cspace_id, 0x2000, PERM_READ):
|
||||||
|
discard emit_access_denied(current_fiber.id, 0x2000, PERM_READ, 0)
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u
|
||||||
return uint(ion_vfs_list(cast[pointer](a0), uint64(a1)))
|
return uint(ion_vfs_list(cast[pointer](a0), uint64(a1)))
|
||||||
|
of 0x205: # IOCTL
|
||||||
|
kprint("[IOCTL] fd="); kprint_hex(a0); kprint(" request="); kprint_hex(a1); kprint("\n")
|
||||||
|
return 0 # stub
|
||||||
|
of 0x206: # FCNTL
|
||||||
|
kprint("[FCNTL] fd="); kprint_hex(a0); kprint(" cmd="); kprint_hex(a1); kprint("\n")
|
||||||
|
if a1 == 0: # F_DUPFD
|
||||||
|
return uint(ion_vfs_dup(int32(a0), int32(a2)))
|
||||||
|
return 0
|
||||||
|
of 0x207: # DUP2
|
||||||
|
return uint(ion_vfs_dup2(int32(a0), int32(a1)))
|
||||||
of 0x905: # SYS_SOCK_RESOLVE
|
of 0x905: # SYS_SOCK_RESOLVE
|
||||||
# TODO: Implement getaddrinfo kernel integration
|
# TODO: Implement getaddrinfo kernel integration
|
||||||
return 0 # Not implemented yet
|
return 0 # Not implemented yet
|
||||||
of 0x203: # READ
|
of 0x203: # READ
|
||||||
# kprint("[Syscall] READ(fd="); kprint_hex(a0); kprint(")\n")
|
# M4: Check console.input capability for stdin (fd 0)
|
||||||
var vres = -2
|
if a0 == 0 and current_fiber != nil and current_fiber.cspace_id < 16:
|
||||||
|
if not cspace_check_channel(current_fiber.cspace_id, 0x1000, PERM_READ):
|
||||||
|
discard emit_access_denied(current_fiber.id, 0x1000, PERM_READ, 0)
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u
|
||||||
|
# M4: Check VFS capability for file reads (fd >= 3)
|
||||||
|
if a0 >= 3 and current_fiber != nil and current_fiber.cspace_id < 16:
|
||||||
|
if not cspace_check_channel(current_fiber.cspace_id, 0x2000, PERM_READ):
|
||||||
|
discard emit_access_denied(current_fiber.id, 0x2000, PERM_READ, 0)
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u
|
||||||
|
var vres: int64 = -2
|
||||||
|
if a0 >= 3:
|
||||||
|
vres = ion_vfs_read(int32(a0), cast[pointer](a1), uint64(a2))
|
||||||
|
|
||||||
|
# Fallback to PTY if FD=0 or VFS returned TTY mode (-2)
|
||||||
if a0 == 0 or vres == -2:
|
if a0 == 0 or vres == -2:
|
||||||
let pid = if current_fiber.pty_id >= 0: current_fiber.pty_id else: 0
|
let pid = if current_fiber.pty_id >= 0: current_fiber.pty_id else: 0
|
||||||
while true:
|
while true:
|
||||||
if pty_has_data_for_slave(pid):
|
if pty_has_data_for_slave(pid):
|
||||||
var buf: array[1, byte]
|
var buf: array[1, byte]
|
||||||
let n = pty_read_slave(PTY_SLAVE_BASE + pid, addr buf[0], 1)
|
let n = pty_read_slave(int(PTY_SLAVE_BASE + pid), addr buf[0], 1)
|
||||||
if n > 0:
|
if n > 0:
|
||||||
# kprint("[Kernel] READ delivered PTY byte: "); kprint_hex8(buf[0]); kprint("\n")
|
|
||||||
cast[ptr UncheckedArray[byte]](a1)[0] = buf[0]
|
cast[ptr UncheckedArray[byte]](a1)[0] = buf[0]
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
var pkt: IonPacket
|
var pkt: IonPacket
|
||||||
if chan_input.recv(pkt):
|
if chan_input.recv(pkt):
|
||||||
kprint("[Kernel] Got Input Packet of len: "); kprint_hex(uint64(pkt.len)); kprint("\n")
|
for i in 0..<int(pkt.len):
|
||||||
let n = if uint64(pkt.len) < a2: uint64(pkt.len) else: a2
|
pty_push_input(pid, char(pkt.data[i]))
|
||||||
if n > 0:
|
ion_free(pkt)
|
||||||
let data = cast[ptr UncheckedArray[byte]](pkt.data)
|
|
||||||
for i in 0 ..< int(n):
|
|
||||||
kprint(" Input Char: "); kprint(cast[cstring](unsafeAddr data[i])); kprint("\n")
|
|
||||||
pty_push_input(pid, char(data[i]))
|
|
||||||
ion_free_raw(pkt.id)
|
|
||||||
# Loop again to read from PTY
|
|
||||||
else:
|
else:
|
||||||
current_fiber.sleep_until = 0xFFFFFFFFFFFFFFFF'u64
|
current_fiber.is_blocked = true
|
||||||
|
current_fiber.blocked_on_mask = 0x1000 # Terminal Input
|
||||||
|
current_fiber.sleep_until = 0xFFFFFFFFFFFFFFFF'u64 # Infinite
|
||||||
fiber_yield()
|
fiber_yield()
|
||||||
return uint(vres)
|
return uint(vres)
|
||||||
of 0x204: # WRITE
|
of 0x204: # WRITE
|
||||||
if a0 == 1 or a0 == 2:
|
if a0 == 1 or a0 == 2:
|
||||||
|
# M4: Check console.output capability (channel 0x1001)
|
||||||
|
if current_fiber != nil and current_fiber.cspace_id < 16:
|
||||||
|
if not cspace_check_channel(current_fiber.cspace_id, 0x1001, PERM_WRITE):
|
||||||
|
discard emit_access_denied(current_fiber.id, 0x1001, PERM_WRITE, 0)
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u
|
||||||
console_write(cast[pointer](a1), csize_t(a2))
|
console_write(cast[pointer](a1), csize_t(a2))
|
||||||
let pid = if current_fiber.pty_id >= 0: current_fiber.pty_id else: 0
|
let pid = if current_fiber.pty_id >= 0: current_fiber.pty_id else: 0
|
||||||
discard pty_write_slave(PTY_SLAVE_BASE + pid, cast[ptr byte](a1), int(a2))
|
discard pty_write_slave(PTY_SLAVE_BASE + pid, cast[ptr byte](a1), int(a2))
|
||||||
return a2
|
return a2
|
||||||
# var vres = libc_impl.libc_impl_write(int(a0), cast[pointer](a1), uint64(a2))
|
elif a0 >= 3:
|
||||||
var vres = -1
|
# M4: Check VFS write capability (channel 0x2000)
|
||||||
return uint(vres)
|
if current_fiber != nil and current_fiber.cspace_id < 16:
|
||||||
of 0x205: return 0 # IOCTL stub
|
if not cspace_check_channel(current_fiber.cspace_id, 0x2000, PERM_WRITE):
|
||||||
|
discard emit_access_denied(current_fiber.id, 0x2000, PERM_WRITE, 0)
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u
|
||||||
|
return uint(ion_vfs_write(int32(a0), cast[pointer](a1), uint64(a2)))
|
||||||
|
return 0xFFFFFFFFFFFFFFFF'u # -1
|
||||||
of 0x600: # EXECV (Legacy - use SYS_SPAWN_FIBER instead)
|
of 0x600: # EXECV (Legacy - use SYS_SPAWN_FIBER instead)
|
||||||
# Manual copy path to subject_loading_path
|
# Manual copy path to subject_loading_path
|
||||||
let p = cast[ptr UncheckedArray[char]](a0)
|
let p = cast[ptr UncheckedArray[char]](a0)
|
||||||
|
|
@ -585,8 +889,6 @@ proc ion_wait_multi*(mask: uint64): int32 {.exportc, cdecl.} =
|
||||||
|
|
||||||
proc kmain() {.exportc, cdecl.} =
|
proc kmain() {.exportc, cdecl.} =
|
||||||
var next_mmio_addr {.importc: "virtio_pci_next_mmio_addr", nodecl.}: uint32
|
var next_mmio_addr {.importc: "virtio_pci_next_mmio_addr", nodecl.}: uint32
|
||||||
kprint("\n[Kernel] next_mmio_addr check: ")
|
|
||||||
kprint_hex(uint64(next_mmio_addr))
|
|
||||||
kprintln("\nNexus Sovereign Core v1.1.2 Starting...")
|
kprintln("\nNexus Sovereign Core v1.1.2 Starting...")
|
||||||
|
|
||||||
# HAL Hardware Inits
|
# HAL Hardware Inits
|
||||||
|
|
@ -636,6 +938,8 @@ proc kmain() {.exportc, cdecl.} =
|
||||||
sys.fn_vfs_read = ion_vfs_read
|
sys.fn_vfs_read = ion_vfs_read
|
||||||
sys.fn_vfs_list = ion_vfs_list
|
sys.fn_vfs_list = ion_vfs_list
|
||||||
sys.fn_vfs_write = wrapper_vfs_write
|
sys.fn_vfs_write = wrapper_vfs_write
|
||||||
|
sys.fn_vfs_dup = ion_vfs_dup
|
||||||
|
sys.fn_vfs_dup2 = ion_vfs_dup2
|
||||||
sys.fn_wait_multi = ion_wait_multi
|
sys.fn_wait_multi = ion_wait_multi
|
||||||
# Point to user slab allocator (shared memory) instead of kernel pool
|
# Point to user slab allocator (shared memory) instead of kernel pool
|
||||||
sys.fn_ion_alloc = ion_user_alloc_systable
|
sys.fn_ion_alloc = ion_user_alloc_systable
|
||||||
|
|
@ -669,11 +973,16 @@ proc kmain() {.exportc, cdecl.} =
|
||||||
chan_net_rx.ring = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0xC000)
|
chan_net_rx.ring = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0xC000)
|
||||||
chan_net_tx.ring = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0xE000)
|
chan_net_tx.ring = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0xE000)
|
||||||
|
|
||||||
|
# Project LibWeb: LWF Rings (after user slab at 0x110000)
|
||||||
|
chan_lwf_rx.ring = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0x110000)
|
||||||
|
chan_lwf_tx.ring = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0x112000)
|
||||||
|
|
||||||
# Initialize Shared Memory Rings
|
# Initialize Shared Memory Rings
|
||||||
chan_rx.ring.mask = 255; chan_tx.ring.mask = 255
|
chan_rx.ring.mask = 255; chan_tx.ring.mask = 255
|
||||||
ring_event.mask = 255; chan_cmd.ring.mask = 255
|
ring_event.mask = 255; chan_cmd.ring.mask = 255
|
||||||
chan_input.ring.mask = 255
|
chan_input.ring.mask = 255
|
||||||
chan_net_rx.ring.mask = 255; chan_net_tx.ring.mask = 255
|
chan_net_rx.ring.mask = 255; chan_net_tx.ring.mask = 255
|
||||||
|
chan_lwf_rx.ring.mask = 255; chan_lwf_tx.ring.mask = 255
|
||||||
# Force reset pointers to zero
|
# Force reset pointers to zero
|
||||||
chan_rx.ring.head = 0; chan_rx.ring.tail = 0
|
chan_rx.ring.head = 0; chan_rx.ring.tail = 0
|
||||||
chan_tx.ring.head = 0; chan_tx.ring.tail = 0
|
chan_tx.ring.head = 0; chan_tx.ring.tail = 0
|
||||||
|
|
@ -682,6 +991,8 @@ proc kmain() {.exportc, cdecl.} =
|
||||||
chan_input.ring.head = 0; chan_input.ring.tail = 0
|
chan_input.ring.head = 0; chan_input.ring.tail = 0
|
||||||
chan_net_rx.ring.head = 0; chan_net_rx.ring.tail = 0
|
chan_net_rx.ring.head = 0; chan_net_rx.ring.tail = 0
|
||||||
chan_net_tx.ring.head = 0; chan_net_tx.ring.tail = 0
|
chan_net_tx.ring.head = 0; chan_net_tx.ring.tail = 0
|
||||||
|
chan_lwf_rx.ring.head = 0; chan_lwf_rx.ring.tail = 0
|
||||||
|
chan_lwf_tx.ring.head = 0; chan_lwf_tx.ring.tail = 0
|
||||||
|
|
||||||
sys.s_rx = chan_rx.ring; sys.s_tx = chan_tx.ring; sys.s_event = ring_event
|
sys.s_rx = chan_rx.ring; sys.s_tx = chan_tx.ring; sys.s_event = ring_event
|
||||||
sys.s_cmd = chan_cmd.ring; sys.s_input = chan_input.ring
|
sys.s_cmd = chan_cmd.ring; sys.s_input = chan_input.ring
|
||||||
|
|
@ -690,6 +1001,10 @@ proc kmain() {.exportc, cdecl.} =
|
||||||
sys.s_net_rx = chan_net_rx.ring
|
sys.s_net_rx = chan_net_rx.ring
|
||||||
sys.s_net_tx = chan_net_tx.ring
|
sys.s_net_tx = chan_net_tx.ring
|
||||||
|
|
||||||
|
# Project LibWeb: Map LWF Rings
|
||||||
|
sys.s_lwf_rx = chan_lwf_rx.ring
|
||||||
|
sys.s_lwf_tx = chan_lwf_tx.ring
|
||||||
|
|
||||||
sys.magic = 0x4E585553
|
sys.magic = 0x4E585553
|
||||||
sys.fb_addr = fb_kern_get_addr()
|
sys.fb_addr = fb_kern_get_addr()
|
||||||
sys.fb_width = 1920; sys.fb_height = 1080; sys.fb_stride = 1920 * 4; sys.fb_bpp = 32
|
sys.fb_width = 1920; sys.fb_height = 1080; sys.fb_stride = 1920 * 4; sys.fb_bpp = 32
|
||||||
|
|
@ -726,46 +1041,52 @@ proc kmain() {.exportc, cdecl.} =
|
||||||
discard emit_capability_grant(2, 2, 0x1001, 1, shell_spawn_id) # Log event
|
discard emit_capability_grant(2, 2, 0x1001, 1, shell_spawn_id) # Log event
|
||||||
kprintln("[CSpace] Granted console capabilities to NexShell")
|
kprintln("[CSpace] Granted console capabilities to NexShell")
|
||||||
|
|
||||||
# Grant console output to Subject (fiber 4)
|
# M4.4: Subject (fiber 4) capabilities are now manifest-driven.
|
||||||
discard fiber_grant_channel(4, 0x1001, PERM_WRITE) # console.output (write-only)
|
# The BKDL manifest in the ELF binary declares what it needs.
|
||||||
discard emit_capability_grant(4, 2, 0x1001, 0, subject_spawn_id) # Log event
|
# Grants are applied in subject_fiber_entry via kload_manifest_tar + apply_manifest.
|
||||||
kprintln("[CSpace] Granted output capability to Subject")
|
kprintln("[CSpace] Subject capabilities deferred to BKDL manifest")
|
||||||
|
|
||||||
# Grant Network I/O (RX/TX)
|
# Grant Network I/O (RX/TX) — kernel fibers only
|
||||||
# NetSwitch (Fiber 6): Full access to shuttle packets
|
# NetSwitch (Fiber 6): Full access to shuttle packets
|
||||||
discard fiber_grant_channel(6, 0x500, PERM_READ or PERM_WRITE) # CMD_NET_TX
|
discard fiber_grant_channel(6, 0x500, PERM_READ or PERM_WRITE) # CMD_NET_TX
|
||||||
discard fiber_grant_channel(6, 0x501, PERM_READ or PERM_WRITE) # CMD_NET_RX
|
discard fiber_grant_channel(6, 0x501, PERM_READ or PERM_WRITE) # CMD_NET_RX
|
||||||
|
kprintln("[CSpace] Granted network capabilities to NetSwitch")
|
||||||
|
|
||||||
# Subject (Fiber 4): Needs to READ RX (0x501) and WRITE TX (0x500)
|
# M4: Grant VFS capabilities — kernel fibers only
|
||||||
discard fiber_grant_channel(4, 0x500, PERM_WRITE) # Can send packets
|
discard fiber_grant_channel(1, 0x2000, PERM_READ or PERM_WRITE) # ION: VFS (ELF loading)
|
||||||
discard fiber_grant_channel(4, 0x501, PERM_READ) # Can receive packets
|
discard fiber_grant_channel(1, 0x1000, PERM_READ or PERM_WRITE) # ION: console.input
|
||||||
kprintln("[CSpace] Granted network capabilities to NetSwitch and Subject")
|
discard fiber_grant_channel(1, 0x1001, PERM_READ or PERM_WRITE) # ION: console.output
|
||||||
|
discard fiber_grant_channel(2, 0x2000, PERM_READ or PERM_WRITE) # NexShell: VFS
|
||||||
|
kprintln("[CSpace] Granted VFS capabilities to kernel fibers")
|
||||||
|
|
||||||
# Init (Subject) lives in Cell 0 (0x88000000) - Needs 64MB for large BSS
|
# Init (Subject) lives in Cell 0 — Needs 64MB for large BSS
|
||||||
fiber_subject.phys_offset = 0x88000000'u64
|
fiber_subject.phys_offset = CELL0_BASE
|
||||||
let init_size = 64 * 1024 * 1024'u64
|
let init_size = 64 * 1024 * 1024'u64
|
||||||
fiber_subject.satp_value = mm_create_worker_map(cast[uint64](addr stack_subject[0]), uint64(sizeof(stack_subject)), SYSTABLE_BASE, fiber_subject.phys_offset, init_size)
|
fiber_subject.satp_value = mm_create_worker_map(cast[uint64](addr stack_subject[0]), uint64(sizeof(stack_subject)), SYSTABLE_BASE, fiber_subject.phys_offset, init_size)
|
||||||
|
|
||||||
# Interrupt Setup
|
# Interrupt Setup (Architecture-specific)
|
||||||
|
when defined(riscv64):
|
||||||
asm "csrsi sstatus, 2"
|
asm "csrsi sstatus, 2"
|
||||||
{.emit: "asm volatile(\"csrs sie, %0\" : : \"r\"(1L << 9));".}
|
{.emit: "asm volatile(\"csrs sie, %0\" : : \"r\"(1L << 9));".}
|
||||||
let plic_base = 0x0c000000'u64
|
let plic_base = 0x0c000000'u64
|
||||||
# Priority (each IRQ has a 4-byte priority register)
|
# Priority (each IRQ has a 4-byte priority register)
|
||||||
cast[ptr uint32](plic_base + 40)[] = 1 # UART (IRQ 10: 10*4 = 40)
|
cast[ptr uint32](plic_base + 40)[] = 1 # UART (IRQ 10: 10*4 = 40)
|
||||||
# cast[ptr uint32](plic_base + 128)[] = 1 # VirtIO-Net (IRQ 32: 32*4 = 128)
|
|
||||||
# cast[ptr uint32](plic_base + 132)[] = 1 # VirtIO-Net (IRQ 33: 33*4 = 132)
|
|
||||||
# cast[ptr uint32](plic_base + 136)[] = 1 # VirtIO-Net (IRQ 34: 34*4 = 136)
|
|
||||||
# cast[ptr uint32](plic_base + 140)[] = 1 # VirtIO-Net (IRQ 35: 35*4 = 140)
|
|
||||||
|
|
||||||
# Enable (Supervisor Context 1)
|
# Enable (Supervisor Context 1)
|
||||||
# IRQs 0-31 (Enable IRQ 10 = UART)
|
# IRQs 0-31 (Enable IRQ 10 = UART)
|
||||||
cast[ptr uint32](plic_base + 0x2000 + 0x80)[] = (1'u32 shl 10)
|
cast[ptr uint32](plic_base + 0x2000 + 0x80)[] = (1'u32 shl 10)
|
||||||
# IRQs 32-63
|
|
||||||
# cast[ptr uint32](plic_base + 0x2000 + 0x80 + 4)[] = 0x0000000F # Enable 32,33,34,35
|
|
||||||
|
|
||||||
# Threshold
|
# Threshold
|
||||||
cast[ptr uint32](plic_base + 0x201000)[] = 0
|
cast[ptr uint32](plic_base + 0x201000)[] = 0
|
||||||
|
|
||||||
|
let en_addr = plic_base + 0x2000 + 0x80
|
||||||
|
let en_val = cast[ptr uint32](en_addr)[]
|
||||||
|
kprint("[PLIC] UART Enable Register at "); kprint_hex(en_addr)
|
||||||
|
kprint(" is "); kprint_hex(uint64(en_val)); kprintln("")
|
||||||
|
elif defined(arm64):
|
||||||
|
# GICv2 is initialized in aarch64_init() before Nim entry
|
||||||
|
kprintln("[GIC] Interrupts configured by HAL")
|
||||||
|
|
||||||
active_fibers_arr[0] = addr fiber_ion; active_fibers_arr[1] = addr fiber_nexshell
|
active_fibers_arr[0] = addr fiber_ion; active_fibers_arr[1] = addr fiber_nexshell
|
||||||
active_fibers_arr[2] = addr fiber_compositor; active_fibers_arr[3] = addr fiber_netswitch
|
active_fibers_arr[2] = addr fiber_compositor; active_fibers_arr[3] = addr fiber_netswitch
|
||||||
active_fibers_arr[4] = addr fiber_subject
|
active_fibers_arr[4] = addr fiber_subject
|
||||||
|
|
@ -777,28 +1098,42 @@ proc kmain() {.exportc, cdecl.} =
|
||||||
(addr fiber_compositor).setSpectrum(Spectrum.Photon)
|
(addr fiber_compositor).setSpectrum(Spectrum.Photon)
|
||||||
(addr fiber_netswitch).setSpectrum(Spectrum.Photon)
|
(addr fiber_netswitch).setSpectrum(Spectrum.Photon)
|
||||||
(addr fiber_nexshell).setSpectrum(Spectrum.Matter) # Interactive
|
(addr fiber_nexshell).setSpectrum(Spectrum.Matter) # Interactive
|
||||||
(addr fiber_subject).setSpectrum(Spectrum.Void) # Untrusted Background
|
(addr fiber_subject).setSpectrum(Spectrum.Matter) # Elevated from Void
|
||||||
(addr fiber_child).setSpectrum(Spectrum.Void) # Child process (spawned)
|
(addr fiber_child).setSpectrum(Spectrum.Matter) # Elevated from Void
|
||||||
current_fiber.setSpectrum(Spectrum.Void) # Main loop (dispatcher)
|
current_fiber.setSpectrum(Spectrum.Void) # Main loop (dispatcher)
|
||||||
|
|
||||||
|
# M4.5: Kinetic Economy — default budgets per Spectrum tier
|
||||||
|
(addr fiber_ion).budget_ns = default_budget_for_spectrum(Spectrum.Photon)
|
||||||
|
(addr fiber_compositor).budget_ns = default_budget_for_spectrum(Spectrum.Photon)
|
||||||
|
(addr fiber_netswitch).budget_ns = default_budget_for_spectrum(Spectrum.Photon)
|
||||||
|
(addr fiber_nexshell).budget_ns = default_budget_for_spectrum(Spectrum.Matter)
|
||||||
|
(addr fiber_subject).budget_ns = default_budget_for_spectrum(Spectrum.Matter)
|
||||||
|
(addr fiber_child).budget_ns = default_budget_for_spectrum(Spectrum.Matter)
|
||||||
|
# Void (main loop): budget_ns = 0 → unlimited (already default)
|
||||||
|
|
||||||
|
# M4: Set initial pledge masks (using top-level set_pledge)
|
||||||
|
fiber_ion.set_pledge(PLEDGE_ALL)
|
||||||
|
fiber_compositor.set_pledge(PLEDGE_ALL)
|
||||||
|
fiber_netswitch.set_pledge(PLEDGE_ALL)
|
||||||
|
fiber_nexshell.set_pledge(PLEDGE_STDIO or PLEDGE_RPATH or PLEDGE_WPATH or PLEDGE_EXEC)
|
||||||
|
# M4.4: Subject/child pledge is set by BKDL manifest in subject_fiber_entry.
|
||||||
|
# Start with PLEDGE_STDIO as safe default (manifest will override).
|
||||||
|
fiber_subject.set_pledge(PLEDGE_STDIO)
|
||||||
|
fiber_child.set_pledge(PLEDGE_STDIO)
|
||||||
|
kprintln("[M4] Pledge masks applied (Subject/child: manifest-driven)")
|
||||||
|
|
||||||
# Ground Zero Phase 2: Introspection
|
# Ground Zero Phase 2: Introspection
|
||||||
stl_print_summary()
|
stl_print_summary()
|
||||||
|
|
||||||
kprintln("[Rumpk] Multi-Fiber Dispatcher starting...")
|
kprintln("[Rumpk] Multi-Fiber Dispatcher starting...")
|
||||||
switch(addr fiber_ion)
|
switch(addr fiber_ion)
|
||||||
|
# Exported from Zig
|
||||||
|
proc uart_poll_input() {.importc, cdecl.}
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
if not sched_tick_spectrum(active_fibers_arr.toOpenArray(0, 5)):
|
# ⌨️ Hardware Input Driver (Polling fallback)
|
||||||
# The Silence Doctrine: Wait for Interrupt
|
uart_poll_input()
|
||||||
let next_wake = sched_get_next_wakeup(active_fibers_arr.toOpenArray(0, 5))
|
|
||||||
let now = sched_get_now_ns()
|
|
||||||
|
|
||||||
if next_wake > now and next_wake != 0xFFFFFFFFFFFFFFFF'u64:
|
if not sched_tick_spectrum(active_fibers_arr):
|
||||||
proc rumpk_timer_set_ns(ns: uint64) {.importc, cdecl.}
|
# Wait for data or timeout
|
||||||
# kprint("Interval: "); kprint_hex(next_wake - now); kprintln("")
|
fiber_sleep(1)
|
||||||
rumpk_timer_set_ns(next_wake - now) # Pass interval
|
|
||||||
else:
|
|
||||||
# No timer needed (or overdue), just WFI for other interrupts (IO)
|
|
||||||
discard
|
|
||||||
|
|
||||||
asm "csrsi sstatus, 2"
|
|
||||||
asm "wfi"
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,76 @@ 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,8 +37,48 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,11 @@ 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.}
|
||||||
|
|
@ -73,6 +78,11 @@ 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)
|
||||||
|
|
@ -86,6 +96,21 @@ proc netswitch_process_packet(pkt: IonPacket): bool =
|
||||||
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)
|
||||||
|
|
@ -116,7 +141,7 @@ proc fiber_netswitch_entry*() {.cdecl.} =
|
||||||
rx_activity = true
|
rx_activity = true
|
||||||
inc rx_count
|
inc rx_count
|
||||||
|
|
||||||
# 3. TX PATH: Userland -> Hardware
|
# 3. TX PATH: Userland -> Hardware (Legacy/LwIP)
|
||||||
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:
|
||||||
|
|
@ -125,6 +150,15 @@ proc fiber_netswitch_entry*() {.cdecl.} =
|
||||||
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,6 +161,24 @@ 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)
|
||||||
|
|
|
||||||
|
|
@ -175,11 +175,18 @@ 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:
|
||||||
term_render()
|
term_render()
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,18 @@ import fiber
|
||||||
# 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.}
|
||||||
|
|
@ -63,11 +75,17 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
# 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 f in fibers:
|
for i in 0..<fibers.len:
|
||||||
|
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:
|
||||||
switch(f); return true
|
photon_idx = (idx + 1) mod fibers.len
|
||||||
|
let t0 = sched_get_now_ns()
|
||||||
|
switch(f)
|
||||||
|
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
||||||
|
return true
|
||||||
else:
|
else:
|
||||||
run_photon = true
|
run_photon = true
|
||||||
if run_photon: return true
|
if run_photon: return true
|
||||||
|
|
@ -76,11 +94,17 @@ 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 f in fibers:
|
for i in 0..<fibers.len:
|
||||||
|
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:
|
||||||
switch(f); return true
|
matter_idx = (idx + 1) mod fibers.len
|
||||||
|
let t0 = sched_get_now_ns()
|
||||||
|
switch(f)
|
||||||
|
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
||||||
|
return true
|
||||||
else:
|
else:
|
||||||
run_matter = true
|
run_matter = true
|
||||||
if run_matter: return true
|
if run_matter: return true
|
||||||
|
|
@ -89,11 +113,17 @@ 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 f in fibers:
|
for i in 0..<fibers.len:
|
||||||
|
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:
|
||||||
switch(f); return true
|
gravity_idx = (idx + 1) mod fibers.len
|
||||||
|
let t0 = sched_get_now_ns()
|
||||||
|
switch(f)
|
||||||
|
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
||||||
|
return true
|
||||||
else:
|
else:
|
||||||
run_gravity = true
|
run_gravity = true
|
||||||
if run_gravity: return true
|
if run_gravity: return true
|
||||||
|
|
@ -101,11 +131,16 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Phase 4: VOID (Scavenger)
|
# Phase 4: VOID (Scavenger)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
for f in fibers:
|
for i in 0..<fibers.len:
|
||||||
|
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
|
||||||
|
|
@ -128,10 +163,20 @@ proc sched_get_next_wakeup*(fibers: openArray[ptr FiberObject]): uint64 =
|
||||||
return min_wakeup
|
return min_wakeup
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# THE RATCHET (Post-Execution Analysis)
|
# M4.5: Budget Defaults Per Spectrum Tier
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
proc default_budget_for_spectrum*(s: Spectrum): uint64 =
|
||||||
|
## Return the default budget_ns for a given Spectrum tier
|
||||||
|
case s
|
||||||
|
of Spectrum.Photon: return 2_000_000'u64 # 2ms — hard real-time
|
||||||
|
of Spectrum.Matter: return 10_000_000'u64 # 10ms — interactive
|
||||||
|
of Spectrum.Gravity: return 50_000_000'u64 # 50ms — batch
|
||||||
|
of Spectrum.Void: return 0'u64 # unlimited — scavenger
|
||||||
|
|
||||||
|
# =========================================================
|
||||||
|
# THE RATCHET (Post-Execution Analysis)
|
||||||
|
# =========================================================
|
||||||
|
|
||||||
proc sched_log(msg: string) =
|
proc sched_log(msg: string) =
|
||||||
if msg.len > 0:
|
if msg.len > 0:
|
||||||
|
|
@ -169,7 +214,16 @@ proc sched_analyze_burst*(f: ptr FiberObject, burst_ns: uint64) =
|
||||||
# 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
|
||||||
console_write(cstring("[Ratchet] Violation: fiber exceeded budget\n"), 42)
|
discard emit_policy_violation(f.id, f.budget_ns, burst_ns, f.violations, 0)
|
||||||
|
console_write(cstring("[Ratchet] Budget violation fiber=0x"), 33)
|
||||||
|
uart_print_hex(f.id)
|
||||||
|
console_write(cstring(" burst="), 7)
|
||||||
|
uart_print_hex(burst_ns)
|
||||||
|
console_write(cstring(" budget="), 8)
|
||||||
|
uart_print_hex(f.budget_ns)
|
||||||
|
console_write(cstring(" strikes="), 9)
|
||||||
|
uart_print_hex(uint64(f.violations))
|
||||||
|
console_write(cstring("\n"), 1)
|
||||||
|
|
||||||
if f.violations >= 3:
|
if f.violations >= 3:
|
||||||
sched_demote(f)
|
sched_demote(f)
|
||||||
|
|
|
||||||
33
hal/abi.zig
33
hal/abi.zig
|
|
@ -55,9 +55,14 @@ 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);
|
||||||
|
|
@ -113,17 +118,27 @@ 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
|
// Force analysis — architecture-independent modules
|
||||||
_ = @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,6 +13,7 @@
|
||||||
//! 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,
|
||||||
|
|
@ -41,8 +42,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 {
|
||||||
// 0x8000_0000 is kernel base, 0x8300_0000 is ION base.
|
const min_valid: u64 = if (builtin.cpu.arch == .aarch64) 0x4000_0000 else 0x8000_0000;
|
||||||
if (ptr < 0x8000_0000) {
|
if (ptr < min_valid) {
|
||||||
@panic("HAL: Invariant Violation - Invalid Ring Pointer");
|
@panic("HAL: Invariant Violation - Invalid Ring Pointer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +73,11 @@ 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
|
||||||
asm volatile ("fence r, rw" ::: .{ .memory = true });
|
switch (builtin.cpu.arch) {
|
||||||
|
.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,6 +230,19 @@ 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,6 +447,13 @@ 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,8 +205,10 @@ 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
|
// 4. Overlap stack with user access (Optional)
|
||||||
|
if (stack_base != 0) {
|
||||||
try map_range(root, stack_base, stack_base, stack_size, PTE_R | PTE_W | PTE_U);
|
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,10 +194,12 @@ pub export fn stl_init() void {
|
||||||
stl_initialized = true;
|
stl_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current timestamp (placeholder - will be replaced by HAL timer)
|
/// Sovereign timer — canonical time source for all kernel timestamps
|
||||||
|
extern fn rumpk_timer_now_ns() u64;
|
||||||
|
|
||||||
|
/// Get current timestamp in nanoseconds since boot
|
||||||
fn get_timestamp_ns() u64 {
|
fn get_timestamp_ns() u64 {
|
||||||
// TODO: Integrate with HAL timer
|
return rumpk_timer_now_ns();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emit event to STL (C ABI)
|
/// Emit event to STL (C ABI)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@
|
||||||
|
|
||||||
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)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -137,13 +140,9 @@ export fn calloc(nmemb: usize, size: usize) ?*anyopaque {
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
export fn get_ticks() u32 {
|
export fn get_ticks() u32 {
|
||||||
var time_val: u64 = 0;
|
// Delegate to sovereign timer — single source of truth for all time
|
||||||
asm volatile ("rdtime %[ret]"
|
const ns = rumpk_timer_now_ns();
|
||||||
: [ret] "=r" (time_val),
|
return @truncate(ns / 1_000_000); // ns → ms
|
||||||
);
|
|
||||||
// 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 {
|
||||||
|
|
@ -160,10 +159,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,11 +27,13 @@ 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 {
|
||||||
uart.print(s[0..len]);
|
_ = s;
|
||||||
|
_ = len;
|
||||||
|
// TODO: Use syscall for userland debug printing
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn kprint_hex(value: u64) void {
|
export fn kprint_hex(value: u64) void {
|
||||||
uart.print_hex(value);
|
_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header structure (64 bytes aligned to match LwIP MEM_ALIGNMENT)
|
// Header structure (64 bytes aligned to match LwIP MEM_ALIGNMENT)
|
||||||
|
|
@ -45,9 +47,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;
|
||||||
|
|
@ -58,11 +60,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,3 +139,29 @@ 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,9 +34,19 @@ 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 => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -107,6 +117,78 @@ 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);
|
||||||
|
|
@ -152,6 +234,13 @@ 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;
|
||||||
|
|
@ -163,6 +252,11 @@ 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,6 +20,7 @@ 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) {
|
||||||
|
|
@ -34,6 +35,9 @@ 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,8 +13,14 @@
|
||||||
//! 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;
|
||||||
|
|
@ -46,13 +52,43 @@ pub fn init() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const VirtioBlkDriver = struct {
|
pub const VirtioBlkDriver = struct {
|
||||||
transport: pci.VirtioTransport,
|
transport: transport_mod.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;
|
||||||
|
|
@ -69,7 +105,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 = pci.VirtioTransport.init(addr),
|
.transport = transport_mod.VirtioTransport.init(addr),
|
||||||
.v_desc = undefined,
|
.v_desc = undefined,
|
||||||
.v_avail = undefined,
|
.v_avail = undefined,
|
||||||
.v_used = undefined,
|
.v_used = undefined,
|
||||||
|
|
@ -87,29 +123,56 @@ 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);
|
self.transport.add_status(1); // ACKNOWLEDGE
|
||||||
self.transport.add_status(2);
|
self.transport.add_status(2); // DRIVER
|
||||||
|
|
||||||
|
// Feature negotiation
|
||||||
|
const dev_features = self.transport.get_device_features();
|
||||||
|
_ = dev_features;
|
||||||
|
// Accept no special features for block — just basic operation
|
||||||
|
self.transport.set_driver_features(0);
|
||||||
|
transport_mod.io_barrier();
|
||||||
|
// FEATURES_OK only on modern (v2) transport
|
||||||
|
if (self.transport.is_modern) {
|
||||||
|
self.transport.add_status(8); // FEATURES_OK
|
||||||
|
transport_mod.io_barrier();
|
||||||
|
}
|
||||||
|
|
||||||
self.transport.select_queue(0);
|
self.transport.select_queue(0);
|
||||||
const count = self.transport.get_queue_size();
|
const max_count = self.transport.get_queue_size();
|
||||||
|
// Cap queue size for memory efficiency
|
||||||
|
const MAX_BLK_QUEUE: u16 = 128;
|
||||||
|
const count = if (max_count > MAX_BLK_QUEUE) MAX_BLK_QUEUE else max_count;
|
||||||
|
|
||||||
// [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);
|
self.transport.add_status(4); // DRIVER_OK
|
||||||
global_blk = self.*;
|
global_blk = self.*;
|
||||||
|
|
||||||
uart.print("[VirtIO-Blk] Device Ready. Queue Size: ");
|
uart.print("[VirtIO-Blk] Device Ready. Queue Size: ");
|
||||||
|
|
@ -151,15 +214,26 @@ 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
|
||||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
const expected_used = self.v_used.idx +% 1;
|
||||||
|
transport_mod.io_barrier();
|
||||||
self.v_avail.idx +%= 1;
|
self.v_avail.idx +%= 1;
|
||||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
|
|
||||||
self.transport.notify(0);
|
self.transport.notify(0);
|
||||||
|
|
||||||
// Wait for device (Polling)
|
// Wait for device (Polling — wait until used ring advances)
|
||||||
while (self.v_used.idx == 0) {
|
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] 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;
|
||||||
|
|
@ -190,14 +264,22 @@ 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;
|
||||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
const expected_used = self.v_used.idx +% 1;
|
||||||
|
transport_mod.io_barrier();
|
||||||
self.v_avail.idx +%= 1;
|
self.v_avail.idx +%= 1;
|
||||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
|
|
||||||
self.transport.notify(0);
|
self.transport.notify(0);
|
||||||
|
|
||||||
while (status == 0xFF) {
|
// Wait for device (Polling — wait until used ring advances)
|
||||||
asm volatile ("nop");
|
var timeout: usize = 0;
|
||||||
|
while (self.v_used.idx != expected_used) {
|
||||||
|
transport_mod.io_barrier();
|
||||||
|
timeout += 1;
|
||||||
|
if (timeout > 100_000_000) {
|
||||||
|
uart.print("[VirtIO-Blk] WRITE TIMEOUT!\n");
|
||||||
|
return error.DiskError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != 0) return error.DiskError;
|
if (status != 0) return error.DiskError;
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,14 @@
|
||||||
//! 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;
|
||||||
|
|
@ -117,47 +123,24 @@ pub export fn rumpk_net_init() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const VirtioNetDriver = struct {
|
pub const VirtioNetDriver = struct {
|
||||||
transport: pci.VirtioTransport,
|
transport: transport_mod.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_cfg...\n");
|
uart.print("[VirtIO-Net] Reading MAC from device config...\n");
|
||||||
if (self.transport.is_modern) {
|
|
||||||
// Use device_cfg directly - this is the VirtIO-Net specific config
|
|
||||||
if (self.transport.device_cfg) |cfg| {
|
|
||||||
const ptr: [*]volatile u8 = @ptrCast(cfg);
|
|
||||||
uart.print(" DeviceCfg at: ");
|
|
||||||
uart.print_hex(@intFromPtr(cfg));
|
|
||||||
uart.print("\n MAC bytes: ");
|
|
||||||
|
|
||||||
for (0..6) |i| {
|
for (0..6) |i| {
|
||||||
out[i] = ptr[i];
|
out[i] = self.transport.get_device_config_byte(i);
|
||||||
uart.print_hex8(ptr[i]);
|
uart.print_hex8(out[i]);
|
||||||
if (i < 5) uart.print(":");
|
if (i < 5) uart.print(":");
|
||||||
}
|
}
|
||||||
uart.print("\n");
|
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)).*;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(base: usize, irq_num: u32) VirtioNetDriver {
|
pub fn init(base: usize, irq_num: u32) VirtioNetDriver {
|
||||||
return .{
|
return .{
|
||||||
.transport = pci.VirtioTransport.init(base),
|
.transport = transport_mod.VirtioTransport.init(base),
|
||||||
.irq = irq_num,
|
.irq = irq_num,
|
||||||
.rx_queue = null,
|
.rx_queue = null,
|
||||||
.tx_queue = null,
|
.tx_queue = null,
|
||||||
|
|
@ -165,6 +148,32 @@ 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;
|
||||||
|
|
@ -213,52 +222,22 @@ 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
|
// 4. Feature Negotiation (unified across PCI and MMIO)
|
||||||
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(f_low);
|
uart.print_hex(dev_features);
|
||||||
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_low: u32 = (1 << VIRTIO_NET_F_MAC);
|
const accept: u64 = (1 << VIRTIO_NET_F_MAC) |
|
||||||
const accept_high: u32 = (1 << (VIRTIO_F_VERSION_1 - 32));
|
(@as(u64, 1) << VIRTIO_F_VERSION_1);
|
||||||
|
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);
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
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;
|
||||||
|
|
@ -267,10 +246,15 @@ 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_count = self.transport.get_queue_size();
|
const rx_max = self.transport.get_queue_size();
|
||||||
|
// Cap queue size to avoid ION pool exhaustion (MMIO v1 reports 1024)
|
||||||
|
const MAX_QUEUE: u16 = 256;
|
||||||
|
const rx_count = if (rx_max > MAX_QUEUE) MAX_QUEUE else rx_max;
|
||||||
uart.print("[VirtIO] RX Queue Size: ");
|
uart.print("[VirtIO] RX Queue Size: ");
|
||||||
uart.print_hex(rx_count);
|
uart.print_hex(rx_count);
|
||||||
uart.print("\n");
|
uart.print(" (max: ");
|
||||||
|
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");
|
||||||
|
|
@ -288,10 +272,13 @@ 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_count = self.transport.get_queue_size();
|
const tx_max = 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("\n");
|
uart.print(" (max: ");
|
||||||
|
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");
|
||||||
|
|
@ -392,11 +379,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;
|
||||||
|
|
||||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
|
|
||||||
if (is_rx) {
|
if (is_rx) {
|
||||||
q_ptr.avail.idx = count;
|
q_ptr.avail.idx = count;
|
||||||
asm volatile ("fence w, w" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
const phys_addr = aligned_addr;
|
const phys_addr = aligned_addr;
|
||||||
|
|
@ -404,6 +391,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
@ -413,7 +401,7 @@ pub const VirtioNetDriver = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rx_poll(self: *VirtioNetDriver, q: *Virtqueue) void {
|
pub fn rx_poll(self: *VirtioNetDriver, q: *Virtqueue) void {
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
|
|
||||||
const used = q.used;
|
const used = q.used;
|
||||||
const hw_idx = used.idx;
|
const hw_idx = used.idx;
|
||||||
|
|
@ -473,7 +461,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;
|
||||||
|
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -486,14 +474,14 @@ pub const VirtioNetDriver = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replenished) {
|
if (replenished) {
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
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;
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
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);
|
||||||
|
|
@ -528,11 +516,11 @@ pub const VirtioNetDriver = struct {
|
||||||
|
|
||||||
q.ids[idx] = slab_id;
|
q.ids[idx] = slab_id;
|
||||||
|
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
avail_ring[idx] = @intCast(idx);
|
avail_ring[idx] = @intCast(idx);
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
q.avail.idx +%= 1;
|
q.avail.idx +%= 1;
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
|
|
||||||
self.transport.notify(1);
|
self.transport.notify(1);
|
||||||
uart.print("[VirtIO TX-Slab] Sent ");
|
uart.print("[VirtIO TX-Slab] Sent ");
|
||||||
|
|
@ -579,11 +567,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;
|
||||||
|
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
avail_ring[idx] = @intCast(desc_idx);
|
avail_ring[idx] = @intCast(desc_idx);
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
q.avail.idx +%= 1;
|
q.avail.idx +%= 1;
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
transport_mod.io_barrier();
|
||||||
|
|
||||||
self.transport.notify(1);
|
self.transport.notify(1);
|
||||||
uart.print("[VirtIO TX] Queued & Notified Len=");
|
uart.print("[VirtIO TX] Queued & Notified Len=");
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
//! 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
|
||||||
|
|
@ -316,6 +317,17 @@ 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;
|
||||||
|
|
@ -345,8 +357,65 @@ 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,7 +1,20 @@
|
||||||
|
// 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;
|
||||||
|
|
@ -15,13 +28,46 @@ struct stat {
|
||||||
|
|
||||||
int errno = 0;
|
int errno = 0;
|
||||||
|
|
||||||
// Basic memory stubs
|
|
||||||
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);
|
void* memset(void* s, int c, size_t n);
|
||||||
|
|
||||||
|
// Basic memory stubs (Bump Allocator) - USERLAND ONLY
|
||||||
|
// Kernel gets malloc/free/calloc from stubs.zig
|
||||||
|
#ifdef RUMPK_USER
|
||||||
|
static uint8_t heap[4 * 1024 * 1024]; // 4MB Heap
|
||||||
|
static size_t heap_idx = 0;
|
||||||
|
|
||||||
|
void* malloc(size_t size) {
|
||||||
|
if (size == 0) return NULL;
|
||||||
|
size = (size + 7) & ~7; // Align to 8 bytes
|
||||||
|
if (heap_idx + size > sizeof(heap)) return NULL;
|
||||||
|
void* ptr = &heap[heap_idx];
|
||||||
|
heap_idx += size;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* ptr) {
|
||||||
|
(void)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* calloc(size_t nmemb, size_t size) {
|
||||||
|
size_t total = nmemb * size;
|
||||||
|
void* ptr = malloc(total);
|
||||||
|
if (ptr) memset(ptr, 0, total);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
#endif // RUMPK_USER
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
int write(int fd, const void *buf, size_t count);
|
||||||
|
int read(int fd, void *buf, size_t count);
|
||||||
|
int open(const char *pathname, int flags, ...);
|
||||||
|
int close(int fd);
|
||||||
|
|
||||||
|
void* memset(void* s, int c, size_t n);
|
||||||
|
|
||||||
|
// Basic memory stubs
|
||||||
|
// Basic memory stubs (Bump Allocator)
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
|
|
@ -44,8 +90,41 @@ 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) {
|
||||||
if (endptr) *endptr = (char*)nptr;
|
const char *p = nptr;
|
||||||
return 0.0;
|
double result = 0.0;
|
||||||
|
double sign = 1.0;
|
||||||
|
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
|
||||||
|
if (*p == '-') { sign = -1.0; p++; }
|
||||||
|
else if (*p == '+') { p++; }
|
||||||
|
while (*p >= '0' && *p <= '9') {
|
||||||
|
result = result * 10.0 + (*p - '0');
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p == '.') {
|
||||||
|
double frac = 0.1;
|
||||||
|
p++;
|
||||||
|
while (*p >= '0' && *p <= '9') {
|
||||||
|
result += (*p - '0') * frac;
|
||||||
|
frac *= 0.1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*p == 'e' || *p == 'E') {
|
||||||
|
p++;
|
||||||
|
int exp_sign = 1, exp_val = 0;
|
||||||
|
if (*p == '-') { exp_sign = -1; p++; }
|
||||||
|
else if (*p == '+') { p++; }
|
||||||
|
while (*p >= '0' && *p <= '9') {
|
||||||
|
exp_val = exp_val * 10 + (*p - '0');
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
double m = 1.0;
|
||||||
|
for (int i = 0; i < exp_val; i++)
|
||||||
|
m = exp_sign > 0 ? m * 10.0 : m * 0.1;
|
||||||
|
result *= m;
|
||||||
|
}
|
||||||
|
if (endptr) *endptr = (char*)p;
|
||||||
|
return sign * result;
|
||||||
}
|
}
|
||||||
|
|
||||||
double pow(double x, double y) { return 0.0; }
|
double pow(double x, double y) { return 0.0; }
|
||||||
|
|
@ -55,8 +134,42 @@ 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);
|
||||||
|
|
@ -222,11 +335,7 @@ int printf(const char *format, ...) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fwrite(const void *ptr, size_t size, size_t nmemb, void *stream) {
|
// REDUNDANT REMOVED
|
||||||
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);
|
||||||
|
|
@ -270,6 +379,16 @@ 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;
|
||||||
|
|
@ -343,13 +462,10 @@ 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,7 +12,8 @@
|
||||||
|
|
||||||
import ../../core/ion
|
import ../../core/ion
|
||||||
|
|
||||||
const SYS_TABLE_ADDR = 0x83000000'u64
|
const SYS_TABLE_ADDR = when defined(arm64): 0x50000000'u64
|
||||||
|
else: 0x83000000'u64
|
||||||
|
|
||||||
const
|
const
|
||||||
GAP = 10 # Pixels between windows
|
GAP = 10 # Pixels between windows
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,16 @@
|
||||||
|
|
||||||
#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,6 +14,7 @@
|
||||||
//! 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) ---
|
||||||
|
|
||||||
|
|
@ -94,7 +95,11 @@ pub fn RingBuffer(comptime T: type) type {
|
||||||
self.data[head & mask] = item;
|
self.data[head & mask] = item;
|
||||||
|
|
||||||
// Commit
|
// Commit
|
||||||
asm volatile ("fence" ::: .{ .memory = true });
|
switch (builtin.cpu.arch) {
|
||||||
|
.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;
|
||||||
}
|
}
|
||||||
|
|
@ -115,6 +120,8 @@ 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
|
||||||
|
|
@ -127,6 +134,7 @@ 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),
|
||||||
|
|
@ -134,16 +142,27 @@ 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) != 184) {
|
if (@sizeOf(SysTable) != 240) {
|
||||||
@compileError("SysTable size mismatch! Expected 184");
|
@compileError("SysTable size mismatch! Expected 240 (LibWeb LWF channels added)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SYSTABLE_ADDR: usize = 0x83000000;
|
const SYSTABLE_ADDR: usize = if (builtin.cpu.arch == .aarch64) 0x50000000 else 0x83000000;
|
||||||
|
|
||||||
// --- API ---
|
// --- API ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@
|
||||||
# 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* = 0x83000000'u64
|
const SYS_TABLE_ADDR* = when defined(arm64): 0x50000000'u64
|
||||||
|
else: 0x83000000'u64
|
||||||
|
|
||||||
# Local type declarations (must match core/ion.nim definitions)
|
# Local type declarations (must match core/ion.nim definitions)
|
||||||
type
|
type
|
||||||
|
|
@ -70,6 +71,8 @@ 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
|
||||||
|
|
@ -97,8 +100,12 @@ type
|
||||||
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) == 208
|
doAssert sizeof(SysTable) == 240
|
||||||
|
|
||||||
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]
|
||||||
|
|
@ -144,10 +151,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 = 0x83010000'u64 # Start of user packet slab in SysTable region
|
USER_SLAB_BASE = SYS_TABLE_ADDR + 0x10000'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 = 0x83000100'u64 # Bitmap stored in SysTable region (after SysTable struct)
|
USER_BITMAP_ADDR = SYS_TABLE_ADDR + 0x100'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] =
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@
|
||||||
# (C) 2026 Markus Maiwald
|
# (C) 2026 Markus Maiwald
|
||||||
|
|
||||||
import ion_client
|
import ion_client
|
||||||
import net_glue
|
when defined(RUMPK_KERNEL):
|
||||||
|
import net_glue
|
||||||
|
export net_glue
|
||||||
|
|
||||||
# memcpy removed to avoid C header conflict
|
# memcpy removed to avoid C header conflict
|
||||||
|
|
||||||
|
|
@ -50,6 +52,16 @@ 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: """
|
||||||
|
register unsigned long x8_ __asm__("x8") = `n`;
|
||||||
|
register unsigned long x0_ __asm__("x0") = `v0`;
|
||||||
|
register unsigned long x1_ __asm__("x1") = `v1`;
|
||||||
|
register unsigned long x2_ __asm__("x2") = `v2`;
|
||||||
|
__asm__ volatile("svc #0" : "+r"(x0_) : "r"(x8_), "r"(x1_), "r"(x2_) : "memory");
|
||||||
|
`res` = (long)x0_;
|
||||||
|
""".}
|
||||||
|
else:
|
||||||
{.emit: """
|
{.emit: """
|
||||||
register unsigned long a7 __asm__("a7") = `n`;
|
register unsigned long a7 __asm__("a7") = `n`;
|
||||||
register unsigned long a0_ __asm__("a0") = `v0`;
|
register unsigned long a0_ __asm__("a0") = `v0`;
|
||||||
|
|
@ -64,25 +76,13 @@ when defined(RUMPK_KERNEL):
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# KERNEL IMPLEMENTATION
|
# KERNEL IMPLEMENTATION
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
# SockState, NexusSock, membrane_init, pump_membrane_stack,
|
||||||
type
|
# glue_connect/bind/listen/accept_peek/setup_socket/write/read/close,
|
||||||
SockState = enum
|
# glue_resolve_start/check — all provided by net_glue (imported above)
|
||||||
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,17 +90,6 @@ 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
|
||||||
|
|
@ -369,8 +358,10 @@ else:
|
||||||
proc yield_fiber*() {.exportc: "yield", cdecl.} =
|
proc yield_fiber*() {.exportc: "yield", cdecl.} =
|
||||||
discard syscall(0x100, 0)
|
discard syscall(0x100, 0)
|
||||||
|
|
||||||
proc pump_membrane_stack*() {.importc, cdecl.}
|
proc pump_membrane_stack*() =
|
||||||
proc membrane_init*() {.importc, cdecl.}
|
## No-op in userland — kernel drives LwIP stack
|
||||||
|
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.} =
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,16 @@ 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".}
|
||||||
|
|
@ -114,6 +124,8 @@ 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
|
||||||
|
|
@ -192,31 +204,14 @@ 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...\n")
|
glue_print("[Membrane] Initialization Starting...\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()
|
||||||
|
|
||||||
# DIAGNOSTIC: Audit Memory Pools
|
dns_init()
|
||||||
{.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: """
|
||||||
|
|
@ -225,34 +220,35 @@ proc membrane_init*() {.exportc, cdecl.} =
|
||||||
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] DNS configured (10.0.2.3)\n")
|
||||||
|
|
||||||
glue_print("[Membrane] lwip_init() returned. DNS Initialized.\n")
|
# 2. Setup Netif with DHCP
|
||||||
|
|
||||||
# 2. Setup Netif
|
|
||||||
{.emit: """
|
{.emit: """
|
||||||
static struct netif ni_static;
|
static struct netif ni_static;
|
||||||
ip4_addr_t ip, mask, gw;
|
ip4_addr_t ip, mask, gw;
|
||||||
|
|
||||||
// Use Static IP to stabilize test environment
|
// Start with zeros — DHCP will assign
|
||||||
IP4_ADDR(&ip, 10, 0, 2, 15);
|
IP4_ADDR(&ip, 0, 0, 0, 0);
|
||||||
IP4_ADDR(&mask, 255, 255, 255, 0);
|
IP4_ADDR(&mask, 0, 0, 0, 0);
|
||||||
IP4_ADDR(&gw, 10, 0, 2, 2);
|
IP4_ADDR(&gw, 0, 0, 0, 0);
|
||||||
|
|
||||||
struct netif *res = netif_add(&ni_static, &ip, &mask, &gw, NULL, (netif_init_fn)ion_netif_init, (netif_input_fn)ethernet_input);
|
struct netif *res = netif_add(&ni_static, &ip, &mask, &gw, NULL,
|
||||||
printf("[Membrane] netif_add returned: 0x%x\n", (unsigned int)res);
|
(netif_init_fn)ion_netif_init,
|
||||||
|
(netif_input_fn)ethernet_input);
|
||||||
|
|
||||||
|
if (res == NULL) {
|
||||||
|
printf("[Membrane] CRITICAL: netif_add FAILED!\n");
|
||||||
|
} else {
|
||||||
netif_set_default(&ni_static);
|
netif_set_default(&ni_static);
|
||||||
netif_set_up(&ni_static);
|
netif_set_up(&ni_static);
|
||||||
|
dhcp_start(&ni_static);
|
||||||
printf("[Membrane] netif_default: 0x%x | netif_list: 0x%x\n", (unsigned int)netif_default, (unsigned int)netif_list);
|
printf("[Membrane] DHCP started on io0\n");
|
||||||
|
}
|
||||||
// dhcp_start(&ni_static); // Bypassing DHCP
|
|
||||||
|
|
||||||
`g_netif` = &ni_static;
|
`g_netif` = &ni_static;
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
glue_print("[Membrane] Network Stack Operational (Waiting for DHCP IP...)\n")
|
glue_print("[Membrane] Network Stack Operational (DHCP...)\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
|
||||||
|
|
@ -667,4 +663,3 @@ void trigger_http_test(void) {
|
||||||
tcp_connect(pcb, &google_ip, 80, tcp_connected_callback);
|
tcp_connect(pcb, &google_ip, 80, tcp_connected_callback);
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,49 @@ 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
|
||||||
import ../../libs/membrane/fs/sfs_user as sfs
|
when not defined(NIPBOX_LITE):
|
||||||
|
import ../../libs/membrane/fs/sfs_user as sfs
|
||||||
import editor
|
import editor
|
||||||
import ../../libs/membrane/term # Phase 26: Visual Cortex
|
when not defined(NIPBOX_LITE):
|
||||||
|
import ../../libs/membrane/term # Phase 26: Visual Cortex
|
||||||
|
|
||||||
|
# --- M4.4: BKDL Capability Manifest (SPEC-071) ---
|
||||||
|
{.emit: """
|
||||||
|
__attribute__((section(".nexus.manifest"), used))
|
||||||
|
static const unsigned char nexus_manifest[166] = {
|
||||||
|
/* BkdlHeader (118 bytes) */
|
||||||
|
0x53, 0x55, 0x58, 0x4E, /* magic: "NXUS" (LE) */
|
||||||
|
0x01, 0x00, /* version: 1 */
|
||||||
|
0x00, 0x00, /* flags: 0 */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* signature[0..63]: zeros */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* pubkey_hash[0..31]: zeros */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, /* cap_count: 4 */
|
||||||
|
0x00, 0x00, 0x00, 0x00, /* blob_size: 0 */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* entry_point: 0 */
|
||||||
|
/* CapDescriptor[0]: console.output (0x1001) WRITE */
|
||||||
|
0x02, 0x02, 0x00, 0x00,
|
||||||
|
0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
/* CapDescriptor[1]: VFS (0x2000) READ */
|
||||||
|
0x02, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
/* CapDescriptor[2]: NET_TX (0x0500) WRITE */
|
||||||
|
0x02, 0x02, 0x00, 0x00,
|
||||||
|
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
/* CapDescriptor[3]: NET_RX (0x0501) READ */
|
||||||
|
0x02, 0x01, 0x00, 0x00,
|
||||||
|
0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
""".}
|
||||||
|
|
||||||
# Phase 30: Pledge Constants
|
# Phase 30: Pledge Constants
|
||||||
const
|
const
|
||||||
|
|
@ -291,6 +331,7 @@ 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 @[]
|
return @[]
|
||||||
|
|
@ -308,10 +349,14 @@ proc cmd_write*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
else:
|
else:
|
||||||
print("Error: Could not write to " & filename & "\n")
|
print("Error: Could not write to " & filename & "\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
else:
|
||||||
|
print("[nipbox] 'write' requires SFS (not available in lite mode)\n")
|
||||||
|
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 @[]
|
return @[]
|
||||||
|
|
@ -330,6 +375,9 @@ proc cmd_read*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
else:
|
else:
|
||||||
print("Error: Could not open " & filename & "\n")
|
print("Error: Could not open " & filename & "\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
else:
|
||||||
|
print("[nipbox] 'read' requires SFS (not available in lite mode)\n")
|
||||||
|
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:
|
||||||
|
|
@ -482,6 +530,7 @@ 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 @[]
|
||||||
|
|
@ -561,7 +610,6 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
var timeout = 0
|
var timeout = 0
|
||||||
|
|
||||||
while timeout < 10000:
|
while timeout < 10000:
|
||||||
# Use libc shim which pumps stack
|
|
||||||
let n = lb.recv(cint(fd), addr buf[0], 4096, 0)
|
let n = lb.recv(cint(fd), addr buf[0], 4096, 0)
|
||||||
|
|
||||||
if n > 0:
|
if n > 0:
|
||||||
|
|
@ -572,8 +620,6 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
if sep != -1:
|
if sep != -1:
|
||||||
header_parsed = true
|
header_parsed = true
|
||||||
|
|
||||||
# Try to find Content-Length
|
|
||||||
# Quick hacky parse
|
|
||||||
let lower_head = header_acc.toLowerAscii()
|
let lower_head = header_acc.toLowerAscii()
|
||||||
let cl_idx = lower_head.find("content-length:")
|
let cl_idx = lower_head.find("content-length:")
|
||||||
if cl_idx != -1:
|
if cl_idx != -1:
|
||||||
|
|
@ -595,13 +641,10 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
print("Error: Headers too large.\n")
|
print("Error: Headers too large.\n")
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# Stream directly to SFS
|
|
||||||
sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
|
sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
|
||||||
total_bytes += int(n)
|
total_bytes += int(n)
|
||||||
|
|
||||||
# Progress Bar
|
|
||||||
if content_len > 0:
|
if content_len > 0:
|
||||||
# let pct = (total_bytes * 100) div content_len
|
|
||||||
if total_bytes mod 10240 < int(n): print(".")
|
if total_bytes mod 10240 < int(n): print(".")
|
||||||
else:
|
else:
|
||||||
if total_bytes mod 10240 < int(n): print(".")
|
if total_bytes mod 10240 < int(n): print(".")
|
||||||
|
|
@ -609,28 +652,20 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
elif n == 0:
|
elif n == 0:
|
||||||
break
|
break
|
||||||
else:
|
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
|
break
|
||||||
|
|
||||||
sfs.sfs_close_write(sfs_h)
|
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")
|
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")
|
||||||
|
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 <host>\n")
|
print("Usage: http.test <host>\n")
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
let host = args[0]
|
let host = args[0]
|
||||||
|
|
@ -646,16 +681,13 @@ proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData =
|
||||||
|
|
||||||
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")
|
||||||
|
|
@ -774,8 +806,18 @@ 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 & " [LITE] (Phase 34: Orbital Drop)\n")
|
||||||
|
print("Commands: ls, cat, cp, mv, touch, edit, echo, where, http, http.get,\n")
|
||||||
|
print(" http.test, http.serve, from_json, mount, matrix, crash,\n")
|
||||||
|
print(" sys.upgrade, set, if, while, help, exit\n")
|
||||||
|
print("Disabled: write, read, http.download (requires SFS)\n")
|
||||||
|
else:
|
||||||
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
|
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
|
||||||
print("Commands: ls, cat, echo, where, http, http.get, http.download, from_json, mount, matrix, set, if, while, help, exit\n")
|
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 ---
|
||||||
|
|
|
||||||
130
run.sh
130
run.sh
|
|
@ -1,22 +1,122 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# Rumpk QEMU Boot Script
|
# ============================================================================
|
||||||
|
# Rumpk QEMU Runner — Boot, Test, or Interactive Shell
|
||||||
|
# ============================================================================
|
||||||
|
# Usage:
|
||||||
|
# ./run.sh # Interactive RISC-V session
|
||||||
|
# ./run.sh test # Automated boot test (10s timeout, check output)
|
||||||
|
# ./run.sh [riscv64|aarch64] # Architecture selection (future)
|
||||||
|
#
|
||||||
|
# Exit codes:
|
||||||
|
# 0 = Boot successful (test mode: all checks passed)
|
||||||
|
# 1 = Boot failed or timeout
|
||||||
|
# ============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
MODE="${1:-interactive}"
|
||||||
KERNEL="$RUMPK_DIR/zig-out/bin/rumpk.elf"
|
ARCH="${2:-riscv64}"
|
||||||
|
TIMEOUT=25
|
||||||
|
|
||||||
|
KERNEL="zig-out/bin/rumpk.elf"
|
||||||
|
DISK="build/disk.img"
|
||||||
|
|
||||||
|
# Fallback to build/ location if zig-out doesn't have it
|
||||||
|
if [ ! -f "$KERNEL" ]; then
|
||||||
|
KERNEL="build/rumpk-riscv64.elf"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f "$KERNEL" ]; then
|
if [ ! -f "$KERNEL" ]; then
|
||||||
echo "ERROR: Kernel not found at $KERNEL"
|
echo "ERROR: No kernel binary found. Run: ./build_nim.sh && zig build -Dtarget=riscv64-freestanding-none -Dcpu=sifive_u54"
|
||||||
echo "Run ./build.sh first"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "🚀 Booting Rumpk..."
|
case "$ARCH" in
|
||||||
echo " Kernel: $KERNEL"
|
riscv64)
|
||||||
echo ""
|
QEMU_CMD=(
|
||||||
|
qemu-system-riscv64
|
||||||
qemu-system-riscv64 \
|
-M virt -m 512M -nographic
|
||||||
-M virt \
|
|
||||||
-cpu max \
|
|
||||||
-m 512M \
|
|
||||||
-nographic \
|
|
||||||
-kernel "$KERNEL"
|
-kernel "$KERNEL"
|
||||||
|
)
|
||||||
|
# Add disk if it exists
|
||||||
|
if [ -f "$DISK" ]; then
|
||||||
|
QEMU_CMD+=(
|
||||||
|
-drive "if=none,format=raw,file=$DISK,id=blk0"
|
||||||
|
-device virtio-blk-pci,drive=blk0
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
QEMU_CMD+=(
|
||||||
|
-device virtio-net-pci,netdev=net0
|
||||||
|
-netdev user,id=net0
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: Architecture '$ARCH' not yet supported"
|
||||||
|
echo "Supported: riscv64"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$MODE" = "test" ]; then
|
||||||
|
echo "=== Rumpk Boot Test ($ARCH, ${TIMEOUT}s timeout) ==="
|
||||||
|
|
||||||
|
LOGFILE=$(mktemp /tmp/rumpk-boot-XXXXXX.log)
|
||||||
|
trap "rm -f $LOGFILE" EXIT
|
||||||
|
|
||||||
|
timeout "$TIMEOUT" "${QEMU_CMD[@]}" > "$LOGFILE" 2>&1 || true
|
||||||
|
|
||||||
|
# Boot verification checks
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
TOTAL=0
|
||||||
|
|
||||||
|
check() {
|
||||||
|
local name="$1"
|
||||||
|
local pattern="$2"
|
||||||
|
TOTAL=$((TOTAL + 1))
|
||||||
|
if grep -q "$pattern" "$LOGFILE"; then
|
||||||
|
echo " PASS: $name"
|
||||||
|
PASS=$((PASS + 1))
|
||||||
|
else
|
||||||
|
echo " FAIL: $name"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check "OpenSBI handoff" "Boot HART ID"
|
||||||
|
check "UART proof-of-life" "UART.*Loopback Test: PASS"
|
||||||
|
check "Zig HAL entry" "zig_entry reached"
|
||||||
|
check "Nim L1 handoff" "Handing off to Nim L1"
|
||||||
|
check "ION pool ready" "ION.*Pool Ready"
|
||||||
|
check "VirtIO-Net init" "VirtIO.*Initialization Complete"
|
||||||
|
check "CSpace init" "Capability system initialized"
|
||||||
|
check "Truth Ledger init" "System Truth Ledger initialized"
|
||||||
|
check "Fiber dispatcher" "Multi-Fiber Dispatcher starting"
|
||||||
|
check "NetSwitch online" "Traffic Engine Online"
|
||||||
|
check "LFS mounted" "Sovereign filesystem mounted"
|
||||||
|
check "Init loaded" "NIPBOX.*Entry point reached"
|
||||||
|
check "Shell spawned" "SOVEREIGN SUPERVISOR"
|
||||||
|
check "DHCP IP acquired" "IP STATUS CHANGE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Result: $PASS/$TOTAL passed, $FAIL failed ==="
|
||||||
|
|
||||||
|
if [ "$FAIL" -gt 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Boot log saved: $LOGFILE"
|
||||||
|
trap - EXIT # Don't delete on failure
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
elif [ "$MODE" = "interactive" ]; then
|
||||||
|
echo "=== Rumpk Interactive Session ($ARCH) ==="
|
||||||
|
echo " Kernel: $KERNEL"
|
||||||
|
echo " Exit: Ctrl-A X"
|
||||||
|
echo ""
|
||||||
|
exec "${QEMU_CMD[@]}"
|
||||||
|
else
|
||||||
|
echo "Usage: ./run.sh [interactive|test] [riscv64]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ BUILD_DIR="../../build"
|
||||||
|
|
||||||
zig cc -target $TARGET -nostdlib -static -T $LINKER_SCRIPT \
|
zig cc -target $TARGET -nostdlib -static -T $LINKER_SCRIPT \
|
||||||
$BUILD_DIR/subject_entry.o \
|
$BUILD_DIR/subject_entry.o \
|
||||||
$BUILD_DIR/stubs_user.o \
|
|
||||||
$BUILD_DIR/libc_shim.o \
|
$BUILD_DIR/libc_shim.o \
|
||||||
stubs_mksh.o \
|
stubs_mksh.o \
|
||||||
$OBJS \
|
$OBJS \
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -13,17 +13,78 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#include <stdarg.h>
|
||||||
// Globals
|
|
||||||
char **environ = NULL;
|
|
||||||
|
|
||||||
extern void console_write(const void* p, size_t len);
|
|
||||||
|
|
||||||
extern long syscall(long nr, long a0, long a1, long a2);
|
extern long syscall(long nr, long a0, long a1, long a2);
|
||||||
long k_handle_syscall(long nr, long a0, long a1, long a2) {
|
long k_handle_syscall(long nr, long a0, long a1, long a2) {
|
||||||
return syscall(nr, a0, a1, a2);
|
return syscall(nr, a0, a1, a2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Globals
|
||||||
|
char **environ = NULL;
|
||||||
|
|
||||||
|
// Safe Userland Allocator (Bump Pointer with Headers)
|
||||||
|
#define HEAP_SIZE (32 * 1024 * 1024)
|
||||||
|
static char heap_memory[HEAP_SIZE];
|
||||||
|
static size_t heap_ptr = 0;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
size_t magic;
|
||||||
|
} BlockHeader;
|
||||||
|
#define ALLOC_MAGIC 0xCAFEBABE
|
||||||
|
|
||||||
|
void *malloc(size_t size) {
|
||||||
|
if (size == 0) return NULL;
|
||||||
|
|
||||||
|
// Align total size (header + data) to 16 bytes
|
||||||
|
size_t required = size + sizeof(BlockHeader);
|
||||||
|
size_t aligned_total = (required + 15) & ~15;
|
||||||
|
|
||||||
|
if (heap_ptr + aligned_total > HEAP_SIZE) return NULL;
|
||||||
|
|
||||||
|
BlockHeader *hdr = (BlockHeader *)&heap_memory[heap_ptr];
|
||||||
|
hdr->size = size;
|
||||||
|
hdr->magic = ALLOC_MAGIC;
|
||||||
|
|
||||||
|
void *ptr = (void *)((char *)hdr + sizeof(BlockHeader));
|
||||||
|
heap_ptr += aligned_total;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void *ptr) {
|
||||||
|
// No-op bump allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
void *realloc(void *ptr, size_t size) {
|
||||||
|
if (!ptr) return malloc(size);
|
||||||
|
if (size == 0) return NULL; // Standard says free.. or return NULL? mksh expects NULL or ptr.
|
||||||
|
|
||||||
|
// Get header
|
||||||
|
BlockHeader *hdr = (BlockHeader *)((char *)ptr - sizeof(BlockHeader));
|
||||||
|
if (hdr->magic != ALLOC_MAGIC) {
|
||||||
|
// Corrupted ptr? return NULL or fail.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimization: If it's the LAST block, simple extend? (Not implemented for simplicity)
|
||||||
|
|
||||||
|
void *new_ptr = malloc(size);
|
||||||
|
if (!new_ptr) return NULL;
|
||||||
|
|
||||||
|
size_t copy_size = (hdr->size < size) ? hdr->size : size;
|
||||||
|
memcpy(new_ptr, ptr, copy_size);
|
||||||
|
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *calloc(size_t nmemb, size_t size) {
|
||||||
|
size_t total = nmemb * size;
|
||||||
|
void *ptr = malloc(total);
|
||||||
|
if (ptr) memset(ptr, 0, total);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Stubs
|
// Stubs
|
||||||
int fstat(int fd, struct stat *buf) { return 0; }
|
int fstat(int fd, struct stat *buf) { return 0; }
|
||||||
int lstat(const char *path, struct stat *buf) { return 0; }
|
int lstat(const char *path, struct stat *buf) { return 0; }
|
||||||
|
|
@ -32,7 +93,14 @@ int stat(const char *path, struct stat *buf) { return 0; }
|
||||||
int sigemptyset(sigset_t *set) { return 0; }
|
int sigemptyset(sigset_t *set) { return 0; }
|
||||||
int sigaddset(sigset_t *set, int signum) { return 0; }
|
int sigaddset(sigset_t *set, int signum) { return 0; }
|
||||||
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { return 0; }
|
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { return 0; }
|
||||||
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { return 0; }
|
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) {
|
||||||
|
if (oldact) {
|
||||||
|
// Initialize to safe defaults - no handler installed
|
||||||
|
memset(oldact, 0, sizeof(struct sigaction));
|
||||||
|
oldact->sa_handler = SIG_DFL; // Default handler
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int sigsuspend(const sigset_t *mask) { return -1; }
|
int sigsuspend(const sigset_t *mask) { return -1; }
|
||||||
int kill(pid_t pid, int sig) { return 0; }
|
int kill(pid_t pid, int sig) { return 0; }
|
||||||
unsigned int alarm(unsigned int seconds) { return 0; }
|
unsigned int alarm(unsigned int seconds) { return 0; }
|
||||||
|
|
@ -45,7 +113,21 @@ off_t lseek(int fd, off_t offset, int whence) { return 0; }
|
||||||
// int close(int fd) { return 0; } // In libc.nim
|
// int close(int fd) { return 0; } // In libc.nim
|
||||||
int pipe(int pipefd[2]) { return -1; }
|
int pipe(int pipefd[2]) { return -1; }
|
||||||
int dup2(int oldfd, int newfd) { return newfd; }
|
int dup2(int oldfd, int newfd) { return newfd; }
|
||||||
int fcntl(int fd, int cmd, ...) { return 0; }
|
extern void console_write(const void* p, unsigned long len);
|
||||||
|
static void ksh_debug(const char* s) {
|
||||||
|
console_write(s, (unsigned long)strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
int fcntl(int fd, int cmd, ...) {
|
||||||
|
ksh_debug("[mksh] fcntl called\n");
|
||||||
|
va_list args;
|
||||||
|
va_start(args, cmd);
|
||||||
|
long arg = va_arg(args, long);
|
||||||
|
va_end(args);
|
||||||
|
int res = (int)syscall(0x206, (long)fd, (long)cmd, arg);
|
||||||
|
ksh_debug("[mksh] fcntl returning\n");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
int ioctl(int fd, unsigned long request, ...) { return 0; }
|
int ioctl(int fd, unsigned long request, ...) { return 0; }
|
||||||
|
|
||||||
// int execve(const char *pathname, char *const argv[], char *const envp[]) { return -1; } // In clib.c
|
// int execve(const char *pathname, char *const argv[], char *const envp[]) { return -1; } // In clib.c
|
||||||
|
|
@ -65,7 +147,26 @@ int seteuid(uid_t uid) { return 0; }
|
||||||
int setegid(gid_t gid) { return 0; }
|
int setegid(gid_t gid) { return 0; }
|
||||||
int setpgid(pid_t pid, pid_t pgid) { return 0; }
|
int setpgid(pid_t pid, pid_t pgid) { return 0; }
|
||||||
|
|
||||||
int tcgetattr(int fd, struct termios *termios_p) { return 0; }
|
int tcgetattr(int fd, struct termios *termios_p) {
|
||||||
|
if (termios_p) {
|
||||||
|
// Initialize with safe defaults (using numeric values to avoid missing constants)
|
||||||
|
memset(termios_p, 0, sizeof(struct termios));
|
||||||
|
// Set basic flags for canonical mode
|
||||||
|
termios_p->c_iflag = 0x0100; // ICRNL
|
||||||
|
termios_p->c_oflag = 0x0001 | 0x0004; // OPOST | ONLCR
|
||||||
|
termios_p->c_cflag = 0x0030 | 0x0080; // CS8 | CREAD
|
||||||
|
termios_p->c_lflag = ISIG | ICANON | ECHO; // These should be defined
|
||||||
|
// Set control characters
|
||||||
|
termios_p->c_cc[VINTR] = 3; // Ctrl-C
|
||||||
|
termios_p->c_cc[VQUIT] = 28; // Ctrl-backslash
|
||||||
|
termios_p->c_cc[VERASE] = 127; // DEL
|
||||||
|
termios_p->c_cc[VKILL] = 21; // Ctrl-U
|
||||||
|
termios_p->c_cc[VEOF] = 4; // Ctrl-D
|
||||||
|
termios_p->c_cc[VMIN] = 1;
|
||||||
|
termios_p->c_cc[VTIME] = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) { return 0; }
|
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) { return 0; }
|
||||||
pid_t tcgetpgrp(int fd) { return 1; }
|
pid_t tcgetpgrp(int fd) { return 1; }
|
||||||
int tcsetpgrp(int fd, pid_t pgrp) { return 0; }
|
int tcsetpgrp(int fd, pid_t pgrp) { return 0; }
|
||||||
|
|
@ -128,7 +229,56 @@ mode_t umask(mode_t mask) { return 0; }
|
||||||
|
|
||||||
int c_ulimit(const char **wp) { return 0; }
|
int c_ulimit(const char **wp) { return 0; }
|
||||||
|
|
||||||
void longjmp(jmp_buf env, int val) { while(1); }
|
int setjmp(jmp_buf env) {
|
||||||
int setjmp(jmp_buf env) { return 0; }
|
__asm__ volatile (
|
||||||
|
"sd ra, 0(%0)\n"
|
||||||
|
"sd sp, 8(%0)\n"
|
||||||
|
"sd s0, 16(%0)\n"
|
||||||
|
"sd s1, 24(%0)\n"
|
||||||
|
"sd s2, 32(%0)\n"
|
||||||
|
"sd s3, 40(%0)\n"
|
||||||
|
"sd s4, 48(%0)\n"
|
||||||
|
"sd s5, 56(%0)\n"
|
||||||
|
"sd s6, 64(%0)\n"
|
||||||
|
"sd s7, 72(%0)\n"
|
||||||
|
"sd s8, 80(%0)\n"
|
||||||
|
"sd s9, 88(%0)\n"
|
||||||
|
"sd s10, 96(%0)\n"
|
||||||
|
"sd s11, 104(%0)\n"
|
||||||
|
"li a0, 0\n"
|
||||||
|
: : "r"(env) : "memory", "a0"
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void exit(int status) { while(1); }
|
void longjmp(jmp_buf env, int val) {
|
||||||
|
__asm__ volatile (
|
||||||
|
"ld ra, 0(%0)\n"
|
||||||
|
"ld sp, 8(%0)\n"
|
||||||
|
"ld s0, 16(%0)\n"
|
||||||
|
"ld s1, 24(%0)\n"
|
||||||
|
"ld s2, 32(%0)\n"
|
||||||
|
"ld s3, 40(%0)\n"
|
||||||
|
"ld s4, 48(%0)\n"
|
||||||
|
"ld s5, 56(%0)\n"
|
||||||
|
"ld s6, 64(%0)\n"
|
||||||
|
"ld s7, 72(%0)\n"
|
||||||
|
"ld s8, 80(%0)\n"
|
||||||
|
"ld s9, 88(%0)\n"
|
||||||
|
"ld s10, 96(%0)\n"
|
||||||
|
"ld s11, 104(%0)\n"
|
||||||
|
"mv a0, %1\n"
|
||||||
|
"seqz t0, a0\n"
|
||||||
|
"add a0, a0, t0\n"
|
||||||
|
: : "r"(env), "r"(val) : "memory", "a0", "t0"
|
||||||
|
);
|
||||||
|
// Note: longjmp should not return. In this freestanding env,
|
||||||
|
// the asm above ends with registers restored and we jump back.
|
||||||
|
// However, to be extra safe we could add a ret at the end of asm.
|
||||||
|
__asm__ volatile("ret");
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit(int status) {
|
||||||
|
syscall(0x01, (long)status, 0, 0);
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue