free linux development course
free linux device drivers course
free linux kernel development course
u-boot environment variables
If you’re taking this free embedded Linux course in sequence, you already know that U-Boot is the
program running on your board before Linux even exists in memory. What you haven’t seen yet is how U-Boot remembers
things between reboots — the load address of your kernel, the IP address of your board, whether autoboot should even
run. All of that lives in a tiny, purpose-built key-value store called the U-Boot environment, and
understanding it is one of the most practical skills you can pick up in a free linux device drivers
course that touches bring-up work.
What You Will Learn
- What the U-Boot environment actually is
- Reading, writing, and deleting variables at the shell
- Where the environment is stored on real hardware
- Writing your first environment-driven boot script
- Common mistakes that leave boards unbootable
Prerequisites
You should already be comfortable with the U-Boot shell itself — connecting over a serial console, interrupting
autoboot, and running a basic command. If that’s new to you, go back to the earlier lecture in this
free embedded systems course that covers the U-Boot shell before continuing here.
What Is the U-Boot Environment?
The U-Boot environment is a flat set of name=value pairs, held in RAM while U-Boot is running and
(usually) mirrored to persistent storage so it survives a power cycle. It is not a filesystem, not a database, and
not related in any way to Linux’s own environment variables — it’s U-Boot’s private configuration store, and Linux
never sees it directly unless U-Boot deliberately passes values through, such as the kernel command line.
Variables serve three overlapping purposes:
- Configuration — e.g.
ipaddr,serverip,ethaddr - State — e.g. a counter tracking failed boots for a fallback strategy
- Scripting — variables can hold whole command sequences that other variables invoke
That third point surprises people the first time they see it: a U-Boot variable can itself be a small program.
Reading and Writing Variables
Four commands cover almost everything you’ll do day to day:
printenv # list every variable
printenv myvar # print one variable
setenv myvar value # create or overwrite a variable
setenv myvar # delete a variable (no value given)
Note the syntax carefully — there is no = sign in setenv. Typing
setenv myvar=value creates a variable literally named myvar=value, which is almost never
what you want, and is one of the most common beginner mistakes in this area.
Persisting the Environment
Everything above only touches the copy of the environment sitting in RAM. Reboot the board right now and every
change is gone. To make changes stick, U-Boot writes the environment out to whatever persistent storage your board
config defines — this could be a reserved region of raw NAND/NOR flash, a file inside an eMMC/SD partition, a serial
EEPROM, or battery-backed SRAM depending on the platform.
setenv bootdelay 1
saveenv
Two details matter here. First, saveenv writes the entire environment as one block, not just
the variable you changed — so a corrupted write can, in theory, wipe everything. That’s why many boards reserve two
copies of the environment region and alternate between them, so a power loss mid-write never leaves the board with
zero valid copies. Second, if saveenv is never run, U-Boot silently falls back to a built-in default
environment compiled into the binary on the next boot — which is a safety net, but also a source of confusing “why
did my setting disappear” bugs.
Default Values at Build Time
A board’s initial variables usually come from its board configuration header, using a macro similar to this:
#define CFG_EXTRA_ENV_SETTINGS \
"ep_kernel_addr=0x82000000\0" \
"ep_fdt_addr=0x83000000\0" \
"ep_bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw\0"
These become the environment that ships on a fresh build before anyone has ever run saveenv. Note
the \0 terminator on each line — this is a C string array being packed into a single environment blob at
compile time, not a runtime construct.
Writing a Real Boot Script
Here’s an original example: an SD-card boot flow that a fresh board could actually use, built entirely from
environment variables and demonstrating why the environment doubles as a scripting language.
U-Boot# setenv ep_load_kernel 'fatload mmc 0:1 ${ep_kernel_addr} Image'
U-Boot# setenv ep_load_fdt 'fatload mmc 0:1 ${ep_fdt_addr} board.dtb'
U-Boot# setenv ep_bootcmd 'run ep_load_kernel; run ep_load_fdt; booti ${ep_kernel_addr} - ${ep_fdt_addr}'
U-Boot# setenv bootcmd 'run ep_bootcmd'
U-Boot# saveenv
Two U-Boot features are doing the heavy lifting here. ${varname} expands the value of another
variable inline, so ep_load_kernel resolves the addresses at run time rather than hard-coding them
twice. And run executes the contents of a variable as if you had typed it at the shell — which is
exactly how bootcmd, the special variable U-Boot executes automatically after the boot delay, is meant
to be used.
|
v
U-Boot starts, checks bootdelay
|
v
No key pressed within bootdelay?
| yes | no
v v
run $bootcmd drop to interactive shell
|
v
run ep_bootcmd
|
v
run ep_load_kernel –> fatload mmc 0:1 …
|
v
run ep_load_fdt –> fatload mmc 0:1 …
|
v
booti $ep_kernel_addr – $ep_fdt_addr
|
v
Linux kernel starts
Common Mistakes and Troubleshooting
| Mistake | Symptom | Fix |
|---|---|---|
Using = in setenv |
Odd variable names in printenv output | Use setenv name value, space-separated |
| Forgetting saveenv | Changes vanish after reboot | Always run saveenv after a change you want to keep |
| Editing bootcmd without testing | Board fails to boot, no interactive fallback | Test with run bootcmd manually before rebooting; keep bootdelay non-zero while iterating |
| Same variable name for state and script | Script logic silently breaks | Use a clear naming prefix like ep_ for your own variables |
Best Practices
- Keep
bootdelaynon-zero (2-3 seconds is common) during development so a bad script never locks you out. - Namespace your custom variables with a project prefix to avoid clashing with vendor-defined ones.
- Treat
bootcmdas an entry point that calls smaller, named scripts — easier to debug than one giant one-liner. - Verify a saved environment with
printenvimmediately aftersaveenvrather than assuming it worked.
Security Considerations
On production devices, an unlocked U-Boot shell with environment write access is effectively root access before
Linux’s own security model even loads — anyone with serial or JTAG access can rewrite bootargs to boot
into a shell as root, bypassing whatever the OS enforces. Locking the console, disabling environment saves in the
field, or requiring a signed environment is standard practice on shipping products.
Summary and Key Takeaways
- The U-Boot environment is a persistent name=value store, separate from Linux environment variables.
printenv,setenv, andsaveenvare the core commands to know.- Storage location (NAND, eMMC, EEPROM, NVRAM) is board-specific and configured at build time.
- Variables can hold command sequences, and
runexecutes them — this is howbootcmddrives automatic booting. - A locked-down environment matters for production security, not just convenience.
Conclusion
The environment is the quiet backbone of every U-Boot-based board: it’s what turns a bootloader from a one-shot
program into something configurable, scriptable, and field-updatable. Once you’re comfortable reading, writing, and
scripting variables, the next lecture in this free linux kernel development course builds directly
on this — you’ll use these same variables to prepare a proper boot image with mkimage.
FAQ
What happens if I never run saveenv?
Your changes exist only in RAM for the current session. On the next power cycle, U-Boot loads whatever was last
saved, or its compiled-in defaults if nothing was ever saved.
Can I have U-Boot variables reference each other?
Yes — using ${varname} expansion, one variable’s value can embed another’s, which is exactly how
scripted boot flows are built.
Is the U-Boot environment the same as Linux environment variables?
No. They’re conceptually similar but completely separate systems. U-Boot may pass some values to Linux (like
bootargs becoming the kernel command line), but the storage and mechanism are unrelated.
What if saveenv fails midway through a write?
This is exactly why many boards keep two redundant copies of the environment region — if one write is
interrupted by a power loss, the other copy is still valid and U-Boot falls back to it.
Can I delete a single variable without affecting others?
Yes, run setenv name with no value — that removes just that one variable, leaving everything else
untouched in RAM until you saveenv.
Continue the Free Embedded Linux Bootloader Course
