Boot Linux from a FireWire Device
By Martyn Honeyford2005-04-12
Building the Boot Image
There are two methods we can use to boot:
• One-phase boot
The kernel boots, mounts the root filesystem, and continues initialization by calling the initialization scripts (usually /sbin/init)
• Two-phase (initrd) boot
The kernel boots, mounts an initial ram disk (initrd), performs further customizable initialization, then mounts the root filesystem and continues initialization (again, usually by calling /sbin/init)
Each of these methods has its own advantages and disadvantages.
One-phase boot
In order to use the one-phase boot, we need to build a kernel that has all the drivers needed to mount the root file system built in (any other drivers can be built at modules that can be loaded from the root partition during normal initialization).
If we are attempting to boot from a very small device, such as a floppy disk, the best approach is to build a kernel with just enough drivers built in to allow us to mount the root external file system -- and build everything else as modules. For example, I have the SCSI support, PCMCIA support, IEE1394, SBP, and like support built in, but everything else -- including graphics card support, networking device support, and so on -- is built as modules that are stored on the root partition (on the external drive), rather than on the floppy.
With a simple (one-phase) boot procedure, we should be able to build a kernel with the required support, put it on a floppy drive, install a boot loader on the floppy (I use GRUB, but there are other options, such as LILO), then boot with something similar to this (for GRUB):
root (fd0)
kernel (fd0)/boot/bzImage root=/dev/sda1
This almost works, except for two problems:
1. Because of the fact that SBP support uses SCSI emulation, the emulated SCSI bus need to be "rescanned" in order to detect the disk and allow /dev/sda1 to be mounted. This scanning is performed with a couple of simple commands. Unfortunately, however, using a one-phase boot, we cannot run any commands until the kernel has finished booting, and the kernel cannot finish booting until the root file system is mounted -- a classic Catch-22 situation. Thankfully, there is a patch available for 2.4 kernels that causes the SCSI bus to be scanned on startup (see Resources for more details). By applying this patch, I was able to have the external drive automatically detected by the kernel during bootup with no rescanning commands required. This leads us to the next problem.
2. There is a timing window within the kernel, which means that often, the kernel will proceed to try to mount the root device before it has had a chance to be properly detected or initialized. Again, there is a patch available for this (please see Resources for a link) that simply makes the kernel wait for a short period of time on startup, and retry if it fails to mount the root filesystem (to give the external drive time to be recognized).
By applying these two patches, I was successfully able to build a kernel on a bootable floppy disk, which would boot and then use the external FireWire drive as root.
The main problem with this approach is that it requires us to patch the kernel source -- which is at best a pain (when new kernel versions are released) and at worst, a real problem (if the patches are not maintained to keep in step with the other changes occurring to the kernel).
It may have occurred to you that we can avoid these two problems if our BIOS supports USB or FireWire and we are booting directly. Unfortunately, this is not the case: while this method uses BIOS calls to access the disk during boot up, once the kernel begins to initialize, the BIOS is no longer used, and the disk is accessed using the kernel drivers -- so the same problems are encountered.
Two-phase boot
As of kernel version 2.0.X, an interesting capability was added to the Linux kernel -- the ability to use an "initial RAM disk" (or initrd) to give a two-phase boot process.
In a nutshell, the kernel is booted as normal; but instead of mounting the "real" root file system, a miniature root filesystem is created in RAM, and that is mounted. Any number of arbitrary steps can then be performed in this initial environment before the real root is mounted and we switch to using the real root and destroy the initial RAM disk.
This is useful in all sorts of circumstances, but for our purposes we will simply be using our mini environment to rescan the SCSI bus, wait for the external disk to be recognized, then switch to using this as our real root and continue to boot.
In order to use this method, we need to create two things, a kernel and an initrd image.
The kernel is just a regular kernel which has initrd support built in. The initrd image is a loopback filesystem image which contains our mini-root filesystem (this image can optionally compressed with gzip to reduce its size).
You can check the Resources section for more information on creating or customizing your own initrd image.
Within the initrd image, there is a file called linuxrc. This is the file that is executed when initrd is loaded, so make sure it has execute permission! For our purposes, the linuxrc is very simple:
Listing 1. initrd linuxrc
All we are doing is loading the appropriate modules to support the external drive: they should be uncommented as required. (I built all the required support into my kernel, hence no modules are required.) We then loop, rescanning the SCSI bus (by echoing a command to a special file in the /proc pseudo-filesystem and calling devfsd) until the root device (/dev/sda1 in my case) is present. In my case, the emulated FireWire SCSI bus in question is 1 0 0, but it doesn't hurt to try a few others -- if you know which one you will be using, you can tailor the script. Also, if you have other SCSI devices (or emulated SCSI devices), the drive may have a different letter (for example, /dev/sdb1). And if you are not using the first partition on the external drive, you will need to use a different number (for example, /dev/sda2).
#!/bin/sh
REAL_ROOT=/dev/sda1
# mount the /proc filesystem
mount -t proc none /proc
#for scsi-emulation
# modprobe sd_mod
#for pcmcia
# modprobe pcmcia_core
#for FireWire
# modprobe ieee1394
# modprobe ohci1394
# modprobe raw1394
# modprobe sbp2
#for USB
# modprobe usbcore
# modprobe ohci-hcd
# modprobe uhci-hcd
# modprobe usb-storage
# loop rescanning the scsi bus + rerunning devfsd
retries=5
i=1
until [ -e $REAL_ROOT ]
do
if [ $i -gt $retries ]
then
echo "Unable to mount real root ($REAL_ROOT) - Giving up!"
/bin/ash
exit
fi
echo "Real root ($REAL_ROOT) not found, retrying ($i)"
sleep 1
echo "scsi add-single-device 0 0 0" > /proc/scsi/scsi
echo "scsi add-single-device 1 0 0" > /proc/scsi/scsi
echo "scsi add-single-device 2 0 0" > /proc/scsi/scsi
/bin/devfsd /dev -np
i=$((i+1))
done
#umount /proc as it will be remounted by the normal init process
umount /proc
#now we simply exit, and the normal boot process should continue
exit 0
All we now need to do is copy the relevant files into the initrd image (you can mount the uncompressed image using the mount -o loop command). In particular, we need to ensure that we have the linuxrc file, all of the commands used therein, and any libraries that those commands rely on. The (unmounted) image can then be optionally compressed.
The kernel (bzImage) and the initrd image (initrd.gz) are then copied to the (bootable, ext3) floppy.
The final step is to install a boot loader on the floppy, and boot the kernel with the following options: kernel bzImage root=/dev/sda1 initrd=initrd.gz.
You should now be able to boot using the floppy: it will load the kernel from the floppy, load the initrd image into RAM, wait for the root device to be recognized, and then continue to boot as normal from there. From this point onwards, the floppy can be removed.
If a floppy is not appropriate (for example, if the machine has no floppy drive), then any device that can be booted by your BIOS can be used. Personally, I use a little 32Mb USB key for this purpose. Alternatively, if you don't mind altering your internal hard drive, a small partition can be created there for more convenient booting.
Tutorial Pages:
» Installing Linux on Removable Drives
» Removable Drive Options
» Linux Support
» Installation
» Booting
» Building the Boot Image
» Resources
First published by IBM DeveloperWorks
| Related Tutorials: » How to Install PHP 5 on Linux » How to Install Apache 2 on Linux » How to Install MySQL 5.0 on Linux » SMB Caching » Mound --Bind » Tar Wild Card Interpretation |
