#! /bin/bash -e arch=`uname -m` rootdir= bootdir= efidir= install_device= efibootdir= ca_string= no_nvram=no removable=no clean=no sysconfdir="/etc" libdir="/usr/lib64" # Beware, this is arch dependent! datadir="/usr/share" source_dir="${datadir}/efi/${arch}" efibootmgr="/usr/sbin/efibootmgr" grub_probe="/usr/sbin/grub2-probe" grub_mkrelpath="/usr/bin/grub2-mkrelpath" grub_install="/usr/sbin/grub2-install" grub_install_target= self="`basename $0`" grub_cfg="/boot/grub2/grub.cfg" update_boot=no def_grub_efi="${source_dir}/grub.efi" def_boot_efi= [ ! -r /usr/etc/default/shim ] || . /usr/etc/default/shim [ ! -r /etc/default/shim ] || . /etc/default/shim if [ -z "$def_shim_efi" -o ! -e ${source_dir}/${def_shim_efi} ] ; then def_shim_efi="shim.efi" fi source_shim_efi="${source_dir}/${def_shim_efi}" if [ x${arch} = xx86_64 ] ; then grub_install_target="x86_64-efi" def_boot_efi="bootx64.efi" elif [ x${arch} = xaarch64 ] ; then grub_install_target="arm64-efi" def_boot_efi="bootaa64.efi" else echo "Unsupported architecture: ${arch}" exit 1 fi if [ ! -d "${source_dir}" -o ! -e "${def_grub_efi}" ] ; then # for outdated packages fall back to previous behavior source_dir="$libdir/efi" def_grub_efi="${source_dir}/grub.efi" fi # Get GRUB_DISTRIBUTOR. if test -f "${sysconfdir}/default/grub" ; then . "${sysconfdir}/default/grub" fi if [ x"${GRUB_DISTRIBUTOR}" = x ] && [ -f "${sysconfdir}/os-release" ] ; then . "${sysconfdir}/os-release" GRUB_DISTRIBUTOR="${NAME} ${VERSION}" fi bootloader_id="$(echo "$GRUB_DISTRIBUTOR" | tr 'A-Z' 'a-z' | cut -d' ' -f1)" if test -z "$bootloader_id"; then bootloader_id=grub fi efi_distributor="$bootloader_id" bootloader_id="${bootloader_id}-secureboot" case "$bootloader_id" in "sle"*) ca_string='SUSE Linux Enterprise Secure Boot CA1';; "opensuse"*) ca_string='openSUSE Secure Boot CA1';; *) ca_string="";; esac is_azure () { local bios_vendor; local product_name; local sys_vendor; local sysfs_dmi_id="/sys/class/dmi/id" if test -e "${sysfs_dmi_id}/bios_vendor"; then bios_vendor=$(cat "${sysfs_dmi_id}/bios_vendor") fi if test -e "${sysfs_dmi_id}/product_name"; then product_name=$(cat "${sysfs_dmi_id}/product_name") fi if test -e "${sysfs_dmi_id}/sys_vendor"; then sys_vendor=$(cat "${sysfs_dmi_id}/sys_vendor") fi if test "x${bios_vendor}" != "xMicrosoft Corporation"; then # return false return 1 fi if test "x${product_name}" != "xVirtual Machine"; then # return false return 1 fi if test "x${sys_vendor}" != "xMicrosoft Corporation"; then # return false return 1 fi # return true return 0 } usage () { echo "Usage: $self [OPTION] [INSTALL_DEVICE]" echo echo "Install Secure Boot Loaders on your drive." echo echo "--directory=DIR use images from DIR." echo "--grub-probe=FILE use FILE as grub-probe." echo "--removable the installation device is removable." echo "--no-nvram don't update the NVRAM variable." echo "--bootloader-id=ID the ID of bootloader." echo "--efi-directory=DIR use DIR as the EFI System Partition root." echo "--config-file=FILE use FILE as config file, default is $grub_cfg." echo "--clean remove all installed files and configs." echo "--suse-enable-tpm install grub.efi with TPM support." echo echo "INSTALL_DEVICE must be system device filename." } argument () { opt="$1" shift if test $# -eq 0; then echo "$0: option requires an argument -- \`$opt'" 1>&2 exit 1 fi echo "$1" } # Check the arguments. while test $# -gt 0 do option=$1 shift case "$option" in -h | --help) usage exit 0 ;; --root-directory) rootdir="`argument $option "$@"`"; shift;; --root-directory=*) rootdir="`echo "$option" | sed 's/--root-directory=//'`" ;; --efi-directory) efidir="`argument $option "$@"`"; shift;; --efi-directory=*) efidir="`echo "$option" | sed 's/--efi-directory=//'`" ;; --directory | -d) source_dir="`argument $option "$@"`"; shift;; --directory=*) source_dir="`echo "$option" | sed 's/--directory=//'`" ;; --bootloader-id) bootloader_id="`argument $option "$@"`"; shift;; --bootloader-id=*) bootloader_id="`echo "$option" | sed 's/--bootloader-id=//'`" ;; --grub-probe) grub_probe="`argument "$option" "$@"`"; shift;; --grub-probe=*) grub_probe="`echo "$option" | sed 's/--grub-probe=//'`" ;; --config-file) grub_cfg="`argument "$option" "$@"`"; shift;; --config-file=*) grub_cfg="`echo "$option" | sed 's/--config-file=//'`" ;; --removable) no_nvram=yes removable=yes ;; --no-nvram) no_nvram=yes ;; --suse-enable-tpm) # bsc#1174320 shim-install uses wrong paths for EFI files # There are 3 possible locations of grub-tpm.efi and we will check them # one by one. if [ -e "${source_dir}/grub-tpm.efi" ]; then source_grub_efi="${source_dir}/grub-tpm.efi" elif [ -e "${datadir}/grub2/${grub_install_target}/grub-tpm.efi" ] ; then source_grub_efi="${datadir}/grub2/${grub_install_target}/grub-tpm.efi" else source_grub_efi="/usr/lib/grub2/${grub_install_target}/grub-tpm.efi" fi ;; --clean) clean=yes ;; -*) echo "Unrecognized option \`$option'" 1>&2 usage exit 1 ;; *) if test "x$install_device" != x; then echo "More than one install device?" 1>&2 usage exit 1 fi install_device="${option}" ;; esac done if test -n "$efidir"; then efi_fs=`"$grub_probe" --target=fs "${efidir}"` if test "x$efi_fs" = xfat; then :; else echo "$efidir doesn't look like an EFI partition." 1>&2 efidir= fi fi if [ -z "$bootdir" ]; then bootdir="/boot" if [ -n "$rootdir" ] ; then # Initialize bootdir if rootdir was initialized. bootdir="${rootdir}/boot" fi fi # Find the EFI System Partition. if test -n "$efidir"; then install_device="`"$grub_probe" --target=device --device-map= "${efidir}"`" else if test -d "${bootdir}/efi"; then install_device="`"$grub_probe" --target=device --device-map= "${bootdir}/efi"`" # Is it a mount point? if test "x$install_device" != "x`"$grub_probe" --target=device --device-map= "${bootdir}"`"; then efidir="${bootdir}/efi" fi elif test -d "${bootdir}/EFI"; then install_device="`"$grub_probe" --target=device --device-map= "${bootdir}/EFI"`" # Is it a mount point? if test "x$install_device" != "x`"$grub_probe" --target=device --device-map= "${bootdir}"`"; then efidir="${bootdir}/EFI" fi elif test -n "$rootdir" && test "x$rootdir" != "x/"; then # The EFI System Partition may have been given directly using # --root-directory. install_device="`"$grub_probe" --target=device --device-map= "${rootdir}"`" # Is it a mount point? if test "x$install_device" != "x`"$grub_probe" --target=device --device-map= "${rootdir}/.."`"; then efidir="${rootdir}" fi fi if test -n "$efidir"; then efi_fs=`"$grub_probe" --target=fs "${efidir}"` if test "x$efi_fs" = xfat; then :; else echo "$efidir doesn't look like an EFI partition." 1>&2 efidir= fi fi fi if test -n "$efidir"; then efi_file=shim.efi efibootdir="$efidir/EFI/boot" mkdir -p "$efibootdir" || exit 1 if test "$removable" = "yes" ; then efidir="$efibootdir" else efidir="$efidir/EFI/$efi_distributor" mkdir -p "$efidir" || exit 1 fi else echo "No valid EFI partition" 1>&2 exit 1; fi if test "$removable" = "no" -a -f "$efibootdir/$def_boot_efi"; then if test -n "$ca_string" && (grep -q "$ca_string" "$efibootdir/$def_boot_efi"); then update_boot=yes fi else update_boot=yes fi if test "$clean" = "yes"; then rm -f "${efidir}/shim.efi" rm -f "${efidir}/MokManager.efi" rm -f "${efidir}/grub.efi" rm -f "${efidir}/grub.cfg" rm -f "${efidir}/boot.csv" if test "$update_boot" = "yes"; then rm -f "${efibootdir}/${def_boot_efi}" rm -f "${efibootdir}/fallback.efi" # bsc#1175626, bsc#1175656 also clean up MokManager rm -f "${efibootdir}/MokManager.efi" fi if test "$no_nvram" = no && test -n "$bootloader_id"; then # Delete old entries from the same distributor. for bootnum in `$efibootmgr | grep '^Boot[0-9]' | \ fgrep -i " $bootloader_id" | cut -b5-8`; do $efibootmgr -b "$bootnum" -B done fi exit 0 fi cp "${source_dir}/MokManager.efi" "${efidir}" if test -n "$source_grub_efi" && ! test -f "$source_grub_efi"; then echo "File $source_grub_efi doesn't exist, fallback to default one" 1>&2 source_grub_efi="" fi if test -z "$source_grub_efi"; then source_grub_efi="$def_grub_efi" fi echo "copying $source_grub_efi to ${efidir}/grub.efi" cp "$source_grub_efi" "${efidir}/grub.efi" if test "$efidir" != "$efibootdir" ; then cp "${source_shim_efi}" "${efidir}/shim.efi" if test -n "$bootloader_id"; then echo "shim.efi,${bootloader_id}" | iconv -f ascii -t ucs2 > "${efidir}/boot.csv" fi fi if test "$update_boot" = "yes"; then cp "$source_shim_efi" "${efibootdir}/${def_boot_efi}" if test "$removable" = "no"; then cp "${source_dir}/fallback.efi" "${efibootdir}" # bsc#1175626, bsc#1175656 Since shim 15, loading MokManager becomes # mandatory if a MOK request exists. Copy MokManager to \EFI\boot so # that boot*.efi can load MokManager to process the request instead # of shutting down the system immediately. cp "${source_dir}/MokManager.efi" "${efibootdir}" fi fi make_grubcfg () { grub_cfg_dirname=`dirname $grub_cfg` grub_cfg_basename=`basename $grub_cfg` cfg_fs_uuid=`"$grub_probe" --target=fs_uuid "$grub_cfg_dirname"` # bsc#1153953 - Leap 42.3 boot error snapshot missing # We have to check btrfs is used as root file system to enable relative path # lookup for file to be on par with other utility which also accounts for it. GRUB_FS="$(stat -f --printf=%T / || echo unknown)" if test "x$SUSE_BTRFS_SNAPSHOT_BOOTING" = "xtrue" && [ "x${GRUB_FS}" = "xbtrfs" ] ; then cat < "${efidir}/grub.cfg" # bnc#889765 GRUB shows broken letters at boot # invoke grub_install to initialize /boot/grub2 directory with files needed by grub.cfg # bsc#1118363 shim-install didn't specify the target for grub2-install # set the target explicitly for some special cases ${grub_install} --target=${grub_install_target} --no-nvram if test "$no_nvram" = no && test -n "$bootloader_id"; then modprobe -q efivars 2>/dev/null || true # Delete old entries from the same distributor. for bootnum in `$efibootmgr | grep '^Boot[0-9]' | \ fgrep -i " $bootloader_id" | cut -b5-8`; do $efibootmgr -b "$bootnum" -B done efidir_drive="$("$grub_probe" --target=drive --device-map= "$efidir")" efidir_disk="$("$grub_probe" --target=disk --device-map= "$efidir")" if test -z "$efidir_drive" || test -z "$efidir_disk"; then echo "Can't find GRUB drive for $efidir; unable to create EFI Boot Manager entry." >&2 # bsc#1119762 If the MD device is partitioned, we just need to create one # boot entry since the partitions are nested partitions and the mirrored # partitions share the same UUID. elif [[ "$efidir_drive" == \(mduuid/* && "$efidir_drive" != \(mduuid/*,* ]]; then eval $(mdadm --detail --export "$efidir_disk" | perl -ne 'print if m{^MD_LEVEL=}; push( @D, $1) if (m{^MD_DEVICE_\S+_DEV=(\S+)$}); sub END() {print "MD_DEVS=\"", join( " ", @D), "\"\n";};') if [ "$MD_LEVEL" != "raid1" ]; then echo "GRUB drive for $efidir not on RAID1; unable to create EFI Boot Manager entry." >&2 fi for mddev in $MD_DEVS; do efidir_drive="$("$grub_probe" --target=drive --device-map= -d "$mddev")" efidir_disk="$("$grub_probe" --target=disk --device-map= -d "$mddev")" efidir_part="$(echo "$efidir_drive" | sed 's/^([^,]*,[^0-9]*//; s/[^0-9].*//')" efidir_d=${mddev#/dev/} $efibootmgr -c -d "$efidir_disk" -p "$efidir_part" -w \ -L "$bootloader_id ($efidir_d)" -l "\\EFI\\$efi_distributor\\$efi_file" done else efidir_part="$(echo "$efidir_drive" | sed 's/^([^,]*,[^0-9]*//; s/[^0-9].*//')" $efibootmgr -c -d "$efidir_disk" -p "$efidir_part" -w \ -L "$bootloader_id" -l "\\EFI\\$efi_distributor\\$efi_file" fi fi # bsc#1185464 bsc#1185961 # The Azure firmware sometimes doesn't respect the boot option created by # either efibootmgr or fallback.efi so we have to remove fallback.efi to # avoid the endless reset loop. if is_azure; then # Skip the workaround if we don't own \EFI\Boot or the removable # option is used if test "$update_boot" = "yes" && test "$removable" = "no"; then # Remove fallback.efi which could cause the reset loop in Azure rm -f "${efibootdir}/fallback.efi" # Remove the older grub binary and config rm -f "${efibootdir}/grub.efi" rm -f "${efibootdir}/grub.cfg" # Install new grub binary and config file to \EFI\Boot as # the "removable" option cp "${efidir}/grub.cfg" "${efibootdir}/grub.cfg" cp "${efidir}/grub.efi" "${efibootdir}/grub.efi" fi fi