Chapter 9. Setting Up the Bootloader

Though the bootloader runs for a very short time during the system’s startup and is mainly responsible for loading the kernel, it is a very important system component. Setting up a bootloader is, to some extent, a task common to all Linux systems. It is a special task, nevertheless, for embedded Linux systems, because the bootloaders used in such systems are either completely different from those used in common systems or, even when they are the same, are configured and operated in very different ways.

Chapter 7 discussed the manipulation of embedded storage devices, and Chapter 8 explained how to set up a root filesystem for use in an embedded target. We are now ready to set up the bootloader along with the other components created earlier so we may obtain a bootable and functional embedded system. Because hardware architectures differ greatly among each other and because boards based on the same architecture differ greatly among themselves, the selection, set up, and configuration of a bootloader depend largely on the hardware you are using.

There is a slew of bootloaders available for Linux, thousands upon thousands of embedded boards, and many possible boot configurations for a same board. It is, therefore, inconceivable to cover all the possible combinations within a single chapter. Nor is it possible to give an in-depth discussion of the use of each of the bootloaders covered. Many existing bootloaders for Linux either already have an entire book describing their use or need one to be written for them.

Also, the number and quality of bootloaders vary greatly between architectures. Some architectures, such as the PPC and the x86, have well known, established bootloaders providing support for a range of hardware. Other architectures have few or no standard bootloaders and mainly rely on the use of bootloaders provided by the hardware manufacturer. If you are using a bootloader provided by the manufacturer, make sure you have all the binaries and documentation. If possible, obtain the source code too so you can reprogram your target freely.

This chapter will concentrate on the bootloader/boot setup combinations most commonly used in embedded systems to load Linux. Although GRUB can be installed and used on hard disks, for example, its most common use in embedded Linux systems is to load Linux from DOC devices. Hence, the GRUB section will cover only GRUB’s use to load Linux from DOC devices.

First, we start by looking at the plethora of embedded bootloaders available for use with Linux. We then discuss how to set up and configure a server to provide BOOTP/DHCP and NFS services for targets that use these services to obtain a kernel image and mount their root filesystem, respectively. This is followed by in-depth discussions of the use of LILO with disk devices, the use of GRUB with DOC devices, and the use of U-Boot.

At the end of this chapter, you will either have installed all the components we created earlier, configured your target with the appropriate bootloader, and be ready to boot your system, or you will know where to get the rest of the information you need to achieve this.

Bootloaders Galore

As I said above, many bootloaders can be used with Linux on various hardware. In this section, I will introduce the most popular and most versatile open source bootloaders for each architecture. Some architectures, such as the MIPS and the m68k, have no standard bootloaders at all. If your target is based on an MIPS or m68k processor, refer to the documentation provided by the manufacturer for instructions on how to set up and boot your hardware.

Also, some publications make a distinction between “bootloader” and “monitor.” In those cases, the bootloader is just the component that boots a device and launches the main software, whereas a monitor provides, in addition to booting capabilities, a command-line interface that can be used for debugging, reading/writing memory, flash reprogramming, configuring, etc. In this chapter, I will refer to both types of software as “bootloaders,” while explicitly mentioning a bootloader’s monitor capabilities when available.

In comparing bootloaders, keep in mind that the availability and extent of monitor capabilities are important during development. Once development is over, however, these capabilities may become a liability, because the priority is to ensure that the user cannot inadvertently enter the monitor mode. Some bootloaders, such as U-Boot for example, can be reconfigured to allow or disallow access to monitor features. Your production hardware may also be built to prevent physical access to the serial port.

Table 9-1 presents the open source bootloaders that can be used with Linux and the architectures they support. For each bootloader, the table also indicates whether the bootloader provides monitor capabilities, and provides a short description of the bootloader. Use this table as a starting point for identifying which bootloader is best for your embedded system.

Table 9-1. Linux-capable open source bootloaders and the architectures they support
   

Architectures

Bootloader

Monitor

Description

x86

ARM

PowerPC

MIPS

SuperH

m68k

LILO

No

The main disk bootloader for Linux

X

GRUB

No

GNU’s successor to LILO

X

ROLO

No

Loads Linux from ROM without a BIOS

X

Loadlin

No

Loads Linux from DOS

X

Etherboot

No

ROMable loader for booting systems through Ethernet cards

X

LinuxBIOS

No

Linux-based BIOSreplacement

X

Compaq’s bootldr

Yes

Versatile loader mainly intended for Compaq iPAQ

X

blob

No

Loader from the LART hardware project

X

PMON

Yes

Loader used in Agenda VR3

X

sh-boot

No

Main loader of the LinuxSH project

X

U-Boot

Yes

Universal loader based on PPCBoot and ARMBoot

X

X

X

RedBoot

Yes

eCos-based loader

X

X

X

X

X

X

In addition to the above table, there are a few observations to be made regarding the various bootloaders available for each architecture:

x86

There are two main bootloaders used for the x86: LILO and GRUB. LILO is the mainstream bootloader for most x86 workstation and server distributions. On Red Hat’s distribution, however, GRUB has replaced LILO. There are other, less known bootloaders, such as Rolo and EtherBoot, which you may be interested in using under certain circumstances.

As you can see, few x86 bootloaders currently provide monitor capabilities. The most glaring limitation of x86 bootloaders is that most require an x86-based host for your development. The Makefiles of LILO and GRUB, for example, are not built to allow cross-compilation. Moreover, it is difficult to install either LILO or GRUB from a non-x86 host on storage media designated for an x86 target. Hence, even if you carry out all your development on a non-x86 host, you may need to use an x86 host to compile and install the x86 bootloader you select.

ARM

Though U-Boot aims at becoming the standard ARM bootloader, there is no standard bootloader for ARM-based systems at the time of this writing. There are, nevertheless, a couple of ARM bootloaders as shown in Table 9-1, each supporting a different set of hardware. There are also many other bootloaders that can be used to boot Linux on an ARM system. Some of these bootloaders are outdated or haven’t been updated for a long time, others are particular to one type of board or are not available under an open source license.

PowerPC

The main bootloader found in most PPC systems is U-Boot (formerly known as PPCBoot.)

MIPS

There is no standard bootloader for MIPS-based embedded Linux systems. Though PMON may be useful as an initial codebase, you will most probably need to port it to your target before using it. At the time of this writing, efforts are underway to add MIPS support to U-Boot.

SuperH

Though sh-boot is the main bootloader for SH-based embedded Linux systems, you may find other bootloaders, such as RedBoot, better adapted to your system.

M68k

Though RedBoot supports some m68k-based systems, there is no standard bootloader for m68k-based embedded Linux systems.

Now that I’ve introduced the various bootloaders and outlined the bootloader support for each architecture, let’s take a closer look at each bootloader.

LILO

The LInux LOader (LILO) was introduced by Werner Almesberger very early in Linux’s history. Now, LILO is maintained by John Coffman and the latest releases are available from http://brun.dyndns.org/pub/linux/lilo/. LILO is a very well documented bootloader. The LILO package, for instance, includes a user manual and an internals manual. The LILO mini-HOWTO available from the LDP completes this documentation by answering some of the most common questions about LILO’s use. In addition, Running Linux contains a "Using LILO" section in Chapter 5.

GRUB

The GRand Unified Bootloader (GRUB) is the main bootloader for the GNU project. GRUB was originally written by Erich Boleyn in the course of finding an appropriate bootloader for what would later be known as GNU Mach. Eric’s work was later picked up by Gordon Matzigkeit and Okuji Yoshinori, who currently continue to maintain and develop GRUB. The GRUB project’s web site is located at http://www.gnu.org/software/grub/. There, you will find the GRUB manual, which discusses the package’s use extensively. One aspect of GRUB’s capabilities you may find helpful during development is its ability to boot over the network using TFTP, and BOOTP or DHCP. Though GRUB’s code can be retrieved using CVS, the latest stable releases are tar-gzipped and made available for download through the project’s web site.

ROLO

The ROmable LOader (ROLO) was written and is being maintained by Robert Kaiser from Sysgo Gmbh. as part of Sysgo’s ELinos distribution. ROLO can boot Linux directly from ROM without requiring any BIOS. ROLO is available from ftp://ftp.elinos.com/pub/elinos/rolo/. Though the package contains little documentation, Vipin Malik has written a thorough article on the use of ROLO in an embedded system at http://www.embeddedlinuxworks.com/articles/rolo_guide.html.

loadlin

loadlin is a DOS utility to load Linux maintained by Hans Lermen at http://elserv.ffm.fgan.de/~lermen/. Though you should avoid building your system in a way that requires DOS to be loaded first, there are cases where such a utility can be very handy. One case where it can be useful, for example, is if you want to use M-Systems’s DOS tools to boot from a DOC device. In that case, you can write an autoexec.bat file that uses the loadlin utility to load Linux. As we will see below, however, you can boot Linux directly from a DOC device using GRUB.

EtherBoot

Many NICs are shipped with a socket for inserting ROM chips. When present and properly signed, these ROM chips are recognized as BIOS extensions and executed during startup. EtherBoot uses this capability to support the booting of diskless systems through the network. EtherBoot has been used in many environments, including X-terminals, routers, and clusters. It is available with complete documentation from http://etherboot.sourceforge.net/. The web site provides links to manufacturers who sell EPROMs preloaded with EtherBoot.

LinuxBIOS

LinuxBIOS is a complete BIOS replacement that boots Linux from ROM at startup. LinuxBIOS was developed as part of clustering research conducted at the Los Alamos National Laboratory and has gathered support from many hardware manufacturers. The LinuxBIOS package and documentation are available at http://www.linuxbios.org/.

Compaq’s bootldr

