Documentation
¶
Index ¶
- Constants
- func Accelerator() string
- func AlpineRootfsPath(home string) string
- func BaseProfileImagePath(home, stack string, modules []string) string
- func BluePixelRatio(ppmPath string) (float64, error)
- func BuildControlVolume(destPath string, payload map[string][]byte) error
- func BuildGoSSHDPayload(dir string) (string, error)
- func BuildGuestLogVolume(destPath string) error
- func BuildInstallCommand(spec Spec, windowsISO, autounattendImage string) []string
- func BuildProjectPullArgv(spec Spec) []string
- func BuildProjectPushArgv(spec Spec) []string
- func BuildRunCommand(spec Spec) []string
- func BuildSSHArgv(spec Spec) []string
- func BuildSSHExecArgv(host string, port uint16, user, keyPath, command string) []string
- func BuildWimBuilderArgv(wbs WimBuilderSpec) []string
- func BuildWinPECommand(spec Spec, winpeISO, answerImage string) []string
- func CacheDir(home string) string
- func CheckKernelBootableFirmware(path string) error
- func CleanStalePIDFile(dir string) error
- func CloneDisk(templateDisk, instanceDisk string) error
- func CollectVolumeLogs(imgPath string, names []string) []winpe.GuestLog
- func ConvertPPMtoPNG(ppmPath, pngPath string) error
- func CreateDisk(path string, sizeGB int) error
- func CreateFATQcow2(imgPath string, files map[string][]byte, capacity int64) error
- func DeterministicMAC(cellName string) string
- func DiskInfo(path string) (string, error)
- func DownloadAlpineRootfs(ctx context.Context, home string, noCache bool, obs Observer) (string, error)
- func DownloadOpenSSH(ctx context.Context, home string, noCache bool, obs Observer) (string, error)
- func DownloadPwsh(ctx context.Context, home string, noCache bool, obs Observer) (string, error)
- func DownloadVirtioDrivers(ctx context.Context, home string, noCache bool, obs Observer) (string, error)
- func DownloadWindowsISO(ctx context.Context, home, language string, noCache bool, obs Observer) (string, error)
- func EnsureScreenshotDir(resultsDir string, source ScreenSource) error
- func ExceptionVectorSlot(pc uint64) (base uint64, desc string)
- func FirmwareBootCount(serial string) int
- func FirmwarePath() string
- func GenerateCreateSessionUserScript(username, password string) string
- func GenerateDevToolsScript() string
- func GenerateDriverTrustScript() string
- func GenerateEnvSetupScript(envVars map[string]string) string
- func GenerateHardenEmulationScript() string
- func GenerateHomeManagerScript(user, drive string) string
- func GenerateHyperVEnableScript() string
- func GenerateHyperVVerifyScript() string
- func GenerateKeepAliveScript() []byte
- func GenerateNixOSWSLImportScript() string
- func GenerateNixVerifyScript() string
- func GenerateProjectMountScript(projectName, mountLetter string) string
- func GenerateSSHConfigScript(pubKey string) string
- func GenerateVirtioAgentInstallScript() string
- func GenerateVirtioFSMountScript(tag, drive string) string
- func GenerateVirtualizationProbeScript() string
- func GenerateWSL2EnableScript() string
- func GenerateWSLEngineInstallScript() string
- func GenerateWSLUserScript(user string) string
- func GenerateWinFspInstallScript() string
- func GuestFile(name string) ([]byte, error)
- func GuestPayload() (map[string][]byte, error)
- func GuestPayloadWithNixhome(nixhomeDir string) (map[string][]byte, error)
- func GuestReboot(ctx context.Context, spec Spec, sshUser, sshKeyPath string, ...) error
- func ImageName(stack string, modules []string) string
- func InstanceDir(home, cellName string) string
- func InstanceVMName(cellName string) string
- func InteractiveShellCommand() string
- func IsProcessAlive(pid int) bool
- func KeepAliveProbeCommand() string
- func KernelFirmwarePath() (string, error)
- func LatestNixReadyTestImage(dir string) (string, error)
- func LatestSSHAbleTestImage(dir string) (string, error)
- func LatestWSLReadyTestImage(dir string) (string, error)
- func NixReadyTestImageName(ts time.Time) string
- func NixhomeTarball(dir string) ([]byte, error)
- func OpenSSHPayloadPath(home string) string
- func ParseMajorVersion(ver string) (int, error)
- func ParseQEMUVersion(output string) (string, error)
- func PowerShellEncodedCommand(script string) string
- func PreferredAccel(tcgFallback string) string
- func PreflightCheck(goos, goarch string) error
- func PreflightCheckHost() error
- func PrepareVarsFile(firmwarePath, varsPath string) error
- func ProbeKVM() error
- func ProvisionedMarker(home, stack string, modules []string) string
- func PwshZipPath(home string) string
- func QEMUBinaryPath() (string, error)
- func QEMUVersion(binaryPath string) (string, error)
- func QMPBlockStats(socketPath string) (map[string]BlockDeviceStats, error)
- func QMPDismissFirstLogonUI(socketPath string) error
- func QMPEjectMedium(socketPath, id string) error
- func QMPHumanMonitor(socketPath, command string) (string, error)
- func QMPQueryKVM(socketPath string) (enabled, present bool, err error)
- func QMPQuit(socketPath string) error
- func QMPScreendump(socketPath, outputFile string) error
- func QMPSendKeys(socketPath string, keystrokes [][]string) error
- func QMPSocketPath(spec Spec) string
- func ReadFileFromFATQcow2(imgPath, filePath string) ([]byte, error)
- func ReadPIDFile(dir string) (int, error)
- func RemoveDownloadMarkers(home string)
- func ResolveAccel(explicit string, kvmRequested bool, goos string, probe func() error) (accel, reason string)
- func ResolveWindowsISO(envISO, configISO, home string) (string, error)
- func RunGuestStages(ctx context.Context, spec Spec, stages []GuestStage, opts StageRunOptions) error
- func SSHAbleImagePath(home, stack string, modules []string) string
- func SSHAbleTestImageName(ts time.Time) string
- func SaveBaseProfileImage(overlayDisk, dest string) error
- func SaveSSHAbleImage(templateDisk, dest string) error
- func ScreenshotPath(resultsDir string, source ScreenSource, now time.Time, screen string, ...) string
- func StackTag(stack string, modules []string) string
- func StageLogName(seq int, component string) string
- func StageLogNames(stages []GuestStage) []string
- func StallPollsFor(budgetSeconds, intervalSeconds int) int
- func StringToQKeyStrokes(s string) [][]string
- func TailProgressLog(path string, onLine func(string), stop <-chan struct{})
- func TemplateDir(home, stack string, modules []string) string
- func TemplateVMName(stack string, modules []string) string
- func ValidateISO(path string) error
- func VirtioISOPath(home string) string
- func VirtiofsdCommand(bin, socketPath, sharedDir string) *exec.Cmd
- func VirtiofsdPath() (string, error)
- func WSLEngineFiles() []string
- func WSLReadyTestImageName(ts time.Time) string
- func WaitForPortDown(host string, port uint16, window, interval time.Duration) bool
- func WaitForSSH(host string, port uint16, timeout, interval time.Duration, obs Observer, ...) error
- func WatchSerialFor(path string, marker string, stop <-chan struct{}) <-chan string
- func WatchSerialForDesktopBoot(path string, n int, stop <-chan struct{}) <-chan string
- func WatchSerialForEFIShell(path string, stop <-chan struct{}) <-chan string
- func WatchSerialForStartupNSHFail(path string, stop <-chan struct{}) <-chan string
- func WatchSerialForSyncException(path string, stop <-chan struct{}) <-chan string
- func WhitePixelRatio(ppmPath string) (float64, error)
- func WindowsISOPath(home, language string) string
- func WindowsPurpleRatio(ppmPath string) (float64, error)
- func WritePIDFile(dir string, pid int) error
- func WritePortMeta(instanceDir string, pm PortMeta) error
- type AllocatedPorts
- type BlockDeviceStats
- type DiscoveredVM
- type Engine
- type FirmwareFault
- type GDBConn
- func (g *GDBConn) Close() error
- func (g *GDBConn) Continue() error
- func (g *GDBConn) ReadMemory(addr uint64, length int) ([]byte, error)
- func (g *GDBConn) ReadRegister(index int) ([]byte, error)
- func (g *GDBConn) ReadRegisters() (string, error)
- func (g *GDBConn) ReadUint16LE(addr uint64) (uint16, error)
- func (g *GDBConn) RemoveBreakpoint(addr uint64) error
- func (g *GDBConn) SetBreakpoint(addr uint64) error
- func (g *GDBConn) Stop() error
- func (g *GDBConn) WaitBreak(timeout time.Duration) (string, error)
- func (g *GDBConn) WriteMemory(addr uint64, data []byte) error
- func (g *GDBConn) WriteRegister(index int, data []byte) error
- func (g *GDBConn) WriteUint16LE(addr uint64, val uint16) error
- type GoSSHDServeData
- type GuestStage
- type ISOMetadata
- type KVMHostCaps
- type LaunchAction
- type LaunchInputs
- type NopObserver
- type Observer
- type PSTATE
- type PortMeta
- type ScreenSource
- type Spec
- type StageRunOptions
- type StallSignal
- type StallTracker
- type VM
- func (v *VM) ExitError() error
- func (v *VM) ForceStop() error
- func (v *VM) LastOutput() string
- func (v *VM) QMPSockPath() string
- func (v *VM) Shutdown(ctx context.Context) error
- func (v *VM) Start(ctx context.Context) error
- func (v *VM) StartInstall(ctx context.Context, windowsISO, autounattendISO string) error
- func (v *VM) State() VMState
- func (v *VM) StateString() string
- func (v *VM) WaitForExit(ctx context.Context) error
- type VMState
- type VMStateFunc
- type WimBuilderResult
- type WimBuilderSpec
- type WriteProgressTracker
Constants ¶
const ( // VirtioDriversURL is the stable direct-download link for the latest VirtIO drivers ISO. VirtioDriversURL = "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" // WindowsISODownloadURL is the Microsoft page for downloading Windows 11 ARM64 ISO. // Kept for the manual-download fallback message in ResolveWindowsISO. WindowsISODownloadURL = "https://www.microsoft.com/en-us/software-download/windows11arm64" // PwshVersion is the PowerShell 7 release shipped on the answer volume. PwshVersion = "7.6.5" // PwshReleaseURL is the direct GitHub download for the self-contained ARM64 zip. PwshReleaseURL = "https://github.com/PowerShell/PowerShell/releases/download/v" + PwshVersion + "/PowerShell-" + PwshVersion + "-win-arm64.zip" // PwshZipName is the cached zip filename. PwshZipName = "pwsh-arm64.zip" // AlpineVersion is the Alpine minirootfs release used as the WSL2 // smoke-test distro — a ~4 MB tarball, the cheapest real Linux there is. AlpineVersion = "3.22.1" // AlpineRootfsURL is the direct CDN download for the aarch64 minirootfs. AlpineRootfsURL = "https://dl-cdn.alpinelinux.org/alpine/v3.22/releases/aarch64/alpine-minirootfs-" + AlpineVersion + "-aarch64.tar.gz" // AlpineRootfsName is the cached tarball filename. AlpineRootfsName = "alpine-minirootfs-" + AlpineVersion + "-aarch64.tar.gz" )
const ( // GoSSHDPayloadName is the server's filename on the agent volume. GoSSHDPayloadName = "devcell-gosshd.exe" // GoSSHDPackage is the package cross-compiled into that payload. GoSSHDPackage = "github.com/devcell-sh/go-winkit/gosshd/cmd/gosshd" // GoSSHDLogFile is the server's log on the shared volume. It is not // written to the guest ramdisk: a session that fails minutes in still // has to be explainable after the VM is gone. GoSSHDLogFile = "devcell-gosshd.log" )
const ( // KeepAliveScriptName is the probe script's filename on the agent volume. KeepAliveScriptName = `devcell-keepalive.ps1` // KeepAliveProbeFile is a host-written file on the shared FAT volume. // Echoing it back proves the host->guest file channel end to end. KeepAliveProbeFile = `devcell-probe.txt` KeepAliveBanner = `=== DEVCELL KEEPALIVE PROBE ===` )
const CDBusID = "cd-scsi-bus"
BuildInstallCommand constructs the QEMU argv for initial Windows installation. windowsISO and any virtioISO are attached as USB CD-ROMs. autounattendImage is attached as a further CD-ROM when it is an .iso, or as a removable usb-storage disk for a raw FAT image; Windows Setup searches both kinds of removable media for autounattend.xml. CDBusID names the virtio-scsi controller used for CD-ROM devices when CDBus is "scsi". Separate from the disk's NVMe controller.
const DefaultDiskSizeGB = 64
DefaultDiskSizeGB is the default Windows VM disk size.
const DefaultSSHPort = 2222
DefaultSSHPort is the *preferred* forwarded SSH port, not a fixed one. ApplyDefaults falls back to a kernel-assigned free port when it is taken.
It used to be assigned unconditionally, so two overlapping VMs raced for the same hostfwd and the loser aborted at launch with
hostfwd=tcp:127.0.0.1:2222-:22: Could not set up host forwarding rule
which surfaced downstream only as "QMP socket did not appear within 30s" and an empty results directory — see TestSpec_ApplyDefaults_SkipsAPortAlreadyInUse.
const DefaultStageTimeout = 90 * time.Minute
DefaultStageTimeout bounds a single stage. The slowest legitimate stage (nix or a driver install under TCG) runs well under an hour; a dev-env stage once sat wedged for three because nothing bounded it.
const DefaultTCGAccel = "tcg,thread=multi"
DefaultTCGAccel is the software-emulation fallback. thread=multi lets TCG spread guest vCPUs across host threads, which is the single largest win available without hardware virtualization.
const EFIShellMarker = `"EFI Internal Shell"`
const GuestControlDir = "/devcell"
GuestControlDir is where the guest tree lands on the control volume.
const GuestLogVolumeMarker = "devcell-guest-logs.txt"
The guest log volume: a FAT image any post-install VM can write logs to — the same channel the install's answer volume provides, for the same reason. The SSH stream dies with its connection, while FAT survives anything short of losing the image file. The guest finds the volume by marker file, never by drive letter.
const InstallerCDDeviceID = "installer-cd"
InstallerCDDeviceID is the qdev id of the installer CD. QMP addresses devices by qdev id, not drive id, so ejecting requires this name.
const KVMDevice = "/dev/kvm"
KVMDevice is the character device QEMU opens to use hardware virtualization on Linux. Inside a container it is present only when the launcher passed --device=/dev/kvm (see `[cell] kvm` in .devcell.toml).
const KernelFirmwareCacheName = "QEMU_EFI.kernel.fd"
KernelFirmwareCacheName is where a kernel-bootable EDK2 image lives in the devcell cache (~/.devcell/cache/qemu/). Named distinctly from QEMU_EFI.fd on purpose: every distro ships a *different, incompatible* build under that name, and telling them apart by filename is exactly the trap.
const MinTemplateSizeBytes int64 = 4 * 1024 * 1024 * 1024
MinTemplateSizeBytes is the minimum valid template disk size (4 GB). A template smaller than this is corrupt or incomplete (e.g. a failed build left a 193 KB stub) and should trigger a rebuild instead of a clone.
const NixOSWSLDistro = "NixOS"
NixOSWSLDistro is the distro name NixOS-WSL's own documentation uses.
const NoChangeMarker = "DEVCELL-NO-CHANGE"
NoChangeMarker is what a stage prints to say it changed nothing, so the runner can skip a reboot it does not need. A TCG reboot costs ~8 minutes (run 20260803T075624 paid exactly that after an engine stage that found the engine already installed).
const ProgressPortName = `devcell.progress.0`
ProgressPortName is the virtio-serial port name used for guest→host progress reporting. The guest writes to \\.\Global\<name>, the host reads from a chardev file wired to GuestProgressLogPath.
const StartupNSHFailMarker = "BOOTAA64.EFI not found"
StartupNSHFailMarker is the message startup.nsh prints when none of FS0–FS4 contain BOOTAA64.EFI. After the firmware drops to the EFI shell, startup.nsh attempts to chainload the Windows bootloader — this marker means it gave up.
const StructuredPortName = `devcell.structured.0`
StructuredPortName is a second virtio-serial port for structured JSONL logging. The guest writes JSON lines to \\.\Global\<name>, the host reads from a chardev file wired to GuestStructuredLogPath.
const SyncExceptionMarker = "Synchronous Exception at"
SyncExceptionMarker is the firmware's fatal crash message. A synchronous exception means the loaded bootloader dereferenced an unmapped address — the boot is dead and will never recover.
const USBBusID = "usb-bus"
USBBusID names the xhci controller so every storage device can state its bus explicitly. Leaving the bus implicit lets QEMU pick, which is how a device ends up somewhere the firmware never enumerates.
const WSLDistroUser = "nixos"
WSLDistroUser is the account the WSL side runs as — a DIFFERENT identity from the Windows session user (SessionUsername(), the host's $USER, which autounattend creates and SSH lands as).
It must equal the username nixhome's WSL home-manager config was built for (`wslUser` in nixhome/flake.nix). home-manager refuses to activate a config whose username differs from the invoking user:
Error: USER is set to "dmitry" but we expect "nixos"
Conflating the two identities is why home-manager activation had never completed: the stages renamed the distro to the Windows user while nix was asked for a config built for "nixos".
Parameterizing this per-user is CELL-404; until then the two identities stay explicitly separate rather than silently equal.
const WSLEngineDestDir = "Program Files/WSL"
WSLEngineDestDir is where the WSL engine lands inside boot.wim — the same path the MSI installs to, so wslservice finds its own layout.
const WindowsBootManagerMarker = `"Windows Boot Manager"`
WindowsBootManagerMarker is the firmware log line when bootmgfw.efi loads.
Variables ¶
This section is empty.
Functions ¶
func Accelerator ¶
func Accelerator() string
Accelerator returns the appropriate QEMU accelerator for the current platform.
func AlpineRootfsPath ¶
AlpineRootfsPath returns the cached Alpine minirootfs tarball path.
func BaseProfileImagePath ¶
BaseProfileImagePath returns where the dev-env pipeline's finished product is saved: an ssh-able Windows with WSL1, nix and the nixhome base profile activated. This is the state `cell build --engine=qemu` is ultimately meant to end at for the base stack.
func BluePixelRatio ¶
BluePixelRatio reads a PPM (P6) file and returns the fraction of pixels that are "blue" — i.e. B >= 120, B > R+40, B > G+30. This detects the Windows Setup installer's characteristic blue background.
func BuildControlVolume ¶
BuildControlVolume writes the per-run control volume: the marker the guest resolves its drive letter by, plus any payload to deliver INTO the guest (the PowerShell module and stage scripts — see CELL-402). Logs come back on the same volume, so one attachment carries both directions.
Built fresh on the host every run and attached at boot, so it is never inside the qcow2: a checkpoint image cannot freeze a stale copy, which is the failure mode that ruled out installing the module onto the guest disk.
func BuildGoSSHDPayload ¶
BuildGoSSHDPayload cross-compiles the guest SSH server for windows/arm64 into dir and returns its path.
Building beats downloading: the previous Win32-OpenSSH payload was a pinned GitHub release that had to be cached, checksummed and version- guarded, and an unpinned URL silently moved us onto a release whose split binaries changed the failure mode mid-investigation. This binary is the tree's own code, so it cannot drift from the harness that talks to it.
CGO is off so the result is a single static binary with no DLL dependencies — WinPE has a reduced System32 and cannot be assumed to carry any particular runtime.
func BuildGuestLogVolume ¶
BuildGuestLogVolume creates the FAT image guests write their logs to. Attach it via Spec.LogVolumePath; read it back with CollectVolumeLogs.
func BuildInstallCommand ¶
func BuildProjectPullArgv ¶
BuildProjectPullArgv builds the scp argv that copies the guest's project tree back over the local one (write-back for "two-way" sync). The destination is the project's parent directory so the tree overlays in place. Returns nil when the spec has no project directory.
func BuildProjectPushArgv ¶
BuildProjectPushArgv builds the scp argv that copies the project tree into the guest's home (creating ~\<basename>, where BuildSSHArgv cd's to). Returns nil when the spec has no project directory.
func BuildRunCommand ¶
BuildRunCommand constructs the QEMU argv for normal VM operation (post-install).
func BuildSSHArgv ¶
BuildSSHArgv constructs the SSH argv for running a command inside a Windows VM. For Windows guests, we use cmd.exe /c or powershell -NoProfile -Command.
func BuildSSHExecArgv ¶
BuildSSHExecArgv constructs a simple SSH argv for running a single command. Used during provisioning.
func BuildWimBuilderArgv ¶
func BuildWimBuilderArgv(wbs WimBuilderSpec) []string
BuildWimBuilderArgv constructs the QEMU argv for the WIM builder VM. When Spec.CDBus is "scsi", ISOs are attached on a virtio-scsi-pci controller (scsi-cd) so EDK2 can boot them on QEMU 11.x/HVF where USB-attached ISOs are invisible to the firmware. The shared FAT volume always goes on usb-storage (Windows needs it as removable media).
func BuildWinPECommand ¶
BuildWinPECommand constructs the QEMU argv for booting WinPE from a custom ISO (CELL-430). Only one CD (the WinPE ISO) plus an answer volume on usb-storage. No Windows installer ISO, no virtio ISO.
func CacheDir ¶
CacheDir returns the QEMU media cache directory.
DEVCELL_QEMU_CACHE_DIR points it somewhere shared. Inside a cell $HOME is itself a per-cell directory, so the default renders as ~/.devcell/<cell>/.devcell/cache/qemu and every cell re-downloads the same ~6 GB of immutable media. There is no way to reach the real host home from inside the container, so the location has to be pointable rather than inferred (CELL-386).
func CheckKernelBootableFirmware ¶
CheckKernelBootableFirmware verifies that path holds the ArmVirtQemuKernel EDK2 build — the relocatable image with the ARM64 kernel-image magic ("ARMd" at offset 56) that QEMU's -kernel loader understands. The common ArmVirtQemu build (what nixpkgs, Debian and openSUSE all ship as QEMU_EFI.fd) is linked for flash address 0 and boots to *silence* when loaded into DRAM, so this must be checked, not assumed.
func CleanStalePIDFile ¶
func CloneDisk ¶
CloneDisk creates a qcow2 snapshot (backing file) from a template disk. The instance disk is thin — only stores delta writes.
func CollectVolumeLogs ¶
CollectVolumeLogs reads the named files off a guest log volume — one entry per name, absence reported rather than skipped, same contract as CollectGuestLogs.
func ConvertPPMtoPNG ¶
ConvertPPMtoPNG reads a PPM (P6 binary) file and writes a PNG. Returns the PNG path (same base, .png extension).
func CreateDisk ¶
CreateDisk creates a new qcow2 disk image at the given path.
func CreateFATQcow2 ¶
CreateFATQcow2 creates a qcow2 disk image containing a FAT32 filesystem with the given files. The virtual disk capacity is set to capacity bytes, but the on-disk qcow2 file is sparse — only clusters with actual data are allocated. This is useful for shared volumes that need large free space for guest writes (e.g. WIM builder writing devcell.wim) without consuming host disk upfront.
func DeterministicMAC ¶
DeterministicMAC derives a stable locally-administered MAC address from a cell name. Same cell -> same MAC -> same DHCP lease.
func DownloadAlpineRootfs ¶
func DownloadAlpineRootfs(ctx context.Context, home string, noCache bool, obs Observer) (string, error)
DownloadAlpineRootfs fetches the Alpine aarch64 minirootfs if not cached.
func DownloadOpenSSH ¶
DownloadOpenSSH fetches Microsoft's signed Win32-OpenSSH ARM64 release.
The guest cannot install OpenSSH Server through Windows servicing: our media carries the capability manifest but not its payload, so the capability sits Staged and the install fails 0x80070002 — with Windows Update reachable and permitted. The Server FoD ships on a separate build-matched ISO, and the UUP package has no Server package at all. This release needs no servicing.
func DownloadPwsh ¶
DownloadPwsh fetches the PowerShell 7 ARM64 self-contained zip if not cached.
func DownloadVirtioDrivers ¶
func DownloadVirtioDrivers(ctx context.Context, home string, noCache bool, obs Observer) (string, error)
DownloadVirtioDrivers downloads the VirtIO drivers ISO if not already cached. Uses .done marker pattern (mirrors tart.DownloadIPSW). When noCache is true, removes the .done marker to force re-download.
func DownloadWindowsISO ¶
func DownloadWindowsISO(ctx context.Context, home, language string, noCache bool, obs Observer) (string, error)
DownloadWindowsISO fetches and caches the Windows 11 ARM64 ISO via UUP dump. Downloads an ESD from Microsoft's CDN and assembles it into a bootable ISO using wimlib-imagex and mkisofs (must be on PATH).
func EnsureScreenshotDir ¶
func EnsureScreenshotDir(resultsDir string, source ScreenSource) error
EnsureScreenshotDir creates the directory ScreenshotPath writes into.
func ExceptionVectorSlot ¶
ExceptionVectorSlot maps a program counter onto AArch64 exception-vector geometry: assuming an 0x800-aligned VBAR, it returns the table base the PC would belong to and a description of the slot it falls in.
This is heuristic — any address has *some* offset mod 0x800 — so it is only evidence when combined with a dead-loop instruction at the PC and DAIF masked. With those, it converts a raw parked address into the exception class: the KVM stall's PC=0x13c347200 → slot +0x200 → a synchronous exception taken at the current EL on SP_ELx.
func FirmwareBootCount ¶
FirmwareBootCount reports how many times the guest firmware started. More than one during an install that has not finished applying its image means the guest reset prematurely.
func FirmwarePath ¶
func FirmwarePath() string
FirmwarePath returns the path to the EDK2 UEFI firmware for ARM64.
func GenerateCreateSessionUserScript ¶
GenerateCreateSessionUserScript returns a PowerShell script that creates a local user matching the host user, with admin privileges and password-free SSH.
func GenerateDevToolsScript ¶
func GenerateDevToolsScript() string
GenerateDevToolsScript returns a PowerShell script that installs essential dev tools (Git) via winget with a Chocolatey fallback.
The fallback fires on winget *failure*, not only on absence: in run 20260801T001059 winget existed, errored, installed nothing — and the step still claimed ok because its stderr was discarded. The step now verifies git actually landed and fails when it did not.
func GenerateDriverTrustScript ¶
func GenerateDriverTrustScript() string
GenerateDriverTrustScript prepares the guest to accept the Dev-signed viofs driver: the signer certificates go into the MACHINE Root and TrustedPublisher stores (read back afterwards — exit codes lied twice), and testsigning is switched on. Iteration 8 proved the stores and the token were right and pnputil still refused: Win11's code-integrity policy rejects non-Microsoft kernel packages until testsigning is LIVE, which takes a reboot — so this runs as its own stage, before one.
func GenerateEnvSetupScript ¶
GenerateEnvSetupScript returns a PowerShell script that sets environment variables for the devcell session.
Built by iteration rather than from a template: the body is one line per variable with no prose around it, so a template would add indirection without removing any escaping hazard.
func GenerateHardenEmulationScript ¶
func GenerateHardenEmulationScript() string
GenerateHardenEmulationScript returns a PowerShell script that disables WerFault and Defender real-time monitoring — the two biggest resource wasters in a TCG-emulated build VM.
func GenerateHomeManagerScript ¶
GenerateHomeManagerScript links the mounted project share to the agreed repo path inside WSL and activates the repo's nixhome via home-manager.
func GenerateHyperVEnableScript ¶
func GenerateHyperVEnableScript() string
GenerateHyperVEnableScript asks Windows to install and launch its hypervisor — what the WSL2 utility VM is actually created on.
func GenerateHyperVVerifyScript ¶
func GenerateHyperVVerifyScript() string
GenerateHyperVVerifyScript asserts the two independent facts the WSL2 utility VM depends on: the hypervisor is INSTALLED, and it is STARTED. They fail for different reasons — a missing payload versus a hypervisor that cannot launch on emulated EL2 — so they are reported and thrown separately.
func GenerateKeepAliveScript ¶
func GenerateKeepAliveScript() []byte
GenerateKeepAliveScript produces the probe a troubleshooting session opens with: echo back the file the host placed on the FAT volume, confirm the agent shell ran, and dump enough state to debug from. The VM is left running afterwards, so this output is the last thing written before the guest goes idle and waits to be driven through QMP.
func GenerateNixOSWSLImportScript ¶
func GenerateNixOSWSLImportScript() string
GenerateNixOSWSLImportScript installs the official NixOS-WSL image as a WSL2 distro, following the project's own instructions: fetch nixos.wsl from the latest release and `wsl --install --from-file` it (WSL 2.4.4+), falling back to `wsl --import … --version 2` on older engines.
func GenerateNixVerifyScript ¶
func GenerateNixVerifyScript() string
GenerateNixVerifyScript proves the toolchain the NixOS-WSL image already carries. NixOS *is* nix — running the upstream installer inside it would be both redundant and non-idiomatic.
func GenerateProjectMountScript ¶
GenerateProjectMountScript returns a PowerShell script that creates a project directory. For QEMU, project files are shared via virtio-fs (see the dev-env pipeline) or copied over SSH.
func GenerateSSHConfigScript ¶
GenerateSSHConfigScript returns a PowerShell script that configures OpenSSH Server on Windows: sets default shell, authorized keys, and firewall rule.
func GenerateVirtioAgentInstallScript ¶
func GenerateVirtioAgentInstallScript() string
GenerateVirtioAgentInstallScript installs the ARM64 virtio drivers Windows did not need during setup (vioserial, viofs, balloon, rng) and then the qemu guest agent — the x64 MSI under Win11's emulation, since no ARM64 agent build exists (see .scratch/VIRTIO.md).
func GenerateVirtioFSMountScript ¶
GenerateVirtioFSMountScript registers virtiofs.exe (from the driver CD) as a service mounting the given tag at the given drive, then proves the mount by reading it. Service manager output is kept and dependencies are probed — the first version piped sc.exe to Out-Null and a silent failure explained nothing.
func GenerateVirtualizationProbeScript ¶
func GenerateVirtualizationProbeScript() string
GenerateVirtualizationProbeScript records whether this guest can host a hypervisor — the question that decides WSL1 vs WSL2. Observation only: the probe never enables a feature, so a run can report "WSL2 was impossible" without having changed the guest to find out.
func GenerateWSL2EnableScript ¶
func GenerateWSL2EnableScript() string
GenerateWSL2EnableScript enables both features WSL2 needs. NixOS-WSL does not support WSL1 (https://nix-community.github.io/NixOS-WSL/install.html), so VirtualMachinePlatform is required rather than optional. The reboot belongs to the caller, which can watch SSH drop and come back.
func GenerateWSLEngineInstallScript ¶
func GenerateWSLEngineInstallScript() string
GenerateWSLEngineInstallScript installs the WSL engine MSI. The inbox wsl.exe on current Win11 is a stub — the engine is a separate MSI from the microsoft/WSL releases. Installing it tears down the SSH session, so the stage runs disconnect-tolerant and reboot-terminated.
func GenerateWSLUserScript ¶
GenerateWSLUserScript renames the distro's default user to the cell's session user, following NixOS-WSL's documented procedure. Without it the distro runs as "nixos" while every path the cell uses is /home/<user>.
func GenerateWinFspInstallScript ¶
func GenerateWinFspInstallScript() string
GenerateWinFspInstallScript fetches and installs WinFsp, the userspace filesystem layer virtiofs.exe requires.
func GuestFile ¶
GuestFile returns one file from the embedded guest tree, addressed the way stages refer to it ("Devcell.psm1", "stages/wsl2-enable.ps1").
func GuestPayload ¶
GuestPayload returns the whole guest tree keyed by its path on the control volume, ready for BuildControlVolume. Everything ships every run: the volume is built on the host and attached at boot, so it can never drift from the repo the way a copy written into the qcow2 would.
func GuestPayloadWithNixhome ¶
GuestPayloadWithNixhome is GuestPayload plus the nixhome tarball the home-manager stage extracts inside the distro — one control volume carries both the scripts and the config they activate.
func GuestReboot ¶
func GuestReboot(ctx context.Context, spec Spec, sshUser, sshKeyPath string, sshDeadline time.Duration, obs Observer, stateFn VMStateFunc) error
GuestReboot is the one way to reboot a guest between stages: request the restart over SSH, wait for sshd to actually GO DOWN (up to downWindow — if it never goes down the restart likely failed, but the follow-up WaitForSSH decides), then wait for it to come back. Every reboot callback (tests and cell build phases alike) should delegate here rather than re-inventing the sleep-and-hope pattern this replaced.
func InstanceDir ¶
InstanceDir returns the per-cell instance directory for Windows VMs. Layout: ~/.devcell/<cellName>/windows/
func InstanceVMName ¶
InstanceVMName returns the QEMU VM name for a running cell instance.
func InteractiveShellCommand ¶
func InteractiveShellCommand() string
InteractiveShellCommand is the agent command for a hands-on troubleshooting session inside a booted image.
It runs the keep-alive probe first — which brings up sshd — and only then hands the console to cmd.exe. Order matters: an SSH session is independent of the console, so it survives anything typed there, while the console itself is fragile. WinPE's shell is bootstrap.cmd running pwsh with `& goto :eof`, so interrupting the agent ends the batch and takes WinPE down with it. Parking the agent inside a blocking cmd.exe keeps that from happening and gives QMP keystrokes a prompt that actually executes them.
-NoNewWindow with no redirection is the load-bearing detail: the child inherits the console's own stdin and stdout rather than the agent's pipeline, so typed keys run and their output is visible on screen.
func IsProcessAlive ¶
func KeepAliveProbeCommand ¶
func KeepAliveProbeCommand() string
KeepAliveProbeCommand is the agent command line for the probe script.
func KernelFirmwarePath ¶
KernelFirmwarePath resolves the firmware for the WSL2 machine (secure=on + -kernel): $DEVCELL_QEMU_EFI_KERNEL if set, else the devcell cache. An explicit override that fails validation is an error rather than a fallthrough — the user pointed at a specific file, and using another would hide the mistake behind a silent boot failure later.
func LatestNixReadyTestImage ¶
LatestNixReadyTestImage returns the newest windows-nix-*.qcow in dir.
func LatestSSHAbleTestImage ¶
LatestSSHAbleTestImage returns the newest windows-sshable-*.qcow in dir.
func LatestWSLReadyTestImage ¶
LatestWSLReadyTestImage returns the newest windows-wsl-*.qcow in dir.
func NixReadyTestImageName ¶
NixReadyTestImageName returns the versioned filename for a saved nix-ready image: a guest where Windows' own hypervisor launches (EL3 machine), NixOS-WSL is imported and nix answers inside it. This is the furthest checkpoint and the base for nix-based tests — from here only in-distro work (home-manager and beyond) remains.
func NixhomeTarball ¶
NixhomeTarball packs a nixhome directory for control-volume delivery, all contents under a top-level "nixhome/" so extraction recreates the layout.
A tarball, not a live reference: activating straight from the project share fails twice over — nix ingests the surrounding repo as a dirty git+file input, and the share's symlinks (36 in the icewm theme alone) die on readlink across virtiofs+drvfs (run 20260804). Inside a tarball the symlinks are just entries; extracted onto the distro's ext4 they work.
func OpenSSHPayloadPath ¶
OpenSSHPayloadPath returns the cached Win32-OpenSSH release path.
func ParseMajorVersion ¶
ParseMajorVersion extracts the major version number from a QEMU version string like "11.0.93" or "10.0.2".
func ParseQEMUVersion ¶
ParseQEMUVersion extracts the version from qemu --version output.
func PowerShellEncodedCommand ¶
PowerShellEncodedCommand wraps a script for SSH transport to a Windows guest. The command string a guest receives is re-parsed by its default shell and again by PowerShell's native argument handling, which eats unescaped double quotes — so any literal script with quoting eventually arrives mangled. -EncodedCommand sidesteps every parser in the chain: the script travels as base64 over UTF-16LE and PowerShell decodes it itself.
func PreferredAccel ¶
PreferredAccel returns hardware virtualization when the host can provide it, and the caller's TCG string otherwise.
It differs from ResolveAccel in who grants consent: there is no cfg layer in a test binary, so a usable device *is* the consent. The fallback stays a caller argument because TCG tuning is workload-specific — tb-size=512 is worth it for a 70-minute Windows install and meaningless elsewhere, and it is rejected outright when passed alongside an accel of kvm.
func PreflightCheck ¶
PreflightCheck validates the host can run QEMU with hardware acceleration. Pure function (takes OS/arch as params for testability).
func PreflightCheckHost ¶
func PreflightCheckHost() error
PreflightCheckHost calls PreflightCheck with runtime values and verifies that qemu-system-aarch64 is installed.
func PrepareVarsFile ¶
PrepareVarsFile copies the UEFI firmware to create a writable vars store.
func ProbeKVM ¶
func ProbeKVM() error
ProbeKVM reports whether /dev/kvm is usable by the current process.
func ProvisionedMarker ¶
ProvisionedMarker returns the path to the ".provisioned" marker file within a template directory.
func PwshZipPath ¶
PwshZipPath returns the cached PowerShell 7 zip path.
func QEMUBinaryPath ¶
QEMUBinaryPath returns the path to qemu-system-aarch64, or an error if not found.
func QEMUVersion ¶
QEMUVersion runs qemu-system-aarch64 --version and extracts the version string.
func QMPBlockStats ¶
func QMPBlockStats(socketPath string) (map[string]BlockDeviceStats, error)
QMPBlockStats returns per-device I/O counters via query-blockstats. Growing read counters on a frozen display prove the guest is still booting.
func QMPDismissFirstLogonUI ¶
QMPDismissFirstLogonUI sends a single Esc to the guest. Windows 11 opens the Start menu on its own at the first sign-in after OOBE, and an unattended VM never produces the input event that would close it — it stays over every subsequent screenshot. One Esc at "SSH ready" (which is also the proof that first logon happened) dismisses it.
func QMPEjectMedium ¶
QMPEjectMedium ejects removable media (id is the drive id, e.g. "cdrom0").
Windows Setup reboots after applying its image. If the installer CD is still attached and holds bootindex=0, the firmware boots the installer again instead of the freshly installed OS, and Setup stops on "It looks like you started an upgrade and booted from installation media". Ejecting once the image is applied sends the next boot to disk. force=true because the guest may hold the tray locked.
func QMPHumanMonitor ¶
QMPHumanMonitor runs an HMP command (e.g. "info registers") via QMP and returns its text output. A changing PC across calls proves the vCPU is alive.
func QMPQueryKVM ¶
QMPQueryKVM reports whether the running VM has hardware virtualization enabled, and whether KVM is present on the host at all.
This is how a run proves its own accelerator. `-accel kvm` cannot silently degrade to TCG — QEMU exits with an error instead — but that is an argument from flag semantics, not evidence in the artifact. Asking the live VM turns "this run should have used KVM" into "this run did".
func QMPQuit ¶
QMPQuit sends the "quit" command which flushes all block device caches and exits the QEMU process cleanly. Unlike Process.Kill(), this ensures writeback-cached qcow2 images are consistent on disk.
func QMPScreendump ¶
QMPScreendump captures the QEMU display framebuffer to a PPM file via QMP.
func QMPSendKeys ¶
QMPSendKeys sends a sequence of keystrokes to the VM via QMP. Each element of keystrokes is a set of QKeyCodes pressed simultaneously (e.g. []string{"shift", "f"} for uppercase F).
func QMPSocketPath ¶
QMPSocketPath returns the path to the QMP unix socket for a given spec.
The socket normally lives next to the VM it belongs to, but that directory is the caller's choice and can be arbitrarily deep — `cell build` nests it four levels under $HOME, which overflows sun_path for an ordinary home path. When the natural path does not fit, fall back to a short one in the system temp directory, keyed by a digest of the original so the result stays stable and unique per VM: `cell rdp`/`cell vnc` find a running VM by recomputing this path, so it must be a pure function of the spec.
func ReadFileFromFATQcow2 ¶
ReadFileFromFATQcow2 reads a single file from a qcow2-backed FAT32 disk image. It converts the qcow2 to a temporary raw image, reads the file with the standard FAT reader, and cleans up.
func ReadPIDFile ¶
func RemoveDownloadMarkers ¶
func RemoveDownloadMarkers(home string)
RemoveDownloadMarkers removes .done markers for all cached ISOs, forcing re-download on next use. Used by --no-cache.
func ResolveAccel ¶
func ResolveAccel(explicit string, kvmRequested bool, goos string, probe func() error) (accel, reason string)
ResolveAccel picks the QEMU accelerator and returns the choice plus a human-readable reason for the launch log.
Order of authority:
- an explicit Spec.Accel — callers (notably tests) always win;
- `[cell] kvm = true` AND the device actually opens — KVM (linux only);
- otherwise TCG.
darwin used to default to HVF, but QEMU 11.x/HVF has USB xhci enumeration bugs (CELL-427) that break WinPE boot. TCG is slower but reliable. Pass Accel:"hvf" explicitly to opt back in.
Both conditions in (2) are load-bearing. Config alone is not enough: it describes intent, and a launch that trusts it on a host without nested virtualization dies with "Could not access KVM kernel module". A usable device alone is not enough either — config stays the authority, so an unrequested accelerator is never silently adopted.
func ResolveWindowsISO ¶
ResolveWindowsISO resolves the Windows ARM64 ISO path. Priority: env DEVCELL_QEMU_WINDOWS_ISO > config path > cached download > error.
func RunGuestStages ¶
func RunGuestStages(ctx context.Context, spec Spec, stages []GuestStage, opts StageRunOptions) error
RunGuestStages runs every stage in order, stopping at the first failure — stages are strictly dependent, so continuing only produces confusing downstream errors.
func SSHAbleImagePath ¶
SSHAbleImagePath returns where a verified template is promoted to. The "ssh-able" image is the contract between the install test and everything built on top of it: an installed Windows whose SSH access has actually been exercised, not merely stamped.
func SSHAbleTestImageName ¶
SSHAbleTestImageName returns the versioned filename for a saved ssh-able image: windows-sshable-<compact ISO 8601 UTC>.qcow. Compact (no colons) so the name is filesystem-safe everywhere, and lexicographic order equals chronological order.
func SaveBaseProfileImage ¶
SaveBaseProfileImage flattens a dev-env overlay (nix + home-manager on top of ssh-able) into a standalone image — see saveStandaloneImage. The VM writing the overlay must be shut down first.
func SaveSSHAbleImage ¶
SaveSSHAbleImage copies a verified template disk to dest as a standalone qcow2 — see saveStandaloneImage.
func ScreenshotPath ¶
func ScreenshotPath(resultsDir string, source ScreenSource, now time.Time, screen string, screenSeq, globalSeq int, ext string) string
ScreenshotPath returns where a captured frame belongs:
<resultsDir>/screenshots/<source>/<ISO>-<screen>-<screenSeq>-<globalSeq>.<ext>
screenSeq counts frames of the current screen (how long it has persisted), globalSeq counts every frame of the run — together they answer "what was on screen, for how long, and where in the run" from a directory listing. The caller creates the directory (see EnsureScreenshotDir).
func StageLogName ¶
StageLogName is the transcript filename for a dev-env component, prefixed with the component's 1-based position in the pipeline. Grouping by component rather than by SSH execution means "what happened with WSL" is one file covering the feature, the engine and the distro import; the number keeps a results directory sorted in execution order.
func StageLogNames ¶
func StageLogNames(stages []GuestStage) []string
StageLogNames maps each stage to its component's log name, numbering components by first appearance. Stages sharing a component share a file.
func StallPollsFor ¶
StallPollsFor converts a stall budget in seconds into a poll count, given the poll interval. Never returns fewer than 2: detecting "unchanged" needs two samples to compare, so a single poll can never establish a stall.
func StringToQKeyStrokes ¶
StringToQKeyStrokes converts a string to QMP keystroke sequences. Each returned element is a slice of simultaneously-pressed QKeyCodes.
func TailProgressLog ¶
TailProgressLog tails the guest-progress.log file and calls onLine for each complete line. The goroutine opens the file lazily (it may not exist when the VM starts) and exits when stop is closed. Unlike WatchSerialFor, which fires once on a marker, this streams every line — it is the live view of the bootstrap's Send-Progress output.
func TemplateDir ¶
TemplateDir returns the path to per-template VM artifacts. Layout: ~/.devcell/windows/<stackTag>/
func TemplateVMName ¶
TemplateVMName returns the QEMU VM name for a built template.
func ValidateISO ¶
ValidateISO checks that a file carries a recognised disc format by reading the volume descriptor at sector 16 (offset 0x8001). Both ISO 9660 (CD001) and UDF (BEA01/NSR02/NSR03) are accepted — Windows ARM64 ISOs built by UUP dump are pure UDF.
func VirtioISOPath ¶
VirtioISOPath returns the path to the cached VirtIO drivers ISO.
func VirtiofsdCommand ¶
VirtiofsdCommand builds the daemon invocation for a project share. --sandbox none: the default sandbox needs user namespaces the devcell container does not have. The caller owns the process — virtiofsd exits whenever its client disconnects, so it must be started fresh for every VM boot that mounts the share.
func VirtiofsdPath ¶
VirtiofsdPath resolves the host-side virtio-fs daemon: $DEVCELL_VIRTIOFSD, then PATH.
func WSLEngineFiles ¶
func WSLEngineFiles() []string
WSLEngineFiles returns the trimmed WSL engine payload, relative to the MSI's WSL directory (PFiles64/WSL in an msiextract layout).
Kept: the engine core plus the Linux kernel side — ~310 MB. Dropped: WSLg/RDP (incl. system.vhd, 411 MB), the wslsettings GUI (a .NET app, WinPE has no CLR anyway), msal auth and the language packs — ~650 MB that a headless WinPE guest cannot use.
func WSLReadyTestImageName ¶
WSLReadyTestImageName returns the versioned filename for a saved WSL-ready image: a guest that already has the virtio drivers, the project share, the WSL2 features and the WSL engine — everything up to the point where a distro can be imported. Checkpointing there turns a ~40-minute prelude into a ~1-minute boot when iterating on the distro itself.
func WaitForPortDown ¶
WaitForPortDown polls host:port until connections stop being accepted, returning true the moment the port is gone and false if it never goes down within window. Used after requesting a guest restart: Windows keeps accepting SSH through its shutdown grace period, so "the port answers" right after Restart-Computer proves nothing (run 20260802T094045 ran a stage against exactly such a dying session).
func WaitForSSH ¶
func WaitForSSH(host string, port uint16, timeout, interval time.Duration, obs Observer, vmState ...VMStateFunc) error
WaitForSSH polls until the SSH port accepts connections. If vmState is non-nil, returns immediately when the VM is no longer running.
func WatchSerialFor ¶
WatchSerialFor tails the serial log file and sends on the returned channel when marker appears. The goroutine exits when stop is closed. The channel is buffered so the caller can select on it without blocking the watcher.
func WatchSerialForDesktopBoot ¶
WatchSerialForDesktopBoot tails the serial log and fires when the firmware boots "Windows Boot Manager" for the Nth time. The first boot is the specialize pass (installs drivers, then reboots); the second is the final boot where OOBE runs, the user logs in, and the bootstrap script fires. Set n=2 for "desktop boot detected".
func WatchSerialForEFIShell ¶
WatchSerialForEFIShell tails the serial log file and sends on the returned channel when the firmware falls through to the EFI Interactive Shell. The goroutine exits when stop is closed. The channel is buffered so the caller can select on it without blocking the watcher.
func WatchSerialForStartupNSHFail ¶
WatchSerialForStartupNSHFail tails the serial log and fires when startup.nsh reports that BOOTAA64.EFI was not found on any filesystem. Use this instead of WatchSerialForEFIShell when the answer volume carries startup.nsh — it gives the script a chance to chainload the bootloader before declaring failure (CELL-427).
func WatchSerialForSyncException ¶
WatchSerialForSyncException tails the serial log and fires when the firmware reports a synchronous exception (instruction/data abort). This catches crashes like cdboot_noprompt translation faults that would otherwise sit silently until the test's overall deadline.
func WhitePixelRatio ¶
WhitePixelRatio returns the fraction of near-white pixels. The Windows 11 Setup wizard is a large white window (~73% of the frame on the real UI).
func WindowsISOPath ¶
WindowsISOPath returns the path to the cached Windows ISO for a given language.
func WindowsPurpleRatio ¶
WindowsPurpleRatio returns the fraction of pixels matching the Windows 11 boot/setup backdrop (RGB 24,0,82 observed via QMP screendump). A mostly purple frame means the NT kernel has taken over the display; purple around a large white region means the Setup wizard is up.
func WritePIDFile ¶
func WritePortMeta ¶
Types ¶
type AllocatedPorts ¶
AllocatedPorts holds the bunk-derived port strings for SSH, VNC, and RDP.
func AllocatePorts ¶
func AllocatePorts(portPrefix string, taken map[int]struct{}) AllocatedPorts
AllocatePorts computes SSH, VNC, and RDP ports using the same bunk-based scheme as the Docker runner. portPrefix is SESSION_PORT_PREFIX + bunk. The taken map (may be nil) lists ports already in use by other processes.
func (AllocatedPorts) RDPPortUint16 ¶
func (p AllocatedPorts) RDPPortUint16() uint16
RDPPortUint16 returns the RDP port as uint16.
func (AllocatedPorts) SSHPortUint16 ¶
func (p AllocatedPorts) SSHPortUint16() uint16
SSHPortUint16 returns the SSH port as uint16.
func (AllocatedPorts) VNCPortUint16 ¶
func (p AllocatedPorts) VNCPortUint16() uint16
VNCPortUint16 returns the VNC port as uint16.
type BlockDeviceStats ¶
BlockDeviceStats holds I/O counters for one block device from query-blockstats.
type DiscoveredVM ¶
DiscoveredVM represents a running QEMU VM found during discovery.
func DiscoverRunningVMs ¶
func DiscoverRunningVMs(home string) []DiscoveredVM
DiscoverRunningVMs scans ~/.devcell/<cell>/windows/ directories for running QEMU VMs with valid PID files and port metadata.
type Engine ¶
type Engine struct {
Spec Spec
// contains filtered or unexported fields
}
Engine implements vm.Engine for QEMU Windows VMs.
type FirmwareFault ¶
type FirmwareFault struct {
SP string
ELR string
ESR string
FAR string
Description string // e.g. "Data abort: Translation fault, second level"
}
FirmwareFault is EDK2's CPU exception dump, which it prints to the serial console before giving up. Its presence means the *firmware* died — not the guest OS — so no amount of further waiting can help.
func ParseFirmwareFault ¶
func ParseFirmwareFault(serial string) (FirmwareFault, bool)
ParseFirmwareFault extracts the crash dump from a serial log, if present.
func (FirmwareFault) Summary ¶
func (f FirmwareFault) Summary() string
Summary interprets the dump in one line.
The load-bearing observation for the 2026-07-30 install failure: the faulting address sits just *below* the stack pointer, and the stack pointer sits at the very bottom of guest RAM (QEMU virt puts RAM at 0x40000000, with the PCIe ECAM window immediately below it). That is a stack that ran off the bottom of its region — not a stray pointer.
type GDBConn ¶
type GDBConn struct {
// contains filtered or unexported fields
}
GDBConn is a minimal GDB Remote Serial Protocol client that can read and write guest virtual memory through QEMU's built-in GDB stub.
func GDBDial ¶
GDBDial connects to QEMU's GDB stub at the given address (e.g. "tcp:localhost:1234" or "unix:/path/to/sock"). It sends the initial handshake and returns a ready-to-use connection.
func (*GDBConn) ReadMemory ¶
ReadMemory reads len bytes from virtual address addr.
func (*GDBConn) ReadRegister ¶
ReadRegister reads a single register by index via the 'p' packet. AArch64 QEMU register indices: x0-x30 = 0-30, SP = 31, PC = 32, CPSR = 33, V0-V31 = 34-65, FPSR = 66, FPCR = 67, ELR_EL1 = 68 (0x44), ... system regs vary by QEMU version.
func (*GDBConn) ReadRegisters ¶
ReadRegisters reads all general-purpose registers via the 'g' packet. Returns the raw hex-encoded register dump.
func (*GDBConn) ReadUint16LE ¶
ReadUint16LE reads a little-endian uint16 from the given virtual address.
func (*GDBConn) RemoveBreakpoint ¶
RemoveBreakpoint removes a software breakpoint (z0) at addr.
func (*GDBConn) SetBreakpoint ¶
SetBreakpoint inserts a software breakpoint (Z0) at addr.
func (*GDBConn) WaitBreak ¶
WaitBreak waits for the stub to report a stop event (breakpoint hit, signal, etc). Returns the raw stop-reply packet body.
func (*GDBConn) WriteMemory ¶
WriteMemory writes data to virtual address addr.
func (*GDBConn) WriteRegister ¶
WriteRegister writes a single register by index via the 'P' packet.
type GoSSHDServeData ¶
type GoSSHDServeData struct {
// SSHExe is the server payload's filename on the agent volume.
SSHExe string
// ServerLog is the server's log filename on the agent volume.
ServerLog string
}
GoSSHDServeData is the template context the "gosshd-serve" partial needs. Embed it in a script's own data struct to include the partial:
data := struct {
GoSSHDServeData
Banner string
}{GoSSHDServeData: DefaultGoSSHDServeData(), Banner: "..."}
func DefaultGoSSHDServeData ¶
func DefaultGoSSHDServeData() GoSSHDServeData
DefaultGoSSHDServeData wires the partial to the payload names the host stages.
type GuestStage ¶
type GuestStage struct {
Name string
// Component groups stages that belong to the same subsystem (provisioning,
// drivers, virtiofs, WSL, nix…). All stages of a component share one log,
// so "what happened with WSL" is one file rather than three.
Component string
// Script runs in the guest over SSH (already transport-safe once wrapped
// in PowerShellEncodedCommand). Legacy path: Go-rendered PowerShell.
Script string
// ScriptFile names a real PowerShell file in the embedded guest tree
// (e.g. "wsl2-enable.ps1"), delivered on the control volume and invoked
// by path. Preferred over Script: real files are lintable, runnable
// standalone on a guest, and carry no Go interpolation (CELL-402).
// Args are passed as PowerShell parameters, not string-substituted.
ScriptFile string
Args map[string]string
// Retries is how many extra attempts the caller should make. Zero means
// one attempt.
Retries int
// RebootAfter: the caller must reboot the guest and wait for SSH to come
// back before the next stage.
RebootAfter bool
// ToleratesDisconnect: the stage's work is expected to tear down the SSH
// session (e.g. the WSL engine MSI). A "closed by remote host" failure is
// not a verdict; the next stage verifies the outcome.
ToleratesDisconnect bool
}
GuestStage is one unit of guest-side work: a PowerShell script run over SSH, plus the contract it imposes on its caller. It is the single stage type for everything the host asks a Windows guest to do — build provisioning and dev-env setup alike — so every such pipeline is one table, named and logged by the same rules.
func DefaultProvisionSteps ¶
func DefaultProvisionSteps(pubKey, username, password string) []GuestStage
DefaultProvisionSteps returns the build-time provisioning pipeline for a new Windows VM, as a GuestStage table — the same shape as DevEnvStages, so both pipelines are named, logged and driven by one set of rules.
func DevEnvStages ¶
func DevEnvStages(user, tag, drive string) []GuestStage
DevEnvStages returns the ordered dev-env provisioning pipeline. Every stage transcripts itself onto the FAT log volume (see BuildDevEnvLogVolume) in addition to its SSH output.
type ISOMetadata ¶
ISOMetadata holds parsed information from an ISO filename.
func ParseISOFilename ¶
func ParseISOFilename(name string) ISOMetadata
ParseISOFilename extracts version and architecture from a Windows ISO filename.
type KVMHostCaps ¶
type KVMHostCaps struct {
APIVersion int
PMUv3 bool
IPABits int // 0 = cap unsupported → architected default of 40
MaxVCPUs int
}
KVMHostCaps holds the host KVM properties that decide whether a Windows ARM64 guest can run at all.
PMUv3 is the load-bearing one: a nested host (macOS vz → Colima) gets no PMU from Apple's hypervisor, the Linux kernel then has no arm_pmu driver, and KVM cannot virtualize a PMU it does not have. Guests on such a host see ID_AA64DFR0_EL1.PMUVer=0 and take an UNDEF on any PMU register access.
func QueryKVMHostCaps ¶
func QueryKVMHostCaps(devPath string) (KVMHostCaps, error)
QueryKVMHostCaps interrogates /dev/kvm directly. It needs only the device fd — no VM is created — so it is safe to run before (or while) a guest is up.
func (KVMHostCaps) Summary ¶
func (c KVMHostCaps) Summary() string
Summary is the one-line form for run-info.txt and the test log.
func (KVMHostCaps) WindowsBootBlocker ¶
func (c KVMHostCaps) WindowsBootBlocker() string
WindowsBootBlocker returns a non-empty reason when this host's KVM cannot boot Windows ARM64 regardless of QEMU configuration, or "" when no known blocker applies.
Root-caused 2026-07-30 (TestWindowsISOBoot_KVM stall diagnostics): bootmgr reads PMCR_EL0 unconditionally; on a PMU-less vCPU KVM injects UNDEF and bootmgr parks in its `b .` panic vector (slot +0x200, DAIF masked). Reproduced without KVM: TCG + pmu=off hangs identically, TCG + PMU boots. No QEMU flag can add a PMU that the host kernel does not have.
type LaunchAction ¶
type LaunchAction int
LaunchAction is one VM-acquisition step.
const ( ActionUseLocal LaunchAction = iota // instance disk exists locally ActionBuild // build from Windows ISO (full install + provision) ActionDryRun // dry-run mode, no VM work ActionAttach // attach to already-running VM ActionClone // clone existing template to instance )
func DecideLaunchActions ¶
func DecideLaunchActions(in LaunchInputs) []LaunchAction
DecideLaunchActions returns the ordered fallback sequence.
DryRun → [DryRun] ExplicitBuild → [Build] VMRunning → [Attach] DiskExists + valid + prov'd → [UseLocal] DiskExists + invalid/unprov'd → [Build] (corrupt leftovers) TemplateExists + valid → [Clone] cold start → [Build]
type LaunchInputs ¶
type LaunchInputs struct {
DryRun bool // --dry-run set
ExplicitBuild bool // --force set, force rebuild
DiskExists bool // instance disk image exists at expected path
DiskSizeBytes int64 // instance disk file size; 0 means unchecked
TemplateExists bool // template disk image exists (ready to clone)
TemplateSizeBytes int64 // template disk file size; 0 means unchecked
VMRunning bool // existing QEMU process detected via PID file + QMP
Provisioned bool // .provisioned marker exists
}
LaunchInputs are the inputs to DecideLaunchActions.
type NopObserver ¶
type NopObserver struct{}
NopObserver silently discards all events.
func (NopObserver) Logf ¶
func (NopObserver) Logf(string, ...any)
func (NopObserver) Progress ¶
func (NopObserver) Progress(float64, string)
type Observer ¶
type Observer interface {
Logf(format string, args ...any)
Progress(fraction float64, message string)
}
Observer receives progress events from long-running QEMU operations.
type PSTATE ¶
type PSTATE struct {
Raw uint64
EL int // exception level, 0-3
SPSel bool // true = SP_ELx ("h"), false = SP_EL0 ("t")
// Interrupt masks.
D bool // debug
A bool // SError
I bool // IRQ
F bool // FIQ
// Condition flags.
N, Z, C, V bool
}
PSTATE is a decoded AArch64 PSTATE word.
It exists to separate two states that a PC-based stall detector cannot tell apart on its own:
- DAIF clear at EL1 with a static PC — the guest is parked in WFI waiting for an interrupt. Normal. It will wake.
- DAIF fully masked at EL1 with a static PC — the guest took an unrecoverable synchronous exception and its handler is spinning (`b .`) with interrupts off. Nothing will ever wake it.
The KVM firmware hang is the second: PSTATE=600003c5.
func DecodePSTATE ¶
DecodePSTATE parses a PSTATE value as printed by QEMU's "info registers" (hex, no 0x prefix).
func (PSTATE) AllInterruptsMasked ¶
AllInterruptsMasked reports whether every DAIF bit is set.
func (PSTATE) MaskedFlags ¶
MaskedFlags lists the masked interrupt types in DAIF order, e.g. "DAIF" when all are masked or "IF" when only IRQ and FIQ are.
type PortMeta ¶
type PortMeta struct {
SSHPort uint16 `json:"ssh"`
VNCPort uint16 `json:"vnc"`
RDPPort uint16 `json:"rdp"`
}
PortMeta records the allocated ports for a running QEMU VM instance. Written to ports.json in the instance directory for discovery by cell vnc/rdp.
func ReadPortMeta ¶
type ScreenSource ¶
type ScreenSource string
screenshotName is `<datetimeISO>-<screenName>-<id>.png`: the capture instant (UTC, ISO 8601 basic — no colons, filename-safe everywhere), the verdict with every measured ratio, then the poll number. Time first makes a directory listing sort chronologically and lets a frame be correlated with guest-side logs by wall clock, which a bare poll number cannot.
The verdict and ratios stay in the name for triage: an earlier form encoded ScreenSource names how a frame was acquired. Frames from different sources see different surfaces — a QMP screendump reads the emulated framebuffer, an RDP capture reads what the guest renders into a session — so each gets its own directory and its own sequence.
const ( ScreenSourceQMP ScreenSource = "qmp" ScreenSourceRDP ScreenSource = "rdp" )
type Spec ¶
type Spec struct {
VMName string
CPUs uint
MemoryGB uint64
DiskPath string // path to qcow2 disk
FirmwarePath string // path to EDK2 UEFI firmware
VarsPath string // path to UEFI variable store (per-VM copy)
VirtioISO string // path to VirtIO drivers ISO
SSHPort uint16 // forwarded port for SSH
VNCPort uint16 // forwarded port for VNC (0 = disabled)
RDPPort uint16 // forwarded port for RDP (0 = disabled)
SSHHost string // SSH host (default "127.0.0.1")
SSHUser string // guest username (defaults to the host $USER)
SSHKeyPath string // path to SSH private key
MACAddr string // deterministic MAC address
Binary string // agent binary (e.g. "claude", "cmd.exe")
DefaultFlags []string
UserArgs []string
EnvVars []string // KEY=VALUE pairs
ProjectDir string // host project directory
DisplayType string // "none", "cocoa", "sdl" (default "none")
QMPSocketDir string // directory for QMP socket; defaults to /tmp
// Accel overrides the QEMU accelerator (e.g. "tcg,thread=multi" to force
// software emulation). Empty means ApplyDefaults resolves it — see
// ResolveAccel.
Accel string
// KVM carries the `[cell] kvm` config intent: the launcher was asked to
// pass /dev/kvm into the container. It is intent only — ApplyDefaults still
// probes the device before selecting KVM.
KVM bool
// AccelReason explains the resolved Accel in one line, for the launch log.
// Set by ApplyDefaults; never an input.
AccelReason string
// CPU overrides the -cpu string (e.g. "max,pauth-impdef=on,pmu=off").
// Empty means cpuType picks the per-accelerator default. Exists so a test
// can vary exactly one CPU feature against an otherwise identical machine
// — the mechanism behind the PMU evidence table in boot_test.go.
CPU string
// SerialLogPath, when set, redirects the guest serial console to this file.
SerialLogPath string
// GuestProgressLogPath, when set, attaches a virtio-serial port
// (ProgressPortName) wired to this file. The guest writes progress via
// \\.\Global\<ProgressPortName>; this works on ARM64 where pci-serial
// 16550 devices don't map to user-mode COMx (CELL-430).
GuestProgressLogPath string
// GuestStructuredLogPath, when set, attaches a second virtio-serial port
// (StructuredPortName) for JSONL output. The guest writes structured
// events to \\.\Global\<StructuredPortName>; the host captures them
// as build.jsonl for machine-parseable progress and metrics.
GuestStructuredLogPath string
// DiskCacheMode sets the qcow2 cache policy (e.g. "unsafe" to drop guest
// flushes). Empty keeps QEMU's safe default. "unsafe" makes the image
// worthless if the host dies mid-run, so it is only for throwaway VMs
// such as the install test, where it removes a large TCG cost.
DiskCacheMode string
// NoReboot stops the VM instead of rebooting it — useful when a reboot
// marks the end of a phase you want to observe.
NoReboot bool
// GuestAgentSocketPath, when set, attaches a virtio-serial port named
// org.qemu.guest_agent.0 (the name qemu-ga looks for by convention) wired
// to a host unix socket. On ARM64 the agent binary is the x64 MSI running
// under Win11's emulation (no native build exists — see VIRTIO.md), but
// the channel wiring is the standard one.
GuestAgentSocketPath string
// VirtioFSSocketPath + VirtioFSTag, when both set, attach a
// vhost-user-fs device backed by a virtiofsd socket. The tag is what the
// guest mounts by. Requires shareable guest RAM (memory-backend-memfd),
// which BuildRunCommand adds alongside the device.
VirtioFSSocketPath string
VirtioFSTag string
// NestedVirt asks for the machine features Windows' own hypervisor needs
// in order to launch inside the guest: EL2 plus a GICv3 with ITS and a
// secure world. Opt-in, because it changes the boot environment of an
// already-installed Windows and the install path is proven without it.
NestedVirt bool
// SecureWorld adds secure=on: Arm Security Extensions (TrustZone), which
// gives the machine an EL3 and a secure flash bank. The firmware in
// pflash then *becomes* the secure-world firmware and is entered at EL3 —
// a normal-world EDK2 build cannot do that job. Separate from NestedVirt
// so the two can be tested apart.
SecureWorld bool
// FirmwareKernel loads the UEFI firmware with -kernel instead of pflash.
// Under secure=on this is what lets a stock, normal-world EDK2 work:
// QEMU's own ARM boot stub takes the EL3 entry and drops the payload to
// non-secure. Firmware-in-pflash has no such stub, so the same binary is
// entered at EL3 and hangs. Costs the pflash NVRAM store.
FirmwareKernel bool
// DiskBus selects the system disk controller: "nvme" (default, inbox
// driver, what our installs use) or "scsi" (virtio-scsi + scsi-hd, what
// the proven Hyper-V config uses).
DiskBus string
// CDBus selects the CD/ISO attachment: "usb" (default, usb-storage on
// xhci — inbox USBSTOR, no extra driver) or "scsi" (scsi-cd on a
// dedicated virtio-scsi-pci controller — needs vioscsi drvload in WinPE).
CDBus string
// MachineType overrides the -machine string (e.g. "virt,highmem=on").
// Empty means machineType() picks the per-accelerator default.
MachineType string
// LogVolumePath, when set, attaches a raw FAT image as removable USB
// storage for guest-written logs — the run-time counterpart of the
// install's answer volume (see BuildDevEnvLogVolume).
LogVolumePath string
// DevcellWimImg, when set, attaches a FAT volume containing the
// custom devcell.wim as an additional USB drive during install. Windows
// Setup reads from <Path> in autounattend.xml to find it. The volume
// is a raw or qcow2 FAT image.
DevcellWimImg string
}
Spec holds everything needed to configure and connect to a QEMU Windows VM.
func FinalizeSpec ¶
FinalizeSpec derives the dev-env boot from a build spec: the same guest (disk, network identity, credentials) on the EL3 machine — secure=on with a kernel-loaded relocatable firmware — which is what lets Windows' own hypervisor, and therefore WSL2, run (docs/spec/QEMU-ARM64-WINDOWS11-WSL2-NIX.md §2.2).
The install boot and this one are intentionally different machines: the installer is proven on the plain pflash machine, the WSL2 stack on this one. Only the boot environment changes; everything identifying the guest is carried over.
func (*Spec) ApplyDefaults ¶
func (s *Spec) ApplyDefaults()
ApplyDefaults fills in zero-value fields with sensible defaults.
type StageRunOptions ¶
type StageRunOptions struct {
// SSHUser and SSHKeyPath authenticate to the guest.
SSHUser string
SSHKeyPath string
// LogDir receives one log per component, named by StageLogNames. Empty
// disables file logging (output is still returned to the observer).
LogDir string
// StageTimeout bounds a single stage. Zero means DefaultStageTimeout.
StageTimeout time.Duration
// Reboot restarts the guest and returns once it answers SSH again.
// Required if any stage sets RebootAfter.
Reboot func(ctx context.Context, reason string) error
// BeforeStage and AfterStage let a caller checkpoint, screenshot or
// annotate around a stage. Both are optional; an error from either fails
// the run.
BeforeStage func(ctx context.Context, idx int, stage GuestStage) error
AfterStage func(ctx context.Context, idx int, stage GuestStage, output string) error
Observer Observer
}
RunGuestStages executes a GuestStage table against a running guest over SSH.
This is the one place that knows how to drive guest-side work: retries, reboots, stages whose own command tears down the session, per-stage deadlines, and component-grouped logs streamed to disk while they run. `cell build` and the dev-env test both call it, so what the test proves is what users get — the two had drifted into separate loops, and the CLI's was the weaker one (no reboots, no timeout, nothing readable until exit).
The caller owns the VM. Rebooting and waiting for SSH are supplied as callbacks because only the caller knows how its VM is started and watched.
type StallSignal ¶
type StallSignal struct {
ScreenHash uint64 // hash of the raw screendump
ReadBytes int64 // cumulative bytes read across block devices
PC string // vCPU program counter, as reported by "info registers"
}
StallSignal is one poll's worth of liveness evidence.
type StallTracker ¶
type StallTracker struct {
// contains filtered or unexported fields
}
StallTracker counts consecutive polls in which nothing observable changed.
func (*StallTracker) Consecutive ¶
func (s *StallTracker) Consecutive() int
Consecutive returns the current unchanged-poll count.
func (*StallTracker) Observe ¶
func (s *StallTracker) Observe(sig StallSignal) int
Observe records a poll and returns the number of consecutive unchanged polls seen so far.
It returns 0 until the guest has read at least one byte: before boot has touched the media, identical polls are expected and say nothing about liveness. That guard is what keeps a slow start from being called a hang.
func (*StallTracker) Reset ¶
func (s *StallTracker) Reset()
Reset zeroes the stall counter, e.g. after a runtime intervention that is expected to unblock the guest.
func (*StallTracker) Stalled ¶
func (s *StallTracker) Stalled(threshold int) bool
Stalled reports whether at least threshold consecutive unchanged polls have been observed.
type VM ¶
type VM struct {
// contains filtered or unexported fields
}
VM wraps a running QEMU process.
func NewVM ¶
NewVM creates a VM handle. Call Start() to launch it. If pidDir is non-empty, Start() writes a PID file there and process exit cleans it up.
func (*VM) ExitError ¶
ExitError returns the error from QEMU process exit (nil if still running or clean exit).
func (*VM) LastOutput ¶
LastOutput returns the tail of QEMU's captured stdout+stderr.
func (*VM) QMPSockPath ¶
QMPSockPath returns the QMP unix socket path for this VM.
func (*VM) StartInstall ¶
StartInstall launches the QEMU process for Windows installation.
func (*VM) StateString ¶
StateString returns the VM state as a string (for VMStateFunc compatibility).
type VMState ¶
type VMState string
VMState represents the current state of a QEMU VM.
func QueryVMState ¶
type VMStateFunc ¶
type VMStateFunc func() VMState
VMStateFunc returns the current VM state. WaitForSSH uses it to bail early when the QEMU process exits (e.g. drive collision, missing firmware).
type WimBuilderResult ¶
type WimBuilderResult struct {
// finished. Contains devcell.wim and the builder log.
SharedImg string
// AgentOutput is the agent's combined stdout/stderr (if available).
AgentOutput string
}
WimBuilderResult holds the output of a successful WIM builder run.
type WimBuilderSpec ¶
type WimBuilderSpec struct {
// Spec is the base QEMU spec (machine, firmware, etc.).
Spec Spec
// WinPEISO is the bootable WinPE ISO (may be stock or custom-injected).
WinPEISO string
SharedImg string
// WindowsISO is the full Windows ISO (provides install.wim).
WindowsISO string
// VirtIOISO is the virtio-win ISO (provides driver directories for
// DISM /Add-Driver). Required when any WimPrepOp uses the Driver field.
VirtIOISO string
// EFIBootLoader is the raw bytes of BOOTAA64.EFI. When set and CDBus
// is "scsi", SharedVolumeFiles ships it at /EFI/BOOT/BOOTAA64.EFI
// alongside startup.nsh so the firmware can chainload WinPE from the
// FAT volume after failing to read ISO9660 on the SCSI CD.
EFIBootLoader []byte
}
WimBuilderSpec configures a WIM builder WinPE boot. Callers populate this and pass it to BuildWimBuilderArgv to get a QEMU command line.
type WriteProgressTracker ¶
type WriteProgressTracker struct {
// Window is how long a guest may write nothing before it counts as stalled.
// It must exceed the longest legitimate pause: Windows goes quiet for a few
// minutes at a time under TCG, so minutes, not seconds.
Window time.Duration
// contains filtered or unexported fields
}
WriteProgressTracker watches cumulative bytes written to the guest's disk.
It answers a narrower question than StallTracker — "is the install making progress?" rather than "is the guest alive?" — and needs only one signal, which makes it usable from the CLI without QMP register dumps. Windows Setup writes continuously, so a full window with no new bytes means the guest never got as far as applying the image: wrong boot device, dead firmware, or Setup exiting before it started.
func (*WriteProgressTracker) Observe ¶
func (w *WriteProgressTracker) Observe(written int64, elapsed time.Duration) bool
Observe records cumulative bytes written at elapsed time since start, and reports whether the guest has now been silent for longer than Window.
func (*WriteProgressTracker) Reason ¶
func (w *WriteProgressTracker) Reason() string
Reason describes the stall in the terms it was measured in.
Source Files
¶
- accel.go
- command.go
- devenv.go
- devenvlogs.go
- discover.go
- disk.go
- download.go
- engine.go
- finalize.go
- firmware.go
- firmware_fault.go
- gdb.go
- gosshd_payload.go
- gosshd_serve.go
- guestfs.go
- interactive.go
- keepalive.go
- kvmcaps.go
- launch_decision.go
- observer.go
- pidfile.go
- portmeta.go
- ports.go
- preflight.go
- prep_wim.go
- progress_watch.go
- projectsync.go
- provision.go
- pstate.go
- qcow2.go
- qmp.go
- reboot.go
- screenshot.go
- serial_watch.go
- spec.go
- ssh.go
- sshable.go
- stageinvoke.go
- stagerunner.go
- stall.go
- transplant_wsl.go
- vector.go
- vm.go