Goal: get WSL2 + NixOS-WSL running inside the Windows 11 ARM64 debug VM, booted
with plain qemu-system-aarch64 on the user's Mac (M4 Pro, macOS 26.5.2) at
native speed. VM disk = UTM bundle test/testdata/Windows.utm/Data/D76FB0BC-D7CC-4481-A6B6-492BEB4D834B.qcow2.
WSL2 under hvf nested virt is impossible today. Chain of evidence:
- QEMU 11.0.93 (= 11.1-rc) installed on the Mac; nested virt (EL2) enabled via
-M virt,virtualization=on,gic-version=3. - EDK2 hangs at "Start boot option" under nested — known upstream issue in the
hvf nested series; workaround
-boot menu=on,splash-time=0(APPLIED in Taskfile, works — Windows boots to desktop under nested config). - Alpine control VM (
task debug:alpine:start): kernel printsCPU: All CPU(s) started at EL2andkvm: Hyp nVHE mode initialized successfully→ nested EL2 WORKS for Linux, but in nVHE-only form. - Windows event log (every boot): Event 43
Hypervisor launch failed; EL2 not present.→ Windows' hypervisor requires VHE; Apple's nested API (macOS 26) is nVHE-only, no FEAT_NV1/VNCR.wsl --install --from-filealways failsHCS_E_HYPERV_NOT_INSTALLED. - Not fixable in QEMU: VHE is pervasive untrappable hardware behavior; hvf has no NV-style exits. Blocked on Apple exposing VHE-in-nested (Feedback Assistant) or QEMU hybrid hvf+tcg (multi-year).
- Practical paths:
ACCEL=tcg(whole WSL2 chain works, slow 10-25 min boot); or run NixOS directly under hvf (nested Linux works great); or wait for Apple. HypervisorPresent:Truefrom WMI is a red herring; event log is authoritative.- Tractable upstream patches if ever desired: tpm-tis HV_BAD_ARGUMENT under nested; EDK2 hang root cause (missing EL2 phys timer); clearer VHE error.
task debug:windows:start — boots UTM disk under hvf, 4 gates
(VNC→IP→SSH→qga), ~10s to green, IP 192.168.2.2, SMB share via dockurr/samba,
VNC 127.0.0.1:5907 pass vnc. Graceful stop verified: "guest shut down
cleanly" (qga path). Passwordless SSH: Mac key in guest's
administrators_authorized_keys; ssh dmitry@192.168.2.2 (password fallback:
rdp). Guest has staged %TEMP%\nixos.aarch64.wsl (577MB) +
C:\Users\Dmitry\nixos-import.ps1; WSL engine 2.7.11 installed; wsl --list
empty (expected — import blocked).
Taskfile changes this session (feature/wip, ALL UNCOMMITTED — git policy: never commit unless asked)
debug:windows:start: NESTED var (default 1; 0 = proven plain-virt boot with TPM); SECURE var (0 = qemu's plain EDK2 instead of UTM secure-code); version-gatedvirtualization=on,gic-version=3+-boot menu=on,splash-time=0when nested; TPM skipped when nested (tpm-tis = HV_BAD_ARGUMENT under EL2; safe: BitLocker Protection Off); NVMe serial truncatedcut -c1-20(QEMU 11.1 enforces spec); launch wrapped inif ! qemu…; then tail -5 qemu.log; fi(daemonize hides errors); pidfile pre-touched user-owned (no more root-owned leftovers); stop reads pidfile viacat || $SUDO cat.- NEW
debug:alpine:start/debug:alpine:stop: nested-virt litmus test. ISO.tmp/alpine-virt-aarch64.iso(downloaded, 80MB). SERIAL_PORT default 5910 → interactive serial on tcp:0.0.0.0:5910 (nc 127.0.0.1 5910); SERIAL_PORT= (empty) → file log + blocking wait for login prompt. NESTED=0 control boots at EL1. - Also earlier (pre-session summary): flake.nix devShell + swtpm; .gitignore
.tmp/; whole start/stop rewrite (pid lock, qmsg perl helper for QMP/qga — macOSnc -Unever returns data;env killnotkillin task scripts — mvdan/sh builtin no-ops).
- I can watch the VM myself from this container: VNC via
host.docker.internal:5907, python vncdotool (pip-installed, user site) captures screenshots → Read the png. Guest SSH direct:ssh -o BatchMode=yes dmitry@192.168.2.2works from container. - Repo
.tmp/is bind-mounted → I can read qemu.log/serial.log live. - Alpine serial over tcp: connect from container, send '\n' to wake getty, login root (no password), run dmesg.
- Windows over cmd-SSH:
&separators; PowerShell$_breaks through ssh quoting — use dism/findstr instead; strip UTF-16 withtr -d '\0\r'.
- All session changes uncommitted; user hasn't asked to commit.
- Optional next: NixOS import via
task debug:windows:start ACCEL=tcgrun (import script staged in guest); user hasn't decided. - deleted CONTINUE.md from a previous session was in git status (D CONTINUE.md); this file replaces it.