Following on from the previous section, we now have a basic Debian
installation in a partition on our external hard-drive. The next task
is to make it bootable. This requires two basic steps:
-
Install a suitable kernel-image.
-
Install a bootloader onto the mbr (master boot record) of the
external drive.
Regarding the kernel image, I went not with a standard Debian kernel,
but rather with one I compiled myself (but using the Debian
kernel-packaging system). The configuration of a kernel is always
important, and especially so when you're booting the system in an
unusual way such as from a USB drive. The bootloader will be able to
get your kernel from the drive and get it going, but you must make
provision so that the kernel is able to access the root file-system
early in the boot-process. This clearly means that you cannot rely on
the kernel loading modules for usb_storage and co. from the
hard-drive! There are two ways around this problem. One is to
compile all the relevant drivers into the kernel. The alternative is
to compile the drivers as modules, but to use an
initial ram disk, or initrd, to give the kernel access to the
modules before the root file-system on the USB HDD is mounted.
The creation of the mkinitrd file-system is controlled by some config
files contained in the directory /etc/mkinitrd/. You should edit the
file "modules", and add the names of modules required to access the
root-filesystem. The ones I included were:
usbcore
uhci_hcd
ehci_hcd
scsi_mod
sd_mod
usb_storage
ide_generic
ide_cd
Then, editing mkinitrd.conf, you should set the options ROOT and DELAY
as follows:
DELAY=10
ROOT=/dev/sda1
The second of these is clear enough. The DELAY option is necessary so
that the boot process pauses for a few seconds to allow the
usb_storage driver to load correctly, before going on to access the
root file-system. The Debian kernel compilation process (described in
subsequent paragraphs) will create and install an initrd file. You
can create such a file by hand using a command like:
mkinitrd -k -o initrd.img -r /dev/sda1
Here, -k means we want to preserve the working directory after the
image is created. -o simply chooses the output file. -r can be used
to over-ride the ROOT setting in /etc/mkinitrd/mkinitrd.conf.
Compiling a Debian kernel is a very straight-forward process.
First unpack the kernel-sources. You can download these by going to
ftp://ftp.kernel.org/ or your local mirror. I used the
2.6.10 sources for the current example. After unpacking the sources
in the directory /usr/local/src/linux:
tar -xzvf linux-2.6.10.tar.gz # If you downloaded a .gz archive
tar -xjvf linux-2.6.10.tar.bz2 # If you downloaded a .bz2 archive
change directory into the unpacked kernel source and clean them.
cd linux-2.6.10 && make mrproper
Before beginning the compilation, you'll need to configure the
sources. If you've already compiled earlier kernels (e.g. 2.6.9) you
can use the config file from that version as a starting point as
follows (for example):
cp ../linux-2.6.9/.config ./
make oldconfig
# Now you'll be prompted for input just on new/changed options
Alternatively, you can start from scratch. This is easiest to do via
the menuconfig interface:
make menuconfig
If you've never done this before, then it's very likely you'll make a
mistake and leave out something that's important for your system. To
become familiar with the process I reccommend you should configure and
later compile a kernel for your current system that keeps all your
hardware working (in particular make sure it still allows you to mount
external USB storage devices!). You'll also need to include support
for cramfs (CONFIG_CRAMFS=y), and for initial ram disk
(CONFIG_RAMFS=y, CONFIG_BLK_DEV_RAM=y, CONFIG_BLK_DEV_INITRD=y).
You can take a look at
my .config file to get an idea of the setup to use, though of
course you will more than likely want to change many of the options
(e.g. what network card drivers to use, sound-drivers, etc.,).
The actual compilation step follows the following pattern:
fakeroot make-kpkg clean && fakeroot make-kpkg --revision=yourid.1.0 --append-to-version=c --initrd kernel_image
Note that this requires that you have access to two Debian packages,
which you can install using
apt-get install fakeroot kernel-package
In the compilation command, you can use whatever "yourid.1.0" you want
for the version number. It is recommended that it begin with a letter
though. The "append-to-version" part allows you to have multiple
versions of a kernel-version installed. When the configuration
process finishes, you'll have a file in the parent directory (e.g.
/usr/local/src/linux) called
kernel-image-2.6.10c_yourid.1.0_i386.deb
I found that later configuration (e.g. of bootloader and initrds) went
more smoothly if I was running the same kernel version in my parent
system as the version I was attempting to install into the USB HDD.
Installing the kernel-image into your parent system is then simply a
matter of
dpkg -i kernel-image-2.6.10c_yourid.1.0_i386.deb
This should (via the magic of Debian) also create the appropriate
entries in your bootloader menus to boot the kernel. To
actually use the kernel, you must reboot the computer (and
choose the appropriate kernel from the GRUB or LILO
boot-menu).
To install the kernel into the USB-based system, copy the deb file
above into the mounted filesystem, then chroot in and run the commands
as follows (and, as always, making appropriate changes if required):
cp kernel-image-2.6.10c_yourid.1.0_i386.deb /mnt/usb1
chroot /mnt/usb1
mount -t proc proc /proc
dpkg -i kernel-image-2.6.10c_yourid.1.0_i386.deb
Next, you need to configure the boot-loader. I worked with GRUB,
though LILO should also be suitable.
From your parent system, you need to run the command:
grub-install --root-directory=/mnt/usb1 /dev/sda
The GRUB configuration is stored in the /mnt/usb1/boot/grub directory.
Now, there's a small gotcha regarding the GRUB configuration. As
things stand, while running your parent system, the hard-drive you
booted off is more than likely known to GRUB as (hd0), and the USB
drive is then (hd1). However, when your computer BIOS starts to boot
your system off the USB drive, then that drive will become the primary
drive and grub will identify it as (hd0). To reflect this, you should
edit the file /mnt/usb1/boot/grub/device.map and change it to read:
(hd0) /dev/sda
The only other file you may need to edit is
/mnt/usb1/boot/grub/menu.lst.
The stanza that finally worked for me runs as follows:
# Next line is how the kernel will be identified in the menu
title Debian GNU/Linux, kernel 2.6.10c
# The location of root fs, note (hd0,0) is the USB drive when
# we're actually booting off the MBR on the drive
root (hd0,0)
# The kernel to boot, and where it'll find its root-fs (in linux device notation)
kernel /boot/vmlinuz-2.6.10c root=/dev/sda1 rw
# The relevant initrd file (note I made this one by hand)
initrd /boot/mc_initrd.img
# The next (commented out) line would use the default initrd
# installed by the Debian kernel package created earlier:
#initrd /boot/initrd.img-2.6.10c
savedefault
boot
Then, with GRUB configured, we can install it into the boot-sector of
the USB HDD using the command
grub-install --root-directory=/mnt/usb1 /dev/sda