Section 01 / kernel

Linux / wasm32

A new Linux architecture port for WebAssembly: conventional kernel subsystems above a wasm32 architecture layer, with compiler-native continuations wherever kernel control flow may suspend.

IDENTITY
Kernel
Linux
Machine
wasm32
Byte order
little-endian
Page size
4 KiB
Syscall IDs
x86_64 numbering
Modules
built-in only
01.01 / SCOPE

A port, not an emulation layer

The system compiles Linux itself to WebAssembly. Architecture-owned code lives under arch/wasm32/; generic Linux code continues to supply VFS, the scheduler, task and signal semantics, networking, terminals, filesystems, and device models. Browser-specific behavior enters through architecture hooks and normal Linux drivers.

Compiler substrate: there is no Emscripten, Binaryen, Asyncify, or JSPI in the build. A pinned LLVM fork and the HwjsCoroutinize pass transform explicitly published suspension paths.

Kernel boundary
Only the wasm32 architecture layer and final-link machinery are port-specific.
Userspace
musl and glibc overlays provide the ABI; bash, coreutils, and BusyBox remain ordinary sources.
Host boundary
HardwareJS supplies hardware-facing WebAssembly imports. It does not replace Linux subsystems.

SOURCE MAP · arch/wasm32 · wasm architecture documentation

01.02 / MODULE & MEMORY

One kernel memory, separate process memories

Current vmlinux.wasm is a single-memory module and imports one shared WebAssembly.Memory as env.kernel_memory. Physical addresses and kernel virtual addresses are offsets into that memory. Linux still maintains its page allocator and software page tables, but WebAssembly supplies no hardware MMU.

env.kernel_memory · shared Linux image · allocator · page cache · task state · device rings
raw_copy_to_user raw_copy_from_user raw_clear_user
env.user_memory · per address space · shared ELF image · heap · user stack · dynamic objects

A process Worker owns the current user address space. Three host-serviced leaf imports bridge Linux uaccess operations to that Worker's user memory. Each validates bounds and follows the Linux convention of returning bytes not copied. A kernel-thread binding has no user address space and rejects these operations loudly.

The kernel uses 4 KiB pages and a flat, single ZONE_NORMAL. Shared memory growth and maximums are fixed as part of module instantiation; host and guest must agree before boot.

Historical note: earlier designs linked kernel and user memory into the kernel module simultaneously. The current single-memory kernel avoids that multi-memory dependency and keeps user access behind explicit imports.

SOURCE MAP · uaccess.h · arch/wasm32/mm · memory model

01.03 / BOOT ABI

HardwareJS is the bootloader

The host writes a packed, 96-byte boot_info version 3 record into kernel memory and calls the kernel entry point. Magic, version, size, and every bounded range are validated before the port trusts the record.

Field groupPurposeKernel use
IdentityMagic, ABI version, structure sizeReject incompatible bootloaders
RAMPhysical base and byte lengthInitialize the flat memory map
Command linePointer and lengthPopulate Linux boot parameters
CPU topologyVirtual CPU count and boot CPUBring the wasm32 SMP topology online
ClockTimer frequency and epoch dataInitialize timekeeping and timer ticks
InitrdOptional rangeMount the initial userspace image
EntropySeed bytesInitialize the kernel random pool
HOSTCompile moduleValidate required wasm features
ALLOCATEShared RAMCreate env.kernel_memory
DESCRIBEboot_info v3Write ranges and topology
ENTERwasm_startArchitecture setup
LINUXstart_kernelGeneric kernel initialization

SOURCE MAP · bootinfo.h · head.c · setup.c

01.04 / CPU & PROCESS TOPOLOGY

A Worker is a CPU and an address-space host

CPU 0 runs in a dedicated kernel Worker and is where kernel threads execute. A configured pool of process Workers supplies the remaining virtual CPUs. Each pool Worker owns one current user-memory binding and may host multiple resident tasks serially as HardwareJS schedules runnable continuations.

cpu=N
N process pool Workers plus the dedicated CPU 0 kernel Worker.
Address space
One shared user memory associated with the process Worker’s current task binding.
Kernel image
Each Worker has a WebAssembly instance of the kernel module against the same shared kernel memory.
Maximum
The kernel is configured for 129 CPUs: CPU 0 plus as many as 128 pool CPUs.

Kernel data is shared; JavaScript call stacks are not. Entry, wakeup, parking, and cross-Worker coordination therefore use explicit shared state and Atomics. The topology is cooperative rather than pretending the browser can migrate a live JavaScript stack.

SOURCE MAP · smp.c · host vCPU policy

01.05 / SYSCALL ABI

A strict six-argument trap

User programs import linux.syscall. HardwareJS routes that trap into the current process Worker’s kernel instance and calls the exported syscall entry with a number plus six machine-word arguments. Linux uses the x86_64 syscall numbering scheme, which gives libc and generated tables one stable vocabulary.

long linux.syscall(
    long nr,
    long a0, long a1, long a2,
    long a3, long a4, long a5
);

WebAssembly indirect calls require exact signatures, so generated wrappers normalize every implementation to the same form. The kernel dispatches only entries present in its generated table; absent or unsupported calls fail as -ENOSYS. Pointer arguments are user offsets and must pass through Linux access checks and the user-copy leaves.

Blocking result: a suspendable syscall may return the internal -517 park/restart token to HardwareJS. It is scheduler protocol, not an errno exposed to a correctly integrated userspace.

SOURCE MAP · syscall_table.c · entry.S · syscall.h

01.06 / SUSPENSION & SCHEDULING

