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.
This commit is contained in:
Markus Maiwald 2026-01-08 21:38:14 +01:00
parent 7207282236
commit 011e0b699e
13 changed files with 121 additions and 6 deletions

View File

@ -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),
);
}

View File

@ -42,7 +42,7 @@ SECTIONS
.stack (NOLOAD) : {
. = ALIGN(16);
. += 0x100000; /* 1MB Stack */
__stack_top = .;
PROVIDE(__stack_top = .);
}
/DISCARD/ : {

View File

@ -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");""" .}

View File

@ -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)

5
core/test_standalone.nim Normal file
View File

@ -0,0 +1,5 @@
proc main() =
discard
when isMainModule:
main()

View File

@ -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;
}

View File

@ -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

BIN
hal/init Executable file

Binary file not shown.

3
hal/initrd.zig Normal file
View File

@ -0,0 +1,3 @@
const data = @embedFile("initrd.tar");
export var _initrd_payload: [data.len]u8 align(4096) linksection(".initrd") = data.*;

BIN
hal/initrd/init Executable file

Binary file not shown.

93
hal/uart_input.zig Normal file
View File

@ -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;
}

BIN
vendor/mksh/mksh.elf.tmp165070a vendored Executable file

Binary file not shown.

BIN
vendor/mksh/mksh.elf.tmp6703f64 vendored Executable file

Binary file not shown.