Article: Introduction to Das U-Boot, the universal open source bootloader
Aug 29, 2004 — by LinuxDevices Staff — from the LinuxDevices Archive — 323 viewsForeword: Das U-Boot is an open source bootloader available for a wide range of embedded processor architectures. This article gently introduces bootloader concepts, traces the origins of Das U-Boot, and offers technical details and tips on using Das U-Boot in embedded Linux devices.
Enjoy . . . !
Exciting new embedded Linux devices are appearing at an astonishing rate. From tiny 3 inch “Gumstix” boards to PDAs and smart-phones embedded Linux is everywhere. Installing and booting Linux on these wildly varying boards is quite a chore. Without a good boot loader these machines are just complicated hunks of silicon with nothing to do. That's where Das U-Boot, a Free Software universal boot loader, steps in.
A boot loader, sometimes referred to as a boot monitor, is a small piece of software that executes soon after powering up a computer. On your desktop Linux PC you may be familiar with lilo or grub, which resides on the master boot record (MBR) of your hard drive. After the PC BIOS performs various system initializations it executes the boot loader located in the MBR. The boot loader then passes system information to the kernel and then executes the kernel. For instance, the boot loader tells the kernel which hard drive partition to mount as root.
In an embedded system the role of the boot loader is more complicated since these systems do not have a BIOS to perform the initial system configuration. The low level initialization of microprocessors, memory controllers, and other board specific hardware varies from board to board and CPU to CPU. These initializations must be performed before a Linux kernel image can execute.
At a minimum an embedded loader provides the following features:
- Initializing the hardware, especially the memory controller.
- Providing boot parameters for the Linux kernel.
- Starting the Linux kernel
Additionally, most boot loaders also provide “convenience” features that simplify development:
- Reading and writing arbitrary memory locations.
- Uploading new binary images to the board's RAM via a serial line or Ethernet
- Copying binary images from RAM to FLASH memory
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, 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, and 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_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
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
Copyright © 2004 by Curt Brune. Used with permission by LinuxDevices according to the terms of the Creative Commons License.
This article was originally published here by Cucy.net, which maintains a discussion thread for the article.
About the author: Curt Brune is an embedded systems consultant for Cucy Systems, based in Mountain View, California. When not championing Free Software he enjoys reading, being outdoors and spending time with his wife Lucy and son Owen.
This article was originally published on LinuxDevices.com and has been donated to the open source community by QuinStreet Inc. Please visit LinuxToday.com for up-to-date news and articles about Linux and open source.