blob: aa21cd82462f9b9991d341a667d8c3f8b781d28a [file] [view] [edit]
# Generic Bootloader Library
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.
## Get source tree and build
### Prerequisites
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:
```sh
sudo apt install repo
```
### Download the source
Use `repo` to download the source using the Android
[uefi-gbl-mainline manifest](https://android.googlesource.com/kernel/manifest/+/refs/heads/uefi-gbl-mainline/default.xml):
```sh
# 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
```
### Build the UEFI applications
```sh
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.
### Run host-side unittests
```sh
./bazel.sh test @gbl//tests
```
### Troubleshooting
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:
```sh
./bazel.sh clean --expunge
```
## IDE Setup
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`:
```sh
./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
```sh
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
```sh
./tools/bazel test @gbl//tests
```
to make sure all generated sources(bindgen) are populated.
And reload your IDE.
## Code Style and Formatting
- **Rust:** Format with `rustfmt`.
- **Python:** Format with `pyink`.
- **C++:** Format with `clang-format` adhering to the Google C++ Style Guide.
- **Markdown:** Format with `prettier`.
## Gemini CLI
This repo can be used with
[Gemini CLI](https://github.com/google-gemini/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.
## Run the EFI application
### Boot Android on Cuttlefish
If you have a main AOSP checkout and it is set up to run
[Cuttlefish](https://source.android.com/docs/setup/create/cuttlefish), you can
run the EFI image directly with:
```sh
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`.
### Boot Fuchsia on Vim3
Booting Fuchsia on a Vim3 development board is supported. To run the
application:
1. Complete all
[bootstrap steps](https://fuchsia.dev/fuchsia-src/development/hardware/khadas-vim3?hl=en)
to setup Vim3 as a Fuchsia device.
2. Reboot the device into fastboot mode.
3. Run fastboot command:
```sh
fastboot stage <path to the EFI binary> && fastboot oem run-staged-efi
```
### Run on standalone QEMU
If you want to test the EFI image directly on QEMU with your custom
configurations:
1. Install EDK, QEMU and u-boot prebuilts
```sh
sudo apt-get install qemu-system ovmf u-boot-qemu
```
1. Depending on the target architecture you want to run:
For `x86_64`:
```sh
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`:
```sh
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`:
```sh
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
```
### Debug with GDB on QEMU
[qemu_gdb_example/](./qemu_gdb_example/) provides an example for debugging
x86_64 GBL EFI app on QEMU using rust-gdb. To try the example:
1. Install necessary dependencies:
```sh
sudo apt-get install qemu-system ovmf
```
For aarch64 target debugging, also install:
```sh
sudo apt-get install gdb-multiarch
```
2. Run the following script:
```sh
./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:
```sh
./qemu_gdb_example/launch_qemu_gdb.sh aarch64
```
### Debug with LLDB on Cuttlefish
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.)
1. Build GBL with GDB connection listening enabled:
```sh
./tools/bazel run //bootable/libbootloader:gbl_efi_dist -c dbg --@gbl//toolchain:always_wait_gdb
```
2. Launch cuttlefish with GBL and qemu.
```sh
launch_cvd \
--vm_manager=qemu_cli \
--android_efi_loader=./out/gbl_efi/gbl_aarch64_dev.efi \
--gdb_port=1337 \
--cpus=1
```
3. After cuttlefish emulator started successfully, in a separate terminal,
launch lldb.
```sh
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()"
```
### Boot Fuchsia on emulator
1. 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](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/firmware/gigaboot/cpp/backends.gni;l=25?q=gigaboot_gbl_efi_app)
Temporarily need to enable GBL usage in gigaboot:
[fuchsia/src/firmware/gigaboot/cpp/backends.gni : gigaboot_use_gbl](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/firmware/gigaboot/cpp/backends.gni;l=25?q=gigaboot_gbl_efi_app#:~:text=to%20use%20GBL.-,gigaboot_use_gbl)
E.g. in `fuchsia/src/firmware/gigaboot/cpp/backends.gni`:
```sh
$ 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`:
```sh
fx set core.x64 --args=gigaboot_gbl_efi_app='"<path to EFI image>/gbl_x86_64.efi"' --args=gigaboot_use_gbl=true
```
2. Build: (this has to be done every time if EFI app changes)
`fx build`
3. Run emulator in UEFI mode with raw disk
```sh
fx qemu -a x64 --uefi --disktype=nvme -D ./out/default/obj/build/images/disk.raw
```
## EFI Protocols
List of EFI protocols used by GBL and a brief description of each
[here](./docs/efi_integration.md).
## Trace Analysis
For aarch64, GBL supports tracing and visualization by Perfetto
([GBL cuttlefish trace example](https://ui.perfetto.dev/#!/?s=38c35e39b29b6cc328336962e8859384b40788d1)).
To enable it, build GBL with the following option:
```sh
./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.
```sh
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](libtrace/include/gbl_trace.h)
To visualize the data with perfetto, run
[tools/gbl-trace-to-perfetto.py](tools/gbl-trace-to-perfetto.py), which converts
the trace file to trace event format with symbolized addresses:
```sh
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](https://ui.perfetto.dev/).
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](tools/gbl-trace-to-perfetto.py) will estimate
and log potential truncation size.
## Licensing
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.