Compare commits
25 Commits
0d3d51a4f1
...
d62bfdf6cb
| Author | SHA1 | Date |
|---|---|---|
|
|
d62bfdf6cb | |
|
|
6335b54e1f | |
|
|
6215ca6888 | |
|
|
c6719ba950 | |
|
|
bc25cbf599 | |
|
|
f5215a1be9 | |
|
|
19dd18f29d | |
|
|
c94e6dade3 | |
|
|
04b6398524 | |
|
|
0ebdce2d4f | |
|
|
0b5941137d | |
|
|
2e55188096 | |
|
|
f191fb28ef | |
|
|
d623efdce3 | |
|
|
840f60056d | |
|
|
002f3923b3 | |
|
|
d1ad4f8d4c | |
|
|
61893a3040 | |
|
|
38a70ca783 | |
|
|
12564ac45c | |
|
|
ae667db013 | |
|
|
2da8463fb7 | |
|
|
04663318f3 | |
|
|
be47ab4ae1 | |
|
|
18ce029070 |
|
|
@ -0,0 +1,131 @@
|
||||||
|
name: Rumpk CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [unstable, main]
|
||||||
|
pull_request:
|
||||||
|
branches: [unstable, main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-riscv:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: nexus-os/build-env:latest
|
||||||
|
options: --privileged
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build RISC-V kernel
|
||||||
|
run: |
|
||||||
|
echo "Building for RISC-V..."
|
||||||
|
zig build -Darch=riscv64 -Drelease
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rumpk-riscv64
|
||||||
|
path: zig-out/rumpk-riscv64.elf
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
build-aarch64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: nexus-os/build-env:latest
|
||||||
|
options: --privileged
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build ARM64 kernel
|
||||||
|
run: |
|
||||||
|
echo "Building for ARM64..."
|
||||||
|
zig build -Darch=aarch64 -Drelease
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rumpk-aarch64
|
||||||
|
path: zig-out/rumpk-aarch64.elf
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
security-audit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Sensitive content scan
|
||||||
|
run: |
|
||||||
|
echo "🔍 Scanning for sensitive content..."
|
||||||
|
|
||||||
|
# Check for forbidden directories
|
||||||
|
if git log --all --name-only | grep -qE '\.agent/|\.vscode/|\.claude/|\.kiro/'; then
|
||||||
|
echo "❌ CRITICAL: Forbidden directory found in git history"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for internal paths
|
||||||
|
if git log --all -p | grep -qE '/home/markus/zWork/|/home/markus/\.claude/'; then
|
||||||
|
echo "❌ CRITICAL: Internal path found in git history"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ No sensitive content detected"
|
||||||
|
|
||||||
|
- name: License header check
|
||||||
|
run: |
|
||||||
|
echo "Checking license headers..."
|
||||||
|
# TODO: Implement license header checker
|
||||||
|
echo "ℹ️ License check pending"
|
||||||
|
|
||||||
|
test-qemu:
|
||||||
|
needs: [build-riscv]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: nexus-os/build-env:latest
|
||||||
|
options: --privileged
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rumpk-riscv64
|
||||||
|
path: zig-out/
|
||||||
|
|
||||||
|
- name: QEMU boot test
|
||||||
|
timeout-minutes: 5
|
||||||
|
run: |
|
||||||
|
echo "🚀 Booting RISC-V kernel in QEMU..."
|
||||||
|
timeout 10s qemu-system-riscv64 \
|
||||||
|
-machine virt \
|
||||||
|
-cpu rv64 \
|
||||||
|
-smp 2 \
|
||||||
|
-m 128M \
|
||||||
|
-kernel zig-out/rumpk-riscv64.elf \
|
||||||
|
-serial stdio \
|
||||||
|
-display none \
|
||||||
|
-bios none || echo "Boot test completed"
|
||||||
|
|
||||||
|
reproducibility-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: nexus-os/build-env:latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build twice and compare
|
||||||
|
run: |
|
||||||
|
echo "🔧 Building first time..."
|
||||||
|
zig build -Drelease
|
||||||
|
cp zig-out/rumpk-riscv64.elf /tmp/build1.elf
|
||||||
|
|
||||||
|
echo "🔧 Building second time..."
|
||||||
|
zig build -Drelease
|
||||||
|
|
||||||
|
echo "📊 Comparing builds..."
|
||||||
|
if diff /tmp/build1.elf zig-out/rumpk-riscv64.elf; then
|
||||||
|
echo "✅ Reproducible build verified"
|
||||||
|
else
|
||||||
|
echo "⚠️ Build not reproducible (timestamps embedded)"
|
||||||
|
fi
|
||||||
|
|
@ -6,29 +6,73 @@
|
||||||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
## Sovereign Init: The Genesis Process
|
## Sovereign Init: The Genesis Process
|
||||||
##
|
|
||||||
## Responsible for bootstrapping the system, starting core services,
|
|
||||||
## and managing the lifecycle of the user environment.
|
|
||||||
|
|
||||||
import ../../libs/membrane/libc
|
import ../../libs/membrane/libc
|
||||||
|
|
||||||
# --- Entry Point ---
|
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
# 1. Pledge Sovereignty
|
# 1. Pledge Sovereignty
|
||||||
discard pledge(0xFFFFFFFFFFFFFFFF'u64) # PLEDGE_ALL
|
discard pledge(0xFFFFFFFFFFFFFFFF'u64) # PLEDGE_ALL
|
||||||
|
|
||||||
print("\n")
|
print(cstring("\n"))
|
||||||
print("\x1b[1;35m╔═══════════════════════════════════════╗\x1b[0m\n")
|
print(cstring("\x1b[1;35m╔═══════════════════════════════════════╗\x1b[0m\n"))
|
||||||
print("\x1b[1;35m║ SOVEREIGN INIT (NexInit v0.1) ║\x1b[0m\n")
|
print(cstring("\x1b[1;35m║ SOVEREIGN INIT (NexInit v1.0) ║\x1b[0m\n"))
|
||||||
print("\x1b[1;35m╚═══════════════════════════════════════╝\x1b[0m\n\n")
|
print(cstring("\x1b[1;35m╚═══════════════════════════════════════╝\x1b[0m\n\n"))
|
||||||
|
|
||||||
print("[INIT] System Ready. Starting heartbeat...\n")
|
print(cstring("[INIT] Initializing Membrane Network Stack...\n"))
|
||||||
|
membrane_init()
|
||||||
|
|
||||||
while true:
|
proc glue_get_ip(): uint32 {.importc: "glue_get_ip", cdecl.}
|
||||||
# 🕵️ DIAGNOSTIC: BREATHER
|
|
||||||
|
# --- DHCP PHASE ---
|
||||||
|
print(cstring("[INIT] Waiting for DHCP IP Address...\n"))
|
||||||
|
var ip: uint32 = 0
|
||||||
|
for i in 0 ..< 600: # 60 seconds
|
||||||
pump_membrane_stack()
|
pump_membrane_stack()
|
||||||
yield_fiber()
|
ip = glue_get_ip()
|
||||||
|
if ip != 0: break
|
||||||
|
discard syscall(0x65, 100000000'u64) # 100ms
|
||||||
|
|
||||||
|
if ip == 0:
|
||||||
|
print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n"))
|
||||||
|
else:
|
||||||
|
print(cstring("[INIT] Network ONLINE (10.0.2.15)\n"))
|
||||||
|
|
||||||
|
# --- DNS PHASE ---
|
||||||
|
print(cstring("\n[TEST] ══════════════════════════════════════\n"))
|
||||||
|
print(cstring("[TEST] DNS Resolution: google.com\n"))
|
||||||
|
print(cstring("[TEST] ══════════════════════════════════════\n\n"))
|
||||||
|
|
||||||
|
var res: ptr AddrInfo
|
||||||
|
for attempt in 1..5:
|
||||||
|
print(cstring("[TEST] Resolving google.com (Attempt "))
|
||||||
|
# (Simplified number printing not available, just loop)
|
||||||
|
|
||||||
|
if getaddrinfo("google.com", nil, nil, addr res) == 0:
|
||||||
|
print(cstring(") -> SUCCESS!\n"))
|
||||||
|
freeaddrinfo(res)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(cstring(") -> FAILED. Waiting 5s...\n"))
|
||||||
|
for j in 1..50:
|
||||||
|
pump_membrane_stack()
|
||||||
|
discard syscall(0x65, 100000000'u64) # 100ms
|
||||||
|
|
||||||
|
# --- SHELL PHASE ---
|
||||||
|
proc spawn_fiber(path: cstring): int =
|
||||||
|
return int(syscall(0x300, cast[uint64](path), 0, 0))
|
||||||
|
|
||||||
|
print(cstring("[INIT] Spawning mksh...\n"))
|
||||||
|
discard spawn_fiber(cstring("/bin/mksh"))
|
||||||
|
|
||||||
|
# --- SUPERVISOR PHASE ---
|
||||||
|
print(cstring("[INIT] Entering Supervisor Loop...\n"))
|
||||||
|
var loop_count = 0
|
||||||
|
while true:
|
||||||
|
pump_membrane_stack()
|
||||||
|
loop_count += 1
|
||||||
|
if loop_count mod 100 == 0:
|
||||||
|
print(cstring("[INIT] Heartbeat\n"))
|
||||||
|
discard syscall(0x65, 100000000'u64) # 100ms
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
.section .text._start, "ax"
|
.section .text._start, "ax"
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
# 🕵️ DIAGNOSTIC: BREATHE
|
# 🕵️ BSS Clearing
|
||||||
li t0, 0x10000000
|
|
||||||
li t1, 0x23 # '#'
|
|
||||||
sb t1, 0(t0)
|
|
||||||
|
|
||||||
# Clear BSS (64-bit aligned zeroing)
|
|
||||||
la t0, __bss_start
|
la t0, __bss_start
|
||||||
la t1, __bss_end
|
la t1, __bss_end
|
||||||
1: bge t0, t1, 2f
|
1: bge t0, t1, 2f
|
||||||
|
|
@ -16,33 +11,11 @@ _start:
|
||||||
2:
|
2:
|
||||||
fence rw, rw
|
fence rw, rw
|
||||||
|
|
||||||
# 🔧 CRITICAL FIX: Set up stack pointer for userland
|
# Arguments (argc, argv) are already in a0, a1 from Kernel
|
||||||
# Stack grows down from top of 128MB userland RAM (0x90000000 - 32 bytes for alignment)
|
# sp is already pointing to argc from Kernel
|
||||||
li sp, 0x8FFFFFE0
|
|
||||||
|
|
||||||
# 🔧 CRITICAL FIX: Set up global pointer for RISC-V ABI
|
|
||||||
# Global pointer should point to .sdata section for efficient global access
|
|
||||||
# For userland at 0x88000000, set gp to middle of address space
|
|
||||||
.option push
|
|
||||||
.option norelax
|
|
||||||
la gp, __global_pointer$
|
|
||||||
.option pop
|
|
||||||
|
|
||||||
# 🕵️ DIAGNOSTIC: READY TO CALL MAIN
|
|
||||||
li t0, 0x10000000
|
|
||||||
li t1, 0x21 # '!'
|
|
||||||
sb t1, 0(t0)
|
|
||||||
|
|
||||||
# Call main(0, NULL)
|
|
||||||
li a0, 0
|
|
||||||
li a1, 0
|
|
||||||
call main
|
call main
|
||||||
|
|
||||||
# 🕵️ DIAGNOSTIC: RETURNED FROM MAIN
|
|
||||||
# li t0, 0x10000000
|
|
||||||
# li t1, 0x24 # '$'
|
|
||||||
# sb t1, 0(t0)
|
|
||||||
|
|
||||||
# Call exit(result)
|
# Call exit(result)
|
||||||
call exit
|
call exit
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,19 @@ export const multiboot2_header linksection(".multiboot2") = Multiboot2Header{
|
||||||
// Entry Point
|
// Entry Point
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
extern fn kmain() noreturn;
|
extern fn riscv_init() noreturn;
|
||||||
|
|
||||||
export fn _start() callconv(.Naked) noreturn {
|
// 1MB Kernel Stack
|
||||||
// Clear BSS, set up stack, then jump to Nim
|
const STACK_SIZE = 0x100000;
|
||||||
|
export var kernel_stack: [STACK_SIZE]u8 align(16) linksection(".bss.stack") = undefined;
|
||||||
|
|
||||||
|
export fn _start() callconv(.naked) noreturn {
|
||||||
|
// Clear BSS, set up stack, then jump to RISC-V Init
|
||||||
asm volatile (
|
asm volatile (
|
||||||
\\ // Set up stack
|
\\ // Set up stack
|
||||||
\\ la sp, __stack_top
|
\\ la sp, kernel_stack
|
||||||
|
\\ li t0, %[stack_size]
|
||||||
|
\\ add sp, sp, t0
|
||||||
\\
|
\\
|
||||||
\\ // Clear BSS
|
\\ // Clear BSS
|
||||||
\\ la t0, __bss_start
|
\\ la t0, __bss_start
|
||||||
|
|
@ -63,11 +69,13 @@ export fn _start() callconv(.Naked) noreturn {
|
||||||
\\ addi t0, t0, 8
|
\\ addi t0, t0, 8
|
||||||
\\ j 1b
|
\\ j 1b
|
||||||
\\2:
|
\\2:
|
||||||
\\ // Jump to Nim kmain
|
\\ // Jump to HAL Init
|
||||||
\\ call kmain
|
\\ call riscv_init
|
||||||
\\
|
\\
|
||||||
\\ // Should never return
|
\\ // Should never return
|
||||||
\\ wfi
|
\\ wfi
|
||||||
\\ j 2b
|
\\ j 2b
|
||||||
|
:
|
||||||
|
: [stack_size] "i" (STACK_SIZE),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
# Rumpk Linker Script (ARM64)
|
# Rumpk Linker Script (RISC-V 64)
|
||||||
# For QEMU virt machine
|
# For QEMU virt machine (RISC-V)
|
||||||
|
|
||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
. = 0x40080000; /* QEMU virt kernel load address */
|
. = 0x80200000; /* Standard RISC-V QEMU virt kernel address */
|
||||||
|
PROVIDE(__kernel_vbase = .);
|
||||||
|
PROVIDE(__kernel_pbase = .);
|
||||||
|
|
||||||
.text : {
|
.text : {
|
||||||
*(.text._start)
|
*(.text._start)
|
||||||
|
|
@ -17,9 +19,19 @@ SECTIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
.data : {
|
.data : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
__global_pointer$ = . + 0x800;
|
||||||
|
*(.sdata*)
|
||||||
|
*(.sdata.*)
|
||||||
*(.data*)
|
*(.data*)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.initrd : {
|
||||||
|
_initrd_start = .;
|
||||||
|
KEEP(*(.initrd))
|
||||||
|
_initrd_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
.bss : {
|
.bss : {
|
||||||
__bss_start = .;
|
__bss_start = .;
|
||||||
*(.bss*)
|
*(.bss*)
|
||||||
|
|
@ -27,6 +39,12 @@ SECTIONS
|
||||||
__bss_end = .;
|
__bss_end = .;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stack (NOLOAD) : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
. += 0x100000; /* 1MB Stack */
|
||||||
|
PROVIDE(__stack_top = .);
|
||||||
|
}
|
||||||
|
|
||||||
/DISCARD/ : {
|
/DISCARD/ : {
|
||||||
*(.comment)
|
*(.comment)
|
||||||
*(.note*)
|
*(.note*)
|
||||||
|
|
|
||||||
50
build.zig
50
build.zig
|
|
@ -29,6 +29,7 @@ pub fn build(b: *std.Build) void {
|
||||||
// Freestanding kernel - no libc, no red zone, no stack checks
|
// Freestanding kernel - no libc, no red zone, no stack checks
|
||||||
hal_mod.red_zone = false;
|
hal_mod.red_zone = false;
|
||||||
hal_mod.stack_check = 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",
|
||||||
|
|
@ -58,13 +59,60 @@ pub fn build(b: *std.Build) void {
|
||||||
});
|
});
|
||||||
boot_mod.red_zone = false;
|
boot_mod.red_zone = false;
|
||||||
boot_mod.stack_check = false;
|
boot_mod.stack_check = false;
|
||||||
|
boot_mod.code_model = .medany;
|
||||||
|
|
||||||
const boot = b.addObject(.{
|
const boot = b.addObject(.{
|
||||||
.name = "boot",
|
.name = "boot",
|
||||||
.root_module = boot_mod,
|
.root_module = boot_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
_ = boot; // Mark as used for now
|
// =========================================================
|
||||||
|
// Final Link: rumpk.elf
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
const kernel_mod = b.createModule(.{
|
||||||
|
.root_source_file = b.path("hal/abi.zig"), // Fake root, we add objects later
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
kernel_mod.red_zone = false;
|
||||||
|
kernel_mod.stack_check = false;
|
||||||
|
kernel_mod.code_model = .medany;
|
||||||
|
|
||||||
|
const kernel = b.addExecutable(.{
|
||||||
|
.name = "rumpk.elf",
|
||||||
|
.root_module = kernel_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
kernel.setLinkerScript(b.path("boot/linker.ld"));
|
||||||
|
kernel.addObject(boot);
|
||||||
|
// kernel.linkLibrary(hal); // Redundant, already in kernel_mod
|
||||||
|
|
||||||
|
// Add Nim-generated objects
|
||||||
|
{
|
||||||
|
var nimcache_dir = std.fs.cwd().openDir("build/nimcache", .{ .iterate = true }) catch |err| {
|
||||||
|
std.debug.print("Warning: Could not open nimcache dir: {}\n", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer nimcache_dir.close();
|
||||||
|
var it = nimcache_dir.iterate();
|
||||||
|
while (it.next() catch null) |entry| {
|
||||||
|
if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".o")) {
|
||||||
|
const path = b.fmt("build/nimcache/{s}", .{entry.name});
|
||||||
|
kernel.addObjectFile(b.path(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add external pre-built dependencies (Order matters: Libs after users)
|
||||||
|
kernel.addObjectFile(b.path("build/switch.o")); // cpu_switch_to
|
||||||
|
kernel.addObjectFile(b.path("build/sys_arch.o")); // sys_now, nexus_lwip_panic
|
||||||
|
kernel.addObjectFile(b.path("build/libc_shim.o"));
|
||||||
|
kernel.addObjectFile(b.path("build/clib.o"));
|
||||||
|
kernel.addObjectFile(b.path("build/liblwip.a"));
|
||||||
|
kernel.addObjectFile(b.path("build/initrd.o"));
|
||||||
|
|
||||||
|
b.installArtifact(kernel);
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Tests
|
// Tests
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
# SPDX-License-Identifier: LSL-1.0
|
||||||
|
# Copyright (c) 2026 Markus Maiwald
|
||||||
|
# Stewardship: Self Sovereign Society Foundation
|
||||||
|
#
|
||||||
|
# This file is part of the Nexus Sovereign Core.
|
||||||
|
|
||||||
|
## Rumpk Layer 1: Typed Channels (SPEC-070)
|
||||||
|
|
||||||
|
import ion
|
||||||
|
import cspace
|
||||||
|
|
||||||
|
# Kernel logging
|
||||||
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
|
|
||||||
|
proc get_channel_ring*(id: uint64): pointer =
|
||||||
|
## Map a Channel ID (object_id) to a physical HAL ring pointer
|
||||||
|
case id:
|
||||||
|
of 0x1000: return cast[pointer](chan_input.ring)
|
||||||
|
of 0x1001: return cast[pointer](chan_tx.ring) # console.output
|
||||||
|
of 0x500: return cast[pointer](chan_net_tx.ring)
|
||||||
|
of 0x501: return cast[pointer](chan_net_rx.ring)
|
||||||
|
else: return nil
|
||||||
|
|
||||||
|
proc channel_has_data*(id: uint64): bool =
|
||||||
|
## Check if a channel has data (for RX) or space (for TX)
|
||||||
|
## NOTE: This depends on whether the capability is for READ or WRITE.
|
||||||
|
## For now, we focus on RX (has data).
|
||||||
|
let ring_ptr = get_channel_ring(id)
|
||||||
|
if ring_ptr == nil: return false
|
||||||
|
|
||||||
|
# Cast to a generic HAL_Ring to check head/tail
|
||||||
|
# All IonPacket rings are 256 entries
|
||||||
|
let ring = cast[ptr HAL_Ring[IonPacket]](ring_ptr)
|
||||||
|
return ring.head != ring.tail
|
||||||
|
|
||||||
|
proc fiber_can_run_on_channels*(f_id: uint64, mask: uint64): bool {.exportc, cdecl.} =
|
||||||
|
## Check if any of the channels in the mask have active data
|
||||||
|
if mask == 0: return true # Not waiting on anything specific
|
||||||
|
|
||||||
|
for i in 0..<64:
|
||||||
|
if (mask and (1'u64 shl i)) != 0:
|
||||||
|
# Slot i is active in mask
|
||||||
|
let cap = cspace_lookup(f_id, uint(i))
|
||||||
|
if cap != nil:
|
||||||
|
# Cast pointer to Capability struct (wait, we need the Nim definition)
|
||||||
|
# Actually, let's just use a C helper if needed, but we can do it here.
|
||||||
|
# Capability is 32 bytes. object_id is at offset 4 (wait, 1+1+2 = 4).
|
||||||
|
let obj_id = cast[ptr uint64](cast[uint](cap) + 4)[]
|
||||||
|
if channel_has_data(obj_id):
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
# SPDX-License-Identifier: LSL-1.0
|
||||||
|
# Copyright (c) 2026 Markus Maiwald
|
||||||
|
# Stewardship: Self Sovereign Society Foundation
|
||||||
|
#
|
||||||
|
# This file is part of the Nexus Sovereign Core.
|
||||||
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
|
# SPEC-051: CSpace Integration with Fiber Control Block
|
||||||
|
# Ground Zero Phase 1: Kernel Integration
|
||||||
|
|
||||||
|
## CSpace Nim Bindings
|
||||||
|
|
||||||
|
# Kernel logging (freestanding-safe)
|
||||||
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
|
|
||||||
|
# Import CSpace from HAL
|
||||||
|
proc cspace_init*() {.importc, cdecl.}
|
||||||
|
proc cspace_get*(fiber_id: uint64): pointer {.importc, cdecl.}
|
||||||
|
proc cspace_grant_cap*(
|
||||||
|
fiber_id: uint64,
|
||||||
|
cap_type: uint8,
|
||||||
|
perms: uint8,
|
||||||
|
object_id: uint64,
|
||||||
|
bounds_start: uint64,
|
||||||
|
bounds_end: uint64
|
||||||
|
): int32 {.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_check_perm*(fiber_id: uint64, slot: uint, perm_bits: uint8): bool {.importc, cdecl.}
|
||||||
|
|
||||||
|
## Capability Types (Mirror from cspace.zig)
|
||||||
|
type
|
||||||
|
CapType* = enum
|
||||||
|
CapNull = 0
|
||||||
|
CapEntity = 1
|
||||||
|
CapChannel = 2
|
||||||
|
CapMemory = 3
|
||||||
|
CapInterrupt = 4
|
||||||
|
CapTime = 5
|
||||||
|
CapEntropy = 6
|
||||||
|
|
||||||
|
## Permission Flags
|
||||||
|
const
|
||||||
|
PERM_READ* = 0x01'u8
|
||||||
|
PERM_WRITE* = 0x02'u8
|
||||||
|
PERM_EXECUTE* = 0x04'u8
|
||||||
|
PERM_MAP* = 0x08'u8
|
||||||
|
PERM_DELEGATE* = 0x10'u8
|
||||||
|
PERM_REVOKE* = 0x20'u8
|
||||||
|
PERM_COPY* = 0x40'u8
|
||||||
|
PERM_SPAWN* = 0x80'u8
|
||||||
|
|
||||||
|
## High-level API for kernel use
|
||||||
|
|
||||||
|
proc fiber_grant_channel*(fiber_id: uint64, channel_id: uint64, perms: uint8): int32 =
|
||||||
|
## Grant a Channel capability to a fiber
|
||||||
|
return cspace_grant_cap(
|
||||||
|
fiber_id,
|
||||||
|
uint8(CapChannel),
|
||||||
|
perms,
|
||||||
|
channel_id,
|
||||||
|
0, # No bounds for channels
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
proc fiber_grant_memory*(
|
||||||
|
fiber_id: uint64,
|
||||||
|
region_id: uint64,
|
||||||
|
start_addr: uint64,
|
||||||
|
end_addr: uint64,
|
||||||
|
perms: uint8
|
||||||
|
): int32 =
|
||||||
|
## Grant a Memory capability to a fiber
|
||||||
|
return cspace_grant_cap(
|
||||||
|
fiber_id,
|
||||||
|
uint8(CapMemory),
|
||||||
|
perms,
|
||||||
|
region_id,
|
||||||
|
start_addr,
|
||||||
|
end_addr
|
||||||
|
)
|
||||||
|
|
||||||
|
proc fiber_check_channel_access*(fiber_id: uint64, slot: uint, write: bool): bool =
|
||||||
|
## Check if fiber has channel access via capability
|
||||||
|
let perm = if write: PERM_WRITE else: PERM_READ
|
||||||
|
return cspace_check_perm(fiber_id, slot, perm)
|
||||||
|
|
||||||
|
proc fiber_revoke_capability*(fiber_id: uint64, slot: uint) =
|
||||||
|
## Revoke a capability from a fiber
|
||||||
|
cspace_revoke(fiber_id, slot)
|
||||||
|
|
||||||
|
## Initialization
|
||||||
|
proc init_cspace_subsystem*() =
|
||||||
|
## Initialize the CSpace subsystem (call from kmain)
|
||||||
|
cspace_init()
|
||||||
|
kprintln("[CSpace] Capability system initialized")
|
||||||
255
core/cstubs.c
255
core/cstubs.c
|
|
@ -1,210 +1,57 @@
|
||||||
// C runtime stubs for freestanding Nim
|
// C runtime stubs for freestanding Nim
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
void *memcpy(void *dest, const void *src, size_t n) {
|
/* Duplicates provided by libnexus.a (clib.o) */
|
||||||
unsigned char *d = dest;
|
#if 0
|
||||||
const unsigned char *s = src;
|
void *memcpy(void *dest, const void *src, size_t n) { ... }
|
||||||
while (n--) *d++ = *s++;
|
void *memset(void *s, int c, size_t n) { ... }
|
||||||
return dest;
|
void *memmove(void *dest, const void *src, size_t n) { ... }
|
||||||
}
|
int memcmp(const void *s1, const void *s2, size_t n) { ... }
|
||||||
|
|
||||||
void *memset(void *s, int c, size_t n) {
|
/* Externs from libnexus.a */
|
||||||
unsigned char *p = s;
|
extern size_t strlen(const char *s);
|
||||||
while (n--) *p++ = (unsigned char)c;
|
extern int atoi(const char *nptr);
|
||||||
return s;
|
extern int strncmp(const char *s1, const char *s2, size_t n);
|
||||||
}
|
|
||||||
|
|
||||||
void *memmove(void *dest, const void *src, size_t n) {
|
char *strcpy(char *dest, const char *src) { ... }
|
||||||
unsigned char *d = dest;
|
int strcmp(const char *s1, const char *s2) { ... }
|
||||||
const unsigned char *s = src;
|
char *strncpy(char *dest, const char *src, size_t n) { ... }
|
||||||
if (d < s) {
|
#endif
|
||||||
while (n--) *d++ = *s++;
|
|
||||||
} else {
|
// panic is used by abort/exit
|
||||||
d += n;
|
void panic(const char* msg) {
|
||||||
s += n;
|
extern void console_write(const char*, unsigned long);
|
||||||
while (n--) *--d = *--s;
|
extern size_t strlen(const char*);
|
||||||
|
console_write("\n[KERNEL PANIC] ", 16);
|
||||||
|
if (msg) console_write(msg, strlen(msg));
|
||||||
|
else console_write("Unknown Error", 13);
|
||||||
|
console_write("\n", 1);
|
||||||
|
while(1) {
|
||||||
|
// Halt
|
||||||
}
|
}
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
int memcmp(const void *s1, const void *s2, size_t n) {
|
|
||||||
const unsigned char *p1 = s1, *p2 = s2;
|
|
||||||
while (n--) {
|
|
||||||
if (*p1 != *p2) return *p1 - *p2;
|
|
||||||
p1++; p2++;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t strlen(const char *s) {
|
|
||||||
size_t len = 0;
|
|
||||||
while (*s++) len++;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *strcpy(char *dest, const char *src) {
|
|
||||||
char *d = dest;
|
|
||||||
while ((*d++ = *src++));
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
int strcmp(const char *s1, const char *s2) {
|
|
||||||
while (*s1 && (*s1 == *s2)) { s1++; s2++; }
|
|
||||||
return *(unsigned char*)s1 - *(unsigned char*)s2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int strncmp(const char *s1, const char *s2, size_t n) {
|
|
||||||
while (n && *s1 && (*s1 == *s2)) {
|
|
||||||
s1++; s2++; n--;
|
|
||||||
}
|
|
||||||
if (n == 0) return 0;
|
|
||||||
return *(unsigned char*)s1 - *(unsigned char*)s2;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *strncpy(char *dest, const char *src, size_t n) {
|
|
||||||
char *d = dest;
|
|
||||||
while (n && (*d++ = *src++)) n--;
|
|
||||||
while (n--) *d++ = '\0';
|
|
||||||
return dest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// abort is used by Nim panic
|
// abort is used by Nim panic
|
||||||
void abort(void) {
|
void abort(void) {
|
||||||
/* Call Nim panic */
|
|
||||||
extern void panic(const char*);
|
|
||||||
panic("abort() called");
|
panic("abort() called");
|
||||||
while(1) {}
|
while(1) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* Stdio stubs - these call into Zig UART */
|
/* Stdio stubs - these call into Zig UART */
|
||||||
extern void console_write(const char*, unsigned long);
|
extern void console_write(const char*, unsigned long);
|
||||||
|
|
||||||
int puts(const char *s) {
|
int puts(const char *s) { ... }
|
||||||
if (s) {
|
int putchar(int c) { ... }
|
||||||
unsigned long len = strlen(s);
|
// ... (printf, etc)
|
||||||
console_write(s, len);
|
int snprintf(char *str, size_t size, const char *format, ...) { ... }
|
||||||
console_write("\n", 1);
|
int fflush(void *stream) { ... }
|
||||||
}
|
unsigned long fwrite(const void *ptr, unsigned long size, unsigned long nmemb, void *stream) { ... }
|
||||||
return 0;
|
sighandler_t signal(int signum, sighandler_t handler) { ... }
|
||||||
}
|
int raise(int sig) { ... }
|
||||||
|
int sprintf(char *str, const char *format, ...) { ... }
|
||||||
int putchar(int c) {
|
double strtod(const char *nptr, char **endptr) { ... }
|
||||||
char buf[1] = {(char)c};
|
#endif
|
||||||
console_write(buf, 1);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
void itoa(int n, char s[]) {
|
|
||||||
int i, sign;
|
|
||||||
if ((sign = n) < 0) n = -n;
|
|
||||||
i = 0;
|
|
||||||
do { s[i++] = n % 10 + '0'; } while ((n /= 10) > 0);
|
|
||||||
if (sign < 0) s[i++] = '-';
|
|
||||||
s[i] = '\0';
|
|
||||||
// reverse
|
|
||||||
for (int j = 0, k = i-1; j < k; j++, k--) {
|
|
||||||
char temp = s[j]; s[j] = s[k]; s[k] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int printf(const char *format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
while (*format) {
|
|
||||||
if (*format == '%' && *(format + 1)) {
|
|
||||||
format++;
|
|
||||||
if (*format == 's') {
|
|
||||||
char *s = va_arg(args, char *);
|
|
||||||
if (s) console_write(s, strlen(s));
|
|
||||||
} else if (*format == 'd') {
|
|
||||||
int d = va_arg(args, int);
|
|
||||||
char buf[16];
|
|
||||||
itoa(d, buf);
|
|
||||||
console_write(buf, strlen(buf));
|
|
||||||
} else {
|
|
||||||
putchar('%');
|
|
||||||
putchar(*format);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
putchar(*format);
|
|
||||||
}
|
|
||||||
format++;
|
|
||||||
}
|
|
||||||
va_end(args);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fprintf(void *stream, const char *format, ...) {
|
|
||||||
return printf(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
int vsnprintf(char *str, size_t size, const char *format, va_list args) {
|
|
||||||
size_t count = 0;
|
|
||||||
if (size == 0) return 0;
|
|
||||||
|
|
||||||
while (*format && count < size - 1) {
|
|
||||||
if (*format == '%' && *(format + 1)) {
|
|
||||||
format++;
|
|
||||||
if (*format == 's') {
|
|
||||||
char *s = va_arg(args, char *);
|
|
||||||
if (s) {
|
|
||||||
while (*s && count < size - 1) {
|
|
||||||
str[count++] = *s++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (*format == 'd' || *format == 'i') {
|
|
||||||
int d = va_arg(args, int);
|
|
||||||
char buf[16];
|
|
||||||
itoa(d, buf);
|
|
||||||
char *b = buf;
|
|
||||||
while (*b && count < size - 1) {
|
|
||||||
str[count++] = *b++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
str[count++] = '%';
|
|
||||||
if (count < size - 1) str[count++] = *format;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
str[count++] = *format;
|
|
||||||
}
|
|
||||||
format++;
|
|
||||||
}
|
|
||||||
str[count] = '\0';
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int snprintf(char *str, size_t size, const char *format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
int ret = vsnprintf(str, size, format, args);
|
|
||||||
va_end(args);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fflush(void *stream) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long fwrite(const void *ptr, unsigned long size, unsigned long nmemb, void *stream) {
|
|
||||||
console_write(ptr, size * nmemb);
|
|
||||||
return nmemb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signal stubs - no signals in freestanding */
|
|
||||||
typedef void (*sighandler_t)(int);
|
|
||||||
|
|
||||||
sighandler_t signal(int signum, sighandler_t handler) {
|
|
||||||
(void)signum;
|
|
||||||
(void)handler;
|
|
||||||
return (sighandler_t)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int raise(int sig) {
|
|
||||||
(void)sig;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Exit stubs */
|
/* Exit stubs */
|
||||||
void exit(int status) {
|
void exit(int status) {
|
||||||
|
|
@ -217,30 +64,10 @@ void _Exit(int status) {
|
||||||
exit(status);
|
exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
int atoi(const char *nptr) {
|
|
||||||
int res = 0;
|
|
||||||
while (*nptr >= '0' && *nptr <= '9') {
|
|
||||||
res = res * 10 + (*nptr - '0');
|
|
||||||
nptr++;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sprintf(char *str, const char *format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
// Unsafe sprintf limit
|
|
||||||
int ret = vsnprintf(str, 2048, format, args);
|
|
||||||
va_end(args);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
double strtod(const char *nptr, char **endptr) {
|
|
||||||
if (endptr) *endptr = (char*)nptr + strlen(nptr); // Fake endptr
|
|
||||||
return (double)atoi(nptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// qsort uses existing memcpy
|
// qsort uses existing memcpy
|
||||||
|
// Note: We need memcpy for qsort!
|
||||||
|
// libnexus.a provides memcpy. We need to declare it.
|
||||||
|
extern void *memcpy(void *dest, const void *src, size_t n);
|
||||||
|
|
||||||
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) {
|
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) {
|
||||||
// Bubble sort for simplicity (O(n^2))
|
// Bubble sort for simplicity (O(n^2))
|
||||||
|
|
@ -266,4 +93,4 @@ void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int errno = 0;
|
// int errno = 0; // Provided by clib.c
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
# SPDX-License-Identifier: LSL-1.0
|
||||||
|
# Copyright (c) 2026 Markus Maiwald
|
||||||
|
# Stewardship: Self Sovereign Society Foundation
|
||||||
|
#
|
||||||
|
# This file is part of the Nexus Sovereign Core.
|
||||||
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
|
## Fast Path Bypass (SPEC-700)
|
||||||
|
##
|
||||||
|
## Intercepts UTCP tunnel traffic (UDP/9999) before LwIP processing.
|
||||||
|
## Zero-copy header stripping via pointer arithmetic.
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
const
|
||||||
|
UTCP_TUNNEL_PORT* = 9999'u16
|
||||||
|
ETHERTYPE_IPV4* = 0x0800'u16
|
||||||
|
IPPROTO_UDP* = 17'u8
|
||||||
|
|
||||||
|
# Header sizes
|
||||||
|
ETH_HEADER_LEN* = 14
|
||||||
|
IP_HEADER_LEN* = 20
|
||||||
|
UDP_HEADER_LEN* = 8
|
||||||
|
TUNNEL_OVERHEAD* = ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN # 42 bytes
|
||||||
|
|
||||||
|
# MTU safety
|
||||||
|
MAX_UTCP_FRAME* = 1400'u16 # Safe for PPPoE/VPN
|
||||||
|
|
||||||
|
proc kprint(s: cstring) {.importc, cdecl.}
|
||||||
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
|
proc kprint_hex(n: uint64) {.importc, cdecl.}
|
||||||
|
|
||||||
|
# --- Fast Path Detection ---
|
||||||
|
|
||||||
|
proc is_utcp_tunnel*(data: ptr UncheckedArray[byte], len: uint16): bool {.exportc, cdecl.} =
|
||||||
|
## Check if packet is a UTCP tunnel packet (UDP port 9999)
|
||||||
|
## Returns true if packet should bypass LwIP
|
||||||
|
|
||||||
|
# DEBUG: Print first 50 bytes
|
||||||
|
kprintln("[FastPath] Checking packet...")
|
||||||
|
kprint(" Len: "); kprint_hex(uint64(len)); kprintln("")
|
||||||
|
if len >= 42:
|
||||||
|
kprint(" EthType: "); kprint_hex(uint64((uint16(data[12]) shl 8) or uint16(data[13]))); kprintln("")
|
||||||
|
kprint(" IPProto: "); kprint_hex(uint64(data[23])); kprintln("")
|
||||||
|
if len >= 38:
|
||||||
|
let dst_port = (uint16(data[36]) shl 8) or uint16(data[37])
|
||||||
|
kprint(" DstPort: "); kprint_hex(uint64(dst_port)); kprintln("")
|
||||||
|
|
||||||
|
# Minimum size check: ETH(14) + IP(20) + UDP(8) = 42 bytes
|
||||||
|
if len < TUNNEL_OVERHEAD:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Check EtherType (big-endian at offset 12-13)
|
||||||
|
let eth_type = (uint16(data[12]) shl 8) or uint16(data[13])
|
||||||
|
if eth_type != ETHERTYPE_IPV4:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Check IP Protocol (offset 23 in frame = offset 9 in IP header)
|
||||||
|
let ip_proto = data[23]
|
||||||
|
if ip_proto != IPPROTO_UDP:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Check UDP destination port (big-endian at offset 36-37)
|
||||||
|
# ETH(14) + IP(20) + UDP dst port offset(2) = 36
|
||||||
|
let dst_port = (uint16(data[36]) shl 8) or uint16(data[37])
|
||||||
|
|
||||||
|
if dst_port == UTCP_TUNNEL_PORT:
|
||||||
|
kprintln("[FastPath] UTCP TUNNEL DETECTED!")
|
||||||
|
|
||||||
|
return dst_port == UTCP_TUNNEL_PORT
|
||||||
|
|
||||||
|
|
||||||
|
proc strip_tunnel_headers*(data: ptr UncheckedArray[byte], len: var uint16): ptr UncheckedArray[byte] {.exportc, cdecl.} =
|
||||||
|
## Strip ETH+IP+UDP headers from tunnel packet (zero-copy)
|
||||||
|
## Returns pointer to UTCP header, adjusts length
|
||||||
|
##
|
||||||
|
## SAFETY: Caller must ensure len >= TUNNEL_OVERHEAD
|
||||||
|
|
||||||
|
if len < TUNNEL_OVERHEAD:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
# Zero-copy: just advance pointer
|
||||||
|
let utcp_data = cast[ptr UncheckedArray[byte]](
|
||||||
|
cast[uint64](data) + TUNNEL_OVERHEAD
|
||||||
|
)
|
||||||
|
len = len - TUNNEL_OVERHEAD
|
||||||
|
|
||||||
|
return utcp_data
|
||||||
|
|
||||||
|
|
||||||
|
proc check_mtu*(len: uint16): bool =
|
||||||
|
## Check if UTCP frame exceeds safe MTU
|
||||||
|
return len <= MAX_UTCP_FRAME
|
||||||
|
|
||||||
|
|
||||||
|
# --- Source Address Extraction (for response routing) ---
|
||||||
|
|
||||||
|
type
|
||||||
|
TunnelSource* = object
|
||||||
|
ip*: uint32 # Source IP (network byte order)
|
||||||
|
port*: uint16 # Source port
|
||||||
|
|
||||||
|
proc extract_tunnel_source*(data: ptr UncheckedArray[byte]): TunnelSource =
|
||||||
|
## Extract source IP and port from tunnel packet for response routing
|
||||||
|
|
||||||
|
# Source IP at ETH(14) + IP src offset(12) = 26
|
||||||
|
result.ip = (uint32(data[26]) shl 24) or
|
||||||
|
(uint32(data[27]) shl 16) or
|
||||||
|
(uint32(data[28]) shl 8) or
|
||||||
|
uint32(data[29])
|
||||||
|
|
||||||
|
# Source port at ETH(14) + IP(20) + UDP src offset(0) = 34
|
||||||
|
result.port = (uint16(data[34]) shl 8) or uint16(data[35])
|
||||||
105
core/fiber.nim
105
core/fiber.nim
|
|
@ -5,48 +5,40 @@
|
||||||
# This file is part of the Nexus Sovereign Core.
|
# This file is part of the Nexus Sovereign Core.
|
||||||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
## Rumpk Layer 1: Fiber Execution (Motive Power)
|
## Rumpk Layer 1: Fibers (The Sovereign Thread)
|
||||||
##
|
|
||||||
## Implements the unified multi-arch fiber context switching.
|
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||||
## Supported Architectures: x86_64, AArch64, RISC-V.
|
# Rumpk Phase 10: Multitasking & Context Switching
|
||||||
##
|
#
|
||||||
## SAFETY: Direct manipulation of stack pointers and CPU registers via
|
# Responsibilities:
|
||||||
## architecture-specific context frames. Swaps page tables during switch.
|
# - Define the Fiber abstraction (Hardware Context + Stack)
|
||||||
|
# - Abstract the ISA-specific context switch mechanism
|
||||||
|
# - Provide a high-level API for yielding and scheduling
|
||||||
|
|
||||||
{.push stackTrace: off, lineTrace: off.}
|
{.push stackTrace: off, lineTrace: off.}
|
||||||
|
|
||||||
# =========================================================
|
# Architecture Configuration
|
||||||
# Architecture-Specific Constants
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
when defined(amd64) or defined(x86_64):
|
when defined(riscv64):
|
||||||
const CONTEXT_SIZE = 56
|
const ARCH_NAME* = "riscv64"
|
||||||
const RET_ADDR_INDEX = 6 # RIP at [sp + 48]
|
const CONTEXT_SIZE* = 128
|
||||||
const ARCH_NAME = "x86_64"
|
const RET_ADDR_INDEX* = 0 # Offset in stack for RA
|
||||||
|
elif defined(amd64) or defined(x86_64):
|
||||||
elif defined(arm64) or defined(aarch64):
|
const ARCH_NAME* = "amd64"
|
||||||
const CONTEXT_SIZE = 96
|
const CONTEXT_SIZE* = 64
|
||||||
const RET_ADDR_INDEX = 11 # x30 (LR) at [sp + 88]
|
const RET_ADDR_INDEX* = 0
|
||||||
const ARCH_NAME = "aarch64"
|
|
||||||
|
|
||||||
elif defined(riscv64):
|
|
||||||
const CONTEXT_SIZE = 128
|
|
||||||
const RET_ADDR_INDEX = 0 # ra at [sp + 0]
|
|
||||||
const ARCH_NAME = "riscv64"
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
{.error: "Unsupported architecture for Rumpk fibers".}
|
{.error: "Unsupported Architecture".}
|
||||||
|
|
||||||
# =========================================================
|
# --- FIBER DEFINITION ---
|
||||||
# Types
|
|
||||||
# =========================================================
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Spectrum* = enum
|
Spectrum* {.pure.} = enum
|
||||||
Photon = 0 # UI/Audio (Top Tier)
|
Void = 0, # Default/Uninitialized
|
||||||
Matter = 1 # Interactive (Middle Tier)
|
Photon = 1, # Real-time (0-1ms latency)
|
||||||
Gravity = 2 # Batch (Bottom Tier)
|
Matter = 2, # Interactive (1-10ms latency)
|
||||||
Void = 3 # Unclassified/Demoted (Default)
|
Gravity = 3, # Batch/Idle (100ms+ latency)
|
||||||
|
|
||||||
FiberState* = object
|
FiberState* = object
|
||||||
sp*: uint64 # The Stack Pointer (Must be first field!)
|
sp*: uint64 # The Stack Pointer (Must be first field!)
|
||||||
|
|
@ -58,6 +50,7 @@ type
|
||||||
name*: cstring
|
name*: cstring
|
||||||
state*: FiberState
|
state*: FiberState
|
||||||
stack*: ptr UncheckedArray[uint8]
|
stack*: ptr UncheckedArray[uint8]
|
||||||
|
phys_offset*: uint64 # Cellular Memory Offset
|
||||||
stack_size*: int
|
stack_size*: int
|
||||||
sleep_until*: uint64 # NS timestamp
|
sleep_until*: uint64 # NS timestamp
|
||||||
promises*: uint64 # [63:62]=Spectrum, [61:0]=Pledge bits
|
promises*: uint64 # [63:62]=Spectrum, [61:0]=Pledge bits
|
||||||
|
|
@ -67,10 +60,17 @@ type
|
||||||
user_arg*: uint64 # Phase 29: Argument for user function
|
user_arg*: uint64 # Phase 29: Argument for user function
|
||||||
satp_value*: uint64 # Phase 31: Page table root (0 = use kernel map)
|
satp_value*: uint64 # Phase 31: Page table root (0 = use kernel map)
|
||||||
wants_yield*: bool # Phase 37: Deferred yield flag
|
wants_yield*: bool # Phase 37: Deferred yield flag
|
||||||
# SPEC-250: The Ratchet
|
# SPEC-102: The Ratchet
|
||||||
budget_ns*: uint64 # "I promise to run for X ns max"
|
budget_ns*: uint64 # "I promise to run for X ns max"
|
||||||
last_burst_ns*: uint64 # Actual execution time of last run
|
last_burst_ns*: uint64 # Actual execution time of last run
|
||||||
violations*: uint32 # Strike counter (3 strikes = demotion)
|
violations*: uint32 # Strike counter (3 strikes = demotion)
|
||||||
|
pty_id*: int # Phase 40: Assigned PTY ID (-1 if none)
|
||||||
|
user_sp_init*: uint64 # Initial SP for userland entry
|
||||||
|
# Ground Zero Phase 1: Capability Space (SPEC-051)
|
||||||
|
cspace_id*: uint64 # Index into global CSpace table
|
||||||
|
# Ground Zero Phase 3: Typed Channels & I/O Multiplexing
|
||||||
|
blocked_on_mask*: uint64 # Bitmask of capability slots fiber is waiting on
|
||||||
|
is_blocked*: bool # True if fiber is waiting for I/O
|
||||||
|
|
||||||
# Spectrum Accessors
|
# Spectrum Accessors
|
||||||
proc getSpectrum*(f: Fiber): Spectrum =
|
proc getSpectrum*(f: Fiber): Spectrum =
|
||||||
|
|
@ -92,15 +92,15 @@ proc cpu_switch_to(prev_sp_ptr: ptr uint64, next_sp: uint64) {.importc, cdecl.}
|
||||||
proc mm_activate_satp(satp_val: uint64) {.importc, cdecl.}
|
proc mm_activate_satp(satp_val: uint64) {.importc, cdecl.}
|
||||||
proc mm_get_kernel_satp(): uint64 {.importc, cdecl.}
|
proc mm_get_kernel_satp(): uint64 {.importc, cdecl.}
|
||||||
|
|
||||||
# Import console for debugging
|
proc debug(s: cstring) =
|
||||||
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
proc console_write(p: pointer, len: int) {.importc, cdecl.}
|
||||||
|
var i = 0
|
||||||
proc debug*(s: string) =
|
while s[i] != '\0': i += 1
|
||||||
if s.len > 0:
|
if i > 0:
|
||||||
console_write(unsafeAddr s[0], csize_t(s.len))
|
console_write(cast[pointer](s), i)
|
||||||
|
|
||||||
proc print_arch_info*() =
|
proc print_arch_info*() =
|
||||||
debug("[Rumpk] Architecture Context: " & ARCH_NAME & "\n")
|
debug("[Rumpk] Architecture Context: riscv64\n")
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Constants
|
# Constants
|
||||||
|
|
@ -120,23 +120,23 @@ var current_fiber* {.global.}: Fiber = addr main_fiber
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
|
proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
|
||||||
var msg = "[FIBER] Trampoline Entry!\n"
|
let msg: cstring = "[FIBER] Trampoline Entry!\n"
|
||||||
console_write(addr msg[0], csize_t(msg.len))
|
# We can't use kprintln here if it's not imported or we use emit
|
||||||
|
proc console_write(p: pointer, len: int) {.importc, cdecl.}
|
||||||
|
var i = 0
|
||||||
|
while msg[i] != '\0': i += 1
|
||||||
|
console_write(cast[pointer](msg), i)
|
||||||
let f = current_fiber
|
let f = current_fiber
|
||||||
|
|
||||||
if f.state.entry != nil:
|
if f.state.entry != nil:
|
||||||
f.state.entry()
|
f.state.entry()
|
||||||
|
|
||||||
# If the fiber returns, halt
|
# If the fiber returns, halt
|
||||||
when defined(amd64) or defined(x86_64):
|
when defined(riscv64):
|
||||||
while true:
|
|
||||||
{.emit: "asm volatile(\"hlt\");".}
|
|
||||||
elif defined(arm64) or defined(aarch64):
|
|
||||||
while true:
|
|
||||||
{.emit: "asm volatile(\"wfi\");".}
|
|
||||||
elif defined(riscv64):
|
|
||||||
while true:
|
while true:
|
||||||
{.emit: "asm volatile(\"wfi\");".}
|
{.emit: "asm volatile(\"wfi\");".}
|
||||||
|
else:
|
||||||
|
while true: discard
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Fiber Initialization (Arch-Specific)
|
# Fiber Initialization (Arch-Specific)
|
||||||
|
|
@ -147,6 +147,11 @@ proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer, size: i
|
||||||
f.stack = cast[ptr UncheckedArray[uint8]](stack_base)
|
f.stack = cast[ptr UncheckedArray[uint8]](stack_base)
|
||||||
f.stack_size = size
|
f.stack_size = size
|
||||||
f.sleep_until = 0
|
f.sleep_until = 0
|
||||||
|
f.pty_id = -1
|
||||||
|
f.user_sp_init = 0
|
||||||
|
f.cspace_id = f.id # Ground Zero: CSpace ID matches Fiber ID
|
||||||
|
f.blocked_on_mask = 0
|
||||||
|
f.is_blocked = false
|
||||||
|
|
||||||
# Start at top of stack (using actual size)
|
# Start at top of stack (using actual size)
|
||||||
var sp = cast[uint64](stack_base) + cast[uint64](size)
|
var sp = cast[uint64](stack_base) + cast[uint64](size)
|
||||||
|
|
|
||||||
311
core/fs/sfs.nim
311
core/fs/sfs.nim
|
|
@ -6,334 +6,135 @@
|
||||||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
## Rumpk Layer 1: Sovereign File System (SFS)
|
## Rumpk Layer 1: Sovereign File System (SFS)
|
||||||
|
##
|
||||||
|
## Freestanding implementation (No OS module dependencies).
|
||||||
|
## Uses fixed-size buffers and raw blocks for persistence.
|
||||||
|
|
||||||
# Markus Maiwald (Architect) | Voxis Forge (AI)
|
import ../ring, ../fiber # For yield
|
||||||
#
|
|
||||||
# Rumpk Phase 23: The Sovereign Filesystem (SFS) v2
|
|
||||||
# Features: Multi-Sector Files (Linked List), Block Alloc Map (BAM)
|
|
||||||
#
|
|
||||||
# DOCTRINE(SPEC-021):
|
|
||||||
# This file currently implements the "Physics-Logic Hybrid" for Bootstrapping.
|
|
||||||
# In Phase 37, this will be deprecated in favor of:
|
|
||||||
# - L0: LittleFS (Atomic Physics)
|
|
||||||
# - L1: SFS Overlay Daemon (Sovereign Logic in Userland)
|
|
||||||
|
|
||||||
proc kprintln(s: cstring) {.importc, cdecl.}
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
proc kprint(s: cstring) {.importc, cdecl.}
|
proc kprint(s: cstring) {.importc, cdecl.}
|
||||||
proc kprint_hex(n: uint64) {.importc, cdecl.}
|
proc kprint_hex(n: uint64) {.importc, cdecl.}
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# SFS Configurations
|
|
||||||
# =========================================================
|
|
||||||
|
|
||||||
const SFS_MAGIC* = 0x31534653'u32
|
const SFS_MAGIC* = 0x31534653'u32
|
||||||
const
|
const
|
||||||
SEC_SB = 0
|
SEC_SB = 0
|
||||||
SEC_BAM = 1
|
SEC_BAM = 1
|
||||||
SEC_DIR = 2
|
SEC_DIR = 2
|
||||||
# Linked List Payload: 508 bytes data + 4 bytes next_sector
|
|
||||||
CHUNK_SIZE = 508
|
CHUNK_SIZE = 508
|
||||||
EOF_MARKER = 0xFFFFFFFF'u32
|
EOF_MARKER = 0xFFFFFFFF'u32
|
||||||
|
|
||||||
type
|
|
||||||
Superblock* = object
|
|
||||||
magic*: uint32
|
|
||||||
disk_size*: uint32
|
|
||||||
|
|
||||||
DirEntry* = object
|
|
||||||
filename*: array[32, char]
|
|
||||||
start_sector*: uint32
|
|
||||||
size_bytes*: uint32
|
|
||||||
reserved*: array[24, byte]
|
|
||||||
|
|
||||||
var sfs_mounted: bool = false
|
var sfs_mounted: bool = false
|
||||||
var io_buffer: array[512, byte]
|
var io_buffer: array[512, byte]
|
||||||
|
|
||||||
proc virtio_blk_read(sector: uint64, buf: pointer) {.importc, cdecl.}
|
proc virtio_blk_read(sector: uint64, buf: pointer) {.importc, cdecl.}
|
||||||
proc virtio_blk_write(sector: uint64, buf: pointer) {.importc, cdecl.}
|
proc virtio_blk_write(sector: uint64, buf: pointer) {.importc, cdecl.}
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Helpers
|
|
||||||
# =========================================================
|
|
||||||
|
|
||||||
# Removed sfs_set_bam (unused)
|
|
||||||
|
|
||||||
proc sfs_alloc_sector(): uint32 =
|
proc sfs_alloc_sector(): uint32 =
|
||||||
# Simple allocator: Scan BAM for first 0 bit
|
|
||||||
virtio_blk_read(SEC_BAM, addr io_buffer[0])
|
virtio_blk_read(SEC_BAM, addr io_buffer[0])
|
||||||
|
|
||||||
for i in 0..<512:
|
for i in 0..<512:
|
||||||
if io_buffer[i] != 0xFF:
|
if io_buffer[i] != 0xFF:
|
||||||
# Found a byte with free space
|
|
||||||
for b in 0..7:
|
for b in 0..7:
|
||||||
if (io_buffer[i] and byte(1 shl b)) == 0:
|
if (io_buffer[i] and byte(1 shl b)) == 0:
|
||||||
# Found free bit
|
|
||||||
let sec = uint32(i * 8 + b)
|
let sec = uint32(i * 8 + b)
|
||||||
# Mark applied in sfs_set_bam but for efficiency do it here/flush
|
|
||||||
io_buffer[i] = io_buffer[i] or byte(1 shl b)
|
io_buffer[i] = io_buffer[i] or byte(1 shl b)
|
||||||
virtio_blk_write(SEC_BAM, addr io_buffer[0])
|
virtio_blk_write(SEC_BAM, addr io_buffer[0])
|
||||||
return sec
|
return sec
|
||||||
return 0 # Error / Full
|
return 0
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# SFS API
|
|
||||||
# =========================================================
|
|
||||||
|
|
||||||
proc sfs_is_mounted*(): bool = sfs_mounted
|
|
||||||
|
|
||||||
proc sfs_format*() =
|
|
||||||
kprintln("[SFS] Formatting disk...")
|
|
||||||
# 1. Clear IO Buffer
|
|
||||||
for i in 0..511: io_buffer[i] = 0
|
|
||||||
|
|
||||||
# 2. Setup Superblock
|
|
||||||
io_buffer[0] = byte('S')
|
|
||||||
io_buffer[1] = byte('F')
|
|
||||||
io_buffer[2] = byte('S')
|
|
||||||
io_buffer[3] = byte('2')
|
|
||||||
# Disk size placeholder (32MB = 65536 sectors)
|
|
||||||
io_buffer[4] = 0x00; io_buffer[5] = 0x00; io_buffer[6] = 0x01; io_buffer[7] = 0x00
|
|
||||||
virtio_blk_write(SEC_SB, addr io_buffer[0])
|
|
||||||
|
|
||||||
# 3. Clear BAM
|
|
||||||
for i in 0..511: io_buffer[i] = 0
|
|
||||||
# Mark sectors 0, 1, 2 as used
|
|
||||||
io_buffer[0] = 0x07
|
|
||||||
virtio_blk_write(SEC_BAM, addr io_buffer[0])
|
|
||||||
|
|
||||||
# 4. Clear Directory
|
|
||||||
for i in 0..511: io_buffer[i] = 0
|
|
||||||
virtio_blk_write(SEC_DIR, addr io_buffer[0])
|
|
||||||
kprintln("[SFS] Format Complete.")
|
|
||||||
|
|
||||||
proc sfs_mount*() =
|
proc sfs_mount*() =
|
||||||
kprintln("[SFS] Mounting System v2...")
|
|
||||||
|
|
||||||
# 1. Read Sector 0 (Superblock)
|
|
||||||
virtio_blk_read(SEC_SB, addr io_buffer[0])
|
virtio_blk_read(SEC_SB, addr io_buffer[0])
|
||||||
|
|
||||||
# 2. Check Magic (SFS2)
|
|
||||||
if io_buffer[0] == byte('S') and io_buffer[1] == byte('F') and
|
if io_buffer[0] == byte('S') and io_buffer[1] == byte('F') and
|
||||||
io_buffer[2] == byte('S') and io_buffer[3] == byte('2'):
|
io_buffer[2] == byte('S') and io_buffer[3] == byte('2'):
|
||||||
kprintln("[SFS] Mount SUCCESS. Version 2 (Linked Chain).")
|
|
||||||
sfs_mounted = true
|
|
||||||
elif io_buffer[0] == 0 and io_buffer[1] == 0:
|
|
||||||
kprintln("[SFS] Fresh disk detected.")
|
|
||||||
sfs_format()
|
|
||||||
sfs_mounted = true
|
sfs_mounted = true
|
||||||
else:
|
else:
|
||||||
kprint("[SFS] Mount FAILED. Invalid Magic/Ver. Found: ")
|
sfs_mounted = false
|
||||||
kprint_hex(cast[uint64](io_buffer[0]))
|
|
||||||
kprintln("")
|
|
||||||
|
|
||||||
proc sfs_list*() =
|
proc sfs_streq(s1, s2: cstring): bool =
|
||||||
|
let p1 = cast[ptr UncheckedArray[char]](s1)
|
||||||
|
let p2 = cast[ptr UncheckedArray[char]](s2)
|
||||||
|
var i = 0
|
||||||
|
while true:
|
||||||
|
if p1[i] != p2[i]: return false
|
||||||
|
if p1[i] == '\0': return true
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
proc sfs_write_file*(name: cstring, data: pointer, data_len: int) {.exportc, cdecl.} =
|
||||||
if not sfs_mounted: return
|
if not sfs_mounted: return
|
||||||
|
|
||||||
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
||||||
kprintln("[SFS] Files:")
|
|
||||||
|
|
||||||
var offset = 0
|
|
||||||
while offset < 512:
|
|
||||||
if io_buffer[offset] != 0:
|
|
||||||
var name: string = ""
|
|
||||||
for i in 0..31:
|
|
||||||
let c = char(io_buffer[offset+i])
|
|
||||||
if c == '\0': break
|
|
||||||
name.add(c)
|
|
||||||
kprint(" - ")
|
|
||||||
kprintln(cstring(name))
|
|
||||||
offset += 64
|
|
||||||
|
|
||||||
proc sfs_get_files*(): string =
|
|
||||||
var res = ""
|
|
||||||
if not sfs_mounted: return res
|
|
||||||
|
|
||||||
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
|
||||||
for offset in countup(0, 511, 64):
|
|
||||||
if io_buffer[offset] != 0:
|
|
||||||
var name = ""
|
|
||||||
for i in 0..31:
|
|
||||||
let c = char(io_buffer[offset+i])
|
|
||||||
if c == '\0': break
|
|
||||||
name.add(c)
|
|
||||||
res.add(name)
|
|
||||||
res.add("\n")
|
|
||||||
return res
|
|
||||||
|
|
||||||
proc sfs_write_file*(name: cstring, data: cstring, data_len: int) {.exportc, cdecl.} =
|
|
||||||
if not sfs_mounted: return
|
|
||||||
|
|
||||||
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
|
||||||
|
|
||||||
var dir_offset = -1
|
var dir_offset = -1
|
||||||
var file_exists = false
|
|
||||||
|
|
||||||
# 1. Find File or Free Slot
|
|
||||||
for offset in countup(0, 511, 64):
|
for offset in countup(0, 511, 64):
|
||||||
if io_buffer[offset] != 0:
|
if io_buffer[offset] != 0:
|
||||||
var entry_name = ""
|
if sfs_streq(name, cast[cstring](addr io_buffer[offset])):
|
||||||
for i in 0..31:
|
|
||||||
if io_buffer[offset+i] == 0: break
|
|
||||||
entry_name.add(char(io_buffer[offset+i]))
|
|
||||||
|
|
||||||
if entry_name == $name:
|
|
||||||
dir_offset = offset
|
dir_offset = offset
|
||||||
file_exists = true
|
|
||||||
# For existing files, efficient rewrite logic is complex (reuse chain vs new).
|
|
||||||
# V2 Simplification: Just create NEW chain, orphan old one (leak) for now.
|
|
||||||
# Future: Walk old chain and free in BAM.
|
|
||||||
break
|
break
|
||||||
elif dir_offset == -1:
|
elif dir_offset == -1: dir_offset = offset
|
||||||
dir_offset = offset
|
if dir_offset == -1: return
|
||||||
|
|
||||||
if dir_offset == -1:
|
|
||||||
kprintln("[SFS] Error: Directory Full.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 2. Chunk and Write Data
|
|
||||||
var remaining = data_len
|
var remaining = data_len
|
||||||
var data_ptr = 0
|
var data_addr = cast[uint64](data)
|
||||||
var first_sector = 0'u32
|
var current_sector = sfs_alloc_sector()
|
||||||
var current_sector = 0'u32
|
if current_sector == 0: return
|
||||||
|
let first_sector = current_sector
|
||||||
# For the first chunk
|
|
||||||
current_sector = sfs_alloc_sector()
|
|
||||||
if current_sector == 0:
|
|
||||||
kprintln("[SFS] Error: Disk Full.")
|
|
||||||
return
|
|
||||||
first_sector = current_sector
|
|
||||||
|
|
||||||
while remaining > 0:
|
while remaining > 0:
|
||||||
var sector_buf: array[512, byte]
|
var sector_buf: array[512, byte]
|
||||||
|
let chunk = if remaining > CHUNK_SIZE: CHUNK_SIZE else: remaining
|
||||||
|
copyMem(addr sector_buf[0], cast[pointer](data_addr), chunk)
|
||||||
|
remaining -= chunk
|
||||||
|
data_addr += uint64(chunk)
|
||||||
|
|
||||||
# Fill Data
|
|
||||||
let chunk_size = if remaining > CHUNK_SIZE: CHUNK_SIZE else: remaining
|
|
||||||
for i in 0..<chunk_size:
|
|
||||||
sector_buf[i] = byte(data[data_ptr + i])
|
|
||||||
|
|
||||||
remaining -= chunk_size
|
|
||||||
data_ptr += chunk_size
|
|
||||||
|
|
||||||
# Determine Next Sector
|
|
||||||
var next_sector = EOF_MARKER
|
var next_sector = EOF_MARKER
|
||||||
if remaining > 0:
|
if remaining > 0:
|
||||||
next_sector = sfs_alloc_sector()
|
next_sector = sfs_alloc_sector()
|
||||||
if next_sector == 0:
|
if next_sector == 0: next_sector = EOF_MARKER
|
||||||
next_sector = EOF_MARKER # Disk full, truncated
|
|
||||||
remaining = 0
|
|
||||||
|
|
||||||
# Write Next Pointer
|
# Write next pointer at end of block
|
||||||
sector_buf[508] = byte(next_sector and 0xFF)
|
cast[ptr uint32](addr sector_buf[508])[] = next_sector
|
||||||
sector_buf[509] = byte((next_sector shr 8) and 0xFF)
|
|
||||||
sector_buf[510] = byte((next_sector shr 16) and 0xFF)
|
|
||||||
sector_buf[511] = byte((next_sector shr 24) and 0xFF)
|
|
||||||
|
|
||||||
# Flush Sector
|
|
||||||
virtio_blk_write(uint64(current_sector), addr sector_buf[0])
|
virtio_blk_write(uint64(current_sector), addr sector_buf[0])
|
||||||
|
|
||||||
current_sector = next_sector
|
current_sector = next_sector
|
||||||
if current_sector == EOF_MARKER: break
|
if current_sector == EOF_MARKER: break
|
||||||
|
|
||||||
# 3. Update Directory Entry
|
# Update Directory
|
||||||
# Need to read Dir again as buffer was used for BAM/Data
|
|
||||||
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
||||||
|
let nm = cast[ptr UncheckedArray[char]](name)
|
||||||
let n_str = $name
|
var i = 0
|
||||||
for i in 0..31:
|
while nm[i] != '\0' and i < 31:
|
||||||
if i < n_str.len: io_buffer[dir_offset+i] = byte(n_str[i])
|
io_buffer[dir_offset + i] = byte(nm[i])
|
||||||
else: io_buffer[dir_offset+i] = 0
|
i += 1
|
||||||
|
io_buffer[dir_offset + i] = 0
|
||||||
io_buffer[dir_offset+32] = byte(first_sector and 0xFF)
|
cast[ptr uint32](addr io_buffer[dir_offset + 32])[] = first_sector
|
||||||
io_buffer[dir_offset+33] = byte((first_sector shr 8) and 0xFF)
|
cast[ptr uint32](addr io_buffer[dir_offset + 36])[] = uint32(data_len)
|
||||||
io_buffer[dir_offset+34] = byte((first_sector shr 16) and 0xFF)
|
|
||||||
io_buffer[dir_offset+35] = byte((first_sector shr 24) and 0xFF)
|
|
||||||
|
|
||||||
let sz = uint32(data_len)
|
|
||||||
io_buffer[dir_offset+36] = byte(sz and 0xFF)
|
|
||||||
io_buffer[dir_offset+37] = byte((sz shr 8) and 0xFF)
|
|
||||||
io_buffer[dir_offset+38] = byte((sz shr 16) and 0xFF)
|
|
||||||
io_buffer[dir_offset+39] = byte((sz shr 24) and 0xFF)
|
|
||||||
|
|
||||||
virtio_blk_write(SEC_DIR, addr io_buffer[0])
|
virtio_blk_write(SEC_DIR, addr io_buffer[0])
|
||||||
kprintln("[SFS] Multi-Sector Write Complete.")
|
|
||||||
|
|
||||||
proc sfs_read_file*(name: cstring, dest: pointer, max_len: int): int {.exportc, cdecl.} =
|
proc sfs_read_file*(name: cstring, dest: pointer, max_len: int): int {.exportc, cdecl.} =
|
||||||
if not sfs_mounted: return -1
|
if not sfs_mounted: return -1
|
||||||
|
|
||||||
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
||||||
|
|
||||||
var start_sector = 0'u32
|
var start_sector = 0'u32
|
||||||
var file_size = 0'u32
|
var file_size = 0'u32
|
||||||
var found = false
|
var found = false
|
||||||
|
|
||||||
for offset in countup(0, 511, 64):
|
for offset in countup(0, 511, 64):
|
||||||
if io_buffer[offset] != 0:
|
if io_buffer[offset] != 0:
|
||||||
var entry_name = ""
|
if sfs_streq(name, cast[cstring](addr io_buffer[offset])):
|
||||||
for i in 0..31:
|
start_sector = cast[ptr uint32](addr io_buffer[offset + 32])[]
|
||||||
if io_buffer[offset+i] == 0: break
|
file_size = cast[ptr uint32](addr io_buffer[offset + 36])[]
|
||||||
entry_name.add(char(io_buffer[offset+i]))
|
|
||||||
|
|
||||||
if entry_name == $name:
|
|
||||||
start_sector = uint32(io_buffer[offset+32]) or
|
|
||||||
(uint32(io_buffer[offset+33]) shl 8) or
|
|
||||||
(uint32(io_buffer[offset+34]) shl 16) or
|
|
||||||
(uint32(io_buffer[offset+35]) shl 24)
|
|
||||||
file_size = uint32(io_buffer[offset+36]) or
|
|
||||||
(uint32(io_buffer[offset+37]) shl 8) or
|
|
||||||
(uint32(io_buffer[offset+38]) shl 16) or
|
|
||||||
(uint32(io_buffer[offset+39]) shl 24)
|
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
|
|
||||||
if not found: return -1
|
if not found: return -1
|
||||||
|
|
||||||
# Read Chain
|
|
||||||
var current_sector = start_sector
|
var current_sector = start_sector
|
||||||
var dest_addr = cast[int](dest)
|
var dest_addr = cast[uint64](dest)
|
||||||
var remaining = int(file_size)
|
var remaining = if int(file_size) < max_len: int(file_size) else: max_len
|
||||||
if remaining > max_len: remaining = max_len
|
var total = 0
|
||||||
|
while remaining > 0 and current_sector != EOF_MARKER:
|
||||||
|
var sector_buf: array[512, byte]
|
||||||
|
virtio_blk_read(uint64(current_sector), addr sector_buf[0])
|
||||||
|
let chunk = if remaining < CHUNK_SIZE: remaining else: CHUNK_SIZE
|
||||||
|
copyMem(cast[pointer](dest_addr), addr sector_buf[0], chunk)
|
||||||
|
dest_addr += uint64(chunk)
|
||||||
|
remaining -= chunk
|
||||||
|
total += chunk
|
||||||
|
current_sector = cast[ptr uint32](addr sector_buf[508])[]
|
||||||
|
return total
|
||||||
|
|
||||||
var total_read = 0
|
proc sfs_get_files*(): cstring = return "boot.kdl\n" # Dummy
|
||||||
|
|
||||||
while remaining > 0 and current_sector != EOF_MARKER and current_sector != 0:
|
|
||||||
var sector_buf: array[512, byte]
|
|
||||||
virtio_blk_read(uint64(current_sector), addr sector_buf[0])
|
|
||||||
|
|
||||||
# Extract Payload
|
|
||||||
let payload_size = min(remaining, CHUNK_SIZE)
|
|
||||||
# Be careful not to overflow dest buffer if payload_size > remaining (handled by min)
|
|
||||||
|
|
||||||
copyMem(cast[pointer](dest_addr), addr sector_buf[0], payload_size)
|
|
||||||
|
|
||||||
dest_addr += payload_size
|
|
||||||
remaining -= payload_size
|
|
||||||
total_read += payload_size
|
|
||||||
|
|
||||||
# Next Sector
|
|
||||||
current_sector = uint32(sector_buf[508]) or
|
|
||||||
(uint32(sector_buf[509]) shl 8) or
|
|
||||||
(uint32(sector_buf[510]) shl 16) or
|
|
||||||
(uint32(sector_buf[511]) shl 24)
|
|
||||||
|
|
||||||
return total_read
|
|
||||||
|
|
||||||
proc vfs_register_sfs(name: string, size: uint64) {.importc, cdecl.}
|
|
||||||
|
|
||||||
proc sfs_sync_vfs*() =
|
|
||||||
if not sfs_mounted: return
|
|
||||||
|
|
||||||
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
|
||||||
for offset in countup(0, 511, 64):
|
|
||||||
if io_buffer[offset] != 0:
|
|
||||||
var name = ""
|
|
||||||
for i in 0..31:
|
|
||||||
let c = char(io_buffer[offset+i])
|
|
||||||
if c == '\0': break
|
|
||||||
name.add(c)
|
|
||||||
|
|
||||||
let f_size = uint32(io_buffer[offset+36]) or
|
|
||||||
(uint32(io_buffer[offset+37]) shl 8) or
|
|
||||||
(uint32(io_buffer[offset+38]) shl 16) or
|
|
||||||
(uint32(io_buffer[offset+39]) shl 24)
|
|
||||||
|
|
||||||
vfs_register_sfs(name, uint64(f_size))
|
|
||||||
|
|
|
||||||
236
core/fs/tar.nim
236
core/fs/tar.nim
|
|
@ -6,217 +6,101 @@
|
||||||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
## Rumpk Layer 1: ROMFS (Static Tar Loader)
|
## Rumpk Layer 1: ROMFS (Static Tar Loader)
|
||||||
|
##
|
||||||
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
## Freestanding implementation (No OS module dependencies).
|
||||||
# Rumpk L1: Sovereign VFS (Indexing TarFS)
|
## Uses a simple flat array for the file index.
|
||||||
|
|
||||||
{.push stackTrace: off, lineTrace: off.}
|
{.push stackTrace: off, lineTrace: off.}
|
||||||
|
|
||||||
import std/tables
|
|
||||||
|
|
||||||
# Kernel Imports
|
|
||||||
proc kprint(s: cstring) {.importc, cdecl.}
|
|
||||||
proc kprintln(s: cstring) {.importc, cdecl.}
|
|
||||||
proc kprint_hex(n: uint64) {.importc, cdecl.}
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TarHeader* = array[512, byte]
|
TarHeader* = array[512, byte]
|
||||||
|
|
||||||
FileEntry = object
|
FileEntry = object
|
||||||
offset*: uint64
|
name: array[64, char]
|
||||||
size*: uint64
|
offset: uint64
|
||||||
is_sfs*: bool
|
size: uint64
|
||||||
|
active: bool
|
||||||
|
|
||||||
FileHandle = object
|
const MAX_INDEX = 64
|
||||||
path*: string
|
var index_table: array[MAX_INDEX, FileEntry]
|
||||||
offset*: uint64
|
var index_count: int = 0
|
||||||
is_sfs*: bool
|
|
||||||
is_ram*: bool
|
|
||||||
|
|
||||||
VFSInitRD* = object
|
|
||||||
start_addr*: uint64
|
|
||||||
end_addr*: uint64
|
|
||||||
index*: Table[string, FileEntry]
|
|
||||||
ram_data*: Table[string, seq[byte]]
|
|
||||||
fds*: Table[int, FileHandle]
|
|
||||||
next_fd*: int
|
|
||||||
|
|
||||||
var vfs*: VFSInitRD
|
|
||||||
|
|
||||||
proc vfs_init*(s: pointer, e: pointer) =
|
proc vfs_init*(s: pointer, e: pointer) =
|
||||||
vfs.start_addr = cast[uint64](s)
|
let start_addr = cast[uint64](s)
|
||||||
vfs.end_addr = cast[uint64](e)
|
let end_addr = cast[uint64](e)
|
||||||
vfs.index = initTable[string, FileEntry]()
|
index_count = 0
|
||||||
vfs.ram_data = initTable[string, seq[byte]]()
|
|
||||||
vfs.fds = initTable[int, FileHandle]()
|
|
||||||
vfs.next_fd = 3
|
|
||||||
|
|
||||||
# kprint("[VFS] InitRD Start: "); kprint_hex(vfs.start_addr); kprintln("")
|
var p = start_addr
|
||||||
# kprint("[VFS] InitRD End: "); kprint_hex(vfs.end_addr); kprintln("")
|
while p < end_addr:
|
||||||
|
|
||||||
var p = vfs.start_addr
|
|
||||||
while p < vfs.end_addr:
|
|
||||||
let h = cast[ptr TarHeader](p)
|
let h = cast[ptr TarHeader](p)
|
||||||
if h[][0] == byte(0): break
|
if h[][0] == byte(0): break
|
||||||
|
|
||||||
# kprint("[VFS] Raw Header: ")
|
# Extract name
|
||||||
# for i in 0..15:
|
|
||||||
# kprint_hex(uint64(h[][i]))
|
|
||||||
# kprint(" ")
|
|
||||||
# kprintln("")
|
|
||||||
|
|
||||||
# Extract and normalize name directly from header
|
|
||||||
var name_len = 0
|
var name_len = 0
|
||||||
while name_len < 100 and h[][name_len] != 0:
|
while name_len < 100 and h[][name_len] != 0: inc name_len
|
||||||
inc name_len
|
|
||||||
|
|
||||||
var start_idx = 0
|
var start_idx = 0
|
||||||
if name_len >= 2 and h[][0] == byte('.') and h[][1] == byte('/'):
|
if name_len >= 2 and h[][0] == byte('.') and h[][1] == byte('/'): start_idx = 2
|
||||||
start_idx = 2
|
elif name_len >= 1 and h[][0] == byte('/'): start_idx = 1
|
||||||
elif name_len >= 1 and h[][0] == byte('/'):
|
|
||||||
start_idx = 1
|
|
||||||
|
|
||||||
let clean_len = name_len - start_idx
|
let clean_len = name_len - start_idx
|
||||||
var clean = ""
|
if clean_len > 0 and index_count < MAX_INDEX:
|
||||||
if clean_len > 0:
|
var entry = addr index_table[index_count]
|
||||||
clean = newString(clean_len)
|
entry.active = true
|
||||||
# Copy directly from header memory
|
let to_copy = if clean_len < 63: clean_len else: 63
|
||||||
for i in 0..<clean_len:
|
for i in 0..<to_copy:
|
||||||
clean[i] = char(h[][start_idx + i])
|
entry.name[i] = char(h[][start_idx + i])
|
||||||
|
entry.name[to_copy] = '\0'
|
||||||
|
|
||||||
if clean.len > 0:
|
# Extract size (octal string at offset 124)
|
||||||
# Extract size (octal string)
|
|
||||||
var size: uint64 = 0
|
var size: uint64 = 0
|
||||||
for i in 124..134:
|
for i in 124..134:
|
||||||
let b = h[][i]
|
let b = h[][i]
|
||||||
if b >= byte('0') and b <= byte('7'):
|
if b >= byte('0') and b <= byte('7'):
|
||||||
size = (size shl 3) or uint64(b - byte('0'))
|
size = (size shl 3) or uint64(b - byte('0'))
|
||||||
|
entry.size = size
|
||||||
vfs.index[clean] = FileEntry(offset: p + 512'u64, size: size, is_sfs: false)
|
entry.offset = p + 512
|
||||||
|
index_count += 1
|
||||||
|
|
||||||
# Move to next header
|
# Move to next header
|
||||||
let padded_size = (size + 511'u64) and not 511'u64
|
let padded_size = (size + 511) and not 511'u64
|
||||||
p += 512'u64 + padded_size
|
p += 512 + padded_size
|
||||||
else:
|
else:
|
||||||
p += 512'u64 # Skip invalid/empty
|
p += 512
|
||||||
|
|
||||||
proc vfs_open*(path: string, flags: int32 = 0): int =
|
proc vfs_streq(s1, s2: cstring): bool =
|
||||||
var start_idx = 0
|
let p1 = cast[ptr UncheckedArray[char]](s1)
|
||||||
if path.len > 0 and path[0] == '/':
|
let p2 = cast[ptr UncheckedArray[char]](s2)
|
||||||
start_idx = 1
|
var i = 0
|
||||||
|
while true:
|
||||||
|
if p1[i] != p2[i]: return false
|
||||||
|
if p1[i] == '\0': return true
|
||||||
|
i += 1
|
||||||
|
|
||||||
let clean_len = path.len - start_idx
|
proc vfs_open*(path: cstring, flags: int32 = 0): int32 =
|
||||||
var clean = ""
|
var p = path
|
||||||
if clean_len > 0:
|
if path != nil and path[0] == '/':
|
||||||
clean = newString(clean_len)
|
p = cast[cstring](cast[uint64](path) + 1)
|
||||||
for i in 0..<clean_len:
|
|
||||||
clean[i] = path[start_idx + i]
|
|
||||||
|
|
||||||
# 1. Check RamFS
|
|
||||||
if vfs.ram_data.hasKey(clean):
|
|
||||||
let fd = vfs.next_fd
|
|
||||||
vfs.fds[fd] = FileHandle(path: clean, offset: 0, is_sfs: false, is_ram: true)
|
|
||||||
vfs.next_fd += 1
|
|
||||||
return fd
|
|
||||||
|
|
||||||
# 2. Check TarFS
|
|
||||||
if vfs.index.hasKey(clean):
|
|
||||||
let entry = vfs.index[clean]
|
|
||||||
let fd = vfs.next_fd
|
|
||||||
vfs.fds[fd] = FileHandle(path: clean, offset: 0, is_sfs: entry.is_sfs,
|
|
||||||
is_ram: false)
|
|
||||||
vfs.next_fd += 1
|
|
||||||
return fd
|
|
||||||
|
|
||||||
# 3. Create if O_CREAT (bit 6 in POSIX)
|
|
||||||
if (flags and 64) != 0:
|
|
||||||
vfs.ram_data[clean] = @[]
|
|
||||||
let fd = vfs.next_fd
|
|
||||||
vfs.fds[fd] = FileHandle(path: clean, offset: 0, is_sfs: false, is_ram: true)
|
|
||||||
vfs.next_fd += 1
|
|
||||||
return fd
|
|
||||||
|
|
||||||
|
for i in 0..<index_count:
|
||||||
|
if vfs_streq(p, cast[cstring](addr index_table[i].name[0])):
|
||||||
|
return int32(i)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc vfs_read_file*(path: string): string =
|
proc vfs_read_at*(path: cstring, buf: pointer, count: uint64, offset: uint64): int64 =
|
||||||
var start_idx = 0
|
let fd = vfs_open(path)
|
||||||
if path.len > 0 and path[0] == '/':
|
if fd < 0: return -1
|
||||||
start_idx = 1
|
let entry = addr index_table[fd]
|
||||||
|
|
||||||
let clean_len = path.len - start_idx
|
|
||||||
var clean = ""
|
|
||||||
if clean_len > 0:
|
|
||||||
clean = newString(clean_len)
|
|
||||||
for i in 0..<clean_len:
|
|
||||||
clean[i] = path[start_idx + i]
|
|
||||||
|
|
||||||
if vfs.ram_data.hasKey(clean):
|
|
||||||
let data = vfs.ram_data[clean]
|
|
||||||
if data.len == 0: return ""
|
|
||||||
var s = newString(data.len)
|
|
||||||
copyMem(addr s[0], unsafeAddr data[0], data.len)
|
|
||||||
return s
|
|
||||||
|
|
||||||
if vfs.index.hasKey(clean):
|
|
||||||
let entry = vfs.index[clean]
|
|
||||||
if entry.is_sfs: return ""
|
|
||||||
var s = newString(int(entry.size))
|
|
||||||
if entry.size > 0:
|
|
||||||
copyMem(addr s[0], cast[pointer](entry.offset), int(entry.size))
|
|
||||||
return s
|
|
||||||
return ""
|
|
||||||
|
|
||||||
proc vfs_read_at*(path: string, buf: pointer, count: uint64, offset: uint64): int64 =
|
|
||||||
if vfs.ram_data.hasKey(path):
|
|
||||||
let data = addr vfs.ram_data[path]
|
|
||||||
if offset >= uint64(data[].len): return 0
|
|
||||||
let available = uint64(data[].len) - offset
|
|
||||||
let actual = min(count, available)
|
|
||||||
if actual > 0:
|
|
||||||
copyMem(buf, addr data[][int(offset)], int(actual))
|
|
||||||
return int64(actual)
|
|
||||||
|
|
||||||
if not vfs.index.hasKey(path): return -1
|
|
||||||
let entry = vfs.index[path]
|
|
||||||
if entry.is_sfs: return -1 # Routed via SFS
|
|
||||||
|
|
||||||
var actual = uint64(count)
|
|
||||||
if offset >= entry.size: return 0
|
if offset >= entry.size: return 0
|
||||||
if offset + count > entry.size:
|
let avail = entry.size - offset
|
||||||
actual = entry.size - offset
|
let actual = if count < avail: count else: avail
|
||||||
|
if actual > 0:
|
||||||
copyMem(buf, cast[pointer](entry.offset + offset), int(actual))
|
copyMem(buf, cast[pointer](entry.offset + offset), int(actual))
|
||||||
return int64(actual)
|
return int64(actual)
|
||||||
|
|
||||||
proc vfs_write_at*(path: string, buf: pointer, count: uint64, offset: uint64): int64 =
|
proc vfs_write_at*(path: cstring, buf: pointer, count: uint64, offset: uint64): int64 =
|
||||||
# Promote to RamFS if on TarFS (CoW)
|
# ROMFS is read-only
|
||||||
if not vfs.ram_data.hasKey(path):
|
return -1
|
||||||
if vfs.index.hasKey(path):
|
|
||||||
let entry = vfs.index[path]
|
|
||||||
var content = newSeq[byte](int(entry.size))
|
|
||||||
if entry.size > 0:
|
|
||||||
copyMem(addr content[0], cast[pointer](entry.offset), int(entry.size))
|
|
||||||
vfs.ram_data[path] = content
|
|
||||||
else:
|
|
||||||
vfs.ram_data[path] = @[]
|
|
||||||
|
|
||||||
let data = addr vfs.ram_data[path]
|
proc vfs_get_names*(): int = index_count # Dummy for listing
|
||||||
let min_size = int(offset + count)
|
|
||||||
if data[].len < min_size:
|
|
||||||
data[].setLen(min_size)
|
|
||||||
|
|
||||||
copyMem(addr data[][int(offset)], buf, int(count))
|
|
||||||
return int64(count)
|
|
||||||
# Removed ion_vfs_* in favor of vfs.nim dispatcher
|
|
||||||
|
|
||||||
proc vfs_get_names*(): seq[string] =
|
|
||||||
var names = initTable[string, bool]()
|
|
||||||
for name, _ in vfs.index: names[name] = true
|
|
||||||
for name, _ in vfs.ram_data: names[name] = true
|
|
||||||
result = @[]
|
|
||||||
for name, _ in names: result.add(name)
|
|
||||||
|
|
||||||
proc vfs_register_sfs*(name: string, size: uint64) {.exportc, cdecl.} =
|
|
||||||
vfs.index[name] = FileEntry(offset: 0, size: size, is_sfs: true)
|
|
||||||
|
|
||||||
{.pop.}
|
|
||||||
|
|
|
||||||
182
core/fs/vfs.nim
182
core/fs/vfs.nim
|
|
@ -6,122 +6,160 @@
|
||||||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
## Rumpk Layer 1: Sovereign VFS (The Loom)
|
## Rumpk Layer 1: Sovereign VFS (The Loom)
|
||||||
|
##
|
||||||
|
## Freestanding implementation (No OS module dependencies).
|
||||||
|
## Uses fixed-size arrays for descriptors to ensure deterministic latency.
|
||||||
|
|
||||||
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
|
||||||
# VFS dispatcher for SPEC-130 alignment.
|
|
||||||
|
|
||||||
import strutils, tables
|
|
||||||
import tar, sfs
|
import tar, sfs
|
||||||
|
|
||||||
type
|
type
|
||||||
VFSMode = enum
|
VFSMode = enum
|
||||||
MODE_TAR, MODE_SFS, MODE_RAM
|
MODE_TAR, MODE_SFS, MODE_RAM, MODE_TTY
|
||||||
|
|
||||||
MountPoint = object
|
MountPoint = object
|
||||||
prefix: string
|
prefix: array[32, char]
|
||||||
mode: VFSMode
|
mode: VFSMode
|
||||||
|
|
||||||
var mounts: seq[MountPoint] = @[]
|
|
||||||
|
|
||||||
type
|
|
||||||
FileHandle = object
|
FileHandle = object
|
||||||
path: string
|
path: array[64, char]
|
||||||
offset: uint64
|
offset: uint64
|
||||||
mode: VFSMode
|
mode: VFSMode
|
||||||
is_ram: bool
|
active: bool
|
||||||
|
|
||||||
var fds = initTable[int, FileHandle]()
|
const MAX_MOUNTS = 8
|
||||||
var next_fd = 3
|
const MAX_FDS = 32
|
||||||
|
|
||||||
|
var mnt_table: array[MAX_MOUNTS, MountPoint]
|
||||||
|
var mnt_count: int = 0
|
||||||
|
|
||||||
|
var fd_table: array[MAX_FDS, FileHandle]
|
||||||
|
|
||||||
|
# Helper: manual string compare
|
||||||
|
proc vfs_starts_with(s, prefix: cstring): bool =
|
||||||
|
let ps = cast[ptr UncheckedArray[char]](s)
|
||||||
|
let pp = cast[ptr UncheckedArray[char]](prefix)
|
||||||
|
var i = 0
|
||||||
|
while pp[i] != '\0':
|
||||||
|
if ps[i] != pp[i]: return false
|
||||||
|
i += 1
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc vfs_streq(s1, s2: cstring): bool =
|
||||||
|
let p1 = cast[ptr UncheckedArray[char]](s1)
|
||||||
|
let p2 = cast[ptr UncheckedArray[char]](s2)
|
||||||
|
var i = 0
|
||||||
|
while true:
|
||||||
|
if p1[i] != p2[i]: return false
|
||||||
|
if p1[i] == '\0': return true
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
proc vfs_add_mount(prefix: cstring, mode: VFSMode) =
|
||||||
|
if mnt_count >= MAX_MOUNTS: return
|
||||||
|
let p = cast[ptr UncheckedArray[char]](prefix)
|
||||||
|
var i = 0
|
||||||
|
while p[i] != '\0' and i < 31:
|
||||||
|
mnt_table[mnt_count].prefix[i] = p[i]
|
||||||
|
i += 1
|
||||||
|
mnt_table[mnt_count].prefix[i] = '\0'
|
||||||
|
mnt_table[mnt_count].mode = mode
|
||||||
|
mnt_count += 1
|
||||||
|
|
||||||
proc vfs_mount_init*() =
|
proc vfs_mount_init*() =
|
||||||
# SPEC-130: The Three-Domain Root
|
# Restore the SPEC-502 baseline
|
||||||
# SPEC-021: The Sovereign Overlay Strategy
|
vfs_add_mount("/nexus", MODE_SFS)
|
||||||
mounts.add(MountPoint(prefix: "/nexus", mode: MODE_SFS)) # The Sovereign State (Persistent)
|
vfs_add_mount("/sysro", MODE_TAR)
|
||||||
mounts.add(MountPoint(prefix: "/sysro", mode: MODE_TAR)) # The Projected Reality (Immutable InitRD)
|
vfs_add_mount("/state", MODE_RAM)
|
||||||
mounts.add(MountPoint(prefix: "/state", mode: MODE_RAM)) # The Mutable Dust (Transient)
|
vfs_add_mount("/dev/tty", MODE_TTY)
|
||||||
|
vfs_add_mount("/Bus/Console/tty0", MODE_TTY)
|
||||||
|
|
||||||
proc resolve_path(path: string): (VFSMode, string) =
|
proc resolve_path(path: cstring): (VFSMode, int) =
|
||||||
for m in mounts:
|
for i in 0..<mnt_count:
|
||||||
if path.startsWith(m.prefix):
|
let prefix = cast[cstring](addr mnt_table[i].prefix[0])
|
||||||
let sub = if path.len > m.prefix.len: path[m.prefix.len..^1] else: "/"
|
if vfs_starts_with(path, prefix):
|
||||||
return (m.mode, sub)
|
var len = 0
|
||||||
return (MODE_TAR, path)
|
while mnt_table[i].prefix[len] != '\0': len += 1
|
||||||
|
return (mnt_table[i].mode, len)
|
||||||
|
return (MODE_TAR, 0)
|
||||||
|
|
||||||
# Syscall implementation procs
|
|
||||||
# Kernel Imports
|
|
||||||
# (Currently unused, relying on kprintln from kernel)
|
|
||||||
proc ion_vfs_open*(path: cstring, flags: int32): int32 {.exportc, cdecl.} =
|
proc ion_vfs_open*(path: cstring, flags: int32): int32 {.exportc, cdecl.} =
|
||||||
let p = $path
|
let (mode, prefix_len) = resolve_path(path)
|
||||||
let (mode, sub) = resolve_path(p)
|
|
||||||
|
# Delegate internal open
|
||||||
|
let sub_path = cast[cstring](cast[uint64](path) + uint64(prefix_len))
|
||||||
|
var internal_fd: int32 = -1
|
||||||
|
|
||||||
var fd = -1
|
|
||||||
case mode:
|
case mode:
|
||||||
of MODE_TAR: fd = tar.vfs_open(sub, flags)
|
of MODE_TAR, MODE_RAM: internal_fd = tar.vfs_open(sub_path, flags)
|
||||||
of MODE_SFS: fd = 0 # Placeholder for SFS open
|
of MODE_SFS: internal_fd = 0 # Shim
|
||||||
of MODE_RAM: fd = tar.vfs_open(sub, flags) # Using TAR's RamFS for now
|
of MODE_TTY: internal_fd = 1 # Shim
|
||||||
|
|
||||||
if fd > 0:
|
if internal_fd >= 0:
|
||||||
let kernel_fd = next_fd
|
for i in 0..<MAX_FDS:
|
||||||
fds[kernel_fd] = FileHandle(path: sub, offset: 0, mode: mode, is_ram: (mode == MODE_RAM))
|
if not fd_table[i].active:
|
||||||
next_fd += 1
|
fd_table[i].active = true
|
||||||
return int32(kernel_fd)
|
fd_table[i].mode = mode
|
||||||
|
fd_table[i].offset = 0
|
||||||
|
let p = cast[ptr UncheckedArray[char]](sub_path)
|
||||||
|
var j = 0
|
||||||
|
while p[j] != '\0' and j < 63:
|
||||||
|
fd_table[i].path[j] = p[j]
|
||||||
|
j += 1
|
||||||
|
fd_table[i].path[j] = '\0'
|
||||||
|
return int32(i + 3) # FDs start at 3
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc ion_vfs_read*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
proc ion_vfs_read*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
||||||
let ifd = int(fd)
|
let idx = int(fd - 3)
|
||||||
if not fds.hasKey(ifd): return -1
|
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||||
let fh = addr fds[ifd]
|
let fh = addr fd_table[idx]
|
||||||
|
|
||||||
case fh.mode:
|
case fh.mode:
|
||||||
|
of MODE_TTY: return -2
|
||||||
of MODE_TAR, MODE_RAM:
|
of MODE_TAR, MODE_RAM:
|
||||||
let n = tar.vfs_read_at(fh.path, buf, count, fh.offset)
|
let path = cast[cstring](addr fh.path[0])
|
||||||
|
let n = tar.vfs_read_at(path, buf, count, fh.offset)
|
||||||
if n > 0: fh.offset += uint64(n)
|
if n > 0: fh.offset += uint64(n)
|
||||||
return n
|
return n
|
||||||
of MODE_SFS:
|
of MODE_SFS:
|
||||||
# SFS current read-whole-file shim
|
let path = cast[cstring](addr fh.path[0])
|
||||||
var temp_buf: array[4096, byte] # FIXME: Small stack buffer
|
var temp: array[256, byte] # Small shim
|
||||||
let total = sfs.sfs_read_file(cstring(fh.path), addr temp_buf[0], 4096)
|
let n = sfs.sfs_read_file(path, addr temp[0], 256)
|
||||||
if total < 0: return -1
|
if n <= 0: return -1
|
||||||
if fh.offset >= uint64(total): return 0
|
let avail = uint64(n) - fh.offset
|
||||||
let avail = uint64(total) - fh.offset
|
let actual = if count < avail: count else: avail
|
||||||
let actual = min(count, avail)
|
|
||||||
if actual > 0:
|
if actual > 0:
|
||||||
copyMem(buf, addr temp_buf[int(fh.offset)], int(actual))
|
copyMem(buf, addr temp[int(fh.offset)], int(actual))
|
||||||
fh.offset += actual
|
fh.offset += actual
|
||||||
return int64(actual)
|
return int64(actual)
|
||||||
|
return 0
|
||||||
|
|
||||||
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 ifd = int(fd)
|
let idx = int(fd - 3)
|
||||||
if not fds.hasKey(ifd): return -1
|
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||||
let fh = addr fds[ifd]
|
let fh = addr fd_table[idx]
|
||||||
|
|
||||||
case fh.mode:
|
case fh.mode:
|
||||||
|
of MODE_TTY: return -2
|
||||||
of MODE_TAR, MODE_RAM:
|
of MODE_TAR, MODE_RAM:
|
||||||
let n = tar.vfs_write_at(fh.path, buf, count, fh.offset)
|
let path = cast[cstring](addr fh.path[0])
|
||||||
|
let n = tar.vfs_write_at(path, buf, count, fh.offset)
|
||||||
if n > 0: fh.offset += uint64(n)
|
if n > 0: fh.offset += uint64(n)
|
||||||
return n
|
return n
|
||||||
of MODE_SFS:
|
of MODE_SFS:
|
||||||
sfs.sfs_write_file(cstring(fh.path), cast[cstring](buf), int(count))
|
let path = cast[cstring](addr fh.path[0])
|
||||||
|
sfs.sfs_write_file(path, buf, int(count))
|
||||||
return int64(count)
|
return int64(count)
|
||||||
|
|
||||||
proc ion_vfs_close*(fd: int32): int32 {.exportc, cdecl.} =
|
proc ion_vfs_close*(fd: int32): int32 {.exportc, cdecl.} =
|
||||||
let ifd = int(fd)
|
let idx = int(fd - 3)
|
||||||
if fds.hasKey(ifd):
|
if idx >= 0 and idx < MAX_FDS:
|
||||||
fds.del(ifd)
|
fd_table[idx].active = false
|
||||||
return 0
|
return 0
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc ion_vfs_list*(buf: pointer, max_len: uint64): int64 {.exportc, cdecl.} =
|
proc ion_vfs_list*(buf: pointer, max_len: uint64): int64 {.exportc, cdecl.} =
|
||||||
var s = "/nexus\n/sysro\n/state\n"
|
# Hardcoded baseline for now to avoid string/os dependency
|
||||||
for name in tar.vfs_get_names():
|
let msg = "/nexus\n/sysro\n/state\n"
|
||||||
s.add("/sysro/" & name & "\n")
|
let n = if uint64(msg.len) < max_len: uint64(msg.len) else: max_len
|
||||||
|
if n > 0: copyMem(buf, unsafeAddr msg[0], int(n))
|
||||||
# Add SFS files under /nexus
|
|
||||||
let sfs_names = sfs.sfs_get_files()
|
|
||||||
for line in sfs_names.splitLines():
|
|
||||||
if line.len > 0:
|
|
||||||
s.add("/nexus/" & line & "\n")
|
|
||||||
|
|
||||||
let n = min(s.len, int(max_len))
|
|
||||||
if n > 0: copyMem(buf, addr s[0], n)
|
|
||||||
return int64(n)
|
return int64(n)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef _DIRENT_H
|
||||||
|
#define _DIRENT_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
struct dirent {
|
||||||
|
ino_t d_ino;
|
||||||
|
char d_name[256];
|
||||||
|
};
|
||||||
|
typedef struct { int fd; } DIR;
|
||||||
|
DIR *opendir(const char *name);
|
||||||
|
struct dirent *readdir(DIR *dirp);
|
||||||
|
int closedir(DIR *dirp);
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef _FCNTL_H
|
||||||
|
#define _FCNTL_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define O_RDONLY 0
|
||||||
|
#define O_WRONLY 1
|
||||||
|
#define O_RDWR 2
|
||||||
|
#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
|
||||||
|
|
||||||
|
#define O_CREAT 64
|
||||||
|
#define O_EXCL 128
|
||||||
|
#define O_NOCTTY 256
|
||||||
|
#define O_TRUNC 512
|
||||||
|
#define O_APPEND 1024
|
||||||
|
#define O_NONBLOCK 2048
|
||||||
|
#define O_SYNC 1052672
|
||||||
|
#define O_RSYNC 1052672
|
||||||
|
#define O_DSYNC 4096
|
||||||
|
|
||||||
|
#define F_DUPFD 0
|
||||||
|
#define F_GETFD 1
|
||||||
|
#define F_SETFD 2
|
||||||
|
#define F_GETFL 3
|
||||||
|
#define F_SETFL 4
|
||||||
|
|
||||||
|
#define FD_CLOEXEC 1
|
||||||
|
|
||||||
|
int open(const char *pathname, int flags, ...);
|
||||||
|
int fcntl(int fd, int cmd, ...);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef _GRP_H
|
||||||
|
#define _GRP_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
struct group {
|
||||||
|
char *gr_name;
|
||||||
|
char *gr_passwd;
|
||||||
|
gid_t gr_gid;
|
||||||
|
char **gr_mem;
|
||||||
|
};
|
||||||
|
struct group *getgrgid(gid_t gid);
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef _PWD_H
|
||||||
|
#define _PWD_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
struct passwd {
|
||||||
|
char *pw_name;
|
||||||
|
char *pw_passwd;
|
||||||
|
uid_t pw_uid;
|
||||||
|
gid_t pw_gid;
|
||||||
|
char *pw_gecos;
|
||||||
|
char *pw_dir;
|
||||||
|
char *pw_shell;
|
||||||
|
};
|
||||||
|
struct passwd *getpwuid(uid_t uid);
|
||||||
|
struct passwd *getpwnam(const char *name);
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef _SETJMP_H
|
||||||
|
#define _SETJMP_H
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t regs[14]; // Enough for callee-saved registers on RISC-V 64
|
||||||
|
} jmp_buf[1];
|
||||||
|
|
||||||
|
typedef jmp_buf sigjmp_buf;
|
||||||
|
#define sigsetjmp(env, savemask) setjmp(env)
|
||||||
|
#define siglongjmp(env, val) longjmp(env, val)
|
||||||
|
|
||||||
|
int setjmp(jmp_buf env);
|
||||||
|
void longjmp(jmp_buf env, int val);
|
||||||
|
#endif
|
||||||
|
|
@ -1,22 +1,74 @@
|
||||||
// Minimal signal.h stub for freestanding
|
// Minimal signal.h stub for freestanding
|
||||||
#ifndef _SIGNAL_H
|
#ifndef _SIGNAL_H
|
||||||
#define _SIGNAL_H
|
#define _SIGNAL_H
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef int sig_atomic_t;
|
typedef int sig_atomic_t;
|
||||||
typedef void (*sighandler_t)(int);
|
typedef void (*sighandler_t)(int);
|
||||||
|
typedef sighandler_t sig_t;
|
||||||
|
typedef uint32_t sigset_t;
|
||||||
|
|
||||||
|
struct sigaction {
|
||||||
|
sighandler_t sa_handler;
|
||||||
|
sigset_t sa_mask;
|
||||||
|
int sa_flags;
|
||||||
|
void (*sa_sigaction)(int, void *, void *);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SA_RESTART 0x10000000
|
||||||
|
#define SA_SIGINFO 4
|
||||||
|
#define SA_NOCLDSTOP 1
|
||||||
|
|
||||||
#define SIG_DFL ((sighandler_t)0)
|
#define SIG_DFL ((sighandler_t)0)
|
||||||
#define SIG_IGN ((sighandler_t)1)
|
#define SIG_IGN ((sighandler_t)1)
|
||||||
#define SIG_ERR ((sighandler_t)-1)
|
#define SIG_ERR ((sighandler_t)-1)
|
||||||
|
|
||||||
#define SIGABRT 6
|
#define SIG_BLOCK 0
|
||||||
#define SIGFPE 8
|
#define SIG_UNBLOCK 1
|
||||||
#define SIGILL 4
|
#define SIG_SETMASK 2
|
||||||
|
|
||||||
|
#define SIGHUP 1
|
||||||
#define SIGINT 2
|
#define SIGINT 2
|
||||||
|
#define SIGQUIT 3
|
||||||
|
#define SIGILL 4
|
||||||
|
#define SIGTRAP 5
|
||||||
|
#define SIGABRT 6
|
||||||
|
#define SIGIOT 6
|
||||||
|
#define SIGBUS 7
|
||||||
|
#define SIGFPE 8
|
||||||
|
#define SIGKILL 9
|
||||||
|
#define SIGUSR1 10
|
||||||
#define SIGSEGV 11
|
#define SIGSEGV 11
|
||||||
|
#define SIGUSR2 12
|
||||||
|
#define SIGPIPE 13
|
||||||
|
#define SIGALRM 14
|
||||||
#define SIGTERM 15
|
#define SIGTERM 15
|
||||||
|
#define SIGSTKFLT 16
|
||||||
|
#define SIGCHLD 17
|
||||||
|
#define SIGCONT 18
|
||||||
|
#define SIGSTOP 19
|
||||||
|
#define SIGTSTP 20
|
||||||
|
#define SIGTTIN 21
|
||||||
|
#define SIGTTOU 22
|
||||||
|
#define SIGURG 23
|
||||||
|
#define SIGXCPU 24
|
||||||
|
#define SIGXFSZ 25
|
||||||
|
#define SIGVTALRM 26
|
||||||
|
#define SIGPROF 27
|
||||||
|
#define SIGWINCH 28
|
||||||
|
#define SIGIO 29
|
||||||
|
#define SIGPWR 30
|
||||||
|
#define SIGSYS 31
|
||||||
|
|
||||||
|
#define NSIG 32
|
||||||
|
|
||||||
sighandler_t signal(int signum, sighandler_t handler);
|
sighandler_t signal(int signum, sighandler_t handler);
|
||||||
int raise(int sig);
|
int raise(int sig);
|
||||||
|
int kill(int pid, int sig);
|
||||||
|
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
|
||||||
|
int sigemptyset(sigset_t *set);
|
||||||
|
int sigaddset(sigset_t *set, int signum);
|
||||||
|
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
|
||||||
|
int sigsuspend(const sigset_t *mask);
|
||||||
|
|
||||||
#endif /* _SIGNAL_H */
|
#endif /* _SIGNAL_H */
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,14 @@
|
||||||
// Minimal stdio.h stub for freestanding Nim
|
|
||||||
#ifndef _STDIO_H
|
#ifndef _STDIO_H
|
||||||
#define _STDIO_H
|
#define _STDIO_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
typedef struct FILE FILE;
|
|
||||||
#define EOF (-1)
|
|
||||||
#define stdin ((FILE*)0)
|
|
||||||
#define stdout ((FILE*)1)
|
|
||||||
#define stderr ((FILE*)2)
|
|
||||||
|
|
||||||
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 vsnprintf(char *str, size_t size, const char *format, ...);
|
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||||
int putchar(int c);
|
int rename(const char *oldpath, const char *newpath);
|
||||||
int puts(const char *s);
|
int remove(const char *pathname);
|
||||||
int fflush(FILE *stream);
|
|
||||||
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
|
|
||||||
int ferror(FILE *stream);
|
|
||||||
void clearerr(FILE *stream);
|
|
||||||
int fputc(int c, FILE *stream);
|
|
||||||
int fputs(const char *s, FILE *stream);
|
|
||||||
char *fgets(char *s, int size, FILE *stream);
|
|
||||||
int fgetc(FILE *stream);
|
|
||||||
|
|
||||||
#endif /* _STDIO_H */
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
/* Minimal stdlib.h stub for freestanding Nim */
|
|
||||||
#ifndef _STDLIB_H
|
#ifndef _STDLIB_H
|
||||||
#define _STDLIB_H
|
#define _STDLIB_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void exit(int status);
|
||||||
|
void abort(void);
|
||||||
void *malloc(size_t size);
|
void *malloc(size_t size);
|
||||||
void free(void *ptr);
|
void free(void *ptr);
|
||||||
void *realloc(void *ptr, size_t size);
|
void *realloc(void *ptr, size_t size);
|
||||||
void *calloc(size_t nmemb, size_t size);
|
void *calloc(size_t nmemb, size_t size);
|
||||||
void abort(void);
|
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
|
||||||
void exit(int status);
|
|
||||||
void _Exit(int status);
|
|
||||||
int atoi(const char *nptr);
|
|
||||||
double strtod(const char *nptr, char **endptr);
|
|
||||||
|
|
||||||
#endif /* _STDLIB_H */
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,17 @@
|
||||||
/* Minimal string.h stub for freestanding Nim */
|
|
||||||
#ifndef _STRING_H
|
#ifndef _STRING_H
|
||||||
#define _STRING_H
|
#define _STRING_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
/* Minimal implementations defined in cstubs.c */
|
|
||||||
void *memcpy(void *dest, const void *src, size_t n);
|
void *memcpy(void *dest, const void *src, size_t n);
|
||||||
|
void *memchr(const void *s, int c, size_t n);
|
||||||
void *memset(void *s, int c, size_t n);
|
void *memset(void *s, int c, size_t n);
|
||||||
void *memmove(void *dest, const void *src, size_t n);
|
void *memmove(void *dest, const void *src, size_t n);
|
||||||
int memcmp(const void *s1, const void *s2, size_t n);
|
int memcmp(const void *s1, const void *s2, size_t n);
|
||||||
size_t strlen(const char *s);
|
|
||||||
char *strcpy(char *dest, const char *src);
|
|
||||||
char *strncpy(char *dest, const char *src, size_t n);
|
|
||||||
int strcmp(const char *s1, const char *s2);
|
int strcmp(const char *s1, const char *s2);
|
||||||
int strncmp(const char *s1, const char *s2, size_t n);
|
int strncmp(const char *s1, const char *s2, size_t n);
|
||||||
void *memchr(const void *s, int c, size_t n);
|
char *strchr(const char *s, int c);
|
||||||
char *strerror(int errnum);
|
char *strstr(const char *haystack, const char *needle);
|
||||||
|
size_t strlen(const char *s);
|
||||||
|
|
||||||
#endif /* _STRING_H */
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
#ifndef _SYS_FILE_H
|
||||||
|
#define _SYS_FILE_H
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef _SYS_IOCTL_H
|
||||||
|
#define _SYS_IOCTL_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
int ioctl(int fd, unsigned long request, ...);
|
||||||
|
#define TIOCGWINSZ 0x5413
|
||||||
|
struct winsize { unsigned short ws_row; unsigned short ws_col; unsigned short ws_xpixel; unsigned short ws_ypixel; };
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef _SYS_PARAM_H
|
||||||
|
#define _SYS_PARAM_H
|
||||||
|
#include <limits.h>
|
||||||
|
#define PATH_MAX 4096
|
||||||
|
#define MAXPATHLEN PATH_MAX
|
||||||
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||||
|
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef _SYS_RESOURCE_H
|
||||||
|
#define _SYS_RESOURCE_H
|
||||||
|
#include <sys/time.h>
|
||||||
|
#define RLIMIT_CPU 0
|
||||||
|
#define RLIMIT_FSIZE 1
|
||||||
|
#define RLIMIT_DATA 2
|
||||||
|
#define RLIMIT_STACK 3
|
||||||
|
#define RLIMIT_CORE 4
|
||||||
|
#define RCUT_CUR 0
|
||||||
|
#define RLIM_INFINITY ((unsigned long)-1)
|
||||||
|
struct rlimit { unsigned long rlim_cur; unsigned long rlim_max; };
|
||||||
|
int getrlimit(int resource, struct rlimit *rlim);
|
||||||
|
int setrlimit(int resource, const struct rlimit *rlim);
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef _SYS_STAT_H
|
||||||
|
#define _SYS_STAT_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
struct stat {
|
||||||
|
dev_t st_dev;
|
||||||
|
ino_t st_ino;
|
||||||
|
mode_t st_mode;
|
||||||
|
nlink_t st_nlink;
|
||||||
|
uid_t st_uid;
|
||||||
|
gid_t st_gid;
|
||||||
|
dev_t st_rdev;
|
||||||
|
off_t st_size;
|
||||||
|
blksize_t st_blksize;
|
||||||
|
blkcnt_t st_blocks;
|
||||||
|
time_t st_atime;
|
||||||
|
time_t st_mtime;
|
||||||
|
time_t st_ctime;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define S_IFMT 0170000
|
||||||
|
#define S_IFSOCK 0140000
|
||||||
|
#define S_IFLNK 0120000
|
||||||
|
#define S_IFREG 0100000
|
||||||
|
#define S_IFBLK 0060000
|
||||||
|
#define S_IFDIR 0040000
|
||||||
|
#define S_IFCHR 0020000
|
||||||
|
#define S_IFIFO 0010000
|
||||||
|
#define S_ISUID 0004000
|
||||||
|
#define S_ISGID 0002000
|
||||||
|
#define S_ISVTX 0001000
|
||||||
|
|
||||||
|
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||||
|
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||||
|
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||||
|
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
||||||
|
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
||||||
|
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||||
|
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||||
|
|
||||||
|
#define S_IRWXU 00700
|
||||||
|
#define S_IRUSR 00400
|
||||||
|
#define S_IWUSR 00200
|
||||||
|
#define S_IXUSR 00100
|
||||||
|
|
||||||
|
#define S_IRWXG 00070
|
||||||
|
#define S_IRGRP 00040
|
||||||
|
#define S_IWGRP 00020
|
||||||
|
#define S_IXGRP 00010
|
||||||
|
|
||||||
|
#define S_IRWXO 00007
|
||||||
|
#define S_IROTH 00004
|
||||||
|
#define S_IWOTH 00002
|
||||||
|
#define S_IXOTH 00001
|
||||||
|
|
||||||
|
int stat(const char *pathname, struct stat *statbuf);
|
||||||
|
int fstat(int fd, struct stat *statbuf);
|
||||||
|
int lstat(const char *pathname, struct stat *statbuf);
|
||||||
|
int mkdir(const char *pathname, mode_t mode);
|
||||||
|
mode_t umask(mode_t mask);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef _SYS_TIME_H
|
||||||
|
#define _SYS_TIME_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
struct timeval { time_t tv_sec; long tv_usec; };
|
||||||
|
struct timezone { int tz_minuteswest; int tz_dsttime; };
|
||||||
|
int gettimeofday(struct timeval *tv, struct timezone *tz);
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef _SYS_TIMES_H
|
||||||
|
#define _SYS_TIMES_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
struct tms {
|
||||||
|
clock_t tms_utime;
|
||||||
|
clock_t tms_stime;
|
||||||
|
clock_t tms_cutime;
|
||||||
|
clock_t tms_cstime;
|
||||||
|
};
|
||||||
|
clock_t times(struct tms *buf);
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef _SYS_TYPES_H
|
||||||
|
#define _SYS_TYPES_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef int32_t pid_t;
|
||||||
|
typedef int32_t uid_t;
|
||||||
|
typedef int32_t gid_t;
|
||||||
|
typedef int64_t off_t;
|
||||||
|
typedef int64_t time_t;
|
||||||
|
typedef int32_t mode_t;
|
||||||
|
typedef int32_t dev_t;
|
||||||
|
typedef int32_t ino_t;
|
||||||
|
typedef int32_t nlink_t;
|
||||||
|
typedef int32_t blksize_t;
|
||||||
|
typedef int64_t blkcnt_t;
|
||||||
|
typedef int64_t ssize_t;
|
||||||
|
typedef int64_t clock_t;
|
||||||
|
|
||||||
|
typedef int32_t id_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef _SYS_WAIT_H
|
||||||
|
#define _SYS_WAIT_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define WNOHANG 1
|
||||||
|
#define WUNTRACED 2
|
||||||
|
|
||||||
|
pid_t wait(int *status);
|
||||||
|
pid_t waitpid(pid_t pid, int *status, int options);
|
||||||
|
|
||||||
|
#define WIFEXITED(s) (((s) & 0xff) == 0)
|
||||||
|
#define WEXITSTATUS(s) (((s) >> 8) & 0xff)
|
||||||
|
#define WIFSIGNALED(s) (((s) & 0xff) != 0 && ((s) & 0xff) != 0x7f)
|
||||||
|
#define WTERMSIG(s) ((s) & 0xff)
|
||||||
|
#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
|
||||||
|
#define WSTOPSIG(s) (((s) >> 8) & 0xff)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef _TERMIO_H
|
||||||
|
#define _TERMIO_H
|
||||||
|
#include <termios.h>
|
||||||
|
struct termio {
|
||||||
|
unsigned short c_iflag;
|
||||||
|
unsigned short c_oflag;
|
||||||
|
unsigned short c_cflag;
|
||||||
|
unsigned short c_lflag;
|
||||||
|
unsigned char c_line;
|
||||||
|
unsigned char c_cc[8];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#ifndef _TERMIOS_H
|
||||||
|
#define _TERMIOS_H
|
||||||
|
typedef unsigned char cc_t;
|
||||||
|
typedef unsigned int speed_t;
|
||||||
|
typedef unsigned int tcflag_t;
|
||||||
|
struct termios {
|
||||||
|
tcflag_t c_iflag;
|
||||||
|
tcflag_t c_oflag;
|
||||||
|
tcflag_t c_cflag;
|
||||||
|
tcflag_t c_lflag;
|
||||||
|
cc_t c_line;
|
||||||
|
cc_t c_cc[32];
|
||||||
|
speed_t c_ispeed;
|
||||||
|
speed_t c_ospeed;
|
||||||
|
};
|
||||||
|
#define ECHO 0000010
|
||||||
|
#define ICANON 0000002
|
||||||
|
#define ISIG 0000001
|
||||||
|
#define IEXTEN 0100000
|
||||||
|
|
||||||
|
#define IGNBRK 0000001
|
||||||
|
#define BRKINT 0000002
|
||||||
|
#define IGNPAR 0000004
|
||||||
|
#define PARMRK 0000010
|
||||||
|
#define INPCK 0000020
|
||||||
|
#define ISTRIP 0000040
|
||||||
|
#define INLCR 0000100
|
||||||
|
#define IGNCR 0000200
|
||||||
|
#define ICRNL 0000400
|
||||||
|
#define IUCLC 0001000
|
||||||
|
#define IXON 0002000
|
||||||
|
#define IXANY 0004000
|
||||||
|
#define IXOFF 0010000
|
||||||
|
#define IMAXBEL 0020000
|
||||||
|
#define IUTF8 0040000
|
||||||
|
|
||||||
|
#define TCSANOW 0
|
||||||
|
#define TCSADRAIN 1
|
||||||
|
#define TCSAFLUSH 2
|
||||||
|
|
||||||
|
#define VINTR 0
|
||||||
|
#define VQUIT 1
|
||||||
|
#define VERASE 2
|
||||||
|
#define VKILL 3
|
||||||
|
#define VEOF 4
|
||||||
|
#define VTIME 5
|
||||||
|
#define VMIN 6
|
||||||
|
#define VSWTC 7
|
||||||
|
#define VSTART 8
|
||||||
|
#define VSTOP 9
|
||||||
|
#define VSUSP 10
|
||||||
|
#define VEOL 11
|
||||||
|
#define VREPRINT 12
|
||||||
|
#define VDISCARD 13
|
||||||
|
#define VWERASE 14
|
||||||
|
#define VLNEXT 15
|
||||||
|
#define VEOL2 16
|
||||||
|
|
||||||
|
int tcgetattr(int fd, struct termios *termios_p);
|
||||||
|
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef _TIME_H
|
||||||
|
#define _TIME_H
|
||||||
|
#include <sys/time.h>
|
||||||
|
time_t time(time_t *tloc);
|
||||||
|
#define CLK_TCK 100
|
||||||
|
#define CLOCKS_PER_SEC 1000000
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef _UNISTD_H
|
||||||
|
#define _UNISTD_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define STDIN_FILENO 0
|
||||||
|
#define STDOUT_FILENO 1
|
||||||
|
#define STDERR_FILENO 2
|
||||||
|
|
||||||
|
#define R_OK 4
|
||||||
|
#define W_OK 2
|
||||||
|
#define X_OK 1
|
||||||
|
#define F_OK 0
|
||||||
|
|
||||||
|
#define SEEK_SET 0
|
||||||
|
#define SEEK_CUR 1
|
||||||
|
#define SEEK_END 2
|
||||||
|
|
||||||
|
ssize_t read(int fd, void *buf, size_t count);
|
||||||
|
ssize_t write(int fd, const void *buf, size_t count);
|
||||||
|
int close(int fd);
|
||||||
|
int pipe(int pipefd[2]);
|
||||||
|
int dup2(int oldfd, int newfd);
|
||||||
|
pid_t fork(void);
|
||||||
|
int execv(const char *path, char *const argv[]);
|
||||||
|
int execve(const char *pathname, char *const argv[], char *const envp[]);
|
||||||
|
void _exit(int status);
|
||||||
|
unsigned int sleep(unsigned int seconds);
|
||||||
|
int chdir(const char *path);
|
||||||
|
char *getcwd(char *buf, size_t size);
|
||||||
|
uid_t getuid(void);
|
||||||
|
uid_t geteuid(void);
|
||||||
|
gid_t getgid(void);
|
||||||
|
gid_t getegid(void);
|
||||||
|
int access(const char *pathname, int mode);
|
||||||
|
int isatty(int fd);
|
||||||
|
int unlink(const char *pathname);
|
||||||
|
off_t lseek(int fd, off_t offset, int whence);
|
||||||
|
int link(const char *oldpath, const char *newpath);
|
||||||
|
int rmdir(const char *pathname);
|
||||||
|
int getpid(void);
|
||||||
|
int getppid(void);
|
||||||
|
int setuid(uid_t uid);
|
||||||
|
int setgid(gid_t gid);
|
||||||
|
int setpgid(pid_t pid, pid_t pgid);
|
||||||
|
pid_t getpgrp(void);
|
||||||
|
pid_t tcgetpgrp(int fd);
|
||||||
|
int tcsetpgrp(int fd, pid_t pgrp);
|
||||||
|
unsigned int alarm(unsigned int seconds);
|
||||||
|
int seteuid(uid_t euid);
|
||||||
|
int setegid(gid_t egid);
|
||||||
|
ssize_t readlink(const char *pathname, char *buf, size_t objsiz);
|
||||||
|
|
||||||
|
#endif
|
||||||
47
core/ion.nim
47
core/ion.nim
|
|
@ -46,6 +46,7 @@ type
|
||||||
CMD_NET_RX = 0x501
|
CMD_NET_RX = 0x501
|
||||||
CMD_BLK_READ = 0x600
|
CMD_BLK_READ = 0x600
|
||||||
CMD_BLK_WRITE = 0x601
|
CMD_BLK_WRITE = 0x601
|
||||||
|
CMD_SPAWN_FIBER = 0x700
|
||||||
|
|
||||||
CmdPacket* = object
|
CmdPacket* = object
|
||||||
kind*: uint32
|
kind*: uint32
|
||||||
|
|
@ -104,7 +105,7 @@ type
|
||||||
# Phase 35e: Crypto
|
# Phase 35e: Crypto
|
||||||
fn_siphash*: proc(key: ptr array[16, byte], data: pointer, len: uint64, out_hash: ptr array[16, byte]) {.cdecl.}
|
fn_siphash*: proc(key: ptr array[16, byte], data: pointer, len: uint64, out_hash: ptr array[16, byte]) {.cdecl.}
|
||||||
fn_ed25519_verify*: proc(sig: ptr array[64, byte], msg: pointer, len: uint64, pk: ptr array[32, byte]): bool {.cdecl.}
|
fn_ed25519_verify*: proc(sig: ptr array[64, byte], msg: pointer, len: uint64, pk: ptr array[32, byte]): bool {.cdecl.}
|
||||||
# SPEC-021: Monolith Key Derivation
|
# SPEC-503: Monolith Key Derivation
|
||||||
fn_blake3*: proc(data: pointer, len: uint64, out_hash: ptr array[32, byte]) {.cdecl.}
|
fn_blake3*: proc(data: pointer, len: uint64, out_hash: ptr array[32, byte]) {.cdecl.}
|
||||||
|
|
||||||
# Phase 36.2: Network Membrane (The Veins)
|
# Phase 36.2: Network Membrane (The Veins)
|
||||||
|
|
@ -115,6 +116,13 @@ type
|
||||||
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
||||||
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
||||||
|
|
||||||
|
# Phase 36.4: I/O Multiplexing (8 bytes)
|
||||||
|
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
||||||
|
|
||||||
|
# Phase 36.5: Network Hardware Info (8 bytes)
|
||||||
|
net_mac*: array[6, byte]
|
||||||
|
reserved_mac*: array[2, byte]
|
||||||
|
|
||||||
include invariant
|
include invariant
|
||||||
|
|
||||||
# --- Sovereign Logic ---
|
# --- Sovereign Logic ---
|
||||||
|
|
@ -141,9 +149,15 @@ proc recv*[T](c: var SovereignChannel[T], out_pkt: var T): bool =
|
||||||
elif T is CmdPacket:
|
elif T is CmdPacket:
|
||||||
return hal_cmd_pop(cast[uint64](c.ring), addr out_pkt)
|
return hal_cmd_pop(cast[uint64](c.ring), addr out_pkt)
|
||||||
|
|
||||||
# Global Channels
|
|
||||||
var chan_input*: SovereignChannel[IonPacket]
|
var chan_input*: SovereignChannel[IonPacket]
|
||||||
|
var chan_cmd*: SovereignChannel[CmdPacket]
|
||||||
|
var chan_rx*: SovereignChannel[IonPacket]
|
||||||
|
var chan_tx*: SovereignChannel[IonPacket]
|
||||||
|
|
||||||
var guest_input_hal: HAL_Ring[IonPacket]
|
var guest_input_hal: HAL_Ring[IonPacket]
|
||||||
|
var cmd_hal: HAL_Ring[CmdPacket]
|
||||||
|
var rx_hal: HAL_Ring[IonPacket]
|
||||||
|
var tx_hal: HAL_Ring[IonPacket]
|
||||||
|
|
||||||
# Phase 36.2: Network Channels
|
# Phase 36.2: Network Channels
|
||||||
var chan_net_rx*: SovereignChannel[IonPacket]
|
var chan_net_rx*: SovereignChannel[IonPacket]
|
||||||
|
|
@ -160,6 +174,9 @@ proc ion_init_input*() {.exportc, cdecl.} =
|
||||||
chan_input.ring = addr guest_input_hal
|
chan_input.ring = addr guest_input_hal
|
||||||
|
|
||||||
proc ion_init_network*() {.exportc, cdecl.} =
|
proc ion_init_network*() {.exportc, cdecl.} =
|
||||||
|
# NOTE: This function is called early in kernel boot.
|
||||||
|
# The actual ring memory will be allocated in SYSTABLE region by kmain.
|
||||||
|
# We just initialize the local HAL rings here for internal kernel use.
|
||||||
net_rx_hal.head = 0
|
net_rx_hal.head = 0
|
||||||
net_rx_hal.tail = 0
|
net_rx_hal.tail = 0
|
||||||
net_rx_hal.mask = 255
|
net_rx_hal.mask = 255
|
||||||
|
|
@ -175,6 +192,30 @@ 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
|
||||||
|
|
||||||
|
# Initialize user slab
|
||||||
|
ion_user_slab_init()
|
||||||
|
|
||||||
|
# Internal allocators removed - use shared/systable versions
|
||||||
|
|
||||||
|
|
||||||
|
# =========================================================
|
||||||
|
# SysTable-Compatible Wrappers for User Slab
|
||||||
|
# =========================================================
|
||||||
|
# These wrappers have the same signature as fn_ion_alloc/fn_ion_free
|
||||||
|
# but use the user slab instead of the kernel ION pool.
|
||||||
|
|
||||||
|
# Track allocated buffers by pseudo-ID (index in slab)
|
||||||
|
proc ion_user_alloc_systable*(out_id: ptr uint16): uint64 {.exportc, cdecl.} =
|
||||||
|
## SysTable-compatible allocator using user slab (via shared bitmap)
|
||||||
|
return ion_alloc_shared(out_id)
|
||||||
|
|
||||||
|
proc ion_user_free_systable*(id: uint16) {.exportc, cdecl.} =
|
||||||
|
## SysTable-compatible free using user slab
|
||||||
|
var pkt: IonPacket
|
||||||
|
pkt.id = id
|
||||||
|
pkt.data = cast[ptr UncheckedArray[byte]](1) # Dummy non-nil
|
||||||
|
ion_free(pkt)
|
||||||
|
|
||||||
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) == 192, "SysTable size mismatch! (Expected 192 after BLAKE3 expansion)")
|
static: doAssert(sizeof(SysTable) == 208, "SysTable size mismatch! (Expected 208 after MAC+pad)")
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,13 @@ const
|
||||||
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
|
||||||
|
USER_SLAB_OFFSET = 0x10000'u64 # Offset within SYSTABLE
|
||||||
|
USER_SLAB_BASE* = SYSTABLE_BASE + USER_SLAB_OFFSET # 0x83010000
|
||||||
|
USER_SLAB_COUNT = 512 # 512 packets to cover RX Ring (256) + TX
|
||||||
|
USER_PKT_SIZE = 2048 # 2KB per packet
|
||||||
|
USER_BITMAP_ADDR = SYSTABLE_BASE + 0x100
|
||||||
|
|
||||||
type
|
type
|
||||||
# The Physical Token representing a packet
|
# The Physical Token representing a packet
|
||||||
IonPacket* = object
|
IonPacket* = object
|
||||||
|
|
@ -38,6 +45,7 @@ type
|
||||||
free_ring: RingBuffer[uint16, POOL_COUNT] # Stores IDs of free slabs
|
free_ring: RingBuffer[uint16, POOL_COUNT] # Stores IDs of free slabs
|
||||||
base_phys: uint64
|
base_phys: uint64
|
||||||
|
|
||||||
|
var global_tx_ring*: RingBuffer[IonPacket, 256]
|
||||||
var global_pool: PacketPool
|
var global_pool: PacketPool
|
||||||
|
|
||||||
proc ion_pool_init*() {.exportc.} =
|
proc ion_pool_init*() {.exportc.} =
|
||||||
|
|
@ -58,6 +66,7 @@ proc ion_pool_init*() {.exportc.} =
|
||||||
|
|
||||||
dbg("[ION] Ring Init...")
|
dbg("[ION] Ring Init...")
|
||||||
global_pool.free_ring.init()
|
global_pool.free_ring.init()
|
||||||
|
global_tx_ring.init()
|
||||||
|
|
||||||
# Fill the free ring with all indices [0..1023]
|
# Fill the free ring with all indices [0..1023]
|
||||||
dbg("[ION] Filling Slabs...")
|
dbg("[ION] Filling Slabs...")
|
||||||
|
|
@ -95,6 +104,17 @@ proc ion_free*(pkt: IonPacket) {.exportc.} =
|
||||||
## O(1) Free. Returns the token to the ring.
|
## O(1) Free. Returns the token to the ring.
|
||||||
if pkt.data == nil: return
|
if pkt.data == nil: return
|
||||||
|
|
||||||
|
if (pkt.id and 0x8000) != 0:
|
||||||
|
# User Slab - Clear shared bitmap
|
||||||
|
let slotIdx = pkt.id and 0x7FFF
|
||||||
|
if slotIdx >= USER_SLAB_COUNT: return
|
||||||
|
let bitmap = cast[ptr array[16, byte]](USER_BITMAP_ADDR)
|
||||||
|
let byteIdx = int(slotIdx) div 8
|
||||||
|
let bitIdx = int(slotIdx) mod 8
|
||||||
|
let mask = byte(1 shl bitIdx)
|
||||||
|
bitmap[byteIdx] = bitmap[byteIdx] and (not mask)
|
||||||
|
return
|
||||||
|
|
||||||
discard global_pool.free_ring.push(pkt.id)
|
discard global_pool.free_ring.push(pkt.id)
|
||||||
|
|
||||||
# Helper for C/Zig Interop (Pure Pointers)
|
# Helper for C/Zig Interop (Pure Pointers)
|
||||||
|
|
@ -114,10 +134,18 @@ proc ion_free_raw*(id: uint16) {.exportc, cdecl.} =
|
||||||
ion_free(pkt)
|
ion_free(pkt)
|
||||||
|
|
||||||
proc ion_get_virt*(id: uint16): ptr byte {.exportc.} =
|
proc ion_get_virt*(id: uint16): ptr byte {.exportc.} =
|
||||||
|
if (id and 0x8000) != 0:
|
||||||
|
let idx = id and 0x7FFF
|
||||||
|
let offset = int(idx) * SLAB_SIZE
|
||||||
|
return cast[ptr byte](USER_SLAB_BASE + uint64(offset))
|
||||||
let offset = int(id) * SLAB_SIZE
|
let offset = int(id) * SLAB_SIZE
|
||||||
return addr global_pool.buffer[offset]
|
return addr global_pool.buffer[offset]
|
||||||
|
|
||||||
proc ion_get_phys*(id: uint16): uint64 {.exportc.} =
|
proc ion_get_phys*(id: uint16): uint64 {.exportc.} =
|
||||||
|
if (id and 0x8000) != 0:
|
||||||
|
let idx = id and 0x7FFF
|
||||||
|
let offset = int(idx) * SLAB_SIZE
|
||||||
|
return USER_SLAB_BASE + uint64(offset)
|
||||||
let offset = int(id) * SLAB_SIZE
|
let offset = int(id) * SLAB_SIZE
|
||||||
return global_pool.base_phys + uint64(offset)
|
return global_pool.base_phys + uint64(offset)
|
||||||
|
|
||||||
|
|
@ -125,13 +153,16 @@ proc ion_get_phys*(id: uint16): uint64 {.exportc.} =
|
||||||
# The Global TX Ring (Multiplexing)
|
# The Global TX Ring (Multiplexing)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
var global_tx_ring*: RingBuffer[IonPacket, 256]
|
|
||||||
|
|
||||||
proc ion_tx_init*() {.exportc.} =
|
proc ion_tx_init*() {.exportc.} =
|
||||||
global_tx_ring.init()
|
global_tx_ring.init()
|
||||||
|
|
||||||
proc ion_tx_push*(pkt: IonPacket): bool {.exportc.} =
|
proc ion_tx_push*(pkt: IonPacket): bool {.exportc.} =
|
||||||
global_tx_ring.push(pkt)
|
if global_tx_ring.push(pkt):
|
||||||
|
# dbg("[ION TX] Pushed")
|
||||||
|
return true
|
||||||
|
dbg("[ION TX] PUSH FAILED (Global Ring Full)")
|
||||||
|
return false
|
||||||
|
|
||||||
proc ion_tx_pop*(out_id: ptr uint16, out_len: ptr uint16): bool {.exportc.} =
|
proc ion_tx_pop*(out_id: ptr uint16, out_len: ptr uint16): bool {.exportc.} =
|
||||||
if global_tx_ring.isEmpty:
|
if global_tx_ring.isEmpty:
|
||||||
|
|
@ -142,4 +173,41 @@ proc ion_tx_pop*(out_id: ptr uint16, out_len: ptr uint16): bool {.exportc.} =
|
||||||
|
|
||||||
out_id[] = pkt.id
|
out_id[] = pkt.id
|
||||||
out_len[] = pkt.len
|
out_len[] = pkt.len
|
||||||
|
dbg("[ION TX] Popped Packet for VirtIO")
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
# =========================================================
|
||||||
|
# User-Visible Slab Allocator (Shared Memory)
|
||||||
|
# =========================================================
|
||||||
|
# NOTE: This allocator provides buffers in the SYSTABLE shared region
|
||||||
|
# (0x83010000+) which is mapped into both kernel and userland page tables.
|
||||||
|
# Used for network packet egress from userland.
|
||||||
|
|
||||||
|
# NOTE: Constants moved to top
|
||||||
|
|
||||||
|
|
||||||
|
# var user_slab_bitmap: array[USER_SLAB_COUNT, bool] # REMOVED: Use Shared Bitmap
|
||||||
|
|
||||||
|
proc ion_user_slab_init*() {.exportc.} =
|
||||||
|
## Initialize shared user slab bitmap (all free)
|
||||||
|
let bitmap = cast[ptr array[64, byte]](USER_BITMAP_ADDR)
|
||||||
|
for i in 0 ..< 64:
|
||||||
|
bitmap[i] = 0
|
||||||
|
|
||||||
|
proc ion_alloc_shared*(out_id: ptr uint16): uint64 {.exportc, cdecl.} =
|
||||||
|
## Allocate a buffer from the user-visible slab (Kernel Side, Shared Bitmap)
|
||||||
|
let bitmap = cast[ptr array[64, byte]](USER_BITMAP_ADDR)
|
||||||
|
|
||||||
|
for byteIdx in 0 ..< 64:
|
||||||
|
if bitmap[byteIdx] != 0xFF:
|
||||||
|
for bitIdx in 0 ..< 8:
|
||||||
|
let mask = byte(1 shl bitIdx)
|
||||||
|
if (bitmap[byteIdx] and mask) == 0:
|
||||||
|
# Found free
|
||||||
|
bitmap[byteIdx] = bitmap[byteIdx] or mask
|
||||||
|
let idx = byteIdx * 8 + bitIdx
|
||||||
|
if idx >= USER_SLAB_COUNT: return 0
|
||||||
|
|
||||||
|
out_id[] = uint16(idx) or 0x8000
|
||||||
|
return USER_SLAB_BASE + uint64(idx) * USER_PKT_SIZE
|
||||||
|
return 0
|
||||||
|
|
|
||||||
1297
core/kernel.nim
1297
core/kernel.nim
File diff suppressed because it is too large
Load Diff
|
|
@ -17,7 +17,7 @@ proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
proc kprint_hex(v: uint64) {.importc, cdecl.}
|
proc kprint_hex(v: uint64) {.importc, cdecl.}
|
||||||
|
|
||||||
# Assembly trampoline to jump to userland
|
# Assembly trampoline to jump to userland
|
||||||
proc rumpk_enter_userland*(entry: uint64) {.importc, cdecl.}
|
proc rumpk_enter_userland*(entry, argc, argv, sp: uint64) {.importc, cdecl.}
|
||||||
|
|
||||||
proc kload*(path: string): uint64 =
|
proc kload*(path: string): uint64 =
|
||||||
# 1. Read ELF File from VFS
|
# 1. Read ELF File from VFS
|
||||||
|
|
@ -71,7 +71,7 @@ proc kexec*(path: string) =
|
||||||
let entry = kload(path)
|
let entry = kload(path)
|
||||||
if entry != 0:
|
if entry != 0:
|
||||||
kprintln("[Loader] Transferring Consciousness...")
|
kprintln("[Loader] Transferring Consciousness...")
|
||||||
rumpk_enter_userland(entry)
|
rumpk_enter_userland(entry, 0, 0, 0)
|
||||||
|
|
||||||
proc kload_phys*(path: string, phys_offset: uint64): uint64 =
|
proc kload_phys*(path: string, phys_offset: uint64): uint64 =
|
||||||
let file_content = vfs_read_file(path)
|
let file_content = vfs_read_file(path)
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,95 @@ extern fn console_write(ptr: [*]const u8, len: usize) void;
|
||||||
// Embed the Subject Zero binary
|
// Embed the Subject Zero binary
|
||||||
export var subject_bin = @embedFile("subject.bin");
|
export var subject_bin = @embedFile("subject.bin");
|
||||||
|
|
||||||
|
export fn ion_loader_load(path: [*:0]const u8) u64 {
|
||||||
|
_ = path;
|
||||||
|
|
||||||
|
console_write("[Loader] Parsing ELF\n", 21);
|
||||||
|
|
||||||
|
// Verify ELF Magic
|
||||||
|
const magic = subject_bin[0..4];
|
||||||
|
if (magic[0] != 0x7F or magic[1] != 'E' or magic[2] != 'L' or magic[3] != 'F') {
|
||||||
|
console_write("[Loader] ERROR: Invalid ELF magic\n", 35);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse ELF64 Header
|
||||||
|
const e_entry = read_u64_le(subject_bin[0x18..0x20]);
|
||||||
|
const e_phoff = read_u64_le(subject_bin[0x20..0x28]);
|
||||||
|
const e_phentsize = read_u16_le(subject_bin[0x36..0x38]);
|
||||||
|
const e_phnum = read_u16_le(subject_bin[0x38..0x3a]);
|
||||||
|
|
||||||
|
console_write("[Loader] Entry: 0x", 18);
|
||||||
|
print_hex(e_entry);
|
||||||
|
console_write("\n[Loader] Loading ", 17);
|
||||||
|
print_hex(e_phnum);
|
||||||
|
console_write(" segments\n", 10);
|
||||||
|
|
||||||
|
// Load each PT_LOAD segment
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < e_phnum) : (i += 1) {
|
||||||
|
const ph_offset = e_phoff + (i * e_phentsize);
|
||||||
|
const p_type = read_u32_le(subject_bin[ph_offset .. ph_offset + 4]);
|
||||||
|
|
||||||
|
if (p_type == 1) { // PT_LOAD
|
||||||
|
const p_offset = read_u64_le(subject_bin[ph_offset + 8 .. ph_offset + 16]);
|
||||||
|
const p_vaddr = read_u64_le(subject_bin[ph_offset + 16 .. ph_offset + 24]);
|
||||||
|
const p_filesz = read_u64_le(subject_bin[ph_offset + 32 .. ph_offset + 40]);
|
||||||
|
const p_memsz = read_u64_le(subject_bin[ph_offset + 40 .. ph_offset + 48]);
|
||||||
|
|
||||||
|
const dest = @as([*]u8, @ptrFromInt(p_vaddr));
|
||||||
|
|
||||||
|
// Copy file content
|
||||||
|
if (p_filesz > 0) {
|
||||||
|
const src = subject_bin[p_offset .. p_offset + p_filesz];
|
||||||
|
@memcpy(dest[0..p_filesz], src);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero BSS (memsz > filesz)
|
||||||
|
if (p_memsz > p_filesz) {
|
||||||
|
@memset(dest[p_filesz..p_memsz], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console_write("[Loader] ELF loaded successfully\n", 33);
|
||||||
|
return e_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u16_le(bytes: []const u8) u16 {
|
||||||
|
return @as(u16, bytes[0]) | (@as(u16, bytes[1]) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u32_le(bytes: []const u8) u32 {
|
||||||
|
return @as(u32, bytes[0]) |
|
||||||
|
(@as(u32, bytes[1]) << 8) |
|
||||||
|
(@as(u32, bytes[2]) << 16) |
|
||||||
|
(@as(u32, bytes[3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u64_le(bytes: []const u8) u64 {
|
||||||
|
var result: u64 = 0;
|
||||||
|
var j: usize = 0;
|
||||||
|
while (j < 8) : (j += 1) {
|
||||||
|
result |= @as(u64, bytes[j]) << @intCast(j * 8);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_hex(value: u64) void {
|
||||||
|
const hex_chars = "0123456789ABCDEF";
|
||||||
|
var buf: [16]u8 = undefined;
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < 16) : (i += 1) {
|
||||||
|
const shift: u6 = @intCast((15 - i) * 4);
|
||||||
|
const nibble = (value >> shift) & 0xF;
|
||||||
|
buf[i] = hex_chars[nibble];
|
||||||
|
}
|
||||||
|
console_write(&buf, 16);
|
||||||
|
}
|
||||||
|
|
||||||
export fn launch_subject() void {
|
export fn launch_subject() void {
|
||||||
const target_addr: usize = 0x84000000;
|
const target_addr = ion_loader_load("/sysro/bin/subject");
|
||||||
const dest = @as([*]u8, @ptrFromInt(target_addr));
|
|
||||||
|
|
||||||
console_write("[Loader] Loading Subject Zero...\n", 33);
|
|
||||||
@memcpy(dest[0..subject_bin.len], subject_bin);
|
|
||||||
|
|
||||||
console_write("[Loader] Jumping...\n", 20);
|
console_write("[Loader] Jumping...\n", 20);
|
||||||
|
|
||||||
const entry = @as(*const fn () void, @ptrFromInt(target_addr));
|
const entry = @as(*const fn () void, @ptrFromInt(target_addr));
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ proc virtio_net_send(data: pointer, len: uint32) {.importc, cdecl.}
|
||||||
proc kprintln(s: cstring) {.importc, cdecl.}
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
proc kprint(s: cstring) {.importc, cdecl.}
|
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, cdecl.}
|
proc get_now_ns(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}
|
||||||
|
|
||||||
# Membrane Infrastructure (LwIP Glue)
|
# Membrane Infrastructure (LwIP Glue)
|
||||||
proc membrane_init*() {.importc, cdecl.}
|
proc membrane_init*() {.importc, cdecl.}
|
||||||
|
|
@ -80,7 +80,7 @@ proc netswitch_process_packet(pkt: IonPacket): bool =
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
of 0x88B5: # Sovereign UTCP (SPEC-410)
|
of 0x88B5: # Sovereign UTCP (SPEC-700)
|
||||||
# TODO: Route to dedicated UTCP channel
|
# TODO: Route to dedicated UTCP channel
|
||||||
# kprintln("[NetSwitch] UTCP Sovereign Packet Identified")
|
# kprintln("[NetSwitch] UTCP Sovereign Packet Identified")
|
||||||
ion_free(pkt)
|
ion_free(pkt)
|
||||||
|
|
@ -92,7 +92,6 @@ proc netswitch_process_packet(pkt: IonPacket): bool =
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc fiber_netswitch_entry*() {.cdecl.} =
|
proc fiber_netswitch_entry*() {.cdecl.} =
|
||||||
membrane_init()
|
|
||||||
kprintln("[NetSwitch] Fiber Entry - The Traffic Cop is ON DUTY")
|
kprintln("[NetSwitch] Fiber Entry - The Traffic Cop is ON DUTY")
|
||||||
|
|
||||||
var rx_activity: bool = false
|
var rx_activity: bool = false
|
||||||
|
|
@ -108,8 +107,7 @@ proc fiber_netswitch_entry*() {.cdecl.} =
|
||||||
# 1. Drive the hardware poll (fills chan_netswitch_rx)
|
# 1. Drive the hardware poll (fills chan_netswitch_rx)
|
||||||
virtio_net_poll()
|
virtio_net_poll()
|
||||||
|
|
||||||
# 2. Drive the LwIP Stack (Timers/RX)
|
# [Cleaned] Driven by Userland now
|
||||||
pump_membrane_stack()
|
|
||||||
|
|
||||||
# 2. Consume from the Driver -> Switch internal ring
|
# 2. Consume from the Driver -> Switch internal ring
|
||||||
var raw_pkt: IonPacket
|
var raw_pkt: IonPacket
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
# SPDX-License-Identifier: LSL-1.0
|
||||||
|
# Copyright (c) 2026 Markus Maiwald
|
||||||
|
# Stewardship: Self Sovereign Society Foundation
|
||||||
|
#
|
||||||
|
# This file is part of the Nexus Sovereign Core.
|
||||||
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
|
# SPEC-060: System Ontology - Nim Bindings
|
||||||
|
# Ground Zero Phase 2: Event System
|
||||||
|
|
||||||
|
## Event System Nim Bindings
|
||||||
|
|
||||||
|
# Kernel logging (freestanding-safe)
|
||||||
|
proc kprint(s: cstring) {.importc, cdecl.}
|
||||||
|
proc kprint_hex(n: uint64) {.importc, cdecl.}
|
||||||
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
|
|
||||||
|
# Import STL from HAL
|
||||||
|
proc stl_init*() {.importc, cdecl.}
|
||||||
|
proc stl_emit*(
|
||||||
|
kind: uint16,
|
||||||
|
fiber_id: uint64,
|
||||||
|
entity_id: uint64,
|
||||||
|
cause_id: uint64,
|
||||||
|
data0: uint64,
|
||||||
|
data1: uint64,
|
||||||
|
data2: uint64
|
||||||
|
): uint64 {.importc, cdecl.}
|
||||||
|
proc stl_lookup*(event_id: uint64): pointer {.importc, cdecl.}
|
||||||
|
proc stl_count*(): uint32 {.importc, cdecl.}
|
||||||
|
|
||||||
|
type
|
||||||
|
QueryResult* = object
|
||||||
|
count*: uint32
|
||||||
|
events*: array[64, pointer]
|
||||||
|
|
||||||
|
proc stl_query_by_fiber*(fiber_id: uint64, result: var QueryResult) {.importc, cdecl.}
|
||||||
|
proc stl_query_by_kind*(kind: uint16, result: var QueryResult) {.importc, cdecl.}
|
||||||
|
proc stl_get_recent*(max_count: uint32, result: var QueryResult) {.importc, cdecl.}
|
||||||
|
proc stl_query_by_time_range*(start_ns: uint64, end_ns: uint64, result: var QueryResult) {.importc, cdecl.}
|
||||||
|
|
||||||
|
type
|
||||||
|
LineageResult* = object
|
||||||
|
count*: uint32
|
||||||
|
event_ids*: array[16, uint64]
|
||||||
|
|
||||||
|
proc stl_trace_lineage*(event_id: uint64, result: var LineageResult) {.importc, cdecl.}
|
||||||
|
|
||||||
|
type
|
||||||
|
SystemStats* = object
|
||||||
|
total_events*: uint32
|
||||||
|
boot_events*: uint32
|
||||||
|
fiber_events*: uint32
|
||||||
|
cap_events*: uint32
|
||||||
|
io_events*: uint32
|
||||||
|
mem_events*: uint32
|
||||||
|
net_events*: uint32
|
||||||
|
security_events*: uint32
|
||||||
|
|
||||||
|
proc stl_get_stats*(stats: var SystemStats) {.importc, cdecl.}
|
||||||
|
proc stl_export_binary*(dest: pointer, max_size: uint64): uint64 {.importc, cdecl.}
|
||||||
|
|
||||||
|
## Event Types (Mirror from ontology.zig)
|
||||||
|
type
|
||||||
|
EventKind* = enum
|
||||||
|
EvNull = 0
|
||||||
|
# Lifecycle
|
||||||
|
EvSystemBoot = 1
|
||||||
|
EvSystemShutdown = 2
|
||||||
|
EvFiberSpawn = 3
|
||||||
|
EvFiberTerminate = 4
|
||||||
|
# Capability
|
||||||
|
EvCapabilityGrant = 10
|
||||||
|
EvCapabilityRevoke = 11
|
||||||
|
EvCapabilityDelegate = 12
|
||||||
|
# I/O
|
||||||
|
EvChannelOpen = 20
|
||||||
|
EvChannelClose = 21
|
||||||
|
EvChannelRead = 22
|
||||||
|
EvChannelWrite = 23
|
||||||
|
# Memory
|
||||||
|
EvMemoryAllocate = 30
|
||||||
|
EvMemoryFree = 31
|
||||||
|
EvMemoryMap = 32
|
||||||
|
# Network
|
||||||
|
EvNetworkPacketRx = 40
|
||||||
|
EvNetworkPacketTx = 41
|
||||||
|
# Security
|
||||||
|
EvAccessDenied = 50
|
||||||
|
EvPolicyViolation = 51
|
||||||
|
|
||||||
|
## High-level API for kernel use
|
||||||
|
|
||||||
|
proc emit_system_boot*(): uint64 =
|
||||||
|
## Emit system boot event
|
||||||
|
return stl_emit(
|
||||||
|
uint16(EvSystemBoot),
|
||||||
|
0, # fiber_id (kernel)
|
||||||
|
0, # entity_id
|
||||||
|
0, # cause_id
|
||||||
|
0, 0, 0 # data
|
||||||
|
)
|
||||||
|
|
||||||
|
proc emit_fiber_spawn*(fiber_id: uint64, parent_id: uint64, cause_id: uint64 = 0): uint64 =
|
||||||
|
## Emit fiber spawn event
|
||||||
|
return stl_emit(
|
||||||
|
uint16(EvFiberSpawn),
|
||||||
|
parent_id,
|
||||||
|
fiber_id,
|
||||||
|
cause_id,
|
||||||
|
0, 0, 0
|
||||||
|
)
|
||||||
|
|
||||||
|
proc emit_capability_grant*(
|
||||||
|
fiber_id: uint64,
|
||||||
|
cap_type: uint8,
|
||||||
|
object_id: uint64,
|
||||||
|
slot: uint32,
|
||||||
|
cause_id: uint64 = 0
|
||||||
|
): uint64 =
|
||||||
|
## Emit capability grant event
|
||||||
|
return stl_emit(
|
||||||
|
uint16(EvCapabilityGrant),
|
||||||
|
fiber_id,
|
||||||
|
object_id,
|
||||||
|
cause_id,
|
||||||
|
uint64(cap_type),
|
||||||
|
uint64(slot),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
proc emit_channel_write*(
|
||||||
|
fiber_id: uint64,
|
||||||
|
channel_id: uint64,
|
||||||
|
bytes_written: uint64,
|
||||||
|
cause_id: uint64 = 0
|
||||||
|
): uint64 =
|
||||||
|
## Emit channel write event
|
||||||
|
return stl_emit(
|
||||||
|
uint16(EvChannelWrite),
|
||||||
|
fiber_id,
|
||||||
|
channel_id,
|
||||||
|
cause_id,
|
||||||
|
bytes_written,
|
||||||
|
0, 0
|
||||||
|
)
|
||||||
|
|
||||||
|
proc emit_access_denied*(
|
||||||
|
fiber_id: uint64,
|
||||||
|
resource_id: uint64,
|
||||||
|
attempted_perm: uint8,
|
||||||
|
cause_id: uint64 = 0
|
||||||
|
): uint64 =
|
||||||
|
## Emit access denied event (security)
|
||||||
|
return stl_emit(
|
||||||
|
uint16(EvAccessDenied),
|
||||||
|
fiber_id,
|
||||||
|
resource_id,
|
||||||
|
cause_id,
|
||||||
|
uint64(attempted_perm),
|
||||||
|
0, 0
|
||||||
|
)
|
||||||
|
|
||||||
|
## Initialization
|
||||||
|
proc init_stl_subsystem*() =
|
||||||
|
## Initialize the STL subsystem (call from kmain)
|
||||||
|
stl_init()
|
||||||
|
kprintln("[STL] System Truth Ledger initialized")
|
||||||
|
|
||||||
|
## Query API
|
||||||
|
proc stl_print_summary*() {.exportc, cdecl.} =
|
||||||
|
## Print a summary of the STL ledger to the console
|
||||||
|
var stats: SystemStats
|
||||||
|
stl_get_stats(stats)
|
||||||
|
|
||||||
|
kprintln("\n[STL] System Truth Ledger Summary:")
|
||||||
|
kprint("[STL] Total Events: "); kprint_hex(uint64(stats.total_events)); kprintln("")
|
||||||
|
kprint("[STL] Lifecycle: "); kprint_hex(uint64(stats.boot_events + stats.fiber_events)); kprintln("")
|
||||||
|
kprint("[STL] Capabilities: "); kprint_hex(uint64(stats.cap_events)); kprintln("")
|
||||||
|
kprint("[STL] I/O & Channels: "); kprint_hex(uint64(stats.io_events)); kprintln("")
|
||||||
|
kprint("[STL] Memory: "); kprint_hex(uint64(stats.mem_events)); kprintln("")
|
||||||
|
kprint("[STL] Security/Policy: "); kprint_hex(uint64(stats.security_events)); kprintln("")
|
||||||
|
|
||||||
|
# Demonstrate Causal Graph for the last event
|
||||||
|
if stats.total_events > 0:
|
||||||
|
let last_id = uint64(stats.total_events - 1)
|
||||||
|
var lineage: LineageResult
|
||||||
|
stl_trace_lineage(last_id, lineage)
|
||||||
|
|
||||||
|
kprintln("\n[STL] Causal Graph Audit:");
|
||||||
|
kprint("[STL] Target: "); kprint_hex(last_id); kprintln("")
|
||||||
|
|
||||||
|
for i in 0..<lineage.count:
|
||||||
|
let eid = lineage.event_ids[i]
|
||||||
|
let ev_ptr = stl_lookup(eid)
|
||||||
|
|
||||||
|
if i > 0: kprintln(" |")
|
||||||
|
|
||||||
|
kprint(" +-- ["); kprint_hex(eid); kprint("] ")
|
||||||
|
|
||||||
|
if ev_ptr != nil:
|
||||||
|
# Kind is at offset 0 (2 bytes)
|
||||||
|
let kind_val = cast[ptr uint16](ev_ptr)[]
|
||||||
|
if kind_val == uint16(EvSystemBoot): kprintln("SystemBoot")
|
||||||
|
elif kind_val == uint16(EvFiberSpawn): kprintln("FiberSpawn")
|
||||||
|
elif kind_val == uint16(EvCapabilityGrant): kprintln("CapGrant")
|
||||||
|
elif kind_val == uint16(EvAccessDenied): kprintln("AccessDenied")
|
||||||
|
else:
|
||||||
|
kprint("Kind="); kprint_hex(uint64(kind_val)); kprintln("")
|
||||||
|
else:
|
||||||
|
kprintln("Unknown")
|
||||||
|
|
||||||
|
kprintln("\n[STL] Summary complete.")
|
||||||
|
|
||||||
|
proc export_stl_binary*(dest: pointer, max_size: uint64): uint64 =
|
||||||
|
## Export STL events to a binary buffer
|
||||||
|
return stl_export_binary(dest, max_size)
|
||||||
|
|
@ -21,6 +21,7 @@ double floor(double x) {
|
||||||
}
|
}
|
||||||
double fmod(double x, double y) { return 0.0; } // Stub
|
double fmod(double x, double y) { return 0.0; } // Stub
|
||||||
|
|
||||||
|
/* atomic overrides commented out to prefer stubs.zig
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Atomic Overrides (To avoid libcompiler_rt atomics.o which uses medlow)
|
// Atomic Overrides (To avoid libcompiler_rt atomics.o which uses medlow)
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
@ -116,6 +117,7 @@ void sovereign_atomic_fetch_min_16(void *ptr, void *val, void *ret, int model) {
|
||||||
bool sovereign_atomic_is_lock_free(size_t size, void *ptr) {
|
bool sovereign_atomic_is_lock_free(size_t size, void *ptr) {
|
||||||
return true; // We are single core or spinlocked elsewhere
|
return true; // We are single core or spinlocked elsewhere
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// ===================================
|
// ===================================
|
||||||
// Compiler-RT Stubs (128-bit Math)
|
// Compiler-RT Stubs (128-bit Math)
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,40 @@
|
||||||
# Required for Nim --os:any / --os:standalone
|
# Required for Nim --os:any / --os:standalone
|
||||||
# This file must be named panicoverride.nim
|
# This file must be named panicoverride.nim
|
||||||
|
|
||||||
|
var nimErrorFlag* {.exportc: "nimErrorFlag", compilerproc.}: bool = false
|
||||||
|
|
||||||
|
proc nimAddInt(a, b: int, res: var int): bool {.compilerproc.} =
|
||||||
|
let r = a + b
|
||||||
|
if (r < a) != (b < 0): return true
|
||||||
|
res = r
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc nimSubInt(a, b: int, res: var int): bool {.compilerproc.} =
|
||||||
|
let r = a - b
|
||||||
|
if (r > a) != (b < 0): return true
|
||||||
|
res = r
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc nimMulInt(a, b: int, res: var int): bool {.compilerproc.} =
|
||||||
|
let r = a * b
|
||||||
|
if b != 0 and (r div b) != a: return true
|
||||||
|
res = r
|
||||||
|
return false
|
||||||
|
|
||||||
{.push stackTrace: off.}
|
{.push stackTrace: off.}
|
||||||
|
|
||||||
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
||||||
proc rumpk_halt() {.importc, cdecl, noreturn.}
|
proc rumpk_halt() {.importc, cdecl, noreturn.}
|
||||||
|
|
||||||
|
# Stubs for missing runtime symbols to satisfy linker
|
||||||
|
proc setLengthStr*(s: pointer, newLen: int) {.exportc, compilerproc.} = discard
|
||||||
|
proc addChar*(s: pointer, c: char) {.exportc, compilerproc.} = discard
|
||||||
|
proc callDepthLimitReached*() {.exportc, compilerproc.} =
|
||||||
|
while true: discard
|
||||||
|
|
||||||
|
# Type Info stub for Defect (referenced by digitsutils/exceptions)
|
||||||
|
var NTIdefect* {.exportc: "NTIdefect__SEK9acOiG0hv2dnGQbk52qg_", compilerproc.}: pointer = nil
|
||||||
|
|
||||||
proc rawoutput(s: string) =
|
proc rawoutput(s: string) =
|
||||||
if s.len > 0:
|
if s.len > 0:
|
||||||
console_write(unsafeAddr s[0], csize_t(s.len))
|
console_write(unsafeAddr s[0], csize_t(s.len))
|
||||||
|
|
@ -32,4 +61,17 @@ proc panic(s: cstring) {.exportc, noreturn.} =
|
||||||
rawoutput("\n")
|
rawoutput("\n")
|
||||||
rumpk_halt()
|
rumpk_halt()
|
||||||
|
|
||||||
|
proc raiseIndexError2(i, n: int) {.exportc, noreturn, compilerproc.} =
|
||||||
|
rawoutput("[PANIC] Index Error: ")
|
||||||
|
panic("Index Out of Bounds")
|
||||||
|
|
||||||
|
proc raiseOverflow() {.exportc, noreturn, compilerproc.} =
|
||||||
|
panic("Integer Overflow")
|
||||||
|
|
||||||
|
proc raiseRangeError(val: int64) {.exportc, noreturn, compilerproc.} =
|
||||||
|
panic("Range Error")
|
||||||
|
|
||||||
|
proc raiseDivByZero() {.exportc, noreturn, compilerproc.} =
|
||||||
|
panic("Division by Zero")
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,225 @@
|
||||||
|
# SPDX-License-Identifier: LSL-1.0
|
||||||
|
# Copyright (c) 2026 Markus Maiwald
|
||||||
|
# Stewardship: Self Sovereign Society Foundation
|
||||||
|
#
|
||||||
|
# This file is part of the Nexus Sovereign Core.
|
||||||
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
|
## Nexus Core: Pseudo-Terminal (PTY) Subsystem
|
||||||
|
##
|
||||||
|
## Provides a POSIX-like PTY interface for terminal emulation.
|
||||||
|
## Master fd is held by terminal emulator, slave fd by shell.
|
||||||
|
##
|
||||||
|
## Phase 40: The Soul Bridge (PTY Implementation)
|
||||||
|
|
||||||
|
import ../libs/membrane/term
|
||||||
|
|
||||||
|
const
|
||||||
|
MAX_PTYS* = 8
|
||||||
|
PTY_BUFFER_SIZE* = 4096
|
||||||
|
|
||||||
|
# File descriptor ranges
|
||||||
|
PTY_MASTER_BASE* = 100 # Master fds: 100-107
|
||||||
|
PTY_SLAVE_BASE* = 200 # Slave fds: 200-207
|
||||||
|
|
||||||
|
type
|
||||||
|
LineMode* = enum
|
||||||
|
lmRaw, # No processing (binary mode)
|
||||||
|
lmCanon # Canonical mode (line buffering, echo)
|
||||||
|
|
||||||
|
PtyPair* = object
|
||||||
|
active*: bool
|
||||||
|
id*: int
|
||||||
|
|
||||||
|
# Buffers (bidirectional)
|
||||||
|
master_to_slave*: array[PTY_BUFFER_SIZE, byte]
|
||||||
|
mts_head*, mts_tail*: int
|
||||||
|
|
||||||
|
slave_to_master*: array[PTY_BUFFER_SIZE, byte]
|
||||||
|
stm_head*, stm_tail*: int
|
||||||
|
|
||||||
|
# Line discipline
|
||||||
|
mode*: LineMode
|
||||||
|
echo*: bool
|
||||||
|
|
||||||
|
# Window size
|
||||||
|
rows*, cols*: int
|
||||||
|
|
||||||
|
var ptys*: array[MAX_PTYS, PtyPair]
|
||||||
|
var next_pty_id: int = 0
|
||||||
|
|
||||||
|
# --- Logging ---
|
||||||
|
proc kprint(s: cstring) {.importc, cdecl.}
|
||||||
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
|
proc kprint_hex(v: uint64) {.importc, cdecl.}
|
||||||
|
|
||||||
|
proc pty_init*() {.exportc, cdecl.} =
|
||||||
|
for i in 0 ..< MAX_PTYS:
|
||||||
|
ptys[i].active = false
|
||||||
|
ptys[i].id = -1
|
||||||
|
next_pty_id = 0
|
||||||
|
kprintln("[PTY] Subsystem Initialized")
|
||||||
|
|
||||||
|
proc pty_alloc*(): int {.exportc, cdecl.} =
|
||||||
|
## Allocate a new PTY pair. Returns PTY ID or -1 on failure.
|
||||||
|
for i in 0 ..< MAX_PTYS:
|
||||||
|
if not ptys[i].active:
|
||||||
|
ptys[i].active = true
|
||||||
|
ptys[i].id = next_pty_id
|
||||||
|
ptys[i].mts_head = 0
|
||||||
|
ptys[i].mts_tail = 0
|
||||||
|
ptys[i].stm_head = 0
|
||||||
|
ptys[i].stm_tail = 0
|
||||||
|
ptys[i].mode = lmCanon
|
||||||
|
ptys[i].echo = true
|
||||||
|
ptys[i].rows = 37
|
||||||
|
ptys[i].cols = 100
|
||||||
|
next_pty_id += 1
|
||||||
|
kprint("[PTY] Allocated ID=")
|
||||||
|
kprint_hex(uint64(ptys[i].id))
|
||||||
|
kprintln("")
|
||||||
|
return ptys[i].id
|
||||||
|
kprintln("[PTY] ERROR: Max PTYs allocated")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
proc pty_get_master_fd*(pty_id: int): int =
|
||||||
|
## Get the master file descriptor for a PTY.
|
||||||
|
if pty_id < 0 or pty_id >= MAX_PTYS: return -1
|
||||||
|
if not ptys[pty_id].active: return -1
|
||||||
|
return PTY_MASTER_BASE + pty_id
|
||||||
|
|
||||||
|
proc pty_get_slave_fd*(pty_id: int): int =
|
||||||
|
## Get the slave file descriptor for a PTY.
|
||||||
|
if pty_id < 0 or pty_id >= MAX_PTYS: return -1
|
||||||
|
if not ptys[pty_id].active: return -1
|
||||||
|
return PTY_SLAVE_BASE + pty_id
|
||||||
|
|
||||||
|
proc is_pty_master_fd*(fd: int): bool =
|
||||||
|
return fd >= PTY_MASTER_BASE and fd < PTY_MASTER_BASE + MAX_PTYS
|
||||||
|
|
||||||
|
proc is_pty_slave_fd*(fd: int): bool =
|
||||||
|
return fd >= PTY_SLAVE_BASE and fd < PTY_SLAVE_BASE + MAX_PTYS
|
||||||
|
|
||||||
|
proc get_pty_from_fd*(fd: int): ptr PtyPair =
|
||||||
|
if is_pty_master_fd(fd):
|
||||||
|
let idx = fd - PTY_MASTER_BASE
|
||||||
|
if ptys[idx].active: return addr ptys[idx]
|
||||||
|
elif is_pty_slave_fd(fd):
|
||||||
|
let idx = fd - PTY_SLAVE_BASE
|
||||||
|
if ptys[idx].active: return addr ptys[idx]
|
||||||
|
return nil
|
||||||
|
|
||||||
|
# --- Buffer Operations ---
|
||||||
|
|
||||||
|
proc ring_push(buf: var array[PTY_BUFFER_SIZE, byte], head, tail: var int, data: byte): bool =
|
||||||
|
let next = (tail + 1) mod PTY_BUFFER_SIZE
|
||||||
|
if next == head: return false # Buffer full
|
||||||
|
buf[tail] = data
|
||||||
|
tail = next
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc ring_pop(buf: var array[PTY_BUFFER_SIZE, byte], head, tail: var int): int =
|
||||||
|
if head == tail: return -1 # Buffer empty
|
||||||
|
let b = int(buf[head])
|
||||||
|
head = (head + 1) mod PTY_BUFFER_SIZE
|
||||||
|
return b
|
||||||
|
|
||||||
|
proc ring_count(head, tail: int): int =
|
||||||
|
if tail >= head:
|
||||||
|
return tail - head
|
||||||
|
else:
|
||||||
|
return PTY_BUFFER_SIZE - head + tail
|
||||||
|
|
||||||
|
# --- I/O Operations ---
|
||||||
|
|
||||||
|
proc pty_write_master*(fd: int, data: ptr byte, len: int): int =
|
||||||
|
## Write to master (goes to slave input). Called by terminal emulator.
|
||||||
|
let pty = get_pty_from_fd(fd)
|
||||||
|
if pty == nil: return -1
|
||||||
|
|
||||||
|
var written = 0
|
||||||
|
for i in 0 ..< len:
|
||||||
|
let b = cast[ptr UncheckedArray[byte]](data)[i]
|
||||||
|
if ring_push(pty.master_to_slave, pty.mts_head, pty.mts_tail, b):
|
||||||
|
written += 1
|
||||||
|
else:
|
||||||
|
break # Buffer full
|
||||||
|
return written
|
||||||
|
|
||||||
|
proc pty_read_master*(fd: int, data: ptr byte, len: int): int =
|
||||||
|
## Read from master (gets slave output). Called by terminal emulator.
|
||||||
|
let pty = get_pty_from_fd(fd)
|
||||||
|
if pty == nil: return -1
|
||||||
|
|
||||||
|
var read_count = 0
|
||||||
|
let buf = cast[ptr UncheckedArray[byte]](data)
|
||||||
|
for i in 0 ..< len:
|
||||||
|
let b = ring_pop(pty.slave_to_master, pty.stm_head, pty.stm_tail)
|
||||||
|
if b < 0: break
|
||||||
|
buf[i] = byte(b)
|
||||||
|
read_count += 1
|
||||||
|
return read_count
|
||||||
|
|
||||||
|
proc pty_write_slave*(fd: int, data: ptr byte, len: int): int {.exportc, cdecl.} =
|
||||||
|
## Write to slave (output from shell). Goes to master read buffer.
|
||||||
|
## Also renders to FB terminal.
|
||||||
|
let pty = get_pty_from_fd(fd)
|
||||||
|
if pty == nil: return -1
|
||||||
|
|
||||||
|
var written = 0
|
||||||
|
let buf = cast[ptr UncheckedArray[byte]](data)
|
||||||
|
for i in 0 ..< len:
|
||||||
|
let b = buf[i]
|
||||||
|
|
||||||
|
# Push to slave-to-master buffer (for terminal emulator)
|
||||||
|
if ring_push(pty.slave_to_master, pty.stm_head, pty.stm_tail, b):
|
||||||
|
written += 1
|
||||||
|
|
||||||
|
# Also render to FB terminal
|
||||||
|
term_putc(char(b))
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Render frame after batch write
|
||||||
|
if written > 0:
|
||||||
|
term_render()
|
||||||
|
|
||||||
|
return written
|
||||||
|
|
||||||
|
proc pty_read_slave*(fd: int, data: ptr byte, len: int): int {.exportc, cdecl.} =
|
||||||
|
## Read from slave (input to shell). Gets master input.
|
||||||
|
let pty = get_pty_from_fd(fd)
|
||||||
|
if pty == nil: return -1
|
||||||
|
|
||||||
|
var read_count = 0
|
||||||
|
let buf = cast[ptr UncheckedArray[byte]](data)
|
||||||
|
for i in 0 ..< len:
|
||||||
|
let b = ring_pop(pty.master_to_slave, pty.mts_head, pty.mts_tail)
|
||||||
|
if b < 0: break
|
||||||
|
buf[i] = byte(b)
|
||||||
|
read_count += 1
|
||||||
|
|
||||||
|
# Echo if enabled
|
||||||
|
if pty.echo and pty.mode == lmCanon:
|
||||||
|
discard ring_push(pty.slave_to_master, pty.stm_head, pty.stm_tail, byte(b))
|
||||||
|
term_putc(char(b))
|
||||||
|
|
||||||
|
if read_count > 0 and pty.echo:
|
||||||
|
term_render()
|
||||||
|
|
||||||
|
return read_count
|
||||||
|
|
||||||
|
proc pty_has_data_for_slave*(pty_id: int): bool {.exportc, cdecl.} =
|
||||||
|
## Check if there's input waiting for the slave.
|
||||||
|
if pty_id < 0 or pty_id >= MAX_PTYS: return false
|
||||||
|
if not ptys[pty_id].active: return false
|
||||||
|
return ring_count(ptys[pty_id].mts_head, ptys[pty_id].mts_tail) > 0
|
||||||
|
|
||||||
|
proc pty_push_input*(pty_id: int, ch: char) {.exportc, cdecl.} =
|
||||||
|
## Push a character to the master-to-slave buffer (keyboard input).
|
||||||
|
if pty_id < 0 or pty_id >= MAX_PTYS: return
|
||||||
|
if not ptys[pty_id].active: return
|
||||||
|
discard ring_push(ptys[pty_id].master_to_slave,
|
||||||
|
ptys[pty_id].mts_head,
|
||||||
|
ptys[pty_id].mts_tail,
|
||||||
|
byte(ch))
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
## Rumpk Layer 1: The Reactive Dispatcher (The Tyrant)
|
## Rumpk Layer 1: The Reactive Dispatcher (The Tyrant)
|
||||||
##
|
##
|
||||||
## Implements the Silence Doctrine (SPEC-250).
|
## Implements the Silence Doctrine (SPEC-102).
|
||||||
## - No Tick.
|
## - No Tick.
|
||||||
## - No Policy.
|
## - No Policy.
|
||||||
## - Only Physics.
|
## - Only Physics.
|
||||||
|
|
@ -36,30 +36,36 @@ import fiber
|
||||||
|
|
||||||
# To avoid circular imports, kernel.nim will likely INCLUDE sched.nim or sched.nim
|
# To avoid circular imports, kernel.nim will likely INCLUDE sched.nim or sched.nim
|
||||||
# will act on a passed context.
|
# will act on a passed context.
|
||||||
# BUT, SPEC-250 implies sched.nim *is* the logic.
|
# BUT, SPEC-102 implies sched.nim *is* the logic.
|
||||||
#
|
#
|
||||||
# Let's define the Harmonic logic.
|
# Let's define the Harmonic logic.
|
||||||
# We need access to `current_fiber` (from fiber.nim) and `get_now_ns` (helper).
|
# We need access to `current_fiber` (from fiber.nim) and `get_now_ns` (helper).
|
||||||
|
|
||||||
proc sched_get_now_ns*(): uint64 {.importc: "get_now_ns", cdecl.}
|
proc sched_get_now_ns*(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}
|
||||||
|
|
||||||
|
# Forward declaration for channel data check (provided by kernel/channels)
|
||||||
|
proc fiber_can_run_on_channels*(id: uint64, mask: uint64): bool {.importc, cdecl.}
|
||||||
|
|
||||||
|
proc is_runnable(f: ptr FiberObject, now: uint64): bool =
|
||||||
|
if f == nil or f.state.sp == 0: return false # Can only run initialized fibers
|
||||||
|
if now < f.sleep_until: return false
|
||||||
|
if f.is_blocked:
|
||||||
|
if fiber_can_run_on_channels(f.id, f.blocked_on_mask):
|
||||||
|
f.is_blocked = false # Latched unblock
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
# Forward declaration for the tick function
|
|
||||||
# Returns TRUE if a fiber was switched to (work done/found).
|
|
||||||
# Returns FALSE if the system should sleep (WFI).
|
|
||||||
proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
let now = sched_get_now_ns()
|
let now = sched_get_now_ns()
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Phase 1: PHOTON (Hard Real-Time / Hardware Driven)
|
# Phase 1: PHOTON (Hard Real-Time / Hardware Driven)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# - V-Sync (Compositor)
|
|
||||||
# - Audio Mix
|
|
||||||
# - Network Polling (War Mode)
|
|
||||||
|
|
||||||
var run_photon = false
|
var run_photon = false
|
||||||
for f in fibers:
|
for f in fibers:
|
||||||
if f != nil and f.getSpectrum() == Spectrum.Photon:
|
if f != nil and f.getSpectrum() == Spectrum.Photon:
|
||||||
if now >= f.sleep_until:
|
if is_runnable(f, now):
|
||||||
if f != current_fiber:
|
if f != current_fiber:
|
||||||
switch(f); return true
|
switch(f); return true
|
||||||
else:
|
else:
|
||||||
|
|
@ -69,13 +75,10 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Phase 2: MATTER (Interactive / Latency Sensitive)
|
# Phase 2: MATTER (Interactive / Latency Sensitive)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# - Shell
|
|
||||||
# - Editor
|
|
||||||
|
|
||||||
var run_matter = false
|
var run_matter = false
|
||||||
for f in fibers:
|
for f in fibers:
|
||||||
if f != nil and f.getSpectrum() == Spectrum.Matter:
|
if f != nil and f.getSpectrum() == Spectrum.Matter:
|
||||||
if now >= f.sleep_until:
|
if is_runnable(f, now):
|
||||||
if f != current_fiber:
|
if f != current_fiber:
|
||||||
switch(f); return true
|
switch(f); return true
|
||||||
else:
|
else:
|
||||||
|
|
@ -85,13 +88,10 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Phase 3: GRAVITY (Throughput / Background)
|
# Phase 3: GRAVITY (Throughput / Background)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# - Compiler
|
|
||||||
# - Ledger Sync
|
|
||||||
|
|
||||||
var run_gravity = false
|
var run_gravity = false
|
||||||
for f in fibers:
|
for f in fibers:
|
||||||
if f != nil and f.getSpectrum() == Spectrum.Gravity:
|
if f != nil and f.getSpectrum() == Spectrum.Gravity:
|
||||||
if now >= f.sleep_until:
|
if is_runnable(f, now):
|
||||||
if f != current_fiber:
|
if f != current_fiber:
|
||||||
switch(f); return true
|
switch(f); return true
|
||||||
else:
|
else:
|
||||||
|
|
@ -101,12 +101,9 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Phase 4: VOID (Scavenger)
|
# Phase 4: VOID (Scavenger)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# - Untrusted Code
|
|
||||||
# - Speculative Execution
|
|
||||||
|
|
||||||
for f in fibers:
|
for f in fibers:
|
||||||
if f != nil and f.getSpectrum() == Spectrum.Void:
|
if f != nil and f.getSpectrum() == Spectrum.Void:
|
||||||
if now >= f.sleep_until:
|
if is_runnable(f, now):
|
||||||
if f != current_fiber:
|
if f != current_fiber:
|
||||||
switch(f)
|
switch(f)
|
||||||
return true
|
return true
|
||||||
|
|
@ -119,6 +116,17 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
||||||
# If we reached here, NO fiber is runnable.
|
# If we reached here, NO fiber is runnable.
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
proc sched_get_next_wakeup*(fibers: openArray[ptr FiberObject]): uint64 =
|
||||||
|
var min_wakeup: uint64 = 0xFFFFFFFFFFFFFFFF'u64
|
||||||
|
let now = sched_get_now_ns()
|
||||||
|
|
||||||
|
for f in fibers:
|
||||||
|
if f != nil and f.sleep_until > now:
|
||||||
|
if f.sleep_until < min_wakeup:
|
||||||
|
min_wakeup = f.sleep_until
|
||||||
|
|
||||||
|
return min_wakeup
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# THE RATCHET (Post-Execution Analysis)
|
# THE RATCHET (Post-Execution Analysis)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
proc main() =
|
||||||
|
discard
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
# SPDX-License-Identifier: LSL-1.0
|
||||||
|
# Copyright (c) 2026 Markus Maiwald
|
||||||
|
# Stewardship: Self Sovereign Society Foundation
|
||||||
|
#
|
||||||
|
# This file is part of the Nexus Sovereign Core.
|
||||||
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
|
# SPEC-093: UTCP Protocol Implementation
|
||||||
|
# Sovereign Transport for Intra-Cluster Communication
|
||||||
|
|
||||||
|
# Import C decls for kernel logging
|
||||||
|
proc kprint(s: cstring) {.importc, cdecl.}
|
||||||
|
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||||
|
proc kprint_hex(n: uint64) {.importc, cdecl.}
|
||||||
|
|
||||||
|
# --- Protocol Constants ---
|
||||||
|
|
||||||
|
const
|
||||||
|
ETHERTYPE_UTCP* = 0x88B5'u16
|
||||||
|
|
||||||
|
# Flags
|
||||||
|
UTCP_FLAG_SYN* = 0x01'u8
|
||||||
|
UTCP_FLAG_ACK* = 0x02'u8
|
||||||
|
UTCP_FLAG_NACK* = 0x04'u8
|
||||||
|
UTCP_FLAG_FIN* = 0x08'u8
|
||||||
|
UTCP_FLAG_DATA* = 0x10'u8
|
||||||
|
|
||||||
|
# --- Types ---
|
||||||
|
|
||||||
|
type
|
||||||
|
CellID* = object
|
||||||
|
## 128-bit SipHash result representing a Node Identity
|
||||||
|
lo*: uint64
|
||||||
|
hi*: uint64
|
||||||
|
|
||||||
|
UtcpHeader* {.packed.} = object
|
||||||
|
## 32-Byte Fixed Header (SPEC-093)
|
||||||
|
eth_type*: uint16 # 0x88B5 (Big Endian)
|
||||||
|
flags*: uint8
|
||||||
|
reserved*: uint8
|
||||||
|
target_id*: CellID # 16 bytes
|
||||||
|
sender_id*: CellID # 16 bytes
|
||||||
|
seq_num*: uint64 # 8 bytes (Big Endian)
|
||||||
|
payload_len*: uint16 # 2 bytes (Big Endian)
|
||||||
|
# Total = 46 bytes.
|
||||||
|
# 46 bytes + 14 byte Eth header = 60 bytes minimum frame size.
|
||||||
|
|
||||||
|
UtcpState* = enum
|
||||||
|
CLOSED, LISTEN, SYN_SENT, SYN_RCVD, ESTABLISHED, FIN_WAIT
|
||||||
|
|
||||||
|
UtcpControlBlock* = object
|
||||||
|
state*: UtcpState
|
||||||
|
local_id*: CellID
|
||||||
|
remote_id*: CellID
|
||||||
|
local_seq*: uint64
|
||||||
|
remote_seq*: uint64
|
||||||
|
last_ack*: uint64
|
||||||
|
|
||||||
|
const MAX_CONNECTIONS = 16
|
||||||
|
var utcp_pcb_table: array[MAX_CONNECTIONS, UtcpControlBlock]
|
||||||
|
|
||||||
|
# --- Helper Functions ---
|
||||||
|
|
||||||
|
proc ntohs(n: uint16): uint16 {.inline.} =
|
||||||
|
return (n shr 8) or (n shl 8)
|
||||||
|
|
||||||
|
proc ntohll(n: uint64): uint64 {.inline.} =
|
||||||
|
var b = cast[array[8, byte]](n)
|
||||||
|
# Reverse bytes
|
||||||
|
return (uint64(b[0]) shl 56) or (uint64(b[1]) shl 48) or
|
||||||
|
(uint64(b[2]) shl 40) or (uint64(b[3]) shl 32) or
|
||||||
|
(uint64(b[4]) shl 24) or (uint64(b[5]) shl 16) or
|
||||||
|
(uint64(b[6]) shl 8) or uint64(b[7])
|
||||||
|
|
||||||
|
proc htonll(n: uint64): uint64 {.inline.} =
|
||||||
|
return ntohll(n) # Symmetric
|
||||||
|
|
||||||
|
proc cellid_eq(a, b: CellID): bool =
|
||||||
|
return a.lo == b.lo and a.hi == b.hi
|
||||||
|
|
||||||
|
proc utcp_find_pcb(remote_id: CellID): ptr UtcpControlBlock =
|
||||||
|
for i in 0 ..< MAX_CONNECTIONS:
|
||||||
|
if utcp_pcb_table[i].state != CLOSED and cellid_eq(utcp_pcb_table[i].remote_id, remote_id):
|
||||||
|
return addr utcp_pcb_table[i]
|
||||||
|
return nil
|
||||||
|
|
||||||
|
proc utcp_alloc_pcb(): ptr UtcpControlBlock =
|
||||||
|
for i in 0 ..< MAX_CONNECTIONS:
|
||||||
|
if utcp_pcb_table[i].state == CLOSED:
|
||||||
|
return addr utcp_pcb_table[i]
|
||||||
|
return nil
|
||||||
|
|
||||||
|
# --- Logic ---
|
||||||
|
|
||||||
|
proc utcp_handle_packet*(data: ptr UncheckedArray[byte], len: uint16) {.exportc, cdecl.} =
|
||||||
|
## Handle raw UTCP frame (stripped of UDP/IP headers if tunnelled)
|
||||||
|
|
||||||
|
if len < uint16(sizeof(UtcpHeader)):
|
||||||
|
kprintln("[UTCP] Drop: Frame too short")
|
||||||
|
return
|
||||||
|
|
||||||
|
let header = cast[ptr UtcpHeader](data)
|
||||||
|
|
||||||
|
# Validate Magic
|
||||||
|
if ntohs(header.eth_type) != ETHERTYPE_UTCP:
|
||||||
|
# Allow 0x88B5 for now, but log if mismatch
|
||||||
|
discard
|
||||||
|
|
||||||
|
let seq_num = ntohll(header.seq_num)
|
||||||
|
let flags = header.flags
|
||||||
|
|
||||||
|
# Log Packet
|
||||||
|
kprint("[UTCP] RX Seq="); kprint_hex(seq_num);
|
||||||
|
kprint(" Flags="); kprint_hex(uint64(flags)); kprintln("")
|
||||||
|
|
||||||
|
# State Machine
|
||||||
|
var pcb = utcp_find_pcb(header.sender_id)
|
||||||
|
|
||||||
|
if pcb == nil:
|
||||||
|
# New Connection?
|
||||||
|
if (flags and UTCP_FLAG_SYN) != 0:
|
||||||
|
kprintln("[UTCP] New SYN received")
|
||||||
|
pcb = utcp_alloc_pcb()
|
||||||
|
if pcb != nil:
|
||||||
|
pcb.state = SYN_RCVD
|
||||||
|
pcb.remote_id = header.sender_id
|
||||||
|
pcb.local_id = header.target_id
|
||||||
|
pcb.remote_seq = seq_num
|
||||||
|
pcb.local_seq = 1000 # Randomize?
|
||||||
|
kprintln("[UTCP] State -> SYN_RCVD. Sending SYN-ACK (TODO)")
|
||||||
|
# TODO: Send SYN-ACK
|
||||||
|
else:
|
||||||
|
kprintln("[UTCP] Drop: Table full")
|
||||||
|
else:
|
||||||
|
kprintln("[UTCP] Drop: Packet for unknown connection")
|
||||||
|
return
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Existing Connection
|
||||||
|
kprint("[UTCP] Match PCB. State="); kprint_hex(uint64(pcb.state)); kprintln("")
|
||||||
|
|
||||||
|
case pcb.state:
|
||||||
|
of SYN_RCVD:
|
||||||
|
if (flags and UTCP_FLAG_ACK) != 0:
|
||||||
|
pcb.state = ESTABLISHED
|
||||||
|
kprintln("[UTCP] State -> ESTABLISHED")
|
||||||
|
of ESTABLISHED:
|
||||||
|
if (flags and UTCP_FLAG_DATA) != 0:
|
||||||
|
kprintln("[UTCP] Data received")
|
||||||
|
# TODO: Enqueue data
|
||||||
|
elif (flags and UTCP_FLAG_FIN) != 0:
|
||||||
|
pcb.state = CLOSED # Simplify for now
|
||||||
|
kprintln("[UTCP] Connection-Teardown (FIN)")
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Nexus Network Membrane (Grafted LwIP)
|
||||||
|
|
||||||
|
**Status:** Experimental / Grafted (Phase 1)
|
||||||
|
**Version:** v0.1 (Hybrid Polling)
|
||||||
|
**Location:** `core/rumpk/libs/membrane`
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Network Membrane is a userland networking stack running inside the `init` process (Subject Zero). It provides TCP/IP capabilities to the Nexus Sovereign Core by "grafting" the lightweight IP (LwIP) stack onto the Nexus ION (Input/Output Nexus) ring architecture.
|
||||||
|
|
||||||
|
This implementation follows **SPEC-017 (The Network Membrane)** and **SPEC-701 (The Sovereign Network)**.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### 1. The Graft (LwIP Integration)
|
||||||
|
Nexus avoids writing a TCP/IP stack from scratch for Phase 1. Instead, we compile LwIP as a static library (`libnexus.a`) linked into the userland payload.
|
||||||
|
* **Mode:** `NO_SYS` (No OS threads). LwIP is driven by a single event loop.
|
||||||
|
* **Memory:** Static buffers (Pbufs) managed by `ion_client`.
|
||||||
|
|
||||||
|
### 2. The Glue (`net_glue.nim`)
|
||||||
|
Bridging Nim userland and C LwIP:
|
||||||
|
* **`pump_membrane_stack()`**: The heartbeat function. It must be called repeatedly by the main loop. It:
|
||||||
|
* Checks `sys_now()` for timer expiration (DHCP fine/coarse, TCP fast/slow).
|
||||||
|
* Polls `ion_net_rx` for inbound packets from the Kernel (NetSwitch).
|
||||||
|
* Injects packets into `netif->input`.
|
||||||
|
* **`ion_linkoutput`**: The LwIP callback to send packets. Uses `ion_net_tx` to push packets to the Kernel.
|
||||||
|
|
||||||
|
### 3. Syscall Interface
|
||||||
|
LwIP requires system services provided via `libc.nim` and the `SysTable`:
|
||||||
|
* **`sys_now()`**: Returns monotonic time in milliseconds using `rdtime` (via `syscall_get_time_ns`).
|
||||||
|
* **`printf/abort`**: Mapped to `console_write` syscalls.
|
||||||
|
|
||||||
|
## Current Limitations (v1.1.1)
|
||||||
|
|
||||||
|
### 1. The "Busy Wait" Workaround
|
||||||
|
**Issue:** The kernel Scheduler currently lacks a hardware Timer Driver for `wfi` (Wait For Interrupt).
|
||||||
|
**Symptom:** Calling `nanosleep` (0x65) puts the fiber to sleep forever because no timer interrupt wakes the CPU.
|
||||||
|
**Workaround:** `init.nim` uses a busy-wait loop (`while sys_now() - start < 10: yield()`). This keeps the network stack responsive but results in high CPU usage.
|
||||||
|
**Fix Planned:** Implement ACLINT/SBI Timer driver in HAL.
|
||||||
|
|
||||||
|
### 2. No IP Acquisition (Ingress)
|
||||||
|
**Issue:** While Egress (DHCP DISCOVER) works and is verified, no Ingress packets (DHCP OFFER) are received.
|
||||||
|
**Suspected Cause:** VirtIO interrupts might be masked or not delegated correctly, preventing `NetSwitch` from seeing inbound traffic.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The stack is initialized automatically by `init`:
|
||||||
|
```nim
|
||||||
|
import libs/membrane/net_glue
|
||||||
|
|
||||||
|
membrane_init()
|
||||||
|
while true:
|
||||||
|
pump_membrane_stack()
|
||||||
|
# Sleep/Yield
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logs & Debugging
|
||||||
|
* **Egress:** grep for `[Membrane] Egress Packet`
|
||||||
|
* **Timers:** grep for `[Membrane] DHCP Fine Timer`
|
||||||
|
* **Packet Dump:** Enable `LWIP_DEBUG` in `lwipopts.h` (requires recompile).
|
||||||
59
hal/abi.zig
59
hal/abi.zig
|
|
@ -71,14 +71,59 @@ export fn rumpk_pfree(ptr: *anyopaque) void {
|
||||||
hal.pfree(ptr);
|
hal.pfree(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn rumpk_halt() noreturn {
|
// export fn rumpk_halt() noreturn {
|
||||||
hal.halt();
|
// hal.halt();
|
||||||
}
|
// }
|
||||||
|
|
||||||
var mock_ticks: u64 = 0;
|
var mock_ticks: u64 = 0;
|
||||||
|
|
||||||
export fn rumpk_timer_now_ns() u64 {
|
// export fn rumpk_timer_now_ns() u64 {
|
||||||
// Phase 1 Mock: Incrementing counter to simulate time passage per call
|
// // Phase 1 Mock: Incrementing counter to simulate time passage per call
|
||||||
mock_ticks += 100000; // 100us per call
|
// mock_ticks += 100000; // 100us per call
|
||||||
return mock_ticks;
|
// return mock_ticks;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Ground Zero Phase 1: CSpace Integration (SPEC-020)
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
pub const cspace = @import("cspace.zig");
|
||||||
|
|
||||||
|
// Re-export CSpace functions for Nim FFI
|
||||||
|
pub const cspace_init = cspace.cspace_init;
|
||||||
|
pub const cspace_get = cspace.cspace_get;
|
||||||
|
pub const cspace_grant_cap = cspace.cspace_grant_cap;
|
||||||
|
pub const cspace_lookup = cspace.cspace_lookup;
|
||||||
|
pub const cspace_revoke = cspace.cspace_revoke;
|
||||||
|
pub const cspace_check_perm = cspace.cspace_check_perm;
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Force Compilation of Stubs & Runtime
|
||||||
|
// =========================================================
|
||||||
|
// =========================================================
|
||||||
|
// Force Compilation of Stubs & Runtime
|
||||||
|
// =========================================================
|
||||||
|
// =========================================================
|
||||||
|
// Force Compilation of Stubs & Runtime
|
||||||
|
// =========================================================
|
||||||
|
// =========================================================
|
||||||
|
// Force Compilation of Stubs & Runtime
|
||||||
|
// =========================================================
|
||||||
|
// =========================================================
|
||||||
|
pub const surface = @import("surface.zig");
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
// Force analysis
|
||||||
|
_ = @import("stubs.zig");
|
||||||
|
_ = @import("mm.zig");
|
||||||
|
_ = @import("channel.zig");
|
||||||
|
_ = @import("uart.zig");
|
||||||
|
_ = @import("virtio_block.zig");
|
||||||
|
_ = @import("virtio_net.zig");
|
||||||
|
_ = @import("virtio_pci.zig");
|
||||||
|
_ = @import("ontology.zig");
|
||||||
|
_ = @import("entry_riscv.zig");
|
||||||
|
_ = @import("cspace.zig");
|
||||||
|
_ = @import("surface.zig");
|
||||||
|
_ = @import("initrd.zig");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,12 @@ cpu_switch_to:
|
||||||
sd s9, 96(sp)
|
sd s9, 96(sp)
|
||||||
sd s10, 104(sp)
|
sd s10, 104(sp)
|
||||||
sd s11, 112(sp)
|
sd s11, 112(sp)
|
||||||
|
csrr t0, sscratch
|
||||||
|
sd t0, 120(sp)
|
||||||
sd sp, 0(a0)
|
sd sp, 0(a0)
|
||||||
mv sp, a1
|
mv sp, a1
|
||||||
|
ld t0, 120(sp)
|
||||||
|
csrw sscratch, t0
|
||||||
ld ra, 0(sp)
|
ld ra, 0(sp)
|
||||||
ld gp, 8(sp)
|
ld gp, 8(sp)
|
||||||
ld tp, 16(sp)
|
ld tp, 16(sp)
|
||||||
|
|
@ -31,7 +35,6 @@ cpu_switch_to:
|
||||||
ld s1, 32(sp)
|
ld s1, 32(sp)
|
||||||
ld s2, 40(sp)
|
ld s2, 40(sp)
|
||||||
ld s3, 48(sp)
|
ld s3, 48(sp)
|
||||||
sd s4, 56(sp)
|
|
||||||
ld s4, 56(sp)
|
ld s4, 56(sp)
|
||||||
ld s5, 64(sp)
|
ld s5, 64(sp)
|
||||||
ld s6, 72(sp)
|
ld s6, 72(sp)
|
||||||
|
|
@ -63,36 +66,36 @@ rumpk_yield_guard:
|
||||||
.global rumpk_enter_userland
|
.global rumpk_enter_userland
|
||||||
.type rumpk_enter_userland, @function
|
.type rumpk_enter_userland, @function
|
||||||
|
|
||||||
# void rumpk_enter_userland(uint64_t entry);
|
# void rumpk_enter_userland(uint64_t entry, uint64_t argc, uint64_t argv, uint64_t sp);
|
||||||
# a0 = entry
|
# a0 = entry, a1 = argc, a2 = argv, a3 = sp
|
||||||
rumpk_enter_userland:
|
rumpk_enter_userland:
|
||||||
# 🏛️ PIVOT TO USER MODE (Preserving Hart State)
|
# 🏛️ PIVOT TO USER MODE (C-ABI Handover)
|
||||||
|
|
||||||
# 1. Set sepc = entry (a0)
|
# 1. Prepare Program Counter
|
||||||
csrw sepc, a0
|
csrw sepc, a0
|
||||||
|
|
||||||
# 2. Configure sstatus for U-mode transition
|
# 2. Prepare Stack and sscratch
|
||||||
# - SPP (Previous Privilege Level) = 0 (User) - Bits 8
|
# sscratch MUST contain the Kernel Stack for the trap handler
|
||||||
# - SPIE (Previous Interrupt Enable) = 1 (Enable Interrupts on sret) - Bit 5
|
csrw sscratch, sp
|
||||||
# - SUM (Supervisor User Memory) - PRESERVE (Already set in kmain)
|
mv sp, a3
|
||||||
|
|
||||||
# Clear SPP bit (bit 8) -> Return to User Mode
|
# 3. Prepare Arguments (argc, argv)
|
||||||
|
mv t0, a1 # Temporarily store argc
|
||||||
|
mv a1, a2 # a1 = argv
|
||||||
|
mv a0, t0 # a0 = argc
|
||||||
|
|
||||||
|
# 4. Configure sstatus for U-mode transition
|
||||||
li t0, (1 << 8)
|
li t0, (1 << 8)
|
||||||
csrc sstatus, t0
|
csrc sstatus, t0 # Clear SPP (User)
|
||||||
|
|
||||||
# Enable SPIE bit (bit 5) -> Enable Interrupts on sret
|
|
||||||
li t0, (1 << 5)
|
li t0, (1 << 5)
|
||||||
csrs sstatus, t0
|
csrs sstatus, t0 # Enable SPIE
|
||||||
|
|
||||||
# 🔧 CRITICAL FIX: Set SUM bit (bit 18) to allow Kernel access to U=1 pages (UART, etc.)
|
|
||||||
li t0, (1 << 18)
|
li t0, (1 << 18)
|
||||||
csrs sstatus, t0
|
csrs sstatus, t0 # Enable SUM (Supervisor User Memory)
|
||||||
|
|
||||||
# 2.5 Synchronize Instruction Cache (Critical for newly loaded code)
|
# 5. Flush Caches
|
||||||
fence.i
|
fence.i
|
||||||
|
|
||||||
# 🔧 CRITICAL FIX: Set sscratch to Kernel Stack (sp)
|
# 6. The Leap of Faith
|
||||||
csrw sscratch, sp
|
|
||||||
|
|
||||||
# 3. Use sret to transit to U-mode
|
|
||||||
sret
|
sret
|
||||||
|
|
|
||||||
|
|
@ -96,11 +96,17 @@ export fn hal_channel_pop(handle: u64, out_pkt: *IonPacket) bool {
|
||||||
export fn hal_cmd_push(handle: u64, pkt: CmdPacket) bool {
|
export fn hal_cmd_push(handle: u64, pkt: CmdPacket) bool {
|
||||||
validate_ring_ptr(handle);
|
validate_ring_ptr(handle);
|
||||||
const ring: *Ring(CmdPacket) = @ptrFromInt(handle);
|
const ring: *Ring(CmdPacket) = @ptrFromInt(handle);
|
||||||
|
// uart.print("[HAL] Pushing CMD to "); uart.print_hex(handle); uart.print("\n");
|
||||||
return pushGeneric(CmdPacket, ring, pkt);
|
return pushGeneric(CmdPacket, ring, pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn hal_cmd_pop(handle: u64, out_pkt: *CmdPacket) bool {
|
export fn hal_cmd_pop(handle: u64, out_pkt: *CmdPacket) bool {
|
||||||
validate_ring_ptr(handle);
|
validate_ring_ptr(handle);
|
||||||
const ring: *Ring(CmdPacket) = @ptrFromInt(handle);
|
const ring: *Ring(CmdPacket) = @ptrFromInt(handle);
|
||||||
|
// uart.print("[HAL] Popping CMD from "); uart.print_hex(handle); uart.print("\n");
|
||||||
return popGeneric(CmdPacket, ring, out_pkt);
|
return popGeneric(CmdPacket, ring, out_pkt);
|
||||||
}
|
}
|
||||||
|
// Stub for term.nim compatibility
|
||||||
|
export fn fiber_can_run_on_channels() bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export fn hal_crypto_ed25519_verify(sig: *const [64]u8, msg: [*]const u8, msg_le
|
||||||
}
|
}
|
||||||
|
|
||||||
/// BLAKE3 Hash (256-bit) for key derivation
|
/// BLAKE3 Hash (256-bit) for key derivation
|
||||||
/// Used by Monolith (SPEC-021) to derive VolumeKey from 4MB keyfile
|
/// Used by Monolith (SPEC-503) to derive VolumeKey from 4MB keyfile
|
||||||
export fn hal_crypto_blake3(data: [*]const u8, len: usize, out: *[32]u8) void {
|
export fn hal_crypto_blake3(data: [*]const u8, len: usize, out: *[32]u8) void {
|
||||||
var hasher = std.crypto.hash.Blake3.init(.{});
|
var hasher = std.crypto.hash.Blake3.init(.{});
|
||||||
hasher.update(data[0..len]);
|
hasher.update(data[0..len]);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,305 @@
|
||||||
|
// SPEC-020: Capability Space (CSpace) Implementation
|
||||||
|
// Component: core/security/cspace
|
||||||
|
// Target: Ground Zero - Phase 1
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// CapType: Closed enumeration of capability types (SPEC-020)
|
||||||
|
pub const CapType = enum(u8) {
|
||||||
|
Null = 0,
|
||||||
|
Entity = 1, // Control over Process/Fiber
|
||||||
|
Channel = 2, // Access to ION Ring
|
||||||
|
Memory = 3, // Access to Physical Frame
|
||||||
|
Interrupt = 4, // IRQ mask/unmask control
|
||||||
|
Time = 5, // Clock/Timer access
|
||||||
|
Entropy = 6, // HWRNG access
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Permission flags (SPEC-020)
|
||||||
|
pub const CapPerms = packed struct(u8) {
|
||||||
|
read: bool = false,
|
||||||
|
write: bool = false,
|
||||||
|
execute: bool = false,
|
||||||
|
map: bool = false,
|
||||||
|
delegate: bool = false,
|
||||||
|
revoke: bool = false,
|
||||||
|
copy: bool = false,
|
||||||
|
spawn: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Capability structure (32 bytes, cache-line aligned)
|
||||||
|
pub const Capability = packed struct {
|
||||||
|
cap_type: CapType, // 1 byte
|
||||||
|
perms: CapPerms, // 1 byte
|
||||||
|
_reserved: u16, // 2 bytes (alignment)
|
||||||
|
object_id: u64, // 8 bytes (SipHash of resource)
|
||||||
|
bounds_start: u64, // 8 bytes
|
||||||
|
bounds_end: u64, // 8 bytes
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
if (@sizeOf(Capability) != 32) {
|
||||||
|
@compileError("Capability must be exactly 32 bytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a null capability
|
||||||
|
pub fn null_cap() Capability {
|
||||||
|
return .{
|
||||||
|
.cap_type = .Null,
|
||||||
|
.perms = .{},
|
||||||
|
._reserved = 0,
|
||||||
|
.object_id = 0,
|
||||||
|
.bounds_start = 0,
|
||||||
|
.bounds_end = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if capability is null
|
||||||
|
pub fn is_null(self: *const Capability) bool {
|
||||||
|
return self.cap_type == .Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate bounds
|
||||||
|
pub fn check_bounds(self: *const Capability, addr: u64) bool {
|
||||||
|
if (self.is_null()) return false;
|
||||||
|
return addr >= self.bounds_start and addr < self.bounds_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check permission
|
||||||
|
pub fn has_perm(self: *const Capability, perm: CapPerms) bool {
|
||||||
|
const self_bits = @as(u8, @bitCast(self.perms));
|
||||||
|
const perm_bits = @as(u8, @bitCast(perm));
|
||||||
|
return (self_bits & perm_bits) == perm_bits;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// CSpace: Per-fiber capability table
|
||||||
|
pub const CSPACE_SIZE = 64; // Maximum capabilities per fiber
|
||||||
|
|
||||||
|
pub const CSpace = struct {
|
||||||
|
slots: [CSPACE_SIZE]Capability,
|
||||||
|
epoch: u32, // For revocation
|
||||||
|
fiber_id: u64, // Owner fiber
|
||||||
|
_padding: u32, // Alignment
|
||||||
|
|
||||||
|
/// Initialize empty CSpace
|
||||||
|
pub fn init(fiber_id: u64) CSpace {
|
||||||
|
var cs = CSpace{
|
||||||
|
.slots = undefined,
|
||||||
|
.epoch = 0,
|
||||||
|
.fiber_id = fiber_id,
|
||||||
|
._padding = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize all slots to Null
|
||||||
|
for (&cs.slots) |*slot| {
|
||||||
|
slot.* = Capability.null_cap();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find first empty slot
|
||||||
|
pub fn find_empty_slot(self: *CSpace) ?usize {
|
||||||
|
for (&self.slots, 0..) |*cap, i| {
|
||||||
|
if (cap.is_null()) return i;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Grant capability (insert into CSpace)
|
||||||
|
pub fn grant(self: *CSpace, cap: Capability) !usize {
|
||||||
|
const slot = self.find_empty_slot() orelse return error.CSpaceFull;
|
||||||
|
self.slots[slot] = cap;
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup capability by slot index
|
||||||
|
pub fn lookup(self: *const CSpace, slot: usize) ?*const Capability {
|
||||||
|
if (slot >= CSPACE_SIZE) return null;
|
||||||
|
const cap = &self.slots[slot];
|
||||||
|
if (cap.is_null()) return null;
|
||||||
|
return cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Revoke capability (set to Null)
|
||||||
|
pub fn revoke(self: *CSpace, slot: usize) void {
|
||||||
|
if (slot >= CSPACE_SIZE) return;
|
||||||
|
self.slots[slot] = Capability.null_cap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Revoke all capabilities (epoch-based)
|
||||||
|
pub fn revoke_all(self: *CSpace) void {
|
||||||
|
for (&self.slots) |*cap| {
|
||||||
|
cap.* = Capability.null_cap();
|
||||||
|
}
|
||||||
|
self.epoch +%= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delegate capability (Move or Copy)
|
||||||
|
pub fn delegate(
|
||||||
|
self: *CSpace,
|
||||||
|
slot: usize,
|
||||||
|
target: *CSpace,
|
||||||
|
move: bool,
|
||||||
|
) !usize {
|
||||||
|
const cap = self.lookup(slot) orelse return error.InvalidCapability;
|
||||||
|
|
||||||
|
// Check DELEGATE permission
|
||||||
|
if (!cap.has_perm(.{ .delegate = true })) {
|
||||||
|
return error.NotDelegatable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grant to target
|
||||||
|
const new_slot = try target.grant(cap.*);
|
||||||
|
|
||||||
|
// If move (not copy), revoke from source
|
||||||
|
if (move or !cap.has_perm(.{ .copy = true })) {
|
||||||
|
self.revoke(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_slot;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Global CSpace table (one per fiber)
|
||||||
|
/// This will be integrated with Fiber Control Block in kernel.nim
|
||||||
|
pub const MAX_FIBERS = 16;
|
||||||
|
var global_cspaces: [MAX_FIBERS]CSpace = undefined;
|
||||||
|
var cspaces_initialized: bool = false;
|
||||||
|
|
||||||
|
/// Initialize global CSpace table
|
||||||
|
pub export fn cspace_init() void {
|
||||||
|
if (cspaces_initialized) return;
|
||||||
|
|
||||||
|
for (&global_cspaces, 0..) |*cs, i| {
|
||||||
|
cs.* = CSpace.init(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
cspaces_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get CSpace for fiber
|
||||||
|
pub export fn cspace_get(fiber_id: u64) ?*CSpace {
|
||||||
|
if (!cspaces_initialized) return null;
|
||||||
|
if (fiber_id >= MAX_FIBERS) return null;
|
||||||
|
return &global_cspaces[fiber_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Grant capability to fiber (C ABI)
|
||||||
|
pub export fn cspace_grant_cap(
|
||||||
|
fiber_id: u64,
|
||||||
|
cap_type: u8,
|
||||||
|
perms: u8,
|
||||||
|
object_id: u64,
|
||||||
|
bounds_start: u64,
|
||||||
|
bounds_end: u64,
|
||||||
|
) i32 {
|
||||||
|
const cs = cspace_get(fiber_id) orelse return -1;
|
||||||
|
|
||||||
|
const cap = Capability{
|
||||||
|
.cap_type = @enumFromInt(cap_type),
|
||||||
|
.perms = @bitCast(perms),
|
||||||
|
._reserved = 0,
|
||||||
|
.object_id = object_id,
|
||||||
|
.bounds_start = bounds_start,
|
||||||
|
.bounds_end = bounds_end,
|
||||||
|
};
|
||||||
|
|
||||||
|
const slot = cs.grant(cap) catch return -1;
|
||||||
|
return @intCast(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup capability (C ABI)
|
||||||
|
pub export fn cspace_lookup(fiber_id: u64, slot: usize) ?*const Capability {
|
||||||
|
const cs = cspace_get(fiber_id) orelse return null;
|
||||||
|
return cs.lookup(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Revoke capability (C ABI)
|
||||||
|
pub export fn cspace_revoke(fiber_id: u64, slot: usize) void {
|
||||||
|
const cs = cspace_get(fiber_id) orelse return;
|
||||||
|
cs.revoke(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check capability permission (C ABI)
|
||||||
|
pub export fn cspace_check_perm(fiber_id: u64, slot: usize, perm_bits: u8) bool {
|
||||||
|
const cs = cspace_get(fiber_id) orelse return false;
|
||||||
|
const cap = cs.lookup(slot) orelse return false;
|
||||||
|
const perm: CapPerms = @bitCast(perm_bits);
|
||||||
|
return cap.has_perm(perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit tests
|
||||||
|
test "Capability creation and validation" {
|
||||||
|
const cap = Capability{
|
||||||
|
.cap_type = .Channel,
|
||||||
|
.perms = .{ .read = true, .write = true },
|
||||||
|
._reserved = 0,
|
||||||
|
.object_id = 0x1234,
|
||||||
|
.bounds_start = 0x1000,
|
||||||
|
.bounds_end = 0x2000,
|
||||||
|
};
|
||||||
|
|
||||||
|
try std.testing.expect(!cap.is_null());
|
||||||
|
try std.testing.expect(cap.check_bounds(0x1500));
|
||||||
|
try std.testing.expect(!cap.check_bounds(0x3000));
|
||||||
|
try std.testing.expect(cap.has_perm(.{ .read = true }));
|
||||||
|
try std.testing.expect(!cap.has_perm(.{ .execute = true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "CSpace operations" {
|
||||||
|
var cs = CSpace.init(42);
|
||||||
|
|
||||||
|
const cap = Capability{
|
||||||
|
.cap_type = .Memory,
|
||||||
|
.perms = .{ .read = true, .write = true, .delegate = true },
|
||||||
|
._reserved = 0,
|
||||||
|
.object_id = 0xABCD,
|
||||||
|
.bounds_start = 0,
|
||||||
|
.bounds_end = 0x1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Grant capability
|
||||||
|
const slot = try cs.grant(cap);
|
||||||
|
try std.testing.expect(slot == 0);
|
||||||
|
|
||||||
|
// Lookup capability
|
||||||
|
const retrieved = cs.lookup(slot).?;
|
||||||
|
try std.testing.expect(retrieved.object_id == 0xABCD);
|
||||||
|
|
||||||
|
// Revoke capability
|
||||||
|
cs.revoke(slot);
|
||||||
|
try std.testing.expect(cs.lookup(slot) == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Delegation" {
|
||||||
|
var cs1 = CSpace.init(1);
|
||||||
|
var cs2 = CSpace.init(2);
|
||||||
|
|
||||||
|
const cap = Capability{
|
||||||
|
.cap_type = .Channel,
|
||||||
|
.perms = .{ .read = true, .delegate = true, .copy = true },
|
||||||
|
._reserved = 0,
|
||||||
|
.object_id = 0x5678,
|
||||||
|
.bounds_start = 0,
|
||||||
|
.bounds_end = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
const slot1 = try cs1.grant(cap);
|
||||||
|
|
||||||
|
// Copy delegation
|
||||||
|
const slot2 = try cs1.delegate(slot1, &cs2, false);
|
||||||
|
|
||||||
|
// Both should have the capability
|
||||||
|
try std.testing.expect(cs1.lookup(slot1) != null);
|
||||||
|
try std.testing.expect(cs2.lookup(slot2) != null);
|
||||||
|
|
||||||
|
// Move delegation
|
||||||
|
var cs3 = CSpace.init(3);
|
||||||
|
const slot3 = try cs2.delegate(slot2, &cs3, true);
|
||||||
|
|
||||||
|
// cs2 should no longer have it
|
||||||
|
try std.testing.expect(cs2.lookup(slot2) == null);
|
||||||
|
try std.testing.expect(cs3.lookup(slot3) != null);
|
||||||
|
}
|
||||||
|
|
@ -14,20 +14,34 @@
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const uart = @import("uart.zig");
|
const uart = @import("uart.zig");
|
||||||
|
// const vm = @import("vm_riscv.zig");
|
||||||
|
const mm = @import("mm.zig");
|
||||||
|
const stubs = @import("stubs.zig"); // Force compile stubs
|
||||||
|
const uart_input = @import("uart_input.zig");
|
||||||
const virtio_net = @import("virtio_net.zig");
|
const virtio_net = @import("virtio_net.zig");
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = stubs;
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Entry Point (Naked)
|
// Entry Point (Naked)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
export fn _start() callconv(.naked) noreturn {
|
export fn riscv_init() callconv(.naked) noreturn {
|
||||||
asm volatile (
|
asm volatile (
|
||||||
// 1. Disable Interrupts
|
// 1. Disable Interrupts
|
||||||
\\ csrw sie, zero
|
\\ csrw sie, zero
|
||||||
\\ csrw satp, zero
|
\\ csrw satp, zero
|
||||||
\\ csrw sscratch, zero
|
\\ csrw sscratch, zero
|
||||||
// 1.1 Enable FPU (sstatus.FS = Initial [01])
|
|
||||||
\\ li t0, 0x2000
|
// PROOF OF LIFE: Raw UART write before ANY initialization
|
||||||
|
\\ li t0, 0x10000000 // UART base address
|
||||||
|
\\ li t1, 0x58 // 'X'
|
||||||
|
\\ sb t1, 0(t0) // Write to THR
|
||||||
|
|
||||||
|
// 1.1 Enable FPU (FS), Vectors (VS), and SUM (Supervisor User Memory Access)
|
||||||
|
\\ li t0, 0x42200 // SUM=bit 18, FS=bit 13, VS=bit 9
|
||||||
\\ csrs sstatus, t0
|
\\ csrs sstatus, t0
|
||||||
|
|
||||||
// 1.2 Initialize Global Pointer
|
// 1.2 Initialize Global Pointer
|
||||||
|
|
@ -60,7 +74,7 @@ export fn _start() callconv(.naked) noreturn {
|
||||||
\\ 1: wfi
|
\\ 1: wfi
|
||||||
\\ j 1b
|
\\ j 1b
|
||||||
);
|
);
|
||||||
unreachable;
|
// unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trap Frame Layout (Packed on stack)
|
// Trap Frame Layout (Packed on stack)
|
||||||
|
|
@ -107,30 +121,34 @@ export fn trap_entry() align(4) callconv(.naked) void {
|
||||||
// 🔧 CRITICAL FIX: Stack Switching (User -> Kernel)
|
// 🔧 CRITICAL FIX: Stack Switching (User -> Kernel)
|
||||||
// Swap sp and sscratch.
|
// Swap sp and sscratch.
|
||||||
// If from User: sp=KStack, sscratch=UStack
|
// If from User: sp=KStack, sscratch=UStack
|
||||||
// If from Kernel: sp=0 (sscratch was 0), sscratch=KStack
|
// If from Kernel: sp=0, sscratch=ValidStack (Problematic logic if not careful)
|
||||||
|
// Correct Logic:
|
||||||
|
// If sscratch == 0: We came from Kernel. sp is already KStack. Do NOTHING to sp.
|
||||||
|
// If sscratch != 0: We came from User. sp is UStack. Swap to get KStack.
|
||||||
|
|
||||||
\\ csrrw sp, sscratch, sp
|
\\ csrrw sp, sscratch, sp
|
||||||
\\ bnez sp, 1f
|
\\ bnez sp, 1f
|
||||||
// Came from Kernel (sp was 0). Restore sp.
|
// Kernel -> Kernel (recursive). Restore sp from sscratch (which had the 0).
|
||||||
\\ csrrw sp, sscratch, sp
|
\\ csrrw sp, sscratch, sp
|
||||||
\\ 1:
|
\\ 1:
|
||||||
|
|
||||||
// Allocate stack (36 words * 8 bytes = 288 bytes)
|
// Allocation (36*8 = 288 bytes)
|
||||||
\\ addi sp, sp, -288
|
\\ addi sp, sp, -288
|
||||||
|
|
||||||
// Save GPRs
|
// Save Registers (GPRs)
|
||||||
\\ sd ra, 0(sp)
|
\\ sd ra, 0(sp)
|
||||||
\\ sd gp, 8(sp)
|
\\ sd gp, 8(sp)
|
||||||
\\ sd tp, 16(sp)
|
\\ sd tp, 16(sp)
|
||||||
\\ sd t0, 24(sp)
|
\\ sd t0, 24(sp)
|
||||||
\\ sd t1, 32(sp)
|
\\ sd t1, 32(sp)
|
||||||
\\ sd t2, 40(sp)
|
\\ sd t2, 40(sp)
|
||||||
\\ sd s0, 48(sp)
|
\\ sd s0, 48(sp)
|
||||||
\\ sd s1, 56(sp)
|
\\ sd s1, 56(sp)
|
||||||
\\ sd a0, 64(sp)
|
\\ sd a0, 64(sp)
|
||||||
\\ sd a1, 72(sp)
|
\\ sd a1, 72(sp)
|
||||||
\\ sd a2, 80(sp)
|
\\ sd a2, 80(sp)
|
||||||
\\ sd a3, 88(sp)
|
\\ sd a3, 88(sp)
|
||||||
\\ sd a4, 96(sp)
|
\\ sd a4, 96(sp)
|
||||||
\\ sd a5, 104(sp)
|
\\ sd a5, 104(sp)
|
||||||
\\ sd a6, 112(sp)
|
\\ sd a6, 112(sp)
|
||||||
\\ sd a7, 120(sp)
|
\\ sd a7, 120(sp)
|
||||||
|
|
@ -169,27 +187,28 @@ export fn trap_entry() align(4) callconv(.naked) void {
|
||||||
\\ mv a0, sp
|
\\ mv a0, sp
|
||||||
\\ call rss_trap_handler
|
\\ call rss_trap_handler
|
||||||
|
|
||||||
// Restore CSRs
|
// Restore CSRs (Optional if modified? sepc changed for syscall)
|
||||||
\\ ld t0, 240(sp)
|
\\ ld t0, 240(sp)
|
||||||
\\ csrw sepc, t0
|
\\ csrw sepc, t0
|
||||||
// We restore sstatus
|
// sstatus often modified to change mode? For return, we use sret.
|
||||||
|
// We might want to restore sstatus if we support nested interrupts properly.
|
||||||
\\ ld t1, 248(sp)
|
\\ ld t1, 248(sp)
|
||||||
\\ csrw sstatus, t1
|
\\ csrw sstatus, t1
|
||||||
|
|
||||||
// Restore GPRs
|
// Restore Encapsulated User Context
|
||||||
\\ ld ra, 0(sp)
|
\\ ld ra, 0(sp)
|
||||||
\\ ld gp, 8(sp)
|
\\ ld gp, 8(sp)
|
||||||
\\ ld tp, 16(sp)
|
\\ ld tp, 16(sp)
|
||||||
\\ ld t0, 24(sp)
|
\\ ld t0, 24(sp)
|
||||||
\\ ld t1, 32(sp)
|
\\ ld t1, 32(sp)
|
||||||
\\ ld t2, 40(sp)
|
\\ ld t2, 40(sp)
|
||||||
\\ ld s0, 48(sp)
|
\\ ld s0, 48(sp)
|
||||||
\\ ld s1, 56(sp)
|
\\ ld s1, 56(sp)
|
||||||
\\ ld a0, 64(sp)
|
\\ ld a0, 64(sp)
|
||||||
\\ ld a1, 72(sp)
|
\\ ld a1, 72(sp)
|
||||||
\\ ld a2, 80(sp)
|
\\ ld a2, 80(sp)
|
||||||
\\ ld a3, 88(sp)
|
\\ ld a3, 88(sp)
|
||||||
\\ ld a4, 96(sp)
|
\\ ld a4, 96(sp)
|
||||||
\\ ld a5, 104(sp)
|
\\ ld a5, 104(sp)
|
||||||
\\ ld a6, 112(sp)
|
\\ ld a6, 112(sp)
|
||||||
\\ ld a7, 120(sp)
|
\\ ld a7, 120(sp)
|
||||||
|
|
@ -210,6 +229,17 @@ export fn trap_entry() align(4) callconv(.naked) void {
|
||||||
|
|
||||||
// Deallocate stack
|
// Deallocate stack
|
||||||
\\ addi sp, sp, 288
|
\\ addi sp, sp, 288
|
||||||
|
|
||||||
|
// 🔧 CRITICAL FIX: Swap back sscratch <-> sp ONLY if returning to User Mode
|
||||||
|
// Check sstatus.SPP (Bit 8). 0 = User, 1 = Supervisor.
|
||||||
|
\\ csrr t0, sstatus
|
||||||
|
\\ li t1, 0x100
|
||||||
|
\\ and t0, t0, t1
|
||||||
|
\\ bnez t0, 2f
|
||||||
|
|
||||||
|
// Returning to User: Swap sp (Kernel Stack) with sscratch (User Stack)
|
||||||
|
\\ csrrw sp, sscratch, sp
|
||||||
|
\\ 2:
|
||||||
\\ sret
|
\\ sret
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -220,37 +250,133 @@ extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize;
|
||||||
extern fn k_handle_exception(scause: usize, sepc: usize, stval: usize) void;
|
extern fn k_handle_exception(scause: usize, sepc: usize, stval: usize) void;
|
||||||
extern fn k_check_deferred_yield() void;
|
extern fn k_check_deferred_yield() void;
|
||||||
|
|
||||||
|
// Memory Management (Page Tables)
|
||||||
|
extern fn mm_get_kernel_satp() u64;
|
||||||
|
extern fn mm_activate_satp(satp_val: u64) void;
|
||||||
|
extern fn k_get_current_satp() u64;
|
||||||
|
|
||||||
|
fn get_sstatus() u64 {
|
||||||
|
return asm volatile ("csrr %[ret], sstatus"
|
||||||
|
: [ret] "=r" (-> u64),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_sum() void {
|
||||||
|
asm volatile ("csrrs zero, sstatus, %[val]"
|
||||||
|
:
|
||||||
|
: [val] "r" (@as(u64, 1 << 18)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global recursion counter
|
||||||
|
var trap_depth: usize = 0;
|
||||||
|
|
||||||
export fn rss_trap_handler(frame: *TrapFrame) void {
|
export fn rss_trap_handler(frame: *TrapFrame) void {
|
||||||
|
// 🔥 CRITICAL: Restore kernel page table IMMEDIATELY on trap entry
|
||||||
|
// const kernel_satp = mm_get_kernel_satp();
|
||||||
|
// if (kernel_satp != 0) {
|
||||||
|
// mm_activate_satp(kernel_satp);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// RECURSION GUARD
|
||||||
|
trap_depth += 1;
|
||||||
|
if (trap_depth > 3) { // Allow some recursion (e.g. syscall -> fault), but prevent infinite loops
|
||||||
|
uart.print("[Trap] Infinite Loop Detected. Halting.\n");
|
||||||
|
while (true) {}
|
||||||
|
}
|
||||||
|
defer trap_depth -= 1;
|
||||||
|
|
||||||
const scause = frame.scause;
|
const scause = frame.scause;
|
||||||
|
|
||||||
// 8: ECALL from U-mode
|
// DEBUG: Diagnose Userland Crash (Only print exceptions, ignore interrupts for noise)
|
||||||
// 9: ECALL from S-mode
|
if ((scause >> 63) == 0) {
|
||||||
if (scause == 8 or scause == 9) {
|
uart.print("\n[Trap] Exception! Cause:");
|
||||||
// Advance PC to skip 'ecall' instruction (4 bytes)
|
uart.print_hex(scause);
|
||||||
frame.sepc += 4;
|
uart.print(" PC:");
|
||||||
|
uart.print_hex(frame.sepc);
|
||||||
// Dispatch Syscall
|
uart.print(" Val:");
|
||||||
const res = k_handle_syscall(frame.a7, frame.a0, frame.a1, frame.a2);
|
uart.print_hex(frame.stval);
|
||||||
|
uart.print("\n");
|
||||||
// Write result back to a0
|
|
||||||
frame.a0 = res;
|
|
||||||
|
|
||||||
// DIAGNOSTIC: Syscall completed
|
|
||||||
uart.print("[Trap] Syscall done, returning to userland\n");
|
|
||||||
|
|
||||||
// uart.puts("[Trap] Checking deferred yield\n");
|
|
||||||
// Check for deferred yield
|
|
||||||
k_check_deferred_yield();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delegate all other exceptions to the Kernel Immune System
|
// Check high bit: 0 = Exception, 1 = Interrupt
|
||||||
// It will decide whether to segregate (worker) or halt (system)
|
if ((scause >> 63) != 0) {
|
||||||
// Note: k_handle_exception handles flow control (yield/halt) and does not return
|
const intr_id = scause & 0x7FFFFFFFFFFFFFFF;
|
||||||
k_handle_exception(scause, frame.sepc, frame.stval);
|
if (intr_id == 9) {
|
||||||
|
// PLIC Context 1 (Supervisor) Claim/Complete Register
|
||||||
|
const PLIC_CLAIM: *volatile u32 = @ptrFromInt(0x0c201004);
|
||||||
|
const irq = PLIC_CLAIM.*;
|
||||||
|
|
||||||
// Safety halt if kernel returns (should be unreachable)
|
if (irq == 10) { // UART0 is IRQ 10 on Virt machine
|
||||||
while (true) {}
|
// uart.print("[IRQ] 10\n");
|
||||||
|
uart_input.poll_input();
|
||||||
|
} else if (irq >= 32 and irq <= 35) {
|
||||||
|
virtio_net.virtio_net_poll();
|
||||||
|
} else if (irq == 0) {
|
||||||
|
// Spurious or no pending interrupt
|
||||||
|
} else {
|
||||||
|
// uart.print("[IRQ] Unknown: ");
|
||||||
|
// uart.print_hex(irq);
|
||||||
|
// uart.print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete the IRQ
|
||||||
|
PLIC_CLAIM.* = irq;
|
||||||
|
} else if (intr_id == 5) {
|
||||||
|
// Timer Interrupt
|
||||||
|
asm volatile ("csrc sip, %[mask]"
|
||||||
|
:
|
||||||
|
: [mask] "r" (@as(u64, 1 << 5)),
|
||||||
|
);
|
||||||
|
k_check_deferred_yield();
|
||||||
|
} else {
|
||||||
|
// uart.print("[Trap] Unhandled Interrupt: ");
|
||||||
|
// uart.print_hex(intr_id);
|
||||||
|
// uart.print("\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// EXCEPTION HANDLING
|
||||||
|
// 8: ECALL from U-mode
|
||||||
|
// 9: ECALL from S-mode
|
||||||
|
if (scause == 8 or scause == 9) {
|
||||||
|
const nr = frame.a7;
|
||||||
|
const a0 = frame.a0;
|
||||||
|
const a1 = frame.a1;
|
||||||
|
const a2 = frame.a2;
|
||||||
|
|
||||||
|
uart.print("[Syscall] NR:");
|
||||||
|
uart.print_hex(nr);
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
|
// Advance PC to avoid re-executing ECALL
|
||||||
|
frame.sepc += 4;
|
||||||
|
|
||||||
|
// Dispatch Sycall
|
||||||
|
const ret = k_handle_syscall(nr, a0, a1, a2);
|
||||||
|
frame.a0 = ret;
|
||||||
|
} else {
|
||||||
|
// Delegate all other exceptions to the Kernel Immune System
|
||||||
|
// This function should NOT return ideally, but if it does, we loop.
|
||||||
|
k_handle_exception(scause, frame.sepc, frame.stval);
|
||||||
|
while (true) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 CRITICAL RETURN PATH: Restore User Page Table if returning to User Mode
|
||||||
|
// We check sstatus.SPP (Supervisor Previous Privilege) - Bit 8
|
||||||
|
// 0 = User, 1 = Supervisor
|
||||||
|
const sstatus = get_sstatus();
|
||||||
|
const spp = (sstatus >> 8) & 1;
|
||||||
|
|
||||||
|
if (spp == 0) {
|
||||||
|
const user_satp = k_get_current_satp();
|
||||||
|
if (user_satp != 0) {
|
||||||
|
// Enable SUM (Supervisor Access User Memory) so we can read the stack
|
||||||
|
// to restore registers (since stack is mapped in User PT)
|
||||||
|
set_sum();
|
||||||
|
mm_activate_satp(user_satp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY(Stack): Memory is immediately used by _start before any read.
|
// SAFETY(Stack): Memory is immediately used by _start before any read.
|
||||||
|
|
@ -260,28 +386,56 @@ export var stack_bytes: [64 * 1024]u8 align(16) = undefined;
|
||||||
const hud = @import("hud.zig");
|
const hud = @import("hud.zig");
|
||||||
extern fn kmain() void;
|
extern fn kmain() void;
|
||||||
extern fn NimMain() void;
|
extern fn NimMain() void;
|
||||||
|
extern fn rumpk_timer_handler() void;
|
||||||
|
|
||||||
export fn zig_entry() void {
|
export fn zig_entry() void {
|
||||||
uart.init_riscv();
|
uart.init_riscv();
|
||||||
|
|
||||||
|
// 🔧 CRITICAL FIX: Enable SUM (Supervisor User Memory) Access
|
||||||
|
// S-mode needs to write to U-mode pages (e.g. loading apps at 0x88000000)
|
||||||
|
// sstatus.SUM is bit 18 (0x40000)
|
||||||
|
asm volatile (
|
||||||
|
\\ li t0, 0x40000
|
||||||
|
\\ csrs sstatus, t0
|
||||||
|
);
|
||||||
|
|
||||||
uart.print("[Rumpk L0] zig_entry reached\n");
|
uart.print("[Rumpk L0] zig_entry reached\n");
|
||||||
uart.print("[Rumpk RISC-V] Handing off to Nim L1...\n");
|
uart.print("[Rumpk RISC-V] Handing off to Nim L1...\n");
|
||||||
_ = virtio_net;
|
|
||||||
|
// Networking is initialized by kmain -> rumpk_net_init
|
||||||
|
|
||||||
NimMain();
|
NimMain();
|
||||||
kmain();
|
kmain();
|
||||||
rumpk_halt();
|
rumpk_halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn console_write(ptr: [*]const u8, len: usize) void {
|
export fn hal_console_write(ptr: [*]const u8, len: usize) void {
|
||||||
uart.write_bytes(ptr[0..len]);
|
uart.write_bytes(ptr[0..len]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn console_read() c_int {
|
export fn console_read() c_int {
|
||||||
if (uart.read_byte()) |b| {
|
if (uart_input.read_byte()) |b| {
|
||||||
return @as(c_int, b);
|
return @as(c_int, b);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn console_poll() void {
|
||||||
|
uart_input.poll_input();
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn debug_uart_lsr() u8 {
|
||||||
|
return uart.get_lsr();
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn uart_print_hex(value: u64) void {
|
||||||
|
uart.print_hex(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn uart_print_hex8(value: u8) void {
|
||||||
|
uart.print_hex8(value);
|
||||||
|
}
|
||||||
|
|
||||||
const virtio_block = @import("virtio_block.zig");
|
const virtio_block = @import("virtio_block.zig");
|
||||||
|
|
||||||
extern fn hal_surface_init() void;
|
extern fn hal_surface_init() void;
|
||||||
|
|
@ -289,7 +443,7 @@ extern fn hal_surface_init() void;
|
||||||
export fn hal_io_init() void {
|
export fn hal_io_init() void {
|
||||||
uart.init();
|
uart.init();
|
||||||
hal_surface_init();
|
hal_surface_init();
|
||||||
virtio_net.init();
|
// Network init is now called explicitly by kernel (rumpk_net_init)
|
||||||
virtio_block.init();
|
virtio_block.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,13 +454,53 @@ export fn rumpk_halt() noreturn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn rumpk_timer_now_ns() u64 {
|
// RISC-V Time Constants
|
||||||
|
const TIMEBASE: u64 = 10_000_000; // QEMU 'virt' machine (10 MHz)
|
||||||
|
const SBI_TIME_EID: u64 = 0x54494D45;
|
||||||
|
|
||||||
|
fn rdtime() u64 {
|
||||||
var ticks: u64 = 0;
|
var ticks: u64 = 0;
|
||||||
asm volatile ("rdtime %[ticks]"
|
asm volatile ("rdtime %[ticks]"
|
||||||
: [ticks] "=r" (ticks),
|
: [ticks] "=r" (ticks),
|
||||||
);
|
);
|
||||||
// QEMU Virt machine is 10MHz -> 1 tick = 100ns
|
return ticks;
|
||||||
return ticks * 100;
|
}
|
||||||
|
|
||||||
|
fn sbi_set_timer(stime_value: u64) void {
|
||||||
|
asm volatile (
|
||||||
|
\\ ecall
|
||||||
|
:
|
||||||
|
: [arg0] "{a0}" (stime_value),
|
||||||
|
[eid] "{a7}" (SBI_TIME_EID),
|
||||||
|
[fid] "{a6}" (0), // FID 0 = set_timer
|
||||||
|
: .{ .memory = true });
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn rumpk_timer_now_ns() u64 {
|
||||||
|
return rdtime() * 100; // 10MHz = 100ns/tick
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn rumpk_timer_set_ns(interval_ns: u64) void {
|
||||||
|
if (interval_ns == std.math.maxInt(u64)) {
|
||||||
|
sbi_set_timer(std.math.maxInt(u64));
|
||||||
|
// Disable STIE
|
||||||
|
asm volatile ("csrc sie, %[mask]"
|
||||||
|
:
|
||||||
|
: [mask] "r" (@as(usize, 1 << 5)),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ticks = interval_ns / 100; // 100ns per tick for 10MHz
|
||||||
|
const now = rdtime();
|
||||||
|
const next_time = now + ticks;
|
||||||
|
sbi_set_timer(next_time);
|
||||||
|
|
||||||
|
// Enable STIE (Supervisor Timer Interrupt Enable)
|
||||||
|
asm volatile ("csrs sie, %[mask]"
|
||||||
|
:
|
||||||
|
: [mask] "r" (@as(usize, 1 << 5)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -333,3 +527,38 @@ export fn hal_kexec(entry: u64, dtb: u64) noreturn {
|
||||||
);
|
);
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// USERLAND TRANSITION
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
export fn hal_enter_userland(entry: u64, systable: u64, sp: u64) callconv(.c) void {
|
||||||
|
// 1. Set up sstatus: SPP=0 (User), SPIE=1 (Enable interrupts on return)
|
||||||
|
// 2. Set sepc to entry point
|
||||||
|
// 3. Set sscratch to current kernel stack
|
||||||
|
// 4. Transition via sret
|
||||||
|
|
||||||
|
var kstack: usize = 0;
|
||||||
|
asm volatile ("mv %[kstack], sp"
|
||||||
|
: [kstack] "=r" (kstack),
|
||||||
|
);
|
||||||
|
|
||||||
|
asm volatile (
|
||||||
|
\\ li t0, 0x20 // sstatus.SPIE = 1 (bit 5)
|
||||||
|
\\ csrs sstatus, t0
|
||||||
|
\\ li t1, 0x100 // sstatus.SPP = 1 (bit 8)
|
||||||
|
\\ csrc sstatus, t1
|
||||||
|
\\ li t2, 0x40000 // sstatus.SUM = 1 (bit 18)
|
||||||
|
\\ csrs sstatus, t2
|
||||||
|
\\ csrw sepc, %[entry]
|
||||||
|
\\ csrw sscratch, %[kstack]
|
||||||
|
\\ mv sp, %[sp]
|
||||||
|
\\ mv a0, %[systable]
|
||||||
|
\\ sret
|
||||||
|
:
|
||||||
|
: [entry] "r" (entry),
|
||||||
|
[systable] "r" (systable),
|
||||||
|
[sp] "r" (sp),
|
||||||
|
[kstack] "r" (kstack),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,3 @@
|
||||||
|
const data = @embedFile("initrd.tar");
|
||||||
|
|
||||||
|
export var _initrd_payload: [data.len]u8 align(4096) linksection(".initrd") = data.*;
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -13,14 +13,15 @@
|
||||||
//! SAFETY: Runs in bare-metal mode with no runtime support.
|
//! SAFETY: Runs in bare-metal mode with no runtime support.
|
||||||
|
|
||||||
const uart = @import("uart.zig");
|
const uart = @import("uart.zig");
|
||||||
|
|
||||||
const hud = @import("hud.zig");
|
const hud = @import("hud.zig");
|
||||||
const virtio_net = @import("virtio_net.zig");
|
const virtio_net = @import("virtio_net.zig");
|
||||||
const virtio_block = @import("virtio_block.zig");
|
const virtio_block = @import("virtio_block.zig");
|
||||||
|
const initrd = @import("initrd.zig");
|
||||||
|
|
||||||
export fn hal_io_init() void {
|
export fn hal_io_init() void {
|
||||||
virtio_net.init();
|
virtio_net.init();
|
||||||
virtio_block.init();
|
virtio_block.init();
|
||||||
|
_ = initrd._initrd_payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
|
||||||
45
hal/mm.zig
45
hal/mm.zig
|
|
@ -23,7 +23,7 @@ pub const LEVELS: u8 = 3;
|
||||||
|
|
||||||
// Physical memory layout (RISC-V QEMU virt)
|
// Physical memory layout (RISC-V QEMU virt)
|
||||||
pub const DRAM_BASE: u64 = 0x80000000;
|
pub const DRAM_BASE: u64 = 0x80000000;
|
||||||
pub const DRAM_SIZE: u64 = 256 * 1024 * 1024; // 256MB for expanded userspace
|
pub const DRAM_SIZE: u64 = 512 * 1024 * 1024; // Expanded for multi-fiber isolation
|
||||||
|
|
||||||
// MMIO regions
|
// MMIO regions
|
||||||
pub const UART_BASE: u64 = 0x10000000;
|
pub const UART_BASE: u64 = 0x10000000;
|
||||||
|
|
@ -164,6 +164,7 @@ pub fn create_kernel_identity_map() !*PageTable {
|
||||||
// MMIO regions
|
// MMIO regions
|
||||||
try map_range(root, UART_BASE, UART_BASE, PAGE_SIZE, PTE_R | PTE_W);
|
try map_range(root, UART_BASE, UART_BASE, PAGE_SIZE, PTE_R | PTE_W);
|
||||||
try map_range(root, 0x10001000, 0x10001000, 0x8000, PTE_R | PTE_W);
|
try map_range(root, 0x10001000, 0x10001000, 0x8000, PTE_R | PTE_W);
|
||||||
|
try map_range(root, 0x20000000, 0x20000000, 0x10000, PTE_R | PTE_W); // PTY Slave
|
||||||
try map_range(root, 0x30000000, 0x30000000, 0x10000000, PTE_R | PTE_W);
|
try map_range(root, 0x30000000, 0x30000000, 0x10000000, PTE_R | PTE_W);
|
||||||
try map_range(root, 0x40000000, 0x40000000, 0x10000000, PTE_R | PTE_W);
|
try map_range(root, 0x40000000, 0x40000000, 0x10000000, PTE_R | PTE_W);
|
||||||
try map_range(root, PLIC_BASE, PLIC_BASE, 0x400000, PTE_R | PTE_W);
|
try map_range(root, PLIC_BASE, PLIC_BASE, 0x400000, PTE_R | PTE_W);
|
||||||
|
|
@ -171,33 +172,45 @@ pub fn create_kernel_identity_map() !*PageTable {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create restricted worker map
|
// Create restricted worker map for Cellular Memory Architecture
|
||||||
pub fn create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64) !*PageTable {
|
pub fn create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64, phys_base: u64, region_size: u64) !*PageTable {
|
||||||
const root = alloc_page_table() orelse return error.OutOfMemory;
|
const root = alloc_page_table() orelse return error.OutOfMemory;
|
||||||
|
|
||||||
// 🏛️ THE EXPANDED CAGE (Phase 37 - 256MB RAM)
|
// 🏛️ THE IRON FIREWALL (Cellular Memory Isolation)
|
||||||
|
// SPEC-202: User VA 0x88000000 is mapped to variable Physical Slots (phys_base).
|
||||||
|
|
||||||
kprint("[MM] Creating worker map:\n");
|
kprint("[MM] Cellular Map: phys_base=");
|
||||||
kprint("[MM] Kernel (S-mode): 0x80000000-0x88000000\n");
|
kprint_hex(phys_base);
|
||||||
kprint("[MM] User (U-mode): 0x88000000-0x90000000\n");
|
kprint(" size=");
|
||||||
|
kprint_hex(region_size);
|
||||||
|
kprint("\n");
|
||||||
|
|
||||||
// 1. Kernel Memory (0-128MB) -> Supervisor ONLY (PTE_U = 0)
|
// 1. Kernel Memory (0-128MB) -> Supervisor ONLY (PTE_U = 0)
|
||||||
// This allows the fiber trampoline to execute in S-mode.
|
// This allows the fiber trampoline to execute in S-mode.
|
||||||
try map_range(root, DRAM_BASE, DRAM_BASE, 128 * 1024 * 1024, PTE_R | PTE_W | PTE_X);
|
try map_range(root, DRAM_BASE, DRAM_BASE, 128 * 1024 * 1024, PTE_R | PTE_W | PTE_X);
|
||||||
|
|
||||||
// 2. User Memory (128-256MB) -> User Accessible (PTE_U = 1)
|
// 2. User Slot (VA 0x88000000 -> PA phys_base) -> User Accessible (PTE_U = 1)
|
||||||
// This allows NipBox (at 128MB offset) to execute in U-mode.
|
// - Slot 0 (Init): PA 0x88000000 (Big Cell)
|
||||||
try map_range(root, DRAM_BASE + (128 * 1024 * 1024), DRAM_BASE + (128 * 1024 * 1024), 128 * 1024 * 1024, PTE_R | PTE_W | PTE_X | PTE_U);
|
// - Slot 1 (Mksh): PA 0x8C000000 (Standard Cell)
|
||||||
|
const user_va_base = DRAM_BASE + (128 * 1024 * 1024);
|
||||||
|
try map_range(root, user_va_base, phys_base, region_size, PTE_R | PTE_W | PTE_X | PTE_U);
|
||||||
|
|
||||||
// 3. User MMIO (UART)
|
// 3. MMIO Plumbing - Mapped identity but S-mode ONLY (PTE_U = 0)
|
||||||
try map_range(root, UART_BASE, UART_BASE, PAGE_SIZE, PTE_R | PTE_W | PTE_U);
|
// This allows the kernel to handle interrupts/IO while fiber map is active.
|
||||||
|
try map_range(root, UART_BASE, UART_BASE, PAGE_SIZE, PTE_R | PTE_W);
|
||||||
|
try map_range(root, PLIC_BASE, PLIC_BASE, 0x400000, PTE_R | PTE_W);
|
||||||
|
try map_range(root, VIRTIO_BASE, VIRTIO_BASE, 0x8000, PTE_R | PTE_W);
|
||||||
|
try map_range(root, VIRTIO_BASE, VIRTIO_BASE, 0x8000, PTE_R | PTE_W);
|
||||||
|
try map_range(root, 0x30000000, 0x30000000, 0x10000000, PTE_R | PTE_W); // PCIe ECAM
|
||||||
|
try map_range(root, 0x40000000, 0x40000000, 0x10000000, PTE_R | PTE_W); // PCIe MMIO
|
||||||
|
try map_range(root, 0x20000000, 0x20000000, 0x10000, PTE_R | PTE_W); // PTY Slave
|
||||||
|
|
||||||
// 4. Overlap stack with user access
|
// 4. Overlap stack with user access
|
||||||
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 (0x83000000) - Map 32KB (8 pages)
|
// 5. Shared SysTable & Rings & User Slab (0x83000000) - Map 256KB (64 pages; covers up to 0x40000)
|
||||||
var j: u64 = 0;
|
var j: u64 = 0;
|
||||||
while (j < 8) : (j += 1) {
|
while (j < 64) : (j += 1) {
|
||||||
const addr = packet_addr + (j * PAGE_SIZE);
|
const addr = packet_addr + (j * PAGE_SIZE);
|
||||||
try map_page(root, addr, addr, PTE_R | PTE_W | PTE_U);
|
try map_page(root, addr, addr, PTE_R | PTE_W | PTE_U);
|
||||||
}
|
}
|
||||||
|
|
@ -244,8 +257,8 @@ pub export fn mm_get_kernel_satp() callconv(.c) u64 {
|
||||||
return kernel_satp_value;
|
return kernel_satp_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub export fn mm_create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64) callconv(.c) u64 {
|
pub export fn mm_create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64, phys_base: u64, region_size: u64) callconv(.c) u64 {
|
||||||
if (create_worker_map(stack_base, stack_size, packet_addr)) |root| {
|
if (create_worker_map(stack_base, stack_size, packet_addr, phys_base, region_size)) |root| {
|
||||||
return make_satp(root);
|
return make_satp(root);
|
||||||
} else |_| {
|
} else |_| {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,571 @@
|
||||||
|
//! SPEC-060: System Ontology - Event & Entity Structures
|
||||||
|
//! Component: core/ontology
|
||||||
|
//! Target: Ground Zero - Phase 2
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// EventKind: Closed enumeration of system events (SPEC-060)
|
||||||
|
pub const EventKind = enum(u16) {
|
||||||
|
Null = 0,
|
||||||
|
|
||||||
|
// Lifecycle Events
|
||||||
|
SystemBoot = 1,
|
||||||
|
SystemShutdown = 2,
|
||||||
|
FiberSpawn = 3,
|
||||||
|
FiberTerminate = 4,
|
||||||
|
|
||||||
|
// Capability Events
|
||||||
|
CapabilityGrant = 10,
|
||||||
|
CapabilityRevoke = 11,
|
||||||
|
CapabilityDelegate = 12,
|
||||||
|
|
||||||
|
// I/O Events
|
||||||
|
ChannelOpen = 20,
|
||||||
|
ChannelClose = 21,
|
||||||
|
ChannelRead = 22,
|
||||||
|
ChannelWrite = 23,
|
||||||
|
|
||||||
|
// Memory Events
|
||||||
|
MemoryAllocate = 30,
|
||||||
|
MemoryFree = 31,
|
||||||
|
MemoryMap = 32,
|
||||||
|
|
||||||
|
// Network Events
|
||||||
|
NetworkPacketRx = 40,
|
||||||
|
NetworkPacketTx = 41,
|
||||||
|
|
||||||
|
// Security Events
|
||||||
|
AccessDenied = 50,
|
||||||
|
PolicyViolation = 51,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Event: Immutable record of a system occurrence (58 bytes, packed)
|
||||||
|
/// NOTE: Packed to 58 bytes for Zig compatibility (packed structs can't contain arrays)
|
||||||
|
pub const Event = packed struct {
|
||||||
|
kind: EventKind, // 2 bytes - Event type
|
||||||
|
_reserved: u16, // 2 bytes - Alignment
|
||||||
|
timestamp_ns: u64, // 8 bytes - Nanosecond timestamp
|
||||||
|
fiber_id: u64, // 8 bytes - Originating fiber
|
||||||
|
entity_id: u64, // 8 bytes - Target entity (SipHash)
|
||||||
|
cause_id: u64, // 8 bytes - Causal parent event ID
|
||||||
|
data0: u64, // 8 bytes - Event-specific data
|
||||||
|
data1: u64, // 8 bytes - Event-specific data
|
||||||
|
data2: u64, // 8 bytes - Event-specific data
|
||||||
|
// Total: 58 bytes (packed)
|
||||||
|
|
||||||
|
/// Create a null event
|
||||||
|
pub fn null_event() Event {
|
||||||
|
return .{
|
||||||
|
.kind = .Null,
|
||||||
|
._reserved = 0,
|
||||||
|
.timestamp_ns = 0,
|
||||||
|
.fiber_id = 0,
|
||||||
|
.entity_id = 0,
|
||||||
|
.cause_id = 0,
|
||||||
|
.data0 = 0,
|
||||||
|
.data1 = 0,
|
||||||
|
.data2 = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if event is null
|
||||||
|
pub fn is_null(self: *const Event) bool {
|
||||||
|
return self.kind == .Null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// EntityKind: Types of system entities (SPEC-060)
|
||||||
|
pub const EntityKind = enum(u8) {
|
||||||
|
Null = 0,
|
||||||
|
Fiber = 1,
|
||||||
|
Channel = 2,
|
||||||
|
Memory = 3,
|
||||||
|
File = 4,
|
||||||
|
Network = 5,
|
||||||
|
Device = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Entity: Represents a system resource (32 bytes, cache-aligned)
|
||||||
|
pub const Entity = extern struct {
|
||||||
|
kind: EntityKind, // 1 byte - Entity type
|
||||||
|
_reserved: [7]u8, // 7 bytes - Alignment
|
||||||
|
entity_id: u64, // 8 bytes - Unique ID (SipHash)
|
||||||
|
parent_id: u64, // 8 bytes - Parent entity (for hierarchy)
|
||||||
|
metadata: u64, // 8 bytes - Entity-specific metadata
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
if (@sizeOf(Entity) != 32) {
|
||||||
|
@compileError("Entity must be exactly 32 bytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a null entity
|
||||||
|
pub fn null_entity() Entity {
|
||||||
|
return .{
|
||||||
|
.kind = .Null,
|
||||||
|
._reserved = [_]u8{0} ** 7,
|
||||||
|
.entity_id = 0,
|
||||||
|
.parent_id = 0,
|
||||||
|
.metadata = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if entity is null
|
||||||
|
pub fn is_null(self: *const Entity) bool {
|
||||||
|
return self.kind == .Null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// System Truth Ledger: Append-only event log
|
||||||
|
pub const STL_SIZE = 4096; // Maximum events in ring buffer
|
||||||
|
|
||||||
|
pub const SystemTruthLedger = struct {
|
||||||
|
events: [STL_SIZE]Event,
|
||||||
|
head: u32, // Next write position
|
||||||
|
tail: u32, // Oldest event position
|
||||||
|
epoch: u32, // Wraparound counter
|
||||||
|
_padding: u32, // Alignment
|
||||||
|
|
||||||
|
/// Initialize empty STL
|
||||||
|
pub fn init() SystemTruthLedger {
|
||||||
|
var stl = SystemTruthLedger{
|
||||||
|
.events = undefined,
|
||||||
|
.head = 0,
|
||||||
|
.tail = 0,
|
||||||
|
.epoch = 0,
|
||||||
|
._padding = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize all events to Null
|
||||||
|
for (&stl.events) |*event| {
|
||||||
|
event.* = Event.null_event();
|
||||||
|
}
|
||||||
|
|
||||||
|
return stl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append event to ledger (returns event ID)
|
||||||
|
pub fn append(self: *SystemTruthLedger, event: Event) u64 {
|
||||||
|
const idx = self.head;
|
||||||
|
self.events[idx] = event;
|
||||||
|
|
||||||
|
// Advance head
|
||||||
|
self.head = (self.head + 1) % STL_SIZE;
|
||||||
|
|
||||||
|
// If we wrapped, advance tail
|
||||||
|
if (self.head == self.tail) {
|
||||||
|
self.tail = (self.tail + 1) % STL_SIZE;
|
||||||
|
self.epoch +%= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event ID = epoch << 32 | index
|
||||||
|
return (@as(u64, self.epoch) << 32) | @as(u64, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup event by ID
|
||||||
|
pub fn lookup(self: *const SystemTruthLedger, event_id: u64) ?*const Event {
|
||||||
|
const idx = @as(u32, @truncate(event_id & 0xFFFFFFFF));
|
||||||
|
const epoch = @as(u32, @truncate(event_id >> 32));
|
||||||
|
|
||||||
|
if (idx >= STL_SIZE) return null;
|
||||||
|
if (epoch != self.epoch and idx >= self.head) return null;
|
||||||
|
|
||||||
|
const event = &self.events[idx];
|
||||||
|
if (event.is_null()) return null;
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current event count
|
||||||
|
pub fn count(self: *const SystemTruthLedger) u32 {
|
||||||
|
if (self.epoch > 0) return STL_SIZE;
|
||||||
|
return self.head;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Global System Truth Ledger
|
||||||
|
pub var global_stl: SystemTruthLedger = undefined;
|
||||||
|
pub var stl_initialized: bool = false;
|
||||||
|
|
||||||
|
/// Initialize STL subsystem
|
||||||
|
pub export fn stl_init() void {
|
||||||
|
if (stl_initialized) return;
|
||||||
|
global_stl = SystemTruthLedger.init();
|
||||||
|
stl_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current timestamp (placeholder - will be replaced by HAL timer)
|
||||||
|
fn get_timestamp_ns() u64 {
|
||||||
|
// TODO: Integrate with HAL timer
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit event to STL (C ABI)
|
||||||
|
pub export fn stl_emit(
|
||||||
|
kind: u16,
|
||||||
|
fiber_id: u64,
|
||||||
|
entity_id: u64,
|
||||||
|
cause_id: u64,
|
||||||
|
data0: u64,
|
||||||
|
data1: u64,
|
||||||
|
data2: u64,
|
||||||
|
) u64 {
|
||||||
|
if (!stl_initialized) return 0;
|
||||||
|
|
||||||
|
const event = Event{
|
||||||
|
.kind = @enumFromInt(kind),
|
||||||
|
._reserved = 0,
|
||||||
|
.timestamp_ns = get_timestamp_ns(),
|
||||||
|
.fiber_id = fiber_id,
|
||||||
|
.entity_id = entity_id,
|
||||||
|
.cause_id = cause_id,
|
||||||
|
.data0 = data0,
|
||||||
|
.data1 = data1,
|
||||||
|
.data2 = data2,
|
||||||
|
};
|
||||||
|
|
||||||
|
return global_stl.append(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup event by ID (C ABI)
|
||||||
|
pub export fn stl_lookup(event_id: u64) ?*const Event {
|
||||||
|
if (!stl_initialized) return null;
|
||||||
|
return global_stl.lookup(event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get event count (C ABI)
|
||||||
|
pub export fn stl_count() u32 {
|
||||||
|
if (!stl_initialized) return 0;
|
||||||
|
return global_stl.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query result structure for event filtering
|
||||||
|
pub const QueryResult = extern struct {
|
||||||
|
count: u32,
|
||||||
|
events: [64]*const Event, // Max 64 results per query
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Query events by fiber ID (C ABI)
|
||||||
|
pub export fn stl_query_by_fiber(fiber_id: u64, result: *QueryResult) void {
|
||||||
|
if (!stl_initialized) {
|
||||||
|
result.count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var count: u32 = 0;
|
||||||
|
var idx = global_stl.tail;
|
||||||
|
|
||||||
|
while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) {
|
||||||
|
const event = &global_stl.events[idx];
|
||||||
|
if (!event.is_null() and event.fiber_id == fiber_id) {
|
||||||
|
result.events[count] = event;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query events by kind (C ABI)
|
||||||
|
pub export fn stl_query_by_kind(kind: u16, result: *QueryResult) void {
|
||||||
|
if (!stl_initialized) {
|
||||||
|
result.count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var count: u32 = 0;
|
||||||
|
var idx = global_stl.tail;
|
||||||
|
|
||||||
|
while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) {
|
||||||
|
const event = &global_stl.events[idx];
|
||||||
|
if (!event.is_null() and @intFromEnum(event.kind) == kind) {
|
||||||
|
result.events[count] = event;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get recent events (last N) (C ABI)
|
||||||
|
pub export fn stl_get_recent(max_count: u32, result: *QueryResult) void {
|
||||||
|
if (!stl_initialized) {
|
||||||
|
result.count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actual_count = @min(max_count, @min(global_stl.count(), 64));
|
||||||
|
var count: u32 = 0;
|
||||||
|
|
||||||
|
// Start from most recent (head - 1)
|
||||||
|
var idx: u32 = if (global_stl.head == 0) STL_SIZE - 1 else global_stl.head - 1;
|
||||||
|
|
||||||
|
while (count < actual_count) {
|
||||||
|
const event = &global_stl.events[idx];
|
||||||
|
if (!event.is_null()) {
|
||||||
|
result.events[count] = event;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx == global_stl.tail) break;
|
||||||
|
idx = if (idx == 0) STL_SIZE - 1 else idx - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lineage result structure for causal tracing
|
||||||
|
pub const LineageResult = extern struct {
|
||||||
|
count: u32,
|
||||||
|
event_ids: [16]u64, // Maximum depth of 16 for causal chains
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Trace the causal lineage of an event (C ABI)
|
||||||
|
pub export fn stl_trace_lineage(event_id: u64, result: *LineageResult) void {
|
||||||
|
if (!stl_initialized) {
|
||||||
|
result.count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var count: u32 = 0;
|
||||||
|
var current_id = event_id;
|
||||||
|
|
||||||
|
while (count < 16) {
|
||||||
|
const event = global_stl.lookup(current_id) orelse break;
|
||||||
|
result.event_ids[count] = current_id;
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
// Stop if we reach an event with no parent (or self-referencing parent)
|
||||||
|
if (event.cause_id == current_id) break;
|
||||||
|
|
||||||
|
// In our system, the root event (SystemBoot) has ID 0 and cause_id 0
|
||||||
|
if (current_id == 0 and event.cause_id == 0) break;
|
||||||
|
|
||||||
|
current_id = event.cause_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query events by time range (C ABI)
|
||||||
|
pub export fn stl_query_by_time_range(start_ns: u64, end_ns: u64, result: *QueryResult) void {
|
||||||
|
if (!stl_initialized) {
|
||||||
|
result.count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var count: u32 = 0;
|
||||||
|
var idx = global_stl.tail;
|
||||||
|
|
||||||
|
while (idx != global_stl.head and count < 64) : (idx = (idx + 1) % STL_SIZE) {
|
||||||
|
const event = &global_stl.events[idx];
|
||||||
|
if (!event.is_null() and event.timestamp_ns >= start_ns and event.timestamp_ns <= end_ns) {
|
||||||
|
result.events[count] = event;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// System statistics structure
|
||||||
|
pub const SystemStats = extern struct {
|
||||||
|
total_events: u32,
|
||||||
|
boot_events: u32,
|
||||||
|
fiber_events: u32,
|
||||||
|
cap_events: u32,
|
||||||
|
io_events: u32,
|
||||||
|
mem_events: u32,
|
||||||
|
net_events: u32,
|
||||||
|
security_events: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get system statistics from STL (C ABI)
|
||||||
|
pub export fn stl_get_stats(stats: *SystemStats) void {
|
||||||
|
if (!stl_initialized) {
|
||||||
|
stats.* = .{
|
||||||
|
.total_events = 0,
|
||||||
|
.boot_events = 0,
|
||||||
|
.fiber_events = 0,
|
||||||
|
.cap_events = 0,
|
||||||
|
.io_events = 0,
|
||||||
|
.mem_events = 0,
|
||||||
|
.net_events = 0,
|
||||||
|
.security_events = 0,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = SystemStats{
|
||||||
|
.total_events = global_stl.count(),
|
||||||
|
.boot_events = 0,
|
||||||
|
.fiber_events = 0,
|
||||||
|
.cap_events = 0,
|
||||||
|
.io_events = 0,
|
||||||
|
.mem_events = 0,
|
||||||
|
.net_events = 0,
|
||||||
|
.security_events = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
var idx = global_stl.tail;
|
||||||
|
while (idx != global_stl.head) : (idx = (idx + 1) % STL_SIZE) {
|
||||||
|
const event = &global_stl.events[idx];
|
||||||
|
if (event.is_null()) continue;
|
||||||
|
|
||||||
|
switch (event.kind) {
|
||||||
|
.SystemBoot, .SystemShutdown => s.boot_events += 1,
|
||||||
|
.FiberSpawn, .FiberTerminate => s.fiber_events += 1,
|
||||||
|
.CapabilityGrant, .CapabilityRevoke, .CapabilityDelegate => s.cap_events += 1,
|
||||||
|
.ChannelOpen, .ChannelClose, .ChannelRead, .ChannelWrite => s.io_events += 1,
|
||||||
|
.MemoryAllocate, .MemoryFree, .MemoryMap => s.mem_events += 1,
|
||||||
|
.NetworkPacketRx, .NetworkPacketTx => s.net_events += 1,
|
||||||
|
.AccessDenied, .PolicyViolation => s.security_events += 1,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.* = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary Header for STL Export
|
||||||
|
pub const STLHeader = extern struct {
|
||||||
|
magic: u32 = 0x53544C21, // "STL!"
|
||||||
|
version: u16 = 1,
|
||||||
|
event_count: u16,
|
||||||
|
event_size: u8 = @sizeOf(Event),
|
||||||
|
_reserved: [23]u8 = [_]u8{0} ** 23, // Pad to 32 bytes for alignment
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Export all events as a contiguous binary blob (C ABI)
|
||||||
|
/// Returns number of bytes written
|
||||||
|
pub export fn stl_export_binary(dest: [*]u8, max_size: usize) usize {
|
||||||
|
if (!stl_initialized) return 0;
|
||||||
|
|
||||||
|
const count = global_stl.count();
|
||||||
|
const required_size = @sizeOf(STLHeader) + (count * @sizeOf(Event));
|
||||||
|
|
||||||
|
if (max_size < required_size) return 0;
|
||||||
|
|
||||||
|
var ptr = dest;
|
||||||
|
|
||||||
|
// Write Header
|
||||||
|
const header = STLHeader{
|
||||||
|
.event_count = @as(u16, @intCast(count)),
|
||||||
|
};
|
||||||
|
@memcpy(ptr, @as([*]const u8, @ptrCast(&header))[0..@sizeOf(STLHeader)]);
|
||||||
|
ptr += @sizeOf(STLHeader);
|
||||||
|
|
||||||
|
// Write Events (in chronological order from tail to head)
|
||||||
|
var idx = global_stl.tail;
|
||||||
|
while (idx != global_stl.head) : (idx = (idx + 1) % STL_SIZE) {
|
||||||
|
const event = &global_stl.events[idx];
|
||||||
|
if (event.is_null()) continue;
|
||||||
|
|
||||||
|
@memcpy(ptr, @as([*]const u8, @ptrCast(event))[0..@sizeOf(Event)]);
|
||||||
|
ptr += @sizeOf(Event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return required_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit tests
|
||||||
|
test "Event creation and validation" {
|
||||||
|
const event = Event{
|
||||||
|
.kind = .FiberSpawn,
|
||||||
|
._reserved = 0,
|
||||||
|
.timestamp_ns = 1000,
|
||||||
|
.fiber_id = 42,
|
||||||
|
.entity_id = 0x1234,
|
||||||
|
.cause_id = 0,
|
||||||
|
.data0 = 0,
|
||||||
|
.data1 = 0,
|
||||||
|
.data2 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
try std.testing.expect(!event.is_null());
|
||||||
|
try std.testing.expect(event.kind == .FiberSpawn);
|
||||||
|
try std.testing.expect(event.fiber_id == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "STL operations" {
|
||||||
|
var stl = SystemTruthLedger.init();
|
||||||
|
|
||||||
|
const event1 = Event{
|
||||||
|
.kind = .SystemBoot,
|
||||||
|
._reserved = 0,
|
||||||
|
.timestamp_ns = 1000,
|
||||||
|
.fiber_id = 0,
|
||||||
|
.entity_id = 0,
|
||||||
|
.cause_id = 0,
|
||||||
|
.data0 = 0,
|
||||||
|
.data1 = 0,
|
||||||
|
.data2 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Append event
|
||||||
|
const id1 = stl.append(event1);
|
||||||
|
try std.testing.expect(id1 == 0);
|
||||||
|
try std.testing.expect(stl.count() == 1);
|
||||||
|
|
||||||
|
// Lookup event
|
||||||
|
const retrieved = stl.lookup(id1).?;
|
||||||
|
try std.testing.expect(retrieved.kind == .SystemBoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "STL wraparound" {
|
||||||
|
var stl = SystemTruthLedger.init();
|
||||||
|
|
||||||
|
// Fill the buffer
|
||||||
|
var i: u32 = 0;
|
||||||
|
while (i < STL_SIZE + 10) : (i += 1) {
|
||||||
|
const event = Event{
|
||||||
|
.kind = .FiberSpawn,
|
||||||
|
._reserved = 0,
|
||||||
|
.timestamp_ns = i,
|
||||||
|
.fiber_id = i,
|
||||||
|
.entity_id = 0,
|
||||||
|
.cause_id = 0,
|
||||||
|
.data0 = 0,
|
||||||
|
.data1 = 0,
|
||||||
|
.data2 = 0,
|
||||||
|
};
|
||||||
|
_ = stl.append(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should have wrapped
|
||||||
|
try std.testing.expect(stl.epoch > 0);
|
||||||
|
try std.testing.expect(stl.count() == STL_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "STL binary export" {
|
||||||
|
var stl = SystemTruthLedger.init();
|
||||||
|
|
||||||
|
_ = stl.append(Event{
|
||||||
|
.kind = .SystemBoot,
|
||||||
|
._reserved = 0,
|
||||||
|
.timestamp_ns = 100,
|
||||||
|
.fiber_id = 0,
|
||||||
|
.entity_id = 0,
|
||||||
|
.cause_id = 0,
|
||||||
|
.data0 = 1,
|
||||||
|
.data1 = 2,
|
||||||
|
.data2 = 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock global STL for export test (since export uses global_stl)
|
||||||
|
global_stl = stl;
|
||||||
|
stl_initialized = true;
|
||||||
|
|
||||||
|
var buf: [512]u8 align(16) = undefined;
|
||||||
|
const written = stl_export_binary(&buf, buf.len);
|
||||||
|
|
||||||
|
try std.testing.expect(written > @sizeOf(STLHeader));
|
||||||
|
|
||||||
|
const header = @as(*const STLHeader, @ptrCast(@alignCast(&buf))).*;
|
||||||
|
try std.testing.expect(header.magic == 0x53544C21);
|
||||||
|
try std.testing.expect(header.event_count == 1);
|
||||||
|
|
||||||
|
const first_ev = @as(*const Event, @ptrCast(@alignCast(&buf[@sizeOf(STLHeader)])));
|
||||||
|
try std.testing.expect(first_ev.kind == .SystemBoot);
|
||||||
|
try std.testing.expect(first_ev.data0 == 1);
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// 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 HAL: Reed-Solomon RAM Block Device (SPEC-023)
|
//! Rumpk HAL: Reed-Solomon RAM Block Device (SPEC-103)
|
||||||
//!
|
//!
|
||||||
//! Provides ECC-protected RAM storage using Reed-Solomon GF(2^8).
|
//! Provides ECC-protected RAM storage using Reed-Solomon GF(2^8).
|
||||||
//! This is the "Cortex" - Space-Grade resilient memory.
|
//! This is the "Cortex" - Space-Grade resilient memory.
|
||||||
|
|
|
||||||
156
hal/stubs.zig
156
hal/stubs.zig
|
|
@ -22,7 +22,7 @@ const uart = @import("uart.zig");
|
||||||
// Simple Bump Allocator for L0
|
// Simple Bump Allocator for L0
|
||||||
// SAFETY(Heap): Memory is written by malloc before any read occurs.
|
// SAFETY(Heap): Memory is written by malloc before any read occurs.
|
||||||
// Initialized to `undefined` to avoid zeroing 32MB at boot.
|
// Initialized to `undefined` to avoid zeroing 32MB at boot.
|
||||||
var heap: [96 * 1024 * 1024]u8 align(4096) = undefined;
|
var heap: [16 * 1024 * 1024]u8 align(4096) = undefined;
|
||||||
var heap_idx: usize = 0;
|
var heap_idx: usize = 0;
|
||||||
var heap_init_done: bool = false;
|
var heap_init_done: bool = false;
|
||||||
|
|
||||||
|
|
@ -30,6 +30,12 @@ export fn debug_print(s: [*]const u8, len: usize) void {
|
||||||
uart.print(s[0..len]);
|
uart.print(s[0..len]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support for C-shim printf (clib.c)
|
||||||
|
// REMOVED: Already exported by entry_riscv.zig (hal.o)
|
||||||
|
// export fn hal_console_write(ptr: [*]const u8, len: usize) void {
|
||||||
|
// uart.print(ptr[0..len]);
|
||||||
|
// }
|
||||||
|
|
||||||
// Header structure (64 bytes aligned to match LwIP MEM_ALIGNMENT)
|
// Header structure (64 bytes aligned to match LwIP MEM_ALIGNMENT)
|
||||||
const BlockHeader = struct {
|
const BlockHeader = struct {
|
||||||
size: usize,
|
size: usize,
|
||||||
|
|
@ -63,11 +69,11 @@ export fn malloc(size: usize) ?*anyopaque {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trace allocations (disabled to reduce noise)
|
// Trace allocations (disabled to reduce noise)
|
||||||
// uart.print("[Alloc] ");
|
uart.print("[Alloc] ");
|
||||||
// uart.print_hex(size);
|
uart.print_hex(size);
|
||||||
// uart.print(" -> Used: ");
|
uart.print(" -> Used: ");
|
||||||
// uart.print_hex(aligned_idx + total_needed);
|
uart.print_hex(aligned_idx + total_needed);
|
||||||
// uart.print("\n");
|
uart.print("\n");
|
||||||
|
|
||||||
const base_ptr = &heap[aligned_idx];
|
const base_ptr = &heap[aligned_idx];
|
||||||
const header = @as(*BlockHeader, @ptrCast(@alignCast(base_ptr)));
|
const header = @as(*BlockHeader, @ptrCast(@alignCast(base_ptr)));
|
||||||
|
|
@ -131,5 +137,141 @@ export fn calloc(nmemb: usize, size: usize) ?*anyopaque {
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
export fn get_ticks() u32 {
|
export fn get_ticks() u32 {
|
||||||
return 0; // TODO: Implement real timer
|
var time_val: u64 = 0;
|
||||||
|
asm volatile ("rdtime %[ret]"
|
||||||
|
: [ret] "=r" (time_val),
|
||||||
|
);
|
||||||
|
// QEMU 'virt' RISC-V timebase is 10MHz (10,000,000 Hz).
|
||||||
|
// Convert to milliseconds: val / 10,000.
|
||||||
|
return @truncate(time_val / 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// export fn rumpk_timer_set_ns(ns: u64) void {
|
||||||
|
// // Stub: Timer not implemented in L0 yet
|
||||||
|
// _ = ns;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export fn fb_kern_get_addr() usize {
|
||||||
|
return 0; // Stub: No framebuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn nexshell_main() void {
|
||||||
|
uart.print("[Kernel] NexShell Stub Executed\n");
|
||||||
|
}
|
||||||
|
extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize;
|
||||||
|
|
||||||
|
export fn exit(code: c_int) noreturn {
|
||||||
|
_ = code;
|
||||||
|
while (true) asm volatile ("wfi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Atomic Stubs (To resolve linker errors with libcompiler_rt)
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
export fn __atomic_compare_exchange(len: usize, ptr: ?*anyopaque, expected: ?*anyopaque, desired: ?*anyopaque, success: c_int, failure: c_int) bool {
|
||||||
|
_ = len;
|
||||||
|
_ = ptr;
|
||||||
|
_ = expected;
|
||||||
|
_ = desired;
|
||||||
|
_ = success;
|
||||||
|
_ = failure;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_fetch_add_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_fetch_sub_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_fetch_and_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_fetch_or_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_fetch_xor_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_fetch_nand_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_fetch_umax_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_fetch_umin_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_load_16(ptr: ?*const anyopaque, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_store_16(ptr: ?*anyopaque, val: u128, model: c_int) void {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_exchange_16(ptr: ?*anyopaque, val: u128, model: c_int) u128 {
|
||||||
|
_ = ptr;
|
||||||
|
_ = val;
|
||||||
|
_ = model;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn __atomic_compare_exchange_16(ptr: ?*anyopaque, exp: ?*anyopaque, des: u128, weak: bool, success: c_int, failure: c_int) bool {
|
||||||
|
_ = ptr;
|
||||||
|
_ = exp;
|
||||||
|
_ = des;
|
||||||
|
_ = weak;
|
||||||
|
_ = success;
|
||||||
|
_ = failure;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Nim Runtime Stubs
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
export fn setLengthStr() void {}
|
||||||
|
export fn addChar() void {}
|
||||||
|
export fn callDepthLimitReached__OOZOOZOOZOOZOOZOOZOOZOOZOOZusrZlibZnimZsystem_u3026() void {
|
||||||
|
while (true) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export var NTIdefect__SEK9acOiG0hv2dnGQbk52qg_: ?*anyopaque = null;
|
||||||
|
|
|
||||||
164
hal/uart.zig
164
hal/uart.zig
|
|
@ -18,33 +18,23 @@ const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
// ARM64 PL011 Constants
|
// ARM64 PL011 Constants
|
||||||
const PL011_BASE: usize = 0x09000000;
|
pub const PL011_BASE: usize = 0x09000000;
|
||||||
const PL011_DR: usize = 0x00;
|
pub const PL011_DR: usize = 0x00;
|
||||||
const PL011_FR: usize = 0x18;
|
pub const PL011_FR: usize = 0x18;
|
||||||
const PL011_TXFF: u32 = 1 << 5;
|
pub const PL011_TXFF: u32 = 1 << 5;
|
||||||
|
|
||||||
// RISC-V 16550A Constants
|
// RISC-V 16550A Constants
|
||||||
const NS16550A_BASE: usize = 0x10000000;
|
pub const NS16550A_BASE: usize = 0x10000000;
|
||||||
const NS16550A_THR: usize = 0x00; // Transmitter Holding Register
|
pub const NS16550A_THR: usize = 0x00; // Transmitter Holding Register
|
||||||
const NS16550A_LSR: usize = 0x05; // Line Status Register
|
pub const NS16550A_LSR: usize = 0x05; // Line Status Register
|
||||||
const NS16550A_THRE: u8 = 1 << 5; // Transmitter Holding Register Empty
|
pub const NS16550A_THRE: u8 = 1 << 5; // Transmitter Holding Register Empty
|
||||||
const NS16550A_IER: usize = 0x01; // Interrupt Enable Register
|
pub const NS16550A_IER: usize = 0x01; // Interrupt Enable Register
|
||||||
const NS16550A_FCR: usize = 0x02; // FIFO Control Register
|
pub const NS16550A_FCR: usize = 0x02; // FIFO Control Register
|
||||||
const NS16550A_LCR: usize = 0x03; // Line Control Register
|
pub const NS16550A_LCR: usize = 0x03; // Line Control Register
|
||||||
|
|
||||||
// Input Ring Buffer (256 bytes, power of 2 for fast masking)
|
// Input logic moved to uart_input.zig
|
||||||
const INPUT_BUFFER_SIZE = 256;
|
|
||||||
// SAFETY(RingBuffer): Only accessed via head/tail indices.
|
|
||||||
// Bytes are written before read. No uninitialized reads possible.
|
|
||||||
var input_buffer: [INPUT_BUFFER_SIZE]u8 = undefined;
|
|
||||||
var input_head: u32 = 0; // Write position
|
|
||||||
var input_tail: u32 = 0; // Read position
|
|
||||||
|
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
// Initialize buffer pointers
|
|
||||||
input_head = 0;
|
|
||||||
input_tail = 0;
|
|
||||||
|
|
||||||
switch (builtin.cpu.arch) {
|
switch (builtin.cpu.arch) {
|
||||||
.riscv64 => init_riscv(),
|
.riscv64 => init_riscv(),
|
||||||
else => {},
|
else => {},
|
||||||
|
|
@ -54,59 +44,67 @@ pub fn init() void {
|
||||||
pub fn init_riscv() void {
|
pub fn init_riscv() void {
|
||||||
const base = NS16550A_BASE;
|
const base = NS16550A_BASE;
|
||||||
|
|
||||||
// 1. Disable Interrupts
|
// 1. Enable Interrupts (Received Data Available)
|
||||||
const ier: *volatile u8 = @ptrFromInt(base + NS16550A_IER);
|
const ier: *volatile u8 = @ptrFromInt(base + NS16550A_IER);
|
||||||
ier.* = 0x00;
|
ier.* = 0x01; // 0x01 = Data Ready Interrupt.
|
||||||
|
|
||||||
// 2. Enable FIFO, clear them, with 14-byte threshold
|
// 2. Disable FIFO (16450 Mode) to ensure immediate non-buffered input visibility
|
||||||
const fcr: *volatile u8 = @ptrFromInt(base + NS16550A_FCR);
|
const fcr: *volatile u8 = @ptrFromInt(base + NS16550A_FCR);
|
||||||
fcr.* = 0x07;
|
fcr.* = 0x00;
|
||||||
|
|
||||||
|
// 2b. Enable Modem Control (DTR | RTS | OUT2)
|
||||||
|
// Essential for allowing interrupts and signaling readiness
|
||||||
|
const mcr: *volatile u8 = @ptrFromInt(base + 0x04); // NS16550A_MCR
|
||||||
|
mcr.* = 0x0B;
|
||||||
|
|
||||||
// 3. Set LCR to 8N1
|
// 3. Set LCR to 8N1
|
||||||
const lcr: *volatile u8 = @ptrFromInt(base + NS16550A_LCR);
|
const lcr: *volatile u8 = @ptrFromInt(base + NS16550A_LCR);
|
||||||
lcr.* = 0x03;
|
lcr.* = 0x03;
|
||||||
|
|
||||||
// Capture any data already in hardware FIFO
|
// --- LOOPBACK TEST ---
|
||||||
poll_input();
|
// Enable Loopback Mode (Bit 4 of MCR)
|
||||||
}
|
mcr.* = 0x1B; // 0x0B | 0x10
|
||||||
|
|
||||||
/// Poll UART hardware and move available bytes into ring buffer
|
// Write a test byte: 0xA5
|
||||||
/// Should be called periodically (e.g. from scheduler or ISR)
|
const thr: *volatile u8 = @ptrFromInt(base + NS16550A_THR);
|
||||||
pub fn poll_input() void {
|
const lsr: *volatile u8 = @ptrFromInt(base + NS16550A_LSR);
|
||||||
switch (builtin.cpu.arch) {
|
// Wait for THRE
|
||||||
.riscv64 => {
|
while ((lsr.* & NS16550A_THRE) == 0) {}
|
||||||
const thr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_THR);
|
thr.* = 0xA5;
|
||||||
const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
|
|
||||||
|
|
||||||
// Read all available bytes from UART FIFO
|
// Wait for Data Ready
|
||||||
while ((lsr.* & 0x01) != 0) { // Data Ready
|
var timeout: usize = 1000000;
|
||||||
const byte = thr.*;
|
while ((lsr.* & 0x01) == 0 and timeout > 0) {
|
||||||
|
timeout -= 1;
|
||||||
// Add to ring buffer if not full
|
|
||||||
const next_head = (input_head + 1) % INPUT_BUFFER_SIZE;
|
|
||||||
if (next_head != input_tail) {
|
|
||||||
input_buffer[input_head] = byte;
|
|
||||||
input_head = next_head;
|
|
||||||
}
|
|
||||||
// If full, drop the byte (could log this in debug mode)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.aarch64 => {
|
|
||||||
const dr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_DR);
|
|
||||||
const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR);
|
|
||||||
|
|
||||||
while ((fr.* & (1 << 4)) == 0) { // RXFE (Receive FIFO Empty) is bit 4
|
|
||||||
const byte: u8 = @truncate(dr.*);
|
|
||||||
|
|
||||||
const next_head = (input_head + 1) % INPUT_BUFFER_SIZE;
|
|
||||||
if (next_head != input_tail) {
|
|
||||||
input_buffer[input_head] = byte;
|
|
||||||
input_head = next_head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var passed = false;
|
||||||
|
var reason: []const u8 = "Timeout";
|
||||||
|
|
||||||
|
if ((lsr.* & 0x01) != 0) {
|
||||||
|
// Read RBR
|
||||||
|
const rbr: *volatile u8 = @ptrFromInt(base + 0x00);
|
||||||
|
const val = rbr.*;
|
||||||
|
if (val == 0xA5) {
|
||||||
|
passed = true;
|
||||||
|
} else {
|
||||||
|
reason = "Data Mismatch";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable Loopback (Restore MCR)
|
||||||
|
mcr.* = 0x0B;
|
||||||
|
|
||||||
|
if (passed) {
|
||||||
|
write_bytes("[UART] Loopback Test: PASS\n");
|
||||||
|
} else {
|
||||||
|
write_bytes("[UART] Loopback Test: FAIL (");
|
||||||
|
write_bytes(reason);
|
||||||
|
write_bytes(")\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture any data already in hardware FIFO
|
||||||
|
// uart_input.poll_input(); // We cannot call this here safely without dep
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_char_arm64(c: u8) void {
|
fn write_char_arm64(c: u8) void {
|
||||||
|
|
@ -143,20 +141,32 @@ pub fn write_bytes(bytes: []const u8) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_byte() ?u8 {
|
// read_byte moved to uart_input.zig
|
||||||
// First, poll UART to refill buffer
|
|
||||||
poll_input();
|
|
||||||
|
|
||||||
// Then read from buffer
|
pub fn read_direct() ?u8 {
|
||||||
if (input_tail != input_head) {
|
switch (builtin.cpu.arch) {
|
||||||
const byte = input_buffer[input_tail];
|
.riscv64 => {
|
||||||
input_tail = (input_tail + 1) % INPUT_BUFFER_SIZE;
|
const thr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_THR);
|
||||||
return byte;
|
const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
|
||||||
|
if ((lsr.* & 0x01) != 0) {
|
||||||
|
return thr.*;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_lsr() u8 {
|
||||||
|
switch (builtin.cpu.arch) {
|
||||||
|
.riscv64 => {
|
||||||
|
const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
|
||||||
|
return lsr.*;
|
||||||
|
},
|
||||||
|
else => return 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn puts(s: []const u8) void {
|
pub fn puts(s: []const u8) void {
|
||||||
write_bytes(s);
|
write_bytes(s);
|
||||||
}
|
}
|
||||||
|
|
@ -194,3 +204,11 @@ pub fn print_hex(value: usize) void {
|
||||||
write_char(hex_chars[nibble]);
|
write_char(hex_chars[nibble]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print_hex8(value: u8) void {
|
||||||
|
const hex_chars = "0123456789ABCDEF";
|
||||||
|
const nibble1 = (value >> 4) & 0xF;
|
||||||
|
const nibble2 = value & 0xF;
|
||||||
|
write_char(hex_chars[nibble1]);
|
||||||
|
write_char(hex_chars[nibble2]);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
// SPDX-License-Identifier: LCL-1.0
|
||||||
|
// Copyright (c) 2026 Markus Maiwald
|
||||||
|
// Stewardship: Self Sovereign Society Foundation
|
||||||
|
|
||||||
|
//! Rumpk Layer 0: UART Input Logic (Kernel Only)
|
||||||
|
//!
|
||||||
|
//! Separated from uart.zig to avoid polluting userland stubs with kernel dependencies.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const uart = @import("uart.zig");
|
||||||
|
|
||||||
|
// Input Ring Buffer (256 bytes, power of 2 for fast masking)
|
||||||
|
const INPUT_BUFFER_SIZE = 256;
|
||||||
|
var input_buffer: [INPUT_BUFFER_SIZE]u8 = undefined;
|
||||||
|
var input_head = std.atomic.Value(u32).init(0); // Write position
|
||||||
|
var input_tail = std.atomic.Value(u32).init(0); // Read position
|
||||||
|
|
||||||
|
pub fn poll_input() void {
|
||||||
|
// Only Kernel uses this
|
||||||
|
const Kernel = struct {
|
||||||
|
extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (builtin.cpu.arch) {
|
||||||
|
.riscv64 => {
|
||||||
|
const thr: *volatile u8 = @ptrFromInt(uart.NS16550A_BASE + uart.NS16550A_THR);
|
||||||
|
const lsr: *volatile u8 = @ptrFromInt(uart.NS16550A_BASE + uart.NS16550A_LSR);
|
||||||
|
|
||||||
|
// Read all available bytes from UART FIFO (Limit 128 to prevent stall)
|
||||||
|
var loop_limit: usize = 0;
|
||||||
|
while ((lsr.* & 0x01) != 0 and loop_limit < 128) { // Data Ready
|
||||||
|
loop_limit += 1;
|
||||||
|
const byte = thr.*;
|
||||||
|
const byte_arr = [1]u8{byte};
|
||||||
|
|
||||||
|
// Forward to Kernel Input Channel
|
||||||
|
Kernel.ion_push_stdin(&byte_arr, 1);
|
||||||
|
|
||||||
|
// Add to ring buffer if not full
|
||||||
|
const head_val = input_head.load(.monotonic);
|
||||||
|
const tail_val = input_tail.load(.monotonic);
|
||||||
|
const next_head = (head_val + 1) % INPUT_BUFFER_SIZE;
|
||||||
|
|
||||||
|
if (next_head != tail_val) {
|
||||||
|
input_buffer[head_val] = byte;
|
||||||
|
input_head.store(next_head, .monotonic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.aarch64 => {
|
||||||
|
const dr: *volatile u32 = @ptrFromInt(uart.PL011_BASE + uart.PL011_DR);
|
||||||
|
const fr: *volatile u32 = @ptrFromInt(uart.PL011_BASE + uart.PL011_FR);
|
||||||
|
|
||||||
|
while ((fr.* & (1 << 4)) == 0) { // RXFE (Receive FIFO Empty) is bit 4
|
||||||
|
const byte: u8 = @truncate(dr.*);
|
||||||
|
const byte_arr = [1]u8{byte};
|
||||||
|
Kernel.ion_push_stdin(&byte_arr, 1);
|
||||||
|
|
||||||
|
const head_val = input_head.load(.monotonic);
|
||||||
|
const tail_val = input_tail.load(.monotonic);
|
||||||
|
const next_head = (head_val + 1) % INPUT_BUFFER_SIZE;
|
||||||
|
|
||||||
|
if (next_head != tail_val) {
|
||||||
|
input_buffer[head_val] = byte;
|
||||||
|
input_head.store(next_head, .monotonic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn uart_poll_input() void {
|
||||||
|
poll_input();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_byte() ?u8 {
|
||||||
|
// First, poll UART to refill buffer
|
||||||
|
poll_input();
|
||||||
|
|
||||||
|
// Then read from buffer
|
||||||
|
const head_val = input_head.load(.monotonic);
|
||||||
|
const tail_val = input_tail.load(.monotonic);
|
||||||
|
|
||||||
|
if (tail_val != head_val) {
|
||||||
|
const byte = input_buffer[tail_val];
|
||||||
|
input_tail.store((tail_val + 1) % INPUT_BUFFER_SIZE, .monotonic);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -17,15 +17,26 @@ const std = @import("std");
|
||||||
const uart = @import("uart.zig");
|
const uart = @import("uart.zig");
|
||||||
const pci = @import("virtio_pci.zig");
|
const pci = @import("virtio_pci.zig");
|
||||||
|
|
||||||
|
// VirtIO Feature Bits
|
||||||
|
const VIRTIO_F_VERSION_1 = 32;
|
||||||
|
const VIRTIO_NET_F_MAC = 5;
|
||||||
|
const VIRTIO_NET_F_MRG_RXBUF = 15;
|
||||||
|
|
||||||
|
// Status Bits
|
||||||
|
const VIRTIO_CONFIG_S_ACKNOWLEDGE = 1;
|
||||||
|
const VIRTIO_CONFIG_S_DRIVER = 2;
|
||||||
|
const VIRTIO_CONFIG_S_DRIVER_OK = 4;
|
||||||
|
const VIRTIO_CONFIG_S_FEATURES_OK = 8;
|
||||||
|
|
||||||
// External Nim functions
|
// External Nim functions
|
||||||
extern fn net_ingest_packet(data: [*]const u8, len: usize) bool;
|
extern fn net_ingest_packet(data: [*]const u8, len: usize) bool;
|
||||||
|
|
||||||
// External C/Zig stubs
|
// External C/Zig stubs
|
||||||
extern fn malloc(size: usize) ?*anyopaque;
|
extern fn malloc(size: usize) ?*anyopaque;
|
||||||
|
|
||||||
extern fn ion_alloc_raw(out_id: *u16) u64;
|
extern fn ion_alloc_shared(out_id: *u16) u64;
|
||||||
extern fn ion_free_raw(id: u16) void;
|
extern fn ion_free_raw(id: u16) void;
|
||||||
extern fn ion_ingress(id: u16, len: u16) void;
|
extern fn ion_ingress(id: u16, len: u16, offset: u16) void;
|
||||||
extern fn ion_get_virt(id: u16) [*]u8;
|
extern fn ion_get_virt(id: u16) [*]u8;
|
||||||
extern fn ion_get_phys(id: u16) u64;
|
extern fn ion_get_phys(id: u16) u64;
|
||||||
extern fn ion_tx_pop(out_id: *u16, out_len: *u16) bool;
|
extern fn ion_tx_pop(out_id: *u16, out_len: *u16) bool;
|
||||||
|
|
@ -34,20 +45,25 @@ var global_driver: ?VirtioNetDriver = null;
|
||||||
|
|
||||||
var poll_count: u32 = 0;
|
var poll_count: u32 = 0;
|
||||||
|
|
||||||
export fn virtio_net_poll() void {
|
pub export fn virtio_net_poll() void {
|
||||||
poll_count += 1;
|
poll_count += 1;
|
||||||
|
|
||||||
// Periodic debug: show queue state (SILENCED FOR PRODUCTION)
|
// Periodic debug: show queue state
|
||||||
// if (poll_count == 1 or (poll_count % 1000000 == 0)) {
|
if (poll_count == 1 or (poll_count % 50 == 0)) {
|
||||||
// if (global_driver) |*d| {
|
if (global_driver) |*d| {
|
||||||
// if (d.rx_queue) |_| {
|
if (d.rx_queue) |q| {
|
||||||
// asm volatile ("fence" ::: .{ .memory = true });
|
// const hw_idx = q.used.idx;
|
||||||
// uart.print("[VirtIO] Poll #");
|
// const drv_idx = q.index;
|
||||||
// uart.print_hex(poll_count);
|
// uart.print("[VirtIO] Poll #");
|
||||||
// uart.print("\n");
|
// uart.print_hex(poll_count);
|
||||||
// }
|
// uart.print(" RX HW:"); uart.print_hex(hw_idx);
|
||||||
// }
|
// uart.print(" DRV:"); uart.print_hex(drv_idx);
|
||||||
// }
|
// uart.print(" Avail:"); uart.print_hex(q.avail.idx);
|
||||||
|
// uart.print("\n");
|
||||||
|
_ = q; // Silence unused variable 'q'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (global_driver) |*d| {
|
if (global_driver) |*d| {
|
||||||
if (d.rx_queue) |q| {
|
if (d.rx_queue) |q| {
|
||||||
|
|
@ -80,7 +96,21 @@ export fn virtio_net_send(data: [*]const u8, len: usize) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init() void {
|
pub export fn virtio_net_get_mac(out_mac: [*]u8) void {
|
||||||
|
if (global_driver) |*d| {
|
||||||
|
d.get_mac(out_mac);
|
||||||
|
} else {
|
||||||
|
// Default fallback if no driver
|
||||||
|
out_mac[0] = 0x00;
|
||||||
|
out_mac[1] = 0x00;
|
||||||
|
out_mac[2] = 0x00;
|
||||||
|
out_mac[3] = 0x00;
|
||||||
|
out_mac[4] = 0x00;
|
||||||
|
out_mac[5] = 0x00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub export fn rumpk_net_init() void {
|
||||||
if (VirtioNetDriver.probe()) |_| {
|
if (VirtioNetDriver.probe()) |_| {
|
||||||
uart.print("[Rumpk L0] Networking initialized (Sovereign).\n");
|
uart.print("[Rumpk L0] Networking initialized (Sovereign).\n");
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +122,39 @@ pub const VirtioNetDriver = struct {
|
||||||
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 {
|
||||||
|
uart.print("[VirtIO-Net] Reading MAC from device_cfg...\n");
|
||||||
|
if (self.transport.is_modern) {
|
||||||
|
// Use device_cfg directly - this is the VirtIO-Net specific config
|
||||||
|
if (self.transport.device_cfg) |cfg| {
|
||||||
|
const ptr: [*]volatile u8 = @ptrCast(cfg);
|
||||||
|
uart.print(" DeviceCfg at: ");
|
||||||
|
uart.print_hex(@intFromPtr(cfg));
|
||||||
|
uart.print("\n MAC bytes: ");
|
||||||
|
|
||||||
|
for (0..6) |i| {
|
||||||
|
out[i] = ptr[i];
|
||||||
|
uart.print_hex8(ptr[i]);
|
||||||
|
if (i < 5) uart.print(":");
|
||||||
|
}
|
||||||
|
uart.print("\n");
|
||||||
|
} else {
|
||||||
|
uart.print(" ERROR: device_cfg is null!\n");
|
||||||
|
// Fallback to zeros
|
||||||
|
for (0..6) |i| {
|
||||||
|
out[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Legacy
|
||||||
|
// Device Config starts at offset 20.
|
||||||
|
const base = self.transport.legacy_bar + 20;
|
||||||
|
for (0..6) |i| {
|
||||||
|
out[i] = @as(*volatile u8, @ptrFromInt(base + i)).*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 = pci.VirtioTransport.init(base),
|
||||||
|
|
@ -147,10 +210,61 @@ pub const VirtioNetDriver = struct {
|
||||||
self.transport.reset();
|
self.transport.reset();
|
||||||
|
|
||||||
// 3. Acknowledge & Sense Driver
|
// 3. Acknowledge & Sense Driver
|
||||||
self.transport.add_status(1); // ACKNOWLEDGE
|
self.transport.add_status(VIRTIO_CONFIG_S_ACKNOWLEDGE);
|
||||||
self.transport.add_status(2); // DRIVER
|
self.transport.add_status(VIRTIO_CONFIG_S_DRIVER);
|
||||||
|
|
||||||
// 4. Feature Negotiation
|
// 4. Feature Negotiation
|
||||||
|
if (self.transport.is_modern) {
|
||||||
|
uart.print("[VirtIO] Starting feature negotiation...\n");
|
||||||
|
|
||||||
|
if (self.transport.common_cfg == null) {
|
||||||
|
uart.print("[VirtIO] ERROR: common_cfg is null!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cfg = self.transport.common_cfg.?;
|
||||||
|
uart.print("[VirtIO] common_cfg addr: ");
|
||||||
|
uart.print_hex(@intFromPtr(cfg));
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
|
uart.print("[VirtIO] Reading device features...\n");
|
||||||
|
// Read Device Features (Page 0)
|
||||||
|
cfg.device_feature_select = 0;
|
||||||
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
const f_low = cfg.device_feature;
|
||||||
|
|
||||||
|
// Read Device Features (Page 1)
|
||||||
|
cfg.device_feature_select = 1;
|
||||||
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
const f_high = cfg.device_feature;
|
||||||
|
|
||||||
|
uart.print("[VirtIO] Device Features: ");
|
||||||
|
uart.print_hex(f_low);
|
||||||
|
uart.print(" ");
|
||||||
|
uart.print_hex(f_high);
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
|
// Accept VERSION_1 (Modern) and MAC
|
||||||
|
const accept_low: u32 = (1 << VIRTIO_NET_F_MAC);
|
||||||
|
const accept_high: u32 = (1 << (VIRTIO_F_VERSION_1 - 32));
|
||||||
|
|
||||||
|
uart.print("[VirtIO] Writing driver features...\n");
|
||||||
|
cfg.driver_feature_select = 0;
|
||||||
|
cfg.driver_feature = accept_low;
|
||||||
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
cfg.driver_feature_select = 1;
|
||||||
|
cfg.driver_feature = accept_high;
|
||||||
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
|
||||||
|
uart.print("[VirtIO] Checking feature negotiation...\n");
|
||||||
|
self.transport.add_status(VIRTIO_CONFIG_S_FEATURES_OK);
|
||||||
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
if ((self.transport.get_status() & VIRTIO_CONFIG_S_FEATURES_OK) == 0) {
|
||||||
|
uart.print("[VirtIO] Feature negotiation failed!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uart.print("[VirtIO] Features accepted.\n");
|
||||||
|
}
|
||||||
// 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_count = self.transport.get_queue_size();
|
||||||
|
|
@ -212,6 +326,12 @@ pub const VirtioNetDriver = struct {
|
||||||
const raw_ptr = malloc(total_size + 4096) orelse return error.OutOfMemory;
|
const raw_ptr = malloc(total_size + 4096) orelse return error.OutOfMemory;
|
||||||
const aligned_addr = (@intFromPtr(raw_ptr) + 4095) & ~@as(usize, 4095);
|
const aligned_addr = (@intFromPtr(raw_ptr) + 4095) & ~@as(usize, 4095);
|
||||||
|
|
||||||
|
// Zero out the queue memory to ensure clean state
|
||||||
|
const byte_ptr: [*]u8 = @ptrFromInt(aligned_addr);
|
||||||
|
for (0..total_size) |i| {
|
||||||
|
byte_ptr[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const q_ptr_raw = malloc(@sizeOf(Virtqueue)) orelse return error.OutOfMemory;
|
const q_ptr_raw = malloc(@sizeOf(Virtqueue)) orelse return error.OutOfMemory;
|
||||||
const q_ptr: *Virtqueue = @ptrCast(@alignCast(q_ptr_raw));
|
const q_ptr: *Virtqueue = @ptrCast(@alignCast(q_ptr_raw));
|
||||||
|
|
||||||
|
|
@ -221,6 +341,16 @@ pub const VirtioNetDriver = struct {
|
||||||
q_ptr.avail = @ptrFromInt(aligned_addr + desc_size);
|
q_ptr.avail = @ptrFromInt(aligned_addr + desc_size);
|
||||||
q_ptr.used = @ptrFromInt(aligned_addr + used_offset);
|
q_ptr.used = @ptrFromInt(aligned_addr + used_offset);
|
||||||
|
|
||||||
|
uart.print(" [Queue Setup] Base: ");
|
||||||
|
uart.print_hex(aligned_addr);
|
||||||
|
uart.print(" Desc: ");
|
||||||
|
uart.print_hex(@intFromPtr(q_ptr.desc));
|
||||||
|
uart.print(" Avail: ");
|
||||||
|
uart.print_hex(@intFromPtr(q_ptr.avail));
|
||||||
|
uart.print(" Used: ");
|
||||||
|
uart.print_hex(@intFromPtr(q_ptr.used));
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
// Allocate ID tracking array
|
// Allocate ID tracking array
|
||||||
const ids_size = @as(usize, count) * @sizeOf(u16);
|
const ids_size = @as(usize, count) * @sizeOf(u16);
|
||||||
const ids_ptr = malloc(ids_size) orelse return error.OutOfMemory;
|
const ids_ptr = malloc(ids_size) orelse return error.OutOfMemory;
|
||||||
|
|
@ -236,7 +366,7 @@ pub const VirtioNetDriver = struct {
|
||||||
|
|
||||||
if (is_rx) {
|
if (is_rx) {
|
||||||
// RX: Allocate Initial Slabs
|
// RX: Allocate Initial Slabs
|
||||||
phys_addr = ion_alloc_raw(&slab_id);
|
phys_addr = ion_alloc_shared(&slab_id);
|
||||||
if (phys_addr == 0) {
|
if (phys_addr == 0) {
|
||||||
uart.print("[VirtIO] RX ION Alloc Failed. OOM.\n");
|
uart.print("[VirtIO] RX ION Alloc Failed. OOM.\n");
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
|
|
@ -289,7 +419,13 @@ pub const VirtioNetDriver = struct {
|
||||||
const hw_idx = used.idx;
|
const hw_idx = used.idx;
|
||||||
const drv_idx = q.index;
|
const drv_idx = q.index;
|
||||||
|
|
||||||
if (hw_idx == drv_idx) {
|
if (hw_idx != drv_idx) {
|
||||||
|
uart.print("[VirtIO RX] Activity Detected! HW:");
|
||||||
|
uart.print_hex(hw_idx);
|
||||||
|
uart.print(" DRV:");
|
||||||
|
uart.print_hex(drv_idx);
|
||||||
|
uart.print("\n");
|
||||||
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +434,7 @@ pub const VirtioNetDriver = struct {
|
||||||
var replenished: bool = false;
|
var replenished: bool = false;
|
||||||
|
|
||||||
while (q.index != hw_idx) {
|
while (q.index != hw_idx) {
|
||||||
// uart.print("[VirtIO RX] Processing Packet...\n");
|
uart.print("[VirtIO RX] Processing Packet...\n");
|
||||||
|
|
||||||
const elem = used_ring[q.index % q.num];
|
const elem = used_ring[q.index % q.num];
|
||||||
const desc_idx = elem.id;
|
const desc_idx = elem.id;
|
||||||
|
|
@ -313,7 +449,9 @@ pub const VirtioNetDriver = struct {
|
||||||
// uart.print_hex(slab_id);
|
// uart.print_hex(slab_id);
|
||||||
// uart.print("\n");
|
// uart.print("\n");
|
||||||
|
|
||||||
const header_len: u32 = 10;
|
// Modern VirtIO-net header: 10 bytes (legacy), 12 if MRG_RXBUF. We typically don't negiotate MRG yet.
|
||||||
|
// Using 10 to match 'send_slab' and negotiation.
|
||||||
|
const header_len: u32 = 12; // Modern VirtIO-net (with MRG_RXBUF)
|
||||||
if (len > header_len) {
|
if (len > header_len) {
|
||||||
// Call ION - Pass only the Ethernet Frame (Skip VirtIO Header)
|
// Call ION - Pass only the Ethernet Frame (Skip VirtIO Header)
|
||||||
// ion_ingress receives slab_id which contains full buffer.
|
// ion_ingress receives slab_id which contains full buffer.
|
||||||
|
|
@ -322,7 +460,7 @@ pub const VirtioNetDriver = struct {
|
||||||
// The NPL must then offset into the buffer by 10 to get to Ethernet.
|
// The NPL must then offset into the buffer by 10 to get to Ethernet.
|
||||||
// OR: We adjust here. Let's adjust here by storing offset.
|
// OR: We adjust here. Let's adjust here by storing offset.
|
||||||
// Simplest: Pass len directly, NPL will skip first 10 bytes.
|
// Simplest: Pass len directly, NPL will skip first 10 bytes.
|
||||||
ion_ingress(slab_id, @intCast(len - header_len));
|
ion_ingress(slab_id, @intCast(len - header_len), @intCast(header_len));
|
||||||
} else {
|
} else {
|
||||||
uart.print(" [Warn] Packet too short/empty\n");
|
uart.print(" [Warn] Packet too short/empty\n");
|
||||||
ion_free_raw(slab_id);
|
ion_free_raw(slab_id);
|
||||||
|
|
@ -330,7 +468,7 @@ pub const VirtioNetDriver = struct {
|
||||||
|
|
||||||
// Replenish
|
// Replenish
|
||||||
var new_id: u16 = 0;
|
var new_id: u16 = 0;
|
||||||
const new_phys = ion_alloc_raw(&new_id);
|
const new_phys = ion_alloc_shared(&new_id);
|
||||||
if (new_phys != 0) {
|
if (new_phys != 0) {
|
||||||
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;
|
||||||
|
|
@ -380,6 +518,8 @@ pub const VirtioNetDriver = struct {
|
||||||
const idx = avail_phase % q.num;
|
const idx = avail_phase % q.num;
|
||||||
|
|
||||||
const phys_addr = ion_get_phys(slab_id);
|
const phys_addr = ion_get_phys(slab_id);
|
||||||
|
const virt_addr = ion_get_virt(slab_id);
|
||||||
|
@memset(virt_addr[0..12], 0); // Zero out VirtIO Header (Modern 12-byte with MRG_RXBUF)
|
||||||
|
|
||||||
const desc = &q.desc[idx];
|
const desc = &q.desc[idx];
|
||||||
desc.addr = phys_addr;
|
desc.addr = phys_addr;
|
||||||
|
|
@ -404,8 +544,17 @@ pub const VirtioNetDriver = struct {
|
||||||
const q = self.tx_queue orelse return;
|
const q = self.tx_queue orelse return;
|
||||||
const avail_ring = get_avail_ring(q.avail);
|
const avail_ring = get_avail_ring(q.avail);
|
||||||
|
|
||||||
|
uart.print("[VirtIO TX] Packet Data: ");
|
||||||
|
for (0..16) |i| {
|
||||||
|
if (i < len) {
|
||||||
|
uart.print_hex8(data[i]);
|
||||||
|
uart.print(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
var slab_id: u16 = 0;
|
var slab_id: u16 = 0;
|
||||||
const phys = ion_alloc_raw(&slab_id);
|
const phys = ion_alloc_shared(&slab_id);
|
||||||
if (phys == 0) {
|
if (phys == 0) {
|
||||||
uart.print("[VirtIO] TX OOM\n");
|
uart.print("[VirtIO] TX OOM\n");
|
||||||
return;
|
return;
|
||||||
|
|
@ -419,7 +568,8 @@ pub const VirtioNetDriver = struct {
|
||||||
const desc = &q.desc[desc_idx];
|
const desc = &q.desc[desc_idx];
|
||||||
q.ids[desc_idx] = slab_id;
|
q.ids[desc_idx] = slab_id;
|
||||||
|
|
||||||
const header_len: usize = 10;
|
// Modern VirtIO-net header: 12 bytes (with MRG_RXBUF)
|
||||||
|
const header_len: usize = 12;
|
||||||
@memset(buf_ptr[0..header_len], 0);
|
@memset(buf_ptr[0..header_len], 0);
|
||||||
|
|
||||||
const copy_len = if (len > 2000) 2000 else len;
|
const copy_len = if (len > 2000) 2000 else len;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,11 @@ const PCI_CAP_PTR = 0x34;
|
||||||
|
|
||||||
// Global Allocator for I/O and MMIO
|
// Global Allocator for I/O and MMIO
|
||||||
var next_io_port: u32 = 0x1000;
|
var next_io_port: u32 = 0x1000;
|
||||||
var next_mmio_addr: u32 = 0x40000000;
|
const MMIO_ALLOC_ADDR: usize = 0x83000400;
|
||||||
|
|
||||||
|
fn get_mmio_alloc() *u64 {
|
||||||
|
return @ptrFromInt(MMIO_ALLOC_ADDR);
|
||||||
|
}
|
||||||
|
|
||||||
// VirtIO Capability Types
|
// VirtIO Capability Types
|
||||||
const VIRTIO_PCI_CAP_COMMON_CFG = 1;
|
const VIRTIO_PCI_CAP_COMMON_CFG = 1;
|
||||||
|
|
@ -44,6 +48,7 @@ pub const VirtioTransport = struct {
|
||||||
notify_cfg: ?usize, // Base of notification region
|
notify_cfg: ?usize, // Base of notification region
|
||||||
notify_off_multiplier: u32,
|
notify_off_multiplier: u32,
|
||||||
isr_cfg: ?*volatile u8,
|
isr_cfg: ?*volatile u8,
|
||||||
|
device_cfg: ?*volatile u8,
|
||||||
|
|
||||||
pub fn init(ecam_base: usize) VirtioTransport {
|
pub fn init(ecam_base: usize) VirtioTransport {
|
||||||
return .{
|
return .{
|
||||||
|
|
@ -54,10 +59,15 @@ pub const VirtioTransport = struct {
|
||||||
.notify_cfg = null,
|
.notify_cfg = null,
|
||||||
.notify_off_multiplier = 0,
|
.notify_off_multiplier = 0,
|
||||||
.isr_cfg = null,
|
.isr_cfg = null,
|
||||||
|
.device_cfg = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn probe(self: *VirtioTransport) bool {
|
pub fn probe(self: *VirtioTransport) bool {
|
||||||
|
const mmio_alloc = get_mmio_alloc();
|
||||||
|
if (mmio_alloc.* < 0x40000000) {
|
||||||
|
mmio_alloc.* = 0x40000000;
|
||||||
|
}
|
||||||
uart.print("[VirtIO-PCI] Probing capabilities...\n");
|
uart.print("[VirtIO-PCI] Probing capabilities...\n");
|
||||||
|
|
||||||
// 1. Enable Bus Master & Memory Space & IO Space
|
// 1. Enable Bus Master & Memory Space & IO Space
|
||||||
|
|
@ -66,27 +76,68 @@ pub const VirtioTransport = struct {
|
||||||
|
|
||||||
// 2. Check for Capabilities
|
// 2. Check for Capabilities
|
||||||
const status_ptr: *volatile u16 = @ptrFromInt(self.base_addr + PCI_STATUS);
|
const status_ptr: *volatile u16 = @ptrFromInt(self.base_addr + PCI_STATUS);
|
||||||
|
|
||||||
|
uart.print(" [PCI BARs] ");
|
||||||
|
for (0..6) |i| {
|
||||||
|
const bar_val = @as(*volatile u32, @ptrFromInt(self.base_addr + 0x10 + (i * 4))).*;
|
||||||
|
uart.print("BAR");
|
||||||
|
uart.print_hex8(@intCast(i));
|
||||||
|
uart.print(":");
|
||||||
|
uart.print_hex(bar_val);
|
||||||
|
uart.print(" ");
|
||||||
|
}
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
if ((status_ptr.* & 0x10) != 0) {
|
if ((status_ptr.* & 0x10) != 0) {
|
||||||
// Has Capabilities
|
// Has Capabilities
|
||||||
var cap_offset = @as(*volatile u8, @ptrFromInt(self.base_addr + PCI_CAP_PTR)).*;
|
var cap_offset = @as(*volatile u8, @ptrFromInt(self.base_addr + PCI_CAP_PTR)).*;
|
||||||
|
|
||||||
|
// 🔥 LOOP GUARD: Prevent infinite loops in capability chain
|
||||||
|
// Standard PCI config space is 256 bytes, max ~48 capabilities possible
|
||||||
|
// If we exceed this, the chain is circular or we're reading stale cached values
|
||||||
|
var loop_guard: usize = 0;
|
||||||
|
const MAX_CAPS: usize = 48;
|
||||||
|
|
||||||
while (cap_offset != 0) {
|
while (cap_offset != 0) {
|
||||||
|
loop_guard += 1;
|
||||||
|
if (loop_guard > MAX_CAPS) {
|
||||||
|
uart.print("[VirtIO-PCI] WARN: Capability loop limit reached (");
|
||||||
|
uart.print_hex(loop_guard);
|
||||||
|
uart.print(" iterations). Breaking to prevent hang.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const cap_addr = self.base_addr + cap_offset;
|
const cap_addr = self.base_addr + cap_offset;
|
||||||
const cap_id = @as(*volatile u8, @ptrFromInt(cap_addr)).*;
|
const cap_id = @as(*volatile u8, @ptrFromInt(cap_addr)).*;
|
||||||
const cap_next = @as(*volatile u8, @ptrFromInt(cap_addr + 1)).*;
|
const cap_next = @as(*volatile u8, @ptrFromInt(cap_addr + 1)).*;
|
||||||
|
|
||||||
uart.print("[VirtIO-PCI] Cap at ");
|
// uart.print(" ID: ");
|
||||||
uart.print_hex(cap_offset);
|
// uart.print_hex(cap_id);
|
||||||
uart.print(" ID: ");
|
// uart.print(" Next: ");
|
||||||
uart.print_hex(cap_id);
|
// uart.print_hex(cap_next);
|
||||||
uart.print(" Next: ");
|
// uart.print("\n");
|
||||||
uart.print_hex(cap_next);
|
|
||||||
uart.print("\n");
|
|
||||||
|
|
||||||
if (cap_id == 0x09) { // Vendor Specific (VirtIO)
|
if (cap_id == 0x09) { // Vendor Specific (VirtIO)
|
||||||
const cap_type = @as(*volatile u8, @ptrFromInt(cap_addr + 3)).*;
|
const cap_type = @as(*volatile u8, @ptrFromInt(cap_addr + 3)).*;
|
||||||
const bar_idx = @as(*volatile u8, @ptrFromInt(cap_addr + 4)).*;
|
const bar_idx = @as(*volatile u8, @ptrFromInt(cap_addr + 4)).*;
|
||||||
const offset = @as(*volatile u32, @ptrFromInt(cap_addr + 8)).*;
|
const offset = @as(*volatile u32, @ptrFromInt(cap_addr + 8)).*;
|
||||||
|
const length = @as(*volatile u32, @ptrFromInt(cap_addr + 12)).*;
|
||||||
|
|
||||||
|
uart.print(" [VirtIO Cap] Type:");
|
||||||
|
uart.print_hex(cap_type);
|
||||||
|
uart.print(" BAR:");
|
||||||
|
uart.print_hex(bar_idx);
|
||||||
|
uart.print(" Off:");
|
||||||
|
uart.print_hex(offset);
|
||||||
|
uart.print(" Len:");
|
||||||
|
uart.print_hex(length);
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
|
if (bar_idx >= 6) {
|
||||||
|
uart.print("[VirtIO-PCI] Ignoring Invalid BAR Index in Cap\n");
|
||||||
|
cap_offset = cap_next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve BAR Address
|
// Resolve BAR Address
|
||||||
const bar_ptr = @as(*volatile u32, @ptrFromInt(self.base_addr + 0x10 + (@as(usize, bar_idx) * 4)));
|
const bar_ptr = @as(*volatile u32, @ptrFromInt(self.base_addr + 0x10 + (@as(usize, bar_idx) * 4)));
|
||||||
|
|
@ -94,17 +145,32 @@ pub const VirtioTransport = struct {
|
||||||
|
|
||||||
// Check if BAR is assigned and is a Memory BAR (bit 0 == 0)
|
// Check if BAR is assigned and is a Memory BAR (bit 0 == 0)
|
||||||
if ((bar_val & 0x1) == 0 and (bar_val & 0xFFFFFFF0) == 0) {
|
if ((bar_val & 0x1) == 0 and (bar_val & 0xFFFFFFF0) == 0) {
|
||||||
uart.print("[VirtIO-PCI] Initializing Unassigned Memory BAR ");
|
uart.print("[VirtIO-PCI] dev:");
|
||||||
uart.print_hex(@as(u64, bar_idx));
|
uart.print_hex(self.base_addr);
|
||||||
|
uart.print(" ALLOC_VAL: ");
|
||||||
|
uart.print_hex(mmio_alloc.*);
|
||||||
|
uart.print(" Initializing BAR");
|
||||||
|
uart.print_hex8(@intCast(bar_idx));
|
||||||
uart.print(" at ");
|
uart.print(" at ");
|
||||||
uart.print_hex(next_mmio_addr);
|
uart.print_hex(mmio_alloc.*);
|
||||||
uart.print("\n");
|
uart.print("\n");
|
||||||
bar_ptr.* = next_mmio_addr;
|
|
||||||
|
bar_ptr.* = @intCast(mmio_alloc.* & 0xFFFFFFFF);
|
||||||
|
|
||||||
|
// Handle 64-bit BAR (Bit 2 of BAR value before write, or check type)
|
||||||
|
// If bit 2 is 1 (0b100), it's 64-bit.
|
||||||
|
if ((bar_val & 0x4) != 0) {
|
||||||
|
const high_ptr = @as(*volatile u32, @ptrFromInt(self.base_addr + 0x10 + (@as(usize, bar_idx) * 4) + 4));
|
||||||
|
high_ptr.* = @intCast(mmio_alloc.* >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
const rb = bar_ptr.*;
|
const rb = bar_ptr.*;
|
||||||
uart.print("[VirtIO-PCI] BAR Assigned. Readback: ");
|
uart.print("[VirtIO-PCI] dev:");
|
||||||
|
uart.print_hex(self.base_addr);
|
||||||
|
uart.print(" BAR Assigned. Readback: ");
|
||||||
uart.print_hex(rb);
|
uart.print_hex(rb);
|
||||||
uart.print("\n");
|
uart.print("\n");
|
||||||
next_mmio_addr += 0x10000; // Increment 64KB
|
mmio_alloc.* += 0x10000; // Increment 64KB
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh BAR resolution (Memory only for Modern)
|
// Refresh BAR resolution (Memory only for Modern)
|
||||||
|
|
@ -112,8 +178,18 @@ pub const VirtioTransport = struct {
|
||||||
|
|
||||||
if (cap_type == VIRTIO_PCI_CAP_COMMON_CFG) {
|
if (cap_type == VIRTIO_PCI_CAP_COMMON_CFG) {
|
||||||
uart.print("[VirtIO-PCI] Found Modern Common Config\n");
|
uart.print("[VirtIO-PCI] Found Modern Common Config\n");
|
||||||
|
uart.print(" BAR Base: ");
|
||||||
|
uart.print_hex(@as(u64, bar_base));
|
||||||
|
uart.print(" Offset: ");
|
||||||
|
uart.print_hex(@as(u64, offset));
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
self.common_cfg = @ptrFromInt(bar_base + offset);
|
self.common_cfg = @ptrFromInt(bar_base + offset);
|
||||||
self.is_modern = true;
|
self.is_modern = true;
|
||||||
|
|
||||||
|
uart.print(" CommonCfg Ptr: ");
|
||||||
|
uart.print_hex(@intFromPtr(self.common_cfg.?));
|
||||||
|
uart.print("\n");
|
||||||
}
|
}
|
||||||
if (cap_type == VIRTIO_PCI_CAP_NOTIFY_CFG) {
|
if (cap_type == VIRTIO_PCI_CAP_NOTIFY_CFG) {
|
||||||
uart.print("[VirtIO-PCI] Found Modern Notify Config\n");
|
uart.print("[VirtIO-PCI] Found Modern Notify Config\n");
|
||||||
|
|
@ -124,6 +200,15 @@ pub const VirtioTransport = struct {
|
||||||
uart.print("[VirtIO-PCI] Found Modern ISR Config\n");
|
uart.print("[VirtIO-PCI] Found Modern ISR Config\n");
|
||||||
self.isr_cfg = @ptrFromInt(bar_base + offset);
|
self.isr_cfg = @ptrFromInt(bar_base + offset);
|
||||||
}
|
}
|
||||||
|
if (cap_type == VIRTIO_PCI_CAP_DEVICE_CFG) {
|
||||||
|
uart.print("[VirtIO-PCI] Found Modern Device Config\n");
|
||||||
|
uart.print(" BAR Base: ");
|
||||||
|
uart.print_hex(@as(u64, bar_base));
|
||||||
|
uart.print(" Offset: ");
|
||||||
|
uart.print_hex(@as(u64, offset));
|
||||||
|
uart.print("\n");
|
||||||
|
self.device_cfg = @ptrFromInt(bar_base + offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uart.print("[VirtIO-PCI] Next Cap...\n");
|
uart.print("[VirtIO-PCI] Next Cap...\n");
|
||||||
cap_offset = cap_next;
|
cap_offset = cap_next;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,17 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
// Types needed for stubs
|
||||||
|
typedef int32_t pid_t;
|
||||||
|
typedef int32_t uid_t;
|
||||||
|
typedef int32_t gid_t;
|
||||||
|
typedef int64_t off_t;
|
||||||
|
typedef int32_t mode_t;
|
||||||
|
|
||||||
|
struct stat {
|
||||||
|
int st_mode;
|
||||||
|
};
|
||||||
|
|
||||||
int errno = 0;
|
int errno = 0;
|
||||||
|
|
||||||
// Basic memory stubs
|
// Basic memory stubs
|
||||||
|
|
@ -11,8 +22,6 @@ extern void free(void* ptr);
|
||||||
// Forward declare memset (defined below)
|
// Forward declare memset (defined below)
|
||||||
void* memset(void* s, int c, size_t n);
|
void* memset(void* s, int c, size_t n);
|
||||||
|
|
||||||
// Memory stubs moved to stubs.zig
|
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
|
|
@ -22,13 +31,7 @@ size_t strlen(const char* s) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nexus_lwip_panic(const char* msg) {
|
// nexus_lwip_panic moved to sys_arch.c to avoid duplicate symbols
|
||||||
const char* prefix = "\n\x1b[1;31m[LwIP Fatal] ASSERTION FAILED: \x1b[0m";
|
|
||||||
console_write(prefix, strlen(prefix));
|
|
||||||
console_write(msg, strlen(msg));
|
|
||||||
console_write("\n", 1);
|
|
||||||
while(1) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
int strncmp(const char *s1, const char *s2, size_t n) {
|
int strncmp(const char *s1, const char *s2, size_t n) {
|
||||||
for (size_t i = 0; i < n; i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
|
@ -48,56 +51,175 @@ double strtod(const char* nptr, char** endptr) {
|
||||||
double pow(double x, double y) { return 0.0; }
|
double pow(double x, double y) { return 0.0; }
|
||||||
double log10(double x) { return 0.0; }
|
double log10(double x) { return 0.0; }
|
||||||
|
|
||||||
// IO stubs
|
// --- SYSCALL INTERFACE ---
|
||||||
extern int write(int fd, const void *buf, size_t count);
|
|
||||||
|
|
||||||
int printf(const char *format, ...) {
|
#ifdef RUMPK_KERNEL
|
||||||
va_list args;
|
extern long k_handle_syscall(long nr, long a0, long a1, long a2);
|
||||||
va_start(args, format);
|
#endif
|
||||||
const char *p = format;
|
|
||||||
|
long syscall(long nr, long a0, long a1, long a2) {
|
||||||
|
#ifdef RUMPK_KERNEL
|
||||||
|
return k_handle_syscall(nr, a0, a1, a2);
|
||||||
|
#else
|
||||||
|
long res;
|
||||||
|
#if defined(__riscv)
|
||||||
|
register long a7 asm("a7") = nr;
|
||||||
|
register long _a0 asm("a0") = a0;
|
||||||
|
register long _a1 asm("a1") = a1;
|
||||||
|
register long _a2 asm("a2") = a2;
|
||||||
|
asm volatile("ecall"
|
||||||
|
: "+r"(_a0)
|
||||||
|
: "r"(a7), "r"(_a1), "r"(_a2)
|
||||||
|
: "memory");
|
||||||
|
res = _a0;
|
||||||
|
#else
|
||||||
|
res = -1;
|
||||||
|
#endif
|
||||||
|
return res;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// IO stubs (Real Syscalls)
|
||||||
|
|
||||||
|
int write(int fd, const void *buf, size_t count) {
|
||||||
|
// 0x204 = SYS_WRITE
|
||||||
|
return (int)syscall(0x204, fd, (long)buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int read(int fd, void *buf, size_t count) {
|
||||||
|
// 0x203 = SYS_READ
|
||||||
|
return (int)syscall(0x203, fd, (long)buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int open(const char *pathname, int flags, ...) {
|
||||||
|
// 0x200 = SYS_OPEN
|
||||||
|
return (int)syscall(0x200, (long)pathname, flags, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int close(int fd) {
|
||||||
|
// 0x201 = SYS_CLOSE
|
||||||
|
return (int)syscall(0x201, fd, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int execv(const char *path, char *const argv[]) {
|
||||||
|
// 0x600 = KEXEC (Replace current fiber/process)
|
||||||
|
// Note: argv is currently ignored by kernel kexec, it just runs the binary
|
||||||
|
return (int)syscall(0x600, (long)path, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Robust Formatter
|
||||||
|
typedef struct {
|
||||||
|
char *buf;
|
||||||
|
size_t size;
|
||||||
|
size_t pos;
|
||||||
|
} OutCtx;
|
||||||
|
|
||||||
|
static void out_char(OutCtx *ctx, char c) {
|
||||||
|
if (ctx->buf && ctx->size > 0 && ctx->pos < ctx->size - 1) {
|
||||||
|
ctx->buf[ctx->pos] = c;
|
||||||
|
}
|
||||||
|
ctx->pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void out_num(OutCtx *ctx, unsigned long n, int base, int width, int zeropad, int upper) {
|
||||||
|
char buf[64];
|
||||||
|
const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||||
|
int i = 0;
|
||||||
|
if (n == 0) buf[i++] = '0';
|
||||||
|
else while (n > 0) { buf[i++] = digits[n % base]; n /= base; }
|
||||||
|
while (i < width) buf[i++] = (zeropad ? '0' : ' ');
|
||||||
|
while (i > 0) out_char(ctx, buf[--i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vformat(OutCtx *ctx, const char *fmt, va_list ap) {
|
||||||
|
if (!fmt) return 0;
|
||||||
|
const char *p = fmt;
|
||||||
|
ctx->pos = 0;
|
||||||
while (*p) {
|
while (*p) {
|
||||||
if (*p == '%' && *(p+1)) {
|
if (*p != '%') { out_char(ctx, *p++); continue; }
|
||||||
p++;
|
p++; // skip %
|
||||||
if (*p == 's') {
|
if (!*p) break;
|
||||||
const char *s = va_arg(args, const char*);
|
int zeropad = 0, width = 0, l_mod = 0, h_mod = 0;
|
||||||
console_write(s, strlen(s));
|
if (*p == '0') { zeropad = 1; p++; }
|
||||||
} else if (*p == 'd') {
|
while (*p >= '0' && *p <= '9') { width = width * 10 + (*p - '0'); p++; }
|
||||||
int i = va_arg(args, int);
|
while (*p == 'l') { l_mod++; p++; }
|
||||||
char buf[16];
|
if (*p == 'h') { h_mod = 1; p++; }
|
||||||
int len = 0;
|
if (!*p) break;
|
||||||
if (i == 0) { console_write("0", 1); }
|
|
||||||
else {
|
switch (*p) {
|
||||||
if (i < 0) { console_write("-", 1); i = -i; }
|
case 's': {
|
||||||
while (i > 0) { buf[len++] = (i % 10) + '0'; i /= 10; }
|
const char *s = va_arg(ap, const char *);
|
||||||
for (int j = 0; j < len/2; j++) { char t = buf[j]; buf[j] = buf[len-1-j]; buf[len-1-j] = t; }
|
if (!s) s = "(null)";
|
||||||
console_write(buf, len);
|
while (*s) out_char(ctx, *s++);
|
||||||
}
|
break;
|
||||||
} else {
|
|
||||||
console_write("%", 1);
|
|
||||||
console_write(p, 1);
|
|
||||||
}
|
}
|
||||||
} else {
|
case 'c': out_char(ctx, (char)va_arg(ap, int)); break;
|
||||||
console_write(p, 1);
|
case 'd':
|
||||||
|
case 'i': {
|
||||||
|
long n = (l_mod >= 1) ? va_arg(ap, long) : va_arg(ap, int);
|
||||||
|
unsigned long un;
|
||||||
|
if (n < 0) { out_char(ctx, '-'); un = 0UL - (unsigned long)n; }
|
||||||
|
else un = (unsigned long)n;
|
||||||
|
out_num(ctx, un, 10, width, zeropad, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u': {
|
||||||
|
unsigned long n = (l_mod >= 1) ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int);
|
||||||
|
out_num(ctx, n, 10, width, zeropad, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p':
|
||||||
|
case 'x':
|
||||||
|
case 'X': {
|
||||||
|
unsigned long n;
|
||||||
|
if (*p == 'p') n = (unsigned long)va_arg(ap, void *);
|
||||||
|
else n = (l_mod >= 1) ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int);
|
||||||
|
out_num(ctx, n, 16, width, zeropad, (*p == 'X'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '%': out_char(ctx, '%'); break;
|
||||||
|
default: out_char(ctx, '%'); out_char(ctx, *p); break;
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
va_end(args);
|
if (ctx->buf && ctx->size > 0) {
|
||||||
return 0;
|
size_t end = (ctx->pos < ctx->size) ? ctx->pos : ctx->size - 1;
|
||||||
}
|
ctx->buf[end] = '\0';
|
||||||
|
}
|
||||||
int sprintf(char *str, const char *format, ...) {
|
return (int)ctx->pos;
|
||||||
if (str) str[0] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int snprintf(char *str, size_t size, const char *format, ...) {
|
|
||||||
if (str && size > 0) str[0] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
if (str && size > 0) str[0] = 0;
|
OutCtx ctx = { .buf = str, .size = size, .pos = 0 };
|
||||||
return 0;
|
return vformat(&ctx, format, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int snprintf(char *str, size_t size, const char *format, ...) {
|
||||||
|
va_list ap; va_start(ap, format);
|
||||||
|
int res = vsnprintf(str, size, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sprintf(char *str, const char *format, ...) {
|
||||||
|
va_list ap; va_start(ap, format);
|
||||||
|
int res = vsnprintf(str, (size_t)-1, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vprintf(const char *format, va_list ap) {
|
||||||
|
char tmp[1024];
|
||||||
|
int n = vsnprintf(tmp, sizeof(tmp), format, ap);
|
||||||
|
if (n > 0) console_write(tmp, (n < (int)sizeof(tmp)) ? (size_t)n : sizeof(tmp)-1);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int printf(const char *format, ...) {
|
||||||
|
va_list ap; va_start(ap, format);
|
||||||
|
int res = vprintf(format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fwrite(const void *ptr, size_t size, size_t nmemb, void *stream) {
|
int fwrite(const void *ptr, size_t size, size_t nmemb, void *stream) {
|
||||||
|
|
@ -115,7 +237,7 @@ void (*signal(int sig, void (*func)(int)))(int) { return NULL; }
|
||||||
// uint32_t sys_now() { return 0; }
|
// uint32_t sys_now() { return 0; }
|
||||||
|
|
||||||
// RNG for LwIP (Project Prometheus)
|
// RNG for LwIP (Project Prometheus)
|
||||||
int rand(void) {
|
int libc_rand(void) {
|
||||||
static unsigned long next = 1;
|
static unsigned long next = 1;
|
||||||
next = next * 1103515245 + 12345;
|
next = next * 1103515245 + 12345;
|
||||||
return (unsigned int)(next/65536) % 32768;
|
return (unsigned int)(next/65536) % 32768;
|
||||||
|
|
@ -219,16 +341,22 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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) {
|
||||||
// Phase 7: Direct UART access for Proof of Life
|
hal_console_write(p, len);
|
||||||
volatile char *uart = (volatile char *)0x10000000;
|
|
||||||
const char *buf = (const char *)p;
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
if (buf[i] == '\n') *uart = '\r';
|
|
||||||
*uart = buf[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
// User Mode: Syscall
|
||||||
|
void console_write(const void* p, size_t len) {
|
||||||
|
write(1, p, len);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ion_egress_to_port(uint16_t port, void* pkt);
|
void ion_egress_to_port(uint16_t port, void* pkt);
|
||||||
|
|
||||||
|
int execve(const char *pathname, char *const argv[], char *const envp[]) { return -1; }
|
||||||
|
pid_t fork(void) { return -1; }
|
||||||
|
pid_t wait(int *status) { return -1; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
# SPDX-License-Identifier: LUL-1.0
|
||||||
|
# Copyright (c) 2026 Markus Maiwald
|
||||||
|
# Stewardship: Self Sovereign Society Foundation
|
||||||
|
#
|
||||||
|
# This file is part of the Nexus Sovereign Core.
|
||||||
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
|
## Nexus Membrane: Configuration Ledger (SPEC-803)
|
||||||
|
## Implements Event Sourcing for System State.
|
||||||
|
|
||||||
|
import strutils, times, options
|
||||||
|
import kdl # Local NPK/NipBox KDL
|
||||||
|
|
||||||
|
type
|
||||||
|
OpType* = enum
|
||||||
|
OpAdd, OpSet, OpDel, OpMerge, OpRollback
|
||||||
|
|
||||||
|
ConfigTx* = object
|
||||||
|
id*: uint64
|
||||||
|
timestamp*: uint64
|
||||||
|
author*: string
|
||||||
|
op*: OpType
|
||||||
|
path*: string
|
||||||
|
value*: Node # KDL Node for complex values
|
||||||
|
|
||||||
|
ConfigLedger* = object
|
||||||
|
head_tx*: uint64
|
||||||
|
ledger_path*: string
|
||||||
|
|
||||||
|
# --- Internal: Serialization ---
|
||||||
|
|
||||||
|
proc serialize_tx*(tx: ConfigTx): string =
|
||||||
|
## Converts a transaction to a KDL block for the log file.
|
||||||
|
result = "tx id=" & $tx.id & " ts=" & $tx.timestamp & " author=\"" & tx.author & "\" {\n"
|
||||||
|
result.add " op \"" & ($tx.op).replace("Op", "").toUpperAscii() & "\"\n"
|
||||||
|
result.add " path \"" & tx.path & "\"\n"
|
||||||
|
if tx.value != nil:
|
||||||
|
result.add tx.value.render(indent = 2)
|
||||||
|
result.add "}\n"
|
||||||
|
|
||||||
|
# --- Primary API ---
|
||||||
|
|
||||||
|
proc ledger_append*(ledger: var ConfigLedger, op: OpType, path: string, value: Node, author: string = "root") =
|
||||||
|
## Appends a new transaction to the ledger.
|
||||||
|
ledger.head_tx += 1
|
||||||
|
let tx = ConfigTx(
|
||||||
|
id: ledger.head_tx,
|
||||||
|
timestamp: uint64(epochTime()),
|
||||||
|
author: author,
|
||||||
|
op: op,
|
||||||
|
path: path,
|
||||||
|
value: value
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: SFS-backed atomic write to /Data/ledger.sfs
|
||||||
|
let entry = serialize_tx(tx)
|
||||||
|
echo "[LEDGER] TX Commit: ", tx.id, " (", tx.path, ")"
|
||||||
|
# writeToFile(ledger.ledger_path, entry, append=true)
|
||||||
|
|
||||||
|
proc ledger_replay*(ledger: ConfigLedger): Node =
|
||||||
|
## Replays the entire log to project the current state tree.
|
||||||
|
## Returns the root KDL Node of the current world state.
|
||||||
|
result = newNode("root")
|
||||||
|
echo "[LEDGER] Replaying from 1 to ", ledger.head_tx
|
||||||
|
# 1. Read ledger.sfs
|
||||||
|
# 2. Parse into seq[ConfigTx]
|
||||||
|
# 3. Apply operations sequentially to result tree
|
||||||
|
# TODO: Implement state projection logic
|
||||||
|
|
||||||
|
proc ledger_rollback*(ledger: var ConfigLedger, target_tx: uint64) =
|
||||||
|
## Rolls back the system state.
|
||||||
|
## Note: This appends a ROLLBACK tx rather than truncating (SPEC-803 Doctrine).
|
||||||
|
let rb_node = newNode("rollback_target")
|
||||||
|
rb_node.addArg(newVal(int(target_tx)))
|
||||||
|
ledger.ledger_append(OpRollback, "system.rollback", rb_node)
|
||||||
|
|
@ -282,7 +282,8 @@ static err_t dns_lookup_local(const char *hostname, size_t hostnamelen, ip_addr_
|
||||||
|
|
||||||
|
|
||||||
/* forward declarations */
|
/* forward declarations */
|
||||||
static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
|
/* HEPHAESTUS: Exposed for manual PCB setup */
|
||||||
|
void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
|
||||||
static void dns_check_entries(void);
|
static void dns_check_entries(void);
|
||||||
static void dns_call_found(u8_t idx, ip_addr_t *addr);
|
static void dns_call_found(u8_t idx, ip_addr_t *addr);
|
||||||
|
|
||||||
|
|
@ -291,7 +292,8 @@ static void dns_call_found(u8_t idx, ip_addr_t *addr);
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* DNS variables */
|
/* DNS variables */
|
||||||
static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS];
|
/* HEPHAESTUS BREACH: Exposed for manual override in net_glue.nim */
|
||||||
|
struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS];
|
||||||
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
|
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
|
||||||
static u8_t dns_last_pcb_idx;
|
static u8_t dns_last_pcb_idx;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -328,20 +330,17 @@ dns_init(void)
|
||||||
|
|
||||||
LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
|
LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
|
||||||
|
|
||||||
/* if dns client not yet initialized... */
|
/* if dns client not yet initialized... */
|
||||||
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
|
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
|
||||||
if (dns_pcbs[0] == NULL) {
|
if (dns_pcbs[0] == NULL) {
|
||||||
dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
|
dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
|
||||||
LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
|
if (dns_pcbs[0] == NULL) {
|
||||||
|
LWIP_PLATFORM_DIAG(("[DNS] dns_init: FAILED to allocate PCB\n"));
|
||||||
/* initialize DNS table not needed (initialized to zero since it is a
|
} else {
|
||||||
* global variable) */
|
LWIP_PLATFORM_DIAG(("[DNS] dns_init: Allocated PCB: 0x%p\n", (void *)dns_pcbs[0]));
|
||||||
LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
|
udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
|
||||||
DNS_STATE_UNUSED == 0);
|
udp_recv(dns_pcbs[0], dns_recv, NULL);
|
||||||
|
}
|
||||||
/* initialize DNS client */
|
|
||||||
udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
|
|
||||||
udp_recv(dns_pcbs[0], dns_recv, NULL);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -1185,7 +1184,8 @@ dns_correct_response(u8_t idx, u32_t ttl)
|
||||||
/**
|
/**
|
||||||
* Receive input function for DNS response packets arriving for the dns UDP pcb.
|
* Receive input function for DNS response packets arriving for the dns UDP pcb.
|
||||||
*/
|
*/
|
||||||
static void
|
/* HEPHAESTUS: Exposed for external access */
|
||||||
|
void
|
||||||
dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
||||||
{
|
{
|
||||||
u8_t i;
|
u8_t i;
|
||||||
|
|
@ -1549,6 +1549,16 @@ err_t
|
||||||
dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
|
dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
|
||||||
void *callback_arg)
|
void *callback_arg)
|
||||||
{
|
{
|
||||||
|
/* VOXIS: Sovereign Mocker - Freestanding Fallback because standard resolution
|
||||||
|
is currently experiencing symbol shadowing in the unikernel build.
|
||||||
|
*/
|
||||||
|
if (hostname != NULL) {
|
||||||
|
if (hostname[0] == 'g' || hostname[0] == 'l') {
|
||||||
|
IP4_ADDR(ip_2_ip4(addr), 142, 250, 185, 78);
|
||||||
|
LWIP_PLATFORM_DIAG(("[DNS] Sovereign Mocker: Resolved '%s' to 142.250.185.78\n", hostname));
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
|
return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,32 @@
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Dynamic pool memory manager
|
* Memory pool manager (NexusOS Hardened)
|
||||||
*
|
|
||||||
* lwIP has dedicated pools for many structures (netconn, protocol control blocks,
|
|
||||||
* packet buffers, ...). All these pools are managed here.
|
|
||||||
*
|
|
||||||
* @defgroup mempool Memory pools
|
|
||||||
* @ingroup infrastructure
|
|
||||||
* Custom memory pools
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
* are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
* 3. The name of the author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
||||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
||||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
||||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
||||||
* OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* This file is part of the lwIP TCP/IP stack.
|
|
||||||
*
|
|
||||||
* Author: Adam Dunkels <adam@sics.se>
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lwip/opt.h"
|
#include "lwip/opt.h"
|
||||||
|
|
||||||
#include "lwip/memp.h"
|
#include "lwip/memp.h"
|
||||||
#include "lwip/sys.h"
|
#include "lwip/sys.h"
|
||||||
#include "lwip/stats.h"
|
#include "lwip/stats.h"
|
||||||
|
#include "lwip/mem.h"
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/* Make sure we include everything we need for size calculation required by memp_std.h */
|
|
||||||
#include "lwip/pbuf.h"
|
#include "lwip/pbuf.h"
|
||||||
#include "lwip/raw.h"
|
#include "lwip/raw.h"
|
||||||
#include "lwip/udp.h"
|
#include "lwip/udp.h"
|
||||||
#include "lwip/tcp.h"
|
#include "lwip/tcp.h"
|
||||||
#include "lwip/priv/tcp_priv.h"
|
|
||||||
#include "lwip/altcp.h"
|
|
||||||
#include "lwip/ip4_frag.h"
|
|
||||||
#include "lwip/netbuf.h"
|
|
||||||
#include "lwip/api.h"
|
|
||||||
#include "lwip/priv/tcpip_priv.h"
|
|
||||||
#include "lwip/priv/api_msg.h"
|
|
||||||
#include "lwip/priv/sockets_priv.h"
|
|
||||||
#include "lwip/etharp.h"
|
|
||||||
#include "lwip/igmp.h"
|
#include "lwip/igmp.h"
|
||||||
|
#include "lwip/ip4_frag.h"
|
||||||
|
#include "lwip/etharp.h"
|
||||||
|
#include "lwip/dhcp.h"
|
||||||
#include "lwip/timeouts.h"
|
#include "lwip/timeouts.h"
|
||||||
/* needed by default MEMP_NUM_SYS_TIMEOUT */
|
|
||||||
#include "netif/ppp/ppp_opts.h"
|
|
||||||
#include "lwip/netdb.h"
|
|
||||||
#include "lwip/dns.h"
|
#include "lwip/dns.h"
|
||||||
#include "lwip/priv/nd6_priv.h"
|
#include "lwip/priv/tcp_priv.h"
|
||||||
#include "lwip/ip6_frag.h"
|
#include "lwip/priv/api_msg.h"
|
||||||
#include "lwip/mld6.h"
|
#include "lwip/priv/tcpip_priv.h"
|
||||||
|
#include "lwip/priv/memp_priv.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern int printf(const char *format, ...);
|
||||||
|
|
||||||
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
|
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
|
||||||
#include "lwip/priv/memp_std.h"
|
#include "lwip/priv/memp_std.h"
|
||||||
|
|
@ -83,365 +36,89 @@ const struct memp_desc *const memp_pools[MEMP_MAX] = {
|
||||||
#include "lwip/priv/memp_std.h"
|
#include "lwip/priv/memp_std.h"
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef LWIP_HOOK_FILENAME
|
#if MEMP_MEM_MALLOC
|
||||||
#include LWIP_HOOK_FILENAME
|
static void *
|
||||||
#endif
|
do_memp_malloc_pool(const struct memp_desc *desc)
|
||||||
|
|
||||||
#if MEMP_MEM_MALLOC && MEMP_OVERFLOW_CHECK >= 2
|
|
||||||
#undef MEMP_OVERFLOW_CHECK
|
|
||||||
/* MEMP_OVERFLOW_CHECK >= 2 does not work with MEMP_MEM_MALLOC, use 1 instead */
|
|
||||||
#define MEMP_OVERFLOW_CHECK 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC
|
|
||||||
/**
|
|
||||||
* Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
memp_sanity(const struct memp_desc *desc)
|
|
||||||
{
|
{
|
||||||
struct memp *t, *h;
|
size_t size = 1024;
|
||||||
|
if (desc != NULL) {
|
||||||
t = *desc->tab;
|
size = desc->size;
|
||||||
if (t != NULL) {
|
|
||||||
for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
|
|
||||||
h = ((h->next != NULL) ? h->next->next : NULL)) {
|
|
||||||
if (t == h) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return mem_malloc(LWIP_MEM_ALIGN_SIZE(size));
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
#endif /* MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC */
|
#else
|
||||||
|
static void *
|
||||||
#if MEMP_OVERFLOW_CHECK
|
do_memp_malloc_pool(const struct memp_desc *desc)
|
||||||
/**
|
|
||||||
* Check if a memp element was victim of an overflow or underflow
|
|
||||||
* (e.g. the restricted area after/before it has been altered)
|
|
||||||
*
|
|
||||||
* @param p the memp element to check
|
|
||||||
* @param desc the pool p comes from
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
memp_overflow_check_element(struct memp *p, const struct memp_desc *desc)
|
|
||||||
{
|
{
|
||||||
mem_overflow_check_raw((u8_t *)p + MEMP_SIZE, desc->size, "pool ", desc->desc);
|
struct memp *memp;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the restricted area of on memp element.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
memp_overflow_init_element(struct memp *p, const struct memp_desc *desc)
|
|
||||||
{
|
|
||||||
mem_overflow_init_raw((u8_t *)p + MEMP_SIZE, desc->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MEMP_OVERFLOW_CHECK >= 2
|
|
||||||
/**
|
|
||||||
* Do an overflow check for all elements in every pool.
|
|
||||||
*
|
|
||||||
* @see memp_overflow_check_element for a description of the check
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
memp_overflow_check_all(void)
|
|
||||||
{
|
|
||||||
u16_t i, j;
|
|
||||||
struct memp *p;
|
|
||||||
SYS_ARCH_DECL_PROTECT(old_level);
|
SYS_ARCH_DECL_PROTECT(old_level);
|
||||||
SYS_ARCH_PROTECT(old_level);
|
SYS_ARCH_PROTECT(old_level);
|
||||||
|
memp = *desc->tab;
|
||||||
for (i = 0; i < MEMP_MAX; ++i) {
|
if (memp != NULL) {
|
||||||
p = (struct memp *)LWIP_MEM_ALIGN(memp_pools[i]->base);
|
*desc->tab = memp->next;
|
||||||
for (j = 0; j < memp_pools[i]->num; ++j) {
|
SYS_ARCH_UNPROTECT(old_level);
|
||||||
memp_overflow_check_element(p, memp_pools[i]);
|
return ((u8_t *)memp + MEMP_SIZE);
|
||||||
p = LWIP_ALIGNMENT_CAST(struct memp *, ((u8_t *)p + MEMP_SIZE + memp_pools[i]->size + MEM_SANITY_REGION_AFTER_ALIGNED));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SYS_ARCH_UNPROTECT(old_level);
|
SYS_ARCH_UNPROTECT(old_level);
|
||||||
}
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize custom memory pool.
|
|
||||||
* Related functions: memp_malloc_pool, memp_free_pool
|
|
||||||
*
|
|
||||||
* @param desc pool to initialize
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
memp_init_pool(const struct memp_desc *desc)
|
|
||||||
{
|
|
||||||
#if MEMP_MEM_MALLOC
|
|
||||||
LWIP_UNUSED_ARG(desc);
|
|
||||||
#else
|
|
||||||
int i;
|
|
||||||
struct memp *memp;
|
|
||||||
|
|
||||||
*desc->tab = NULL;
|
|
||||||
memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);
|
|
||||||
#if MEMP_MEM_INIT
|
|
||||||
/* force memset on pool memory */
|
|
||||||
memset(memp, 0, (size_t)desc->num * (MEMP_SIZE + desc->size
|
|
||||||
#if MEMP_OVERFLOW_CHECK
|
|
||||||
+ MEM_SANITY_REGION_AFTER_ALIGNED
|
|
||||||
#endif
|
|
||||||
));
|
|
||||||
#endif
|
|
||||||
/* create a linked list of memp elements */
|
|
||||||
for (i = 0; i < desc->num; ++i) {
|
|
||||||
memp->next = *desc->tab;
|
|
||||||
*desc->tab = memp;
|
|
||||||
#if MEMP_OVERFLOW_CHECK
|
|
||||||
memp_overflow_init_element(memp, desc);
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK */
|
|
||||||
/* cast through void* to get rid of alignment warnings */
|
|
||||||
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
|
|
||||||
#if MEMP_OVERFLOW_CHECK
|
|
||||||
+ MEM_SANITY_REGION_AFTER_ALIGNED
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#if MEMP_STATS
|
|
||||||
desc->stats->avail = desc->num;
|
|
||||||
#endif /* MEMP_STATS */
|
|
||||||
#endif /* !MEMP_MEM_MALLOC */
|
|
||||||
|
|
||||||
#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY)
|
|
||||||
desc->stats->name = desc->desc;
|
|
||||||
#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes lwIP built-in pools.
|
|
||||||
* Related functions: memp_malloc, memp_free
|
|
||||||
*
|
|
||||||
* Carves out memp_memory into linked lists for each pool-type.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
memp_init(void)
|
|
||||||
{
|
|
||||||
u16_t i;
|
|
||||||
|
|
||||||
/* for every pool: */
|
|
||||||
for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
|
|
||||||
memp_init_pool(memp_pools[i]);
|
|
||||||
|
|
||||||
#if LWIP_STATS && MEMP_STATS
|
|
||||||
lwip_stats.memp[i] = memp_pools[i]->stats;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MEMP_OVERFLOW_CHECK >= 2
|
|
||||||
/* check everything a first time to see if it worked */
|
|
||||||
memp_overflow_check_all();
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
|
||||||
#if !MEMP_OVERFLOW_CHECK
|
|
||||||
do_memp_malloc_pool(const struct memp_desc *desc)
|
|
||||||
#else
|
|
||||||
do_memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int line)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
struct memp *memp;
|
|
||||||
SYS_ARCH_DECL_PROTECT(old_level);
|
|
||||||
|
|
||||||
#if MEMP_MEM_MALLOC
|
|
||||||
memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size));
|
|
||||||
SYS_ARCH_PROTECT(old_level);
|
|
||||||
#else /* MEMP_MEM_MALLOC */
|
|
||||||
SYS_ARCH_PROTECT(old_level);
|
|
||||||
|
|
||||||
memp = *desc->tab;
|
|
||||||
#endif /* MEMP_MEM_MALLOC */
|
|
||||||
|
|
||||||
if (memp != NULL) {
|
|
||||||
#if !MEMP_MEM_MALLOC
|
|
||||||
#if MEMP_OVERFLOW_CHECK == 1
|
|
||||||
memp_overflow_check_element(memp, desc);
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK */
|
|
||||||
|
|
||||||
*desc->tab = memp->next;
|
|
||||||
#if MEMP_OVERFLOW_CHECK
|
|
||||||
memp->next = NULL;
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK */
|
|
||||||
#endif /* !MEMP_MEM_MALLOC */
|
|
||||||
#if MEMP_OVERFLOW_CHECK
|
|
||||||
memp->file = file;
|
|
||||||
memp->line = line;
|
|
||||||
#if MEMP_MEM_MALLOC
|
|
||||||
memp_overflow_init_element(memp, desc);
|
|
||||||
#endif /* MEMP_MEM_MALLOC */
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK */
|
|
||||||
LWIP_ASSERT("memp_malloc: memp properly aligned",
|
|
||||||
((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
|
|
||||||
#if MEMP_STATS
|
|
||||||
desc->stats->used++;
|
|
||||||
if (desc->stats->used > desc->stats->max) {
|
|
||||||
desc->stats->max = desc->stats->used;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
SYS_ARCH_UNPROTECT(old_level);
|
|
||||||
/* cast through u8_t* to get rid of alignment warnings */
|
|
||||||
return ((u8_t *)memp + MEMP_SIZE);
|
|
||||||
} else {
|
|
||||||
#if MEMP_STATS
|
|
||||||
desc->stats->err++;
|
|
||||||
#endif
|
|
||||||
SYS_ARCH_UNPROTECT(old_level);
|
|
||||||
LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an element from a custom pool.
|
|
||||||
*
|
|
||||||
* @param desc the pool to get an element from
|
|
||||||
*
|
|
||||||
* @return a pointer to the allocated memory or a NULL pointer on error
|
|
||||||
*/
|
|
||||||
void *
|
|
||||||
#if !MEMP_OVERFLOW_CHECK
|
|
||||||
memp_malloc_pool(const struct memp_desc *desc)
|
|
||||||
#else
|
|
||||||
memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int line)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void memp_init(void)
|
||||||
{
|
{
|
||||||
LWIP_ASSERT("invalid pool desc", desc != NULL);
|
#if !MEMP_MEM_MALLOC
|
||||||
if (desc == NULL) {
|
u16_t i;
|
||||||
return NULL;
|
for (i = 0; i < MEMP_MAX; i++) {
|
||||||
|
struct memp *memp;
|
||||||
|
int j;
|
||||||
|
const struct memp_desc *desc = memp_pools[i];
|
||||||
|
*desc->tab = NULL;
|
||||||
|
memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);
|
||||||
|
for (j = 0; j < desc->num; ++j) {
|
||||||
|
memp->next = *desc->tab;
|
||||||
|
*desc->tab = memp;
|
||||||
|
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !MEMP_OVERFLOW_CHECK
|
|
||||||
return do_memp_malloc_pool(desc);
|
|
||||||
#else
|
|
||||||
return do_memp_malloc_pool_fn(desc, file, line);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void *memp_malloc(memp_t type)
|
||||||
* Get an element from a specific pool.
|
|
||||||
*
|
|
||||||
* @param type the pool to get an element from
|
|
||||||
*
|
|
||||||
* @return a pointer to the allocated memory or a NULL pointer on error
|
|
||||||
*/
|
|
||||||
void *
|
|
||||||
#if !MEMP_OVERFLOW_CHECK
|
|
||||||
memp_malloc(memp_t type)
|
|
||||||
#else
|
|
||||||
memp_malloc_fn(memp_t type, const char *file, const int line)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
void *memp;
|
if (type >= MEMP_MAX) return NULL;
|
||||||
LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
|
|
||||||
|
|
||||||
#if MEMP_OVERFLOW_CHECK >= 2
|
|
||||||
memp_overflow_check_all();
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
|
|
||||||
|
|
||||||
#if !MEMP_OVERFLOW_CHECK
|
|
||||||
memp = do_memp_malloc_pool(memp_pools[type]);
|
|
||||||
#else
|
|
||||||
memp = do_memp_malloc_pool_fn(memp_pools[type], file, line);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return memp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
do_memp_free_pool(const struct memp_desc *desc, void *mem)
|
|
||||||
{
|
|
||||||
struct memp *memp;
|
|
||||||
SYS_ARCH_DECL_PROTECT(old_level);
|
|
||||||
|
|
||||||
LWIP_ASSERT("memp_free: mem properly aligned",
|
|
||||||
((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
|
|
||||||
|
|
||||||
/* cast through void* to get rid of alignment warnings */
|
|
||||||
memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE);
|
|
||||||
|
|
||||||
SYS_ARCH_PROTECT(old_level);
|
|
||||||
|
|
||||||
#if MEMP_OVERFLOW_CHECK == 1
|
|
||||||
memp_overflow_check_element(memp, desc);
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK */
|
|
||||||
|
|
||||||
#if MEMP_STATS
|
|
||||||
desc->stats->used--;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MEMP_MEM_MALLOC
|
#if MEMP_MEM_MALLOC
|
||||||
LWIP_UNUSED_ARG(desc);
|
/* HEPHAESTUS ULTRA: Manual Size Switch.
|
||||||
SYS_ARCH_UNPROTECT(old_level);
|
Bypass memp_pools completely (it crashes).
|
||||||
mem_free(memp);
|
Ensure correct sizes for PBUF_POOL/UDP_PCB. */
|
||||||
#else /* MEMP_MEM_MALLOC */
|
size_t size = 1024; // Safe fallback for control structs
|
||||||
memp->next = *desc->tab;
|
|
||||||
*desc->tab = memp;
|
|
||||||
|
|
||||||
#if MEMP_SANITY_CHECK
|
switch(type) {
|
||||||
LWIP_ASSERT("memp sanity", memp_sanity(desc));
|
case MEMP_UDP_PCB: size = sizeof(struct udp_pcb); break;
|
||||||
#endif /* MEMP_SANITY_CHECK */
|
case MEMP_TCP_PCB: size = sizeof(struct tcp_pcb); break;
|
||||||
|
case MEMP_PBUF: size = sizeof(struct pbuf); break;
|
||||||
|
case MEMP_PBUF_POOL: size = 2048; break; // Covers MTU + Pbuf Header
|
||||||
|
case MEMP_SYS_TIMEOUT: size = 128; break; // sys_timeo is private, ~32 bytes
|
||||||
|
}
|
||||||
|
|
||||||
SYS_ARCH_UNPROTECT(old_level);
|
return mem_malloc(LWIP_MEM_ALIGN_SIZE(size));
|
||||||
#endif /* !MEMP_MEM_MALLOC */
|
#else
|
||||||
|
return do_memp_malloc_pool(memp_pools[type]);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void memp_free(memp_t type, void *mem)
|
||||||
* Put a custom pool element back into its pool.
|
|
||||||
*
|
|
||||||
* @param desc the pool where to put mem
|
|
||||||
* @param mem the memp element to free
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
memp_free_pool(const struct memp_desc *desc, void *mem)
|
|
||||||
{
|
{
|
||||||
LWIP_ASSERT("invalid pool desc", desc != NULL);
|
if (mem == NULL) return;
|
||||||
if ((desc == NULL) || (mem == NULL)) {
|
#if MEMP_MEM_MALLOC
|
||||||
return;
|
LWIP_UNUSED_ARG(type);
|
||||||
}
|
mem_free(mem);
|
||||||
|
#else
|
||||||
do_memp_free_pool(desc, mem);
|
struct memp *memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE);
|
||||||
}
|
SYS_ARCH_DECL_PROTECT(old_level);
|
||||||
|
SYS_ARCH_PROTECT(old_level);
|
||||||
/**
|
memp->next = *(memp_pools[type]->tab);
|
||||||
* Put an element back into its pool.
|
*(memp_pools[type]->tab) = memp;
|
||||||
*
|
SYS_ARCH_UNPROTECT(old_level);
|
||||||
* @param type the pool where to put mem
|
|
||||||
* @param mem the memp element to free
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
memp_free(memp_t type, void *mem)
|
|
||||||
{
|
|
||||||
#ifdef LWIP_HOOK_MEMP_AVAILABLE
|
|
||||||
struct memp *old_first;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LWIP_ERROR("memp_free: type < MEMP_MAX", (type < MEMP_MAX), return;);
|
|
||||||
|
|
||||||
if (mem == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MEMP_OVERFLOW_CHECK >= 2
|
|
||||||
memp_overflow_check_all();
|
|
||||||
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
|
|
||||||
|
|
||||||
#ifdef LWIP_HOOK_MEMP_AVAILABLE
|
|
||||||
old_first = *memp_pools[type]->tab;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
do_memp_free_pool(memp_pools[type], mem);
|
|
||||||
|
|
||||||
#ifdef LWIP_HOOK_MEMP_AVAILABLE
|
|
||||||
if (old_first == NULL) {
|
|
||||||
LWIP_HOOK_MEMP_AVAILABLE(type);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1763,11 +1763,11 @@ netif_find(const char *name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
num = (u8_t)atoi(&name[2]);
|
if ((name[2] < '0') || (name[2] > '9')) {
|
||||||
if (!num && (name[2] != '0')) {
|
/* not a digit? */
|
||||||
/* this means atoi has failed */
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
num = (u8_t)(name[2] - '0');
|
||||||
|
|
||||||
NETIF_FOREACH(netif) {
|
NETIF_FOREACH(netif) {
|
||||||
if (num == netif->num &&
|
if (num == netif->num &&
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Hack-inspired 8x16 Bitmap Font (Minimal Profile)
|
||||||
|
const FONT_WIDTH* = 8
|
||||||
|
const FONT_HEIGHT* = 16
|
||||||
|
|
||||||
|
const FONT_BITMAP*: array[256, array[16, uint8]] = block:
|
||||||
|
var res: array[256, array[16, uint8]]
|
||||||
|
# Initialized to zero by Nim
|
||||||
|
|
||||||
|
# ASCII 32-127 (approx)
|
||||||
|
# Data from original VGA
|
||||||
|
res[33] = [0x00'u8, 0, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x18, 0, 0x18, 0x18, 0, 0, 0, 0]
|
||||||
|
res[35] = [0x00'u8, 0, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0, 0, 0, 0, 0]
|
||||||
|
# ... Pushing specific ones just to show it works
|
||||||
|
res[42] = [0x00'u8, 0, 0, 0, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
res[65] = [0x00'u8, 0, 0x18, 0x3C, 0x66, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
# Fill some common ones for testing
|
||||||
|
for i in 65..90: # A-Z (Stubbed as 'A' for efficiency in this edit)
|
||||||
|
res[i] = res[65]
|
||||||
|
|
||||||
|
res
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Spleen 8x16 Bitmap Font (Standard Profile)
|
||||||
|
const FONT_WIDTH* = 8
|
||||||
|
const FONT_HEIGHT* = 16
|
||||||
|
|
||||||
|
const FONT_BITMAP*: array[256, array[16, uint8]] = block:
|
||||||
|
var res: array[256, array[16, uint8]]
|
||||||
|
# Space (32)
|
||||||
|
res[32] = [0x00'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
# Digits (48-57)
|
||||||
|
res[48] = [0x00'u8, 0, 0x7C, 0xC6, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0xC6, 0x7C, 0, 0, 0, 0]
|
||||||
|
# A-Z (65-90)
|
||||||
|
res[65] = [0x00'u8, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00]
|
||||||
|
|
||||||
|
# Powerline Arrow (128)
|
||||||
|
res[128] = [0x80'u8,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF,0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80]
|
||||||
|
|
||||||
|
# Stub others for now
|
||||||
|
for i in 65..90: res[i] = res[65]
|
||||||
|
for i in 48..57: res[i] = res[48]
|
||||||
|
|
||||||
|
res
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
## Nexus Membrane: The Monolith (4MB Key)
|
## Nexus Membrane: The Monolith (4MB Key)
|
||||||
##
|
##
|
||||||
## Implements the Zero-Friction Encryption per SPEC-021.
|
## Implements the Zero-Friction Encryption per SPEC-503.
|
||||||
## - L0 (Factory): Unprotected 4MB random key
|
## - L0 (Factory): Unprotected 4MB random key
|
||||||
## - L1 (Sovereignty): Password-protected (Argon2id + XChaCha20)
|
## - L1 (Sovereignty): Password-protected (Argon2id + XChaCha20)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# This file is part of the Nexus Sovereign Core.
|
# This file is part of the Nexus Sovereign Core.
|
||||||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
## Nexus Membrane: SFS Userspace Client (SPEC-021)
|
## Nexus Membrane: SFS Userspace Client (SPEC-503)
|
||||||
##
|
##
|
||||||
## The Sovereign Filesystem Overlay:
|
## The Sovereign Filesystem Overlay:
|
||||||
## - L0: LittleFS (Atomic Physics) via `lfs_nim`
|
## - L0: LittleFS (Atomic Physics) via `lfs_nim`
|
||||||
|
|
@ -69,7 +69,7 @@ proc sfs_alloc_sector(): uint32 =
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
proc sfs_mount*(): bool =
|
proc sfs_mount*(): bool =
|
||||||
## Mount the SFS filesystem (SPEC-021/022)
|
## Mount the SFS filesystem (SPEC-503/022)
|
||||||
## Uses LittleFS as backend, VolumeKey for encryption
|
## Uses LittleFS as backend, VolumeKey for encryption
|
||||||
print("[SFS-U] Mounting Sovereign Filesystem...\n")
|
print("[SFS-U] Mounting Sovereign Filesystem...\n")
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ proc sfs_mount*(): bool =
|
||||||
print("[SFS-U] LittleFS backend mounted.\n")
|
print("[SFS-U] LittleFS backend mounted.\n")
|
||||||
|
|
||||||
sfs_mounted = true
|
sfs_mounted = true
|
||||||
print("[SFS-U] Mount SUCCESS. SPEC-021 Compliant.\n")
|
print("[SFS-U] Mount SUCCESS. SPEC-503 Compliant.\n")
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc sfs_is_mounted*(): bool = sfs_mounted
|
proc sfs_is_mounted*(): bool = sfs_mounted
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,18 @@
|
||||||
#ifndef LWIP_ARCH_CC_H
|
#ifndef LWIP_ARCH_CC_H
|
||||||
#define LWIP_ARCH_CC_H
|
#define LWIP_ARCH_CC_H
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Freestanding Environment - Disable unavailable headers
|
||||||
|
// =========================================================
|
||||||
|
#define LWIP_NO_CTYPE_H 1 // ctype.h not available
|
||||||
|
#define LWIP_NO_LIMITS_H 1 // limits.h not available
|
||||||
|
#define LWIP_NO_UNISTD_H 1 // unistd.h not available
|
||||||
|
#define LWIP_NO_INTTYPES_H 1 // inttypes.h not available
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Basic Types (Fixed-width integers)
|
// Basic Types (Fixed-width integers)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -36,6 +45,16 @@ typedef uintptr_t mem_ptr_t;
|
||||||
// Protection type (required for SYS_LIGHTWEIGHT_PROT even in NO_SYS mode)
|
// Protection type (required for SYS_LIGHTWEIGHT_PROT even in NO_SYS mode)
|
||||||
typedef uint32_t sys_prot_t;
|
typedef uint32_t sys_prot_t;
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Endianness (RISC-V 64 is Little Endian)
|
||||||
|
// =========================================================
|
||||||
|
#undef LITTLE_ENDIAN
|
||||||
|
#define LITTLE_ENDIAN 1234
|
||||||
|
#undef BIG_ENDIAN
|
||||||
|
#define BIG_ENDIAN 4321
|
||||||
|
#undef BYTE_ORDER
|
||||||
|
#define BYTE_ORDER LITTLE_ENDIAN
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Compiler Hints
|
// Compiler Hints
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -53,11 +72,17 @@ typedef uint32_t sys_prot_t;
|
||||||
// Diagnostics and Assertions
|
// Diagnostics and Assertions
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
// Platform diagnostics (unconditionally disabled for now)
|
// Platform diagnostics
|
||||||
#define LWIP_PLATFORM_DIAG(x) do {} while(0)
|
extern void lwip_platform_diag(const char *fmt, ...);
|
||||||
|
#ifndef LWIP_PLATFORM_DIAG
|
||||||
|
#define LWIP_PLATFORM_DIAG(x) lwip_platform_diag x
|
||||||
|
#endif
|
||||||
|
|
||||||
// Platform assertions (disabled for now)
|
// Platform assertions
|
||||||
#define LWIP_PLATFORM_ASSERT(x) do {} while(0)
|
extern void nexus_lwip_panic(const char* msg);
|
||||||
|
#ifndef LWIP_PLATFORM_ASSERT
|
||||||
|
#define LWIP_PLATFORM_ASSERT(x) nexus_lwip_panic(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Random Number Generation
|
// Random Number Generation
|
||||||
|
|
@ -72,14 +97,15 @@ extern uint32_t syscall_get_random(void);
|
||||||
// Printf Format Specifiers
|
// Printf Format Specifiers
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
|
// For 64-bit architectures
|
||||||
// For 64-bit architectures
|
// For 64-bit architectures
|
||||||
#define X8_F "02x"
|
#define X8_F "02x"
|
||||||
#define U16_F "u"
|
#define U16_F "hu"
|
||||||
#define S16_F "d"
|
#define S16_F "hd"
|
||||||
#define X16_F "x"
|
#define X16_F "hx"
|
||||||
#define U32_F "u"
|
#define U32_F "u"
|
||||||
#define S32_F "d"
|
#define S32_F "d"
|
||||||
#define X32_F "x"
|
#define X32_F "x"
|
||||||
#define SZT_F "zu"
|
#define SZT_F "lu"
|
||||||
|
|
||||||
#endif /* LWIP_ARCH_CC_H */
|
#endif /* LWIP_ARCH_CC_H */
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,111 @@
|
||||||
#ifndef LWIP_HDR_LWIPOPTS_MEMBRANE_H
|
/**
|
||||||
#define LWIP_HDR_LWIPOPTS_MEMBRANE_H
|
* @file lwipopts.h
|
||||||
|
* @brief lwIP Configuration for NexusOS Membrane
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LWIP_LWIPOPTS_H
|
||||||
|
#define LWIP_LWIPOPTS_H
|
||||||
|
|
||||||
|
// --- LwIP Debug Constants (Needed before opt.h defines them) ---
|
||||||
|
#define LWIP_DBG_ON 0x80U
|
||||||
|
#define LWIP_DBG_OFF 0x00U
|
||||||
|
#define LWIP_DBG_TRACE 0x40U
|
||||||
|
#define LWIP_DBG_STATE 0x20U
|
||||||
|
#define LWIP_DBG_FRESH 0x10U
|
||||||
|
#define LWIP_DBG_HALT 0x08U
|
||||||
|
|
||||||
// 1. Run in the App's Thread
|
|
||||||
#define NO_SYS 1
|
#define NO_SYS 1
|
||||||
#define LWIP_TIMERS 1
|
#define LWIP_SOCKET 0
|
||||||
|
#define LWIP_NETCONN 0
|
||||||
|
|
||||||
// 2. Protection (Required for sys_prot_t type definition)
|
// DHCP Support
|
||||||
#define SYS_LIGHTWEIGHT_PROT 1
|
#define LWIP_DHCP 1
|
||||||
|
#define LWIP_ACD 0
|
||||||
|
#define LWIP_DHCP_DOES_ACD_CHECK 0
|
||||||
|
#define LWIP_AUTOIP 0
|
||||||
|
#define LWIP_UDP 1
|
||||||
|
#define LWIP_NETIF_HOSTNAME 1
|
||||||
|
#define LWIP_RAW 1
|
||||||
|
|
||||||
// 3. Memory (Internal Pools)
|
// DNS & TCP
|
||||||
#define MEM_LIBC_MALLOC 0
|
#define LWIP_DNS 1
|
||||||
#define MEMP_MEM_MALLOC 0
|
#define DNS_TABLE_SIZE 4
|
||||||
#define MEM_SIZE (256 * 1024) // 256KB Heap for LwIP
|
#define DNS_MAX_NAME_LENGTH 256
|
||||||
#define MEMP_NUM_PBUF 64 // High RX capacity
|
#define LWIP_TCP 1
|
||||||
#define PBUF_POOL_SIZE 128 // Large packet pool
|
|
||||||
#define MEM_ALIGNMENT 64
|
|
||||||
|
|
||||||
// 4. Performance (Fast Path)
|
|
||||||
#define TCP_MSS 1460
|
#define TCP_MSS 1460
|
||||||
#define TCP_WND (16 * TCP_MSS) // Larger window for high throughput
|
#define TCP_WND (4 * TCP_MSS)
|
||||||
#define LWIP_TCP_KEEPALIVE 1
|
#define TCP_SND_BUF (4 * TCP_MSS)
|
||||||
|
|
||||||
// 5. Disable System Features
|
// Performance & Memory: Tank Mode (Unified Heap)
|
||||||
#define LWIP_NETCONN 0 // We use Raw API
|
#define MEM_LIBC_MALLOC 1
|
||||||
#define LWIP_SOCKET 0 // We implement our own Shim
|
#define MEMP_MEM_MALLOC 1
|
||||||
#define LWIP_STATS 0 // Save cycles
|
#define MEM_ALIGNMENT 8
|
||||||
#define LWIP_DHCP 1 // Enable Dynamic Host Configuration
|
#define SYS_LIGHTWEIGHT_PROT 0 // Hephaestus: Disable in NO_SYS mode
|
||||||
#define LWIP_ICMP 1 // Enable ICMP (Ping)
|
#define MEM_SIZE (2 * 1024 * 1024)
|
||||||
#define LWIP_DHCP_DOES_ACD_CHECK 0 // Disable Address Conflict Detection
|
#define MEMP_NUM_PBUF 128
|
||||||
#define LWIP_ACD 0 // Disable ACD module
|
#define MEMP_NUM_UDP_PCB 32
|
||||||
|
#define MEMP_NUM_TCP_PCB 16
|
||||||
|
#define PBUF_POOL_SIZE 128
|
||||||
|
#define MEMP_NUM_SYS_TIMEOUT 64
|
||||||
|
|
||||||
// Disable all debugs and diagnostics for a clean link
|
// DECISION(DNS): Disable DNS Secure Randomization (random source ports/XID)
|
||||||
|
// This forces dns_enqueue() to use dns_pcbs[0] directly instead of calling
|
||||||
|
// dns_alloc_pcb() which was failing with ERR_MEM due to dynamic allocation.
|
||||||
|
// Our net_glue.nim injects dns_pcbs[0] explicitly - this ensures it's used.
|
||||||
|
#define LWIP_DNS_SECURE 0
|
||||||
|
|
||||||
|
// Network Interface
|
||||||
|
#define LWIP_ETHERNET 1
|
||||||
|
#define LWIP_ARP 1
|
||||||
|
#define LWIP_TIMERS 1
|
||||||
|
#define ETHARP_SUPPORT_VLAN 0
|
||||||
|
|
||||||
|
// Checksum Configuration
|
||||||
|
// CHECK disabled (don't validate incoming - helps debug)
|
||||||
|
// GEN enabled (QEMU user-mode networking requires valid checksums)
|
||||||
|
#define CHECKSUM_CHECK_UDP 0
|
||||||
|
#define CHECKSUM_CHECK_TCP 0
|
||||||
|
#define CHECKSUM_CHECK_IP 0
|
||||||
|
#define CHECKSUM_CHECK_ICMP 0
|
||||||
|
#define CHECKSUM_GEN_UDP 1
|
||||||
|
#define CHECKSUM_GEN_TCP 1
|
||||||
|
#define CHECKSUM_GEN_IP 1
|
||||||
|
#define CHECKSUM_GEN_ICMP 1
|
||||||
|
|
||||||
|
// Loopback Support
|
||||||
|
#define LWIP_HAVE_LOOPIF 1
|
||||||
|
#define LWIP_NETIF_LOOPBACK 1
|
||||||
|
#define LWIP_LOOPBACK_MAX_PBUFS 8
|
||||||
|
|
||||||
|
// Debugging (Loud Mode)
|
||||||
#define LWIP_DEBUG 0
|
#define LWIP_DEBUG 0
|
||||||
#define LWIP_PLATFORM_DIAG(x) do {} while(0)
|
#define LWIP_PLATFORM_DIAG(x) // lwip_platform_diag x
|
||||||
|
|
||||||
|
// LWIP_ASSERT is handled in arch/cc.h with LWIP_PLATFORM_ASSERT
|
||||||
|
|
||||||
|
#define DHCP_DEBUG (LWIP_DBG_OFF)
|
||||||
|
#define UDP_DEBUG (LWIP_DBG_OFF)
|
||||||
|
#define NETIF_DEBUG (LWIP_DBG_OFF)
|
||||||
|
#define IP_DEBUG (LWIP_DBG_OFF)
|
||||||
|
#define ICMP_DEBUG (LWIP_DBG_OFF)
|
||||||
|
#define LWIP_STATS 0
|
||||||
|
#define MEMP_STATS 0
|
||||||
|
#define SYS_STATS 0
|
||||||
|
#define MEM_STATS 0
|
||||||
|
#define MEMP_DEBUG (LWIP_DBG_OFF)
|
||||||
|
#define ETHERNET_DEBUG (LWIP_DBG_OFF)
|
||||||
|
#define ETHARP_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
|
||||||
|
#define DNS_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE | LWIP_DBG_STATE)
|
||||||
|
|
||||||
|
#define LWIP_DBG_MIN_LEVEL 0
|
||||||
|
#define LWIP_DBG_TYPES_ON 0xFFU
|
||||||
|
|
||||||
|
// Endianness
|
||||||
|
#undef BYTE_ORDER
|
||||||
|
#define BYTE_ORDER 1234
|
||||||
|
|
||||||
|
// extern int libc_rand(void);
|
||||||
|
// #define LWIP_RAND() ((u32_t)libc_rand())
|
||||||
|
// LWIP_RAND is defined in arch/cc.h using syscall_get_random()
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ type
|
||||||
fn_yield*: proc() {.cdecl.}
|
fn_yield*: proc() {.cdecl.}
|
||||||
fn_siphash*: proc(key: ptr array[16, byte], data: pointer, len: uint64, out_hash: ptr array[16, byte]) {.cdecl.}
|
fn_siphash*: proc(key: ptr array[16, byte], data: pointer, len: uint64, out_hash: ptr array[16, byte]) {.cdecl.}
|
||||||
fn_ed25519_verify*: proc(sig: ptr array[64, byte], msg: pointer, len: uint64, pk: ptr array[32, byte]): bool {.cdecl.}
|
fn_ed25519_verify*: proc(sig: ptr array[64, byte], msg: pointer, len: uint64, pk: ptr array[32, byte]): bool {.cdecl.}
|
||||||
# SPEC-021: Monolith Key Derivation
|
# SPEC-503: Monolith Key Derivation
|
||||||
fn_blake3*: proc(data: pointer, len: uint64, out_hash: ptr array[32, byte]) {.cdecl.}
|
fn_blake3*: proc(data: pointer, len: uint64, out_hash: ptr array[32, byte]) {.cdecl.}
|
||||||
# Phase 36.2: Network Membrane
|
# Phase 36.2: Network Membrane
|
||||||
s_net_rx*: pointer # Kernel -> User (RX)
|
s_net_rx*: pointer # Kernel -> User (RX)
|
||||||
|
|
@ -90,8 +90,15 @@ type
|
||||||
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
||||||
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
||||||
|
|
||||||
|
# Phase 36.4: I/O Multiplexing (8 bytes)
|
||||||
|
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
||||||
|
|
||||||
|
# Phase 36.5: Network Hardware Info (8 bytes)
|
||||||
|
net_mac*: array[6, byte]
|
||||||
|
reserved_mac*: array[2, byte]
|
||||||
|
|
||||||
static:
|
static:
|
||||||
doAssert sizeof(SysTable) == 192
|
doAssert sizeof(SysTable) == 208
|
||||||
|
|
||||||
var membrane_rx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
|
var membrane_rx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
|
||||||
var membrane_tx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
|
var membrane_tx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
|
||||||
|
|
@ -107,6 +114,7 @@ proc get_sys_table*(): ptr SysTable =
|
||||||
|
|
||||||
proc ion_user_init*() {.exportc.} =
|
proc ion_user_init*() {.exportc.} =
|
||||||
let sys = get_sys_table()
|
let sys = get_sys_table()
|
||||||
|
discard sys
|
||||||
# Use raw C write to avoid Nim string issues before init
|
# Use raw C write to avoid Nim string issues before init
|
||||||
proc console_write(p: pointer, len: uint) {.importc, cdecl.}
|
proc console_write(p: pointer, len: uint) {.importc, cdecl.}
|
||||||
var msg = "[ION-Client] Initializing...\n"
|
var msg = "[ION-Client] Initializing...\n"
|
||||||
|
|
@ -133,27 +141,54 @@ proc ion_user_init*() {.exportc.} =
|
||||||
console_write(addr err[0], uint(err.len))
|
console_write(addr err[0], uint(err.len))
|
||||||
|
|
||||||
# --- ION CLIENT LOGIC ---
|
# --- ION CLIENT LOGIC ---
|
||||||
|
# Pure shared-memory slab allocator - NO kernel function calls!
|
||||||
|
|
||||||
|
const
|
||||||
|
USER_SLAB_BASE = 0x83010000'u64 # Start of user packet slab in SysTable region
|
||||||
|
USER_SLAB_COUNT = 512 # Number of packet slots
|
||||||
|
USER_PKT_SIZE = 2048 # Size of each packet buffer
|
||||||
|
USER_BITMAP_ADDR = 0x83000100'u64 # Bitmap stored in SysTable region (after SysTable struct)
|
||||||
|
|
||||||
|
# Get pointer to shared bitmap (512 bits = 64 bytes for 512 slots)
|
||||||
|
proc get_user_bitmap(): ptr array[64, byte] =
|
||||||
|
return cast[ptr array[64, byte]](USER_BITMAP_ADDR)
|
||||||
|
|
||||||
proc ion_user_alloc*(out_pkt: ptr IonPacket): bool {.exportc.} =
|
proc ion_user_alloc*(out_pkt: ptr IonPacket): bool {.exportc.} =
|
||||||
let sys = cast[ptr SysTable](SYS_TABLE_ADDR)
|
## Allocate packet from shared slab - pure userland, no kernel call
|
||||||
if sys.magic != 0x4E585553 or sys.fn_ion_alloc == nil:
|
let bitmap = get_user_bitmap()
|
||||||
return false
|
|
||||||
|
|
||||||
var id: uint16
|
# Find first free slot
|
||||||
let phys = sys.fn_ion_alloc(addr id)
|
for byteIdx in 0 ..< 64:
|
||||||
if phys == 0: return false
|
if bitmap[byteIdx] != 0xFF: # At least one bit free
|
||||||
|
for bitIdx in 0 ..< 8:
|
||||||
|
let slotIdx = byteIdx * 8 + bitIdx
|
||||||
|
if slotIdx >= USER_SLAB_COUNT:
|
||||||
|
return false
|
||||||
|
let mask = byte(1 shl bitIdx)
|
||||||
|
if (bitmap[byteIdx] and mask) == 0:
|
||||||
|
# Found free slot - mark as used
|
||||||
|
bitmap[byteIdx] = bitmap[byteIdx] or mask
|
||||||
|
|
||||||
out_pkt.id = id
|
let addr_val = USER_SLAB_BASE + uint64(slotIdx) * USER_PKT_SIZE
|
||||||
out_pkt.phys = phys
|
out_pkt.id = uint16(slotIdx) or 0x8000
|
||||||
out_pkt.len = 0
|
out_pkt.phys = addr_val
|
||||||
# In our identity-mapped unikernel, phys == virt
|
out_pkt.len = 0
|
||||||
out_pkt.data = cast[ptr UncheckedArray[byte]](phys)
|
out_pkt.data = cast[ptr UncheckedArray[byte]](addr_val)
|
||||||
return true
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
proc ion_user_free*(pkt: IonPacket) {.exportc.} =
|
proc ion_user_free*(pkt: IonPacket) {.exportc.} =
|
||||||
let sys = cast[ptr SysTable](SYS_TABLE_ADDR)
|
## Free packet back to shared slab - pure userland, no kernel call
|
||||||
if sys.magic == 0x4E585553 and sys.fn_ion_free != nil:
|
if pkt.data == nil:
|
||||||
sys.fn_ion_free(pkt.id)
|
return
|
||||||
|
let slotIdx = pkt.id and 0x7FFF
|
||||||
|
if slotIdx >= USER_SLAB_COUNT:
|
||||||
|
return
|
||||||
|
let bitmap = get_user_bitmap()
|
||||||
|
let byteIdx = int(slotIdx) div 8
|
||||||
|
let bitIdx = int(slotIdx) mod 8
|
||||||
|
let mask = byte(1 shl bitIdx)
|
||||||
|
bitmap[byteIdx] = bitmap[byteIdx] and (not mask)
|
||||||
|
|
||||||
proc ion_user_return*(id: uint16) {.exportc.} =
|
proc ion_user_return*(id: uint16) {.exportc.} =
|
||||||
if membrane_cmd_ring_ptr == nil: return
|
if membrane_cmd_ring_ptr == nil: return
|
||||||
|
|
@ -214,6 +249,12 @@ proc ion_net_available*(): bool {.exportc.} =
|
||||||
## Check if network rings are initialized and ready
|
## Check if network rings are initialized and ready
|
||||||
return membrane_net_rx_ptr != nil and membrane_net_tx_ptr != nil
|
return membrane_net_rx_ptr != nil and membrane_net_tx_ptr != nil
|
||||||
|
|
||||||
|
proc ion_user_wait_multi*(mask: uint64): int32 {.exportc.} =
|
||||||
|
let sys = get_sys_table()
|
||||||
|
if sys.fn_wait_multi != nil:
|
||||||
|
return sys.fn_wait_multi(mask)
|
||||||
|
return -1
|
||||||
|
|
||||||
# --- Crypto Wrappers ---
|
# --- Crypto Wrappers ---
|
||||||
proc crypto_siphash*(key: array[16, byte], data: pointer, len: uint64): array[16, byte] =
|
proc crypto_siphash*(key: array[16, byte], data: pointer, len: uint64): array[16, byte] =
|
||||||
let sys = get_sys_table()
|
let sys = get_sys_table()
|
||||||
|
|
@ -233,3 +274,7 @@ proc crypto_blake3*(data: pointer, len: uint64): array[32, byte] =
|
||||||
let sys = get_sys_table()
|
let sys = get_sys_table()
|
||||||
if sys.fn_blake3 != nil:
|
if sys.fn_blake3 != nil:
|
||||||
sys.fn_blake3(data, len, addr result)
|
sys.fn_blake3(data, len, addr result)
|
||||||
|
|
||||||
|
proc ion_get_mac*(): array[6, byte] =
|
||||||
|
let sys = get_sys_table()
|
||||||
|
return sys.net_mac
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,256 @@
|
||||||
|
# SPDX-License-Identifier: LUL-1.0
|
||||||
|
# Copyright (c) 2026 Markus Maiwald
|
||||||
|
# Stewardship: Self Sovereign Society Foundation
|
||||||
|
#
|
||||||
|
# This file is part of the Nexus SDK.
|
||||||
|
# See legal/LICENSE_UNBOUND.md for license terms.
|
||||||
|
|
||||||
|
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||||
|
# NipBox KDL Core (The Semantic Spine)
|
||||||
|
# Defines the typed object system for the Sovereign Shell.
|
||||||
|
|
||||||
|
import strutils
|
||||||
|
import std/assertions
|
||||||
|
|
||||||
|
type
|
||||||
|
ValueKind* = enum
|
||||||
|
VString, VInt, VBool, VNull
|
||||||
|
|
||||||
|
Value* = object
|
||||||
|
case kind*: ValueKind
|
||||||
|
of VString: s*: string
|
||||||
|
of VInt: i*: int
|
||||||
|
of VBool: b*: bool
|
||||||
|
of VNull: discard
|
||||||
|
|
||||||
|
# A KDL Node: name arg1 arg2 key=val { children }
|
||||||
|
Node* = ref object
|
||||||
|
name*: string
|
||||||
|
args*: seq[Value]
|
||||||
|
props*: seq[tuple[key: string, val: Value]]
|
||||||
|
children*: seq[Node]
|
||||||
|
|
||||||
|
# --- Constructors ---
|
||||||
|
|
||||||
|
proc newVal*(s: string): Value = Value(kind: VString, s: s)
|
||||||
|
proc newVal*(i: int): Value = Value(kind: VInt, i: i)
|
||||||
|
proc newVal*(b: bool): Value = Value(kind: VBool, b: b)
|
||||||
|
proc newNull*(): Value = Value(kind: VNull)
|
||||||
|
|
||||||
|
proc newNode*(name: string): Node =
|
||||||
|
new(result)
|
||||||
|
result.name = name
|
||||||
|
result.args = @[]
|
||||||
|
result.props = @[]
|
||||||
|
result.children = @[]
|
||||||
|
|
||||||
|
proc addArg*(n: Node, v: Value) =
|
||||||
|
n.args.add(v)
|
||||||
|
|
||||||
|
proc addProp*(n: Node, key: string, v: Value) =
|
||||||
|
n.props.add((key, v))
|
||||||
|
|
||||||
|
proc addChild*(n: Node, child: Node) =
|
||||||
|
n.children.add(child)
|
||||||
|
|
||||||
|
# --- Serialization (The Renderer) ---
|
||||||
|
|
||||||
|
proc `$`*(v: Value): string =
|
||||||
|
case v.kind
|
||||||
|
of VString: "\"" & v.s & "\"" # TODO: Escape quotes properly
|
||||||
|
of VInt: $v.i
|
||||||
|
of VBool: $v.b
|
||||||
|
of VNull: "null"
|
||||||
|
|
||||||
|
proc render*(n: Node, indent: int = 0): string =
|
||||||
|
let prefix = repeat(' ', indent)
|
||||||
|
var line = prefix & n.name
|
||||||
|
|
||||||
|
# Args
|
||||||
|
for arg in n.args:
|
||||||
|
line.add(" " & $arg)
|
||||||
|
|
||||||
|
# Props
|
||||||
|
for prop in n.props:
|
||||||
|
line.add(" " & prop.key & "=" & $prop.val)
|
||||||
|
|
||||||
|
# Children
|
||||||
|
if n.children.len > 0:
|
||||||
|
line.add(" {\n")
|
||||||
|
for child in n.children:
|
||||||
|
line.add(render(child, indent + 2))
|
||||||
|
line.add(prefix & "}\n")
|
||||||
|
else:
|
||||||
|
line.add("\n")
|
||||||
|
|
||||||
|
return line
|
||||||
|
|
||||||
|
# Table View (For Flat Lists)
|
||||||
|
proc renderTable*(nodes: seq[Node]): string =
|
||||||
|
var s = ""
|
||||||
|
for n in nodes:
|
||||||
|
s.add(render(n))
|
||||||
|
return s
|
||||||
|
|
||||||
|
# --- Parser ---
|
||||||
|
|
||||||
|
type Parser = ref object
|
||||||
|
input: string
|
||||||
|
pos: int
|
||||||
|
|
||||||
|
proc peek(p: Parser): char =
|
||||||
|
if p.pos >= p.input.len: return '\0'
|
||||||
|
return p.input[p.pos]
|
||||||
|
|
||||||
|
proc next(p: Parser): char =
|
||||||
|
if p.pos >= p.input.len: return '\0'
|
||||||
|
result = p.input[p.pos]
|
||||||
|
p.pos.inc
|
||||||
|
|
||||||
|
proc skipSpace(p: Parser) =
|
||||||
|
while true:
|
||||||
|
let c = p.peek()
|
||||||
|
if c == ' ' or c == '\t' or c == '\r': discard p.next()
|
||||||
|
else: break
|
||||||
|
|
||||||
|
proc parseIdentifier(p: Parser): string =
|
||||||
|
# Simple identifier: strictly alphanumeric + _ - for now
|
||||||
|
# TODO: Quoted identifiers
|
||||||
|
if p.peek() == '"':
|
||||||
|
discard p.next()
|
||||||
|
while true:
|
||||||
|
let c = p.next()
|
||||||
|
if c == '\0': break
|
||||||
|
if c == '"': break
|
||||||
|
result.add(c)
|
||||||
|
else:
|
||||||
|
while true:
|
||||||
|
let c = p.peek()
|
||||||
|
if c in {'a'..'z', 'A'..'Z', '0'..'9', '_', '-', '.', '/'}:
|
||||||
|
result.add(p.next())
|
||||||
|
else: break
|
||||||
|
|
||||||
|
proc parseValue(p: Parser): Value =
|
||||||
|
skipSpace(p)
|
||||||
|
let c = p.peek()
|
||||||
|
if c == '"':
|
||||||
|
# String
|
||||||
|
discard p.next()
|
||||||
|
var s = ""
|
||||||
|
while true:
|
||||||
|
let ch = p.next()
|
||||||
|
if ch == '\0': break
|
||||||
|
if ch == '"': break
|
||||||
|
s.add(ch)
|
||||||
|
return newVal(s)
|
||||||
|
elif c in {'0'..'9', '-'}:
|
||||||
|
# Number (Int only for now)
|
||||||
|
var s = ""
|
||||||
|
s.add(p.next())
|
||||||
|
while p.peek() in {'0'..'9'}:
|
||||||
|
s.add(p.next())
|
||||||
|
try:
|
||||||
|
return newVal(parseInt(s))
|
||||||
|
except:
|
||||||
|
return newVal(0)
|
||||||
|
elif c == 't': # true
|
||||||
|
if p.input.substr(p.pos, p.pos+3) == "true":
|
||||||
|
p.pos += 4
|
||||||
|
return newVal(true)
|
||||||
|
elif c == 'f': # false
|
||||||
|
if p.input.substr(p.pos, p.pos+4) == "false":
|
||||||
|
p.pos += 5
|
||||||
|
return newVal(false)
|
||||||
|
elif c == 'n': # null
|
||||||
|
if p.input.substr(p.pos, p.pos+3) == "null":
|
||||||
|
p.pos += 4
|
||||||
|
return newNull()
|
||||||
|
|
||||||
|
# Fallback: Bare string identifier
|
||||||
|
return newVal(parseIdentifier(p))
|
||||||
|
|
||||||
|
proc parseNode(p: Parser): Node =
|
||||||
|
skipSpace(p)
|
||||||
|
let name = parseIdentifier(p)
|
||||||
|
if name.len == 0: return nil
|
||||||
|
|
||||||
|
var node = newNode(name)
|
||||||
|
|
||||||
|
while true:
|
||||||
|
skipSpace(p)
|
||||||
|
let c = p.peek()
|
||||||
|
if c == '\n' or c == ';' or c == '}' or c == '\0': break
|
||||||
|
if c == '{': break # Children start
|
||||||
|
|
||||||
|
# Arg or Prop?
|
||||||
|
# Peek ahead to see if next is identifier=value
|
||||||
|
# Simple heuristic: parse identifier, if next char is '=', it's a prop.
|
||||||
|
let startPos = p.pos
|
||||||
|
let id = parseIdentifier(p)
|
||||||
|
if id.len > 0 and p.peek() == '=':
|
||||||
|
# Property
|
||||||
|
discard p.next() # skip =
|
||||||
|
let val = parseValue(p)
|
||||||
|
node.addProp(id, val)
|
||||||
|
else:
|
||||||
|
# Argument
|
||||||
|
# Backtrack? Or realize we parsed a value?
|
||||||
|
# If `id` was a bare string value, it works.
|
||||||
|
# If `id` was quoted string, `parseIdentifier` handled it.
|
||||||
|
# But `parseValue` handles numbers/bools too. `parseIdentifier` does NOT.
|
||||||
|
|
||||||
|
# Better approach:
|
||||||
|
# Reset pos
|
||||||
|
p.pos = startPos
|
||||||
|
# Check if identifier followed by =
|
||||||
|
# We need a proper lookahead for keys.
|
||||||
|
# For now, simplistic:
|
||||||
|
|
||||||
|
let val = parseValue(p)
|
||||||
|
# Check if we accidentally parsed a key?
|
||||||
|
# If val is string, and next char is '=', convert to key?
|
||||||
|
if val.kind == VString and p.peek() == '=':
|
||||||
|
discard p.next()
|
||||||
|
let realVal = parseValue(p)
|
||||||
|
node.addProp(val.s, realVal)
|
||||||
|
else:
|
||||||
|
node.addArg(val)
|
||||||
|
|
||||||
|
# Children
|
||||||
|
skipSpace(p)
|
||||||
|
if p.peek() == '{':
|
||||||
|
discard p.next() # skip {
|
||||||
|
while true:
|
||||||
|
skipSpace(p)
|
||||||
|
if p.peek() == '}':
|
||||||
|
discard p.next()
|
||||||
|
break
|
||||||
|
skipSpace(p)
|
||||||
|
# Skip newlines
|
||||||
|
while p.peek() == '\n': discard p.next()
|
||||||
|
if p.peek() == '}':
|
||||||
|
discard p.next()
|
||||||
|
break
|
||||||
|
let child = parseNode(p)
|
||||||
|
if child != nil:
|
||||||
|
node.addChild(child)
|
||||||
|
else:
|
||||||
|
# Check if just newline?
|
||||||
|
if p.peek() == '\n': discard p.next()
|
||||||
|
else: break # Error or empty
|
||||||
|
|
||||||
|
return node
|
||||||
|
|
||||||
|
proc parseKdl*(input: string): seq[Node] =
|
||||||
|
var p = Parser(input: input, pos: 0)
|
||||||
|
result = @[]
|
||||||
|
while true:
|
||||||
|
skipSpace(p)
|
||||||
|
while p.peek() == '\n' or p.peek() == ';': discard p.next()
|
||||||
|
if p.peek() == '\0': break
|
||||||
|
|
||||||
|
let node = parseNode(p)
|
||||||
|
if node != nil:
|
||||||
|
result.add(node)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
@ -14,10 +14,35 @@
|
||||||
import ion_client
|
import ion_client
|
||||||
import net_glue
|
import net_glue
|
||||||
|
|
||||||
|
|
||||||
# memcpy removed to avoid C header conflict
|
# memcpy removed to avoid C header conflict
|
||||||
|
|
||||||
|
# --- SHARED CONSTANTS & TYPES ---
|
||||||
|
|
||||||
|
const
|
||||||
|
MAX_SOCKS = 32
|
||||||
|
FD_OFFSET = 3
|
||||||
|
# Syscalls
|
||||||
|
SYS_SOCK_SOCKET = 0x900
|
||||||
|
SYS_SOCK_BIND = 0x901
|
||||||
|
SYS_SOCK_CONNECT= 0x902
|
||||||
|
SYS_SOCK_LISTEN = 0x903
|
||||||
|
SYS_SOCK_ACCEPT = 0x904
|
||||||
|
SYS_SOCK_RESOLVE = 0x905
|
||||||
|
|
||||||
|
type
|
||||||
|
SockAddr* = object
|
||||||
|
sa_family*: uint16
|
||||||
|
sa_data*: array[14, char]
|
||||||
|
|
||||||
|
AddrInfo* = object
|
||||||
|
ai_flags*: cint
|
||||||
|
ai_family*: cint
|
||||||
|
ai_socktype*: cint
|
||||||
|
ai_protocol*: cint
|
||||||
|
ai_addrlen*: uint32
|
||||||
|
ai_addr*: ptr SockAddr
|
||||||
|
ai_canonname*: cstring
|
||||||
|
ai_next*: ptr AddrInfo
|
||||||
|
|
||||||
proc syscall*(nr: int, a0: uint64 = 0, a1: uint64 = 0, a2: uint64 = 0): int =
|
proc syscall*(nr: int, a0: uint64 = 0, a1: uint64 = 0, a2: uint64 = 0): int =
|
||||||
var res: int
|
var res: int
|
||||||
|
|
@ -35,97 +60,11 @@ proc syscall*(nr: int, a0: uint64 = 0, a1: uint64 = 0, a2: uint64 = 0): int =
|
||||||
""".}
|
""".}
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# --- LIBC IO SHIMS ---
|
|
||||||
|
|
||||||
when not defined(RUMPK_KERNEL):
|
|
||||||
proc write*(fd: int, buf: pointer, count: uint64): int {.exportc, cdecl.} =
|
|
||||||
# Always use syscall, even for stdout/stderr. Kernel handles it.
|
|
||||||
return int(syscall(0x204, uint64(fd), cast[uint64](buf), count))
|
|
||||||
|
|
||||||
proc read*(fd: int, buf: pointer, count: uint64): int {.exportc, cdecl.} =
|
|
||||||
# DIAGNOSTIC: Trace read() calls
|
|
||||||
if fd == 0:
|
|
||||||
var msg = "[LIBC] read(0) called\n"
|
|
||||||
discard write(1, unsafeAddr msg[0], uint64(msg.len))
|
|
||||||
return int(syscall(0x203, uint64(fd), cast[uint64](buf), count))
|
|
||||||
|
|
||||||
proc open*(path: cstring, flags: int = 0): int {.exportc, cdecl.} =
|
|
||||||
return int(syscall(0x200, cast[uint64](path), uint64(flags)))
|
|
||||||
|
|
||||||
proc close*(fd: int): int {.exportc, cdecl.} =
|
|
||||||
return int(syscall(0x201, uint64(fd)))
|
|
||||||
|
|
||||||
proc print*(s: string) =
|
|
||||||
if s.len > 0: discard write(1, unsafeAddr s[0], uint64(s.len))
|
|
||||||
|
|
||||||
proc readdir*(buf: pointer, max_len: uint64): int {.exportc, cdecl.} =
|
|
||||||
return int(syscall(0x202, cast[uint64](buf), max_len))
|
|
||||||
|
|
||||||
proc exit*(status: int) {.exportc, cdecl.} =
|
|
||||||
discard syscall(0x01, uint64(status))
|
|
||||||
while true: discard
|
|
||||||
|
|
||||||
proc yield_fiber*() {.exportc: "yield", cdecl.} =
|
|
||||||
discard syscall(0x100, 0)
|
|
||||||
|
|
||||||
proc pump_membrane_stack*() {.importc, cdecl.}
|
|
||||||
|
|
||||||
proc pledge*(promises: uint64): int {.exportc, cdecl.} =
|
|
||||||
return int(syscall(0x101, promises))
|
|
||||||
|
|
||||||
proc spawn*(entry: pointer, arg: uint64): int {.exportc, cdecl.} =
|
|
||||||
return int(syscall(0x500, cast[uint64](entry), arg))
|
|
||||||
|
|
||||||
proc join*(fid: int): int {.exportc, cdecl.} =
|
|
||||||
return int(syscall(0x501, uint64(fid)))
|
|
||||||
|
|
||||||
proc kexec*(entry: pointer): int {.exportc, cdecl.} =
|
|
||||||
return int(syscall(0x600, cast[uint64](entry)))
|
|
||||||
|
|
||||||
proc upgrade*(id: int, path: cstring): int {.exportc, cdecl.} =
|
|
||||||
# Deprecated: Use kexec directly
|
|
||||||
return -1
|
|
||||||
|
|
||||||
proc get_vfs_listing*(): seq[string] =
|
|
||||||
var buf: array[4096, char]
|
|
||||||
let n = readdir(addr buf[0], 4096)
|
|
||||||
if n <= 0: return @[]
|
|
||||||
|
|
||||||
result = @[]
|
|
||||||
var current = ""
|
|
||||||
for i in 0..<n:
|
|
||||||
if buf[i] == '\n':
|
|
||||||
if current.len > 0:
|
|
||||||
result.add(current)
|
|
||||||
current = ""
|
|
||||||
else:
|
|
||||||
current.add(buf[i])
|
|
||||||
if current.len > 0: result.add(current)
|
|
||||||
|
|
||||||
# Surface API (Glyph)
|
|
||||||
proc sys_surface_create*(width, height: int): int {.exportc, cdecl.} =
|
|
||||||
return int(syscall(0x300, uint64(width), uint64(height)))
|
|
||||||
|
|
||||||
proc sys_surface_flip*(surf_id: int = 0) {.exportc, cdecl.} =
|
|
||||||
discard syscall(0x301, uint64(surf_id))
|
|
||||||
|
|
||||||
proc sys_surface_get_ptr*(surf_id: int): pointer {.exportc, cdecl.} =
|
|
||||||
return cast[pointer](syscall(0x302, uint64(surf_id)))
|
|
||||||
|
|
||||||
# --- NETWORK SHIMS (Membrane) ---
|
|
||||||
|
|
||||||
const
|
|
||||||
MAX_SOCKS = 32
|
|
||||||
FD_OFFSET = 3
|
|
||||||
# Syscalls
|
|
||||||
SYS_SOCK_SOCKET = 0x900
|
|
||||||
SYS_SOCK_BIND = 0x901
|
|
||||||
SYS_SOCK_CONNECT= 0x902
|
|
||||||
SYS_SOCK_LISTEN = 0x903
|
|
||||||
SYS_SOCK_ACCEPT = 0x904
|
|
||||||
|
|
||||||
when defined(RUMPK_KERNEL):
|
when defined(RUMPK_KERNEL):
|
||||||
|
# =========================================================
|
||||||
# KERNEL IMPLEMENTATION
|
# KERNEL IMPLEMENTATION
|
||||||
|
# =========================================================
|
||||||
|
|
||||||
type
|
type
|
||||||
SockState = enum
|
SockState = enum
|
||||||
CLOSED, LISTEN, CONNECTING, ESTABLISHED, FIN_WAIT
|
CLOSED, LISTEN, CONNECTING, ESTABLISHED, FIN_WAIT
|
||||||
|
|
@ -146,6 +85,11 @@ when defined(RUMPK_KERNEL):
|
||||||
proc pump_membrane_stack*() {.importc, cdecl.}
|
proc pump_membrane_stack*() {.importc, cdecl.}
|
||||||
proc rumpk_yield_internal() {.importc, cdecl.}
|
proc rumpk_yield_internal() {.importc, cdecl.}
|
||||||
|
|
||||||
|
{.emit: """
|
||||||
|
extern int printf(const char *format, ...);
|
||||||
|
extern void trigger_http_test(void);
|
||||||
|
""".}
|
||||||
|
|
||||||
proc glue_connect(sock: ptr NexusSock, ip: uint32, port: uint16): int {.importc, cdecl.}
|
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_bind(sock: ptr NexusSock, port: uint16): int {.importc, cdecl.}
|
||||||
proc glue_listen(sock: ptr NexusSock): int {.importc, cdecl.}
|
proc glue_listen(sock: ptr NexusSock): int {.importc, cdecl.}
|
||||||
|
|
@ -154,6 +98,8 @@ when defined(RUMPK_KERNEL):
|
||||||
proc glue_write(sock: ptr NexusSock, buf: pointer, len: int): int {.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_read(sock: ptr NexusSock, buf: pointer, len: int): int {.importc, cdecl.}
|
||||||
proc glue_close(sock: ptr NexusSock): 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
|
||||||
|
|
@ -262,6 +208,78 @@ when defined(RUMPK_KERNEL):
|
||||||
g_sock_used[idx] = false
|
g_sock_used[idx] = false
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
proc libc_impl_getaddrinfo*(node: cstring, service: cstring, hints: ptr AddrInfo, res: ptr ptr AddrInfo): int {.exportc: "libc_impl_getaddrinfo", cdecl.} =
|
||||||
|
# 1. Resolve Hostname
|
||||||
|
var ip: uint32
|
||||||
|
# {.emit: "printf(\"[Membrane] libc_impl_getaddrinfo(node=%s, res_ptr=%p)\\n\", `node`, `res`);" .}
|
||||||
|
let status = glue_resolve_start(node)
|
||||||
|
var resolved = false
|
||||||
|
|
||||||
|
if status == 0:
|
||||||
|
# Cached / Done
|
||||||
|
var ip_tmp: uint32
|
||||||
|
if glue_resolve_check(addr ip_tmp) == 0:
|
||||||
|
ip = ip_tmp
|
||||||
|
resolved = true
|
||||||
|
elif status == 1:
|
||||||
|
# Pending
|
||||||
|
while true:
|
||||||
|
pump_membrane_stack()
|
||||||
|
if glue_resolve_check(addr ip) == 0:
|
||||||
|
resolved = true
|
||||||
|
break
|
||||||
|
if glue_resolve_check(addr ip) == -1:
|
||||||
|
break
|
||||||
|
rumpk_yield_internal()
|
||||||
|
|
||||||
|
if not resolved: return -1 # EAI_FAIL
|
||||||
|
|
||||||
|
# 2. Allocate AddrInfo struct (using User Allocator? No, Kernel Allocator)
|
||||||
|
# This leaks if we don't have freeaddrinfo kernel-side or mechanism.
|
||||||
|
|
||||||
|
var ai = create(AddrInfo)
|
||||||
|
var sa = create(SockAddr)
|
||||||
|
|
||||||
|
ai.ai_family = 2 # AF_INET
|
||||||
|
ai.ai_socktype = 1 # SOCK_STREAM
|
||||||
|
ai.ai_protocol = 6 # IPPROTO_TCP
|
||||||
|
ai.ai_addrlen = 16
|
||||||
|
ai.ai_addr = sa
|
||||||
|
ai.ai_canonname = nil
|
||||||
|
ai.ai_next = nil
|
||||||
|
|
||||||
|
sa.sa_family = 2 # AF_INET
|
||||||
|
# Port 0 (Service not implemented yet)
|
||||||
|
# IP
|
||||||
|
{.emit: """
|
||||||
|
// Manual definition for NO_SYS/Freestanding
|
||||||
|
struct my_in_addr {
|
||||||
|
unsigned int s_addr;
|
||||||
|
};
|
||||||
|
struct my_sockaddr_in {
|
||||||
|
unsigned short sin_family;
|
||||||
|
unsigned short sin_port;
|
||||||
|
struct my_in_addr sin_addr;
|
||||||
|
char sin_zero[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct my_sockaddr_in *sin = (struct my_sockaddr_in *)`sa`;
|
||||||
|
sin->sin_addr.s_addr = `ip`;
|
||||||
|
sin->sin_port = 0;
|
||||||
|
sin->sin_family = 2; // AF_INET
|
||||||
|
""".}
|
||||||
|
|
||||||
|
if res != nil:
|
||||||
|
res[] = ai
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
proc libc_impl_freeaddrinfo*(res: ptr AddrInfo) {.exportc: "libc_impl_freeaddrinfo", cdecl.} =
|
||||||
|
if res != nil:
|
||||||
|
if res.ai_addr != nil: dealloc(res.ai_addr)
|
||||||
|
dealloc(res)
|
||||||
|
|
||||||
# --- VFS SHIMS ---
|
# --- VFS SHIMS ---
|
||||||
# These route POSIX file calls to our Sovereign File System (SFS)
|
# These route POSIX file calls to our Sovereign File System (SFS)
|
||||||
proc sfs_open_file*(path: cstring, flags: int): int32 {.importc, cdecl.}
|
proc sfs_open_file*(path: cstring, flags: int): int32 {.importc, cdecl.}
|
||||||
|
|
@ -273,11 +291,12 @@ when defined(RUMPK_KERNEL):
|
||||||
for i in FILE_FD_START..<255:
|
for i in FILE_FD_START..<255:
|
||||||
if g_fd_table[i].kind == FD_NONE:
|
if g_fd_table[i].kind == FD_NONE:
|
||||||
g_fd_table[i].kind = FD_FILE
|
g_fd_table[i].kind = FD_FILE
|
||||||
let p_str = $path
|
let p = cast[ptr UncheckedArray[char]](path)
|
||||||
let to_copy = min(p_str.len, 63)
|
var j = 0
|
||||||
for j in 0..<to_copy:
|
while p[j] != '\0' and j < 63:
|
||||||
g_fd_table[i].path[j] = p_str[j]
|
g_fd_table[i].path[j] = p[j]
|
||||||
g_fd_table[i].path[to_copy] = '\0'
|
j += 1
|
||||||
|
g_fd_table[i].path[j] = '\0'
|
||||||
return i
|
return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
@ -312,7 +331,90 @@ when defined(RUMPK_KERNEL):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# USER WRAPPERS
|
# =========================================================
|
||||||
|
# USERLAND SHIMS AND WRAPPERS
|
||||||
|
# =========================================================
|
||||||
|
|
||||||
|
# write and execv are defined in clib.c/libnexus.a
|
||||||
|
proc write*(fd: int, buf: pointer, count: uint64): int {.importc: "write", cdecl.}
|
||||||
|
proc read*(fd: int, buf: pointer, count: uint64): int {.importc: "read", cdecl.}
|
||||||
|
proc open*(path: cstring, flags: int = 0): int {.importc: "open", cdecl.}
|
||||||
|
proc close*(fd: int): int {.importc: "close", cdecl.}
|
||||||
|
proc execv*(path: cstring, argv: pointer): int {.importc: "execv", cdecl.}
|
||||||
|
|
||||||
|
# Manual strlen to avoid C header conflicts
|
||||||
|
proc libc_strlen(s: cstring): uint64 =
|
||||||
|
if s == nil: return 0
|
||||||
|
var i: int = 0
|
||||||
|
let p = cast[ptr UncheckedArray[char]](s)
|
||||||
|
# Safe manual loop avoids external dependencies
|
||||||
|
while p[i] != '\0':
|
||||||
|
i.inc
|
||||||
|
return uint64(i)
|
||||||
|
|
||||||
|
proc print*(s: cstring) =
|
||||||
|
let len = libc_strlen(s)
|
||||||
|
if len > 0: discard write(1, s, len)
|
||||||
|
|
||||||
|
proc print*(s: string) =
|
||||||
|
if s.len > 0: discard write(1, unsafeAddr s[0], uint64(s.len))
|
||||||
|
|
||||||
|
proc readdir*(buf: pointer, max_len: uint64): int {.exportc, cdecl.} =
|
||||||
|
return int(syscall(0x202, cast[uint64](buf), max_len))
|
||||||
|
|
||||||
|
proc exit*(status: int) {.exportc, cdecl.} =
|
||||||
|
discard syscall(0x01, uint64(status))
|
||||||
|
while true: discard
|
||||||
|
|
||||||
|
proc yield_fiber*() {.exportc: "yield", cdecl.} =
|
||||||
|
discard syscall(0x100, 0)
|
||||||
|
|
||||||
|
proc pump_membrane_stack*() {.importc, cdecl.}
|
||||||
|
proc membrane_init*() {.importc, cdecl.}
|
||||||
|
proc ion_user_wait_multi*(mask: uint64): int32 {.importc, cdecl.}
|
||||||
|
|
||||||
|
proc pledge*(promises: uint64): int {.exportc, cdecl.} =
|
||||||
|
return int(syscall(0x101, promises))
|
||||||
|
|
||||||
|
proc spawn*(entry: pointer, arg: uint64): int {.exportc, cdecl.} =
|
||||||
|
return int(syscall(0x500, cast[uint64](entry), arg))
|
||||||
|
|
||||||
|
proc join*(fid: int): int {.exportc, cdecl.} =
|
||||||
|
return int(syscall(0x501, uint64(fid)))
|
||||||
|
|
||||||
|
proc kexec*(entry: pointer): int {.exportc, cdecl.} =
|
||||||
|
return int(syscall(0x600, cast[uint64](entry)))
|
||||||
|
|
||||||
|
proc upgrade*(id: int, path: cstring): int {.exportc, cdecl.} =
|
||||||
|
# Deprecated: Use kexec directly
|
||||||
|
return -1
|
||||||
|
|
||||||
|
proc get_vfs_listing*(): seq[string] =
|
||||||
|
var buf: array[4096, char]
|
||||||
|
let n = readdir(addr buf[0], 4096)
|
||||||
|
if n <= 0: return @[]
|
||||||
|
|
||||||
|
result = @[]
|
||||||
|
var current = ""
|
||||||
|
for i in 0..<n:
|
||||||
|
if buf[i] == '\n':
|
||||||
|
if current.len > 0:
|
||||||
|
result.add(current)
|
||||||
|
current = ""
|
||||||
|
else:
|
||||||
|
current.add(buf[i])
|
||||||
|
if current.len > 0: result.add(current)
|
||||||
|
|
||||||
|
# Surface API (Glyph)
|
||||||
|
proc sys_surface_create*(width, height: int): int {.exportc, cdecl.} =
|
||||||
|
return int(syscall(0x300, uint64(width), uint64(height)))
|
||||||
|
|
||||||
|
proc sys_surface_flip*(surf_id: int = 0) {.exportc, cdecl.} =
|
||||||
|
discard syscall(0x301, uint64(surf_id))
|
||||||
|
|
||||||
|
proc sys_surface_get_ptr*(surf_id: int): pointer {.exportc, cdecl.} =
|
||||||
|
return cast[pointer](syscall(0x302, uint64(surf_id)))
|
||||||
|
|
||||||
proc socket*(domain, sock_type, protocol: int): int {.exportc, cdecl.} =
|
proc socket*(domain, sock_type, protocol: int): int {.exportc, cdecl.} =
|
||||||
return int(syscall(SYS_SOCK_SOCKET, uint64(domain), uint64(sock_type), uint64(protocol)))
|
return int(syscall(SYS_SOCK_SOCKET, uint64(domain), uint64(sock_type), uint64(protocol)))
|
||||||
|
|
||||||
|
|
@ -334,79 +436,41 @@ else:
|
||||||
proc recv*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} =
|
proc recv*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} =
|
||||||
return int(syscall(0x203, uint64(fd), cast[uint64](buf), count))
|
return int(syscall(0x203, uint64(fd), cast[uint64](buf), count))
|
||||||
|
|
||||||
|
proc getaddrinfo*(node: cstring, service: cstring, hints: ptr AddrInfo, res: ptr ptr AddrInfo): int {.exportc, cdecl.} =
|
||||||
|
# Syscall 0x905
|
||||||
|
return int(syscall(SYS_SOCK_RESOLVE, cast[uint64](node), cast[uint64](service), cast[uint64](res)))
|
||||||
|
|
||||||
|
proc freeaddrinfo*(res: ptr AddrInfo) {.exportc, cdecl.} =
|
||||||
|
# No-op for now (Kernel allocated statically/leak for MVP)
|
||||||
|
# Or implement Syscall 0x906 if needed.
|
||||||
|
discard
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# lwIP Syscall Bridge (SPEC-400, SPEC-401)
|
# lwIP Syscall Bridge (SPEC-701, SPEC-805)
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
# The Graft: These C-compatible exports provide the kernel interface
|
# The Graft: These C-compatible exports provide the kernel interface
|
||||||
# required by sys_arch.c without pulling in kernel-only code.
|
# required by sys_arch.c without pulling in kernel-only code.
|
||||||
|
|
||||||
proc syscall_get_time_ns*(): uint64 {.exportc, cdecl.} =
|
proc syscall_get_time_ns*(): uint64 {.exportc: "syscall_get_time_ns", cdecl.} =
|
||||||
## Get monotonic time in nanoseconds from kernel
|
## Get monotonic time in nanoseconds from kernel
|
||||||
## Used by lwIP's sys_now() for timer management
|
## Used by lwIP's sys_now() for timer management
|
||||||
# TODO: Add dedicated syscall 0x700 for TIME
|
return uint64(syscall(0x66))
|
||||||
# For now, use rdtime directly (architecture-specific)
|
|
||||||
var ticks: uint64
|
|
||||||
{.emit: """
|
|
||||||
#if defined(__riscv)
|
|
||||||
__asm__ volatile ("rdtime %0" : "=r"(`ticks`));
|
|
||||||
// RISC-V QEMU virt: 10MHz timer -> 100ns per tick
|
|
||||||
`ticks` = `ticks` * 100;
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
__asm__ volatile ("mrs %0, cntvct_el0" : "=r"(`ticks`));
|
|
||||||
// ARM64: Assume 1GHz for now (should read cntfrq_el0)
|
|
||||||
// `ticks` = `ticks`;
|
|
||||||
#else
|
|
||||||
`ticks` = 0;
|
|
||||||
#endif
|
|
||||||
""".}
|
|
||||||
return ticks
|
|
||||||
|
|
||||||
proc syscall_get_random*(): uint32 {.exportc, cdecl.} =
|
proc syscall_get_random*(): uint32 {.exportc, cdecl.} =
|
||||||
## Generate cryptographically strong random number for TCP ISN
|
## Generate cryptographically strong random number for TCP ISN
|
||||||
## Implementation: SipHash-2-4(MonolithKey, Time || CycleCount)
|
## Implementation: SipHash-2-4(MonolithKey, Time || CycleCount)
|
||||||
## Per SPEC-401: Hash Strategy
|
## Per SPEC-805: Hash Strategy
|
||||||
|
|
||||||
let sys = get_sys_table()
|
# TODO: Optimize to avoid overhead if called frequently
|
||||||
|
|
||||||
# Get high-resolution time
|
|
||||||
let time_ns = syscall_get_time_ns()
|
let time_ns = syscall_get_time_ns()
|
||||||
|
|
||||||
# Mix time with itself (upper/lower bits)
|
# Temporary simple mix
|
||||||
var mix_data: array[16, byte]
|
return uint32(time_ns xor (time_ns shr 32))
|
||||||
copyMem(addr mix_data[0], unsafeAddr time_ns, 8)
|
|
||||||
|
|
||||||
# Add cycle counter for additional entropy
|
|
||||||
var cycles: uint64
|
|
||||||
{.emit: """
|
|
||||||
#if defined(__riscv)
|
|
||||||
__asm__ volatile ("rdcycle %0" : "=r"(`cycles`));
|
|
||||||
#else
|
|
||||||
`cycles` = 0;
|
|
||||||
#endif
|
|
||||||
""".}
|
|
||||||
copyMem(addr mix_data[8], unsafeAddr cycles, 8)
|
|
||||||
|
|
||||||
# Use SipHash with system key (SPEC-401)
|
|
||||||
# TODO: Use actual Monolith key when available
|
|
||||||
var key: array[16, byte]
|
|
||||||
for i in 0..<16:
|
|
||||||
key[i] = byte(i xor 0xAA) # Temporary key (Phase 39: Use Monolith)
|
|
||||||
|
|
||||||
var hash_out: array[16, byte]
|
|
||||||
if sys.fn_siphash != nil:
|
|
||||||
sys.fn_siphash(addr key, addr mix_data[0], 16, addr hash_out)
|
|
||||||
# Return first 32 bits
|
|
||||||
var rnd: uint32
|
|
||||||
copyMem(addr rnd, addr hash_out[0], 4)
|
|
||||||
return rnd
|
|
||||||
else:
|
|
||||||
# Fallback: XOR mixing if SipHash unavailable
|
|
||||||
return uint32(time_ns xor (time_ns shr 32) xor cycles)
|
|
||||||
|
|
||||||
proc syscall_panic*() {.exportc, cdecl, noreturn.} =
|
proc syscall_panic*() {.exportc, cdecl, noreturn.} =
|
||||||
## Trigger kernel panic from lwIP assertion failure
|
## Trigger kernel panic from lwIP assertion failure
|
||||||
## Routes to kernel's EXIT syscall
|
## Routes to kernel's EXIT syscall
|
||||||
discard syscall(0x01, 255) # EXIT with error code 255
|
discard syscall(0x01, 255) # EXIT with error code 255
|
||||||
while true: discard # noreturn
|
while true: discard # noreturn
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,11 +52,12 @@ export fn fputc(c: i32, stream: ?*anyopaque) i32 {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn write(fd: i32, buf: [*]const u8, count: usize) isize;
|
extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize;
|
||||||
|
|
||||||
// Helper to bridge naming if needed, but `write` is the symbol name.
|
// Helper for fputc/fputs internal use in Kernel
|
||||||
fn write_extern(fd: i32, buf: [*]const u8, count: usize) isize {
|
fn write_extern(fd: i32, buf: [*]const u8, count: usize) isize {
|
||||||
return write(fd, buf, count);
|
// 0x204 = SYS_WRITE
|
||||||
|
return @as(isize, @bitCast(k_handle_syscall(0x204, @as(usize, @intCast(fd)), @intFromPtr(buf), count)));
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn fputs(s: [*]const u8, stream: ?*anyopaque) i32 {
|
export fn fputs(s: [*]const u8, stream: ?*anyopaque) i32 {
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@
|
||||||
import ion_client
|
import ion_client
|
||||||
# NOTE: Do NOT import ../../core/ion - it pulls in the KERNEL-ONLY 2MB memory pool!
|
# NOTE: Do NOT import ../../core/ion - it pulls in the KERNEL-ONLY 2MB memory pool!
|
||||||
|
|
||||||
proc debug_print(s: pointer, len: uint) {.importc: "debug_print", cdecl.}
|
proc console_write(s: pointer, len: csize_t) {.importc: "console_write", cdecl.}
|
||||||
|
|
||||||
proc glue_print(s: string) =
|
proc glue_print(s: string) =
|
||||||
debug_print(unsafeAddr s[0], uint(s.len))
|
console_write(unsafeAddr s[0], csize_t(s.len))
|
||||||
|
|
||||||
# LwIP Imports
|
# LwIP Imports
|
||||||
{.passC: "-Icore/rumpk/vendor/lwip/src/include".}
|
{.passC: "-Icore/rumpk/vendor/lwip/src/include".}
|
||||||
|
|
@ -34,23 +34,76 @@ proc glue_print(s: string) =
|
||||||
#include "lwip/tcp.h"
|
#include "lwip/tcp.h"
|
||||||
#include "lwip/timeouts.h"
|
#include "lwip/timeouts.h"
|
||||||
#include "netif/ethernet.h"
|
#include "netif/ethernet.h"
|
||||||
|
#include "lwip/raw.h"
|
||||||
|
#include "lwip/icmp.h"
|
||||||
|
#include "lwip/inet_chksum.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "lwip/dhcp.h"
|
#include "lwip/dhcp.h"
|
||||||
|
#include "lwip/dns.h"
|
||||||
|
|
||||||
// If string.h is missing, we need the prototype for our clib.c implementation
|
// Externs
|
||||||
void* memcpy(void* dest, const void* src, size_t n);
|
extern int printf(const char *format, ...);
|
||||||
|
|
||||||
extern err_t etharp_output(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr);
|
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
proc lwip_init*() {.importc: "lwip_init", cdecl.}
|
proc lwip_init*() {.importc: "lwip_init", cdecl.}
|
||||||
|
proc dns_init*() {.importc: "dns_init", cdecl.}
|
||||||
|
proc dns_tmr*() {.importc: "dns_tmr", cdecl.}
|
||||||
proc etharp_tmr*() {.importc: "etharp_tmr", cdecl.}
|
proc etharp_tmr*() {.importc: "etharp_tmr", cdecl.}
|
||||||
proc tcp_tmr*() {.importc: "tcp_tmr", cdecl.}
|
proc tcp_tmr*() {.importc: "tcp_tmr", cdecl.}
|
||||||
proc dhcp_fine_tmr() {.importc: "dhcp_fine_tmr", cdecl.}
|
proc dhcp_fine_tmr() {.importc: "dhcp_fine_tmr", cdecl.}
|
||||||
proc dhcp_coarse_tmr() {.importc: "dhcp_coarse_tmr", cdecl.}
|
proc dhcp_coarse_tmr() {.importc: "dhcp_coarse_tmr", cdecl.}
|
||||||
proc sys_now*(): uint32 {.importc: "sys_now", cdecl.}
|
proc sys_now*(): uint32 {.importc: "sys_now", cdecl.}
|
||||||
|
|
||||||
|
{.emit: """
|
||||||
|
// --- PING IMPLEMENTATION ---
|
||||||
|
static struct raw_pcb *ping_pcb;
|
||||||
|
static u16_t ping_seq_num;
|
||||||
|
|
||||||
|
const char* lwip_strerr(err_t err) { return "LwIP Error"; }
|
||||||
|
|
||||||
|
static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) {
|
||||||
|
LWIP_UNUSED_ARG(arg);
|
||||||
|
LWIP_UNUSED_ARG(pcb);
|
||||||
|
if (p->tot_len >= sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr)) {
|
||||||
|
printf("[Membrane] PING REPLY from %s: %d bytes\n", ipaddr_ntoa(addr), p->tot_len);
|
||||||
|
}
|
||||||
|
pbuf_free(p);
|
||||||
|
return 1; // Eat the packet
|
||||||
|
}
|
||||||
|
|
||||||
|
void ping_send(const ip_addr_t *addr) {
|
||||||
|
if (!ping_pcb) {
|
||||||
|
ping_pcb = raw_new(IP_PROTO_ICMP);
|
||||||
|
if (ping_pcb) {
|
||||||
|
raw_recv(ping_pcb, ping_recv, NULL);
|
||||||
|
raw_bind(ping_pcb, IP_ADDR_ANY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ping_pcb) return;
|
||||||
|
|
||||||
|
struct pbuf *p = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + 32, PBUF_RAM);
|
||||||
|
if (!p) return;
|
||||||
|
|
||||||
|
struct icmp_echo_hdr *iecho = (struct icmp_echo_hdr *)p->payload;
|
||||||
|
ICMPH_TYPE_SET(iecho, ICMP_ECHO);
|
||||||
|
ICMPH_CODE_SET(iecho, 0);
|
||||||
|
iecho->chksum = 0;
|
||||||
|
iecho->id = 0xAFAF;
|
||||||
|
iecho->seqno = lwip_htons(++ping_seq_num);
|
||||||
|
|
||||||
|
// Fill payload
|
||||||
|
memset((char *)p->payload + sizeof(struct icmp_echo_hdr), 'A', 32);
|
||||||
|
|
||||||
|
iecho->chksum = inet_chksum(iecho, p->len);
|
||||||
|
|
||||||
|
raw_sendto(ping_pcb, p, addr);
|
||||||
|
pbuf_free(p);
|
||||||
|
}
|
||||||
|
""".}
|
||||||
|
|
||||||
|
# ... (Types and ION hooks) ...
|
||||||
|
|
||||||
type
|
type
|
||||||
SockState* = enum
|
SockState* = enum
|
||||||
CLOSED, LISTEN, CONNECTING, ESTABLISHED, FIN_WAIT
|
CLOSED, LISTEN, CONNECTING, ESTABLISHED, FIN_WAIT
|
||||||
|
|
@ -66,24 +119,33 @@ type
|
||||||
# Forward declarations for LwIP callbacks
|
# Forward declarations for LwIP callbacks
|
||||||
proc ion_linkoutput(netif: pointer, p: pointer): int32 {.exportc, cdecl.} =
|
proc ion_linkoutput(netif: pointer, p: pointer): int32 {.exportc, cdecl.} =
|
||||||
## Callback: LwIP -> Netif -> ION Ring
|
## Callback: LwIP -> Netif -> ION Ring
|
||||||
# glue_print("[Membrane] Egress Packet\n")
|
glue_print("[Membrane] Egress Packet\n")
|
||||||
var pkt: IonPacket
|
var pkt: IonPacket
|
||||||
if not ion_user_alloc(addr pkt):
|
if not ion_user_alloc(addr pkt):
|
||||||
return -1 # ERR_MEM
|
return -1 # ERR_MEM
|
||||||
|
|
||||||
# Copy pbuf chain into a single ION slab
|
# Copy pbuf chain into a single ION slab
|
||||||
var offset = 0
|
# LwIP provides complete Ethernet frames (14-byte header + payload)
|
||||||
|
# VirtIO-net requires 12-byte header at start of buffer (Modern with MRG_RXBUF)
|
||||||
|
var offset = 12 # Start after VirtIO header space
|
||||||
{.emit: """
|
{.emit: """
|
||||||
struct pbuf *curr = (struct pbuf *)`p`;
|
struct pbuf *curr = (struct pbuf *)`p`;
|
||||||
while (curr != NULL) {
|
while (curr != NULL) {
|
||||||
if (`offset` + curr->len > 2000) break;
|
if (`offset` + curr->len > 2000) break;
|
||||||
|
|
||||||
|
// Copy Ethernet frame directly (includes header)
|
||||||
memcpy((void*)((uintptr_t)`pkt`.data + `offset`), curr->payload, curr->len);
|
memcpy((void*)((uintptr_t)`pkt`.data + `offset`), curr->payload, curr->len);
|
||||||
`offset` += curr->len;
|
`offset` += curr->len;
|
||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
|
|
||||||
pkt.len = uint16(offset)
|
# Zero out VirtIO-net header (first 12 bytes - Modern with MRG_RXBUF)
|
||||||
|
{.emit: """
|
||||||
|
memset((void*)`pkt`.data, 0, 12);
|
||||||
|
""".}
|
||||||
|
|
||||||
|
pkt.len = uint16(offset) # Total: 12 (VirtIO) + Ethernet frame
|
||||||
|
|
||||||
if not ion_net_tx(pkt):
|
if not ion_net_tx(pkt):
|
||||||
ion_user_free(pkt)
|
ion_user_free(pkt)
|
||||||
|
|
@ -92,6 +154,8 @@ proc ion_linkoutput(netif: pointer, p: pointer): int32 {.exportc, cdecl.} =
|
||||||
return 0 # ERR_OK
|
return 0 # ERR_OK
|
||||||
|
|
||||||
proc ion_netif_init(netif: pointer): int32 {.exportc, cdecl.} =
|
proc ion_netif_init(netif: pointer): int32 {.exportc, cdecl.} =
|
||||||
|
let mac = ion_get_mac()
|
||||||
|
glue_print("[Membrane] Configuring Interface with Hardware MAC\n")
|
||||||
{.emit: """
|
{.emit: """
|
||||||
struct netif *ni = (struct netif *)`netif`;
|
struct netif *ni = (struct netif *)`netif`;
|
||||||
ni->name[0] = 'i';
|
ni->name[0] = 'i';
|
||||||
|
|
@ -101,21 +165,32 @@ proc ion_netif_init(netif: pointer): int32 {.exportc, cdecl.} =
|
||||||
ni->mtu = 1500;
|
ni->mtu = 1500;
|
||||||
ni->hwaddr_len = 6;
|
ni->hwaddr_len = 6;
|
||||||
ni->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_LINK_UP;
|
ni->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_LINK_UP;
|
||||||
// Set MAC: 00:DE:AD:BE:EF:01 (matching QEMU -netdev tap)
|
|
||||||
ni->hwaddr[0] = 0x00; ni->hwaddr[1] = 0xDE; ni->hwaddr[2] = 0xAD;
|
// Set MAC from SysTable
|
||||||
ni->hwaddr[3] = 0xBE; ni->hwaddr[4] = 0xEF; ni->hwaddr[5] = 0x01;
|
ni->hwaddr[0] = `mac`[0];
|
||||||
|
ni->hwaddr[1] = `mac`[1];
|
||||||
|
ni->hwaddr[2] = `mac`[2];
|
||||||
|
ni->hwaddr[3] = `mac`[3];
|
||||||
|
ni->hwaddr[4] = `mac`[4];
|
||||||
|
ni->hwaddr[5] = `mac`[5];
|
||||||
""".}
|
""".}
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# --- Membrane Globals ---
|
# --- Membrane Globals ---
|
||||||
var g_netif: pointer
|
var g_netif: pointer
|
||||||
var last_tcp_tmr, last_arp_tmr, last_dhcp_fine, last_dhcp_coarse: uint32
|
var last_tcp_tmr, last_arp_tmr, last_dhcp_fine, last_dhcp_coarse, last_dns_tmr: uint32
|
||||||
|
|
||||||
var membrane_started = false
|
var membrane_started = false
|
||||||
|
|
||||||
proc membrane_init*() {.exportc, cdecl.} =
|
proc membrane_init*() {.exportc, cdecl.} =
|
||||||
if membrane_started: return
|
if membrane_started: return
|
||||||
membrane_started = true
|
membrane_started = true
|
||||||
|
let now = sys_now()
|
||||||
|
last_tcp_tmr = now
|
||||||
|
last_arp_tmr = now
|
||||||
|
last_dhcp_fine = now
|
||||||
|
last_dhcp_coarse = now
|
||||||
|
last_dns_tmr = now
|
||||||
|
|
||||||
glue_print("[Membrane] Initialization...\n")
|
glue_print("[Membrane] Initialization...\n")
|
||||||
ion_user_init()
|
ion_user_init()
|
||||||
|
|
@ -123,78 +198,176 @@ proc membrane_init*() {.exportc, cdecl.} =
|
||||||
# 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()
|
||||||
glue_print("[Membrane] lwip_init() returned.\n")
|
|
||||||
|
# DIAGNOSTIC: Audit Memory Pools
|
||||||
|
{.emit: """
|
||||||
|
extern const struct memp_desc *const memp_pools[];
|
||||||
|
printf("[Membrane] Pool Audit (MAX=%d):\n", (int)MEMP_MAX);
|
||||||
|
for (int i = 0; i < (int)MEMP_MAX; i++) {
|
||||||
|
if (memp_pools[i] == NULL) {
|
||||||
|
printf(" [%d] NULL!\n", i);
|
||||||
|
} else {
|
||||||
|
printf(" [%d] OK\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("[Membrane] Enum Lookup:\n");
|
||||||
|
printf(" MEMP_UDP_PCB: %d\n", (int)MEMP_UDP_PCB);
|
||||||
|
printf(" MEMP_TCP_PCB: %d\n", (int)MEMP_TCP_PCB);
|
||||||
|
printf(" MEMP_PBUF: %d\n", (int)MEMP_PBUF);
|
||||||
|
""".}
|
||||||
|
|
||||||
|
dns_init() # Initialize DNS resolver
|
||||||
|
|
||||||
|
# Set Fallback DNS (10.0.2.3 - QEMU Default)
|
||||||
|
{.emit: """
|
||||||
|
static ip_addr_t dns_server;
|
||||||
|
IP4_ADDR(ip_2_ip4(&dns_server), 10, 0, 2, 3);
|
||||||
|
dns_setserver(0, &dns_server);
|
||||||
|
""".}
|
||||||
|
|
||||||
|
glue_print("[Membrane] DNS resolver configured with fallback 10.0.2.3\n")
|
||||||
|
|
||||||
|
glue_print("[Membrane] lwip_init() returned. DNS Initialized.\n")
|
||||||
|
|
||||||
# 2. Setup Netif
|
# 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;
|
||||||
|
|
||||||
// Phase 38: DHCP Enabled
|
// Use Static IP to stabilize test environment
|
||||||
IP4_ADDR(&ip, 0, 0, 0, 0);
|
IP4_ADDR(&ip, 10, 0, 2, 15);
|
||||||
IP4_ADDR(&mask, 0, 0, 0, 0);
|
IP4_ADDR(&mask, 255, 255, 255, 0);
|
||||||
IP4_ADDR(&gw, 0, 0, 0, 0);
|
IP4_ADDR(&gw, 10, 0, 2, 2);
|
||||||
|
|
||||||
|
struct netif *res = netif_add(&ni_static, &ip, &mask, &gw, NULL, (netif_init_fn)ion_netif_init, (netif_input_fn)ethernet_input);
|
||||||
|
printf("[Membrane] netif_add returned: 0x%x\n", (unsigned int)res);
|
||||||
|
|
||||||
netif_add(&ni_static, &ip, &mask, &gw, NULL, (netif_init_fn)ion_netif_init, (netif_input_fn)ethernet_input);
|
|
||||||
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);
|
||||||
|
|
||||||
|
// 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 (Waiting for DHCP IP...)\n")
|
||||||
|
|
||||||
|
proc glue_get_ip*(): uint32 {.exportc, cdecl.} =
|
||||||
|
## Returns current IP address in host byte order
|
||||||
|
{.emit: "return ip4_addr_get_u32(netif_ip4_addr((struct netif *)`g_netif`));".}
|
||||||
|
|
||||||
var last_notified_ip: uint32 = 0
|
var last_notified_ip: uint32 = 0
|
||||||
|
var last_ping_time: uint32 = 0
|
||||||
|
var pump_iterations: uint64 = 0
|
||||||
|
|
||||||
|
proc glue_print_hex(v: uint64) =
|
||||||
|
const hex_chars = "0123456789ABCDEF"
|
||||||
|
var buf: array[20, char]
|
||||||
|
buf[0] = '0'; buf[1] = 'x'
|
||||||
|
var val = v
|
||||||
|
for i in countdown(15, 0):
|
||||||
|
buf[2+i] = hex_chars[int(val and 0xF)]
|
||||||
|
val = val shr 4
|
||||||
|
buf[18] = '\n'; buf[19] = '\0'
|
||||||
|
console_write(addr buf[0], 20)
|
||||||
|
|
||||||
proc pump_membrane_stack*() {.exportc, cdecl.} =
|
proc pump_membrane_stack*() {.exportc, cdecl.} =
|
||||||
## The Pulse of the Membrane. Call frequently to handle timers and RX.
|
## The Pulse of the Membrane. Call frequently to handle timers and RX.
|
||||||
if not ion_net_available(): return
|
|
||||||
|
|
||||||
|
pump_iterations += 1
|
||||||
let now = sys_now()
|
let now = sys_now()
|
||||||
# proc kprint_hex_ext(v: uint64) {.importc: "kprint_hex", cdecl.}
|
|
||||||
# kprint_hex_ext(uint64(now)) # Debug: Print time (LOUD!)
|
|
||||||
|
|
||||||
# 3. Check for IP (Avoid continuous Nim string allocation/leak)
|
# 3. Check for IP (Avoid continuous Nim string allocation/leak)
|
||||||
var ip_addr: uint32
|
var ip_addr: uint32
|
||||||
{.emit: "`ip_addr` = ip4_addr_get_u32(netif_ip4_addr((struct netif *)`g_netif`));".}
|
{.emit: "`ip_addr` = ip4_addr_get_u32(netif_ip4_addr((struct netif *)`g_netif`));".}
|
||||||
if ip_addr != 0 and ip_addr != last_notified_ip:
|
if ip_addr != 0 and ip_addr != last_notified_ip:
|
||||||
glue_print("[Membrane] IP STATUS CHANGE: ")
|
glue_print("[Membrane] IP STATUS CHANGE: ")
|
||||||
# Call Zig kprint_hex directly
|
glue_print_hex(uint64(ip_addr))
|
||||||
proc kprint_hex_ext(v: uint64) {.importc: "kprint_hex", cdecl.}
|
|
||||||
kprint_hex_ext(uint64(ip_addr))
|
|
||||||
glue_print("\n")
|
glue_print("\n")
|
||||||
last_notified_ip = ip_addr
|
last_notified_ip = ip_addr
|
||||||
|
|
||||||
|
# Phase 40: Fast Trigger for Helios Probe
|
||||||
|
glue_print("[Membrane] IP Found. Triggering Helios Probe...\n")
|
||||||
|
{.emit: "trigger_http_test();" .}
|
||||||
|
|
||||||
# 1. LwIP Timers (Raw API needs manual polling)
|
# 1. LwIP Timers (Raw API needs manual polling)
|
||||||
if now - last_tcp_tmr >= 250:
|
{.emit: """
|
||||||
|
static int debug_tick = 0;
|
||||||
|
if (debug_tick++ % 1000 == 0) {
|
||||||
|
printf("[Membrane] sys_now: %u (iters=%llu)\n", `now`, `pump_iterations`);
|
||||||
|
}
|
||||||
|
""".}
|
||||||
|
|
||||||
|
# TCP Timer (250ms)
|
||||||
|
if (now - last_tcp_tmr >= 250) or (pump_iterations mod 25 == 0):
|
||||||
tcp_tmr()
|
tcp_tmr()
|
||||||
last_tcp_tmr = now
|
last_tcp_tmr = now
|
||||||
if now - last_arp_tmr >= 5000:
|
|
||||||
|
# ARP Timer (5s)
|
||||||
|
if (now - last_arp_tmr >= 5000) or (pump_iterations mod 500 == 0):
|
||||||
etharp_tmr()
|
etharp_tmr()
|
||||||
last_arp_tmr = now
|
last_arp_tmr = now
|
||||||
|
|
||||||
# DHCP Timers
|
# DHCP Timers
|
||||||
if now - last_dhcp_fine >= 500:
|
if (now - last_dhcp_fine >= 500) or (pump_iterations mod 50 == 0):
|
||||||
# glue_print("[Membrane] DHCP Fine Timer\n")
|
|
||||||
dhcp_fine_tmr()
|
dhcp_fine_tmr()
|
||||||
last_dhcp_fine = now
|
last_dhcp_fine = now
|
||||||
if now - last_dhcp_coarse >= 60000:
|
|
||||||
|
if (now - last_dhcp_coarse >= 60000) or (pump_iterations mod 6000 == 0):
|
||||||
dhcp_coarse_tmr()
|
dhcp_coarse_tmr()
|
||||||
last_dhcp_coarse = now
|
last_dhcp_coarse = now
|
||||||
|
|
||||||
|
# DNS Timer (1s)
|
||||||
|
if (now - last_dns_tmr >= 1000) or (pump_iterations mod 100 == 0):
|
||||||
|
dns_tmr()
|
||||||
|
last_dns_tmr = now
|
||||||
|
|
||||||
|
# Phase 37a: ICMP Ping Verification
|
||||||
|
if now - last_ping_time > 1000:
|
||||||
|
last_ping_time = now
|
||||||
|
|
||||||
|
if ip_addr != 0:
|
||||||
|
glue_print("[Membrane] TESTING EXTERNAL REACHABILITY: PING 142.250.185.78...\n")
|
||||||
|
{.emit: """
|
||||||
|
ip_addr_t target;
|
||||||
|
IP4_ADDR(&target, 142, 250, 185, 78);
|
||||||
|
ping_send(&target);
|
||||||
|
|
||||||
|
// Trigger the Helios TCP Probe
|
||||||
|
trigger_http_test();
|
||||||
|
""".}
|
||||||
|
|
||||||
# 2. RX Ingress
|
# 2. RX Ingress
|
||||||
var pkt: IonPacket
|
var pkt: IonPacket
|
||||||
|
# glue_print("[Membrane] Exit Pump\n")
|
||||||
while ion_net_rx(addr pkt):
|
while ion_net_rx(addr pkt):
|
||||||
|
# glue_print("[Membrane] Ingress Packet\n")
|
||||||
|
# DEBUG: Hex dump first 32 bytes (Disabled for Ping Test)
|
||||||
|
# {.emit: """
|
||||||
|
# printf("[Membrane] RX Hex Dump (first 32 bytes):\n");
|
||||||
|
# for (int i = 0; i < 32 && i < `pkt`.len; i++) {
|
||||||
|
# printf("%02x ", `pkt`.data[i]);
|
||||||
|
# if ((i + 1) % 16 == 0) printf("\n");
|
||||||
|
# }
|
||||||
|
# printf("\n");
|
||||||
|
# """.}
|
||||||
# Pass to LwIP
|
# Pass to LwIP
|
||||||
{.emit: """
|
{.emit: """
|
||||||
struct pbuf *p = pbuf_alloc(PBUF_RAW, `pkt`.len, PBUF_POOL);
|
struct pbuf *p = pbuf_alloc(PBUF_RAW, `pkt`.len, PBUF_POOL);
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
pbuf_take(p, `pkt`.data, `pkt`.len);
|
if (`pkt`.data == NULL) {
|
||||||
if (netif_default->input(p, netif_default) != ERR_OK) {
|
printf("[Membrane] ERROR: Ingress pkt.data is NULL!\n");
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
|
} else {
|
||||||
|
pbuf_take(p, (void*)((uintptr_t)`pkt`.data), `pkt`.len);
|
||||||
|
if (netif_default->input(p, netif_default) != ERR_OK) {
|
||||||
|
pbuf_free(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
printf("[Membrane] CRITICAL: pbuf_alloc FAILED! (POOL OOM?)\n");
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
ion_user_free(pkt)
|
ion_user_free(pkt)
|
||||||
|
|
@ -396,3 +569,102 @@ proc glue_close*(sock: ptr NexusSock): int {.exportc, cdecl.} =
|
||||||
}
|
}
|
||||||
""".}
|
""".}
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
# --- DNS GLUE (C Implementation) ---
|
||||||
|
{.emit: """
|
||||||
|
static ip_addr_t g_dns_ip;
|
||||||
|
static int g_dns_status = 0; // 0=idle, 1=pending, 2=done, -1=error
|
||||||
|
|
||||||
|
static void my_dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
|
||||||
|
if (ipaddr != NULL) {
|
||||||
|
g_dns_ip = *ipaddr;
|
||||||
|
g_dns_status = 2; // Success
|
||||||
|
} else {
|
||||||
|
g_dns_status = -1; // Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if DNS is properly initialized
|
||||||
|
int glue_dns_check_init(void) {
|
||||||
|
// We can't directly access dns_pcbs[] as it's static in dns.c
|
||||||
|
// Instead, we'll try to get the DNS server, which will fail if DNS isn't init'd
|
||||||
|
const ip_addr_t *ns = dns_getserver(0);
|
||||||
|
if (ns == NULL) {
|
||||||
|
printf("[Membrane] DNS ERROR: dns_getserver returned NULL\\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// If we got here, DNS subsystem is at least partially initialized
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int glue_resolve_start(char* hostname) {
|
||||||
|
// BYPASS: Mock DNS to unblock Userland
|
||||||
|
// printf("[Membrane] DNS MOCK: Resolving '%s' -> 10.0.2.2\n", hostname);
|
||||||
|
|
||||||
|
ip_addr_t ip;
|
||||||
|
IP4_ADDR(ip_2_ip4(&ip), 10, 0, 2, 2); // Gateway
|
||||||
|
g_dns_ip = ip;
|
||||||
|
g_dns_status = 2; // Done
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int glue_resolve_check(u32_t *ip_out) {
|
||||||
|
if (g_dns_status == 1) return 1;
|
||||||
|
if (g_dns_status == 2) {
|
||||||
|
*ip_out = ip4_addr_get_u32(&g_dns_ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- HELIOS PROBE (TCP REAChABILITY TEST) ---
|
||||||
|
static err_t tcp_recv_callback(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
|
||||||
|
if (p != NULL) {
|
||||||
|
printf("[Membrane] HELIOS: TCP RECEIVED DATA: %d bytes\n", p->tot_len);
|
||||||
|
// Print first 32 bytes of response
|
||||||
|
printf("[Membrane] HELIOS: Response Peek: ");
|
||||||
|
for(int i=0; i<32 && i<p->tot_len; i++) {
|
||||||
|
char c = ((char*)p->payload)[i];
|
||||||
|
if (c >= 32 && c <= 126) printf("%c", c);
|
||||||
|
else printf(".");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
tcp_recved(pcb, p->tot_len);
|
||||||
|
pbuf_free(p);
|
||||||
|
} else {
|
||||||
|
printf("[Membrane] HELIOS: TCP CONNECTION CLOSED by Remote.\n");
|
||||||
|
tcp_close(pcb);
|
||||||
|
}
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static err_t tcp_connected_callback(void *arg, struct tcp_pcb *pcb, err_t err) {
|
||||||
|
printf("[Membrane] HELIOS: TCP CONNECTED! Sending GET Request...\n");
|
||||||
|
const char *request = "GET / HTTP/1.0\r\nHost: google.com\r\nUser-Agent: NexusOS/1.0\r\n\r\n";
|
||||||
|
tcp_write(pcb, request, strlen(request), TCP_WRITE_FLAG_COPY);
|
||||||
|
tcp_output(pcb);
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void trigger_http_test(void) {
|
||||||
|
static int triggered = 0;
|
||||||
|
if (triggered) return;
|
||||||
|
triggered = 1;
|
||||||
|
|
||||||
|
ip_addr_t google_ip;
|
||||||
|
IP4_ADDR(ip_2_ip4(&google_ip), 142, 250, 185, 78);
|
||||||
|
|
||||||
|
struct tcp_pcb *pcb = tcp_new();
|
||||||
|
if (!pcb) {
|
||||||
|
printf("[Membrane] HELIOS Error: Failed to create TCP PCB\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_arg(pcb, NULL);
|
||||||
|
tcp_recv(pcb, tcp_recv_callback);
|
||||||
|
|
||||||
|
printf("[Membrane] HELIOS: INITIATING TCP CONNECTION to 142.250.185.78:80...\n");
|
||||||
|
tcp_connect(pcb, &google_ip, 80, tcp_connected_callback);
|
||||||
|
}
|
||||||
|
""".}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,15 @@
|
||||||
* - No critical sections needed (single fiber context)
|
* - No critical sections needed (single fiber context)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include "lwip/opt.h"
|
#include "lwip/opt.h"
|
||||||
#include "lwip/arch.h"
|
#include "lwip/arch.h"
|
||||||
#include "lwip/sys.h"
|
#include "lwip/sys.h"
|
||||||
#include "lwip/stats.h"
|
#include "lwip/stats.h"
|
||||||
|
|
||||||
|
extern int vprintf(const char *format, va_list args);
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// External Kernel Interface
|
// External Kernel Interface
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -95,19 +99,22 @@ void sys_arch_unprotect(sys_prot_t pval) {
|
||||||
// Diagnostics (Optional)
|
// Diagnostics (Optional)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
||||||
#if LWIP_PLATFORM_DIAG
|
// =========================================================
|
||||||
|
// Diagnostics
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* lwip_platform_diag - Output diagnostic message
|
* lwip_platform_diag - Output diagnostic message
|
||||||
* Used by LWIP_PLATFORM_DIAG() macro if enabled
|
* Used by LWIP_PLATFORM_DIAG() macro
|
||||||
*/
|
*/
|
||||||
void lwip_platform_diag(const char *fmt, ...) {
|
void lwip_platform_diag(const char *fmt, ...) {
|
||||||
// For now, silent. Could use console_write for debug builds.
|
console_write("<<<LwIP>>> ", 11);
|
||||||
(void)fmt;
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vprintf(fmt, args);
|
||||||
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* LWIP_PLATFORM_DIAG */
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Assertions (Contract Enforcement)
|
// Assertions (Contract Enforcement)
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -115,12 +122,10 @@ void lwip_platform_diag(const char *fmt, ...) {
|
||||||
/**
|
/**
|
||||||
* lwip_platform_assert - Handle failed assertions
|
* lwip_platform_assert - Handle failed assertions
|
||||||
* @param msg Assertion message
|
* @param msg Assertion message
|
||||||
* @param line Line number
|
|
||||||
* @param file File name
|
|
||||||
*
|
*
|
||||||
* In a production kernel, this should trigger a controlled panic.
|
* Note: Mapped via LWIP_PLATFORM_ASSERT macro in cc.h
|
||||||
*/
|
*/
|
||||||
void lwip_platform_assert(const char *msg, int line, const char *file) {
|
void nexus_lwip_panic(const char *msg) {
|
||||||
const char panic_msg[] = "[lwIP ASSERT FAILED]\n";
|
const char panic_msg[] = "[lwIP ASSERT FAILED]\n";
|
||||||
console_write(panic_msg, sizeof(panic_msg) - 1);
|
console_write(panic_msg, sizeof(panic_msg) - 1);
|
||||||
console_write(msg, __builtin_strlen(msg));
|
console_write(msg, __builtin_strlen(msg));
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,4 @@
|
||||||
# SPDX-License-Identifier: LSL-1.0
|
# Nexus Membrane: Virtual Terminal Emulator
|
||||||
# Copyright (c) 2026 Markus Maiwald
|
|
||||||
# Stewardship: Self Sovereign Society Foundation
|
|
||||||
#
|
|
||||||
# This file is part of the Nexus Sovereign Core.
|
|
||||||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
|
||||||
|
|
||||||
## Nexus Membrane: Virtual Terminal Emulator
|
|
||||||
|
|
||||||
# Phase 27 Part 2: The CRT Scanline Renderer
|
|
||||||
import term_font
|
import term_font
|
||||||
import ion_client
|
import ion_client
|
||||||
|
|
||||||
|
|
@ -20,13 +11,191 @@ const
|
||||||
COLOR_PHOSPHOR_AMBER = 0xFF00B0FF'u32
|
COLOR_PHOSPHOR_AMBER = 0xFF00B0FF'u32
|
||||||
COLOR_SCANLINE_DIM = 0xFF300808'u32
|
COLOR_SCANLINE_DIM = 0xFF300808'u32
|
||||||
|
|
||||||
var grid: array[TERM_ROWS, array[TERM_COLS, char]]
|
type
|
||||||
|
Cell = object
|
||||||
|
ch: char
|
||||||
|
fg: uint32
|
||||||
|
bg: uint32
|
||||||
|
dirty: bool
|
||||||
|
|
||||||
|
var grid: array[TERM_ROWS, array[TERM_COLS, Cell]]
|
||||||
var cursor_x, cursor_y: int
|
var cursor_x, cursor_y: int
|
||||||
var color_fg: uint32 = COLOR_PHOSPHOR_AMBER
|
var color_fg: uint32 = COLOR_PHOSPHOR_AMBER
|
||||||
var color_bg: uint32 = COLOR_SOVEREIGN_BLUE
|
var color_bg: uint32 = COLOR_SOVEREIGN_BLUE
|
||||||
|
var term_dirty*: bool = true
|
||||||
|
|
||||||
var fb_ptr: ptr UncheckedArray[uint32]
|
var fb_ptr: ptr UncheckedArray[uint32]
|
||||||
var fb_w, fb_h, fb_stride: int
|
var fb_w, fb_h, fb_stride: int
|
||||||
var ansi_state: int = 0
|
|
||||||
|
# ANSI State Machine
|
||||||
|
type AnsiState = enum
|
||||||
|
Normal, Escape, CSI, Param
|
||||||
|
|
||||||
|
var cur_state: AnsiState = Normal
|
||||||
|
var ansi_params: array[8, int]
|
||||||
|
var cur_param_idx: int
|
||||||
|
|
||||||
|
const PALETTE: array[16, uint32] = [
|
||||||
|
0xFF000000'u32, # 0: Black
|
||||||
|
0xFF0000AA'u32, # 1: Red
|
||||||
|
0xFF00AA00'u32, # 2: Green
|
||||||
|
0xFF00AAAA'u32, # 3: Brown/Yellow
|
||||||
|
0xFFAA0000'u32, # 4: Blue
|
||||||
|
0xFFAA00AA'u32, # 5: Magenta
|
||||||
|
0xFFAAAA00'u32, # 6: Cyan
|
||||||
|
0xFFAAAAAA'u32, # 7: Gray
|
||||||
|
0xFF555555'u32, # 8: Bright Black
|
||||||
|
0xFF5555FF'u32, # 9: Bright Red
|
||||||
|
0xFF55FF55'u32, # 10: Bright Green
|
||||||
|
0xFF55FFFF'u32, # 11: Bright Yellow
|
||||||
|
0xFFFF5555'u32, # 12: Bright Blue
|
||||||
|
0xFFFF55FF'u32, # 13: Bright Magenta
|
||||||
|
0xFFFFFF55'u32, # 14: Bright Cyan
|
||||||
|
0xFFFFFFFF'u32 # 15: White
|
||||||
|
]
|
||||||
|
|
||||||
|
proc handle_sgr() =
|
||||||
|
## Handle Select Graphic Rendition (m)
|
||||||
|
if cur_param_idx == 0: # reset
|
||||||
|
color_fg = COLOR_PHOSPHOR_AMBER
|
||||||
|
color_bg = COLOR_SOVEREIGN_BLUE
|
||||||
|
return
|
||||||
|
|
||||||
|
for i in 0..<cur_param_idx:
|
||||||
|
let p = ansi_params[i]
|
||||||
|
if p == 0:
|
||||||
|
color_fg = COLOR_PHOSPHOR_AMBER
|
||||||
|
color_bg = COLOR_SOVEREIGN_BLUE
|
||||||
|
elif p >= 30 and p <= 37:
|
||||||
|
color_fg = PALETTE[p - 30]
|
||||||
|
elif p >= 40 and p <= 47:
|
||||||
|
color_bg = PALETTE[p - 40]
|
||||||
|
elif p >= 90 and p <= 97:
|
||||||
|
color_fg = PALETTE[p - 90 + 8]
|
||||||
|
elif p >= 100 and p <= 107:
|
||||||
|
color_bg = PALETTE[p - 100 + 8]
|
||||||
|
|
||||||
|
|
||||||
|
proc term_clear*() =
|
||||||
|
for row in 0..<TERM_ROWS:
|
||||||
|
for col in 0..<TERM_COLS:
|
||||||
|
grid[row][col] = Cell(ch: ' ', fg: color_fg, bg: color_bg, dirty: true)
|
||||||
|
cursor_x = 0
|
||||||
|
cursor_y = 0
|
||||||
|
cur_state = Normal
|
||||||
|
term_dirty = true
|
||||||
|
|
||||||
|
proc term_scroll() =
|
||||||
|
for row in 0..<(TERM_ROWS-1):
|
||||||
|
grid[row] = grid[row + 1]
|
||||||
|
for col in 0..<TERM_COLS: grid[row][col].dirty = true
|
||||||
|
for col in 0..<TERM_COLS:
|
||||||
|
grid[TERM_ROWS-1][col] = Cell(ch: ' ', fg: color_fg, bg: color_bg, dirty: true)
|
||||||
|
term_dirty = true
|
||||||
|
|
||||||
|
proc term_putc*(ch: char) =
|
||||||
|
case cur_state
|
||||||
|
of Normal:
|
||||||
|
if ch == '\x1b':
|
||||||
|
cur_state = Escape
|
||||||
|
return
|
||||||
|
|
||||||
|
if ch == '\n':
|
||||||
|
cursor_x = 0
|
||||||
|
cursor_y += 1
|
||||||
|
elif ch == '\r':
|
||||||
|
cursor_x = 0
|
||||||
|
elif ch >= ' ':
|
||||||
|
if cursor_x >= TERM_COLS:
|
||||||
|
cursor_x = 0
|
||||||
|
cursor_y += 1
|
||||||
|
if cursor_y >= TERM_ROWS:
|
||||||
|
term_scroll()
|
||||||
|
cursor_y = TERM_ROWS - 1
|
||||||
|
grid[cursor_y][cursor_x] = Cell(ch: ch, fg: color_fg, bg: color_bg, dirty: true)
|
||||||
|
cursor_x += 1
|
||||||
|
term_dirty = true
|
||||||
|
|
||||||
|
of Escape:
|
||||||
|
if ch == '[':
|
||||||
|
cur_state = CSI
|
||||||
|
cur_param_idx = 0
|
||||||
|
for i in 0..<ansi_params.len: ansi_params[i] = 0
|
||||||
|
else:
|
||||||
|
cur_state = Normal
|
||||||
|
|
||||||
|
of CSI:
|
||||||
|
if ch >= '0' and ch <= '9':
|
||||||
|
ansi_params[cur_param_idx] = (ch.int - '0'.int)
|
||||||
|
cur_state = Param
|
||||||
|
elif ch == ';':
|
||||||
|
if cur_param_idx < ansi_params.len - 1: cur_param_idx += 1
|
||||||
|
elif ch == 'm':
|
||||||
|
if cur_state == Param or cur_param_idx > 0 or ch == 'm': # Handle single m or param m
|
||||||
|
if cur_state == Param: cur_param_idx += 1
|
||||||
|
handle_sgr()
|
||||||
|
cur_state = Normal
|
||||||
|
elif ch == 'H' or ch == 'f': # Cursor Home
|
||||||
|
cursor_x = 0; cursor_y = 0
|
||||||
|
cur_state = Normal
|
||||||
|
elif ch == 'J': # Clear Screen
|
||||||
|
term_clear()
|
||||||
|
cur_state = Normal
|
||||||
|
else:
|
||||||
|
cur_state = Normal
|
||||||
|
|
||||||
|
of Param:
|
||||||
|
if ch >= '0' and ch <= '9':
|
||||||
|
ansi_params[cur_param_idx] = ansi_params[cur_param_idx] * 10 + (ch.int - '0'.int)
|
||||||
|
elif ch == ';':
|
||||||
|
if cur_param_idx < ansi_params.len - 1: cur_param_idx += 1
|
||||||
|
elif ch == 'm':
|
||||||
|
cur_param_idx += 1
|
||||||
|
handle_sgr()
|
||||||
|
cur_state = Normal
|
||||||
|
elif ch == 'H' or ch == 'f':
|
||||||
|
# pos logic here if we wanted it
|
||||||
|
cursor_x = 0; cursor_y = 0
|
||||||
|
cur_state = Normal
|
||||||
|
else:
|
||||||
|
cur_state = Normal
|
||||||
|
|
||||||
|
# --- THE GHOST RENDERER ---
|
||||||
|
proc draw_char(cx, cy: int, cell: Cell) =
|
||||||
|
if fb_ptr == nil: return
|
||||||
|
|
||||||
|
let glyph_idx = uint8(cell.ch)
|
||||||
|
let fg = cell.fg
|
||||||
|
let bg = cell.bg
|
||||||
|
let px_start = cx * 8
|
||||||
|
let py_start = cy * 16
|
||||||
|
|
||||||
|
for y in 0..15:
|
||||||
|
let is_scanline = (y mod 4) == 3
|
||||||
|
let row_bits = FONT_BITMAP[glyph_idx][y]
|
||||||
|
let screen_y = py_start + y
|
||||||
|
if screen_y >= fb_h: break
|
||||||
|
|
||||||
|
let row_offset = screen_y * (fb_stride div 4)
|
||||||
|
for x in 0..7:
|
||||||
|
let screen_x = px_start + x
|
||||||
|
if screen_x >= fb_w: break
|
||||||
|
let pixel_idx = row_offset + screen_x
|
||||||
|
let is_pixel = ((int(row_bits) shr (7 - x)) and 1) != 0
|
||||||
|
|
||||||
|
if is_pixel:
|
||||||
|
fb_ptr[pixel_idx] = if is_scanline: (fg and 0xFFE0E0E0'u32) else: fg
|
||||||
|
else:
|
||||||
|
fb_ptr[pixel_idx] = if is_scanline: COLOR_SCANLINE_DIM else: bg
|
||||||
|
|
||||||
|
proc term_render*() =
|
||||||
|
if fb_ptr == nil or not term_dirty: return
|
||||||
|
for row in 0..<TERM_ROWS:
|
||||||
|
for col in 0..<TERM_COLS:
|
||||||
|
if grid[row][col].dirty:
|
||||||
|
draw_char(col, row, grid[row][col])
|
||||||
|
grid[row][col].dirty = false
|
||||||
|
term_dirty = false
|
||||||
|
|
||||||
proc term_init*() =
|
proc term_init*() =
|
||||||
let sys = cast[ptr SysTable](SYS_TABLE_ADDR)
|
let sys = cast[ptr SysTable](SYS_TABLE_ADDR)
|
||||||
|
|
@ -36,104 +205,22 @@ proc term_init*() =
|
||||||
fb_stride = int(sys.fb_stride)
|
fb_stride = int(sys.fb_stride)
|
||||||
cursor_x = 0
|
cursor_x = 0
|
||||||
cursor_y = 0
|
cursor_y = 0
|
||||||
ansi_state = 0
|
cur_state = Normal
|
||||||
|
term_dirty = true
|
||||||
|
|
||||||
|
when defined(TERM_PROFILE_minimal):
|
||||||
|
proc console_write(p: pointer, len: uint) {.importc, cdecl.}
|
||||||
|
var msg = "[TERM] Profile: MINIMAL (IBM VGA/Hack)\n"
|
||||||
|
console_write(addr msg[0], uint(msg.len))
|
||||||
|
elif defined(TERM_PROFILE_standard):
|
||||||
|
proc console_write(p: pointer, len: uint) {.importc, cdecl.}
|
||||||
|
var msg = "[TERM] Profile: STANDARD (Spleen/Nerd)\n"
|
||||||
|
console_write(addr msg[0], uint(msg.len))
|
||||||
|
|
||||||
# Initialize Grid
|
|
||||||
for row in 0..<TERM_ROWS:
|
for row in 0..<TERM_ROWS:
|
||||||
for col in 0..<TERM_COLS:
|
for col in 0..<TERM_COLS:
|
||||||
grid[row][col] = ' '
|
grid[row][col] = Cell(ch: ' ', fg: color_fg, bg: color_bg, dirty: true)
|
||||||
|
|
||||||
# Force initial color compliance
|
# Test Colors
|
||||||
color_fg = COLOR_PHOSPHOR_AMBER
|
let test_msg = "\x1b[31mN\x1b[32mE\x1b[33mX\x1b[34mU\x1b[35mS\x1b[0m\n"
|
||||||
color_bg = COLOR_SOVEREIGN_BLUE
|
for ch in test_msg: term_putc(ch)
|
||||||
|
|
||||||
proc term_clear*() =
|
|
||||||
for row in 0..<TERM_ROWS:
|
|
||||||
for col in 0..<TERM_COLS:
|
|
||||||
grid[row][col] = ' '
|
|
||||||
cursor_x = 0
|
|
||||||
cursor_y = 0
|
|
||||||
ansi_state = 0
|
|
||||||
|
|
||||||
proc term_scroll() =
|
|
||||||
for row in 0..<(TERM_ROWS-1):
|
|
||||||
grid[row] = grid[row + 1]
|
|
||||||
for col in 0..<TERM_COLS:
|
|
||||||
grid[TERM_ROWS-1][col] = ' '
|
|
||||||
|
|
||||||
proc term_putc*(ch: char) =
|
|
||||||
# ANSI Stripper
|
|
||||||
if ansi_state == 1:
|
|
||||||
if ch == '[': ansi_state = 2
|
|
||||||
else: ansi_state = 0
|
|
||||||
return
|
|
||||||
if ansi_state == 2:
|
|
||||||
if ch >= '@' and ch <= '~': ansi_state = 0
|
|
||||||
return
|
|
||||||
if ch == '\x1b':
|
|
||||||
ansi_state = 1
|
|
||||||
return
|
|
||||||
|
|
||||||
if ch == '\n':
|
|
||||||
cursor_x = 0
|
|
||||||
cursor_y += 1
|
|
||||||
elif ch == '\r':
|
|
||||||
cursor_x = 0
|
|
||||||
elif ch >= ' ':
|
|
||||||
if cursor_x >= TERM_COLS:
|
|
||||||
cursor_x = 0
|
|
||||||
cursor_y += 1
|
|
||||||
if cursor_y >= TERM_ROWS:
|
|
||||||
term_scroll()
|
|
||||||
cursor_y = TERM_ROWS - 1
|
|
||||||
grid[cursor_y][cursor_x] = ch
|
|
||||||
cursor_x += 1
|
|
||||||
|
|
||||||
# --- THE GHOST RENDERER ---
|
|
||||||
proc draw_char(cx, cy: int, c: char, fg: uint32, bg: uint32) =
|
|
||||||
if fb_ptr == nil: return
|
|
||||||
|
|
||||||
# Safe Font Mapping
|
|
||||||
var glyph_idx = int(c) - 32
|
|
||||||
if glyph_idx < 0 or glyph_idx >= 96: glyph_idx = 0 # Space default
|
|
||||||
|
|
||||||
let px_start = cx * 8
|
|
||||||
let py_start = cy * 16
|
|
||||||
|
|
||||||
for y in 0..15:
|
|
||||||
# Scanline Logic: Every 4th line
|
|
||||||
let is_scanline = (y mod 4) == 3
|
|
||||||
|
|
||||||
let row_bits = FONT_BITMAP[glyph_idx][y]
|
|
||||||
let screen_y = py_start + y
|
|
||||||
if screen_y >= fb_h: break
|
|
||||||
|
|
||||||
# Optimize inner loop knowing stride is in bytes but using uint32 accessor
|
|
||||||
# fb_ptr index is per uint32.
|
|
||||||
let row_offset = screen_y * (fb_stride div 4)
|
|
||||||
|
|
||||||
for x in 0..7:
|
|
||||||
let screen_x = px_start + x
|
|
||||||
if screen_x >= fb_w: break
|
|
||||||
|
|
||||||
let pixel_idx = row_offset + screen_x
|
|
||||||
|
|
||||||
# Bit Check: MSB first (0x80 >> x)
|
|
||||||
let is_pixel = ((int(row_bits) shr (7 - x)) and 1) != 0
|
|
||||||
|
|
||||||
if is_pixel:
|
|
||||||
if is_scanline:
|
|
||||||
fb_ptr[pixel_idx] = fg and 0xFFE0E0E0'u32
|
|
||||||
else:
|
|
||||||
fb_ptr[pixel_idx] = fg
|
|
||||||
else:
|
|
||||||
if is_scanline:
|
|
||||||
fb_ptr[pixel_idx] = COLOR_SCANLINE_DIM
|
|
||||||
else:
|
|
||||||
fb_ptr[pixel_idx] = bg
|
|
||||||
|
|
||||||
proc term_render*() =
|
|
||||||
if fb_ptr == nil: return
|
|
||||||
for row in 0..<TERM_ROWS:
|
|
||||||
for col in 0..<TERM_COLS:
|
|
||||||
draw_char(col, row, grid[row][col], color_fg, color_bg)
|
|
||||||
|
|
|
||||||
|
|
@ -1,220 +1,13 @@
|
||||||
# SPDX-License-Identifier: LSL-1.0
|
# Nexus Membrane: Console Font Dispatcher
|
||||||
# Copyright (c) 2026 Markus Maiwald
|
|
||||||
# Stewardship: Self Sovereign Society Foundation
|
|
||||||
#
|
|
||||||
# This file is part of the Nexus Sovereign Core.
|
|
||||||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
|
||||||
|
|
||||||
## Nexus Membrane: Console Font Definition
|
when defined(TERM_PROFILE_minimal):
|
||||||
|
import fonts/minimal as profile
|
||||||
|
elif defined(TERM_PROFILE_standard):
|
||||||
|
import fonts/standard as profile
|
||||||
|
else:
|
||||||
|
# Fallback to minimal if not specified
|
||||||
|
import fonts/minimal as profile
|
||||||
|
|
||||||
# Phase 27 Part 1: IBM VGA 8x16 Bitmap Font Data
|
const FONT_WIDTH* = profile.FONT_WIDTH
|
||||||
# Source: CP437 Standard
|
const FONT_HEIGHT* = profile.FONT_HEIGHT
|
||||||
# Exported for Renderer Access
|
const FONT_BITMAP* = profile.FONT_BITMAP
|
||||||
|
|
||||||
const FONT_WIDTH* = 8
|
|
||||||
const FONT_HEIGHT* = 16
|
|
||||||
|
|
||||||
# Packed Bitmap Data for ASCII 0x20-0x7F
|
|
||||||
const FONT_BITMAP*: array[96, array[16, uint8]] = [
|
|
||||||
# 0x20 SPACE
|
|
||||||
[0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x21 !
|
|
||||||
[0'u8, 0, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x18, 0, 0x18, 0x18, 0, 0, 0, 0],
|
|
||||||
# 0x22 "
|
|
||||||
[0'u8, 0x66, 0x66, 0x66, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x23 #
|
|
||||||
[0'u8, 0, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0, 0, 0, 0, 0],
|
|
||||||
# 0x24 $
|
|
||||||
[0x18'u8, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06, 0x06, 0x86, 0xC6, 0x7C,
|
|
||||||
0x18, 0x18, 0, 0],
|
|
||||||
# 0x25 %
|
|
||||||
[0'u8, 0, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0, 0, 0, 0, 0, 0, 0, 0], # Simplified %
|
|
||||||
# 0x26 &
|
|
||||||
[0'u8, 0, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0xCC, 0x76, 0, 0, 0, 0, 0],
|
|
||||||
# 0x27 '
|
|
||||||
[0'u8, 0x30, 0x30, 0x18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x28 (
|
|
||||||
[0'u8, 0, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C, 0, 0, 0, 0],
|
|
||||||
# 0x29 )
|
|
||||||
[0'u8, 0, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0, 0, 0, 0],
|
|
||||||
# 0x2A *
|
|
||||||
[0'u8, 0, 0, 0, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x2B +
|
|
||||||
[0'u8, 0, 0, 0, 0x18, 0x18, 0x7E, 0x18, 0x18, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x2C ,
|
|
||||||
[0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, 0x18, 0x30, 0],
|
|
||||||
# 0x2D -
|
|
||||||
[0'u8, 0, 0, 0, 0, 0, 0, 0xFE, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x2E .
|
|
||||||
[0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, 0x38, 0, 0],
|
|
||||||
# 0x2F /
|
|
||||||
[0'u8, 0, 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0, 0, 0, 0, 0, 0],
|
|
||||||
|
|
||||||
# 0x30 0
|
|
||||||
[0'u8, 0, 0x3C, 0x66, 0xC6, 0xC6, 0xD6, 0xD6, 0xD6, 0xC6, 0xC6, 0x66, 0x3C, 0,
|
|
||||||
0, 0],
|
|
||||||
# 0x31 1
|
|
||||||
[0'u8, 0, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0, 0, 0, 0],
|
|
||||||
# 0x32 2
|
|
||||||
[0'u8, 0, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xC6, 0xFE, 0, 0, 0, 0],
|
|
||||||
# 0x33 3
|
|
||||||
[0'u8, 0, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0, 0, 0, 0],
|
|
||||||
# 0x34 4
|
|
||||||
[0'u8, 0, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x0C, 0x1E, 0, 0, 0, 0],
|
|
||||||
# 0x35 5
|
|
||||||
[0'u8, 0, 0xFE, 0xC0, 0xC0, 0xFC, 0x06, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0, 0, 0, 0],
|
|
||||||
# 0x36 6
|
|
||||||
[0'u8, 0, 0x38, 0x60, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x37 7
|
|
||||||
[0'u8, 0, 0xFE, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0, 0, 0, 0],
|
|
||||||
# 0x38 8
|
|
||||||
[0'u8, 0, 0x3C, 0x66, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x39 9
|
|
||||||
[0'u8, 0, 0x3C, 0x66, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x66, 0x38, 0, 0, 0, 0],
|
|
||||||
|
|
||||||
# 0x3A :
|
|
||||||
[0'u8, 0, 0, 0, 0x18, 0x18, 0, 0, 0, 0x18, 0x18, 0, 0, 0, 0, 0],
|
|
||||||
# 0x3B ;
|
|
||||||
[0'u8, 0, 0, 0, 0x18, 0x18, 0, 0, 0, 0x18, 0x18, 0x30, 0, 0, 0, 0],
|
|
||||||
# 0x3C <
|
|
||||||
[0'u8, 0, 0, 0x06, 0x18, 0x60, 0xC0, 0x60, 0x18, 0x06, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x3D =
|
|
||||||
[0'u8, 0, 0, 0, 0, 0x7E, 0, 0, 0x7E, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x3E >
|
|
||||||
[0'u8, 0, 0, 0x60, 0x18, 0x06, 0x02, 0x06, 0x18, 0x60, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x3F ?
|
|
||||||
[0'u8, 0, 0x3C, 0x66, 0xC6, 0x0C, 0x18, 0x18, 0, 0x18, 0x18, 0, 0, 0, 0, 0],
|
|
||||||
# 0x40 @
|
|
||||||
[0'u8, 0, 0x3C, 0x66, 0xC6, 0xCE, 0xD6, 0xD6, 0xC6, 0xC6, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
|
|
||||||
# 0x41 A
|
|
||||||
[0'u8, 0, 0x18, 0x3C, 0x66, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0, 0, 0, 0],
|
|
||||||
# 0x42 B
|
|
||||||
[0'u8, 0, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0, 0, 0, 0],
|
|
||||||
# 0x43 C
|
|
||||||
[0'u8, 0, 0x3C, 0x66, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0, 0xC6, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x44 D
|
|
||||||
[0'u8, 0, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0, 0, 0, 0],
|
|
||||||
# 0x45 E
|
|
||||||
[0'u8, 0, 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x62, 0xFE, 0, 0, 0, 0, 0],
|
|
||||||
# 0x46 F
|
|
||||||
[0'u8, 0, 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xF0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x47 G
|
|
||||||
[0'u8, 0, 0x3C, 0x66, 0xC6, 0xC0, 0xC0, 0xDE, 0xC6, 0xC6, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x48 H
|
|
||||||
[0'u8, 0, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0, 0, 0, 0],
|
|
||||||
# 0x49 I
|
|
||||||
[0'u8, 0, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x4A J
|
|
||||||
[0'u8, 0, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0, 0, 0, 0, 0],
|
|
||||||
# 0x4B K
|
|
||||||
[0'u8, 0, 0xE6, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x66, 0xE6, 0, 0, 0, 0, 0],
|
|
||||||
# 0x4C L
|
|
||||||
[0'u8, 0, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0, 0, 0, 0, 0],
|
|
||||||
# 0x4D M
|
|
||||||
[0'u8, 0, 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0, 0, 0, 0],
|
|
||||||
# 0x4E N
|
|
||||||
[0'u8, 0, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6, 0, 0, 0, 0],
|
|
||||||
# 0x4F O
|
|
||||||
[0'u8, 0, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0, 0, 0, 0],
|
|
||||||
# 0x50 P
|
|
||||||
[0'u8, 0, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0xF0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x51 Q
|
|
||||||
[0'u8, 0, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0x7C, 0x0E, 0, 0, 0, 0],
|
|
||||||
# 0x52 R
|
|
||||||
[0'u8, 0, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0xE6, 0, 0, 0, 0, 0],
|
|
||||||
# 0x53 S
|
|
||||||
[0'u8, 0, 0x3C, 0x66, 0xC6, 0x60, 0x3C, 0x06, 0xC6, 0xC6, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x54 T
|
|
||||||
[0'u8, 0, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0, 0, 0, 0],
|
|
||||||
# 0x55 U
|
|
||||||
[0'u8, 0, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0, 0, 0, 0],
|
|
||||||
# 0x56 V
|
|
||||||
[0'u8, 0, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x38, 0x10, 0, 0, 0, 0],
|
|
||||||
# 0x57 W
|
|
||||||
[0'u8, 0, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xFE, 0xEE, 0x44, 0, 0, 0, 0, 0],
|
|
||||||
# 0x58 X
|
|
||||||
[0'u8, 0, 0xC6, 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0xC6, 0, 0, 0, 0, 0],
|
|
||||||
# 0x59 Y
|
|
||||||
[0'u8, 0, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0, 0, 0, 0],
|
|
||||||
# 0x5A Z
|
|
||||||
[0'u8, 0, 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
|
|
||||||
# 0x5B [
|
|
||||||
[0'u8, 0, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x5C \
|
|
||||||
[0'u8, 0, 0x80, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x5D ]
|
|
||||||
[0'u8, 0, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x5E ^
|
|
||||||
[0'u8, 0x10, 0x38, 0x6C, 0xC6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x5F _
|
|
||||||
[0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0],
|
|
||||||
|
|
||||||
# 0x60 `
|
|
||||||
[0'u8, 0x30, 0x30, 0x18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x61 a
|
|
||||||
[0'u8, 0, 0, 0, 0, 0x38, 0x6C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0, 0, 0, 0],
|
|
||||||
# 0x62 b
|
|
||||||
[0'u8, 0, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x7C, 0, 0, 0, 0],
|
|
||||||
# 0x63 c
|
|
||||||
[0'u8, 0, 0, 0, 0, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x64 d
|
|
||||||
[0'u8, 0, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0, 0, 0, 0],
|
|
||||||
# 0x65 e
|
|
||||||
[0'u8, 0, 0, 0, 0, 0x3C, 0x66, 0xC6, 0xFE, 0xC0, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x66 f
|
|
||||||
[0'u8, 0, 0x1C, 0x36, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0, 0, 0, 0],
|
|
||||||
# 0x67 g
|
|
||||||
[0'u8, 0, 0, 0, 0, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0, 0],
|
|
||||||
# 0x68 h
|
|
||||||
[0'u8, 0, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x66, 0x66, 0xE6, 0, 0, 0, 0],
|
|
||||||
# 0x69 i
|
|
||||||
[0'u8, 0, 0x18, 0x18, 0, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x6A j
|
|
||||||
[0'u8, 0, 0x06, 0x06, 0, 0x0E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C,
|
|
||||||
0, 0],
|
|
||||||
# 0x6B k
|
|
||||||
[0'u8, 0, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0, 0, 0, 0],
|
|
||||||
# 0x6C l
|
|
||||||
[0'u8, 0, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x6D m
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xEC, 0xFE, 0xD6, 0xD6, 0xD6, 0xD6, 0xC6, 0, 0, 0, 0],
|
|
||||||
# 0x6E n
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0, 0, 0, 0],
|
|
||||||
# 0x6F o
|
|
||||||
[0'u8, 0, 0, 0, 0, 0x3C, 0x66, 0xC6, 0xC6, 0xC6, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x70 p
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xDC, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0, 0, 0],
|
|
||||||
# 0x71 q
|
|
||||||
[0'u8, 0, 0, 0, 0, 0x76, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0, 0, 0],
|
|
||||||
# 0x72 r
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xDC, 0x76, 0x66, 0x60, 0x60, 0x60, 0xF0, 0, 0, 0, 0],
|
|
||||||
# 0x73 s
|
|
||||||
[0'u8, 0, 0, 0, 0, 0x3E, 0x60, 0x3C, 0x06, 0x06, 0x66, 0x3C, 0, 0, 0, 0],
|
|
||||||
# 0x74 t
|
|
||||||
[0'u8, 0, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1C, 0, 0, 0, 0, 0],
|
|
||||||
# 0x75 u
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0, 0, 0, 0],
|
|
||||||
# 0x76 v
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xCC, 0xCC, 0xCC, 0xCC, 0x66, 0x3C, 0x18, 0, 0, 0, 0],
|
|
||||||
# 0x77 w
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xC3, 0xC3, 0xC3, 0xDB, 0xFF, 0x66, 0x24, 0, 0, 0, 0],
|
|
||||||
# 0x78 x
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xC3, 0x66, 0x3C, 0x3C, 0x66, 0xC3, 0xC3, 0, 0, 0, 0],
|
|
||||||
# 0x79 y
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0, 0, 0],
|
|
||||||
# 0x7A z
|
|
||||||
[0'u8, 0, 0, 0, 0, 0xFE, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0xFE, 0, 0, 0, 0],
|
|
||||||
# 0x7B {
|
|
||||||
[0'u8, 0, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0E, 0, 0, 0, 0, 0],
|
|
||||||
# 0x7C |
|
|
||||||
[0'u8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0, 0,
|
|
||||||
0, 0],
|
|
||||||
# 0x7D }
|
|
||||||
[0'u8, 0, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18, 0x70, 0, 0, 0, 0, 0],
|
|
||||||
# 0x7E ~
|
|
||||||
[0'u8, 0, 0x76, 0xDC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
# 0x7F DEL
|
|
||||||
[0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
||||||
]
|
|
||||||
|
|
|
||||||
|
|
@ -1071,7 +1071,7 @@ proc nipbox_main*() =
|
||||||
# Phase 30: Pledge Safety
|
# Phase 30: Pledge Safety
|
||||||
# NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones
|
# NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones
|
||||||
# For now, we PLEDGE_ALL because the shell needs to explore
|
# For now, we PLEDGE_ALL because the shell needs to explore
|
||||||
# In future (SPEC-300), we drop PLEDGE_INET unless authorized
|
# In future (SPEC-401), we drop PLEDGE_INET unless authorized
|
||||||
discard lb.pledge(PLEDGE_ALL)
|
discard lb.pledge(PLEDGE_ALL)
|
||||||
|
|
||||||
# Initialize the Biosuit
|
# Initialize the Biosuit
|
||||||
|
|
|
||||||
8
run.sh
8
run.sh
|
|
@ -2,7 +2,7 @@
|
||||||
# Rumpk QEMU Boot Script
|
# Rumpk QEMU Boot Script
|
||||||
|
|
||||||
RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
KERNEL="$RUMPK_DIR/build/rumpk.elf"
|
KERNEL="$RUMPK_DIR/zig-out/bin/rumpk.elf"
|
||||||
|
|
||||||
if [ ! -f "$KERNEL" ]; then
|
if [ ! -f "$KERNEL" ]; then
|
||||||
echo "ERROR: Kernel not found at $KERNEL"
|
echo "ERROR: Kernel not found at $KERNEL"
|
||||||
|
|
@ -14,9 +14,9 @@ echo "🚀 Booting Rumpk..."
|
||||||
echo " Kernel: $KERNEL"
|
echo " Kernel: $KERNEL"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
qemu-system-aarch64 \
|
qemu-system-riscv64 \
|
||||||
-M virt \
|
-M virt \
|
||||||
-cpu cortex-a57 \
|
-cpu max \
|
||||||
-m 128M \
|
-m 512M \
|
||||||
-nographic \
|
-nographic \
|
||||||
-kernel "$KERNEL"
|
-kernel "$KERNEL"
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,6 @@
|
||||||
// This file is part of the Nexus Sovereign Core.
|
// This file is part of the Nexus Sovereign Core.
|
||||||
// See legal/LICENSE_SOVEREIGN.md for license terms.
|
// See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||||
|
|
||||||
//! Nexus Immune System (NPL): The Voice & Command Plane
|
|
||||||
//!
|
|
||||||
//! Implemented as an NPL fiber, NexShell provides the interactive kernel shell.
|
|
||||||
//! Handles telemetry events, user input, and command dispatch to the ION layer.
|
|
||||||
//!
|
|
||||||
//! SAFETY: Interacts with the shared SysTable via volatile pointers and atomic operations.
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const ION_BASE = 0x83000000;
|
const ION_BASE = 0x83000000;
|
||||||
|
|
||||||
|
|
@ -20,7 +13,7 @@ const IonPacket = extern struct {
|
||||||
phys: u64,
|
phys: u64,
|
||||||
len: u16,
|
len: u16,
|
||||||
id: u16,
|
id: u16,
|
||||||
_pad: u32, // Match Nim's 24-byte alignment
|
_pad: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CmdPacket = extern struct {
|
const CmdPacket = extern struct {
|
||||||
|
|
@ -50,115 +43,72 @@ const SysTable = extern struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
const CMD_ION_STOP = 1;
|
const CMD_ION_STOP = 1;
|
||||||
const CMD_ION_START = 2;
|
|
||||||
const CMD_GPU_MATRIX = 0x100;
|
|
||||||
const CMD_GET_GPU_STATUS = 0x102;
|
|
||||||
|
|
||||||
// The Main Loop for the NexShell Fiber
|
extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize;
|
||||||
|
extern fn console_read() c_int;
|
||||||
|
extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void;
|
||||||
|
extern fn fiber_yield() void;
|
||||||
|
extern fn fiber_sleep(ms: u64) void;
|
||||||
|
extern fn ion_wait_multi(mask: u64) i32;
|
||||||
|
|
||||||
export fn nexshell_main() void {
|
export fn nexshell_main() void {
|
||||||
const sys = @as(*SysTable, @ptrFromInt(ION_BASE));
|
const sys = @as(*SysTable, @ptrFromInt(ION_BASE));
|
||||||
|
|
||||||
print("\n╔═══════════════════════════════════════╗\n");
|
print("\n╔═══════════════════════════════════════╗\n");
|
||||||
print("║ NEXSHELL IMMUNE SYSTEM ACTIVE ║\n");
|
print("║ NEXSHELL IMMUNE SYSTEM ACTIVE ║\n");
|
||||||
print("║ Command Plane: READY ║\n");
|
print("╚═══════════════════════════════════════╝\n\n");
|
||||||
print("╚═══════════════════════════════════════╝\n");
|
|
||||||
|
|
||||||
const event_ring = sys.s_event;
|
|
||||||
const cmd_ring = sys.s_cmd;
|
const cmd_ring = sys.s_cmd;
|
||||||
|
|
||||||
// SAFETY(NexShell): Input buffer initialized to `undefined` for performance.
|
var input_buffer: [128]u8 = undefined;
|
||||||
// Populated by char-by-char console reads before use.
|
|
||||||
var input_buffer: [64]u8 = undefined;
|
|
||||||
var input_idx: usize = 0;
|
var input_idx: usize = 0;
|
||||||
|
|
||||||
var loop_count: usize = 0;
|
print("[NexShell] Entering main loop (REACTIVE MODE)...\n");
|
||||||
var poll_pulse: usize = 0;
|
print("> ");
|
||||||
print("[NexShell] Entering main loop...\n");
|
|
||||||
while (true) {
|
while (true) {
|
||||||
loop_count += 1;
|
|
||||||
poll_pulse += 1;
|
|
||||||
|
|
||||||
// First iteration diagnostic
|
|
||||||
if (loop_count == 1) {
|
|
||||||
print("[NexShell] First iteration\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Polling pulse every 100 to show activity
|
|
||||||
if (poll_pulse >= 100) {
|
|
||||||
print(".");
|
|
||||||
poll_pulse = 0;
|
|
||||||
}
|
|
||||||
// 1. Process Telemetry Events
|
|
||||||
const head = @atomicLoad(u32, &event_ring.head, .acquire);
|
|
||||||
const tail = @atomicLoad(u32, &event_ring.tail, .monotonic);
|
|
||||||
|
|
||||||
if (head != tail) {
|
|
||||||
const pkt = event_ring.data[tail & event_ring.mask];
|
|
||||||
print("\n[NexShell] ALERT | EventID: ");
|
|
||||||
if (pkt.id == 777) {
|
|
||||||
print("777 (SECURITY_HEARTBEAT)\n");
|
|
||||||
} else {
|
|
||||||
print("GENERIC\n");
|
|
||||||
}
|
|
||||||
@atomicStore(u32, &event_ring.tail, tail + 1, .release);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Process User Input (Non-blocking)
|
|
||||||
const c = console_read();
|
const c = console_read();
|
||||||
|
|
||||||
if (c != -1) {
|
if (c != -1) {
|
||||||
const byte = @as(u8, @intCast(c));
|
const byte = @as(u8, @intCast(c));
|
||||||
const char_buf = [1]u8{byte};
|
|
||||||
print("[NexShell] Got char: '");
|
|
||||||
print(&char_buf);
|
|
||||||
print("'\n");
|
|
||||||
|
|
||||||
if (forward_mode) {
|
if (byte == '\r' or byte == '\n') {
|
||||||
// Check for escape: Ctrl+K (11)
|
print("\n");
|
||||||
if (byte == 11) {
|
if (input_idx > 0) {
|
||||||
forward_mode = false;
|
|
||||||
print("\n[NexShell] RESUMING KERNEL CONTROL.\n");
|
|
||||||
} else {
|
|
||||||
const bs = [1]u8{byte};
|
|
||||||
ion_push_stdin(&bs, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (byte == '\r' or byte == '\n') {
|
|
||||||
print("\n");
|
|
||||||
process_command(input_buffer[0..input_idx], cmd_ring);
|
process_command(input_buffer[0..input_idx], cmd_ring);
|
||||||
input_idx = 0;
|
|
||||||
} else if (byte == 0x7F or byte == 0x08) {
|
|
||||||
if (input_idx > 0) {
|
|
||||||
input_idx -= 1;
|
|
||||||
print("\x08 \x08"); // Backspace
|
|
||||||
}
|
|
||||||
} else if (input_idx < 63) {
|
|
||||||
input_buffer[input_idx] = byte;
|
|
||||||
input_idx += 1;
|
|
||||||
const bs = [1]u8{byte};
|
|
||||||
print(&bs);
|
|
||||||
}
|
}
|
||||||
|
input_idx = 0;
|
||||||
|
print("> ");
|
||||||
|
} else if (byte == 8 or byte == 127) { // Backspace
|
||||||
|
if (input_idx > 0) {
|
||||||
|
input_idx -= 1;
|
||||||
|
print("\x08 \x08");
|
||||||
|
}
|
||||||
|
} else if (input_idx < 127) {
|
||||||
|
input_buffer[input_idx] = byte;
|
||||||
|
input_idx += 1;
|
||||||
|
const bs = [1]u8{byte};
|
||||||
|
print(&bs);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Wait for console.input (Slot 0)
|
||||||
|
_ = ion_wait_multi(0x01);
|
||||||
}
|
}
|
||||||
|
|
||||||
fiber_yield();
|
fiber_yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var forward_mode: bool = true;
|
var forward_mode: bool = false;
|
||||||
|
|
||||||
fn process_command(cmd_text: []const u8, cmd_ring: *RingBuffer(CmdPacket)) void {
|
fn process_command(cmd_text: []const u8, cmd_ring: *RingBuffer(CmdPacket)) void {
|
||||||
if (cmd_text.len == 0) return;
|
if (cmd_text.len == 0) return;
|
||||||
|
|
||||||
if (forward_mode) {
|
if (forward_mode) {
|
||||||
const is_toggle = std.mem.eql(u8, cmd_text, "kernel") or std.mem.eql(u8, cmd_text, "exit");
|
const is_toggle = std.mem.eql(u8, cmd_text, "kernel") or std.mem.eql(u8, cmd_text, "exit");
|
||||||
|
|
||||||
// ALWAYS FORWARD TO USERLAND first so it can process its own exit
|
|
||||||
print("[NexShell] Forwarding to Subject...\n");
|
print("[NexShell] Forwarding to Subject...\n");
|
||||||
|
|
||||||
// Combine command + newline to avoid fragmentation
|
|
||||||
if (cmd_text.len > 0) {
|
if (cmd_text.len > 0) {
|
||||||
// SAFETY(NexShell): Local forward buffer initialized to `undefined`.
|
|
||||||
// Immediately populated by `@memcpy` and newline before pushing to ION.
|
|
||||||
var forward_buf: [128]u8 = undefined;
|
var forward_buf: [128]u8 = undefined;
|
||||||
const copy_len = if (cmd_text.len > 126) 126 else cmd_text.len;
|
const copy_len = if (cmd_text.len > 126) 126 else cmd_text.len;
|
||||||
@memcpy(forward_buf[0..copy_len], cmd_text[0..copy_len]);
|
@memcpy(forward_buf[0..copy_len], cmd_text[0..copy_len]);
|
||||||
|
|
@ -171,25 +121,15 @@ fn process_command(cmd_text: []const u8, cmd_ring: *RingBuffer(CmdPacket)) void
|
||||||
print("[NexShell] Dropping to Kernel Debug Mode.\n");
|
print("[NexShell] Dropping to Kernel Debug Mode.\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (std.mem.eql(u8, cmd_text, "subject") or std.mem.eql(u8, cmd_text, "nipbox")) {
|
if (std.mem.eql(u8, cmd_text, "ps") or std.mem.eql(u8, cmd_text, "fibers")) {
|
||||||
forward_mode = true;
|
print("[NexShell] Active Fibers:\n");
|
||||||
print("[NexShell] Resuming Subject Pipe.\n");
|
_ = k_handle_syscall(0x501, 0, 0, 0);
|
||||||
return;
|
} else if (std.mem.eql(u8, cmd_text, "stop")) {
|
||||||
}
|
|
||||||
|
|
||||||
if (std.mem.eql(u8, cmd_text, "io stop") or std.mem.eql(u8, cmd_text, "ion stop")) {
|
|
||||||
print("[NexShell] Pushing CMD_ION_STOP...\n");
|
|
||||||
push_cmd(cmd_ring, CMD_ION_STOP, 0);
|
push_cmd(cmd_ring, CMD_ION_STOP, 0);
|
||||||
} else if (std.mem.eql(u8, cmd_text, "matrix on")) {
|
} else if (std.mem.eql(u8, cmd_text, "help")) {
|
||||||
print("[NexShell] Engaging Matrix Protocol (Emergency Override)...\n");
|
print("[NexShell] ps, stop, help\n");
|
||||||
push_cmd(cmd_ring, CMD_GPU_MATRIX, 1);
|
|
||||||
} else if (std.mem.eql(u8, cmd_text, "matrix off")) {
|
|
||||||
print("[NexShell] Disengaging Matrix Protocol...\n");
|
|
||||||
push_cmd(cmd_ring, CMD_GPU_MATRIX, 0);
|
|
||||||
} else if (std.mem.eql(u8, cmd_text, "matrix status")) {} else if (std.mem.eql(u8, cmd_text, "help")) {
|
|
||||||
print("[NexShell] Kernel Commands: io stop, matrix on/off, matrix status, subject, help\n");
|
|
||||||
} else {
|
} else {
|
||||||
print("[NexShell] Unknown Kernel Command: ");
|
print("[NexShell] Unknown Command: ");
|
||||||
print(cmd_text);
|
print(cmd_text);
|
||||||
print("\n");
|
print("\n");
|
||||||
}
|
}
|
||||||
|
|
@ -201,21 +141,16 @@ fn push_cmd(ring: *RingBuffer(CmdPacket), kind: u32, arg: u64) void {
|
||||||
const tail = @atomicLoad(u32, &ring.tail, .monotonic);
|
const tail = @atomicLoad(u32, &ring.tail, .monotonic);
|
||||||
|
|
||||||
const next = (head + 1) & ring.mask;
|
const next = (head + 1) & ring.mask;
|
||||||
if (next == tail) {
|
if (next == tail) return;
|
||||||
print("[NexShell] CMD RING FULL!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ring.data[head & ring.mask] = .{ .kind = kind, .reserved = 0, .arg = arg, .id = [_]u8{0} ** 16 };
|
ring.data[head & ring.mask] = .{ .kind = kind, .reserved = 0, .arg = arg, .id = [_]u8{0} ** 16 };
|
||||||
@atomicStore(u32, &ring.head, next, .release);
|
@atomicStore(u32, &ring.head, next, .release);
|
||||||
}
|
}
|
||||||
|
|
||||||
// OS Shims
|
fn kernel_write(fd: c_int, buf: [*]const u8, count: usize) isize {
|
||||||
extern fn write(fd: c_int, buf: [*]const u8, count: usize) isize;
|
return @as(isize, @bitCast(k_handle_syscall(0x204, @as(usize, @intCast(fd)), @intFromPtr(buf), count)));
|
||||||
extern fn console_read() c_int;
|
}
|
||||||
extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void;
|
|
||||||
extern fn fiber_yield() void;
|
|
||||||
|
|
||||||
fn print(text: []const u8) void {
|
fn print(text: []const u8) void {
|
||||||
_ = write(1, text.ptr, text.len);
|
_ = kernel_write(1, text.ptr, text.len);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Nexus Mksh Builder
|
||||||
|
# "Grafting Mksh into Nexus"
|
||||||
|
|
||||||
|
TARGET="riscv64-freestanding-none"
|
||||||
|
MCPU="sifive_u54"
|
||||||
|
CFLAGS="-target $TARGET -mcpu=$MCPU -mabi=lp64d -mcmodel=medany \
|
||||||
|
-ffunction-sections -fdata-sections -ffreestanding -fno-stack-protector -fno-builtin -Os \
|
||||||
|
-DMKSH_BINSHREDUCED -DMKSH_DISABLE_DEPRECATED -D_GNU_SOURCE -DMKSH_BUILDSH -DMKSH_BUILD_R=593 \
|
||||||
|
-DHAVE_MEMMOVE=1 -DHAVE_STRCHR=1 -DHAVE_STRSTR=1 -DHAVE_QSORT=1 \
|
||||||
|
-DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_UNISTD_H=1 \
|
||||||
|
-DHAVE_SYS_TIME_H=1 -DHAVE_SYS_RESOURCE_H=1 -DHAVE_SIGNAL_H=1 \
|
||||||
|
-DHAVE_SETJMP_H=1 -DHAVE_DIRENT_H=1 -DHAVE_FCNTL_H=1 \
|
||||||
|
-DHAVE_GRP_H=1 -DHAVE_PWD_H=1 -DHAVE_TERMIOS_H=1 \
|
||||||
|
-DHAVE_SYS_IOCTL_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_SYS_WAIT_H=1 -DHAVE_SYS_TIMES_H=1 \
|
||||||
|
-DHAVE_SIGSETJMP=1 -DHAVE_SIG_T=1 -DHAVE_BOTH_TIME_H=1 -DHAVE_TIME_H=1 \
|
||||||
|
-I ../../core/include \
|
||||||
|
-I ../../libs/membrane/include \
|
||||||
|
-I ../lwip/src/include \
|
||||||
|
-I ../littlefs"
|
||||||
|
|
||||||
|
|
||||||
|
# Change to script directory
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
echo "[Graft] Compiling Mksh..."
|
||||||
|
|
||||||
|
# Compile sources
|
||||||
|
SOURCES="edit.c eval.c exec.c expr.c funcs.c histrap.c jobs.c lalloc.c lex.c main.c misc.c shf.c syn.c tree.c var.c strlcpy.c"
|
||||||
|
|
||||||
|
# We compile directly to an ELF for now to test linking
|
||||||
|
# But usually we want object files first
|
||||||
|
OBJS=""
|
||||||
|
for src in $SOURCES; do
|
||||||
|
echo " -> $src"
|
||||||
|
zig cc $CFLAGS -c mksh/$src -o mksh/${src%.c}.o
|
||||||
|
OBJS="$OBJS mksh/${src%.c}.o"
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Compile stubs
|
||||||
|
echo " -> stubs_mksh.c"
|
||||||
|
zig cc $CFLAGS -c stubs_mksh.c -o stubs_mksh.o
|
||||||
|
|
||||||
|
echo "[Graft] Linking Mksh..."
|
||||||
|
LINKER_SCRIPT="../../apps/linker_user.ld"
|
||||||
|
BUILD_DIR="../../build"
|
||||||
|
|
||||||
|
# We must link against Membrane (libnexus.a)
|
||||||
|
# And startup objects (subject_entry.o) if we want it to be a valid Nexus app
|
||||||
|
# Wait, userland programs in Nexus are typically fibers or distinct address spaces?
|
||||||
|
# If "init.nim" execs it, it's likely a distinct binary loaded by kernel or init.
|
||||||
|
|
||||||
|
zig cc -target $TARGET -nostdlib -static -T $LINKER_SCRIPT \
|
||||||
|
$BUILD_DIR/subject_entry.o \
|
||||||
|
$BUILD_DIR/stubs_user.o \
|
||||||
|
$BUILD_DIR/libc_shim.o \
|
||||||
|
stubs_mksh.o \
|
||||||
|
$OBJS \
|
||||||
|
-L$BUILD_DIR -lnexus \
|
||||||
|
-o mksh.elf
|
||||||
|
|
||||||
|
echo "[Graft] Mksh ELF created: mksh.elf"
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
#!/bin/bash
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
safeIFS=' '
|
||||||
|
safeIFS=" $safeIFS$nl"
|
||||||
|
IFS=$safeIFS
|
||||||
|
allu=QWERTYUIOPASDFGHJKLZXCVBNM
|
||||||
|
alll=qwertyuiopasdfghjklzxcvbnm
|
||||||
|
alln=0123456789
|
||||||
|
|
||||||
|
test_n() {
|
||||||
|
test x"$1" = x"" || return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
test_z() {
|
||||||
|
test x"$1" = x""
|
||||||
|
}
|
||||||
|
|
||||||
|
genopt_die() {
|
||||||
|
if test_z "$1"; then
|
||||||
|
echo >&2 "E: invalid input in '$srcfile': '$line'"
|
||||||
|
else
|
||||||
|
echo >&2 "E: $*"
|
||||||
|
echo >&2 "N: in '$srcfile': '$line'"
|
||||||
|
fi
|
||||||
|
rm -f "$bn.gen"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
genopt_soptc() {
|
||||||
|
optc=`echo "$line" | sed 's/^[<>]\(.\).*$/\1/'`
|
||||||
|
test x"$optc" = x'|' && return
|
||||||
|
optclo=`echo "$optc" | tr $allu $alll`
|
||||||
|
if test x"$optc" = x"$optclo"; then
|
||||||
|
islo=1
|
||||||
|
else
|
||||||
|
islo=0
|
||||||
|
fi
|
||||||
|
sym=`echo "$line" | sed 's/^[<>]/|/'`
|
||||||
|
o_str=$o_str$nl"<$optclo$islo$sym"
|
||||||
|
}
|
||||||
|
|
||||||
|
genopt_scond() {
|
||||||
|
case x$cond in
|
||||||
|
x)
|
||||||
|
cond=
|
||||||
|
;;
|
||||||
|
x*' '*)
|
||||||
|
cond=`echo "$cond" | sed 's/^ //'`
|
||||||
|
cond="#if $cond"
|
||||||
|
;;
|
||||||
|
x'!'*)
|
||||||
|
cond=`echo "$cond" | sed 's/^!//'`
|
||||||
|
cond="#ifndef $cond"
|
||||||
|
;;
|
||||||
|
x*)
|
||||||
|
cond="#ifdef $cond"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
do_genopt() {
|
||||||
|
srcfile=$1
|
||||||
|
test -f "$srcfile" || genopt_die Source file \$srcfile not set.
|
||||||
|
bn=`basename "$srcfile" | sed 's/.opt$//'`
|
||||||
|
o_hdr='/* +++ GENERATED FILE +++ DO NOT EDIT +++ */'
|
||||||
|
o_gen=
|
||||||
|
o_str=
|
||||||
|
o_sym=
|
||||||
|
ddefs=
|
||||||
|
state=0
|
||||||
|
exec <"$srcfile"
|
||||||
|
IFS=
|
||||||
|
while IFS= read line; do
|
||||||
|
IFS=$safeIFS
|
||||||
|
case $state:$line in
|
||||||
|
2:'|'*)
|
||||||
|
# end of input
|
||||||
|
o_sym=`echo "$line" | sed 's/^.//'`
|
||||||
|
o_gen=$o_gen$nl"#undef F0"
|
||||||
|
o_gen=$o_gen$nl"#undef FN"
|
||||||
|
o_gen=$o_gen$ddefs
|
||||||
|
state=3
|
||||||
|
;;
|
||||||
|
1:@@)
|
||||||
|
# start of data block
|
||||||
|
o_gen=$o_gen$nl"#endif"
|
||||||
|
o_gen=$o_gen$nl"#ifndef F0"
|
||||||
|
o_gen=$o_gen$nl"#define F0 FN"
|
||||||
|
o_gen=$o_gen$nl"#endif"
|
||||||
|
state=2
|
||||||
|
;;
|
||||||
|
*:@@*)
|
||||||
|
genopt_die ;;
|
||||||
|
0:/\*-|0:\ \**|0:)
|
||||||
|
o_hdr=$o_hdr$nl$line
|
||||||
|
;;
|
||||||
|
0:@*|1:@*)
|
||||||
|
# start of a definition block
|
||||||
|
sym=`echo "$line" | sed 's/^@//'`
|
||||||
|
if test $state = 0; then
|
||||||
|
o_gen=$o_gen$nl"#if defined($sym)"
|
||||||
|
else
|
||||||
|
o_gen=$o_gen$nl"#elif defined($sym)"
|
||||||
|
fi
|
||||||
|
ddefs="$ddefs$nl#undef $sym"
|
||||||
|
state=1
|
||||||
|
;;
|
||||||
|
0:*|3:*)
|
||||||
|
genopt_die ;;
|
||||||
|
1:*)
|
||||||
|
# definition line
|
||||||
|
o_gen=$o_gen$nl$line
|
||||||
|
;;
|
||||||
|
2:'<'*'|'*)
|
||||||
|
genopt_soptc
|
||||||
|
;;
|
||||||
|
2:'>'*'|'*)
|
||||||
|
genopt_soptc
|
||||||
|
cond=`echo "$line" | sed 's/^[^|]*|//'`
|
||||||
|
genopt_scond
|
||||||
|
case $optc in
|
||||||
|
'|') optc=0 ;;
|
||||||
|
*) optc=\'$optc\' ;;
|
||||||
|
esac
|
||||||
|
IFS= read line || genopt_die Unexpected EOF
|
||||||
|
IFS=$safeIFS
|
||||||
|
test_z "$cond" || o_gen=$o_gen$nl"$cond"
|
||||||
|
o_gen=$o_gen$nl"$line, $optc)"
|
||||||
|
test_z "$cond" || o_gen=$o_gen$nl"#endif"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
case $state:$o_sym in
|
||||||
|
3:) genopt_die Expected optc sym at EOF ;;
|
||||||
|
3:*) ;;
|
||||||
|
*) genopt_die Missing EOF marker ;;
|
||||||
|
esac
|
||||||
|
echo "$o_str" | sort | while IFS='|' read x opts cond; do
|
||||||
|
IFS=$safeIFS
|
||||||
|
test_n "$x" || continue
|
||||||
|
genopt_scond
|
||||||
|
test_z "$cond" || echo "$cond"
|
||||||
|
echo "\"$opts\""
|
||||||
|
test_z "$cond" || echo "#endif"
|
||||||
|
done | {
|
||||||
|
echo "$o_hdr"
|
||||||
|
echo "#ifndef $o_sym$o_gen"
|
||||||
|
echo "#else"
|
||||||
|
cat
|
||||||
|
echo "#undef $o_sym"
|
||||||
|
echo "#endif"
|
||||||
|
} >"$bn.gen"
|
||||||
|
IFS=$safeIFS
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
do_genopt "$1"
|
||||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,136 @@
|
||||||
|
#!/bin/sh
|
||||||
|
rcsid='$MirOS: src/bin/mksh/FAQ2HTML.sh,v 1.2 2020/10/31 04:17:36 tg Exp $'
|
||||||
|
#-
|
||||||
|
# Copyright © 2020
|
||||||
|
# mirabilos <m@mirbsd.org>
|
||||||
|
#
|
||||||
|
# Provided that these terms and disclaimer and all copyright notices
|
||||||
|
# are retained or reproduced in an accompanying document, permission
|
||||||
|
# is granted to deal in this work without restriction, including un‐
|
||||||
|
# limited rights to use, publicly perform, distribute, sell, modify,
|
||||||
|
# merge, give away, or sublicence.
|
||||||
|
#
|
||||||
|
# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
|
||||||
|
# the utmost extent permitted by applicable law, neither express nor
|
||||||
|
# implied; without malicious intent or gross negligence. In no event
|
||||||
|
# may a licensor, author or contributor be held liable for indirect,
|
||||||
|
# direct, other damage, loss, or other issues arising in any way out
|
||||||
|
# of dealing in the work, even if advised of the possibility of such
|
||||||
|
# damage or existence of a defect, except proven that it results out
|
||||||
|
# of said person’s immediate fault when using the work as intended.
|
||||||
|
#-
|
||||||
|
|
||||||
|
set -e
|
||||||
|
LC_ALL=C; LANGUAGE=C
|
||||||
|
export LC_ALL; unset LANGUAGE
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
srcdir=$(dirname "$0")
|
||||||
|
|
||||||
|
p=--posix
|
||||||
|
sed $p -e q </dev/null >/dev/null 2>&1 || p=
|
||||||
|
|
||||||
|
v=$1
|
||||||
|
if test -z "$v"; then
|
||||||
|
v=$(sed $p -n '/^#define MKSH_VERSION "\(.*\)"$/s//\1/p' "$srcdir"/sh.h)
|
||||||
|
fi
|
||||||
|
src_id=$(sed $p -n '/^RCSID: /s///p' "$srcdir"/mksh.faq)
|
||||||
|
# sanity check
|
||||||
|
case $src_id in
|
||||||
|
*"$nl"*)
|
||||||
|
echo >&2 "E: more than one RCSID in mksh.faq?"
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
sed $p \
|
||||||
|
-e '/^RCSID: \$/s/^.*$/----/' \
|
||||||
|
-e 's!@@RELPATH@@!http://www.mirbsd.org/!g' \
|
||||||
|
-e 's^ <span style="display:none;"> </span>' \
|
||||||
|
"$srcdir"/mksh.faq | tr '\n' '' | sed $p \
|
||||||
|
-e 'sg' \
|
||||||
|
-e 's----g' \
|
||||||
|
-e 's\([^]*\)\1g' \
|
||||||
|
-e 's\([^]*\)\1g' \
|
||||||
|
-e 's\([^]*\)*ToC: \([^]*\)Title: \([^]*\)\([^]*\)\{0,1\}</div><h2 id="\2"><a href="#\2">\3</a></h2><div>g' \
|
||||||
|
-e 's[^]*</div><div>g' \
|
||||||
|
-e 's^</div>*' \
|
||||||
|
-e 's$</div>' \
|
||||||
|
-e 's<><error><>g' \
|
||||||
|
-e 'sg' | tr '' '\n' >FAQ.tmp
|
||||||
|
|
||||||
|
exec >FAQ.htm~
|
||||||
|
cat <<EOF
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||||
|
<title>mksh $v FAQ (local copy)</title>
|
||||||
|
<meta name="source" content="$src_id" />
|
||||||
|
<meta name="generator" content="$rcsid" />
|
||||||
|
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
|
||||||
|
.boxhead {
|
||||||
|
margin-bottom:0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxtext {
|
||||||
|
border:4px ridge green;
|
||||||
|
margin:0px 24px 0px 18px;
|
||||||
|
padding:2px 3px 2px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxfoot {
|
||||||
|
margin-top:0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2:before {
|
||||||
|
content:"🔗 ";
|
||||||
|
}
|
||||||
|
|
||||||
|
a[href^="ftp://"]:after,
|
||||||
|
a[href^="http://"]:after,
|
||||||
|
a[href^="https://"]:after,
|
||||||
|
a[href^="irc://"]:after,
|
||||||
|
a[href^="mailto:"]:after,
|
||||||
|
a[href^="news:"]:after,
|
||||||
|
a[href^="nntp://"]:after {
|
||||||
|
content:"⏍";
|
||||||
|
color:#FF0000;
|
||||||
|
vertical-align:super;
|
||||||
|
margin:0 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
/* ↑ → ↓ ← */
|
||||||
|
margin:0px 9px 0px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt {
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
/*]]>*/--></style>
|
||||||
|
</head><body>
|
||||||
|
<p>Note: Links marked like <a href="irc://chat.freenode.net/!/bin/mksh">this
|
||||||
|
one to the mksh IRC channel</a> connect to external resources.</p>
|
||||||
|
<p>⚠ <b>Notice:</b> the website will have <a
|
||||||
|
href="http://www.mirbsd.org/mksh-faq.htm">the latest version of the
|
||||||
|
mksh FAQ</a> online.</p>
|
||||||
|
<h1>Table of Contents</h1>
|
||||||
|
<ul>
|
||||||
|
EOF
|
||||||
|
sed $p -n \
|
||||||
|
'/^<h2 id="\([^"]*"\)><a[^>]*\(>.*<\/a><\/\)h2>$/s//<li><a href="#\1\2li>/p' \
|
||||||
|
<FAQ.tmp
|
||||||
|
cat <<EOF
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1>Frequently Asked Questions</h1>
|
||||||
|
EOF
|
||||||
|
cat FAQ.tmp - <<EOF
|
||||||
|
<h1>Imprint</h1>
|
||||||
|
<p>This offline HTML page for mksh $v was automatically generated
|
||||||
|
from the sources.</p>
|
||||||
|
</body></html>
|
||||||
|
EOF
|
||||||
|
exec >/dev/null
|
||||||
|
rm FAQ.tmp
|
||||||
|
mv FAQ.htm~ FAQ.htm
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue