The U-Boot firmware utils allow Linux to access the U-Boot environment partition, and its configuration has changed in Petalinux 2023.1.
Overview
Before Petalinux 2023.1, the U-Boot firmware utilities did not have a dedicated Bitbake recipe, and required to be manually added and patched. While this was cumbersome and awkward to keep up-to-date with Petalinux versions, it did work.
In Petalinux 2023.1, there’s a ready-made recipe included in the Yocto layers that simplifies including and compiling the utilities into the build. The original bbappend file is under <Petalinux-project>/components/yocto/layers/poky/meta/recipes-bsp/u-boot/libubootenv_0.3.3.bb.
Simply copying this .bb file to under the u-boot directory <Petalinux-project>/project-spec/meta-user/recipes-bsp/u-boot
does the trick, and after the build, the utilities will be added to the rootfs. These are fw_printenv, to print any variables (or all) from the environment; and fw_setenv, to set a variable and save it to the environment.
While the utilities are available in the rootfs, using them throws these error messages:
root@target:~# fw_printenv
Cannot read environment, using default.
Cannot read default environment from file.
These messages show two issues with our system that we will address next.
The configuration file
Cannot read environment, using default means that there was some issue reading the U-Boot environment, in this case off the QSPI flash partition. The utilities use a configuration file to know how to find this information. By default, this file is expected to be /etc/fw_env.config
.
The configuration file is quite simple and details where to find the U-Boot environment in the system. In this case, we’re using a QSPI partition, so we’ll specify the device, the offset, the environment size and the erase sector size. This will have to be specified for both environment partitions (principal & redundant) as we will see below.
# Device Start offset Size Erase size
/dev/mtd2 0x00000 0x40000 0x20000
/dev/mtd2 0x40000 0x40000 0x20000
We find the erase sector size from the mtd partitions, as it must match the size in the configuration file.
root@target:~# cat /proc/mtd
dev: size erasesize name
mtd0: 02580000 00020000 "boot"
mtd1: 00080000 00020000 "bootscr"
mtd2: 00080000 00020000 "bootenv"
mtd3: 05980000 00020000 "kernel"
Another change in 2023.1, is that U-Boot by default enables the redundant environment partition, through the SYS_REDUNDAND_ENVIRONMENT [sic]. That’s why we have to specify two environments in the configuration file. For a total of 0x80000, the partition is split in two 0x40000 areas. Another alternative is to disable the redundant environment, but we kept it on.
The second message Cannot read default environment from file means that since the environment could not be read, the alternative file was also not found. This file is by default called /etc/u-boot-initial-env
and would provide a nominal environment.
Customizing
Now we need to include the fw_env.config file in our build, so it’s available to the utilities in the running system. For this, we modify the libubootenv recipe to add our customized file.
We add the fw_env.config file under files/ and modify the recipe as shown below.
.
├── files
│ ├── bsp.cfg
│ ├── fw_env.config
│ └── platform-top.h
├── libubootenv_0.3.3.bb
└── u-boot-xlnx_%.bbappend
Adding these lines to libubootenv_0.3.3.bb:
L = "${WORKDIR}"
SRC_URI += " file://fw_env.config"
do_install:append() {
install -d ${D}${sysconfdir}
install -m 0644 ${L}/fw_env.config ${D}${sysconfdir}/fw_env.config
}
Lastly, we add the U-Boot configuration settings related to the environment, in this case to the files/bsp.cfg
CONFIG_ENV_SIZE=0x40000
CONFIG_ENV_OFFSET=0x2600000
CONFIG_ENV_OFFSET_REDUND=0x2640000
CONFIG_ENV_SECT_SIZE=0x4000
Checking
We will read a variable from U-Boot, then save the environment and modify it from Linux.
ZynqMP> echo ${ethaddr}
00:0a:35:00:22:01
ZynqMP> env save
Saving Environment to SPIFlash... Erasing SPI flash...Writing to SPI flash...done
Valid environment: 2
OK
From Linux, we can verify the environment by reading back the variable.
root@target:~# fw_printenv ethaddr
00:0a:35:00:22:01
We set a new value to the variable and save it.
root@target:~# fw_setenv ethaddr 70:B3:D5:xx:xx:xx
Back in U-Boot, we notice this message, where U-Boot has two MAC addresses on hand, the first hard-coded in the device tree (DT) and the other read from its environment. The latter will always take precedence.
Warning: ethernet@ff0c0000 MAC addresses don't match:
Address in DT is 00:0a:35:00:22:01
Address in environment is 70:b3:d5:xx:xx:xx
We can check the new variable value:
ZynqMP> echo ${ethaddr}
70:B3:D5:xx:xx:xx
Note: The ethaddr variable is perhaps not the best example of manipulating a variable, as it is handled differently by U-Boot. Since it carries the hardware Ethernet MAC, U-Boot does not allow it to be re-written, throwing the message “Error: Can’t overwrite "ethaddr"“.
ZynqMP> setenv ethaddr 00:0a:35:00:22:01
## Error: Can't overwrite "ethaddr"
## Error inserting "ethaddr" variable, errno=1
However, this limitation is not observed by the fw_setenv utility.
Conclusion
In this post, we updated the u-boot-fw-utils recipe to use the new default of enabling the redundant environment partition, customized the recipe to add our configuration file and verified the utilities can read and write U-Boot variables in non-volatile flash storage.