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>
This commit is contained in:
parent
15593de376
commit
c7e8cffcb2
@ -309,6 +309,21 @@ menu "Target Images"
|
||||
depends on GRUB_IMAGES || GRUB_EFI_IMAGES
|
||||
select PACKAGE_kmod-e1000
|
||||
|
||||
config ONIE_INSTALLER_IMAGES
|
||||
bool "Build ONIE installer image (self-extracting .bin)"
|
||||
depends on TARGET_x86
|
||||
depends on GRUB_EFI_IMAGES
|
||||
help
|
||||
Build a self-extracting ONIE installer .bin for installing
|
||||
OpenWrt on switches that ship with the Open Network Install
|
||||
Environment (e.g. Mellanox/NVIDIA Spectrum-based SN series).
|
||||
|
||||
The resulting .bin wraps a tar payload (rootfs + kernel) in a
|
||||
shell script that follows the ONIE demo installer pattern:
|
||||
a new GPT partition labelled OPENWRT-ROOT is created for
|
||||
OpenWrt, and the existing ONIE-BOOT partition is preserved so
|
||||
ONIE rescue remains bootable.
|
||||
|
||||
config TARGET_SERIAL
|
||||
string "Serial port device"
|
||||
depends on TARGET_x86 || TARGET_armsr || TARGET_loongarch64
|
||||
|
||||
@ -79,6 +79,29 @@ define Build/grub-install
|
||||
$@
|
||||
endef
|
||||
|
||||
define Build/onie-installer
|
||||
rm -rf $@.onie
|
||||
mkdir -p $@.onie/payload
|
||||
$(TAR) --numeric-owner --owner=0 --group=0 --sort=name \
|
||||
$(if $(SOURCE_DATE_EPOCH),--mtime=@$(SOURCE_DATE_EPOCH)) \
|
||||
-C $(TARGET_DIR)/ -cf - . | gzip -9n > $@.onie/payload/rootfs.tar.gz
|
||||
$(CP) $(KDIR)/$(KERNEL_NAME) $@.onie/payload/vmlinuz
|
||||
$(TAR) --numeric-owner --owner=0 --group=0 --sort=name \
|
||||
$(if $(SOURCE_DATE_EPOCH),--mtime=@$(SOURCE_DATE_EPOCH)) \
|
||||
-C $@.onie/payload -cf $@.onie/payload.tar .
|
||||
sed \
|
||||
-e 's#@SERIAL_CONFIG@#$(strip $(GRUB_SERIAL_CONFIG))#g' \
|
||||
-e 's#@TERMINAL_CONFIG@#$(strip $(GRUB_TERMINAL_CONFIG))#g' \
|
||||
-e 's#@CMDLINE@#$(strip $(BOOTOPTS) $(GRUB_CONSOLE_CMDLINE))#g' \
|
||||
-e 's#@TITLE@#$(GRUB_TITLE)#g' \
|
||||
-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
|
||||
cat $@.onie/installer.sh $@.onie/payload.tar > $@
|
||||
chmod +x $@
|
||||
endef
|
||||
|
||||
define Build/iso
|
||||
$(CP) $(KDIR)/$(KERNEL_NAME) $@.boot/boot/vmlinuz
|
||||
cat \
|
||||
@ -114,6 +137,7 @@ define Device/Default
|
||||
IMAGE/combined-efi.vdi := grub-config efi | combined efi | grub-install efi | qemu-image vdi
|
||||
IMAGE/combined-efi.vmdk := grub-config efi | combined efi | grub-install efi | qemu-image vmdk
|
||||
IMAGE/combined-efi.vhdx := grub-config efi | combined efi | grub-install efi | qemu-image vhdx -o subformat=dynamic
|
||||
ARTIFACT/onie-installer.bin := onie-installer
|
||||
ifeq ($(CONFIG_TARGET_IMAGES_GZIP),y)
|
||||
IMAGES-y := rootfs.img.gz
|
||||
IMAGES-$$(CONFIG_GRUB_IMAGES) += combined.img.gz
|
||||
@ -130,6 +154,7 @@ define Device/Default
|
||||
ARTIFACTS-$$(CONFIG_GRUB_IMAGES) += image.iso
|
||||
ARTIFACTS-$$(CONFIG_GRUB_EFI_IMAGES) += image-efi.iso
|
||||
endif
|
||||
ARTIFACTS-$$(CONFIG_ONIE_INSTALLER_IMAGES) += onie-installer.bin
|
||||
ifeq ($(CONFIG_VDI_IMAGES),y)
|
||||
IMAGES-$$(CONFIG_GRUB_IMAGES) += combined.vdi
|
||||
IMAGES-$$(CONFIG_GRUB_EFI_IMAGES) += combined-efi.vdi
|
||||
|
||||
190
target/linux/x86/image/onie-install.sh.in
Normal file
190
target/linux/x86/image/onie-install.sh.in
Normal file
@ -0,0 +1,190 @@
|
||||
#!/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
|
||||
Loading…
Reference in New Issue
Block a user