diff --git a/diskimage_builder/elements/block-device-efi/environment.d/15-block-device.bash b/diskimage_builder/elements/block-device-efi/environment.d/15-block-device.bash
new file mode 100644
index 000000000..ad8a26745
--- /dev/null
+++ b/diskimage_builder/elements/block-device-efi/environment.d/15-block-device.bash
@@ -0,0 +1,10 @@
+#
+# Arch gate
+#
+
+if [[ "ppc64 ppc64le ppc64el" =~ "$ARCH" ]]; then
+    echo "block-device-efi is not supported on Power; use block-device-gpt or block-device-mbr"
+    exit 1
+fi
+
+export DIB_BLOCK_DEVICE=efi
diff --git a/diskimage_builder/elements/block-device-gpt/environment.d/15-block-device.bash b/diskimage_builder/elements/block-device-gpt/environment.d/15-block-device.bash
new file mode 100644
index 000000000..213d2ae1e
--- /dev/null
+++ b/diskimage_builder/elements/block-device-gpt/environment.d/15-block-device.bash
@@ -0,0 +1,10 @@
+#
+# Arch gate
+#
+
+if [[ "arm64 aarch64" =~ $ARCH ]]; then
+    echo "block-device-gpt is not supported on AARCH64; use block-device-efi"
+    exit 1
+fi
+
+export DIB_BLOCK_DEVICE=gpt
diff --git a/diskimage_builder/elements/block-device-mbr/environment.d/15-block-device.bash b/diskimage_builder/elements/block-device-mbr/environment.d/15-block-device.bash
new file mode 100644
index 000000000..42e34897a
--- /dev/null
+++ b/diskimage_builder/elements/block-device-mbr/environment.d/15-block-device.bash
@@ -0,0 +1,10 @@
+#
+# Arch gate
+#
+
+if [[ "arm64 aarch64" =~ $ARCH ]]; then
+    echo "block-device-mbr is not supported on AARCH64; use block-device-efi"
+    exit 1
+fi
+
+export DIB_BLOCK_DEVICE=mbr
diff --git a/diskimage_builder/elements/bootloader/finalise.d/50-bootloader b/diskimage_builder/elements/bootloader/finalise.d/50-bootloader
index 4b9f9df77..be8e7ad3a 100755
--- a/diskimage_builder/elements/bootloader/finalise.d/50-bootloader
+++ b/diskimage_builder/elements/bootloader/finalise.d/50-bootloader
@@ -55,13 +55,21 @@ function install_grub2 {
     # Check for offline installation of grub
     if [ -f "/tmp/grub/install" ] ; then
         source /tmp/grub/install
-    # Right now we can't use pkg-map to branch by arch, so tag an architecture
-    # specific virtual package so we can install the rigth thing based on
-    # distribution.
+
+    # Right now we can't use pkg-map to branch by arch, so tag an
+    # architecture specific virtual package so we can install the
+    # rigth thing based on distribution.
     elif [[ "$ARCH" =~ "ppc" ]]; then
         install-packages -m bootloader grub-ppc64
-    else
+    elif [[ "${DIB_BLOCK_DEVICE}" == "mbr" ||
+                "${DIB_BLOCK_DEVICE}" == "gpt" ]]; then
         install-packages -m bootloader grub-pc
+    elif [[ "${DIB_BLOCK_DEVICE}" == "efi" ]]; then
+        install-packages -m bootloader grub-efi-$ARCH
+    else
+        echo "Failure: I'm not sure what bootloader to install"
+        echo "Ensure you have included a block-device-* element"
+        exit 1
     fi
 
     # XXX: grub-probe on the nbd0/loop0 device returns nothing - workaround, manually
@@ -127,7 +135,27 @@ function install_grub2 {
         # that a dev/loopXpN node will work fine.
         $GRUBNAME --modules="part_msdos" $GRUB_OPTS ${DEVICES[boot]} --no-nvram
     else
-        $GRUBNAME --modules="biosdisk part_msdos" $GRUB_OPTS $BOOT_DEV
+        # This set of modules is sufficient for all installs (mbr/gpt/efi)
+        modules="part_msdos part_gpt lvm"
+        extra_options=""
+        if [[ ${DIB_BLOCK_DEVICE} == "mbr" || ${DIB_BLOCK_DEVICE} == "gpt" ]]; then
+            modules="$modules biosdisk"
+        elif [[ ${DIB_BLOCK_DEVICE} == "efi" ]]; then
+            # This tells the EFI install to put the EFI binaries into
+            # the generic /BOOT directory and avoids trying to update
+            # nvram settings.
+            extra_options="--removable"
+            # We need to manually set the target if it's different to
+            # the host.  Setup for EFI
+            case $ARCH in
+                "x86_64"|"amd64")
+                    GRUB_OPTS="--target=x86_64-efi"
+                    ;;
+                # At this point, we don't need to override the target
+                # for any other architectures.
+            esac
+        fi
+        $GRUBNAME --modules="$modules" $extra_options $GRUB_OPTS $BOOT_DEV
     fi
 
     # This might be better factored out into a per-distro 'install-bootblock'
@@ -144,7 +172,7 @@ function install_grub2 {
     echo 'GRUB_DISABLE_LINUX_UUID=true' >> /etc/default/grub
     echo "GRUB_TIMEOUT=${DIB_GRUB_TIMEOUT:-5}" >>/etc/default/grub
     echo 'GRUB_TERMINAL="serial console"' >>/etc/default/grub
-    echo 'GRUB_GFXPAYLOAD_LINUX=text' >>/etc/default/grub
+    echo 'GRUB_GFXPAYLOAD_LINUX=auto' >>/etc/default/grub
 
     # Serial console on Power is hvc0
     if [[ "powerpc ppc64 ppc64le" =~ "$ARCH" ]]; then
diff --git a/diskimage_builder/elements/bootloader/pkg-map b/diskimage_builder/elements/bootloader/pkg-map
index 1436e54be..b5fefc79f 100644
--- a/diskimage_builder/elements/bootloader/pkg-map
+++ b/diskimage_builder/elements/bootloader/pkg-map
@@ -3,16 +3,19 @@
     "gentoo": {
       "dkms_package": "",
       "extlinux": "syslinux",
-      "grub-pc": "grub"
+      "grub-pc": "grub",
+      "grub-efi": "grub"
     },
     "suse": {
       "dkms_package": "",
       "extlinux": "syslinux",
-      "grub-pc": "grub2"
+      "grub-pc": "grub2",
+      "grub-efi": "grub2"
     },
     "redhat": {
       "extlinux": "syslinux-extlinux",
       "grub-pc": "grub2-tools grub2",
+      "grub-efi": "grub2-tools grub2-efi",
       "grub-ppc64": "grub2-tools grub2"
     }
   },
@@ -20,6 +23,8 @@
     "dkms_package": "dkms",
     "extlinux": "extlinux",
     "grub-pc": "grub-pc",
+    "grub-efi-amd64": "grub-efi",
+    "grub-efi-arm64": "grub-efi-arm64",
     "grub-ppc64": "grub-ieee1275"
   }
 }