diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d2e0a78 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +## 0.1.1 + +- `tools/vm-run.py` boots each run from a temporary overlay of the image + (qemu-img), so the image is never written to and two runs at once do not + meet; the overlay is discarded when the machine is shut down. The image is + `--image` or `OKF_VM_IMAGE`, and KVM is used where the host has it. +- `tools/vm-runner` is a stable entry a runner declaration can name: + `[target.x86_64-freebsd] runner = [".../tools/vm-runner", "{}"]` (README, + "The runner declaration"). + +## 0.1.0 + +- openkal for FreeBSD, on the kernel's system-call interface: fourteen + interfaces of openkal 0.25, hosted and standalone. diff --git a/README.md b/README.md index 15e3a1e..e97fdfb 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ written on the kernel's own system-call interface. ```toml [target.'cfg(os = "freebsd")'.dependencies] -openkal-freebsd = "0.1.0" +openkal-freebsd = "0.1.1" ``` ## What it is @@ -108,7 +108,30 @@ runs above openkal-musl on the `*-freebsd-musl` rows. - `tools/vm-run.py` runs a program in a local FreeBSD VM image for development (`OKF_VM_IMAGE`, `tools/vm-runner`). It forwards `OPENKAL_*`, `KAL_*` and the mtest protocol's variables, so the suite can report through mtest once the - runner's listing pass honours `--run-under` (mtest 0.6.0). + runner's listing pass honours `--run-under` (mtest 0.6.0). Each run boots a + temporary overlay of the image, so the image is never written to and two runs + at once do not meet. + +### The runner declaration + +A program built for FreeBSD on another host is run in a virtual machine by +`tools/vm-runner`, which a runner declaration names: + +```toml +[target.x86_64-freebsd] +runner = ["/path/to/openkal-freebsd/tools/vm-runner", "{}"] +``` + +The declaration's `{}` is the built artefact. `vm-runner` boots the FreeBSD +image that `OKF_VM_IMAGE` names under `qemu-system-x86_64` (KVM where the host +has it, the software machine otherwise), offers the artefact on a read-only +drive, runs it as root on the serial console, writes what it writes to the +standard output and error, answers with its exit status, and shuts the machine +down; the temporary overlay it booted from is discarded. The disk is the +FreeBSD 14.5-RELEASE boot disk of the `xim` package `freebsd-disk` (The +FreeBSD Project's release, installed from the official installer disc; +published separately), and `qemu-system-x86_64` with `qemu-img` are the +host's dependencies. The targets need mcpp 2026.11.10.1 or later. diff --git a/mcpp.toml b/mcpp.toml index f225bc1..d7cad68 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-freebsd" -version = "0.1.0" +version = "0.1.1" description = "An implementation of openkal for FreeBSD, written on the kernel's own system-call interface." license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/tools/vm-run.py b/tools/vm-run.py index c506fcd..2ef5df6 100755 --- a/tools/vm-run.py +++ b/tools/vm-run.py @@ -1,14 +1,16 @@ #!/usr/bin/env python3 """Runs a program in a FreeBSD virtual machine and answers as the program did. - vm-run.py --image [--file ]... -- [arguments...] + vm-run.py [--image ] [--file ]... -- [arguments...] -The machine is booted under qemu/KVM from (an overlay of FreeBSD's own -VM image; nothing is written to its backing file), the files are offered to it -on a read-only FAT drive and copied to /tmp/run, and the program is run there as -root on the serial console. What it writes is written here, and its status is -this program's status. Every argument is passed through single quotes, so the -command line is the one given. +The image is , or OKF_VM_IMAGE where --image is absent. The machine is +booted under qemu from a temporary overlay of (qemu-img; nothing is +written to itself, and two runs at once do not meet), the files are +offered to it on a read-only FAT drive and copied to /tmp/run, and the program +is run there as root on the serial console. What it writes is written here, and +its status is this program's status. Every argument is passed through single +quotes, so the command line is the one given. KVM is used where the host has it +(a writable /dev/kvm) and the software machine otherwise. THE PROGRAM'S ENVIRONMENT. Variables named OPENKAL_* and KAL_* are passed to the program. Where a test runner speaks the mtest protocol to it (MTEST_PROTOCOL @@ -17,14 +19,15 @@ (a file, offered to the machine) are passed as well. It is how this repository's conformance suite is run on FreeBSD from a Linux -host: the suite's runner (OPENKAL_CONFORMANCE_RUNNER). The -image's kernel console is moved to the serial line at the loader prompt, as the -official image boots to the video console. +host: the suite's runner (OPENKAL_CONFORMANCE_RUNNER), which a consumer states +as a runner declaration naming tools/vm-runner. The image's kernel console is +moved to the serial line at the loader prompt, as the official image boots to +the video console. """ import argparse, os, pty, re, select, shutil, subprocess, sys, tempfile, time ap = argparse.ArgumentParser() -ap.add_argument("--image", required=True) +ap.add_argument("--image", default=os.environ.get("OKF_VM_IMAGE")) ap.add_argument("--file", action="append", default=[]) ap.add_argument("--memory", default="2048") ap.add_argument("--timeout", type=int, default=1800) @@ -33,8 +36,11 @@ command = args.command[1:] if args.command[:1] == ["--"] else args.command if not command: ap.error("no program to run") +if not args.image: + ap.error("no image: --image or OKF_VM_IMAGE") share = tempfile.mkdtemp(prefix="vm-run-") +overlay = tempfile.mkdtemp(prefix="vm-run-overlay-") forward = {k: v for k, v in os.environ.items() if k.startswith("OPENKAL_") or k.startswith("KAL_")} report = os.environ.get("MTEST_REPORT") if os.environ.get("MTEST_PROTOCOL") else None if report: @@ -57,10 +63,16 @@ def quote(a): line = " ".join(["env"] + [quote(f"{k}={v}") for k, v in sorted(forward.items())] + [quote(program)] + [quote(a) for a in command[1:]]) -argv = ["qemu-system-x86_64", "-enable-kvm", "-m", args.memory, "-smp", "2", "-nographic", - "-drive", f"file={args.image},if=virtio,format=qcow2", +overlay_disk = os.path.join(overlay, "run.qcow2") +subprocess.run(["qemu-img", "create", "-f", "qcow2", "-b", os.path.abspath(args.image), + "-F", "qcow2", overlay_disk], check=True, + stdout=subprocess.DEVNULL) +argv = ["qemu-system-x86_64", "-m", args.memory, "-smp", "2", "-nographic", + "-drive", f"file={overlay_disk},if=virtio,format=qcow2", "-drive", f"file=fat:{share},if=virtio,format=raw,readonly=on", "-nic", "none"] +if os.access("/dev/kvm", os.W_OK): + argv.insert(1, "-enable-kvm") pid, fd = pty.fork() if pid == 0: os.execvp(argv[0], argv) @@ -94,6 +106,7 @@ def stop(status): except OSError: pass shutil.rmtree(share, ignore_errors=True) + shutil.rmtree(overlay, ignore_errors=True) sys.exit(status) if read_until(rb"Autoboot in", 180): diff --git a/tools/vm-runner b/tools/vm-runner index 8118188..7493130 100755 --- a/tools/vm-runner +++ b/tools/vm-runner @@ -1,4 +1,5 @@ #!/bin/sh -# The conformance suite's runner on a Linux host: the program, run in the -# FreeBSD virtual machine whose disk OKF_VM_IMAGE names (tools/vm-run.py). +# The runner a runner declaration names on a Linux host: the program, run in +# the FreeBSD virtual machine whose disk OKF_VM_IMAGE names (tools/vm-run.py). +# The declaration's payload is the first argument, as with any command. exec python3 "$(dirname "$0")/vm-run.py" --image "${OKF_VM_IMAGE:?the FreeBSD disk image}" -- "$@"