Build a kernel snap¶
The kernel snap is one of the essential snaps that need to be specified in the model assertion when building a custom image. The snap can be updated but it cannot be swapped for a different kernel snap.
Canonical publishes several kernel snaps, including ones for certified public clouds, realtime, and general purpose devices such as x86_64 machines and the Raspberry Pi.
A custom Linux kernel build allows for device-specific configuration and modifications, and building the kernel snap has the same advantages and similar requirements.
Before building a kernel snap, we highly recommend building a working kernel first before migrating to a kernel snap. The same configuration options and dependencies will be required.
See Kernel snaps for reference details on the composition of a kernel snap, and see Types of snap for details on the other types of snap that make up an Ubuntu Core image.
Important
Building a kernel snap is useful for prototyping but its maintenance and support becomes your responsibility.
Inside a kernel snap¶
A kernel snap contains the Linux kernel image and its associated modules, alongside a RAM disk image as well as firmware and, on non-x86_64 platforms, device tree files.
Its size and complexity corresponds to the size and complexity of the un-snapped
kernel. The pc-kernel snap for Linux 6.8.0-40, for example, contains over 1400
directories and 9000 files, with the majority of those files being firmware and
kernel modules.
├── config-6.8.0-40-generic
├── doc
├── firmware
├── kernel.efi
├── meta
├── modules
├── snap
└── System.map-6.8.0-40-generic
When a system is using the GRUB bootloader, the kernel and initrd must be
bundled as a Unified Kernel Image
(UKI) and named kernel.efi.
The snap directory includes the snapcraft.yaml
that was used to build the kernel snap.
Canonical’s IoT Devices Field team maintains a GitHub repository
that includes template files in its main branch alongside several
snapcraft.yaml snippets which explain several different ways kernel snaps
can be constructed, as well as example kernel implementations for specific core
releases as branches.
Crafting the snap¶
Snapcraft provides kernel and initrd plugins for creating kernel snaps. Refer to their documentation to understand the available options and features.
The minimal case¶
The most basic kernel snap would be repackaging an available debian package from the archive into a snap. This allows you to control the flavour (generic, lowlatency, etc.) as well as the overall size of the snap by filtering out unnecessary kernel modules.
A minimal snapcraft.yaml that accomplishes this may look like:
name: pc-lowlatency-kernel
adopt-info: kernel
build-base: core24
type: kernel
grade: stable
confinement: strict
summary: An Ubuntu Core lowlatency kernel for x86_64
description: An Ubuntu Core lowlatency kernel for x86_64
parts:
kernel:
plugin: kernel
kernel-ubuntu-binary-package: true
kernel-ubuntu-abinumber: 6.8.0-106
kernel-ubuntu-kconfigflavour: lowlatency
stage-packages:
- linux-firmware:${CRAFT_ARCH_BUILD_FOR}
- wireless-regdb
override-build: |
craftctl default
kver="$(basename "${CRAFT_PART_INSTALL}/lib/modules/"*)"
craftctl set version="$kver"
override-prime: |
craftctl default
"${CRAFT_STAGE}/trim-firmware" "${CRAFT_PRIME}"
prime:
- -lib/modules/*/kernel/fs/9p/
- -vmlinuz*
firmware:
after: [kernel]
plugin: dump
source: https://git.launchpad.net/canonical-kernel-snaps
source-depth: 1
source-type: git
source-branch: main
prime:
- -*
initrd:
after: [firmware, kernel]
plugin: initrd
initrd-build-efi-image: true
initrd-modules: [nls_iso8859-1]
Take special note of the prime: key for the kernel part: the 9p filesystem
kernel modules are filtered out of the final snap. the firmware part provides
a script which will trim the firmware files which aren’t required by any
available kernel modules. Also notably, the nls_iso8859-1.ko kernel module is
builtin to the initrd via the initrd-modules initrd plugin option.
Increasing complexity¶
Of course, the primary motivation for building your own kernel snap would be to perform more complex tasks such as modifying options the kernel itself is built with or patching the code to add new features or test fixes.
Additionally, building your own initrd as part of this process enables you to introduce even more complex feature support, such as leveraging OP-TEE for Full Disk Encryption on non-x86_64 platforms.
For instance the below two parts within a kernel snap’s snapcraft.yaml will
build an entirely different kernel from the standard Canonical ones, trimming
extra kernel modules along the way. It also embeds some key binaries and
libraries within the initrd which are critical for OP-TEE to function properly.
parts:
kernel:
plugin: kernel
source: https://git.launchpad.net/~ondrak/ondras-snaps/+git/linux-kernel
source-depth: 1
source-type: git
source-branch: Ubuntu-imx-6.18.0-1006.6
kernel-ubuntu-kconfigflavour: imx
override-build: |
craftctl default
craftctl set version=$(git describe --tags |\
cut -c 12-42 |\
sed 's/-snap$//')
cp -f "${CRAFT_PART_SRC}/imx-keep.modules" \
"${CRAFT_PART_SRC}/prune-kernel-modules.sh" \
"${CRAFT_PART_INSTALL}/"
override-stage: |
craftctl default
# keep only listed modules
"${CRAFT_STAGE}/prune-kernel-modules.sh" \
"${CRAFT_STAGE}" \
"${CRAFT_STAGE}/imx-keep.modules"
prime:
- lib/
- modules/
initrd:
after: [firmware, kernel, imx-firmware, optee-uc-fde-client]
plugin: initrd
initrd-addons:
- usr/bin/fde-reveal-key
- usr/bin/fde-setup
- usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}/libteec.so*
initrd-firmware:
- imx/sdma/sdma-imx7d.bin
- regulatory.db
- regulatory.db.p7s
prime:
- -initrd.img*
If you already have a known-working kernel, you may well be just a short bit of repackaging to end up with a kernel snap for your hardware platform!
Build the snap¶
The build system must support snapd, and have both the Snapcraft build tool and the LXD virtualisation platform installed, all of which are provided by any Ubuntu release.
Important considerations¶
Depending on the build host, some additional steps may be required for cross-building a kernel snap build to succeed.
The build host must be properly configured to support installing packages and executing binaries for a non-native architecture.
Package support¶
These steps may only be required when building core22 and earlier kernel snaps.
In the case of supporting installing packages for a different architecture the following steps may suffice:
adding the relevant Ubuntu archive for the target architecture,
specifying that the original archive is for the host architecture,
informing dpkg that a foreign architecture is allowed, and
installing the correct libc interpreter
For instance, the below will add support for cross-building arm64 kernel snaps on an amd64 host:
{ echo 'Types: deb'; \
echo 'URIs: http://ports.ubuntu.com/ubuntu-ports'; \
echo 'Suites: jammy jammy-updates jammy-backports'; \
echo 'Components: main universe restricted multiverse'; \
echo 'Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg'; \
echo 'Architectures: arm64'; } >> /etc/apt/sources.list.d/cross.sources
sed -i 's/deb h/deb [arch=amd64] h/' /etc/apt/sources.list
dpkg --add-architecture arm64
apt update
apt install libc6-arm64-cross
ln -sf /usr/aarch64-linux-gnu/ld-linux-aarch64.so.1 /lib
It’s important that these changes be made within the build environment;
Snapcraft’s default behavior is to build within a managed LXD container,
so you may want to build either within some persistent LXD container
with --destructive-mode or use the --debug flag to enter the
Snapcraft-managed container to make these changes.
Execution support¶
The initrd plugin constructs a minimal chroot to emulate the target architecture’s root filesystem. This means that the host machine must support executing binaries built for different architectures. Most modern machines should be capable of this, and the support is enabled by binfmt_misc.
When building in a LXD container, LXD should handle binfmt_misc itself. However, the host machine must install the relevant QEMU packages:
sudo apt install qemu-user-static
sudo apt install binfmt-support qemu-user-binfmt qemu-user
Finally, restart LXD:
snap restart lxd
Building¶
Once the prerequisites have been handled, one can finally build the snap!
# snapcraft --build-for=arm64
[...]
Snapped kernel-snap-name_kernel-snap-version_arm64.snap
The kernel snap has now been built and is ready to be used in an Ubuntu Core image. See Custom images for further details.