How to Modify U-Boot Environment Variables in PetaLinux

How to Modify U-Boot Environment Variables in PetaLinux

In this post we will look at two methods for modifying the U-Boot environment variables. The first method is changing the values stored in flash from the UBoot command prompt. The second method is changing the hard-coded default values in the PetaLinux project. We’ll be assuming a boot from QSPI flash, although the concepts also apply to booting from SD card or other non-voltatile storage devices.

The U-Boot environment variables determine exactly how a board is supposed to boot. From the UBoot command prompt, you can display the environment variables and their values by using the printenv command. The console display below shows a typical example of interrupting the flash boot process and running the printenv command:

FS-BOOT First Stage Bootloader (c) 2013-2014 Xilinx Inc.
Build date: Jan 11 2021 03:08:26
Serial console: Uart16550
FS-BOOT: Booting from FLASH.


U-Boot 2020.01 (Jan 11 2021 - 03:13:09 +0000)

Model: Xilinx MicroBlaze
DRAM:  2 GiB
WDT:   Not found!
Loading Environment from SPI Flash... SF: Detected n25q256ax1 with page size 256 Bytes, erase size 4 KiB, total 32 MiB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Model: Xilinx MicroBlaze
U-BOOT for xilinx-kcu105-2020_2

Hit any key to stop autoboot:  4
U-Boot>printenv
arch=microblaze
autoload=no
baudrate=115200
board=microblaze-generic
board_name=microblaze-generic
boot_img=u-boot-s.bin
bootcmd=bootm 0x80000000 0x82e00000 0x81e00000
bootdelay=4
bootenvsize=0x40000
bootenvstart=0x1180000
bootsize=0x180000
bootstart=0x1000000
clobstart=0x80000000
console=console=ttyS0,115200
cp_kernel2ram=sf probe 0 && sf read ${netstart} ${kernelstart} ${kernelsize}
default_bootcmd=bootcmd
dtbnetstart=0x81e00000
eraseenv=sf probe 0 && sf erase ${bootenvstart} ${bootenvsize}
eserial0=setenv stdout eserial0;setenv stdin eserial0
fault=echo ${img} image size is greater than allocated place - partition ${img} is NOT UPDATED
fdt_addr_r=81f00000
fdt_high=b0000000
fdtcontroladdr=fffe8170
fpga_img=system.bit.bin
fpgasize=0x1000000
fpgastart=0x0
initrd_high=b0000000
install_boot=sf probe 0 && sf erase ${bootstart} ${bootsize} && sf write ${clobstart} ${bootstart} ${filesize}
install_fpga=sf probe 0 && sf erase ${fpgastart} ${fpgasize} && sf write ${clobstart} ${fpgastart} ${filesize}
install_kernel=sf probe 0 && sf erase ${kernelstart} ${kernelsize} && sf write ${clobstart} ${kernelstart} ${filesize}
kernel_addr_r=82000000
kernel_img=image.ub
kernelsize=0xc00000
kernelstart=0x11c0000
loadaddr=0x80000000
netstart=0x80000000
netstartaddr=0x81000000
pxefile_addr_r=ff100000
ramdisk_addr_r=82600000
script_offset_f=fc0000
scriptaddr=ff200000
sdbootdev=0
serial=setenv stdout serial;setenv stdin serial
test_crc=if imi ${clobstart}; then run test_img; else echo ${img} Bad CRC - ${img} is NOT UPDATED; fi
test_img=setenv var "if test ${filesize} -gt ${psize}; then run fault; else run ${installcmd}; fi"; run var; setenv var
vendor=xilinx

Environment size: 1665/262140 bytes
U-Boot>

From the U-Boot prompt, you can modify the environment values by using the setenv command. For example, it you want to increase the boot delay to 10, you can do this:

U-Boot>setenv bootdelay 10
U-Boot>printenv bootdelay
bootdelay=10
U-Boot>

Note that the changes you make to the environment variables in this way will not be persistent - the values will revert to the default values when you reboot.

Store the environment variables in flash

When UBoot runs, it first tries to load its environment variables from the non-volatile storage (eg. QSPI flash). To save your environment variable changes to the flash, you should use the saveenv command.

U-Boot>saveenv
Saving Environment to SPI Flash... SF: Detected n25q256ax1 with page size 256 Bytes, erase size 4 KiB, total 32 MiB
Erasing SPI flash...Writing to SPI flash...done
OK
U-Boot>

Now when I power-cycle my board, I should find that UBoot loads my saved environment.

Where does it go?

