feat(kernel): implement Sv39 fiber memory isolation and hardened ELF loader
This commit is contained in:
parent
72891287fb
commit
bf427290f1
|
|
@ -25,16 +25,26 @@ proc main() =
|
|||
|
||||
print(cstring("[INIT] System Ready. Starting heartbeat...\n"))
|
||||
|
||||
print(cstring("[INIT] Spawning Sovereign Shell (Mksh)...\n"))
|
||||
|
||||
# Attempt to handover control to Mksh
|
||||
# execv(path, argv) - argv can be nil for now
|
||||
if execv(cstring("/bin/mksh"), nil) < 0:
|
||||
print(cstring("\x1b[1;31m[INIT] Failed to spawn shell! Entering fallback loop.\x1b[0m\n"))
|
||||
while true:
|
||||
# 🕵️ DIAGNOSTIC: BREATHER
|
||||
pump_membrane_stack()
|
||||
yield_fiber()
|
||||
# Spawn mksh as a separate fiber (NOT execv - we stay alive as supervisor)
|
||||
proc spawn_fiber(path: cstring): int =
|
||||
# SYS_SPAWN_FIBER = 0x300
|
||||
return int(syscall(0x300, cast[uint64](path), 0, 0))
|
||||
|
||||
let fiber_id = spawn_fiber(cstring("/bin/mksh"))
|
||||
if fiber_id > 0:
|
||||
print(cstring("[INIT] Spawned mksh fiber ID: "))
|
||||
# Note: Can't easily print int in minimal libc, just confirm success
|
||||
print(cstring("OK\n"))
|
||||
else:
|
||||
print(cstring("\x1b[1;31m[INIT] Failed to spawn shell!\x1b[0m\n"))
|
||||
|
||||
# Supervisor loop - stay alive, check fiber health periodically
|
||||
print(cstring("[INIT] Entering supervisor mode...\n"))
|
||||
while true:
|
||||
# Sleep 1 second between checks
|
||||
discard syscall(0x65, 1000000000'u64) # nanosleep: 1 second
|
||||
pump_membrane_stack()
|
||||
yield_fiber()
|
||||
|
||||
when isMainModule:
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
.section .text._start, "ax"
|
||||
.global _start
|
||||
_start:
|
||||
# 🕵️ DIAGNOSTIC: BREATHE
|
||||
li t0, 0x10000000
|
||||
li t1, 0x23 # '#'
|
||||
sb t1, 0(t0)
|
||||
|
||||
# Clear BSS (64-bit aligned zeroing)
|
||||
# 🕵️ BSS Clearing
|
||||
la t0, __bss_start
|
||||
la t1, __bss_end
|
||||
1: bge t0, t1, 2f
|
||||
|
|
@ -16,11 +11,6 @@ _start:
|
|||
2:
|
||||
fence rw, rw
|
||||
|
||||
# 🕵️ DIAGNOSTIC: READY TO CALL MAIN
|
||||
li t0, 0x10000000
|
||||
li t1, 0x21 # '!'
|
||||
sb t1, 0(t0)
|
||||
|
||||
# Arguments (argc, argv) are already in a0, a1 from Kernel
|
||||
# sp is already pointing to argc from Kernel
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ type
|
|||
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
|
||||
|
||||
# Spectrum Accessors
|
||||
proc getSpectrum*(f: Fiber): Spectrum =
|
||||
|
|
@ -147,6 +149,8 @@ proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer, size: i
|
|||
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
|
||||
|
||||
# Start at top of stack (using actual size)
|
||||
var sp = cast[uint64](stack_base) + cast[uint64](size)
|
||||
|
|
|
|||
313
core/fs/sfs.nim
313
core/fs/sfs.nim
|
|
@ -6,334 +6,135 @@
|
|||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||
|
||||
## 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)
|
||||
#
|
||||
# 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)
|
||||
import fiber # For yield
|
||||
|
||||
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||
proc kprint(s: cstring) {.importc, cdecl.}
|
||||
proc kprint_hex(n: uint64) {.importc, cdecl.}
|
||||
|
||||
# =========================================================
|
||||
# SFS Configurations
|
||||
# =========================================================
|
||||
|
||||
const SFS_MAGIC* = 0x31534653'u32
|
||||
const
|
||||
SEC_SB = 0
|
||||
SEC_BAM = 1
|
||||
SEC_DIR = 2
|
||||
# Linked List Payload: 508 bytes data + 4 bytes next_sector
|
||||
CHUNK_SIZE = 508
|
||||
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 io_buffer: array[512, byte]
|
||||
|
||||
proc virtio_blk_read(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 =
|
||||
# Simple allocator: Scan BAM for first 0 bit
|
||||
virtio_blk_read(SEC_BAM, addr io_buffer[0])
|
||||
|
||||
for i in 0..<512:
|
||||
if io_buffer[i] != 0xFF:
|
||||
# Found a byte with free space
|
||||
for b in 0..7:
|
||||
if (io_buffer[i] and byte(1 shl b)) == 0:
|
||||
# Found free bit
|
||||
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)
|
||||
virtio_blk_write(SEC_BAM, addr io_buffer[0])
|
||||
return sec
|
||||
return 0 # Error / Full
|
||||
|
||||
# =========================================================
|
||||
# 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.")
|
||||
return 0
|
||||
|
||||
proc sfs_mount*() =
|
||||
kprintln("[SFS] Mounting System v2...")
|
||||
|
||||
# 1. Read Sector 0 (Superblock)
|
||||
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
|
||||
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
|
||||
else:
|
||||
kprint("[SFS] Mount FAILED. Invalid Magic/Ver. Found: ")
|
||||
kprint_hex(cast[uint64](io_buffer[0]))
|
||||
kprintln("")
|
||||
sfs_mounted = false
|
||||
|
||||
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
|
||||
|
||||
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 file_exists = false
|
||||
|
||||
# 1. Find File or Free Slot
|
||||
for offset in countup(0, 511, 64):
|
||||
if io_buffer[offset] != 0:
|
||||
var entry_name = ""
|
||||
for i in 0..31:
|
||||
if io_buffer[offset+i] == 0: break
|
||||
entry_name.add(char(io_buffer[offset+i]))
|
||||
|
||||
if entry_name == $name:
|
||||
if sfs_streq(name, cast[cstring](addr io_buffer[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
|
||||
elif dir_offset == -1:
|
||||
dir_offset = offset
|
||||
elif dir_offset == -1: 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 data_ptr = 0
|
||||
var first_sector = 0'u32
|
||||
var current_sector = 0'u32
|
||||
|
||||
# For the first chunk
|
||||
current_sector = sfs_alloc_sector()
|
||||
if current_sector == 0:
|
||||
kprintln("[SFS] Error: Disk Full.")
|
||||
return
|
||||
first_sector = current_sector
|
||||
var data_addr = cast[uint64](data)
|
||||
var current_sector = sfs_alloc_sector()
|
||||
if current_sector == 0: return
|
||||
let first_sector = current_sector
|
||||
|
||||
while remaining > 0:
|
||||
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
|
||||
if remaining > 0:
|
||||
next_sector = sfs_alloc_sector()
|
||||
if next_sector == 0:
|
||||
next_sector = EOF_MARKER # Disk full, truncated
|
||||
remaining = 0
|
||||
|
||||
# Write Next Pointer
|
||||
sector_buf[508] = byte(next_sector and 0xFF)
|
||||
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
|
||||
if next_sector == 0: next_sector = EOF_MARKER
|
||||
|
||||
# Write next pointer at end of block
|
||||
cast[ptr uint32](addr sector_buf[508])[] = next_sector
|
||||
virtio_blk_write(uint64(current_sector), addr sector_buf[0])
|
||||
|
||||
current_sector = next_sector
|
||||
if current_sector == EOF_MARKER: break
|
||||
|
||||
# 3. Update Directory Entry
|
||||
# Need to read Dir again as buffer was used for BAM/Data
|
||||
# Update Directory
|
||||
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
||||
|
||||
let n_str = $name
|
||||
for i in 0..31:
|
||||
if i < n_str.len: io_buffer[dir_offset+i] = byte(n_str[i])
|
||||
else: io_buffer[dir_offset+i] = 0
|
||||
|
||||
io_buffer[dir_offset+32] = byte(first_sector and 0xFF)
|
||||
io_buffer[dir_offset+33] = byte((first_sector shr 8) and 0xFF)
|
||||
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)
|
||||
|
||||
let nm = cast[ptr UncheckedArray[char]](name)
|
||||
var i = 0
|
||||
while nm[i] != '\0' and i < 31:
|
||||
io_buffer[dir_offset + i] = byte(nm[i])
|
||||
i += 1
|
||||
io_buffer[dir_offset + i] = 0
|
||||
cast[ptr uint32](addr io_buffer[dir_offset + 32])[] = first_sector
|
||||
cast[ptr uint32](addr io_buffer[dir_offset + 36])[] = uint32(data_len)
|
||||
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.} =
|
||||
if not sfs_mounted: return -1
|
||||
|
||||
virtio_blk_read(SEC_DIR, addr io_buffer[0])
|
||||
|
||||
var start_sector = 0'u32
|
||||
var file_size = 0'u32
|
||||
var found = false
|
||||
|
||||
for offset in countup(0, 511, 64):
|
||||
if io_buffer[offset] != 0:
|
||||
var entry_name = ""
|
||||
for i in 0..31:
|
||||
if io_buffer[offset+i] == 0: break
|
||||
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)
|
||||
if sfs_streq(name, cast[cstring](addr io_buffer[offset])):
|
||||
start_sector = cast[ptr uint32](addr io_buffer[offset + 32])[]
|
||||
file_size = cast[ptr uint32](addr io_buffer[offset + 36])[]
|
||||
found = true
|
||||
break
|
||||
|
||||
if not found: return -1
|
||||
|
||||
# Read Chain
|
||||
var current_sector = start_sector
|
||||
var dest_addr = cast[int](dest)
|
||||
var remaining = int(file_size)
|
||||
if remaining > max_len: remaining = max_len
|
||||
var dest_addr = cast[uint64](dest)
|
||||
var remaining = if int(file_size) < max_len: int(file_size) else: 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
|
||||
|
||||
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))
|
||||
proc sfs_get_files*(): cstring = return "boot.kdl\n" # Dummy
|
||||
|
|
|
|||
240
core/fs/tar.nim
240
core/fs/tar.nim
|
|
@ -6,217 +6,101 @@
|
|||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||
|
||||
## Rumpk Layer 1: ROMFS (Static Tar Loader)
|
||||
|
||||
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||
# Rumpk L1: Sovereign VFS (Indexing TarFS)
|
||||
##
|
||||
## Freestanding implementation (No OS module dependencies).
|
||||
## Uses a simple flat array for the file index.
|
||||
|
||||
{.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
|
||||
TarHeader* = array[512, byte]
|
||||
|
||||
FileEntry = object
|
||||
offset*: uint64
|
||||
size*: uint64
|
||||
is_sfs*: bool
|
||||
name: array[64, char]
|
||||
offset: uint64
|
||||
size: uint64
|
||||
active: bool
|
||||
|
||||
FileHandle = object
|
||||
path*: string
|
||||
offset*: uint64
|
||||
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
|
||||
const MAX_INDEX = 64
|
||||
var index_table: array[MAX_INDEX, FileEntry]
|
||||
var index_count: int = 0
|
||||
|
||||
proc vfs_init*(s: pointer, e: pointer) =
|
||||
vfs.start_addr = cast[uint64](s)
|
||||
vfs.end_addr = cast[uint64](e)
|
||||
vfs.index = initTable[string, FileEntry]()
|
||||
vfs.ram_data = initTable[string, seq[byte]]()
|
||||
vfs.fds = initTable[int, FileHandle]()
|
||||
vfs.next_fd = 3
|
||||
let start_addr = cast[uint64](s)
|
||||
let end_addr = cast[uint64](e)
|
||||
index_count = 0
|
||||
|
||||
# kprint("[VFS] InitRD Start: "); kprint_hex(vfs.start_addr); kprintln("")
|
||||
# kprint("[VFS] InitRD End: "); kprint_hex(vfs.end_addr); kprintln("")
|
||||
|
||||
var p = vfs.start_addr
|
||||
while p < vfs.end_addr:
|
||||
var p = start_addr
|
||||
while p < end_addr:
|
||||
let h = cast[ptr TarHeader](p)
|
||||
if h[][0] == byte(0): break
|
||||
|
||||
# kprint("[VFS] Raw Header: ")
|
||||
# for i in 0..15:
|
||||
# kprint_hex(uint64(h[][i]))
|
||||
# kprint(" ")
|
||||
# kprintln("")
|
||||
|
||||
# Extract and normalize name directly from header
|
||||
# Extract name
|
||||
var name_len = 0
|
||||
while name_len < 100 and h[][name_len] != 0:
|
||||
inc name_len
|
||||
while name_len < 100 and h[][name_len] != 0: inc name_len
|
||||
|
||||
var start_idx = 0
|
||||
if name_len >= 2 and h[][0] == byte('.') and h[][1] == byte('/'):
|
||||
start_idx = 2
|
||||
elif name_len >= 1 and h[][0] == byte('/'):
|
||||
start_idx = 1
|
||||
if name_len >= 2 and h[][0] == byte('.') and h[][1] == byte('/'): start_idx = 2
|
||||
elif name_len >= 1 and h[][0] == byte('/'): start_idx = 1
|
||||
|
||||
let clean_len = name_len - start_idx
|
||||
var clean = ""
|
||||
if clean_len > 0:
|
||||
clean = newString(clean_len)
|
||||
# Copy directly from header memory
|
||||
for i in 0..<clean_len:
|
||||
clean[i] = char(h[][start_idx + i])
|
||||
if clean_len > 0 and index_count < MAX_INDEX:
|
||||
var entry = addr index_table[index_count]
|
||||
entry.active = true
|
||||
let to_copy = if clean_len < 63: clean_len else: 63
|
||||
for i in 0..<to_copy:
|
||||
entry.name[i] = char(h[][start_idx + i])
|
||||
entry.name[to_copy] = '\0'
|
||||
|
||||
if clean.len > 0:
|
||||
# Extract size (octal string)
|
||||
# Extract size (octal string at offset 124)
|
||||
var size: uint64 = 0
|
||||
for i in 124..134:
|
||||
let b = h[][i]
|
||||
if b >= byte('0') and b <= byte('7'):
|
||||
size = (size shl 3) or uint64(b - byte('0'))
|
||||
|
||||
vfs.index[clean] = FileEntry(offset: p + 512'u64, size: size, is_sfs: false)
|
||||
entry.size = size
|
||||
entry.offset = p + 512
|
||||
index_count += 1
|
||||
|
||||
# Move to next header
|
||||
let padded_size = (size + 511'u64) and not 511'u64
|
||||
p += 512'u64 + padded_size
|
||||
let padded_size = (size + 511) and not 511'u64
|
||||
p += 512 + padded_size
|
||||
else:
|
||||
p += 512'u64 # Skip invalid/empty
|
||||
p += 512
|
||||
|
||||
proc vfs_open*(path: string, flags: int32 = 0): int =
|
||||
var start_idx = 0
|
||||
if path.len > 0 and path[0] == '/':
|
||||
start_idx = 1
|
||||
|
||||
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]
|
||||
|
||||
# 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
|
||||
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_open*(path: cstring, flags: int32 = 0): int32 =
|
||||
var p = path
|
||||
if path != nil and path[0] == '/':
|
||||
p = cast[cstring](cast[uint64](path) + 1)
|
||||
|
||||
for i in 0..<index_count:
|
||||
if vfs_streq(p, cast[cstring](addr index_table[i].name[0])):
|
||||
return int32(i)
|
||||
return -1
|
||||
|
||||
proc vfs_read_file*(path: string): string =
|
||||
var start_idx = 0
|
||||
if path.len > 0 and path[0] == '/':
|
||||
start_idx = 1
|
||||
|
||||
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)
|
||||
proc vfs_read_at*(path: cstring, buf: pointer, count: uint64, offset: uint64): int64 =
|
||||
let fd = vfs_open(path)
|
||||
if fd < 0: return -1
|
||||
let entry = addr index_table[fd]
|
||||
|
||||
if offset >= entry.size: return 0
|
||||
if offset + count > entry.size:
|
||||
actual = entry.size - offset
|
||||
|
||||
copyMem(buf, cast[pointer](entry.offset + offset), int(actual))
|
||||
let avail = entry.size - offset
|
||||
let actual = if count < avail: count else: avail
|
||||
if actual > 0:
|
||||
copyMem(buf, cast[pointer](entry.offset + offset), int(actual))
|
||||
return int64(actual)
|
||||
|
||||
proc vfs_write_at*(path: string, buf: pointer, count: uint64, offset: uint64): int64 =
|
||||
# Promote to RamFS if on TarFS (CoW)
|
||||
if not vfs.ram_data.hasKey(path):
|
||||
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] = @[]
|
||||
proc vfs_write_at*(path: cstring, buf: pointer, count: uint64, offset: uint64): int64 =
|
||||
# ROMFS is read-only
|
||||
return -1
|
||||
|
||||
let data = addr vfs.ram_data[path]
|
||||
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.}
|
||||
proc vfs_get_names*(): int = index_count # Dummy for listing
|
||||
|
|
|
|||
184
core/fs/vfs.nim
184
core/fs/vfs.nim
|
|
@ -6,122 +6,160 @@
|
|||
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
||||
|
||||
## 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
|
||||
|
||||
type
|
||||
VFSMode = enum
|
||||
MODE_TAR, MODE_SFS, MODE_RAM
|
||||
MODE_TAR, MODE_SFS, MODE_RAM, MODE_TTY
|
||||
|
||||
MountPoint = object
|
||||
prefix: string
|
||||
prefix: array[32, char]
|
||||
mode: VFSMode
|
||||
|
||||
var mounts: seq[MountPoint] = @[]
|
||||
|
||||
type
|
||||
FileHandle = object
|
||||
path: string
|
||||
path: array[64, char]
|
||||
offset: uint64
|
||||
mode: VFSMode
|
||||
is_ram: bool
|
||||
active: bool
|
||||
|
||||
var fds = initTable[int, FileHandle]()
|
||||
var next_fd = 3
|
||||
const MAX_MOUNTS = 8
|
||||
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*() =
|
||||
# SPEC-130: The Three-Domain Root
|
||||
# SPEC-021: The Sovereign Overlay Strategy
|
||||
mounts.add(MountPoint(prefix: "/nexus", mode: MODE_SFS)) # The Sovereign State (Persistent)
|
||||
mounts.add(MountPoint(prefix: "/sysro", mode: MODE_TAR)) # The Projected Reality (Immutable InitRD)
|
||||
mounts.add(MountPoint(prefix: "/state", mode: MODE_RAM)) # The Mutable Dust (Transient)
|
||||
# Restore the SPEC-130 baseline
|
||||
vfs_add_mount("/nexus", MODE_SFS)
|
||||
vfs_add_mount("/sysro", MODE_TAR)
|
||||
vfs_add_mount("/state", MODE_RAM)
|
||||
vfs_add_mount("/dev/tty", MODE_TTY)
|
||||
vfs_add_mount("/Bus/Console/tty0", MODE_TTY)
|
||||
|
||||
proc resolve_path(path: string): (VFSMode, string) =
|
||||
for m in mounts:
|
||||
if path.startsWith(m.prefix):
|
||||
let sub = if path.len > m.prefix.len: path[m.prefix.len..^1] else: "/"
|
||||
return (m.mode, sub)
|
||||
return (MODE_TAR, path)
|
||||
proc resolve_path(path: cstring): (VFSMode, int) =
|
||||
for i in 0..<mnt_count:
|
||||
let prefix = cast[cstring](addr mnt_table[i].prefix[0])
|
||||
if vfs_starts_with(path, prefix):
|
||||
var len = 0
|
||||
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.} =
|
||||
let p = $path
|
||||
let (mode, sub) = resolve_path(p)
|
||||
let (mode, prefix_len) = resolve_path(path)
|
||||
|
||||
# Delegate internal open
|
||||
let sub_path = cast[cstring](cast[uint64](path) + uint64(prefix_len))
|
||||
var internal_fd: int32 = -1
|
||||
|
||||
var fd = -1
|
||||
case mode:
|
||||
of MODE_TAR: fd = tar.vfs_open(sub, flags)
|
||||
of MODE_SFS: fd = 0 # Placeholder for SFS open
|
||||
of MODE_RAM: fd = tar.vfs_open(sub, flags) # Using TAR's RamFS for now
|
||||
|
||||
if fd > 0:
|
||||
let kernel_fd = next_fd
|
||||
fds[kernel_fd] = FileHandle(path: sub, offset: 0, mode: mode, is_ram: (mode == MODE_RAM))
|
||||
next_fd += 1
|
||||
return int32(kernel_fd)
|
||||
of MODE_TAR, MODE_RAM: internal_fd = tar.vfs_open(sub_path, flags)
|
||||
of MODE_SFS: internal_fd = 0 # Shim
|
||||
of MODE_TTY: internal_fd = 1 # Shim
|
||||
|
||||
if internal_fd >= 0:
|
||||
for i in 0..<MAX_FDS:
|
||||
if not fd_table[i].active:
|
||||
fd_table[i].active = true
|
||||
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
|
||||
|
||||
proc ion_vfs_read*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
||||
let ifd = int(fd)
|
||||
if not fds.hasKey(ifd): return -1
|
||||
let fh = addr fds[ifd]
|
||||
let idx = int(fd - 3)
|
||||
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||
let fh = addr fd_table[idx]
|
||||
|
||||
case fh.mode:
|
||||
of MODE_TTY: return -2
|
||||
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)
|
||||
return n
|
||||
of MODE_SFS:
|
||||
# SFS current read-whole-file shim
|
||||
var temp_buf: array[4096, byte] # FIXME: Small stack buffer
|
||||
let total = sfs.sfs_read_file(cstring(fh.path), addr temp_buf[0], 4096)
|
||||
if total < 0: return -1
|
||||
if fh.offset >= uint64(total): return 0
|
||||
let avail = uint64(total) - fh.offset
|
||||
let actual = min(count, avail)
|
||||
let path = cast[cstring](addr fh.path[0])
|
||||
var temp: array[256, byte] # Small shim
|
||||
let n = sfs.sfs_read_file(path, addr temp[0], 256)
|
||||
if n <= 0: return -1
|
||||
let avail = uint64(n) - fh.offset
|
||||
let actual = if count < avail: count else: avail
|
||||
if actual > 0:
|
||||
copyMem(buf, addr temp_buf[int(fh.offset)], int(actual))
|
||||
fh.offset += actual
|
||||
return int64(actual)
|
||||
copyMem(buf, addr temp[int(fh.offset)], int(actual))
|
||||
fh.offset += actual
|
||||
return int64(actual)
|
||||
return 0
|
||||
|
||||
proc ion_vfs_write*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
|
||||
let ifd = int(fd)
|
||||
if not fds.hasKey(ifd): return -1
|
||||
let fh = addr fds[ifd]
|
||||
let idx = int(fd - 3)
|
||||
if idx < 0 or idx >= MAX_FDS or not fd_table[idx].active: return -1
|
||||
let fh = addr fd_table[idx]
|
||||
|
||||
case fh.mode:
|
||||
of MODE_TTY: return -2
|
||||
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)
|
||||
return n
|
||||
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)
|
||||
|
||||
proc ion_vfs_close*(fd: int32): int32 {.exportc, cdecl.} =
|
||||
let ifd = int(fd)
|
||||
if fds.hasKey(ifd):
|
||||
fds.del(ifd)
|
||||
let idx = int(fd - 3)
|
||||
if idx >= 0 and idx < MAX_FDS:
|
||||
fd_table[idx].active = false
|
||||
return 0
|
||||
return -1
|
||||
|
||||
proc ion_vfs_list*(buf: pointer, max_len: uint64): int64 {.exportc, cdecl.} =
|
||||
var s = "/nexus\n/sysro\n/state\n"
|
||||
for name in tar.vfs_get_names():
|
||||
s.add("/sysro/" & name & "\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)
|
||||
# Hardcoded baseline for now to avoid string/os dependency
|
||||
let msg = "/nexus\n/sysro\n/state\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))
|
||||
return int64(n)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ type
|
|||
CMD_NET_RX = 0x501
|
||||
CMD_BLK_READ = 0x600
|
||||
CMD_BLK_WRITE = 0x601
|
||||
CMD_SPAWN_FIBER = 0x700
|
||||
|
||||
CmdPacket* = object
|
||||
kind*: uint32
|
||||
|
|
@ -141,9 +142,15 @@ proc recv*[T](c: var SovereignChannel[T], out_pkt: var T): bool =
|
|||
elif T is CmdPacket:
|
||||
return hal_cmd_pop(cast[uint64](c.ring), addr out_pkt)
|
||||
|
||||
# Global Channels
|
||||
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 cmd_hal: HAL_Ring[CmdPacket]
|
||||
var rx_hal: HAL_Ring[IonPacket]
|
||||
var tx_hal: HAL_Ring[IonPacket]
|
||||
|
||||
# Phase 36.2: Network Channels
|
||||
var chan_net_rx*: SovereignChannel[IonPacket]
|
||||
|
|
|
|||
1155
core/kernel.nim
1155
core/kernel.nim
File diff suppressed because it is too large
Load Diff
|
|
@ -5,13 +5,95 @@ extern fn console_write(ptr: [*]const u8, len: usize) void;
|
|||
// Embed the Subject Zero binary
|
||||
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 {
|
||||
const target_addr: usize = 0x84000000;
|
||||
const dest = @as([*]u8, @ptrFromInt(target_addr));
|
||||
|
||||
console_write("[Loader] Loading Subject Zero...\n", 33);
|
||||
@memcpy(dest[0..subject_bin.len], subject_bin);
|
||||
|
||||
const target_addr = ion_loader_load("/sysro/bin/subject");
|
||||
console_write("[Loader] Jumping...\n", 20);
|
||||
|
||||
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 kprint(s: cstring) {.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)
|
||||
proc membrane_init*() {.importc, cdecl.}
|
||||
|
|
|
|||
12
core/pty.nim
12
core/pty.nim
|
|
@ -53,14 +53,14 @@ proc kprint(s: cstring) {.importc, cdecl.}
|
|||
proc kprintln(s: cstring) {.importc, cdecl.}
|
||||
proc kprint_hex(v: uint64) {.importc, cdecl.}
|
||||
|
||||
proc pty_init*() =
|
||||
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 =
|
||||
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:
|
||||
|
|
@ -160,7 +160,7 @@ proc pty_read_master*(fd: int, data: ptr byte, len: int): int =
|
|||
read_count += 1
|
||||
return read_count
|
||||
|
||||
proc pty_write_slave*(fd: int, data: ptr byte, len: int): int =
|
||||
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)
|
||||
|
|
@ -186,7 +186,7 @@ proc pty_write_slave*(fd: int, data: ptr byte, len: int): int =
|
|||
|
||||
return written
|
||||
|
||||
proc pty_read_slave*(fd: int, data: ptr byte, len: int): int =
|
||||
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
|
||||
|
|
@ -209,13 +209,13 @@ proc pty_read_slave*(fd: int, data: ptr byte, len: int): int =
|
|||
|
||||
return read_count
|
||||
|
||||
proc pty_has_data_for_slave*(pty_id: int): bool =
|
||||
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) =
|
||||
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
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ import fiber
|
|||
# Let's define the Harmonic logic.
|
||||
# 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 the tick function
|
||||
# Returns TRUE if a fiber was switched to (work done/found).
|
||||
|
|
@ -119,6 +119,17 @@ proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
|||
# If we reached here, NO fiber is runnable.
|
||||
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)
|
||||
# =========================================================
|
||||
|
|
|
|||
|
|
@ -22,8 +22,12 @@ cpu_switch_to:
|
|||
sd s9, 96(sp)
|
||||
sd s10, 104(sp)
|
||||
sd s11, 112(sp)
|
||||
csrr t0, sscratch
|
||||
sd t0, 120(sp)
|
||||
sd sp, 0(a0)
|
||||
mv sp, a1
|
||||
ld t0, 120(sp)
|
||||
csrw sscratch, t0
|
||||
ld ra, 0(sp)
|
||||
ld gp, 8(sp)
|
||||
ld tp, 16(sp)
|
||||
|
|
@ -31,7 +35,6 @@ cpu_switch_to:
|
|||
ld s1, 32(sp)
|
||||
ld s2, 40(sp)
|
||||
ld s3, 48(sp)
|
||||
sd s4, 56(sp)
|
||||
ld s4, 56(sp)
|
||||
ld s5, 64(sp)
|
||||
ld s6, 72(sp)
|
||||
|
|
|
|||
|
|
@ -107,30 +107,34 @@ export fn trap_entry() align(4) callconv(.naked) void {
|
|||
// 🔧 CRITICAL FIX: Stack Switching (User -> Kernel)
|
||||
// Swap sp and sscratch.
|
||||
// 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
|
||||
\\ 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
|
||||
\\ 1:
|
||||
|
||||
// Allocate stack (36 words * 8 bytes = 288 bytes)
|
||||
// Allocation (36*8 = 288 bytes)
|
||||
\\ addi sp, sp, -288
|
||||
|
||||
// Save GPRs
|
||||
\\ sd ra, 0(sp)
|
||||
\\ sd gp, 8(sp)
|
||||
\\ sd tp, 16(sp)
|
||||
\\ sd t0, 24(sp)
|
||||
\\ sd t1, 32(sp)
|
||||
\\ sd t2, 40(sp)
|
||||
\\ sd s0, 48(sp)
|
||||
\\ sd s1, 56(sp)
|
||||
\\ sd a0, 64(sp)
|
||||
\\ sd a1, 72(sp)
|
||||
\\ sd a2, 80(sp)
|
||||
\\ sd a3, 88(sp)
|
||||
\\ sd a4, 96(sp)
|
||||
// Save Registers (GPRs)
|
||||
\\ sd ra, 0(sp)
|
||||
\\ sd gp, 8(sp)
|
||||
\\ sd tp, 16(sp)
|
||||
\\ sd t0, 24(sp)
|
||||
\\ sd t1, 32(sp)
|
||||
\\ sd t2, 40(sp)
|
||||
\\ sd s0, 48(sp)
|
||||
\\ sd s1, 56(sp)
|
||||
\\ sd a0, 64(sp)
|
||||
\\ sd a1, 72(sp)
|
||||
\\ sd a2, 80(sp)
|
||||
\\ sd a3, 88(sp)
|
||||
\\ sd a4, 96(sp)
|
||||
\\ sd a5, 104(sp)
|
||||
\\ sd a6, 112(sp)
|
||||
\\ sd a7, 120(sp)
|
||||
|
|
@ -169,27 +173,28 @@ export fn trap_entry() align(4) callconv(.naked) void {
|
|||
\\ mv a0, sp
|
||||
\\ call rss_trap_handler
|
||||
|
||||
// Restore CSRs
|
||||
// Restore CSRs (Optional if modified? sepc changed for syscall)
|
||||
\\ ld t0, 240(sp)
|
||||
\\ 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)
|
||||
\\ csrw sstatus, t1
|
||||
|
||||
// Restore GPRs
|
||||
\\ ld ra, 0(sp)
|
||||
\\ ld gp, 8(sp)
|
||||
\\ ld tp, 16(sp)
|
||||
\\ ld t0, 24(sp)
|
||||
\\ ld t1, 32(sp)
|
||||
\\ ld t2, 40(sp)
|
||||
\\ ld s0, 48(sp)
|
||||
\\ ld s1, 56(sp)
|
||||
\\ ld a0, 64(sp)
|
||||
\\ ld a1, 72(sp)
|
||||
\\ ld a2, 80(sp)
|
||||
\\ ld a3, 88(sp)
|
||||
\\ ld a4, 96(sp)
|
||||
// Restore Encapsulated User Context
|
||||
\\ ld ra, 0(sp)
|
||||
\\ ld gp, 8(sp)
|
||||
\\ ld tp, 16(sp)
|
||||
\\ ld t0, 24(sp)
|
||||
\\ ld t1, 32(sp)
|
||||
\\ ld t2, 40(sp)
|
||||
\\ ld s0, 48(sp)
|
||||
\\ ld s1, 56(sp)
|
||||
\\ ld a0, 64(sp)
|
||||
\\ ld a1, 72(sp)
|
||||
\\ ld a2, 80(sp)
|
||||
\\ ld a3, 88(sp)
|
||||
\\ ld a4, 96(sp)
|
||||
\\ ld a5, 104(sp)
|
||||
\\ ld a6, 112(sp)
|
||||
\\ ld a7, 120(sp)
|
||||
|
|
@ -211,10 +216,16 @@ export fn trap_entry() align(4) callconv(.naked) void {
|
|||
// Deallocate stack
|
||||
\\ addi sp, sp, 288
|
||||
|
||||
// 🔧 CRITICAL FIX: Swap back sscratch <-> sp before sret
|
||||
// If returning to U-mode, sscratch currently holds User Stack.
|
||||
// We need: sp = User Stack, sscratch = Kernel Stack
|
||||
// 🔧 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
|
||||
);
|
||||
}
|
||||
|
|
@ -227,6 +238,37 @@ extern fn k_check_deferred_yield() void;
|
|||
|
||||
export fn rss_trap_handler(frame: *TrapFrame) void {
|
||||
const scause = frame.scause;
|
||||
// uart.print("[Trap] Entered Handler. scause: ");
|
||||
// uart.print_hex(scause);
|
||||
// uart.print("\n");
|
||||
|
||||
// Check high bit: 0 = Exception, 1 = Interrupt
|
||||
if ((scause >> 63) != 0) {
|
||||
const intr_id = scause & 0x7FFFFFFFFFFFFFFF;
|
||||
if (intr_id == 9) {
|
||||
// PLIC Context 1 (Supervisor) Claim/Complete Register
|
||||
const PLIC_CLAIM: *volatile u32 = @ptrFromInt(0x0c201004);
|
||||
const irq = PLIC_CLAIM.*;
|
||||
|
||||
if (irq == 10) { // UART0 is IRQ 10 on Virt machine
|
||||
uart.poll_input();
|
||||
} else if (irq == 0) {
|
||||
// Spurious or no pending interrupt
|
||||
}
|
||||
|
||||
// Complete the interrupt
|
||||
PLIC_CLAIM.* = irq;
|
||||
} else if (intr_id == 5) {
|
||||
// Supervisor Timer Interrupt
|
||||
// Disable (One-shot)
|
||||
asm volatile ("csrc sie, %[mask]"
|
||||
:
|
||||
: [mask] "r" (@as(usize, 1 << 5)),
|
||||
);
|
||||
}
|
||||
k_check_deferred_yield();
|
||||
return;
|
||||
}
|
||||
|
||||
// 8: ECALL from U-mode
|
||||
// 9: ECALL from S-mode
|
||||
|
|
@ -241,17 +283,13 @@ export fn rss_trap_handler(frame: *TrapFrame) void {
|
|||
frame.a0 = res;
|
||||
|
||||
// DIAGNOSTIC: Syscall completed
|
||||
uart.print("[Trap] Syscall done, returning to userland\n");
|
||||
// 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
|
||||
// It will decide whether to segregate (worker) or halt (system)
|
||||
// Note: k_handle_exception handles flow control (yield/halt) and does not return
|
||||
k_handle_exception(scause, frame.sepc, frame.stval);
|
||||
|
||||
// Safety halt if kernel returns (should be unreachable)
|
||||
|
|
@ -268,6 +306,15 @@ extern fn NimMain() void;
|
|||
|
||||
export fn zig_entry() void {
|
||||
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 RISC-V] Handing off to Nim L1...\n");
|
||||
_ = virtio_net;
|
||||
|
|
@ -287,6 +334,14 @@ export fn console_read() c_int {
|
|||
return -1;
|
||||
}
|
||||
|
||||
export fn console_poll() void {
|
||||
uart.poll_input();
|
||||
}
|
||||
|
||||
export fn debug_uart_lsr() u8 {
|
||||
return uart.get_lsr();
|
||||
}
|
||||
|
||||
const virtio_block = @import("virtio_block.zig");
|
||||
|
||||
extern fn hal_surface_init() void;
|
||||
|
|
@ -314,6 +369,27 @@ export fn rumpk_timer_now_ns() u64 {
|
|||
return ticks * 100;
|
||||
}
|
||||
|
||||
export fn sched_arm_timer(deadline_ns: u64) void {
|
||||
// 1 tick = 100ns (10MHz)
|
||||
const deadline_ticks = deadline_ns / 100;
|
||||
|
||||
// Use SBI Time Extension (0x54494D45) to set timer
|
||||
// FID=0: sbi_set_timer(stime_value)
|
||||
asm volatile (
|
||||
\\ ecall
|
||||
:
|
||||
: [arg0] "{a0}" (deadline_ticks),
|
||||
[eid] "{a7}" (0x54494D45),
|
||||
[fid] "{a6}" (0),
|
||||
: .{ .memory = true });
|
||||
|
||||
// Enable STIE (Supervisor Timer Interrupt Enable) in sie (bit 5)
|
||||
asm volatile ("csrs sie, %[mask]"
|
||||
:
|
||||
: [mask] "r" (@as(usize, 1 << 5)),
|
||||
);
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// KEXEC (The Phoenix Protocol)
|
||||
// =========================================================
|
||||
|
|
@ -338,3 +414,38 @@ export fn hal_kexec(entry: u64, dtb: u64) noreturn {
|
|||
);
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
33
hal/mm.zig
33
hal/mm.zig
|
|
@ -23,7 +23,7 @@ pub const LEVELS: u8 = 3;
|
|||
|
||||
// Physical memory layout (RISC-V QEMU virt)
|
||||
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
|
||||
pub const UART_BASE: u64 = 0x10000000;
|
||||
|
|
@ -164,6 +164,7 @@ pub fn create_kernel_identity_map() !*PageTable {
|
|||
// MMIO regions
|
||||
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, 0x20000000, 0x20000000, 0x10000, PTE_R | PTE_W); // PTY Slave
|
||||
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, PLIC_BASE, PLIC_BASE, 0x400000, PTE_R | PTE_W);
|
||||
|
|
@ -172,25 +173,35 @@ pub fn create_kernel_identity_map() !*PageTable {
|
|||
}
|
||||
|
||||
// Create restricted worker map
|
||||
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, code_base_pa: u64) !*PageTable {
|
||||
const root = alloc_page_table() orelse return error.OutOfMemory;
|
||||
|
||||
// 🏛️ THE EXPANDED CAGE (Phase 37 - 256MB RAM)
|
||||
// 🏛️ THE ISOLATED CAGE (Phase 40 - Per-Fiber Memory Isolation)
|
||||
|
||||
kprint("[MM] Creating worker map:\n");
|
||||
kprint("[MM] Kernel (S-mode): 0x80000000-0x88000000\n");
|
||||
kprint("[MM] User (U-mode): 0x88000000-0x90000000\n");
|
||||
kprint("[MM] User VA: 0x88000000-0x90000000\n");
|
||||
kprint("[MM] User PA: ");
|
||||
kprint_hex(code_base_pa);
|
||||
kprint("\n");
|
||||
|
||||
// 1. Kernel Memory (0-128MB) -> Supervisor ONLY (PTE_U = 0)
|
||||
// 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);
|
||||
|
||||
// 2. User Memory (128-256MB) -> User Accessible (PTE_U = 1)
|
||||
// This allows NipBox (at 128MB offset) to execute in U-mode.
|
||||
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);
|
||||
// 2. User Memory (VA 0x88000000 -> PA code_base_pa) -> User Accessible (PTE_U = 1)
|
||||
// This creates ISOLATED physical regions per fiber:
|
||||
// - Init: VA 0x88000000 -> PA 0x88000000
|
||||
// - Child: VA 0x88000000 -> PA 0x90000000 (or higher)
|
||||
const user_va_base = DRAM_BASE + (128 * 1024 * 1024);
|
||||
try map_range(root, user_va_base, code_base_pa, 128 * 1024 * 1024, PTE_R | PTE_W | PTE_X | PTE_U);
|
||||
|
||||
// 3. User MMIO (UART)
|
||||
try map_range(root, UART_BASE, UART_BASE, PAGE_SIZE, PTE_R | PTE_W | PTE_U);
|
||||
// 3. MMIO Plumbing - Mapped identity but S-mode ONLY (PTE_U = 0)
|
||||
// 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, 0x20000000, 0x20000000, 0x10000, PTE_R | PTE_W); // PTY Slave
|
||||
|
||||
// 4. Overlap stack with user access
|
||||
try map_range(root, stack_base, stack_base, stack_size, PTE_R | PTE_W | PTE_U);
|
||||
|
|
@ -244,8 +255,8 @@ pub export fn mm_get_kernel_satp() callconv(.c) u64 {
|
|||
return kernel_satp_value;
|
||||
}
|
||||
|
||||
pub export fn mm_create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64) callconv(.c) u64 {
|
||||
if (create_worker_map(stack_base, stack_size, packet_addr)) |root| {
|
||||
pub export fn mm_create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64, code_base_pa: u64) callconv(.c) u64 {
|
||||
if (create_worker_map(stack_base, stack_size, packet_addr, code_base_pa)) |root| {
|
||||
return make_satp(root);
|
||||
} else |_| {
|
||||
return 0;
|
||||
|
|
|
|||
123
hal/uart.zig
123
hal/uart.zig
|
|
@ -35,15 +35,16 @@ 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: u32 = 0; // Write position
|
||||
var input_tail: u32 = 0; // Read position
|
||||
var input_head = std.atomic.Value(u32).init(0); // Write position
|
||||
var input_tail = std.atomic.Value(u32).init(0); // Read position
|
||||
|
||||
pub fn init() void {
|
||||
// Initialize buffer pointers
|
||||
input_head = 0;
|
||||
input_tail = 0;
|
||||
input_head.store(0, .monotonic);
|
||||
input_tail.store(0, .monotonic);
|
||||
|
||||
switch (builtin.cpu.arch) {
|
||||
.riscv64 => init_riscv(),
|
||||
|
|
@ -54,18 +55,65 @@ pub fn init() void {
|
|||
pub fn init_riscv() void {
|
||||
const base = NS16550A_BASE;
|
||||
|
||||
// 1. Disable Interrupts
|
||||
// 1. Enable Interrupts (Received Data Available)
|
||||
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);
|
||||
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
|
||||
const lcr: *volatile u8 = @ptrFromInt(base + NS16550A_LCR);
|
||||
lcr.* = 0x03;
|
||||
|
||||
// --- LOOPBACK TEST ---
|
||||
// Enable Loopback Mode (Bit 4 of MCR)
|
||||
mcr.* = 0x1B; // 0x0B | 0x10
|
||||
|
||||
// Write a test byte: 0xA5
|
||||
const thr: *volatile u8 = @ptrFromInt(base + NS16550A_THR);
|
||||
const lsr: *volatile u8 = @ptrFromInt(base + NS16550A_LSR);
|
||||
// Wait for THRE
|
||||
while ((lsr.* & NS16550A_THRE) == 0) {}
|
||||
thr.* = 0xA5;
|
||||
|
||||
// Wait for Data Ready
|
||||
var timeout: usize = 1000000;
|
||||
while ((lsr.* & 0x01) == 0 and timeout > 0) {
|
||||
timeout -= 1;
|
||||
}
|
||||
|
||||
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
|
||||
poll_input();
|
||||
}
|
||||
|
|
@ -83,10 +131,13 @@ pub fn poll_input() void {
|
|||
const byte = thr.*;
|
||||
|
||||
// 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;
|
||||
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)
|
||||
}
|
||||
|
|
@ -98,10 +149,13 @@ pub fn poll_input() void {
|
|||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -148,15 +202,42 @@ pub fn read_byte() ?u8 {
|
|||
poll_input();
|
||||
|
||||
// Then read from buffer
|
||||
if (input_tail != input_head) {
|
||||
const byte = input_buffer[input_tail];
|
||||
input_tail = (input_tail + 1) % INPUT_BUFFER_SIZE;
|
||||
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;
|
||||
}
|
||||
|
||||
pub fn read_direct() ?u8 {
|
||||
switch (builtin.cpu.arch) {
|
||||
.riscv64 => {
|
||||
const thr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_THR);
|
||||
const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
|
||||
if ((lsr.* & 0x01) != 0) {
|
||||
return thr.*;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
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 {
|
||||
write_bytes(s);
|
||||
}
|
||||
|
|
@ -194,3 +275,7 @@ pub fn print_hex(value: usize) void {
|
|||
write_char(hex_chars[nibble]);
|
||||
}
|
||||
}
|
||||
|
||||
export fn uart_print_hex(value: u64) void {
|
||||
print_hex(value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -274,13 +274,8 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
|||
}
|
||||
|
||||
void console_write(const void* p, size_t len) {
|
||||
// Phase 7: Direct UART access for Proof of Life
|
||||
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];
|
||||
}
|
||||
// Phase 11: Real Syscalls only. No direct MMIO.
|
||||
write(1, p, len);
|
||||
}
|
||||
|
||||
void ion_egress_to_port(uint16_t port, void* pkt);
|
||||
|
|
|
|||
|
|
@ -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-140)
|
||||
## 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-140 Doctrine).
|
||||
let rb_node = newNode("rollback_target")
|
||||
rb_node.addArg(newVal(int(target_tx)))
|
||||
ledger.ledger_append(OpRollback, "system.rollback", rb_node)
|
||||
|
|
@ -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
|
||||
|
|
@ -107,6 +107,7 @@ proc get_sys_table*(): ptr SysTable =
|
|||
|
||||
proc ion_user_init*() {.exportc.} =
|
||||
let sys = get_sys_table()
|
||||
discard sys
|
||||
# Use raw C write to avoid Nim string issues before init
|
||||
proc console_write(p: pointer, len: uint) {.importc, cdecl.}
|
||||
var msg = "[ION-Client] Initializing...\n"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -277,11 +277,12 @@ when defined(RUMPK_KERNEL):
|
|||
for i in FILE_FD_START..<255:
|
||||
if g_fd_table[i].kind == FD_NONE:
|
||||
g_fd_table[i].kind = FD_FILE
|
||||
let p_str = $path
|
||||
let to_copy = min(p_str.len, 63)
|
||||
for j in 0..<to_copy:
|
||||
g_fd_table[i].path[j] = p_str[j]
|
||||
g_fd_table[i].path[to_copy] = '\0'
|
||||
let p = cast[ptr UncheckedArray[char]](path)
|
||||
var j = 0
|
||||
while p[j] != '\0' and j < 63:
|
||||
g_fd_table[i].path[j] = p[j]
|
||||
j += 1
|
||||
g_fd_table[i].path[j] = '\0'
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
|
|
|||
|
|
@ -52,11 +52,13 @@ export fn fputc(c: i32, stream: ?*anyopaque) i32 {
|
|||
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;
|
||||
extern fn console_write(ptr: [*]const u8, len: usize) void;
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,4 @@
|
|||
# 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 Membrane: Virtual Terminal Emulator
|
||||
|
||||
# Phase 27 Part 2: The CRT Scanline Renderer
|
||||
# Nexus Membrane: Virtual Terminal Emulator
|
||||
import term_font
|
||||
import ion_client
|
||||
|
||||
|
|
@ -20,13 +11,191 @@ const
|
|||
COLOR_PHOSPHOR_AMBER = 0xFF00B0FF'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 color_fg: uint32 = COLOR_PHOSPHOR_AMBER
|
||||
var color_bg: uint32 = COLOR_SOVEREIGN_BLUE
|
||||
var term_dirty*: bool = true
|
||||
|
||||
var fb_ptr: ptr UncheckedArray[uint32]
|
||||
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*() =
|
||||
let sys = cast[ptr SysTable](SYS_TABLE_ADDR)
|
||||
|
|
@ -36,104 +205,22 @@ proc term_init*() =
|
|||
fb_stride = int(sys.fb_stride)
|
||||
cursor_x = 0
|
||||
cursor_y = 0
|
||||
ansi_state = 0
|
||||
cur_state = Normal
|
||||
term_dirty = true
|
||||
|
||||
# Initialize Grid
|
||||
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))
|
||||
|
||||
for row in 0..<TERM_ROWS:
|
||||
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
|
||||
color_fg = COLOR_PHOSPHOR_AMBER
|
||||
color_bg = COLOR_SOVEREIGN_BLUE
|
||||
|
||||
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)
|
||||
# Test Colors
|
||||
let test_msg = "\x1b[31mN\x1b[32mE\x1b[33mX\x1b[34mU\x1b[35mS\x1b[0m\n"
|
||||
for ch in test_msg: term_putc(ch)
|
||||
|
|
|
|||
|
|
@ -1,220 +1,13 @@
|
|||
# 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 Membrane: Console Font Dispatcher
|
||||
|
||||
## 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
|
||||
# Source: CP437 Standard
|
||||
# Exported for Renderer Access
|
||||
|
||||
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]
|
||||
]
|
||||
const FONT_WIDTH* = profile.FONT_WIDTH
|
||||
const FONT_HEIGHT* = profile.FONT_HEIGHT
|
||||
const FONT_BITMAP* = profile.FONT_BITMAP
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ export fn nexshell_main() void {
|
|||
print("║ Command Plane: READY ║\n");
|
||||
print("╚═══════════════════════════════════════╝\n");
|
||||
|
||||
const event_ring = sys.s_event;
|
||||
// TEMP: event_ring disabled due to NULL pointer issue
|
||||
// const event_ring = sys.s_event;
|
||||
const cmd_ring = sys.s_cmd;
|
||||
|
||||
// SAFETY(NexShell): Input buffer initialized to `undefined` for performance.
|
||||
|
|
@ -73,6 +74,7 @@ export fn nexshell_main() void {
|
|||
|
||||
var loop_count: usize = 0;
|
||||
var poll_pulse: usize = 0;
|
||||
var last_lsr: u8 = 0;
|
||||
print("[NexShell] Entering main loop...\n");
|
||||
while (true) {
|
||||
loop_count += 1;
|
||||
|
|
@ -89,28 +91,40 @@ export fn nexshell_main() void {
|
|||
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);
|
||||
}
|
||||
// TEMPORARILY DISABLED: event_ring causes page fault (NULL pointer?)
|
||||
// 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)
|
||||
console_poll();
|
||||
|
||||
const current_lsr = debug_uart_lsr();
|
||||
if (current_lsr != last_lsr) {
|
||||
print("[LSR:");
|
||||
print_hex(current_lsr);
|
||||
print("]");
|
||||
last_lsr = current_lsr;
|
||||
}
|
||||
|
||||
if ((loop_count % 20) == 0) {
|
||||
print("."); // Alive heartbeat
|
||||
}
|
||||
const c = console_read();
|
||||
if (c != -1) {
|
||||
print("[GOT]");
|
||||
const byte = @as(u8, @intCast(c));
|
||||
const char_buf = [1]u8{byte};
|
||||
print("[NexShell] Got char: '");
|
||||
print(&char_buf);
|
||||
print("'\n");
|
||||
// print("[NexShell] Got char\n");
|
||||
|
||||
if (forward_mode) {
|
||||
// Check for escape: Ctrl+K (11)
|
||||
|
|
@ -138,13 +152,26 @@ export fn nexshell_main() void {
|
|||
print(&bs);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fiber_sleep(20); // 50Hz poll is plenty for keyboard (Wait... fiber_sleep takes milliseconds in Nim wrapper!)
|
||||
// Re-checking kernel.nim: fiber_sleep(ms) multiplies by 1_000_000.
|
||||
// So 20 is Correct for 20ms.
|
||||
// Wait. If kernel.nim multiplies by 1M, then passing 20 = 20M ns = 20ms.
|
||||
// My analysis in Thought Process was confused.
|
||||
// kernel.nim:
|
||||
// proc fiber_sleep*(ms: uint64) = current_fiber.sleep_until = now + (ms * 1_000_000)
|
||||
// So nexshell.zig calling fiber_sleep(20) -> 20ms.
|
||||
// THIS IS CORRECT.
|
||||
// I will NOT change this to 20_000_000. That would be 20,000 seconds!
|
||||
// I will restore the comment to be accurate.
|
||||
fiber_sleep(20);
|
||||
}
|
||||
|
||||
fiber_yield();
|
||||
}
|
||||
}
|
||||
|
||||
var forward_mode: bool = true;
|
||||
var forward_mode: bool = false;
|
||||
|
||||
fn process_command(cmd_text: []const u8, cmd_ring: *RingBuffer(CmdPacket)) void {
|
||||
if (cmd_text.len == 0) return;
|
||||
|
|
@ -186,8 +213,43 @@ fn process_command(cmd_text: []const u8, cmd_ring: *RingBuffer(CmdPacket)) void
|
|||
} 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 if (std.mem.eql(u8, cmd_text, "matrix status")) {
|
||||
push_cmd(cmd_ring, CMD_GET_GPU_STATUS, 0);
|
||||
} else if (std.mem.eql(u8, cmd_text, "ps") or std.mem.eql(u8, cmd_text, "fibers")) {
|
||||
print("[NexShell] Active Fibers:\n");
|
||||
print(" - ION (Packet Engine)\n");
|
||||
print(" - NexShell (Command Plane)\n");
|
||||
print(" - Compositor (Render Pipeline)\n");
|
||||
print(" - NetSwitch (Traffic Engine)\n");
|
||||
print(" - Subject (Userland Loader)\n");
|
||||
print(" - Kernel (Main)\n");
|
||||
} else if (std.mem.eql(u8, cmd_text, "mem")) {
|
||||
print("[NexShell] Memory Status:\n");
|
||||
print(" Ion Pool: 32MB allocated\n");
|
||||
print(" Surface: 32MB framebuffer pool\n");
|
||||
print(" Stack Usage: ~512KB (6 fibers)\n");
|
||||
} else if (std.mem.eql(u8, cmd_text, "uptime")) {
|
||||
print("[NexShell] System Status: OPERATIONAL\n");
|
||||
print(" Architecture: RISC-V (Virt)\n");
|
||||
print(" Timer: SBI Extension\n");
|
||||
print(" Input: Interrupt-Driven (IRQ 10)\n");
|
||||
} else if (std.mem.eql(u8, cmd_text, "reboot")) {
|
||||
print("[NexShell] Initiating system reboot...\n");
|
||||
// SBI shutdown extension (EID=0x53525354, FID=0)
|
||||
asm volatile (
|
||||
\\ li a7, 0x53525354
|
||||
\\ li a6, 0
|
||||
\\ li a0, 0
|
||||
\\ ecall
|
||||
);
|
||||
} else if (std.mem.eql(u8, cmd_text, "clear")) {
|
||||
print("\x1b[2J\x1b[H"); // ANSI clear screen + cursor home
|
||||
} else if (std.mem.eql(u8, cmd_text, "help")) {
|
||||
print("[NexShell] Kernel Commands:\n");
|
||||
print(" System: ps, fibers, mem, uptime, reboot, clear\n");
|
||||
print(" IO: io stop, ion stop\n");
|
||||
print(" Matrix: matrix on/off/status\n");
|
||||
print(" Shell: subject, nipbox, help\n");
|
||||
} else {
|
||||
print("[NexShell] Unknown Kernel Command: ");
|
||||
print(cmd_text);
|
||||
|
|
@ -211,11 +273,27 @@ fn push_cmd(ring: *RingBuffer(CmdPacket), kind: u32, arg: u64) void {
|
|||
}
|
||||
|
||||
// OS Shims
|
||||
extern fn write(fd: c_int, buf: [*]const u8, count: usize) isize;
|
||||
extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize;
|
||||
extern fn console_read() c_int;
|
||||
extern fn console_poll() void;
|
||||
extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void;
|
||||
extern fn fiber_sleep(ms: u64) void;
|
||||
extern fn fiber_yield() void;
|
||||
extern fn debug_uart_lsr() u8;
|
||||
|
||||
fn print_hex(val: u8) void {
|
||||
const chars = "0123456789ABCDEF";
|
||||
const hi = chars[(val >> 4) & 0xF];
|
||||
const lo = chars[val & 0xF];
|
||||
const buf = [_]u8{ hi, lo };
|
||||
print(&buf);
|
||||
}
|
||||
|
||||
fn kernel_write(fd: c_int, buf: [*]const u8, count: usize) isize {
|
||||
// 0x204 = SYS_WRITE
|
||||
return @as(isize, @bitCast(k_handle_syscall(0x204, @as(usize, @intCast(fd)), @intFromPtr(buf), count)));
|
||||
}
|
||||
|
||||
fn print(text: []const u8) void {
|
||||
_ = write(1, text.ptr, text.len);
|
||||
_ = kernel_write(1, text.ptr, text.len);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue