Linux Kernel Map

Building a Linux Kernel

I find myself fairly regularly hearing about new features in the Linux kernel (the core of Linux operating systems), getting excited, and checking to see if my distribution's repository of packages has the latest kernel built. Unfortunately, Ubuntu's repositories don't have the most recent versions of the kernel available for some time.

Fortunately, the Linux kernel is open source, so we can freely download the kernel's source code, compile it, and install it.

This article details the steps necessary on a fresh install of Ubuntu 19.10 (Eoan Ermine).

Prerequisites

Although we can freely download the code and compile it, we still need some applications installed to download the code and compile the kernel.

We'll need

    • git, as it allows us to synchronize files from the kernel's codebase
    • build-essential, as it is required to compile the source code
    • kernel-package, to allow us to build kernel packages
    • fakeroot, as it allows us to build Debian packages (which Ubuntu can install)
    • libncurses5, as it allows us to use nconfig to configure our kernel easily
    • libssl, to allow git to pull from secure repositories
    • ccache, to cache the build process, which will speed up subsequent builds
    • flex and bison, which are necessary for newer versions to build

All of these can be installed by running:

sudo apt install -y git build-essential kernel-package fakeroot libncurses5-dev libssl-dev ccache flex bison

Note: You may be presented with a Modified configuration file dialog. If you are, select install the package maintainer's version, and select Ok

Finishing Installing Prerequisites
Finishing Installing Prerequisites

Downloading the Source

We start off by going to the Linux kernel's repository at https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/.

This shows us a bunch of information related to branches of the kernel (kernel development is done on branches for specific things). The master branch is the main branch for code, and versions of linux are done on separate branches, such as the branch linux-5.4.y, which holds the source for Linux 5.4.

In this article, I am going to build Linux 5.4, so I'll use this branch.

We start by syncing up with the code in this branch with the command:

git clone -b linux-5.4.y git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

This tells our source control tool (git) to clone, or copy the code from the linux-5.4.y branch out of the kernel repository (git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git).

This will take a while to complete, as we have to wait on all of the kernel code to download.

Cloning the Linux Kernel's Repository
Cloning the Linux Kernel's Repository

Configuring

We'll start by moving into the code's directory by running `cd linux`.

Now that we have the source code, we need to configure the kernel. We have a few different options for doing this. I'll lay out our options below:

    1. We can use the kernel configuration used to build our current kernel, and select default options for the new features in the kernel we downloaded by running cp /boot/config-`uname -r` .config
      This code copies the configuration for our current kernel, which is located at /boot/config-[kernel version], to the source code's directory.
      To select all default options, we run yes '' | make oldconfig.
      make oldconfig updates our old configuration file, and asks for what new options we want to enable. yes '' just boils down to "hit enter at every option"

      Default options selected
      Default options selected
    2. We can ignore our current configuration, and just immediately begin configuring.
      Note: This is the hardest way to configure, as improper options can lead to the system not booting.

Now that we're ready to fire up the configuration option selector, we can run make nconfig.

`make nconfig`
make nconfig

nconfig has every option for your kernel in it. Any option with ---> to the side means that there's a submenu. Use the arrow key on your keyboard to select options, and use the Enter key to toggle it.

Many options have these options:

    • A space means that it's not selected to be built
    • * means that it will be compiled and loaded when the kernel starts
    • M means that it will be compiled, but only loaded when needed
Some of the options in `Device Drivers`
Some of the options in `Device Drivers`

Normally, no additional configuration is necessary when you've started the configuration with the old kernel's config.

To save your configuration, press F6, and F9 to quit.

Compiling

I'm going to cover compiling an installable package, so that it's easy to switch kernels, and so that it can be distributed to others as a .deb file.

To start the build (which can take hours), run

time KBUILD_BUILD_TIMESTAMP='' sudo make CC="ccache gcc" -j$(nproc --all) deb-pkg LOCALVERSION=-custom

Let's dig into what this command does.

    • time KBUILD_BUILD_TIMESTAMP='' is necessary, as newer versions of the kernel's source code don't cache properly (which helps future build complete way faster). As a side effect, this also shows the total time that it took to build the kernel.
    • sudo runs the rest of the command as root
    • make is the build command
    • CC="ccache gcc" tells make to use the ccache version of gcc, which allows caching
    • -j$(nproc --all) tells make to use however many processor cores we have on our system
    • deb-pkg says that we want the make command to make an installable debian package
    • LOCALVERSION=-custom tells make that our kernel version should end with -custom, so that we can know which kernels we have installed are custom builds of the kernel

Now we play the waiting game. Once the build is complete, it'll drop you back to your terminal input.

On my virtual machine, this took 96m83s.

Installing the Kernel

Now that we have a built kernel, we need to install it, make grub wait for us, and reboot into the new kernel.

Your installable kernel packages will likely be different than mine, as different versions, and different kernel suffixes will have different names.

The kernel packages will be located in the parent directory of the kernel's source code. For example, my source code is located at ~/Documents/linux, so my installable packages are in ~/Documents.

Here's a list of the packages I got.

linux-headers-5.4.7-custom_5.4.7-custom-1_amd64.deb
linux-image-5.4.7-custom_5.4.7-custom-1_amd64.deb
linux-image-5.4.7-custom_5.4.7-custom-1_amd64.deb
linux-libc-dev_5.4.7-custom-1_amd64.deb

All of these can be installed by running sudo dpkg -i linux-*.deb

After the packages are installed, run sudo vi /etc/default/grub. This brings you to the vi editor with the grub settings open. Press i to get into insert mode, and change the line that says GRUB_TIMEOUT=0 to GRUB_TIMEOUT=10, or how many seconds you want it to wait. After editing this line, hit ESC to get to command mode, type in :wq to write the file and quit, and hit Enter.

To make these changes apply, run sudo update-grub.

Updating Grub's Configuration
Updating Grub's Configuration

Now we can restart with sudo reboot, and when the computer is turning back on, it will show a purple screen. Press Left Shift to get to the boot menu, choose Advanced options for Ubuntu, hit Enter, and you'll see your new kernel, your old one, and recovery mode options.

Kernels Installed
Kernels Installed

Once the machine finishes booting, pop open a terminal, and run uname -r, and you'll see that we're running the kernel we just built.

We're now using our custom kernel!
We're now using our custom kernel!

If it doesn't boot...

If your custom kernel doesn't boot, then it's pretty quick and easy to get back on the old kernel again. Hold Ctrl + Alt + SysRq, and while holding those tap R, then E, I, S, U, B, and the computer will restart.

Go back to the advanced options in GRUB again (the purple screen), and select your old kernel (the topmost one that doesn't have -custom at the end).

You should be back at a working system. Try configuring your kernel again, and adding more drivers, as a failed boot is usually caused by you not including the drivers that your computer needs to run.

Congratulations!

Congratulations on being one of the Linux users who have built the core of their system on their own. Have fun looking into what all of the configuration options are.

If you're looking for a pretty cool customization, look into building a "realtime" kernel. The patches are available at https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/, and Google is a great place to find out how to apply them.

Leave a Reply