Appendix C. Linux Kernel Building Reference

This is a quick guide to building a custom 2.6 kernel, patching the kernel, and adding loadable kernel modules. You'll find detailed recipes in Linux Cookbook(O'Reilly) in Chapter 10, "Patching, Customizing, and Upgrading Kernels," and Chapter 12, "Managing the Bootloader and Multi-Booting," which tells how to customize your GRUB or LILO boot menus for different kernels.

Why would you want to build a custom kernel? To add features or remove unnecessary features. On routers and firewalls, it adds a bit of security to use kernels that have had all the unnecessary features removed, and you can reduce the size considerably to fit on devices with limited storage.

Many distributions have their own distribution-specific tools for building kernels. You don't need these for building vanilla kernels from kernel.org. But, it's a different story when you're using distribution-specific kernel sources. Red Hat and Fedora package theirs as source RPMs, so you can't just build the kernel, but must also build an RPM. Fear not, for this appendix reveals how. Red Hat/Fedora kernels are heavily patched, to the point that a vanilla kernel may not even work, so you need to know the Red Hat Way of customizing kernels.

Debian, on the other hand, does very little modification to Linux kernels. They remove any bits that don't meet their policies, and that's all. So, vanilla kernels work fine on Debian systems.

You'll need a build environment, kernel source code for your distribution, and at least 2 GB of free disk space. You can build a kernel on any system, then copy it to other systems. If you like to modify kernels a lot, you might set up an old PC as a dedicated kernel-building station. Then, you'll only have to maintain source trees and utilities on a single box.

Most documentation tells you to unpack kernel sources into /usr/src/linux. Don't do this. As the kernel README says:

You may store binaries and source trees anywhere, and execute almost every step as an unprivileged user. Only the final steps require superuser privileges.

You may install as many kernels as you like, selecting the one you want to use at boot.

Obtaining a kernel that has not been altered by distribution vendors is easy—go to http://kernel.org/, the mothership of the Linux kernel. Download and unpack it into a folder in your own home directory; for example ~/kernel:

	[carla@windbag:~/kernel]$ wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.20.1
	[carla@windbag:~/kernel]$ tar zxvf linux-2.6.20.1

This is about a 40 MB download that will unpack to about 240 MB.

Change to the top-level directory of your new source tree. All of the following commands will be run from here:

	$ cd linux-2.6.20.1

Read the Documentation/Changes file to make sure you have the correct gcc version and other necessary utilities. Read the README for installation tips and other useful information.

Edit the new kernel makefile (~/kernel/linux-2.6.20.1/Makefile) to give a custom value to EXTRAVERSION, such as EXTRAVERSION =-test. Or, in the kernel configuration, enter your custom value in General Setup → Local version → append to kernel release.

Let's see what options the make command has:

	$ make help

Even though this is a brand-new source tree, run a cleanup first:

	$ make mrproper

At this point, you may copy your own custom config file to this directory, or just let make take care of it for you. If you don't provide one, it will use your /boot/config-* file. You can change everything anyway, so it doesn't matter all that much.

Now, run these commands:

	$ make xconfig
	$ make
	$ su
	# make modules_install
	# mkinitrd -o /boot/initrd-linux-2.6.20.1
	# cp linux-2.6.20.1/arch/i386/boot/bzImage /boot/vmlinuz-linux-2.6.20.1
	# cp linux-2.6.20.1/System.map /boot/System.map-linux-2.6.20.1

Save a copy of your new config file in a directory outside of the build tree. Add the new kernel to your GRUB bootloader menu:

	# /boot/grub/menu.lst
	title     new test kernel
	root      (hd0,0)
	kernel    /boot/vmlinuz-2.6.20.1 root=UUID=b099f554-db0b-45d4-843e-0d6a1c43ba44 ro
	initrd    /boot/initrd-2.6.20.1

Where does the UUID come from? From running the blkid command:

	$ blkid
	/dev/sda1: UUID="b099f554-db0b-45d4-843e-0d6a1c43ba44" SEC_TYPE="ext2" TYPE="ext3"
	/dev/hda1: UUID="1a5408ad-7d1d-4e24-b9db-d132d76e9e8e" SEC_TYPE="ext2" TYPE="ext3"

Remember that GRUB counts from zero, so hd0, 0 means /dev/hda1, or the first partition of the first block device. In this era of mixed PATA and SATA drives, this depends on the BIOS order of your hard drives, so you may need to dig around in your BIOS settings to see which drive the BIOS recognizes as the first, second, and so forth.

Reboot to your new kernel and enjoy. If it doesn't work, simply reboot to your old kernel, and try again.

