1
1
openwrt/target/linux/x86/base-files/lib/upgrade/platform.sh
Keno Fischer 9a143bf7ff
x86: onie-installer: wire up sysupgrade via ONIE install mode
This adds support for sysupgrade on ONIE-installed systems.
The install is chained through ONIE (using the ONIE installer image),
rather than attempting to manually upgrade the partition.
The idea is to allow future OpenWRT installs flexibility to use
a different partition table. By putting the installer in charge
of setting up the file system partition, the upgrade process needs
to have no knowledge of the internals of the image.

Config preservation is accomplished by appending the sysupgrade .tar.gz
to the ONIE installer image. Of course this also works for a clean
install using a sysupgrade.tar.gz created via `sysupgrade -b`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Keno Fischer <keno@juliahub.com>
Link: https://github.com/openwrt/openwrt/pull/23062
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
2026-05-23 17:23:54 +02:00

231 lines
6.0 KiB
Bash

RAMFS_COPY_BIN='grub-bios-setup grub-editenv'
find_partname_dev() {
local partname="$1"
local uevent dev
for uevent in /sys/class/block/*/uevent; do
grep -q "^PARTNAME=$partname\$" "$uevent" 2>/dev/null || continue
dev=$(sed -n 's/^DEVNAME=//p' "$uevent")
[ -n "$dev" ] && { echo "/dev/$dev"; return 0; }
done
return 1
}
# True when the boot disk hosts ONIE: both ONIE-BOOT and OPENWRT-ROOT
# GPT-labelled partitions present.
is_onie_install() {
grep -q PARTNAME=ONIE-BOOT /sys/class/block/*/uevent 2>/dev/null || return 1
grep -q PARTNAME=OPENWRT-ROOT /sys/class/block/*/uevent 2>/dev/null || return 1
return 0
}
# Reinstall via ONIE: append the preserved-config tarball to the installer
# image (installer detects it via size check), drop the combined file on
# OPENWRT-ROOT as onie-installer-x86_64, and flip both grubenv files so the
# next boot chainloads ONIE in install mode. ONIE re-runs our installer,
# which recreates OPENWRT-ROOT, extracts the new rootfs, and stashes
# /sysupgrade.tgz for OpenWrt's preinit to restore.
platform_do_upgrade_onie() {
local image="$1"
local backup="$UPGRADE_BACKUP"
local root_dev onie_dev
root_dev=$(find_partname_dev OPENWRT-ROOT) || {
v "ONIE upgrade: OPENWRT-ROOT not found"; return 1;
}
onie_dev=$(find_partname_dev ONIE-BOOT) || {
v "ONIE upgrade: ONIE-BOOT not found"; return 1;
}
local root_mnt=/tmp/upgrade-root
local onie_mnt=/tmp/upgrade-onie
mkdir -p "$root_mnt" "$onie_mnt"
mount -t ext4 -o rw,noatime "$root_dev" "$root_mnt" || {
v "ONIE upgrade: mount $root_dev failed"
return 1
}
local bundle="$root_mnt/onie-installer-x86_64"
v "ONIE upgrade: writing bundle to $bundle"
if [ -s "$backup" ]; then
cat "$image" "$backup" > "$bundle"
else
cp "$image" "$bundle"
fi
chmod +x "$bundle"
# One-shot: OpenWrt's grub.cfg reads next_entry and chainloads ONIE.
grub-editenv "$root_mnt/boot/grub/grubenv" set next_entry=ONIE || {
v "ONIE upgrade: failed to set next_entry in OPENWRT-ROOT grubenv"
sync; umount "$root_mnt"
return 1
}
sync
umount "$root_mnt"
mount -o rw,noatime "$onie_dev" "$onie_mnt" || {
v "ONIE upgrade: mount $onie_dev failed"
return 1
}
grub-editenv "$onie_mnt/grub/grubenv" set onie_mode=install || {
v "ONIE upgrade: failed to set onie_mode in ONIE-BOOT grubenv"
sync; umount "$onie_mnt"
return 1
}
sync
umount "$onie_mnt"
v "ONIE upgrade: ready; stage2 will reboot"
return 0
}
platform_check_image() {
local diskdev partdev diff
[ "$#" -gt 1 ] && return 1
if is_onie_install; then
[ "$(get_magic_word "$1")" = "2321" ] || {
v "Invalid image: expected ONIE installer (shell script)"
return 1
}
head -c 4096 "$1" | grep -q '^PAYLOAD_OFFSET=' || {
v "Invalid image: no PAYLOAD_OFFSET header"
return 1
}
return 0
fi
case "$(get_magic_word "$1")" in
eb48|eb63) ;;
*)
v "Invalid image type"
return 1
;;
esac
export_bootdevice && export_partdevice diskdev 0 || {
v "Unable to determine upgrade device"
return 1
}
get_partitions "/dev/$diskdev" bootdisk
v "Extract boot sector from the image"
get_image_dd "$1" of=/tmp/image.bs count=63 bs=512b
get_partitions /tmp/image.bs image
#compare tables
diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)"
rm -f /tmp/image.bs /tmp/partmap.bootdisk /tmp/partmap.image
if [ -n "$diff" ]; then
v "Partition layout has changed. Full image will be written."
ask_bool 0 "Abort" && exit 1
return 0
fi
}
platform_copy_config() {
local partdev parttype=ext4
# ONIE upgrade bundles sysupgrade.tgz into the installer itself.
is_onie_install && return 0
if export_partdevice partdev 1; then
part_magic_fat "/dev/$partdev" && parttype=vfat
mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt
cp -af "$UPGRADE_BACKUP" "/mnt/$BACKUP_FILE"
umount /mnt
fi
}
platform_do_bootloader_upgrade() {
local bootpart parttable=msdos
local diskdev="$1"
if export_partdevice bootpart 1; then
mkdir -p /tmp/boot
mount -o rw,noatime "/dev/$bootpart" /tmp/boot
echo "(hd0) /dev/$diskdev" > /tmp/device.map
part_magic_efi "/dev/$diskdev" && parttable=gpt
v "Upgrading bootloader on /dev/$diskdev..."
grub-bios-setup \
-m "/tmp/device.map" \
-d "/tmp/boot/boot/grub" \
-r "hd0,${parttable}1" \
"/dev/$diskdev"
umount /tmp/boot
fi
}
platform_do_upgrade() {
local diskdev partdev diff
if is_onie_install; then
platform_do_upgrade_onie "$1"
return $?
fi
export_bootdevice && export_partdevice diskdev 0 || {
v "Unable to determine upgrade device"
return 1
}
sync
if [ "$UPGRADE_OPT_SAVE_PARTITIONS" = "1" ]; then
get_partitions "/dev/$diskdev" bootdisk
v "Extract boot sector from the image"
get_image_dd "$1" of=/tmp/image.bs count=63 bs=512b
get_partitions /tmp/image.bs image
#compare tables
diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)"
else
diff=1
fi
if [ -n "$diff" ]; then
get_image_dd "$1" of="/dev/$diskdev" bs=4096 conv=fsync
# Separate removal and addtion is necessary; otherwise, partition 1
# will be missing if it overlaps with the old partition 2
partx -d - "/dev/$diskdev"
partx -a - "/dev/$diskdev"
return 0
fi
#iterate over each partition from the image and write it to the boot disk
while read part start size; do
if export_partdevice partdev $part; then
v "Writing image to /dev/$partdev..."
get_image_dd "$1" of="/dev/$partdev" ibs=512 obs=1M skip="$start" count="$size" conv=fsync
else
v "Unable to find partition $part device, skipped."
fi
done < /tmp/partmap.image
v "Writing new UUID to /dev/$diskdev..."
get_image_dd "$1" of="/dev/$diskdev" bs=1 skip=440 count=4 seek=440 conv=fsync
platform_do_bootloader_upgrade "$diskdev"
local parttype=ext4
part_magic_efi "/dev/$diskdev" || return 0
if export_partdevice partdev 1; then
part_magic_fat "/dev/$partdev" && parttype=vfat
mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt
set -- $(dd if="/dev/$diskdev" bs=1 skip=1168 count=16 2>/dev/null | hexdump -v -e '8/1 "%02x "" "2/1 "%02x""-"6/1 "%02x"')
sed -i "s/\(PARTUUID=\)[a-f0-9-]\+/\1$4$3$2$1-$6$5-$8$7-$9/ig" /mnt/boot/grub/grub.cfg
umount /mnt
fi
}