In Debian Linux, there are two ways to compile custom kernels:
Using Debian packages
We’ll use the traditional method because it applies to all Linux distributions (in fact, the guide we linked is from the ArchWiki).
First and foremost, please take a snapshot of your VM before you get started.
Make sure you have the following kernel configuration/compilation dependencies installed:
build-essential bc python bison flex rsync
libelf-dev libssl-dev libncurses-dev dwarves
Reboot and make sure that your system is all good.
Run uname -r
to make sure that you are running the stock kernel that ships
with Debian. At this time of writing, our arm64 system reports 5.10.0-20-arm64
.
If you are on an x86 system, it should report 5.10.0-20-amd64
.
In the Spring 2023 semester, you will be working with Linux kernel version 5.10.158.
But instead of fetching the Linux source code from kernel.org,
you will be compiling the kernel source code provided in each kernel
assignment’s skeleton repo. You will find the pristine, mainline
Linux kernel source tree in the linux/
directory in your assignment repo.
You should verify the version of the kernel. The first 6 lines of Linux’s
top-level Makefile
will show you the version:
$ head -n 6 Makefile
# SPDX-License-Identifier: GPL-2.0
VERSION = 5
PATCHLEVEL = 10
SUBLEVEL = 158
EXTRAVERSION =
NAME = Dare mighty things
It is standard practice to run make mrproper
in a freshly cloned repo before
proceeding. This removes any configuration files that might have been
accidentally left over from previous builds.
The Linux kernel build system can be configured using .config
, which must
be located at the root of the Linux source tree. This file is used to specify a
truly astounding number of options with which you can build your kernel.
You can start with the .config
file of your running kernel (e.g. the stock
Debian kernel). The .config
file that was used to build the stock Debian
kernel is located in the /boot/
directory. The following command copies over
that .config
file, and updates any missing options with default values:
$ make olddefconfig
Depending on how your stock kernel is configured, this may report some warnings. For example:
/boot/config-5.10.0-20-arm64:7855:warning: symbol value 'm' invalid for ASHMEM
/boot/config-5.10.0-20-arm64:8981:warning: symbol value 'm' invalid for ANDROID_BINDER_IPC
You should be able to ignore these without running into any trouble.
There are two recommended methods to edit your .config
file:
You can run make menuconfig
, which will open up an interactive menu that
organizes configuration options in a more user-friendly manner. If you make
any configuration changes, it will also create a backup of your original
.config
file, named .config.old
.
config.old
will be created every time you run a make
target related to
config
, and every time you save in make menuconfig
. If you choose this
method, we recommend that you save once, only after making all of the
changes listed below – this will allow you to take a clear diff of the
updated .config
vs. the original config file saved in .config.old
.
You can use the scripts/config
command, which allows you to manipulate
options in a .config
file from the command line. Note that this method
does not create a .config.old
file. However, you can find the original
.config
file in the /boot
directory if necessary.
Make the following changes, using either of the above methods:
CONFIG_LOCALVERSION
: This setting gives your custom kernel a unique
name to distinguish it from other kernels present in your system.
The local version will be appended to your kernel version to
form your kernel name. For example, if we build a 5.10.158 kernel with the
local version set to -cs4118
, it will be named 5.10.158-cs4118
.
For your pristine kernel build, set this to -cs4118
.
In menuconfig
, this can be found under General setup
, in the
“Local Version - append to kernel release” option.
Alternatively, run scripts/config --set-str LOCALVERSION "-cs4118"
.
SYSTEM_TRUSTED_KEYS
: This is used to bake additional trusted X.509 keys
directly into the kernel image, which can be used to verify kernel modules
before loading them. Debian 11 comes with this
configuration, but we won’t actually need this, so we will remove it.
You may find this option set to debian/certs/debian-uefi-certs.pem
;
clear out this field and leave it empty.
In menuconfig
, this can be found by opening the Cryptographic API
section, then opening the Certificates for signature checking
section
at the bottom. The specific field is “Additional X.509 keys for default
system keyring”.
Alternatively, run scripts/config --set-str SYSTEM_TRUSTED_KEYS ""
.
After setting your local version, take a moment to inspect the contents of the
.config
file. Make sure that the options you configured are set to what you
expect them to be, using scripts/config --state <option>
for each of the
above options.
Linux also provides the scripts/diffconfig
utility, which can be used to
compare different .config
files. For example, if you used make menuconfig
,
you would see:
$ scripts/diffconfig .config.old .config
LOCALVERSION "" -> "-cs4118"
SYSTEM_TRUSTED_KEYS "debian/certs/debian-uefi-certs.pem" -> ""
If you used scripts/config
, you can find your stock .config
file in the
/boot
directory, as config-5.10.0-20-arm64
(on an arm64 machine). You can
then run scripts/diffconfig /boot/config-5.10.0-20-arm64 .config
, and should
see the same output.
localmodconfig
A large amount of time is spent compiling and installing kernel modules you
never use. You can regenerate .config
so that it contains only the modules
your system is currently using. This will drastically cut down the number of
modules built.
Running make localmodconfig
will take your current .config
and turn off
any unused modules. Running this will ask you several questions about how to
configure the kernel, so rather than doing this manually, run:
$ yes '' | make localmodconfig
This should generate a much smaller .config
, leading to a shorter compilation
time. You can use the scripts/config
commands listed above to verify that the
configuration options we care about are still set properly.
Compiling a kernel takes a long time – it can take over an hour on a slow
machine. You can speed up this process by running compilation jobs in parallel
by using make
’s -j
flag, which takes the number of parallel jobs to spawn
as an argument. If your computer has multiple CPU cores, you should configure
your VM to use them.
Run make
as a non-root user:
$ make -j$(nproc)
This uses command substitution to invoke the nproc
command, which prints the
number of cores available on the machine.
Check that running umask
yields 0022
(this should be the case by default).
If not, run umask 0022
. This will ensure that the kernel modules are
installed with correct permissions. Note that if your umask
is not 0022
,
you’ve likely modified your umask
in your ~/.bashrc
or ~/.bash_profile
to
a non-default value. Since this is a single-user system, a umask of 0022
is
reasonable, and you should undo those changes.
Then, install the kernel modules and the kernel itself:
# make modules_install
# make install
Verify that you have the following 3 files in /boot/
:
initrd.img-5.10.158-cs4118
System.map-5.10.158-cs4118
vmlinuz-5.10.158-cs4118
When you are hacking kernel code, you’ll often make simple changes to only a
handful of source files. If you didn’t modify any header or module source
files, the modules will not be rebuilt when you run make
. As such, there is
no reason to reinstall all modules every time you rebuild your kernel.
Reboot by running:
# reboot
Make sure to pick your custom kernel from the boot loader menu.
Now verify that you’re running your own custom kernel by running:
$ uname -a
Instead of 5.10.0-20-arm64
, you should now see your kernel version string,
5.10.158-cs4118
!
Last updated: 2023-01-04