Though initially developed for the Compaq iPAQ only, Compaq’s bootldr currently supports Intel’s Assabet and HP’s Jornada 720. Though it is limited in the range of hardware it supports, bootldr provides a very rich command set and is capable of loading kernels directly from JFFS2 MTD partitions. Bootldr is part of the software collection maintained by http://www.handhelds.org/ and is available for download from ftp://ftp.handhelds.org/bootldr/.

blob

blob was introduced as the bootloader for the LART hardware project.[1] Since its introduction, blob has been ported to many other ARM-based systems, including Intel’s Assabet and Brutus, Shannon, and Nesa boards. Unlike ARMBoot and Compaq’s bootldr, blob does not provide monitor capabilities, though it can be used to reprogram the flash and can load kernels directly from JFFS2 MTD partitions. blob is available from the LART web site along with documentation at http://www.lart.tudelft.nl/lartware/blob/.

PMON

The Prom Monitor (PMON) was written by Phil Bunce to support LSI LOGIC’s MIPS boards. It is distributed under a very simplistic license, which stipulates that PMON comes with no warranty and that you are free to redistribute it without any restriction. Though Phil’s PMON has not been updated since 1999, it is still available at http://www.carmel.com/pmon/. Others have nevertheless used PMON for more recent projects. It was ported to the now discontinued Agenda VR3 Linux PDA by Bradely LaRonde. That version is available from the AGOS SourceForge workspace at http://agos.sourceforge.net/ and information on its use is available from the Agenda Wiki site at http://agendawiki.com/. It remains that there is no central authority or roadmap for PMON, and few boards are actually supported. As I said earlier, you may find the PMON codebase a good starting point, but you will most probably need to port it to your system to use it.

sh-Boot

sh-boot is developed as part of the Linux SH project on SourceForge. Unfortunately, sh-boot has not been updated for a while, so you may need to evaluate its usability for your system. Also, sh-boot is a simple bootloader and does not provide any monitor capabilities. The bootloader is available using CVS through the Linux SH project site at http://linuxsh.sourceforge.net/.

U-Boot

Though there are quite a few other bootloaders, “Das U-Boot,” the universal bootloader, is arguably the richest, most flexible, and most actively developed open source bootloader available. It is currently maintained by Wolfgang Denk of DENX Software Engineering, and is contributed to by a wide range of developers. U-Boot is based on the PPCBoot and ARMBoot projects. PPCBoot was itself based on 8xxrom sources, and ARMBoot was an ARM port of PPCBoot done by Sysgo Gmbh. At the time of this writing, U-Boot supports around 100 different PPC-based boards, more than a dozen ARM-based boards, and a handful of x86-based boards. U-Boot’s success in scaling to a wide range of hardware has prompted developers to continue porting it to even more new boards and architectures.

Among other things, U-Boot is capable of booting a kernel through TFTP, from an IDE or SCSI disk, and from a DOC. Also, it includes read-only support for JFFS2. Besides having an extensive command set and quite a few capabilities, it is also fairly well documented. The README included with the package provides an in-depth discussion of the use of U-Boot. The doc directory in the package’s source includes any extra instructions required for certain boards. In addition to the instructions found in the package, Wolfgang wrote the DENX PPCBoot and Linux Guide, available at http://www.denx.de/re/DPLG.html, which provides many practical examples of the use of PPCBoot with Linux on a TQM8xxL board. Though the discussion assumes that you are using PPCBoot and DENX’s Embedded Linux Development Kit (ELDK) distribution,[2] the sections relating to the use of PPCBoot apply with little or no changes to U-Boot, and are helpful regardless of whether you use any distribution.

The U-Boot project workspace is located at http://sourceforge.net/projects/u-boot. The U-Boot package is available from that site. If you intend to use U-Boot often, you will find it useful to subscribe to the very active U-Boot users mailing list at that site. Though there is no on-site documentation for U-Boot at the time of this writing, you can still rely on the documentation and background provided by the two projects on which U-Boot is based, PPCBoot and ARMBoot. PPCBoot’s web site is located at http://ppcboot.sourceforge.net/, and ARMBoot’s project web site is located at http://armboot.sourceforge.net/. We will explore U-Boot’s use later in this chapter.

RedBoot

RedBoot is supposed to be a next generation bootloader from Red Hat, replacing CygMon and GDB stubs with a firmware supporting a very wide range of hardware. Although Red Hat has stopped active development of eCos, the OS on which RedBoot is based, eCos has now been relicensed under the GPL and continues to be maintained by some of the Red Hat core eCos developers. eCos’ future, and RedBoot’s as well, is therefore in the hands of those developers.

Despite its dependency on eCos,[3] RedBoot remains a very powerful bootloader. It is, for instance, the only open source bootloader that currently supports all the architectures presented in depth in Chapter 3 and a wide range of boards based on these architectures. Also, the RedBoot package is fairly well documented, including a RedBoot User’s Guide that provides actual examples of its use on more than a dozen different systems. RedBoot’s web site is located at http://sources.redhat.com/redboot/ and its sources are available with the rest of the eCos sources using CVS. Lately, eCosCentric Ltd., the company formed by the core eCos developers from Red Hat, has been providing CVS snapshots at http://www.ecoscentric.com/snapshots/.

Server Setup for Network Boot

As we saw in Chapter 2, setting up a target for network boot is ideal during the early stages of development, because you can gradually modify the kernel and the root filesystem without having to update the target’s storage devices every time you make a modification. Though not all bootloaders can use this setup to boot, I recommend that you use such a setup whenever possible.

As I said earlier, the simplest way to boot your target from the network is to use BOOTP/DHCP, TFTP, and NFS. BOOTP/DHCP is the standard way to provide a network host with basic boot information, including the location of other servers such as TFTP and NFS. TFTP is the simplest network protocol for downloading remote files. In the case of an embedded Linux system, it is used by the target to obtain a kernel image from the TFTP server. Finally, NFS is the standard and simplest protocol for sharing entire directory trees between a client and a server. In the case of an embedded Linux system, it is used by the target to mount its root filesystem from the NFS server. NFS cannot be used for any earlier activity, because it requires a booted Linux kernel to operate. Together, these three protocols provide a very efficient host/target development setup.

To enable network booting of the target, you must set up the development host’s network services so that the target can access the components it needs. In particular, you need to set up a host to respond to BOOTP/DHCP requests, provide a kernel using a TFTP server, and enable NFS mounts. The subsections below discuss each issue separately.

Setting Up the DHCP Daemon

Unlike other network services, DHCP is not dependent on the internet super-server. Instead, the DHCP daemon is a service of its own, and you need to start it manually. First, you need to make sure that the DHCP server is installed on your system. Though you can download it from http://www.isc.org/, the DHCP server is part of most mainstream distributions.

If you are using an RPM-based distribution, use the following command to check for the presence of the DHCP daemon:

$ rpm -q dhcp
dhcp-2.0-5

In this case, DHCP 2.0-5 is already installed. If it is not already installed on your system, use the appropriate tools for your distribution to install the DHCP server. Note that most distributions include two DHCP packages, a client and a server. The package containing the client is usually called dhcpc- VERSION. There is an additional “c” after “dhcp” to identify the client package.

To operate properly, the kernel on which the DHCP server runs has to be configured with the CONFIG_PACKET and CONFIG_FILTER options. The kernels shipped by default in most distributions almost always have these enabled. If you are in the habit of building your own kernels for your workstation, as I often do, watch out for those options when configuring the kernel. If the kernel wasn’t built properly, the DHCP daemon will output the following message when it tries to start:

socket: Protocol not available - make sure CONFIG_PACKET and CONFIG_FILTER are 
defined in your kernel configuration!
exiting.

With the package installed and the kernel properly configured, create or edit the /etc/dhcpd.conf file and add an entry for your target. For example, here is the /etc/dhcpd.conf file for my control module:

subnet 192.168.172.0 netmask 255.255.255.0 {
        option routers 192.168.172.50;
        option subnet-mask 255.255.255.0;

        host ctrl-mod {
                hardware ethernet 00:D0:93:00:05:E3;
                fixed-address 192.168.172.10;
                option host-name "ctrl-mod";
                next-server 192.168.172.50;
                filename "/home/karim/vmlinux-2.4.18.img";
                option root-path "/home/karim/ctrl-rootfs";
        }
}

Essentially, this entry states that the host and target are on the 192.168.172.0 network, that the TFTP server is located at 192.168.172.50, and that the address allocated to the target when it issues its DHCP or BOOTP request is 192.168.172.10. The hardware ethernet field uniquely identifies the target through its MAC address, which is 00:D0:93:00:05:E3 for my control module. The fixed-address field tells the DHCP server which IP address should be allocated to the designated MAC address. The option host-name field gives the hostname to the target so that it can use it internally. The next-sever tells the target where the TFTP server is located. The filename field is the filename[4] of the image that has to be loaded by the target. According to RFC 2131, which specifies DHCP, the filename is limited to 128 bytes. Finally, the option root-path field provides the path to the target’s root filesystem on the NFS server. If your target does not need to load its root filesystem from an NFS server, you can omit this last field. Because the host is the only network link to the target in this case, option routers points to the host’s address. If the target was linked to an entire network with a real router, option routers should point to that network’s default router.

The example configuration provided above should be easy to adapt to your own target. If you need more information regarding the configuration of the DHCP server, have a look at the manpage for dhcpd.conf and the sample configuration file installed by your distribution, if one is present.

Note that if you are using a version of the DHCP daemon later than 3.0b2pl11, such as the one shipped with Red Hat 8.0, you will need to add the following line to your dhcpd.conf file:

ddns-update-style ad-hoc;

With the DHCP server configured for the target, you are almost ready to start the DHCP server. Before you do so, however, you need to make sure the /var/state/dhcp/dhcpd.leases file exists. If it doesn’t, create it using the touch command. If the file isn’t created, the DHCP daemon will refuse to start.

Finally, start the DHCP server. On distributions based on Red Hat, enter:

# /etc/init.d/dhcpd start

Setting Up the TFTP Daemon

The first step in setting up the TFTP daemon is to make sure the TFTP package is installed. Though the latest version of the TFTP daemon is available for download as part of the NetKit package at ftp://ftp.uk.linux.org/pub/linux/Networking/netkit/, TFTP was most likely already installed on your system as part of your distribution or is available to be installed from your distribution’s CDs.

If you are using an RPM-based distribution, use the following command to check for the presence of the TFTP daemon:

$ rpm -q tftp
tftp-0.16-5

In this case, TFTP 0.16-5 is already installed. If it is not available on your system, install the TFTP package using the appropriate tool for your distribution. Alternatively, if your system doesn’t rely on a package manager or if some components have been installed without a package manager, you can also check for the presence of the actual TFTP daemon binary using the whereis command.

Once the package is installed, enable the TFTP service by modifying the appropriate internet super-server configuration file. In brief, the internet super-server listens on designated ports on behalf of the various network services. When a request for certain service is received, the super-server spawns the appropriate daemon and hands it the request. Hence, only the minimal number of daemons run at all times. TFTP is one of the daemons normally handled by the super-server.

To enable the TFTP service in a system based on the inetd super-server, edit /etc/inetd.conf, uncomment the line for the TFTP service by removing the # character at the beginning, and send a SIGHUP signal to the inetd process so that it rereads its configuration file. To enable the TFTP service in a system based on the xinetd super-server, edit /etc/xinetd.d/tftp and comment the line containing disable = yes by adding a # character at the beginning. As with inetd, you must send a SIGHUP to xinetd.

Finally, you must provide the TFTP server with a list of directories containing files that should be made available to TFTP clients. In a system based on the inetd super-server, append the list of directories to the TFTP line in /etc/inetd.conf. In a system based on the xinetd super-server, edit the /etc/xinetd.d/tftp file and append the list of directories to the server_args = line. The default directory for TFTP is /tftpboot. You may choose to modify this to match your setup. Whichever directory you choose, make sure its access permissions include read and execute for the “other” permission.

For example, here is a TFTP line in /etc/inetd.conf for a host using the inetd super-server:

tftp   dgram  udp    wait   root  /usr/sbin/tcpd  in.tftpd /home/karim/

In this case, images are placed in the /home/karim directory, which has the following permissions:

$ ls -ld /home/karim
drwxr-xr-x    4 karim     karim         4096 Aug 29 16:13 karim

Here is a modified /etc/xinetd.d/tftp file from a Red Hat-based installation providing the same functionality for a host using the xinetd super-server:

service tftp
{
        socket_type             = dgram
        protocol                = udp
        wait                    = yes
        user                    = root
        server                  = /usr/sbin/in.tftpd
        server_args             = /home/karim
#        disable                 = yes
        per_source              = 11
        cps                     = 100 2
}

Regardless of the super-server in use on a host, the TFTP service is usually disabled by default. Hence, even if you use the default /tftpboot, you will need to modify the super-server’s configuration files to enable TFTP.

Mounting a Root Filesystem on an NFS Server

As I explained in Chapter 2, while a bootloader and kernel must be stored locally or retrieved to local storage through one of the methods shown earlier, the target’s kernel can mount its root filesystem from a remote NFS server. To this end, the NFS server must be properly installed and configured. Chapter 6 showed how to build your target’s root filesystem. Though Chapter 8 showed how to prepare this filesystem for use in the target, the root filesystem we created in Chapter 6 does not need any special preparation for use by the NFS server.

The NFS server daemon is available in two flavors: as a standalone user application or as a part of the kernel. Besides being faster, the latter is also the standard way most distributions are configured. In addition to the NFS server itself, you need to have the NFS utilities installed. Usually, there is an nfs-utils package as part of your distribution. Use the following command to identify whether nfs-utils is installed:

$ rpm -q nfs-utils
nfs-utils-0.3.1-13

With the nfs-utils installed, you need to make sure that the appropriate configuration files are present and that the corresponding services are started.

The main file we need to configure for the NFS server is /etc/exports. Entries in this file describe the directories each host or set of hosts can access. As an example, here is the entry in my /etc/exports for my control module:

/home/karim/ctrl-rootfs 192.168.172.10(rw,no_root_squash)

This entry states that the machine with address 192.168.172.10 has read and write (rw) access to the /home/karim/ctrl-rootfs directory, which is the path to the root filesystem we built for the target in Chapter 6. In addition, the no_root_squash argument indicates that the server should allow the remote system to access the directory with its root privileges. These are very powerful rights that we are granting to the target. If we have total control over access to the device, as is the case in most development setups, there is obviously no security risk. If, however, the target’s location is less secure or if it is directly connected to the Internet, for example, you may prefer to use the default root_squash instead. In that case, the target will not be able to write to most of its own root filesystem, though it will still be able to read and write to all directories and files that are readable and writable by anybody. In practical terms, however, the target’s operation will be very limited.

Because offering the NFS service also involves the risk of network abuse, it is often pertinent to use some minimal protection mechanisms to avoid intrusions. One simple way to do this is to customize the /etc/hosts.deny and /etc/hosts.allow files to restrict access to network services. For example, here is the /etc/hosts.deny file for my Red Hat-based host:

#
# hosts.deny
#

portmap: ALL
lockd: ALL
mountd: ALL
rquotad: ALL
statd: ALL

and here is my /etc/hosts.allow file:

#
# hosts.allow
#
 
portmap: 192.168.172.10
lockd: 192.168.172.10
mountd: 192.168.172.10
rquotad: 192.168.172.10
statd: 192.168.172.10

The rules specified in this files restrict access to the various file-sharing services. Together, these files indicate that only the machine with address 192.168.172.10 can use the NFS services. This is fine in the case of my setup, since I don’t want to share my workstation with anyone else. Even if you do not customize /etc/hosts.deny and /etc/hosts.allow, I encourage you to take security issues to heart and use whichever means necessary, such as backups, to protect your work.

Once the configuration files are created, you can start the portmapper service, which is required by the NFS server:

# /etc/init.d/portmap start

Finally, you can start the NFS server itself:

# /etc/init.d/nfs start

If you would like more information on the configuration of remote boot using NFS, see the two Diskless root NFS HOWTOs on the issue at the LDP. Also, you may be interested by the NFS HOWTO, also at the LDP.

Using LILO with Disk and CompactFlash Devices

Because there is already ample documentation on the installation, configuration, and use of LILO, I will cover only its specific use in embedded PC-like systems. Specifically, I will provide the instructions to use on the host to install LILO on a storage device meant to be used in the target.

The installation of LILO on a target’s storage device requires the use of the removable storage setup as explained in Chapter 2. In this scenario, the target’s storage device is removed from the target and connected to the host’s own hardware to be programmed. Hence, the target’s storage device is controlled by the host’s operating system like any other host device. The target’s storage device is therefore seen as an extra storage device for the host. It can be seen, for example, as a secondary IDE disk (/dev/hdb) or as a primary SCSI disk (/dev/sda). Regardless of the way it is seen by the host’s kernel, LILO needs to be used in a specific way to install itself on this secondary storage and not on the host’s boot media, as is the default.

As we discussed in Chapter 8, CF devices are quite peculiar in this regard, because they can be seen on the host as a SCSI disk (/dev/sd X) when accessed through a USB CF reader, while being seen on the target as an IDE disk (/dev/hd X) when accessed through a CF-to-IDE or CF-to-PCMCIA adapter. The configuration file example I provide below takes care of this issue by using the appropriate BIOS and kernel flags so that the disk seen as a SCSI disk on the host can boot normally as an IDE disk once put back in the target.

