Kernel and Boot Customisation

How to customise the Linux kernel, configuration fragments, and boot-related behaviour in Yocto

Most embedded Linux projects eventually need to adjust the kernel or boot process.

That may mean enabling drivers, applying patches, selecting a kernel provider, adding configuration fragments, or changing how the bootloader and kernel fit together for a specific platform.

This is one of the most hardware-sensitive parts of a Yocto project, so keeping the responsibilities clear matters.

Where Kernel and Boot Customisation Fits

In most projects, kernel and boot changes sit close to the BSP layer because they are usually tied to hardware.

That often includes:

  • selecting the correct kernel recipe
  • choosing the bootloader configuration
  • adding board-specific kernel patches
  • enabling drivers or options through kernel config
  • selecting device trees
  • controlling the image formats used for deployment

These are usually not general product policy decisions. They are answers to a much more specific question: what does this board need in order to boot and run correctly?

Kernel Recipes in Yocto

The Linux kernel is just another BitBake recipe, although it is a very special one.

That means the kernel still fits into the normal Yocto model:

  • a recipe fetches source
  • patches may be applied
  • configuration is prepared
  • the source is built
  • deployable artifacts are emitted

You will often see kernel recipes or selections tied to:

PREFERRED_PROVIDER_virtual/kernel = "linux-myvendor"

This tells BitBake which recipe should provide the kernel for the active machine or platform.

Some kernel customisation usually happens through variables rather than rewriting tasks.

Common examples include:

PREFERRED_PROVIDER_virtual/kernel

Selects which recipe provides the kernel.

KERNEL_DEVICETREE

Selects which device tree blob or blobs should be built and deployed.

SRC_URI

Adds patches, config fragments, or other kernel-related files.

KBUILD_DEFCONFIG

Selects a default kernel defconfig in some kernel workflows.

UBOOT_MACHINE

Selects the U-Boot defconfig where U-Boot is used.

Not every project uses every variable, but this is the kind of metadata you will often touch when adapting Yocto to real hardware.

Configure Versus Patch

One of the most useful habits is knowing when a problem should be solved by configuration and when it should be solved by patching.

Use kernel configuration when:

  • you need to enable or disable a driver
  • you need to switch on a subsystem or option
  • the kernel source already supports what you need

Use a patch when:

  • you need to change kernel source code
  • you need to add support that is not already present
  • you need to fix a bug in the kernel tree you are using

This sounds obvious, but in practice it prevents a lot of unnecessary patching.

If the feature already exists in the kernel, a config fragment is usually a better and more maintainable solution than editing source.

Kernel Configuration Fragments

In Yocto, a common way to customise the kernel is with configuration fragments.

A fragment is a small file containing only the kernel options you need to change.

For example:

CONFIG_CAN=y
CONFIG_CAN_RAW=y
CONFIG_SPI_SPIDEV=y

This is usually cleaner than maintaining one huge monolithic defconfig for every board variation.

Fragments are often added through SRC_URI from a kernel append file.

For example:

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append = " file://myboard.cfg"

This lets you keep your kernel adjustments in your own layer rather than editing the base kernel recipe directly.

Kernel .bbappend Files

In many projects, you do not create a new kernel recipe from scratch.

Instead, an upstream or vendor layer already provides one, and you extend it from your own BSP layer using a .bbappend.

For example:

meta-my-bsp/
└── recipes-kernel/
    └── linux/
        ├── linux-myvendor_%.bbappend
        └── files/
            ├── myboard.cfg
            └── fix-uart.patch

And the append file might contain:

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append = " file://myboard.cfg file://fix-uart.patch"

This is usually the safest pattern because it keeps your changes separate from the vendor or upstream layer.

Device Trees

For many ARM and embedded platforms, device trees are a major part of hardware customisation.

They describe the hardware layout that the kernel should expect, such as:

  • buses
  • peripherals
  • interrupt wiring
  • memory layout
  • enabled or disabled devices

In Yocto, the selected device tree is often controlled with:

KERNEL_DEVICETREE = "vendor/myboard.dtb"

or sometimes several device trees:

KERNEL_DEVICETREE = "vendor/myboard.dtb vendor/myboard-revB.dtb"

