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.
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
- Understand the basic concepts of Toradex Embedded Linux offerings, such as release cycles, distributions and images. This content is available at BSP Layers and Reference Images for Yocto Project Software.
- A pre-built linux image. The kernel from this image will be substituted by your custom one.
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 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
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-aarch64-none-linux-gnu.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu/12.3.rel1/binrel/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz?rev=cf8baa0ef2e54e9286f0409cdda4f66c&hash=4E1BA6BFC2C09EA04DBD36C393C9DD3A"
$ tar xvf arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
$ ln -s arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu gcc-linaro-aarch64
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-64 setup file with the commands below.
export ARCH=arm64
export PATH=${HOME}/gcc-linaro-aarch64/bin/:${PATH}
export DTC_FLAGS='-@'
export CROSS_COMPILE=aarch64-none-linux-gnu-
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-64
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
Clone the corresponding <branch> for your SoC. The branches for each SoC are shown in the table below.
| SoC | Kernel Git Branch |
|---|---|
| NXP i.MX 8/8X/8MM/8MP | toradex_6.6-2.2.x-imx |
| TI AM62x, AM62Px, AM69 | toradex_ti-linux-6.6.y |
$ git clone -b <branch> git://git.toradex.com/linux-toradex.git
$ cd linux-toradex
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.
-
Create a
patchesfolder on the linux directory. Although not mandatory, the patches directory help organize them. This directory will not show on Git because it is in.gitignoreof Linux.$ mkdir ~/linux/patches -
Get the patches from the
meta-toradex-bsp-commonmeta 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 -
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].patchFor a double check, use
git log --onelineto 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:
-
Go to the Artifacts repository: Toradex Artifact Repository Browser.
-
Download the
kernel-configfile 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 pathtdxref-oe-prod-frankfurt
└── <yocto-branch>
└── release
└── <build-number>
└── <SoM>
└── <distro>
└── <image>
└── oedeploy
└── kernel-config -
Source the setup file you created at the Prerequisites section, or execute the
exportcommands described in the file, if you have not done so already. -
Copy the
kernel-configfile into the linux source directory, renaming it to.configand apply its configurations on top of the existing ones:$ cp kernel-config ~/linux && cd ~/linux
$ mv kernel-config .config
$ make olddefconfig$ cp kernel-config ~/linux-toradex && cd ~/linux-toradex
$ mv kernel-config .config
$ make olddefconfigAs an alternative for using the
kernel-configfile 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. -
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
$ make -j$(nproc) Image.gz 2>&1 | tee build.log
$ ls ./arch/arm64/boot/Image.gz
$ make -j$(nproc) zImage 2>&1 | tee build.log
$ ls ./arch/arm/boot/zImage
The Linux kernel for our i.MX7 based modules can show linking issues when using the gold linker:
arm-angstrom-linux-gnueabi-ld: --pic-veneer: unknown option
arm-angstrom-linux-gnueabi-ld: use the --help option for usage information
The recommended solution is to just revert to using the regular bfd linker as follows:
$ make -j$(nproc) zImage LD=${CROSS_COMPILE}ld.bfd | tee build.log
To compile the kernel for BSP 2.8b2 or newer:
$ make -j$(nproc) zImage LOADADDR=10008000 2>&1 | tee build.log
$ ls ./arch/arm/boot/zImage
To compile the kernel:
$ make -j$(nproc) zImage 2>&1 | tee build.log
$ ls ./arch/arm/boot/zImage
To compile the kernel for BSP 2.8 or older:
The Linux kernel for our i.MX 6ULL based modules can show linking issues when using the gold linker:
arm-angstrom-linux-gnueabi-ld: --pic-veneer: unknown option
arm-angstrom-linux-gnueabi-ld: use the --help option for usage information
The recommended solution is to just revert to using the regular bfd linker as follows:
$ make -j$(nproc) zImage LD=${CROSS_COMPILE}ld.bfd | tee build.log
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
$ make DTC_FLAGS="-@" ti/<device-tree>.dtb
$ ls ./arch/arm64/boot/dts/ti/<device-tree>.dtb
$ make DTC_FLAGS="-@" <device-tree>.dtb
$ ls ./arch/arm/boot/dts/<device-tree>.dtb
Deploy the Kernel to an Image
You have two alternatives to deploy your custom kernel:
- Modify a Toradex Easy Installer compatible image to use your custom kernel
- Substitute your custom kernel on a running image
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.
-
Start from a Existing Sample Image: Download one of Toradex's prebuilt images into the
workdiryou created previously. -
Integrate Artifacts: Substitute the downloaded Linux image's artifacts by the ones you built.
-
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 -
Copy your kernel (
Image.gzorzImage) into thebootfsand the kernel modules into/lib/modulesin therootfs. If you built custom device trees, copy them into thebootfsas 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/ -
Compress your custom
rootfsandbootfsonce again, substituting thetar.xzfiles 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
zImageand the<device-tree>.dtbbinaries 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 . -
-
Edit the
image.jsonfile: 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",
...
} -
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