Last active
September 29, 2024 13:01
-
-
Save kepstin/ce11403963dab015ae4278234972c9b5 to your computer and use it in GitHub Desktop.
OpenWrt multi-slot boot with UEFI systemd-boot (might work with gummiboot)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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