Skip to main content
Version: BSP 7.x.y

Build Linux Kernel from Source Code

Introduction

This article describes how to build the Linux kernel without using a higher-level build system such as the Yocto Project/OpenEmbedded. This procedure mostly makes sense during Linux development.

We provide OpenEmbedded recipes that build U-Boot and Linux as part of a complete BSP image. If you plan to build a full BSP image, follow the Build a Reference Image with Yocto Project/OpenEmbedded article.

This is the second article of a three-part series about building from source code. Check the following articles if you are looking for information about:

This article complies with the Typographic Conventions for Torizon Documentation.

warning

When building the kernel from source code for some System on Modules (SoMs), the graphical driver deployed is different from the one built using the Toradex Linux BSP. This can cause issues with graphical functionality in general. For example, Weston is likely to not work. The affected SoMs are:

  • Apalis iMX8
  • Colibri iMX8X
  • Verdin AM62
  • Verdin AM62P
  • Verdin iMX8M
  • Verdin iMX95

To obtain consistent graphical behavior, we recommend building the kernel with the Yocto Project using our BSP.

Prerequisites

Prepare the Host Machine for Cross-Compilation

Use version 9.2 or higher of the Arm releases binary toolchains to cross-compile software for Toradex modules:

  • For 32 bit Arm: arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-linux-gnueabihf.tar.xz
  • For 64 bit Arm: arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz

You have to choose to download either the 32 bit or 64 bit Arm cross-toolchain, according to the architecture of your System on Module SoC. Select the correct one from the tabs below:

To install the toolchain on your host machine, download and unpack the tar.xz file with the command below. Alternatively, you can obtain the toolchain directly from the Arm website.

$ cd ~
$ wget -O arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-linux-gnueabihf.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu/12.3.rel1/binrel/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-linux-gnueabihf.tar.xz?rev=9d73bfdd64a34550b9ec38a5ead7c01a&hash=774AAE1A6D6996CFB89FD7E367C0B59B"
$ tar xvf arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-linux-gnueabihf.tar.xz
$ ln -s arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-linux-gnueabihf gcc-linaro-arm

U-Boot and Linux Makefiles use environment variables such as ARCH and CROSS_COMPILE to configure and call the appropriate compiler. Therefore, you must set these variables in the current shell when compiling the Kernel or U-Boot.

To facilitate this process, create an export-compiler-32 setup file with the commands below.

export-compiler-32
export ARCH=arm
export PATH=${HOME}/gcc-linaro-arm/bin/:${PATH}
export DTC_FLAGS='-@'
export CROSS_COMPILE=arm-none-linux-gnueabihf-

Please note that creating the setup file is not mandatory, it serves only to simplify executing the commands above. You could also run each command individually on every new shell.

Source the setup file. Note that you will also need to source it every time you open a new shell to build the Kernel/U-Boot.

$ source ~/export-compiler-32

Install Tools and Dependencies

Install the essential build tools to compile the Kernel:

$ sudo apt-get install bc build-essential git libncurses5-dev lzop perl libssl-dev bison flex kmod device-tree-compiler

Download the Kernel Source

Create a directory to store the necessary repositories for building and deploying the kernel:

$ mkdir ~/workdir
$ cd ~/workdir

Use Git to clone the kernel source code, which differs depending on the System on Module used. Refer to our Embedded Linux Release Matrix and check the following information:

  • If your SoM uses the Downstream or Upstream kernel;
  • The corresponding kernel <branch> (for Downstream) or <version> (for Upstream) associated with the BSP version used in your image.

According to above specifications for your module, clone the linux kernel with the command below:

Clone the corresponding <version> for your BSP version.

$ git clone -b <version> git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
$ cd linux

Get and Apply the Patches (upstream only)

If you are using using an upstream-based module, follow the steps below to apply the necessary patches to the kernel. If you are using a downstream-based module, advance to the Kernel Configuration section.

  1. Create a patches folder on the linux directory. Although not mandatory, the patches directory help organize them. This directory will not show on Git because it is in .gitignore of Linux.

    $ mkdir ~/linux/patches
  2. Get the patches from the meta-toradex-bsp-common meta layer. Fetch the layer with Git and change to the hash of the BSP release version and then copy the patches to Linux patches directory.

    $ git clone https://git.toradex.com/cgit/meta-toradex-bsp-common.git
    $ cd meta-toradex-bsp-common
    $ git checkout <bsp-hash>
    $ cp -r ../meta-toradex-bsp-common/recipes-kernel/linux/linux-toradex-upstream/* ../patches
  3. Apply the patches using the command below. The patches must be applied in the correct order (check the TDX_PATCHES variable of this recipe).

    $ cd ~/linux/patches

    $ git am [first-patch-file-name].patch \
    [second-patch-file-name].patch \
    [third-patch-file-name].patch \
    [fourth-patch-file-name].patch \
    [fifth-patch-file-name].patch

    For a double check, use git log --oneline to see the patches commit messages.

    $ git log --oneline

Configure the Kernel

The file kernel-config contains the kernel configurations needed for Toradex SoMs. Follow the steps below to obtain the kernel-config file:

  1. Go to the Artifacts repository: Toradex Artifact Repository Browser.

  2. Download the kernel-config file at the location below.

    The directories specified between <> depend on your module and the linux pre-built image you prepared in the Prerequisites. To understand how to identify the <yocto-branch>, <distro> and <image> directories for your image, refer to our BSP Documentation. The <build-number> appears at the end of your pre-built image's directory as +build.<build-number>.

    kernel-config file path
    tdxref-oe-prod-frankfurt
    └── <yocto-branch>
    └── release
    └── <build-number>
    └── <SoM>
    └── <distro>
    └── <image>
    └── oedeploy
    └── kernel-config
  3. Source the setup file you created at the Prerequisites section, or execute the export commands described in the file, if you have not done so already.

  4. Copy the kernel-config file into the linux source directory, renaming it to .config and apply its configurations on top of the existing ones:

    $ cp kernel-config ~/linux && cd ~/linux 
    $ mv kernel-config .config
    $ make olddefconfig

    As an alternative for using the kernel-config file directly, you can also follow the instructions at the Linux Toradex Kernel Configuration Repository to run a script that will automatically apply the required configurations for your machine.

  5. If you wish to, modify the configuration options for your specific use-case. Run the command below to open an interactive interface and modify the configuration as you wish.

    $ make menuconfig

Build the Kernel

The kernel compilation process generates a single binary kernel file (typically named Image.gz or zImage). However, the kernel also has kernel modules and device trees as runtime dependencies:

  • Kernel Modules: Extensions to the kernel that can be loaded or unloaded at runtime. You must build and deploy the kernel modules for your custom kernel, otherwise the system will not boot correctly. This is because the kernel will only load the modules that mach its version perfectly.
  • Device Trees: Tree data structures that provide configuration details to the kernel modules about the devices that compose your system. They are required for the kernel to function properly, however, since the deployment process is done to a working system, they are already present, and you may not need to rebuild them on every kernel build. For more details on device trees, refer to our Device Tree documentation.

Compile the Kernel

Follow the specific steps for the System on Chip (SoC) present on your SoM:

$ make -j$(nproc) Image.gz 2>&1 | tee build.log
$ ls ./arch/arm64/boot/Image.gz

Compile the Kernel Modules

Run the commands below to compile the kernel modules for your previously built kernel.

$ make -j$(nproc) modules
$ make modules_install INSTALL_MOD_PATH=modules
$ ls modules/lib/modules/<kernel-version>

In the running system, the kernel modules are located at the /lib/modules/<kernel-version> directory. To assure module compatibility, the Kernel refuses to load modules with a <kernel-version> that does not match its own. Thus, you need to compile and deploy the kernel modules together with the Kernel in order to use them.

The source code for out-of-tree modules (such as GPU-related drivers) is stored externally from the kernel source tree. Therefore, to add these specific modules, you need to fetch the respective repositories, compile the modules specifically against your kernel version, and deploy the compiled binaries to the lib/modules/<kernel-version> directory.

For additional information on building external kernel modules, please refer to the Linux Kernel documentation.

Compile the Device Trees

As explained in Build the Kernel, this step may not be necessary for your use-case.

Follow the steps below for your specific SoC to compile the device trees. Replace <device-tree> by the Device Tree binary for your specific configuration. The <device-tree> should be the device tree binary (.dtb) correspondent to the device tree source (.dts) your SoM and Carrier Board (or your own custom device tree, if you wrote one). As an example, for a Verdin AM62 module on a mallow carrier board, the device tree binary would be k3-am625-verdin-wifi-mallow.dtb.

$ make DTC_FLAGS="-@" freescale/<device-tree>.dtb
$ ls ./arch/arm64/boot/dts/freescale/<device-tree>.dtb

Deploy the Kernel to an Image

You have two alternatives to deploy your custom kernel:

Each one of those options is detailed as follows:

Deploy to a Toradex Easy Installer Image

Follow the steps below to update your Kernel using Toradex Easy Installer.

  1. Start from a Existing Sample Image: Download one of Toradex's prebuilt images into the workdir you created previously.

  2. Integrate Artifacts: Substitute the downloaded Linux image's artifacts by the ones you built.

    1. Change into the directory where you downloaded the image and uncompress it. Also uncompress the root and boot file systems into separate directories:

      $ cd ~/workdir
      $ mkdir bootfs rootfs
      $ tar xvf <Machine>_<image>-Tezi_version+build.<build-number>.tar
      $ tar xvf <Machine>_<image>-Tezi_version+build.<build-number>/<image>-<machine>.rootfs.bootfs.tar.xz -C bootfs
      $ tar xvf <Machine>_<image>-Tezi_version+build.<build-number>/<image>-<machine>.rootfs.tar.xz -C rootfs
    2. Copy your kernel (Image.gz or zImage) into the bootfs and the kernel modules into /lib/modules in the rootfs. If you built custom device trees, copy them into the bootfs as well:

      $ cp <linux-directory>/arch/<architecture>/boot/<Image> bootfs/
      $ cp <linux-directory>/arch/<architecture>/boot/dts/<soc-vendor>/*.dtb bootfs/
      $ cp -r <linux-directory>/modules/lib/modules/<kernel-version> rootfs/usr/lib/modules/
    3. Compress your custom rootfs and bootfs once again, substituting the tar.xz files inside the image:

      $ tar -C bootfs -cJvf <Machine>_<image>-Tezi_version+build.<build-number>/<image>-<machine>.rootfs.bootfs.tar.xz .
      $ tar -C rootfs -cJvf <Machine>_<image>-Tezi_version+build.<build-number>/<image>-<machine>.rootfs.tar.xz .

      In case of using a Colibri iMX7 NAND version or a Colibri iMX6ULL NAND version, replace the zImage and the <device-tree>.dtb binaries directly in the downloaded embedded Linux image folder.

    As an example, check below the complete process for substituting the kernel for your custom one in a Verdin AM62 Reference Image:

    Deploying a Custom Kernel to a Verdin AM62 Reference Image
    $ mkdir bootfs rootfs
    $ tar xvf Verdin-AM62_Reference-Multimedia-Image-Tezi_7.3.0+build.8.tar
    $ tar xvf Verdin-AM62_Reference-Multimedia-Image-Tezi_7.3.0+build.8/Reference-Multimedia-Image-verdin-am62.rootfs.bootfs.tar.xz -C bootfs
    $ tar xvf Verdin-AM62_Reference-Multimedia-Image-Tezi_7.3.0+build.8/Reference-Multimedia-Image-verdin-am62.rootfs.tar.xz -C rootfs

    $ cp linux-toradex/arch/arm64/boot/Image.gz bootfs/
    $ cp linux-toradex/arch/arm64/boot/dts/ti/*.dtb bootfs/
    $ cp -r linux-toradex/modules/lib/modules/6.6.94+/ rootfs/usr/lib/modules/

    $ tar -C bootfs -cJvf Verdin-AM62_Reference-Multimedia-Image-Tezi_7.3.0+build.8/Reference-Multimedia-Image-verdin-am62.rootfs.bootfs.tar.xz .
    $ tar -C rootfs -cJvf Verdin-AM62_Reference-Multimedia-Image-Tezi_7.3.0+build.8/Reference-Multimedia-Image-verdin-am62.rootfs.tar.xz .
  3. Edit the image.json file: You can give the image an identifiable name by changing the field name on this JSON file:

    Verdin-AM62_Reference-Multimedia-Image-Tezi_7.3.0+build.8/image.json
    {
    ...
    "name": "My Custom Kernel Image",
    ...
    }
  4. Deploy your Image: Follow the steps on the 'Flash the image with Toradex Easy Installer' article

Deploy to a Running Image

To deploy your custom kernel to a running device, you will need an SSH connection to your board.

In a running image, both the kernel and the device trees are both located at the /boot directory, and the kernel modules are located at /lib/modules.

Copy your custom artifacts into their respective locations on the board:

$ cd ~/workdir
$ scp <linux-directory>/arch/<architecture>/boot/<Image> root@<target-ip>:/boot/
$ scp <linux-directory>/arch/<architecture>/boot/dts/<soc-vendor>/*.dtb root@<target-ip>:/boot/
$ scp -r <linux-directory>/modules/lib/modules/<kernel-version> root@<target-ip>:/lib/modules/

Reboot the board into your custom kernel:

# reboot
Send Feedback!