Skip to content

Instantly share code, notes, and snippets.

@kepstin
Last active September 29, 2024 13:01
Show Gist options
  • Save kepstin/ce11403963dab015ae4278234972c9b5 to your computer and use it in GitHub Desktop.
Save kepstin/ce11403963dab015ae4278234972c9b5 to your computer and use it in GitHub Desktop.
OpenWrt multi-slot boot with UEFI systemd-boot (might work with gummiboot)
#!/bin/sh
set -e
rootimg=$1
kernelbin=$2
if [ -z "$rootimg" ] || [ -z "$kernelbin" ] ; then
printf 'Usage: %s ROOTIMG KERNELBIN\n' "$0"
exit 1
fi
if ! [ -e "$rootimg" ] ; then
printf 'ROOTIMG "%s" was not found\n' "${rootimg}"
exit 1
fi
if ! [ -e "$kernelbin" ] ; then
printf 'KERNELBIN "%s" was not found\n' "${kernelbin}"
exit 1
fi
current_root=$(findmnt -M / -o PARTLABEL -nr)
case "$current_root" in
ROOT_A)
current_slot=a
target_slot=b
target_root=ROOT_B
;;
ROOT_B)
current_slot=b
target_slot=a
target_root=ROOT_A
;;
*)
printf 'Could not determine current slot from root PARTLABEL="%s"\n' "${current_root}"
exit 1
esac
printf 'Detected current slot %s, installing to slot %s\n\n' "${current_slot}" "${target_slot}"
mkdir -p /tmp/upgradeslot
current_entry="openwrt-${current_slot}.conf"
target_rootdev=$(blkid -t PARTLABEL="${target_root}" -o device)
target_root_mnt="/tmp/upgradeslot/root"
target_kernel_dir="/boot/openwrt/${target_slot}"
target_kernel="${target_kernel_dir}/kernel"
target_entry="openwrt-${target_slot}.conf"
if [ -z "${target_rootdev}" ] ; then
printf 'Could not locate target root device with PARTLABEL="%s"\n' "${target_root}"
exit 1
fi
read -r entries_srel </boot/loader/entries.srel
if [ "${entries_srel}" != "type1" ] ; then
printf 'Boot configuration conforming to Boot Loader Specification was not found.\n'
exit 1
fi
if findmnt -S "${target_rootdev}" >/dev/null ; then
printf 'Slot %s root device %s is mounted:\n\n' "${target_slot}" "${target_rootdev}"
findmnt -S "${target_rootdev}"
printf '\nPlease unmount it to proceed.\n'
exit 1
fi
if findmnt -M "${target_root_mnt}" >/dev/null ; then
printf 'Temporary mountpoint %s is in use:\n\n' "${target_root_mnt}"
findmnt -M "${target_root_mnt}"
printf '\nPlease unmount it to proceed.\n'
exit 1
fi
read_bls_var() {
bls_var_value=$(cat "/sys/firmware/efi/efivars/$2-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" | tail -c +5 | iconv -f UCS-2LE -t UTF-8 | tr '\000' '\n' ; printf 'x\n')
bls_var_value=${bls_var_value%?}
eval "$1=\${bls_var_value}"
}
read_bls_var selected_entry LoaderEntrySelected
read -r selected_entry <<END
${selected_entry}
END
printf 'Boot loader selected entry is %s\n' "${selected_entry}"
if [ -z "${selected_entry}" ] ; then
printf 'Warning: Unable to read selected boot entry from boot loader\n'
elif [ "${selected_entry}" != "${current_entry}" ] ; then
printf 'Boot loader selected entry "%s" doesn'\''t match root device PARTLABEL="%s"\n' "${selected_entry}" "${current_root}"
printf 'Please correct this before proceeding.\n'
fi
printf 'Wiping boot loader entry /boot/loader/entries/%s ...\n' "${target_entry}"
rm -f "/boot/loader/entries/${target_entry}"
fsync /boot/loader/entries
printf '\n'
printf 'Wiping kernel from %s ...\n' "${target_kernel_dir}"
rm -rf "${target_kernel_dir}"
printf '\n'
printf 'Wiping root device %s ...\n' "${target_rootdev}"
wipefs -a "${target_rootdev}"
printf '\n'
printf 'Formatting root device %s ...\n' "${target_rootdev}"
mkfs.ext4 -L "${target_root}" "${target_rootdev}"
printf '\n'
printf 'Mounting root device %s to %s ...\n' "${target_rootdev}" "${target_root_mnt}"
mkdir -p "${target_root_mnt}"
mount "${target_rootdev}" "${target_root_mnt}"
printf '\n'
printf 'Extracting %s to %s ...\n' "${rootimg}" "${target_root_mnt}"
tar x -f "${rootimg}" -z -C "${target_root_mnt}"
printf '\n'
printf 'Reading version information from %s/etc/os-release ...\n' "${target_root_mnt}"
source "${target_root_mnt}/etc/os-release"
printf ' %s\n VERSION_ID="%s"\n BUILD_ID="%s"\n\n' "${PRETTY_NAME}" "${VERSION_ID}" "${BUILD_ID}"
printf 'Writing sysupgrade.tgz to %s ...\n' "${target_root_mnt}"
sysupgrade -b "${target_root_mnt}/sysupgrade.tgz"
printf '\n'
printf 'Unmounting %s ...\n' "${target_root_mnt}"
umount "${target_root_mnt}"
printf '\n'
printf 'Copying %s to %s ...\n' "${kernelbin}" "${targetkernel}"
mkdir -p "${target_kernel_dir}"
cp "${kernelbin}" "${target_kernel}"
fsync "${target_kernel}"
printf '\n'
printf 'Writing boot loader entry to /boot/loader/entries/%s ...\n' "${target_entry}"
[ -f /etc/kernel/cmdline ] && read -r kernel_cmdline </etc/kernel/cmdline
{
printf 'title %s (Slot %s)\n' "${PRETTY_NAME}" "${target_slot}"
printf 'version %s %s %s\n' "${VERSION_ID}" "${BUILD_ID}" "$(date -u +%Y%m%d.%H%M%S)"
printf 'sort-key openwrt\n'
printf 'linux openwrt/%s/kernel\n' "${target_slot}"
printf 'options root=PARTLABEL=%s %s\n' "${target_root}" "${kernel_cmdline}"
} >"/boot/loader/entries/${target_entry}.new"
fsync "/boot/loader/entries/${target_entry}.new"
mv "/boot/loader/entries/${target_entry}.new" "/boot/loader/entries/${target_entry}"
fsync "/boot/loader/entries/${target_entry}"
printf '\n'
printf 'Upgrade is installed to slot %s.\nPlease reboot into the new system.\n' "${target_slot}"
# TODO: Should update bootloader configuration default instead of relying only on
# version number comparison. Also should set up boot counting for automatic failover
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment