UEFI Hello World with qemu and EDK2
This is a quick guide on how to write a UEFI Hello, world application and run it
using qemu
.
First, create a working directory and git clone
the EDK
2 repository inside
it:
$ mkdir ~/uefi-hello && cd ~/uefi-hello
$ git clone https://github.com/tianocore/edk2
$ cd edk2
Then follow the setup instructions in the EDK 2 repository to set up prerequisites for the build tools:
$ sudo apt install gcc build-essential uuid-dev iasl nasm python-is-python3
Afterwards follow the common instructions to set up a build environment:
$ git checkout edk2-stable202302
$ git submodule update --init
$ make -C BaseTools
$ source ./edksetup.sh
Now you are ready to build a UEFI Hello, world application. In order to test
that the application does not brick the device it’s running on, it’s good to try
it out in a virtual machine first. For that, install qemu
and ovmf
(a port of
Intel’s tianocore firmware to the qemu
virtual machine):
$ sudo apt install qemu-system ovmf
Next, create a bootable EFI image for qemu
:
$ mkdir ~/uefi-hello/qemu/ && cd ~/uefi-hello/qemu
$ dd if=/dev/zero of=boot.img bs=1M count=512
$ sudo mkfs.vfat boot.img
$ sudo mount boot.img /mnt/
$ sudo mkdir -p /mnt/efi/boot/
$ sudo cp /usr/lib/grub/x86_64-efi/monolithic/grubx64.efi /mnt/efi/boot/bootx64.efi
$ sudo umount /mnt/
Then copy over the firmware from ovmf
and start qemu
:
$ cp /usr/share/OVMF/OVMF_CODE.fd ./ovmf.fd
$ qemu-system-x86_64 -drive file=./ovmf.fd,format=raw,if=pflash -cdrom boot.img
Which if done correctly should look like this:
Off to the UEFI Hello, world application – the EDK 2 repository contains such an application, and it looks like this:
/** @file
This sample application bases on HelloWorld PCD setting
to print "UEFI Hello World!" to the UEFI Console.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/PcdLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringHelpTokenId = STRING_TOKEN (STR_HELLO_WORLD_HELP_INFORMATION);
EFI_STATUS EFIAPI UefiMain (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
UINT32 Index;
Index = 0;
if (FeaturePcdGet (PcdHelloWorldPrintEnable)) {
for (Index = 0; Index < PcdGet32 (PcdHelloWorldPrintTimes); Index++) {
Print ((CHAR16 *)PcdGetPtr (PcdHelloWorldPrintString));
}
}
return EFI_SUCCESS;
}
There is one caveat to this app. It finishes execution too quickly to be seen with the naked eye, so it would be best to add a call to make the application sleep for 30s after printing its message:
$ cd ~/uefi-hello/edk2
$ git diff
diff --git a/MdeModulePkg/Application/HelloWorld/HelloWorld.c b/MdeModulePkg/Application/HelloWorld/HelloWorld.c
index ab581c040c..a704325f56 100644
--- a/MdeModulePkg/Application/HelloWorld/HelloWorld.c
+++ b/MdeModulePkg/Application/HelloWorld/HelloWorld.c
@@ -11,6 +11,7 @@
#include <Library/PcdLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
//
// String token ID of help message text.
@@ -53,6 +54,7 @@ UefiMain (
// Use UefiLib Print API to print string to UEFI console
//
Print ((CHAR16 *)PcdGetPtr (PcdHelloWorldPrintString));
+ gBS->Stall (30000000);
}
}
Then modify Conf/target.txt
to configure a release build for x64:
$ echo 'ACTIVE_PLATFORM = MdeModulePkg/MdeModulePkg.dsc
TARGET = RELEASE
TARGET_ARCH = X64
TOOL_CHAIN_CONF = Conf/tools_def.txt
TOOL_CHAIN_TAG = GCC5
BUILD_RULE_CONF = Conf/build_rule.txt' > Conf/target.txt
Trigger the build:
$ build
//...output omitted
$ file Build/MdeModule/RELEASE_GCC5/X64/HelloWorld.efi
Build/MdeModule/RELEASE_GCC5/X64/HelloWorld.efi: PE32+ executable (EFI application) x86-64, for MS Windows, 3 sections
Remount boot.img
and copy over the HelloWorld.efi
application:
$ cd ~/uefi-hello/qemu
$ sudo mount boot.img /mnt/
$ sudo cp ~/uefi-hello/edk2/Build/MdeModule/RELEASE_GCC5/X64/HelloWorld.efi /mnt/efi/boot/bootx64.efi
$ sudo umount /mnt
Finally, launch qemu again and you will see the UEFI program run:
$ qemu-system-x86_64 -drive file=./ovmf.fd,format=raw,if=pflash -cdrom boot.img
With that you successfully ran a UEFI Hello, World application inside qemu
.
References
https://www.linux.it/~ema/posts/efi-sb-notes/