Files
cabletest/mkimage
T

167 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
work=$(dirname "$repo")/kernel
src=$work/linux
fwdir=$work/firmware
initdir=$work/initramfs
cpiolist=$work/initramfs.list
upstream=https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
esp=/boot
subdir=EFI/cabletest
loader='\EFI\cabletest\cabletest.efi'
label=cabletest
# Releases are signed by one of these two and nothing else is accepted.
keys=(
647F28654894E3BD457199BE38DBBDC86092693E
ABAF11C65A2970B130ABE3C479BE3E4300411886
)
# Nothing here can be loaded from disk at runtime, so every blob the drivers
# ask for is linked into the image. The EDID is ours; the rest come from the
# distro's linux-firmware.
edid=edid/waveshare-1024x600.bin
distrofw=(
intel/ice/ddp/ice.pkg
i915/adlp_dmc.bin
i915/adlp_guc_70.bin
i915/tgl_huc.bin
)
cmdline="drm.edid_firmware=HDMI-A-1:$edid video=HDMI-A-1:e"
# Every setting the appliance depends on, re-asserted after the stored config
# is loaded so a renamed or newly defaulted symbol cannot quietly drop one.
wanty=(
EFI_STUB BLK_DEV_INITRD INITRAMFS_COMPRESSION_NONE CMDLINE_BOOL
PACKET INET DEVTMPFS PROC_FS SYSFS TMPFS
ICE ICE_HWTS PTP_1588_CLOCK
DRM_I915 DRM_FBDEV_EMULATION DRM_LOAD_EDID_FIRMWARE DRM_PANIC
FB_DEVICE FRAMEBUFFER_CONSOLE VT_CONSOLE
USB_XHCI_PCI USB_HID HID_MULTITOUCH INPUT_EVDEV
CPU_FREQ_GOV_PERFORMANCE X86_INTEL_PSTATE
)
wantn=(MODULES BLOCK I40E IGC)
say() { printf '\n\033[36m== %s\033[0m\n' "$*"; }
die() { printf '\033[31merror: %s\033[0m\n' "$*" >&2; exit 1; }
say "sudo is needed at the end, asking now so the build runs unattended"
sudo -v
say "kernel source"
if [ ! -d "$src/.git" ]; then
echo "cloning $upstream (one time, several GB)"
git clone "$upstream" "$src"
fi
git -C "$src" fetch --tags --prune origin
# sed rather than head: head closes the pipe early, which under pipefail turns
# the upstream SIGPIPE into a failed pipeline.
tag=$(git -C "$src" tag -l 'v[0-9]*' --sort=-v:refname | grep -vE -- '-(rc|tree)' | sed -n 1p)
[ -n "$tag" ] || die "no release tag found"
status=$(git -C "$src" verify-tag --raw "$tag" 2>&1) || die "$tag is not signed"
signer=""
for key in "${keys[@]}"; do
case "$status" in *"VALIDSIG $key"*) signer=$key ;; esac
done
[ -n "$signer" ] || die "$tag is not signed by a pinned kernel.org key"
echo "$tag verified against $signer"
git -C "$src" checkout -q --detach "$tag"
say "go binary"
mkdir -p "$initdir"
( cd "$repo" && CGO_ENABLED=0 go build -trimpath -o "$initdir/init" . )
go version -m "$initdir/init" | grep -E 'vcs\.(revision|modified)' ||
die "no VCS stamp; build from a git checkout so the image is traceable"
say "firmware"
mkdir -p "$fwdir/$(dirname "$edid")"
cp "$repo/kernel/$edid" "$fwdir/$edid"
for f in "${distrofw[@]}"; do
mkdir -p "$fwdir/$(dirname "$f")"
if [ -e "/lib/firmware/$f" ]; then
cp "/lib/firmware/$f" "$fwdir/$f"
elif [ -e "/lib/firmware/$f.zst" ]; then
zstd -qdf -o "$fwdir/$f" "/lib/firmware/$f.zst"
else
die "/lib/firmware/$f is missing; install linux-firmware"
fi
done
# The binary is embedded by the kernel link, so it has to exist before make.
# /dev/console must be in the cpio: the kernel opens it for init's stdio before
# anything has had a chance to mount devtmpfs over /dev.
say "initramfs"
cat > "$cpiolist" <<EOF
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
dir /proc 0755 0 0
dir /sys 0755 0 0
file /init $initdir/init 0755 0 0
EOF
cat "$cpiolist"
say "configure"
cp "$repo/kernel/config" "$src/.config"
( cd "$src" && scripts/config \
--disable MODULES \
--enable EXPERT --disable BLOCK \
--disable I40E --disable IGC \
--enable DRM_LOAD_EDID_FIRMWARE \
--set-str EXTRA_FIRMWARE "$edid ${distrofw[*]}" \
--set-str EXTRA_FIRMWARE_DIR "$fwdir" \
--set-str INITRAMFS_SOURCE "$cpiolist" \
--disable INITRAMFS_COMPRESSION_GZIP --enable INITRAMFS_COMPRESSION_NONE \
--enable CMDLINE_BOOL --set-str CMDLINE "$cmdline" \
--disable DEBUG_INFO_DWARF5 --enable DEBUG_INFO_NONE \
--disable LOCALVERSION_AUTO --set-str LOCALVERSION "-cabletest" )
make -C "$src" olddefconfig
for s in "${wanty[@]}"; do
grep -qx "CONFIG_$s=y" "$src/.config" || die "CONFIG_$s is not y after olddefconfig"
done
for s in "${wantn[@]}"; do
grep -qx "# CONFIG_$s is not set" "$src/.config" || die "CONFIG_$s survived olddefconfig"
done
echo "${#wanty[@]} required and ${#wantn[@]} forbidden symbols check out"
say "build $tag"
make -C "$src" -j"$(nproc)"
say "package"
image=$src/arch/x86/boot/bzImage
cpio -itv < "$src/usr/initramfs_data.cpio"
[ -z "$(find "$src" -name '*.ko' -print -quit)" ] || die "modules were built"
ls -l "$image"
say "install"
[ "$(findmnt -no FSTYPE "$esp")" = vfat ] || die "$esp is not a mounted ESP"
espdev=$(findmnt -no SOURCE "$esp")
disk=/dev/$(lsblk -no PKNAME "$espdev")
part=$(cat "/sys/class/block/$(basename "$espdev")/partition")
sudo mkdir -p "$esp/$subdir"
sudo cp "$image" "$esp/$subdir/cabletest.efi"
sync
sudo cmp "$image" "$esp/$subdir/cabletest.efi"
bootnum=$(efibootmgr | awk -v l="$label" '$1 ~ /^Boot[0-9A-Fa-f]{4}/ && $2 == l {print substr($1, 5, 4)}')
if [ -z "$bootnum" ]; then
# A new entry is prepended to BootOrder, which would silently take over the
# default boot, so the previous order is put back with ours on the end.
order=$(efibootmgr | sed -n 's/^BootOrder: //p')
sudo efibootmgr --create --disk "$disk" --part "$part" \
--loader "$loader" --label "$label" > /dev/null
bootnum=$(efibootmgr | awk -v l="$label" '$1 ~ /^Boot[0-9A-Fa-f]{4}/ && $2 == l {print substr($1, 5, 4)}')
sudo efibootmgr -o "${order:+$order,}$bootnum" > /dev/null
fi
sudo efibootmgr -n "$bootnum" > /dev/null
efibootmgr
say "$tag installed as Boot$bootnum, armed for the next reboot only"