Skip to content
This repository has been archived by the owner on Aug 12, 2021. It is now read-only.
Andrew Baumann edited this page Jun 3, 2018 · 9 revisions

Note (June 2018): the bulk of the raspi2 emulation and Windows-related fixes were upstreamed to mainline QEMU years ago. There is no reason to be using this code unless you really care about (1) the raspi1 machine or (2) the very flaky and incomplete USB host emulation.


Windows for Raspberry Pi 2 on QEMU HOWTO

Getting started: creating a bootable image

  1. Download and install a recent version of Windows 10 IoT for Raspberry Pi 2. (I've mostly tested with builds 10531 and 10556.)
  2. Create a clean 8GB VHD. Make sure it's a dynamic expanding VHD, since QEMU doesn't seem to recognise the fixed format. QEMU also doesn't support VHDX out of the box, so don't use that either.
  3. Mount the VHD. Find the device ID of the mounted VHD by running:
    C:\Windows\System32\wbem\WMIC.exe diskdrive list brief
  4. Assuming the VHD shows up as drive 9, do:
    C:\Program Files (x86)\Microsoft IoT\dism\dism.exe /apply-image /imagefile:C:\Program Files (x86)\Microsoft IoT\FFU\RaspberryPi2\flash.ffu /applydrive:\\.\PhysicalDrive9 /skipplatformcheck
    This applies the image and generates a usable VHD.
  5. Copy out kernel.img from the boot partition, which is the UEFI bootloader, so that QEMU can load it directly.
  6. Don't forget to unmount the VHD before trying to use it.

Booting Windows

Assuming you have a VHD named win10iot.vhd and the UEFI image named kernel.img, then the minimum command line is:
qemu-system-arm -M raspi2 -smp 4 -global bcm2835-fb.pixo=0 -bios kernel.img -sd win10iot.vhd

Other useful parameters to qemu include:

  • -serial stdio to see (just a tiny bit of) serial output from the bootloader
  • -d guest_errors,unimp to see debug prints for access to unimplemented registers
  • -global bcm2835-fb.xres=1024 -global bcm2835-fb.yres=768 to change the initial framebuffer resolution

If you're running QEMU on Windows, don't forget to set SDL_STDIO_REDIRECT=0 in your environment, or it will silently write to stdout.txt and stderr.txt instead of sending anything useful to the console.

Kernel debugger

After enabling the debug stub using bcdedit, it is possible to debug Windows using the virtual serial port, but it requires a small proxy program due to limitations in QEMU's implementation of named pipes. Essentially, the serial port is connected to a local TCP socket, and the proxy connects this to a named pipe (to which you can connect WinDBG et al).

Known issues

  • Build 10586, aka the "November update", seems to suffer from a late crash in sihost.exe that prevents the UI from coming up. I've yet to debug this issue.
  • The USB controller emulation is incomplete and does not work with the Windows driver. Consequently, there is no keyboard / mouse / network.
  • Boot times are very high (10s of minutes until the display comes up). The main culprits are:
  • It is necessary to emulate all four cores (-smp 4), or UEFI refuses to boot. QEMU emulates these cores on a single thread, which can slow things down significantly.
  • Windows does not appear to be using DMA for SD card I/O.
  • If Windows does not make it far enough in its boot process, it leaves the VHD image in a bad state such that a subsequent boot attempts (but apparently fails) to enter a recovery mode. The primary symptom of this is seeing nothing but a black screen at startup, rather than the Windows logo. There are two workarounds:
  1. Quit and restart QEMU. The next boot attempt will not enter recovery.
  2. Save a backup copy of the VHD, and restore it between boot attempts.
  • On build 10556, Windows often (but not always) hits a CRITICAL_PROCESS_DIED bluescreen late in the startup process. This appears to be a timeout/race condition; one workaround (other than using a different build) is to attach a kernel debugger, which allows you to ignore the crashing svchost process and continue.

Booting Linux

The main focus of this work is supporting Windows on Pi2, but it's also possible to boot a recent version of Raspbian using a command such as:
qemu-system-arm -M raspi2 -kernel raspbian-boot/kernel7.img -sd 2015-09-24-raspbian-jessie.vhd -append "rw earlyprintk loglevel=8 console=ttyAMA0,115200 dwc_otg.lpm_enable=0 root=/dev/mmcblk0p2" -dtb raspbian-boot/bcm2709-rpi-2-b.dtb -usbdevice mouse -usbdevice keyboard -serial stdio

Note that it is necessary to first comment out the contents of /etc/ld.so.preload to avoid executing an unimplemented SETEND instruction. There are also a number of errors and warnings, that I haven't attempted to debug.

Rasberry Pi 1 support is also still present, but has bit-rotted somewhat. It is possible to boot older versions of Raspbian up to 2014-09-09-wheezy, but there is an unresolved issue with interrupt timeouts on the SD card I/O, so it takes a very long time. Newer versions fail to boot entirely due to a kernel-mode setend instruction (apparently introduced by this commit) which qemu does not support.