WEMU

The machine. Launched from a wemu-system-aarch64 command line, driven through QMP and the human monitor, exactly like a QEMU process.

WEMU is Warm64's one public interface. You launch it with the same command line you would give qemu-system-aarch64, and control it with QEMU's own protocol (QMP) and monitor (HMP). There is no separate options object: the command line is the machine's configuration.

import { WEMU } from "warm64";
import { nodeWEMUHost } from "warm64/wemu-node";

const vm = await WEMU.launch(
  "-M virt -m 1G -S -display none -kernel vmlinuz-virt -initrd initramfs-virt -append console=ttyAMA0",
  nodeWEMUHost(),
);
vm.serial[0].onData((bytes) => process.stdout.write(bytes));
await vm.qmp("cont");
const { code } = await vm.wait();

Static methods

WEMU.launch()

Builds a machine from a command line and starts it.

WEMU.launch(args, host): Promise<WEMU>

Parameters

ParameterTypeDescription
argsstring, string[] or WEMUConfigA wemu-system-aarch64 command line: one string split like a POSIX shell would (a leading qemu-system-aarch64 or wemu-system-aarch64 is dropped), an argv array, or a config already parsed by parseWEMUArgs(). See the command-line reference.
hostWEMUHostWhat the platform lends the machine: its WebAssembly modules, files, disks, sockets, display and sound. Use nodeWEMUHost() in Node or browserWEMUHost() in a browser.

Return value

A promise that resolves to the running (or, with -S, paused) WEMU once the machine is built.

Exceptions

  • WEMUError when the command line is refused, in QEMU's wording: an unknown option, something this machine doesn't have (KVM, VNC, -dtb, more than 8 CPUs), or nothing to boot (no -kernel, -bios or -pflash).
  • WEMUError carrying the help or version text for -help and -version.
  • A plain Error when a file can't be read (for example "<url>: 404 Not Found" in a browser) or the kernel image isn't one it can boot.

If anything fails after the machine is built, launch() releases it before rethrowing.

Tip Unless the command line has -S, the machine starts running inside launch(), before your code can attach listeners, so its first console output is lost. Launch with -S, attach onData, then await vm.qmp("cont").

Instance properties

PropertyTypeDescription
configWEMUConfigThe parsed command line the machine was built from.
hostWEMUHostThe host it was launched with.
serialWEMUChardevPort[]The serial ports. serial[0] is the guest's ttyAMA0 console and always exists; serial[1] exists with a second -serial.
chardevsMap<string, WEMUChardevPort>Every character device by id: serial0, serial1 and each -chardev.
sharesMap<string, WEMUShare>Shared folders by mount tag. See WEMUShare.
statusWEMURunStateThe run state, as query-status reports it (read-only). See Run states.

Instance methods

qmp()

Runs a QMP command and resolves with its return value.

vm.qmp(command, args = {}): Promise<unknown>

It rejects with a QmpError whose class and desc are what QEMU would send (GenericError, CommandNotFound, DeviceNotFound…). The commands are listed in the QMP reference.

const { status } = await vm.qmp("query-status");      // "running"
await vm.qmp("screendump", { filename: "shot.png", format: "png" });

qmpJson()

Runs one QMP message given as JSON text and resolves with the reply as JSON text. It never rejects: errors come back as QMP error replies. Use it to bridge a QMP client you already have.

const reply = await vm.qmpJson('{"execute":"query-status"}');
// the reply as JSON text: {"return": {"status": "running", ...}}

hmp()

Runs a human monitor command and resolves with the text it prints.

console.log(await vm.hmp("info block"));
await vm.hmp("savevm before-upgrade");

It rejects with QmpError("GenericError", …) for an unknown command or bad arguments, for example unknown command: 'x'.

on()

Listens for a QMP event by name, or for every event with "*". Returns a function that removes the listener. Listeners are called synchronously, with a QmpEvent: { event, data?, timestamp: { seconds, microseconds } }.

const off = vm.on("SHUTDOWN", (e) => console.log(e.data.reason));
vm.on("*", (e) => console.log(e.event));

See Events for what is emitted.

wait()

Resolves with { code } when the machine exits: on quit, or when the guest powers off (unless -no-shutdown keeps it). The code is what the wemu-system-aarch64 process would exit with: 0 normally, 1 after a panic with -action panic=shutdown.

setUIInfo()

Offers the guest a display size, the way resizing a QEMU window does. Returns false without a virtio-gpu device or when the size is refused.

vm.setUIInfo({ width: 1280, height: 800 });

quit()

Stops the machine and releases it. Emits SHUTDOWN with reason: "host-qmp-quit" and resolves wait() with { code: 0 }. Calling it again does nothing.

Run states

status and query-status report QEMU's run states:

StateWhen
runningRunning.
prelaunchLaunched with -S and not yet continued.
pausedStopped with stop.
inmigrateLaunched with -incoming defer, waiting for migrate-incoming.
postmigrateAfter migrate to a file.
guest-panickedThe guest panicked with -action panic=pause (the default).
shutdownThe guest powered off with -no-shutdown. cont is refused until the machine is reset.

Events

EventDataWhen
STOPnoneThe machine paused: stop, or a guest panic.
RESUMEnoneIt continued.
RESET{ guest, reason }system_reset, or a guest reboot.
POWERDOWNnonesystem_powerdown pressed the power button.
SHUTDOWN{ guest, reason }The guest powered off (guest-shutdown), rebooted with -no-reboot (guest-reset), or the host quit (host-qmp-quit).
GUEST_PANICKED{ action, info }The guest kernel panicked.
MIGRATION{ status }A migrate or migrate-incoming changed state.
JOB_STATUS_CHANGE{ id, status }A snapshot-save, snapshot-load or snapshot-delete job moved on.
DUMP_COMPLETEDnonedump-guest-memory finished.

WEMUChardevPort

A serial port or -chardev: a stream of bytes to and from the guest.

MemberDescription
idThe character device's id, for example serial0.
onData(listener)Calls listener(bytes: Uint8Array) with what the guest writes. The bytes are a fresh copy you may keep. Returns a function that removes the listener.
write(data)Sends a Uint8Array, or a string as UTF-8, to the guest.
sendBreak()Sends a serial break (only on serial0).
hasRingtrue for a ringbuf chardev, which QMP's ringbuf-read and ringbuf-write reach.
vm.serial[0].onData((bytes) => process.stdout.write(bytes));
vm.serial[0].write("uname -a\n");

WEMUShare

A shared folder the guest mounts with virtio-fs or 9p (see Shared folders). Its tree lives in memory: put files in before or while the guest runs, and read back what the guest wrote.

MethodDescription
put(path, data, mode = 0o644)Writes a file, creating its parent directories. Throws on failure.
get(path)The file's bytes, or null when it's absent or a directory.
list(path = "/")The directory's entries (subdirectories end with /), or null when it isn't a directory.
mkdir(path)Creates a directory.
remove(path)Removes a file or an empty directory.
vm.shares.get("host").put("input/config.json", new TextEncoder().encode("{}"));

See also