Das U-Boot
Das U-Boot is a GPL'ed cross-platform boot loader shepherded
by
project leader Wolfgang Denk and backed by an active developer and
user community. U-Boot provides out-of-the-box support for hundreds of
embedded boards and a wide variety of CPUs including PowerPC, ARM,
XScale, MIPS, Coldfire, NIOS, Microblaze and x86. You can easily
configure U-Boot to strike the right balance between a rich feature
set and a small binary footprint.
U-Boot has its origins in the 8xxROM project, a boot loader
for 8xx
PowerPC systems by Magnus Damm. When bringing that project to
Sourceforge in 2000 the current project leader, Wolfgang Denk, renamed
the project PPCBoot since Sourceforge did not allow project names to
begin with a digit.
The openness and utility of PPCBoot fanned the flames of its
popularity, driving developers to port PPCBoot to new
architectures. By September 2002 PPCBoot supported four different ARM
processors and the name PPCBoot was becoming quaint. In November 2002
the PPCBoot team retired the project, which led directly to the
surfacing of "Das U-Boot".
The strength of the Free Software development process is
clearly
evident in the success of U-Boot. The four freedoms expressed by the
FSF's Free
Software Definition directly fuel U-Boot's impressive
progress and wide spread deployment.
You can jump start your next embedded Linux project using
U-Boot to
take care of the low-level board initializations, allowing you to
focus on the core of your embedded application. Downloading images and
flashing kernels should be the least of your worries. If the need
arises, however, you have the source code can can add support for new
hardware or add a special feature.
Prerequisites
Before building and installing U-Boot you need a
cross-development
tool chain for your target architecture. The term tool chain is a bit
squishy, but generally means a C/C++ compiler, an assembler, a
linker/loader, associated binary utilities and header files for a
specific architecture, like PowerPC or ARM. Collectively these
programs are called a tool chain.
You are probably familiar with the tool chain on your desktop
Linux
PC. That tool chain runs on an x86 platform and generates binaries
for an x86 platform. A cross-development tool chain executes on one
CPU architecture, but generates binaries for a different architecture.
In my case the host architecture is x86 while the target architecture
is ARM and PowerPC. Sometimes this process is also referred to as
cross-compiling.
While it is possible to configure and build a tool chain from
source
it is time consuming and the myriad of configuration options makes it
quite error prone. I highly recommend using a pre-built tool chain
from a Linux vendor. The Embedded Linux Development Kit (ELDK), also
by Wolfgang Denk, contains the tool chains used in this article.
Using cross-development tools makes it an absolute dream to
develop
embedded systems using Linux as the host development workstation. No
need to maintain machines running other operating systems in order to
run the compiler.
Configuring and Building
Building U-Boot for a supported platform is very straight
forward,
very similar to the familiar "untar, configure, make" method used by
many software projects. To setup a default configuration for a
particular board type this at the shell prompt after untarring the
U-Boot tarball,
localhost:$ cd u-boot
localhost:$ make mrproper
localhost:$ make <board>_config
where <board> matches the board you want to build. This
step setups
the CPU architecture and board type.
You can fine tune the default configuration for your
particular
environment and board by editing the configuration file,
"include/configs/<board>.h". This file contains several
C-preprocessor #define macros that you can modify for your needs.
Some common options you might want to change are:
/* Serial port configuration */
#define CONFIG_BAUDRATE 19200
#define CFG_BAUDRATE_TABLE { 9600, 19200 }
/* Network configuration */
#define CONFIG_IPADDR 10.0.0.11
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_SERVERIP 10.0.0.1
These definitions are self explanatory, except for maybe
CONFIG_SERVERIP, which is the IP address U-Boot uses for TFTP and
NFS. Don't worry too much about these right now as you can change
them later when U-Boot is running. For the complete list of
configuration options see u-boot/README.
Now to build the binary image, u-boot.bin, type the following:
cd u-boot
make
You have several options for installing the binary image on
your
target board, and which option you select depends on your board and
development environment. You might use a BDM/JTAG programmer, an
existing vendor's boot loader or even an older version of
U-Boot. While this step is crucial it is beyond the scope of this
article to describe this procedure. From here on I will assume you
have successfully installed U-Boot on your target board.
Getting Started
The next step is configuring your host workstation for serial
communications with your target platform. On my system I have a DB9
serial cable connected to /dev/ttyS0. U-Boot expects the serial line
to be set for 8 data bits, 1 stop bit, no parity and no handshake.
You can use your favorite serial communications program to
connect to
U-Boot. I prefer to use Kermit and a tiny Kermit script from within
an emacs shell buffer. I put the following Kermit script into a file
called "serial-term" and make the file executable:
#!/usr/bin/kermit
echo connecting /dev/ttyS0 .....
set line /dev/ttyS0
set FLOW AUTO
set speed 19200
set serial 8n1
SET CARRIER-WATCH OFF
connect
I like running serial-term from within an emacs shell because
emacs
keeps track of my command history, which the U-Boot shell does not
support. Trust me, while developing you will be hitting the reset
button on your board a lot and want to "up arrow" to the previous load
command you just entered.
The user interface to U-Boot consists of a command line
interrupter,
much like a Linux shell prompt. When connected via a serial line you
can interactively enter commands and see the results. After power on
the initial u-boot prompt looks like this:
U-Boot 1.1.2 (Aug 3 2004 - 17:31:20)
RAM Configuration:
Bank #0: 00000000 8 MB
Flash: 2 MB
In: serial
Out: serial
Err: serial
u-boot #
This tells you that you have 8 megabytes of RAM starting at
address
0x00000000, two megabytes of Flash and that the C-library file streams
stdin, stdout and stderr are connected to the serial line.
You can receive more information about our board be issuing
the board
info command, bdinfo. In the folowing the commands
typed by me appear in bold.
u-boot # bdinfo
DRAM bank = 0x00000000
-> start = 0x00000000
-> size = 0x00800000
ethaddr = 00:40:95:36:35:33
ip_addr = 10.0.0.11
baudrate = 19200 bps
Here you see the see the RAM configuration information again,
the
Ethernet MAC address, the IP address and serial port baud rate.
Environment Variables
Much like a traditional Linux shell the U-Boot shell uses
environment
variables to tailor its operation. The U-Boot commands to manipulate
environment variables have the same names as the BASH shell. For
instance printenv and setenv
behave the same as their BASH shell
counterparts.
In the following example you will dump the current environment
variables using the "printenv" command and change the IP address of
the TFTP server using the "setenv" command.
u-boot # printenv
baudrate=19200
ethaddr=00:40:95:36:35:33
netmask=255.255.255.0
ipaddr=10.0.0.11
serverip=10.0.0.1
stdin=serial
stdout=serial
stderr=serial
u-boot # setenv serverip 10.0.0.2
u-boot # printenv serverip
serverip=10.0.0.2
You can create short shell scripts by storing a sequence of
U-Boot
commands, separated by semicolons, in an environment variable. To
execute the script use the "run" command followed by the variable
name. This can be handy to automate repetitive tasks during
development.
Network Commands
Having a network connection on your boot loader is very
convenient
during development. If your project requires several networked boards
they can all download and boot the same kernel image from a
centralized server. When you update the kernel you only need to
update the single copy on the server and not each board individually.
U-Boot supports TFTP (Trivial FTP), a stripped down FTP that
does not
require user authentication, for downloading images into the board's
RAM. The "tftp" command needs two pieces of information, the name of
the file to download and where in memory to store the file as shown in
the following example:
u-boot # tftp 8000 u-boot.bin
>From server 10.0.0.1; our IP address is 10.0.0.11
Filename 'u-boot.bin'.
Load address: 0x8000
Loading: ###################
done
Bytes transferred = 95032 (17338 hex)
The size and location of the downloaded image are stored in
the
fileaddr and filesize
environment variables for possible latter
use by other shell commands and scripts.
U-Boot also supports NFS so you can download images from your
existing
NFS server as well.
Flash Commands
Some embedded projects only have access to a network while
being
programmed "in the factory". When deployed in the field the boards
boot a kernel stored in the flash memory. The board can be updated in
the field by reprogramming the flash memory with a new kernel. U-Boot
offers several commands for programming, erasing and protecting the
flash memory.
To see what type of flash memory your board has enter the flinfo
command:
u-boot # flinfo
Bank # 1: AMD Am29LV160DB 16KB,2x8KB,32KB,31x64KB
Size: 2048 KB in 35 Sectors
Sector Start Addresses:
S00 @ 0x01000000 ! S01 @ 0x01004000 !
S02 @ 0x01006000 ! S03 @ 0x01008000 !
S04 @ 0x01010000 ! S05 @ 0x01020000 !
S06 @ 0x01030000 S07 @ 0x01040000
...
S32 @ 0x011D0000 S33 @ 0x011E0000
S34 @ 0x011F0000
The output carries quite a lot of information. Immediately you
see
the flash manufacturer, part number and sector layout. This
particular part begins with a 16KB sector at address 0x01000000,
followed by two 8KB sectors, a 32KB sector and 31 64KB sectors for a
total of 2 megabytes in 35 sectors.
The exclamation points following sectors 0 through 5 indicate
that
those sectors are "protected". In this example sectors 0 through 4
contain the code for U-Boot itself, and sector 5 is used to store the
environment variables. Any attempt to program these sectors without
first unlocking them will fail. This offers some level of protection
from "rm -rf /" type mistakes when programming the flash.
Continuing the TFTP example, let's assume the file you
uploaded is a
new version of U-Boot. You need to first unlock flash sectors 0
through 4 before programming the flash. Type "protect off 1:0-4",
which instructs U-Boot to allow write access to flash bank 1, sectors
0 through 4.
u-boot # protect off 1:0-4
Un-Protect Flash Sectors 0-4 in Bank # 1
Next you must prepare the flash sectors for programming by
erasing
them. Enter "erase 1:0-4", which tells U-Boot to erase sectors 0
through 4 of flash bank 1.
u-boot # erase off 1:0-4
Erase Flash Sectors 0-4 in Bank # 1
Erasing Sector 0 @ 0x01000000 ... done
Erasing Sector 1 @ 0x01004000 ... done
Erasing Sector 2 @ 0x01006000 ... done
Erasing Sector 3 @ 0x01008000 ... done
Erasing Sector 4 @ 0x01010000 ... done
[end courier]
To program the flash memory you need to copy the image from
RAM to the
address of flash sector 0, 0x01000000, using the cp
command. You
will use the byte version of the command to copy the specified number
of bytes. In this case you can use the fileaddr
and filesize
environment variables, which contains the RAM address and number of
bytes loaded by the last TFTP command. Type cp.b ${fileaddr}
1000000
${filesize} at the u-boot prompt.
u-boot # cp.b ${fileaddr} 1000000 ${filesize}
Copy to Flash... ................ done
Finally restore the write protection on flash sectors 0
through 4 by
typing protect on 1:0-4 at the U-Boot prompt.
u-boot # protect on 1:0-4
Protect Flash Sectors 0-5 in Bank # 1
You have just updated the U-Boot code for you board. The next
reboot
will run the newly uploaded U-Boot code. Well done!
The final flash related command is the saveenv
command, which like
the name implies saves your current environment variables to a
reserved flash sector. This allows your environment variables to
persist across power cycles and reboots. You might want do this after
updating the server IP address or when adding a new script. Type
saveenv to save your environment.
u-boot # saveenv
Saving Environment to Flash...
Un-Protected 1 sectors
Erasing Flash...
Erasing Sector 5 @ 0x01020000 ... done
Erased 1 sectors
Writing to Flash... ................ done
Protected 1 sectors
As you can see the saveenv command
bundles together the un-protect,
erase, copy and protect steps you covered in the previous example.
Booting
OK, now you know how to load your image into RAM or flash.
Next you
want to run your kernel and boot into Linux. Most kernel images
expect to start executing from a known location in memory, so you need
to load your image into the correct place. The U-Boot distribution
provides a nice utility for the host system called "mkimage" for just
this purpose.
After creating your kernel image use mkimage
to add a tiny header
containing the load and execute address for the image, like this (all
on one line):
localhost:$ mkimage -A arm -O linux -T kernel -C none -a
0x8000 -e 0x8000 -n "Linux 2.6.6" -d linux.bin uImage.bin
This command appends a small header containing the load and
execute
address 0x8000 to your kernel image and creates a new file called
uImage.bin. The header also contains a CRC32 checksum, checked later
during the image load. Remember the above command is run on your
development workstation, not the embedded target.
You can now upload uImage.bin to your board and use the bootm
command to boot your image. In the following example let's assume you
stored uImage.bin at address 0x01030000 in the flash memory.
u-boot # bootm 1030000
## Booting image at 01030000 ...
Image Name: Linux 2.6.6
Image Type: ARM Linux Kernel Image
Data Size: 700256 Bytes = 683.8 kB
Load Address: 00008000
Entry Point: 00008000
Verifying Checksum ... OK
Starting kernel ...
And off you go! U-Boot uses the header information to copy
your image
to the correct location and then to start execution at the specified
address. Notice that U-Boot also verifies the CRC32 checksum contained
in the header before executing the kernel.
Conclusion
This introduction to the basic features gives you feel for the
power
and utility of "Das U-Boot". For the more advanced features consult
the project README file and visit the U-Boot Wiki.
I hope you
consider using U-Boot on your next embedded project.
Acknowledgments
I would like to personally thank Wolfgang Denk and the other
members
of the U-Boot project for their mostly thankless efforts in creating,
maintaining and extending "Das U-Boot" – open firmware today for an
open tomorrow.
Resources
Das U-Boot project home page
http://sourceforge.net/projects/u-boot/
DENX U-Boot and Linux Guide
http://www.denx.de/twiki/bin/view/DULG/Manual
Free Software Foundation's "Free Software Definition" Page
http://www.fsf.org/philosophy/free-sw.html
TFTP
http://rfc.net/rfc1350.html
|