Index: usr.sbin/efi-update-loader/efi-update-loader.8 =================================================================== --- /dev/null +++ usr.sbin/efi-update-loader/efi-update-loader.8 @@ -0,0 +1,68 @@ +\" +.\" Copyright (c) 2019 Rebecca Cran . +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 12, 2019 +.Dt EFI-UPDATE-LOADER 8 +.Os +.Sh NAME +.Nm efi-update-loader +.Nd Update an EFI System Partition with a FreeBSD loader +.Sh SYNOPSIS +.Nm +.Op Fl d Ar device +.Op Fl l Ar loader +.Sh DESCRIPTION +This program updates the +.Dq Unified Extensible Firmware Interface +.Pq UEFI +.Dq EFI System Partition +.Pq ESP +with a new FreeBSD loader.efi file. + +The ESP is a FAT12, FAT16 or FAT32 filesystem that is used by the UEFI system +firmware to load and run OS boot loaders that are compiled as +.Dq Portable Executable +.Pq PE +binaries. The UEFI Boot Manager, which can be configured with the +.Xr efibootmgr 8 +utility, contains entries which point to the location of the boot loader +within the ESP. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl d Ar device +Specify the device to use as the ESP. +.It Fl l Ar loader +Specify the file to install as the FreeBSD boot loader. This should normally +be /boot/loader.efi, but could also for example be /boot/loader_4th.efi etc. +.Sh SEE ALSO +.Xr efibootmgr 8 , +.Xr efivar 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 13.0 Index: usr.sbin/efi-update-loader/efi-update-loader.sh =================================================================== --- /dev/null +++ usr.sbin/efi-update-loader/efi-update-loader.sh @@ -0,0 +1,212 @@ +#!/bin/sh +# +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# Copyright (c) 2019 Rebecca Cran . +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +set -e + +get_uefi_bootname() { + + case ${TARGET:-$(uname -m)} in + amd64) echo bootx64 ;; + arm64) echo bootaa64 ;; + i386) echo bootia32 ;; + arm) echo bootarm ;; + *) + echo "machine type $(uname -m) doesn't support UEFI" + exit 1 + ;; + esac +} + + +clean_up() { + if [ -z "${mntpt}" ]; then + exit 1 + fi + + if mount | grep -q "${mntpt}" ; then + umount "${mntpt}" + fi + + if [ -d "${mntpt}" ]; then + rmdir "${mntpt}" + fi +} + + +bootefi_is_freebsd() { + efibootname=$(get_uefi_bootname) + + if [ -f "${mntpt}/efi/boot/${efibootname}.efi" ]; then + boot1asbootefi=$(grep "FreeBSD EFI boot block" "${mntpt}/efi/boot/${efibootname}.efi") || true + if [ -n "${boot1asbootefi}" ]; then + echo "true" + fi + fi +} + + +get_freespace_on_esp() { + df -k "${mntpt}" | tail -1 | awk '{print $4}' +} + + +copyloader() { + mntpt=$(mktemp -d /tmp/esp.XXXXXX) + + mount -t msdosfs "$espdev" "${mntpt}" + + ldrsize=$(stat -f %z "${loader}") + # Convert to KB + ldrsize=$((ldrsize / 1024)) + + efibootname=$(get_uefi_bootname) + + if [ ! -d "${mntpt}/EFI" ]; then + mkdir "${mntpt}/EFI" + fi + + if [ ! -d "${mntpt}/EFI/FreeBSD" ]; then + mkdir "${mntpt}/EFI/FreeBSD" + fi + + if [ -n "$(bootefi_is_freebsd)" ]; then + + echo "Moving FreeBSD bootx64.efi to /EFI/FreeBSD/${efibootname}-old.efi" + mv "${mntpt}/EFI/BOOT/${efibootname}.efi" "${mntpt}/EFI/FreeBSD/${efibootname}-old.efi" + fi + + if [ -f "${mntpt}/EFI/FreeBSD/loader.efi" ]; then + existing_ldr_size=$(stat -f %z "${mntpt}/EFI/FreeBSD/loader.efi") + existing_ldr_size=$((existing_ldr_size / 1024)) + fi + + if [ -f "${mntpt}/efi/freebsd/loader.efi" ] && ! cmp -sz "${loader}" "${mntpt}/efi/freebsd/loader.efi"; then + # Check we have enough free space + if [ ! -e "${mntpt}/EFI/FreeBSD/loader-old.efi" ] && [ $(($(get_freespace_on_esp))) -lt ${existing_ldr_size} ] ; then + echo "Insufficient space on ESP to back up loader.efi; skipping backup" + else + echo "Backing up /EFI/FreeBSD/loader.efi to /EFI/FreeBSD/loader-old.efi" + cp "${mntpt}/EFI/FreeBSD/loader.efi" "${mntpt}/EFI/FreeBSD/loader-old.efi" + fi + fi + + if [ -e "${mntpt}/EFI/FreeBSD/loader.efi" ]; then + spaceneeded=$((existing_ldr_size - ldrsize)) + else + spaceneeded=${ldrsize} + fi + + if [ $(($(get_freespace_on_esp))) -lt ${spaceneeded} ]; then + echo "Error: Insufficient space on ESP ${espdev} to install ${loader}." + eval clean_up + exit 1 + fi + + echo "Copying ${loader} to /EFI/FreeBSD/loader.efi" + cp "${loader}" "${mntpt}/EFI/FreeBSD/loader.efi" + + if ! efibootmgr -v | grep -qi "EFI/FreeBSD/loader.efi" ; then + echo "Boot Option for FreeBSD doesn't exist: it's recommended to run efibootmgr. e.g.:" + echo " efibootmgr -c -a -L FreeBSD -l /mnt/EFI/FreeBSD/loader.efi" + fi + + umount "${mntpt}" + rmdir "${mntpt}" +} + + +usage() { + printf 'usage: %s -d [espdev] -l [loader]\n' "${progname}" + printf '\t-d ESP device\n' + printf '\t-l FreeBSD EFI loader\n' + exit 0 +} + + +progname=$0 +loader=/boot/loader.efi +espdev= + +while getopts "d:l:h" opt; do + case "$opt" in + d) + espdev=${OPTARG} + ;; + l) + loader=${OPTARG} + ;; + ?) + usage + ;; + esac +done + +trap clean_up 1 2 15 EXIT + +if [ -z "${espdev}" ]; then + + mntpt=$(mktemp -d /tmp/esp.XXXXXX) + + for disk in $(sysctl -n kern.disks); do + espindexes=$(gpart show "${disk}" 2> /dev/null | grep efi | awk '{print $3}') + for idx in ${espindexes}; do + if [ -e "/dev/${disk}p${idx}" ]; then + dev=/dev/${disk}p${idx} + elif [ -e "/dev/${disk}s${idx}" ]; then + dev=/dev/${disk}s${idx} + fi + + if mount -t msdosfs "${dev}" "${mntpt}" 2> /dev/null ; then + + if [ -n "$(bootefi_is_freebsd)" ] || [ -e "${mntpt}/efi/freebsd/loader.efi" ]; then + espdev=${dev} + echo "Found FreeBSD ESP device ${espdev}" + fi + + umount "${mntpt}" + fi + done + done + + rmdir "${mntpt}" + + if [ -z "${espdev}" ]; then + echo "Error: could not detect ESP containing FreeBSD loader to update" + exit 1 + fi +fi + +if [ -z "${espdev}" ]; then + eval usage + exit 1 +fi + +eval copyloader "${espdev}" "${loader}" + +trap 1 2 15 EXIT