WASM Execution Pipeline, File Dispatch & Supporting Infrastructure #52

Merged
admin merged 6 commits from feature/wasm into develop 2026-03-07 22:29:31 +00:00
16 changed files with 1795 additions and 68 deletions
+2 -1
View File
@@ -16,4 +16,5 @@ dockerout.txt
AGENTS.md AGENTS.md
lessons_learnt.md lessons_learnt.md
*.log *.log
/doc/*.md /doc/*.md
wasuro
+1
View File
@@ -24,6 +24,7 @@ declare -a run_steps=(
"compile_stub.sh" "Failed to compile stub!" "compile_stub.sh" "Failed to compile stub!"
"compile_vergen.sh" "Versions failed to compile" "compile_vergen.sh" "Versions failed to compile"
"compile_lvgl.sh" "Failed to compile LVGL!" "compile_lvgl.sh" "Failed to compile LVGL!"
"compile_wasuro.sh" "Failed to pull Wasuro!"
"compile_sources.sh" "Failed to compile FPC Sources!" "compile_sources.sh" "Failed to compile FPC Sources!"
"compile_link.sh" "Failed linking!" "compile_link.sh" "Failed linking!"
"compile_isogen.sh" "Failed to create ISO!" "compile_isogen.sh" "Failed to create ISO!"
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# compile_wasuro.sh — Pull Wasuro WASM runtime source into wasuro/
# Clones the repo (sparse, src/wasm only) and copies the Pascal sources
# into $PWD/wasuro so the FPC build can reference them via -Fu.
set -e
WASURO_REPO="https://gitea.spexeah.com/Spexeah/Wasuro.git"
WASURO_BRANCH="develop"
WASURO_TMP="/tmp/wasuro"
WASURO_OUT="$(pwd)/wasuro"
echo " "
echo "======================="
echo " "
echo "Pulling Wasuro WASM runtime..."
echo " "
# Skip if wasuro/ already has .pas files (cached from previous build)
if [ -n "$(find "${WASURO_OUT}" -name '*.pas' 2>/dev/null | head -1)" ]; then
echo "Wasuro sources already present in ${WASURO_OUT}, skipping pull."
echo " "
exit 0
fi
# Clone sparse checkout (only src/wasm)
rm -rf "$WASURO_TMP"
git clone --depth 1 --branch "${WASURO_BRANCH}" --filter=blob:none --sparse \
"${WASURO_REPO}" "${WASURO_TMP}" 2>&1
cd "$WASURO_TMP"
git sparse-checkout set src/wasm 2>&1
cd -
echo "Copying src/wasm to ${WASURO_OUT}..."
rm -rf "$WASURO_OUT"
mkdir -p "$WASURO_OUT"
cp -a "${WASURO_TMP}/src/wasm/"* "$WASURO_OUT"/
rm -rf "$WASURO_TMP"
TOTAL=$(find "$WASURO_OUT" -name '*.pas' | wc -l)
echo "Copied ${TOTAL} Pascal source files."
echo " "
echo "Wasuro pull complete."
echo " "
+4 -1
View File
@@ -29,11 +29,14 @@ unit contextswitcher;
interface interface
uses uses
idt, isrmanager, processmanager, proctypes, util, tracer; idt, isrmanager, processmanager, proctypes, util, syslog, tracer;
{ Initialise: overwrite IDT gate 32 with our custom ISR. } { Initialise: overwrite IDT gate 32 with our custom ISR. }
procedure init; procedure init;
{ No idle ESP variable needed — idle is now a formal process (PID 0)
with its SavedESP managed like any other process. }
implementation implementation
{ ----------------------------------------------------------------------- { -----------------------------------------------------------------------
File diff suppressed because it is too large Load Diff
+64 -10
View File
@@ -1157,6 +1157,13 @@ var
pvVol : PStorage_Volume; pvVol : PStorage_Volume;
pvStatus : puint32; pvStatus : puint32;
pvDirList: PLinkedListBase; pvDirList: PLinkedListBase;
pvSplitRel : PLinkedListBase;
pvSegCount : uint32;
pvIdx : uint32;
pvLeafName : pchar;
pvParentDir : pchar;
pvTmpConcat : pchar;
pvEntry : PDirectory_Entry;
begin begin
tracer.push_trace('vfs.PathValid.enter'); tracer.push_trace('vfs.PathValid.enter');
@@ -1175,22 +1182,69 @@ begin
RelPath[0] := '/'; RelPath[0] := '/';
end; end;
pvVol := PStorage_Volume(Obj^.Reference); pvVol := PStorage_Volume(Obj^.Reference);
{ Actually verify the path exists on the volume } { Verify the path exists on the volume }
if (pvVol^.filesystem <> nil) and (pvVol^.filesystem^.readDirCallback <> nil) then begin if (pvVol^.filesystem <> nil) and (pvVol^.filesystem^.readDirCallback <> nil) then begin
{ If RelPath is just '/' we're at volume root — always valid } { If RelPath is just '/' we're at volume root — always valid }
if StringEquals(RelPath, '/') then begin if StringEquals(RelPath, '/') then begin
PathValid := pvDirectory; PathValid := pvDirectory;
end else begin end else begin
{ Ask the filesystem if this directory actually exists } { Split RelPath into parent directory and leaf name,
pvStatus := puint32(kalloc(4)); then list the parent and look for the leaf entry. }
pvStatus^ := 0; pvSplitRel := STRLL_FromString(RelPath, '/');
pvDirList := pvVol^.filesystem^.readDirCallback(pvVol, RelPath, pvStatus); pvSegCount := STRLL_Size(pvSplitRel);
if pvStatus^ = 0 then if pvSegCount = 0 then begin
PathValid := pvDirectory
else
PathValid := pvInvalid; PathValid := pvInvalid;
if pvDirList <> nil then LL_Free(pvDirList); end else begin
kfree(puint32(pvStatus)); pvLeafName := STRLL_Get(pvSplitRel, pvSegCount - 1);
{ Build parent directory path from all segments except the last }
if pvSegCount <= 1 then begin
pvParentDir := stringNew(1);
pvParentDir[0] := '/';
end else begin
pvParentDir := stringNew(0);
for pvIdx := 0 to pvSegCount - 2 do begin
if stringSize(pvParentDir) > 0 then begin
pvTmpConcat := stringConcat(pvParentDir, '/');
kfree(void(pvParentDir));
pvParentDir := pvTmpConcat;
end;
pvTmpConcat := stringConcat(pvParentDir, STRLL_Get(pvSplitRel, pvIdx));
kfree(void(pvParentDir));
pvParentDir := pvTmpConcat;
end;
end;
{ List parent directory }
pvStatus := puint32(kalloc(4));
pvStatus^ := 0;
pvDirList := pvVol^.filesystem^.readDirCallback(pvVol, pvParentDir, pvStatus);
PathValid := pvInvalid;
if (pvDirList <> nil) and (pvStatus^ = 0) and (LL_Size(pvDirList) > 0) then begin
for pvIdx := 0 to LL_Size(pvDirList) - 1 do begin
pvEntry := PDirectory_Entry(LL_Get(pvDirList, pvIdx));
if (pvEntry <> nil) and (pvEntry^.fileName <> nil) then begin
if stringEquals(pvEntry^.fileName, pvLeafName) then begin
case pvEntry^.entryType of
fileEntry: PathValid := pvFile;
directoryEntry: PathValid := pvDirectory;
mountEntry: PathValid := pvDirectory;
end;
break;
end;
end;
end;
{ Free entry file names and list }
for pvIdx := 0 to LL_Size(pvDirList) - 1 do begin
pvEntry := PDirectory_Entry(LL_Get(pvDirList, pvIdx));
if (pvEntry <> nil) and (pvEntry^.fileName <> nil) then
kfree(void(pvEntry^.fileName));
end;
LL_Free(pvDirList);
end else if pvDirList <> nil then
LL_Free(pvDirList);
kfree(puint32(pvStatus));
kfree(void(pvParentDir));
end;
STRLL_Free(pvSplitRel);
end; end;
end else end else
PathValid := pvInvalid; PathValid := pvInvalid;
+41 -26
View File
@@ -29,6 +29,9 @@ procedure relayout;
implementation implementation
uses
syslog, lists, lmemorymanager;
const const
DOCK_HEIGHT = 48; DOCK_HEIGHT = 48;
DOCK_MARGIN = 8; DOCK_MARGIN = 8;
@@ -47,8 +50,6 @@ const
SYSINFO_W = 480; SYSINFO_W = 480;
SYSINFO_H = 560; SYSINFO_H = 560;
MAX_PROGRAMS = 16;
{ Animation } { Animation }
ANIM_SPEED = 12; { pixels per frame to slide } ANIM_SPEED = 12; { pixels per frame to slide }
@@ -59,10 +60,10 @@ type
launch : TProgLaunchProc; launch : TProgLaunchProc;
active : boolean; active : boolean;
end; end;
PProgEntry = ^TProgEntry;
var var
programs : array[0..MAX_PROGRAMS-1] of TProgEntry; programs : PLinkedListBase;
prog_count : uint32;
{ ---- Desktop UI elements ---- } { ---- Desktop UI elements ---- }
var var
@@ -99,12 +100,15 @@ var
Program Registry Program Registry
============================================================ } ============================================================ }
procedure registerProgram(name: pchar; launcher: TProgLaunchProc); procedure registerProgram(name: pchar; launcher: TProgLaunchProc);
var
entry : PProgEntry;
begin begin
if prog_count >= MAX_PROGRAMS then exit; if programs = nil then
programs[prog_count].name := name; programs := LL_New(sizeof(TProgEntry));
programs[prog_count].launch := launcher; entry := PProgEntry(LL_Add(programs));
programs[prog_count].active := true; entry^.name := name;
inc(prog_count); entry^.launch := launcher;
entry^.active := true;
end; end;
{ ============================================================ { ============================================================
@@ -435,6 +439,7 @@ var
code : uint32; code : uint32;
target : Plv_obj; target : Plv_obj;
i, pi : uint32; i, pi : uint32;
entry : PProgEntry;
begin begin
code := lv_event_get_code(e); code := lv_event_get_code(e);
if code <> LV_EVENT_CLICKED then exit; if code <> LV_EVENT_CLICKED then exit;
@@ -443,10 +448,13 @@ begin
for i := 0 to RESULTS_MAX_VISIBLE - 1 do begin for i := 0 to RESULTS_MAX_VISIBLE - 1 do begin
if result_btns[i] = target then begin if result_btns[i] = target then begin
pi := result_prog[i]; pi := result_prog[i];
if (pi < prog_count) and programs[pi].active then begin if (programs <> nil) and (pi < LL_Size(programs)) then begin
serial.sendString('[Desktop] Launching program' + #10); entry := PProgEntry(LL_Get(programs, pi));
clearAndClose; if (entry <> nil) and entry^.active then begin
programs[pi].launch; syslog.logln('Desktop', 'Launching program');
clearAndClose;
entry^.launch;
end;
end; end;
exit; exit;
end; end;
@@ -547,6 +555,7 @@ var
query : pchar; query : pchar;
i, vis : uint32; i, vis : uint32;
panel_h : sint32; panel_h : sint32;
fentry : PProgEntry;
begin begin
if not results_open then exit; if not results_open then exit;
if results_panel = nil then exit; if results_panel = nil then exit;
@@ -554,15 +563,18 @@ begin
query := lv_textarea_get_text(search_ta); query := lv_textarea_get_text(search_ta);
vis := 0; vis := 0;
for i := 0 to prog_count - 1 do begin if programs <> nil then begin
if vis >= RESULTS_MAX_VISIBLE then break; for i := 0 to LL_Size(programs) - 1 do begin
if not programs[i].active then continue; if vis >= RESULTS_MAX_VISIBLE then break;
fentry := PProgEntry(LL_Get(programs, i));
if (fentry = nil) or (not fentry^.active) then continue;
if (query = nil) or (query^ = #0) or ciContains(programs[i].name, query) then begin if (query = nil) or (query^ = #0) or ciContains(fentry^.name, query) then begin
lv_label_set_text(result_lbls[vis], programs[i].name); lv_label_set_text(result_lbls[vis], fentry^.name);
result_prog[vis] := i; result_prog[vis] := i;
lv_obj_remove_flag(result_btns[vis], LV_OBJ_FLAG_HIDDEN); lv_obj_remove_flag(result_btns[vis], LV_OBJ_FLAG_HIDDEN);
inc(vis); inc(vis);
end;
end; end;
end; end;
@@ -588,7 +600,8 @@ end;
============================================================ } ============================================================ }
procedure search_event_cb(e: Plv_event); cdecl; procedure search_event_cb(e: Plv_event); cdecl;
var var
code : uint32; code : uint32;
rentry : PProgEntry;
begin begin
code := lv_event_get_code(e); code := lv_event_get_code(e);
@@ -618,9 +631,12 @@ begin
else if code = LV_EVENT_READY then begin else if code = LV_EVENT_READY then begin
{ Enter pressed (one-line textarea) — launch first result } { Enter pressed (one-line textarea) — launch first result }
if results_visible > 0 then begin if results_visible > 0 then begin
if result_prog[0] < prog_count then begin if (programs <> nil) and (result_prog[0] < LL_Size(programs)) then begin
clearAndClose; rentry := PProgEntry(LL_Get(programs, result_prog[0]));
programs[result_prog[0]].launch; if rentry <> nil then begin
clearAndClose;
rentry^.launch;
end;
end; end;
end; end;
end; end;
@@ -701,7 +717,6 @@ begin
tracer.push_trace('desktop.init.enter'); tracer.push_trace('desktop.init.enter');
sysinfo_win_id := 0; sysinfo_win_id := 0;
prog_count := 0;
results_open := false; results_open := false;
results_panel := nil; results_panel := nil;
anim_closing := false; anim_closing := false;
File diff suppressed because it is too large Load Diff
+203 -18
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -106,6 +106,10 @@ uses
volcmd, volcmd,
volumemanager, volumemanager,
vterminal, vterminal,
wasm,
wasm.vm.io,
wasm.test,
wasm.test.framework,
Windows, Windows,
XHCI; XHCI;
@@ -243,6 +247,10 @@ begin
vfs.init(); vfs.init();
storagetest.init; storagetest.init;
{ Let's test Wasuro! }
wasm.vm.io.io_set_writechar(@syslog.logchar);
wasm.wasm_init;
{ Management Interfaces } { Management Interfaces }
tracer.push_trace('kmain.STRMGMT'); tracer.push_trace('kmain.STRMGMT');
splash.update(10, 'Initializing storage management...'); splash.update(10, 'Initializing storage management...');
@@ -328,6 +336,7 @@ begin
cfifols.UnitTest; cfifols.UnitTest;
lifo.UnitTest; lifo.UnitTest;
circ.UnitTest; circ.UnitTest;
wasm.test.run_all_tests;
splash.update(90, 'Running test suite...'); splash.update(90, 'Running test suite...');
minh.UnitTest; minh.UnitTest;
maxh.UnitTest; maxh.UnitTest;
+83 -5
View File
@@ -18,7 +18,7 @@ interface
uses uses
lvgl, video, windows, desktop, keyboard, tracer, lvgl, video, windows, desktop, keyboard, tracer,
strings, util, lmemorymanager, asuro, stdio, vfs, strings, util, lmemorymanager, asuro, stdio, vfs,
processmanager, proctypes, lists, hashmap; processmanager, proctypes, lists, hashmap, filedispatch;
procedure init; procedure init;
@@ -445,6 +445,8 @@ var
lastParam : pchar; lastParam : pchar;
job : PBackgroundJob; job : PBackgroundJob;
slotPtr : ^uint32; slotPtr : ^uint32;
absPath : pchar;
dispatchPID : uint32;
begin begin
{ Null-terminate input } { Null-terminate input }
state^.line_buf[state^.line_len] := 0; state^.line_buf[state^.line_len] := 0;
@@ -693,10 +695,86 @@ begin
end; end;
end; end;
end else begin end else begin
appendText(state, 'Unknown command. Type HELP for a list.'); { No built-in command found — try file dispatch }
appendChar(state, #10); absPath := vfs.makeAbsolutePathFrom(params^.Param, state^.cwd);
stdio.freeParams(params); if absPath <> nil then begin
showPrompt(state); if is_bg then begin
{ Background file dispatch }
job := PBackgroundJob(kalloc(SizeOf(TBackgroundJob)));
if job <> nil then begin
memset(uint32(job), 0, SizeOf(TBackgroundJob));
job^.StdOut := stdio.createOutBuf(1024);
job^.StdErr := stdio.createOutBuf(1024);
job^.LastDrain := 0;
job^.LastDrainE := 0;
inc(state^.bg_next_job);
job^.JobNum := state^.bg_next_job;
pcount := stringSize(params^.Param);
if pcount > 31 then pcount := 31;
memcpy(uint32(params^.Param), uint32(@job^.CmdName[0]), pcount);
job^.CmdName[pcount] := #0;
dispatchPID := filedispatch.dispatch(absPath, params,
nil, job^.StdOut, job^.StdErr);
if dispatchPID > 0 then begin
job^.PID := dispatchPID;
if state^.bg_jobs = nil then
state^.bg_jobs := DL_New(SizeOf(uint32));
slotPtr := DL_Add(state^.bg_jobs);
slotPtr^ := uint32(job);
appendText(state, '[');
lastParam := intToString(job^.JobNum);
appendText(state, lastParam);
kfree(void(lastParam));
appendText(state, '] ');
lastParam := intToString(job^.PID);
appendText(state, lastParam);
kfree(void(lastParam));
appendChar(state, #10);
end else begin
appendText(state, 'Unknown command. Type HELP for a list.');
appendChar(state, #10);
stdio.freeOutBuf(job^.StdOut);
stdio.freeOutBuf(job^.StdErr);
kfree(void(job));
end;
end;
kfree(void(absPath));
stdio.freeParams(params);
showPrompt(state);
end else begin
{ Foreground file dispatch }
state^.fg_stdout := stdio.createOutBuf(1024);
state^.fg_stderr := stdio.createOutBuf(1024);
state^.fg_stdin := stdio.createOutBuf(0);
state^.last_drain := 0;
state^.last_drain_err := 0;
state^.fg_params := params;
dispatchPID := filedispatch.dispatch(absPath, params,
state^.fg_stdin, state^.fg_stdout, state^.fg_stderr);
kfree(void(absPath));
if dispatchPID > 0 then begin
state^.ForegroundPID := dispatchPID;
end else begin
appendText(state, 'Unknown command. Type HELP for a list.');
appendChar(state, #10);
stdio.freeOutBuf(state^.fg_stdout); state^.fg_stdout := nil;
stdio.freeOutBuf(state^.fg_stderr); state^.fg_stderr := nil;
stdio.freeOutBuf(state^.fg_stdin); state^.fg_stdin := nil;
stdio.freeParams(params);
state^.fg_params := nil;
state^.ForegroundPID := 0;
showPrompt(state);
end;
end;
end else begin
appendText(state, 'Unknown command. Type HELP for a list.');
appendChar(state, #10);
stdio.freeParams(params);
showPrompt(state);
end;
end; end;
end else begin end else begin
if params <> nil then stdio.freeParams(params); if params <> nil then stdio.freeParams(params);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+44
View File
@@ -0,0 +1,44 @@
{
Prog->WASM->WASMShim - Bridges the Asuro process model to WASURO VM.
Defines the shim record that ties a PProcessContext to a
PWASMProcessContext, plus fault classification types.
@author(Kieron Morris <[email protected]>)
}
unit wasmshim;
interface
uses
proctypes,
wasm.types.context;
type
{ Fault classification — detected Asuro-side after wasm_tick returns false }
TWASMFaultKind = (
wfNone, { No fault — normal completion }
wfInvalidBinary, { Parse or validation failure }
wfNoStartExport, { _start not found in exports }
wfUnexpectedHalt, { Running=false with no ExitCode, IP < Limit }
wfCodeOverrun, { IP ran past code limit }
wfUnknown { Catch-all }
);
{ The shim bridges Asuro process context to the WASURO VM context }
PProcessWASMShim = ^TProcessWASMShim;
TProcessWASMShim = record
ProcessCtx : PProcessContext; { The owning Asuro process }
WASMCtx : PWASMProcessContext; { The WASURO VM context }
FileBuffer : puint8; { kalloc'd buffer holding the .wasm file }
FileSize : uint32; { Size of the loaded file }
FaultKind : TWASMFaultKind; { Fault classification after execution }
FaultIP : uint32; { IP at which fault occurred }
ArgCount : uint32; { Number of command-line arguments }
ArgBuf : pchar; { Flat buffer of null-terminated arg strings }
ArgBufSize : uint32; { Total byte size of ArgBuf }
end;
implementation
end.
+18 -7
View File
@@ -23,9 +23,17 @@ unit progmanager;
interface interface
uses uses
tracer, tracer, stdio, processmanager,
//progs //progs
base64_prog, md5sum, dhclient, vbeinfo, testcmd, ping, meminfo, setres; base64_prog, md5sum, dhclient, vbeinfo, testcmd, ping, meminfo, setres,
//drivers
ramdrive, drivermanagement,
//network
ipv4, arp, tcp,
//dispatch
filedispatch,
//wasm
wasmrunner;
{ Initialize all baked-in programs } { Initialize all baked-in programs }
procedure init(); procedure init();
@@ -33,10 +41,9 @@ procedure init();
implementation implementation
uses uses
stdio, kernel,
//command provider units //command provider units
kernel, cpu, drivermanagement, processmanager, cpu,
arp, ipv4, tcp,
diskcmd, usbcore, diskutil, notepad, partcmd, volcmd; diskcmd, usbcore, diskutil, notepad, partcmd, volcmd;
procedure init(); procedure init();
@@ -60,8 +67,8 @@ begin
diskcmd.init(); diskcmd.init();
partcmd.init(); partcmd.init();
volcmd.init(); volcmd.init();
diskutil.init; diskutil.init();
notepad.init; notepad.init();
md5sum.init(); md5sum.init();
base64_prog.init(); base64_prog.init();
dhclient.init(); dhclient.init();
@@ -69,6 +76,10 @@ begin
testcmd.init(); testcmd.init();
ping.init(); ping.init();
meminfo.init(); meminfo.init();
{ File dispatch & WASM integration }
ramdrive.init();
filedispatch.init();
wasmrunner.init();
setres.init(); setres.init();
end; end;