Build a kernel snap
The kernel snap is one of Ubuntu Core’s key components. It holds the Linux kernel image alongside its associated modules, the ramdisk image for system initialisation, and optional firmware and device tree files.
It’s one of the essential snaps that need to be specified in the model assertion when building a custom image. Being such a vital part of the system, it can be updated but it cannot be swapped.
See Types of snap for details on which snaps are integral to Ubuntu Core’s functionality.
Canonical publishes several reference kernel snaps, alongside kernel snaps for models such as the official Ubuntu Core VMs on various certified public clouds. There are also several general purpose computing images for popular physical devices such as 64-bit x86 PC and Raspberry Pi 2, 3 and 4:
- pi-kernel (the generic Raspberry Pi kernel for all supported Pi devices)
- pc-kernel
A custom build of the Linux kernel allows for device-specific architectures, configuration and modifications, and manually building the kernel snap has the same advantages and requirements. Consequently, building a working kernel first is highly recommended, before migrating this to a kernel snap build. The same configuration options and dependencies will be required.
The main difference between a standard manual kernel build and building a kernel snap is that the latter is built via the snapcraft command, and its accompanying snapcraft.yaml, as outlined below.
Using LXD
Unlike broader snap packages, kernel snaps are typically built within the host environment using snapcraft --destructive-mode. This step can still be isolated from the host system by building the kernel with LXD. To do this, install LXD (if it’s not already installed), instantiate an Ubuntu image (Ubuntu 20.04.4 LTS Focal Fossa), and create the kernel build there:
sudo snap install lxd
sudo lxd init --auto
sudo lxc launch ubuntu:20.04 focal
sudo lxc shell focal
snap install snapcraft --classic
kernel snapcraft.yaml
The Ubuntu Linux kernels include a snapcraft.yaml for building a kernel snap, as do the kernels in the sample-kernels repository.
The following is a breakdown of a typical snapcraft.yaml used to build a kernel snap. It will need to be modified to fit your chosen platform and requirements, starting with the standard global keys and values used by all snaps:
name: kernel-snap-name
version: kernel version
summary: kernel snap quick description
description: kernel snap longer description
grade: stable
confinement: strict
Next comes the snap-type definition to specify that we’re building a kernel snap:
type: kernel
The influence of the host environment, or LXD instance, can be mitigated with the optional build-base
key, used to explicitly define the base snap used for the build environment.
build-base: core18
Similarly, it’s also common to build a kernel for a different architecture. This can be accomplished with architectures. The following example allows for an arm64 kernel to be built from an amd64 host:
architectures:
- build-on: amd64
run-on: arm64
The kernel part
declares the kernel plugin, its arguments, and the kernel build configuration:
parts:
kernel:
plugin: kernel
source: .
source-type: git
kconfigflavour: generic
kconfigs:
- CONFIG_DEBUG_INFO=n
kernel-image-target: Image
kernel-with-firmware: false
The kernel plugin documentation lists the available options, but it’s the kconfigs:
key that lists the kernel configuration options for the kernel snap.
The kernel can optionally be accompanied by a firmware section for bundling the firmware within the snap (the following example taken from the Ubuntu 20.04 kernel):
firmware:
plugin: nil
stage-packages:
- linux-firmware
organize:
lib/firmware: firmware
prime:
- -usr
- -lib
build-packages:
- cpio
- libssl-dev
Building the kernel snap
With snapcraft.yaml complete, and the kernel source either cloned locally or linked to from the snapcraft.yaml, the snapcraft
command will build the kernel. As mentioned earlier, it’s often more convenient to build the kernel within the host environment, using --destructive-mode
:
# snapcraft --destructive-mode --target-arch=arm64
[...]
Snapped kernal-snap-name_arm64.snap
If the above command was executed within an LXD environment, the resultant kernel snap can be extracted with the following commands:
# exit
$ lxc file pull path/to/kernal-snap-name_arm64.snap .
The kernel snap has now been built and is ready to be integrated into an Ubuntu Core image. See Custom images for further details.