205 lines
6.6 KiB
Nim
205 lines
6.6 KiB
Nim
# 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.
|
|
|
|
## Rumpk Layer 1: Fibers (The Sovereign Thread)
|
|
|
|
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
|
# Rumpk Phase 10: Multitasking & Context Switching
|
|
#
|
|
# Responsibilities:
|
|
# - 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.}
|
|
|
|
# Architecture Configuration
|
|
# =========================================================
|
|
|
|
when defined(riscv64):
|
|
const ARCH_NAME* = "riscv64"
|
|
const CONTEXT_SIZE* = 128
|
|
const RET_ADDR_INDEX* = 0 # Offset in stack for RA
|
|
elif defined(arm64):
|
|
const ARCH_NAME* = "aarch64"
|
|
const CONTEXT_SIZE* = 96 # 6 register pairs (x19-x30) * 16 bytes
|
|
const RET_ADDR_INDEX* = 11 # x30 (LR) at [sp + 88] = index 11
|
|
elif defined(amd64) or defined(x86_64):
|
|
const ARCH_NAME* = "amd64"
|
|
const CONTEXT_SIZE* = 64
|
|
const RET_ADDR_INDEX* = 0
|
|
else:
|
|
{.error: "Unsupported Architecture".}
|
|
|
|
# --- FIBER DEFINITION ---
|
|
|
|
type
|
|
Spectrum* {.pure.} = enum
|
|
Void = 0, # Default/Uninitialized
|
|
Photon = 1, # Real-time (0-1ms latency)
|
|
Matter = 2, # Interactive (1-10ms latency)
|
|
Gravity = 3, # Batch/Idle (100ms+ latency)
|
|
|
|
FiberState* = object
|
|
sp*: uint64 # The Stack Pointer (Must be first field!)
|
|
entry*: proc() {.cdecl.} # Entry point for this fiber
|
|
|
|
Fiber* = ptr FiberObject
|
|
FiberObject* = object
|
|
id*: uint64
|
|
name*: cstring
|
|
state*: FiberState
|
|
stack*: ptr UncheckedArray[uint8]
|
|
phys_offset*: uint64 # Cellular Memory Offset
|
|
stack_size*: int
|
|
sleep_until*: uint64 # NS timestamp
|
|
promises*: uint64 # [63:62]=Spectrum, [61:0]=Pledge bits
|
|
pledge_budget*: uint64 # Microseconds allowed per frame (legacy)
|
|
avg_burst*: uint64 # Moving average for telemetry (The Ratchet)
|
|
user_entry*: pointer # Phase 29: User function pointer for workers
|
|
user_arg*: uint64 # Phase 29: Argument for user function
|
|
satp_value*: uint64 # Phase 31: Page table root (0 = use kernel map)
|
|
wants_yield*: bool # Phase 37: Deferred yield flag
|
|
# SPEC-102: The Ratchet
|
|
budget_ns*: uint64 # "I promise to run for X ns max"
|
|
last_burst_ns*: uint64 # Actual execution time of last run
|
|
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
|
|
proc getSpectrum*(f: Fiber): Spectrum =
|
|
if f == nil: return Spectrum.Void
|
|
Spectrum((f.promises shr 62) and 0x3)
|
|
|
|
proc setSpectrum*(f: Fiber, s: Spectrum) =
|
|
if f == nil: return
|
|
f.promises = (f.promises and 0x3FFFFFFFFFFFFFFF'u64) or (uint64(s) shl 62)
|
|
|
|
proc fiber_yield*() {.importc, cdecl.}
|
|
# Imports
|
|
# =========================================================
|
|
|
|
# Import the Assembly Magic (same symbol name, different implementation per arch)
|
|
proc cpu_switch_to(prev_sp_ptr: ptr uint64, next_sp: uint64) {.importc, cdecl.}
|
|
|
|
# Phase 31: Page Table Activation
|
|
proc mm_activate_satp(satp_val: uint64) {.importc, cdecl.}
|
|
proc mm_get_kernel_satp(): uint64 {.importc, cdecl.}
|
|
|
|
proc debug(s: cstring) =
|
|
proc console_write(p: pointer, len: int) {.importc, cdecl.}
|
|
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: riscv64\n")
|
|
|
|
# =========================================================
|
|
# Constants
|
|
# =========================================================
|
|
|
|
const STACK_SIZE* = 4096
|
|
|
|
# =========================================================
|
|
# Fiber State
|
|
# =========================================================
|
|
|
|
var main_fiber*: FiberObject
|
|
var current_fiber* {.global.}: Fiber = addr main_fiber
|
|
|
|
# =========================================================
|
|
# Trampoline (Entry point for new fibers)
|
|
# =========================================================
|
|
|
|
proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
|
|
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.}
|
|
var i = 0
|
|
while msg[i] != '\0': i += 1
|
|
console_write(cast[pointer](msg), i)
|
|
let f = current_fiber
|
|
|
|
if f.state.entry != nil:
|
|
f.state.entry()
|
|
|
|
# If the fiber returns, halt
|
|
when defined(riscv64):
|
|
while true:
|
|
{.emit: "asm volatile(\"wfi\");".}
|
|
elif defined(arm64):
|
|
while true:
|
|
{.emit: "asm volatile(\"wfe\");".}
|
|
else:
|
|
while true: discard
|
|
|
|
# =========================================================
|
|
# Fiber Initialization (Arch-Specific)
|
|
# =========================================================
|
|
|
|
proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer, size: int) =
|
|
f.state.entry = entry
|
|
f.stack = cast[ptr UncheckedArray[uint8]](stack_base)
|
|
f.stack_size = size
|
|
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)
|
|
var sp = cast[uint64](stack_base) + cast[uint64](size)
|
|
|
|
# 1. Align to 16 bytes (Universal requirement)
|
|
sp = sp and not 15'u64
|
|
|
|
# 2. Reserve space for the context frame
|
|
sp = sp - CONTEXT_SIZE
|
|
|
|
# 3. Setup the Context (zero all, set return address)
|
|
var stack_ptr = cast[ptr UncheckedArray[uint64]](sp)
|
|
|
|
# Zero out the frame
|
|
let num_regs = CONTEXT_SIZE div 8
|
|
for i in 0..<num_regs:
|
|
stack_ptr[i] = 0
|
|
|
|
# Set return address to trampoline
|
|
stack_ptr[RET_ADDR_INDEX] = cast[uint64](fiber_trampoline)
|
|
|
|
f.state.sp = sp
|
|
|
|
# =========================================================
|
|
# Context Switch
|
|
# =========================================================
|
|
|
|
proc switch*(next: Fiber) =
|
|
let prev = current_fiber
|
|
current_fiber = next
|
|
|
|
# Swap address space if necessary (Phase 31: Sv39 Memory Isolation)
|
|
if next.satp_value != 0:
|
|
mm_activate_satp(next.satp_value)
|
|
else:
|
|
# If fiber has no specific satp (0), restore kernel identity map
|
|
let k_satp = mm_get_kernel_satp()
|
|
if k_satp != 0:
|
|
mm_activate_satp(k_satp)
|
|
|
|
cpu_switch_to(addr prev.state.sp, next.state.sp)
|
|
|
|
{.pop.}
|