Notes: building the Linux kernel

Sep 20, 2018, by Hugo Lefeuvre. Note: this article is archived.

A few notes I took while building my first custom kernel a year back.

Building and installing a custom Linux kernel on Debian

$ cp /boot/config-$(uname -r) .config  
$ sudo make -j 4 && sudo make modules_install -j 4 && sudo make install -j 4
$ update-initramfs -c -k $(VERSION)
$ update-grub

make -j4 builds the kernel and the kernel modules. 4 is the number of threads used for the build, adjust it to match your hardware. Build time: about 1-2 hours on a decent machine

make modules_install installs the kernel modules to /lib/modules or /lib/modules/<version>. make install installs the newly built kernel to /vmlinuz.

update-initramfs creates initramfs for the new kernel.

update-grub updates GRUB's config files to take the new kernel in account.

What is initramfs ?

initramfs is a tiny rootfs loaded by the kernel at startup. It is responsible for mounting the actual root filesystem.

A rootfs is a ramfs (or a tmpfs, depending on your kernel configuration) which is always present in > 2.6 systems. A ramfs is basically the kernel's caching mechanisms exported as a ram-based filesystem. The ramfs implementation is very simple (the code base is actually so small that it's always enabled in the kernel) but this comes with a few drawbacks: ramfs memory can't be freed (a ramfs grows indefinitely) nor limited (it will fill up the ram until the system goes oom).

tmpfs addresses most of these issues at the cost of increased complexity.

Why not directly mount the actual /root ?

If the system you're booting on is clearly defined during kernel build, that is it is possible to statically build the kernel modules into the kernel, it isn't needed, no. However, GNU/Linux distributions like Debian distribute generic kernels where all drivers are distributed as modules (statically compiling them into the kernel would produce way too big kernel images), and in this case modules have to be selected and loaded from /lib at boot time.

This is the "main" reason why systems like Debian typically need to mount initramfs before mounting the actual /root.

That said, there are other reasons to use initramfs, even with statically compiled drivers.

For example, in the case where the HDD is encrypted, user-space binaries are needed to decrypt the disk (along with an interface to retrieve the passphrase, etc). All of this is not handled by the kernel, meaning that it has to be included in a minimal user-space environment in the initramfs.

Similarily, booting from the network requires networking tools not provided by the kernel itself, so they have to be included in the initramfs.

What's the difference with initrd ?

It is quite common to call initramfs initrd nowadays. initramfs is kind of the modern way to implement initrd.

Why updating the GRUB config ?

The GRUB displays a list of available kernels to boot from at startup. This list has to be updated somehow. Back in the days you had to enter them manually in the configuration files, but now there are scripts capable of scanning the root filesystem and generating these config files for you. That's exactly what grub-mkconfig does, and update-grub is nothing more than a wrapper for grub-mkconfig -o /boot/grub/grub.cfg.

Sources, further reading