Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Linux 0.11 Web — Hybrid WebAssembly Port

The preserved Linux 0.11 kernel — scheduler, 72-entry system call table, VFS, MINIX filesystem, buffer cache, TTY line discipline, pipes, signals, fork/exec/exit/wait — compiled to a single WebAssembly module and running in the browser with a VT100-style terminal. No fake kernel, no simulated Linux in JavaScript: the C sources of the original kernel are compiled (unmodified where possible) by Emscripten into linux011.wasm.

browser (index.html + terminal.js)
   │  boot / keyboard / pump / screen cells
   ▼
linux011.wasm  ──  preserved kernel (linux011/) + Web platform layer (arch/web/)
   │  MINIX root filesystem image (disk.img, built by scripts/mkrootfs.py)
   ▼
init → /bin/sh → ls, cat, echo, ps, uname, mkdir, rm, cp, mv, hello, clear

What works today

Verified by tests/smoke.js (Node, drives the real module end to end — ALL 11 CHECKS PASS, three consecutive deterministic runs), plus a live-browser session (Chromium via CDP): boot, typing, fork/exec and echo web > /tmp/w + cat /tmp/w round-trip.

Check Status
Kernel boots, banner renders (console con_write path) PASS
mount_root() on a genuine MINIX v1 ramdisk image PASS
init runs, rc shell + login shell respawn cycle works PASS
echo — fork → exec → tty write → exit → wait PASS
ls (directory read through the preserved namei/iget) PASS
cat /etc/motd (file read via the buffer cache) PASS
uname -a (0.11's own placeholder strings) PASS
echo data123 > /tmp/f1 — redirect, O_CREAT, read-back PASS
cp (create + copy through the buffer cache) PASS
rm (unlink) + read-back failure check PASS
ps (walks the real task table) PASS
mkdir + cd + pwd PASS

Boot messages are real kernel output, e.g.:

Linux version 0.11-Web (Hybrid WebAssembly Port)
1362/1440 free blocks
40/64 free inodes
247 buffers = 252928 bytes buffer space
Free mem: 9011200 bytes

Fixed this iteration (was: the respawn-cycle blocker)

The "init respawns shells that exec /bin/#" cycle had four stacked root causes, all fixed and verified:

  1. wasm-ld signature-mismatch trap stubs. The karottc source tree declares do_exit as volatile void in two headers but defines it as int — harmless on i386 gcc, but wasm-ld wired every call from the void-declaring translation units to an unreachable stub. Aligned all declarations (sched.h, signal.c, panic.c's sys_sync, sys_signal arity).
  2. Positional asyncify rewind alignment. Binaryen's asyncify lowering restores frames positionally: the JS export that re-enters a suspended fiber must be the function whose frame is the spill's outermost one. Every task fiber is now born through dynCall_vi (which IS instrumented and sits above web_fiber_resume), so the cascade realigns exactly.
  3. Fork mark moved off globals. The parent/child discriminator lived in a global; a delayed parent resume (cooperative scheduling!) could run after a second fork overwrote it, inverting the fork-return values and re-running init. The mark is now a local in web_fork_prepare's frame, which the fork-copied continuation carries for free.
  4. create_tables argv layout. 0.11 stages [argc][argv-ptr][envp-ptr] then the arrays; task_entry treated the argv-ptr slot as the array, so every exec'd program read pointer bytes as its first argument.

Also fixed: MINIX ./.. directory entries (relative paths), spare inodes for O_CREAT, inode-0 bitmap reservation, tty_read probe removal.

Fixed in the current iteration

  1. The -ENOSPC create bug (root cause found and fixed). patch_kernel.py had translated super.c's local set_bit — which is the x86 bt instruction, a bit TEST that does not write memory — with test-and-SET semantics copied from fs/bitmap.c's btsl. mount_root's free-space counting loops scan every inode and zone bit through that macro, so the kernel poisoned its own bitmap caches at mount: the banner printed "40/64 free inodes" while the bitmap in the buffer cache had every bit set. The first new_inode then found no free bit and every create failed with -ENOSPC. The translation now preserves the bt semantics (read-only). O_CREAT, creat, mkdir, cp, rm and redirection all pass. Diagnosis trail in PORTING_NOTES.md (iteration 4 addendum).
  2. Browser input freeze (the 0.11 ring-0 busy-wait). tty_read's partial-line wait busy-spins at ring 0 on a PC until the next keyboard interrupt delivers a newline. A wasm call stack cannot be interrupted, so the identical code froze the browser event loop — the very thing that delivers keystrokes. In exactly the busy-wait case the task now yields to the host event loop (same semantics, event-paced). Found by CDP-pausing the wedged page (tty_read ← rw_char ← sys_read ← sh_main), fixed in sleep_if_empty via patch_kernel.py.
  3. index.html loads linux011.js — the glue script defining the Linux011 factory was never loaded; the boot could never start.
  4. /etc/rc is empty — the shell has no comment syntax; the previous sample text executed as a boot-time command.
  5. sh reports redirect-open failures instead of exiting silently.

Known limitations (honest status)

See PORTING_NOTES.md §6 for the full list. Highlights: custom signal handlers fall back to the default action (v1); no demand paging (flat memory, honest page-table bookkeeping); single interactive console; ls -l/pipes/job control are v1-userland gaps (the 72-syscall surface is complete); WEB_TRACE=1 debug builds may stall the boot. The dbgopen diagnostic command on the rootfs prints raw syscall returns for open/creat/mkdir — it is the regression probe that caught bug #1.

Layout

Path Contents
linux011/ pristine Linux 0.11 sources (vendored, never modified)
arch/web/ Web platform layer (HAL): fibers, syscall entry, console, disk
arch/web/include/ overlay headers shadowing x86-specific kernel headers
scripts/ gen_overlays.py, patch_kernel.py, gen_syswrappers.py, mkrootfs.py
user/ user programs compiled into the module (sh, ls, cat, …)
frontend/ index.html, terminal.js, terminal.css (plain JS, no framework)
tests/ smoke.js end-to-end test, debug-boot.js boot diagnostics
build/web/ build output: linux011.wasm, linux011.js, disk.img, frontend

Running

./build-web.sh                 # needs emsdk; see BUILDING.md
cd build/web && python3 -m http.server 8000
# open http://localhost:8000 — click the terminal, type commands

For the headless smoke test:

node tests/smoke.js

About

Linux 0.11 ported to WebAssembly

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages