|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +set -e |
| 4 | + |
| 5 | +# deps: |
| 6 | +# - dpkg (dpkg-deb) |
| 7 | +# - openssl |
| 8 | +# - zstd |
| 9 | +# - cpio |
| 10 | + |
| 11 | +# TODO: Currently, we just assume we're Pi5/2712. |
| 12 | +# There is currently no functionality to insert firmware for Pi4 and earlier. |
| 13 | + |
| 14 | +TMPDIR="${TMPDIR:=/tmp}" |
| 15 | + |
| 16 | +if [[ -z "${1}" ]]; then |
| 17 | + >&2 echo "No linux image specified" |
| 18 | + exit 1 |
| 19 | +fi |
| 20 | + |
| 21 | +if [[ -z "${VENDOR}" ]]; then |
| 22 | + >&2 echo "'VENDOR' not specified" |
| 23 | + exit 1 |
| 24 | +fi |
| 25 | + |
| 26 | +if [[ -z "${OPENSSL}" || ! -f "${OPENSSL}" ]]; then |
| 27 | + >&2 echo "'OPENSSL' not set or binary does not exist" |
| 28 | + exit 1 |
| 29 | +fi |
| 30 | + |
| 31 | +if [[ -z "${CUSTOMER_KEY_FILE_PEM}" || ! -f "${CUSTOMER_KEY_FILE_PEM}" ]]; then |
| 32 | + >&2 echo "'CUSTOMER_KEY_FILE_PEM' not set or file does not exist" |
| 33 | + exit 1 |
| 34 | +fi |
| 35 | + |
| 36 | +LINUX_IMAGE="${1}" |
| 37 | + |
| 38 | +# Should be set by systemd |
| 39 | +SERVICE_NAME="make-boot-image@$(systemd-escape $LINUX_IMAGE).service" |
| 40 | +CACHE_DIRECTORY="${CACHE_DIRECTORY:=/var/cache/${SERVICE_NAME}}" |
| 41 | +RUNTUME_DIRECTORY="${RUNTIME_DIRECTORY:=/run/${SERVICE_NAME}}" |
| 42 | + |
| 43 | +# TODO: Might be interesting to start rpi-package-download with --no-block to |
| 44 | +# allow multiple simultaneous downloads. |
| 45 | +function download_package() { |
| 46 | + systemctl start \ |
| 47 | + --wait \ |
| 48 | + rpi-package-download@$(systemd-escape ${1}).service |
| 49 | +} |
| 50 | + |
| 51 | +KERNEL_2712="linux-image-${LINUX_IMAGE}" |
| 52 | +>&2 echo "Downloading ${KERNEL_2712}" |
| 53 | +download_package "$KERNEL_2712" |
| 54 | + |
| 55 | +PACKAGE_NAME="boot-image-${VENDOR}-${LINUX_IMAGE}" |
| 56 | + |
| 57 | +# Temp directory cleanup |
| 58 | +TEMP_DIRS=() |
| 59 | +function remove_temp_dirs() { |
| 60 | + >&2 echo "Removing temporary directories" |
| 61 | + for dir in "${TEMP_DIRS[@]}" |
| 62 | + do |
| 63 | + rm -rf "$dir" |
| 64 | + done |
| 65 | +} |
| 66 | +trap remove_temp_dirs EXIT |
| 67 | + |
| 68 | +>&2 echo -n "Creating filesystem hierarchy for deb package: " |
| 69 | +DEB_HIER="$(mktemp --directory --tmpdir debhier.XXX)" |
| 70 | +TEMP_DIRS+=("${DEB_HIER}") |
| 71 | +>&2 echo "${DEB_HIER}" |
| 72 | + |
| 73 | +>&2 echo -n "Create rootfs working directory: " |
| 74 | +WORK_DIR="$(mktemp --directory --tmpdir boot-image-rootfs.XXX)" |
| 75 | +TEMP_DIRS+=("${WORK_DIR}") |
| 76 | +>&2 echo "${WORK_DIR}" |
| 77 | + |
| 78 | +function latest_pkg_dir() { |
| 79 | + echo "/var/cache/rpi-package-download@$(systemd-escape ${1}).service/latest" |
| 80 | +} |
| 81 | + |
| 82 | +>&2 echo "Extracting package contents" |
| 83 | +dpkg-deb --raw-extract "$(latest_pkg_dir $KERNEL_2712)/package.deb" "${WORK_DIR}" |
| 84 | + |
| 85 | +function get_dctrl_field() { |
| 86 | + grep-dctrl \ |
| 87 | + --field=Package \ |
| 88 | + --exact-match "${2}" \ |
| 89 | + --no-field-names \ |
| 90 | + --show-field="${3}" \ |
| 91 | + "${1}" |
| 92 | +} |
| 93 | + |
| 94 | +# Determine package version for later reuse |
| 95 | +KERNEL_2712_VERSION="$(get_dctrl_field ${WORK_DIR}/DEBIAN/control ${KERNEL_2712} Version)" |
| 96 | +>&2 echo "Extracted ${KERNEL_2712}, version ${KERNEL_2712_VERSION}" |
| 97 | + |
| 98 | +# rootfs kernel modules |
| 99 | +>&2 echo "Copy kernel modules into deb package" |
| 100 | +mkdir -p "${DEB_HIER}/lib/modules" |
| 101 | +rsync -crt "${WORK_DIR}/lib/modules/"* "${DEB_HIER}/lib/modules" |
| 102 | + |
| 103 | +>&2 echo -n "Create ramdisk working directory: " |
| 104 | +BFS_DIR="$(mktemp --directory --tmpdir boot-image-bootfs.XXX)" |
| 105 | +TEMP_DIRS+=("${BFS_DIR}") |
| 106 | +>&2 echo "${BFS_DIR}" |
| 107 | + |
| 108 | +# Kernel Images |
| 109 | +>&2 echo "Copy kernel to ramdisk" |
| 110 | +cp "${WORK_DIR}/boot/vmlinuz-${LINUX_IMAGE}" "${BFS_DIR}/zImage" |
| 111 | + |
| 112 | +# Overlays |
| 113 | +>&2 echo "Copy overlays to ramdisk" |
| 114 | +OVERLAY_PATH="${WORK_DIR}/usr/lib/${KERNEL_2712}/overlays" |
| 115 | +rsync -crt "${OVERLAY_PATH}"/*.dtb* "${OVERLAY_PATH}/README" "${BFS_DIR}/overlays" |
| 116 | + |
| 117 | +# DTBs |
| 118 | +>&2 echo "Copy DTBs to ramdisk" |
| 119 | +DTB_PATH="${WORK_DIR}/usr/lib/${KERNEL_2712}/broadcom" |
| 120 | +rsync -crt "${DTB_PATH}"/bcm27*.dtb "${BFS_DIR}" |
| 121 | + |
| 122 | +# Insert an initramfs |
| 123 | +>&2 echo "Add cryptoot initramfs to ramdisk (with necessary kernel modules)" |
| 124 | +INITRAMFS_EXTRACT="$(mktemp --directory --tmpdir initramfs-extract.XXX)" |
| 125 | +TEMP_DIRS+=("${INITRAMFS_EXTRACT}") |
| 126 | +zstd -q -d /usr/share/misc/cryptroot_initramfs -o "${INITRAMFS_EXTRACT}/initramfs.cpio" |
| 127 | +mkdir -p "${INITRAMFS_EXTRACT}/initramfs" |
| 128 | +pushd "${INITRAMFS_EXTRACT}/initramfs" > /dev/null |
| 129 | +cpio --quiet -id < ../initramfs.cpio > /dev/null |
| 130 | +rm ../initramfs.cpio |
| 131 | +pushd "${WORK_DIR}" > /dev/null |
| 132 | +find lib/modules \ |
| 133 | + \( \ |
| 134 | + -name 'dm-mod.*' \ |
| 135 | + -o \ |
| 136 | + -name 'dm-crypt.*' \ |
| 137 | + -o \ |
| 138 | + -name 'af_alg.*' \ |
| 139 | + -o \ |
| 140 | + -name 'algif_skcipher.*' \ |
| 141 | + -o \ |
| 142 | + -name 'libaes.*' \ |
| 143 | + -o \ |
| 144 | + -name 'aes_generic.*' \ |
| 145 | + -o \ |
| 146 | + -name 'aes-arm64.*' \ |
| 147 | + \) \ |
| 148 | + -exec cp -r --parents "{}" "${INITRAMFS_EXTRACT}/initramfs/usr/" \; |
| 149 | +popd > /dev/null |
| 150 | +find . -print0 | cpio --quiet --null -ov --format=newc > ../initramfs.cpio 2> /dev/null |
| 151 | +popd > /dev/null |
| 152 | +zstd -q -6 "${INITRAMFS_EXTRACT}/initramfs.cpio" -o "${BFS_DIR}/rootfs.cpio.zst" |
| 153 | + |
| 154 | +# cmdline.txt |
| 155 | +>&2 echo "Add cmdline.txt to ramdisk" |
| 156 | +# TODO: Needs to be user-modifiable |
| 157 | +echo "rootwait console=tty0 console=serial0,115200 root=/dev/ram0" > "${BFS_DIR}/cmdline.txt" |
| 158 | + |
| 159 | +# Inner config.txt |
| 160 | +>&2 echo "Add config.txt to ramdisk" |
| 161 | +# TODO: Needs to be user-modifiable |
| 162 | +echo \ |
| 163 | +'[all] |
| 164 | +kernel=zImage |
| 165 | +initramfs rootfs.cpio.zst |
| 166 | +enable_uart=1 |
| 167 | +uart_2ndstage=1 |
| 168 | +disable_overscan=1 |
| 169 | +cmdline=cmdline.txt |
| 170 | +
|
| 171 | +[cm4] |
| 172 | +dtoverlay=dwc2,dr_mode=host |
| 173 | +
|
| 174 | +[none] |
| 175 | +' > "${BFS_DIR}/config.txt" |
| 176 | + |
| 177 | +# Invoke make-boot-image |
| 178 | +>&2 echo "Finalise ramdisk in deb package (boot.img)" |
| 179 | +mkdir -p "${DEB_HIER}/boot/firmware" |
| 180 | +# TODO: Assuming pi5 here |
| 181 | +make-boot-image \ |
| 182 | + -b pi5 \ |
| 183 | + -d "${BFS_DIR}" \ |
| 184 | + -o "${DEB_HIER}/boot/firmware/boot.img" > /dev/null |
| 185 | + |
| 186 | +# Outer config.txt |
| 187 | +>&2 echo "Add config.txt to deb package (ensure boot.img is used)" |
| 188 | +cp /usr/share/misc/boot_ramdisk_config.txt "${DEB_HIER}/boot/firmware/config.txt" |
| 189 | + |
| 190 | +# boot.sig generation |
| 191 | +>&2 echo "Signing ramdisk image" |
| 192 | +sha256sum "${DEB_HIER}/boot/firmware/boot.img" | awk '{print $1}' > "${DEB_HIER}/boot/firmware/boot.sig" |
| 193 | +echo -n "rsa2048: " >> "${DEB_HIER}/boot/firmware/boot.sig" |
| 194 | +${OPENSSL} dgst \ |
| 195 | + -sign "${CUSTOMER_KEY_FILE_PEM}" \ |
| 196 | + -keyform PEM \ |
| 197 | + -sha256 \ |
| 198 | + "${DEB_HIER}/boot/firmware/boot.img" \ |
| 199 | + | xxd -c 4096 -p >> "${DEB_HIER}/boot/firmware/boot.sig" |
| 200 | + |
| 201 | +# Insert control file |
| 202 | +# TODO: Needs to be user modifiable |
| 203 | +# TODO: depends on kmod, linux-base, etc? |
| 204 | +# TODO: breaks for fwupdate, initramfs-tools, etc? |
| 205 | +mkdir "${DEB_HIER}/DEBIAN" |
| 206 | +echo \ |
| 207 | +"Package: ${PACKAGE_NAME} |
| 208 | +Source: linux |
| 209 | +Version: ${KERNEL_2712_VERSION} |
| 210 | +Architecture: arm64 |
| 211 | +Maintainer: John Smith <[email protected]> |
| 212 | +Section: kernel |
| 213 | +Priority: optional |
| 214 | +Homepage: https://github.com/raspberrypi/linux/ |
| 215 | +Provides: ${KERNEL_2712} |
| 216 | +Conflicts: ${KERNEL_2712} |
| 217 | +Replaces: ${KERNEL_2712} |
| 218 | +Description: TODO: Provide a better description" \ |
| 219 | +> "${DEB_HIER}/DEBIAN/control" |
| 220 | + |
| 221 | +# Create Debian package |
| 222 | +dpkg-deb --build "${DEB_HIER}" "${CACHE_DIRECTORY}" |
0 commit comments