Skip to content

Instantly share code, notes, and snippets.

@satmandu
Last active September 25, 2024 03:44
Show Gist options
  • Save satmandu/a507c59d84737f6d29ff353395819d51 to your computer and use it in GitHub Desktop.
Save satmandu/a507c59d84737f6d29ff353395819d51 to your computer and use it in GitHub Desktop.
Make arm64 deb packages for the offical Raspberry Pi Foundation arm64 kernels, tested with ubuntu 23.04
#!/bin/bash -x
# make_arm64_rpi_kernel_debs.sh
# Builds arm64 debian packages from the CURRENT rpi firmware repository kernel which is installed by:
# sudo rpi-update
# This runs on an arm64 host with arm64 compilation tools...
# or with some sort of cross-compilation setup.
# Debs are put in $workdir/build
#
# This will NOT work in Raspbian unless you have an arm64 compilation
# environment setup. Appears to work on
# Raspberry Pi OS (64 bit) beta test version
#
#
#
# Install packages you should probably have if you are needing to install kernel headers.
arch=$(uname -m)
if [[ $arch == 'aarch64' ]]; then
echo ""
else
echo "This needs to be run when booted into 64-bit mode."
exit 1
fi
# sudo apt install -f -y build-essential flex gawk bison libssl-dev bc dkms autoconf libtool || (sudo apt install -f -y || true)
workdir="${HOME}/workdir"
[[ ! -d "$workdir" ]] && ( mkdir -p "$workdir" || exit 1)
[[ ! -d "$workdir"/tmp ]] && ( mkdir -p "$workdir"/tmp || exit 1)
[[ ! -d "$workdir"/build ]] && ( mkdir -p "$workdir"/build || exit 1)
echo "workdir is ${workdir}"
tmpdir=$(mktemp -d deb_XXXX -p "$workdir"/tmp)
echo "tmpdir is ${tmpdir}"
dhpath="$tmpdir/headers"
dipath="$tmpdir/image"
src_temp=$(mktemp -d rpi_src_XXXi -p "$workdir"/tmp)
git_base="https://github.com/raspberrypi/rpi-firmware"
git_branch="master"
FIRMWARE_REV=$(git ls-remote "https://github.com/raspberrypi/rpi-firmware" refs/heads/$git_branch | awk '{print $1}')
cd "$src_temp" && curl -OLf https://github.com/raspberrypi/rpi-firmware/raw/$git_branch/git_hash
KERNEL_REV=$(cat "$src_temp"/git_hash)
SHORT_HASH=$(echo ${KERNEL_REV:0:7})
setup_git_fw() {
if [[ -d "$workdir/rpi-firmware" ]]; then
( sudo rm -rf "$workdir"/rpi-firmware.old || true )
( sudo mv "$workdir"/rpi-firmware "$workdir"/rpi-firmware.old || true )
( sudo rm -rf "$workdir"/rpi-firmware.old || true )
fi
cd "$workdir" && git clone --depth=1 -b $git_branch $git_base
}
update_git_fw() {
[[ ! -d "$workdir/rpi-firmware" ]] && setup_git_fw
( cd "$workdir"/rpi-firmware && git fetch && git reset --hard origin/$git_branch ) || setup_git_fw
cd "$workdir"/rpi-firmware && git config pull.ff only
cd "$workdir"/rpi-firmware && git pull
#cd "$workdir"/rpi-firmware && git_hash=$(git rev-parse origin/$git_branch)
}
check_zfs() {
# Install zfs prerequisites
sudo apt install -f -y autoconf libtool uuid-dev libudev-dev \
libssl-dev zlib1g-dev libaio-dev libattr1-dev python3 python3-dev \
python3-setuptools autoconf automake libtool gawk dkms libblkid-dev \
uuid-dev libudev-dev libssl-dev libelf-dev python3-cffi libffi-dev || true
}
make_headers_deb_files() {
installed_size_headers=$(du -a "$dhpath" | tail -n 1 | awk '{print $1}')
mkdir -p "$dhpath"/DEBIAN
chmod 777 "$dhpath"/DEBIAN
cat <<-EOF | dd status=none of="$dhpath"/DEBIAN/control
Source: linux-$kver
Section: kernel
Priority: optional
Maintainer: root <root@$SHORT_HASH>
Standards-Version: 4.1.3
Homepage: http://www.kernel.org/
Package: linux-headers-$kver
Architecture: all
Version: $kver-1
Depends: build-essential, flex, bison, bc
Installed-Size: $installed_size_headers
Description: Linux kernel headers for $kver on arm64
This package provides kernel header files for $kver on arm64
built from:
https://github.com/raspberrypi/linux/tree/$FIRMWARE_REV
This is useful for people who need to build external modules
EOF
cat <<-EOF | dd status=none of="$dhpath"/DEBIAN/preinst
#!/bin/sh
set -e
version=$kver
if [ "\$1" = abort-upgrade ]; then
exit 0
fi
if [ "\$1" = install ]; then
mkdir -p /lib/modules/\$version
mkdir -p /usr/src/linux-headers-\$version || true
cd /lib/modules/\$version && ln -snrvf /usr/src/linux-headers-\$version build
fi
if [ -d /etc/kernel/header_preinst.d ]; then
DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
--arg=\$image_path /etc/kernel/header_preinst.d
fi
exit 0
EOF
chmod +x "$dhpath"/DEBIAN/preinst
cat <<-EOF | dd status=none of="$dhpath"/DEBIAN/postinst
#!/bin/bash
set -e
version=$kver
[[ -f /etc/environment ]] && . /etc/environment
if [ "\$1" != configure ]; then
exit 0
fi
if [ -d /etc/kernel/header_postinst.d ]; then
DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
/etc/kernel/header_postinst.d
fi
exit 0
EOF
chmod +x "$dhpath"/DEBIAN/postinst
chmod -R 0755 "$dhpath"/DEBIAN
cd "$tmpdir" && sudo nice -n 20 dpkg-deb -b headers/
sudo mv "$tmpdir"/headers.deb "$workdir"/build/linux-headers-"${kver}"_all.deb
}
make_image_deb_files() {
installed_size_image=$(du -a "$dipath" | tail -n 1 | awk '{print $1}')
mkdir -p "$dipath"/DEBIAN
chmod 777 "$dipath"/DEBIAN
cat <<-EOF | dd status=none of="$dipath"/DEBIAN/control
Package: linux-image-$kver
Source: linux-$kver
Version: $kver-1
Architecture: arm64
Maintainer: root <root@$SHORT_HASH>
Installed-Size: $installed_size_image
Section: kernel
Priority: optional
Homepage: http://www.kernel.org/
Description: Linux kernel, version $kver
This package contains the Linux kernel, modules and corresponding other
files, version: $kver.
EOF
cat <<-EOFF | dd status=none of="$dipath"/DEBIAN/postinst
#!/bin/bash
set -e
version=$kver
image_path=/boot/vmlinuz-\$version
bootfsmount=\$(mount | grep boot | awk '{print \$3}')
[[ \$(echo \$bootfsmount | wc -w) == "1" ]] && BOOTFS=\${bootfsmount}
BOOTFS=\${BOOTFS:-/boot}
# Install kernel (This avoids an issue if /boot is fat32.)
mount -o remount,rw /boot 2>/dev/null || true
cp /usr/share/rpikernelhack/vmlinuz-"$kver" \$image_path || true
# If custom kernel= line is being used don't replace kernel8.img,
# overlays, or dtb files.
if ! vcgencmd get_config str | grep -q kernel ; then
cp /usr/share/rpikernelhack/vmlinuz-"$kver" /boot/kernel8.img
fi
cp /usr/lib/linux-image-"$kver"/broadcom/*.dtb \$BOOTFS/
cp /usr/lib/linux-image-"$kver"/overlays/* \$BOOTFS/overlays/
# This is needed to keep flash-kernel from throwing an error.
cp /usr/lib/linux-image-"$kver"/broadcom/*.dtb /etc/flash-kernel/dtbs/
# Make sure flash-kernel db allows for any kernel flavor
if ! grep -q 'Kernel-Flavors: any' /etc/flash-kernel/db ; then
mkdir -p /etc/flash-kernel/
cat <<FLASHDBRPI4 >> /etc/flash-kernel/db
Machine: Raspberry Pi 4 Model B
Machine: Raspberry Pi 4 Model B Rev 1.1
Machine: Raspberry Pi 4 Model B Rev 1.2
Machine: Raspberry Pi 4 Model B Rev 1.4
Machine: Raspberry Pi Compute Module 4 Rev 1.0
Machine: Raspberry Pi 400 Rev 1.0
Machine: Raspberry Pi 400 Rev 1.1
Machine: Raspberry Pi *
Kernel-Flavors: any
FLASHDBRPI4
fi
#
# When we install linux-image we have to run kernel postinst.d support to
# generate the initramfs, create links etc. Should it have an associated
# linux-image-extra package and we install that we also need to run kernel
# postinst.d, to regenerate the initramfs. If we are installing both at the
# same time, we necessarily trigger kernel postinst.d twice. As this includes
# rebuilding the initramfs and reconfiguring the boot loader this is very time
# consuming.
#
# Similarly for removal when we remove the linux-image-extra package we need to
# run kernel postinst.d handling in order to pare down the initramfs to
# linux-image contents only. When we remove the linux-image need to remove the
# now redundant initramfs. If we are removing both at the same time, then
# we will rebuilt the initramfs and then immediatly remove it.
#
# Switches to using a trigger against the linux-image package for all
# postinst.d and postrm.d handling. On installation postinst.d gets triggered
# twice once by linux-image and once by linux-image-extra. As triggers are
# non-cumulative we will only run this processing once. When removing both
# packages we will trigger postinst.d from linux-image-extra and then in
# linux-image postrm.d we effectivly ignore the pending trigger and simply run
# the postrm.d. This prevents us from rebuilding the initramfs.
#
if [ "\$1" = triggered ]; then
trigger=/usr/lib/linux/triggers/\$version
if [ -f "\$trigger" ]; then
sh "\$trigger"
rm -f "\$trigger"
fi
exit 0
fi
if [ "\$1" != configure ]; then
exit 0
fi
depmod \$version
if [ -f /lib/modules/\$version/.fresh-install ]; then
change=install
else
change=upgrade
fi
# linux-update-symlinks \$change \$version \$image_path
rm -f /lib/modules/\$version/.fresh-install
if [ -d /etc/kernel/postinst.d ]; then
mkdir -p /usr/lib/linux/triggers
cat - >/usr/lib/linux/triggers/\$version <<EOF
DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
--arg=\$image_path /etc/kernel/postinst.d
EOF
dpkg-trigger --no-await linux-update-\$version
fi
exit 0
EOFF
chmod +x "$dipath"/DEBIAN/postinst
cat <<-EOF | dd status=none of="$dipath"/DEBIAN/triggers
interest linux-update-$kver
EOF
cat <<-EOF | dd status=none of="$dipath"/DEBIAN/postrm
#!/bin/sh
set -e
version=$kver
image_path=/boot/vmlinuz-\$version
rm -f /lib/modules/\$version/.fresh-install
#if [ "\$1" != upgrade ] && command -v linux-update-symlinks >/dev/null; then
# linux-update-symlinks remove \$version \$image_path
#fi
if [ -d /etc/kernel/postrm.d ]; then
# We cannot trigger ourselves as at the end of this we will no longer
# exist and can no longer respond to the trigger. The trigger would
# then become lost. Therefore we clear any pending trigger and apply
# postrm directly.
if [ -f /usr/lib/linux/triggers/\$version ]; then
echo "\$0 ... removing pending trigger"
rm -f /usr/lib/linux/triggers/\$version
fi
DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
--arg=\$image_path /etc/kernel/postrm.d
fi
if [ "\$1" = purge ]; then
for extra_file in modules.dep modules.isapnpmap modules.pcimap \\
modules.usbmap modules.parportmap \\
modules.generic_string modules.ieee1394map \\
modules.ieee1394map modules.pnpbiosmap \\
modules.alias modules.ccwmap modules.inputmap \\
modules.symbols modules.ofmap \\
modules.seriomap modules.\\*.bin \\
modules.softdep modules.devname; do
eval rm -f /lib/modules/\$version/\$extra_file
done
rmdir /lib/modules/\$version || true
fi
exit 0
EOF
chmod +x "$dipath"/DEBIAN/postrm
cat <<-EOF | dd status=none of="$dipath"/DEBIAN/preinst
#!/bin/sh
set -e
version=$kver
image_path=/boot/vmlinuz-\$version
if [ "\$1" = abort-upgrade ]; then
exit 0
fi
if [ "\$1" = install ]; then
# Create a flag file for postinst
mkdir -p /lib/modules/\$version
touch /lib/modules/\$version/.fresh-install
fi
if [ -d /etc/kernel/preinst.d ]; then
DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
--arg=\$image_path /etc/kernel/preinst.d
fi
if [ ! -e /lib/modules/\$version/build ]; then
mkdir -p /usr/src/linux-headers-\$version || true
cd /lib/modules/\$version && ln -snrvf /usr/src/linux-headers-\$version build || true
fi
exit 0
EOF
chmod +x "$dipath"/DEBIAN/preinst
cat <<-EOF | dd status=none of="$dipath"/DEBIAN/prerm
#!/bin/sh
set -e
version=$kver
image_path=/boot/vmlinuz-\$version
if [ "\$1" != remove ]; then
exit 0
fi
linux-check-removal \$version
if [ -d /etc/kernel/prerm.d ]; then
DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
--arg=\$image_path /etc/kernel/prerm.d
fi
exit 0
EOF
chmod +x "$dipath"/DEBIAN/prerm
chmod -R 0755 "$dipath"/DEBIAN
cd "$tmpdir" && sudo nice -n 20 dpkg-deb -b image/
sudo mv "$tmpdir"/image.deb "$workdir"/build/linux-image-"${kver}"_arm64.deb
}
make_debs() {
cd "$src_temp" && curl -L https://github.com/raspberrypi/linux/archive/"${KERNEL_REV}".tar.gz >rpi-linux.tar.gz
cd "$src_temp" && curl --retry 3 -OL "https://github.com/raspberrypi/rpi-firmware/raw/${FIRMWARE_REV}/Module8.symvers"
mv $src_temp/Module8.symvers $src_temp/Module.symvers
kver=$(find "$workdir"/rpi-firmware/modules/ -type d -name '*v8+' -printf "%P\n")
l=$kver
# Build kernel header package
# Adapted from scripts/package/builddeb
mkdir -p $src_temp/header_tmp/debian
cd $src_temp/header_tmp && tar --strip-components 1 -xf "${src_temp}"/rpi-linux.tar.gz
SRCARCH=arm64
(cd $src_temp/header_tmp; cp $src_temp/header_tmp/arch/arm64/configs/bcm2711_defconfig $src_temp/header_tmp/.config) # copy .config manually to be where it's expected to be
cd "$src_temp/header_tmp" && (yes "" | sudo make oldconfig)
mkdir -p "$dhpath"/boot
cp "$src_temp/header_tmp"/.config "$dhpath"/boot/config-"${kver}"
cd "$src_temp/header_tmp" && (yes "" | sudo make modules_prepare) && sudo chown -R `id -u` .
#cd "$src_temp/header_tmp" && (yes "" | sudo make scripts) && sudo chown -R `id -u` .
(cd $src_temp/header_tmp; find . -name Makefile\* -o -name Kconfig\* -o -name \*.pl) > "$src_temp/header_tmp/debian/hdrsrcfiles"
(cd $src_temp/header_tmp; find arch/*/include include scripts -type f -o -type l) >> "$src_temp/header_tmp/debian/hdrsrcfiles"
(cd $src_temp/header_tmp; find arch/$SRCARCH -name module.lds -o -name Kbuild.platforms -o -name Platform) >> "$src_temp/header_tmp/debian/hdrsrcfiles"
(cd $src_temp/header_tmp; find $(find arch/$SRCARCH -name include -o -name scripts -type d) -type f) >> "$src_temp/header_tmp/debian/hdrsrcfiles"
(cd $src_temp/header_tmp; find tools/objtool -type f -executable) >> "$src_temp/header_tmp/debian/hdrobjfiles"
(cd $src_temp/header_tmp; find arch/$SRCARCH/include modules.builtin.modinfo Module.symvers include scripts -type f) >> "$src_temp/header_tmp/debian/hdrobjfiles"
(cd $src_temp/header_tmp; find scripts/gcc-plugins -name \*.so -o -name gcc-common.h) >> "$src_temp/header_tmp/debian/hdrobjfiles"
destdir="$dhpath"/usr/src/linux-headers-$kver
mkdir -p "$destdir"
(cd $src_temp/header_tmp; tar -c -f - -T -) < "$src_temp/header_tmp/debian/hdrsrcfiles" | (cd $destdir; tar -xf -)
(cd $src_temp/header_tmp; tar -c -f - -T -) < "$src_temp/header_tmp/debian/hdrobjfiles" | (cd $destdir; tar -xf -)
(cd $src_temp/header_tmp; cp $src_temp/header_tmp/arch/arm64/configs/bcm2711_defconfig $destdir/.config) # copy .config manually to be where it's expected to be
rm -rf "$src_temp/header_tmp/debian/hdrsrcfiles" "$src_temp/header_tmp/debian/hdrobjfiles"
cp "$src_temp"/Module.symvers "$destdir"/Module.symvers
make_headers_deb_files
mkdir -p "$dipath"/usr/share/rpikernelhack/
cp "$workdir"/rpi-firmware/kernel8.img "$dipath"/usr/share/rpikernelhack/vmlinuz-"$l"
mkdir -p "$dipath"/lib/modules/
cp -r "$workdir"/rpi-firmware/modules/"$l" "$dipath"/lib/modules/
mkdir -p "$dipath"/usr/lib/linux-image-"$l"/broadcom && mkdir -p "$dipath"/usr/lib/linux-image-"$l"/overlays
cp -f "$workdir"/rpi-firmware/*.dtb "$dipath"/usr/lib/linux-image-"$l"/broadcom/
cp -f "$workdir"/rpi-firmware/overlays/* "$dipath"/usr/lib/linux-image-"$l"/overlays/
[[ ! -e "$dipath/lib/firmware/$l/device-tree" ]] && mkdir -p "$dipath"/lib/firmware/"$l"/device-tree
make_image_deb_files
# Clean up.
cd "$workdir"
sudo rm -rf "$src_temp"
sudo rm -rf "$tmpdir"
}
install_headers() {
if [[ $(uname -m) == "aarch64" ]]; then
bootfsmount=$(mount | grep boot | awk '{print $3}')
[[ $(echo $bootfsmount | wc -w) == "1" ]] && BOOTFS=${bootfsmount}
BOOTFS=${BOOTFS:-/boot}
sudo mount -o remount,rw $BOOTFS
fi
sudo nice -n 20 dpkg -i "$workdir"/build/linux-headers-"${kver}"*.deb || sudo apt install -f -y
}
install_image() {
if [[ $(uname -m) == "aarch64" ]]; then
bootfsmount=$(mount | grep boot | awk '{print $3}')
[[ $(echo $bootfsmount | wc -w) == "1" ]] && BOOTFS=${bootfsmount}
BOOTFS=${BOOTFS:-/boot}
sudo mount -o remount,rw $BOOTFS
fi
sudo nice -n 20 dpkg -i "$workdir"/build/linux-image-"${kver}"*.deb || sudo apt install -f -y
}
update_git_fw
make_debs
mkdir -p "$workdir"/kernel-test
cp "$workdir"/build/linux-image-"${kver}"*.deb "$workdir"/kernel-test/
cp "$workdir"/build/linux-headers-"${kver}"*.deb "$workdir"/kernel-test/
install_headers
install_image
#sudo flash-image --force ${kver}
#trap '' HUP
#if [[ $(uname -m) == "aarch64" ]]; then
#bootfsmount=$(mount | grep boot | awk '{print $3}')
#[[ $(echo $bootfsmount | wc -w) == "1" ]] && BOOTFS=${bootfsmount}
#BOOTFS=${BOOTFS:-/boot}
#sudo mount -o remount,rw $BOOTFS
#fi
#mkdir "$workdir"/bin
#curl -Lf https://github.com/raspberrypi/rpi-update/raw/master/rpi-update -o "$workdir"/bin/rpi-update && chmod +x "$workdir"/bin/rpi-update
#sudo touch $BOOTFS/kernel7l.img && sudo WANT_32BIT=0 WANT_64BIT=1 WANT_PI4=1 SKIP_WARNING=1 BOOT_PATH=$BOOTFS ROOT_PATH=/ BRANCH=master "$workdir"/bin/rpi-update
echo "done."
@satmandu
Copy link
Author

Now updated to create a headers package with just the headers, and not the entire kernel source. :/

@heini
Copy link

heini commented Sep 20, 2020

I really like these ones:

cat <<-EOF | dd status=none of=/some/file

Never seen a construct like this before, since I only use

cat >/some/file <<-EOF

What's the sense of using | dd here?

@satmandu
Copy link
Author

I really like these ones:

cat <<-EOF | dd status=none of=/some/file

Never seen a construct like this before, since I only use

cat >/some/file <<-EOF

What's the sense of using | dd here?

If you're tailing the output of the script, it is nice not to have all the cat concatenations also echoed back into the tail output. Using dd avoids this, whereas using cat or tee does not!

@heini
Copy link

heini commented Sep 20, 2020

OK. Thanks a lot for clarifying.
Script works fine, btw. Could finally install openafs-modules-dkms.

@satmandu
Copy link
Author

OK. Thanks a lot for clarifying.
Script works fine, btw. Could finally install openafs-modules-dkms.

NIce. I was also successful in getting openafs-modules-dkms to install.

@geerlingguy
Copy link

FYI, if you're using 64-bit beta Pi OS 'lite' version, you have to manually install git before running this gist script... that was tripping me up as there aren't any obvious errors in the script and it completes with some strange errors caused the the initial clone not working.

@satmandu
Copy link
Author

FYI, if you're using 64-bit beta Pi OS 'lite' version, you have to manually install git before running this gist script... that was tripping me up as there aren't any obvious errors in the script and it completes with some strange errors caused the the initial clone not working.

Thanks! I'll add that!

@benni-tec
Copy link

benni-tec commented Mar 24, 2022

Hey, this script seems to do what I need. I want to install v4l2-loopback but 'am missing header files.

As I understand it this script builds headers for the most current version. My problem is that uname -r shows I'm running 5.10.103-v8+ however the script builds headers for 5.10.73-v8+ which to me seems like a lower version.

Is this a bug or does this mean the script is outdated?
Any help would be greatly appriciated!

Secondly I was wondering, if it is relativly simple to build these headers shouldn't it be possible to make these available through a raspberrypi-kernel-headers style package for 64-bit raspbian?

EDIT: welp I'm an idiot. I didn't install the image.deb along with the headers. v4l2loopback now shows up in modprobe. However the raspberrypi-camera is gone. Anyway I belive this isn't really the solution to my problem since I think I just changed kernels (which is an older version than before)

@satmandu
Copy link
Author

Here is my current version, which builds the 5.15 kernel images...

#!/bin/bash -x
# make_arm64_rpi_kernel_debs.sh
# Builds arm64 debian packages from the CURRENT rpi firmware repository kernel which is installed by:
# sudo rpi-update
# This runs on an arm64 host with arm64 compilation tools... 
# or with some sort of cross-compilation setup.
# Debs are put in $workdir/build
#
# This will NOT work in Raspbian unless you have an arm64 compilation
# environment setup. Appears to work on
# Raspberry Pi OS (64 bit) beta test version
#
#
#
# Install packages you should probably have if you are needing to install kernel headers.
arch=$(uname -m)
if [[ $arch == 'aarch64' ]]; then
    echo ""
else 
    echo "This needs to be run when booted into 64-bit mode."
    exit 1
fi

# sudo apt install -f -y build-essential flex gawk bison libssl-dev bc dkms autoconf libtool   || (sudo apt install -f -y || true)
workdir="${HOME}/workdir"
[[ ! -d "$workdir" ]] && ( mkdir -p "$workdir" || exit 1)
[[ ! -d "$workdir"/tmp ]] && ( mkdir -p "$workdir"/tmp || exit 1)
[[ ! -d "$workdir"/build ]] && ( mkdir -p "$workdir"/build || exit 1)
echo "workdir is ${workdir}"

tmpdir=$(mktemp -d deb_XXXX -p "$workdir"/tmp)
echo "tmpdir is ${tmpdir}"
dhpath="$tmpdir/headers"
dipath="$tmpdir/image"
src_temp=$(mktemp -d rpi_src_XXXi -p "$workdir"/tmp)

git_base="https://github.com/raspberrypi/rpi-firmware"
git_branch="master" 

FIRMWARE_REV=$(git ls-remote "https://github.com/raspberrypi/rpi-firmware" refs/heads/$git_branch | awk '{print $1}')
cd "$src_temp" && curl -OLf https://github.com/raspberrypi/rpi-firmware/raw/$git_branch/git_hash
KERNEL_REV=$(cat "$src_temp"/git_hash)
SHORT_HASH=$(echo ${KERNEL_REV:0:7})

setup_git_fw() {
if [[ -d "$workdir/rpi-firmware" ]]; then
    (  sudo rm -rf "$workdir"/rpi-firmware.old || true )
    (  sudo mv "$workdir"/rpi-firmware "$workdir"/rpi-firmware.old || true )
    (  sudo rm -rf "$workdir"/rpi-firmware.old || true )
fi
    cd "$workdir" && git clone --depth=1 -b $git_branch $git_base
}

update_git_fw() {
[[ ! -d "$workdir/rpi-firmware" ]] && setup_git_fw

( cd "$workdir"/rpi-firmware && git fetch && git reset --hard origin/$git_branch ) || setup_git_fw
    cd "$workdir"/rpi-firmware && git config pull.ff only
    cd "$workdir"/rpi-firmware && git pull
    #cd "$workdir"/rpi-firmware && git_hash=$(git rev-parse origin/$git_branch)
}

check_zfs() {
        # Install zfs prerequisites
        sudo apt install -f -y  autoconf libtool  uuid-dev libudev-dev \
    libssl-dev zlib1g-dev libaio-dev libattr1-dev python3 python3-dev \
    python3-setuptools autoconf automake libtool gawk dkms libblkid-dev \
    uuid-dev libudev-dev libssl-dev libelf-dev python3-cffi libffi-dev || true
    
}

make_headers_deb_files() {
installed_size_headers=$(du -a "$dhpath" | tail -n 1 | awk '{print $1}')
mkdir -p "$dhpath"/DEBIAN
chmod 777 "$dhpath"/DEBIAN
cat <<-EOF | dd status=none of="$dhpath"/DEBIAN/control
Source: linux-$kver
Section: kernel
Priority: optional
Maintainer: root <root@$SHORT_HASH>
Standards-Version: 4.1.3
Homepage: http://www.kernel.org/
Package: linux-headers-$kver
Architecture: all
Version: $kver-1
Depends: build-essential, flex, bison, bc
Installed-Size: $installed_size_headers
Description: Linux kernel headers for $kver on arm64
 This package provides kernel header files for $kver on arm64
 built from:
 https://github.com/raspberrypi/linux/tree/$FIRMWARE_REV
 This is useful for people who need to build external modules
EOF
cat <<-EOF | dd status=none of="$dhpath"/DEBIAN/preinst
#!/bin/sh
set -e

version=$kver

if [ "\$1" = abort-upgrade ]; then
    exit 0
fi

if [ "\$1" = install ]; then
    mkdir -p /lib/modules/\$version
    mkdir -p /usr/src/linux-headers-\$version || true
    cd /lib/modules/\$version && ln -snrvf /usr/src/linux-headers-\$version build
fi

if [ -d /etc/kernel/header_preinst.d ]; then
    DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
          --arg=\$image_path /etc/kernel/header_preinst.d
fi


exit 0
EOF
chmod +x "$dhpath"/DEBIAN/preinst
cat <<-EOF | dd status=none of="$dhpath"/DEBIAN/postinst
#!/bin/bash

set -e

version=$kver
[[ -f /etc/environment ]] && . /etc/environment
if [ "\$1" != configure ]; then
    exit 0
fi




if [ -d /etc/kernel/header_postinst.d ]; then
    DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
                /etc/kernel/header_postinst.d
fi


exit 0
EOF
chmod +x "$dhpath"/DEBIAN/postinst
chmod -R 0755 "$dhpath"/DEBIAN
cd "$tmpdir" && sudo nice -n 20 dpkg-deb -b headers/
sudo mv "$tmpdir"/headers.deb "$workdir"/build/linux-headers-"${kver}"_all.deb
}

make_image_deb_files() {
installed_size_image=$(du -a "$dipath" | tail -n 1 | awk '{print $1}')
mkdir -p "$dipath"/DEBIAN
chmod 777 "$dipath"/DEBIAN
cat <<-EOF | dd status=none of="$dipath"/DEBIAN/control
Package: linux-image-$kver
Source: linux-$kver
Version: $kver-1
Architecture: arm64
Maintainer: root <root@$SHORT_HASH>
Installed-Size: $installed_size_image
Section: kernel
Priority: optional
Homepage: http://www.kernel.org/
Description: Linux kernel, version $kver
 This package contains the Linux kernel, modules and corresponding other
 files, version: $kver.
EOF
cat <<-EOFF | dd status=none of="$dipath"/DEBIAN/postinst
#!/bin/sh
set -e

version=$kver
image_path=/boot/vmlinuz-\$version

# Install kernel (This avoids an issue if /boot is fat32.)
mount -o remount,rw /boot 2>/dev/null || true
cp /usr/share/rpikernelhack/vmlinuz-"$kver" \$image_path || true
# If custom kernel= line is being used don't replace kernel8.img,
# overlays, or dtb files.
if ! vcgencmd get_config str | grep -q kernel ; then
    cp /usr/share/rpikernelhack/vmlinuz-"$kver" /boot/kernel8.img
    cp /usr/lib/linux-image-"$kver"/broadcom/*.dtb /boot/
    cp /usr/lib/linux-image-"$kver"/overlays/* /boot/overlays/
fi



#
# When we install linux-image we have to run kernel postinst.d support to
# generate the initramfs, create links etc.  Should it have an associated
# linux-image-extra package and we install that we also need to run kernel
# postinst.d, to regenerate the initramfs.  If we are installing both at the
# same time, we necessarily trigger kernel postinst.d twice. As this includes
# rebuilding the initramfs and reconfiguring the boot loader this is very time
# consuming.
#
# Similarly for removal when we remove the linux-image-extra package we need to
# run kernel postinst.d handling in order to pare down the initramfs to
# linux-image contents only.  When we remove the linux-image need to remove the
# now redundant initramfs.  If we are removing both at the same time, then
# we will rebuilt the initramfs and then immediatly remove it.
#
# Switches to using a trigger against the linux-image package for all
# postinst.d and postrm.d handling.  On installation postinst.d gets triggered
# twice once by linux-image and once by linux-image-extra.  As triggers are
# non-cumulative we will only run this processing once.  When removing both
# packages we will trigger postinst.d from linux-image-extra and then in
# linux-image postrm.d we effectivly ignore the pending trigger and simply run
# the postrm.d.  This prevents us from rebuilding the initramfs.
#
if [ "\$1" = triggered ]; then
    trigger=/usr/lib/linux/triggers/\$version
    if [ -f "\$trigger" ]; then
    sh "\$trigger"
    rm -f "\$trigger"
    fi
    exit 0
fi

if [ "\$1" != configure ]; then
    exit 0
fi

depmod \$version

if [ -f /lib/modules/\$version/.fresh-install ]; then
    change=install
else
    change=upgrade
fi
# linux-update-symlinks \$change \$version \$image_path
rm -f /lib/modules/\$version/.fresh-install

if [ -d /etc/kernel/postinst.d ]; then
    mkdir -p /usr/lib/linux/triggers
    cat - >/usr/lib/linux/triggers/\$version <<EOF
DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
      --arg=\$image_path /etc/kernel/postinst.d
EOF
    dpkg-trigger --no-await linux-update-\$version
fi

exit 0

EOFF
chmod +x "$dipath"/DEBIAN/postinst

cat <<-EOF | dd status=none of="$dipath"/DEBIAN/triggers
interest linux-update-$kver
EOF

cat <<-EOF | dd status=none of="$dipath"/DEBIAN/postrm
#!/bin/sh
set -e

version=$kver
image_path=/boot/vmlinuz-\$version

rm -f /lib/modules/\$version/.fresh-install

#if [ "\$1" != upgrade ] && command -v linux-update-symlinks >/dev/null; then
#    linux-update-symlinks remove \$version \$image_path
#fi

if [ -d /etc/kernel/postrm.d ]; then
    # We cannot trigger ourselves as at the end of this we will no longer
    # exist and can no longer respond to the trigger.  The trigger would
    # then become lost.  Therefore we clear any pending trigger and apply
    # postrm directly.
    if [ -f /usr/lib/linux/triggers/\$version ]; then
    echo "\$0 ... removing pending trigger"
    rm -f /usr/lib/linux/triggers/\$version
    fi
    DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
          --arg=\$image_path /etc/kernel/postrm.d
fi

if [ "\$1" = purge ]; then
    for extra_file in modules.dep modules.isapnpmap modules.pcimap \\
                      modules.usbmap modules.parportmap \\
                      modules.generic_string modules.ieee1394map \\
                      modules.ieee1394map modules.pnpbiosmap \\
                      modules.alias modules.ccwmap modules.inputmap \\
                      modules.symbols modules.ofmap \\
                      modules.seriomap modules.\\*.bin \\
              modules.softdep modules.devname; do
    eval rm -f /lib/modules/\$version/\$extra_file
    done
    rmdir /lib/modules/\$version || true
fi

exit 0
EOF
chmod +x "$dipath"/DEBIAN/postrm
cat <<-EOF | dd status=none of="$dipath"/DEBIAN/preinst
#!/bin/sh
set -e

version=$kver
image_path=/boot/vmlinuz-\$version

if [ "\$1" = abort-upgrade ]; then
    exit 0
fi

if [ "\$1" = install ]; then
    # Create a flag file for postinst
    mkdir -p /lib/modules/\$version
    touch /lib/modules/\$version/.fresh-install
fi

if [ -d /etc/kernel/preinst.d ]; then
    DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
          --arg=\$image_path /etc/kernel/preinst.d
fi

if [ ! -e /lib/modules/\$version/build ]; then
    mkdir -p /usr/src/linux-headers-\$version || true
    cd /lib/modules/\$version && ln -snrvf /usr/src/linux-headers-\$version build || true
fi

exit 0
EOF
chmod +x "$dipath"/DEBIAN/preinst
cat <<-EOF | dd status=none of="$dipath"/DEBIAN/prerm
#!/bin/sh
set -e

version=$kver
image_path=/boot/vmlinuz-\$version

if [ "\$1" != remove ]; then
    exit 0
fi

linux-check-removal \$version

if [ -d /etc/kernel/prerm.d ]; then
    DEB_MAINT_PARAMS="\$*" run-parts --report --exit-on-error --arg=\$version \\
          --arg=\$image_path /etc/kernel/prerm.d
fi

exit 0
EOF
chmod +x "$dipath"/DEBIAN/prerm
chmod -R 0755 "$dipath"/DEBIAN
cd "$tmpdir" && sudo nice -n 20 dpkg-deb -b image/
sudo mv "$tmpdir"/image.deb "$workdir"/build/linux-image-"${kver}"_arm64.deb
}

make_debs() {
    cd "$src_temp" && curl -L https://github.com/raspberrypi/linux/archive/"${KERNEL_REV}".tar.gz >rpi-linux.tar.gz
    cd "$src_temp" && curl --retry 3 -OL "https://github.com/raspberrypi/rpi-firmware/raw/${FIRMWARE_REV}/Module8.symvers"
    mv $src_temp/Module8.symvers $src_temp/Module.symvers
    kver=$(find "$workdir"/rpi-firmware/modules/ -type d -name '*v8+' -printf "%P\n")
    l=$kver
    
    # Build kernel header package
    # Adapted from scripts/package/builddeb
    mkdir -p $src_temp/header_tmp/debian
    cd $src_temp/header_tmp && tar --strip-components 1 -xf "${src_temp}"/rpi-linux.tar.gz
    SRCARCH=arm64
    (cd $src_temp/header_tmp; cp $src_temp/header_tmp/arch/arm64/configs/bcm2711_defconfig $src_temp/header_tmp/.config) # copy .config manually to be where it's expected to be
    cd "$src_temp/header_tmp" && (yes "" | sudo make modules_prepare) && sudo chown -R `id -u` .
    #cd "$src_temp/header_tmp" && (yes "" | sudo make scripts) && sudo chown -R `id -u` .
    (cd $src_temp/header_tmp; find . -name Makefile\* -o -name Kconfig\* -o -name \*.pl) > "$src_temp/header_tmp/debian/hdrsrcfiles"
    (cd $src_temp/header_tmp; find arch/*/include include scripts -type f -o -type l) >> "$src_temp/header_tmp/debian/hdrsrcfiles"
    (cd $src_temp/header_tmp; find arch/$SRCARCH -name module.lds -o -name Kbuild.platforms -o -name Platform) >> "$src_temp/header_tmp/debian/hdrsrcfiles"
    (cd $src_temp/header_tmp; find $(find arch/$SRCARCH -name include -o -name scripts -type d) -type f) >> "$src_temp/header_tmp/debian/hdrsrcfiles"
    (cd $src_temp/header_tmp; find tools/objtool -type f -executable) >> "$src_temp/header_tmp/debian/hdrobjfiles"
    
    (cd $src_temp/header_tmp; find arch/$SRCARCH/include modules.builtin.modinfo Module.symvers include scripts -type f) >> "$src_temp/header_tmp/debian/hdrobjfiles"
    (cd $src_temp/header_tmp; find scripts/gcc-plugins -name \*.so -o -name gcc-common.h) >> "$src_temp/header_tmp/debian/hdrobjfiles"
    destdir="$dhpath"/usr/src/linux-headers-$kver
    mkdir -p "$destdir"
    (cd $src_temp/header_tmp; tar -c -f - -T -) < "$src_temp/header_tmp/debian/hdrsrcfiles" | (cd $destdir; tar -xf -)
    (cd $src_temp/header_tmp; tar -c -f - -T -) < "$src_temp/header_tmp/debian/hdrobjfiles" | (cd $destdir; tar -xf -)
    (cd $src_temp/header_tmp; cp $src_temp/header_tmp/arch/arm64/configs/bcm2711_defconfig $destdir/.config) # copy .config manually to be where it's expected to be
    rm -rf "$src_temp/header_tmp/debian/hdrsrcfiles" "$src_temp/header_tmp/debian/hdrobjfiles"
    cp "$src_temp"/Module.symvers "$destdir"/Module.symvers
    
    make_headers_deb_files

    mkdir -p "$dipath"/usr/share/rpikernelhack/
    cp "$workdir"/rpi-firmware/kernel8.img "$dipath"/usr/share/rpikernelhack/vmlinuz-"$l"
    mkdir -p "$dipath"/lib/modules/
    cp -r "$workdir"/rpi-firmware/modules/"$l" "$dipath"/lib/modules/
    mkdir -p "$dipath"/usr/lib/linux-image-"$l"/broadcom && mkdir -p "$dipath"/usr/lib/linux-image-"$l"/overlays   
    cp -f "$workdir"/rpi-firmware/*.dtb "$dipath"/usr/lib/linux-image-"$l"/broadcom/
    cp -f "$workdir"/rpi-firmware/overlays/* "$dipath"/usr/lib/linux-image-"$l"/overlays/
    [[ ! -e "$dipath/lib/firmware/$l/device-tree" ]] && mkdir -p "$dipath"/lib/firmware/"$l"/device-tree
    
    make_image_deb_files
    
    # Clean up.
    cd "$workdir"
    sudo rm -rf "$src_temp"
    sudo rm -rf "$tmpdir"
}

install_headers() {
if [[ $(uname -m) == "aarch64" ]]; then
bootfsmount=$(mount | grep boot | awk '{print $3}')
[[ $(echo $bootfsmount | wc -w) == "1" ]] && BOOTFS=${bootfsmount}
BOOTFS=${BOOTFS:-/boot}
sudo mount -o remount,rw $BOOTFS
fi
sudo nice -n 20 dpkg -i "$workdir"/build/linux-headers-"${kver}"*.deb || sudo apt install -f -y
}
install_image() {
	if [[ $(uname -m) == "aarch64" ]]; then
		bootfsmount=$(mount | grep boot | awk '{print $3}')
		[[ $(echo $bootfsmount | wc -w) == "1" ]] && BOOTFS=${bootfsmount}
		BOOTFS=${BOOTFS:-/boot}
		sudo mount -o remount,rw $BOOTFS
	fi
	sudo nice -n 20 dpkg -i "$workdir"/build/linux-image-"${kver}"*.deb || sudo apt install -f -y
}

update_git_fw
make_debs
install_headers
install_image
sudo flash-image --force ${kver} 
echo "done."

@alphafox02
Copy link

alphafox02 commented May 30, 2022

@satmandu Would you script be okay to generate the image/headers/tools etc for use in a Ubuntu aarch64 20.04 build running on a Pi4? I've been keeping a look out for a 5.15 kernel to come to 20.04 aarch64 like how it's coming to the x86_64 version, but so far all I see is 5.4

I installed a 5.15 kernel from Ubuntu 22.04 aarch64 for testing, which is fine, but the headers/tools can't be installed to do a dependency being to low a version in 20.04.

I have your script running now, so I guess I'll see what happens. I'm not sure how different a Pi OS Kernel vs a Ubuntu aarch64 kernel.

edit: I think I answered my own question as the script doesn't appear to work. It states the following "Ignoring old or unknown version 5.15.43-v8+ (latest is 5.15.0-1008-raspi)" towards the end.

@satmandu
Copy link
Author

Here is the current version I use with Ubuntu 22.04:

https://paste.ubuntu.com/p/SWF2nRX92c/

@satmandu
Copy link
Author

@alphafox02 I updated the script to the version I use for generating kernels for Ubuntu 22.04.

@alphafox02
Copy link

I’ll check it out here shortly. I’m super curious to see if it’ll work on 20.04 Ubuntu aarch64. Thank you for sharing.

@alphafox02
Copy link

@satmandu both the kernel and headers built and installed and no immediate problem booting. Should I be looking for a way to build the Linux-modules/Linux/modules-extra and Linux-raspi-tools/Linux-tools that typically accompany the kernel and header install on the Pi w/ aarch64 20.04?

@satmandu
Copy link
Author

@alphafox02 I have never bothered with those packages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment