This directory hosts the Generic Bootloader Library project. A Bazel workspace is setup for building the library as well as an EFI executable that can be loaded directly from the firmware.
The GBL build currently only supports Linux x86_64 host machines.
Your machine must have the following dependencies installed:
repo to work with android repositories (https://source.android.com/docs/setup/reference/repo)On Google Linux machines this tool can be installed via:
sudo apt install repo
Use repo to download the source using the Android uefi-gbl-mainline manifest:
# You can choose a different directory name if you prefer. mkdir gbl cd gbl repo init -u https://android.googlesource.com/kernel/manifest -b uefi-gbl-mainline repo sync -j16
cd bootable/libbootloader/gbl ./bazel.sh run //bootable/libbootloader:gbl_efi_dist
This command builds all variations of the EFI application (dev + prod for each of x86_64, aarch64, and riscv64 architectures). The application binaries will be placed in out/gbl_efi/ of the repo root.
Note: The bootable/libbootloader/gbl/bazel.sh build script can be executed from anywhere in the source tree.
./bazel.sh test @gbl//tests
The Bazel build can sometimes get into a state where it has cached some incorrect build files which causes errors on re-build, one example failure message in this case is Inconsistent filesystem operations.
If you run into this, clean the build with the --expunge flag to reset your Bazel state:
./bazel.sh clean --expunge
For rust development, we recommend using the VSCode + rust-analyzer plugin.
rust-analyzer requires rust-project.json to work properly. Luckily, bazel has support for generating rust-project.json:
./tools/bazel run @rules_rust//tools/rust_analyzer:gen_rust_project --norepository_disable_download -- --bazel ./tools/bazel @gbl//efi/...
@gbl//efi/... is the target to generate rust project for, here it means “everything under @gbl//efi/ directory” . Omitting the target specifier would result in analyzing “@/...” , which would most likely fail due to some obscure reason. Should targets get moved around in the future, this path spec also needs to be updated.
After generating rust-project.json, you would notice that your IDE still doesn't offer auto completion. This is because some source file paths point to bazel-output dir, and you are most likely editing source files in bootable/libbootloader/gbl. In addition, the generated rust-project.json sets “cfg=test” for all targets, which causes certain dependency graph to resolve incorrectly. To fix this, run
python3 bootable/libbootloader/gbl/rewrite_rust_project_path.py rust-project.json --arch <arch>
where <arch> is the target architecture of interest and should be one of x86_64, aarch64, riscv64. <arch> affects intellisense on architecture specific code.
Note: The generated rust-project.json points to host version of generated sources. You may need to run
./tools/bazel test @gbl//tests
to make sure all generated sources(bindgen) are populated.
And reload your IDE.
rustfmt.pyink.clang-format adhering to the Google C++ Style Guide.prettier.This repo can be used with Gemini CLI. Refer to that link for installation instructions.
To use it, run gemini from this directory so that it sees the GEMINI.md file to help specialize it for this codebase.
If you have a main AOSP checkout and it is set up to run Cuttlefish, you can run the EFI image directly with:
cvd create --android_efi_loader=<path to the EFI image> ...
The above uses the same setting as a normal cvd create run, except that instead of booting Android directly, the emulator first hands off to the EFI application, which will take over booting android.
Note: For x86 platform, use the EFI image built for x86_64.
Booting Fuchsia on a Vim3 development board is supported. To run the application:
fastboot stage <path to the EFI binary> && fastboot oem run-staged-efi
If you want to test the EFI image directly on QEMU with your custom configurations:
Install EDK, QEMU and u-boot prebuilts
sudo apt-get install qemu-system ovmf u-boot-qemu
Depending on the target architecture you want to run:
For x86_64:
mkdir -p /tmp/esp/EFI/BOOT && \ cp <path to EFI image> /tmp/esp/EFI/BOOT/bootx64.efi && \ qemu-system-x86_64 -nographic -m 1G \ -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd \ -drive format=raw,file=fat:rw:/tmp/esp
For aarch64:
mkdir -p /tmp/esp/EFI/BOOT && \ cp <path to EFI image> /tmp/esp/EFI/BOOT/bootaa64.efi && \ qemu-system-aarch64 -nographic -machine virt -m 1G -cpu cortex-a57 \ -drive if=pflash,format=raw,readonly=on,file=/usr/share/AAVMF/AAVMF_CODE.fd \ -drive format=raw,file=fat:rw:/tmp/esp
For riscv64:
mkdir -p /tmp/esp/EFI/BOOT && \ cp <path to EFI image> /tmp/esp/EFI/BOOT/bootriscv64.efi && \ qemu-system-riscv64 -nographic -machine virt -m 1G \ -bios /usr/lib/u-boot/qemu-riscv64/u-boot.bin \ -drive format=raw,file=fat:rw:/tmp/esp
qemu_gdb_example/ provides an example for debugging x86_64 GBL EFI app on QEMU using rust-gdb. To try the example:
Install necessary dependencies:
sudo apt-get install qemu-system ovmf
For aarch64 target debugging, also install:
sudo apt-get install gdb-multiarch
Run the following script:
./qemu_gdb_example/launch_qemu_gdb.sh
The above command builds a debug x86_64 GBL EFI app, launches it in QEMU and starts rust-gdb in a separate terminal for debugging.
For debugging aarch64 target, run:
./qemu_gdb_example/launch_qemu_gdb.sh aarch64
For x86_64 and aarch64, a pdb file is built along the GBL EFI application. The following gives an example of debugging GBL with the pdb file on Cuttlefish. (Currently only aarch64 is supported.)
Build GBL with GDB connection listening enabled:
./tools/bazel run //bootable/libbootloader:gbl_efi_dist -c dbg --@gbl//toolchain:always_wait_gdb
Launch cuttlefish with GBL and qemu.
launch_cvd \ --vm_manager=qemu_cli \ --android_efi_loader=./out/gbl_efi/gbl_aarch64_dev.efi \ --gdb_port=1337 \ --cpus=1
After cuttlefish emulator started successfully, in a separate terminal, launch lldb.
lldb \ -o "target create ./out/gbl_efi/gbl_aarch64_dev.efi" \ -o "gdb-remote localhost:1337" \ -o "command script import bootable/libbootloader/gbl/qemu_gdb_example/lldb_load_gbl.py" \ -o "script lldb_load_gbl.start_gbl()"
Make sure the Fuchsia target passes control to GBL.
Set path to GBL binary here: fuchsia/src/firmware/gigaboot/cpp/backends.gni : gigaboot_gbl_efi_app
Temporarily need to enable GBL usage in gigaboot: fuchsia/src/firmware/gigaboot/cpp/backends.gni : gigaboot_use_gbl
E.g. in fuchsia/src/firmware/gigaboot/cpp/backends.gni:
$ cat ./fuchsia/src/firmware/gigaboot/cpp/backends.gni ... declare_args() { ... gigaboot_gbl_efi_app = "<path to EFI image>/gbl_x86_64.efi" gigaboot_use_gbl = true }
Or in fx set:
fx set core.x64 --args=gigaboot_gbl_efi_app='"<path to EFI image>/gbl_x86_64.efi"' --args=gigaboot_use_gbl=true
Build: (this has to be done every time if EFI app changes)
fx build
Run emulator in UEFI mode with raw disk
fx qemu -a x64 --uefi --disktype=nvme -D ./out/default/obj/build/images/disk.raw
List of EFI protocols used by GBL and a brief description of each here.
For aarch64, GBL supports tracing and visualization by Perfetto (GBL cuttlefish trace example). To enable it, build GBL with the following option:
./bazel.sh run //bootable/libbootloader:gbl_efi_dist \ --@gbl//toolchain:enable_tracing \ --@gbl//toolchain:pause_boot_in_fastboot
--@gbl//toolchain:enable_tracing enables compiler instrumentation for tracing. --@gbl//toolchain:pause_boot_in_fastboot instructs GBL to always stop in fastboot after loading OS and before booting, which will be needed for retrieving trace data.
Boot GBL on your device. Wait until GBL finishes loading OS and stops in fastboot. Then run the following fastboot commands to retrieve trace.
fastboot oem gbl-stage trace fastboot get_staged <trace file>
<trace file> is the output path for the trace data. The format of the trace data is defined in libtrace/include/gbl_trace.h
To visualize the data with perfetto, run tools/gbl-trace-to-perfetto.py, which converts the trace file to trace event format with symbolized addresses:
python3 tools/gbl-trace-to-perfetto.py \ <trace file> \ <path to GBL binary> \ <output trace event format file>
<path to GBL binary> is the path to the GBL binary. The corresponding PDB file must be in the same directory. <output trace event format file> can then be opened with Perfetto UI.
By default, GBL allocates 64MB of space from EFI allocation for collecting traces. To change the setting, add option --@gbl//toolchain:trace_buffer_size_mb=<size in MB> when building. If GBL runs out of space, additional traces will be truncated. tools/gbl-trace-to-perfetto.py will estimate and log potential truncation size.
Unless stated otherwise, all GBL source files are licensed under the Apache License, Version 2.0.
UEFI definitions, along with UEFI, community, and GBL-specific protocol headers located in libefi_types/include/**/*.h, may alternatively be used under the BSD-2-Clause-Patent license.
See ../LICENSES/Apache-2.0.txt and ../LICENSES/BSD-2-Clause-Patents.txt for the full texts.
GBL also uses third-party code which may be licensed under different terms. When the GBL UEFI application is built, a LICENSE file will also be generated in the output directory containing the set of license texts that apply for GBL and its dependencies.