Aug 17, 2012

Compiling the Linux Kernel in Ubuntu

Linux is awesome. However, there are always its shortcomings, such as hardware not being recognized. For my laptop, a Toshiba Satellite L655, I must compile my own, custom kernel such that Ubuntu will see my battery's charge. The original solution for this problem is from Techinterplay here, but I have made a few modifications of my own to make the process even more efficient.

Obtaining the source and dependencies

Personally, I like to keep all of my kernel-related files under the root user's directory. To switch to the root user:

sudo -i

The first step to compiling anything is to obtain its source. There are a few ways to do this, but the way I will explore in this article is by using git. Git can be obtained in Ubuntu and Debian through the repositories with the following command:

sudo apt-get install git

After putting in your password, the next step is to clone the kernel tree. Now, a word of warning - the Linux kernel tree is over 1.5GiB in space. Furthermore, compiling the kernel with default flags will take around 10GiB. However, the git tree's disk space will only be used once, and this won't be increasing too much every time the kernel is updated. Also, the compiled kernel itself shouldn't be anywhere near this number - it should only be about 40-50MiB.

To clone the Ubuntu Precise kernel tree, issue the following command:

git clone git://kernel.ubuntu.com/ubuntu/ubuntu-precise.git

Though the command above explicitly states Precise (version 12.04) as the release, it can also be used for other versions of Ubuntu. Simply replace "precise" with your codename of Ubuntu (see here for the table: https://wiki.ubuntu.com/DevelopmentCodeNames) and run the git command, for example:

git clone git://kernel.ubuntu.com/ubuntu/ubuntu-lucid.git

for Ubuntu 10.04 Lucid Lynx.

The next thing we need to do is get the build dependencies. Luckily, Ubuntu and Debian make it easy, so we can just run one command:

sudo apt-get build-dep --no-install-recommends linux-image-$(uname -r)

Also, after that, we need to get some more programs:

sudo apt-get install fakeroot kernel-wedge build-essential makedumpfile kernel-package libncurses5 libncurses5-dev

That should do it for the dependencies. The next step is making your custom kernel custom.

Applying the patch

Since the git repository is a working tree for the Ubuntu Kernel Team, we have to checkout the latest stable release of the code. To do this, run:

git checkout `git describe --abbrev=0`

What this does is it finds the latest tag (basically, they're the releases; this is what git describe does), and then tells git to check it out. The --abbrev=0 part takes out the commit name off of the end of the tag.

Now is the time to apply the patch (if at all; you can skip the rest of this step if you just want to compile the kernel for fun). To apply the patch, run:

patch -i $filepath -p1

Where $filepath is the path to your patch, and '-p1' means how many directories are excluded from your patch. For example, if the beginning of your patch looks similar to this:

diff --git a/.config b/.config
new file mode 100644
index 0000000..0cf0de0
--- /dev/null
+++ b/.config

Then you want to keep the -p1 as it is. The key here is the a/ and b/ in the first line (could be farther down in some patches). If there are no a/ or b/, then use -p0 instead of -p1.

Now is the part where the magic of git comes into play. With many version control systems (a. k. a. VCS), we can have branches to manage our code more efficiently. What we can do here is check out our changes into a new branch.

git checkout -b patched

This command will take our new changes into a separate branch, and, later on, this will make the process of updating the source significantly easier.

Compiling the kernel

Finally, the last step in the endeavor is here: compiling it. First, we want to speed up the compile by taking advantage of all cores available on the machine (e.g. using all four cores on a quad-core computer). First, we need to see how many cores we have available:

numprocessors=$(grep -c processor /proc/cpuinfo)

There should be no spaces around the "=" here.

On some newer machines, the processor might be "hyperthreaded", meaning that each core of the processor can have multiple threads. For example, my computer has a hyperthreaded Intel Core i3 dual-core processor, so it has 4 threads available. Note that the output above should yield, in my case, 4 processors because the operating system sees each thread as a separate core.

What we now want to do is tell the compiler how many threads we want to run. To do this, type the following (note, it is case sensitive):

export CONCURRENCY_LEVEL=$numprocessors

There should be no spaces around the "=" here either.

Now, we want to clean the source to make sure nothing from previous compiles were applied through the patch:

make-kpkg clean

Now, we want to copy the current kernel's configuration file into the source. To do so, run the following:

cp -vp /boot/config-`uname -r` ./.config

The -vp option makes cp keep the timestamp (date modified) on the file, and it also shows the command again, for you to make sure you didn't mess up.

What we want to do now is regenerate the configurations:

make menuconfig

Now here is where I ran into some unexpected problems. If you use VirtualBox, you probably use the dkms module for virtualization support. To add support for this:

make prepare

So, all of the preparations are done, and all that is left to do is actually compile it. To compile it, run this command:

time fakeroot make-kpkg --initrd --append-to-version=-kroqernel kernel-image kernel-headers

Replace -kroqernel with whatever name you want (e.g. -noobsauce), and fire off the command. Note that it will also print the amount of time it takes once it's done, if you're too lazy to keep a stopwatch running (like me). So, just sit back, take a nap, have some tea, cookies (:D), or whatever. For me, this usually takes around one to one and a half hours, but will likely take much more on older machines.

Afterwards, I suggest cleaning the source to make some free space (around 7GiB):

make-kpkg clean

Installing the custom kernel

Now that all the compiling is over, we just need to install the new kernel and we're pretty much done. So, navigate to the parent directory, and find which .deb files (packages) are available:

cd ../
ls *.deb

Determine which file is the newest by looking at the version numbers, and then install the kernel image and headers.

sudo dpkg -i linux-image-VERSION-$NAME-(random stuff).deb

Let the packages install, and then restart the machine:

sudo reboot

... and that's it! You've installed a shiny, new, custom kernel! :D

Updating the source

Every now and then, new updates come to the kernel both from Ubuntu and upstream, and we should update our own custom kernels to keep ourselves secure and up-to-date, too. Git, however, can make this process a breeze. We just need to issue a few commands to update our tree and have it ready to compile again. First, get the changes from upstream:

git pull

Now, we need to merge in the changes with our patched branch. Remember, since this isn't a completely stable tree, we have to use tags. First, make sure you're on the patched branch:

git checkout patched

Replaced patched with whatever you called the branch initially. Then, merge it with the latest tag:

git merge `git describe --abbrev=0`

Now our tree is all up to date, and we can simply follow the instructions above to recompile the code.

Tell me if you know of any suggestions for this article, and I hope you enjoyed! :)

Further Reading