opnsense-src/tools/boot/install-boot.sh
Marius Strobl 4ef1c6f75d base: Remove support for the VTOC8 partitioning scheme
The removal of the sparc64 support in February 2020 obsoleted the
VTOC8 partitioning scheme as no other FreeBSD platform makes use
of it. Moreover, the code is bitrotting as nothing defines e. g.
LOADER_VTOC8_SUPPORT any more and, thus, should go now, too. With
this change, the following commits are reverted as far as VTOC8
is concerned and parts haven't already previously been deleted
along with prior sparc64 removals:
094fcb157d
a7d366e958
ba8d50d08b

The alignment example d9711c28ef
added to the VTOC8 section of gpart.8 is folded into the MBR one.

This should finally conclude the deorbit of sparc64-specific bits.

        We had joy, we had fun
        we ran Unix on a Sun.
        But that source and the song
        of FreeBSD have all gone.

Credits to Michael Bueker for the original "Unix on a Sun" and Rod
McKuen for the "Seasons in the Sun" lyrics.
2023-07-26 13:16:12 +02:00

431 lines
11 KiB
Bash
Executable file

#!/bin/sh
# $FreeBSD$
#
# Installs/updates the necessary boot blocks for the desired boot environment
#
# Lightly tested.. Intended to be installed, but until it matures, it will just
# be a boot tool for regression testing.
# insert code here to guess what you have -- yikes!
# Minimum size of FAT filesystems, in KB.
fat32min=33292
fat16min=2100
die() {
echo $*
exit 1
}
doit() {
echo $*
eval $*
}
find_part() {
dev=$1
part=$2
gpart show $dev | tail +2 | awk '$4 == "'$part'" { print $3; }'
}
get_uefi_bootname() {
case ${TARGET:-$(uname -m)} in
amd64) echo bootx64 ;;
arm64) echo bootaa64 ;;
i386) echo bootia32 ;;
arm) echo bootarm ;;
riscv) echo bootriscv64 ;;
*) die "machine type $(uname -m) doesn't support UEFI" ;;
esac
}
make_esp_file() {
local file sizekb loader device stagedir fatbits efibootname
file=$1
sizekb=$2
loader=$3
if [ "$sizekb" -ge "$fat32min" ]; then
fatbits=32
elif [ "$sizekb" -ge "$fat16min" ]; then
fatbits=16
else
fatbits=12
fi
stagedir=$(mktemp -d /tmp/stand-test.XXXXXX)
mkdir -p "${stagedir}/EFI/BOOT"
efibootname=$(get_uefi_bootname)
cp "${loader}" "${stagedir}/EFI/BOOT/${efibootname}.efi"
makefs -t msdos \
-o fat_type=${fatbits} \
-o sectors_per_cluster=1 \
-o volume_label=EFISYS \
-s ${sizekb}k \
"${file}" "${stagedir}"
rm -rf "${stagedir}"
}
make_esp_device() {
local dev file mntpt fstype efibootname kbfree loadersize efibootfile
local isboot1 existingbootentryloaderfile bootorder bootentry
# ESP device node
dev=$1
file=$2
mntpt=$(mktemp -d /tmp/stand-test.XXXXXX)
# See if we're using an existing (formatted) ESP
fstype=$(fstyp "${dev}")
if [ "${fstype}" != "msdosfs" ]; then
newfs_msdos -F 32 -c 1 -L EFISYS "${dev}" > /dev/null 2>&1
fi
mount -t msdosfs "${dev}" "${mntpt}"
if [ $? -ne 0 ]; then
die "Failed to mount ${dev} as an msdosfs filesystem"
fi
echo "Mounted ESP ${dev} on ${mntpt}"
efibootname=$(get_uefi_bootname)
kbfree=$(df -k "${mntpt}" | tail -1 | cut -w -f 4)
loadersize=$(stat -f %z "${file}")
loadersize=$((loadersize / 1024))
# Check if /EFI/BOOT/BOOTxx.EFI is the FreeBSD boot1.efi
# If it is, remove it to avoid leaving stale files around
efibootfile="${mntpt}/EFI/BOOT/${efibootname}.efi"
if [ -f "${efibootfile}" ]; then
isboot1=$(strings "${efibootfile}" | grep "FreeBSD EFI boot block")
if [ -n "${isboot1}" ] && [ "$kbfree" -lt "${loadersize}" ]; then
echo "Only ${kbfree}KB space remaining: removing old FreeBSD boot1.efi file /EFI/BOOT/${efibootname}.efi"
rm "${efibootfile}"
rmdir "${mntpt}/EFI/BOOT"
else
echo "${kbfree}KB space remaining on ESP: renaming old boot1.efi file /EFI/BOOT/${efibootname}.efi /EFI/BOOT/${efibootname}-old.efi"
mv "${efibootfile}" "${mntpt}/EFI/BOOT/${efibootname}-old.efi"
fi
fi
if [ ! -f "${mntpt}/EFI/freebsd/loader.efi" ] && [ "$kbfree" -lt "$loadersize" ]; then
umount "${mntpt}"
rmdir "${mntpt}"
echo "Failed to update the EFI System Partition ${dev}"
echo "Insufficient space remaining for ${file}"
echo "Run e.g \"mount -t msdosfs ${dev} /mnt\" to inspect it for files that can be removed."
die
fi
mkdir -p "${mntpt}/EFI/freebsd"
# Keep a copy of the existing loader.efi in case there's a problem with the new one
if [ -f "${mntpt}/EFI/freebsd/loader.efi" ] && [ "$kbfree" -gt "$((loadersize * 2))" ]; then
cp "${mntpt}/EFI/freebsd/loader.efi" "${mntpt}/EFI/freebsd/loader-old.efi"
fi
echo "Copying loader to /EFI/freebsd on ESP"
cp "${file}" "${mntpt}/EFI/freebsd/loader.efi"
if [ -n "${updatesystem}" ]; then
existingbootentryloaderfile=$(efibootmgr -v | grep "${mntpt}//EFI/freebsd/loader.efi")
if [ -z "$existingbootentryloaderfile" ]; then
# Try again without the double forward-slash in the path
existingbootentryloaderfile=$(efibootmgr -v | grep "${mntpt}/EFI/freebsd/loader.efi")
fi
if [ -z "$existingbootentryloaderfile" ]; then
echo "Creating UEFI boot entry for FreeBSD"
efibootmgr --create --label FreeBSD --loader "${mntpt}/EFI/freebsd/loader.efi" > /dev/null
if [ $? -ne 0 ]; then
die "Failed to create new boot entry"
fi
# When creating new entries, efibootmgr doesn't mark them active, so we need to
# do so. It doesn't make it easy to find which entry it just added, so rely on
# the fact that it places the new entry first in BootOrder.
bootorder=$(efivar --name 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder --print --no-name --hex | head -1)
bootentry=$(echo "${bootorder}" | cut -w -f 3)$(echo "${bootorder}" | cut -w -f 2)
echo "Marking UEFI boot entry ${bootentry} active"
efibootmgr --activate "${bootentry}" > /dev/null
else
echo "Existing UEFI FreeBSD boot entry found: not creating a new one"
fi
else
# Configure for booting from removable media
if [ ! -d "${mntpt}/EFI/BOOT" ]; then
mkdir -p "${mntpt}/EFI/BOOT"
fi
cp "${file}" "${mntpt}/EFI/BOOT/${efibootname}.efi"
fi
umount "${mntpt}"
rmdir "${mntpt}"
echo "Finished updating ESP"
}
make_esp() {
local file loaderfile
file=$1
loaderfile=$2
if [ -f "$file" ]; then
make_esp_file ${file} ${fat32min} ${loaderfile}
else
make_esp_device ${file} ${loaderfile}
fi
}
make_esp_mbr() {
dev=$1
dst=$2
s=$(find_part $dev "!239")
if [ -z "$s" ] ; then
s=$(find_part $dev "efi")
if [ -z "$s" ] ; then
die "No ESP slice found"
fi
fi
make_esp /dev/${dev}s${s} ${dst}/boot/loader.efi
}
make_esp_gpt() {
dev=$1
dst=$2
idx=$(find_part $dev "efi")
if [ -z "$idx" ] ; then
die "No ESP partition found"
fi
make_esp /dev/${dev}p${idx} ${dst}/boot/loader.efi
}
boot_nogeli_gpt_ufs_legacy() {
dev=$1
dst=$2
idx=$(find_part $dev "freebsd-boot")
if [ -z "$idx" ] ; then
die "No freebsd-boot partition found"
fi
doit gpart bootcode -b ${gpt0} -p ${gpt2} -i $idx $dev
}
boot_nogeli_gpt_ufs_uefi() {
make_esp_gpt $1 $2
}
boot_nogeli_gpt_ufs_both() {
boot_nogeli_gpt_ufs_legacy $1 $2 $3
boot_nogeli_gpt_ufs_uefi $1 $2 $3
}
boot_nogeli_gpt_zfs_legacy() {
dev=$1
dst=$2
idx=$(find_part $dev "freebsd-boot")
if [ -z "$idx" ] ; then
die "No freebsd-boot partition found"
fi
doit gpart bootcode -b ${gpt0} -p ${gptzfs2} -i $idx $dev
}
boot_nogeli_gpt_zfs_uefi() {
make_esp_gpt $1 $2
}
boot_nogeli_gpt_zfs_both() {
boot_nogeli_gpt_zfs_legacy $1 $2 $3
boot_nogeli_gpt_zfs_uefi $1 $2 $3
}
boot_nogeli_mbr_ufs_legacy() {
dev=$1
dst=$2
doit gpart bootcode -b ${mbr0} ${dev}
s=$(find_part $dev "freebsd")
if [ -z "$s" ] ; then
die "No freebsd slice found"
fi
doit gpart bootcode -p ${mbr2} ${dev}s${s}
}
boot_nogeli_mbr_ufs_uefi() {
make_esp_mbr $1 $2
}
boot_nogeli_mbr_ufs_both() {
boot_nogeli_mbr_ufs_legacy $1 $2 $3
boot_nogeli_mbr_ufs_uefi $1 $2 $3
}
boot_nogeli_mbr_zfs_legacy() {
dev=$1
dst=$2
# search to find the BSD slice
s=$(find_part $dev "freebsd")
if [ -z "$s" ] ; then
die "No BSD slice found"
fi
idx=$(find_part ${dev}s${s} "freebsd-zfs")
if [ -z "$idx" ] ; then
die "No freebsd-zfs slice found"
fi
# search to find the freebsd-zfs partition within the slice
# Or just assume it is 'a' because it has to be since it fails otherwise
doit gpart bootcode -b ${dst}/boot/mbr ${dev}
dd if=${dst}/boot/zfsboot of=/tmp/zfsboot1 count=1
doit gpart bootcode -b /tmp/zfsboot1 ${dev}s${s} # Put boot1 into the start of part
sysctl kern.geom.debugflags=0x10 # Put boot2 into ZFS boot slot
doit dd if=${dst}/boot/zfsboot of=/dev/${dev}s${s}a skip=1 seek=1024
sysctl kern.geom.debugflags=0x0
}
boot_nogeli_mbr_zfs_uefi() {
make_esp_mbr $1 $2
}
boot_nogeli_mbr_zfs_both() {
boot_nogeli_mbr_zfs_legacy $1 $2 $3
boot_nogeli_mbr_zfs_uefi $1 $2 $3
}
boot_geli_gpt_ufs_legacy() {
boot_nogeli_gpt_ufs_legacy $1 $2 $3
}
boot_geli_gpt_ufs_uefi() {
boot_nogeli_gpt_ufs_uefi $1 $2 $3
}
boot_geli_gpt_ufs_both() {
boot_nogeli_gpt_ufs_both $1 $2 $3
}
boot_geli_gpt_zfs_legacy() {
boot_nogeli_gpt_zfs_legacy $1 $2 $3
}
boot_geli_gpt_zfs_uefi() {
boot_nogeli_gpt_zfs_uefi $1 $2 $3
}
boot_geli_gpt_zfs_both() {
boot_nogeli_gpt_zfs_both $1 $2 $3
}
# GELI+MBR is not a valid configuration
boot_geli_mbr_ufs_legacy() {
exit 1
}
boot_geli_mbr_ufs_uefi() {
exit 1
}
boot_geli_mbr_ufs_both() {
exit 1
}
boot_geli_mbr_zfs_legacy() {
exit 1
}
boot_geli_mbr_zfs_uefi() {
exit 1
}
boot_geli_mbr_zfs_both() {
exit 1
}
usage() {
printf 'Usage: %s -b bios [-d destdir] -f fs [-g geli] [-h] [-o optargs] -s scheme <bootdev>\n' "$0"
printf 'Options:\n'
printf ' bootdev device to install the boot code on\n'
printf ' -b bios bios type: legacy, uefi or both\n'
printf ' -d destdir destination filesystem root\n'
printf ' -f fs filesystem type: ufs or zfs\n'
printf ' -g geli yes or no\n'
printf ' -h this help/usage text\n'
printf ' -u Run commands such as efibootmgr to update the\n'
printf ' currently running system\n'
printf ' -o optargs optional arguments\n'
printf ' -s scheme mbr or gpt\n'
exit 0
}
srcroot=/
# Note: we really don't support geli boot in this script yet.
geli=nogeli
while getopts "b:d:f:g:ho:s:u" opt; do
case "$opt" in
b)
bios=${OPTARG}
;;
d)
srcroot=${OPTARG}
;;
f)
fs=${OPTARG}
;;
g)
case ${OPTARG} in
[Yy][Ee][Ss]|geli) geli=geli ;;
*) geli=nogeli ;;
esac
;;
u)
updatesystem=1
;;
o)
opts=${OPTARG}
;;
s)
scheme=${OPTARG}
;;
?|h)
usage
;;
esac
done
if [ -n "${scheme}" ] && [ -n "${fs}" ] && [ -n "${bios}" ]; then
shift $((OPTIND-1))
dev=$1
fi
# For gpt, we need to install pmbr as the primary boot loader
# it knows about
gpt0=${srcroot}/boot/pmbr
gpt2=${srcroot}/boot/gptboot
gptzfs2=${srcroot}/boot/gptzfsboot
# For MBR, we have lots of choices, but select mbr, boot0 has issues with UEFI
mbr0=${srcroot}/boot/mbr
mbr2=${srcroot}/boot/boot
# sanity check here
# Check if we've been given arguments. If not, this script is probably being
# sourced, so we shouldn't run anything.
if [ -n "${dev}" ]; then
eval boot_${geli}_${scheme}_${fs}_${bios} $dev $srcroot $opts || echo "Unsupported boot env: ${geli}-${scheme}-${fs}-${bios}"
fi