You’ve built U-Boot and gotten it to boot. Now what? This lecture in our free linux development course covers the U-Boot command shell itself: how it differs from a normal Linux shell, its environment-variable system, and the handful of commands you’ll use in almost every debugging session.
bootargs
saveenv
Hush shell
free linux development course
What You Will Learn
- How the default U-Boot command interpreter differs from bash, and why
- The environment variable system and how it drives the boot process
- The commands you’ll reach for constantly: help, printenv, setenv, saveenv, boot
- How to load and boot a kernel image over the network with tftpboot
Prerequisites
- A working U-Boot prompt, either on QEMU (lecture 11) or real hardware (lecture 12)
- Basic shell scripting familiarity from regular Linux use
A Deliberately Minimal Shell
The default U-Boot command interpreter looks like a shell but behaves nothing like bash. There’s no command history, no tab completion, and no left/right cursor editing in the default interpreter — pressing any of those keys mid-command usually garbles your input, and the only safe recovery key is backspace or Ctrl+C to start over. That’s not an oversight; it’s a deliberate size trade-off. Every byte of interactive polish competes with SRAM budget on the smallest targets U-Boot supports.
If you want a friendlier interactive experience, U-Boot ships an alternative interpreter called Hush, which adds proper scripting constructs (if/then, for loops) and is enabled on most modern defconfigs by default. Check with help at your prompt — if commands like if and for show up, you already have Hush.
The Environment Variable System
Almost everything U-Boot does is driven by environment variables, not command-line flags. This is the single most important mental model shift coming from a regular shell:
=> printenv
baudrate=115200
bootdelay=2
bootcmd=run distro_bootcmd
bootargs=console=ttyAMA0,115200 root=/dev/mmcblk0p2 rw
ethaddr=52:54:00:12:34:56
...
bootcmd is what runs automatically after the countdown you saw at the prompt (Hit any key to stop autoboot). bootargs is the kernel command line U-Boot will pass to Linux once it jumps to it. Change either one and you change how the board boots — no rebuild required.
| Command | What it does |
|---|---|
| printenv [var] | Print all variables, or one specific variable |
| setenv var value | Set a variable in RAM only (lost on reboot unless saved) |
| saveenv | Persist the current environment to flash/eMMC |
| run var | Execute the contents of a variable as a command sequence |
| boot | Run bootcmd immediately, skipping the remaining countdown |
| help [command] | List all commands, or show detailed usage for one |
|
v
run bootcmd (e.g. “run distro_bootcmd”)
|
v
distro_bootcmd tries boot targets in order:
mmc -> usb -> pxe -> dhcp
|
v
First successful target loads kernel + bootargs
|
v
U-Boot jumps to the kernel entry point
Try It Yourself
On the QEMU setup from lecture 11, experiment with the environment safely — nothing here can brick a virtual machine:
=> setenv bootargs console=ttyAMA0,115200 root=/dev/vda2 rw
=> printenv bootargs
bootargs=console=ttyAMA0,115200 root=/dev/vda2 rw
=> setenv mytest "echo Hello from U-Boot"
=> run mytest
Hello from U-Boot
=> printenv | grep boot
bootcmd=run distro_bootcmd
bootdelay=2
bootargs=console=ttyAMA0,115200 root=/dev/vda2 rw
Notice run mytest executed the string stored in mytest as a command — this is exactly the mechanism bootcmd relies on, just applied to a variable you made up yourself.
Loading a Kernel Over the Network
During active development, flashing a new kernel to SD/eMMC on every test cycle gets old fast. TFTP lets you load and boot straight from RAM instead:
=> setenv serverip 192.168.1.10
=> setenv ipaddr 192.168.1.50
=> tftpboot 0x42000000 zImage
Using eth0 device
TFTP from server 192.168.1.10; our IP address is 192.168.1.50
Filename 'zImage'.
Load address: 0x42000000
Loading: #################### 4.8 MiB/s
done
Bytes transferred = 6291456 (600000 hex)
=> bootz 0x42000000
No SD card rewrite, no reboot cycle for reflashing — just a fast iteration loop that most embedded teams rely on daily during kernel bring-up.
Common Mistakes
- Running setenv and expecting it to survive a reboot. It doesn’t — you need an explicit
saveenvto persist changes to flash. - Fighting the default shell’s line editing. Trying to use arrow keys or Tab in the default interpreter corrupts the command; switch to Hush if you need that.
- Editing bootargs without understanding root= — a wrong root device string is the single most common cause of a kernel panic right after U-Boot hands off control.
Best Practices
- Keep a backup of your working environment (
printenv > env-backup.txtvia console capture) before experimenting on real hardware. - Use TFTP boot during development and only flash to persistent storage once the kernel and bootargs are confirmed working.
- Script repeated environment changes as a single variable and
runit, rather than retyping commands by hand.
Summary
The U-Boot shell trades interactive comfort for a tiny footprint, and everything it does flows through environment variables rather than command-line flags. Once bootcmd, bootargs, and run click for you, U-Boot stops feeling like a mystery box and starts feeling like a small, very deliberate scripting environment — the last piece you need before moving on to device trees and kernel hand-off in this free linux development course.
Frequently Asked Questions
Why doesn’t setenv persist across reboots?
setenv only modifies the in-RAM copy of the environment. saveenv writes that copy to persistent storage (flash or eMMC), which is what survives a reboot.
What’s the difference between the default shell and Hush?
The default interpreter is minimal with no scripting constructs and fragile line editing. Hush adds if/for/while-style scripting and better line handling, and is the default on most modern board configs.
What does bootcmd actually contain?
It’s just another environment variable holding a command string, commonly something like “run distro_bootcmd” that tries several boot targets (MMC, USB, network) in sequence.
Is TFTP boot safe to leave enabled in production?
No — network boot paths are normally disabled or restricted in production builds since they represent an extra attack surface for loading unsigned code.
How do I recover if I break my environment badly?
Most boards support env_default or a build-time compiled-in default; running “env default -a” followed by “saveenv” restores factory defaults.
Can I write custom U-Boot commands?
Yes, U-Boot supports adding new C-level commands via its command table macros, useful for board-specific diagnostics beyond what’s built in.
Comfortable with the U-Boot shell now?
The next chapter moves into device trees and how U-Boot hands them off to the Linux kernel.
