Skip to content

solcreek/frost

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

frost

CI License: Apache 2.0

Freeze and instantly thaw Windows-on-ARM VMs on Apple Silicon. Build a Windows 11 ARM golden image from your own ISO, then — in one command — clone it, boot it headless, push a binary in over SSH, run it, and capture its stdout/stderr and exit code. Snapshot a warm WSL2 environment and resume it in seconds. Built for testing cross-platform binaries inside Windows.

frost is built on the vmkit engine (packages/vmkit) — the reusable VM configuration + QEMU launch library that powers it.

Status: end-to-end validated. The full loop (build golden → instant clone → boot → push binary → run → capture exit code → teardown) works on Apple Silicon (verified on M2 Max, macOS 26, QEMU 11 + Hypervisor.framework).


Why

Running and automatically testing software inside Windows-on-ARM on a Mac is largely unaddressed — existing VM image-builder tooling is almost entirely x86. frost fills that gap: a reproducible, license-clean way to build a Windows-ARM test VM and drive a binary through it from the host.

Distribution model — ship the recipe, not the image. We never redistribute Windows. You supply your own Windows ISO and license; the image is built locally on your machine and owned by you.

Requirements

  • Apple Silicon Mac, macOS 13+ (tested on macOS 26).
  • brew install qemu swtpm; brew install hudochenkov/sshpass/sshpass.
  • Your own Windows 11 ARM64 ISO + a VirtIO driver ISO (for guest networking).
  • ~25 GB free disk per golden build (a slimmed golden is ~13 GB; clones are tiny overlays).

Quick start

# 1) Build a golden image from your own ISO (unattended install + provisioning).
#    --slim shrinks it (~20G → ~13G); --sysprep generalizes it (optional).
bin/build-golden.sh --iso Win11_ARM64.iso --virtio virtio-win.iso --slim

# 2) Run a test: instant clone → boot → push a binary over SSH → run → capture →
#    teardown (golden untouched). The script's exit code IS the guest command's
#    exit code, so CI can assert on it.
bin/test-run.sh --binary ./mytool.exe --run 'C:\Windows\Temp\mytool.exe --flag'

Configuration

frost resolves tool locations and credentials from the environment, falling back to Homebrew defaults — so a packaged build, CI, or a non-standard install can override them without editing scripts:

Variable Purpose Default
FROST_QEMU_BIN_DIR directory containing qemu-system-* /opt/homebrew/bin
FROST_QEMU_SHARE_DIR directory containing the EDK2 *.fd firmware /opt/homebrew/share/qemu
FROST_SWTPM swtpm executable <bin>/swtpm
FROST_VMKIT prebuilt vmkit engine binary (else built on first use)
FROST_SSH_PASS default guest SSH password (the --pass flag still overrides) Test1234!

The default password is a convenience for throwaway, host-only test VMs; set FROST_SSH_PASS (or pass --pass) for anything you keep.

How it works

your Windows ISO ─▶ build-golden.sh ─▶ golden.qcow2 (bootable, self-contained, ~13G)
                     (unattended install via dism /Apply-Image; provisions netkvm + OpenSSH)
                                              │
 per test:  golden ──qcow2 overlay clone (instant)──▶ boot headless (~20-30s)
            ──ssh: push binary, run──▶ capture stdout/stderr + exit code ──▶ delete clone

Key design decisions, all verified the hard way (details in docs/WINDOWS-CONFIG.md and docs/AUTOUNATTEND-NOTES.md):

  • dism /Apply-Image install flow (not setup.exe) — reliable, scriptable, and bypasses the flaky modern-OOBE path.
  • ramfb graphics (not virtio-gpu) — the latter hangs the Windows bootloader.
  • NVMe system disk — the firmware enumerates it and can boot it (AHCI cannot).
  • usb-storage,removable=on for the answer file — modern Windows Setup only scans removable media for autounattend.xml.
  • VirtIO netkvm injected during install → guest networking → host↔guest SSH.
  • qcow2 backing-file overlays for clones — each test run is a throwaway diff over the shared read-only golden, so clones are instant and tiny.

