A little convention make things easy to use, but only if you know the convention.
In this blog post, we’ll explain three key environment variable conventions used on the Nitrogen6X (and Sabre Lite before it).
For the impatient:
- Both of these devices ship with U-Boot configured to load and run a boot script named 6q_upgrade from the root of either SD card.
- A pre-configured command upgradeu can be used to upgrade U-Boot in serial flash
- Another pre-configured command clearenv will erase the environment variables in serial flash.
For those that want an explanation:
Both of these boards boot to SPI EEPROM (serial flash), and both U-Boot and its’ environment variables are stored there.
Convention for many modern ARM boards, including i.MX in recent years has been to boot directly to SD card, either by using unformatted areas of the disk or carefully placing special files in prescribed areas of a filesystem.
We jokingly refer to this as a game of “Hide the U-Boot”, and don’t want to play.
Instead, we added a serial flash chip and store the boot loader there. The flash device size is 2MB, which is plenty of space for the boot loader and environment, but not enough for a complete O/S, and we defer that to SD card, which brings us to the first environment variable:
bootcmd
We’ve pre-configured the bootcmd environment variable to load and launch a boot script from the root directory of either SD card slot by using the looping features of the Hush parser:
for disk in 0 1 ; do
mmc dev ${disk} ;
for fs in fat ext2 ; do
${fs}load mmc ${disk}:1 10008000 /6q_bootscript &&
source 10008000 ;
done ;
done
If you’re not familiar with U-Boot boot scripts, we wrote a backgrounder in this blog post.
To make compiling boot scripts a bit easier, we created a web-based tool to compile them for you. There’s also a bash snippet to make this easy from the command-line.
upgradeu
Once you’re running U-Boot from serial EEPROM, the question is how do you upgrade it?
The easy answer is that you ask U-Boot to do it:
MX6Q SABRELITE U-Boot > run upgradeu ...bunch of debug spew ## Executing script at 10008000 check U-Boot Loading file "u-boot.bin" from mmc device 1:1 (xxb1) 179632 bytes read ...more debug spew... SUCCESS Total of 179632 bytes were the same ------- U-Boot versions match
If it’s not entirely obvious to you what this example illustrated, let’s dig a little deeper.
If you run print at the U-Boot prompt, you’ll see that upgradeu is defined to contain some U-Boot commands:
MX6Q SABRELITE U-Boot > print
bootdelay=3
...
upgradeu=for disk in 0 1 ; do mmc dev ${disk} ;for fs in fat ext2 ; do ${fs}load mmc ${disk}:1 10008000 /6q_upgrade && source 10008000 ; done ; ...
Environment size: 908/8188 bytes
That’s a pretty long command-line, but is essentially the same loop as we described in the bootcmd section of this post. It will try to read a script named 6q_upgrade from either SD card using either the FAT or ext2/3/4 filesystem and source it. The source command simply executes a boot script.
If you’ve followed along this far, you may have just realized that we haven’t really followed either the full boot path or the upgrade path because we’ve waved our hands over what the script does. We’ll address that in a later section.
clearenv
The final convention may be the most useful for the programmer. If a Sabre Lite or Nitrogen6X board spends much time on your desk, you’ll invariably set something in the U-Boot environment. Perhaps you’re bypassing all of these conventions and booting over NFS (the topic of another blog post). Maybe you’re booting to a SATA drive or a USB stick or tweaking some kernel parameters.
If so, you may ultimately want to put things back the way you found them before you hand the device off to someone else. If you know the layout of the serial EEPROM, you can simply erase the sector(s) containing the environment variables and go back to the compiled-in defaults.
I’m assuming you don’t (know or remember the flash layout) because I don’t and I helped come up with it. This is where the clearenv variable or command comes into play. Going back to the print example will tell you:
MX6Q SABRELITE U-Boot > print bootdelay=3 ... clearenv=sf probe 1 && sf erase 0xc0000 0x2000 && echo restored environment to factory default ... Environment size: 908/8188 bytes
Aha! The environment is stored at offset 0xc0000 (768k) and is 0x2000 (8192) bytes long!
And if you want to clear it, you can simply run run clearenv from the shell.
The gory details
Unless you’re as much of a U-Boot geek as I am, you’re probably wishing you’d stopped at the for the impatient section, but you may someday need the details, so I’m going to describe a handful of additional things that will help in the use and deployment of boot scripts for your use.
To begin with, I promised the details of the boot script itself, but I lied.
I can’t really give that to you because the things needed really depend on your environment. If you’ve spent time with various embedded Linux distributions, you’ll find that many of them require different sorts of kernel command-line parameters. Also, some distributions, like Android require the use of a RAM disk, but others don’t. These differences (additions) are best expressed in the actual userspace image, so we add them there.
Here’s an example of what we shipped on SabreLite boards with Ubuntu:
Ubuntu boot script
set bootargs $bootargs root=/dev/mmcblk0p1 rootwait fixrtc ;
ext2load mmc ${disk}:1 10800000 /boot/uImage && bootm 10800000 ;
echo "Error loading kernel image"
If you look closely, you’ll see that it’s adding a couple of things to the bootargs environment variable, then loading the kernel from /boot/uImage and booting it using bootm and that it’s printing an error message in case something fails. The bootm command will normally not return.
Also note that this script uses the disk variable that was defined in the loop from which it was called, so the boot script must reside on the same disk as the kernel.
Android boot script
Here’s another example of loading Android ICS on an i.MX6:
${fs}load mmc ${disk}:1 10800000 uimage &&
${fs}load mmc ${disk}:1 12800000 uramdisk.img &&
bootm 10800000 12800000 ;
echo "Error loading kernel image"
This one doesn’t add anything to the bootargs, and uses the && operators liberally, but the primary thing it does differently from the Ubuntu script is load a ram disk (uramdisk.img). It also uses the fs parameter from the calling loop in the same way the Ubuntu script used the disk variable.
A lot more could be done inside this boot script. The Android boot script could (and probably should) handle recovery mode. Both of them should probably loop in the case of errors, although we don’t yet have display support in i.MX6 U-Boot.
We should also add support for SATA into our default environment, but those are topics for another day and perhaps another blog post.
Upgrade script
The content of the 6q_upgrade script is a little more complex, but shouldn’t be too surprising. It tries to load a file named u-boot.bin from the same partition in which the script was found and compares it against the content of the serial EEPROM. If the two are different, it will wait for 10 seconds before (re)programming the flash to match the content of the SD card. It will also perform a verification pass before telling you that things are good:
echo "check U-Boot" ;
if ${fs}load mmc ${disk}:1 12000000 u-boot.bin ; then
echo "read $filesize bytes from SD card" ;
if sf probe 1 27000000 ; then
echo "probed SPI ROM" ;
if sf read 0x12400000 0 $filesize ; then
if cmp.b 0x12000000 0x12400000 $filesize ; then
echo "------- U-Boot versions match" ;
else
echo "Need U-Boot upgrade" ;
echo "Program in 10 seconds" ;
for n in 9 8 7 6 5 4 3 2 1 0 ; do
echo $n ;
sleep 1 ;
done
echo "erasing" ;
sf erase 0 0x40000 ;
# two steps to prevent bricking
echo "programming" ;
sf write 0x12000000 0 $filesize ;
echo "verifying" ;
if sf read 0x12400000 0 $filesize ; then
if cmp.b 0x12000000 0x12400000 $filesize ; then
while echo "---- U-Boot upgraded. reset" ; do
sleep 120
done
else
echo "Read verification error" ;
fi
else
echo "Error re-reading EEPROM" ;
fi
fi
else
echo "Error reading boot loader from EEPROM" ;
fi
else
echo "Error initializing EEPROM" ;
fi ;
else
echo "No U-Boot image found on SD card" ;
fi
Recap and best practices
I should probably move this section to the top of the document, but here’s some additional rationale for how and why we use the boot script scheme.
Two-file U-Boot upgrades
To build an SD card to upgrade U-Boot, you can copy just two files (6q_upgrade and u-boot.bin)to a fresh, out-of-the-box SD card (which tend to come formatted as FAT32), insert the card and run run upgradeu.
Single partition Ubuntu
To run Ubuntu, LTIB or Timesys single-partition images, you can create an SD card by simply formatting an ext2/3/4 partition and copy your filesystem image, boot script and kernel using cp. There’s no need to use dd or any other special formatting commands.
Windows CE, too
I guess I should say Windows Embedded 7. As the name implies, U-Boot is the Universal Boot Loader, and it can load and launch Windows Embedded using a boot script in a very similar manner as Linux. In this environment, you shouldn’t need to format the SD card at all: FAT32 should work.
And NFS
It may not be clear, but the use of a boot script on SD can also be useful in booting over NFS. This may sound like an oxymoron, but it’s often more convenient to write a network load script and copy it to an SD card than it is to hook up a serial port and tweak settings there.
This is especially true for those that infrequently use a serial port or when devices are enclosed in a manner that makes access to the serial port difficult.
Best practices
If U-Boot and environment variables are all stored on SD card, you don’t have much choice about what goes where, and environment variables are often used and changed to reflect the combination of distribution-related variables like whether or not a RAM disk is loaded and machine-specific variables like ethernet addresses or display settings.
When the two are separated as they are in SabreLite and Nitrogen6 boards, you should try to use environment variables for only those things which are tied to the board, such as the type of display. Any distribution-related bootargs should be added by the boot script.
If we haven’t yet convinced you that this is a good idea, I hope that at least you’ll understand what you’re seeing when you receive a board on your desk. At that point, I’ll refer you to the Linaro way, which allows you to override all of this and play “Hide the U-Boot” using a small shim in SPI ROM.
For what it’s worth, you can load and program that using the upgradeu command and the 6q_upgrade script by naming the shim u-boot.bin.