In the following, I assume that the storage device where LILO will be installed is accessible on your host, and that you are using LILO Version 22.3 or later. If you are using an earlier version, an important command will fail, as I will explain shortly. Follow these steps to install LILO on a secondary IDE or SCSI storage device on your host:

  1. Create appropriate /dev entries in your target’s root filesystem for the storage device where LILO is to be installed. This is not the storage device as it will be accessed once in your target. Rather, this is the storage device entry used by the host to access the designated storage device. If, for example, you want to install LILO on /dev/sda (usually the first SCSI hard disk in your system), there must be a /dev/sda entry on your target’s root filesystem. It is very likely that this entry does not correspond to a valid device on your target. Indeed, it is possible that the disk accessed as /dev/sda on the host may be accessed as /dev/hda once on the target. Nevertheless, you must create the /dev/sda entry in your target’s root filesystem for LILO to use when running on the host. The reasons for this will soon become evident. For more information on the relationship between /dev entries and the actual physical storage devices, see Chapter 3 of Running Linux.

  2. Create a LILO configuration file on your target root filesystem. To avoid damaging your host’s configuration when installing LILO on the target’s storage device, put your LILO configuration in /etc/target.lilo.conf on your target’s root filesystem instead of the usual /etc/lilo.conf. Hence, if you accidentally issue a LILO command that modifies your host, the tool will complain about a missing file and no damage will be done to your host.

    Here is a sample /etc/target.lilo.conf to boot my DAQ module from a CF card:

    boot = /dev/sda
    disk = /dev/sda
      bios = 0x80
    
    image = /boot/bzImage-2.4.18
      root = /dev/sda1
      append = "root=/dev/hda1"
      label = Linux
      read-only

    In this case, the CF card is accessed through a USB CF reader and is visible on my host as a SCSI disk through /dev/sda. On the target, however, it will be accessed through a CF-to-IDE adapter and will be visible as an IDE drive through /dev/hda. If you use a normal LILO configuration file to configure LILO, it would guess the BIOS ID of the disk it is operating on, and would use that ID at startup to make access requests to the BIOS. Since, in this case, it is operating on a SCSI disk, it would assume a SCSI BIOS ID and would make access requests for such a disk. Since no such disk exists on the target, the BIOS would return an error and LILO would fail to boot. The trick in the configuration file above lies in the bios = 0x80 line. This informs LILO that it is booting from the disk with BIOS ID 0x80, which is the first IDE drive in the system. Because of the confusion between SCSI and IDE, I must also append a root=/dev/hda1 option to the kernel’s boot parameters. Otherwise, the kernel would fail to find its root filesystem and crash while trying to mount it.[5]

    Alternatively, if you want to install LILO on /dev/hdb, replace the /dev/sda entries above with /dev/hdb. In this case, you won’t need to append the root=/dev/hda1 option to the kernel’s boot instructions, because the disk appears as IDE both on the host and the target.

    When LILO is run with the configuration file above, it opens the host’s /dev/sda device and installs itself there. Because this configuration file is located in ${PRJROOT}/rootfs/etc/target.lilo.conf instead of /etc/lilo.conf, special options must be used with LILO to provide it with the location of this alternative configuration file. I will present the complete LILO command line to use in this configuration shortly.

    For a complete discussion of how LILO is installed on an alternative storage device, see the Installing hdc to Boot as hda and Using bios= section in the LILO mini-HOWTO provided by the LDP.

  3. If necessary, partition the storage device using fdisk.

  4. Create filesystems on the storage device for the filesystem types you selected using the appropriate filesystem creation tools. For an ext2 filesystem, for example, use mke2fs.

  5. Mount the root filesystem partition on an appropriate directory in /mnt.

  6. Copy the root filesystem to its designated partition using cp -a. The root filesystem must contain the kernel image referenced by the /etc/target.lilo.conf file created earlier, /boot/bzImage-2.4.18 in this case.

  7. Install LILO on the storage device. For my DAQ module’s storage device, for example, which is mounted as /mnt/cf on my host, I use the following command:

    # lilo -r /mnt/cf -C etc/target.lilo.conf
    Warning: etc/target.lilo.conf should be owned by root
    Warning: LBA32 addressing assumed
    Added Linux *

    This command instructs lilo to use the chroot( ) system call to change its root directory to /mnt/cf directory and to use the etc/target.lilo.conf configuration file found in that directory. The command programs the devices specified in the target.lilo.conf configuration file. The /dev entries specified in the configuration file are located starting from the root directory entry, /mnt/cf. If /dev/sda must be programmed, for example, LILO attempts to open and program /mnt/cf/dev/sda.

    If you had forgotten to create the /dev entries specified in target.lilo.conf on your target’s root filesystem, this command will fail. It will also fail if there is no /tmp directory on your target’s root filesystem. Furthermore, if you are using a LILO version earlier than 22.3, the command will report the following error and fail:

    Fatal: open /boot/boot.b: No such file or directory

    This error message is due to the fact that, prior to Version 22.3, LILO’s components were separated across different files, some of which were .b files. Since 22.3, all .b files are part of the lilo binary.

  8. Unmount the root filesystem partition.

You can now remove the storage device from your host, either by shutting down the host and removing the hard disk or by removing the CF card from the CF reader, instaling it in your target, and booting it.

Using GRUB with DiskOnChip Devices

Since the use of GRUB with conventional disk devices is already amply covered in the GRUB manual, we will mainly concentrate on the installation and use of GRUB with DOC devices. Before I start covering the details of how to compile and use GRUB with a DOC device, I must warn you that an improper configuration of GRUB for your DOC can render your system unbootable. Let’s see why this happens and how it can be avoided.

As I explained in Chapter 7 when describing the use of the doc_loadbios command, DOC devices contain a ROM program called the IPL that is detected as a BIOS extension at startup and is executed by the BIOS. When it runs, this IPL installs another program, the SPL. To boot from a DOC device using GRUB, the SPL must be replaced by a version of GRUB specifically tailored to boot from a DOC.

Since there may be other BIOS extensions in the system, the SPL loaded by the IPL cannot boot the system right away. Instead, it must install a Terminate and Stay Resident (TSR) program that will lay dormant until the BIOS is ready to boot the system. In the case of GRUB, the GRUB SPL replaces the BIOS’s bootstrap interrupt, INT 19h, with a custom interrupt handler that will execute the rest of the GRUB code to finish booting from the DOC device. Hence, the other BIOS extensions get to run and GRUB is called only when the system is ready to be booted.

The problem with this scheme, however, is that the default bootstrap handler installed by the BIOS never gets a chance to run, and any boot configuration option you may have selected in your BIOS—such as booting from disk or floppy first—will be completely ignored by GRUB when its handler is invoked. This is fine if the configuration file on the DOC is correct. At worst, you would then boot using the DOC, change the configuration file in Linux, or completely remove GRUB from the DOC to set the system as you desire.

If you make any mistakes in the GRUB configuration file that result in boot failure, however, you will be unable to restart your system normally without finding a way to disable the replacement of the bootstrap interrupt handler at startup. There are four known ways to do this:

  • You can physically remove the DOC from the system before starting it. The problem with this choice is that your only way to reprogram the DOC thereafter, if you do not have access to a hardware DOC programer, is to insert the DOC after the system has been started. In other words, you would have to connect the DOC to a live electronic circuit. Needless to say, neither the DOC nor the electronic circuits interfacing with it have been designed for this sort of manipulation. Also, I neither encourage you to try this nor take any responsibility if you are crazy enough to do it. However, a few courageous people on the MTD mailing list have reported that they successfully inserted their DOC in a running system in this way to reprogram it.

  • If jumpers are available for configuring the address region to which the DOC device is mapped, you can try removing the jumpers completely and starting the system. In some cases, such as when using the ISA DOC evaluation board provided by M-Systems, this will result in the BIOS not recognizing the IPL and, hence, not running it. In other cases, however, this may result in a system hang. If this trick works for you, you will be able to boot the system using the BIOS’s configuration. However, to access the DOC again once the system is running, you will have to insert the jumper while the system is powered on. Again, though this is reported to work, the hardware was not designed for this, I don’t encourage you to do it, and I take no responsibility whatsoever for any possible outcome.

  • The configuration of GRUB allows it to use the ROM BASIC interrupt, INT 18h, instead of the bootstrap interrupt. Lately, in addition to being the ROM BASIC interrupt, INT 18h is sometimes used for network boot. When configured to use this interrupt, GRUB would kick in only if the BIOS configuration is set to network boot or if there are no boot devices set in the BIOS. This approach has a few drawbacks. First, it requires changing the BIOS configuration every time you want to switch from booting from the DOC to booting from a hard disk. This can be time-consuming during development. In addition, the use of INT 18h by recent BIOSes is not standardized, as the case of the BIOSes using it to provide network boot demonstrates.

  • Having seen the above choices while writing this book, your author decided to find a “cleaner” way of doing things. Hence, I set out digging in some of my old DOS and BIOS hacking books and came up with a solution that’s both elegant and simple. Basically, instead of replacing the default bootstrap interrupt handler outright, my modified GRUB SPL makes a copy of the original handler, replaces it with the GRUB bootstrap handler, and lets the BIOS continue looking for other extensions in the system. When GRUB’s bootstrap handler is invoked, it then checks whether the user is holding down the Ctrl key. If so, the original bootstrap handler is restored, and the BIOS is left to continue the bootstrap using the boot configuration chosen by the user. If the Ctrl key isn’t held down, GRUB continues its normal procedure to load whatever is on the DOC. As you can see, this solution does not involve any dangerous hardware manipulations; save, maybe, for people suffering from carpal tunnel syndrome.

For obvious reasons, I strongly encourage you to use the last solution. This enhancement is, however, fairly recent at the time of this writing and you will only find it starting with GRUB patch grub-2002-10-08-doc.patch, which is available in the MTD CVS. I will explain how this option is enabled during GRUB’s configuration in the next section.

Having covered the dangers of using GRUB to boot from a DOC, let’s discuss the building, installation, and use of GRUB with a DOC.

Configuring and Building GRUB for the DOC

As I said earlier, you will need an x86 host to build GRUB. The following instructions assume that you are using such an x86 host. GRUB will fail to build or will create unusable binaries on any other type of host.

To start, download GRUB into your ${PRJROOT}/bootldr directory and extract it there. Then copy the GRUB patch from the ${PRJROOT}/sysapps/mtd/patches directory to the GRUB directory in ${PRJROOT}/bootldr. In the case of my DAQ module, for example, I used GRUB 0.92 and the grub-2002-02-19-doc.patch patch. Now apply the patch to GRUB:

$ cd ${PRJROOT}/bootldr/grub-0.92
$ patch -p0 < grub-2002-02-19-doc.patch

Because this patch was originally meant for GRUB 0.90, there were some warnings and one failure when applying it to 0.92. The failure in this case was in ChangeLog and can therefore be ignored.

If you want to use the Ctrl key method discussed in the previous section to avoid having to hotplug your DOC, use the grub-2002-10-08-doc.patch patch or a later version against a GRUB version retrieved from the CVS repository. Because the CVS repository is constantly changing, however, this patch may not apply cleanly to the latest CVS contents. To get the patch to apply as cleanly as possible and have the resulting source tree compile, for example, I had to retrieve the GRUB sources from the CVS repository as they were on October 10, 2002 and then manually edit a couple of files in the source code. To retrieve the code as it was on the date I mentioned, I used the following command:

$ cvs -z3 -d:pserver:[email protected]:/cvsroot/grub 
               > co -D"10/10/02" grub

With the code patched, you are ready to build GRUB. First, create the Makefile using the automake tools:

$ aclocal && automake && autoconf

Now, configure GRUB to build for the DOC:

$ ./configure --enable-diskonchip-2000 
               > --enable-diskonchip-ctrlbypass 
               > --enable-ext2fs 
               > --disable-ffs --disable-xfs --disable-jfs --disable-vstafs 
               > --disable-reiserfs --disable-minix --disable-fat

This command line disables GRUB’s support for all filesystems except ext2 and enables support for the DOC 2000 device. It also enables the Ctrl key bypass method I described in the previous section using the - -enable-diskonchip-ctrlbypass option. There are a few other configuration options relevant to the DOC. If you are using DOC Millennium, for example, you may want to use the - -enable-diskonchip-mil256 or - -enable-diskonchip-mil512 option, depending on whether your DOC Millennium is using 256- or 512-byte page sizes. You can also use the - -enable-diskonchip-biosnetboot option to boot GRUB on the network boot interrupt instead of the bootstrap interrupt as described earlier. For a complete description of the options available for configuring GRUB for the DOC, have a look at the README_DiskOnChip created in the GRUB package directory when the DOC patch was applied earlier.

Once the configuration is done, you can build GRUB:

$ make

Once the compilation is done, the stage1/grub_firmware file will contain the GRUB image to be written to the DOC. Copy this file to ${PRJROOT}/images/grub_firmware-0.92 for future use:

$ cp stage1/grub_firmware ${PRJROOT}/images/grub_firmware-0.92

Installing GRUB on a DOC

I have already covered the installation of the GRUB bootloader image in Section 7.1.3.5. Follow the instructions given in that section to install the GRUB image created here on your DOC device.

Configuring GRUB to Boot from a DOC

As with LILO, GRUB uses a configuration file to determine the boot media and kernel it has to boot. Unlike LILO, however, you do not need to run the GRUB binary to parse and update its configuration. Instead, the GRUB configuration file, menu.lst, is placed as-is in the /boot/grub directory of the target’s root filesystem and is read by GRUB at startup. To configure GRUB to boot from a DOC, this is the file that we must create.

As an example, here is a simple menu.lst file for booting from a DOC device:

timeout 5
default 0
 
title DiskOnChip 2000 Boot
kernel (dc0,0)/boot/bzImage-2.4.18 root=/dev/nftla1
 
title HD Boot
kernel (hd0,0)/boot/bzImage-2.4.18 root=/dev/hda1

This file states that there are two boot possibilities. The first, which is also the default, involves booting kernel /boot/bzImage-2.4.18 from the first partition of the first DOC, dc0. The second involves booting a kernel by the same name as the previous item from the first partition of the first hard disk, hd0. For each configuration, the root= option indicates the device where the booting kernel will find its root filesystem.

This configuration is useful during development, since it allows you to choose between booting from the DOC and from your hard disk. On a production system, you probably want to remove the entry for the hard disk and set the timeout to zero so that booting from the DOC becomes the only possible option.

You can further modify GRUB’s configuration and allow for a number of boot options. Look at GRUB’s manual for a complete description of the configuration file format.

U-Boot

As I said earlier, U-Boot is a richly documented bootloader. The README file included with the package, for example, covers the use of U-Boot extensively. Among other things, it discusses the package’s source code layout, the available build options, U-Boot’s command set, and the typical environment variables used in U-Boot. In the following, I will cover the essential aspects of U-Boot and provide practical examples of its use. An in-depth discussion of U-Boot would, however, require a book of its own. For this reason, I encourage you to print a copy of the README provided with U-Boot and have a look at the other documentation written by the project maintainer.

Compiling and Installing

Start by downloading and extracting the latest version of U-Boot in your ${PRJROOT}/bootldr directory. As of this writing, the latest U-Boot version is 0.2.0. Once extracted, move to the package’s directory:

$ cd ${PRJROOT}/bootldr/u-boot-0.2.0

Before you can build U-Boot, you need to configure it for your target. The package includes a number of preset configurations for quite a few boards. So, a configuration may very well exist for your target already. Look at the README file to see if your board is supported. For each supported board, U-Boot’s Makefile includes a BOARD_NAME _config target, which is used to configure U-Boot’s build for the designated board. The configuration target for the TQM860L board I use for my control module, for example, is TQM860L_config. Once you have determined the proper Makefile target to use, configure U-Boot’s build process:

$ make TQM860L_config

Now, build U-Boot:

$ make CROSS_COMPILE=powerpc-linux-

In addition to generating bootloader images, the build process will compile a few tools to be used on the host for conditioning binary images before downloading them off to the target to a running U-Boot. Table 9-2 lists the files generated during U-Boot’s compilation.

Table 9-2. Files generated during U-Boot’s compilation

Filename

Description

System.map

The symbol map

u-boot

U-Boot in ELF binary format

u-boot.bin

U-Boot raw binary image that can be written to the boot storage device

u-boot.srec

U-Boot image in Motorola’s S-Record format

You can now download the U-Boot image onto your target’s boot storage device using the appropriate procedure. If you already have U-Boot, or one its ancestors (PPCBoot or ARMBoot) installed on your target, you can use the installed copy to update U-Boot to a new version, as we shall see in Section 9.5.10. If you have another bootloader installed, follow the procedures described in that bootloader’s documentation for updating bootloaders. Finally, if you have no bootloader whatsoever installed on your target, you need to use a hardware programming device, such as a flash programmer or a BDM debugger, to copy U-Boot to your target.

Whichever method you use to copy the actual bootloader image to your target, make a copy of the relevant bootloader images to your ${PRJROOT}/images directory. For my control module for example, I copy the images as follows:

$ cp System.map ${PRJROOT}/images/u-boot.System.map-0.2.0
$ cp u-boot.bin ${PRJROOT}/images/u-boot.bin-0.2.0
$ cp u-boot.srec ${PRJROOT}/images/u-boot.srec-0.2.0

If you intend to debug U-Boot itself, copy the ELF binary also:

$ cp u-boot ${PRJROOT}/images/u-boot-0.2.0

Finally, install the host tool generated by the U-Boot build:

$ cp tools/mkimage ${PREFIX}/bin

Booting with U-Boot

Once U-Boot is properly installed on your target, you can boot it while being connected to the target through a serial line and using a terminal emulator to interface with the target. As I said in Chapter 4, not all terminal emulators interact cleanly with all bootloaders. In the case of U-Boot, avoid using minicom for file transfers, since problems may occur during such transfers.

Here is a sample boot output for my control module:

U-Boot 0.2.0 (Jan 27 2003 - 20:20:21)
 
CPU:   XPC860xxZPnnD3 at 80 MHz: 4 kB I-Cache 4 kB D-Cache FEC present
Board: TQM860LDB0A3-T80.201
DRAM:  16 MB
FLASH:  8 MB
In:    serial
Out:   serial
Err:   serial
Net:   SCC ETHERNET, FEC ETHERNET
PCMCIA:   No Card found
Hit any key to stop autoboot:  5

As you can see, U-Boot prints version information and then provides some detail regarding the hardware it is running on. As soon as it boots, a five second timer starts ticking at the last output line. If you do not press a key during those five seconds, U-Boot boots its default configuration. By pressing a key, you get a prompt:

=>

One of the first things you probably want to try is obtaining help from U-Boot:

=> help
askenv  - get environment variables from stdin
autoscr - run script from memory
base    - print or set address offset
bdinfo  - print Board Info structure
bootm   - boot application image from memory
bootp   - boot image via network using BootP/TFTP protocol
bootd   - boot default, i.e., run 'bootcmd'
cmp     - memory compare
coninfo - print console devices and informations
cp      - memory copy
crc32   - checksum calculation
date    - get/set/reset date & time
dhcp    - invoke DHCP client to obtain IP/boot params
diskboot- boot from IDE device
echo    - echo args to console
erase   - erase FLASH memory
flinfo  - print FLASH memory information
go      - start application at address 'addr'
help    - print online help
ide     - IDE sub-system
iminfo  - print header information for application image
loadb   - load binary file over serial line (kermit mode)
loads   - load S-Record file over serial line
loop    - infinite loop on address range
md      - memory display
mm      - memory modify (auto-incrementing)
mtest   - simple RAM test
mw      - memory write (fill)
nm      - memory modify (constant address)
printenv- print environment variables
protect - enable or disable FLASH write protection
rarpboot- boot image via network using RARP/TFTP protocol
reset   - Perform RESET of the CPU
run     - run commands in an environment variable
saveenv - save environment variables to persistent storage
setenv  - set environment variables
sleep   - delay execution for some time
tftpboot- boot image via network using TFTP protocol
               and env variables ipaddr and serverip
version - print monitor version
?       - alias for 'help'

As you can see, U-Boot has a lot of commands. Fortunately, U-Boot also provides per-command help:

=> help cp
cp [.b, .w, .l] source target count
    - copy memory

When U-Boot appends the [.b, .w, .l] expression to a command, this means that you need to append one of the indicated strings to the command to invoke the desired version of the command. In the case of cp, for example, there are three versions, cp.b, cp.w, and cp.l, for copying bytes, words, and longs, respectively.

U-Boot is strict in its argument parsing. It expects most values to be provided in hexadecimal form. In the case of the cp command, for example, this means that the source address, the target address, and the byte count must be provided in hexadecimal values. You don’t need to prepend or append those values with any sort of special characters, such as “0x” or “h”. If your source address is 0x40000000, for example, simply type 40000000.