Blocking paths become resumable continuations

Ordinary C control flow cannot survive a return to the browser event loop. The compiler pass transforms functions reachable from published suspension points into LLVM-coroutine state machines. A parked call retains its frames in a bounded arena; HardwareJS can later invoke its continuation on the owning Worker.

MechanismMeaningEnforcement
HWJS_SUSPENDSPublishes that a function may reach a suspension pointCall-graph publication tools and compiler pass
HWJS_NATIVEMarks a deliberately non-suspending armLocal declaration and provenance checks
Park tokenTransfers control from a blocking syscall to the host schedulerSlot witness and generation validation
Continuation arenaStores the transformed frame tree outside the JS stackCapacity bounds and arena census
Wake wordSignals work or an interrupt across WorkersShared Atomics state

The default userspace behavior parks blocking waits. A kernel-command-line wasm_user_pin fallback instead keeps a task pinned to its Worker; current glibc images require that compatibility mode because their fast syscall path does not decode the internal park token.

SOURCE MAP · hwjs-cc · continuation semantics · toolchain specification

01.07 / PROCESS LIFECYCLE

Fork copies a continuation; exec replaces the image

A process is more than user-memory bytes: it also has a suspended kernel continuation, Worker ownership, Linux task state, signal state, and host bindings. Fork captures that complete boundary. The child receives copied user memory and a forked continuation whose memory delta is zero; Linux supplies the expected parent and child return values.

Exec remains kernel-routed and runs in place: the same pid and Worker replace their user image, stack, and dynamic-loader records. ELF parsing, argument/environment construction, and credentials remain Linux responsibilities. HardwareJS prepares and instantiates the resulting WebAssembly program only through explicit loader contracts.

ELFLinux binfmtValidate wasm32 executable
LOADMap imageBuild user memory and stack
RUNWorkerEnter program continuation
FORKClone stateMemory plus continuation
EXECReplace imageKeep pid and Worker

Dynamic objects are tracked by runtime loader records, allowing dlopen and symbol resolution while preserving the same address-space and syscall contracts.

SOURCE MAP · process.c · binfmt_wasm.c

01.08 / SIGNALS

Linux semantics, Worker delivery

Linux decides which signal is pending, blocked, ignored, fatal, or delivered to a handler. HardwareJS provides the cross-Worker transport. Shared signal rings and Atomics wake the Worker that owns the target task; the wasm32 architecture builds and restores the userspace signal frame.

Generation
Kernel code records signal state using normal Linux paths.
Notification
A shared ring identifies the task/Worker work that must run.
Delivery
The owning Worker enters its kernel instance and prepares the user handler frame.
Return
rt_sigreturn restores the saved wasm32 user context.

SOURCE MAP · signal.c · signal-ring.ts

01.09 / TOOLCHAIN & ARTIFACTS

One pinned compiler lineage

The kernel is compiled by a pinned clang/LLVM fork with the checked-out HwjsCoroutinize plugin. Kbuild calls clang directly; userspace uses the wasmcc and wasmld drivers. The final kernel artifact is vmlinux.wasm.

StageOwnerInvariant
CompilePinned clang/LLVMwasm32 object code with suspension annotations intact
TransformHwjsCoroutinizeOnly published suspension closure is transformed
LinkKernel final-link scriptsImports, exports, memory limits, and witnesses are checked
AttestPlugin provenance gatePlugin source SHA equals the clean checked-out submodule SHA
Boot preflightHardwareJSShared memory, threads, bulk memory, and module shape must match

The plugin is built from the submodule automatically and stamped with its exact source revision. Stale, dirty, or unattested plugins are refused rather than silently reused.

SOURCE MAP · toolchain environment · plugin build entry

01.10 / CONFIGURED KERNEL SURFACE

Normal drivers at a browser-shaped machine edge

The reference kernel is SMP-enabled with PREEMPT_NONE and a 100 Hz tick. It includes the generic subsystems needed by the shipped machines and wasm32 drivers for HardwareJS device boundaries.

AreaEnabled surfaceHardwareJS boundary
Filesystemstmpfs, devtmpfs, proc, sysfs, FUSE, virtio-fsBrowser-backed FUSE request server
Consolevirtio console, PTYs, N_TTYShared terminal rings and browser streams
DisplayDRM, fbdev, input, virtio-inputShared scanout, Canvas, keyboard and pointer
Networkvirtio-net, IPv4/IPv6 stack, Unix socketsPacket transport, fetch shim, optional WebSocket uplink
USBwasm virtual HCD, USB core, FTDI USB serialWebUSB URB forwarding
Serialwasm serial TTYWeb Serial byte transport
IsolationUTS and IPC namespacesEntirely kernel-owned

SOURCE MAP · wasm32 defconfig · wasm32 drivers

01.11 / CURRENT BOUNDARIES

Deliberate omissions and browser constraints

The reference configuration does not enable loadable kernel modules, swap, block devices, cgroups, audit, PID/user/network namespaces, KASAN, lockdep, or ftrace. Audio is not part of the current published device surface. These are explicit scope choices, not browser-side reimplementations.

WEB-01

Cross-origin isolation

Shared WebAssembly memory requires a secure, cross-origin-isolated browser context.

WEB-02

Permissioned devices

WebUSB and Web Serial require user action and remain subject to browser policy.

ABI-01

glibc compatibility

Current glibc images use wasm_user_pin for blocking syscall compatibility.

For the matching host behavior, continue to Section 02: HardwareJS. For the exact seam between them, use the boundary contract reference.