Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion mcpp.toml
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
39 changes: 26 additions & 13 deletions tools/vm-run.py
Original file line number Diff line number Diff line change
@@ -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 <disk.qcow2> [--file <path>]... -- <program> [arguments...]
vm-run.py [--image <disk.qcow2>] [--file <path>]... -- <program> [arguments...]

The machine is booted under qemu/KVM from <disk> (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 <disk>, or OKF_VM_IMAGE where --image is absent. The machine is
booted under qemu from a temporary overlay of <disk> (qemu-img; nothing is
written to <disk> 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
Expand All @@ -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)
Expand All @@ -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:
Expand All @@ -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)
Expand Down Expand Up @@ -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):
Expand Down
5 changes: 3 additions & 2 deletions tools/vm-runner
Original file line number Diff line number Diff line change
@@ -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}" -- "$@"
Loading