Running Windows 10 for ARM64 in a QEMU virtual machine

/images/2020-08-04_scrot.png

Since the development stages of Windows 10, Microsoft has been releasing a version of Windows that runs on 64-bit ARM (AArch64) based CPUs. Despite some hardware shipping with Windows 10 ARM 1 2 3 this port has received little attention and you can barely find programs that run on it.

Naturally, I wanted to try this out to see if it worked. And it turned out it does!

Getting the ISO

I'm not aware of any Microsoft page that lets you download an ARM64 ISO, so this part relies on community-made solutions instead.

I looked for the right ESD download link and used an ESD>ISO conversion script to get a bootable ISO (both found on the MDL forums).

Alternatively adguard's download page provides similar scripts that download and pack an ISO for you. However, in my experience these take a long time (hours) to finish.

There's one more important point:

I had no success booting version 2004 or 20H2 (specifically: 19041.388 / 19041.423) so I went with version 1909 (18363.592) instead.

Installation

Before we begin we also need:

  • the virtio driver ISO

  • an appropriately sized disk image (qemu-img create -f qcow2 disk.qcow2 64G)

  • QEMU_EFI.fd extracted from the edk2.git-aarch64 RPM found here

The qemu command line then looks as follows:

isoname=18363.592.200109-2016.19H2_RELEASE_SVC_REFRESH_CLIENTBUSINESS_VOL_A64FRE_DE-DE.ISO
virtio=~/Downloads/virtio-win-0.1.185.iso
qemu-system-aarch64 -M virt -cpu cortex-a53 -smp 4 -m 4096 \
        -device qemu-xhci -device usb-kbd -device usb-tablet \
        -drive file=disk.qcow2,if=virtio \
        -nic user,model=virtio \
        -drive file="$isoname",media=cdrom,if=none,id=cdrom -device usb-storage,drive=cdrom \
        -drive file="$virtio",media=cdrom,if=none,id=drivers -device usb-storage,drive=drivers \
        -bios QEMU_EFI.fd -device ramfb

You can then follow the installation process as normal. Before partitioning the disks the setup will ask you to load disk drivers, these can be found at viostor/w10/ARM64 on the virtio cdrom.

Qemu video output

The above command line already takes these limitations into account, these sections are for explanation only.

A previous blogpost on running Windows 10 ARM in QEMU has used a patched EDK2 to get support for standard VGA back in. It's not clear to me why EDK2 removed support if it was working, but this is not a solution I wanted to use either way.

It turns out 4 that the options on ARM are limited to virtio gpu and ramfb. Virtio graphics are Linux-only so that leaves ramfb.

Attaching disks with Qemu

Since the virt machine has no SATA controller we cannot attach a hard disk to the VM the usual way, I went with virtio here instead. It would have been possible to do this over usb-storage, this works out of the box and would have saved us all the work with virtio drivers (except for networking 5).

This also means something else (which has wasted me quite some time): You cannot use -cdrom.

If you do, EDK2 will boot the Windows CD fine but setup will ask you to load drivers early (because it cannot find its own CD). None of the virtio drivers can fix this situation, leaving you stuck with no clear indication what went wrong.

After installation

The onboarding process has a few hiccups (in particular device detection), if you retry it a few times it'll let you continue anyway.

High CPU Usage

After the first boot I noticed two regsvr32.exe processes at 100% CPU that didn't seem to finish in reasonable time.

Further investigation with Process Explorer 6 showed these belonging to Windows' printing service. Since I don't want to print in this VM anyway, the affected service can just be stopped and disabled:

sc stop "Spooler"
sc config "Spooler" start= disabled

Networking

We're still missing the network driver from the virtio cdrom. Unfortunately the NetKVM driver doesn't seem to be properly signed, so you have to enable loading unsigned drivers first (and reboot!):

bcdedit /set testsigning on

Afterwards the right driver can be installed from the device manager (NetKVM/w10/ARM64 on cdrom).

General Performance Tweaks

These aren't specific to Windows 10 ARM or Virtual Machines, but are most useful in that case.

REM Disable Windows Search Indexing
sc stop "WSearch"
sc config "WSearch" start= disabled
REM Disable Automatic Defragmentation
schtasks /Delete /TN "\Microsoft\Windows\Defrag\ScheduledDefrag" /F
REM Disable Pagefile
wmic computersystem set AutomaticManagedPagefile=FALSE
wmic pagefileset delete
REM Disable Hibernation
powercfg -h off

Higher Display Resolution

As of writing QEMU's ramfb has its resolution locked to 800x600, which even breaks EDK2's menu (press F2 or Esc during boot).

Fortunately, this has already been fixed in master 7 and will be in qemu 5.1.0. You can compile 5.1.0-rc3 today if you don't want to wait.

In addition to that you need vars-template-pflash.raw from the same edk package as earlier (UEFI will store its settings in there).
Add the following to your qemu args: -drive file=vars-template-pflash.raw,if=pflash,index=1

The display resolution can then be set up to 1024x768 under Device Manager > OVMF Platform Configuration.

Wrapping up

With a bit of preparation it is possible to run Windows 10 ARM in a virtual machine. Although the emulation is pretty slow you could feasibly use this to test one or two programs.

If you have ARM64 hardware with sufficient specs and KVM support, the -enable-kvm flag can get you native execution speed, though I haven't had a chance to see how this performs yet.

1

https://www.samsung.com/au/tablets/galaxy-book-s-w767/SM-W767NZAAXSA/

2

https://www.lenovo.com/ie/en/laptops/yoga/yoga-c-series/Yoga-C630-13Q50/p/88YGC601090

3

https://www.microsoft.com/en-us/p/surface-pro-x/8vdnrp2m6hhc

4

https://www.kraxel.org/blog/2019/09/display-devices-in-qemu/#tldr

5

An usb-net device does not function and doesn't appear in Windows' device manager at all.

6

Procexp for ARM64 is available here: http://live.sysinternals.com/ARM64/

7

https://github.com/qemu/qemu/commit/c326eedc7584b94f6f9f3b8ba61a6e9ff04ad681