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 <cjwat...@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_pref...@exec_prefix@ sbind...@sbindir@ bind...@bindir@ libd...@libdir@ +sysconfd...@sysconfdir@ package_na...@package_name@ package_tarna...@package_tarname@ package_versi...@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 [cjwat...@ubuntu.com] _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel