Virtualize UEFI on ARM using QEMU
On my quest for Linux on Surface, I got too tiring of rebooting the tablet, tweaking some changes on the bcd store or efi partition, rebooting, failing and redoing the whole cycle again.
Surface has a ARM processor with a UEFI implemented. So I started looking whether I could emulate that setup to iterate more quickly over the changes I wanted to test, and only if they worked on the emulate system, I would go over the trouble of doing them on the actual tablet.
Here’s the setup that got me somewhere:
- QEMU for emulation, in their own words, is a “generic and open source machine emulator and virtualizer”, and they support ARM emulation.
- EDK2 UEFI ARM implementation
There were a couple of articles that helped me get it to work, namingly Linaro’s UEFI QEMU article and eciton’s blog on the topic.
Some code, if that’s what you came for:
# downloads the edk2 prebuilt image for ARM (32 bits)
wget http://snapshots.linaro.org/components/kernel/leg-virt-tianocore-edk2-upstream/latest/QEMU-ARM/DEBUG_GCC5/QEMU_EFI.fd
# create the raw images to be used as flag for UEFI, efi var storage, etc
rm -f flash0.img flash1.img
dd if=/dev/zero bs=1M count=64 of=flash0.img
dd if=/dev/zero bs=1M count=64 of=flash1.img
dd if=QEMU_EFI.fd bs=1M of=flash0.img conv=notrunc
# creates a folder that we will use to emulate the EFI boot partition
# put your boot files here
# in my case I used these: http://bep.si/?nge3y originally
# from https://kiwiirc.com/client/irc.rol.im/rtchurch
mkdir boot
# cp <your_boot_files> boot/
# starts qemu using the edk2 UEFI images created
# and a local boot folder as a FAT disk
qemu-system-arm -m 1024 -cpu cortex-a15 -M virt -pflash flash0.img -pflash flash1.img -nographic -drive file=fat:rw:boot/
A bit of explaining: I used the prebuild assemblies from the Linaro distribution for the EDK2 UEFI image. From that, I created 2 image files of 64MB full of 0s (reasons are described here), but the first image, that starts with the UEFI binary.
There is a lot of variation in the ARM word it seems.I wanted to emulate the closest CPU and system I could to the Nvidia’s Tegra-3 SoC that is on the Surface. Tegra-3’s ARM CPU is the Cortex-A9 which is supported by QEMU, but not by EDK2 it seems - not really due to the CPU itself, but because QEMU can only emulate some ARM CPUs in some particular system configurations, and I couldn’t find any configuration in which I could get the A9 that also worked for EDK2. Well, my hopes are that all I really need to work with is abstracted by UEFI so using the precisely same platform may not be required.
For the EFI partition, which needs to be formatted as FAT, I used QEMU’s virtual FAT disk. To start off, I used RoL’s EFI files, which seems to be Windows Boot Manager for ARM with a BCD entry for a grub image.
If you just run QEMU with this setup, you will eventually get to the EFI Shell, which is the default behavior of the EDK2 UEFI image when it can’t boot anything. This is mainly because there is no entry in the boot efi vars pointing to the boot loader in the EFI disk. That can be added by entering this on the EFI shell:
bcfg boot add 0 fs0:\efi\boot\bootarm.efi
fs0 is the first file system, that’s the one I want, since I only have one. While you are at it, make sure to remove the PXE network boot from the boot list, since it happens before the EFI Shell and in case your image fails to boot, you don’t want to keep waiting a minute for the PXE time out.
After all this I got GRUB! I got GRUB, loaded though Windows BootManager for ARM, on a emulated ARM system, running on x86. Now let’s see if I can build some very simple Linux images and boot to them. If it works, I’ll try on the actual device.
A side note for whoever cares: I misused the term virtualize in the title. In reality, what I am doing is emulating the ARM processor. This is so because I am running QEMU on a x86 architecture, so all I can do is emulate ARM instructions, not really virtualuze the CPU. Check this StackOverflow post out, if you are interested in the distinction.