You should use UUIDs to identify your block devices because /dev names are no longer static, but at the mercy of udev. You need to create an initrd image because the /dev directory is not populated until after boot, so there is no way to build the boot device into the kernel anymore.

make xconfig is time-consuming, but very important. If you leave out anything important, some things won't work, or it might not boot at all. Every configuration item has a Help entry. The kernel source tree has reams of help in the Documentation/directory.

You have three options for each configuration item: leave it out, build it into the kernel, or build it as a loadable module. These things should be built-in to the kernel:

Any hardware support related to boot devices should be built into the kernel:

These are fine to have as loadable modules:

It doesn't matter if you prefer a large statically built kernel, or a lean kernel with lots of loadable modules. Don't obsess over building the leanest possible kernel because it doesn't matter—performance is the same either way. Just be sure to enable loadable module support so that you can add additional modules as needed; this is a lot quicker and easier than rebuilding a kernel. Your best chance of improving performance is to select support for your particular CPU, rather than generic i386.

Fedora patches kernels heavily; a vanilla kernel from kernel.org may or may not work. So, let's do this the 100 percent Fedora way.

Fedora supplies only source RPMs, so you'll have to customize your kernel and then package it into an RPM. Download your kernel SRPM from your favorite Fedora mirror, such as:

	$ wget http://mirrors.kernel.org/fedora/core/development/source/SRPMS/kernel-2.6.21
	1.3194.fc7.src.rpm

Then, make sure you have all the build tools you need:

	# yum install rpmdevtools

Now, set up a build tree in your home directory, and make sure to do this as yourself and not as the root user:

	$ fedora-buildrpmtree

This creates an rpmbuildtree directory populated with BUILD, RPMS, SOURCES, SPECS, and SRPMS directories.

Now, install the source RPM. This will unpack files into your new rpmbuildtree directory:

	$ rpm -ivh 2.6.21-1.3194.fc7.src.rpm

Ignore any warnings about "group kojibuilder does not exist."

Next, run the %prep stage of the RPM rebuild. Make the --target option match your CPU type:

	$ rpmbuild -bp --target=i686 ~/rpmbuild/SPECS/kernel-2.6.spec

The kernel tarball has been extracted, and all the Fedora patches applied. Change to the source directory of your new build tree:

	$ cd ~/rpmbuild/BUILD/kernel-2.6.21/linux-2.6.21-1.3194.i686/

Do housecleaning:

	$ make mrproper$

Now, let's get started with configuring the new kernel:

	$ make xconfig

And, finally:

	$ rpmbuild --target i686 -ba ~/rpmbuild/SPECS/kernel-2.6.spec

Again, make the --target option match your CPU type.

This builds the kernel.rpm, the kernel-devel.rpm, and rebuilds the kernel.src.rpm with your custom config included. The new binary kernel RPM is in ~/rpmbuild/RPMS/i686/. Grab your new kernel.rpm, and install it just like any other RPM:

	# rpm -ivh kernel-2.6.21-1.3194.i686.rpm

Then, reboot and enjoy your new kernel.

Debian users can employ either vanilla kernels, or have the option to fetch official Debian kernel sources with aptitude. You should also install kernel-package and fakeroot:

	# aptitude install linux-source-2.6.22 kernel-package fakeroot

This downloads the source tarball into /usr/src/, so you need to move it to your personal kernel-building directory:

	# mv /usr/src/linux-source-2.6.20.tar.bz2 ~/kernel

Remember that dpkg-L [package name] shows you all the installed files in a package if you can't find them.

Change to your ordinary user, change to your kernel directory, and unpack the tarball:

	$ su carla
	$ cd ~/kernel
	$ tar zxvf linux-source-2.6.20.tar.bz2

Then, change to the top-level source directory, and start configuring your new kernel:

	$ cd linux-source-2.6.20
	$ make mrproper
	$ make xconfig

When you're done slogging through configuration, run these commands:

	$ make-kpkg clean
	$ make-kpkg -rootcmd fakeroot -rev test.1 linux_image

This produces a .deb package named linux-image-2.6.20_test.1_i686.deb, which you can install in the usual way with dpkg:

	# dpkg -i linux-image-2.6.20_test.1_i686.deb

This should put everything where it belongs and create a GRUB menu entry.

fakeroot fools the system into thinking you are the root user when you're not. It won't let you run commands that need genuine root privileges, but it's good enough for kernel-building.

Debian's binary kernel packages are named linux-image-*, and the kernel source packages are named linux-source-*. It has been this way since the 2.6.12 kernel; before then, they were called kernel-image-* and kernel-source-*. The new naming convention is in hopes of allowing other kernels to be used with Debian in addition to the Linux kernel.