Skip to content

WHPX: unconditional kernel-irqchip=off prevents VM boot on Windows ARM64 #34

Description

@shi-weili

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions