Summary
quicksand_core/qemu/platform.py appends kernel-irqchip=off to the WHPX accelerator argument
unconditionally. QEMU rejects that on aarch64 hosts, so Quicksand cannot start a VM at all on Windows
ARM64, even though WHPX is available and the aarch64 guest images exist.
The failure surfaces as VM process exited unexpectedly (exit code: 1) with an empty QEMU stderr, which
makes it quite hard to trace back to the accelerator flag.
Repro
quicksand_core-0.12.1, quicksand-qemu 0.5.10, quick-sandbox 0.11.15
QEMU 10.2.92 (v11.0.0-rc2), Python 3.12.10 (ARM64)
Windows 11 Enterprise 26200, Snapdragon X (Surface Pro 11), WHPX enabled
Minimal reproduction with the bundled QEMU — current behaviour:
$ qemu-system-aarch64.exe -machine virt -accel "whpx,kernel-irqchip=off" -cpu max -m 512 \
-display none -nodefaults -no-user-config -S -monitor none -serial none
qemu-system-aarch64.EXE: -accel whpx,kernel-irqchip=off: WHPX: on Arm, only kernel-irqchip=on is currently supported
qemu-system-aarch64.EXE: -accel whpx,kernel-irqchip=off: failed to initialize whpx: Invalid argument
EXIT=1
Same command without the flag — initializes fine and stays paused at -S:
$ qemu-system-aarch64.exe -machine virt -accel whpx -cpu max -m 512 \
-display none -nodefaults -no-user-config -S -monitor none -serial none
(no output; process running)
Cause
quicksand_core/qemu/platform.py (0.12.1), around line 224:
if accelerator:
accel_arg = accelerator.value
if accelerator == Accelerator.WHPX:
# kernel-irqchip=off routes interrupts through userspace. WHPX's
# in-kernel irqchip combined with the guest's noapic boot param
# (our IO-APIC workaround) delivers device interrupts unreliably:
# later `mount -t cifs` operations hang in the guest kernel until
# they time out. This was previously only applied when nested
# (baseboard == "Microsoft Corporation"), but bare-metal Windows
# hosts need it too, so apply it for WHPX unconditionally.
accel_arg = "whpx,kernel-irqchip=off"
cmd.extend(["-accel", accel_arg])
As the comment itself explains, this is a workaround for the x86 noapic boot parameter
(WindowsConfig.extra_kernel_params returns ["noapic"], an IO-APIC concept that doesn't apply to the
Arm virt machine). On Arm it isn't needed, and QEMU refuses it outright.
Suggested fix
Scope the flag to x86_64. arch_type is already the module's idiom for this
(self.arch.machine_type, self.arch.qemu_suffix, etc.), but note that Architecture is not currently
imported in platform.py — only ArchitectureConfig, BaseArchitectureConfig and MachineType are.
from .arch import (
+ Architecture,
ArchitectureConfig,
BaseArchitectureConfig,
MachineType,
)
if accelerator:
accel_arg = accelerator.value
if accelerator == Accelerator.WHPX:
# kernel-irqchip=off routes interrupts through userspace. WHPX's
# in-kernel irqchip combined with the guest's noapic boot param
# (our IO-APIC workaround) delivers device interrupts unreliably:
# later `mount -t cifs` operations hang in the guest kernel until
# they time out. This was previously only applied when nested
# (baseboard == "Microsoft Corporation"), but bare-metal Windows
# hosts need it too, so apply it for WHPX unconditionally.
- accel_arg = "whpx,kernel-irqchip=off"
+ # Arm hosts only support kernel-irqchip=on, and the noapic
+ # workaround above is x86-only, so scope this to x86_64.
+ if self.arch.arch_type == Architecture.X86_64:
+ accel_arg = "whpx,kernel-irqchip=off"
cmd.extend(["-accel", accel_arg])
x86_64 behaviour is byte-identical — the flag is still applied there.
Verified
With that change applied to 0.12.1 in a local venv, a full boot on Windows ARM64 succeeds with hardware
acceleration:
VM config: memory=6G, cpus=1, port_forwards=10 ports
VM started successfully (~5s)
Init setup complete.
QuicksandBrowserManager ready: 5 slots
QEMU command line becomes -accel whpx -machine virt, and the guest boots at hardware speed rather than
falling back to TCG. This was via magentic-ui's Quicksand sandbox, using the published
quicksand-cua==0.3.11 / quicksand-agent==0.4.9 / quicksand-ubuntu==0.9.11 win_arm64 fat wheels —
so no new images are needed; the existing ARM64 image chain works as-is once the accelerator flag is fixed.
Separate, optional observation — please treat independently
A few lines above, WHPX is missing from the hardware-acceleration tuple:
has_hw_accel = accelerator in (Accelerator.KVM, Accelerator.HVF)
cmd.extend(self.arch.build_cpu_args(has_hw_accel))
so WHPX never gets -cpu host and falls back to default_cpu_model.
This is not required to fix the bug above — on ARM64 the fallback is "max", which is in WHPX's accepted
set (cortex-a53, cortex-a57, host, max), and the VM boots fine with it (verified at ~5s).
I'm flagging it only because it looks unintentional. It does change x86 behaviour, so it needs your
testing rather than a blind fix: X86_64Config.default_cpu_model is "", meaning build_cpu_args currently
emits no -cpu argument at all for WHPX on x86. Adding Accelerator.WHPX to that tuple would start
emitting -cpu host there. I have no x86 Windows host to validate that on, so I'd suggest keeping it as a
separate commit from the blocker fix above.
(For what it's worth, adding it on ARM64 works — -accel whpx -cpu host boots correctly.)
Context
Found while testing whether MagenticLite's desktop build can run on Windows ARM64. Happy to test any patch
on this hardware — just ping me.
Summary
quicksand_core/qemu/platform.pyappendskernel-irqchip=offto the WHPX accelerator argumentunconditionally. QEMU rejects that on aarch64 hosts, so Quicksand cannot start a VM at all on Windows
ARM64, even though WHPX is available and the aarch64 guest images exist.
The failure surfaces as
VM process exited unexpectedly (exit code: 1)with an empty QEMU stderr, whichmakes it quite hard to trace back to the accelerator flag.
Repro
Minimal reproduction with the bundled QEMU — current behaviour:
Same command without the flag — initializes fine and stays paused at
-S:Cause
quicksand_core/qemu/platform.py(0.12.1), around line 224:As the comment itself explains, this is a workaround for the x86
noapicboot parameter(
WindowsConfig.extra_kernel_paramsreturns["noapic"], an IO-APIC concept that doesn't apply to theArm
virtmachine). On Arm it isn't needed, and QEMU refuses it outright.Suggested fix
Scope the flag to x86_64.
arch_typeis already the module's idiom for this(
self.arch.machine_type,self.arch.qemu_suffix, etc.), but note thatArchitectureis not currentlyimported in
platform.py— onlyArchitectureConfig,BaseArchitectureConfigandMachineTypeare.from .arch import ( + Architecture, ArchitectureConfig, BaseArchitectureConfig, MachineType, )if accelerator: accel_arg = accelerator.value if accelerator == Accelerator.WHPX: # kernel-irqchip=off routes interrupts through userspace. WHPX's # in-kernel irqchip combined with the guest's noapic boot param # (our IO-APIC workaround) delivers device interrupts unreliably: # later `mount -t cifs` operations hang in the guest kernel until # they time out. This was previously only applied when nested # (baseboard == "Microsoft Corporation"), but bare-metal Windows # hosts need it too, so apply it for WHPX unconditionally. - accel_arg = "whpx,kernel-irqchip=off" + # Arm hosts only support kernel-irqchip=on, and the noapic + # workaround above is x86-only, so scope this to x86_64. + if self.arch.arch_type == Architecture.X86_64: + accel_arg = "whpx,kernel-irqchip=off" cmd.extend(["-accel", accel_arg])x86_64 behaviour is byte-identical — the flag is still applied there.
Verified
With that change applied to 0.12.1 in a local venv, a full boot on Windows ARM64 succeeds with hardware
acceleration:
QEMU command line becomes
-accel whpx -machine virt, and the guest boots at hardware speed rather thanfalling back to TCG. This was via magentic-ui's Quicksand sandbox, using the published
quicksand-cua==0.3.11/quicksand-agent==0.4.9/quicksand-ubuntu==0.9.11win_arm64fat wheels —so no new images are needed; the existing ARM64 image chain works as-is once the accelerator flag is fixed.
Separate, optional observation — please treat independently
A few lines above, WHPX is missing from the hardware-acceleration tuple:
so WHPX never gets
-cpu hostand falls back todefault_cpu_model.This is not required to fix the bug above — on ARM64 the fallback is
"max", which is in WHPX's acceptedset (
cortex-a53,cortex-a57,host,max), and the VM boots fine with it (verified at ~5s).I'm flagging it only because it looks unintentional. It does change x86 behaviour, so it needs your
testing rather than a blind fix:
X86_64Config.default_cpu_modelis"", meaningbuild_cpu_argscurrentlyemits no
-cpuargument at all for WHPX on x86. AddingAccelerator.WHPXto that tuple would startemitting
-cpu hostthere. I have no x86 Windows host to validate that on, so I'd suggest keeping it as aseparate commit from the blocker fix above.
(For what it's worth, adding it on ARM64 works —
-accel whpx -cpu hostboots correctly.)Context
Found while testing whether MagenticLite's desktop build can run on Windows ARM64. Happy to test any patch
on this hardware — just ping me.