kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

Windows Targets

kit's Windows targets are PE/COFF, 64-bit only:

The hosted profile is MinGW-w64 UCRT via llvm-mingw. kit uses the target headers, CRT objects, and import libraries from that sysroot; it does not use llvm-mingw's compiler, assembler, or linker tools for the cross-compile path.

UCRT Sysroots

The pinned sysroot source is mstorsjo/llvm-mingw release 20260602.

Provision both target sysroots:

scripts/llvm_mingw_sysroot.sh prepare all

Downloads go under ${XDG_CACHE_HOME:-$HOME/.cache}/kit/llvm-mingw/20260602 by default. Extracted target sysroots live under:

build/llvm-mingw/20260602/ucrt/x86_64-w64-mingw32
build/llvm-mingw/20260602/ucrt/aarch64-w64-mingw32

Only each target's include/ and lib/ directories are extracted.

Direct compile examples:

build/kit cc -target x86_64-windows \
  --sysroot "$(scripts/llvm_mingw_sysroot.sh path x64)" \
  test.c -o test-x64.exe

build/kit cc -target aarch64-windows \
  --sysroot "$(scripts/llvm_mingw_sysroot.sh path aarch64)" \
  test.c -o test-arm64.exe

The opt-in test target provisions the sysroots and runs the hosted PE/COFF smokes for both architectures:

make test-coff-windows-ucrt

make test-coff remains self-skipping when no UCRT sysroot is present, so the default suite does not download release archives.

Windows VMs

Execution uses a Windows 11 ARM64 VM under QEMU + Hypervisor.framework (hvf, -cpu host) on Apple Silicon. A single ARM64 VM serves both targets: aarch64-windows binaries run natively, and x86_64-windows binaries run through Windows' in-box x64 emulator. There is no hardware acceleration for x86_64 on Apple Silicon, so a dedicated x64 VM would need slow TCG software emulation; the ARM64 VM with x64 emulation is used instead. So run x64 and run aarch64 target the same VM.

Everything is driven by scripts/windows_vm.sh (modeled on scripts/freebsd_vm.sh). The unattended-install answer file and guest bootstrap live in scripts/win/ (autounattend.xml.in, bootstrap.ps1.in).

scripts/windows_vm.sh with no arguments (or help) prints the full command list. scripts/windows_vm.sh doctor prints host tools, firmware, media, the golden-disk status, and whether a VM is running — run it first if anything looks off.

Layout

One-time install (produces the golden disk)

