U-Boot
Introduction
This article explains how to manage the console and environment variables in a running U-Boot instance so you can troubleshoot, modify, or set up your own boot configuration. If you wish to build a customized version of U-Boot, see Build U-Boot From Source Code.
What is U-Boot?
U-Boot is an open-source bootloader commonly used in embedded devices. It originated as a simple bootloader for the PowerPC architecture, publicly released in 2000 under the name PPCBoot. Shortly thereafter, it was renamed U-Boot (short for Universal Boot Loader) to reflect its evolution into a multi-architectural bootloader.
Today, U-Boot is a fully-fledged bootloader supporting more than a dozen architectures, several filesystems, and a range of interfaces. It features a console interface through the serial port, offering low-level commands and environment variables that provide high flexibility in configuring the boot process.
Its most remarkable achievement, however, is its broad driver support, which has established it as the preferred bootloader for most embedded platforms. Toradex also uses U-Boot as the bootloader for its images. You can find the code in our repositories.
Requirements
To access the U-Boot console, you need a running U-Boot instance on your module. U-Boot is automatically available in your image if you have either:
- Installed a compatible image from the Linux Software page on our site.
- Used the Toradex Easy Installer to install a compatible image from our feed servers.
- Built your own image using the Toradex Open Embedded meta layers from our git repositories.
The U-Boot Console
When U-Boot is running in RAM, it sends its output through the primary Full Function (FF) UART (usually UART_A). Toradex carrier boards typically route this UART to a USB or RS-232 connector. For detailed signal availability and routing information, refer to the corresponding carrier board datasheet.
To view the serial output from a host machine, use a serial port reader program such as Minicom or PuTTY, configured to read the corresponding USB serial port (e.g., /dev/ttyUSB* on Linux). For more information, see Configuring Serial Port Debug Console (Linux/U-Boot).
Once you can read the output from your host machine, press any key before the autoboot sequence starts to access the U-Boot console. By default, U-Boot waits up to 3 seconds before continuing with the boot process. See below an example output after preventing autoboot:
U-Boot 2024.07-7.3.0+git.3f772959501c (Jan 01 1970 - 00:00:00 +0000)
CPU: Freescale i.MX8MP[8] rev1.1 1600 MHz (running at 1200 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 66C
DRAM: 4 GiB
Core: 177 devices, 31 uclasses, devicetree: separate
WDT: Started watchdog@30280000 with servicing every 1000ms (60s timeout)
MMC: FSL_SDHC: 1, FSL_SDHC: 2
Loading Environment from MMC... Reading from MMC(2)... OK
In: serial@30880000
Out: serial@30880000
Err: serial@30880000
Model: Toradex 0058 Verdin iMX8M Plus Quad 4GB WB IT V1.1B
Serial#: 15403726
Carrier: Toradex Yavia V1.0A, Serial# 35069094
SEC0: RNG instantiated
Net: pca953x gpio-expander@21: Error reading output register
eth1: ethernet@30be0000, eth0: ethernet@30bf0000 [PRIME]
Hit any key to stop autoboot: 0
Verdin iMX8MP #
U-Boot Variables
This section presents the variables that control bootloader behavior, influencing aspects such as boot order, console settings, and hardware initialization during system startup.
Environment Variables
The following table lists the most important environment variables and their default values:
| Environment Variable | Description | Default Value | Allowed Values |
|---|---|---|---|
| baudrate | Debug console baud rate. | 115200 | UART baud rates supported by the SoM. |
| boot_targets | Defines the sequence of device types U-Boot will try to boot from, in priority order. | SoM-specific e.g., mmc1 mmc2 dhcp for the Verdin iMX8M Plus | Space-separated list of valid targets such as mmc, usb, nvme, pxe, dhcp. Order determines boot priority. |
| console | Specifies the UART device used for serial console output. | SoM-specific e.g., ttymxc0 for the Colibri i.MX7 | UART device name, such as ttymxc0, ttymxc1 or ttymxc2. |
| devnum | Index of the current boot device (e.g., 0 for mmc0, 1 for mmc1). | Set at runtime | Non-negative integers. |
| devtype | Type of the current boot device being probed by Distro Boot. | Set at runtime | mmc, usb, scsi, sata, nvme, pxe, dhcp |
| distro_bootpart | Partition number of the boot device used to load kernel and device tree. | Set at runtime | Integer value representing a partition number. |
| fdtfile | Name of the device tree binary (DTB) file used during boot. | SoM-specific e.g., imx8mp-verdin-wifi-dev.dtb | Any .dtb file located on the boot partition. |
| fdt_board | Identifies the carrier board to select the appropriate DTB. This is not a standard U-Boot variable, but is commonly used in Toradex BSPs. | SoM-specific e.g., dev for the Verdin Family | Strings representing carrier board identifiers. |
| kernel_image | Name of the Linux kernel image file. This is a Toradex-specific variable. | Not set by default | Any valid kernel image filename (e.g., Image, zImage, uImage, or custom naming scheme). |
| rootfsargs | Optional variable that may be used in custom boot scripts to define the root filesystem location. | Set dynamically | Any valid kernel parameter string (e.g., root=/dev/mmcblk0p2 rw). |
| root_devnum | Index of the device containing the root filesystem. | Set at runtime | Non-negative integers. |
| root_devtype | Type of the device used for the root filesystem. | Set at runtime | mmc, usb, nfs-dhcp or nfs-static. |
| root_part | Partition number containing the root filesystem. | 2 | Integer value. |
| skip_fdt_overlays | Set to skip loading device tree overlays. | Not set by default | "" (unset), "0" (apply overlays), or "1" (skip overlays). |
| tdxargs | Kernel arguments used in Toradex BSPs. This is a Toradex-specific variable. | Set dynamically | Any string containing kernel parameters. |
Some variables, such as arch and boot_targets store configuration values used by U-Boot and the Linux kernel. Others, such as scan_dev_for_boot and bootcmd, contain shell-style command strings (e.g., run, echo, etc) that U-Boot can interpret and execute. For example:
arch=arm
...
boot_targets=mmc1 mmc2 dhcp
...
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;
...
bootcmd=run distro_bootcmd
Distroboot Variables
DistroBoot (also known as the Generic Distro Configuration Concept) is U-Boot's standard boot-discovery flow.
When DistroBoot is enabled, U-Boot scans a predefined set of boot targets, looking for standard boot configuration files (for example, extlinux/extlinux.conf) or a U-Boot boot script (boot.scr). For more details about how DistroBoot works specifically on Toradex System on Modules, see DistroBoot.
The list below includes variables used by Toradex boot scripts that are relevant for customizing or debugging the boot process:
| Variable | Description | Example |
|---|---|---|
| boot_targets | A space-separated list defining the order in which U-Boot probes boot sources. | mmc1 mmc2 dhcp |
| devnum | Set by DistroBoot to the device index of the current boot target. | 1 for mmc1 |
| devtype | Set by DistroBoot when probing a target. It defines the device type being scanned . | mmc, usb, scsi |
devtype and devnum are runtime variables that U-Boot sets automatically while scanning each entry in boot_targets. To control the boot order, edit the boot_targets variable. For example, if boot_targets=mmc1 mmc2 dhcp, U-Boot follows the procedure below:
- Tries to boot into the
mmc1device first (settingdevtype=mmcanddevnum=1). - Scans for boot files such as
extlinux/extlinux.conforboot.scr. - If the scan fails, U-Boot retries the process with
mmc2, thendhcp.
Internal Variables
Internal variables are used by U-Boot and Toradex boot scripts. If you need to adjust kernel arguments, use the following methods depending on your objective:
- At build time: Modify the APPEND variable in Yocto
- At runtime (for debugging): Modify
tdxargs, as explained further in this article
Variables like rootfsargs and setupargs are managed by the boot script and should not be modified manually.
Treat these variables as read-only. Modifying these variables directly can break device tree loading, kernel arguments, or the entire boot process.
| Variable | Description |
|---|---|
| bootargs | Final string of kernel arguments passed to Linux. Built from rootfsargs, setupargs, appendargs (from the Yocto APPEND variable) and tdxargs. Do not set it directly. |
| bootcmd_boot | Stores the script that executes the actual boot process. |
| bootcmd_dtb | Stores the script used to load the device tree. |
| bootcmd_kernel | Stores the script snippet that loads the kernel from storage. |
| bootcmd_unzip | Unzips compressed kernels on i.MX8-based SoMs. |
| fdt_addr_r | Set by Toradex. Contains the memory address where the device tree is loaded. |
| fdt_high | Set by Toradex. Contains the memory address where the device tree is relocated. |
| fdt_overlays | Aggregates overlay names to be loaded by the boot script. |
| filesize | Set by U-Boot. Contains the file size in hexadecimal. Example: after loading a file using load mmc 0 ${loadaddr} zImage. |
| fitconf_fdt_overlays | Aggregates overlay values parsed from overlays.txt. |
| kernel_addr_load | Set by the boot script. Stores the memory address where the kernel should be loaded. |
| kernel_addr_r | Set by Toradex. Contains the memory address where the kernel is loaded. |
| load_cmd | Stores commands related to file loading in the boot script. |
| load_overlays_file | Stores the script that loads overlays.txt and imports overlay variables. |
| loadaddr | Set by Toradex. Contains the memory address where data (e.g., kernel image) can be loaded. |
| overlay_file | Temporarily stores the name of the overlay currently being applied in the boot script loop. |
| ramdisk_addr_r | Set by Toradex. Contains the memory address where the initial ramdisk is loaded. |
| rootfsargs_set | Contains the kernel parameters used to locate the root filesystem. Set within the boot script. Use rootfsargs to override it. |
| setupargs | Defined dynamically at boot time using run setup. Used to set platform-specific kernel arguments. |
| set_apply_overlays | Stores the script snippet that applies device tree overlays. |
| set_bootcmd_dtb | Stores the script snippet that sets up device tree loading. |
| set_bootcmd_kernel | Stores the script snippet that sets up kernel loading. |
| set_load_overlays_file | Stores the script snippet that loads and imports overlay variables from overlays.txt. |
| soc | Set by U-Boot. Used by Toradex to set fdtfile on some platforms. |
| variant | Set at every boot. Indicates the SoM variant and features (e.g., Wi-Fi, eMMC). Used to select the correct device tree (e.g., -wifi-v1.1, -emmc). |
U-Boot Commands
List Environment Variables
To list all currently set variables, run the printenv command in the U-Boot console.
> printenv
To print the value of a single variable, use printenv <variable>. For example:
> printenv fdt_board
fdt_board=eval-v3
Create New Variables
You can create a new variable using the setenv command:
> setenv myvar myvalue
> printenv myvar
myvar=myvalue
Modify Existing Variables
You can set the value of an existing variable using setenv <variable> <value>. In the following example, the variable fdt_board contains the default string eval-v3, which will be modified to aster using the setenv command:
> printenv fdt_board
fdt_board=eval-v3
> setenv fdt_board aster
> printenv fdt_board
fdt_board=aster
Reference Other Variables
You can reference the value of a variable inside another by prepending a $ symbol:
> setenv myvar2 $myvar
> printenv myvar2
myvar2=myvalue
Set Multi-Parameter Arguments
To prevent variable expansion or to define multiple statements, enclose the value in single quotes:
> setenv myvar 'myvalue1 myvalue2 myvalue3'
Save the Environment
Using setenv to create or modify variables only changes their values in RAM. To make the changes permanent, you must store them in flash using the saveenv command:
> setenv myvar myvalue
> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
If you now reboot your system, the variable myvar from the previous example will still be defined in U-Boot.
Reset the Environment to Default Values
If you want to discard any environment changes you made using saveenv, you can restore the default values with the env default -a command:
> env default -a
## Resetting to default environment
> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
Execute Commands in Variables
Variables may contain commands that are executable by U-Boot, such as echo, ls, or reset. To list the available commands, type help in the U-Boot console. To execute a variable that contains commands, use run <variable>. For example:
> setenv myvar 'setenv myvar2 myvalue2'
> printenv myvar2
## Error: "myvar2" not defined
> run myvar
> printenv myvar2
myvar2=myvalue2
A common example of variable execution occurs during autoboot. Autoboot essentially runs the boot command, which internally calls run bootcmd. The bootcmd variable contains other commands and references to additional variables that, in turn, contain more commands. This process resembles the execution of a shell script.
Configure U-Boot at Runtime
Set Custom Kernel Parameters
All custom kernel parameters must be set using the tdxargs environment variable. By default, tdxargs is empty and reserved for user-defined parameters.
On startup, U-Boot runs a boot script that assembles the final kernel command line bootargs as follows:
env set bootcmd_args 'run rootfsargs_set && env set bootargs ${rootfsargs} ${setupargs} ${appendargs} ${tdxargs}'
In other words, if tdxargs is defined, its contents are appended to the end of bootargs.
If you define them elsewhere, the changes will not take effect. The boot script recomposes bootargs at every boot from rootfsargs, setupargs, appendargs, and tdxargs.
A complete list of kernel parameters is available in the Linux kernel documentation: The kernel's command-line parameters.
As mentioned earlier, changing an existing variable requires the use of setenv. In some cases, such as modifying internal environment parameters, you may need to embed the changes inside variables like setupargs or tdxargs.
The following variables are appended to the end of bootcmd_args and used during the boot process:
rootfsargssetupargstdxargs
For example, to add temporary debugging parameters at runtime:
> setenv tdxargs "ignore_loglevel printk.devkmsg=on"
> saveenv
Overriding the console and baud rate can lead to unexpected results. For example, U-Boot and the Linux kernel might end up using different UART ports or baud rates.
Switch Device Trees
At runtime, you can update the device tree in two ways:
- Set a new device tree directly in
fdtfile: Modify it directly usingsetenv. - Set a different board name in
fdt_board: This requires additional configuration, follow the steps below.
The fdtfile variable is automatically set by the U-Boot preboot script. However, if fdtfile is already saved, the preboot step won't overwrite it. To use the fdt_board method:
-
Unset
fdtfile.> setenv fdtfile -
Set
fdt_board.> setenv fdt_board dahlia -
Save the environment.
> saveenv
After rebooting, you should see the regenerated values:
> printenv fdt_board
fdt_board=dahlia
> printenv fdtfile
fdtfile=imx8mp-verdin-wifi-dahlia.dtb
To view Toradex's device tree hierarchy diagrams, see Device Trees on Toradex System on Modules. This resource includes carrier-board-level device trees, which can help you determine the correct fdt_board to set.
For alternative methods, such as rebuilding U-Boot to change the default device tree, see First Steps with Device Trees.
Handle and Re-create the Toradex Config Block
The Toradex Config Block holds information about the module used by the bootloader, such as the module type, version and serial number.
The cfgblock command is used to manage Toradex config block operations. If you stop the boot process and type cfgblock, you will see the available options:
> cfgblock
cfgblock - Toradex config block handling commands
Usage:
cfgblock create [-y] [barcode] - (Re-)create Toradex config block
create carrier [-y] [barcode] - (Re-)create Toradex Carrier config block
cfgblock reload - Reload Toradex config block from flash
The cfgblock create command is used to (re)create the config block. When you run this command in U-Boot, follow the on-screen prompts, as shown in the example below for the Apalis iMX6Q:
> cfgblock create
A valid Toradex config block is present, still recreate? [y/N] y
Enabled modules:
0027 Apalis iMX6Q 1GB
0028 Apalis iMX6Q 2GB IT
0029 Apalis iMX6D 512MB
0035 Apalis iMX6D 1GB IT
Enter the module ID: 0027
Enter the module version (e.g. V1.1B or V1.1#26): V1.1A
Enter module serial number: 05041231
Toradex config block successfully written
If the Config Block is missing or corrupted, it is also possible to recreate it with Toradex Easy Installer.
Troubleshooting
Warning - bad CRC, using default environment
You may see the following message during boot:
*** Warning - bad CRC, using default environment
This is most likely not an issue. The flash sector containing the environment variables is not initialized yet. Save the environment variables using the saveenv command, and the message will go away.
Source: https://www.denx.de/wiki/view/DULG/WarningBadCRCUsingDefaultEnvironment (archived)