U-Boot environment access from any OS

Published on March 24, 2022

U-Boot environment variables are an important piece of the boot process as they can customize the bootloader behavior or even set the display configuration. This blog post will therefore detail how to access them from any OS. It is an update to a previous article as we now use the eMMC as our bootloader storage.

What is the U-Boot environment?

As the documentation puts it: the U-Boot environment is a block of memory that is kept on persistent storage and copied to RAM when U-Boot starts. It is used to store environment variables which can be used to configure the system. The environment is protected by a CRC32 checksum. Here are the details of a few important variables:

  • bootargs: This variable contains the Linux kernel boot arguments (cmdline)
  • bootcmd: U-Boot executes automatically that command at bootup after the countdown
  • bootdelay: Number of seconds U-Boot waits to execute the bootcmd variable
  • cmd_custom: BD-specific variable that is executed inside our bootscripts, this variable allows to modify the bootargs or even the device tree
  • fb_hdmi / fb_lvds / fb_mipi: BD-specific variables to configure the display

The persistent storage on all our i.MX8 platforms is an eMMC. The bootloader (U-Boot) and its environment are stored into the boot hardware partition which is different from the main storage area. This boot partition usually weighs a few MegaBytes (4MB currently, but older parts only had 2MB). The size reserved for U-Boot environment is 8kB in our default bootloader configuration which resides at the end of the boot partition. U-Boot offers a few commands in its prompt (over serial connection) to access the environment:

  • printenv: shows all the environment variables
  • saveenv: saves the current environment into the persistent storage
  • setenv: sets a specific variable to some values

However it sometimes is useful to customers to modify the environment from the OS which we will see in the next sections.

OS integration

Accessing those variables is possible for any OS we support in our wiki thanks to the libubootenv project and its fw_printenv & fw_setenv tools.

Yocto

All our Yocto images, starting with the Honister release, now includes the tools in our default image:

You can see all the configuration files for each CPU inside the layer too:

That configuration file is always located under /etc/fw_env.config.

Ubuntu/Debian

On Ubuntu/Debian, you can install those tools like any other package:

$ sudo apt install u-boot-tools

The configuration uses mmcblk0 devices which is correct for most platforms but not all. That is why if the tool says "configuration file wrong or corrupted", please make sure to check /etc/fw_env.config against the one from the Yocto section.

Android

Boundary Devices actually contributed to the libubootenv project to add Android support. So we now include those tools in all our Android images starting with Android 11.

How do the tools work?

The way the tools work is identical across all OSes and relies on 2 binaries: fw_printenv & fw_setenv.

fw_printenv

This tool, as its name suggests, prints/shows all the variables from the U-Boot environment:

# fw_printenv 
arch=arm
baudrate=115200
board=nitrogen8m
board_name=nitrogen8m
...

Note that you can specify one or several variables you need to see instead of dumping all of them:

# fw_printenv board uboot_defconfig uboot_release                                                                                                                                                
board=nitrogen8m
uboot_defconfig=nitrogen8m_2gr0
uboot_release=2020.10-53225-ge71deb4

This can be useful to check the U-Boot version you are running and trigger an update if it isn't up to date.

fw_setenv

This tool is also pretty explicit as it allows to set a variable into the U-Boot environment.

# fw_setenv test 'this is a test for the blog post'

As a result, you can see in U-Boot over the serial prompt that the variable matches the new value:

=> printenv test
test=this is a test for the blog post

Here are a few examples of useful settings from the OS:

  1. Forcing one MIPI display configuration:
# fw_setenv fb_mipi m101nwwb
  1. Adding some debug logs to the kernel:
# fw_setenv cmd_custom 'setenv bootargs ${bootargs} initcall_debug no_console_suspend'
  1. Disabling one peripheral from the device tree:
# fw_setenv cmd_custom 'fdt set uart1 status disabled;'
  1. Disabling U-Boot countdown:
# fw_setenv bootdelay 0

  That's it, you should now be able to read/write U-Boot environment variables from your OS. As usual, let us know if anything is unclear.