For the factory recovery in the Ninja Sphere we needed a way to force a particular boot mode (NAND boot instead of SD card boot) for just exactly one boot. A useful and underused feature of U-Boot is the ability to adjust environment variables from within a Linux system to alter the details of the boot process, including overriding the default boot command to trigger a one-shot boot to an alternate medium.

A package is readily available in both Yocto/OE (u-boot-fw-utils) and Ubuntu (u-boot-tools) that package up the fw_printenv and fw_setenv utilities to read and update environment variables respectively.

Configuration

The documentation is a little sparse on how to configure this, but if you have NAND and MTD partitions set up, then /proc/mtd should look something like:

dev:    size   erasesize  name
mtd0: 00020000 00020000 "NAND.SPL"
mtd1: 00020000 00020000 "NAND.SPL.backup1"
mtd2: 00020000 00020000 "NAND.SPL.backup2"
mtd3: 00020000 00020000 "NAND.SPL.backup3"
mtd4: 00040000 00020000 "NAND.u-boot-spl-os"
mtd5: 00100000 00020000 "NAND.u-boot"
mtd6: 00020000 00020000 "NAND.u-boot-env"
mtd7: 00020000 00020000 "NAND.u-boot-env.backup1"
mtd8: 00800000 00020000 "NAND.kernel"
mtd9: 07600000 00020000 "NAND.file-system"

Specifically, you want to take note of the mtd number for “NAND.u-boot-env” and “NAND.u-boot-env.backup1”. Construct a file /etc/fw_env.config that looks like:

# device   offset  env size  flash sector size
/dev/mtd6  0x0000  0x20000   0x20000
/dev/mtd7  0x0000  0x20000   0x20000

Where a line exists for each of the 2 environment partitions described above, containing the path to the mtd device and the appropriate sizes/offsets. Looking at the output from /proc/mtd above, both environments in this case have a size and sector (erase) size of 0x20000. The offset is 0x0000 in both cases because the /dev/mtdX device is already pointing to the right part of the flash chip - the offset would only be required if you were addressing into a single mtd device covering the whole flash chip (or if the values given to the kernel were incorrect).

One-Shot Boot

By design, we were not using the environment for booting, and instead relying on the default environment baked into the U-Boot flash, which would be loaded by U-Boot whenever an empty environment was discovered at boot. Unfortunately, the tools in Linux don’t know about this environment, so the following little snippet was used to set a one-shot boot that would work in this situation, even if this became the entire environment:

fw_setenv bootcmd "env default -a; saveenv; run nandboot"

This works by setting the boot command (which U-Boot first runs after setting up drivers) to a sequence of resetting to the default (built-in) environment, saving that back to flash (overwriting this one-shot), then booting to NAND. This works for situations where the environment isn’t used, but will cause any customisations to the environment to be destroyed and restored back to the U-Boot built-in environment.

In a situation where the environment was customised and used as part of the boot, one could read the existing bootcmd, save it to a backup variable, then override bootcmd to restore the original then boot a custom way, similar to above.