Running in-guest hypervisor workloads (WSL2 / Hyper-V)

Some binaries need a hypervisor inside the guest (e.g. WSL2). The hardware-accelerated (hvf) path can't give a Windows guest a usable hypervisor on Apple Silicon, so frost runs the guest under full software emulation with a firmware that exposes EL3 (bin/build-emu-firmware.sh

  • bin/test-run-emulated.sh --firmware). A real WSL2 Linux kernel boots this way (verified on M2) — slower than hvf but hardware-agnostic. For ~10 s per-test startup, snapshot a warm WSL2 state and resume it (bin/build-wsl-snapshot.sh / bin/test-run-snapshot.sh).

Plain binaries (including x86_64 via Windows' emulation) need none of this. See docs/NESTED-VIRT-KNOWHOW.md for the recipe and usage.

Project structure

.
├── bin/
│   ├── frost                  agent-friendly CLI dispatcher (subcommands; --dry-run, --json)
│   ├── build-golden.sh        build a golden image from your ISO (+ --slim, --sysprep)
│   ├── build-wsl-golden.sh    derive a WSL2-ready golden (VMPlatform + WSL pkg + distro)
│   ├── test-run.sh            the test harness (clone → boot → push → run → capture → teardown)
│   ├── test-run-emulated.sh   emulation harness (--firmware) for in-guest hypervisor (WSL2)
│   ├── build-emu-firmware.sh  build the EL3 ArmVirtQemuKernel firmware (container) for WSL2
│   ├── convert-to-virtio-blk.sh  make a savevm-capable (virtio-blk) golden via viostor boot-start
│   ├── build-wsl-snapshot.sh  (root) one-time: vmnet boot + warm WSL2 + savevm a resumable snapshot
│   └── test-run-snapshot.sh   (root) fast harness: loadvm-resume (~10s) → run → capture
├── packages/vmkit/           Swift core engine (SPM): VMConfiguration → QEMUArgumentBuilder + lifecycle
├── docs/                     know-how: QEMU config, autounattend, nested-virt/WSL2, disk formats
├── share/                    committed assets — answer file + guest provisioning scripts
│   ├── autounattend/         autounattend.xml (de-branded, dism-apply flow)
│   └── provision/            *.ps1 (setup, WSL install, viostor, boot test)
├── work/                     runtime only (VM disks, firmware, swtpm, ISOs) — git-ignored
├── THIRD_PARTY_NOTICES.md    required third-party license notices
├── LICENSE  ·  NOTICE        Apache-2.0
└── README.md

Architecture principles

  • A future GUI/app drives CLI engines as subprocesses (vmkit / qemu / swtpm). The subprocess boundary is also the clean GPL boundary (the frontend never links libqemu).
  • Integration language is Swift (packages/vmkit); each engine uses whatever fits.
cd packages/vmkit && swift build && swift test
# print the exact QEMU command a profile would launch (install|hvf|emulated|snapshot):
swift run vmkit args --profile hvf --disk golden.qcow2 --vars golden-vars.fd

Docs

  • THIRD_PARTY_NOTICES.md — third-party license notices.
  • docs/WINDOWS-CONFIG.md — the verified-working QEMU config for Windows 11 ARM, and why.
  • docs/AUTOUNATTEND-NOTES.md — unattended-install findings (removable media, OOBE, NVMe).
  • docs/UNATTEND-KNOWHOW.md — answer-file optimization catalog + per-profile trade-offs.
  • docs/NESTED-VIRT-KNOWHOW.md — running WSL2/Hyper-V in the guest: the EL3-firmware recipe, the pre-baked WSL golden, and snapshot/resume fast-start.

License

frost is licensed under the Apache License 2.0 (see LICENSE). frost orchestrates QEMU, swtpm, EDK2/OVMF and VirtIO drivers as separate subprocesses — it does not link or redistribute their code; see THIRD_PARTY_NOTICES.md.

About

Build, test, and snapshot Windows-on-ARM VMs on Apple Silicon — BYO ISO, headless SSH runs, and WSL2 booted under emulation.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors