RPi Kernel

Details on building a realtime kernel for the Rpi.
linuxcnc seems to recommend the Rpi4 as a minimum

Downloading Sources

Install some dependencies

sudo apt install git bc bison flex libssl-dev make gcc raspberrypi-kernel-headers libc6-dev
sudo apt install patch xz-utils libncurses-dev

To download the sources without the full git repository (as this can take a long time)

# Using kernel 6.3 as an example here
cd /usr/src
# For the latest master
wget https://github.com/raspberrypi/linux/archive/refs/heads/rpi-6.3.y.tar.gz
# For a specific version based on the commit hash (what I'm current;y using)
wget https://github.com/raspberrypi/linux/archive/2d0a7ff52ebfe7bc1c08fd7e444b9ce3d34385bf.tar.gz -O rpi-6.3.y.tar.gz

# Extract the files
tar -xvf rpi-6.3.y.tar.gz
mv linux-rpi-6.3.y linux-6.3.2-rt

Patch Sources

To determine which version the sources are using

head linux-6.3.2-rt/Makefile -n 4
# Given I've specified the git hash above for a specific version, this should be 6.3.2

Download and apply the patch file for the relevant kernel version.

cd /usr/src/
wget https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/6.3/patch-6.3.3-rt15.patch.xz
cd linux-6.3.2-rt
xzcat ../patch-6.3.3-rt15.patch.xz | patch -p1

Note for this specific patch, it may complain while trying to apply the last hunk.
This is due to it already being applied in the kernel sources, so just answer the prompt with 'n'

Configure Sources

Next create a default configuration for the RPI4

# 64bit Rpi4 / Rpi3
make bcm2711_defconfig

Next let's make some configuration changes.

make menuconfig

The main ones to change are:

  • General -> Preemption Model
    switch this to Full Real-time
  • General Setup -> Local Version add something to the end of CONFIG_LOCALVERSION to make it unique.
    Such as -rt-linuxcnc1

Build / Install kernel

Next we're going to build the kernel sources.
Something to remember at this point is if you've already added isolcpus=2,3 to the /boot/cmdline.txt file.
Then it's going to take twice as long to compile due to two of the core's being unavailable for general use.

# Build the kernel - 64bit
make -j4 Image.gz modules dtbs

Next we're going to install the kernel modules

# This typically copies the modules across to a directory such as /lib/modules/6.3.2-rt13-v8-rt-linuxcnc1
make modules_install

Finally, we're going to install the kernel image and other boot related files. The best way to do this is to use a separate directory so that we don't overwrite the default kernel.
We can then just switch back and forth between the default and the realtime kernel using a single config line under /boot/config.txt

In this example we're using the directory /boot/rt-6.3/

# Install the kernel image and boot files - 64bit
mkdir -p /boot/rt-6.3/overlays/
cp arch/arm64/boot/dts/broadcom/*.dtb /boot/rt-6.3/
cp arch/arm64/boot/dts/overlays/*.dtb* /boot/rt-6.3/overlays/
cp arch/arm64/boot/dts/overlays/README /boot/rt-6.3/overlays/
cp arch/arm64/boot/Image.gz /boot/rt-6.3/kernel8.img

Boot Config

Next edit the /boot/config.txt file.

# Add this to the top of the file
os_prefix=/rt-6.3/

This should select the realtime kernel we're ve just installed into the /boot/rt-6.3 directory on the next reboot.
If you need to switch back to the default kernel, just comment out the line and reboot.
To reboot the machine /sbin/reboot
After a reboot, we can check the kernel version with uname -a to make sure it has RT in it's name

32bit / 64bit kernel

For the RPI4 we have kernel8.img for 64 bit and kernel7l.img for 32bit.
It's determined which one to use within config.txt via the arm_64bit setting.
This currently defaults to 1 now for 64bit, unless specifically set to 0 for 32bit.

arm_64bit=1

Reading The Live Kernel Configuration

If you want to read the config from the running kernel
(what was used to compile the kernel)

modprobe configs
zcat /proc/config.gz > ./output