Caleb is currently in integration hell (preparing to upstream) so please bare with!
Main feature branch is caleb/rbx-integration
, clone U-Boot and check out that branch.
git clone https://git.codelinaro.org/linaro/qcomlt/u-boot.git -b caleb/rbx-integration
Many of the things in branch are different to how they will be upstream. The defconfig and the config options for clocks, pinctrl, etc will be changing as this huge tech debt gets cleaned up and moved upstream.
Building
Drop these somewhere and source them.
KERNEL_OUTDIR=.output
# Make wrapper
mu() {
make O=$KERNEL_OUTDIR CROSS_COMPILE=aarch64-linux-gnu- -j"$(nproc --all)" $@
}
# Make and generate boot image wrapper
budt() {
mu || return 1
rm "$KERNEL_OUTDIR"/u-boot-nodtb.bin.gz 2>/dev/null || true
gzip "$KERNEL_OUTDIR"/u-boot-nodtb.bin
rm -f /tmp/dt.dtb
for DT in "$@"; do
cat "$KERNEL_OUTDIR"/arch/arm/dts/"$DT".dtb >> /tmp/dt.dtb
done
cat "$KERNEL_OUTDIR"/u-boot-nodtb.bin.gz /tmp/dt.dtb > /tmp/kernel-dtb
mkbootimg --base '0x00000000' \
--kernel_offset '0x00008000' \
--ramdisk_offset '0x01000000' \
--tags_offset '0x00000100' \
--pagesize '4096' \
--kernel /tmp/kernel-dtb -o '/tmp/u-boot.img'
}
Now we can build U-Boot and boot it.
mu qcom_defconfig
budt dragonboard845c
fastboot boot /tmp/u-boot.img
This will work on RB1/2/3/5 with their respective DTBs specified. From my testing, RB3 and 5 have "good enough" DTB selection in ABL so that a single image can work on both, demonstrated below by appending both DTBs.
budt rb1
budt rb2
budt dragonboard845c rb5
Configuring
The default environment will scan UFS, sdcard, and USB, it will boot the first EFI bootloader it can find (a binary at patch /EFI/Boot/bootaa64.efi
on the partition). This uses a feature called standard boot. To boot a boot.img file it would be necessary to enable support for decoding Android boot images in U-Boot (some config option), and then adding a "bootmeth" which would discover the boot.img file at some well known path, unpack it and boot it. (see boot/bootmeth_efi.c
as an example).
The environment can be adjusted in include/configs/qcom-common.h
.
Developing
U-Boot is fairly similar to Linux, the main differences are that each device (e.g. GPIO controller, button, clock, pinctrl) has a 1:1 mapping with a struct udevice
(analogous to struct device
in Linux. This means that you can't have the clock and reset controllers reference the same device as you can on Linux.
Drivers are probed on demand (rather than greedily on Linux), there are some edgecases where this breaks, but most iterators (e.g. iterating over all the clock devices) will cause devices to be probed.
- U-Boot docs on the driver model https://docs.u-boot.org/en/latest/develop/driver-model/index.html
Code completion
I recommend using clangd
for autocompletion in your favourite IDE, there are plugins available for all major ones. To generate the compile_commands.json
compiler database, this repo can be cloned, then after compiling (see below) run .vscode/generate_compdb.py -O .output
and restart clangd. I've found this to work near-flawlessly and it makes exploring the codebase a lot easier.
This is (roughly) the make wrapper I use to build U-Boot and wrap it up into the boot.img format for ABL. U-Boot includes it's own DT but we don't want to use that because it won't be seen by ABL. Instead we append the DTB to the gzipped U-Boot binary (just like with Linux) so that ABL will populate board specific bits like the memory node. This is in contrast to current dragonboard documentation in U-Boot which did rely on hardcoding everything.
Debugging
If necessary, open configs/qcom_defconfig
and at the bottom enable CONFIG_DEBUG_UART
and ensure that CONFIG_DEBUG_UART_CLOCK=7372800
is set (for rb3, for rb1/2/5 the clock should be 14745600
and the UART_BASE adjusted to the address of the debug UART node for the respective platform).
The loglevel can also be adjusted, and U-Boot will print logs pre-relocation + device discovery logs. This can be a little verbose so a good alternative is to add #define LOG_DEBUG
to the top of the files you want to debug.
U-Boot docs say to just
#define DEBUG
at the top of a source file, however this relies on the loglevel, definingLOG_DEBUG
will always enable the prints.
Analysing crashes
I have written this python script which you can install somewhere in your PATH, you must be in the U-Boot source folder when running it.
It will proxy your serial port (you'll need to point your serial program at the /dev/pty/XX
port it allocates) and will monitor for U-Boot aborts. When U-Boot crashes it will try to find the function where the crash occured by parsing the register dump. It prints the source file and line number as well as the decompiled assembly for further debugging if needed.
- U-Boot docs on crash debugging https://docs.u-boot.org/en/latest/develop/crash_dumps.html