mirror of
https://github.com/openwrt/openwrt.git
synced 2026-05-27 23:00:59 +04:00
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>
This commit is contained in:
committed by
Jonas Jelonek
parent
c7e8cffcb2
commit
9a143bf7ff
@@ -313,6 +313,7 @@ menu "Target Images"
|
||||
bool "Build ONIE installer image (self-extracting .bin)"
|
||||
depends on TARGET_x86
|
||||
depends on GRUB_EFI_IMAGES
|
||||
select PACKAGE_grub2-editenv
|
||||
help
|
||||
Build a self-extracting ONIE installer .bin for installing
|
||||
OpenWrt on switches that ship with the Open Network Install
|
||||
|
||||
@@ -1,9 +1,101 @@
|
||||
RAMFS_COPY_BIN='grub-bios-setup'
|
||||
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) ;;
|
||||
*)
|
||||
@@ -39,6 +131,9 @@ platform_check_image() {
|
||||
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
|
||||
@@ -71,6 +166,11 @@ platform_do_bootloader_upgrade() {
|
||||
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
|
||||
|
||||
@@ -97,7 +97,10 @@ define Build/onie-installer
|
||||
-e 's#@TIMEOUT@#$(GRUB_TIMEOUT)#g' \
|
||||
./onie-install.sh.in > $@.onie/installer.sh
|
||||
script_len=$$(wc -c < $@.onie/installer.sh | tr -d ' '); \
|
||||
sed -i "s/__PYLOAD__/$$(printf '%010d' $$script_len)/" $@.onie/installer.sh
|
||||
payload_len=$$(wc -c < $@.onie/payload.tar | tr -d ' '); \
|
||||
total_len=$$(( script_len + payload_len )); \
|
||||
sed -i "s/__PYLOAD__/$$(printf '%010d' $$script_len)/" $@.onie/installer.sh; \
|
||||
sed -i "s/__TOTLEN__/$$(printf '%010d' $$total_len)/" $@.onie/installer.sh
|
||||
cat $@.onie/installer.sh $@.onie/payload.tar > $@
|
||||
chmod +x $@
|
||||
endef
|
||||
|
||||
@@ -14,9 +14,20 @@
|
||||
|
||||
set -e
|
||||
|
||||
# 10-char zero-padded byte offset of the tar payload. Length-preserving
|
||||
# substitution: __PYLOAD__ (10 chars) -> "NNNNNNNNNN" at build time.
|
||||
# 10-char zero-padded byte offsets, substituted length-preserving at build:
|
||||
# __PYLOAD__ -> byte offset where payload.tar starts (== script size)
|
||||
# __TOTLEN__ -> total file size at build time (script + payload)
|
||||
# Sysupgrade appends the preserved-config tarball to the tail of the file;
|
||||
# the installer detects it by comparing actual file size to BUILD_SIZE.
|
||||
PAYLOAD_OFFSET=__PYLOAD__
|
||||
BUILD_SIZE=__TOTLEN__
|
||||
# Strip leading zeros: shells (dash/ash) parse a leading 0 as octal,
|
||||
# which breaks on digits >=8. Parameter expansion keeps us clear of `expr`
|
||||
# (which returns exit 1 for "0" and trips `set -e`).
|
||||
PAYLOAD_OFFSET=${PAYLOAD_OFFSET#${PAYLOAD_OFFSET%%[!0]*}}
|
||||
BUILD_SIZE=${BUILD_SIZE#${BUILD_SIZE%%[!0]*}}
|
||||
: "${PAYLOAD_OFFSET:=0}"
|
||||
: "${BUILD_SIZE:=0}"
|
||||
|
||||
# Substituted by image.mk:
|
||||
GRUB_CMDLINE='@CMDLINE@'
|
||||
@@ -69,7 +80,17 @@ openwrt_mnt=$(mktemp -d)
|
||||
trap 'cd /; umount "$openwrt_mnt" 2>/dev/null; rm -rf "$payload_dir" "$openwrt_mnt"' EXIT
|
||||
|
||||
echo "Extracting installer payload..."
|
||||
dd if="$0" bs="$PAYLOAD_OFFSET" skip=1 2>/dev/null | tar -x -C "$payload_dir"
|
||||
actual_size=$(wc -c < "$0")
|
||||
if [ "$actual_size" -gt "$BUILD_SIZE" ]; then
|
||||
# Sysupgrade has appended sysupgrade.tgz past the build-time tail.
|
||||
payload_size=$(( BUILD_SIZE - PAYLOAD_OFFSET ))
|
||||
tail -c "+$(( PAYLOAD_OFFSET + 1 ))" "$0" | head -c "$payload_size" \
|
||||
| tar -x -C "$payload_dir"
|
||||
tail -c "+$(( BUILD_SIZE + 1 ))" "$0" > "$payload_dir/sysupgrade.tgz"
|
||||
echo "Preserved config : $(wc -c < "$payload_dir/sysupgrade.tgz") bytes"
|
||||
else
|
||||
dd if="$0" bs="$PAYLOAD_OFFSET" skip=1 2>/dev/null | tar -x -C "$payload_dir"
|
||||
fi
|
||||
[ -f "$payload_dir/rootfs.tar.gz" ] || { echo "ERROR: rootfs.tar.gz missing" >&2; exit 1; }
|
||||
[ -f "$payload_dir/vmlinuz" ] || { echo "ERROR: vmlinuz missing" >&2; exit 1; }
|
||||
|
||||
@@ -103,6 +124,13 @@ echo "Installing kernel..."
|
||||
mkdir -p "$openwrt_mnt/boot"
|
||||
cp "$payload_dir/vmlinuz" "$openwrt_mnt/boot/vmlinuz"
|
||||
|
||||
# Stash preserved config at rootfs root; OpenWrt's preinit
|
||||
# (80_mount_root) extracts it on first boot and /etc/init.d/done
|
||||
# removes it.
|
||||
if [ -f "$payload_dir/sysupgrade.tgz" ]; then
|
||||
cp "$payload_dir/sysupgrade.tgz" "$openwrt_mnt/sysupgrade.tgz"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------ install GRUB to ESP ---
|
||||
if [ "$firmware" = "uefi" ]; then
|
||||
uefi_part=0
|
||||
@@ -153,12 +181,21 @@ cat > "$openwrt_mnt/boot/grub/grub.cfg" <<EOF
|
||||
$GRUB_SERIAL_CONFIG
|
||||
$GRUB_TERMINAL_CONFIG
|
||||
|
||||
set default="0"
|
||||
set timeout="$GRUB_TIMEOUT"
|
||||
|
||||
# GRUB finds this filesystem by ext4 label (set by mkfs.ext4 -L).
|
||||
search --no-floppy --label --set=root $OPENWRT_VOLUME_LABEL
|
||||
|
||||
# One-shot boot selector: sysupgrade writes next_entry=ONIE into
|
||||
# /boot/grub/grubenv to chainload ONIE (in install mode) on the next boot.
|
||||
load_env
|
||||
if [ -n "\$next_entry" ]; then
|
||||
set default="\$next_entry"
|
||||
set next_entry=
|
||||
save_env next_entry
|
||||
else
|
||||
set default="0"
|
||||
fi
|
||||
set timeout="$GRUB_TIMEOUT"
|
||||
|
||||
menuentry "$GRUB_TITLE" {
|
||||
linux /boot/vmlinuz root=PARTUUID=$rootpart_partuuid rootwait $GRUB_CMDLINE noinitrd
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user