Custom kernels in Ubuntu/Debian – how, when and why

So, you’ve decided to try out this thing you heard others talk about, called ‘compiling a custom kernel’. If you are trying this as a hobby, or because you want to learn a new skill, very well – read on.

However, before we start, we will try to explain situations when this need arises and how to deal with it. Note that this is an extensive subject which needs much more in terms of space than we will provide here. You will learn the basics, what you need, what to do and what you’ll achieve.

For more info, as usual, Google is your friend; also , the documentation resident in the kernel source tree will answer lots of questions. So, let’s start with the beginning, with a last note : if need arises, we will publish more articles related to kernel compiling pertaining to other distros.

When you will need to compile your own kernel

The first situation would be when you need a newer kernel than your distro provides, especially when you run a stable distribution (e.g. Debian stable, CentOS), because your kernel doesn’t support some feature/driver you need, or simply because you feel you wanna try the latest and greatest.

A warning, however : if you wanna run a bleeding-edge kernel, make sure it will be compatible with other key components of your system (like glibc), remember that more recent means less testing and (probably) more instability; your distribution’s kernel maintainers usually do a good job backporting some new features to older , more stable kernels, so make sure you really need bleeding-edge. Our advice is not to use these kernels (the longterm kernels would make an exception here) on production systems. As said, trust your distro’s maintainers.

The second situation is that you feel you are running a bloated kernel, with lots of components unneeded. While the kernel only loads modules matching an existent piece of hardware, a smaller memory footprint of the kernel usually helps speed and boot time.

# lspci -vv
# lsusb
# hwinfo
# cat /proc/cpuinfo

Above commands help you to better know your hardware. Write down what you found and make sure you run the ls* commands above as root, for extra information.

Another situation would be you want to help with testing the kernel by running the latest on your system. The same warnings as above apply : do that on testing systems, keep in touch with upstream in case you find a bug (lkml.org is the main kernel mailing list – high traffic) and try to be as helpful as possible when asked for information.This cannot be stressed enough : read the documentation, either because you find yourself in a nasty situation , want to report a bug or simply have questions.

Next, you may want to be the cool kid on the block and run the latest and greatest, maybe glancing at the source (provided you’re into this and you have some C and ASM knowledge) and even submit patches. You may want to look first if your distribution doesn’t already offer a newer kernel package, for example you can install kernels from Debian experimental on a testing system, if you alter your sources.list accordingly. Again, work with the maintainers if you stumble onto a bug.

Terminology

Before we delve any further into this somewhat arcane subject, we should make some basic terms clear(er); this is essential for understanding key parts of kernel testing and compiling.

  • kernel – The core of the operating system, responsible with managing hardware resources (I/O, networking, CPU, memory…). Basically, it’s the essential part of the OS responsible for all the dirty work. User programs communicate with the kernel, requesting CPU time or other resources by system libraries that act as intermediaries between userland (see below) and the kernel / hardware. Kernels can be monolithic and microkernels (for more information, if you’re interested, see Kernel-wide design approaches at Wikipedia.org . Microkernels (like Minix) use a design scheme that divides the core from the rest of the kernel, and that rest is divided into components, each one doing something specific : I/O, networking, etc. Monolithic kernels (Linux, BSD, Solaris), as the name suggests, comprise most of the kernel in one unit, having extra functionality (e.g. drivers) provided by modules. There are also hybrid kernels, a combination between the two, a good example being the Windows kernel.
  • userland – everything in an OS that is not part of the kernel ( libraries, applications ) are said to be part of userland. The name is that obvious.
  • module – as shown before, a kernel module is a piece of binary software that basically “teaches” the kernel how to “talk” with a piece of hardware or provide some functionality (e.g. nfs)
  • compiler – the compiler is an application that basically takes the written code, as downloaded by you from kernel.org, and transforms it into binaries. The compiler found in Linux distributions is called ‘gcc’, and that stands for GNU Compiler Collection, which also needs components essential to building software : utilities found in binutils like the assembler (as) or library archiver (ar) .On Debian systems, or Ubuntu, one can find which package a file belongs to by installing and running apt-file. Speaking of that, let’s see what packages we need to install for a successful kernel build.
  • vanilla kernel – this is the name used for the upstream kernel, as found on kernel.org, so with no distro-specific patches.

Please note that whenever you see a command in this document starting with the prompt ‘$’ that means the command will have to be run as normal, everyday user; whenever you see the ‘#’ prompt, that means the command is to be run as root (we use sudo, but that is not mandatory). The current directory, unless otherwise specified, is the one holding your source tree, in this case, linux-2.6.

What you need to install

  • gcc – of course, the compiler is essential
  • binutils – this package contains the linker, assembler and other utilities vital to compiling programs written in C.
  • gcc-doc – the manual and info pages for gcc. Useful if you wanna get dirty and modify some compile flags. Useful anyway if you wanna write or compile C packages.
  • gdb – The GNU debugger. Not mandatory but useful if something goes wrong. Gdb-doc will also help.
  • libreadline5-dev – for using the kernel configuration ncurses-based interface. You can use other interfaces (see below).
  • make – will be installed as dependency, but few words are in order. Consult the manual or books, because this is not a subject to be explained lightly in such a short space. Make is an utility used when compiling C/C++ programs, and what it does is it looks in a Makefile, containing rules about how and in what order the build should happen, and tries to execute those directives. Read the Makefiles in the source tree to get a glimpse.
  • git – Git is a VCS (version control system), doing what cvs or subversion does, namely keeping you up-to-date with the latest kernel tree.

Getting the kernel source

If you want to install the source from your distribution, use

# apt-get install linux-source-<version>

where <version> can be gathered from issuing:

uname -r

Use this if you want to modify your existing kernel ( adding drivers, trimming, etc. ). Otherwise, you want the vanilla kernel. You can get it from www.kernel.org ( we suggest wget or curl here as download managers ) or, if you want the latest, you will use git. We recommend storing the source in your user’s home directory, and the command to get the latest mainline tree is ( see man git ):

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux-2.6

In the past we found the mini-howto at http://linux.yyz.us/git-howto.html to be useful; also check out kernelnewbies.org . The above command will create a folder in your current directory named linux-2.6, which can be updated later by cd’ing into it and issuing a simple

make clean; git pull 

Now, after you have the source, we will have to configure the kernel .

Configuration and building

If you have an existing .config file (the file that holds the options for kernel building – what goes in and what doesn’t), copy it in linux-2.6 (from /boot/config-<version> or /proc/config.gz – the current configuration). If you do not want to modify the existing .config, just issue

$ make oldconfig

Otherwise, read on. If you want to modify the existing configuration, issue

$ make menuconfig

(recommendation: you can use make config for lots of questions about options in the kernel, or make xconfig , which requires qt libraries, for a nicer , graphical menu), and select “Load an Alternate Configuration File” and press enter for .config, the default name of the configuration file, or type an alternative file name already saved in linux-2.6.

After that, start going through the menus to make the necessary alterations The rule of thumb here is “if you don’t know what it does, don’t mess with it”, at least until you get some experience. In the end, from the top menu, select “Save an Alternate Configuration File” , press enter for the default name (.config – recommended) and then “Exit” from below. If you wanna start from scratch, forget about the “Load an Alternate Configuration File” step and continue. At your next kernel compile , after cleaning and updating the tree, use ‘make oldconfig’ as above to use the old configuration. OK, now we have the configuration tailored to our needs that just waits to be built. Building a kernel is as simple as configuring it (!). Just type make and the output should look like below:

$ make 

  HOSTCC  scripts/basic/fixdep 
  HOSTCC  scripts/kconfig/conf.o 
  SHIPPED scripts/kconfig/zconf.tab.c 
  SHIPPED scripts/kconfig/zconf.lex.c 
  SHIPPED scripts/kconfig/zconf.hash.c 
  HOSTCC  scripts/kconfig/zconf.tab.o 
  HOSTLD  scripts/kconfig/conf 
  
  CHK     include/linux/version.h 
  UPD     include/linux/version.h 
  CHK     include/generated/utsrelease.h 
  UPD     include/generated/utsrelease.h 
  CC        kernel/bounds.s 
  GEN     include/generated/bounds.h 
  CC        arch/x86/kernel/asm-offsets.s 
...

and , after a while, depending on your machine and kernel configuration, it will be done. If you want to speed things up a bit, use the -jn flag to make, where n is number of processors/cores + 1. Be careful however, that can expose bugs in the kernel or build infrastructure, so if something goes wrong, try again by just using make without any flags. If after reading the gcc manual (and if you’re still sane), you feel adventurous and want to modify some hardware-specific flags, or feel like optimising the code, use the make manual page to find out how (mainly COPTS and CFLAGS). However, optimisations bigger than -O2 are risky.

Be careful and expect breakage, the kernel may compile without problems, but it can act weird. Remember to type all the commands as normal user. No need to build as root and the kernel developers frown upon the idea.

Installation

Now let’s install the modules : this needs to be done as root, since the modules are installed in /lib, and the normal user doesn’t have write access there. So,

# make modules_install 

does just that, and this needs to be done before installing the kernel so that modules and installed kernel are in sync. Use

# make install 

to install the kernel to /boot, then

# depmod 

and prepare for creating an initramfs ( initial RAM filesystem ), which is a temporary filesystem loaded to RAM in early boot stages, and is used for providing basic drivers and other facilities in order for the root filesystem to be mounted. More information can be found Wikipedia’s Initrd page. The command needed for the task is update-initramfs (this is invoked also whenever a new kernel is installed, triggered by the package manager) which can create an initramfs ( -c ) or update an existing one ( -u ) . The complete command is

# update-initramfs -c -k  

The version is the one you will see after ‘make modules_install’ finishes ( the last line of its output will be “DEPMOD” ).If you want to have the exact and longer version number, so you can tell developers what “git moment” you used, select “General Setup” → “Automatically append version information to the version string” after issuing menuconfig. Output on my Ubuntu system looks like so :

update-initramfs: Generating /boot/initrd.img-3.1.0-rc3+
...

Update your Grub so it notices your new kernel with

# update-grub

On my Debian testing machine, the output looks like this :

Generating grub.cfg ... 
Found background image: /usr/share/images/desktop-base/desktop-grub.png 
Found linux image: /boot/vmlinuz-3.0.0-1-amd64 
Found initrd image: /boot/initrd.img-3.0.0-1-amd64 
Found linux image: /boot/vmlinuz-3.0.0-rc6-amd64 
Found initrd image: /boot/initrd.img-3.0.0-rc6-amd64 
Found linux image: /boot/vmlinuz-2.6.39-07727-gbd1bfe4 
Found initrd image: /boot/initrd.img-2.6.39-07727-gbd1bfe4 
Found linux image: /boot/vmlinuz-2.6.39-2-amd64 
Found initrd image: /boot/initrd.img-2.6.39-2-amd64 
Found linux image: /boot/vmlinuz-2.6.39-rc7-amd64 
Found initrd image: /boot/initrd.img-2.6.39-rc7-amd64 
Found linux image: /boot/vmlinuz-2.6.38.5 
Found initrd image: /boot/initrd.img-2.6.38.5 
Found linux image: /boot/vmlinuz-2.6.38.4-00001-gfaa8ee7 
Found initrd image: /boot/initrd.img-2.6.38.4-00001-gfaa8ee7 
Found linux image: /boot/vmlinuz-2.6.38.4 
Found initrd image: /boot/initrd.img-2.6.38.4 
Found linux image: /boot/vmlinuz-2.6.38-2-amd64 
Found initrd image: /boot/initrd.img-2.6.38-2-amd64 
Found linux image: /boot/vmlinuz-2.6.32-5-amd64 
Found initrd image: /boot/initrd.img-2.6.32-5-amd64 
Found memtest86+ image: /memtest86+.bin 
Found memtest86+ multiboot image: /memtest86+_multiboot.bin 
done 

Of course, your output will not look exactly the same, but the outline should be the same; finally, remember : if you want, edit /etc/default/grub to alter some options before update-grub and , with fingers crossed, reboot your machine to test the new kernel.

If something goes wrong

The most usual situations when your new kernel is unusable is that you can’t boot it in the first place, or that it boots and it doesn’t have some essential driver (for example networking drivers). Usually, update-grub does a good job in writing the grub menu file, but you might wanna check it out anyway. If you pass grub, chances are you got overzealous and wed out an essential driver for the system, like the disk-related parts (ATA, SATA, SCSI…), or maybe NFS, if you have a NFS-mounted root. Boot a working kernel and reconfigure it, using Google and possible other sources, like IRC .

Chances are someone already stumbled upon your issue in the past and you have chances to find an answer. If the problem is more serious, and you are confident you read about netiquette and how to post to the kernel mailing list, ask nicely. There are lots of nice and helpful people out there, but they tend to be not-so-nice when you haven’t done your homework and/or wasting their time. If you have a separate /boot, keep in mind that it’s usually not very big and it might fill up with kernels quickly. Also /lib/modules tends to accumulate lots of space in /, so make sure to do some cleaning up from time to time. Remember that the kernel is a complex piece of software and many causes can be at the root of your issues. If you had no problems following this guide your are ready for more advanced Linux kernel configuration.