Starting with UEFI with CMake and VirtualBox

In the beginning there was the BIOS and every OS had to switch from real mode to protected mode. Basic services where provided via interrupts with an interface whose principles were grounded in the '80s and before. As a fact of the introduction of the Itanium platform there was an effort in defining a new boot level interface capable of supporting more functionalities and a solid interface: Unified Extensible Firmware Interface (UEFI). UEFI grew fast and now the last 2.7 account for 2700 pages.

Some time ago I played with boot procedure and the switch to 32-bit flat address protected mode (PitOS in 2002), and then I have been curious on testing the UEFI capabilities. In this post, and the associated github repository (uefiboot) I wanted to build a UEFI application using CMake and test it in both QEmu and VirtualBox.

In the conclusion I report the possibility of multiprocessing using UEFI.

UEFI supports development of applications or drivers, and OS boot loaders are considered UEFI application. The application starts directly in 64-bit flat mode and it has access to UEFI services via a series of function pointers.

The specification requires to write Portable Executables (PE) with Microsoft calling convention. This can be achieved in two ways, with or without a cross-compiler. Without the cross-compiler an ELF is generated and then converted into an equivalent PE, while it is much easier to install a cross-compiler. My choice is MXE because it supports both Linux and OSX.

The requirements of this exercise are:
  • cross-compiler like x86_64-w64-mingw32 or MXE
  • MTools
  • VirtualBox or QEmu
  • gnu_efi is used for includes and basic library functionalities
In the uefiboot repository the hello application simply writes a message and waits for a key.


The combination of CMake with a cross-compiler makes the compilation of the UEFI application an easy task. Just create a build folder and issue:
cmake -DCMAKE_TOOLCHAIN_FILE=/Applications/mxe/usr/x86_64-w64-mingw32.static/share/cmake/mxe-conf.cmake ..
The result of the build process are several shared libraries (DLL) that needs to be loaded into a disk image. This operation is performed using the MTools package that allows to easily create a FAT disk without mounting it:
dd if=/dev/zero of=fat.img bs=1k count=1440
mformat -i fat.img -f 1440 ::
mmd -i fat.img ::/EFI
mmd -i fat.img ::/EFI/BOOT
echo "\EFI\BOOT\BOOTX64.EFI" > startup.nsh
mcopy -i fat.img startup.nsh ::/
The startup.nsh is used to tell VirtualBox to choose automatically the BOOTX64.EFI file. Then after this setup it is possible to activate one of the built UEFI applications with the following:
make &&  mcopy -oi fat.img src/libhello.dll ::/EFI/BOOT/BOOTX64.EFI

Run QEmu

The execution with QEmu is straightforward and it employs the TianoCore firmware OVMF.fd:
qemu-system-x86_64 -smp 2 -cpu host -L OVMF_dir/ -bios OVMF.fd -drive file=fat.img,if=ide,id=drive-ide0-0-0

Run VirtualBox

VirtualBox requires to create a VM and configure it enabling EFI, in particular 64-bit mode:
VBoxManage createvm --name $VM --ostype "Other" --register
VBoxManage modifyvm $VM --ioapic on
VBoxManage modifyvm $VM --boot1 floppy
VBoxManage modifyvm $VM --memory 1024 --vram 128
VBoxManage modifyvm $VM --firmware efi64
VboxManage storagectl $VM --name "Floppy" --add floppy
Then we specify the floppy image:
VboxManage storageattach  $VM --storagectl "Floppy" --port 0 --device 0 --type fdd --medium "fat.img"
And start:
VboxManage startvm $VM 


There are many interesting aspects that could be explorer with the powerful UEFI API such as graphics, networking and multiprocessing. The latter is quite interesting and it is based on two UEFI extensions (procotols) that supports the easy execution of task over the AP (Application Processors) started from the BSP (Boostrap Processor).

The example checkmp verifies if one of these two protocols are available. In practice seems that most UEFI firmware implement one of them (e.g. I have tested with a Apple MacBook Pro 2014), while they are not supported in Qemu or VirtualBox at the time of writing. This means that for activating the AP in these virtualization tools it is necessary to execute a lengthly assembler based setup aimed at sending the interprocessor interrupts.

Depending on time in a future post I will make an example with these protocols.


There are several references for making simple UEFI applications, and the one discussed here tries to make the process as simple as possible.


Popular posts from this blog

Docker for our ROS robotic overlords

cmakego: Simpler access to external libraries in CMake

Algebrical Data Types in C++