I have the Nitrogen6w board booting from SD, but is does not boot from a connected sata drive with the boot switches at “fuses” and does not boot from USB when the switches is set for USB. What do I need to change for U-boot to be able to boot from USB or sata ( preferrably sata ). If I abort the sd boot, and issue the “sata” command via console, it does not see the device.
Hi,
“We jokingly refer to this as a game of “Hide the U-Boot”, and don’t want to play. ”
Sorry, I simply don’t get the point!
I almost overlooked that you, contrary to the FS i.MX53 reference designs, have added this serial flash device. Using the SabreLITE design as reference for a real product adds unnecessary cost – however little – to the product, and makes SW management more complicated. The “unbricking” guide wouldn’t be needed if U-boot is on the SD card – you then “unbrick” simply by rewriting the SD card in another computer.
I assume that getting rid of the serial flash is just a matter of adjusting the eFUSEs BOOT_CFG[7:4] settings, right? Is there some important point about this that I misssed?
Best regards
Henrik
Hi Henrik,
Thanks for the feedback.
You’re absolutely right about a few things:
I think that whether booting to serial EEPROM makes SW management more difficult depends on what tools you expect users to have. Do you need them to have ready access to ‘dd’ and instructions for use? USB Image Creator? Freescale’s Manufacturing Tool? Fastboot? FWIW, we’ve tried to reduce that number.
We’re also aiming for a place we’re at on our i.MX5x boards, where you get a display immediately on boot so you can prompt a user to “Insert an SD card”. We’re not quite there yet, but Fabio’s recent patches to main-line U-Boot get us close.
Hi Eric,
Thanks for your prompt reply! I think you can say that it comes down to whether your end product is the development board – as in your case, or it’s some other kind of appliance. For instance, in our product we don’t see much idea in an “Please insert SD card” message, as the SD card is physically inaccessible to the end user. But yes, I do see that there are some advantages in having the 1st level boot code stored separately.
Best regards
Henrik
Context is everything.
Hi,
trying to update the flash in my sabrelite, using https://wiki.linaro.org/Boards/MX6QSabreLite, in particular, here is the output captured from serial:
U-Boot 2009.08-00381-gc41f1c6-dirty (Nov 03 2011 – 07:05:05)
CPU: Freescale i.MX 6 family 0.0V at 792 MHz
mx6q pll1: 792MHz
mx6q pll2: 528MHz
mx6q pll3: 480MHz
mx6q pll8: 50MHz
ipg clock : 66000000Hz
ipg per clock : 66000000Hz
uart clock : 80000000Hz
cspi clock : 60000000Hz
ahb clock : 132000000Hz
axi clock : 264000000Hz
emi_slow clock: 29333333Hz
ddr clock : 528000000Hz
usdhc1 clock : 200000000Hz
usdhc2 clock : 200000000Hz
usdhc3 clock : 200000000Hz
usdhc4 clock : 200000000Hz
nfc clock : 24000000Hz
Board: MX6Q-SABRELITE:[ POR]
Boot Device: I2C
I2C: ready
DRAM: 1 GB
MMC: FSL_USDHC: 0,FSL_USDHC: 1
JEDEC ID: 0xbf:0×25:0×41
Reading SPI NOR flash 0xc0000 [0x2000 bytes] -> ram 0x276009b8
SUCCESS
*** Warning – bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: got MAC address from IIM: 00:19:b8:00:e5:40
FEC0 [PRIME]
Hit any key to stop autoboot: 0
mmc0 is current device
** Unable to use mmc 0:1 for fatload **
** Bad partition 1 **
Card did not respond to voltage select!
mmc1(part 0) is current device
MMC: block number 0×1 exceeds max(0×0)
** Can’t read from device 1 **
** Unable to use mmc 1:1 for fatload **
** Bad partition 1 **
MX6Q SABRELITE U-Boot > mmc dev 0
mmc0 is current device
MX6Q SABRELITE U-Boot > mmc read 0×10800000 0 200
MMC read: dev # 0, block # 0, count 512 … 512 blocks read: OK
MX6Q SABRELITE U-Boot > sf probe 1
JEDEC ID: 0×00:0×00:0×00
Failed to initialize SPI flash at 0:1
I’m having trouble updating flash, could you please recommend some way to get past this?
Hi Konstantinos,
It appears that your SD card is formatted without a partition table, so the fatload statement is failing.
Can you run fatls mmc 0 and see the files? How about ext2ls mmc 0?
Actually, and rather surprisingly I managed to flash u-boot, by booting from Linaro’s u-boot (following the instructions in this blog post in this site:
http://boundarydevices.com/running-linaro-ubuntu-on-i-mx6-sabre-lite-and-nitrogen6x/
Afterwards, rerunning sf probe 1 and the rest of instructions seemed to work fine.
Thanks again
Konstantinos,
I’m not sure if you’re still following up to this thread but I’m seeing the same problem on my board. It doesn’t seem to be a badly formatted SD card, but the “sf probe” command might be the cause.
I’ve tried asking for help via email, via imxcommunity.org, and now here. Hopefully someone is listening….
Hi Konstantinos, any luck with this error? I am getting it as well. Thanks
Hi Louis (and Vars),
This seemed to have slipped past me. Can you post the output from the version and print commands under U-Boot?
The sf probe command changed when we made the move from Freescale’s 2009.08 code base to the main-line 2013-01.
In U-Boot 2009.08, the command usage was sf probe 1, and under 2013.01, this is just sf probe.
This and other subtle command incompatibilities are the reason we switched to 6x_upgrade from 6q_upgrade in previous releases.
Please advise,
Eric
Sorry we didn’t see your post on i.MX Community previously, but I just added a reply and a link to this blog post that gives the details.
Eric,
Thanks for the clarification. I guess the root cause of my issue was that my environment was still trying to use the 6q_ script and not the 6x_. It’s kind of a tricky situation to be sure you’ve caught, perhaps adding a line in the FAQ about the JEDEC coming up as 0:0:0:0:0 could help future users. But thanks for the response, I’ve gotten the EEPROM back online and the board is running…
Hello there!
I would like to get the default image of the microSD card that comes with the development board sabre-lite.
Is there some place I can download it from?
Also, how can I mount this microSD card in a linux host?
Thank you in advance for your help!