If the wrong device tree is selected, the kernel may build successfully but the board may still boot incorrectly or fail to initialise hardware.

Bootloader Configuration

The kernel is only part of the boot path.

Many embedded systems also need bootloader customisation, often through U-Boot.

That may include:

  • selecting the correct bootloader configuration
  • enabling storage or network support
  • setting boot arguments
  • loading the correct kernel and device tree
  • handling secure boot or board-specific boot sequences

A common U-Boot setting is:

UBOOT_MACHINE = "myboard_defconfig"

That tells the build which U-Boot configuration should be used for the target.

Boot Arguments and Board Behaviour

Boot behaviour is often shaped by several pieces working together:

  • the bootloader environment
  • the selected kernel image
  • the selected device tree
  • kernel command line arguments
  • the root filesystem layout

This is why boot problems can be deceptive.

A board that does not reach userspace may not have a kernel bug at all. The issue could be:

  • the wrong bootloader configuration
  • the wrong device tree
  • the wrong root filesystem argument
  • a missing storage driver in the kernel config

Kernel and boot customisation work best when you treat the boot chain as one connected system.

Image Formats and Deployment Artifacts

Some boot-related settings also influence which artifacts Yocto should emit.

For example, a platform may need:

  • a compressed kernel image
  • a raw binary image
  • a wic image for SD card deployment
  • a separate boot partition artifact

These decisions are often tied to the board and deployment method rather than the application layer.

That is why machine and BSP metadata often influence image formats, kernel image types, and boot artifacts.

A Practical Example

Suppose a custom board needs:

  • one extra kernel driver
  • a board-specific device tree
  • a small fix to a vendor kernel source file

A reasonable approach would be:

  1. Keep the vendor kernel recipe as the base.
  2. Add a .bbappend in your own BSP layer.
  3. Add a kernel config fragment for the driver option.
  4. Add a patch for the source fix.
  5. Select the correct device tree in machine metadata.

That is usually much cleaner than copying the whole kernel recipe into your own layer and maintaining a fork immediately.

Keep Board Truth Separate from Product Policy

This module connects directly to the earlier BSP lesson.

A good boundary is:

  • board-specific kernel and boot requirements belong near the BSP
  • product-level policy belongs in the distro or image layer

For example:

  • selecting the board device tree is usually BSP work
  • deciding whether the image should include SSH is not
  • choosing a bootloader defconfig is usually BSP work
  • deciding whether the product uses systemd is usually distro work

If those boundaries stay clear, the project remains easier to understand and reuse.

Prefer Small, Traceable Changes

Kernel and boot work can become difficult to maintain if the changes are too large or too hidden.

Good habits include:

  • use config fragments for kernel options
  • keep patches small and well scoped
  • prefer .bbappend files over editing vendor metadata directly
  • keep device tree selection explicit
  • document why a bootloader or kernel choice exists

This makes upgrades much more manageable later.

Debugging Kernel and Boot Problems

When something goes wrong, it helps to separate build success from boot success.

If the build fails:

  • inspect the kernel recipe logs
  • inspect patch application and config handling
  • check that your fragment or patch is actually included in SRC_URI

If the build succeeds but the board does not boot:

  • verify the selected device tree
  • verify the bootloader configuration
  • verify kernel command line arguments
  • verify required drivers are enabled in the kernel configuration
  • verify the root filesystem and deployment artifact are correct for the board

This is an important mindset shift: a successful bitbake result does not always mean the kernel and boot chain are configured correctly for the hardware.

Summary

Kernel and boot customisation in Yocto is mostly about selecting and extending the right low-level metadata.

The main patterns are:

  • select the right kernel provider
  • use configuration fragments for kernel options
  • use patches only when source changes are truly needed
  • keep board-specific boot choices in BSP or machine metadata
  • extend vendor layers cleanly with .bbappend files

If you keep those changes small, explicit, and hardware-focused, the system is much easier to debug and maintain.

Quick quiz: kernel and boot

A quick review of low-level platform customisation patterns.

Question 1 A driver is already in the kernel source, but your board image still needs one configuration option enabled. What is the least invasive fix?
Question 2 Which of these belongs closest to BSP or machine metadata rather than distro or product policy?
Question 3 Your kernel recipe builds successfully, but the board still does not boot. What is the best next conclusion?