1
1
openwrt/target/linux/x86/image/onie-install.sh.in
Keno Fischer c7e8cffcb2
x86: add onie-installer image type
The current documentation for using OpenWRT on Mellanox Spectrum
switches (https://openwrt.org/toh/mellanox/spectrum) suggests
reflashing the entire harddrive from the recovery USB. This is not
the most friendly way to install a new OS on these switches. From
factory, they come with ONIE (Open Network Install Environment),
which is a linux-based preboot environment for fetching an OS
image from the network and installing it on disk. The installer
is a self-executing bash script that executes inside the ONIE
environment. The installer is expected to preserve the ONIE partition
for use as recovery environement. To be a better citizen on
these platforms, it would be preferrable to provide OpenWRT as
an ONIE-compatible installer.

This PR adds an ONIE_INSTALLER_IMAGES build option that produces
an ONIE compatible .bin. The generated .bin follows the ONIE demo
installer pattern [1]: it creates a new GPT partition
labelled OPENWRT-ROOT on the ONIE install device, formats ext4, extracts
the OpenWrt rootfs and kernel into it, installs GRUB into the existing
UEFI ESP under bootloader-id "OpenWrt", and adds a NVRAM boot entry via
efibootmgr.  ONIE-BOOT is preserved so ONIE rescue remains available.

Tested with the config at [2] on a Mellanox Spectrum SN3800 to produce
a booting OpenWRT install.

[1] https://github.com/opencomputeproject/onie/demo/installer/grub-arch/install.sh
[2] https://gist.github.com/Keno/abc8c5b72645e73fadd1ff0d9616b23d

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:53 +02:00

191 lines
6.7 KiB
Bash

#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
#
# OpenWrt ONIE installer -- self-extracting shell archive.
# Wrapped around a tar payload that contains:
# rootfs.tar.gz -- OpenWrt root tree
# vmlinuz -- OpenWrt kernel
# Appended at PAYLOAD_OFFSET (filled in by image.mk at build time).
#
# Follows the ONIE demo installer pattern:
# https://github.com/opencomputeproject/onie/blob/master/demo/installer/grub-arch/install.sh
# The existing ESP and ONIE-BOOT partition are preserved; a new GPT
# partition labelled OPENWRT-ROOT is created for OpenWrt.
set -e
# 10-char zero-padded byte offset of the tar payload. Length-preserving
# substitution: __PYLOAD__ (10 chars) -> "NNNNNNNNNN" at build time.
PAYLOAD_OFFSET=__PYLOAD__
# Substituted by image.mk:
GRUB_CMDLINE='@CMDLINE@'
GRUB_SERIAL_CONFIG='@SERIAL_CONFIG@'
GRUB_TERMINAL_CONFIG='@TERMINAL_CONFIG@'
GRUB_TITLE='@TITLE@'
GRUB_TIMEOUT='@TIMEOUT@'
OPENWRT_VOLUME_LABEL="OPENWRT-ROOT"
OPENWRT_PART_SIZE=512 # MiB -- OpenWrt x86 default is 104; 512 gives headroom
echo ""
echo "=========================================================="
echo " OpenWrt ONIE installer"
echo " $GRUB_TITLE"
echo "=========================================================="
echo ""
cd "$(dirname "$0")"
lib_dir="/lib/onie"
# shellcheck disable=SC1090,SC1091
[ -r "$lib_dir/onie-blkdev-common" ] && . "$lib_dir/onie-blkdev-common"
[ -r /etc/machine.conf ] && . /etc/machine.conf
# Install on the same disk that holds ONIE-BOOT (demo-installer idiom)
blk_dev=$(blkid | awk -F: '/ONIE-BOOT/ {print $1; exit}' \
| sed -e 's/[0-9]*$//' -e 's/p$//')
[ -b "$blk_dev" ] || {
echo "ERROR: unable to determine ONIE install block device" >&2
exit 1
}
echo "Install device : $blk_dev"
if [ -d /sys/firmware/efi/efivars ]; then
firmware="uefi"
else
firmware="bios"
fi
echo "Firmware mode : $firmware"
blk_suffix=
case "$blk_dev" in
*mmcblk*|*nvme*) blk_suffix="p" ;;
esac
# ------------------------------------------------ extract the payload ---
payload_dir=$(mktemp -d)
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"
[ -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; }
# --------------------------------------------- create target partition ---
prev_part=$(sgdisk -p "$blk_dev" | awk -v lbl="$OPENWRT_VOLUME_LABEL" '$NF==lbl {print $1}')
if [ -n "$prev_part" ]; then
echo "Removing previous $OPENWRT_VOLUME_LABEL partition (#$prev_part)..."
sgdisk -d "$prev_part" "$blk_dev" >/dev/null
partprobe "$blk_dev" || true
fi
last_part=$(sgdisk -p "$blk_dev" | awk '/^ *[0-9]+ /{p=$1} END{print p+0}')
openwrt_part=$(( last_part + 1 ))
echo "Creating ${blk_dev}${blk_suffix}${openwrt_part} (${OPENWRT_PART_SIZE} MiB, label $OPENWRT_VOLUME_LABEL)..."
sgdisk --new=${openwrt_part}::+${OPENWRT_PART_SIZE}M \
--attributes=${openwrt_part}:=:0x0 \
--change-name=${openwrt_part}:"$OPENWRT_VOLUME_LABEL" \
"$blk_dev" >/dev/null
partprobe "$blk_dev" || blockdev --rereadpt "$blk_dev" || true
sleep 1
openwrt_dev="${blk_dev}${blk_suffix}${openwrt_part}"
mkfs.ext4 -F -q -L "$OPENWRT_VOLUME_LABEL" "$openwrt_dev"
mount -t ext4 -o defaults,rw "$openwrt_dev" "$openwrt_mnt"
# ----------------------------------------------- populate the rootfs ---
echo "Extracting OpenWrt root filesystem..."
tar -xzf "$payload_dir/rootfs.tar.gz" -C "$openwrt_mnt"
echo "Installing kernel..."
mkdir -p "$openwrt_mnt/boot"
cp "$payload_dir/vmlinuz" "$openwrt_mnt/boot/vmlinuz"
# ------------------------------------------------ install GRUB to ESP ---
if [ "$firmware" = "uefi" ]; then
uefi_part=0
for p in $(seq 1 16); do
if sgdisk -i "$p" "$blk_dev" 2>/dev/null \
| grep -q C12A7328-F81F-11D2-BA4B-00A0C93EC93B; then
uefi_part=$p; break
fi
done
[ "$uefi_part" -ne 0 ] || { echo "ERROR: cannot find UEFI ESP on $blk_dev" >&2; exit 1; }
echo "Reusing ESP : ${blk_dev}${blk_suffix}${uefi_part}"
echo "Installing GRUB (x86_64-efi)..."
grub-install \
--no-nvram \
--bootloader-id="OpenWrt" \
--efi-directory="/boot/efi" \
--boot-directory="$openwrt_mnt/boot" \
--recheck "$blk_dev" >/dev/null
for b in $(efibootmgr | awk '/OpenWrt/ {gsub("Boot",""); gsub("\\*",""); print $1}'); do
efibootmgr -b "$b" -B >/dev/null 2>&1 || true
done
efibootmgr --quiet --create \
--label "OpenWrt" \
--disk "$blk_dev" --part "$uefi_part" \
--loader "/EFI/OpenWrt/grubx64.efi"
else
echo "Installing GRUB (i386-pc, BIOS)..."
grub-install \
--target="i386-pc" \
--boot-directory="$openwrt_mnt/boot" \
--recheck "$blk_dev" >/dev/null
fi
# ---------------------------------------------- write target grub.cfg ---
# The kernel built-in root parser handles root=PARTUUID=... but not
# root=LABEL=... -- query the real PARTUUID and bake it in. We read it
# from the GPT via sgdisk since busybox blkid in ONIE doesn't support
# `-s PARTUUID -o value` and returns the device path instead.
rootpart_partuuid=$(sgdisk -i "$openwrt_part" "$blk_dev" \
| awk '/Partition unique GUID:/ {print tolower($NF)}')
[ -n "$rootpart_partuuid" ] || { echo "ERROR: cannot read PARTUUID of $openwrt_dev" >&2; exit 1; }
echo "Root PARTUUID : $rootpart_partuuid"
mkdir -p "$openwrt_mnt/boot/grub"
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
menuentry "$GRUB_TITLE" {
linux /boot/vmlinuz root=PARTUUID=$rootpart_partuuid rootwait $GRUB_CMDLINE noinitrd
}
menuentry "$GRUB_TITLE (failsafe mode)" {
linux /boot/vmlinuz failsafe=true root=PARTUUID=$rootpart_partuuid rootwait $GRUB_CMDLINE noinitrd
}
EOF
onie_grub_frag="${onie_root_dir:-/mnt/onie-boot/onie}/grub.d/50_onie_grub"
if [ -x "$onie_grub_frag" ]; then
echo "Appending ONIE boot entry to grub.cfg..."
"$onie_grub_frag" >> "$openwrt_mnt/boot/grub/grub.cfg" 2>/dev/null || true
fi
# --------------------------------------------------- finish and exit ---
sync
umount "$openwrt_mnt"
trap - EXIT
rm -rf "$payload_dir" "$openwrt_mnt"
if [ -x /bin/onie-nos-mode ]; then
/bin/onie-nos-mode -s
fi
echo ""
echo "=========================================================="
echo " OpenWrt installed. ONIE will reboot into it now."
echo "=========================================================="
exit 0