U-Boot

U-Boot, or to give its full name, Das U-Boot, began life as an open source bootloader for embedded PowerPC boards. Then, it was ported to ARM-based boards and later to other architectures, including MIPS, SH, and x86. It is hosted and maintained by Denx Software Engineering. There is plenty of information available, and a good place to start is www.denx.de/wiki/U-Boot. There is also a mailing list at .

Begin by getting the source code. As with most projects, the recommended way is to clone the git archive and check out the tag you intend to use which, in this case, is the version that was current at the time of writing:

Alternatively, you can get a tarball from ftp://ftp.denx.de/pub/u-boot/.

There are more than 1,000 configuration files for common development boards and devices in the configs/ directory. In most cases, you can make a good guess of which to use, based on the filename, but you can get more detailed information by looking through the per-board README files in the board/ directory, or you can find information in an appropriate web tutorial or forum. Beware, though, the way U-Boot is configured has undergone a lot of changes since the 2014.10 release. Double-check that the instructions you are following are appropriate.

Taking the BeagleBone Black as an example, we find that there is a likely configuration file named am335x_boneblack_defconfig in configs/ and we find the text The binary produced by this board supports … Beaglebone Black in the board README files for the am335x chip, board/ti/am335x/README. With this knowledge, building U-Boot for a BeagleBone Black is simple. You need to inform U-Boot of the prefix for your cross compiler by setting the make variable CROSS_COMPILE and then select the configuration file using a command of the type make [board]_defconfig, as follows:

The results of the compilation are:

The BeagleBone Black also requires a Secondary Program Loader (SPL), as described earlier. This is built at the same time and is named MLO:

The procedure is similar for other targets.

Installing a bootloader on a board for the first time requires some outside assistance. If the board has a hardware debug interface, such as JTAG, it is usually possible to load a copy of U-Boot directly into RAM and set it running. From that point, you can use U-Boot commands to copy it into flash memory. The details of this are very board-specific and outside the scope of this book.

Some SoC designs have a boot ROM built in which can be used to read boot code from various external sources such as SD cards, serial interfaces, or USBs, and this is the case with the AM335x chip in the BeagleBone Black. Here is how to load U-Boot via the micro-SD card.

Firstly, format a micro-SD card so that the first partition is in FAT32 format, and mark it as bootable. If you have a direct SD slot available, the card appears as /dev/mmcblk0, otherwise, if you are using a memory card reader, it will be seen as /dev/sdb, or /dev/sdc, and so on. Now, type the following command to partition the micro-SD card, assuming that the card is seen as /dev/mmcblk0:

Format the first partition as FAT16:

Now, mount the partition you have just formatted: on some systems it is enough to simply remove the micro-SD card and then plug it back in again, on others you may have to click on an icon. On current versions of Ubuntu, it should be mounted as /media/[user]/boot so I would copy U-Boot and the SPL to it like this:

Finally, unmount it.

With no power on the BeagleBone board, insert the micro-SD card.

Plug in the serial cable. A serial port should appear on your PC as /dev/ttyUSB0 or similar.

Start a suitable terminal program such as gtkterm, minicom, or picocom and attach to the port at 115,200 bps with no flow control:

Press and hold the Boot Switch button on the Beaglebone, power up the board using the external 5V power connector, and release the button after about 5 seconds. You should see a U-Boot prompt on the serial console:

In this section, I will describe some of the common tasks that you can use U-Boot to perform.

Usually, U-Boot offers a command-line interface over a serial port. It gives a command prompt which is customized for each board. In the examples, I will use U-Boot#. Typing help prints out all the commands configured in this version of U-Boot; typing help <command> prints out more information about a particular command.

The default command interpreter is quite simple. There is no command-line editing by pressing cursor left or right keys; there is no command completion by pressing the Tab key; there is no command history by pressing the cursor up key. Pressing any of these keys will disrupt the command you are currently trying to type and you will have to type Ctrl+C and start over again. The only line editing key you can safely use is the back space. As an option, you can configure a different command shell called Hush, which has more sophisticated interactive support.

The default number format is hexadecimal. For example, as shown in this command:

This command will read 0x200000 bytes from offset 0x400000 from the start of the NAND flash memory into RAM address 0x82000000.

Usually, you will load images from removable storage such as an SD card or a network. SD cards are handled in U-Boot by the mmc driver. A typical sequence to load an image into memory would be:

The command mmc rescan re-initializes the mmc driver, perhaps to detect that an SD card has recently been inserted. Next, fatload is used to read a file from a FAT-formatted partition on the SD card. The format is:

fatload <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]

If <interface> is mmc, as in our case, <dev:part> is the device number of the mmc interface counting from zero, and the partition number counting from one. Hence <0:1> is the first partition on the first device. The memory location, 0x82000000, is chosen to be in an area of RAM that is not being used at this moment. If we intend to boot this kernel, we have to make sure that this area of RAM will not be overwritten when the kernel image is decompressed and located at the runtime location, 0x80008000.

