From fbb9189b59e1acac2a09e373d68eb3eea243908f Mon Sep 17 00:00:00 2001 From: Markus Maiwald Date: Thu, 8 Jan 2026 21:38:14 +0100 Subject: [PATCH] fix(rumpk): enable user stack access and repair boot process - Enabled SUM (Supervisor Access to User Memory) in riscv_init to allow kernel loader to write to user stacks. - Removed dangerous 'csrc sstatus' in kload_phys that revoked access. - Aligned global fiber stacks to 4096 bytes to prevent unmapped page faults at stack boundaries. - Restored 'boot.o' linking to fix silent boot failure. - Implemented 'fiber_can_run_on_channels' stub to satisfy Membrane linking. - Defined kernel stack in header.zig to fix '__stack_top' undefined symbol. - Resolved duplicate symbols in overrides.c and nexshell. --- boot/header.zig | 10 ++++- boot/linker.ld | 2 +- core/kernel.nim | 4 +- core/overrides.c | 2 + core/test_standalone.nim | 5 +++ hal/channel.zig | 4 ++ hal/entry_riscv.zig | 4 +- hal/initrd.zig | 3 ++ hal/uart_input.zig | 93 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 core/test_standalone.nim create mode 100644 hal/initrd.zig create mode 100644 hal/uart_input.zig diff --git a/boot/header.zig b/boot/header.zig index 7748aea..0765659 100644 --- a/boot/header.zig +++ b/boot/header.zig @@ -48,11 +48,17 @@ export const multiboot2_header linksection(".multiboot2") = Multiboot2Header{ extern fn riscv_init() noreturn; +// 1MB Kernel Stack +const STACK_SIZE = 0x100000; +export var kernel_stack: [STACK_SIZE]u8 align(16) linksection(".bss.stack") = undefined; + export fn _start() callconv(.naked) noreturn { // Clear BSS, set up stack, then jump to RISC-V Init asm volatile ( \\ // Set up stack - \\ la sp, __stack_top + \\ la sp, kernel_stack + \\ li t0, %[stack_size] + \\ add sp, sp, t0 \\ \\ // Clear BSS \\ la t0, __bss_start @@ -69,5 +75,7 @@ export fn _start() callconv(.naked) noreturn { \\ // Should never return \\ wfi \\ j 2b + : + : [stack_size] "i" (STACK_SIZE), ); } diff --git a/boot/linker.ld b/boot/linker.ld index 26b090f..0922459 100644 --- a/boot/linker.ld +++ b/boot/linker.ld @@ -42,7 +42,7 @@ SECTIONS .stack (NOLOAD) : { . = ALIGN(16); . += 0x100000; /* 1MB Stack */ - __stack_top = .; + PROVIDE(__stack_top = .); } /DISCARD/ : { diff --git a/core/kernel.nim b/core/kernel.nim index 47c7771..9abd9b5 100644 --- a/core/kernel.nim +++ b/core/kernel.nim @@ -42,7 +42,7 @@ var initrd_end {.importc: "_initrd_end" .}: byte # Globals var fiber_ion, fiber_subject, fiber_child, fiber_compositor, fiber_nexshell, fiber_netswitch: FiberObject - stack_ion, stack_subject, stack_child, stack_compositor, stack_nexshell, stack_netswitch: array[MAX_FIBER_STACK, byte] + stack_ion {.align: 4096.}, stack_subject {.align: 4096.}, stack_child {.align: 4096.}, stack_compositor {.align: 4096.}, stack_nexshell {.align: 4096.}, stack_netswitch {.align: 4096.}: array[MAX_FIBER_STACK, byte] subject_loading_path: array[64, char] = [ '/', 's', 'y', 's', 'r', 'o', '/', 'b', 'i', 'n', '/', 'm', 'k', 's', 'h', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' ] matrix_enabled: bool = false active_fibers_arr: array[16, ptr FiberObject] @@ -165,7 +165,7 @@ proc kload_phys(path: cstring, phys_offset: uint64): uint64 = kprint(" - Zeroing BSS: VA="); kprint_hex(bss_start); kprint(" Len="); kprint_hex(bss_len); kprintln("") k_zero_mem(cast[pointer](bss_start), bss_len) - {.emit: """asm volatile ("li t1, 0x40000; csrc sstatus, t1" : : : "t1");""" .} + # {.emit: """asm volatile ("li t1, 0x40000; csrc sstatus, t1" : : : "t1");""" .} # ⚡ ARCH-SYNC: Flush I-Cache after loading new code {.emit: """asm volatile ("fence.i" : : : "memory");""" .} diff --git a/core/overrides.c b/core/overrides.c index 745db12..89920b6 100644 --- a/core/overrides.c +++ b/core/overrides.c @@ -21,6 +21,7 @@ double floor(double x) { } 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) // ---------------------------------------------------------------------------- @@ -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) { return true; // We are single core or spinlocked elsewhere } +*/ // =================================== // Compiler-RT Stubs (128-bit Math) diff --git a/core/test_standalone.nim b/core/test_standalone.nim new file mode 100644 index 0000000..933153f --- /dev/null +++ b/core/test_standalone.nim @@ -0,0 +1,5 @@ +proc main() = + discard + +when isMainModule: + main() diff --git a/hal/channel.zig b/hal/channel.zig index 425ae27..948395d 100644 --- a/hal/channel.zig +++ b/hal/channel.zig @@ -106,3 +106,7 @@ export fn hal_cmd_pop(handle: u64, out_pkt: *CmdPacket) bool { // uart.print("[HAL] Popping CMD from "); uart.print_hex(handle); uart.print("\n"); return popGeneric(CmdPacket, ring, out_pkt); } +// Stub for term.nim compatibility +export fn fiber_can_run_on_channels() bool { + return true; +} diff --git a/hal/entry_riscv.zig b/hal/entry_riscv.zig index a25c577..78bc65f 100644 --- a/hal/entry_riscv.zig +++ b/hal/entry_riscv.zig @@ -40,8 +40,8 @@ export fn riscv_init() callconv(.naked) noreturn { \\ 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 + // 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 // 1.2 Initialize Global Pointer diff --git a/hal/initrd.zig b/hal/initrd.zig new file mode 100644 index 0000000..e7c1290 --- /dev/null +++ b/hal/initrd.zig @@ -0,0 +1,3 @@ +const data = @embedFile("initrd.tar"); + +export var _initrd_payload: [data.len]u8 align(4096) linksection(".initrd") = data.*; diff --git a/hal/uart_input.zig b/hal/uart_input.zig new file mode 100644 index 0000000..05aef43 --- /dev/null +++ b/hal/uart_input.zig @@ -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; +}