feat(tinybox): graft toybox integration and build system automation

- Integrated ToyBox as git submodule
- Added src/nexus/builder/toybox.nim for automated cross-compilation
- Updated InitRD builder to support symlinks
- Refactored Kernel builder to fix duplicate symbol and path issues
- Modified forge.nim to orchestrate TinyBox synthesis (mksh + toybox)
- Updated SPEC-006-TinyBox.md with complete architecture
- Added mksh binary to initrd graft source
This commit is contained in:
Markus Maiwald 2026-01-08 21:18:08 +01:00
parent 11db62ea8c
commit 7207282236
29 changed files with 746 additions and 691 deletions

View File

@ -46,10 +46,10 @@ export const multiboot2_header linksection(".multiboot2") = Multiboot2Header{
// Entry Point
// =========================================================
extern fn kmain() noreturn;
extern fn riscv_init() noreturn;
export fn _start() callconv(.Naked) noreturn {
// Clear BSS, set up stack, then jump to Nim
export fn _start() callconv(.naked) noreturn {
// Clear BSS, set up stack, then jump to RISC-V Init
asm volatile (
\\ // Set up stack
\\ la sp, __stack_top
@ -63,8 +63,8 @@ export fn _start() callconv(.Naked) noreturn {
\\ addi t0, t0, 8
\\ j 1b
\\2:
\\ // Jump to Nim kmain
\\ call kmain
\\ // Jump to HAL Init
\\ call riscv_init
\\
\\ // Should never return
\\ wfi

View File

@ -1,11 +1,13 @@
# Rumpk Linker Script (ARM64)
# For QEMU virt machine
# Rumpk Linker Script (RISC-V 64)
# For QEMU virt machine (RISC-V)
ENTRY(_start)
SECTIONS
{
. = 0x40080000; /* QEMU virt kernel load address */
. = 0x80200000; /* Standard RISC-V QEMU virt kernel address */
PROVIDE(__kernel_vbase = .);
PROVIDE(__kernel_pbase = .);
.text : {
*(.text._start)
@ -17,9 +19,19 @@ SECTIONS
}
.data : {
. = ALIGN(16);
__global_pointer$ = . + 0x800;
*(.sdata*)
*(.sdata.*)
*(.data*)
}
.initrd : {
_initrd_start = .;
KEEP(*(.initrd))
_initrd_end = .;
}
.bss : {
__bss_start = .;
*(.bss*)
@ -27,6 +39,12 @@ SECTIONS
__bss_end = .;
}
.stack (NOLOAD) : {
. = ALIGN(16);
. += 0x100000; /* 1MB Stack */
__stack_top = .;
}
/DISCARD/ : {
*(.comment)
*(.note*)

View File

@ -1,210 +1,57 @@
// C runtime stubs for freestanding Nim
#include <stddef.h>
void *memcpy(void *dest, const void *src, size_t n) {
unsigned char *d = dest;
const unsigned char *s = src;
while (n--) *d++ = *s++;
return dest;
}
/* Duplicates provided by libnexus.a (clib.o) */
#if 0
void *memcpy(void *dest, const void *src, size_t n) { ... }
void *memset(void *s, int c, size_t n) { ... }
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) {
unsigned char *p = s;
while (n--) *p++ = (unsigned char)c;
return s;
}
/* Externs from libnexus.a */
extern size_t strlen(const char *s);
extern int atoi(const char *nptr);
extern int strncmp(const char *s1, const char *s2, size_t n);
void *memmove(void *dest, const void *src, size_t n) {
unsigned char *d = dest;
const unsigned char *s = src;
if (d < s) {
while (n--) *d++ = *s++;
} else {
d += n;
s += n;
while (n--) *--d = *--s;
char *strcpy(char *dest, const char *src) { ... }
int strcmp(const char *s1, const char *s2) { ... }
char *strncpy(char *dest, const char *src, size_t n) { ... }
#endif
// panic is used by abort/exit
void panic(const char* msg) {
extern void console_write(const char*, unsigned long);
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
void abort(void) {
/* Call Nim panic */
extern void panic(const char*);
panic("abort() called");
while(1) {}
}
#if 0
/* Stdio stubs - these call into Zig UART */
extern void console_write(const char*, unsigned long);
int puts(const char *s) {
if (s) {
unsigned long len = strlen(s);
console_write(s, len);
console_write("\n", 1);
}
return 0;
}
int putchar(int c) {
char buf[1] = {(char)c};
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;
}
int puts(const char *s) { ... }
int putchar(int c) { ... }
// ... (printf, etc)
int snprintf(char *str, size_t size, const char *format, ...) { ... }
int fflush(void *stream) { ... }
unsigned long fwrite(const void *ptr, unsigned long size, unsigned long nmemb, void *stream) { ... }
sighandler_t signal(int signum, sighandler_t handler) { ... }
int raise(int sig) { ... }
int sprintf(char *str, const char *format, ...) { ... }
double strtod(const char *nptr, char **endptr) { ... }
#endif
/* Exit stubs */
void exit(int status) {
@ -217,30 +64,10 @@ void _Exit(int 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
// 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 *)) {
// 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

View File

@ -92,13 +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_get_kernel_satp(): uint64 {.importc, cdecl.}
proc debug(s: string) =
proc debug(s: cstring) =
proc console_write(p: pointer, len: int) {.importc, cdecl.}
if s.len > 0:
console_write(unsafeAddr s[0], s.len)
var i = 0
while s[i] != '\0': i += 1
if i > 0:
console_write(cast[pointer](s), i)
proc print_arch_info*() =
debug("[Rumpk] Architecture Context: " & ARCH_NAME & "\n")
debug("[Rumpk] Architecture Context: riscv64\n")
# =========================================================
# Constants
@ -118,10 +120,12 @@ var current_fiber* {.global.}: Fiber = addr main_fiber
# =========================================================
proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
var msg = "[FIBER] Trampoline Entry!\n"
let msg: cstring = "[FIBER] Trampoline Entry!\n"
# We can't use kprintln here if it's not imported or we use emit
proc console_write(p: pointer, len: int) {.importc, cdecl.}
console_write(addr msg[0], msg.len)
var i = 0
while msg[i] != '\0': i += 1
console_write(cast[pointer](msg), i)
let f = current_fiber
if f.state.entry != nil:

View File

@ -10,7 +10,7 @@
## Freestanding implementation (No OS module dependencies).
## Uses fixed-size buffers and raw blocks for persistence.
import ring, fiber # For yield
import ../ring, ../fiber # For yield
proc kprintln(s: cstring) {.importc, cdecl.}
proc kprint(s: cstring) {.importc, cdecl.}

View File

@ -1,27 +1,14 @@
/* Minimal stdio.h stub for freestanding Nim */
#ifndef _STDIO_H
#define _STDIO_H
#include <stddef.h>
#include <stdarg.h>
typedef struct {
int fd;
} FILE;
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
#define EOF (-1)
int printf(const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
int fprintf(FILE *stream, const char *format, ...);
int rename(const char *oldpath, const char *newpath);
int remove(const char *pathname);
#endif /* _STDIO_H */
#endif

View File

@ -1,24 +1,14 @@
/* Minimal stdlib.h stub for freestanding Nim */
#ifndef _STDLIB_H
#define _STDLIB_H
#include <stddef.h>
void exit(int status);
void abort(void);
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);
void *calloc(size_t nmemb, size_t size);
void abort(void);
void exit(int status);
void _Exit(int status);
int atoi(const char *nptr);
double strtod(const char *nptr, char **endptr);
long strtol(const char *nptr, char **endptr, int base);
unsigned long strtoul(const char *nptr, char **endptr, int base);
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
int rand(void);
void srand(unsigned int seed);
#endif /* _STDLIB_H */
#endif

View File

@ -1,33 +1,17 @@
/* Minimal string.h stub for freestanding Nim */
#ifndef _STRING_H
#define _STRING_H
#include <stddef.h>
/* Minimal implementations defined in cstubs.c */
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 *memmove(void *dest, const void *src, 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);
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
void *memchr(const void *s, int c, size_t n);
char *strerror(int errnum);
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);
char *strstr(const char *haystack, const char *needle);
char *strdup(const char *s);
size_t strspn(const char *s, const char *accept);
size_t strcspn(const char *s, const char *reject);
char *strpbrk(const char *s, const char *accept);
char *strsep(char **stringp, const char *delim);
int strcasecmp(const char *s1, const char *s2);
int strncasecmp(const char *s1, const char *s2, size_t n);
size_t strlen(const char *s);
#endif /* _STRING_H */
#endif

View File

@ -8,11 +8,10 @@
# Nexus Sovereign Core: Kernel Implementation
# target Bravo: Complete Build Unification
import ring, fiber, ion, sched, pty, cspace, ontology, channels, fastpath, utcp
import fs/vfs, fs/tar, fs/sfs
import fiber, ion, sched, pty, cspace, ontology, fastpath, utcp
import fs/vfs, fs/tar
import loader/elf
import ../libs/membrane/term
import ../libs/membrane/libc as libc_impl
const
MAX_WORKERS* = 8
@ -31,8 +30,9 @@ proc virtio_blk_read(sector: uint64, buf: ptr byte) {.importc, cdecl.}
proc virtio_blk_write(sector: uint64, buf: ptr byte) {.importc, cdecl.}
proc fb_kern_get_addr(): uint64 {.importc, cdecl.}
proc hal_io_init() {.importc, cdecl.}
proc console_write*(p: pointer, len: csize_t) {.importc, cdecl.}
proc console_write*(p: pointer, len: csize_t) {.importc: "hal_console_write", cdecl.}
proc nexshell_main() {.importc, cdecl.}
proc console_poll() {.importc, cdecl.}
proc ion_get_virt(id: uint16): uint64 {.importc, cdecl.}
# InitRD Symbols
@ -193,11 +193,33 @@ proc subject_fiber_entry() {.cdecl.} =
# Fallback (Legacy/Init) - Top of the 64MB Sentinel Cell
sp = 0x8BFFFFF0'u64
kprint("[Subject:"); kprint_hex(fid); kprint("] JUMPING to Userland. SP="); kprint_hex(sp); kprintln("")
kprintln("╔════════════════════════════════════════════════════╗")
kprintln("║ PRE-FLIGHT: USERLAND TRANSITION ║")
kprintln("╚════════════════════════════════════════════════════╝")
kprint(" Entry: "); kprint_hex(entry_addr); kprintln("")
kprint(" SysTable: "); kprint_hex(SYSTABLE_BASE); kprintln("")
kprint(" Stack: "); kprint_hex(sp); kprintln("")
kprint(" SATP: "); kprint_hex(current_fiber.satp_value); kprintln("")
kprint(" Phys Off: "); kprint_hex(current_fiber.phys_offset); kprintln("")
kprintln("")
# 🔥 CRITICAL: Activate worker page table BEFORE entering userland!
# Without this, userland executes with kernel identity map → instant page fault
if current_fiber.satp_value != 0:
proc mm_activate_satp(satp_val: uint64) {.importc, cdecl.}
kprint("[Subject:"); kprint_hex(fid); kprint("] Activating worker page table: "); kprint_hex(current_fiber.satp_value); kprintln("")
mm_activate_satp(current_fiber.satp_value)
hal_enter_userland(entry_addr, SYSTABLE_BASE, sp)
else:
kprint("[Subject:"); kprint_hex(fid); kprintln("] Loader failed to find/load payload!")
while true: fiber_sleep(1000)
proc nexshell_fiber_entry() {.cdecl.} =
kprintln("[NexShell] Interactive Fiber Online")
while true:
console_poll()
fiber_sleep(10)
proc compositor_fiber_entry() {.cdecl.} =
kprintln("[Compositor] Fiber Entry reached.")
@ -254,6 +276,7 @@ proc ion_fiber_entry() {.cdecl.} =
while true:
var pkt: CmdPacket
if chan_cmd.recv(pkt):
kprint("[ION] Received Packet Kind: "); kprint_hex(uint64(pkt.kind))
case CmdType(pkt.kind):
of CMD_SYS_EXIT:
kprintln("[ION] Restarting Subject...")
@ -294,7 +317,9 @@ proc ion_fiber_entry() {.cdecl.} =
fiber_child.satp_value = mm_create_worker_map(cast[uint64](addr stack_child[0]), uint64(sizeof(stack_child)), SYSTABLE_BASE, cell_base, cell_size)
kprintln("[ION] Child fiber spawned successfully")
else: discard
fiber_sleep(10_000_000) # 10ms
else:
# No pending commands - yield to scheduler (short sleep to avoid busy spin)
fiber_sleep(1) # 1ms
proc rumpk_yield_internal*() {.exportc, cdecl.} =
# Switch back to the main dispatcher loop
@ -345,11 +370,15 @@ proc fiber_netswitch_entry() {.cdecl.} =
var res = ion_tx_push(pkt)
if not res: kprintln("[NetSwitch] Drop (TX Full)")
# Manual Polling (Interrupts Disabled)
# Poll Network
virtio_net_poll()
# Poll UART (Backup/Primary Polling Mode)
{.emit: "extern void uart_poll_input(void); uart_poll_input();".}
# Prevent Starvation
fiber_sleep(10) # 10ms - allow DHCP state machine to execute
# 10ms - allow DHCP state machine to execute
proc ion_ingress*(id: uint16, len: uint16, offset: uint16) {.exportc, cdecl.} =
## Handle packet from Network Driver
@ -384,22 +413,58 @@ proc k_check_deferred_yield*() {.exportc, cdecl.} =
fiber_yield()
proc k_handle_exception*(scause, sepc, stval: uint) {.exportc, cdecl.} =
kprint("[IMMUNE] EXCEPTION: scause="); kprint_hex(scause)
kprint(" sepc="); kprint_hex(sepc)
kprint(" stval="); kprint_hex(stval)
kprintln("")
kprintln("╔════════════════════════════════════════════════════╗")
kprintln("║ KERNEL IMMUNE SYSTEM: EXCEPTION DETECTED ║")
kprintln("╚════════════════════════════════════════════════════╝")
# Decode scause
let cause_code = scause and 0x7FFFFFFFFFFFFFFF'u64
kprint(" SCAUSE: "); kprint_hex(scause); kprint(" (")
case cause_code:
of 0: kprint("Instruction address misaligned")
of 1: kprint("Instruction access fault")
of 2: kprint("Illegal instruction")
of 3: kprint("Breakpoint")
of 4: kprint("Load address misaligned")
of 5: kprint("Load access fault")
of 6: kprint("Store/AMO address misaligned")
of 7: kprint("Store/AMO access fault")
of 8: kprint("Environment call from U-mode")
of 9: kprint("Environment call from S-mode")
of 12: kprint("Instruction page fault")
of 13: kprint("Load page fault")
of 15: kprint("Store/AMO page fault")
else: kprint("Unknown exception")
kprintln(")")
kprint(" SEPC: "); kprint_hex(sepc); kprintln(" (Faulting PC)")
kprint(" STVAL: "); kprint_hex(stval); kprintln(" (Fault address/info)")
# Dump current fiber context
if current_fiber != nil:
kprint(" Fiber: "); kprint_hex(current_fiber.id)
kprint(" SATP: "); kprint_hex(current_fiber.satp_value)
kprintln("")
kprintln("")
kprintln("[IMMUNE] System HALTING (Trap Loop Prevention).")
while true:
{.emit: "asm volatile(\"wfi\");".}
proc k_get_current_satp*(): uint64 {.exportc, cdecl.} =
if current_fiber != nil:
return current_fiber.satp_value
return 0
proc wrapper_vfs_write(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} =
return ion_vfs_write(fd, buf, count)
# --- SYSCALL HANDLER ---
proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
# if nr != 0x100:
# kprint("[Syscall] NR: "); kprint_hex(nr); kprintln("")
if nr != 0x100 and nr != 0x205 and nr != 0x204 and nr != 0x203:
kprint("[Syscall] NR: "); kprint_hex(uint64(nr)); kprintln("")
case nr:
of 0x01: # EXIT
@ -433,26 +498,30 @@ proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
of 0x202: # LIST
return uint(ion_vfs_list(cast[pointer](a0), uint64(a1)))
of 0x905: # SYS_SOCK_RESOLVE
return uint(libc_impl.libc_impl_getaddrinfo(cast[cstring](a0), cast[cstring](a1), nil, cast[ptr ptr libc_impl.AddrInfo](a2)))
# TODO: Implement getaddrinfo kernel integration
return 0 # Not implemented yet
of 0x203: # READ
# kprint("[Syscall] READ(fd="); kprint_hex(a0); kprint(")\n")
var vres = -2
if a0 == 0 or vres == -2:
let pid = if current_fiber.pty_id >= 0: current_fiber.pty_id else: 0
while true:
if pty_has_data_for_slave(pid):
var buf: array[1, byte]
let n = pty_read_slave(PTY_SLAVE_BASE + pid, addr buf[0], 1)
if n > 0:
cast[ptr UncheckedArray[byte]](a1)[0] = buf[0]
return 1
var buf: array[1, byte]
let n = pty_read_slave(PTY_SLAVE_BASE + pid, addr buf[0], 1)
if n > 0:
# kprint("[Kernel] READ delivered PTY byte: "); kprint_hex8(buf[0]); kprint("\n")
cast[ptr UncheckedArray[byte]](a1)[0] = buf[0]
return 1
var pkt: IonPacket
if chan_input.recv(pkt):
kprint("[Kernel] Got Input Packet of len: "); kprint_hex(uint64(pkt.len)); kprint("\n")
let n = if uint64(pkt.len) < a2: uint64(pkt.len) else: a2
if n > 0:
# copyMem(cast[pointer](a1), cast[pointer](pkt.data), int(n))
# console_write(pkt.data, csize_t(n))
let data = cast[ptr UncheckedArray[byte]](pkt.data)
for i in 0 ..< int(n): pty_push_input(pid, char(data[i]))
for i in 0 ..< int(n):
kprint(" Input Char: "); kprint(cast[cstring](unsafeAddr data[i])); kprint("\n")
pty_push_input(pid, char(data[i]))
ion_free_raw(pkt.id)
# Loop again to read from PTY
else:
@ -494,7 +563,13 @@ proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
# Re-initialize fiber_subject with new binary
# The ION fiber will pick this up and restart the Subject fiber
var pkt = CmdPacket(kind: uint32(CMD_SPAWN_FIBER), arg: 0)
discard chan_cmd.send(pkt)
# DEBUG: Check if send works
if chan_cmd.send(pkt):
kprintln("[Kernel] CMD_SPAWN_FIBER sent to ION.")
else:
kprintln("[Kernel] CRITICAL: Failed to send CMD_SPAWN_FIBER to ION!")
# Return fiber ID (always 4 for Subject currently)
return 4
else: return 0
@ -631,7 +706,7 @@ proc kmain() {.exportc, cdecl.} =
let compositor_spawn_id = emit_fiber_spawn(3, 0, boot_id) # Compositor fiber
discard compositor_spawn_id
init_fiber(addr fiber_nexshell, nexshell_main, addr stack_nexshell[0], sizeof(stack_nexshell))
init_fiber(addr fiber_nexshell, nexshell_fiber_entry, addr stack_nexshell[0], sizeof(stack_nexshell))
let shell_spawn_id = emit_fiber_spawn(2, 0, boot_id) # NexShell fiber
# NetSwitch Spawn
@ -683,8 +758,8 @@ proc kmain() {.exportc, cdecl.} =
# cast[ptr uint32](plic_base + 140)[] = 1 # VirtIO-Net (IRQ 35: 35*4 = 140)
# Enable (Supervisor Context 1)
# IRQs 0-31
# cast[ptr uint32](plic_base + 0x2000 + 0x80)[] = (1'u32 shl 10)
# IRQs 0-31 (Enable IRQ 10 = UART)
cast[ptr uint32](plic_base + 0x2000 + 0x80)[] = (1'u32 shl 10)
# IRQs 32-63
# cast[ptr uint32](plic_base + 0x2000 + 0x80 + 4)[] = 0x0000000F # Enable 32,33,34,35

View File

@ -11,11 +11,40 @@
# Required for Nim --os:any / --os:standalone
# 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.}
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
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) =
if s.len > 0:
console_write(unsafeAddr s[0], csize_t(s.len))
@ -32,4 +61,17 @@ proc panic(s: cstring) {.exportc, noreturn.} =
rawoutput("\n")
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.}

View File

@ -71,17 +71,17 @@ export fn rumpk_pfree(ptr: *anyopaque) void {
hal.pfree(ptr);
}
export fn rumpk_halt() noreturn {
hal.halt();
}
// export fn rumpk_halt() noreturn {
// hal.halt();
// }
var mock_ticks: u64 = 0;
export fn rumpk_timer_now_ns() u64 {
// Phase 1 Mock: Incrementing counter to simulate time passage per call
mock_ticks += 100000; // 100us per call
return mock_ticks;
}
// export fn rumpk_timer_now_ns() u64 {
// // Phase 1 Mock: Incrementing counter to simulate time passage per call
// mock_ticks += 100000; // 100us per call
// return mock_ticks;
// }
// =========================================================
// Ground Zero Phase 1: CSpace Integration (SPEC-020)
@ -96,3 +96,34 @@ 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");
}

View File

@ -96,11 +96,13 @@ export fn hal_channel_pop(handle: u64, out_pkt: *IonPacket) bool {
export fn hal_cmd_push(handle: u64, pkt: CmdPacket) bool {
validate_ring_ptr(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);
}
export fn hal_cmd_pop(handle: u64, out_pkt: *CmdPacket) bool {
validate_ring_ptr(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);
}

View File

@ -14,20 +14,34 @@
const std = @import("std");
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");
comptime {
_ = stubs;
}
// =========================================================
// Entry Point (Naked)
// =========================================================
export fn _start() callconv(.naked) noreturn {
export fn riscv_init() callconv(.naked) noreturn {
asm volatile (
// 1. Disable Interrupts
\\ csrw sie, zero
\\ csrw satp, 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 (sstatus.FS = Initial [01]) and Vectors (sstatus.VS = Initial [01])
\\ li t0, 0x2200 // FS=bit 13, VS=bit 9
\\ csrs sstatus, t0
// 1.2 Initialize Global Pointer
@ -60,7 +74,7 @@ export fn _start() callconv(.naked) noreturn {
\\ 1: wfi
\\ j 1b
);
unreachable;
// unreachable;
}
// Trap Frame Layout (Packed on stack)
@ -236,11 +250,54 @@ 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_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 {
// 🔥 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;
// uart.print("[Trap] Entered Handler. scause: ");
// uart.print_hex(scause);
// uart.print("\n");
// DEBUG: Diagnose Userland Crash (Only print exceptions, ignore interrupts for noise)
if ((scause >> 63) == 0) {
uart.print("\n[Trap] Exception! Cause:");
uart.print_hex(scause);
uart.print(" PC:");
uart.print_hex(frame.sepc);
uart.print(" Val:");
uart.print_hex(frame.stval);
uart.print("\n");
}
// Check high bit: 0 = Exception, 1 = Interrupt
if ((scause >> 63) != 0) {
@ -251,54 +308,75 @@ export fn rss_trap_handler(frame: *TrapFrame) void {
const irq = PLIC_CLAIM.*;
if (irq == 10) { // UART0 is IRQ 10 on Virt machine
uart.poll_input();
// 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 interrupt
// Complete the IRQ
PLIC_CLAIM.* = irq;
} else if (intr_id == 5) {
// Supervisor Timer Interrupt
// Disable (One-shot)
asm volatile ("csrc sie, %[mask]"
// Timer Interrupt
asm volatile ("csrc sip, %[mask]"
:
: [mask] "r" (@as(usize, 1 << 5)),
: [mask] "r" (@as(u64, 1 << 5)),
);
// Call Nim Handler
rumpk_timer_handler();
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) {}
}
k_check_deferred_yield();
return;
}
// 8: ECALL from U-mode
// 9: ECALL from S-mode
if (scause == 8 or scause == 9) {
// Advance PC to skip 'ecall' instruction (4 bytes)
frame.sepc += 4;
// 🔥 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;
// Dispatch Syscall
const res = k_handle_syscall(frame.a7, frame.a0, frame.a1, frame.a2);
// Write result back to a0
frame.a0 = res;
// DIAGNOSTIC: Syscall completed
// uart.print("[Trap] Syscall done, returning to userland\n");
k_check_deferred_yield();
return;
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);
}
}
// Delegate all other exceptions to the Kernel Immune System
k_handle_exception(scause, frame.sepc, frame.stval);
// Safety halt if kernel returns (should be unreachable)
while (true) {}
}
// SAFETY(Stack): Memory is immediately used by _start before any read.
@ -331,25 +409,33 @@ export fn zig_entry() void {
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]);
}
export fn console_read() c_int {
if (uart.read_byte()) |b| {
if (uart_input.read_byte()) |b| {
return @as(c_int, b);
}
return -1;
}
export fn console_poll() void {
uart.poll_input();
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");
extern fn hal_surface_init() void;

BIN
hal/initrd/bin/mksh Executable file

Binary file not shown.

View File

@ -13,14 +13,15 @@
//! SAFETY: Runs in bare-metal mode with no runtime support.
const uart = @import("uart.zig");
const hud = @import("hud.zig");
const virtio_net = @import("virtio_net.zig");
const virtio_block = @import("virtio_block.zig");
const initrd = @import("initrd.zig");
export fn hal_io_init() void {
virtio_net.init();
virtio_block.init();
_ = initrd._initrd_payload;
}
// =========================================================

View File

@ -22,7 +22,7 @@ const uart = @import("uart.zig");
// Simple Bump Allocator for L0
// SAFETY(Heap): Memory is written by malloc before any read occurs.
// 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_init_done: bool = false;
@ -30,6 +30,12 @@ export fn debug_print(s: [*]const u8, len: usize) void {
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)
const BlockHeader = struct {
size: usize,
@ -139,3 +145,133 @@ export fn get_ticks() u32 {
// 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;

View File

@ -18,34 +18,23 @@ const std = @import("std");
const builtin = @import("builtin");
// ARM64 PL011 Constants
const PL011_BASE: usize = 0x09000000;
const PL011_DR: usize = 0x00;
const PL011_FR: usize = 0x18;
const PL011_TXFF: u32 = 1 << 5;
pub const PL011_BASE: usize = 0x09000000;
pub const PL011_DR: usize = 0x00;
pub const PL011_FR: usize = 0x18;
pub const PL011_TXFF: u32 = 1 << 5;
// RISC-V 16550A Constants
const NS16550A_BASE: usize = 0x10000000;
const NS16550A_THR: usize = 0x00; // Transmitter Holding Register
const NS16550A_LSR: usize = 0x05; // Line Status Register
const NS16550A_THRE: u8 = 1 << 5; // Transmitter Holding Register Empty
const NS16550A_IER: usize = 0x01; // Interrupt Enable Register
const NS16550A_FCR: usize = 0x02; // FIFO Control Register
const NS16550A_LCR: usize = 0x03; // Line Control Register
pub const NS16550A_BASE: usize = 0x10000000;
pub const NS16550A_THR: usize = 0x00; // Transmitter Holding Register
pub const NS16550A_LSR: usize = 0x05; // Line Status Register
pub const NS16550A_THRE: u8 = 1 << 5; // Transmitter Holding Register Empty
pub const NS16550A_IER: usize = 0x01; // Interrupt Enable Register
pub const NS16550A_FCR: usize = 0x02; // FIFO Control Register
pub const NS16550A_LCR: usize = 0x03; // Line Control Register
// Input Ring Buffer (256 bytes, power of 2 for fast masking)
const INPUT_BUFFER_SIZE = 256;
// SAFETY(RingBuffer): Only accessed via head/tail indices.
// 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 = std.atomic.Value(u32).init(0); // Write position
var input_tail = std.atomic.Value(u32).init(0); // Read position
// Input logic moved to uart_input.zig
pub fn init() void {
// Initialize buffer pointers
input_head.store(0, .monotonic);
input_tail.store(0, .monotonic);
switch (builtin.cpu.arch) {
.riscv64 => init_riscv(),
else => {},
@ -115,52 +104,7 @@ pub fn init_riscv() void {
}
// Capture any data already in hardware FIFO
poll_input();
}
/// Poll UART hardware and move available bytes into ring buffer
/// Should be called periodically (e.g. from scheduler or ISR)
pub fn poll_input() void {
switch (builtin.cpu.arch) {
.riscv64 => {
const thr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_THR);
const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
// Read all available bytes from UART FIFO
while ((lsr.* & 0x01) != 0) { // Data Ready
const byte = thr.*;
// 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);
}
// 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 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 => {},
}
// uart_input.poll_input(); // We cannot call this here safely without dep
}
fn write_char_arm64(c: u8) void {
@ -197,22 +141,7 @@ pub fn write_bytes(bytes: []const u8) void {
}
}
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;
}
// read_byte moved to uart_input.zig
pub fn read_direct() ?u8 {
switch (builtin.cpu.arch) {
@ -276,10 +205,6 @@ pub fn print_hex(value: usize) void {
}
}
export fn uart_print_hex(value: u64) void {
print_hex(value);
}
pub fn print_hex8(value: u8) void {
const hex_chars = "0123456789ABCDEF";
const nibble1 = (value >> 4) & 0xF;
@ -287,7 +212,3 @@ pub fn print_hex8(value: u8) void {
write_char(hex_chars[nibble1]);
write_char(hex_chars[nibble2]);
}
export fn uart_print_hex8(value: u8) void {
print_hex8(value);
}

View File

@ -52,17 +52,15 @@ pub export fn virtio_net_poll() void {
if (poll_count == 1 or (poll_count % 50 == 0)) {
if (global_driver) |*d| {
if (d.rx_queue) |q| {
const hw_idx = q.used.idx;
const drv_idx = q.index;
uart.print("[VirtIO] Poll #");
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");
// const hw_idx = q.used.idx;
// const drv_idx = q.index;
// uart.print("[VirtIO] Poll #");
// 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'
}
}
}

View File

@ -92,7 +92,21 @@ pub const VirtioTransport = struct {
// Has Capabilities
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) {
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_id = @as(*volatile u8, @ptrFromInt(cap_addr)).*;
const cap_next = @as(*volatile u8, @ptrFromInt(cap_addr + 1)).*;

View File

@ -53,7 +53,14 @@ double log10(double x) { return 0.0; }
// --- SYSCALL INTERFACE ---
#ifdef RUMPK_KERNEL
extern long k_handle_syscall(long nr, long a0, long a1, long a2);
#endif
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;
@ -69,6 +76,7 @@ long syscall(long nr, long a0, long a1, long a2) {
res = -1;
#endif
return res;
#endif
}
// IO stubs (Real Syscalls)
@ -229,7 +237,7 @@ void (*signal(int sig, void (*func)(int)))(int) { return NULL; }
// uint32_t sys_now() { return 0; }
// RNG for LwIP (Project Prometheus)
int rand(void) {
int libc_rand(void) {
static unsigned long next = 1;
next = next * 1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
@ -333,10 +341,19 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
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) {
hal_console_write(p, len);
}
#else
// User Mode: Syscall
void console_write(const void* p, size_t len) {
// Phase 11: Real Syscalls only. No direct MMIO.
write(1, p, len);
}
#endif
void ion_egress_to_port(uint16_t port, void* pkt);

View File

@ -330,14 +330,14 @@ dns_init(void)
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 (dns_pcbs[0] == NULL) {
dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
if (dns_pcbs[0] == NULL) {
printf("[DNS] dns_init: FAILED to allocate PCB\n");
LWIP_PLATFORM_DIAG(("[DNS] dns_init: FAILED to allocate PCB\n"));
} else {
printf("[DNS] dns_init: Allocated PCB: 0x%x\n", (unsigned int)dns_pcbs[0]);
LWIP_PLATFORM_DIAG(("[DNS] dns_init: Allocated PCB: 0x%p\n", (void *)dns_pcbs[0]));
udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
udp_recv(dns_pcbs[0], dns_recv, NULL);
}
@ -1555,7 +1555,7 @@ dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback foun
if (hostname != NULL) {
if (hostname[0] == 'g' || hostname[0] == 'l') {
IP4_ADDR(ip_2_ip4(addr), 142, 250, 185, 78);
printf("[DNS] Sovereign Mocker: Resolved '%s' to 142.250.185.78\n", hostname);
LWIP_PLATFORM_DIAG(("[DNS] Sovereign Mocker: Resolved '%s' to 142.250.185.78\n", hostname));
return ERR_OK;
}
}

View File

@ -1763,11 +1763,11 @@ netif_find(const char *name)
return NULL;
}
num = (u8_t)atoi(&name[2]);
if (!num && (name[2] != '0')) {
/* this means atoi has failed */
if ((name[2] < '0') || (name[2] > '9')) {
/* not a digit? */
return NULL;
}
num = (u8_t)(name[2] - '0');
NETIF_FOREACH(netif) {
if (num == netif->num &&

View File

@ -78,26 +78,22 @@
#define LWIP_LOOPBACK_MAX_PBUFS 8
// Debugging (Loud Mode)
#define LWIP_DEBUG 1
#define LWIP_PLATFORM_DIAG(x) lwip_platform_diag x
#define LWIP_DEBUG 0
#define LWIP_PLATFORM_DIAG(x) // lwip_platform_diag x
extern int printf(const char *format, ...);
#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) { \
printf("\n[LwIP ASSERT] %s\n", message); \
while(1); \
}} while(0)
// LWIP_ASSERT is handled in arch/cc.h with LWIP_PLATFORM_ASSERT
#define DHCP_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE | LWIP_DBG_STATE)
#define UDP_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
#define NETIF_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE | LWIP_DBG_STATE)
#define IP_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
#define ICMP_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
#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_ON | LWIP_DBG_TRACE)
#define ETHERNET_DEBUG (LWIP_DBG_ON | LWIP_DBG_TRACE)
#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)
@ -108,4 +104,8 @@ extern int printf(const char *format, ...);
#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

View File

@ -14,12 +14,35 @@
import ion_client
import net_glue
# 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 =
var res: int
@ -37,121 +60,11 @@ proc syscall*(nr: int, a0: uint64 = 0, a1: uint64 = 0, a2: uint64 = 0): int =
""".}
return res
# --- LIBC IO SHIMS ---
when not defined(RUMPK_KERNEL):
# 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)))
# --- 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
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
when defined(RUMPK_KERNEL):
# =========================================================
# KERNEL IMPLEMENTATION
# =========================================================
type
SockState = enum
CLOSED, LISTEN, CONNECTING, ESTABLISHED, FIN_WAIT
@ -298,7 +211,7 @@ when defined(RUMPK_KERNEL):
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`);" .}
# {.emit: "printf(\"[Membrane] libc_impl_getaddrinfo(node=%s, res_ptr=%p)\\n\", `node`, `res`);" .}
let status = glue_resolve_start(node)
var resolved = false
@ -323,20 +236,6 @@ when defined(RUMPK_KERNEL):
# 2. Allocate AddrInfo struct (using User Allocator? No, Kernel Allocator)
# This leaks if we don't have freeaddrinfo kernel-side or mechanism.
# For MVP: We return a static buffer or allocated one.
# Since we are single-threaded kernel-side handling this syscall, static is risky but ok for MVP.
# Better: Allocate using proper allocator.
# We'll use a simplified approach: Return success and fill a static struct for now.
# TODO: Proper allocation.
# Construct SockAddr
# 10.0.2.15 -> 0x0F02000A
# sin_port = 0
# sin_addr = ip
# sin_zero = 0
# We emit C to malloc or use a static buffer?
# We can use Nim's `create` if `useMalloc` is on.
var ai = create(AddrInfo)
var sa = create(SockAddr)
@ -370,14 +269,10 @@ when defined(RUMPK_KERNEL):
sin->sin_family = 2; // AF_INET
""".}
{.emit: "printf(\"[Membrane] libc_impl_getaddrinfo: SUCCESS. AI=%p, SA=%p\\n\", `ai`, `sa`);" .}
if res != nil:
res[] = ai
{.emit: "printf(\"[Membrane] DNS RESOLVED. Helios Probe should be active.\\n\");" .}
return 0
else:
{.emit: "printf(\"[Membrane] libc_impl_getaddrinfo ERROR: res is NULL!\\n\");" .}
return -1
proc libc_impl_freeaddrinfo*(res: ptr AddrInfo) {.exportc: "libc_impl_freeaddrinfo", cdecl.} =
@ -436,7 +331,90 @@ when defined(RUMPK_KERNEL):
return 0
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.} =
return int(syscall(SYS_SOCK_SOCKET, uint64(domain), uint64(sock_type), uint64(protocol)))
@ -485,46 +463,14 @@ proc syscall_get_random*(): uint32 {.exportc, cdecl.} =
## Implementation: SipHash-2-4(MonolithKey, Time || CycleCount)
## Per SPEC-805: Hash Strategy
let sys = get_sys_table()
# Get high-resolution time
# TODO: Optimize to avoid overhead if called frequently
let time_ns = syscall_get_time_ns()
# Mix time with itself (upper/lower bits)
var mix_data: array[16, byte]
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-805)
# 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)
# Temporary simple mix
return uint32(time_ns xor (time_ns shr 32))
proc syscall_panic*() {.exportc, cdecl, noreturn.} =
## Trigger kernel panic from lwIP assertion failure
## Routes to kernel's EXIT syscall
discard syscall(0x01, 255) # EXIT with error code 255
while true: discard # noreturn

View File

@ -53,7 +53,6 @@ export fn fputc(c: i32, stream: ?*anyopaque) i32 {
}
extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize;
extern fn console_write(ptr: [*]const u8, len: usize) void;
// Helper for fputc/fputs internal use in Kernel
fn write_extern(fd: i32, buf: [*]const u8, count: usize) isize {

View File

@ -598,42 +598,14 @@ int glue_dns_check_init(void) {
}
int glue_resolve_start(char* hostname) {
// BYPASS: Mock DNS to unblock Userland
// printf("[Membrane] DNS MOCK: Resolving '%s' -> 10.0.2.2\n", hostname);
ip_addr_t ip;
err_t err;
g_dns_status = 1; // Pending default
printf("[Membrane] DNS: Attempting to resolve '%s'...\n", hostname);
// Ensure we have a DNS server
const ip_addr_t *ns = dns_getserver(0);
if (ns == NULL || ip_addr_isany(ns)) {
printf("[Membrane] DNS: No server configured. Falling back to 10.0.2.3\n");
static ip_addr_t fallback;
IP4_ADDR(ip_2_ip4(&fallback), 10, 0, 2, 3);
dns_setserver(0, &fallback);
ns = dns_getserver(0);
}
printf("[Membrane] DNS: Using server %s\n", ipaddr_ntoa(ns));
// DIAGNOSTIC: Check state via accessor
// Note: We don't check NULL here because the core check is inside dns_gethostbyname
// which now has its own internal debug print.
err = dns_gethostbyname(hostname, &ip, my_dns_callback, NULL);
if (err == ERR_OK) {
printf("[Membrane] DNS: Instant success for '%s' -> %s\n", hostname, ipaddr_ntoa(&ip));
g_dns_ip = ip;
g_dns_status = 2; // Done
return 0;
} else if (err == ERR_INPROGRESS) {
printf("[Membrane] DNS: Query enqueued for '%s' (IN_PROGRESS)\n", hostname);
return 1;
} else {
printf("[Membrane] DNS: dns_gethostbyname FAILED (%d)\n", (int)err);
g_dns_status = -1;
return -1;
}
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) {

8
run.sh
View File

@ -2,7 +2,7 @@
# Rumpk QEMU Boot Script
RUMPK_DIR="$(cd "$(dirname "$0")" && pwd)"
KERNEL="$RUMPK_DIR/build/rumpk.elf"
KERNEL="$RUMPK_DIR/zig-out/bin/rumpk.elf"
if [ ! -f "$KERNEL" ]; then
echo "ERROR: Kernel not found at $KERNEL"
@ -14,9 +14,9 @@ echo "🚀 Booting Rumpk..."
echo " Kernel: $KERNEL"
echo ""
qemu-system-aarch64 \
qemu-system-riscv64 \
-M virt \
-cpu cortex-a57 \
-m 128M \
-cpu max \
-m 512M \
-nographic \
-kernel "$KERNEL"

0
vendor/mksh/build_nexus.sh vendored Normal file → Executable file
View File

View File

@ -19,6 +19,11 @@ char **environ = NULL;
extern void console_write(const void* p, size_t len);
extern long syscall(long nr, long a0, long a1, long a2);
long k_handle_syscall(long nr, long a0, long a1, long a2) {
return syscall(nr, a0, a1, a2);
}
// Stubs
int fstat(int fd, struct stat *buf) { return 0; }
int lstat(const char *path, struct stat *buf) { return 0; }