You supply the Windows 11 ARM64 install ISO (Microsoft ISOs can't be redistributed or pinned). Point the script at it or drop it in the cache:

export KIT_WINDOWS_ISO=/path/to/Win11_<ver>_Arm64.iso
# or, auto-discovered:  cp Win11_<ver>_Arm64.iso "${XDG_CACHE_HOME:-$HOME/.cache}/kit/"

The virtio-win NIC driver and a Win32-OpenSSH ARM64 build are fetched + checksum-pinned automatically and installed into the guest offline, so setup needs no Windows Update. The generated autounattend.xml seed bypasses the TPM 2.0 / Secure Boot / RAM / CPU checks (no swtpm), installs to an NVMe disk (in-box driver — no "load driver" step), creates a local kit admin account, skips the OOBE Microsoft-account flow, and on first logon installs the NetKVM driver + OpenSSH and authorizes the host's generated key.

Pick one of:

# Automated, hands-off. Applies the image, finishes first boot + bootstrap,
# then cleanly shuts down and caches the golden disk.
scripts/windows_vm.sh install

# Interactive. Opens a QEMU window so you can drive Setup (press a key at the
# "Press any key to boot from CD" prompt and watch). When Setup hits ~100% and
# reboots, close the window, then run `firstboot` to finish + cache the golden.
scripts/windows_vm.sh console-install
scripts/windows_vm.sh firstboot

Progress screenshots land in build/windows-vm/screenshots/ (note: the display freezes on a boot frame once Windows fully starts — Windows has no ramfb driver — so a frozen screen is normal; SSH readiness is the real signal). The install takes a while under hvf and reboots several times.

When it finishes, the working disk is banked as the golden disk and you never have to reinstall.

Daily use: start, run, stop

scripts/windows_vm.sh boot                       # boot the installed VM headless (fast)
scripts/windows_vm.sh wait-ssh                   # wait until SSH answers, print guest info
scripts/windows_vm.sh run x64 build/probe-x64.exe arg1 arg2
scripts/windows_vm.sh run aarch64 build/probe-arm64.exe
scripts/windows_vm.sh ssh [cmd...]               # shell / one-off command in the VM
scripts/windows_vm.sh screenshot [name]          # capture the framebuffer (debug)
scripts/windows_vm.sh stop                       # clean ACPI power down

run copies the executable to a temp dir on the guest via scp (Win32-OpenSSH's sftp subsystem), runs it through PowerShell, returns the guest exit code, and removes the temp dir unless KIT_WINDOWS_VM_KEEP=1. console opens a windowed session on the installed VM for manual inspection.

Golden disk lifecycle

The long install runs once; the result is cached as a compressed golden disk in the durable cache. The working disk (alongside it in the cache) is restored from it cheaply.

scripts/windows_vm.sh snapshot   # cache the current (stopped) disk as golden
scripts/windows_vm.sh reset      # restore the working disk from golden (discard changes)
scripts/windows_vm.sh prepare    # restore golden if cached, else stage a fresh install

boot auto-restores from the golden disk if the working disk is missing, so a fresh checkout only needs the cached golden to be present.

Command reference

Command What it does
doctor host tools, firmware, media, golden + VM status
fetch-virtio / fetch-openssh download + verify the pinned guest helpers
seed rebuild the autounattend.xml seed ISO
prepare restore golden if cached, else stage for install
install automated unattended install → cache golden
console-install interactive (windowed) install you drive
firstboot finish a fresh install (no install CD) → cache golden
console open a window on the installed VM
boot boot the installed VM headless (background)
wait-ssh wait for SSH, then print guest info
ssh [cmd] ssh into the running VM
run <arch> exe [args] upload + run an exe, return its exit code
smoke <arch> run a small probe in the VM
screenshot [name] capture the framebuffer
snapshot / reset cache / restore the golden disk
stop clean ACPI power down

Test integration

make test-coff-windows-vm

boots the VM, waits for SSH, and runs the hosted PE/COFF smokes against it so their per-program run lanes execute for real. make test-coff-windows-ucrt stays link-only (build + objdump -p, no VM) and make test-coff stays self-skipping when no UCRT sysroot is present.

How it works (and a key quirk)

Windows Setup's NVRAM "Windows Boot Manager" entry does not persist in QEMU's emulated firmware, so after Setup applies the image and reboots, a still-attached bootable install CD makes the firmware loop back into Setup instead of booting the new OS. The fix (handled by install/firstboot): reset the UEFI vars (forcing a fresh enumeration that finds Windows' ARM fallback loader \EFI\Boot\bootaa64.efi) and boot without the install CD. QEMU is also launched under the argv[0] kit-qemu-win so a broad pkill -f qemu-system-aarch64 (e.g. from a concurrent FreeBSD VM workflow in the same repo) cannot kill it.

Using an external Windows VM instead

To run against an existing Windows VM reachable over SSH (a remote arm64 box, or a real x64 machine) instead of the local hvf VM, set the endpoint env and the smoke tests / run will use it in preference to the local VM:

export KIT_WINDOWS_VM_X64=user@host       KIT_WINDOWS_VM_X64_PORT=22
export KIT_WINDOWS_VM_AARCH64=user@host   KIT_WINDOWS_VM_AARCH64_PORT=22
export KIT_WINDOWS_VM_SSH_KEY=$HOME/.ssh/id_ed25519
export KIT_WINDOWS_VM_SSH_OPTS="-o UserKnownHostsFile=/dev/null"

The COFF Windows smoke scripts prefer a configured VM endpoint. If none is set and no local VM is provisioned, they fall back to the podman/Wine path and self-skip when that is unavailable.

Tuning knobs (env)

KIT_WINDOWS_VM_PORT (host SSH port, default 2227), KIT_WINDOWS_MEM / KIT_WINDOWS_CPUS (guest RAM MiB / vcpus), KIT_WINDOWS_VM_DISPLAY (none or cocoa), KIT_WINDOWS_EDITION (default Windows 11 Pro), KIT_WINDOWS_SSH_USER / KIT_WINDOWS_SSH_PASS (default kit/kit), KIT_WINDOWS_GOLDEN (golden-disk path).