Kernel Compiling

One of the things that can really mess up your system is a failed kernel build. But, you have just installed your shiny new distro and you really need to tweak it a bit, or maybe you need modular support for a piece of hardware so you can add some options when it is loaded. Whatever the reasons, let's take a look at the process, and how to automate and simplify it (and hopefully make it a touch safer).

Throughout this article whenever you see $version in lowercase it means the version of kernel, which you should substitute for actual version.

First of all though, there are some differing opinions to where you should put the kernel source. In Slackware by default it is installed to /usr/src, but there is an argument for putting it in one's home folder when compiling a custom build. I have used both and never had any problems with either, but I don't really want to crowd out my home folder with source that I will probably only look at a few times after a new install so I go with /usr/src for now. This argument dates back to an old mailing list post by Linus Torvalds - link - but wherever you, or your distro, installs the source doesn't make much difference to this HowTo.

Pre-Compile Tasks

Now is the time to do a lspci and make note of your hardware. It will help a lot if you want to take out the generic settings and only put in support for your chipset and other hardware. It might also help with diagnosing problems later on. You might also want to run lsmod and see which modules are currently being used.

So let's say you have untarred your linux source in /usr/src where $version is the version of your source.

cd /usr/src/linux-$version
Configuring a Newer Kernel Release

The process is a little different depending on whether you are reconfiguring an existing, already installed kernel, or installing a different version.

You will need to get the current running config and use that as a starting point for the new build. Hopefully your current kernel supports putting the running config in /proc. If so then you can zcat it into the linux source directory, and then import the settings into the new kernel:

zcat /proc/config.gz > .config
make oldconfig

The make oldconfig command will load your old settings in and mark newer ones for you. If you don't have a current config to use then you will need to build the new kernel from scratch, going through all the settings.

Configuring an Already Installed kernel

You may only need to zcat the config into /usr/src/linux-$version:

zcat /proc/config.gz > .config

Configuring

This is the part that matters the most. You need support for all your hardware. Gotchas are file system drivers - unless you are making an initrd make sure that you have support compiled in (not as a module) for your local file systems.

There are a few ways of running the configure but I recommend doing it in a console with make menuconfig. There are some advantages to this - you can actually change more options than you can with xconfig (like local version which I usually set to '-custom'). You can also edit .config by hand and do make config. But for now:

make menuconfig

The Compile

After running make menuconfig and configuring the kernel it's time to build it.

Note: There is a make install option, but I won't be using that here - I prefer to cp the kernel image and files over manually.

So the build order is:

make
make modules_install
cp arch/x86/boot/bzImage /boot/vmlinuz-$version-custom
cp System.map /boot/System.map-$version-custom
cp .config /boot/config-$version-custom

The Boot Loader

You need to add an entry for your custom kernel in /etc/lilo.conf or your grub config file. Make sure you leave your old entry in there as a failsafe. Here is a lilo example (you will need to set the correct 'root' partition):

# Linux bootable partition config begins
image = /boot/vmlinuz-$version-custom
root = /dev/hda1
label = Linux-$version-custom
read-only
# Linux bootable partition config ends

Then apply your new lilo settings:

lilo -v
The Reboot Test

Ok - moment of truth. Reboot the machine, choose your new kernel at the bootloader prompt, and watch the bootup messages. If all goes well then you have a working system and you can have a look through dmesg and check for any errors. If all didn't go well, reboot with your old kernel (you left the entry for it in /etc/lilo.conf right?). Now have a good look through your kernel setup to see what you missed. From here on it's a case of rinse and repeat until it all works as expected.

Automation

The build commands can be put into a small bash script to make life easier and to avoid mistakes. You will need to edit /etc/lilo.conf manually though, before running this script:

#!/bin/sh
# set -e should exit on most errors:
set -e
VERSION="2.6.29.6"
LOCALNAME="custom"

cd /usr/src/linux-$VERSION
make menuconfig
make
make modules_install
cp arch/x86/boot/bzImage /boot/vmlinuz-$VERSION-$LOCALNAME
cp System.map /boot/System.map-$VERSION-$LOCALNAME
cp .config /boot/config-$VERSION-$LOCALNAME
lilo -v

Now one just has to change $VERSION and $LOCALNAME and the script takes care of everything else.

Out of Tree Kernel Modules

If you have any out-tree kernel modules then they will need reinstalling. If you are using the same kernel version and local name as you were using when the modules were compiled you should be able to just reinstall the package, if you have one. If not, then you will need to recompile those modules and reinstall them. Typical examples are network and sound card drivers such as MadWifi and Alsa.

Conclusion

Compiling a kernel successfully and getting a system to run depends largely on getting the right support for your hardware. That's why lspci is important - get to know what hardware you have while using the old kernel, before building a new one. You might also want to check with lsmod to see which modules are being used. Good luck!

Addendum

While it's nice to compile and install custom kernels and modules, it's a lot nicer to package them. See kernel packaging for more details.