Build U-Boot from Source Code
Introductionβ
This article describes how to build the U-Boot without using a higher-level build system such as the Yocto Project/OpenEmbedded. This procedure mostly makes sense during bootloader development.
We provide OpenEmbedded recipes that build U-Boot and Linux as part of a complete BSP image; hence if you plan to build a full BSP image, follow the Build a Reference Image with Yocto Project/OpenEmbedded article.
The U-Boot bootloader's source code is available on our Git server at git.toradex.com.
This is the first article of a three-part series about building from source code. Check the following articles if you are looking for information about:
Prerequisitesβ
- Understand the basic concepts of Toradex Embedded Linux offerings, such as release cycles, distributions and images. This content is available at BSP Layers and Reference Images for Yocto Project Software.
- Follow the steps at the section Install the GNU Toolchain for Hard Float Calling Convention
- Install the necessary tools and dependencies as explained in the section Install Tools and Dependencies
Prepare the Host Machine for Cross-Compilationβ
Use version 9.2 of the Arm releases binary toolchains to cross-compile software for Toradex modules:
- For 32 bit Arm: gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz
- For 64 bit Arm: gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
You have to choose to download either the 32 bit or 64 bit Arm cross-toolchain, according to the architecture of your Computer on Module SoC. Select the correct one from the tabs below:
To install the toolchain on your host machine, download and unpack the tar.xz file. From the command-line:
$ cd ~
$ wget -O gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu-a/9.2-2019.12/binrel/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz?revision=fed31ee5-2ed7-40c8-9e0e-474299a3c4ac&la=en&hash=76DAF56606E7CB66CC5B5B33D8FB90D9F24C9D20"
$ tar xvf gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz
$ ln -s gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf gcc-linaro
Or you can download the toolchain from Arm website.
The U-Boot and Linux makefiles use the environment variables ARCH/CROSS_COMPILE to configure and call the compiler correctly. Therefore, these environment variables must be exported in any shell instance that will run configure/compile commands to build U-Boot or Linux for the target module.
$ export ARCH=arm
$ export DTC_FLAGS="-@"
$ export PATH=~/gcc-linaro/bin/:$PATH
$ export CROSS_COMPILE=arm-none-linux-gnueabihf-
You can put those commands into a file and source that file to export it more easily, e.g.:
$ echo "export ARCH=arm" >> ~/export_compiler
$ echo "export DTC_FLAGS='-@'" >> ~/export_compiler
$ echo "export PATH=~/gcc-linaro/bin/:$PATH" >> ~/export_compiler
$ echo "export CROSS_COMPILE=arm-none-linux-gnueabihf-" >> ~/export_compiler
$ source ~/export_compiler
To install the toolchain on your host machine, download and unpack the tar.xz file. From the command-line:
$ cd ~
$ wget -O gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu-a/9.2-2019.12/binrel/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz?revision=61c3be5d-5175-4db6-9030-b565aae9f766&la=en&hash=0A37024B42028A9616F56A51C2D20755C5EBBCD7"
$ tar xvf gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu.tar.xz
$ ln -s gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu gcc-linaro
Or you can download the toolchain from Arm website.
The U-Boot and Linux makefiles use the environment variables ARCH/CROSS_COMPILE to configure and call the compiler correctly. Therefore, these environment variables must be exported in any shell instance that will run configure/compile commands to build U-Boot or Linux for the target module.
$ export ARCH=arm64
$ export DTC_FLAGS="-@"
$ export PATH=~/gcc-linaro/bin/:$PATH
$ export CROSS_COMPILE=aarch64-none-linux-gnu-
You can put those commands into a file and source that file to export it more easily, E.g.:
$ echo "export ARCH=arm64" >> ~/export_compiler
$ echo "export DTC_FLAGS='-@'" >> ~/export_compiler
$ echo "export PATH=~/gcc-linaro/bin/:$PATH" >> ~/export_compiler
$ echo "export CROSS_COMPILE=aarch64-none-linux-gnu-" >> ~/export_compiler
$ source ~/export_compiler
Install Tools and Dependenciesβ
Device Tree Compiler (DTC) Toolβ
U-Boot and the device tree overlays compilation for some modules needs a device tree compiler (DTC). We recommend DTC version 1.6.0 or higher.
You can build the latest version (DTC 1.6.0 at the time of writing) from the source:
$ git clone git://git.kernel.org/pub/scm/utils/dtc/dtc.git -b v1.6.0 ~/dtc
$ cd ~/dtc
$ make
$ export PATH=$HOME/dtc/:$PATH
info
On Fedora and on Ubuntu 22.04, there have been reported DTC build errors libfdt/libfdt.h:251:28: warning: array subscript βstruct fdt_header[0]β is partly outside array bounds of βunsigned char[4]β [-Warray-bounds]
.
To disable treating errors as warnings, you can remove the flag -Werror
from the CFLAGS
on the Makefile.
We have not evaluated the consequences of doing it, do it at your own risk!
U-Boot Toolsβ
The uImage
target of the Linux kernel compilation needs a recent mkimage
tool.
One can install the Fedora package uboot-tools
:
$ sudo dnf install uboot-tools
Or with the corresponding Debian/Ubuntu package u-boot-tools
:
$ sudo apt-get install u-boot-tools
Alternatively, mkimage tool is also built during the U-Boot compilation. You can follow the U-Boot building instructions as explained further in this article, and after that, include it in PATH.
U-boot Version Informationβ
The required git branch, U-Boot configuration, and U-Boot/Linux binaries to be used depend on module type and BSP version, as we will explain in this article.
For a high-level overview of the BSP Versions, check out our Embedded Linux Release Matrix. There you will find the version information of the Linux kernel, U-Boot, Yocto/OpenEmbedded, the Toradex BSP, and Linux images, along with release dates.
SoC | U-Boot Git Branch | U-Boot Configuration | U-Boot Binary |
---|---|---|---|
i.MX 8/8X/8MM/8MP | toradex_imx_lf_v2022.04 | <family>-<SoC>_defconfig | 8/8X: u-boot.bin 8MM/8MP: u-boot.bin & u-boot-spl.bin |
i.MX 7 | v2022.07 | colibri_imx7_defconfig colibri_imx7_emmc_defconfig | u-boot-nand.imx u-boot.imx |
i.MX 6 | v2022.07 | <family>_<SoC>_defconfig | u-boot.img |
i.MX 6ULL | v2022.07 | colibri-imx6ull_defconfig colibri-imx6ull-emmc_defconfig | u-boot-nand.imx u-boot.imx |
For i.MX 8/8X/8MM/8MP and i.MX 6 based modules, you might replace:
<family>
: colibri, apalis or verdin<SoC>
: imx6, imx8, imx8x, imx8mm or imx8mp
Building U-Bootβ
info
In the following instructions, you will be directed to create a directory called ~/workdir
to store all the source and build files. You can, of course, replace this directory name with any other name you prefer.
Sourceβ
Obtain the U-Boot source code using Git:
$ mkdir -p ~/workdir
$ cd ~/workdir
$ git clone -b <branch> git://git.toradex.com/u-boot-toradex.git
Replace <branch>
by the U-Boot Git Branch for your specific configuration. Check the section U-boot Version for this specific information.
info
Should your company firewall/gateway inhibit the git protocol, you may use HTTP or HTTPS instead (e.g. git clone https://git.toradex.com/u-boot-toradex.git
).
Configurationβ
Ensure the environment is configured for cross-compilation as explained in the toolchain chapter. Then choose one of those configurations and load it:
$ cd ~/workdir/u-boot-toradex
$ make <config>
info
Replace <config>
by the U-Boot Configuration for your specific configuration. Check the section U-boot Version for this specific information.
Compilationβ
The following is the procedure to compile the boot loader.
$ make -j$(nproc) 2>&1 | tee build.log
$ ls u-boot.bin
info
Starting with BSP 3, we use U-Boot based on 2019.07 which changed certain make targets to include -dtb
in their names. If you need backward compatibility with older BSPs, make sure you explicitly state the make target e.g., as follows: make u-boot.imx
. Otherwise, the new target named u-boot-dtb.imx
will be built. However, the two are equal apart from their name.
The resulting U-boot binary will be a file with the name u-boot.bin
.
U-Boot Compilation Troubleshootβ
Click below to see info for U-Boot some common issues when building U-Boot.
Cannot find -lgcc
When using the cross compiler built by an OpenEmbedded build of our BSP V2.5 or later, the compiler might have an error during linking
arm-angstrom-linux-gnueabi-ld.bfd: cannot find -lgcc
make[2]: *** [examples/standalone/hello_world] Error 1
make[1]: *** [examples/standalone] Error 2
make: *** [examples] Error 2
The new cross compiler does not have a hard-coded default sysroot and hence needs the sysroot to be specified explicitly:
$ make CC='arm-angstrom-linux-gnueabi-gcc --sysroot=${HOME}/oe-core/build/out-glibc/sysroots/colibri-vf'
Additional Mandatory Steps: Boot Containerβ
Some SoCs will require additional steps to build the components necessary for the boot. Check on the tabs below each steps that refer to your specific SoC. If you are targetting a different SoC, you can skip this session.
The NXP i.MX 8QuadMax / i.MX 8QuadXPlus boots using a Cortex-M4 boot CPU called System Controller Unit (SCU). The SCU firmware (SCFW) is in large part provided by NXP, but Toradex has slightly altered it for the Apalis iMX8/Apalis iMX8X and Colibri iMX8X. The firmware also loads an intermediate firmware running on the Cortex-A class CPUs: The ARM trusted firmware (ATF). The ARM trusted firmware then hands over control to U-Boot, which then can boot Linux.
To learn more about SCFW, see the Build Custom i.MX 8/8X System Controller Firmware (SCFW) article.
The DDR memory timings, the SCU firmware, the ATF and U-Boot, and any potential Cortex-M4 auxiliary firmware are all stored in a single boot container. This boot container is read by the boot ROM from the boot device, starting at a specific offset (33kB for V1.0A modules booting from SD cards).
Follow the steps described in the sections below in the specific order.
The NXP i.MX 8M Mini/8M Plus requires a boot container. The boot container is built with the Yocto recipe imx-boot
. Nevertheless, if you want to build it manually, this section provides information on how to do it.
Follow the steps described in the sections below in the specific order.
Starting with BSP 3, we use U-Boot based on 2019.07, which dropped the NAND make targets (only required for Colibri iMX6ULL and the NAND based Colibri iMX7).
You may manually create u-boot-nand.imx
as follows:
$ dd bs=10k count=1 if=/dev/zero | cat u-boot.imx - > u-boot.imx.zero-padded
# U-Boot is flashed 1k into a NAND block, create a binary which prepends
# U-boot with 1k of zeros to ease flashing
$ dd bs=1024 count=1 if=/dev/zero | cat - u-boot.imx.zero-padded > u-boot-nand.imx
Obtain the DDR Training Firmwareβ
SoC | NXP BSP Version | NXP i.MX Firmware File Name |
---|---|---|
i.MX 8MM/8MP | L5.15.32_2.0.0 | firmware-imx-8.17.bin |
E.g. use wget
for download, and extract the firmware, for example:
$ cd ~/workdir
$ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-8.17.bin
$ chmod u+x firmware-imx-8.17.bin
$ ./firmware-imx-8.17.bin
$ ls firmware-imx-8.17/firmware/ddr/synopsys/
NXP provides the DDR training firmware in binary form. You will need to accept the NXP License Agreement as part of the unpacking process.
- lpddr4_pmu_train_1d_dmem.bin
- lpddr4_pmu_train_2d_dmem.bin
- lpddr4_pmu_train_1d_imem.bin
- lpddr4_pmu_train_2d_imem.bin
- lpddr4_pmu_train_1d_dmem_202006.bin
- lpddr4_pmu_train_1d_imem_202006.bin
- lpddr4_pmu_train_2d_dmem_202006.bin
- lpddr4_pmu_train_2d_imem_202006.bin |
Obtaining the Security Controller (SECO) Firmwareβ
BSP Version | NXP BSP Version | NXP Firmware File Name | NXP Firmware Path |
---|---|---|---|
6.0.0 | L5.15.32_2.0.0 | imx-seco-3.8.6.bin | imx-seco-3.8.6/firmware/seco/<SoC> -ahab-container.img |
6.1.0 | L5.15.52_2.1.0 | imx-seco_5.8.7.1.bin | imx-seco-5.8.7.1/firmware/seco/<SoC> -ahab-container.img |
Replace <SoC>
for one of the following:
- Apalis iMX8:
mx8qmb0
- Colibri iMX8X V1.0B and earlier:
mx8qxb0
- Colibri iMX8X V1.0C and later:
mx8qxc0
You can use wget
for download, and extract the firmware. For example:
$ cd ~/workdir
$ wget -c https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/imx-seco-5.8.7.bin
$ chmod u+x imx-seco-3.8.6.bin
$ ./imx-seco-5.8.7.bin
$ ls imx-seco-5.8.7/firmware/seco/
info
NXP provides the SECO firmware in binary form. You will need to accept the NXP End-User License Agreement (EULA) as part of the unpacking process.
Build the SCU Firmware (SCFW)β
The SCU firmwares for Apalis iMX8/Apalis iMX8X and Colibri iMX8X are available in our i.MX-System-Controller-Firmware GitHub repository.
BSP Version | NXP BSP Version | SCFW Version |
---|---|---|
6.0.0 | L5.15.32_2.0.0 | 1.14.0 |
6.1.0 | L5.15.52_2.1.0 | 1.14.0 |
info
Toradex customers rarely need to rebuild SCFW. Therefore, in the following instructions, we will show how you can download the SCFW firmware. If you, for any reason, need to customize SCFW, see the Build Custom i.MX 8/8X System Controller Firmware (SCFW) article.
You can use clone the SFCW repository from Toradex Github:
$ mkdir -p ~/workdir/scfw-bin && cd ~/workdir/scfw-bin
$ git clone https://github.com/toradex/i.MX-System-Controller-Firmware.git
$ mv ./i.MX-System-Controller-Firmware/src/scfw_export_<mx8qm-or-mx8qx>_b0/build_<mx8qm-or-mx8qx>_b0/mx8qm-apalis-scfw-tcm.bin ./scfw_tcm.bin
If you need, you can use wget to download an specific version. For example:
$ wget https://github.com/toradex/i.MX-System-Controller-Firmware/raw/d8d7320aec228cd73c4512a92d55c900786873e0/src/scfw_export_mx8qx_b0/build_mx8qx_b0/mx8qx-colibri-scfw-tcm.bin
The file scfw_tcm.bin
is necessary for the final container.
Build the ARM trusted firmware (ATF/TF-A)β
For the i.MX 8/8X and i.MX 8MM/8MP based modules, the ATF/TF-A Branch is lf_v2.6
and the binary is always the following form:
build/<Platform>/release/bl31.bin
Replace <Platform>
by the one of the platforms indicated below.
- Apalis iMX8:
imx8qm
- Colibri iMX8X:
imx8qx
- Verdin iMX8M Mini:
imx8mm
- Verdin iMX8M Plus:
imx8mp
On top of it, OpenEmbedded applies two patches, necessary for the system to behave as expected:
- 0001-Revert-Add-NXP-s-SoCs-partition-reboot-support.patch
- 0002-imx8m-hab.c-work-around-gcc-12.1-false-positives.patch
The patches can be found at: files Β» imx-atf Β» recipes-bsp - meta-toradex-nxp.git and you can apply them manually using git am
or git apply
.
ATF / TF-A can be obtained from NXP git servers:
$ cd ~/workdir
$ git clone https://github.com/nxp-imx/imx-atf.git -b lf_v2.6
info
If your company firewall/gateway inhibit the git protocol, you may use HTTP or HTTPS instead.
Currently, there are no changes necessary for ARM Trusted Firmware. To be able to build the ATF firmware, first you'll need to download and setup the GNU Toolchain for Hard Float Calling Convention. When the toolchain is installed and you have the CROSS_COMPILE, ARCH and PATH correctly pointing to it, you can proceed to build the firmware directly:
$ cd imx-atf
$ make PLAT=<Platform> bl31
The resulting binary file bl31.bin
created in build/<Platform>/release/
is necessary for the final container.
Assemble the Boot Containerβ
Finally, to assembly the boot container, you need to obtain the imx-mkimage
utility.
SoC | Branch |
---|---|
i.MX 8/8x | lf-5.15.32_2.0.0 |
i.MX 8MM/8MP | lf-5.15.32_2.0.0 |
Git clone it from the repository:
$ cd ~/workdir
$ git clone -b <Branch> https://source.codeaurora.org/external/imx/imx-mkimage/
Replace <Branch>
by the Branch for your specific configuration as indicated in the above table.
caution
The binary u-boot.bin
is necessary for the final boot container. Make sure you followed the previous instructions on how to build U-Boot.
Copy all the binaries to the corresponding SoC directory inside imx-mkimage
. For Apalis iMX8QM, for example
$ cp ~/workdir/scfw-bin/scfw_tcm.bin iMX8QM
$ cp ~/workdir/imx-atf/build/imx8qm/release/bl31.bin iMX8QM
$ cp ~/workdir/u-boot-toradex/u-boot.bin iMX8QM
$ cp ~/workdir/imx-seco-3.7.4/firmware/seco/<SECO Firmware File> iMX8QM
Finally, use make
to build the boot container. For Apalis iMX8QM:
$ make SOC=iMX8QM flash_b0
$ ls iMX8QM/flash.bin
And for Colibri iMX8QX:
$ make REV=C0 SOC=iMX8QX flash
$ ls iMX8QX/flash.bin
Replace <SECO Firmware File>
by the SECO Firmware File for your specific configuration as indicated before and rename the file flash.bin
to imx-boot
so it will be suitable for a Toradex Easy Installer Image. Make sure to remove REV=C0
if you are building for B0 or older silicon versions.
$ mv iMX8<QM-or-QX>/flash.bin iMX8QM/imx-boot
The binary i.MX 8M Mini/8M Plus SoC boot container aggregates:
- DDR training firmware (
lpddr4_pmu_train*
) - TF-A firmware (
bl31.bin
) - U-Boot SPL (
spl/u-boot-spl.bin
) - U-Boot proper (
u-boot-nodtb.bin
) - U-Boot device tree:
- for Verdin iMX8M Mini: (
arch/arm/dts/imx8mm-verdin.dtb
renamed tofsl-imx8mm-evk.dtb
) - for Verdin iMX8M Plus: (
arch/arm/dts/imx8mp-verdin.dtb
renamed tofsl-imx8mp-evk.dtb
) mkimage
executable (tools/mkimage
renamed tomkimage_uboot
)
Copy them to the 'iMX8M' SoC directory inside imx-mkimage
. For example, for Verdin iMX8MM:
$ cd ~/workdir/imx-mkimage/
$ cp ~/workdir/firmware-imx-*/firmware/ddr/synopsys/lpddr4_pmu_train_1d_dmem.bin iMX8M
$ cp ~/workdir/firmware-imx-*/firmware/ddr/synopsys/lpddr4_pmu_train_2d_dmem.bin iMX8M
$ cp ~/workdir/firmware-imx-*/firmware/ddr/synopsys/lpddr4_pmu_train_1d_imem.bin iMX8M
$ cp ~/workdir/firmware-imx-*/firmware/ddr/synopsys/lpddr4_pmu_train_2d_imem.bin iMX8M
$ cp ~/workdir/imx-atf/build/imx8mm/release/bl31.bin iMX8M
$ cp ~/workdir/u-boot-toradex/spl/u-boot-spl.bin iMX8M
$ cp ~/workdir/u-boot-toradex/u-boot-nodtb.bin iMX8M
$ cp ~/workdir/u-boot-toradex/arch/arm/dts/imx8mm-verdin.dtb iMX8M/fsl-imx8mm-evk.dtb
$ cp ~/workdir/u-boot-toradex/tools/mkimage iMX8M/mkimage_uboot
And, finally, use make
to build the boot container. For Verdin iMX8MM:
$ make clean; make SOC=iMX8MM flash_evk_emmc_fastboot
$ ls iMX8M/flash.bin
And for Verdin iMX8MP:
$ make clean; make SOC=iMX8MP DCD_BOARD=imx8mp_evk flash_evk_emmc_fastboot
$ ls iMX8M/flash.bin
Rename the file flash.bin
to imx-boot
so it will be suitable for a Toradex Easy Installer Image.
$ mv iMX8M/flash.bin iMX8M/imx-boot
Deploying the U-Boot binary to an Imageβ
To deploy your custom U-Boot binary to an image, follow the steps described bellow:
- Start from a Existing Sample Image: Download and extract one of the Toradex prebuild images. Choose the appropriate image for your SoM in the Reference Images for Yocto Project Software Downloads
- Integrate Artifacts: Integrate the U-Boot artifacts into our regular Toradex Easy Installer package: Replace the U-Boot binary and, if applicable, the SPL in the unpacked Toradex Easy Installer directory.
- Adjust Image.json: Now adjust the
image.json
to your liking (e.g., change the name and description for you to distinguish it from the original package). You may, of course, also change any of the other properties as documented in the Toradex Easy Installer article on our developer website. - Deploy the Toradex Easy Installer Image: You may now use the above prepared Toradex Easy Installer package with the Toradex Easy Installer.
U-Boot: Standalone Applicationβ
info
If you don't know if you need to read this, you will not need to read this.
U-Boot allows one to run standalone applications
which may use callbacks into the U-Boot binary. One has to overcome some obstacles to get the hello_world
example in the U-Boot sources up and running on an Arm-based CPU.
Details
1) The binary needs to be linked to an address where it eventually gets loaded and executed. I.e., set CONFIG_STANDALONE_LOAD_ADDR to your chosen address.
2) U-Boot and the standalone application must be compiled for the same ARM instruction set (arm vs. thumb) if you build both binaries from the same configured U-Boot tree that is inherently so.
3) If the binaries are built for thumb then one has to set the LSB in the address given to the go command. E.g. if the entry point is 0x12000000, then one has to start with 'go 12000001'.
Example for Colibri iMX6:
Link the standalone binary to 0x11000000 and build it as follows:
$ make CONFIG_STANDALONE_LOAD_ADDR=0x11000000 -j$(nproc)
Copy the resulting binary file to an SD card
$ cp examples/standalone/hello_world.bin <path to SD card>/
In U-Boot, load the binary to RAM and execute it. Note that for some time, we compile using the thumb instruction set.
> load mmc 1:1 0x11000000 hello_world.bin
reading hello_world.bin
578 bytes read in 14 ms (40 KiB/s)
> go 0x11000001 Hello World!
## Starting application at 0x11000001 ...
Example expects ABI version 9
Actual U-Boot ABI version 9
Hello World
argc = 3
argv[0] = "0x11000001"
argv[1] = "Hello"
argv[2] = "World!"
argv[3] = "<NULL>"
Hit any key to exit ...
## Application terminated, rc = 0x0