UEFI Development On x86 With EDK2
I made this blog so I could remember how to do stuff that had instructions spread around the internet. So here is how I setup my environment for developing EFI applications.
Requirements
On Artix or other Arch-based distros like Manjaro I installed the following packages: gcc nasm iasl
Here is what the packages do:
- GCC is obviously the GNU Compiler Collection and it allows us to compile C code to machine code.
- NASM stands for Netwide Assembler. It is an assembler and disassembler for 32 and 64 bit Intel x86 platforms.
- IASL stands for the ACPI Source Language Compiler/Decompiler. This will compile any ACPI calls to our local machine’s code.
We need all these packages to start our (U)EFI journey. Now that these are installed, let’s setup our environment.
Building EDK2
I used the stable/202011 branch as that is latest stable version of the EDK2 project.
So first let’s pull the project:
git clone https://github.com/tianocore/edk2.git
Now, let’s fetch the tags and switch to the latest stable version:
cd edk2
git fetch
git checkout stable/202011
Perfect! We’re on stable now! Let’s grab all our submodules: git submodule update --init
This will take a bit the first time you do it. But no fear, once that’s done, we can finally build the base tools.
make -C BaseTools
export EDK_TOOLS_PATH=$HOME/Documents/edk2/BaseTools
. edksetup.sh BaseTools
Notice we source a file with .
before continuing. This is needed to load some tools and options into our shell for later. The environment variable EDK_TOOLS_PATH
is set so that EDK knows where to find itself later. Now that everything is loaded up, we can modify a config file located at Conf/target.txt
.
The most important options are these, feel free to append them to the end of the file; there is no default value for them.
ACTIVE_PLATFORM = MdeModulePkg/MdeModulePkg.dsc TOOL_CHAIN_TAG = GCC5 # for 64-bit development TARGET_ARCH = X64 # for 32-bit development TARGET_ARCH = IA32 # for 32 and 64-bit development TARGET_ARCH = IA32 X64 # set multithreading to 1 + (2 x # of cores) MAX_CONCURRENT_THREAD_NUMBER = 9
There are other options, but I don’t know about them much, so I’m just sticking with this for now.
Finally, after all this work, we can build some .efi files. Let’s compile the Helloworld.efi
file!
Simply run the build
command in the terminal.
You can find your compiled EFI files by running this ls
command:
ls Build/MdeModule/DEBUG_*/*/HelloWorld.efi
This will show all HelloWorld.efi files for all architectures and toolchains (if you decide to change them).
Running In UEFI Shell
Once all this is complete, you will want to run your EFI files. To do so, let’s first add an EFI shell to use at boot. This will appear as an option in your bootloader, like GRUB, which is what I will show documentation for in this article.
So, first thing is first,
download and EFI shell file.
Second, move it to a partition (FAT formatted) which can be used for the UEFI.
On my Linux system, this is /boot. On others there may be no FAT filesystem so attach a USB and format it as FAT.
Third, add the EFI Shell
option to your grub boot file.
Substitute hdX with the right hard drive (I did it with trial and error) as when it doesn’t work I would hit ‘e’ on grub and add the ls
GRUB command.
Substitute the gptX with the correct partition, or msdosX if it is a DOS formatted partition table.
My Shell.efi
was placed in /boot/EFI/.
menuentry "EFI Shell" { insmod part_gpt insmod chain insmod fat set root='(hd4,gpt2)' chainloader /EFI/Shell.efi }
Now regenerate your grub configuration file with grub-update
(Debian-based) or grub-mkconfig -o /boot/grub/grub.cfg
(Other).
You’ll know if your shell is working if you see the following text on boot into the EFI shell:
UEFI Interactive Shell v2.1 EDK II UEFI v2.4 (EDI II, 0x000100000) Mapping table: ... Shell>
Running Hello World
When we run our ls
command from earlier, remember we saw our HelloWorld.efi
file.
Let’s move this file somewhere useful, like for me, /boot
.
Then, once we’re in our UEFI shell we can run commands:
Shell> .\HelloWorld.efi UEFI Hello World! Shell>
And that… All that is how you set up a UEFI development environment.
Conclusion
This took me a long time to figure out. I needed to scrounge resources from around the internet, and I had to look at my config files for hours to make sure that I hadn’t missed a step that I did without thinking. I hope this will be useful to you and my future self.
Happy UEFI hacking :)