This directory provides support code for building UEFI applications using the efi_executable()
GN template. The UEFI build environment provided here is basically similar to the phys
environment for phys_executable()
and zbi_executable()
, but somewhat less constrained. The kernel's basic libc and the ktl
subset of standard C++ library features are available.
The //zircon/kernel/phys/efi:main
target provides entry code that calls the standard C main
function with argc
and argv
. main
will be called with argc == 0
when booted directly from the UEFI Boot Manager, but gets full arguments when launched from the UEFI Shell.
UEFI code can use dynamic relocation freely, and can have static constructors and destructors. (Destructors are called only if main
returns. There is no standard C exit()
function.)
Though meant for low-level situations, the UEFI API is just a bunch of C data structures and function pointers. So it's eminently amenable to mocking for unit tests. The //zircon/kernel/lib/efi/testing
library works with gmock and gtest to make this easy. So most phys/efi
code is written to allow compilation on host platforms for the benefit of unit tests. To this end, it uses standard C++ headers and std::
symbols directly rather than using <ktl/....h>
headers and ktl::
symbols as kernel code normally does. Like library code shared between kernel and other contexts, it must adhere to the strict subset of standard C++ library features that have ktl
counterparts defined.
UEFI supports standard disk partitioning and the VFAT filesystem format, which is a traditional MS-DOS/Window filesystem format that‘s what’s usually used on USB thumb drives. On most UEFI systems, any attached disk (USB, etc.) in this format will be available to boot or run UEFI applications from. VFAT file names are case-insensitive but case-preserving.
When booting from a VFAT filesystem, UEFI firmware will look for the file \EFI\BOOT\BOOT$cpu.EFI
where $cpu
is X64
on x86 and AA64
on ARM64. If this file exists, it will be run directly. If no such file exists, some systems will fall back to running the UEFI Shell.
Some UEFI systems support the UEFI Shell, including the EDK2 reference implementation used with QEMU and other emulators. This provides a simple command-line interface that can launch UEFI applications, pass them arguments, etc. as well as simple MS-DOS/Unix-like commands for filesystem access and so on. When booted into the UEFI shell, UEFI applications can be run directly by filesystem path, using the extension .EFI
(explicit or implicit):
Shell> efi-hello-world-test
UEFI applications run this way can take command-line arguments:
Shell> efi-hello-world-test arg1 "argument 2"
When the application exits, it will return to the shell prompt. However, tests that crash may wedge the system so physical reset (or killing the emulator) is required.
The UEFI Shell command reset
can be used to reboot the machine or exit the emulator.
The Fuchsia checkout includes UEFI firmware images usable with QEMU or other emulators (with or without KVM). To run UEFI binaries under QEMU, launch QEMU manually as normal (e.g. using the same switches fx qemu
prints out) but with a few different switches.
First, discard any -kernel ...
, -initrd ...
, or -append ...
switches, as these will not have any effect. Replace them with the -bios
switch giving the path to the UEFI firmware image:
-bios prebuilt/third_party/edk2/x64/OVMF.fd
or
-bios prebuilt/third_party/edk2/arm64/QEMU_EFI.fd
The QEMU virtual machine will attempt to boot from disk or network just like a real UEFI-based PC would. If you have a disk image as would go onto a USB thumb drive, you can use QEMU switches such as -hda ...
to point at it. QEMU also offers a convenient feature to mock up a VFAT filesystem image on a virtual disk device directly from your host filesystem. To use this, first populate a small directory with the files you want visible to UEFI. For example:
mkdir qemu-efi-root (cd qemu-efi-root; ln -snf ../out/default/kernel.efi_x64/*.efi .)
or:
mkdir -p qemu-efi-root/efi/boot ln out/default/kernel.efi_x64/efi-hello-world-test.efi qemu-efi-root/efi/boot/bootx64.efi
Then invoke QEMU with this additional switch, providing the path to your directory after file=fat:
.
-drive driver=vvfat,rw=true,snapshot=true,file=fat:qemu-efi-root
Don‘t put too much into the directory, or the virtual filesystem image will be too big for QEMU to handle. (It likely won’t work to just point it at your whole build directory, for example.) Once QEMU starts, you shouldn‘t modify the contents of that directory. You’ll probably need to restart QEMU after any changes to the directory or the files in it.