Boot loaders
A boot loader (or bootstrap loader) is a program that loads another program. The boot loader is a small and specialized hunk of code, specific to a target system, that has just enough complexity to find the kernel and load it without being a full-featured kernel. Different systems use a variety of different boot loaders, from the huge and complicated BIOS programs common on desktop PCs, to very small and simple programs more common on embedded systems.
The TS-7800 uses an unusually simple boot loader, which simply picks up
the kernel from a predetermined partition of the SD card or on-board
flash. A jumper determines whether the board looks first at the
on-board flash, or at the SD cards. There are no other configuration
settings. More complicated boot loaders (such as
grub, commonly used on desktop PCs) have
options for configuring kernel options and so on at boot time. On this
system, the kernel's default options must be compiled in, as described
previously.
The decision to compile in kernel options is a typical example of a choice that makes some sense in an embedded system, but would be uncomfortably limiting on a desktop.
There are a variety of formats in which kernels are stored. The initial
Linux kernel binary, named vmlinux, is
rarely the file that a boot loader will work with. On the TS-7800, the
boot loader can use two files, either Image
(uncompressed) or zImage (compressed).
These files are created in the
arch/arm/boot directory within the kernel
tree.
People often describe the zImage kernel as a
compressed kernel, and so expect the boot loader to need to provide
decompression. In fact, it's rather more clever. The
zImage kernel is an uncompressed
executable, which contains a particularly large static data object
which is a compressed kernel image. When the boot loader loads and
runs the zImage executable, that executable
then unpacks the kernel image and executes it. This way, you get most
of the benefit of compression without imposing additional effort on
the boot loader.
The SD card needs an MBR table containing DOS-style partition information. A sample MBR table is available for download from Technologic's site; this is for a 512MB card, but it is easy to edit the fourth partition to a size suiting whatever size card you want to use. The first three partitions are 4MB each; the first is unused on smaller cards, and the second and third hold the kernel and initial ramdisk image respectively.
For my purposes, I used the sfdisk utility,
which is not always recommended but has the desirable trait of
trusting me when I ask it to create a partition that does not align on
a partition boundary.
The kernel for the TS-7800 is dumped directly into the second partition
of the SD card. The exact path to the card depends on how you are
accessing it; typically, if you have the card on a USB card reader, it
will be detected as a SCSI device. Note that there is no file system
access involved; the raw kernel is just dumped into the partition. The
dd command copies raw data from one source
to another, as in this example:
$ dd if=zImage of=/dev/sdd2 bs=16k
93+1 records in
93+1 records out
1536688 bytes (1.5 MB) copied, 0.847047 s, 1.8 MB/s
This command dumps raw data from the zImage
file to the second partition of /dev/sdd,
using 16KB blocks.
The output of this command is a little cryptic (as are its inputs,
honestly). When dd runs, it copies data in
"records," which are by default 512-byte blocks; this command
specifies a block size of 16k (which dd
understands to mean 16*1024).
The dd command reports the amount of data
copied first in blocks; the number after the plus sign is the number
of partial blocks copied. In this case, because 1,536,688 is not an
exact multiple of the block size, the remaining bytes of the file are
read (and written) separately as a partial block. This information is
harmless for most modern devices but crucial to diagnosing problems
with some older ones.
The ability to control block sizes (and reblock data when transferring it) was exceptionally useful for working with tape devices and other specialized media that required writes to be of particular fixed sizes, and also helps for performance and reliability reasons with flash devices.
While the kernel device representing flash media can often take writes of arbitrary sizes, it is common for the underlying device to work only in full blocks, often of somewhat larger sizes (4KB or larger). To do a partial write, the flash device must extract the current contents of the full block, modify them with the input data, and flash the whole block back. If a device uses 4KB blocks, and you write to it in 512-byte blocks, each device block gets rewritten eight times for a single copy. This is bad for the device's longevity, and also bad for performance. (A 512-byte write of the same file was half as fast on the flash card I used.)
Booting the kernel is simple enough. Set the jumper for an SD boot, put the card in the system, and power it up. If you started with a blank card, this produces a predictable result:
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(1,0)
This cryptic message indicates that the kernel has been unable to find its root filesystem. That means it's about time to create one.