U-Boot accepts any unique subset of characters that starts a command name. If you want to use the erase command, for example, you can type just the first three letters, era, since erase is the only command to start with those three first letters. On the other hand, you can’t type lo and expect U-Boot to understand it, since there are three commands that start with those letters: loadb, loads, and loop.

Using U-Boot’s Environment Variables

Once U-Boot is up and running, you can configure it by setting the appropriate environment variables. The use of U-Boot environment variables is very similar to the use of environment variables in Unix shells, such as bash. To view the current values of the environment variables on your target, use the printenv command. Here is a subset of the environment variables found on my control module:

=> printenv
bootdelay=5
baudrate=115200
loads_echo=1
serial#=...
ethaddr=00:D0:93:00:05:E3
netmask=255.255.255.0
ipaddr=192.168.172.10
serverip=192.168.172.50
clocks_in_mhz=1
stdin=serial
stdout=serial
stderr=serial
 
Environment size: 791/16380 bytes

Each environment variable has a different meaning. Some environment variables, such as bootdelay, serial#, or ipaddr, have predetermined uses that are recognized by U-Boot itself. See the README file for a complete discussion of U-Boot’s environment variables and their meanings.

As with Unix shells, you can add environment variables in U-Boot. To do so, you must use the setenv command. Here is an example session where I add a few environment variables to my control module (the third command must be entered as a single line, even though it appears broken on the page):

=> setenv rootpath /home/karim/ctrl-rootfs
=> setenv kernel_addr 40100000
=> setenv nfscmd setenv bootargs root=/dev/nfs rw nfsroot=$(serverip):$(rootpath)
   ip=$(ipaddr):$(serverip):$(gatewayip):$(netmask):$(hostname)::off panic=1;
   bootm $(kernel_addr)
=> setenv bootcmd run nfscmd

In this case, I set U-Boot to boot from the kernel found at 0x40100000 and to mount its root filesystem using NFS. Notice that I used the character to tell U-Boot that the character following should not be interpreted as a special character. This is how the nfscmd looks like, for example, after U-Boot has read it:

=> printenv nfscmd
nfs2cmd=setenv bootargs root=/dev/nfs rw nfsroot=$(serverip):$(rootpath) 
ip=$(ipaddr):$(serverip):$(gatewayip):$(netmask):$(hostname)::off panic=1;bootm 
$(kernel_addr)

The setenv command adds the environment variables to the current session only. Hence, if you reset the system, any environment variable you set only with setenv will be lost. For the environment variables to survive reboots, they must be saved to flash. This is done using the saveenv command:

=> saveenv
Saving Enviroment to Flash...
Un-Protected 1 sectors
Erasing Flash...
. done
Erased 1 sectors
Writing to Flash... done
Protected 1 sectors

Be careful when using saveenv, since it will save all the environment variables currently defined, even those you may have been using temporarily. Before using saveenv, use printenv to take a look at the currently defined environment variables to make sure you are saving only the necessary variables. If you want to delete a variable, simply use setenv on the variable without providing any values. Here’s an example:

=> setenv RAMDisk_addr 40500000
=> printenv RAMDisk_addr
RAMDisk_addr=40500000 
=> setenv RAMDisk_addr
=> printenv RAMDisk_addr
## Error: "RAMDisk_addr" not defined

Note that the = character is not treated as a special character by setenv. In fact, it is seen as another character in the string making up the environment variable, as we saw earlier in this section. The following command, for example, is flawed (notice the extra = displayed by printenv in comparison to the same printenv shown in the previous capture):

=> setenv RAMDisk_addr = 40500000
=> printenv RAMDisk_addr
RAMDisk_addr=  = 40500000

Creating Boot Scripts

U-Boot environment variables can be used to create boot scripts. Such boot scripts are actually environment variables containing a set of U-Boot command sequences. By using a combination of the run command and the ; (semicolon) operator, you can make U-Boot run boot scripts. The environment variables I set in the previous section, for instance, are actually part of a boot script, nfscmd.

The key to the way the script I provided in the previous section works is the bootcmd environment variable. This variable is recognized by U-Boot as the script to run automatically when the system is booted. I set this variable as run nfscmd. In other words, U-Boot should run the nfscmd script to boot the system. In turn, this environment variable is a set of commands of its own. First, it sets the bootargs environment variable, which U-Boot passes to the kernel as its boot parameters, and then uses the bootm command to boot the kernel located at $(kernel_addr). The semicolon separates commands. The use of the $( VAR_NAME) operator tells U-Boot to replace the entire string with the value of the VAR_NAME environment variable. Hence, when nfscmd runs, $(kernel_addr) is replaced by 40100000, which is the value I set earlier. In the same way, $(rootpath) is replaced by /home/karim/ctrl-rootfs, and the rest of the environment variables included in nfscmd are replaced by their respective values.

Though it would have been possible to set bootcmd to contain the entire boot script instead of using run nfscmd, it would have been much harder then to specify alternative boot scripts at the boot command line. By using the run command in the bootcmd script, multiple boot scripts can coexist within U-Boot’s environment variables. You can then change the system’s default boot using:

=> setenv bootcmd run 
               OTHER_BOOT_SCRIPT

Or you can run boot scripts directly from the command line without changing the value of the bootcmd environment variable:

=> run 
               OTHER_BOOT_SCRIPT

Scripts are a very useful feature of U-Boot and you should use them whenever you need to automate a certain task in U-Boot.

Preparing Binary Images

Since the raw flash is not structured like a filesystem and does not contain any sort of file headers, binary images downloaded to the target must carry headers for U-Boot to recognize their content and understand how to load them. The mkimage utility we installed earlier was packaged with U-Boot for this purpose. It adds the information U-Boot needs to binary images while attaching a checksum for verification purposes.

Tip

While the use of image headers is not a technical requirement for a bootloader, such headers are very convenient both during development and in the field. Hence, their use by U-Boot.

To see the typical use of mkimage, type the command without any parameters:

$ mkimage
Usage: mkimage -l image
          -l =  => list image header information
       mkimage -A arch -O os -T type -C comp -a addr -e ep -n name
       -d data_file[:data_file...] image
          -A =  => set architecture to 'arch'
          -O =  => set operating system to 'os'
          -T =  => set image type to 'type'
          -C =  => set compression type 'comp'
          -a =  => set load address to 'addr' (hex)
          -e =  => set entry point to 'ep' (hex)
          -n =  => set image name to 'name'
          -d =  => use image data from 'datafile'
          -x =  => set XIP (execute in place)

For example here is how I create a U-Boot image of the 2.4.18 kernel I compiled for my control module:

$ cd ${PRJROOT}/images
$ mkimage -n '2.4.18 Control Module' 
               > -A ppc -O linux -T kernel -C gzip -a 00000000 -e 00000000 
               > -d vmlinux-2.4.18.gz vmlinux-2.4.18.img
Image Name:   2.4.18 Control Module
Created:      Wed Feb  5 14:19:08 2003
Image Type:   PowerPC Linux Kernel Image (gzip compressed)
Data Size:    530790 Bytes = 518.35 kB = 0.51 MB
Load Address: 0x00000000
Entry Point:  0x00000000

The command takes quite a few flags, but their meaning is easily understood by looking at the usage message provided by mkimage. Note that the name of the image, provided in the -n option, cannot be more than 32 characters. Any excess characters will be ignored by mkimage. The rest of the command line tells mkimage that this is a gzipped PPC Linux kernel image that should be loaded at address 0x00000000 and started from that same address. The image being provided in input is vmlinux-2.4.18.gz and the U-Boot-formatted image will be output to vmlinux-2.4.18.img.

RAM disk images can be processed in a similar fashion:

$ mkimage -n 'RAM disk' 
               > -A ppc -O linux -T ramdisk -C gzip 
               > -d initrd.bin initrd.boot
Image Name:   RAM disk
Created:      Wed Feb  5 14:20:35 2003
Image Type:   PowerPC Linux RAMDisk Image (gzip compressed)
Data Size:    470488 Bytes = 459.46 kB = 0.45 MB
Load Address: 0x00000000
Entry Point:  0x00000000

In this case, the number of parameters is shorter, since we don’t need to specify start and load addresses. Note that the image type has changed to ramdisk.

We can also create a multi-type image that combines both the kernel image and a RAM disk. In that case, the files included are listed sequentially using a colon separator:

$ mkimage -n '2.4.18 Ctrl and Initrd' 
               > -A ppc -O linux -T multi -C gzip -a 00000000 -e 00000000 
               > -d vmlinux-2.4.18.gz:initrd.bin 
               > vmlinux-2.4.18-initrd.img
Image Name:   2.4.18 Ctrl and Initrd
Created:      Wed Feb  5 14:23:29 2003
Image Type:   PowerPC Linux Multi-File Image (gzip compressed)
Data Size:    1001292 Bytes = 977.82 kB = 0.95 MB
Load Address: 0x00000000
Entry Point:  0x00000000
Contents:
   Image 0:   530790 Bytes =  518 kB = 0 MB
   Image 1:   470488 Bytes =  459 kB = 0 MB

Once you have prepared an image with mkimage, it is ready to be used by U-Boot and can be downloaded to the target. As we’ll see below, U-Boot can receive binary images in a number of different ways. One way is to use images formatted in Motorola’s S-Record format. If you intend to use this format, you need to further process the images generated by mkimage by converting them to the S-Record format. Here is an example conversion of the multi-type image generated above:

$ powerpc-linux-objcopy -I binary -O srec 
               > vmlinux-2.4.18-initrd.img vmlinux-2.4.18-initrd.srec