To load image files over a network you use the Trivial File Transfer Protocol (TFTP). This requires you to install a TFTP daemon, tftpd, on your development system and start it running. You also have to configure any firewalls between your PC and the target board to allow the TFTP protocol on UDP port 69 to pass through. The default configuration of tftpd allows access only to the directory /var/lib/tftpboot. The next step is to copy the files you want to transfer to the target into that directory. Then, assuming that you are using a pair of static IP addresses, which removes the need for further network administration, the sequence of commands to load a set of kernel image files should look like this:

Finally, let's look at how to program images into NAND flash memory and read them back, which is is handled by the nand command. This example loads a kernel image via TFTP and programs it into flash:

Now you can load the kernel from flash memory using nand read:

The bootm command starts a kernel image running. The syntax is:

bootm [address of kernel] [address of ramdisk] [address of dtb].

The address of the kernel image is necessary, but the address of ramdisk and dtb can be omitted if the kernel configuration does not need them. If there is a dtb but no ramdisk, the second address can be replaced with a dash (-). That would look like this:

Let's assume that your hardware department has created a new board called "Nova" that is based on the BeagleBone Black and that you need to port U-Boot to it. You will need to understand the layout of the U-Boot code and how the board configuration mechanism works. In the 2014.10 release, U-Boot adopted the same configuration mechanism as the Linux kernel, Kconfig. Over the next few releases, the existing configuration settings will be moved from the current location in the header files in include/configs into Kconfig files. As of the 2014.10 release, each board had a Kconfig file which contains minimal information derived from the old boards.cfg file.

The main directories you will be dealing with are:

The way that Kconfig extracts configuration information from Kconfig files and stores the total system configuration in a file named .config is described in some detail in Chapter 4, Porting and Configuring the Kernel. U-Boot has adopted kconfig and kbuild with one change. A U-Boot build can produce up to three binaries: a normal u-boot.bin, a Secondary Program Loader (SPL), and a Tertiary Program Loader (TPL), each with possibly different configuration options. Consequently, lines in .config and default configuration files can be prefixed with the codes shown in the following table to indicate which target they apply to:

None

Normal image only

S:

SPL image only

T:

TPL image only

ST:

SPL and TPL images

+S:

Normal and SPL images

+T:

Normal and TPL images

+ST:

Normal, SPL and TPL images

Each board has a default configuration stored in configs/[board name}_defconfig. For your Nova board, you will have to create a file named nova_defonfig. for example, and add these lines to it:

On the first line, CONFIG_SPL=y causes the SPL binary, MLO, to be generated, CONFIG_ARM=y causes the contents of arch/arm/Kconfig to be included on line three. On line four, CONFIG_TARGET_NOVA=y selects your board. Note that lines three and four are prefixed by +S: so that they apply to both the SPL and normal binaries.

You should also add a menu option to the ARM architecture Kconfig that allows people to select Nova as a target:

Each board has a subdirectory named board/[board name] or board/[vendor]/[board name] which should contain:

In addition, there may be source files for board specific functions.

Your Nova board is based on a BeagleBone which, in turn, is based on a TI AM335x EVM, so, you can start by taking a copy of the am335x board files:

Next, change the Kconfig file to reflect the Nova board:

Setting SYS_CPU to armv7 causes the code in arch/arm/cpu/armv7 to be compiled and linked. Setting SYS_SOC to am33xx causes the code in arch/arm/cpu/armv7/am33xx to be included, setting SYS_BOARD to nova brings in board/nova and setting SYS_CONFIG_NAME to nova means that the header file include/configs/nova.h is used for further configuration options.

There is one other file in board/nova that you need to change, the linker script placed at board/nova/u-boot.lds, which has a hard-coded reference to board/ti/am335x/built-in.o. Change this to use the copy local to nova:

To build for the Nova board, select the configuration you have just created:

Copy MLO and u-boot.img to the FAT partition of the micro-SD card you created earlier and boot the board.

We are used to the idea that booting a modern embedded processor involves the CPU boot ROM loading an SPL which loads u-boot.bin which then loads a Linux kernel. You may be wondering if there is a way to reduce the number of steps, thereby simplifying and speeding up the boot process. The answer is U-Boot "Falcon mode", named after the Peregrine falcon which is claimed to be the fastest of all birds.

The idea is simple: have the SPL load a kernel image directly, missing out u-boot.bin. There is no user interaction and there are no scripts. It just loads a kernel from a known location in flash or eMMC into memory, passes it a pre-prepared parameter block and starts it running. The details of configuring Falcon mode are beyond this book. If you would like more information, take a look at doc/README.falcon.