The saveenv command will save the environment variables to the bootenv partition of the flash. This partition is defined by the environment variables bootenvstart and bootenvsize. In your PetaLinux project configuration, this partition is defined in the project-spec/configs/config file.

Modify the hard-coded default values

If UBoot doesn’t find a valid environment in the flash, it loads the hard coded (default) values and displays this message: *** Warning - bad CRC, using default environment

In a PetaLinux project, the default values of the environment variables can be found in this file:

project-spec/configs/u-boot-xlnx/platform-auto.h

That file is automatically generated by PetaLinux using details from the hardware (Vivado) design and the settings of the PetaLinux project. We shouldn’t modify that file because our changes will just be overwritten by the tools. Instead, if we want to modify the values defined in that file, we can do so by writing the new values to this file:

project-spec/meta-user/recipes-bsp/u-boot/files/platform-top.h

You can replace any of the #defines from platform-auto.h by redefining them in the platform-top.h file.

Example: Let’s modify bootcmd

Let’s suppose we want to change the bootcmd variable. This is the variable containing the command to boot the board. The bootcmd variable is contained in the #define CONFIG_EXTRA_ENV_SETTINGS, so we first need to copy this multi-line define and paste it into the platform-top.h file.

Here is an example of my platform-top.h file:

#include <configs/platform-auto.h>

#define CONFIG_SYS_BOOTM_LEN 0xF000000

/* Extra U-Boot Env settings */
#define CONFIG_EXTRA_ENV_SETTINGS \
	SERIAL_MULTI \ 
	CONSOLE_ARG \ 
	ESERIAL0 \ 
	"autoload=no\0" \ 
	"sdbootdev=0\0" \ 
	"clobstart=0x80000000\0" \ 
	"netstart=0x80000000\0" \ 
	"dtbnetstart=0x81e00000\0" \ 
	"netstartaddr=0x81000000\0"  "bootcmd=run cp_kernel2ram && bootm ${netstartaddr}\0"  "loadaddr=0x80000000\0" \ 
	"initrd_high=0x0\0" \ 
	"bootsize=0x180000\0" \ 
	"bootstart=0x1000000\0" \ 
	"boot_img=u-boot-s.bin\0" \ 
	"install_boot=sf probe 0 && sf erase ${bootstart} ${bootsize} && " \ 
		"sf write ${clobstart} ${bootstart} ${filesize}\0" \ 
	"bootenvsize=0x40000\0" \ 
	"bootenvstart=0x1180000\0" \ 
	"eraseenv=sf probe 0 && sf erase ${bootenvstart} ${bootenvsize}\0" \ 
	"kernelsize=0xc00000\0" \ 
	"kernelstart=0x11c0000\0" \ 
	"kernel_img=image.ub\0" \ 
	"install_kernel=sf probe 0 && sf erase ${kernelstart} ${kernelsize} && " \ 
		"sf write ${clobstart} ${kernelstart} ${filesize}\0" \ 
	"cp_kernel2ram=sf probe 0 && sf read ${netstartaddr} ${kernelstart} ${kernelsize}\0" \ 
	"fpgasize=0x1000000\0" \ 
	"fpgastart=0x0\0" \ 
	"fpga_img=system.bit.bin\0" \ 
	"install_fpga=sf probe 0 && sf erase ${fpgastart} ${fpgasize} && " \ 
		"sf write ${clobstart} ${fpgastart} ${filesize}\0" \ 
	"fault=echo ${img} image size is greater than allocated place - partition ${img} is NOT UPDATED\0" \ 
	"test_crc=if imi ${clobstart}; then run test_img; else echo ${img} Bad CRC - ${img} is NOT UPDATED; fi\0" \ 
	"test_img=setenv var \"if test ${filesize} -gt ${psize}\\; then run fault\\; else run ${installcmd}\\; fi\"; run var; setenv var\0" \ 
	"default_bootcmd=bootcmd\0" \ 
""

Rebuild, repackage and boot

Once I’ve made my changes, I can rebuild U-Boot using the following commands:

petalinux-build -c u-boot -x cleanall
petalinux-build -c u-boot

When UBoot is finished building, I then need to repackage PetaLinux using the petalinux-package command. The exact command options to use will depend on your hardware and setup, but just to give you an example, here is my package command for the KCU105 board using Dual Quad SPI flash for boot:

petalinux-package --boot --force --fpga ./images/linux/system.bit --u-boot --kernel --flash-size 64 --flash-intf SPIx8

Now when I program my flash with the generated .mcs file, I should see that UBoot is using the new modified environment variables that I wrote to platform-top.h.