From: Colin Watson <cjwatson@ubuntu.com>
To: grub-devel@gnu.org
Subject: [PATCH] Improve EFI grub-install to handle non-Apple systems
Date: Mon, 12 Jul 2010 12:52:36 +0100 [thread overview]
Message-ID: <20100712115236.GL21862@riva.ucam.org> (raw)
At the moment, the EFI grub-install script only handles Apple Mac
systems. This patch adds support for systems that conform to the UEFI
specification, while retaining support for Apple systems.
I couldn't find anything approaching a standard for where the EFI System
Partition should be mounted, so I chose to look for it in /boot/efi,
since I don't like creating new top-level directories. If there's a
different convention then I'd be happy to add support for it.
Vladimir asked whether I could merge util/i386/efi/grub-install.in into
util/grub-install.in at the same time. This is of course desirable but
I haven't got round to it yet, and I didn't want to stall this patch
indefinitely until I find time for that.
2010-07-12 Colin Watson <cjwatson@ubuntu.com>
* util/i386/efi/grub-install.in: Add support for systems that
conform to the UEFI specification, as well as Apple systems.
Currently looks for the EFI System Partition on /boot/efi.
=== modified file 'util/i386/efi/grub-install.in'
--- util/i386/efi/grub-install.in 2010-07-04 12:23:55 +0000
+++ util/i386/efi/grub-install.in 2010-07-12 11:43:20 +0000
@@ -24,6 +24,7 @@ exec_prefix=@exec_prefix@
sbindir=@sbindir@
bindir=@bindir@
libdir=@libdir@
+sysconfdir=@sysconfdir@
PACKAGE_NAME=@PACKAGE_NAME@
PACKAGE_TARNAME=@PACKAGE_TARNAME@
PACKAGE_VERSION=@PACKAGE_VERSION@
@@ -43,10 +44,14 @@ rootdir=
grub_prefix=`echo /boot/grub | sed ${transform}`
modules=
+efibootmgr=`which efibootmgr 2>/dev/null || true`
+
no_floppy=
force_lba=
recheck=no
+removable=no
debug=no
+efi_quiet=
# Usage: usage
# Print the usage.
@@ -65,6 +70,7 @@ Install GRUB on your EFI partition.
--grub-probe=FILE use FILE as grub-probe
--no-floppy do not probe any floppy drive
--recheck probe a device map even if it already exists
+ --removable the installation device is removable
$self copies GRUB images into the DIR/boot directory specified by
--root-directory.
@@ -127,9 +133,14 @@ do
no_floppy="--no-floppy" ;;
--recheck)
recheck=yes ;;
+ --removable)
+ removable=yes ;;
# This is an undocumented feature...
--debug)
debug=yes ;;
+ # Intentionally undocumented; for compatibility only.
+ -f | --force)
+ ;;
*)
echo "Unrecognized option \`$option'" 1>&2
usage
@@ -138,9 +149,13 @@ do
esac
done
+# for make_system_path_relative_to_its_root()
+. ${libdir}/grub/grub-mkconfig_lib
+
# If the debugging feature is enabled, print commands.
if test $debug = yes; then
set -x
+ efi_quiet=-q
fi
# Initialize these directories here, since ROOTDIR was initialized.
@@ -177,6 +192,106 @@ else
exit 1
fi
+# Get GRUB_DISTRIBUTOR.
+if test -f ${sysconfdir}/default/grub ; then
+ . ${sysconfdir}/default/grub
+fi
+
+# Find the EFI System Partition.
+efidir=
+if test -d ${bootdir}/efi; then
+ install_device=`$grub_mkdevicemap --device-map=/dev/stdout | $grub_probe --target=device --device-map=/dev/stdin ${bootdir}/efi`
+ # Is it a mount point?
+ if test "x$install_device" != "x`$grub_mkdevicemap --device-map=/dev/stdout | $grub_probe --target=device --device-map=/dev/stdin ${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_mkdevicemap --device-map=/dev/stdout | $grub_probe --target=device --device-map=/dev/stdin ${rootdir}`
+ # Is it a mount point?
+ if test "x$install_device" != "x`$grub_mkdevicemap --device-map=/dev/stdout | $grub_probe --target=device --device-map=/dev/stdin ${rootdir}/..`"; then
+ efidir=${rootdir}
+ fi
+fi
+
+if test -n "$efidir"; then
+ efi_fs=`$grub_probe --target=fs --device-map=${device_map} ${efidir}`
+ if test "x$efi_fs" = xfat; then :; else
+ echo "${efidir} doesn't look like an EFI partition." 1>&2
+ efidir=
+ fi
+fi
+
+if test -n "$efidir"; then
+ # The EFI specification requires that an EFI System Partition must
+ # contain an "EFI" subdirectory, and that OS loaders are stored in
+ # subdirectories below EFI. Vendors are expected to pick names that do
+ # not collide with other vendors. To minimise collisions, we use the
+ # name of our distributor if possible.
+ if test $removable = yes; then
+ # The specification makes stricter requirements of removable
+ # devices, in order that only one image can be automatically loaded
+ # from them. The image must always reside under /EFI/BOOT, and it
+ # must have a specific file name depending on the architecture.
+ efi_distributor=BOOT
+ case "$target_cpu" in
+ i386)
+ efi_file=BOOTIA32.EFI
+ ;;
+ x86-64)
+ efi_file=BOOTX64.EFI
+ ;;
+ # GRUB does not yet support these architectures, but they're defined
+ # by the specification so we include them here to ease future
+ # expansion.
+ ia64)
+ efi_file=BOOTIA64.EFI
+ ;;
+ arm)
+ efi_file=BOOTARM.EFI
+ ;;
+ esac
+ else
+ efi_distributor="$(echo "$GRUB_DISTRIBUTOR" | tr '[A-Z]' '[a-z]' | cut -d' ' -f1)"
+ if test -z "$efi_distributor"; then
+ efi_distributor=grub
+ fi
+ # It is convenient for each architecture to have a different
+ # efi_file, so that different versions can be installed in parallel.
+ case "$target_cpu" in
+ i386)
+ efi_file=grubia32.efi
+ ;;
+ x86-64)
+ efi_file=grubx64.efi
+ ;;
+ # GRUB does not yet support these architectures, but they're defined
+ # by the specification so we include them here to ease future
+ # expansion.
+ ia64)
+ efi_file=grubia64.efi
+ ;;
+ arm)
+ efi_file=grubarm.efi
+ ;;
+ *)
+ efi_file=grub.efi
+ ;;
+ esac
+ # TODO: We should also use efibootmgr, if available, to add a Boot
+ # entry for ourselves.
+ fi
+ efidir="$efidir/EFI/$efi_distributor"
+ mkdir -p "$efidir" || exit 1
+else
+ # We don't know what's going on. Fall back to traditional
+ # (non-specification-compliant) behaviour.
+ efidir="$grubdir"
+ efi_distributor=
+ efi_file=grub.efi
+fi
+
# Create the GRUB directory if it is not present.
mkdir -p "$grubdir" || exit 1
@@ -221,13 +336,16 @@ for dir in ${localedir}/*; do
fi
done
+# Write device to a variable so we don't have to traverse /dev every time.
+grub_device=`$grub_probe --target=device --device-map=${device_map} ${grubdir}` || exit 1
+
if ! test -f ${grubdir}/grubenv; then
$grub_editenv ${grubdir}/grubenv create
fi
# Create the core image. First, auto-detect the filesystem module.
-fs_module=`$grub_probe --target=fs --device-map=${device_map} ${grubdir}`
-if test "x$fs_module" = xfat; then :; else
+fs_module=`$grub_probe --target=fs --device-map=${device_map} --device ${grub_device}`
+if test "$efidir" != "$grubdir" || test "x$fs_module" = xfat; then :; else
echo "${grubdir} doesn't look like an EFI partition." 1>&2
exit 1
fi
@@ -236,17 +354,86 @@ fi
# this command is allowed to fail (--target=fs already grants us that the
# filesystem will be accessible).
partmap_module=
-for x in `$grub_probe --target=partmap --device-map=${device_map} ${grubdir} 2> /dev/null`; do
+for x in `$grub_probe --target=partmap --device-map=${device_map} --device ${grub_device} 2> /dev/null`; do
partmap_module="$partmap_module part_$x";
done
# Device abstraction module, if any (lvm, raid).
-devabstraction_module=`$grub_probe --target=abstraction --device-map=${device_map} ${grubdir}`
+devabstraction_module=`$grub_probe --target=abstraction --device-map=${device_map} --device ${grub_device}`
# The order in this list is critical. Be careful when modifying it.
modules="$modules $fs_module $partmap_module $devabstraction_module"
-$grub_mkimage -O ${target_cpu}-efi --output=${grubdir}/grub.efi $modules || exit 1
+relative_grubdir=`make_system_path_relative_to_its_root ${grubdir}` || exit 1
+if [ "x${relative_grubdir}" = "x" ] ; then
+ relative_grubdir=/
+fi
+
+prefix_drive=
+config_opt=
+
+if [ "x${devabstraction_module}" = "x" ] ; then
+ if [ x"${install_device}" != x ]; then
+ if echo "${install_device}" | grep -qx "(.*)" ; then
+ install_drive="${install_device}"
+ else
+ install_drive="`$grub_probe --target=drive --device-map=${device_map} --device ${install_device}`" || exit 1
+ fi
+ install_drive="`echo ${install_drive} | sed -e s/,[a-z0-9,]*//g`"
+ fi
+ grub_drive="`$grub_probe --target=drive --device-map=${device_map} --device ${grub_device}`" || exit 1
+
+ # Strip partition number
+ grub_drive="`echo ${grub_drive} | sed -e s/,[a-z0-9,]*//g`"
+ if [ "x${grub_drive}" != "x${install_drive}" ] ; then
+ uuid="`$grub_probe --target=fs_uuid --device ${grub_device}`"
+ if [ "x${uuid}" = "x" ] ; then
+ echo "You attempted a cross-disk install, but the filesystem containing ${grubdir} does not support UUIDs." 1>&2
+ exit 1
+ fi
+ echo "search.fs_uuid ${uuid} root " > ${grubdir}/load.cfg
+ echo 'set prefix=($root)'"${relative_grubdir}" >> ${grubdir}/load.cfg
+ config_opt="-c ${grubdir}/load.cfg "
+ modules="$modules search_fs_uuid"
+ fi
+else
+ prefix_drive=`$grub_probe --target=drive --device ${grub_device}` || exit 1
+fi
+
+$grub_mkimage ${config_opt} -O ${target_cpu}-efi --output=${efidir}/${efi_file} --prefix=${prefix_drive}${relative_grubdir} $modules || exit 1
+
+# Try to make this image bootable using the EFI Boot Manager, if available.
+if test "$removable" = no && test -n "$efi_distributor" && \
+ test -n "$efibootmgr"; then
+ # On Linux, we need the efivars kernel modules.
+ case "$host_os" in
+ linux*)
+ modprobe -q efivars 2>/dev/null || true
+ ;;
+ esac
+
+ # Delete old entries from the same distributor.
+ for bootnum in `efibootmgr | grep '^Boot[0-9]' | \
+ fgrep " $efi_distributor" | cut -b5-8`; do
+ efibootmgr $efi_quiet -b "$bootnum" -B
+ done
+
+ # Add a new entry for the image we just created. efibootmgr needs to be
+ # given the disk device and partition number separately, so we have to
+ # fiddle about with grub-probe to get hold of this reasonably reliably.
+ # Use fresh device map text to avoid any problems with stale data, since
+ # all we need here is a one-to-one mapping.
+ clean_devmap="$($grub_mkdevicemap --device-map=/dev/stdout)"
+ efidir_drive="$(echo "$clean_devmap" | $grub_probe --target=drive --device-map=/dev/stdin "$efidir")"
+ if test -z "$efidir_drive"; then
+ echo "Can't find GRUB drive for $efidir; unable to create EFI Boot Manager entry." >&2
+ else
+ efidir_disk="$(echo "$clean_devmap" | grep "^$(echo "$efidir_drive" | sed 's/,[^)]*//')" | cut -f2)"
+ efidir_part="$(echo "$efidir_drive" | sed 's/^([^,]*,[^0-9]*//; s/[^0-9].*//')"
+ efibootmgr $efi_quiet -c -d "$efidir_disk" -p "$efidir_part" -w \
+ -L "$GRUB_DISTRIBUTOR" -l "\\EFI\\$efi_distributor\\$efi_file"
+ fi
+fi
# Prompt the user to check if the device map is correct.
echo "Installation finished. No error reported."
Thanks,
--
Colin Watson [cjwatson@ubuntu.com]
next reply other threads:[~2010-07-12 12:31 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-07-12 11:52 Colin Watson [this message]
2010-07-12 21:40 ` [PATCH] Improve EFI grub-install to handle non-Apple systems Vladimir 'φ-coder/phcoder' Serbinenko
2010-07-12 21:53 ` Dmitrijs Ledkovs
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100712115236.GL21862@riva.ucam.org \
--to=cjwatson@ubuntu.com \
--cc=grub-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.