Peter Davies

Ramblings

Building a custom Ubuntu kernel

Most of the time the stock Ubuntu kernel is exactly what you want and you should leave it alone. Occasionally you need to change something the packaged kernel will not let you: patch an in-tree driver, flip a build-time option, or tweak the input subsystem. In my case it was the input drivers, trimming how evdev, mousedev and joydev behave so a touchscreen-only device did not expose a stray pointer. Whatever the reason, the clean way on Ubuntu is to build the kernel into .deb packages and install those, so it is tracked by the package manager like any other kernel.

This is on Ubuntu 22.04 building a 5.15 series kernel.

Build dependencies

sudo apt install build-essential libncurses-dev flex bison openssl libssl-dev \
  dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf bc zstd dwarves rsync

libelf-dev, bc, dwarves and zstd are the ones people miss, and the build fails partway through without them.

Get the source

Pull a mainline tarball from kernel.org and unpack it into /usr/src:

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.46.tar.xz
tar -xf linux-5.15.46.tar.xz
sudo mv linux-5.15.46 /usr/src/
cd /usr/src/linux-5.15.46

Start from your running config

Do not configure from scratch. Start from the config of the kernel you are already running, so you keep all the driver and hardware choices that make your machine boot, then change only what you came to change:

cp /boot/config-$(uname -r) .config

Carry forward any new options non-interactively, accepting the defaults:

make olddefconfig

If you need to make your changes through the menu interface, do it now:

make menuconfig

Or, if you are patching driver source directly, edit the files under drivers/ before you build.

Module signing certificate

Modern kernel configs sign their modules, and the build will stop asking for a signing key if there is not one. Generate a self-signed certificate for it:

mkdir -p certs
openssl req -x509 -newkey rsa:4096 -keyout certs/mycert.pem -out certs/mycert.pem -nodes -days 3650

If you would rather not sign modules at all for a local build, turn CONFIG_MODULE_SIG off in the config instead; signing is the better habit.

Build the packages

Build straight into Debian packages, using all your cores. LOCALVERSION tags the kernel so it is clearly yours and does not collide with the stock one in the boot menu:

make -j$(nproc) bindeb-pkg LOCALVERSION=-custom

This takes a while. When it finishes, the .deb files are written to the parent directory, /usr/src here.

Install and boot

Install the image and headers packages and reboot:

cd /usr/src
sudo dpkg -i linux-image-*-custom_*.deb linux-headers-*-custom_*.deb
sudo reboot

Confirm you are running the kernel you built:

uname -r

You should see your -custom version. Because these went in as proper packages, the kernel shows up in the boot menu, is managed by apt, and removes cleanly with dpkg -r if the experiment does not work out, which is the whole reason to build it this way rather than running make install by hand.