Booting Using BOOTP/DHCP, TFTP, and NFS

If you have properly configured a server to provide the target with DHCP, TFTP, and NFS services, as I explained earlier, you can boot your target remotely. Back from U-Boot’s prompt on my control module, here is how I boot my target remotely, for example:

=> bootp
BOOTP broadcast 1
DHCP client bound to address 192.168.172.10
ARP broadcast 1
TFTP from server 192.168.172.50; our IP address is 192.168.172.10
Filename '/home/karim/vmlinux-2.4.18.img'.
Load address: 0x100000
Loading: ################################################### ...
done
Bytes transferred = 530854 (819a6 hex)

The bootp command issues a request that is answered by the DHCP server. Using the DHCP server’s answer, U-Boot contacts the TFTP server and obtains the vmlinux-2.4.18.img image file, which it places at address 0x00100000 in RAM. You can verify the image’s header information using the iminfo command:

=> imi 00100000
 
## Checking Image at 00100000 ...
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK

As you can see, the information printed out by iminfo on the target is very similar to that printed out on the host by mkinfo. The OK string reported for the checksum means that the image has been downloaded properly and that we can boot it:

=> bootm 00100000
## Booting image at 00100000 ...
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK
Linux version 2.4.18 (karim@Teotihuacan) (gcc version 2.95.3 20010315 ...
On node 0 totalpages: 4096
zone(0): 4096 pages.
zone(1): 0 pages.
zone(2): 0 pages.
Kernel command line: root=/dev/nfs rw nfsroot= ...
Decrementer Frequency: 5000000
Calibrating delay loop... 79.66 BogoMIPS
...
VFS: Cannot open root device "" or 02:00
Please append a correct "root=" boot option
Kernel panic: VFS: Unable to mount root fs on 02:00
 <0>Rebooting in 180 seconds..

In this case, the kernel panics because it is unable to find any root filesystem. To solve this problem, we must use the environment variables to create a boot script for passing appropriate boot options to the kernel. The following commands create a new boot script, bootpnfs, and modify the special bootcmd script, as we did in Section 9.5.3, in order for the system to boot using BOOTP/DHCP, TFTP, and NFS:

=> setenv bootpnfs bootp; setenv kernel_addr 00100000; run nfscmd
=> printenv bootpnfs
bootpnfs=bootp; setenv kernel_addr 00100000; run nfscmd
=> setenv bootcmd run bootpnfs
=> printenv bootcmd
bootcmd=run bootpnfs

In this case, the bootpnfs script automatically executes the bootp instruction we used earlier in this section to obtain a kernel from the TFTP server. It then uses the nfscmd script we created in Section 9.5.3 to boot this kernel. The value of kernel_addr is changed so that the nfscmd script would use the kernel loaded using TFTP, not the one located at 40100000.

If you use the boot command now, U-Boot will boot entirely from the network. It will download the kernel through TFTP and mount its root filesystem on NFS. If you would like to save the environment variables we just set, use the saveenv command before rebooting the system, otherwise, you will have set the same variables again at the next reboot.

Downloading Binary Images to Flash

Booting from the network is fine for early development and testing. For production use, the target must have its kernel stored in flash. As we will see shortly, there a few ways to copy a kernel from the host to the target and store it to flash. Before you can copy any kernel image, however, you must first choose a flash region to store it and erase the flash region for the incoming kernel. In the case of my control module, I store the default kernel between 0x40100000 and 0x401FFFFF. Hence, from U-Boot’s prompt, I erase this region:

=> erase 40100000 401FFFFF
Erase Flash from 0x40100000 to 0x401fffff
........ done
Erased 8 sectors

The simplest way to install a kernel in the target’s flash is to first download it into RAM and then copy it to the flash. You can use the tftpboot command to download a kernel from the host to RAM:

=> tftpboot 00100000 /home/karim/vmlinux-2.4.18.img
ARP broadcast 1
TFTP from server 192.168.172.50; our IP address is 192.168.172.10
Filename '/home/karim/vmlinux-2.4.18.img'.
Load address: 0x100000
Loading: ################################################### ...
done
Bytes transferred = 530854 (819a6 hex)

When tftpboot is run, it adds the filesize environment variable to the existing environment variables and sets it to the size of the file downloaded:

=> printenv filesize
filesize=819a6

You can use this environment variable in subsequent commands to avoid typing in the file size by hand. Don’t forget to erase this environment variable before saving the environment variables, or it, too, will be saved.

In addition to tftpboot, you can use the loadb command to download images to the target:

=> loadb 00100000
## Ready for binary (kermit) download ...

At this point, U-Boot suspends and you must use the terminal emulator on the host to send the image file to the target. In this case, U-Boot expects to download the data according to the kermit binary protocol, and you must therefore use kermit to download a binary image to U-Boot. Once the transfer is done, U-Boot will output:

## Total Size      = 0x000819a6 = 530854 Bytes
## Start Addr      = 0x00100000

Here, too, U-Boot will set the filesize environment variable to the size of the file downloaded. As we did earlier, you may want to use the iminfo command to verify that the image has been properly downloaded.

Once the image is in RAM, you can copy it to flash:

=> cp.b 00100000 40100000 $(filesize)
Copy to Flash... done
=> imi 40100000
 
## Checking Image at 40100000 ...
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK

Alternatively, instead of downloading the image to RAM first using tfptboot or loadb and then writing it to flash, you can download the image directly to flash using loads. In this case, the host sends the image to the target in S-Record format. In comparison to the two previous methods, however, downloading an S-Record file is extremely slow. In most cases, it is preferable to use tftpboot or loadb instead.[6]

To download S-Record files, you will need to use the cu terminal emulator to transfer them to the target, because the other terminal emulators don’t interact properly with U-Boot when downloading this sort of file. When connected through cu, use the following commands:

=> loads 40100000
## Ready for S-Record download ...
~>vmlinux-2.4.18.srec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...
               ...
               ...176 33177 33178 33179 33180 33181
[file transfer complete]
[connected]
 
## First Load Addr = 0x40100000
## Last  Load Addr = 0x401819A5
## Total Size      = 0x000819A6 = 530854 Bytes
## Start Addr      = 0x00000000

The ~> string shown here is actually part of the input you have to type. It is actually the cu command used to initiate a file download.

As before, you can verify the image once it’s in memory:

=> imi 40100000
 
## Checking Image at 40100000 ...
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK

Every time you want to load a new image to flash, you have to start back at the erase command shown in the beginning of this section.

Booting Using a RAM Disk

The first step in booting from a RAM disk is to download the RAM disk from the host and install it on the target’s flash. Many of the commands are the same as those shown and explained in previous sections. Here is how I do this for my control module:

=> tftpboot 00100000 /home/karim/initrd.boot
ARP broadcast 1
TFTP from server 192.168.172.50; our IP address is 192.168.172.10
Filename '/home/karim/initrd.boot'.
Load address: 0x100000
Loading: ################################################### ...
done
Bytes transferred = 470552 (72e18 hex)
=> imi 00100000
 
## Checking Image at 00100000 ...
   Image Name:   RAM disk
   Created:      2003-02-05  19:20:35 UTC
   Image Type:   PowerPC Linux RAMDisk Image (gzip compressed)
   Data Size:    470488 Bytes = 459.5 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
=> printenv filesize
filesize=72e18
=> imi 40200000
 
## Checking Image at 40200000 ...
   Bad Magic Number
=> erase 40200000 402FFFFF
Erase Flash from 0x40200000 to 0x402fffff
........ done
Erased 8 sectors
=> cp.b 00100000 40200000 $(filesize)
Copy to Flash... done
=> imi 40200000
 
## Checking Image at 40200000 ...
   Image Name:   RAM disk
   Created:      2003-02-05  19:20:35 UTC
   Image Type:   PowerPC Linux RAMDisk Image (gzip compressed)
   Data Size:    470488 Bytes = 459.5 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK

Since I had already installed a kernel, I can boot the kernel available in flash with the RAM disk I just installed:

=> bootm 40100000 40200000
## Booting image at 40100000 ...
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK
## Loading RAMDisk Image at 40200000 ...
   Image Name:   RAM disk
   Created:      2003-02-05  19:20:35 UTC
   Image Type:   PowerPC Linux RAMDisk Image (gzip compressed)
   Data Size:    470488 Bytes = 459.5 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   Loading Ramdisk to 00f2c000, end 00f9edd8 ... OK
Linux version 2.4.18 (karim@Teotihuacan) (gcc version 2.95.3 20010 ...
On node 0 totalpages: 4096
zone(0): 4096 pages.
zone(1): 0 pages.
zone(2): 0 pages.
Kernel command line:
Decrementer Frequency: 5000000
Calibrating delay loop... 79.66 BogoMIPS
...
RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize
...
RAMDISK: Compressed image found at block 0
...
VFS: Mounted root (ext2 filesystem).
...

Here, too, we can use environment variables to automate the booting process. Also, instead of using separate images for the kernel and the RAM disk, we could use a single image containing both, such as the one we created in Section 9.5.5. As I said earlier, U-Boot is a very flexible bootloader with many possible configurations. Though we cannot hope to cover all its possibilities here, feel free to experiment with U-Boot to obtain the setup that suits you best.

Booting from CompactFlash Devices

Before booting a kernel from a CF card using U-Boot, you need to properly partition and populate the CF card. Use pdisk or fdisk to partition the CF device, depending on your host. Since U-Boot does not recognize any disk filesystem, you will need to create a few small partitions to hold raw binary images and one large partition to hold your root filesystem, as I explained in Chapter 7.

For my control module, for example, I used a 32 MB CF card on which I created three partitions using fdisk: two 2 MB partitions to hold one stable kernel and one experimental kernel, and one 30 MB partition to hold my root filesystem. To copy the kernels to their respective partitions, I used the dd command:

# dd if=vmlinux-2.4.18.img of=/dev/sda1
1036+1 records in
1036+1 records out
# dd if=vmlinux-2.4.18-preempt.img of=/dev/sda2
1040+1 records in
1040+1 records out

I formatted /dev/sda3 using mke2fs, mounted it on /mnt/cf, and copied the root filesystem to it using the techniques described in the previous chapter.

After I inserted the CF card in the PCMCIA port using a CF-to-PCMCIA adapter, here was the output of U-Boot at startup:

U-Boot 0.2.0 (Jan 27 2003 - 20:20:21)
 
CPU:   XPC860xxZPnnD3 at 80 MHz: 4 kB I-Cache 4 kB D-Cache FEC present
Board: TQM860LDB0A3-T80.201
DRAM:  16 MB
FLASH:  8 MB
In:    serial
Out:   serial
Err:   serial
Net:   SCC ETHERNET, FEC ETHERNET
PCMCIA: 3.3V card found: SunDisk SDP 5/3 0.6
            Fixed Disk Card
            IDE interface
            [silicon] [unique] [single] [sleep] [standby] [idle] [low power]
Bus 0: OK
  Device 0: Model: SanDisk SDCFB-32 Firm: vde 1.10 Ser#: 163194D0310
            Type: Removable Hard Disk
            Capacity: 30.6 MB = 0.0 GB (62720 x 512)
Hit any key to stop autoboot:  5

U-Boot identifies the storage device at startup. U-Boot provides a wide range of ide commands for manipulating IDE storage devices. You can see these commands by typing the help command:

=> help ide
ide reset - reset IDE controller
ide info  - show available IDE devices
ide device [dev] - show or set current device
ide part [dev] - print partition table of one or all IDE devices
ide read  addr blk# cnt
ide write addr blk# cnt - read/write `cnt' blocks starting at block `blk#'
    to/from memory address `addr'

We can further use U-Boot’s command line to get more information regarding the device:

=> ide part
 
Partition Map for IDE device 0  --   Partition Type: DOS
 
Partition     Start Sector     Num Sectors     Type
    1                   62            4154      83
    2                 4216            4154      83
    3                 8370           54312      83

This command reads the partition table of the CF device and prints it out. In this case, the partition printed out by U-Boot fits the description provided earlier.

Loading a kernel image from one of the partitions on the CF device is done using the diskboot command. This command takes two arguments: the address where the kernel is to be loaded and a partition identifier. The latter is a concatenation of the device number and the partition number on that device separated by a colon. This is how I load the kernel image found on partition 1 of device 0 to address 0x00400000:

=> diskboot 00400000 0:1
 
Loading from IDE device 0, partition 1: Name: hda1
  Type: U-Boot
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000 
=> imi 00400000
 
## Checking Image at 00400000 ...
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK

Once the kernel is loaded, you can use the bootm command to boot that kernel. This can also be automated by setting the autostart environment variable to yes. In that case, diskboot will automatically boot the kernel it loads:

=> setenv autostart yes
=> disk 00400000 0:1

Loading from IDE device 0, partition 1: Name: hda1
  Type: U-Boot
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000
Automatic boot of image at addr 0x00400000 ...
## Booting image at 00400000 ...
   Image Name:   2.4.18 Control Module
   Created:      2003-02-05  19:19:08 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    530790 Bytes = 518.3 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK
Linux version 2.4.18 (karim@Teotihuacan) (gcc version 2.95.3 ...
On node 0 totalpages: 4096
...

As we did in Section 9.5.3 and Section 9.5.6, you can script the bootup from the CF device by setting the appropriate U-Boot environment variables. Also, if you wish, you can write to the disk directly from U-Boot using the ide write command. Have a look at the help output and the documentation for more information regarding the use of U-Boot’s IDE capabilities.

Updating U-Boot

U-Boot is like any other open source project; it continues to evolve over time as contributions are made and bug fixes are integrated to the codebase. Consequently, you may feel the need to update your target’s firmware version. Fortunately, because U-Boot runs from RAM, it can be used to update itself. Essentially, we have to download a new version to the target, erase the old firmware version, and copy the new version over it.

Warning

There are obvious dangers to this operation, because a mistake or a power failure will render the target unbootable. Hence, utmost caution must be used when carrying out the following steps. Make sure you have a copy of the original bootloader you are about to replace so that you can at least fall back to a known working version. Also, seriously consider avoiding the replacement of your firmware if you have no hardware means to reprogram the target’s flash if the upgrade fails. If you do not have access to a BDM debugger or a flash programmer, for example, there is a great risk that you will be left with a broken system if one of the following steps fails. Dealing with buggy software is one thing; ending up with unusable hardware is another.

Once you have taken the necessary precautions, download the U-Boot image into RAM using TFTP:

=> tftp 00100000 /home/karim/u-boot.bin-0.2.0
ARP broadcast 1
TFTP from server 192.168.172.50; our IP address is 192.168.172.10
Filename '/home/karim/u-boot.bin-0.2.0'.
Load address: 0x100000
Loading: #################################
done
Bytes transferred = 166532 (28a84 hex)

If you do not have a TFTP server set up, you can also use the terminal emulator to send the image:

=> loadb 00100000
## Ready for binary (kermit) download ... 

## Start Addr      = 0x00100000

Unlike other images we have downloaded to the target, you cannot use the imi command to check the image, since the U-Boot image downloaded was not packaged on the host using the mkimage command. You can, however, use crc32 before and after copying the image to flash to verify proper copying.

Now, unprotect the flash region where U-Boot is located so you can erase it (in this case, U-Boot occupies the flash region from 0x40000000 to 0x4003FFFF):

=> protect off 40000000 4003FFFF
Un-Protected 5 sectors

Erase the previous bootloader image:

=> erase 40000000 4003FFFF
Erase Flash from 0x40000000 to 0x4003ffff
... done
Erased 5 sectors

Copy the new bootloader to its final destination:

=> cp.b 00100000 40000000 $(filesize)
Copy to Flash... done

Erase the filesize environment variable set during the download:

=> setenv filesize

Save the environment variables:

=> saveenv
Saving Enviroment to Flash...
Un-Protected 1 sectors
Erasing Flash...
. done
Erased 1 sectors
Writing to Flash... done
Protected 1 sectors

At this stage, the new bootloader image has been installed and is ready to be used. Until you issue the reset command, however, you can still use the old U-Boot currently running to fix any problems that may have occurred during the update. Once you are satisfied that every step of the update has gone through cleanly, you can go ahead and restart the system:

=> reset
 
 
U-Boot 0.2.0 (Jan 27 2003 - 20:20:21)
 
CPU:   XPC860xxZPnnD3 at 80 MHz: 4 kB I-Cache 4 kB D-Cache FEC present
Board: TQM860LDB0A3-T80.201
DRAM:  16 MB
FLASH:  8 MB
In:    serial
Out:   serial
Err:   serial
Net:   SCC ETHERNET, FEC ETHERNET
PCMCIA:   No Card found
Hit any key to stop autoboot:  5

If you can see the U-Boot boot message again, U-Boot has been successfully updated. Otherwise, there has been a problem with the replacement of the firmware and you need to reprogram the flash device using the appropriate hardware tools.

Sometimes, kernel images that used to boot with the older bootloader version will fail to boot with newer versions. When upgrading from a PPCBoot version prior to 1.0.5 to Version 1.0.5 or later, for example, kernels prior to 2.4.5-pre5 may fail to boot. In that case, the reason behind the problem is in the way U-Boot passes the clock speed to the kernel. Prior to kernel 2.4.5-pre5, kernels expected to receive the speed in MHz, while later kernels expect to receive the speed in Hz. To this end, PPCBoot 1.0.5 passes the clock speed to kernels in Hz. Kernels that expect to receive it in MHz, however, fail to boot. In practice, the boot process will start as it would normally, but the system will freeze right after U-Boot finishes uncompressing the images for startup. You will, therefore, see something like:

               ...
   Entry Point:  00000000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK

Nothing will be output after that, and there will be no responses to any input from the terminal. To solve the problem, you need to tell the newer version of U-Boot to keep passing the clock speed in MHz to the older kernels. This is done by setting the clocks_in_mhz environment variable to 1:

=> setenv clocks_in_mhz 1
=> saveenv

Though this sort of problem does not occur for every upgrade, changes in the kernel sometimes require significant changes to the tools that interface with it. Given that such problems are difficult to figure out if you are not involved in the actual development of each project, I strongly encourage you to keep in touch with the rest of the U-Boot users by subscribing to the U-Boot users mailing list from the project’s web site and to read announcements of new versions carefully.



[1] See LART description in Appendix B.

[2] The ELDK is an open source development and target distribution.

[3] RedBoot is part of the eCos source code tree and requires some of the code provided by that OS to provide its own services. Hence, RedBoot’s development is tied to, but not entirely dependent on, eCos’ development. Some platforms supported by RedBoot, for example, aren’t supported by eCos.

[4] For the example to fit in the printed page’s width, I avoid using the complete /home/karim/control-project/control-module/... path. Use the actual complete path for your own development.

[5] Normally, you shouldn’t need to append a root= option to the kernel’s boot parameters if you already have a root line in your image description. In this case, however, the software involved takes for granted that disks cannot change types, and fails to configure the boot process properly without the double declaration.

[6] The loadb command and, by default, the tftpboot command can’t be used to download directly to flash. Though U-Boot can be configured at compile time to allow direct flash download using tftpboot, direct flash download using loadb is not supported.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.8.127